На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
  
> Загрузка файла GDI+ и передача в программу, Очень нужно исправить баг
    Доброго всем времени суток. Имеется проект >6000 строк на Делфи, в котором дня три назад был найден недостаток - VCL через раз не может загрузить png файл, а загружает чёрный квадрат (и делает это только на двух компах: на моём и на компе принимающей стороны). Проверил сразу после загрузки - это действительно ошибка загрузки, а не отрисовки или чего-то ещё, действительно загружается чёрный квадрат. Png решил загружать через GDI+, так как у него много полезностей: меньше размер, поддержка TIF и EMF, да и вообще собираюсь переписать эту программу на C++, так что начал добавлять GDI+. Из-за того, что перевод Gdiplus.h на делфи корявый и динамическая загрузка сложная, решил делать dll на C++. Начались проблемы. В интернете нахожу десятками варианты загрузки изображений, причём видно, что у кого-то они таки компилировались, но не у меня. Итого:

    1) Как загрузить картинку?
    2) Как передать её в программу? Можно ли передать какой-то хендл, разрушить объект C++ без освобождения картинки и создать TBitMap так, чтобы загрузить хендл извне? Если нет, буду передавать через GetDiBits/Scanline;

    Прошу помощи. Я не очень хорошо знаю C++, и мне он сейчас нужен только чтоб исправить этот один баг. А я уже 3 дня мучаюсь. Использую CodeBlocks\MinGW. Заранее спасибо.
      1. Да загружайте как раньше - через GDI+, это действительно весьма неплохой способ/метод.
      2. Передать из DLL в основную (или несколько "наоборот"?)? Да тоже вполне ГОСТ'ом - через указатель=HANDLE. Просто нужно следить, чтобы инициализация/выгруз GDI+ шли в основной программе, ну или не разрывались где-то посредине.

      Короче, набросайте какой-то идеальный пример с возникающей ошибкой - там и поглядим что ломается. :blush:
        Цитата k.sovailo @
        1) Как загрузить картинку?
        2) Как передать её в программу? Можно ли передать какой-то хендл, разрушить объект C++ без освобождения картинки и создать TBitMap так, чтобы загрузить хендл извне? Если нет, буду передавать через GetDiBits/Scanline;

        Почитай тут:
        1
        2
        3
        там и исходники имеются.

        После прочтения
        1. сделаем объект "memdc" (DC в памяти).
        2. при помощи объектов GDI+ загрузим (в memdc) картинку из файла или из ресурсов приложения.
        3. сможем выводить картинку в окно.
        Сообщение отредактировано: ЫукпШ -
        Подпись была выключена в связи с наложенным заземлением.
          Я тут накидал. На стороне Dll получилось это:
          ExpandedWrap disabled
            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, проблем вроде нет. Проблема тут:

          ExpandedWrap disabled
            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*"? На какой символ строки требуется взять указатель? А то я играюсь, и на первый даю, и на нулевой, а всё равно никак.
            Так нельзя делать!!
            Картинки имеют разный размер, а потому память должна выделяться динамически! Посему в DLL-ке должно быть исправление вида:
            ExpandedWrap disabled
              Gdiplus::Bitmap *bitmap = new Gdiplus::Bitmap(h,Path); // h - hInstance
              ///a'la GetHBITMAP  ...
              delete bitmap;
              Славян, спасибо, уже исправил. Хотя сути исправления я не понял, потому что думал, что объект созданный так: 'Gdiplus::Bitmap bitmap' кладётся на стек и убирается после окончания функции. Но ладно, всё равно спасибо. Но у меня назрела другая проблема. Я уже писал, что не могу передать строку, а потом начал ковырять программу и понял, что вообще ничего не могу передать. Совсем, даже один байт. Причём если попробовать прописать соглашение stdcall, то вообще вылетает ошибка. Не могу передать даже один байт, передаётся всегда одно и то же число: 200. Что это такое? Когда-то я уже писал Dll, всё работало.
                После того, как в Делфи явно указал stdcall, а в C++ это же _stdcall убрал, заработало, иначе была либо ошибка доступа, либо просто передавалась какая-то фигня в аргументы. Вроде заработало, но с небольшим но: нет полупрозрачности. Загружает png, проверяю попиксельно - 32 бита/пиксель, альфа сохранилась, но при отрисовке она не учитывается. Как сделать так, чтоб рисовалось с учётом альфы? Какой параметр TBitmap за это отвечает?
                  Никакой как бы. Надо рисовать правильной функцией - AlphaBlend(...).
                    Цитата k.sovailo @
                    Какой параметр TBitmap за это отвечает?

                    Создавать её надо с соответствующим параметром
                    ExpandedWrap disabled
                       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
                    ...one shot at glory in the crossfire overhead...© JP
                      Цитата Славян @
                      Так нельзя делать!!
                      Картинки имеют разный размер, а потому память должна выделяться динамически!

                      Почему ?
                      Если посмотреть sizeof(Gdiplus::Bitmap) получится 16 байт.
                      Картинка помещается, очевидно, не в сам объект.
                      (Да и как бы это можно было сделать ?)
                      Для неё выделяется память динамически, а в объекте указатель.
                      Сообщение отредактировано: ЫукпШ -
                      Подпись была выключена в связи с наложенным заземлением.
                        Да, тут я несколько неточно выразился. Зная особенность класса, можно и было забить на динамическое создание. Просто если делать динамику на автомате, то можно не заморачиваться на внутреннее исследование класса. Прошу пардона, если что. :oops:
                          Цитата Славян @
                          Прошу пардона, если что. :oops:

                          Да я не об этом. :D
                          Тут другое - если мы начинаем активно создавать динамически объекты очень разных
                          размеров, тогда не только тратим время на создание/уничтожение, но и фрагментируем память.
                          Действительно, размеры картинок и размеры объектов очень разные.
                          Мастерить крупные объекты на стеке я бы не рискнул.
                          А вот создавать много мелких объектов вперемешку с крупными просто не выгодно.
                          Подпись была выключена в связи с наложенным заземлением.
                            Ого, спасибо спасибо за ответы. Тогда можно ещё один вопрос по оптимизации всего этого дела в плане оперативной памяти? Не получается загрузить картинку сразу в PixelFormat24bppRGB, даже если достоверно известно, что она будет 24-битной. Если формат JPEG например. То есть моё приложение потом всё равно уменьшит до 24 или до 16 бит, но хотелось бы без этих ненужных перевыделений памяти.

                            Ещё было бы неплохо определять до загрузки, содержит ли изображение альфа-канал или нет. Нет, это можно конечно сделать через поток и почитав про формат шапки каждого формата, но не очень надёжно и красиво.

                            Метода AlphaBlend я ни у какого компонента не нашёл (только у TForm, но это не то). проблема решилась так:
                            ExpandedWrap disabled
                              bitmap.AlphaFormat:=afDefined;
                              1.AlphaBlend - WinAPI-шная, для рисования HBITMAP'ами.
                              2.Определить альфа-канал действительно надо, думаю, читая шапку формата, ибо тонкости хранения (в общем случае) - неизвестны, а винда, читая, в итоге всё равно сделает 32 бита и адью. Впрочем, если у картинки есть альфа, но она всюду равна 255 (не прозрачно), то означает ли это, что картинка содержит альфа-канал?
                              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                              0 пользователей:


                              Рейтинг@Mail.ru
                              [ Script Execution time: 0,1259 ]   [ 18 queries used ]   [ Generated: 21.08.18, 07:51 GMT ]