Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions plugin/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@
"message": "Compress Images",
"description": "Label for checkbox and value to compress images to a certain size"
},
"__MSG_label_Compress_Images_Animated__": {
"message": "Compress Animated Images to single frame (GIF, APNG, WebP)",
"description": "Label for animated compression checkbox"
},
"__MSG_label_Compress_Images_JPG_Cover__": {
"message": "Restrict Cover Image to JPEG",
"description": "Label for JPEG cover restriction checkbox"
Expand Down
35 changes: 34 additions & 1 deletion plugin/js/ImageCollector.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,42 @@ class ImageCollector {
});
}

isAnimatedImage(imageInfo) {
const animatedTypes = ["image/gif", "image/png", "image/webp"];

if (animatedTypes.includes(imageInfo.mediaType))
{
const maxScanBytes = 65536;
const buffer = imageInfo.arraybuffer;
const scanLimit = Math.min(buffer.byteLength, maxScanBytes);
const view = new DataView(buffer, 0, scanLimit);
const limit = Math.max(0, view.byteLength - 4);

try {
for (let i = 0; i < limit; i++) {
const current32 = view.getUint32(i, false);

if ((imageInfo.mediaType == "image/webp" && (current32 === 0x414E494D || current32 === 0x414E4D46))
|| (imageInfo.mediaType == "image/png" && current32 === 0x6163544C)
|| (imageInfo.mediaType == "image/gif" && (current32 >>> 8) === 0x21F904)
) return true;
}
}
catch (e) {
console.error("Binary animation detection exception:", e);
}
}
return false;
}

skipAnimated(imageInfo) {
if (this.userPreferences.compressImagesAnimated.value) return false;
return this.isAnimatedImage(imageInfo);
}

runCompression(imageInfo, img) {
return new Promise((resolve, reject) => {
if (this.userPreferences.compressImages.value && imageInfo.mediaType != "image/gif")
if (this.userPreferences.compressImages.value && !this.skipAnimated(imageInfo))
{
let outputType = "image/jpeg";
switch (this.userPreferences.compressImagesType.value) {
Expand Down
1 change: 1 addition & 0 deletions plugin/js/UserPreferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class UserPreferences { // eslint-disable-line no-unused-vars
this.overrideMinimumDelay = this.addPreference("overrideMinimumDelay", "overrideMinimumDelayCheckbox", false);
this.skipImages = this.addPreference("skipImages", "skipImagesCheckbox", false);
this.compressImages = this.addPreference("compressImages", "compressImagesCheckbox", false);
this.compressImagesAnimated = this.addPreference("compressImagesAnimated", "compressImagesAnimatedCheckbox", true);
this.compressImagesJpgCover = this.addPreference("compressImagesJpgCover", "compressImagesJpgCoverCheckbox", false);
this.compressImagesType = this.addPreference("compressImagesType", "compressImagesType", "jpg");
this.compressImagesMaxResolution = this.addPreference("compressImagesMaxResolution", "compressImagesMaxResolutionTag", "1080");
Expand Down
8 changes: 7 additions & 1 deletion plugin/popup.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
Expand Down Expand Up @@ -387,6 +387,12 @@ <h3>Instructions</h3>
</td>
<td>__MSG_label_Compress_Images_JPG_Cover__</td>
</tr>
<tr>
<td>
<input id="compressImagesAnimatedCheckbox" type="checkbox" name="compressImagesAnimated" value="true">
</td>
<td>__MSG_label_Compress_Images_Animated__</td>
</tr>
</table>
</td>
</tr>
Expand Down
Loading