Blog. Just Blog

Вывод изображения на Ассемблере с помощью WIC

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
На сайте уже есть две статьи про загрузку и вывод изображения с помощью GDI+ и OLE, сегодня расскажу про еще очередной способ загрузки изображений, на этот раз с помощью WIC (Windows Imaging Component). Эта технология позволяет не привязываться к конкретным форматам изображений, а делегировать большую часть рутинной работы системе. Если установлен соответствующий графический кодек и система в принципе может открыть изображение во встроенном Просмотрщике фотографий, значит его можно будет прочитать и при помощи Windows Imaging Component. Например, популярный формат WebP в чистой Windows 7 не открывается, но если поставить кодеки, то все сразу заработает.

Переходим к программированию. Поскольку это COM-технология, то нам понадобится описание множества GUID, констант и интерфейсов. Надо ли говорить, что FASM про них ничего не знает? А множество действительно внушительное.
  1. ; GUID {CACAF262-9370-4615-A13B-9F5539DA4C0A}
  2. CLSID_WICImagingFactory dd 0CACAF262h
  3.                         dw 09370h
  4.                         dw 04615h
  5.                         db 0A1h, 03Bh, 09Fh, 055h, 039h, 0DAh, 04Ch, 00Ah
  6.  
  7. ; GUID {EC5EC8A9-C395-4314-9C77-54D7A935FF70}
  8. IID_IWICImagingFactory dd 0EC5EC8A9h
  9.                        dw 0C395h
  10.                        dw 04314h
  11.                        db 09Ch, 077h, 054h, 0D7h, 0A9h, 035h, 0FFh, 070h
  12.  
  13. ; IID_IWICImagingFactory Interface
  14. struct IWICImagingFactory
  15.     ; IUnknown
  16.     QueryInterface                           dd ?   ; 000h
  17.     AddRef                                   dd ?   ; 004h
  18.     Release                                  dd ?   ; 008h
  19.     ; IWICImagingFactory
  20.     CreateDecoderFromFilename                dd ?   ; 00Ch
  21.     CreateDecoderFromStream                  dd ?   ; 010h
  22.     CreateDecoderFromFileHandle              dd ?   ; 014h
  23.     CreateComponentInfo                      dd ?   ; 018h
  24.     CreateDecoder                            dd ?   ; 01Ch
  25.     CreateEncoder                            dd ?   ; 020h
  26.     CreatePalette                            dd ?   ; 024h
  27.     CreateFormatConverter                    dd ?   ; 028h
  28.     CreateBitmapScaler                       dd ?   ; 02Ch
  29.     CreateBitmapClipper                      dd ?   ; 030h
  30.     CreateBitmapFlipRotator                  dd ?   ; 034h
  31.     CreateStream                             dd ?   ; 038h
  32.     CreateColorContext                       dd ?   ; 03Ch
  33.     CreateColorTransformer                   dd ?   ; 040h
  34.     CreateBitmap                             dd ?   ; 044h
  35.     CreateBitmapFromSource                   dd ?   ; 048h
  36.     CreateBitmapFromSourceRect               dd ?   ; 04Ch
  37.     CreateBitmapFromMemory                   dd ?   ; 050h
  38.     CreateBitmapFromHBITMAP                  dd ?   ; 054h
  39.     CreateBitmapFromHICON                    dd ?   ; 058h
  40.     CreateComponentEnumerator                dd ?   ; 05Ch
  41.     CreateFastMetadataEncoderFromDecoder     dd ?   ; 060h
  42.     CreateFastMetadataEncoderFromFrameDecode dd ?   ; 064h
  43.     CreateQueryWriter                        dd ?   ; 068h
  44.     CreateQueryWriterFromReader              dd ?   ; 06Ch
  45. ends
  46.  
  47. ; IID_IWICBitmapDecoder Interface
  48. struct IWICBitmapDecoder
  49.     ; IUnknown
  50.     QueryInterface         dd ?   ; 000h
  51.     AddRef                 dd ?   ; 004h
  52.     Release                dd ?   ; 008h
  53.     ; IWICBitmapDecoder
  54.     QueryCapability        dd ?   ; 00Ch
  55.     Initialize             dd ?   ; 010h
  56.     GetContainerFormat     dd ?   ; 014h
  57.     GetDecoderInfo         dd ?   ; 018h
  58.     CopyPalette            dd ?   ; 01Ch
  59.     GetMetadataQueryReader dd ?   ; 020h
  60.     GetPreview             dd ?   ; 024h
  61.     GetColorContexts       dd ?   ; 028h
  62.     GetThumbnail           dd ?   ; 02Ch
  63.     GetFrameCount          dd ?   ; 030h
  64.     GetFrame               dd ?   ; 034h
  65. ends
  66.  
  67. ; IWICBitmapFrameDecode Interface
  68. struct IWICBitmapFrameDecode
  69.     ; IUnknown
  70.     QueryInterface         dd ?   ; 000h
  71.     AddRef                 dd ?   ; 004h
  72.     Release                dd ?   ; 008h
  73.     ; IWICBitmapFrameDecode
  74.     GetSize                dd ?   ; 00Ch
  75.     GetPixelFormat         dd ?   ; 010h
  76.     GetResolution          dd ?   ; 014h
  77.     CopyPalette            dd ?   ; 018h
  78.     CopyPixels             dd ?   ; 01Ch
  79.     GetMetadataQueryReader dd ?   ; 020h
  80.     GetColorContexts       dd ?   ; 024h
  81.     GetThumbnail           dd ?   ; 028h
  82. ends
  83.  
  84. ; IID_IWICFormatConverter Interface
  85. struct IWICFormatConverter
  86.     ; IUnknown
  87.     QueryInterface dd ?   ; 000h
  88.     AddRef         dd ?   ; 004h
  89.     Release        dd ?   ; 008h
  90.     ; IWICFormatConverter
  91.     GetSize        dd ?   ; 00Ch
  92.     GetPixelFormat dd ?   ; 010h
  93.     GetResolution  dd ?   ; 014h
  94.     CopyPalette    dd ?   ; 018h
  95.     CopyPixels     dd ?   ; 01Ch
  96.     Initialize     dd ?   ; 020h
  97.     CanConvert     dd ?   ; 024h
  98. ends
  99.  
  100. ; IID_IWICBitmap Interface
  101. struct IWICBitmap
  102.     ; IUnknown
  103.     QueryInterface dd ?   ; 000h
  104.     AddRef         dd ?   ; 004h
  105.     Release        dd ?   ; 008h
  106.     ; IWICBitmap
  107.     GetSize        dd ?   ; 00Ch
  108.     GetPixelFormat dd ?   ; 010h
  109.     GetResolution  dd ?   ; 014h
  110.     CopyPalette    dd ?   ; 018h
  111.     CopyPixels     dd ?   ; 01Ch
  112.     Lock           dd ?   ; 020h
  113.     SetPalette     dd ?   ; 024h
  114.     SetResolution  dd ?   ; 028h
  115. ends
  116.  
  117. ; IID_IWICBitmapLock Interface
  118. struct IWICBitmapLock
  119.     ; IUnknown
  120.     QueryInterface dd ?   ; 000h
  121.     AddRef         dd ?   ; 004h
  122.     Release        dd ?   ; 008h
  123.     ; IWICBitmapLock
  124.     GetSize        dd ?   ; 00Ch
  125.     GetStride      dd ?   ; 010h
  126.     GetDataPointer dd ?   ; 014h
  127.     GetPixelFormat dd ?   ; 018h
  128. ends
  129.  
  130. ; GUID {6FDDC324-4E03-4BFE-B185-3D77768DC910}
  131. GUID_WICPixelFormat32bppPBGRA dd 06FDDC324h
  132.                               dw 04E03h
  133.                               dw 04BFEh
  134.                               db 0B1h, 085h, 03Dh, 077h, 076h, 08Dh, 0C9h, 010h
  135.  
  136. CLSCTX_INPROC_SERVER = 0x01
  137.  
  138. WICBitmapDitherTypeNone        = 0x00000000
  139. WICBitmapPaletteTypeCustom     = 0x00000000
  140. WICBitmapCacheOnDemand         = 0x00000001
  141. WICBitmapLockWrite             = 0x00000002
  142. WICDecodeMetadataCacheOnDemand = 0x00000000
Процесс загрузки изображения начинается с создания фабрики объектов типа IWICImagingFactory, с помощью которой мы в дальнейшем будем создавать декодеры, модификаторы и все прочие инструменты для работы. Тут ничего сложного, обычная работа с COM.

Затем надо создать декодер изображения, объект типа IWICBitmapDecoder. Для этого на фабрике есть несколько разных методов интерфейса, самым универсальным будет метод CreateDecoderFromFilename. Он как раз позволяет не заморачиваться с определением формата, а поручить всю грязную работу системе. В этом случае одним кодом можно открывать картинки разных форматов. Если на этом этапе объект декодера создать не удалось, то значит система не поддерживает этот формат и надо попросить пользователя установить соответствующие кодеки или сделать это самостоятельно, все зависит от приложения. Если объект декодера создан, то практически со 100% вероятностью дальше все будет работать как надо.
  1.         ; Инициализация фабрики
  2.         invoke  CoCreateInstance,CLSID_WICImagingFactory,NULL,\
  3.                 CLSCTX_INPROC_SERVER,IID_IWICImagingFactory,pFactory
  4.  
  5.         ; Создать декодер
  6.         mov     eax, [pFactory]
  7.         mov     eax, [eax]
  8.         stdcall dword [eax+IWICImagingFactory.CreateDecoderFromFilename],\
  9.                 [pFactory],\
  10.                 buff,\
  11.                 NULL,\
  12.                 GENERIC_READ,\
  13.                 WICDecodeMetadataCacheOnDemand,\
  14.                 pDecoder
  15.         cmp     eax,0
  16.         ; Формат файла не поддерживается
  17.         jne     loc_clear
Когда изображение обработано декодером, из него можно получить общее количество кадров при помощи метода GetFrameCount, а методом GetFrame - содержимое любого из этих кадров по его индексу. Для статичных картинок будет единственный кадр с нулевым индексом. Из анимированных изображений можно извлечь любой кадр, что-то похожее уже было в статье про GIF-анимацию.
  1.         ; Получить первый фрейм
  2.         mov     eax, [pDecoder]
  3.         mov     eax, [eax]
  4.         stdcall dword [eax+IWICBitmapDecoder.GetFrame],[pDecoder],\
  5.                 0,\
  6.                 pSource
Кадр получен, но работать с ним еще рано. На фабрике инструментов надо создать объект типа IWICFormatConverter, это конвертер. С его помощью содержимое кадра, полученное из декодера, будет преобразовано в объект-битмап. Там же можно дополнительно проверить, действительно ли получится сконвертировать данные в выбранный формат, но это уже больше для всякой экзотики.
  1.         ; Создать конвертер
  2.         mov     eax, [pFactory]
  3.         mov     eax, [eax]
  4.         stdcall dword [eax+IWICImagingFactory.CreateFormatConverter],[pFactory],\
  5.                 pConverter
  6.  
  7.         ; Инициализировать конвертер
  8.         mov     eax, [pConverter]
  9.         mov     eax, [eax]
  10.         stdcall dword [eax+IWICFormatConverter.Initialize],[pConverter],\
  11.                 [pSource],\
  12.                 GUID_WICPixelFormat32bppPBGRA,\
  13.                 WICBitmapDitherTypeNone,\
  14.                 NULL,\
  15.                 0,0,\
  16.                 WICBitmapPaletteTypeCustom
  17.  
  18.         ; Создать битмап из фрейма
  19.         mov     eax, [pFactory]
  20.         mov     eax, [eax]
  21.         stdcall dword [eax+IWICImagingFactory.CreateBitmapFromSource],[pFactory],\
  22.                 [pConverter],\
  23.                 WICBitmapCacheOnDemand,\
  24.                 pBitmap
Битмап создан, но это еще не совсем битмап, с которым мы привыкли работать. Это очередной объект, на этот раз формата IWICBitmap. Его высоту и ширину можно получить при помощи метода GetSize созданного ранее объекта IWICBitmapFrameDecode, а размер данных битмапа и указатель на их расположение в памяти при помощи последовательного вызова метода Lock и GetDataPointer.
  1.         ; Получить размер фрейма
  2.         mov     eax, [pSource]
  3.         mov     eax, [eax]
  4.         stdcall dword [eax+IWICBitmapFrameDecode.GetSize],[pSource],\
  5.                 rcLock.right,\
  6.                 rcLock.bottom
  7.  
  8.         ; Получить данные битмапа
  9.         mov     eax, [pBitmap]
  10.         mov     eax, [eax]
  11.         stdcall dword [eax+IWICBitmap.Lock],[pBitmap],\
  12.                 rcLock,\
  13.                 WICBitmapLockWrite,\
  14.                 pLock
  15.  
  16.         ; Размер и указатель на данные
  17.         mov     eax, [pLock]
  18.         mov     eax, [eax]
  19.         stdcall dword [eax+IWICBitmapLock.GetDataPointer],[pLock],\
  20.                 BufferSize,\
  21.                 BufferData
Остался последний шаг. Во всех источниках, где описывается работа с Windows Imaging Component, дальнейшие операции с объектом WICBitmap выполняются с помощью Direct2D и прочей дичи. Ради одной строчки кода мне пришлось угрохать кучу времени и перелопатить множество сайтов, и вот, наконец, достигнут нужный результат.
  1.         ; Создать HBITMAP из WICBitmap
  2.         invoke  CreateBitmap,[rcLock.right],[rcLock.bottom],1,32,[BufferData]
  3.         mov     [hBitmap],eax
Теперь у нас есть хэндл hBitmap, который можно отправить на форму, сохранить в файл, как-нибудь преобразовать и т.д. Как и в других примерах, картинка будет отмасштабирована и выведена на форму.

В приложении пример программы с исходным текстом, которая загружает изображение из файла picture.webp и выводит его в диалоговое окно. Для корректной работы необходимо установить кодеки, ссылка на дистрибутив дана в первом абзаце.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Picture.to.Form.Demo.WIC.zip (27,644 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 911 | Комментариев: 5

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (17.01.2023 в 12:37):
У IrfanView используется свой декодер, а тут просто вызываются системные функции. Если сама система не может отобразить картинку, то программа и подавно.
Андрей (03.01.2023 в 19:35):
В первом абзаце верно указано, что программа открывает все изображения, которые можно просмотреть встроенным Просмотрщиком. У меня WinXP(SP3). Мой Просмотрщик не видит формат WebP. Все другие форматы ваша программа открыла. Скорее всего моя ОС по какой-то причине не может воспользоваться установленным кодеком.
ManHunter (03.01.2023 в 15:29):
Minimum supported client : Windows XP with SP2, Windows Vista
Minimum supported server : Windows Server 2008
Андрей (03.01.2023 в 15:09):
На Win XP при наличии кодека программа открывает пустое диалоговое окно без изображения. Хотя файлы в формате WebP на Win XP открывает программа IrfanView. Кодек установлен в каталог "Program Files\WebP Codec".
кодер (14.08.2022 в 13:41):
Спасибо за очередную толковую статью по низкоуровнему программированию под Windows!

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0042 сек.) / Память: 4.5 Mb
Наверх