Search code examples
javascriptangularjsdrawimageimage-compression

JIC - Java Image Compressor : Failed to execute 'drawImage' on 'CanvasRenderingContext2D': No function was found that matched the signature


Just trying hands on JIC, but always come across this error:

Failed to execute 'drawImage' on 'CanvasRenderingContext2D': No function was found that matched the signature.

Following is my code :

HTML View:

<input type="file" ng-file-select="uploadJobImage($files)" class="upload" id="imgFle"> 

Controller method :

$scope.uploadJobImage              = function($files){
//Images Objects
        var source_image = $files[0],
         target_img = $files[0];
         console.log(source_image);
        //(NOTE: see the examples/js/demo.js file to understand how this object could be a local image 
        //from your filesystem using the File API)
        var output_format = {
                    resizeType        : 'jpg',
                    resizeQuality     : 80,
                    resizeMaxHeight   : 456,
                    resizeMaxWidth    : window.innerWidth,
                };
        target_img.src = jic.compress(source_image,output_format);

} JIC compress Method:

var jic = {
    /**
     * Receives an Image Object (can be JPG OR PNG) and returns a new Image Object compressed
     * @param {Image} source_img_obj The source Image Object
     * @param {Integer} quality The output quality of Image Object
     * @param {String} output format. Possible values are jpg and png
     * @return {Image} result_image_obj The compressed Image Object
     */

    compress: function(sourceImgObj, options){
        console.log(sourceImgObj);

         // var mime_type = "image/jpeg";
         // if(typeof output_format !== "undefined" && output_format=="png"){
         //    mime_type = "image/png";
         // }
         // var cvs = document.createElement('canvas');
         // cvs.width = source_img_obj.naturalWidth;
         // cvs.height = source_img_obj.naturalHeight;
         // var ctx = cvs.getContext("2d").drawImage(source_img_obj, 0, 0);
         // var newImageData = cvs.toDataURL(mime_type, quality/100);
         // var result_image_obj = new Image();
         // result_image_obj.src = newImageData;
         // return result_image_obj;

        var outputFormat = options.resizeType;
        var quality = options.resizeQuality * 100 || 70;
        var mimeType = 'image/jpeg';
        if (outputFormat !== undefined && outputFormat === 'png') {
            mimeType = 'image/png';
        }


        var maxHeight = options.resizeMaxHeight || 300;
        var maxWidth = options.resizeMaxWidth || 250;

        var height = sourceImgObj.height;
        var width = sourceImgObj.width;

        // calculate the width and height, constraining the proportions
        if (width > height) {
            if (width > maxWidth) {
                height = Math.round(height *= maxWidth / width);
                width = maxWidth;
            }
        }
        else {
            if (height > maxHeight) {
                width = Math.round(width *= maxHeight / height);
                height = maxHeight;
            }
        }

        var cvs = document.createElement('canvas');
        cvs.width = width; //sourceImgObj.naturalWidth;
        cvs.height = height; //sourceImgObj.naturalHeight;
        var ctx = cvs.getContext('2d').drawImage(sourceImgObj, 0, 0, width, height);
        var newImageData = cvs.toDataURL(mimeType, quality / 100);
        var resultImageObj = new Image();
        resultImageObj.src = newImageData;
        return resultImageObj.src;
    },

Any help on this is really appreciated.


Solution

  • Found the issue at last, the image object source_image should have been a dataURL base 64 encoded image format for the JIC to process it correctly. After a long effort fixed the issue as:

    test.html file
    - Added a couple of divs for source and target image and a file selector

    <img src="" height="100%" align="middle" id="source_image">
    <img src="" height="100%" align="middle" id="result_image">
    <input type="file" ng-file-select="uploadJobImage($files)" 
     class="upload" id="imgFle"> 
    

    testController.js

    1) Retrieved the file blob url
    - using the var URL = window.webkitURL || window.URL; var url = URL.createObjectURL($files[0]);

    Code:

     // Retieves the file's blob url and passes it onto 
     //convertImgToBase64URL() method to convert it to base 64 encoded format
     $scope.uploadJobImage              = function($files){
            var URL = window.webkitURL || window.URL;
            var url = URL.createObjectURL($files[0]);
            alert("Image quality : "+$scope.qlty);
            var  output_format = 'jpg';
            $scope.convertImgToBase64URL(url, output_format);
        };
    


    2) Using Base 64 encoding
    Then encoded it using base 64 encoding using the $scope.convertImgToBase64URL(url, output_format); method:

    Code:

       // convertImgToBase64URL returns a dataurl corresponding to the 
       // input blob url
       $scope.convertImgToBase64URL  =  function(url,  outputFormat){
                alert();
                var canvas = document.createElement('CANVAS'),
                    ctx = canvas.getContext('2d'),
                    img = new Image;
                img.crossOrigin = 'Anonymous';
                img.onload = function(){
                    var dataURL;
                    canvas.height = img.height;
                    canvas.width = img.width;
                    ctx.drawImage(img, 0, 0);
                    dataURL = canvas.toDataURL(outputFormat);
                    $scope.callback(dataURL);
                    // console.log("hhhh dataurl : "dataURL);
                    canvas = null; 
                };
                img.src = url;
            }
    


    3) Use the JIC compression code:
    - In the callback() method, call the JIC image compression method using: result_image.src = jic.compress(source_image,quality,'jpg').src;

    Code:

    $scope.callback = function(dataURL){
                    console.log("callback dataURL: "+dataURL);
                    document.getElementById("source_image").src = dataURL;
    
                     var source_image = document.getElementById('source_image');
                    var result_image = document.getElementById('result_image');
                    if (source_image.src == "") {
                        alert("You must load an image first!");
                        return false;
                    }
    
                    var quality = parseInt($scope.qlty);
                    console.log("Quality >>" + quality);
    
                    console.log("process start...");
                    var time_start = new Date().getTime();
    
                    // The JIC compression Method
                    result_image.src = jic.compress(source_image,quality,'jpg').src; 
                   console.log(source_image+'  '+quality+'  '+output_format);
                    result_image.onload = function(){
                        var image_width=$(result_image).width(),
                        image_height=$(result_image).height();
    
                        if(image_width > image_height){
                            result_image.style.width="320px";
                        }else{
                            result_image.style.height="300px";
                        }
                       result_image.style.display = "block";
                    }
                    var duration = new Date().getTime() - time_start;
    
                    console.log("process finished...");
                    console.log('Processed in: ' + duration + 'ms');   
                    console.log(document.getElementById('result_image'));       
                }
    


    The JIC open source Image compressor library:

    /*!
     * JIC JavaScript Library v1.1
     * https://github.com/brunobar79/J-I-C/
     *
     * Copyright 2012, Bruno Barbieri
     * Dual licensed under the MIT or GPL Version 2 licenses.
     *
     * Date: Sat Mar 24 15:11:03 2012 -0200
     */
    
    
    
    /**
     * Create the jic object.
     * @constructor
     */
    
    var jic = {
            /**
             * Receives an Image Object (can be JPG OR PNG) and returns a new Image Object compressed
             * @param {Image} source_img_obj The source Image Object
             * @param {Integer} quality The output quality of Image Object
             * @param {String} output format. Possible values are jpg and png
             * @return {Image} result_image_obj The compressed Image Object
             */
    
            compress: function(source_img_obj, quality, output_format){
    
                 var mime_type = "image/jpeg";
                 if(typeof output_format !== "undefined" && output_format=="png"){
                    mime_type = "image/png";
                 }
    
                 var cvs = document.createElement('canvas');
                 cvs.width = source_img_obj.naturalWidth;
                 cvs.height = source_img_obj.naturalHeight;
                 // alert(source_img_obj.naturalWidth);
                 // alert(source_img_obj.naturalHeight);
                 console.log(source_img_obj);
                 var ctx = cvs.getContext("2d").drawImage(source_img_obj, 0, 0);
                 var newImageData = cvs.toDataURL(mime_type, quality/100);
    
                 console.log(newImageData);
                 var result_image_obj = new Image();
                 result_image_obj.src = newImageData;
                 return result_image_obj;
            },
    
            /**
             * Receives an Image Object and upload it to the server via ajax
             * @param {Image} compressed_img_obj The Compressed Image Object
             * @param {String} The server side url to send the POST request
             * @param {String} file_input_name The name of the input that the server will receive with the file
             * @param {String} filename The name of the file that will be sent to the server
             * @param {function} successCallback The callback to trigger when the upload is succesful.
             * @param {function} (OPTIONAL) errorCallback The callback to trigger when the upload failed.
             * @param {function} (OPTIONAL) duringCallback The callback called to be notified about the image's upload progress.
             * @param {Object} (OPTIONAL) customHeaders An object representing key-value  properties to inject to the request header.
             */
    
            upload: function(compressed_img_obj, upload_url, file_input_name, filename, successCallback, errorCallback, duringCallback, customHeaders){
    
    
                var cvs = document.createElement('canvas');
                cvs.width = compressed_img_obj.naturalWidth;
                cvs.height = compressed_img_obj.naturalHeight;
                var ctx = cvs.getContext("2d").drawImage(compressed_img_obj, 0, 0);
    
                //ADD sendAsBinary compatibilty to older browsers
                if (XMLHttpRequest.prototype.sendAsBinary === undefined) {
                    XMLHttpRequest.prototype.sendAsBinary = function(string) {
                        var bytes = Array.prototype.map.call(string, function(c) {
                            return c.charCodeAt(0) & 0xff;
                        });
                        this.send(new Uint8Array(bytes).buffer);
                    };
                }
    
                var type = "image/jpeg";
                if(filename.substr(-4)==".png"){
                    type = "image/png";
                }
    
                var data = cvs.toDataURL(type);
                data = data.replace('data:' + type + ';base64,', '');
    
                var xhr = new XMLHttpRequest();
                xhr.open('POST', upload_url, true);
                var boundary = 'someboundary';
    
                xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
    
            // Set custom request headers if customHeaders parameter is provided
            if (customHeaders && typeof customHeaders === "object") {
                for (var headerKey in customHeaders){
                    xhr.setRequestHeader(headerKey, customHeaders[headerKey]);
                }
            }
    
            // If a duringCallback function is set as a parameter, call that to notify about the upload progress
            if (duringCallback && duringCallback instanceof Function) {
                xhr.onprogress = function (evt) {
                    if (evt.lengthComputable) {  
                        return (evt.loaded / evt.total)*100;  
                    }
                };
            }
    
                xhr.sendAsBinary(['--' + boundary, 'Content-Disposition: form-data; name="' + file_input_name + '"; filename="' + filename + '"', 'Content-Type: ' + type, '', atob(data), '--' + boundary + '--'].join('\r\n'));
    
                xhr.onreadystatechange = function() {
                if (this.readyState == 4){
                    if (this.status == 200) {
                        successCallback(this.responseText);
                    }else if (this.status >= 400) {
                        if (errorCallback &&  errorCallback instanceof Function) {
                            errorCallback(this.responseText);
                        }
                    }
                }
                };
    
    
            }
    };