Недавно познакомился с FreeBasic. Создаю dll, которую будет вызывать внешняя программа. Задача dll - в разных потоках независимо рисовать текст с помощью gdip+ и показывать картинки в ImageGadget.
Однако при вызове функции загрузки картинки рисование текста притормаживает.
Не могу понять, дело в неправильном алгоритме, или это глобальное ограничение языка?
Dim Shared GDIPLUSSTARTUPINPUT As GDIPLUSSTARTUPINPUT Dim Shared As ULONG_PTR gdiplusToken
Type S_info gpim As PVOID g As PVOID fg As PVOID BrushText As PVOID FontFam As PVOID font As PVOID Brush1 As PVOID hWnd As HWND End Type
Dim Shared S1B As S_info Dim Shared As RECTF SR1 = Type(0,0,1000,100) Dim Shared S1_cc As Double
Dim Shared hwnd0 As HWND Dim Shared As String sIm
Type boxinfo hWnd As HWND End Type
Dim Shared mbox As boxinfo
Sub _OnPaint(g As Any Ptr) SR1 = Type(S1_cc, 0, 1000,100) S1_cc -= 0.5 If S1_cc <= - 1000 Then S1_cc = 1000 EndIf GdipSetSmoothingMode(S1B.fg, 5) GdipGraphicsClear(S1B.fg,&hFF000000) GdipFillRectangle(S1B.fg, S1B.Brush1, 0, 0, 1000,100) GdipDrawString(S1B.fg, WSTR("FreeBasic FreeBasic FreeBasic FreeBasic FreeBasic FreeBasic"), LEN("FreeBasic FreeBasic FreeBasic FreeBasic FreeBasic FreeBasic"), S1B.font, @SR1, NULL, S1B.BrushText) GdipDrawImage(S1B.g,S1B.gpim, 0, 0) End Sub
Function WndProc(hWnd As HWND,uMsg As UINT,wParam As WPARAM,lParam As LPARAM) As Integer Select Case uMsg Case WM_CLOSE KillTimer(hwnd,1) GdipDeleteGraphics(S1B.g) GdipDeleteGraphics(S1B.fg) GdipDisposeImage(S1B.gpim) GdipDeleteBrush(S1B.BrushText) GdipDeleteBrush(S1B.Brush1) GdiplusShutdown gdiplusToken PostQuitMessage(0) Case WM_CREATE GDIPLUSSTARTUPINPUT.GdiplusVersion = 1 GdiplusStartup(@gdiplusToken, @GDIPLUSSTARTUPINPUT, NULL) GdipCreateFromHWND(hWnd,@S1B.g) GdipCreateBitmapFromScan0(1000,100, NULL, PixelFormat32bppARGB, NULL, @S1B.gpim) GdipGetImageGraphicsContext(S1B.gpim,@S1B.fg) GdipCreateSolidFill(&hFFFF0000,@S1B.BrushText) GdipCreateFontFamilyFromName("Tahoma",NULL,@S1B.FontFam) GdipCreateFont(S1B.FontFam, 35, NULL, NULL, @S1B.font) GdipCreateSolidFill(&hFF000000,@S1B.Brush1) SetTimer(hWnd, 1, 10, 0) Case WM_TIMER _OnPaint(S1B.g) Case Else Return DefWindowProc(hWnd,uMsg,wParam,lParam) End Select End Function
Sub _Create(mPtr As S_info Ptr) Dim wc As WNDCLASSEX Dim msg As MSG With wc .hInstance=GetModuleHandle(0) .cbSize=SizeOf(WNDCLASSEX) .style=CS_HREDRAW Or CS_VREDRAW .lpfnWndProc=@WndProc .lpszClassName=StrPtr("Class") .hCursor=LoadCursor(NULL,IDC_ARROW) End With RegisterClassEx(@wc) CreateWindowEx(0,wc.lpszClassName,"Str",WS_OVERLAPPEDWINDOW Or WS_VISIBLE,0,100,1000,100,0,0,wc.hInstance,0) _OnPaint(S1B.g) While GetMessage(@msg,0,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend End Sub
Sub _tImBox (mPtr As boxinfo Ptr) Dim info As boxinfo = *mPtr Dim As Integer event hwnd0 = OpenWindow("Image",100,200,500,500, WS_OVERLAPPEDWINDOW Or WS_VISIBLE) ShowWindow(hwnd0,SW_SHOWNORMAL) ImageGadget(1,0,0,500,500) ' Do event=WaitEvent() If event=EventClose Then End Loop End Sub
Sub Im(patch As String) Var hbitmap=Load_image(sIm) hbitmap=Resize_image(hbitmap,500,500) SetImageGadget(1,hbitmap) Free_Image(hBitmap) End Sub
' функции, вызываемые внешней программой Extern "windows-ms"
Function _RunImage(ByVal TextP As String) As String Export ' загрузка картинки sIm = TextP ThreadCreate(@Im, @TextP) Function = TextP End Function
Function _ImageInit(ByVal hWndC As HWND) As HWND Export ' запуск ImageGadget mbox.hWnd = hWndC ThreadCreate(Cast(Any Ptr, @_tImBox), @mbox) Function = mbox.hWnd End Function
Function _StrInit(ByVal hWnd As HWND) As HWND Export ' запуск движущегося текста S1B.hWnd = hWnd ThreadCreate(Cast(Any Ptr, @_Create), @S1B) Function = S1B.hWnd End Function
1) Интерфейс GUI рекомендуется создавать в одном основном потоке, потому что я уже не раз сталкивался с тем, что многие сообщения просто не дойдут до окна, созданного в другом потоке. Отсюда все окошки правильно создавать в основном потоке
2) Даже если наплевать на 1 пункт, то в первую очередь нужно понять, что создавая отдельные потоки, обязательно нужно заботиться о безопасности доступа к данным в программе. В коде же предложенном выше я вижу сплошные ошибки. Окна создаются в разных потоках, но обработка сообщений в одной процедуре. Или переменная S1B одна на все потоки без мьютексов, получается что несколько потоков в определенный момент обращаются к данным одновременно, что вполне закономерно вызовет не только тормоза, но вылеты программы. Представим себе разные потоки как 3-4 человека, которые зашивают одну разорванную рубашку. Пока все шьют в разных местах, все нормально, но вот они дошли до места, где каждый из них пытается иголкой попадать в одно место и в одно время, что будет?
Мой совет:
1) как следует изучить управление потоками (хотя бы прочитать и понять это) 2) определить места в программе , где без потоков никак не обойтись, все остальные места сделать в основном потоке. Неправильно пихать эту технику во все дыры. В одной из моих программ без потоков никак не обойтись, мне пришлось их создать 2 или 3. И даже с этим малым кол-вом потоков я испытал настоящий напряг с обработкой общих данных.
В любом случае, дело тут не в языке, а в знаниях и умениях применять данную технику. В реале , все что я увидел в коде, можно сделать вообще без лишних потоков, исключительно с использованием SetTimer. По этому поводу я конечно могу ошибаться, поскольку не вижу реальной цели программы и слабо пока понимаю, что в конце концов должна делать конечная программа. Вы сохраняете власть над людьми покуда оставляете им что-то…Отберите у человека все, и этот человек уже будет неподвластен вам…
1) как следует изучить управление потоками (хотя бы прочитать и понять это)
С этого материала я и начал (вообще, спасибо за подробный материал), но пока не могу осознать полезность мьютексов конкретно для моей проблемы.
Цитата
В любом случае, дело тут не в языке, а в знаниях и умениях применять данную технику. В реале , все что я увидел в коде, можно сделать вообще без лишних потоков, исключительно с использованием SetTimer. По этому поводу я конечно могу ошибаться, поскольку не вижу реальной цели программы и слабо пока понимаю, что в конце концов должна делать конечная программа.
Я пишу программу на языке autoit, и хочу расширить ее функционал с помощью dll, написанных на FreeBasic, т.к. в autoit нет многопоточности. Все делается в одном цикле, и рисование gdip-функциями притормаживает, если добавить в цикл еще какое-нибудь действие, вроде загрузки картинки.
Что она делает - при запуске нужно указать папку с jpg, и программа покажет 3 окна: 1 свое, autoit-кое, и 2 - вызванные из dll FreeBasic, одно окно - движущийся текст, а другое - для показа картинок в ImageGadget Если нажимать клавишу "Pause", скрипт autoit вызывает функцию из dll для переключения картинок в ImageGadget, картинки переключаются. Но проблема в том, что при этом текст начинает двигаться рывками.
Задача - сделать, чтобы и картинки грузились, и тормозов текста не было.
Сообщение отредактировал WQ - Воскресенье, 04.05.2014, 16:43
#Include "window9.bi" Using GDIPLUS Dim Shared As String sPath(100) ' список путей к файлам изображений.
Function LoadImageWindow() As hwnd Export Var hwnd = OpenWindow("ImageAnimate",10,10,600,600) ImageGadget(1,10,10,580,580) Return hwnd End Function
Function LoadTextWindow() As hwnd Export Var hwnd = OpenWindow("TextAnimate",10,10,200,200) ImageGadget(2,10,10,180,180) Return hwnd End Function
Sub AnimateText(sText As ZString Ptr) Export Static As Integer y = 10,x = 0 Do WindowStartDrawA(GadgetID(2)) BoxDrawA(0,0,200,200,&hfff0f0f0,,&hfff0f0f0) ModeDrawA(2) TextDrawA(*sText,x,y,,&h80FF0000,,TextRenderingHintAntiAlias) StopDrawA x+=2 If x>200 Then x = 0 If WindowEvent = eventclose Then End Sleep(10) Loop
End Sub
Sub AnimateImage(Directory As ZString Ptr) Export Dim As Integer i Var Dir_=ExamineDirectory(*Directory, "*.bmp*") Do sPath(i) = *Directory & DirectoryEntryName(Dir_) i+=1 Loop While NextDirectoryEntry(Dir_)
For j As Integer = 0 To i-1 Var hbitmap=Load_image(sPath(j)) hbitmap=Resize_image(hbitmap,500,500) SetImageGadget(1,hbitmap) Free_Image(hBitmap) If WindowEvent = eventclose Then End Sleep(500) Next DisableGadget(100,0) End Sub
Do Var ev = WaitEvent Select Case ev Case eventclose End Case eventgadget If EventNumber = 100 Then ThreadCreate(Cast(Any Ptr,@AnimateImage), @"D:\OBOI\") disableGadget(100,1) EndIf End Select Loop
Для путей я брал свой каталог с файлами bmp. Данный код просто для теста без использования autoIt . Только средствами FB. Но если он работает нормально, то переписать (обернуть в функции) часть кода не будет сложным. Я сделал с 2 потоками. Вы сохраняете власть над людьми покуда оставляете им что-то…Отберите у человека все, и этот человек уже будет неподвластен вам…
К сожалению, если картинки формата jpg или png, то рывки у текста все равно наблюдаются, с bmp вроде нормально. Но bmp несжатый формат, возможно дело в этом...
К сожалению, если картинки формата jpg или png, то рывки у текста все равно наблюдаются, с bmp вроде нормально. Но bmp несжатый формат, возможно дело в этом...
Да, я попробовал на bmp больших размеров, есть тормоза. Я не знаю точно почему это возникает, у меня только смутные догадки.
1 догадка: Как не распараллеливай задачи, все равно вся отрисовка происходит в одних и тех же функциях DLL. То есть пока рисуется изображение на одном окне, отрисовка текста в другом окне встает в очередь и наоборот. GDI никогда не славилась скоростью. Стабильностью, совместимостью да, но только не скоростью. 2 догадка: Может влияет то, что файлы считываются постоянно с жесткого диска (все таки скорость обмена данными с хардом низкая).
Что можно сделать в этой ситуации. Я пока вижу только один выход( может их конечно несколько и есть более простые и более удобные, но я пока вижу только один). Сделать отрисовку разных окон разными графическими библиотеками. То есть, например текст и еще какую нибудь мелочевку в одном окне рисовать с помощью GDI+ , а изображения в другом окне с помощью того же встроенного FBGFX или с помощью OpenGL.
Вы сохраняете власть над людьми покуда оставляете им что-то…Отберите у человека все, и этот человек уже будет неподвластен вам…
Да, я попробовал на bmp больших размеров, есть тормоза. Я не знаю точно почему это возникает, у меня только смутные догадки. 1 догадка: Как не распараллеливай задачи, все равно вся отрисовка происходит в одних и тех же функциях DLL. То есть пока рисуется изображение на одном окне, отрисовка текста в другом окне встает в очередь и наоборот. GDI никогда не славилась скоростью.
Я пока вижу только один выход( может их конечно несколько и есть более простые и более удобные, но я пока вижу только один). Сделать отрисовку разных окон разными графическими библиотеками. То есть, например текст и еще какую нибудь мелочевку в одном окне рисовать с помощью GDI+ , а изображения в другом окне с помощью того же встроенного FBGFX или с помощью OpenGL.
Действительно, похоже что все упирается в функцию Load_image, которая пересекается с рисованием. Плохо, этими функциями просто и удобно загружать и обрезать картинки.
В сборке Freebasic есть пример TestFBEPictViewLib.bas Попробовал прикрутить показ картинок из него - вроде рисование работает без тормозов, но там мне непонятно, как указывать размер и положение этого самого элемента показывающего картинку + нужно размеры картинки подгонять. Смотрел библиотеки IUP и FLTK - там неплохие примеры с картинками, но пока не разобрался с документацией. В общем, мне бы небольшой пример загрузки картинки в произвольное окошко какой-нибудь сторонней библиотекой, а дальше я сам разбираться буду.
В FLTK есть и рисование текста, вроде не хуже GDI. Вообще, опять же, неплохо бы заменить GDI на что-то с поддержкой аппаратного ускорения - мне нужно рисовать несколько окон с движущимся текстом, а это сильно нагружает процессор.
Чтобы что-то советовать, надо четко понимать, что в конце концов нужно сделать. По прошлому примеру я видел, что надо создать одно окно для вывода изображений с жестко заданным размером и одно окно с текстом. Теперь оказывается надо несколько окон с текстом. Потом может быть окажется что и изображения надо рисовать с любыми размерами, а может быть и не в одном окне. Опять же я гадаю... изображения будут всегда одни и те же (в одной папке) , или это на выбор пользователя. Может быть вообще все удобнее\правильнее сделать в одном окне при помощи OpenGL или FbGfx... В общем пока нет четко установленной задачи , все что я советую (или буду советовать) , окажется малопродуктивным.
Цитата
Вообще, опять же, неплохо бы заменить GDI на что-то с поддержкой аппаратного ускорения - мне нужно рисовать несколько окон с движущимся текстом, а это сильно нагружает процессор.
Аппаратное ускорение - это конечно хорошо, но как его сделать на нескольких окнах, я если честно ни разу не пробовал, да и возможно ли это? Поэтому и предлагал текст рисовать с помощью GDI, а загрузку картинок, с последующим выводом с помощью другой библиотеки.
Цитата
В общем, мне бы небольшой пример загрузки картинки в произвольное окошко какой-нибудь сторонней библиотекой, а дальше я сам разбираться буду.
Если тормоза именно из-за Load_image , то можно попробовать загрузку картинок с помощью freeimage (понимает кучу форматов), на моем сайте есть ссылка на архив. А с выводом на окна надо определяться с графическими библиотеками. Я могу ошибаться, но мне кажется что все библиотеки fltk, iup , cairo завязаны на GDI под windows. То есть наверняка эффект будет тот же. Вы сохраняете власть над людьми покуда оставляете им что-то…Отберите у человека все, и этот человек уже будет неподвластен вам…
В сборке Freebasic есть пример PictCtrl.Bas, а в нем функции LoadPict и BitmapFromPicture(на основе WinApi, как я понимаю). Замена Load_image на LoadPict привела к получению нужного результата. Правда, не читает png. Freeimage не хочется использовать, т.к. с программой придется таскать и dll