Files
AVEF/imports/ui/dialogs/SelectImageDialog.js

382 lines
13 KiB
JavaScript

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();
}
});