Build webp

This commit is contained in:
Surma
2024-08-07 18:48:23 +01:00
parent a0f9fea679
commit b5bd766a4e
37 changed files with 473 additions and 6253 deletions

View File

@@ -0,0 +1,18 @@
# WebP decoder
- Source: <https://github.com/webmproject/libwebp>
- Version: v1.0.2
## Example
See `example.html`
## API
### `int version()`
Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c
### `RawImage decode(std::string buffer)`
Decodes the given webp buffer into raw RGBA. `RawImage` is a class with 3 fields: `buffer`, `width`, and `height`.

View File

@@ -0,0 +1,20 @@
<!doctype html>
<script src='webp_dec.js'></script>
<script>
async function loadFile(src) {
const resp = await fetch(src);
return await resp.arrayBuffer();
}
webp_dec().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadFile('../../example.webp');
const imageData = module.decode(image);
const canvas = document.createElement('canvas');
canvas.width = imageData.width;
canvas.height = imageData.height;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.putImageData(imageData, 0, 0);
});
</script>

View File

@@ -0,0 +1,29 @@
#include <string>
#include "emscripten/bind.h"
#include "emscripten/val.h"
#include "webp/decode.h"
#include "webp/demux.h"
using namespace emscripten;
int version() {
return WebPGetDecoderVersion();
}
thread_local const val Uint8ClampedArray = val::global("Uint8ClampedArray");
thread_local const val ImageData = val::global("ImageData");
val decode(std::string buffer) {
int width, height;
std::unique_ptr<uint8_t[]> rgba(
WebPDecodeRGBA((const uint8_t*)buffer.c_str(), buffer.size(), &width, &height));
return rgba ? ImageData.new_(
Uint8ClampedArray.new_(typed_memory_view(width * height * 4, rgba.get())),
width, height)
: val::null();
}
EMSCRIPTEN_BINDINGS(my_module) {
function("decode", &decode);
function("version", &version);
}

View File

@@ -0,0 +1,7 @@
export interface WebPModule extends EmscriptenWasm.Module {
decode(data: BufferSource): ImageData | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1,22 @@
# WebP encoder
- Source: <https://github.com/webmproject/libwebp>
- Version: v1.0.2
## Dependencies
- Docker
## Example
See `example.html`
## API
### `int version()`
Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c
### `UInt8Array encode(uint8_t* image_buffer, int image_width, int image_height, WebPConfig config)`
Encodes the given image with given dimension to WebP.

View File

@@ -0,0 +1,58 @@
<!doctype html>
<script src='webp_enc.js'></script>
<script>
async function loadImage(src) {
// Load image
const img = document.createElement('img');
img.src = src;
await new Promise(resolve => img.onload = resolve);
// Make canvas same size as image
const canvas = document.createElement('canvas');
[canvas.width, canvas.height] = [img.width, img.height];
// Draw image onto canvas
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return ctx.getImageData(0, 0, img.width, img.height);
}
webp_enc().then(async module => {
console.log('Version:', module.version().toString(16));
const image = await loadImage('../../example.png');
const result = module.encode(image.data, image.width, image.height, {
quality: 75,
target_size: 0,
target_PSNR: 0,
method: 4,
sns_strength: 50,
filter_strength: 60,
filter_sharpness: 0,
filter_type: 1,
partitions: 0,
segments: 4,
pass: 1,
show_compressed: 0,
preprocessing: 0,
autofilter: 0,
partition_limit: 0,
alpha_compression: 1,
alpha_filtering: 1,
alpha_quality: 100,
lossless: 0,
exact: 0,
image_hint: 0,
emulate_jpeg_size: 0,
thread_level: 0,
low_memory: 0,
near_lossless: 100,
use_delta_palette: 0,
use_sharp_yuv: 0,
});
console.log('size', result.length);
const blob = new Blob([result], {type: 'image/webp'});
const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img');
img.src = blobURL;
document.body.appendChild(img);
});
</script>

View File

@@ -0,0 +1,85 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <stdlib.h>
#include <string.h>
#include <stdexcept>
#include "webp/encode.h"
using namespace emscripten;
int version() {
return WebPGetEncoderVersion();
}
thread_local const val Uint8Array = val::global("Uint8Array");
val encode(std::string img, int width, int height, WebPConfig config) {
auto img_in = (uint8_t*)img.c_str();
// A lot of this is duplicated from Encode in picture_enc.c
WebPPicture pic;
WebPMemoryWriter wrt;
int ok;
if (!WebPPictureInit(&pic)) {
// shouldn't happen, except if system installation is broken
return val::null();
}
// Allow quality to go higher than 0.
config.qmax = 100;
// Only use use_argb if we really need it, as it's slower.
pic.use_argb = config.lossless || config.use_sharp_yuv || config.preprocessing > 0;
pic.width = width;
pic.height = height;
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &wrt;
WebPMemoryWriterInit(&wrt);
ok = WebPPictureImportRGBA(&pic, img_in, width * 4) && WebPEncode(&config, &pic);
WebPPictureFree(&pic);
val js_result = ok ? Uint8Array.new_(typed_memory_view(wrt.size, wrt.mem)) : val::null();
WebPMemoryWriterClear(&wrt);
return js_result;
}
EMSCRIPTEN_BINDINGS(my_module) {
enum_<WebPImageHint>("WebPImageHint")
.value("WEBP_HINT_DEFAULT", WebPImageHint::WEBP_HINT_DEFAULT)
.value("WEBP_HINT_PICTURE", WebPImageHint::WEBP_HINT_PICTURE)
.value("WEBP_HINT_PHOTO", WebPImageHint::WEBP_HINT_PHOTO)
.value("WEBP_HINT_GRAPH", WebPImageHint::WEBP_HINT_GRAPH);
value_object<WebPConfig>("WebPConfig")
.field("lossless", &WebPConfig::lossless)
.field("quality", &WebPConfig::quality)
.field("method", &WebPConfig::method)
.field("image_hint", &WebPConfig::image_hint)
.field("target_size", &WebPConfig::target_size)
.field("target_PSNR", &WebPConfig::target_PSNR)
.field("segments", &WebPConfig::segments)
.field("sns_strength", &WebPConfig::sns_strength)
.field("filter_strength", &WebPConfig::filter_strength)
.field("filter_sharpness", &WebPConfig::filter_sharpness)
.field("filter_type", &WebPConfig::filter_type)
.field("autofilter", &WebPConfig::autofilter)
.field("alpha_compression", &WebPConfig::alpha_compression)
.field("alpha_filtering", &WebPConfig::alpha_filtering)
.field("alpha_quality", &WebPConfig::alpha_quality)
.field("pass", &WebPConfig::pass)
.field("show_compressed", &WebPConfig::show_compressed)
.field("preprocessing", &WebPConfig::preprocessing)
.field("partitions", &WebPConfig::partitions)
.field("partition_limit", &WebPConfig::partition_limit)
.field("emulate_jpeg_size", &WebPConfig::emulate_jpeg_size)
.field("low_memory", &WebPConfig::low_memory)
.field("near_lossless", &WebPConfig::near_lossless)
.field("exact", &WebPConfig::exact)
.field("use_delta_palette", &WebPConfig::use_delta_palette)
.field("use_sharp_yuv", &WebPConfig::use_sharp_yuv);
function("version", &version);
function("encode", &encode);
}

View File

@@ -0,0 +1,42 @@
export interface EncodeOptions {
quality: number;
target_size: number;
target_PSNR: number;
method: number;
sns_strength: number;
filter_strength: number;
filter_sharpness: number;
filter_type: number;
partitions: number;
segments: number;
pass: number;
show_compressed: number;
preprocessing: number;
autofilter: number;
partition_limit: number;
alpha_compression: number;
alpha_filtering: number;
alpha_quality: number;
lossless: number;
exact: number;
image_hint: number;
emulate_jpeg_size: number;
thread_level: number;
low_memory: number;
near_lossless: number;
use_delta_palette: number;
use_sharp_yuv: number;
}
export interface WebPModule extends EmscriptenWasm.Module {
encode(
data: BufferSource,
width: number,
height: number,
options: EncodeOptions,
): Uint8Array | null;
}
declare var moduleFactory: EmscriptenWasm.ModuleFactory<WebPModule>;
export default moduleFactory;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
export { default } from './webp_enc.js';