Stai effettuando la migrazione da un altro provider? Check out our migration guide
Il formato di output Delta PNG può far risparmiare molta latenza e larghezza di banda, cosa particolarmente utile quando la latenza e la larghezza di banda sono un fattore da considerare, come le app mobili.
Richiede il caricamento sul client dei pixel nell'immagine originale, e l'applicazione di Delta PNG all'immagine originale per la produzione dell'immagine risultato.
Un esempio:
778 ×
639 px
409.048 byte
110.904 byte
Anche in questo esempio centrato sui capelli (peggior caso per il formato Delta PNG), i risparmi sono notevoli: 73%
Un file Delta PNG è un file PNG regolare e può essere letto da qualsiasi libreria software in grado di leggere file PNG. L'unica differenza rispetto a un risultato PNG regolare consiste nei valori di pixel. Lo sfondo è codificato come nero trasparente 0x00000000 e il primo piano come bianco trasparente 0x00FFFFFF. I pixel parzialmente trasparenti hanno i propri valori effettivi di colore.
| Tipo di pixel | Originale | PNG regolare | Delta PNG | Fonte di output |
|---|---|---|---|---|
| Primo piano |
0xFFrrggbb
|
0xFFrrggbb
|
0x00FFFFFF
|
Originale |
| Sfondo |
0xFFrrggbb
|
0x00000000
|
0x00000000
|
Delta PNG |
| Bordo |
0xFFrrggbb
|
0x80rrggbb
|
0x80rrggbb
|
Delta PNG |
Ciò significa che quando decodifichi i valori di pixel Delta PNG, devi solamente prendere il valore di pixel attuale dall'Originale quando vedi il bianco trasparente 0x00FFFFFF. Gli altri pixel hanno lo stesso valore nel formato PNG regolare.
Ecco un esempio di codice TypeScript per la decodifica del formato Delta PNG:
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;
}
Per maggiori informazioni sulle operazioni con immagini e dati di pixel in JavaScript, consulta l'eccellente guida Pixel manipulation with canvas tutorial sulla Mozilla Developer Network.
La tua libreria di caricamento immagini deve essere in grado di preservare i valori di pixel anche per i pixel interamente trasparenti, come funziona normalmente.
Tuttavia, se usi per esempio Python e la nota libreria OpenCV, devi usare il flag cv2.IMREAD_UNCHANGED e caricare l'immagine nel seguente modo: cv2.imread(path, cv2.IMREAD_UNCHANGED). In caso contrario OpenCV altera i valori di pixel effettivi dei pixel completamente trasparenti.
Purtroppo OpenCV non applica nessuna informazione di rotazione conservata nell'immagine quando usi il flag. Per questo motivo restituiamo l'intestazione X-Input-Orientation per consentirti di applicare l'orientamento corretto all'immagine in tale caso.
Il seguente è un esempio di codice Python+OpenCV per applicare l'orientamento:
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