Delta-PNG-Ausgabeformat

API-Schlüssel abrufen

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:

Original

778 × 639 px

Normale PNG

409.048 Byte

Delta-PNG

110.904 Byte
73% Einsparungen.

Selbst in diesem haarzentrischen Beispiel (was für das Delta-PNG-Format den ungünstigsten Fall darstellt) sind die Einsparungen erheblich: 73%

Decodierung der Delta-PNG

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.

Einschränkungen

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
API-Schlüssel abrufen