Formato de saída PNG Delta

Obter a chave de API

Está migrando de outro fornecedor? Check out our migration guide

O formato de saída PNG Delta pode economizar bastante latência e largura de banda. É útil principalmente em cenários onde a latência e a largura de banda são críticas, como no caso dos aplicativos móveis.

O formato requer que os pixels na imagem original sejam carregados no dispositivo cliente. A partir daí, o PNG Delta é aplicado à imagem original para produzir a imagem resultante.

Exemplo:

Original

778 × 639 px

PNG normal

409.048 bytes

PNG Delta

110.904 bytes
Economia de 73%.

Mesmo neste exemplo, onde o foco são os cabelos (o pior caso para o formato PNG Delta), a economia é substancial: 73%

O que é o PNG Delta

PNG Delta é apenas um arquivo PNG normal e pode ser lido por qualquer biblioteca de software capaz de ler PNGs. A única diferença, quando comparado a um resultado PNG normal, está nos valores dos pixels. O fundo é codificado como preto transparente 0x00000000 e o primeiro plano como branco transparente 0x00FFFFFF. Pixels parcialmente transparentes têm os valores reais das cores.

Tipo de pixel Original PNG normal PNG Delta Fonte de saída
Primeiro plano 0xFFrrggbb 0xFFrrggbb 0x00FFFFFF Original
Fundo 0xFFrrggbb 0x00000000 0x00000000 PNG Delta
Borda 0xFFrrggbb 0x80rrggbb 0x80rrggbb PNG Delta

Isso significa que, ao decodificar os valores de pixel no PNG Delta, é preciso buscar o valor real do pixel no Original sempre que um branco transparente 0x00FFFFFF é encontrado. Os outros pixels têm os mesmos valores de um formato PNG normal.

Este é um exemplo de código TypeScript para decodificar o formato PNG Delta:

export function decodeDeltaPngInPlace(originalPixels: Uint8Array, deltaPngPixels: Uint8Array): Uint8Array {
    const N = originalPixels.length / 4; // Array of RGBA values, div 4 to get number of pixels
    for (let i = 0; i < N; i++) {
        const i4 = i * 4;
        const alpha = deltaPngPixels[i4 + 3]; // JavaScript is RGBA, +3 to get alpha
        if (alpha == 0) {
            const r = deltaPngPixels[i4]; // JavaScript is RGBA, +0 to get red
            if (r == 0xFF) {
                // Transparent white => foreground => take values from original
                deltaPngPixels[i4] = originalPixels[i4];
                deltaPngPixels[i4 + 1] = originalPixels[i4 + 1];
                deltaPngPixels[i4 + 2] = originalPixels[i4 + 2];
                deltaPngPixels[i4 + 3] = originalPixels[i4 + 3];
            } // else transparent black => background => keep values
        } // else partially transparent => keep values
    }
    return deltaPngPixels;
}

Para obter mais informações sobre a operação em imagens e dados de pixel em JavaScript, consulte o excelente tutorial Pixel manipulation with canvas na Mozilla Developer Network.

Limitações

Sua biblioteca de carregamento de imagens precisa preservar os valores de pixels mesmo quando eles são totalmente transparentes, que é o modo usual de funcionamento.

Entretanto, se estiver usando Python e a conhecida biblioteca OpenCV, por exemplo, você deverá usar a flag cv2.IMREAD_UNCHANGED e carregar a imagem assim: cv2.imread(path, cv2.IMREAD_UNCHANGED). Caso contrário, OpenCV desconsidera os valores de pixel reais dos pixels totalmente transparentes.

Infelizmente, OpenCV não aplica nenhuma informação de rotação armazenada na imagem quando você usa essa flag. É por isso que retornamos o cabeçalho X-Input-Orientation para que você possa aplicar a orientação correta à imagem nest cenário.

Veja um exemplo de código Python+OpenCV para aplicar a orientação:

def apply_exif_rotation(im: np.ndarray, orientation: int) -> np.ndarray:
    # https://note.nkmk.me/en/python-opencv-numpy-rotate-flip/
    if 1 < orientation <= 8:
        if 2 == orientation:  # TOP-RIGHT, flip left-right, [1, 1] -> [-1, 1]
            im = cv2.flip(im, 1)
        elif 3 == orientation:  # BOTTOM-RIGHT, rotate 180
            im = cv2.rotate(im, cv2.ROTATE_180)
        elif 4 == orientation:  # BOTTOM-LEFT, flip up-down, [1, 1] -> [1, -1]
            im = cv2.flip(im, 0)
        elif 5 == orientation:  # LEFT-TOP, Rotate 90 and flip left-right
            im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE)
            im = cv2.flip(im, 1)
        elif 6 == orientation:  # RIGHT-TOP, Rotate 90
            im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE)
        elif 7 == orientation:  # RIGHT-BOTTOM,
            im = cv2.rotate(im, cv2.ROTATE_90_CLOCKWISE)
            im = cv2.flip(im, 0)
        else:  # 8 == orientation:  # LEFT-BOTTOM, Rotate 270
            im = cv2.rotate(im, cv2.ROTATE_90_COUNTERCLOCKWISE)
    return im
Obter a chave de API