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:
778 × 639 px
409.048 bytes
110.904 bytes
Mesmo neste exemplo, onde o foco são os cabelos (o pior caso para o formato PNG Delta), a economia é substancial: 73%
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.
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