fork of https://bellard.org/bpg/ Better Portable Graphics format including working WASM (WebAssembly) build.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

222 lines
6.9 KiB

/*
* BPG Javascript decoder
*
* Copyright (c) 2014 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
window['BPGDecoder'] = function(ctx) {
this.ctx = ctx;
this['imageData'] = null;
this['onload'] = null;
this['frames'] = null;
this['loop_count'] = 0;
}
window['BPGDecoder'].prototype = {
malloc: Module['cwrap']('malloc', 'number', [ 'number' ]),
free: Module['cwrap']('free', 'void', [ 'number' ]),
bpg_decoder_open: Module['cwrap']('bpg_decoder_open', 'number', [ ]),
bpg_decoder_decode: Module['cwrap']('bpg_decoder_decode', 'number', [ 'number', 'array', 'number' ]),
bpg_decoder_get_info: Module['cwrap']('bpg_decoder_get_info', 'number', [ 'number', 'number' ]),
bpg_decoder_start: Module['cwrap']('bpg_decoder_start', 'number', [ 'number', 'number' ]),
bpg_decoder_get_frame_duration: Module['cwrap']('bpg_decoder_get_frame_duration', 'void', [ 'number', 'number', 'number' ]),
bpg_decoder_get_line: Module['cwrap']('bpg_decoder_get_line', 'number', [ 'number', 'number' ]),
bpg_decoder_close: Module['cwrap']('bpg_decoder_close', 'void', [ 'number' ] ),
isReady: function() {return wasmModuleIsReady},
load: function(url) {
var request = new XMLHttpRequest();
var this1 = this;
request.open("get", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
wasmModuleIsReady.then(() => this1.receiveData(new Uint8Array(request.response)));
};
request.send();
},
receiveData: function(array) {
var img, w, h, img_info_buf, cimg, p0, rgba_line, w4, frame_count;
var heap8, heap16, heap32, dst, v, i, y, func, duration, frames, loop_count;
// console.log("loaded " + data.byteLength + " bytes");
img = this.bpg_decoder_open();
if (this.bpg_decoder_decode(img, array, array.length) < 0) {
console.log("could not decode image");
return;
}
img_info_buf = this.malloc(5 * 4);
this.bpg_decoder_get_info(img, img_info_buf);
/* extract the image info */
heap8 = Module['HEAPU8'];
heap16 = Module['HEAPU16'];
heap32 = Module['HEAPU32'];
w = heap32[img_info_buf >> 2];
h = heap32[(img_info_buf + 4) >> 2];
loop_count = heap16[(img_info_buf + 16) >> 1];
// console.log("image: w=" + w + " h=" + h + " loop_count=" + loop_count);
w4 = w * 4;
rgba_line = this.malloc(w4);
frame_count = 0;
frames = [];
for(;;) {
/* select RGBA32 output */
if (this.bpg_decoder_start(img, 1) < 0)
break;
this.bpg_decoder_get_frame_duration(img, img_info_buf,
img_info_buf + 4);
duration = (heap32[img_info_buf >> 2] * 1000) / heap32[(img_info_buf + 4) >> 2];
cimg = this.ctx.createImageData(w, h);
dst = cimg.data;
p0 = 0;
for(y = 0; y < h; y++) {
this.bpg_decoder_get_line(img, rgba_line);
for(i = 0; i < w4; i = (i + 1) | 0) {
dst[p0] = heap8[(rgba_line + i) | 0] | 0;
p0 = (p0 + 1) | 0;
}
}
frames[frame_count++] = { 'img': cimg, 'duration': duration };
}
this.free(rgba_line);
this.free(img_info_buf);
this.bpg_decoder_close(img);
this['loop_count'] = loop_count;
this['frames'] = frames;
this['imageData'] = frames[0]['img'];
if (this['onload'])
this['onload']();
}
};
window.createBPGElement = function(parent, url, className, elementWidth, elementHeight) {
const base64ToUint8Array = (string) => {
var raw = window.atob(string);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
};
const canvas = document.createElement("canvas");
//canvas.style.display = "none";
parent.appendChild(canvas);
const promise = new Promise((resolve) => {
if(elementWidth) {
canvas.style.width = elementWidth;
}
if(elementHeight) {
canvas.style.height = elementHeight;
}
if(className) {
canvas.className = className;
}
const ctx = canvas.getContext("2d");
const dec = new BPGDecoder(ctx);
dec.onload = (function(canvas, ctx) {
var dec = this;
var frames = this['frames'];
var imageData = frames[0]['img'];
function next_frame() {
var frame_index = dec.frame_index;
/* compute next frame index */
if (++frame_index >= frames.length) {
if (dec['loop_count'] == 0 ||
dec.loop_counter < dec['loop_count']) {
frame_index = 0;
dec.loop_counter++;
} else {
frame_index = -1;
}
}
if (frame_index >= 0) {
dec.frame_index = frame_index;
ctx.putImageData(frames[frame_index]['img'], 0, 0);
setTimeout(next_frame, frames[frame_index]['duration']);
}
};
/* resize the canvas to the image size */
canvas.width = imageData.width;
canvas.height = imageData.height;
/* draw the image */
ctx.putImageData(imageData, 0, 0);
//canvas.style.removeProperty('display');
/* if it is an animation, add a timer to display the next frame */
if (frames.length > 1) {
dec.frame_index = 0;
dec.loop_counter = 0;
setTimeout(next_frame, frames[0]['duration']);
}
resolve(canvas);
}).bind(dec, canvas, ctx);
if(url.indexOf('data:image/bpg;base64,') == 0) {
wasmModuleIsReady.then(() => {
dec.receiveData( base64ToUint8Array(url.replace('data:image/bpg;base64,', '')) );
});
} else {
dec.load(url);
}
});
return {canvas,promise};
};
/* end of dummy function enclosing all the emscripten code */
})();