Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.97.248] |
|
Сообщ.
#1
,
|
|
|
Доброго всем времени суток. Имеется проект >6000 строк на Делфи, в котором дня три назад был найден недостаток - VCL через раз не может загрузить png файл, а загружает чёрный квадрат (и делает это только на двух компах: на моём и на компе принимающей стороны). Проверил сразу после загрузки - это действительно ошибка загрузки, а не отрисовки или чего-то ещё, действительно загружается чёрный квадрат. Png решил загружать через GDI+, так как у него много полезностей: меньше размер, поддержка TIF и EMF, да и вообще собираюсь переписать эту программу на C++, так что начал добавлять GDI+. Из-за того, что перевод Gdiplus.h на делфи корявый и динамическая загрузка сложная, решил делать dll на C++. Начались проблемы. В интернете нахожу десятками варианты загрузки изображений, причём видно, что у кого-то они таки компилировались, но не у меня. Итого:
1) Как загрузить картинку? 2) Как передать её в программу? Можно ли передать какой-то хендл, разрушить объект C++ без освобождения картинки и создать TBitMap так, чтобы загрузить хендл извне? Если нет, буду передавать через GetDiBits/Scanline; Прошу помощи. Я не очень хорошо знаю C++, и мне он сейчас нужен только чтоб исправить этот один баг. А я уже 3 дня мучаюсь. Использую CodeBlocks\MinGW. Заранее спасибо. |
Сообщ.
#2
,
|
|
|
1. Да загружайте как раньше - через GDI+, это действительно весьма неплохой способ/метод.
2. Передать из DLL в основную (или несколько "наоборот"?)? Да тоже вполне ГОСТ'ом - через указатель=HANDLE. Просто нужно следить, чтобы инициализация/выгруз GDI+ шли в основной программе, ну или не разрывались где-то посредине. Короче, набросайте какой-то идеальный пример с возникающей ошибкой - там и поглядим что ломается. |
Сообщ.
#3
,
|
|
|
Цитата k.sovailo @ 1) Как загрузить картинку? 2) Как передать её в программу? Можно ли передать какой-то хендл, разрушить объект C++ без освобождения картинки и создать TBitMap так, чтобы загрузить хендл извне? Если нет, буду передавать через GetDiBits/Scanline; Почитай тут: 1 2 3 там и исходники имеются. После прочтения 1. сделаем объект "memdc" (DC в памяти). 2. при помощи объектов GDI+ загрузим (в memdc) картинку из файла или из ресурсов приложения. 3. сможем выводить картинку в окно. |
Сообщ.
#4
,
|
|
|
Я тут накидал. На стороне Dll получилось это:
ULONG_PTR gdiplusToken; BOOL DLL_EXPORT GDIStart() { Gdiplus::GdiplusStartupInput gdiplusStartupInput; return((Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL))==Gdiplus::Ok); } void DLL_EXPORT GDIShutDown() { Gdiplus::GdiplusShutdown(gdiplusToken); } // a sample exported function HBITMAP DLL_EXPORT LoadPic(const WCHAR* Path) { Gdiplus::Bitmap bitmap(Path); HBITMAP hBitmap; if ((bitmap.GetHBITMAP(Gdiplus::Color(255, 255, 255, 255), &hBitmap))==Gdiplus::Ok){ return (hBitmap); }else{ return 0; //0 - загрузка не удалась } } А на стороне приёма там динамическая загрузка и вызов GDIStart, проблем вроде нет. Проблема тут: procedure TForm1.FormPaint(Sender: TObject); var Path: string; Bitmap: TBitmap; hBit: HBITMAP; begin Path:='C:/Users/irochka/Desktop/DelphiTests/Source/Goal.bmp'; if (hCriticalDll<>0)and(fileexists(Path)) then begin hBit:=LoadPic(PWideChar(@Path[1])); //LoadPic(Path: PWideChar): HBITMAP - функция из DLL if hBit<>0 then begin Bitmap:=TBitmap.Create; Bitmap.Handle:=hBit; Form1.Canvas.Draw(0,0,Bitmap); Bitmap.Free; end; end; end; Если в Dll написать имя файла, то всё вроде нормально. Не получается передать имя. Как изобразить на делфи "const WCHAR*"? На какой символ строки требуется взять указатель? А то я играюсь, и на первый даю, и на нулевой, а всё равно никак. |
Сообщ.
#5
,
|
|
|
Так нельзя делать!!
Картинки имеют разный размер, а потому память должна выделяться динамически! Посему в DLL-ке должно быть исправление вида: Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(h,Path); // h - hInstance ///a'la GetHBITMAP ... delete bitmap; |
Сообщ.
#6
,
|
|
|
Славян, спасибо, уже исправил. Хотя сути исправления я не понял, потому что думал, что объект созданный так: 'Gdiplus::Bitmap bitmap' кладётся на стек и убирается после окончания функции. Но ладно, всё равно спасибо. Но у меня назрела другая проблема. Я уже писал, что не могу передать строку, а потом начал ковырять программу и понял, что вообще ничего не могу передать. Совсем, даже один байт. Причём если попробовать прописать соглашение stdcall, то вообще вылетает ошибка. Не могу передать даже один байт, передаётся всегда одно и то же число: 200. Что это такое? Когда-то я уже писал Dll, всё работало.
|
Сообщ.
#7
,
|
|
|
После того, как в Делфи явно указал stdcall, а в C++ это же _stdcall убрал, заработало, иначе была либо ошибка доступа, либо просто передавалась какая-то фигня в аргументы. Вроде заработало, но с небольшим но: нет полупрозрачности. Загружает png, проверяю попиксельно - 32 бита/пиксель, альфа сохранилась, но при отрисовке она не учитывается. Как сделать так, чтоб рисовалось с учётом альфы? Какой параметр TBitmap за это отвечает?
|
Сообщ.
#8
,
|
|
|
Никакой как бы. Надо рисовать правильной функцией - AlphaBlend(...).
|
Сообщ.
#9
,
|
|
|
Цитата k.sovailo @ Какой параметр TBitmap за это отвечает? Создавать её надо с соответствующим параметром Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(PaintBox1->Width, PaintBox1->Height, PixelFormat32bppARGB); Всё ж в документации есть! http://rsdn.org/?article/gdi/gdiplus2mag.xml https://msdn.microsoft.com/en-us/library/wi...7(v=vs.85).aspx |
Сообщ.
#10
,
|
|
|
Цитата Славян @ Так нельзя делать!! Картинки имеют разный размер, а потому память должна выделяться динамически! Почему ? Если посмотреть sizeof(Gdiplus::Bitmap) получится 16 байт. Картинка помещается, очевидно, не в сам объект. (Да и как бы это можно было сделать ?) Для неё выделяется память динамически, а в объекте указатель. |
Сообщ.
#11
,
|
|
|
Да, тут я несколько неточно выразился. Зная особенность класса, можно и было забить на динамическое создание. Просто если делать динамику на автомате, то можно не заморачиваться на внутреннее исследование класса. Прошу пардона, если что.
|
Сообщ.
#12
,
|
|
|
Цитата Славян @ Прошу пардона, если что. Да я не об этом. Тут другое - если мы начинаем активно создавать динамически объекты очень разных размеров, тогда не только тратим время на создание/уничтожение, но и фрагментируем память. Действительно, размеры картинок и размеры объектов очень разные. Мастерить крупные объекты на стеке я бы не рискнул. А вот создавать много мелких объектов вперемешку с крупными просто не выгодно. |
Сообщ.
#13
,
|
|
|
Ого, спасибо спасибо за ответы. Тогда можно ещё один вопрос по оптимизации всего этого дела в плане оперативной памяти? Не получается загрузить картинку сразу в PixelFormat24bppRGB, даже если достоверно известно, что она будет 24-битной. Если формат JPEG например. То есть моё приложение потом всё равно уменьшит до 24 или до 16 бит, но хотелось бы без этих ненужных перевыделений памяти.
Ещё было бы неплохо определять до загрузки, содержит ли изображение альфа-канал или нет. Нет, это можно конечно сделать через поток и почитав про формат шапки каждого формата, но не очень надёжно и красиво. Метода AlphaBlend я ни у какого компонента не нашёл (только у TForm, но это не то). проблема решилась так: bitmap.AlphaFormat:=afDefined; |
Сообщ.
#14
,
|
|
|
1.AlphaBlend - WinAPI-шная, для рисования HBITMAP'ами.
2.Определить альфа-канал действительно надо, думаю, читая шапку формата, ибо тонкости хранения (в общем случае) - неизвестны, а винда, читая, в итоге всё равно сделает 32 бита и адью. Впрочем, если у картинки есть альфа, но она всюду равна 255 (не прозрачно), то означает ли это, что картинка содержит альфа-канал? |