Sie migrieren von einem anderen Anbieter? Check out our migration guide
Das Delta-PNG-Ausgabeformat kann sehr viel Latenz und Bandbreite einsparen und ist besonders in latenz- und bandbreitenkritischen Anwendungsbereichen wie mobilen Apps nützlich.
Dabei müssen die Pixel im Originalbild zum Client hochgeladen werden. Anschließend wird das Delta-PNG auf das Originalbild angewendet, um das Ergebnisbild zu erzeugen.
Beispiel:
778 × 639 px
409.048 Byte
110.904 Byte
Selbst in diesem haarzentrischen Beispiel (was für das Delta-PNG-Format den ungünstigsten Fall darstellt) sind die Einsparungen erheblich: 73%
Eine Delta-PNG ist lediglich eine normale PNG-Datei und kann von einer beliebigen Softwarebibliothek gelesen werden, die PNGs lesen kann. Der einzige Unterschied im Vergleich zu einem normalen PNG-Ergebnis besteht in den eigentlichen Pixelwerten. Der Hintergrund wird als transparentes Schwarz 0x00000000 und der Vordergrund als transparentes Weiß 0x00FFFFFF codiert. Teilweise transparente Pixel besitzen ihre tatsächlichen Farbwerte.
| Pixeltyp | Original | Normale PNG | Delta-PNG | Ausgabequelle |
|---|---|---|---|---|
| Vordergrund |
0xFFrrggbb
|
0xFFrrggbb
|
0x00FFFFFF
|
Original |
| Hintergrund |
0xFFrrggbb
|
0x00000000
|
0x00000000
|
Delta-PNG |
| Kante |
0xFFrrggbb
|
0x80rrggbb
|
0x80rrggbb
|
Delta-PNG |
Das bedeutet, dass Sie bei der Decodierung der Delta-PNG-Pixelwerte den eigentlichen Pixelwert aus dem Original verwenden müssen, wenn transparentes Weiß 0x00FFFFFF vorkommt. Die anderen Pixel haben dieselben Werte wie im normalen PNG-Format.
Hier ist ein Beispiel in TypeScript-Code zur Decodierung des Delta-PNG-Formats:
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;
}
Weitere Informationen über die Ausführung von Vorgängen an Bild- und Pixeldaten in JavaScript finden Sie diesem ausgezeichneten Tutorial zur Manipulation von Pixeln mit Canvas im Mozilla Developer Network.
Ihre Bildbibliothek muss in der Lage sein, die Pixelwerte selbst bei vollständig transparenten Pixel beizubehalten (normale Funktionsweise).
Wenn Sie jedoch z. B. Python und die bekannte OpenCV-Bibliothek verwenden, müssen Sie das Flag cv2.IMREAD_UNCHANGED verwenden und das Bild wie folgt laden: cv2.imread(path, cv2.IMREAD_UNCHANGED). Ansonsten zerstört OpenCV die tatsächlichen Pixelwerte der vollständig transparenten Pixel.
Leider wendet OpenCV keine im Bild gespeicherten Informationen an, wenn Sie dieses Flag benutzen. Deshalb geben wir die Kopfzeile X-Input-Orientation zurück, damit Sie unter diesen Umständen die korrekte Ausrichtung auf das Bild anwenden können.
Hier ist ein Beispiel für einen Python+OpenCV-Code zur Anwendung der Ausrichtung:
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