import './SelectImageDialog.html'; Template.SelectImageDialog.onCreated(function() { this.originalImage = new ReactiveVar(undefined); //The original image. This is used to regenerate the displayed image after altering the rendering pipeline. this.currentWidth = new ReactiveVar(0); //The current width of the image. Must be > 0 and <= the original image width. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio). this.currentHeight = new ReactiveVar(0); //The current height of the image. Must be > 0 and <= the original image height. The aspect ratio must be maintained (there is no reason to ever allow a change in aspect ratio). this.currentCompression = new ReactiveVar(8); //On a range of 0..10 where 10 is maximum, and zero is none. this.lossyCompression = new ReactiveVar(true); //Boolean value indicating whether lossy compression should be utilized to reduce image sizes. this.compressedSize = new ReactiveVar(0); //The current display image's size once compressed (in base64 units). Note we could start saving the image outside the html (non-embeded), in which case the image size would be reduced by 1/4 roughly. this.previewCompression = new ReactiveVar(false); //Whether the viewed image includes the compression. this.convertedImage = undefined; }); Template.SelectImageDialog.onRendered(function() { let template = this; //TODO: Setup Jimp to edit the image in the canvas let $canvas = template.$('.insertImageCanvas'); let canvas = $canvas[0]; let context = canvas.getContext('2d'); canvas.height = 300; canvas.width = 300; template.readFile = function(file) { let reader = new FileReader(); reader.onload = function(e) { let image = document.createElement("img"); image.addEventListener("load", function() { //canvas.height = image.height; //canvas.width = image.width; //$canvas.css({width: image.width, height: image.height}); template.currentHeight.set(image.height); template.currentWidth.set(image.width); //context.clearRect(0, 0, canvas.width, canvas.height); //context.drawImage(image, 0, 0); $canvas.removeClass("empty"); template.originalImage.set(image); template.rerender(); }, false); image.src = e.target.result; //var id = 'blobid' + (new Date()).getTime(); //var blobCache = tinymce.activeEditor.editorUpload.blobCache; //var base64 = reader.result.split(',')[1]; //var blobInfo = blobCache.create(id, file, base64); //blobCache.add(blobInfo); // //// call the callback and populate the Title field with the file name //cb(blobInfo.blobUri(), { title: file.name }); }; reader.readAsDataURL(file); }; context.font = '10pt Arial, Sans Serif'; context.fillStyle = 'red'; context.textAlign = 'center'; context.fillText("Drag & Drop", canvas.width / 2, canvas.height / 2 - 14); context.fillStyle = '#666'; context.fillText("or", canvas.width / 2, canvas.height / 2); context.fillStyle = 'red'; context.fillText("Click To Add Image", canvas.width / 2, canvas.height / 2 + 16); template.changeWidth = function(delta) { let originalImage = template.originalImage.get(); let ratio = originalImage.height / originalImage.width; let width = template.currentWidth.get() + delta; let height = 0; if(width < 0) width = 1; else if(width > originalImage.width) width = originalImage.width; height = Math.round(ratio * width); template.setSize(width, height <= 0 ? 1 : height); }; template.changeHeight = function(delta) { let originalImage = template.originalImage.get(); let ratio = originalImage.width / originalImage.height; let height = template.currentHeight.get() + delta; let width; if(height < 0) height = 1; else if(height > originalImage.height) height = originalImage.height; width = Math.round(ratio * height); template.setSize(width <= 0 ? 1 : width, height); }; template.setSize = function(width, height) { template.currentWidth.set(width); template.currentHeight.set(height); template.rerender(); }; template.rerender = function() { let image = template.originalImage.get(); //Apply the width/height and other changes to the original image and render to the canvas. canvas.height = image.height; canvas.width = image.width; $canvas.css({width: image.width, height: image.height}); context.clearRect(0, 0, canvas.width, canvas.height); context.drawImage(image, 0, 0); //Apply filters //Resize if(template.currentWidth.get() !== image.width && template.currentHeight.get() !== image.height) { let width = template.currentWidth.get(); let height = template.currentHeight.get(); template.resizeCanvas(canvas, width, height, true); $canvas.css({width: width, height: height}); } //If the user has requested that the display show the compression then get the compressed canvas contents and re-display them in the canvas such that the compression is shown. if(template.previewCompression.get()) { let image = new Image(); //Save the converted image such that we don't compress an already compressed image if the user saves the changes. template.convertedImage = getImageFromCanvas(); //Use an image object to convert the compressed image into something we can render to the canvas. image.onload = function() { context.drawImage(image, 0, 0); }; image.src = template.convertedImage; } else { template.convertedImage = undefined; } template.collectStatistics(); }; function getImageFromCanvas() { //Gets the base64 string containing the image with selected compression and in the desired format. let compression = Math.abs(template.currentCompression.get() -10) / 10; let type = template.lossyCompression.get() ? "image/jpeg" : "image/png"; return canvas.toDataURL(type, compression); } template.saveImage = function() { let dataURL = template.convertedImage ? template.convertedImage : getImageFromCanvas(); let data = Template.currentData(); if(data && data.onApply && typeof data.onApply === 'function') { data.onApply(dataURL); } }; template.collectStatistics = function() { let imageData = getImageFromCanvas(); //Save the compressed base64 size. If we were to allow the image to be saved outside the html (non-embedded) then we should multiply this by 0.75 for an estimate of a binary format. //Convert to kilo bytes and ensure the value is at least 1kb (it would be weird to have zero kb files). template.compressedSize.set(Math.max(1, Math.round(imageData.length / 1000)) + "kb"); }; template.resizeCanvas = function(canvas, width, height, resizeCanvas) { let width_source = canvas.width; let height_source = canvas.height; width = Math.round(width); height = Math.round(height); let ratio_w = width_source / width; let ratio_h = height_source / height; let ratio_w_half = Math.ceil(ratio_w / 2); let ratio_h_half = Math.ceil(ratio_h / 2); let ctx = canvas.getContext("2d"); let img = ctx.getImageData(0, 0, width_source, height_source); let img2 = ctx.createImageData(width, height); let data = img.data; let data2 = img2.data; for(let j = 0; j < height; j++) { for(let i = 0; i < width; i++) { let x2 = (i + j * width) * 4; let weight = 0; let weights = 0; let weights_alpha = 0; let gx_r = 0; let gx_g = 0; let gx_b = 0; let gx_a = 0; let center_y = (j + 0.5) * ratio_h; let yy_start = Math.floor(j * ratio_h); let yy_stop = Math.ceil((j + 1) * ratio_h); for (let yy = yy_start; yy < yy_stop; yy++) { let dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; let center_x = (i + 0.5) * ratio_w; let w0 = dy * dy; //pre-calc part of w let xx_start = Math.floor(i * ratio_w); let xx_stop = Math.ceil((i + 1) * ratio_w); for (let xx = xx_start; xx < xx_stop; xx++) { let dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; let w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far continue; } //hermite filter weight = 2 * w * w * w - 3 * w * w + 1; let pos_x = 4 * (xx + yy * width_source); //alpha gx_a += weight * data[pos_x + 3]; weights_alpha += weight; //colors if (data[pos_x + 3] < 255) weight = weight * data[pos_x + 3] / 250; gx_r += weight * data[pos_x]; gx_g += weight * data[pos_x + 1]; gx_b += weight * data[pos_x + 2]; weights += weight; } } data2[x2] = gx_r / weights; data2[x2 + 1] = gx_g / weights; data2[x2 + 2] = gx_b / weights; data2[x2 + 3] = gx_a / weights_alpha; } } //clear and resize canvas if(resizeCanvas) { canvas.width = width; canvas.height = height; } else { ctx.clearRect(0, 0, width_source, height_source); } //draw ctx.putImageData(img2, 0, 0); }; }); Template.SelectImageDialog.events({ 'click .close': function(event, template) { let data = Template.currentData(); if(data && data.onClose && typeof data.onClose === 'function') { data.onClose(); } }, 'mouseup .insertImageCanvas': function(event, template) { let input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.onchange = function() { template.readFile(this.files[0]); }; input.click(); }, 'click .select': function(event, template) { template.saveImage(); }, 'focusout .width': function(event, template) { let width = parseInt(event.target.value); if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get()); }, 'keypress .width': function(event, template) { if(event.which === 13) { let width = parseInt(event.target.value); if(width !== template.currentWidth.get()) template.changeWidth(event.target.value - template.currentWidth.get()); } }, 'click .widthLess': function(event, template) { template.changeWidth(-1); }, 'click .widthMore': function(event, template) { template.changeWidth(1); }, 'focusout .height': function(event, template) { let height = parseInt(event.target.value); if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get()); }, 'keypress .height': function(event, template) { if(event.which === 13) { let height = parseInt(event.target.value); if(height !== template.currentHeight.get()) template.changeHeight(height - template.currentHeight.get()); } }, 'click .heightLess': function(event, template) { template.changeHeight(-1); }, 'click .heightMore': function(event, template) { template.changeHeight(1); }, 'dragover .insertImageCanvas': function(event, template) { event.preventDefault(); }, 'drop .insertImageCanvas': function(event, template) { event.preventDefault(); let files = event.originalEvent.dataTransfer.files; if(files.length > 0) { let file = files[0]; if(typeof FileReader !== 'undefined' && file.type.indexOf("image") !== -1) { template.readFile(file); } } }, 'focusout .compression': function(event, template) { let compression = parseInt(event.target.value); if(compression !== template.currentCompression.get()) { template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0); template.collectStatistics(); } }, 'keypress .compression': function(event, template) { if(event.which === 13) { let compression = parseInt(event.target.value); if(compression !== template.currentCompress.get()) { template.currentCompression.set(compression >= 0 ? (compression <= 10 ? compression : 10) : 0); if(template.previewCompression.get()) template.rerender(); else template.collectStatistics(); } } }, 'click .compressionLess': function(event, template) { let compression = template.currentCompression.get() - 1; template.currentCompression.set(compression >= 0 ? compression : 0); if(template.previewCompression.get()) template.rerender(); else template.collectStatistics(); }, 'click .compressionMore': function(event, template) { let compression = template.currentCompression.get() + 1; template.currentCompression.set(compression <= 10 ? compression : 10); if(template.previewCompression.get()) template.rerender(); else template.collectStatistics(); }, 'change .lossyCompression': function(event, template) { template.lossyCompression.set(event.target.checked); template.collectStatistics(); }, 'change .previewCompression': function(event, template) { template.previewCompression.set(event.target.checked); template.rerender(); } }); Template.SelectImageDialog.helpers({ image: function() { return Template.instance().originalImage.get(); }, originalWidth: function() { let image = Template.instance().originalImage.get(); return image ? image.width : 0; }, originalHeight: function() { let image = Template.instance().originalImage.get(); return image ? image.height : 0; }, currentWidth: function() { return Template.instance().currentWidth.get(); }, currentHeight: function() { return Template.instance().currentHeight.get(); }, currentCompression() { return Template.instance().currentCompression.get(); }, lossyCompression() { return Template.instance().lossyCompression.get(); }, compressedSize() { return Template.instance().compressedSize.get(); }, previewCompression() { return Template.instance().previewCompression.get(); } });