更新了wasm 支持了渐进式jpg

This commit is contained in:
kura 2025-11-28 11:05:01 +08:00
parent 408a1cd8d5
commit f9d6e45dc7
15 changed files with 55 additions and 4779 deletions

BIN
.DS_Store vendored

Binary file not shown.

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
output/.DS_Store
output/output/.DS_Store
output/output/convert_image_to_webp(H5原始胶水代码).js
output/output/readme.md
output/output/utils(小程序兼容代码).js
output/output/256MB/convert_image_to_webp.js
output/output/512MB/convert_image_to_webp.wasm

View File

@ -3,6 +3,7 @@
"*.sdp": "xml",
"*.json": "jsonc",
"vector": "cpp",
"type_traits": "cpp"
"type_traits": "cpp",
"__config": "cpp"
}
}

View File

@ -1,10 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#include "stb_image_resize.h"
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
extern "C" {
@ -14,7 +16,7 @@ extern "C" {
unsigned char *decoded_data = nullptr;
if (!is_original) {
decoded_data = stbi_load_from_memory(input_data, input_size, &width, &height, &channels, preserve_alpha ? 4 : 3); // 根据是否保留 alpha 通道加载
decoded_data = stbi_load_from_memory(input_data, input_size, &width, &height, &channels, 0); // 0自动判断是否保留 alpha 通道加载
} else {
decoded_data = (unsigned char *)input_data;
// 当使用原始数据时需要确保width和height已设置
@ -25,6 +27,7 @@ extern "C" {
// 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数)
if (!decoded_data) {
// printf("stbi_failure_reason: %s\n", stbi_failure_reason());
return nullptr; // 图像解码失败
}
@ -32,7 +35,7 @@ extern "C" {
unsigned char *resized_data = decoded_data;
if (target_width > 0 && target_height > 0) {
// 分配用于存储调整大小后图像的缓冲区
resized_data = (unsigned char *)malloc(target_width * target_height * (preserve_alpha ? 4 : 3)); // 根据是否保留 alpha 通道调整缓冲区大小
resized_data = (unsigned char *)malloc(target_width * target_height * channels); // 根据是否保留 alpha 通道调整缓冲区大小
if (!resized_data) {
// 内存分配失败的处理
@ -43,7 +46,7 @@ extern "C" {
// 使用 stb_image_resize 调整图像大小
int result = stbir_resize_uint8(decoded_data, width, height, 0,
resized_data, target_width, target_height, 0, preserve_alpha ? 4 : 3);
resized_data, target_width, target_height, 0, channels);
if (!result) {
// 如果调整大小失败,释放已分配的内存
@ -59,9 +62,9 @@ extern "C" {
// 使用 libwebp 的有损编码函数WebPEncodeRGB将 RGB 图像编码为 WebP
unsigned char *webp_output = NULL;
if (preserve_alpha) {
if (channels == 4) {
*output_size = WebPEncodeRGBA(resized_data, target_width, target_height, target_width * 4, quality_factor, &webp_output);
} else {
} else if (channels == 3) {
*output_size = WebPEncodeRGB(resized_data, target_width, target_height, target_width * 3, quality_factor, &webp_output);
}

File diff suppressed because one or more lines are too long

BIN
output/convert_image_to_webp.wasm Executable file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#include "stb_image_resize.h"
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
extern "C" {
@ -25,6 +26,7 @@ extern "C" {
// 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数)
if (!decoded_data) {
printf("stbi_failure_reason: %s\n", stbi_failure_reason());
return nullptr; // 图像解码失败
}

View File

@ -1111,79 +1111,16 @@ function dbg(...args) {
var _emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num);
var getHeapMax = () =>
// Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate
// full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side
// for any code that deals with heap sizes, which would require special
// casing all heap size related code to treat 0 specially.
2147483648;
HEAPU8.length;
var growMemory = (size) => {
var b = wasmMemory.buffer;
var pages = (size - b.byteLength + 65535) / 65536;
try {
// round size grow request up to wasm page size (fixed 64KB per spec)
wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size
updateMemoryViews();
return 1 /*success*/;
} catch(e) {
err(`growMemory: Attempted to grow heap from ${b.byteLength} bytes to ${size} bytes, but got error: ${e}`);
}
// implicit 0 return to save code size (caller will cast "undefined" into 0
// anyhow)
var abortOnCannotGrowMemory = (requestedSize) => {
abort(`Cannot enlarge memory arrays to size ${requestedSize} bytes (OOM). Either (1) compile with -sINITIAL_MEMORY=X with X higher than the current value ${HEAP8.length}, (2) compile with -sALLOW_MEMORY_GROWTH which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -sABORTING_MALLOC=0`);
};
var _emscripten_resize_heap = (requestedSize) => {
var oldSize = HEAPU8.length;
// With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
requestedSize >>>= 0;
// With multithreaded builds, races can happen (another thread might increase the size
// in between), so return a failure, and let the caller retry.
assert(requestedSize > oldSize);
// Memory resize rules:
// 1. Always increase heap size to at least the requested size, rounded up
// to next page multiple.
// 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap
// geometrically: increase the heap size according to
// MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most
// overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB).
// 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap
// linearly: increase the heap size by at least
// MEMORY_GROWTH_LINEAR_STEP bytes.
// 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by
// MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest
// 4. If we were unable to allocate as much memory, it may be due to
// over-eager decision to excessively reserve due to (3) above.
// Hence if an allocation fails, cut down on the amount of excess
// growth, in an attempt to succeed to perform a smaller allocation.
// A limit is set for how much we can grow. We should not exceed that
// (the wasm binary specifies it, so if we tried, we'd fail anyhow).
var maxHeapSize = getHeapMax();
if (requestedSize > maxHeapSize) {
err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`);
return false;
}
var alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple;
// Loop through potential heap size increases. If we attempt a too eager
// reservation that fails, cut down on the attempted size and reserve a
// smaller bump instead. (max 3 times, chosen somewhat arbitrarily)
for (var cutDown = 1; cutDown <= 4; cutDown *= 2) {
var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); // ensure geometric growth
// but limit overreserving (default to capping at +96MB overgrowth at most)
overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 );
var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536));
var replacement = growMemory(newSize);
if (replacement) {
return true;
}
}
err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`);
return false;
abortOnCannotGrowMemory(requestedSize);
};
var SYSCALLS = {
@ -1436,8 +1373,8 @@ var wasmImports = {
};
var wasmExports = createWasm();
var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors');
var _malloc = Module['_malloc'] = createExportWrapper('malloc');
var _free = Module['_free'] = createExportWrapper('free');
var _malloc = Module['_malloc'] = createExportWrapper('malloc');
var _convert_image_to_webp = Module['_convert_image_to_webp'] = createExportWrapper('convert_image_to_webp');
var _fflush = createExportWrapper('fflush');
var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])();
@ -1469,6 +1406,7 @@ var missingLibrarySymbols = [
'convertU32PairToI53',
'zeroMemory',
'exitJS',
'growMemory',
'isLeapYear',
'ydayFromDate',
'arraySum',
@ -1663,7 +1601,7 @@ var unexportedSymbols = [
'convertI32PairToI53Checked',
'ptrToString',
'getHeapMax',
'growMemory',
'abortOnCannotGrowMemory',
'ENV',
'MONTH_DAYS_REGULAR',
'MONTH_DAYS_LEAP',

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -13,10 +13,10 @@
<input type="file" id="imageUpload" accept="image/*" onchange="handleFileUpload(event)">
<br>
<label for="width">宽度:</label>
<input type="number" id="width" value="0">
<input type="number" id="width" value="1920">
<br>
<label for="height">高度:</label>
<input type="number" id="height" value="0">
<input type="number" id="height" value="1080">
<br>
<label for="quality">质量:</label>
<input type="number" id="quality" value="50" min="0" max="100">
@ -45,7 +45,7 @@
return btoa(binary); // 使用 JavaScript 的 btoa() 函数
}
var fileurl = './image.bin'
var fileurl = './img1.jpg'
// 初始化 WebAssembly 模块
var Module = {
onRuntimeInitialized: function () {
@ -102,11 +102,11 @@
* @param {number} qualityFactor - 压缩质量 (0 - 100)
*/
function decodeToWebpp(buffer, targetWidth = 0, targetHeight = 0, qualityFactor = 50) {
setTimeout(() => {
decodeItbyworker(buffer);
// setTimeout(() => {
// decodeItbyworker(buffer);
}, 1000);
return
// }, 1000);
// return
// 显示原始图片大小
let size = (buffer.byteLength / 1024).toFixed(2);
@ -119,7 +119,9 @@
// 设置目标宽高和质量因子
var inputDataPtr = Module._malloc(inputData.length);
Module.HEAPU8.set(inputData, inputDataPtr);
var webpPtr = Module._convert_image_to_webp(inputDataPtr, inputData.length, targetWidth, targetHeight, qualityFactor, outputSizePtr);
var webpPtr = Module._convert_image_to_webp(
inputDataPtr, inputData.length, 0,0,targetWidth, targetHeight, qualityFactor, outputSizePtr,0,0
);
// 调用 WebAssembly 函数进行图像转换,返回 WebP 数据指针
@ -142,16 +144,16 @@
Module._free(webpPtr);
Module._free(inputDataPtr);
}
var worker = new Worker('./worker.js');
worker.onmessage = function (e) {
console.log(e.data);
switch(e.data.type){
case 'webp':
let img = document.getElementById("webpImage");
img.src = "data:image/webp;base64," + arrayBufferToBase64(e.data.webpData);
break;
}
}
// var worker = new Worker('./worker.js');
// worker.onmessage = function (e) {
// console.log(e.data);
// switch(e.data.type){
// case 'webp':
// let img = document.getElementById("webpImage");
// img.src = "data:image/webp;base64," + arrayBufferToBase64(e.data.webpData);
// break;
// }
// }
function decodeItbyworker(buffer, width=0, height=0, quality=50) {
worker.postMessage({
buffer,

Binary file not shown.

4
生成
View File

@ -1,6 +1,6 @@
# 调试模式
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convert_image_to_webp.cpp stb_image.c libwebp.a libsharpyuv.a libwebpdecoder.a libwebpdemux.a libwebpmux.a -o ./test/convert_image_to_webp.js -g -s WASM=1 -s INITIAL_MEMORY=20MB -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS="['_free','_malloc','_convert_image_to_webp']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "getValue"]' -gsource-map
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convert_image_to_webp.cpp libwebp.a libsharpyuv.a libwebpdecoder.a libwebpdemux.a libwebpmux.a -o ./test/convert_image_to_webp.js -g -s WASM=1 -s INITIAL_MEMORY=256MB -s EXPORTED_FUNCTIONS="['_free','_malloc','_convert_image_to_webp']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "getValue"]' -gsource-map
# 优化后的命令
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convert_image_to_webp.cpp stb_image.c libwebp.a libsharpyuv.a -o ./output/convert_image_to_webp.js -s WASM=1 -s NO_FILESYSTEM=1 -s INITIAL_MEMORY=512MB -s EXPORTED_FUNCTIONS="['_free','_malloc','_convert_image_to_webp']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "getValue"]' -O2
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convert_image_to_webp.cpp libwebp.a libsharpyuv.a -o ./output/convert_image_to_webp.js -s WASM=1 -s NO_FILESYSTEM=1 -s INITIAL_MEMORY=64MB -s EXPORTED_FUNCTIONS="['_free','_malloc','_convert_image_to_webp']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "getValue"]' -O2