Поделиться через


Общие сведения о кодировке

Кодировщик записывает данные изображения в поток. Кодировщики могут сжимать, шифровать и изменять пиксели изображения несколькими способами до их записи в поток. Использование некоторых кодировщиков приводит к компромиссам, например JPEG, что позволяет лучше сжать сведения о цвете. Другие кодировщики не приводят к таким потерям, например растровое изображение (BMP). Так как многие кодеки используют собственную технологию для повышения точности сжатия и изображения, сведения о том, как изображение закодировано, зависит от разработчика кодека.

IWICBitmapEncoder

IWICBitmapEncoder — это основной интерфейс для кодирования изображения в целевом формате и используется для сериализации компонентов изображения, таких как эскиз (SetThumbnail) и кадры (CreateNewFrame) в файл изображения.

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

IWICBitmapFrameEncode

IWICBitmapFrameEncode — это интерфейс для кодирования отдельных кадров изображения. Он предоставляет методы настройки отдельных компонентов изображения кадров, таких как эскизы и кадры, а также измерения изображений, DPI и форматы пикселей.

Отдельные кадры могут быть закодированы с помощью метаданных, зависящих от кадра, поэтому IWICBitmapFrameEncode предоставляет доступ к записи метаданных с помощью метода GetMetadataQueryWriter .

Метод commit кадра фиксирует все изменения в отдельном кадре и указывает, что изменения в этом кадре больше не должны приниматься.

Пример кодирования (TIFF)

В следующем примере изображение формата файла тегов (TIFF) закодировано с помощью IWICBitmapEncoder и IWICBitmapFrameEncode. Выходные данные TIFF настраиваются с помощью WICTiffCompressionOption , а кадр растрового изображения инициализируется с помощью заданных параметров. После создания образа с помощью WritePixels, кадр фиксируется с помощью Commit, а изображение сохраняется с помощью Commit.

IWICImagingFactory *piFactory = NULL;
IWICBitmapEncoder *piEncoder = NULL;
IWICBitmapFrameEncode *piBitmapFrame = NULL;
IPropertyBag2 *pPropertybag = NULL;

IWICStream *piStream = NULL;
UINT uiWidth = 640;
UINT uiHeight = 480;

HRESULT hr = CoCreateInstance(
                CLSID_WICImagingFactory,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IWICImagingFactory,
                (LPVOID*) &piFactory);

if (SUCCEEDED(hr))
{
    hr = piFactory->CreateStream(&piStream);
}

if (SUCCEEDED(hr))
{
    hr = piStream->InitializeFromFilename(L"output.tif", GENERIC_WRITE);
}

if (SUCCEEDED(hr))
{
   hr = piFactory->CreateEncoder(GUID_ContainerFormatTiff, NULL, &piEncoder);
}

if (SUCCEEDED(hr))
{
    hr = piEncoder->Initialize(piStream, WICBitmapEncoderNoCache);
}

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // This is how you customize the TIFF output.
    PROPBAG2 option = { 0 };
    option.pstrName = L"TiffCompressionMethod";
    VARIANT varValue;    
    VariantInit(&varValue);
    varValue.vt = VT_UI1;
    varValue.bVal = WICTiffCompressionZIP;      
    hr = pPropertybag->Write(1, &option, &varValue);        
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->SetSize(uiWidth, uiHeight);
}

WICPixelFormatGUID formatGUID = GUID_WICPixelFormat24bppBGR;
if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->SetPixelFormat(&formatGUID);
}

if (SUCCEEDED(hr))
{
    // We're expecting to write out 24bppRGB. Fail if the encoder cannot do it.
    hr = IsEqualGUID(formatGUID, GUID_WICPixelFormat24bppBGR) ? S_OK : E_FAIL;
}

if (SUCCEEDED(hr))
{
    UINT cbStride = (uiWidth * 24 + 7)/8/***WICGetStride***/;
    UINT cbBufferSize = uiHeight * cbStride;

    BYTE *pbBuffer = new BYTE[cbBufferSize];

    if (pbBuffer != NULL)
    {
        for (UINT i = 0; i < cbBufferSize; i++)
        {
            pbBuffer[i] = static_cast<BYTE>(rand());
        }

        hr = piBitmapFrame->WritePixels(uiHeight, cbStride, cbBufferSize, pbBuffer);

        delete[] pbBuffer;
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
}

if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->Commit();
}    

if (SUCCEEDED(hr))
{
    hr = piEncoder->Commit();
}

if (piFactory)
    piFactory->Release();

if (piEncoder)
    piEncoder->Release();

if (piBitmapFrame)
    piBitmapFrame->Release();

if (pPropertybag)
    pPropertybag->Release();

if (piStream)
    piStream->Release();

return hr;

Использование параметров кодировщика

Различные кодировщики для разных форматов должны предоставлять различные параметры кодирования изображения. Компонент образов Windows (WIC) предоставляет согласованный механизм для определения необходимости параметров кодирования, позволяя приложениям работать с несколькими кодировщиками без необходимости знать определенный формат. Это достигается путем предоставления параметра IPropertyBag в методе CreateNewFrame и методе Initialize .

Фабрика компонентов предоставляет простой способ создания контейнера свойств опций кодировщика. Кодеки могут использовать эту службу, если им нужно предоставить простой, интуитивно понятный и не конфликтующий набор параметров кодировщика. Контейнер свойств изображения должен быть инициализирован во время создания со всеми параметрами кодировщика, соответствующими этому кодеку. Для параметров кодировщика из канонического набора диапазон значений будет применяться в процессе записи. Для более сложных потребностей кодеки должны писать собственную реализацию пакета свойств.

Приложение получает пакет параметров кодировщика во время создания кадра и должен настроить все значения перед инициализацией кадра кодировщика. Для приложения, управляемого пользовательским интерфейсом, он может предложить фиксированный пользовательский интерфейс для параметров канонического кодировщика и расширенное представление для оставшихся параметров. Изменения можно вносить по одному за раз с помощью метода Write, и любая ошибка будет сообщаться через IErrorLog. Приложение пользовательского интерфейса всегда должно повторно считывать и отображать все параметры после внесения изменений в случае изменения, вызвавшего каскадный эффект. Приложение должно быть готово к обработке неудачной инициализации кадров для кодеков, которые предоставляют только минимальные отчеты об ошибках через контейнер свойств.

Параметры кодировщика

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

Название свойства Тип переменной Ценность Применимые кодеки
Преобразование растрового изображения VT_UI1 WICBitmapTransformOptions JPEG, JPEG XL, HEIF
КачествоСжатия VT_R4 0-1.0 TIFF, JPEG XL
Метод сжатия HEIF VT_UI1 WICHeifCompressionOption HEIF
ImageQuality VT_R4 0-1.0 JPEG, JPEG XL, HDPhoto, HEIF
Без потерь VT_BOOL ИСТИНА, ЛОЖЬ JPEG XL, HDPhoto

Качество изображения 0.0 означает наименьший возможный уровень точности, а 1.0 означает самый высокий уровень точности, который также может подразумевать отсутствие потерь в зависимости от кодека.

CompressionQuality 0.0 означает наименее эффективную схему сжатия, что обычно приводит к быстрому кодированию, но увеличенному объёму данных. Значение 1.0 означает, что наиболее эффективная схема доступна, как правило, занимает больше времени для кодирования, но создает меньшие выходные данные. В зависимости от возможностей кодека этот диапазон может быть сопоставлен с дискретным набором доступных методов сжатия.

Потеря данных отсутствует означает, что кодек кодирует изображение как без потерь. Если включен режим Lossless, параметр ImageQuality игнорируется.

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

Название свойства Тип переменной Ценность Применимые кодеки
InterlaceOption VT_BOOL Вкл./Выкл. PNG
Опция фильтра VT_UI1 WICPngFilterOption PNG
Метод сжатия TIFF VT_UI1 WICTiffCompressionOption РАЗМОЛВКА
Яркость VT_UI4/VT_ARRAY 64 записи (DCT) JPEG
Цветность VT_UI4/VT_ARRAY 64 записи (DCT) JPEG
JpegYCrCbSubsampling VT_UI1 WICJpegYCrCbSubsamplingOption JPEG
СкрытьApp0 VT_BOOL JPEG
EnableV5Header32bppBGRA VT_BOOL Вкл./Выкл. БМП

Используйте VT_EMPTY , чтобы указать *not set* в качестве значения по умолчанию. Если заданы дополнительные свойства, но не поддерживаются, кодировщик должен игнорировать их; это позволяет приложениям кодировать меньше логики, если они хотят возможности, которые могут или не могут присутствовать.

Примеры параметров кодировщика

В приведенном выше примере кодирования TIFF устанавливается определенный параметр кодировщика. Элемент pstrName структуры PROPBAG2 имеет соответствующее имя свойства, а параметр VARIANT имеет соответствующее значение VARTYPE и требуемое значение, в данном случае член перечисления WICTiffCompressionOption .

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // This is how you customize the TIFF output.
    PROPBAG2 option = { 0 };
    option.pstrName = L"TiffCompressionMethod";
    VARIANT varValue;    
    VariantInit(&varValue);
    varValue.vt = VT_UI1;
    varValue.bVal = WICTiffCompressionZIP;      
    hr = pPropertybag->Write(1, &option, &varValue);        
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

Чтобы использовать параметры кодировщика по умолчанию, просто инициализировать кадр растрового изображения с помощью контейнера свойств, возвращаемого при создании кадра.

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // Accept the default encoder options.
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

Кроме того, можно исключить контейнер свойств при отсутствии параметров кодировщика.

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, 0);
}

if (SUCCEEDED(hr))
{        
    // No encoder options.
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(0);
    }
}