Memory view rather than pointers (#144). Part of #141.

* Returning an object seems to work well

* This doesn't work

* This does!

* Better cast?

* Updating usage in Squoosh
This commit is contained in:
Jake Archibald
2018-08-21 09:27:04 +01:00
committed by GitHub
parent 1ae65dd4a1
commit 8006a1a5e7
14 changed files with 62 additions and 146 deletions

View File

@@ -19,26 +19,27 @@ See `example.html`
Returns the version of MozJPEG as a number. va.b.c is encoded as 0x0a0b0c Returns the version of MozJPEG as a number. va.b.c is encoded as 0x0a0b0c
### `uint8_t* create_buffer(int width, int height)`
Allocates an RGBA buffer for an image with the given dimension.
### `void destroy_buffer(uint8_t* p)`
Frees a buffer created with `create_buffer`.
### `void encode(uint8_t* image_buffer, int image_width, int image_height, int quality)`
Encodes the given image with given dimension to JPEG. `quality` is a number between 0 and 100. The higher the number, the better the quality of the encoded image. The result is implicitly stored and can be accessed using the `get_result_*()` functions.
### `void free_result()` ### `void free_result()`
Frees the result created by `encode()`. Frees the result created by `encode()`.
### `int get_result_pointer()` ### `Uint8Array encode(std::string image_in, int image_width, int image_height, MozJpegOptions opts)`
Returns the pointer to the start of the buffer holding the encoded data. Encodes the given image with given dimension to JPEG. Options looks like this:
### `int get_result_size()` ```c++
struct MozJpegOptions {
Returns the length of the buffer holding the encoded data. int quality;
bool baseline;
bool arithmetic;
bool progressive;
bool optimize_coding;
int smoothing;
int color_space;
int quant_table;
bool trellis_multipass;
bool trellis_opt_zero;
bool trellis_opt_table;
int trellis_loops;
};
```

View File

@@ -20,9 +20,7 @@
module.onRuntimeInitialized = async _ => { module.onRuntimeInitialized = async _ => {
console.log('Version:', module.version().toString(16)); console.log('Version:', module.version().toString(16));
const image = await loadImage('../example.png'); const image = await loadImage('../example.png');
const p = module.create_buffer(image.width, image.height); const result = module.encode(image.data, image.width, image.height, {
module.HEAP8.set(image.data, p);
module.encode(p, image.width, image.height, {
quality: 40, quality: 40,
baseline: false, baseline: false,
arithmetic: false, arithmetic: false,
@@ -36,13 +34,6 @@
trellis_opt_table: true, trellis_opt_table: true,
trellis_loops: 1, trellis_loops: 1,
}); });
const resultPointer = module.get_result_pointer();
const resultSize = module.get_result_size();
console.log('size', resultSize);
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
module.free_result();
module.destroy_buffer(p);
const blob = new Blob([result], {type: 'image/jpeg'}); const blob = new Blob([result], {type: 'image/jpeg'});
const blobURL = URL.createObjectURL(blob); const blobURL = URL.createObjectURL(blob);

View File

@@ -1,10 +1,10 @@
#include "emscripten.h" #include <emscripten/bind.h>
#include <emscripten/val.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <setjmp.h> #include <setjmp.h>
#include <string.h> #include <string.h>
#include <emscripten/bind.h>
#include "config.h" #include "config.h"
#include "jpeglib.h" #include "jpeglib.h"
#include "cdjpeg.h" #include "cdjpeg.h"
@@ -47,17 +47,10 @@ int version() {
return version; return version;
} }
int create_buffer(int width, int height) { uint8_t* last_result;
return (int) malloc(width * height * 4 * sizeof(uint8_t));
}
void destroy_buffer(int p) { val encode(std::string image_in, int image_width, int image_height, MozJpegOptions opts) {
free((uint8_t*) p); uint8_t* image_buffer = (uint8_t*) image_in.c_str();
}
int result[2];
void encode(int image_in, int image_width, int image_height, MozJpegOptions opts) {
uint8_t* image_buffer = (uint8_t*) image_in;
// The code below is basically the `write_JPEG_file` function from // The code below is basically the `write_JPEG_file` function from
// https://github.com/mozilla/mozjpeg/blob/master/example.c // https://github.com/mozilla/mozjpeg/blob/master/example.c
@@ -191,25 +184,17 @@ void encode(int image_in, int image_width, int image_height, MozJpegOptions opts
jpeg_finish_compress(&cinfo); jpeg_finish_compress(&cinfo);
/* Step 7: release JPEG compression object */ /* Step 7: release JPEG compression object */
result[0] = (int)output;
result[1] = size;
/* This is an important step since it will release a good deal of memory. */ /* This is an important step since it will release a good deal of memory. */
jpeg_destroy_compress(&cinfo); jpeg_destroy_compress(&cinfo);
last_result = output;
/* And we're done! */ /* And we're done! */
return val(typed_memory_view(size, output));
} }
void free_result() { void free_result() {
free((void*)result[0]); // not sure if this is right with mozjpeg free(last_result); // not sure if this is right with mozjpeg
}
int get_result_pointer() {
return result[0];
}
int get_result_size() {
return result[1];
} }
EMSCRIPTEN_BINDINGS(my_module) { EMSCRIPTEN_BINDINGS(my_module) {
@@ -229,10 +214,6 @@ EMSCRIPTEN_BINDINGS(my_module) {
; ;
function("version", &version); function("version", &version);
function("create_buffer", &create_buffer, allow_raw_pointers()); function("encode", &encode);
function("destroy_buffer", &destroy_buffer, allow_raw_pointers());
function("encode", &encode, allow_raw_pointers());
function("free_result", &free_result); function("free_result", &free_result);
function("get_result_pointer", &get_result_pointer, allow_raw_pointers());
function("get_result_size", &get_result_size, allow_raw_pointers());
} }

View File

@@ -1,13 +1,8 @@
import { EncodeOptions } from '../../src/codecs/mozjpeg/encoder'; import { EncodeOptions } from '../../src/codecs/mozjpeg/encoder';
interface MozJPEGModule extends EmscriptenWasm.Module { interface MozJPEGModule extends EmscriptenWasm.Module {
create_buffer(width: number, height: number): number; encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array;
encode(pointer: number, width: number, height: number, options: EncodeOptions): void;
get_result_pointer(): number;
get_result_size(): number;
free_result(): void; free_result(): void;
destroy_buffer(pointer: number): void;
} }
export default function(opts: EmscriptenWasm.ModuleOpts): MozJPEGModule; export default function(opts: EmscriptenWasm.ModuleOpts): MozJPEGModule;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -17,26 +17,10 @@ See `example.html`
Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c Returns the version of libwebp as a number. va.b.c is encoded as 0x0a0b0c
### `uint8_t* create_buffer(int width, int height)` ### `UInt8Array encode(uint8_t* image_buffer, int image_width, int image_height, WebPConfig config)`
Allocates an RGBA buffer for an image with the given dimension. Encodes the given image with given dimension to WebP.
### `void destroy_buffer(uint8_t* p)`
Frees a buffer created with `create_buffer`.
### `void encode(uint8_t* image_buffer, int image_width, int image_height, float quality)`
Encodes the given image with given dimension to WebP. `quality` is a number between 0 and 100. The higher the number, the better the quality of the encoded image. The result is implicitly stored and can be accessed using the `get_result_*()` functions.
### `void free_result()` ### `void free_result()`
Frees the result created by `encode()`. Frees the last result created by `encode()`.
### `int get_result_pointer()`
Returns the pointer to the start of the buffer holding the encoded data.
### `int get_result_size()`
Returns the length of the buffer holding the encoded data.

View File

@@ -20,14 +20,11 @@
module.onRuntimeInitialized = async _ => { module.onRuntimeInitialized = async _ => {
console.log('Version:', module.version().toString(16)); console.log('Version:', module.version().toString(16));
const image = await loadImage('../example.png'); const image = await loadImage('../example.png');
const p = module.create_buffer(image.width, image.height); const result = module.encode(image.data, image.width, image.height, {
module.HEAP8.set(image.data, p); quality: 75,
module.encode(p, image.width, image.height, {
quality: 100,
image_hint: 0,
target_size: 0, target_size: 0,
target_PSNR: 0, target_PSNR: 0,
method: 6, method: 4,
sns_strength: 50, sns_strength: 50,
filter_strength: 60, filter_strength: 60,
filter_sharpness: 0, filter_sharpness: 0,
@@ -42,7 +39,7 @@
alpha_compression: 1, alpha_compression: 1,
alpha_filtering: 1, alpha_filtering: 1,
alpha_quality: 100, alpha_quality: 100,
lossless: 1, lossless: 0,
exact: 0, exact: 0,
image_hint: 0, image_hint: 0,
emulate_jpeg_size: 0, emulate_jpeg_size: 0,
@@ -52,15 +49,11 @@
use_delta_palette: 0, use_delta_palette: 0,
use_sharp_yuv: 0, use_sharp_yuv: 0,
}); });
const resultPointer = module.get_result_pointer(); console.log('size', result.length);
const resultSize = module.get_result_size();
console.log('size', resultSize);
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView);
module.free_result();
module.destroy_buffer(p);
const blob = new Blob([result], {type: 'image/webp'}); const blob = new Blob([result], {type: 'image/webp'});
module.free_result();
const blobURL = URL.createObjectURL(blob); const blobURL = URL.createObjectURL(blob);
const img = document.createElement('img'); const img = document.createElement('img');
img.src = blobURL; img.src = blobURL;

View File

@@ -1,7 +1,9 @@
#include "emscripten.h" #include <emscripten/bind.h>
#include <emscripten/val.h>
#include "src/webp/encode.h" #include "src/webp/encode.h"
#include <stdlib.h> #include <stdlib.h>
#include <emscripten/bind.h> #include <string.h>
#include <stdexcept>
using namespace emscripten; using namespace emscripten;
@@ -9,23 +11,19 @@ int version() {
return WebPGetEncoderVersion(); return WebPGetEncoderVersion();
} }
int create_buffer(int width, int height) { uint8_t* last_result;
return (int) malloc(width * height * 4 * sizeof(uint8_t));
}
void destroy_buffer(int p) { val encode(std::string img, int width, int height, WebPConfig config) {
free((uint8_t*) p); uint8_t* img_in = (uint8_t*) img.c_str();
}
int result[2];
void encode(int img_in, int width, int height, WebPConfig config) {
// A lot of this is duplicated from Encode in picture_enc.c // A lot of this is duplicated from Encode in picture_enc.c
WebPPicture pic; WebPPicture pic;
WebPMemoryWriter wrt; WebPMemoryWriter wrt;
int ok; int ok;
if (!WebPPictureInit(&pic)) { if (!WebPPictureInit(&pic)) {
return; // shouldn't happen, except if system installation is broken // shouldn't happen, except if system installation is broken
throw std::runtime_error("Unexpected error");
} }
pic.use_argb = !!config.lossless; pic.use_argb = !!config.lossless;
@@ -40,23 +38,18 @@ void encode(int img_in, int width, int height, WebPConfig config) {
WebPPictureFree(&pic); WebPPictureFree(&pic);
if (!ok) { if (!ok) {
WebPMemoryWriterClear(&wrt); WebPMemoryWriterClear(&wrt);
return; throw std::runtime_error("Encode failed");
} }
result[0] = (int)wrt.mem;
result[1] = wrt.size; last_result = wrt.mem;
return val(typed_memory_view(wrt.size, wrt.mem));
} }
void free_result() { void free_result() {
WebPFree((void*)result[0]); WebPFree(last_result);
} }
int get_result_pointer() {
return result[0];
}
int get_result_size() {
return result[1];
}
EMSCRIPTEN_BINDINGS(my_module) { EMSCRIPTEN_BINDINGS(my_module) {
enum_<WebPImageHint>("WebPImageHint") enum_<WebPImageHint>("WebPImageHint")
@@ -97,10 +90,6 @@ EMSCRIPTEN_BINDINGS(my_module) {
; ;
function("version", &version); function("version", &version);
function("create_buffer", &create_buffer, allow_raw_pointers()); function("encode", &encode);
function("destroy_buffer", &destroy_buffer, allow_raw_pointers());
function("encode", &encode, allow_raw_pointers());
function("free_result", &free_result); function("free_result", &free_result);
function("get_result_pointer", &get_result_pointer, allow_raw_pointers());
function("get_result_size", &get_result_size, allow_raw_pointers());
} }

View File

@@ -1,12 +1,8 @@
import { EncodeOptions } from '../../src/codecs/webp/encoder'; import { EncodeOptions } from '../../src/codecs/webp/encoder';
interface WebPModule extends EmscriptenWasm.Module { interface WebPModule extends EmscriptenWasm.Module {
create_buffer(width: number, height: number): number; encode(data: BufferSource, width: number, height: number, options: EncodeOptions): Uint8Array;
encode(pointer: number, width: number, height: number, options: EncodeOptions): void;
get_result_pointer(): number;
get_result_size(): number;
free_result(): void; free_result(): void;
destroy_buffer(pointer: number): void;
} }

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -33,16 +33,9 @@ export default class MozJpegEncoder {
async encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> { async encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
const module = await this.emscriptenModule; const module = await this.emscriptenModule;
const resultView = module.encode(data.data, data.width, data.height, options);
const p = module.create_buffer(data.width, data.height);
module.HEAP8.set(data.data, p);
module.encode(p, data.width, data.height, options);
const resultPointer = module.get_result_pointer();
const resultSize = module.get_result_size();
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView); const result = new Uint8Array(resultView);
module.free_result(); module.free_result();
module.destroy_buffer(p);
// wasm cant run on SharedArrayBuffers, so we hard-cast to ArrayBuffer. // wasm cant run on SharedArrayBuffers, so we hard-cast to ArrayBuffer.
return result.buffer as ArrayBuffer; return result.buffer as ArrayBuffer;

View File

@@ -34,17 +34,10 @@ export default class WebPEncoder {
async encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> { async encode(data: ImageData, options: EncodeOptions): Promise<ArrayBuffer> {
const module = await this.emscriptenModule; const module = await this.emscriptenModule;
const p = module.create_buffer(data.width, data.height); const resultView = module.encode(data.data, data.width, data.height, options);
module.HEAP8.set(data.data, p);
module.encode(p, data.width, data.height, options);
const resultPointer = module.get_result_pointer();
const resultSize = module.get_result_size();
const resultView = new Uint8Array(module.HEAP8.buffer, resultPointer, resultSize);
const result = new Uint8Array(resultView); const result = new Uint8Array(resultView);
module.free_result(); module.free_result();
module.destroy_buffer(p);
// wasm cant run on SharedArrayBuffers, so we hard-cast to ArrayBuffer.
return result.buffer as ArrayBuffer; return result.buffer as ArrayBuffer;
} }
} }