function StringToArrayBuffer(str) { var buf = new ArrayBuffer(str.length); var bufView = new Uint8Array(buf); for (var i=0, strLen=str.length; i<strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } var HeifImage = function(handle) { this.handle = handle; this.img = null; }; HeifImage.prototype.free = function() { if (this.handle) { Module.heif_image_handle_release(this.handle); this.handle = null; } }; HeifImage.prototype._ensureImage = function() { if (this.img) { return; } var img = Module.heif_js_decode_image(this.handle, Module.heif_colorspace.heif_colorspace_YCbCr, Module.heif_chroma.heif_chroma_420); if (!img || img.code) { console.log("Decoding image failed", this.handle, img); return; } this.data = new Uint8Array(StringToArrayBuffer(img.data)); delete img.data; this.img = img; if (img.alpha !== undefined) { this.alpha = new Uint8Array(StringToArrayBuffer(img.alpha)); delete img.alpha; } }; HeifImage.prototype.get_width = function() { return Module.heif_image_handle_get_width(this.handle); }; HeifImage.prototype.get_height = function() { return Module.heif_image_handle_get_height(this.handle); }; HeifImage.prototype.is_primary = function() { return !!heif_image_handle_is_primary_image(this.handle); } HeifImage.prototype.display = function(image_data, callback) { // Defer color conversion. var w = this.get_width(); var h = this.get_height(); setTimeout(function() { // If image hasn't been loaded yet, decode the image if (!this.img) { var img = Module.heif_js_decode_image2(this.handle, Module.heif_colorspace.heif_colorspace_RGB, Module.heif_chroma.heif_chroma_interleaved_RGBA); if (!img || img.code) { console.log("Decoding image failed", this.handle, img); callback(null); return; } for (let c of img.channels) { if (c.id == Module.heif_channel.heif_channel_interleaved) { // copy image into output array if (c.stride == c.width * 4) { image_data.data.set(c.data); } else { for (let y = 0; y < c.height; y++) { let slice = c.data.slice(y * c.stride, y * c.stride + c.width * 4); let offset = y * c.width * 4; image_data.data.set(slice, offset); } } } } Module.heif_image_release(img.image); } callback(image_data); }.bind(this), 0); }; var HeifDecoder = function() { this.decoder = null; }; HeifDecoder.prototype.decode = function(buffer) { if (this.decoder) { Module.heif_context_free(this.decoder); } this.decoder = Module.heif_context_alloc(); if (!this.decoder) { console.log("Could not create HEIF context"); return []; } var error = Module.heif_context_read_from_memory(this.decoder, buffer); if (error.code !== Module.heif_error_code.heif_error_Ok) { console.log("Could not parse HEIF file", error.message); return []; } var ids = Module.heif_js_context_get_list_of_top_level_image_IDs(this.decoder); if (!ids || ids.code) { console.log("Error loading image ids", ids); return []; } else if (!ids.length) { console.log("No images found"); return []; } var result = []; for (var i = 0; i < ids.length; i++) { var handle = Module.heif_js_context_get_image_handle(this.decoder, ids[i]); if (!handle || handle.code) { console.log("Could not get image data for id", ids[i], handle); continue; } result.push(new HeifImage(handle)); } return result; }; var fourcc = function(s) { return s.charCodeAt(0) << 24 | s.charCodeAt(1) << 16 | s.charCodeAt(2) << 8 | s.charCodeAt(3); } Module.HeifImage = HeifImage; Module.HeifDecoder = HeifDecoder; Module.fourcc = fourcc; // Expose enum values. const enums = [ 'heif_error_code', 'heif_suberror_code', 'heif_compression_format', 'heif_chroma', 'heif_colorspace', 'heif_channel' ]; for (const e of enums) { for (const key in Module[e]) { if (!Module[e].hasOwnProperty(key) || key === 'values') { continue; } Module[key] = Module[e][key]; } } // Expose internal C API. for (const key in Module) { if (key.indexOf('_heif_') !== 0 || Module[key.slice(1)] !== undefined) { continue; } Module[key.slice(1)] = Module[key]; }