Compartilhar via


Formato BC7

O formato BC7 é um formato de compactação de textura usado para compactaçã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 quaisquer superfícies de mapa MIP associadas a esses recursos.

BC7 usa um tamanho de bloco fixo de 16 bytes (128 bits) e um tamanho de bloco fixo de texels 4x4. Assim como acontece com os formatos bc anteriores, imagens de textura maiores que o tamanho do bloco com suporte (4x4) são compactadas usando vários blocos. Essa identidade de endereçamento também se aplica a imagens tridimensionais e MIP-mapas, cubemaps e matrizes de textura. Todos os blocos de imagem devem ter o mesmo formato.

BC7 compacta imagens de dados de ponto fixo de três canais (RGB) e RGBA (quatro canais). 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 descompactação antes da filtragem de textura ser aplicada.

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

Implementação do 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 ponto de extremidade. Para fins desta documentação, o conjunto de índices que correspondem a um par de pontos de extremidade pode ser chamado de "subconjunto". Além disso, em alguns modos de bloco, a representação do ponto de extremidade é codificada em uma forma que, novamente, para fins desta documentação, deve ser chamada de "RGBP", em que 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", o ponto de extremidade será interpretado como um valor RGB 6.6.6, em que o estado do bit P define o bit menos significativo de cada componente. Da mesma forma, para dados de origem com um canal alfa, se a representação do formato for "RGBAP 5.5.5.5.1", o ponto de extremidade será 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 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 têm componentes alfa e de cor combinados, 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 de componente alfa são interpolados junto com os valores de componente de cor.

Para blocos BC7 que têm componentes alfa e de cor separados, um bloco consiste em bits de modo, bits de rotação, pontos de extremidade compactados, índices compactados e um bit de seletor de índice opcional. Esses blocos têm um vetor RGB efetivo [R, G, B] e um canal alfa escalar [A] codificados 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 do seletor de índice bits de partição pontos de extremidade compactados P-bit índices compactados
somente 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 ponto de extremidade de interpolação 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 de extremidade que permite que o índice para o índice designado de "correção" defina seu bit mais significativo como 0 e, em seguida, pode ser descartado, salvando um bit por subconjunto. Para modos de bloco com apenas um único subconjunto, o índice de correção é sempre índice 0.

Decodificação do 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; que "e0" seja esse componente do ponto de extremidade 0 do subconjunto; e deixe "e1" ser esse componente do ponto de extremidade 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 alfa e de cor. Blocos com cor e alfa separados também têm dois conjuntos de dados de índice: um para o canal de vetor 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 escalares ou vetoriais usam os índices de 3 bits. (Extrair a contagem de bits alfa é semelhante à extração da contagem de bits de cores, mas com comportamento inverso com base 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;
}

compactação de bloco de textura no direct3D 11