Compartilhar via


Visão geral da codificação

Um codificador grava dados de imagem em um fluxo. Os codificadores podem compactar, criptografar e alterar os pixels de imagem de várias maneiras antes de escrevê-los no fluxo. O uso de alguns codificadores resulta em compensações, por exemplo, JPEG, que troca informações de cor por melhor compactação. Outros codificadores não resultam em tais perdas—por exemplo, bitmap (BMP). Como muitos codecs usam tecnologia proprietária para obter melhor compactação e fidelidade de imagem, os detalhes sobre como uma imagem é codificada ficam a critério do desenvolvedor do codec.

IWICBitmapEncoder

IWICBitmapEncoder é a interface principal para codificar uma imagem no formato de destino e usada para serializar os componentes de uma imagem, como miniatura (SetThumbnail) e quadros (CreateNewFrame), para o arquivo de imagem.

Como e quando ocorre a serialização é deixado para o desenvolvedor do codec. Cada bloco individual de dados dentro do formato de arquivo de destino deve ser definido independentemente da ordem, mas, novamente, essa é a decisão do desenvolvedor do codec. No entanto, depois que o método Commit for chamado, as alterações na imagem não deverão ser permitidas e o fluxo deverá ser fechado.

IWICBitmapFrameEncode

IWICBitmapFrameEncode é a interface para codificar os quadros individuais de uma imagem. Ele fornece métodos para definir componentes individuais de imagem de quadro, como miniaturas e quadros, bem como dimensões de imagem, DPI e formatos de pixel.

Quadros individuais podem ser codificados com metadados específicos do quadro, portanto , IWICBitmapFrameEncode fornece acesso a um gravador de metadados por meio do método GetMetadataQueryWriter .

O método Commit do quadro confirma todas as alterações no quadro individual e indica que as alterações nesse quadro não devem mais ser aceitas.

Exemplo de codificação (TIFF)

No exemplo a seguir, uma imagem TIFF (Formato de Arquivo de Imagem Marcada) é codificada usando IWICBitmapEncoder e um IWICBitmapFrameEncode. A saída TIFF é personalizada usando o WICTiffCompressionOption e o quadro de bitmap é inicializado usando as opções fornecidas. Depois que a imagem tiver sido criada usando WritePixels, o quadro será confirmado por meio de Commit e a imagem será salva usando 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;

Uso de opções de codificador

Codificadores diferentes para formatos diferentes precisam expor diferentes opções de como uma imagem é codificada. O WIC (Componente de Imagem do Windows) fornece um mecanismo consistente para expressar se as opções de codificação são necessárias enquanto ainda permitem que os aplicativos trabalhem com vários codificadores sem exigir conhecimento de um formato específico. Isso é feito fornecendo um parâmetro IPropertyBag no método CreateNewFrame e no método Initialize .

A fábrica de componentes oferece um ponto de criação fácil para criar um contêiner de propriedades de opções do codificador. Os codecs poderão usar esse serviço se precisarem fornecer um conjunto simples, intuitivo e não conflitante de opções de codificador. O conjunto de propriedades de imagem deve ser inicializado durante a criação com todas as opções de codificador relevantes para esse codec. Para opções de codificador do conjunto canônico, o intervalo de valores será imposto em Gravação. Para necessidades mais avançadas, os codecs devem criar sua própria estrutura de dados de propriedades.

Um aplicativo recebe o conjunto de opções do codificador durante a criação do frame e deve configurar os valores antes de inicializar o frame do codificador. Para um aplicativo controlado por interface do usuário, ele pode oferecer uma interface do usuário fixa para as opções de codificador canônico e uma exibição avançada para opções restantes. As alterações podem ser feitas uma de cada vez por meio do método Write e qualquer erro será relatado por meio do IErrorLog. O aplicativo de interface do usuário deve sempre ler novamente e exibir todas as opções depois de fazer uma alteração caso a alteração tenha causado um efeito em cascata. Um aplicativo deve estar preparado para lidar com falhas na inicialização de quadros de vídeo para codecs que fornecem apenas relatórios de erros mínimos por meio de seu conjunto de propriedades.

Opções de codificador

Um aplicativo pode esperar encontrar o seguinte conjunto de opções de codificador. As opções de codificador refletem os recursos de um codificador e o formato de contêiner subjacente e, portanto, não são, por sua natureza, realmente independentes de codec. Quando possível, novas opções devem ser normalizadas para que possam ser aplicadas a novos codecs que surgem.

Nome da propriedade VARTYPE Valor Codecs aplicáveis
BitmapTransform VT_UI1 wicbitmapTransformOptions JPEG, JPEG XL, HEIF
Qualidade de Compressão VT_R4 0-1.0 TIFF, JPEG XL
HeifCompressionMethod VT_UI1 WICHeifCompressionOption HEIF
ImageQuality VT_R4 0-1.0 JPEG, JPEG XL, HDPhoto, HEIF
Sem Perda VT_BOOL TRUE, FALSE JPEG XL, HDPhoto

ImageQuality de 0.0 significa a menor representação de fidelidade possível e 1.0 significa a maior fidelidade, o que também pode implicar sem perda, dependendo do codec.

CompressionQuality de 0.0 significa o esquema de compactação menos eficiente disponível, normalmente resultando em uma codificação rápida, mas em um arquivo de saída maior. Um valor 1.0 significa o esquema mais eficiente disponível, normalmente levando mais tempo para codificar, mas produzindo uma saída menor. Dependendo dos recursos do codec, esse intervalo pode ser mapeado para um conjunto discreto de métodos de compactação disponíveis.

Lossless significa que o codec codifica a imagem sem nenhuma perda de dados. Se Lossless estiver habilitado, ImageQuality será ignorado.

Além das opções de codificador genérico acima, os codecs fornecidos com WIC dão suporte às seguintes opções. Se um codec tiver a necessidade de dar suporte a uma opção que seja consistente com o uso nesses codecs fornecidos, será recomendável fazer isso.

Nome da propriedade VARTYPE Valor Codecs aplicáveis
Opção de entrelaçamento VT_BOOL Ativar/Desativar PNG
Opção de Filtro VT_UI1 WICPngFilterOption PNG
Método de Compressão TIFF VT_UI1 WICTiffCompressionOption TIFF
Luminância VT_UI4/VT_ARRAY 64 Entradas (DCT) JPEG
Crominância VT_UI4/VT_ARRAY 64 Entradas (DCT) JPEG
JpegYCrCbSubsampling VT_UI1 WICJpegYCrCbSubsamplingOption JPEG
SuppressApp0 VT_BOOL JPEG
EnableV5Header32bppBGRA VT_BOOL Ativar/Desativar BMP

Use VT_EMPTY para indicar *não definido* como o padrão. Se propriedades adicionais forem definidas, mas não tiverem suporte, o codificador deverá ignorá-las; isso permite que os aplicativos codifiquem menos lógica se quiserem uma funcionalidade que possa ou não estar presente.

Exemplos de opções de codificador

No exemplo de codificação TIFF acima, uma opção de codificador específica é definida. O membro pstrName da estrutura PROPBAG2 é definido como o nome apropriado da propriedade, e o VARIANT é definido como o VARTYPE correspondente e o valor desejado, nesse caso, um membro da enumeração 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);
    }
}

Para usar as opções de codificador padrão, basta inicializar a estrutura de bitmap com o conjunto de propriedades retornado quando a estrutura foi criada.

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

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

Também é possível eliminar o recipiente de propriedades quando nenhuma opção de codificador está sendo considerada.

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

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