Compartilhar via


Suporte a JPEG YCbCr

Começando com Windows 8.1, o codec JPEG do Componente de Imagem do Windows (WIC) dá suporte à leitura e gravação de dados de imagem em seu formulário YCbCr nativo. O suporte a WIC YCbCr pode ser usado em conjunto com Direct2D para renderizar dados de pixel YCbCr com um efeito de imagem. Além disso, o codec DO WIC JPEG pode consumir dados de pixel YCbCr produzidos por determinados drivers de câmera por meio do Media Foundation.

Os dados de pixel YCbCr consomem significativamente menos memória do que os formatos de pixel BGRA padrão. Além disso, acessar dados do YCbCr permite descarregar alguns estágios do pipeline de decodificação/codificação jpeg para Direct2D que é acelerada por GPU. Usando o YCbCr, seu aplicativo pode reduzir o consumo de memória JPEG e os tempos de carga para as mesmas imagens de tamanho e qualidade. Ou, seu aplicativo pode usar mais imagens JPEG de maior resolução sem sofrer penalidades de desempenho.

Este tópico descreve como os dados do YCbCr funcionam e como usá-los no WIC e Direct2D.

Sobre os dados do JPEG YCbCr

Esta seção explica alguns dos principais conceitos necessários para entender como o suporte do YCbCr no WIC funciona e seus principais benefícios.

Modelo de cor YCbCr

O WIC em Windows 8 e anteriores dá suporte a quatro modelos de cores diferentes, dos quais o mais comum é RGB/BGR. Esse modelo de cor define os dados de cor usando componentes vermelhos, verdes e azuis; um quarto componente alfa também pode ser usado.

Aqui está uma imagem decomposta em seus componentes vermelho, verde e azul.

uma imagem decomposta em seus componentes vermelho, verde e azul.

YCbCr é um modelo de cor alternativo que define dados de cor usando um componente de luminância (Y) e dois componentes de crominância (Cb e Cr). Ele é comumente usado em cenários digitais de imagens e vídeos. O termo YCbCr geralmente é usado de forma intercambiável com YUV, embora os dois sejam tecnicamente distintos.

Há várias variações de YCbCr que diferem no espaço de cores e nas definições de intervalo dinâmico – o WIC dá suporte especificamente aos dados JFIF YCbCr do JPEG. Para obter mais informações, consulte a especificação JPEG ITU-T81.

Aqui está uma imagem decomposta em seus componentes Y, Cb e Cr .

uma imagem decomposta em seus componentes y, cb e cr.

Layouts de memória planar versus intercalado

Esta seção descreve algumas diferenças entre acessar e armazenar dados de pixel RGB na memória versus dados YCbCr .

Os dados de pixel RGB normalmente são armazenados usando um layout de memória intercalado. Isso significa que os dados de um único componente de cor são intercalados entre pixels e cada pixel é armazenado contíguamente na memória.

Aqui está uma figura mostrando dados de pixel RGBA armazenados em um layout de memória intercalado.

uma figura mostrando dados de pixel rgba armazenados em um layout de memória intercalado.

Os dados YCbCr normalmente são armazenados usando um layout de memória planar. Isso significa que cada componente de cor é armazenado separadamente em seu próprio plano contíguo, para um total de três planos. Em outra configuração comum, os componentes Cb e Cr são intercalados e armazenados juntos, enquanto o componente Y permanece em seu próprio plano, para um total de dois planos.

Aqui está uma figura mostrando os dados de pixelde Cr c r planar e intercalados, um layout de memória YCbCr comum.

uma figura mostrando dados de pixel cbcr planar y e intercalados, um layout de memória ycbcr comum.

Em wic e Direct2D, cada plano de cores é tratado como seu próprio objeto distinto (um IWICBitmapSource ou ID2D1Bitmap) e, coletivamente, esses planos formam os dados de suporte para uma imagem YCbCr.

Embora o WIC dê suporte ao acesso a dados YCbCr nas configurações do plano 2 e 3, Direct2D dá suporte apenas ao primeiro (Y e CbCr). Portanto, ao usar WIC e Direct2D juntos, você sempre deve usar a configuração de 2 planos YCbCr.

Subampling chroma

O modelo de cores YCbCr é adequado para cenários de geração de imagens digitais porque pode aproveitar determinados aspectos do sistema visual humano. Em particular, os humanos são mais sensíveis às alterações na luminância (brilho) de uma imagem e menos sensíveis à crominância (cor). Dividindo os dados de cor em componentes separados de luminância e crominância, podemos compactar seletivamente apenas os componentes de chrominance para obter economia de espaço com uma perda mínima de qualidade.

Uma técnica para fazer isso é chamada de subampa de croma. Os planos Cb e Cr são subemplados (em escala inferior) em uma ou ambas as dimensões horizontal e vertical. Por motivos históricos, cada modo de subampação de chroma é comumente chamado de usando uma taxa J:a:b de três partes.

Modo de subampasagem Escala de downscale horizontal Escala de downscale vertical Bits por pixel*
4:4:4 1x 1x 24
4:2:2 2x 1x 16
4:4:0 1x 2x 16
4:2:0 2x 2x 12

 

* Inclui dados Y.

Na tabela acima, se você usar YCbCr para armazenar dados de imagem não compactados, poderá obter uma economia de memória de 25% a 62,5% contra 32 bits por pixel de dados RGBA, dependendo de qual modo de subampação chroma é usado.

Uso de JPEG de YCbCr

Em um alto nível, o pipeline de descompactação JPEG consiste nos seguintes estágios:

  1. Executar entropia (por exemplo, Huffman) descompactação
  2. Executar desquantização
  3. Executar transformação inversa de cosseno discreto
  4. Executar upsampling chroma em dados CbCr
  5. Converter dados YCbCr em RGBA (se necessário)

Ao fazer com que o codec JPEG produza dados YCbCr , podemos evitar as duas etapas finais do processo de decodificação ou adiá-las para a GPU. Além da economia de memória listada na seção anterior, isso reduz significativamente o tempo geral necessário para decodificar a imagem. A mesma economia se aplica ao codificar dados YCbCr .

Usando dados do JPEG YCbCr

Esta seção explica como usar WIC e Direct2D para operar em dados YCbCr.

Para ver as diretrizes deste documento usadas na prática, consulte as otimizações jpeg YCbCr em Direct2D e exemplo de WIC que demonstra todas as etapas necessárias para decodificar e renderizar conteúdo YCbCr em um aplicativo Direct2D.

Usando imagens YCbCr JPEG

A grande maioria das imagens JPEG são armazenadas como YCbCr. Alguns JPEGs contêm dados CMYK ou em escala de cinza e não usam YCbCr. Isso significa que você normalmente, mas nem sempre, pode usar diretamente o conteúdo JPEG pré-existente sem nenhuma modificação.

WIC e Direct2D não dão suporte a todas as configurações possíveis do YCbCr e o suporte a YCbCr em Direct2D depende do hardware e do driver gráfico subjacentes. Por isso, um pipeline de geração de imagens de uso geral precisa ser robusto para imagens que não usam YCbCr (incluindo outros formatos de imagem comuns, como PNG ou BMP) ou para casos em que o suporte a YCbCr não está disponível. Recomendamos que você mantenha seu pipeline de geração de imagens baseado em BGRA existente e habilite o YCbCr como uma otimização de desempenho quando disponível.

APIs de componente de imagens do Windows

O WIC no Windows 8.1 adiciona três novas interfaces para fornecer acesso aos dados JPEG YCbCr.

IWICPlanarBitmapSourceTransform

IWICPlanarBitmapSourceTransform é análogo a IWICBitmapSourceTransform, exceto que ele produz pixels em uma configuração planar, incluindo dados YCbCr . Você pode obter essa interface chamando QueryInterface em uma implementação de IWICBitmapSource que dá suporte ao acesso planar. Isso inclui a implementação do codec JPEG de IWICBitmapFrameDecode , bem como IWICBitmapScaler, IWICBitmapFlipRotator e IWICColorTransform.

IWICPlanarBitmapFrameEncode

IWICPlanarBitmapFrameEncode fornece a capacidade de codificar dados de pixel planar, incluindo dados YCbCr . Você pode obter essa interface chamando QueryInterface na implementação do codec JPEG de IWICBitmapFrameEncode.

IWICPlanarFormatConverter

IWICPlanarFormatConverter permite que IWICFormatConverter consuma dados de pixel planar, incluindo YCbCr, e converta-os em um formato de pixel intercalado. Ele não expõe a capacidade de converter dados de pixel intercalados em um formato planar. Você pode obter essa interface chamando QueryInterface na implementação fornecida pelo Windows de IWICFormatConverter.

APIs Direct2D

Direct2D no Windows 8.1 dá suporte a dados de pixel planar YCbCr com o novo efeito de imagem YCbCr . Esse efeito fornece a capacidade de renderizar dados YCbCr . O efeito usa como entrada duas interfaces ID2D1Bitmap : uma contendo dados Y planares no formato DXGI_FORMAT_R8_UNORM e outra contendo dados CbCr intercalados no formato DXGI_FORMAT_R8G8_UNORM. Normalmente, você usa esse efeito no lugar do ID2D1Bitmap que conteria dados de pixel BGRA.

O efeito de imagem YCbCr destina-se a ser usado em conjunto com as APIs WIC YCbCr que fornecem os dados YCbCr . Isso atua efetivamente para descarregar parte do trabalho de decodificação da CPU para a GPU, em que ela pode ser processada muito mais rápido e em paralelo.

Determinando se a configuração do YCbCr tem suporte

Conforme observado anteriormente, seu aplicativo deve ser robusto para casos em que o suporte a YCbCr não está disponível. Esta seção discute as condições para as quais seu aplicativo deve marcar. Se qualquer uma das verificações a seguir falhar, seu aplicativo deverá voltar para um pipeline baseado em BGRA padrão.

O componente WIC dá suporte ao acesso a dados YCbCr ?

Somente o codec JPEG fornecido pelo Windows e determinadas transformações wic dão suporte ao acesso a dados YCbCr . Para obter uma lista completa, consulte a seção APIs do Componente de Imagem do Windows .

Para obter uma das interfaces YCbCr planar, chame QueryInterface na interface original. Isso falhará se o componente não der suporte ao acesso a dados YCbCr .

A transformação de WIC solicitada tem suporte para YCbCr?

Depois de obter um IWICPlanarBitmapSourceTransform, você deve primeiro chamar DoesSupportTransform. Esse método usa como parâmetros de entrada o conjunto completo de transformações que você deseja aplicar aos dados YCbCr planar e retorna um booliano indicando suporte, bem como as dimensões mais próximas do tamanho solicitado que podem ser retornadas. Você deve marcar todos os três valores antes de acessar os dados de pixel com IWICPlanarBitmapSourceTransform::CopyPixels.

Esse padrão é semelhante a como IWICBitmapSourceTransform é usado.

O driver gráfico dá suporte aos recursos necessários para usar o YCbCr com Direct2D?

Esse marcar só será necessário se você estiver usando o efeito Direct2D YCbCr para renderizar o conteúdo YCbCr. Direct2D armazena dados YCbCr usando os formatos de pixel DXGI_FORMAT_R8_UNORM e DXGI_FORMAT_R8G8_UNORM, que não estão disponíveis em todos os drivers gráficos.

Antes de usar o efeito de imagem YCbCr , você deve chamar ID2D1DeviceContext::IsDxgiFormatSupported para garantir que ambos os formatos sejam compatíveis com o driver.

Código de exemplo

Veja abaixo um exemplo de código que demonstra as verificações recomendadas. Este exemplo foi obtido das otimizações de JPEG YCbCr em Direct2D e no exemplo de WIC.

bool DirectXSampleRenderer::DoesWicSupportRequestedYCbCr()
{
    ComPtr<IWICPlanarBitmapSourceTransform> wicPlanarSource;
    HRESULT hr = m_wicScaler.As(&wicPlanarSource);
    if (SUCCEEDED(hr))
    {
        BOOL isTransformSupported;
        uint32 supportedWidth = m_cachedBitmapPixelWidth;
        uint32 supportedHeight = m_cachedBitmapPixelHeight;
        DX::ThrowIfFailed(
            wicPlanarSource->DoesSupportTransform(
                &supportedWidth,
                &supportedHeight,
                WICBitmapTransformRotate0,
                WICPlanarOptionsDefault,
                SampleConstants::WicYCbCrFormats,
                m_planeDescriptions,
                SampleConstants::NumPlanes,
                &isTransformSupported
                )
            );

        // The returned width and height may be larger if IWICPlanarBitmapSourceTransform does not
        // exactly support what is requested.
        if ((isTransformSupported == TRUE) &&
            (supportedWidth == m_cachedBitmapPixelWidth) &&
            (supportedHeight == m_cachedBitmapPixelHeight))
        {
            return true;
        }
    }

    return false;
}

bool DirectXSampleRenderer::DoesDriverSupportYCbCr()
{
    auto d2dContext = m_deviceResources->GetD2DDeviceContext();

    return (d2dContext->IsDxgiFormatSupported(DXGI_FORMAT_R8_UNORM)) &&
        (d2dContext->IsDxgiFormatSupported(DXGI_FORMAT_R8G8_UNORM));
}

Decodificação de dados de pixel YCbCr

Se você quiser obter dados de pixel YCbCr , chame IWICPlanarBitmapSourceTransform::CopyPixels. Esse método copia dados de pixel em uma matriz de estruturas WICBitmapPlane preenchidas, uma para cada plano de dados desejado (por exemplo, Y e CbCr). Um WICBitmapPlane contém informações sobre os dados de pixel e aponta para o buffer de memória que receberá os dados.

Se você quiser usar os dados de pixel YCbCr com outras APIs WIC, deverá criar um IWICBitmap configurado adequadamente, chamar Lock para obter o buffer de memória subjacente e associar o buffer ao WICBitmapPlane usado para receber os dados de pixel YCbCr . Em seguida, você pode usar o IWICBitmap normalmente.

Por fim, se você quiser renderizar os dados YCbCr em Direct2D, deverá criar um ID2D1Bitmap de cada IWICBitmap e usá-los como fonte para o efeito de imagem YCbCr. O WIC permite que você solicite várias configurações planares. Ao interoperar com Direct2D você deve solicitar dois planos, um usando GUID_WICPixelFormat8bppY e o outro usando GUID_WICPixelFormat16bppCbCr, pois essa é a configuração esperada por Direct2D.

Exemplo de código

Veja abaixo um exemplo de código que demonstra as etapas para decodificar e renderizar dados YCbCr em Direct2D. Este exemplo foi obtido das otimizações de JPEG YCbCr em Direct2D e no exemplo de WIC.

void DirectXSampleRenderer::CreateYCbCrDeviceResources()
{
    auto wicFactory = m_deviceResources->GetWicImagingFactory();
    auto d2dContext = m_deviceResources->GetD2DDeviceContext();

    ComPtr<IWICPlanarBitmapSourceTransform> wicPlanarSource;
    DX::ThrowIfFailed(
        m_wicScaler.As(&wicPlanarSource)
        );

    ComPtr<IWICBitmap> bitmaps[SampleConstants::NumPlanes];
    ComPtr<IWICBitmapLock> locks[SampleConstants::NumPlanes];
    WICBitmapPlane planes[SampleConstants::NumPlanes];

    for (uint32 i = 0; i < SampleConstants::NumPlanes; i++)
    {
        DX::ThrowIfFailed(
            wicFactory->CreateBitmap(
                m_planeDescriptions[i].Width,
                m_planeDescriptions[i].Height,
                m_planeDescriptions[i].Format,
                WICBitmapCacheOnLoad,
                &bitmaps[i]
                )
            );

        LockBitmap(bitmaps[i].Get(), WICBitmapLockWrite, nullptr, &locks[i], &planes[i]);
    }

    DX::ThrowIfFailed(
        wicPlanarSource->CopyPixels(
            nullptr, // Copy the entire source region.
            m_cachedBitmapPixelWidth,
            m_cachedBitmapPixelHeight,
            WICBitmapTransformRotate0,
            WICPlanarOptionsDefault,
            planes,
            SampleConstants::NumPlanes
            )
        );

    DX::ThrowIfFailed(d2dContext->CreateEffect(CLSID_D2D1YCbCr, &m_d2dYCbCrEffect));

    ComPtr<ID2D1Bitmap1> d2dBitmaps[SampleConstants::NumPlanes];
    for (uint32 i = 0; i < SampleConstants::NumPlanes; i++)
    {
        // IWICBitmapLock must be released before using the IWICBitmap.
        locks[i] = nullptr;

        // First ID2D1Bitmap1 is DXGI_FORMAT_R8 (Y), second is DXGI_FORMAT_R8G8 (CbCr).
        DX::ThrowIfFailed(d2dContext->CreateBitmapFromWicBitmap(bitmaps[i].Get(), &d2dBitmaps[i]));
        m_d2dYCbCrEffect->SetInput(i, d2dBitmaps[i].Get());
    }
}

void DirectXSampleRenderer::LockBitmap(
    _In_ IWICBitmap *pBitmap,
    DWORD bitmapLockFlags,
    _In_opt_ const WICRect *prcSource,
    _Outptr_ IWICBitmapLock **ppBitmapLock,
    _Out_ WICBitmapPlane *pPlane
    )
{
    // ComPtr guarantees the IWICBitmapLock is released if an exception is thrown.
    ComPtr<IWICBitmapLock> lock;
    DX::ThrowIfFailed(pBitmap->Lock(prcSource, bitmapLockFlags, &lock));
    DX::ThrowIfFailed(lock->GetStride(&pPlane->cbStride));
    DX::ThrowIfFailed(lock->GetDataPointer(&pPlane->cbBufferSize, &pPlane->pbBuffer));
    DX::ThrowIfFailed(lock->GetPixelFormat(&pPlane->Format));
    *ppBitmapLock = lock.Detach();
}

Transformando dados de pixel YCbCr

Transformar dados YCbCr é quase idêntico à decodificação, pois ambos envolvem IWICPlanarBitmapSourceTransform. A única diferença é de qual objeto WIC você obteve a interface. O dimensionador fornecido pelo Windows, o rotador de inversão e a transformação de cores dão suporte ao acesso YCbCr .

Encadeando transformações juntas

O WIC dá suporte à noção de encadeamento de várias transformações. Por exemplo, você pode criar o seguinte pipeline wic:

um diagrama de um pipeline wic começando com um decodificador jpeg.

Em seguida, você pode chamar QueryInterface no IWICColorTransform para obter IWICPlanarBitmapSourceTransform. A transformação de cores pode se comunicar com as transformações anteriores e pode expor os recursos agregados de cada estágio do pipeline. O WIC garante que os dados YCbCr sejam preservados durante todo o processo. Esse encadeamento só funciona ao usar componentes que dão suporte ao acesso YCbCr .

Otimizações de Codec JPEG

Semelhante à implementação de decodificação de quadro JPEG de IWICBitmapSourceTransform, a implementação de decodificação de quadro JPEG de IWICPlanarBitmapSourceTransform dá suporte à escala e à rotação do domínio DCT JPEG nativo. Você pode solicitar uma potência de dois downscale ou uma rotação diretamente do decodificador JPEG. Isso normalmente resulta em maior qualidade e desempenho do que usar as transformações discretas.

Além disso, quando você encadeia uma ou mais transformações de WIC após o decodificador JPEG, ele pode aproveitar o dimensionamento e a rotação jpeg nativos para atender à operação solicitada de agregação.

Conversões de formato

Use IWICPlanarFormatConverter para converter dados de pixel planar YCbCr em um formato de pixel intercalado, como GUID_WICPixelFormat32bppPBGRA. O WIC em Windows 8.1 não fornece a capacidade de converter em um formato de pixel YCbCr planar.

Codificando dados de pixel YCbCr

Use IWICPlanarBitmapFrameEncode para codificar dados de pixel YCbCr para o codificador JPEG. A codificação de dados YCbCrIWICPlanarBitmapFrameEncode é semelhante, mas não idêntica à codificação de dados intercalados usando IWICBitmapFrameEncode. A interface planar expõe apenas a capacidade de gravar dados de imagem de quadro planar e você deve continuar a usar a interface de codificação de quadro para definir metadados ou uma miniatura e confirmar no final da operação.

Para o caso típico, você deve seguir estas etapas:

  1. Obtenha o IWICBitmapFrameEncode normalmente. Se você quiser configurar a subamostragem chroma, defina a opção de codificador JpegYCrCbSubsampling ao criar o quadro.
  2. Se você precisar definir metadados ou uma miniatura, faça isso usando IWICBitmapFrameEncode normalmente.
  3. QueryInterface para o IWICPlanarBitmapFrameEncode.
  4. Defina os dados de pixel YCbCr usando IWICPlanarBitmapFrameEncode::WriteSource ou IWICPlanarBitmapFrameEncode::WritePixels. Ao contrário das contrapartes IWICBitmapFrameEncode , você fornece a esses métodos uma matriz de IWICBitmapSource ou WICBitmapPlane que contêm os planos de pixel YCbCr .
  5. Quando terminar, chame IWICBitmapFrameEncode::Commit.

Decodificação de dados de pixel YCbCr em Windows 10

A partir Windows 10 build 1507, Direct2D fornece ID2D1ImageSourceFromWic, uma maneira mais simples de decodificar JPEGs em Direct2D ao aproveitar as otimizações YCbCr. ID2D1ImageSourceFromWic executa automaticamente todas as verificações de funcionalidade YCbCr necessárias para você; ele usa o codepath otimizado quando possível e usa um fallback caso contrário. Ele também habilita novas otimizações, como apenas o cache de sub-regiões da imagem que são necessárias por vez.

Para obter mais informações sobre como usar ID2D1ImageSourceFromWic, consulte o exemplo do SDK de Ajuste de Foto do Direct2D.