更新了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", "*.sdp": "xml",
"*.json": "jsonc", "*.json": "jsonc",
"vector": "cpp", "vector": "cpp",
"type_traits": "cpp" "type_traits": "cpp",
"__config": "cpp"
} }
} }

View File

@ -1,10 +1,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "webp/encode.h" // WebP 头文件,用于编码 WebP #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#define STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#include "stb_image_resize.h" #include "stb_image_resize.h"
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
extern "C" { extern "C" {
@ -14,7 +16,7 @@ extern "C" {
unsigned char *decoded_data = nullptr; unsigned char *decoded_data = nullptr;
if (!is_original) { 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 { } else {
decoded_data = (unsigned char *)input_data; decoded_data = (unsigned char *)input_data;
// 当使用原始数据时需要确保width和height已设置 // 当使用原始数据时需要确保width和height已设置
@ -25,6 +27,7 @@ extern "C" {
// 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数) // 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数)
if (!decoded_data) { if (!decoded_data) {
// printf("stbi_failure_reason: %s\n", stbi_failure_reason());
return nullptr; // 图像解码失败 return nullptr; // 图像解码失败
} }
@ -32,7 +35,7 @@ extern "C" {
unsigned char *resized_data = decoded_data; unsigned char *resized_data = decoded_data;
if (target_width > 0 && target_height > 0) { 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) { if (!resized_data) {
// 内存分配失败的处理 // 内存分配失败的处理
@ -43,7 +46,7 @@ extern "C" {
// 使用 stb_image_resize 调整图像大小 // 使用 stb_image_resize 调整图像大小
int result = stbir_resize_uint8(decoded_data, width, height, 0, 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) { if (!result) {
// 如果调整大小失败,释放已分配的内存 // 如果调整大小失败,释放已分配的内存
@ -59,9 +62,9 @@ extern "C" {
// 使用 libwebp 的有损编码函数WebPEncodeRGB将 RGB 图像编码为 WebP // 使用 libwebp 的有损编码函数WebPEncodeRGB将 RGB 图像编码为 WebP
unsigned char *webp_output = NULL; 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); *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); *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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "webp/encode.h" // WebP 头文件,用于编码 WebP #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#define STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image.h" // stb_image 头文件,用于解码 PNG/JPG
#include "stb_image_resize.h" #include "stb_image_resize.h"
#include "webp/encode.h" // WebP 头文件,用于编码 WebP
extern "C" { extern "C" {
@ -25,6 +26,7 @@ extern "C" {
// 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数) // 使用 stb_image 解码输入图像(根据 preserve_alpha 决定加载通道数)
if (!decoded_data) { if (!decoded_data) {
printf("stbi_failure_reason: %s\n", stbi_failure_reason());
return nullptr; // 图像解码失败 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 _emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num);
var getHeapMax = () => var getHeapMax = () =>
// Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate HEAPU8.length;
// 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;
var growMemory = (size) => { var abortOnCannotGrowMemory = (requestedSize) => {
var b = wasmMemory.buffer; 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 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 _emscripten_resize_heap = (requestedSize) => { var _emscripten_resize_heap = (requestedSize) => {
var oldSize = HEAPU8.length; var oldSize = HEAPU8.length;
// With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
requestedSize >>>= 0; requestedSize >>>= 0;
// With multithreaded builds, races can happen (another thread might increase the size abortOnCannotGrowMemory(requestedSize);
// 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;
}; };
var SYSCALLS = { var SYSCALLS = {
@ -1436,8 +1373,8 @@ var wasmImports = {
}; };
var wasmExports = createWasm(); var wasmExports = createWasm();
var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors'); var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors');
var _malloc = Module['_malloc'] = createExportWrapper('malloc');
var _free = Module['_free'] = createExportWrapper('free'); 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 _convert_image_to_webp = Module['_convert_image_to_webp'] = createExportWrapper('convert_image_to_webp');
var _fflush = createExportWrapper('fflush'); var _fflush = createExportWrapper('fflush');
var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])(); var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])();
@ -1469,6 +1406,7 @@ var missingLibrarySymbols = [
'convertU32PairToI53', 'convertU32PairToI53',
'zeroMemory', 'zeroMemory',
'exitJS', 'exitJS',
'growMemory',
'isLeapYear', 'isLeapYear',
'ydayFromDate', 'ydayFromDate',
'arraySum', 'arraySum',
@ -1663,7 +1601,7 @@ var unexportedSymbols = [
'convertI32PairToI53Checked', 'convertI32PairToI53Checked',
'ptrToString', 'ptrToString',
'getHeapMax', 'getHeapMax',
'growMemory', 'abortOnCannotGrowMemory',
'ENV', 'ENV',
'MONTH_DAYS_REGULAR', 'MONTH_DAYS_REGULAR',
'MONTH_DAYS_LEAP', '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)"> <input type="file" id="imageUpload" accept="image/*" onchange="handleFileUpload(event)">
<br> <br>
<label for="width">宽度:</label> <label for="width">宽度:</label>
<input type="number" id="width" value="0"> <input type="number" id="width" value="1920">
<br> <br>
<label for="height">高度:</label> <label for="height">高度:</label>
<input type="number" id="height" value="0"> <input type="number" id="height" value="1080">
<br> <br>
<label for="quality">质量:</label> <label for="quality">质量:</label>
<input type="number" id="quality" value="50" min="0" max="100"> <input type="number" id="quality" value="50" min="0" max="100">
@ -45,7 +45,7 @@
return btoa(binary); // 使用 JavaScript 的 btoa() 函数 return btoa(binary); // 使用 JavaScript 的 btoa() 函数
} }
var fileurl = './image.bin' var fileurl = './img1.jpg'
// 初始化 WebAssembly 模块 // 初始化 WebAssembly 模块
var Module = { var Module = {
onRuntimeInitialized: function () { onRuntimeInitialized: function () {
@ -102,11 +102,11 @@
* @param {number} qualityFactor - 压缩质量 (0 - 100) * @param {number} qualityFactor - 压缩质量 (0 - 100)
*/ */
function decodeToWebpp(buffer, targetWidth = 0, targetHeight = 0, qualityFactor = 50) { function decodeToWebpp(buffer, targetWidth = 0, targetHeight = 0, qualityFactor = 50) {
setTimeout(() => { // setTimeout(() => {
decodeItbyworker(buffer); // decodeItbyworker(buffer);
}, 1000); // }, 1000);
return // return
// 显示原始图片大小 // 显示原始图片大小
let size = (buffer.byteLength / 1024).toFixed(2); let size = (buffer.byteLength / 1024).toFixed(2);
@ -119,7 +119,9 @@
// 设置目标宽高和质量因子 // 设置目标宽高和质量因子
var inputDataPtr = Module._malloc(inputData.length); var inputDataPtr = Module._malloc(inputData.length);
Module.HEAPU8.set(inputData, inputDataPtr); 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 数据指针 // 调用 WebAssembly 函数进行图像转换,返回 WebP 数据指针
@ -142,16 +144,16 @@
Module._free(webpPtr); Module._free(webpPtr);
Module._free(inputDataPtr); Module._free(inputDataPtr);
} }
var worker = new Worker('./worker.js'); // var worker = new Worker('./worker.js');
worker.onmessage = function (e) { // worker.onmessage = function (e) {
console.log(e.data); // console.log(e.data);
switch(e.data.type){ // switch(e.data.type){
case 'webp': // case 'webp':
let img = document.getElementById("webpImage"); // let img = document.getElementById("webpImage");
img.src = "data:image/webp;base64," + arrayBufferToBase64(e.data.webpData); // img.src = "data:image/webp;base64," + arrayBufferToBase64(e.data.webpData);
break; // break;
} // }
} // }
function decodeItbyworker(buffer, width=0, height=0, quality=50) { function decodeItbyworker(buffer, width=0, height=0, quality=50) {
worker.postMessage({ worker.postMessage({
buffer, 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