Partilhar via


Formato BC7

O formato BC7 é um formato de compressão de textura usado para compressão de alta qualidade de dados RGB e RGBA.

Para obter informações sobre os modos de bloco do formato BC7, consulte de referência do modo de formato BC7.

Sobre BC7/DXGI_FORMAT_BC7

BC7 é especificado pelos seguintes valores de enumeração DXGI_FORMAT:

  • DXGI_FORMAT_BC7_TYPELESS.
  • DXGI_FORMAT_BC7_UNORM.
  • DXGI_FORMAT_BC7_UNORM_SRGB.

O formato BC7 pode ser usado para recursos de textura Texture2D (incluindo matrizes), Texture3D ou TextureCube (incluindo matrizes). Da mesma forma, esse formato se aplica a qualquer superfície de mapa MIP associada a esses recursos.

BC7 usa um tamanho de bloco fixo de 16 bytes (128 bits) e um tamanho de bloco fixo de 4x4 texels. Tal como nos formatos BC anteriores, as imagens de textura maiores do que o tamanho de mosaico suportado (4x4) são comprimidas utilizando vários blocos. Essa identidade de endereçamento também se aplica a imagens tridimensionais e mapas MIP, cubemaps e matrizes de textura. Todos os blocos de imagem devem ter o mesmo formato.

O BC7 comprime imagens de dados de ponto fixo de três canais (RGB) e quatro canais (RGBA). Normalmente, os dados de origem são de 8 bits por componente de cor (canal), embora o formato seja capaz de codificar dados de origem com bits mais altos por componente de cor. Todos os blocos de imagem devem ter o mesmo formato.

O decodificador BC7 executa a descompressão antes que a filtragem de textura seja aplicada.

O hardware de descompressão BC7 deve ser preciso em bits; ou seja, o hardware deve retornar resultados idênticos aos resultados retornados pelo decodificador descrito neste documento.

Implementação BC7

Uma implementação BC7 pode especificar um dos 8 modos, com o modo especificado no bit menos significativo do bloco de 16 bytes (128 bits). O modo é codificado por zero ou mais bits com um valor de 0 seguido por um 1.

Um bloco BC7 pode conter vários pares de pontos finais. Para os fins desta documentação, o conjunto de índices que correspondem a um par de pontos finais pode ser referido como um "subconjunto". Além disso, em alguns modos de bloco, a representação do ponto de extremidade é codificada em um formato que -- novamente, para os fins desta documentação -- deve ser referido como "RGBP", onde o bit "P" representa um bit menos significativo compartilhado para os componentes de cor do ponto de extremidade. Por exemplo, se a representação do ponto de extremidade para o formato for "RGB 5.5.5.1", então o ponto de extremidade será interpretado como um valor RGB 6.6.6, onde o estado do P-bit define o bit menos significativo de cada componente. Da mesma forma, para dados de origem com um canal alfa, se a representação para o formato for "RGBAP 5.5.5.5.1", então o ponto de extremidade é interpretado como RGBA 6.6.6.6. Dependendo do modo de bloco, você pode especificar o bit menos significativo compartilhado para ambos os pontos de extremidade de um subconjunto individualmente (2 P-bits por subconjunto) ou compartilhado entre os pontos de extremidade de um subconjunto (1 P-bit por subconjunto).

Para blocos BC7 que não codificam explicitamente o componente alfa, um bloco BC7 consiste em bits de modo, bits de partição, pontos de extremidade compactados, índices compactados e um P-bit opcional. Nesses blocos, os pontos de extremidade têm uma representação somente RGB e o componente alfa é decodificado como 1.0 para todos os texels nos dados de origem.

Para blocos BC7 que combinaram componentes de cor e alfa, um bloco consiste em bits de modo, pontos de extremidade compactados, índices compactados e bits de partição opcionais e um P-bit. Nesses blocos, as cores do ponto de extremidade são expressas no formato RGBA e os valores dos componentes alfa são interpolados ao lado dos valores dos componentes de cor.

Para blocos BC7 que têm componentes de cor e alfa separados, um bloco consiste em bits de modo, bits de rotação, pontos de extremidade compactados, índices compactados e um bit seletor de índice opcional. Estes blocos têm um vetor RGB efetivo [R, G, B] e um canal alfa escalar [A] codificado separadamente.

A tabela a seguir lista os componentes de cada tipo de bloco.

O bloco BC7 contém... bits de modo bits de rotação bit seletor de índice bits de partição Pontos finais comprimidos P-bit índices comprimidos
apenas componentes de cor Necessário N/A N/A Necessário Necessário opcional Necessário
cor + alfa combinado Necessário N/A N/A opcional Necessário opcional Necessário
cor e alfa separados Necessário Necessário opcional N/A Necessário N/A Necessário

 

BC7 define uma paleta de cores em uma linha aproximada entre dois pontos de extremidade. O valor do modo determina o número de pares de pontos finais interpolantes por bloco. BC7 armazena um índice de paleta por texel.

Para cada subconjunto de índices que corresponde a um par de pontos de extremidade, o codificador corrige o estado de um bit dos dados de índice compactados para esse subconjunto. Ele faz isso escolhendo uma ordem de ponto final que permite que o índice para o índice "fix-up" designado defina seu bit mais significativo como 0 e que pode ser descartado, salvando um bit por subconjunto. Para modos de bloco com apenas um único subconjunto, o índice de correção é sempre o índice 0.

Decodificando o formato BC7

O pseudocódigo a seguir descreve as etapas para descompactar o pixel em (x,y) dado o bloco BC7 de 16 bytes.

decompress_bc7(x, y, block)
{
    mode = extract_mode(block);
    
    //decode partition data from explicit partition bits
    subset_index = 0;
    num_subsets = 1;
    
    if (mode.type == 0 OR == 1 OR == 2 OR == 3 OR == 7)
    {
        num_subsets = get_num_subsets(mode.type);
        partition_set_id = extract_partition_set_id(mode, block);
        subset_index = get_partition_index(num_subsets, partition_set_id, x, y);
    }
    
    //extract raw, compressed endpoint bits
    UINT8 endpoint_array[2 * num_subsets][4] = extract_endpoints(mode, block);
    
    //decode endpoint color and alpha for each subset
    fully_decode_endpoints(endpoint_array, mode, block);
    
    //endpoints are now complete.
    UINT8 endpoint_start[4] = endpoint_array[2 * subset_index];
    UINT8 endpoint_end[4]   = endpoint_array[2 * subset_index + 1];
        
    //Determine the palette index for this pixel
    alpha_index     = get_alpha_index(block, mode, x, y);
    alpha_bitcount  = get_alpha_bitcount(block, mode);
    color_index     = get_color_index(block, mode, x, y);
    color_bitcount  = get_color_bitcount(block, mode);

    //determine output
    UINT8 output[4];
    output.rgb = interpolate(endpoint_start.rgb, endpoint_end.rgb, color_index, color_bitcount);
    output.a   = interpolate(endpoint_start.a,   endpoint_end.a,   alpha_index, alpha_bitcount);
    
    if (mode.type == 4 OR == 5)
    {
        //Decode the 2 color rotation bits as follows:
        // 00 – Block format is Scalar(A) Vector(RGB) - no swapping
        // 01 – Block format is Scalar(R) Vector(AGB) - swap A and R
        // 10 – Block format is Scalar(G) Vector(RAB) - swap A and G
        // 11 - Block format is Scalar(B) Vector(RGA) - swap A and B
        rotation = extract_rot_bits(mode, block);
        output = swap_channels(output, rotation);
    }
    
}

O pseudocódigo a seguir descreve as etapas para decodificar totalmente a cor do ponto de extremidade e os componentes alfa para cada subconjunto dado um bloco BC7 de 16 bytes.

fully_decode_endpoints(endpoint_array, mode, block)
{
    //first handle modes that have P-bits
    if (mode.type == 0 OR == 1 OR == 3 OR == 6 OR == 7)
    {
        for each endpoint i
        {
            //component-wise left-shift
            endpoint_array[i].rgba = endpoint_array[i].rgba << 1;
        }
        
        //if P-bit is shared
        if (mode.type == 1) 
        {
            pbit_zero = extract_pbit_zero(mode, block);
            pbit_one = extract_pbit_one(mode, block);
            
            //rgb component-wise insert pbits
            endpoint_array[0].rgb |= pbit_zero;
            endpoint_array[1].rgb |= pbit_zero;
            endpoint_array[2].rgb |= pbit_one;
            endpoint_array[3].rgb |= pbit_one;  
        }
        else //unique P-bit per endpoint
        {  
            pbit_array = extract_pbit_array(mode, block);
            for each endpoint i
            {
                endpoint_array[i].rgba |= pbit_array[i];
            }
        }
    }

    for each endpoint i
    {
        // Color_component_precision & alpha_component_precision includes pbit
        // left shift endpoint components so that their MSB lies in bit 7
        endpoint_array[i].rgb = endpoint_array[i].rgb << (8 - color_component_precision(mode));
        endpoint_array[i].a = endpoint_array[i].a << (8 - alpha_component_precision(mode));

        // Replicate each component's MSB into the LSBs revealed by the left-shift operation above
        endpoint_array[i].rgb = endpoint_array[i].rgb | (endpoint_array[i].rgb >> color_component_precision(mode));
        endpoint_array[i].a = endpoint_array[i].a | (endpoint_array[i].a >> alpha_component_precision(mode));
    }
        
    //If this mode does not explicitly define the alpha component
    //set alpha equal to 1.0
    if (mode.type == 0 OR == 1 OR == 2 OR == 3)
    {
        for each endpoint i
        {
            endpoint_array[i].a = 255; //i.e. alpha = 1.0f
        }
    }
}

Para gerar cada componente interpolado para cada subconjunto, use o seguinte algoritmo: deixe "c" ser o componente a ser gerado; Seja "e0" o componente do ponto final 0 do subconjunto; e que "e1" seja o componente do ponto final 1 do subconjunto.

UINT16 aWeights2[] = {0, 21, 43, 64};
UINT16 aWeights3[] = {0, 9, 18, 27, 37, 46, 55, 64};
UINT16 aWeights4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};

UINT8 interpolate(UINT8 e0, UINT8 e1, UINT8 index, UINT8 indexprecision)
{
    if(indexprecision == 2)
        return (UINT8) (((64 - aWeights2[index])*UINT16(e0) + aWeights2[index]*UINT16(e1) + 32) >> 6);
    else if(indexprecision == 3)
        return (UINT8) (((64 - aWeights3[index])*UINT16(e0) + aWeights3[index]*UINT16(e1) + 32) >> 6);
    else // indexprecision == 4
        return (UINT8) (((64 - aWeights4[index])*UINT16(e0) + aWeights4[index]*UINT16(e1) + 32) >> 6);
}

O pseudocódigo a seguir ilustra como extrair índices e contagens de bits para componentes de cor e alfa. Blocos com cor separada e alfa também têm dois conjuntos de dados de índice: um para o canal vetorial e outro para o canal escalar. Para o Modo 4, esses índices são de larguras diferentes (2 ou 3 bits), e há um seletor de um bit que especifica se os dados vetoriais ou escalares usam os índices de 3 bits. (Extrair a contagem de bits alfa é semelhante a extrair a contagem de bits de cor, mas com comportamento inverso baseado no idxMode bit.)

bitcount get_color_bitcount(block, mode)
{
    if (mode.type == 0 OR == 1)
        return 3;
    
    if (mode.type == 2 OR == 3 OR == 5 OR == 7)
        return 2;
    
    if (mode.type == 6)
        return 4;
        
    //The only remaining case is Mode 4 with 1-bit index selector
    idxMode = extract_idxMode(block);
    if (idxMode == 0)
        return 2;
    else
        return 3;
}

Compressão de blocos de textura no Direct3D 11