382 lines
13 KiB
JavaScript
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();
|
|
}
|
|
}); |