Здравствуйте!

WIC - это набор объектов, которые позволяют распаковывать сжатые данные и понимать их структуру. Данные могут быть из файла, потока или памяти. Можно получать пиксели изображений, кадры GIF и подобное.

Результат декодирования - это указатель на объект, который содержит или даёт доступ к пиксельным данным.

С них и начнём.

  1. IWICBitmapDecoder - главный интерфейс декодера. Это контейнер, из которого можно получить кадры, эскизы и метаданные.

  2. IWICBitmapFrameDecode - представляет отдельный кадр изображения. Именно из него читаются пиксели.

  3. IWICBitmapSourceTransform - оптимизированный интерфейс. Позволяет декодеру выполнять преобразования (масштабирование, смену формата) во время декодирования.

  4. IWICBitmap - изменяемый растровый образ в памяти. Это результат декодирования, который можно изменять.

  5. IWICFormatConverter - преобразователь. Он реализует IWICBitmapSource и часто используется как результат декодирования после смены формата.

  6. IWICBitmapScaler, IWICBitmapClipper, IWICBitmapFlipRotator- изменённые версии исходного изображения. Они тоже являются IWICBitmapSource.

Пока просто запомните это. Позже вы встретите эти термины и поймёте, о чём речь. Теперь поговорим о том, как декодировать данные. Всё начинается с создания фабрики WIC.

Интерфейс фабрики: IWICImagingFactory / IWICImagingFactory2. Во второй версии есть поддержка IWICImageEncoder для сохранения изображений.

Пример:

#include <wincodec.h>
#include <wrl/client.h>

using Microsoft::WRL::ComPtr;

ComPtr<IWICImagingFactory> pFactory;
HRESULT hr = CoCreateInstance(
    CLSID_WICImagingFactory,
    nullptr,
    CLSCTX_INPROC_SERVER,
    IID_IWICImagingFactory,
    reinterpret_cast<void**>(&pFactory)
);

По аргументам функции CoCreateInstance:

  1. ID того, что хотим создать.

  2. указатель на "внешний" объект (если объект создаётся как часть агрегации).

  3. определяет контекст выполнения - где будет размещён объект: в том же процессе, в другом, на удалённом сервере и т.п. CLSCTX_INPROC_SERVER - объект загружается в тот же процесс, что и вызывающий код.

  4. идентификатор интерфейса (IID), который запрашивается у созданного объекта. IID_IWICImagingFactory (или __uuidof(IWICImagingFactory)) - мы хотим получить интерфейс IWICImagingFactory.

  5. указатель на указатель на интерфейс.

Теперь можно декодировать файл. Рассмотрим три способа: из файла, из памяти и из потока.

Начнём с загрузки изображения. Она начинается с декодера файла, который содержит изображение.

#include <wincodec.h>
#include <wrl/client.h>

ComPtr<IWICBitmapDecoder> pDecoder;
hr = pFactory->CreateDecoderFromFilename(
    L"C:\\images\\photo.jpg",     // путь к файлу
    nullptr,                       // не предпочитаем конкретного производителя
    GENERIC_READ,                  // режим доступа
    WICDecodeMetadataCacheOnLoad,  // кэшировать метаданные при загрузке
    &pDecoder
);

Аргументы функции CreateDecoderFromFilename:

  1. Путь к файлу в широких строках (wchar_t*). Можно использовать любой допустимый путь Windows.

  2. Указатель на предпочтительного производителя декодера (например, для конкретного кодека). В 99% случаев передаётся nullptr, чтобы система сама выбрала подходящий декодер по расширению или содержимому файла.

  3. GENERIC_READ - режим доступа. Обычно только чтение. Можно использовать GENERIC_READ | GENERIC_WRITE, если нужно записывать в файл, но для декодера достаточно чтения.

  4. WICDecodeMetadataCacheOnLoad - указывает, когда кэшировать метаданные. Возможные значения: WICDecodeMetadataCacheOnLoad - прочитать и закэшировать метаданные сразу при загрузке; WICDecodeMetadataCacheOnDemand - кэшировать по запросу (может быть быстрее, если метаданные не нужны). В большинстве случаев подходит OnLoad.

  5. Адрес переменной, куда будет записан указатель на созданный декодер (IWICBitmapDecoder*).

Вариант, если данные в потоке:

#include <wincodec.h>
#include <wrl/client.h>
#include <shlwapi.h> 

using Microsoft::WRL::ComPtr;

// Создаём поток из файла 
ComPtr<IStream> pStream;
HRESULT hr = SHCreateStreamOnFile(L"C:\\images\\photo.jpg", STGM_READ, &pStream);
if (FAILED(hr)) return;

// Создаём декодер из потока
ComPtr<IWICBitmapDecoder> pDecoder;
hr = pFactory->CreateDecoderFromStream(
    pStream.Get(),                    // поток
    nullptr,                          // предпочтительный производитель (не указываем)
    WICDecodeMetadataCacheOnLoad,     // кэшировать метаданные
    &pDecoder
);
if (SUCCEEDED(hr)) {
    
}

Декодирование, если файл в памяти:

#include <wincodec.h>
#include <wrl/client.h>

// Допустим, у нас есть буфер с данными файла
BYTE* pBuffer = ...;   // указатель на данные
UINT cbSize = ...;     // размер буфера

ComPtr<IWICStream> pStream;
HRESULT hr = pFactory->CreateStream(&pStream);
if (SUCCEEDED(hr)) {
    // Инициализируем поток из памяти
    hr = pStream->InitializeFromMemory(pBuffer, cbSize);
}

if (SUCCEEDED(hr)) {
    ComPtr<IWICBitmapDecoder> pDecoder;
    hr = pFactory->CreateDecoderFromStream(
        pStream.Get(),
        nullptr,
        WICDecodeMetadataCacheOnLoad,
        &pDecoder
    );
    if (SUCCEEDED(hr)) {
        // Декодер готов
    }
}

Что можно делать с декодированными данными? Сначала опишу некоторые доступные функции двух объектов: IWICBitmapDecoder и IWICBitmapFrameDecode.

Интерфейс IWICBitmapDecoder

функция QueryCapability
Аргумент IStream pIStream - поток, для которого проверяются возможности декодера.
Аргумент DWORD pdwCapability - возвращает битовую маску возможностей (чтение, запись, поддержка эскизов и т.д.). Возвращаемое значение HRESULT. Успех - S_OK.

функция Initialize
Аргумент IStream *pIStream - поток с данными изображения.
Аргумент WICDecodeOptions cacheOptions - флаг кэширования метаданных: WICDecodeMetadataCacheOnLoad (кэшировать сразу) или WICDecodeMetadataCacheOnDemand (кэшировать по запросу). Возвращаемое значение HRESULT.

функция GetContainerFormat
Аргумент GUID *pguidContainerFormat - указатель, куда записывается GUID формата контейнера (например, GUID_ContainerFormatJpeg). Возвращаемое значение HRESULT.

функция GetDecoderInfo
Аргумент IWICBitmapDecoderInfo **ppIDecoderInfo - указатель, куда возвращается интерфейс с информацией о декодере (автор, версия, возможности). Возвращаемое значение HRESULT.

функция GetFrameCount
Аргумент UINT *pCount - указатель, куда записывается количество кадров. Возвращаемое значение HRESULT.

функция GetFrame
Аргумент UINT index - индекс кадра (от 0 до GetFrameCount()-1).
Аргумент IWICBitmapFrameDecode **ppIBitmapFrame - указатель, куда возвращается интерфейс кадра. Возвращаемое значение HRESULT.

функция GetPreview
Аргумент IWICBitmapSource **ppIPreview - указатель, куда возвращается интерфейс предварительного просмотра (если есть). Возвращаемое значение HRESULT.

функция GetThumbnail
Аргумент IWICBitmapSource **ppIThumbnail - указатель, куда возвращается интерфейс эскиза (если есть). Возвращаемое значение HRESULT.

функция GetColorContexts
Аргумент UINT cCount - количество элементов в массиве ppIColorContexts. Если 0, возвращается необходимое количество.
Аргумент IWICColorContext **ppIColorContexts - массив указателей на интерфейсы цветовых контекстов (ICC-профилей).
Аргумент UINT *pcActualCount - возвращает реальное количество цветовых контекстов. Возвращаемое значение HRESULT.

функция GetMetadataQueryReader
Аргумент IWICMetadataQueryReader **ppIMetadataQueryReader - указатель, куда возвращается читатель глобальных метаданных контейнера. Возвращаемое значение HRESULT.

функция CopyPalette
Аргумент IWICPalette *pIPalette - указатель на интерфейс палитры, куда копируется глобальная палитра (если есть). Возвращаемое значение HRESULT.

Интерфейс IWICBitmapFrameDecode

Он наследует IWICBitmapSource, поэтому сначала перечислены методы базового интерфейса, затем добавленные.

функция GetSize (из IWICBitmapSource)

функция GetPixelFormat (из IWICBitmapSource)

функция GetResolution (из IWICBitmapSource)

функция CopyPixels (из IWICBitmapSource)

функция CopyPalette (из IWICBitmapSource)

функция GetThumbnail
Аргумент IWICBitmapSource **ppIThumbnail - указатель, куда возвращается эскиз кадра (если есть). Возвращаемое значение HRESULT.

функция GetColorContexts
Аргумент UINT cCount - количество элементов в массиве ppIColorContexts.
Аргумент IWICColorContext **ppIColorContexts - массив указателей на интерфейсы цветовых контекстов.
Аргумент UINT *pcActualCount - возвращает реальное количество цветовых контекстов. Возвращаемое значение HRESULT.

функция GetMetadataQueryReader (специфичный для IWICBitmapFrameDecode)
Аргумент IWICMetadataQueryReader **ppIMetadataQueryReader - указатель, куда возвращается читатель метаданных кадра (EXIF, XMP). Возвращаемое значение HRESULT.

Пример кода с комментариями:

Код
#include <windows.h>
#include <wincodec.h>
#include <wrl/client.h>
#include <iostream>
#include <vector>
#include <propvarutil.h>  // для PropVariant

using Microsoft::WRL::ComPtr;

// Вспомогательная функция для вывода GUID в читаемом виде
void PrintGUID(const GUID& guid) {
    wchar_t str[39];
    StringFromGUID2(guid, str, 39);
    wprintf(L"%s", str);
}

int wmain(int argc, wchar_t* argv[]) {
    if (argc < 2) {
        wprintf(L"Usage: WICFullDemo.exe <imagefile>\n");
        return 1;
    }
    const wchar_t* filename = argv[1];

    // Инициализация COM (обязательно для WIC)
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        wprintf(L"CoInitializeEx failed\n");
        return 1;
    }

    // Создание фабрики WIC – основной объект для создания декодеров, конвертеров и т.д.
    ComPtr<IWICImagingFactory> pFactory;
    hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFactory));
    if (FAILED(hr)) {
        wprintf(L"Failed to create WIC factory\n");
        CoUninitialize();
        return 1;
    }

    // -----------------------------------------------------------------
    // 1. Создание декодера из файла (это самый простой способ)
    //    Внутри себя CreateDecoderFromFilename вызывает Initialize.
    // -----------------------------------------------------------------
    ComPtr<IWICBitmapDecoder> pDecoder;
    hr = pFactory->CreateDecoderFromFilename(filename, nullptr, GENERIC_READ,
                                              WICDecodeMetadataCacheOnLoad, &pDecoder);
    if (FAILED(hr)) {
        wprintf(L"Failed to create decoder from file\n");
        CoUninitialize();
        return 1;
    }

    wprintf(L"=== IWICBitmapDecoder methods ===\n");

    // -----------------------------------------------------------------
    // 2. QueryCapability – проверяет, какие операции поддерживает декодер для данного потока.
    //    Не используется в обычных приложениях, но для полноты покажем.
    //    Возвращает битовую маску, где каждый бит отвечает за возможность:
    //      - WICBitmapDecoderCapabilityCanDecodeSomeImages (1)
    //      - WICBitmapDecoderCapabilityCanDecodeAllImages (2)
    //      - WICBitmapDecoderCapabilityCanDecodeThumbnail (4)
    //      - и т.д.
    // -----------------------------------------------------------------
    {
        ComPtr<IStream> pStream;
        hr = SHCreateStreamOnFile(filename, STGM_READ, &pStream);
        if (SUCCEEDED(hr)) {
            DWORD caps = 0;
            hr = pDecoder->QueryCapability(pStream.Get(), &caps);
            if (SUCCEEDED(hr)) {
                wprintf(L"QueryCapability: capabilities = 0x%08X\n", caps);
                if (caps & 0x1) wprintf(L"  - Can decode some images\n");
                if (caps & 0x2) wprintf(L"  - Can decode all images\n");
                if (caps & 0x4) wprintf(L"  - Can decode thumbnail\n");
            } else {
                wprintf(L"QueryCapability failed (may be not supported)\n");
            }
        }
    }

    // -----------------------------------------------------------------
    // 3. Initialize – инициализирует декодер потоком. Обычно не вызывается напрямую,
    //    так как CreateDecoderFromFilename уже это сделала.
    //    Параметр cacheOptions: OnLoad – кэшировать метаданные сразу,
    //                          OnDemand – по запросу (экономит память, если метаданные не нужны).
    // -----------------------------------------------------------------
    {
        ComPtr<IStream> pStream;
        if (SUCCEEDED(SHCreateStreamOnFile(filename, STGM_READ, &pStream))) {
            hr = pDecoder->Initialize(pStream.Get(), WICDecodeMetadataCacheOnDemand);
            if (FAILED(hr)) {
                wprintf(L"Initialize failed (likely already initialized)\n");
            } else {
                wprintf(L"Initialize succeeded\n");
            }
        }
    }

    // -----------------------------------------------------------------
    // 4. GetContainerFormat – возвращает GUID, идентифицирующий формат файла.
    //    Это может быть GUID_ContainerFormatJpeg, GUID_ContainerFormatPng, GUID_ContainerFormatBmp и т.д.
    //    Полезно, чтобы определить тип изображения без расширения файла.
    // -----------------------------------------------------------------
    {
        GUID containerFormat;
        hr = pDecoder->GetContainerFormat(&containerFormat);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetContainerFormat: ");
            PrintGUID(containerFormat);
            wprintf(L"\n");
            // Сравниваем с известными GUID
            if (containerFormat == GUID_ContainerFormatJpeg) wprintf(L"  -> JPEG file\n");
            else if (containerFormat == GUID_ContainerFormatPng) wprintf(L"  -> PNG file\n");
            else if (containerFormat == GUID_ContainerFormatBmp) wprintf(L"  -> BMP file\n");
            else if (containerFormat == GUID_ContainerFormatGif) wprintf(L"  -> GIF file\n");
            else if (containerFormat == GUID_ContainerFormatTiff) wprintf(L"  -> TIFF file\n");
            else if (containerFormat == GUID_ContainerFormatDds) wprintf(L"  -> DDS file\n");
            else wprintf(L"  -> Unknown format\n");
        }
    }

    // -----------------------------------------------------------------
    // 5. GetDecoderInfo – возвращает интерфейс с информацией о декодере.
    //    Можно извлечь дружеское имя, версию, поддерживаемые форматы и т.д.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICBitmapDecoderInfo> pDecoderInfo;
        hr = pDecoder->GetDecoderInfo(&pDecoderInfo);
        if (SUCCEEDED(hr)) {
            UINT nameLen = 0;
            pDecoderInfo->GetFriendlyName(0, nullptr, &nameLen);
            if (nameLen > 0) {
                std::vector<wchar_t> name(nameLen);
                pDecoderInfo->GetFriendlyName(nameLen, name.data(), &nameLen);
                wprintf(L"GetDecoderInfo: FriendlyName = %s\n", name.data());
            }
            // Можно также получить версию, CLSID, поддерживаемые расширения и т.д.
        }
    }

    // -----------------------------------------------------------------
    // 6. GetFrameCount – возвращает количество кадров в изображении.
    //    Для JPEG, PNG обычно 1. Для GIF, TIFF может быть больше.
    // -----------------------------------------------------------------
    UINT frameCount = 0;
    hr = pDecoder->GetFrameCount(&frameCount);
    if (SUCCEEDED(hr)) {
        wprintf(L"GetFrameCount: %u\n", frameCount);
    }

    // -----------------------------------------------------------------
    // 7. GetFrame – извлекает кадр по индексу (0 .. frameCount-1).
    //    Возвращает IWICBitmapFrameDecode, через который мы будем читать пиксели и метаданные.
    // -----------------------------------------------------------------
    ComPtr<IWICBitmapFrameDecode> pFrame;
    hr = pDecoder->GetFrame(0, &pFrame);
    if (FAILED(hr)) {
        wprintf(L"GetFrame failed\n");
        CoUninitialize();
        return 1;
    }
    wprintf(L"GetFrame: succeeded (first frame)\n");

    // -----------------------------------------------------------------
    // 8. GetPreview – возвращает предварительный просмотр (маленькое изображение),
    //    если он встроен в файл (например, в TIFF или JPEG). Может отсутствовать.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICBitmapSource> pPreview;
        hr = pDecoder->GetPreview(&pPreview);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetPreview: preview exists\n");
            UINT w, h;
            pPreview->GetSize(&w, &h);
            wprintf(L"  Preview size: %u x %u\n", w, h);
        } else {
            wprintf(L"GetPreview: no preview\n");
        }
    }

    // -----------------------------------------------------------------
    // 9. GetThumbnail (глобальный эскиз) – возвращает эскиз всего файла.
    //    Может присутствовать в JPEG (как APP1 thumbnail) или TIFF.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICBitmapSource> pThumb;
        hr = pDecoder->GetThumbnail(&pThumb);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetThumbnail: global thumbnail exists\n");
            UINT w, h;
            pThumb->GetSize(&w, &h);
            wprintf(L"  Thumbnail size: %u x %u\n", w, h);
        } else {
            wprintf(L"GetThumbnail: no global thumbnail\n");
        }
    }

    // -----------------------------------------------------------------
    // 10. GetColorContexts (глобальные) – возвращает ICC-профили,
    //     связанные со всем файлом (например, для TIFF или JPEG с встроенным профилем).
    // -----------------------------------------------------------------
    {
        UINT actualCount = 0;
        hr = pDecoder->GetColorContexts(0, nullptr, &actualCount);
        if (SUCCEEDED(hr) && actualCount > 0) {
            std::vector<ComPtr<IWICColorContext>> contexts(actualCount);
            hr = pDecoder->GetColorContexts(actualCount, (IWICColorContext**)contexts.data(), &actualCount);
            if (SUCCEEDED(hr)) {
                wprintf(L"GetColorContexts (global): %u contexts\n", actualCount);
            }
        } else {
            wprintf(L"GetColorContexts (global): none\n");
        }
    }

    // -----------------------------------------------------------------
    // 11. GetMetadataQueryReader (глобальный) – читатель метаданных на уровне файла.
    //     Редко используется, так как обычно метаданные хранятся в кадре.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICMetadataQueryReader> pGlobalMeta;
        hr = pDecoder->GetMetadataQueryReader(&pGlobalMeta);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetMetadataQueryReader (global): supported\n");
        } else {
            wprintf(L"GetMetadataQueryReader (global): not supported\n");
        }
    }

    // -----------------------------------------------------------------
    // 12. CopyPalette (глобальная палитра) – копирует палитру,
    //     если файл имеет общую палитру для всех кадров (например, некоторые GIF).
    //     Обычно палитра копируется из кадра, а не из декодера.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICPalette> pPalette;
        hr = pFactory->CreatePalette(&pPalette);
        if (SUCCEEDED(hr)) {
            hr = pDecoder->CopyPalette(pPalette.Get());
            if (SUCCEEDED(hr)) {
                UINT colorCount = 0;
                pPalette->GetColorCount(&colorCount);
                wprintf(L"CopyPalette (global): %u colors\n", colorCount);
            } else {
                wprintf(L"CopyPalette (global): no global palette\n");
            }
        }
    }

    wprintf(L"\n=== IWICBitmapFrameDecode methods ===\n");

    // -----------------------------------------------------------------
    // 13. GetSize – возвращает ширину и высоту кадра в пикселях.
    // -----------------------------------------------------------------
    UINT width = 0, height = 0;
    hr = pFrame->GetSize(&width, &height);
    if (SUCCEEDED(hr)) {
        wprintf(L"GetSize: %u x %u\n", width, height);
    }

    // -----------------------------------------------------------------
    // 14. GetPixelFormat – возвращает GUID формата пикселей, в котором хранятся данные.
    //     Например: GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppBGRA,
    //     GUID_WICPixelFormat8bppIndexed и т.д.
    // -----------------------------------------------------------------
    GUID pixelFormat;
    hr = pFrame->GetPixelFormat(&pixelFormat);
    if (SUCCEEDED(hr)) {
        wprintf(L"GetPixelFormat: ");
        PrintGUID(pixelFormat);
        wprintf(L"\n");
    }

    // -----------------------------------------------------------------
    // 15. GetResolution – возвращает разрешение в точках на дюйм (DPI).
    //     Если в файле нет информации о DPI, возвращается 96.0 по умолчанию.
    // -----------------------------------------------------------------
    double dpiX = 0.0, dpiY = 0.0;
    hr = pFrame->GetResolution(&dpiX, &dpiY);
    if (SUCCEEDED(hr)) {
        wprintf(L"GetResolution: %.2f x %.2f DPI\n", dpiX, dpiY);
    }

    // -----------------------------------------------------------------
    // 16. CopyPixels – самый важный метод: копирует пиксели в предоставленный буфер.
    //     Для удобства мы преобразуем исходный формат в 32-битный PBGRA (premultiplied BGRA)
    //     с помощью IWICFormatConverter. Это универсальный формат для Direct2D и многих задач.
    //     Аргументы:
    //       prc – прямоугольник для копирования (NULL = весь кадр)
    //       cbStride – шаг строки в байтах (должен быть >= width * bytes_per_pixel)
    //       cbBufferSize – размер буфера
    //       pbBuffer – указатель на буфер
    // -----------------------------------------------------------------
    {
        ComPtr<IWICFormatConverter> pConverter;
        hr = pFactory->CreateFormatConverter(&pConverter);
        if (SUCCEEDED(hr)) {
            // Инициализируем конвертер для преобразования в PBGRA
            hr = pConverter->Initialize(pFrame.Get(), GUID_WICPixelFormat32bppPBGRA,
                                        WICBitmapDitherTypeNone, nullptr, 0.0f,
                                        WICBitmapPaletteTypeCustom);
            if (SUCCEEDED(hr)) {
                UINT stride = width * 4;  // 4 байта на пиксель
                UINT bufferSize = stride * height;
                std::vector<BYTE> buffer(bufferSize);
                // Копируем весь кадр
                hr = pConverter->CopyPixels(nullptr, stride, bufferSize, buffer.data());
                if (SUCCEEDED(hr)) {
                    wprintf(L"CopyPixels: copied %u bytes of pixel data (format PBGRA)\n", bufferSize);
                    // Теперь буфер содержит пиксели в порядке B, G, R, A (premultiplied alpha)
                    // Можно обрабатывать их, например, изменить цвет или сохранить в файл.
                } else {
                    wprintf(L"CopyPixels failed\n");
                }
            }
        }
    }

    // -----------------------------------------------------------------
    // 17. CopyPalette (из кадра) – копирует палитру, если изображение индексированное
    //     (например, 8-битный PNG или GIF). Позволяет получить массив цветов ARGB.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICPalette> pFramePalette;
        hr = pFactory->CreatePalette(&pFramePalette);
        if (SUCCEEDED(hr)) {
            hr = pFrame->CopyPalette(pFramePalette.Get());
            if (SUCCEEDED(hr)) {
                UINT colorCount = 0;
                pFramePalette->GetColorCount(&colorCount);
                wprintf(L"CopyPalette (frame): %u colors (indexed image)\n", colorCount);
                // Можно получить цвета: std::vector<UINT32> colors(colorCount);
                // pFramePalette->GetColors(colorCount, colors.data(), &colorCount);
            } else {
                wprintf(L"CopyPalette (frame): not indexed or no palette\n");
            }
        }
    }

    // -----------------------------------------------------------------
    // 18. GetThumbnail (эскиз кадра) – возвращает эскиз, встроенный в кадр.
    //     Некоторые форматы (например, TIFF) могут иметь эскиз для каждого кадра.
    // -----------------------------------------------------------------
    {
        ComPtr<IWICBitmapSource> pFrameThumb;
        hr = pFrame->GetThumbnail(&pFrameThumb);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetThumbnail (frame): thumbnail exists\n");
            UINT w, h;
            pFrameThumb->GetSize(&w, &h);
            wprintf(L"  Frame thumbnail size: %u x %u\n", w, h);
        } else {
            wprintf(L"GetThumbnail (frame): no thumbnail\n");
        }
    }

    // -----------------------------------------------------------------
    // 19. GetColorContexts (цветовые профили кадра) – ICC-профили, специфичные для кадра.
    //     Например, в TIFF каждый кадр может иметь свой профиль.
    // -----------------------------------------------------------------
    {
        UINT actualCount = 0;
        hr = pFrame->GetColorContexts(0, nullptr, &actualCount);
        if (SUCCEEDED(hr) && actualCount > 0) {
            std::vector<ComPtr<IWICColorContext>> contexts(actualCount);
            hr = pFrame->GetColorContexts(actualCount, (IWICColorContext**)contexts.data(), &actualCount);
            if (SUCCEEDED(hr)) {
                wprintf(L"GetColorContexts (frame): %u contexts\n", actualCount);
            }
        } else {
            wprintf(L"GetColorContexts (frame): none\n");
        }
    }

    // -----------------------------------------------------------------
    // 20. GetMetadataQueryReader (специфичный для кадра) – читатель метаданных.
    //     Позволяет извлекать EXIF, XMP, IPTC и другие метаданные.
    //     Мы покажем пример чтения даты и времени съёмки (тег EXIF 36867).
    // -----------------------------------------------------------------
    {
        ComPtr<IWICMetadataQueryReader> pFrameMeta;
        hr = pFrame->GetMetadataQueryReader(&pFrameMeta);
        if (SUCCEEDED(hr)) {
            wprintf(L"GetMetadataQueryReader (frame): supported\n");
            PROPVARIANT value;
            PropVariantInit(&value);
            // Запрос EXIF: DateTimeOriginal
            hr = pFrameMeta->GetMetadataByName(L"/app1/ifd/exif/{ushort=36867}", &value);
            if (SUCCEEDED(hr) && value.vt == VT_LPWSTR) {
                wprintf(L"  EXIF DateTimeOriginal: %s\n", value.pwszVal);
            } else {
                wprintf(L"  EXIF DateTimeOriginal not found\n");
            }
            PropVariantClear(&value);
        } else {
            wprintf(L"GetMetadataQueryReader (frame): not supported\n");
        }
    }

    CoUninitialize();
    return 0;
}

Далее идёт IWICBitmapSourceTransform:

Этот интерфейс реализуется декодерами, которые поддерживают аппаратное масштабирование, поворот и преобразование формата на этапе декодирования. Это позволяет избежать лишних операций с пикселями.

функция DoesSupportTransform
Аргумент WICBitmapTransformOptions dstTransform - запрашиваемая операция трансформации (например, WICBitmapTransformRotate90).
Аргумент BOOL pfIsSupported - указатель, который при успешном возврате метода получает TRUE, если операция поддерживается, или FALSE в противном случае.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Метод GetClosestPixelFormat
Аргумент WICPixelFormatGUID pguidDstFormat - указатель. На входе содержит желаемый формат пикселей. На выходе метод записывает в него ближайший формат, поддерживаемый декодером.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Метод GetClosestSize
Аргумент UINT puiWidth - указатель. На входе содержит желаемую ширину. На выходе метод записывает ближайшую поддерживаемую ширину.
Аргумент UINT puiHeight - указатель. На входе содержит желаемую высоту. На выходе метод записывает ближайшую поддерживаемую высоту.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Пример кода с комментариями:

Код
ComPtr<IWICBitmapFrameDecode> pFrame; // ... получение кадра
ComPtr<IWICBitmapSourceTransform> pTransform;

if (SUCCEEDED(pFrame->QueryInterface(IID_PPV_ARGS(&pTransform)))) {
    // 1. Проверяем поддержку поворота на 90 градусов
    BOOL isSupported = FALSE;
    HRESULT hr = pTransform->DoesSupportTransform(WICBitmapTransformRotate90, &isSupported);

    if (SUCCEEDED(hr) && isSupported) {
        // 2. Запрашиваем ближайший размер, например, 640x480
        UINT width = 640, height = 480;
        hr = pTransform->GetClosestSize(&width, &height);

        if (SUCCEEDED(hr)) {
            // 3. Запрашиваем ближайший формат к 32-битному BGRA
            GUID dstFormat = GUID_WICPixelFormat32bppBGRA;
            hr = pTransform->GetClosestPixelFormat(&dstFormat);

            if (SUCCEEDED(hr)) {
                // 4. Копируем пиксели с применением трансформаций
                UINT stride = width * 4; // вычисляем шаг строки
                UINT bufferSize = stride * height;
                std::vector<BYTE> buffer(bufferSize);
                
                // Используем CopyPixels (унаследован от IWICBitmapSource)
                hr = pTransform->CopyPixels(nullptr, stride, bufferSize, buffer.data());
            }
        }
    }
}

Далее идёт IWICBitmap (изменяемый растр):

Этот интерфейс расширяет IWICBitmapSource, добавляя возможность изменять пиксельные данные.

Метод Lock
Аргумент const WICRect prcLock - указатель на структуру WICRect, определяющую прямоугольную область для блокировки. Если NULL, блокируется всё изображение.
Аргумент DWORD flags - флаги доступа (WICBitmapLockRead или WICBitmapLockWrite).
Аргумент IWICBitmapLock ppILock - адрес, куда будет записан указатель на объект IWICBitmapLock для доступа к памяти.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Метод SetPalette
Аргумент IWICPalette pIPalette - указатель на объект палитры (IWICPalette), который будет установлен для растрового изображения.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Метод SetResolution
Аргумент double dpiX - новое горизонтальное разрешение в точках на дюйм.
Аргумент double dpiY - новое вертикальное разрешение в точках на дюйм.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Пример кода:

Код
ComPtr<IWICBitmap> pWICBitmap; // ... получение из IWICBitmapSource
ComPtr<IWICBitmapLock> pLock;
WICRect rcLock = {0, 0, width, height};

// 1. Блокируем для записи
if (SUCCEEDED(pWICBitmap->Lock(&rcLock, WICBitmapLockWrite, &pLock))) {
    BYTE* pPixelData = nullptr;
    UINT cbBufferSize = 0;
    UINT stride = 0;

    // 2. Получаем указатель на данные
    if (SUCCEEDED(pLock->GetDataPointer(&cbBufferSize, &pPixelData)) &&
        SUCCEEDED(pLock->GetStride(&stride))) {

        // 3. Модифицируем пиксели (например, инвертируем цвета)
        for (UINT y = 0; y < height; ++y) {
            BYTE* pRow = pPixelData + y * stride;
            for (UINT x = 0; x < width; ++x) {
                pRow[x*4 + 0] = 255 - pRow[x*4 + 0]; // B
                pRow[x*4 + 1] = 255 - pRow[x*4 + 1]; // G
                pRow[x*4 + 2] = 255 - pRow[x*4 + 2]; // R
                // pRow[x*4 + 3] = pRow[x*4 + 3]; // A оставляем без изменений
            }
        }
    } // Блокировка автоматически снимается при разрушении pLock
}

Далее IWICFormatConverter (преобразователь форматов):

Предназначен для преобразования формата пикселей. Как и другие, наследует IWICBitmapSource, что позволяет использовать его в цепочках обработки.

Метод CanConvert
Аргумент REFWICPixelFormatGUID srcPixelFormat - GUID исходного формата.
Аргумент REFWICPixelFormatGUID dstPixelFormat - GUID целевого формата.
Аргумент BOOL pfCanConvert - указатель, который при успехе получает TRUE, если преобразование возможно, иначе FALSE.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Метод Initialize
Аргумент IWICBitmapSource pISource - указатель на исходный источник пикселей.
Аргумент REFWICPixelFormatGUID dstFormat - GUID целевого формата.
Аргумент WICBitmapDitherType dither - тип диффузии (сглаживания) для преобразования.
Аргумент IWICPalette pIPalette - указатель на объект палитры для целевого формата. Если не используется, передайте NULL.
Аргумент double alphaThresholdPercent - порог альфа-канала (от 0.0 до 100.0).
Аргумент WICBitmapPaletteType paletteTranslate - тип преобразования палитры.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Пример кода:

Код
ComPtr<IWICImagingFactory> pFactory; // ... инициализация фабрики
ComPtr<IWICBitmapFrameDecode> pFrame; // ... получение кадра

// 1. Проверяем возможность преобразования в формат 32-бит PBGRA
BOOL canConvert = FALSE;
HRESULT hr = pFormatConverter->CanConvert(pixelFormat, GUID_WICPixelFormat32bppPBGRA, &canConvert);

if (SUCCEEDED(hr) && canConvert) {
    ComPtr<IWICFormatConverter> pConverter;
    if (SUCCEEDED(pFactory->CreateFormatConverter(&pConverter))) {

        // 2. Инициализируем конвертер
        hr = pConverter->Initialize(
            pFrame.Get(),                       // Источник
            GUID_WICPixelFormat32bppPBGRA,      // Целевой формат
            WICBitmapDitherTypeNone,            // Без диффузии
            nullptr,                            // Палитра не используется
            0.0f,                               // Порог альфа
            WICBitmapPaletteTypeCustom          // Тип палитры
        );

        if (SUCCEEDED(hr)) {
            // 3. Получаем размеры и копируем преобразованные пиксели
            UINT width = 0, height = 0;
            pConverter->GetSize(&width, &height);
            
            UINT stride = width * 4;
            UINT bufferSize = stride * height;
            std::vector<BYTE> buffer(bufferSize);
            
            hr = pConverter->CopyPixels(nullptr, stride, bufferSize, buffer.data());
        }
    }
}

Далее IWICBitmapScaler, IWICBitmapClipper, IWICBitmapFlipRotator:

Все эти интерфейсы имеют похожую структуру: метод Initialize для настройки и наследование IWICBitmapSource для доступа к пикселям.

IWICBitmapScaler
Аргумент IWICBitmapSource pISource - указатель на исходный источник пикселей.
Аргумент UINT uiWidth - целевая ширина изображения после масштабирования.
Аргумент UINT uiHeight - целевая высота изображения после масштабирования.
Аргумент WICBitmapInterpolationMode mode - алгоритм интерполяции (например, WICBitmapInterpolationModeFantastic).
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

IWICBitmapClipper
Аргумент IWICBitmapSource pISource - указатель на исходный источник пикселей.
Аргумент const WICRect prc - указатель на структуру WICRect, задающую прямоугольник для обрезки.
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

IWICBitmapFlipRotator
Аргумент IWICBitmapSource pISource - указатель на исходный источник пикселей.
Аргумент WICBitmapTransformOptions options - параметр трансформации (отражение и/или поворот).
Возвращаемое значение HRESULT: S_OK в случае успеха, иначе код ошибки.

Пример кода:

Код
ComPtr<IWICImagingFactory> pFactory; // ... инициализация фабрики
ComPtr<IWICBitmapFrameDecode> pFrame; // ... получение кадра

// --- Пример масштабирования ---
ComPtr<IWICBitmapScaler> pScaler;
if (SUCCEEDED(pFactory->CreateBitmapScaler(&pScaler))) {
    if (SUCCEEDED(pScaler->Initialize(pFrame.Get(), 640, 480, WICBitmapInterpolationModeFantastic))) {
        // pScaler теперь можно использовать для копирования уменьшенных пикселей
    }
}

// --- Пример обрезки ---
ComPtr<IWICBitmapClipper> pClipper;
if (SUCCEEDED(pFactory->CreateBitmapClipper(&pClipper))) {
    WICRect rcClip = {100, 100, 400, 300};
    if (SUCCEEDED(pClipper->Initialize(pFrame.Get(), &rcClip))) {
        // pClipper теперь можно использовать для копирования обрезанных пикселей
    }
}

// --- Пример поворота ---
ComPtr<IWICBitmapFlipRotator> pRotator;
if (SUCCEEDED(pFactory->CreateBitmapFlipRotator(&pRotator))) {
    if (SUCCEEDED(pRotator->Initialize(pFrame.Get(), WICBitmapTransformRotate90))) {
        // pRotator теперь можно использовать для копирования повернутых пикселей
    }
}

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

При желании материально подержать перевод и структурирование информации — средства можете отправить через сбор в ЮМани .