Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a JS WebP encoder/decoder and a PHP WebP decoder #6168

Open
wants to merge 8 commits into
base: 6.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion ts/WoltLabSuite/Core/Image/ExifUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* @woltlabExcludeBundle tiny
*/

import { parseWebPFromBuffer } from "./WebP";

const Tag = {
SOI: 0xd8, // Start of image
APP0: 0xe0, // JFIF tag
Expand Down Expand Up @@ -70,6 +72,20 @@ async function blobToUint8(blob: Blob | File): Promise<Uint8Array> {
});
}

export async function getExifBytesFromWebp(blob: Blob | File): Promise<Exif> {
if (!((blob as any) instanceof Blob) && !(blob instanceof File)) {
throw new TypeError("The argument must be a Blob or a File");
}

const webp = parseWebPFromBuffer(await blob.arrayBuffer());
const exif = webp?.getExifData();
if (exif === undefined) {
return new Uint8Array(0);
}

return exif;
}

/**
* Extracts the EXIF / XMP sections of a JPEG blob.
*/
Expand Down Expand Up @@ -157,8 +173,28 @@ export async function removeExifData(blob: Blob | File): Promise<Blob> {
return new Blob([result], { type: blob.type });
}

export async function setWebpExifData(blob: Blob, exif: Exif): Promise<Blob> {
const webp = parseWebPFromBuffer(await blob.arrayBuffer());
if (webp === undefined) {
return blob;
}

let webpWithExif: Uint8Array;
try {
webpWithExif = webp.exportWithExif(exif);
} catch (e) {
if (window.ENABLE_DEBUG_MODE) {
console.error(e);
}

throw e;
}

return new Blob([webpWithExif], { type: blob.type });
}

/**
* Overrides the APP1 (EXIF / XMP) sections of a JPEG blob with the given data.
* Overrides the APP1 (EXIF / XMP) sections of a JPEG or WebP blob with the given data.
*/
export async function setExifData(blob: Blob, exif: Exif): Promise<Blob> {
blob = await removeExifData(blob);
Expand Down
10 changes: 8 additions & 2 deletions ts/WoltLabSuite/Core/Image/Resizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@ class ImageResizer {

let blob = await pica.toBlob(data.image, fileType, quality);

if (fileType === "image/jpeg" && typeof data.exif !== "undefined") {
blob = await ExifUtil.setExifData(blob, data.exif);
if (typeof data.exif !== "undefined") {
if (fileType === "image/jpeg") {
blob = await ExifUtil.setExifData(blob, data.exif);
} else if (fileType === "image/webp") {
blob = await ExifUtil.setWebpExifData(blob, data.exif);
}
}

return FileUtil.blobToFile(blob, basename![1]);
Expand All @@ -105,6 +109,8 @@ class ImageResizer {

// Strip EXIF data
fileData = await ExifUtil.removeExifData(fileData);
} else if (file.type === "image/webp") {
exifBytes = ExifUtil.getExifBytesFromWebp(file);
}

const imageLoader: Promise<HTMLImageElement> = new Promise((resolve, reject) => {
Expand Down
Loading
Loading