Codebase list libheif / HEAD post.js
HEAD

Tree @HEAD (Download .tar.gz)

post.js @HEADraw · history · blame

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) {
        libheif.heif_image_handle_release(this.handle);
        this.handle = null;
    }
};

HeifImage.prototype._ensureImage = function() {
    if (this.img) {
        return;
    }

    var img = libheif.heif_js_decode_image(this.handle,
        libheif.heif_colorspace_YCbCr, libheif.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;
};

HeifImage.prototype.get_width = function() {
    this._ensureImage();
    return this.img.width;
};

HeifImage.prototype.get_height = function() {
    this._ensureImage();
    return this.img.height;
};

HeifImage.prototype.is_primary = function() {
    this._ensureImage();
    return !!this.img.is_primary;
}

HeifImage.prototype.display = function(image_data, callback) {
    // Defer color conversion.
    var w = this.get_width();
    var h = this.get_height();

    setTimeout(function() {
        this._ensureImage();
        if (!this.img) {
            // Decoding failed.
            callback(null);
            return;
        }

        var yval;
        var uval;
        var vval;
        var xpos = 0;
        var ypos = 0;
        var yoffset = 0;
        var uoffset = 0;
        var voffset = 0;
        var x2;
        var i = 0;
        var maxi = w*h;
        var stridey = w;
        var strideu = Math.ceil(w / 2);
        var stridev = Math.ceil(w / 2);
        var h2 = Math.ceil(h / 2);
        var y = this.data;
        var u = this.data.subarray(stridey * h, stridey * h + (strideu * h2));
        var v = this.data.subarray(stridey * h + (strideu * h2), stridey * h + (strideu * h2) + (stridev * h2));
        var dest = image_data.data;
        while (i < maxi) {
            x2 = (xpos >> 1);
            yval = 1.164 * (y[yoffset + xpos] - 16);

            uval = u[uoffset + x2] - 128;
            vval = v[voffset + x2] - 128;
            dest[(i<<2)+0] = yval + 1.596 * vval;
            dest[(i<<2)+1] = yval - 0.813 * vval - 0.391 * uval;
            dest[(i<<2)+2] = yval + 2.018 * uval;
            dest[(i<<2)+3] = 0xff;
            i++;
            xpos++;

            if (xpos < w) {
                yval = 1.164 * (y[yoffset + xpos] - 16);
                dest[(i<<2)+0] = yval + 1.596 * vval;
                dest[(i<<2)+1] = yval - 0.813 * vval - 0.391 * uval;
                dest[(i<<2)+2] = yval + 2.018 * uval;
                dest[(i<<2)+3] = 0xff;
                i++;
                xpos++;
            }

            if (xpos === w) {
                xpos = 0;
                ypos++;
                yoffset += stridey;
                uoffset = ((ypos >> 1) * strideu);
                voffset = ((ypos >> 1) * stridev);
            }
        }
        callback(image_data);
    }.bind(this), 0);
};

var HeifDecoder = function() {
    this.decoder = null;
};

HeifDecoder.prototype.decode = function(buffer) {
    if (this.decoder) {
        libheif.heif_context_free(this.decoder);
    }
    this.decoder = libheif.heif_context_alloc();
    if (!this.decoder) {
        console.log("Could not create HEIF context");
        return [];
    }
    var error = libheif.heif_context_read_from_memory(this.decoder, buffer);
    if (error.code !== libheif.heif_error_Ok) {
        console.log("Could not parse HEIF file", error);
        return [];
    }

    var ids = libheif.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 = libheif.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 libheif = {
    // Expose high-level API.
    /** @expose */
    HeifDecoder: HeifDecoder,

    // Expose low-level API.
    /** @expose */
    fourcc: function(s) {
        return s.charCodeAt(0) << 24 |
            s.charCodeAt(1) << 16 |
            s.charCodeAt(2) << 8 |
            s.charCodeAt(3);
    }
};

var key;

// Expose enum values.
var enums = {
    "heif_error_code": true,
    "heif_suberror_code": true,
    "heif_compression_format": true,
    "heif_chroma": true,
    "heif_colorspace": true,
    "heif_channel": true
};
var e;
for (e in enums) {
    if (!enums.hasOwnProperty(e)) {
        continue;
    }
    for (key in Module[e]) {
        if (!Module[e].hasOwnProperty(key) ||
            key === "values") {
            continue;
        }

        libheif[key] = Module[e][key];
    }
}

// Expose internal C API.
for (key in Module) {
    if (enums.hasOwnProperty(key) || key.indexOf("heif_") !== 0) {
        continue;
    }
    libheif[key] = Module[key];
}

// don't pollute the global namespace
delete this['Module'];

// On IE this function is called with "undefined" as first parameter. Override
// with a version that supports this behaviour.
function createNamedFunction(name, body) {
    if (!name) {
      name = "function_" + (new Date());
    }
    name = makeLegalFunctionName(name);
    /*jshint evil:true*/
    return new Function(
        "body",
        "return function " + name + "() {\n" +
        "    \"use strict\";" +
        "    return body.apply(this, arguments);\n" +
        "};\n"
    )(body);
}

var root = this;

if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
        /** @expose */
        exports = module.exports = libheif;
    }
    /** @expose */
    exports.libheif = libheif;
} else {
    /** @expose */
    root.libheif = libheif;
}

if (typeof define === "function" && define.amd) {
    /** @expose */
    define([], function() {
        return libheif;
    });
}

// NOTE: wrapped inside "(function() {" block from pre.js
}).call(this);