Add miniPaint as new frontend base

This commit is contained in:
motion
2026-01-26 12:20:16 -05:00
parent bb859105ff
commit 6ae4cbcb77
306 changed files with 60729 additions and 4050 deletions
+82
View File
@@ -0,0 +1,82 @@
import config from "../../config";
import Base_layers_class from './../../core/base-layers.js';
import File_save_class from './../file/save.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Copy_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.File_save = new File_save_class();
//events
document.addEventListener('keydown', (event) => {
var code = event.key.toLowerCase();
var ctrlDown = event.ctrlKey || event.metaKey;
if (this.Helper.is_input(event.target))
return;
if (code == "c" && ctrlDown == true) {
//copy to clipboard
this.copy_to_clipboard();
}
}, false);
}
async copy_to_clipboard(){
var _this = this;
const canWriteToClipboard = await this.askWritePermission();
if (canWriteToClipboard) {
//get data - current layer
var canvas = this.Base_layers.convert_layer_to_canvas();
var ctx = canvas.getContext("2d");
if (config.TRANSPARENCY == false) {
//add white background
ctx.globalCompositeOperation = 'destination-over';
this.File_save.fillCanvasBackground(ctx, '#ffffff');
ctx.globalCompositeOperation = 'source-over';
}
//save using lib
canvas.toBlob(function (blob) {
_this.setToClipboard(blob);
});
}
else{
alertify.error('Missing permissions to write to Clipboard.cc');
}
}
async setToClipboard(blob) {
const data = [new ClipboardItem({ [blob.type]: blob })];
await navigator.clipboard.write(data);
}
async askWritePermission() {
try {
// The clipboard-write permission is granted automatically to pages
// when they are the active tab. So it's not required, but it's more safe.
const { state } = await navigator.permissions.query({ name: 'clipboard-write' })
return state === 'granted';
}
catch (error) {
// Browser compatibility / Security error (ONLY HTTPS) ...
return false;
}
}
}
export default Copy_class;
+10
View File
@@ -0,0 +1,10 @@
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Edit_paste_class {
paste() {
alertify.error('Use Ctrl+V keyboard shortcut to paste from Clipboard.');
}
}
export default Edit_paste_class;
+14
View File
@@ -0,0 +1,14 @@
import Base_state_class from './../../core/base-state.js';
class Edit_redo_class {
constructor() {
this.Base_state = new Base_state_class();
}
redo() {
this.Base_state.redo();
}
}
export default Edit_redo_class;
+26
View File
@@ -0,0 +1,26 @@
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Selection_class from './../../tools/selection.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Edit_selection_class {
constructor() {
this.Base_layers = new Base_layers_class();
this.Selection = new Selection_class(this.Base_layers.ctx);
}
select_all() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
this.Selection.select_all();
}
delete() {
this.Selection.delete_selection();
}
}
export default Edit_selection_class;
+31
View File
@@ -0,0 +1,31 @@
import Base_state_class from './../../core/base-state.js';
var instance = null;
class Edit_undo_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_state = new Base_state_class();
this.events();
}
events(){
var _this = this;
document.querySelector('#undo_button').addEventListener('click', function (event) {
_this.Base_state.undo();
});
}
undo() {
this.Base_state.undo();
}
}
export default Edit_undo_class;
@@ -0,0 +1,71 @@
import app from './../../../app.js';
import config from './../../../config.js';
import Dialog_class from './../../../libs/popup.js';
import Base_layers_class from './../../../core/base-layers.js';
import Helper_class from './../../../libs/helpers.js';
class Effects_common_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.params = null;
}
show_dialog(type, params, filter_id) {
var _this = this;
var title = this.Helper.ucfirst(type);
title = title.replace(/-/g, ' ');
var preview_padding = 0;
if(typeof this.preview_padding != "undefined"){
preview_padding = this.preview_padding;
}
var settings = {
title: title,
preview: true,
preview_padding: preview_padding,
effects: true,
params: params,
on_change: function (params, canvas_preview, w, h) {
_this.params = params;
canvas_preview.filter = _this.preview(params, type);
canvas_preview.drawImage(this.layer_active_small,
preview_padding, preview_padding,
_this.POP.width_mini - preview_padding * 2, _this.POP.height_mini - preview_padding * 2
);
},
on_finish: function (params) {
_this.params = params;
_this.save(params, type, filter_id);
},
};
this.Base_layers.disable_filter(filter_id);
this.POP.show(settings);
this.Base_layers.disable_filter(null);
}
save(params, type, filter_id) {
return app.State.do_action(
new app.Actions.Add_layer_filter_action(null, type, params, filter_id)
);
}
preview(params, type) {
if(type == 'shadow'){
type = 'drop-shadow';
}
var value = this.convert_value(params.value, params, 'preview');
return type + "(" + value + ")";
}
convert_value(value, params) {
return value;
}
}
export default Effects_common_class;
@@ -0,0 +1,219 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_backAndWhite_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
}
black_and_white() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//create tmp canvas
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//calc default level
var default_level = this.thresholding(ctx, canvas.width, canvas.height, true);
var settings = {
title: 'Black and White',
preview: true,
effects: true,
params: [
{name: "level", title: "Level:", value: default_level, range: [0, 255]},
{name: "dithering", title: "Dithering:", value: false},
],
on_change: function (params, canvas_preview, w, h) {
//check params
var level = document.getElementById("pop_data_level");
if (params.dithering == false)
level.disabled = false;
else
level.disabled = true;
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var W = data.width;
var H = data.height;
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = W;
canvas.height = H;
var imgData = data.data;
var grey, c, quant_error, m;
if (params.dithering !== true) {
//no differing
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
if (grey <= params.level)
c = 0;
else
c = 255;
imgData[i] = c;
imgData[i + 1] = c;
imgData[i + 2] = c;
}
}
else {
//FloydSteinberg dithering
var img2 = canvas.getContext("2d").getImageData(0, 0, W, H);
var imgData2 = img2.data;
for (var j = 0; j < H; j++) {
for (var i = 0; i < W; i++) {
var k = ((j * (W * 4)) + (i * 4));
if (imgData[k + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[k] + 0.7152 * imgData[k + 1] + 0.0722 * imgData[k + 2]);
grey = grey + imgData2[k]; //add data shft from previous iterations
c = Math.floor(grey / 256);
if (c == 1)
c = 255;
imgData[k] = c;
imgData[k + 1] = c;
imgData[k + 2] = c;
quant_error = grey - c;
if (i + 1 < W) {
m = k + 4;
imgData2[m] += Math.round(quant_error * 7 / 16);
}
if (i - 1 > 0 && j + 1 < H) {
m = k - 4 + W * 4;
imgData2[m] += Math.round(quant_error * 3 / 16);
}
if (j + 1 < H) {
m = k + W * 4;
imgData2[m] += Math.round(quant_error * 5 / 16);
}
if (i + 1 < W && j + 1 < H) {
m = k + 4 + W * 4;
imgData2[m] += Math.round(quant_error * 1 / 16);
}
}
}
}
return data;
}
thresholding(ctx, W, H, only_level) {
var img = ctx.getImageData(0, 0, W, H);
var imgData = img.data;
var hist_data = [];
var grey;
for (var i = 0; i <= 255; i++)
hist_data[i] = 0;
for (var i = 0; i < imgData.length; i += 4) {
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
hist_data[grey]++;
}
var level = this.otsu(hist_data, W * H);
if (only_level === true)
return level;
var c;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
if (grey < level)
c = 0;
else
c = 255;
imgData[i] = c;
imgData[i + 1] = c;
imgData[i + 2] = c;
}
ctx.putImageData(img, 0, 0);
}
//http://en.wikipedia.org/wiki/Otsu%27s_Method
otsu(histogram, total) {
var sum = 0;
for (var i = 1; i < 256; ++i)
sum += i * histogram[i];
var mB, mF, between;
var sumB = 0;
var wB = 0;
var wF = 0;
var max = 0;
var threshold = 0;
for (var i = 0; i < 256; ++i) {
wB += histogram[i];
if (wB == 0)
continue;
wF = total - wB;
if (wF == 0)
break;
sumB += i * histogram[i];
mB = sumB / wB;
mF = (sum - sumB) / wF;
between = wB * wF * Math.pow(mB - mF, 2);
if (between > max) {
max = between;
threshold = i;
}
}
return threshold;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var default_level = this.thresholding(ctx, canvas_thumb.width, canvas_thumb.height, true);
var params = {
level: default_level,
dithering: false,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_backAndWhite_class;
@@ -0,0 +1,157 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_blueprint_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.ImageFilters = ImageFilters;
this.fx_filter = false;
}
blueprint() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var ctx = canvas.getContext("2d");
//create blue layer
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.fillStyle = '#0e58a3';
ctx2.fillRect(0, 0, width, height);
//apply edges
var img = ctx.getImageData(0, 0, width, height);
var img = this.ImageFilters.Edge(img);
ctx.putImageData(img, 0, 0);
//denoise
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).denoise(20).update(); //effect
canvas = this.fx_filter;
//Brightness
var img = ctx.getImageData(0, 0, width, height);
var img = this.ImageFilters.BrightnessContrastPhotoshop(img, 80, 0);
ctx.putImageData(img, 0, 0);
//merge
ctx2.globalCompositeOperation = "screen";
ctx2.filter = 'grayscale(1)';
ctx2.drawImage(canvas, 0, 0);
ctx2.globalCompositeOperation = "source-over";
ctx2.filter = 'none';
//draw lines
this.draw_grid(ctx2, 20);
return canvas2;
}
/**
* draw grid
*
* @param {CanvasContext} ctx
* @param {Int} size
*/
draw_grid(ctx, size) {
if (this.grid == false)
return;
var width = config.WIDTH;
var height = config.HEIGHT;
var color_main = 'rgba(255, 255, 255, 0.5)';
var color_small = 'rgba(255, 255, 255, 0.1)';
//size
if (size != undefined && size != undefined)
this.grid_size = [size, size];
else {
size = this.grid_size[0];
size = this.grid_size[1];
}
size = parseInt(size);
size = parseInt(size);
ctx.lineWidth = 1;
ctx.beginPath();
if (size < 2)
size = 2;
if (size < 2)
size = 2;
for (var i = size; i < width; i = i + size) {
if (size == 0)
break;
if (i % (size * 5) == 0) {
//main lines
ctx.strokeStyle = color_main;
}
else {
//small lines
ctx.strokeStyle = color_small;
}
ctx.beginPath();
ctx.moveTo(0.5 + i, 0);
ctx.lineTo(0.5 + i, height);
ctx.stroke();
}
for (var i = size; i < height; i = i + size) {
if (size == 0)
break;
if (i % (size * 5) == 0) {
//main lines
ctx.strokeStyle = color_main;
}
else {
//small lines
ctx.strokeStyle = color_small;
}
ctx.beginPath();
ctx.moveTo(0, 0.5 + i);
ctx.lineTo(width, 0.5 + i);
ctx.stroke();
}
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var data = this.change(canvas, canvas_thumb.width, canvas_thumb.height);
ctx.drawImage(data, 0, 0);
}
}
export default Effects_blueprint_class;
+107
View File
@@ -0,0 +1,107 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Effects_browser_class from "./browser";
class Effects_borders_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Effects_browser = new Effects_browser_class();
}
borders(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var _this = this;
var filter = this.Base_layers.find_filter_by_id(filter_id, 'borders');
var settings = {
title: 'Borders',
params: [
{name: "color", title: "Color:", value: filter.color ??= config.COLOR, type: 'color'},
{name: "size", title: "Size:", value: filter.size ??= 10},
],
on_finish: function (params) {
var target = Math.min(config.WIDTH, config.HEIGHT);
_this.add_borders(params, filter_id);
},
};
var rotate = config.layer.rotate;
config.layer.rotate = 0;
this.Base_layers.disable_filter(filter_id);
this.POP.show(settings);
config.layer.rotate = rotate;
this.Base_layers.disable_filter(null);
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
ctx.drawImage(canvas_thumb,
5, 5,
this.Effects_browser.preview_width - 10, this.Effects_browser.preview_height - 10);
//add borders
ctx.strokeStyle = '#000000';
ctx.lineWidth = 10;
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.stroke();
}
render_pre(ctx, data) {
}
render_post(ctx, data, layer){
var size = Math.max(0, data.params.size);
var x = layer.x;
var y = layer.y;
var width = parseInt(layer.width);
var height = parseInt(layer.height);
//legacy check
if(x == null) x = 0;
if(y == null) y = 0;
if(!width) width = config.WIDTH;
if(!height) height = config.HEIGHT;
ctx.save();
//set styles
ctx.strokeStyle = data.params.color;
ctx.lineWidth = size;
//draw with rotation support
ctx.translate(layer.x + width / 2, layer.y + height / 2);
ctx.rotate(layer.rotate * Math.PI / 180);
var x_new = -width / 2;
var y_new = -height / 2;
ctx.beginPath();
ctx.rect(x_new - size * 0.5, y_new - size * 0.5, width + size, height + size);
ctx.stroke();
ctx.restore();
}
add_borders(params, filter_id) {
//apply effect
return app.State.do_action(
new app.Actions.Add_layer_filter_action(config.layer.id, 'borders', params, filter_id)
);
}
}
export default Effects_borders_class;
@@ -0,0 +1,88 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_boxBlur_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
box_blur() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Box blur',
preview: true,
effects: true,
params: [
{name: "param1", title: "H Radius:", value: 3, range: [1, 20]},
{name: "param2", title: "V Radius:", value: 3, range: [1, 20]},
{name: "param3", title: "Quality:", value: 3, range: [1, 20]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var param1 = params.param1;
var param2 = params.param2;
var param3 = params.param3;
var filtered = ImageFilters.BoxBlur(data, param1, param2, param3);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
param1: 20,
param2: 1,
param3: 1,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_boxBlur_class;
+140
View File
@@ -0,0 +1,140 @@
import config from './../../config.js';
import Base_tools_class from './../../core/base-tools.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_browser_class extends Base_tools_class {
constructor(ctx) {
super();
this.POP = new Dialog_class();
this.preview_width = 150;
this.preview_height = 120;
}
async browser() {
var _this = this;
var html = '';
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var data = this.get_effects_list();
for (var i in data) {
var title = data[i].title;
html += '<div class="item">';
html += ' <canvas id="c_' + data[i].key + '" width="' + this.preview_width + '" height="'
+ this.preview_height + '" class="effectsPreview" data-key="'
+ data[i].key + '"></canvas>';
html += '<div class="preview-item-title">' + title + '</div>';
html += '</div>';
}
for (var i = 0; i < 4; i++) {
html += '<div class="item"></div>';
}
var settings = {
title: 'Effects browser',
className: 'wide',
on_load: function (params, popup) {
var node = document.createElement("div");
node.classList.add('flex-container');
node.innerHTML = html;
popup.el.querySelector('.dialog_content').appendChild(node);
//events
var targets = popup.el.querySelectorAll('.item canvas');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', function (event) {
//we have click
var key = this.dataset.key;
for (var i in data) {
if(data[i].key == key){
var function_name = _this.get_function_from_path(key);
_this.POP.hide();
data[i].object[function_name]();
}
}
});
}
},
};
this.POP.show(settings);
//sleep, lets wait till DOM is finished
await new Promise(r => setTimeout(r, 10));
//generate thumb
var active_image = this.Base_layers.convert_layer_to_canvas();
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = this.preview_width;
canvas.height = this.preview_height;
ctx.scale(this.preview_width / active_image.width, this.preview_height / active_image.height);
ctx.drawImage(active_image, 0, 0);
ctx.scale(1, 1);
//draw demo thumbs
for (var i in data) {
var title = data[i].title;
var function_name = 'demo';
if(typeof data[i].object[function_name] == "undefined")
continue;
data[i].object[function_name]('c_'+data[i].key, canvas);
}
}
get_effects_list() {
var list = [];
for (var i in this.Base_gui.modules) {
if (i.indexOf("effects") == -1 || i.indexOf("abstract") > -1 || i.indexOf("browser") > -1)
continue;
list.push({
title: this.get_filter_title(i),
key: i,
object: this.Base_gui.modules[i],
});
}
list.sort(function(a, b) {
var nameA = a.title.toUpperCase();
var nameB = b.title.toUpperCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
return list;
}
get_filter_title(key) {
var parts = key.split("/");
var title = parts[parts.length - 1];
//exceptions
if (title == 'negative')
title = 'invert';
title = title.replace(/_/g, ' ');
title = title.charAt(0).toUpperCase() + title.slice(1); //make first letter uppercase
return title;
}
get_function_from_path(path){
var parts = path.split("/");
var result = parts[parts.length - 1];
result = result.replace(/-/, '_');
return result;
}
}
export default Effects_browser_class;
@@ -0,0 +1,68 @@
import config from '../../../config.js';
import Effects_common_class from '../abstract/css.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from './../../../core/base-layers.js';
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_blur_class extends Effects_common_class {
constructor() {
super();
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
blur(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'blur');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 5, range: [0, 50]},
];
this.show_dialog('blur', params, filter_id);
}
convert_value(value, params, type) {
//adapt size to real canvas dimensions
if (type == 'preview') {
var diff = (this.POP.width_mini / this.POP.height_mini) / (config.WIDTH / config.HEIGHT);
value = value * diff;
}
return value + 'px';
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(5, null, 'preview');
ctx.filter = "blur("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'blur(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_blur_class;
@@ -0,0 +1,68 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_brightness_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
brightness(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'brightness');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 50, range: [-100, 100]},
];
this.show_dialog('brightness', params, filter_id);
}
convert_value(value) {
var system_value;
if (value > 0) {
system_value = value / 100 + 1;
}
else if (value < 0) {
system_value = value / 100 + 1;
}
else {
system_value = 1;
}
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(30, null, 'preview');
ctx.filter = "brightness("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'brightness(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_brightness_class;
@@ -0,0 +1,69 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_contrast_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
contrast(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'contrast');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 40, range: [-100, 100]},
];
this.show_dialog('contrast', params, filter_id);
}
convert_value(value) {
var system_value;
if (value > 0) {
system_value = value / 100 + 1;
}
else if (value < 0) {
system_value = value / 100 + 1;
}
else {
system_value = 1;
}
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(40, null, 'preview');
ctx.filter = "contrast("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'contrast(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_contrast_class;
@@ -0,0 +1,60 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_grayscale_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
grayscale(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'grayscale');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 100, range: [0, 100]},
];
this.show_dialog('grayscale', params, filter_id);
}
convert_value(value) {
var system_value = value / 100;
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(100, null, 'preview');
ctx.filter = "grayscale("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'grayscale(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_grayscale_class;
@@ -0,0 +1,58 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_hueRotate_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
hue_rotate(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'hue-rotate');
var params = [
{name: "value", title: "Degree:", value: filter.value ??= 90, range: [0, 360]},
];
this.show_dialog('hue-rotate', params, filter_id);
}
convert_value(value) {
return value + 'deg';
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(90, null, 'preview');
ctx.filter = "hue-rotate("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'hue-rotate(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_hueRotate_class;
@@ -0,0 +1,59 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_invert_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
invert(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'invert');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 100, range: [0, 100]},
];
this.show_dialog('invert', params, filter_id);
}
convert_value(value) {
var system_value = value / 100;
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(100, null, 'preview');
ctx.filter = "invert("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'invert(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_invert_class;
@@ -0,0 +1,69 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_saturate_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
saturate(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'saturate');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= -50, range: [-100, 100]},
];
this.show_dialog('saturate', params, filter_id);
}
convert_value(value) {
var system_value;
if (value > 0) {
system_value = value / 100 + 1;
}
else if (value < 0) {
system_value = value / 100 + 1;
}
else {
system_value = 1;
}
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(-50, null, 'preview');
ctx.filter = "saturate("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'saturate(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_saturate_class;
@@ -0,0 +1,60 @@
import Effects_common_class from '../abstract/css.js';
import Base_layers_class from './../../../core/base-layers.js';
import config from "../../../config";
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_sepia_class extends Effects_common_class {
constructor() {
super();
this.Base_layers = new Base_layers_class();
}
sepia(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'sepia');
var params = [
{name: "value", title: "Percentage:", value: filter.value ??= 60, range: [0, 100]},
];
this.show_dialog('sepia', params, filter_id);
}
convert_value(value) {
var system_value = value / 100;
return system_value;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(60, null, 'preview');
ctx.filter = "sepia("+size+")";
ctx.drawImage(canvas_thumb, 0, 0);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'sepia(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_sepia_class;
@@ -0,0 +1,79 @@
import config from '../../../config.js';
import Effects_common_class from '../abstract/css.js';
import Dialog_class from '../../../libs/popup.js';
import Effects_browser_class from '../browser.js';
import Base_layers_class from './../../../core/base-layers.js';
import alertify from './../../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_brightness_class extends Effects_common_class {
constructor() {
super();
this.POP = new Dialog_class();
this.Effects_browser = new Effects_browser_class();
this.Base_layers = new Base_layers_class();
this.preview_padding = 20;
}
shadow(filter_id) {
if (config.layer.type == null) {
alertify.error('Layer is empty.');
return;
}
var filter = this.Base_layers.find_filter_by_id(filter_id, 'shadow');
var params = [
{name: "x", title: "Offset X:", value: filter.x ??= 10, range: [-100, 100]},
{name: "y", title: "Offset Y:", value: filter.y ??= 10, range: [-100, 100]},
{name: "value", title: "Radius:", value: filter.value ??= 5, range: [0, 100]},
{name: "color", title: "Color:", value: filter.color ??= "#000000", type: 'color'},
];
this.show_dialog('shadow', params, filter_id);
}
convert_value(value, params, type) {
var system_value = value;
//adapt size to real canvas dimensions
if (type == 'preview') {
var diff = (this.POP.width_mini / this.POP.height_mini) / (config.WIDTH / config.HEIGHT);
params.x = params.x * (this.POP.width_mini / config.WIDTH);
params.y = params.y * (this.POP.height_mini / config.HEIGHT);
params.value = params.value * diff;
}
return params.x + "px " + params.y + "px " + params.value + "px " + params.color;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//draw
var size = this.convert_value(null, {x: 5, y: 5, value: 5, color: '#000000'}, 'preview');
ctx.filter = "drop-shadow("+size+")";
ctx.drawImage(canvas_thumb,
10, 10,
this.Effects_browser.preview_width - 20, this.Effects_browser.preview_height - 20);
ctx.filter = 'none';
}
render_pre(ctx, data) {
var value = this.convert_value(data.params.value, data.params, 'save');
var filter = 'drop-shadow(' + value + ')';
if(ctx.filter == 'none')
ctx.filter = filter;
else
ctx.filter += ' ' + filter;
}
render_post(ctx, data){
ctx.filter = 'none';
}
}
export default Effects_brightness_class;
@@ -0,0 +1,89 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_denoise_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
denoise() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Denoise',
preview: true,
effects: true,
params: [
{name: "param1", title: "Exponent:", value: 20, range: [0, 50]},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
var data = _this.change(canvas_, params);
canvas_preview.clearRect(0, 0, canvas_.width, canvas_.height);
canvas_preview.drawImage(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, params);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var param1 = parseFloat(params.param1);
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).denoise(param1).update(); //effect
return this.fx_filter;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {
param1: 20,
};
var data = this.change(canvas_thumb, params);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_denoise_class;
+82
View File
@@ -0,0 +1,82 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_dither_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
dither() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Dither',
preview: true,
effects: true,
params: [
{name: "param1", title: "Levels:", value: "8", range: [2, 32]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var param1 = parseFloat(params.param1);
var filtered = ImageFilters.Dither(data, param1);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
param1: 8,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_dither_class;
@@ -0,0 +1,89 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_dotScreen_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
dot_screen() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Dot Screen',
preview: true,
effects: true,
params: [
{name: "size", title: "Size:", value: "3", range: [1, 20]},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
var data = _this.change(canvas_, params);
canvas_preview.clearRect(0, 0, canvas_.width, canvas_.height);
canvas_preview.drawImage(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, params);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var size = parseFloat(params.size);
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).dotScreen(Math.round(canvas.width / 2), Math.round(canvas.height / 2), 0, size).update();
return this.fx_filter;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {
size: 3,
};
var data = this.change(canvas_thumb, params);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_dotScreen_class;
+55
View File
@@ -0,0 +1,55 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_edge_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
edge() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {
var filtered = ImageFilters.Edge(data);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_edge_class;
+55
View File
@@ -0,0 +1,55 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_emboss_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
emboss() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {
var filtered = ImageFilters.Emboss(data);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_emboss_class;
+76
View File
@@ -0,0 +1,76 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_enrich_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
enrich() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Enrich',
preview: true,
effects: true,
params: [],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var filtered = ImageFilters.Enrich(data);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_enrich_class;
+111
View File
@@ -0,0 +1,111 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_grains_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
}
grains() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Grains',
preview: true,
effects: true,
params: [
{name: "level", title: "Level:", value: "30", range: [0, 50]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
if (params.level == 0)
return data;
var imgData = data.data;
var H = data.height;
var W = data.width;
for (var j = 0; j < H; j++) {
for (var i = 0; i < W; i++) {
var x = (i + j * W) * 4;
if (imgData[x + 3] == 0)
continue; //transparent
//increase it's lightness
var delta = this.Helper.getRandomInt(0, params.level);
if (delta == 0)
continue;
if (imgData[x] - delta < 0)
imgData[x] = -(imgData[x] - delta);
else
imgData[x] = imgData[x] - delta;
if (imgData[x + 1] - delta < 0)
imgData[x + 1] = -(imgData[x + 1] - delta);
else
imgData[x + 1] = imgData[x + 1] - delta;
if (imgData[x + 2] - delta < 0)
imgData[x + 2] = -(imgData[x + 2] - delta);
else
imgData[x + 2] = imgData[x + 2] - delta;
}
}
return data;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
level: 30,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_grains_class;
+111
View File
@@ -0,0 +1,111 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_heatmap_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
heatmap() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {
var imgData = data.data;
var grey, RGB;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
RGB = this.color2heat(grey);
imgData[i] = RGB.R;
imgData[i + 1] = RGB.G;
imgData[i + 2] = RGB.B;
}
return data;
}
color2heat(value) {
var RGB = {R: 0, G: 0, B: 0};
value = value / 255;
if (0 <= value && value <= 1 / 8) {
RGB.R = 0;
RGB.G = 0;
RGB.B = 4 * value + .5; // .5 - 1 // b = 1/2
}
else if (1 / 8 < value && value <= 3 / 8) {
RGB.R = 0;
RGB.G = 4 * value - .5; // 0 - 1 // b = - 1/2
RGB.B = 1; // small fix
}
else if (3 / 8 < value && value <= 5 / 8) {
RGB.R = 4 * value - 1.5; // 0 - 1 // b = - 3/2
RGB.G = 1;
RGB.B = -4 * value + 2.5; // 1 - 0 // b = 5/2
}
else if (5 / 8 < value && value <= 7 / 8) {
RGB.R = 1;
RGB.G = -4 * value + 3.5; // 1 - 0 // b = 7/2
RGB.B = 0;
}
else if (7 / 8 < value && value <= 1) {
RGB.R = -4 * value + 4.5; // 1 - .5 // b = 9/2
RGB.G = 0;
RGB.B = 0;
}
else {
// should never happen - value > 1
RGB.R = .5;
RGB.G = 0;
RGB.B = 0;
}
// scale for hex conversion
RGB.R *= 255;
RGB.G *= 255;
RGB.B *= 255;
RGB.R = Math.round(RGB.R);
RGB.G = Math.round(RGB.G);
RGB.B = Math.round(RGB.B);
return RGB;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_heatmap_class;
@@ -0,0 +1,72 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_1977_class {
constructor() {
this.POP = new Dialog_class();
//this.Color_matrix = new Color_matrix_class();
this.Base_layers = new Base_layers_class();
}
1977() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "screen";
ctx2.fillStyle = 'rgba(243, 106, 188, 0.3)';
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'contrast(1.1) brightness(1.1) saturate(1.3)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_1977_class;
@@ -0,0 +1,74 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_aden_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
aden() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
var gradient = ctx2.createLinearGradient(0, 0, width, height);
gradient.addColorStop(0, "rgba(66, 10, 14, 0.2)");
gradient.addColorStop(1, "rgba(66, 10, 14, 0.2)");
ctx2.fillStyle = gradient;
ctx2.fillRect(0, 0, width, height);
//merge
ctx2.globalCompositeOperation = "darken";
ctx2.drawImage(canvas, 0, 0);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'hue-rotate(-20deg) contrast(0.9) saturate(0.85) brightness(1.2)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_aden_class;
@@ -0,0 +1,72 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_clarendon_class {
constructor() {
this.POP = new Dialog_class();
//this.Color_matrix = new Color_matrix_class();
this.Base_layers = new Base_layers_class();
}
clarendon() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.fillStyle = 'rgba(127, 187, 227, 0.2)';
ctx2.fillRect(0, 0, width, height);
//merge
ctx2.globalCompositeOperation = "overlay";
ctx2.drawImage(canvas, 0, 0);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'contrast(1.2) saturate(1.35)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_clarendon_class;
@@ -0,0 +1,71 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_gingham_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
gingham() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "soft-light";
ctx2.fillStyle = '#e6e6fa';
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'brightness(1.05) hue-rotate(-10deg)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_gingham_class;
@@ -0,0 +1,69 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
/*
https://github.com/una/CSSgram/blob/master/source/css/toaster.css
https://github.com/vigetlabs/canvas-instagram-filters
*/
class Effects_inkwell_class {
constructor() {
this.POP = new Dialog_class();
//this.Color_matrix = new Color_matrix_class();
this.Base_layers = new Base_layers_class();
}
inkwell() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
//apply more effects
ctx2.filter = 'sepia(0.3) contrast(1.1) brightness(1.1) grayscale(1)';
ctx2.drawImage(canvas, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_inkwell_class;
@@ -0,0 +1,75 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_lofi_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
lofi() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "multiply";
var min = Math.min(width, height);
var gradient = ctx2.createRadialGradient(width / 2, height / 2, min * 0.7, width / 2, height / 2, min * 1.5);
gradient.addColorStop(0, "rgba(0,0,0,0)");
gradient.addColorStop(1, "#222222");
ctx2.fillStyle = gradient;
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'saturate(1.1) contrast(1.5)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_lofi_class;
@@ -0,0 +1,79 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
/*
https://github.com/una/CSSgram/blob/master/source/css/toaster.css
https://github.com/vigetlabs/canvas-instagram-filters
*/
class Effects_toaster_class {
constructor() {
this.POP = new Dialog_class();
//this.Color_matrix = new Color_matrix_class();
this.Base_layers = new Base_layers_class();
}
toaster() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "screen";
var gradient = ctx2.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, width * 0.6);
gradient.addColorStop(0, "#804e0f");
gradient.addColorStop(1, "#3b003b");
ctx2.fillStyle = gradient;
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'contrast(1.5) brightness(0.9)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_toaster_class;
@@ -0,0 +1,71 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_valencia_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
valencia() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "exclusion";
ctx2.fillStyle = '3a0339';
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'contrast(1.08) brightness(1.08) sepia(0.08)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_valencia_class;
@@ -0,0 +1,75 @@
import app from '../../../app.js';
import config from '../../../config.js';
import Dialog_class from '../../../libs/popup.js';
import Base_layers_class from '../../../core/base-layers.js';
import alertify from 'alertifyjs/build/alertify.min.js';
class Effects_xpro2_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
xpro2() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
//create temp canvas
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
//merge
ctx2.globalCompositeOperation = "color-burn";
var min = Math.min(width, height);
var gradient = ctx2.createRadialGradient(width / 2, height / 2, min * 0.4, width / 2, height / 2, min * 1.1);
gradient.addColorStop(0, "#e6e7e0");
gradient.addColorStop(1, "rgba(43, 42, 161, 0.6)");
ctx2.fillStyle = gradient;
ctx2.fillRect(0, 0, width, height);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'sepia(0.3)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_xpro2_class;
+86
View File
@@ -0,0 +1,86 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_mosaic_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
mosaic() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Mosaic',
preview: true,
effects: true,
params: [
{name: "size", title: "Size:", value: 10, range: [1, 100]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var size = parseFloat(params.size);
//convert % to px
size = Math.min(data.width, data.height) * size / 100;
size = Math.round(size);
var filtered = ImageFilters.Mosaic(data, size);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
size: 10,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_mosaic_class;
@@ -0,0 +1,82 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import ImageFilters_class from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_nightVision_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
this.ImageFilters = ImageFilters_class;
}
night_vision() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
//create second copy
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, 0, 0);
// green overlay
var img = ctx2.getImageData(0, 0, width, height);
//RGB corrections
var img = this.ImageFilters.ColorTransformFilter(img, 1, 1, 1, 1, 0, 100, 0, 1);
//hue/saturation/luminance
var img = this.ImageFilters.HSLAdjustment(img, 0, 0, -50);
ctx2.putImageData(img, 0, 0);
//vignete
var texture = this.fx_filter.texture(canvas2);
this.fx_filter.draw(texture).vignette(0.2, 0.9).update(); //effect
canvas2 = this.fx_filter;
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {};
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_nightVision_class;
+85
View File
@@ -0,0 +1,85 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_oil_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
oil() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Oil',
preview: true,
effects: true,
params: [
{name: "param1", title: "Range:", value: 2, range: [1, 10]},
{name: "param2", title: "Levels:", value: "32", range: [1, 256]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var param1 = parseFloat(params.param1);
var param2 = parseInt(params.param2);
var filtered = ImageFilters.Oil(data, param1, param2);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
param1: 2,
param2: 32,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_oil_class;
+73
View File
@@ -0,0 +1,73 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_pencil_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
pencil() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {
var offset = Math.min(width, height) / 1000;
offset = Math.ceil(offset);
//create second copy
var canvas2 = document.createElement('canvas');
var ctx2 = canvas2.getContext("2d");
canvas2.width = width;
canvas2.height = height;
ctx2.drawImage(canvas, -offset, -offset);
//merge
ctx2.globalCompositeOperation = "difference";
ctx2.drawImage(canvas, 0, 0);
ctx2.globalCompositeOperation = "source-over";
//apply more effects
ctx2.filter = 'brightness(2) invert(1) grayscale(1)';
ctx2.drawImage(canvas2, 0, 0);
ctx2.filter = 'none';
return canvas2;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {};
var data = this.change(canvas_thumb, canvas_thumb.width, canvas_thumb.height);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_pencil_class;
@@ -0,0 +1,82 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_sharpen_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
sharpen() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Sharpen',
preview: true,
effects: true,
params: [
{name: "param1", title: "Factor:", value: "3", range: [1, 10], step: 0.1},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {
var param1 = parseFloat(params.param1);
var filtered = ImageFilters.Sharpen(data, param1);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var params = {
param1: 3,
}
var data = this.change(img, params);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_sharpen_class;
@@ -0,0 +1,55 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_solarize_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
solarize() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {
var filtered = ImageFilters.Solarize(data);
return filtered;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var img = ctx.getImageData(0, 0, canvas_thumb.width, canvas_thumb.height);
var data = this.change(img);
ctx.putImageData(data, 0, 0);
}
}
export default Effects_solarize_class;
@@ -0,0 +1,144 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import ImageFilters from './../../libs/imagefilters.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_tiltShift_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
tilt_shift() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Tilt Shift',
preview: true,
effects: true,
params: [
//extra
{name: "param7", title: "Saturation:", value: "3", range: [0, 20]},
{name: "param8", title: "Sharpen:", value: "1", range: [0, 5]},
//main
{name: "param1", title: "Blur Radius:", value: 10, range: [0, 30]},
{name: "param2", title: "Gradient Radius:", value: 70, range: [40, 100]},
//startX, startY, endX, endY
{name: "param3", title: "X start:", value: 0, range: [0, 100]},
{name: "param4", title: "Y start:", value: 50, range: [0, 100]},
{name: "param5", title: "X end:", value: 100, range: [0, 100]},
{name: "param6", title: "Y end:", value: 50, range: [0, 100]},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
//recalc param by size
_this.change(canvas_, params);
//convert % to px for line
params.param3 = canvas_.width * params.param3 / 100;
params.param4 = canvas_.height * params.param4 / 100;
params.param5 = canvas_.width * params.param5 / 100;
params.param6 = canvas_.height * params.param6 / 100;
//draw line
canvas_preview.beginPath();
canvas_preview.strokeStyle = "#ff0000";
canvas_preview.lineWidth = 1;
canvas_preview.moveTo(params.param3 + 0.5, params.param4 + 0.5);
canvas_preview.lineTo(params.param5 + 0.5, params.param6 + 0.5);
canvas_preview.stroke();
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
this.change(canvas, params);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var param1 = parseInt(params.param1);
var param2 = parseInt(params.param2);
var param3 = parseInt(params.param3);
var param4 = parseInt(params.param4);
var param5 = parseInt(params.param5);
var param6 = parseInt(params.param6);
var param7 = parseInt(params.param7);
var param8 = parseInt(params.param8);
//convert % to px
param1 = canvas.height * param1 / 100;
param2 = canvas.height * param2 / 100;
param3 = canvas.width * param3 / 100;
param4 = canvas.height * param4 / 100;
param5 = canvas.width * param5 / 100;
param6 = canvas.height * param6 / 100;
var ctx = canvas.getContext("2d");
//main effect
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).tiltShift(param3, param4, param5, param6, param1, param2).update();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(this.fx_filter, 0, 0);
//saturation
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = ImageFilters.HSLAdjustment(data, 0, param7, 0);
ctx.putImageData(data, 0, 0);
//sharpen
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = ImageFilters.Sharpen(data, param8);
ctx.putImageData(data, 0, 0);
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var params = {
param7: 3,
param8: 1,
param1: 10,
param2: 70,
param3: 0,
param4: 50,
param5: 100,
param6: 50,
}
var data = this.change(canvas, params);
}
}
export default Effects_tiltShift_class;
@@ -0,0 +1,89 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_vibrance_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
vibrance() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Vibrance',
preview: true,
effects: true,
params: [
{name: "level", title: "Level:", value: "0.5", range: [-1, 1], step: 0.01},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
var data = _this.change(canvas_, params);
canvas_preview.clearRect(0, 0, canvas_.width, canvas_.height);
canvas_preview.drawImage(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, params);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var param1 = parseFloat(params.level);
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).vibrance(param1).update(); //effect
return this.fx_filter;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {
level: 0.5,
};
var data = this.change(canvas_thumb, params);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_vibrance_class;
@@ -0,0 +1,92 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_vignette_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
vignette() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Vignette',
preview: true,
effects: true,
params: [
{name: "param1", title: "Level:", value: "0.5", range: [0, 1], step: 0.01},
{name: "param2", title: "Size:", value: "0.5", range: [0, 1], step: 0.01},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
var data = _this.change(canvas_, params);
canvas_preview.clearRect(0, 0, canvas_.width, canvas_.height);
canvas_preview.drawImage(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, params);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var param1 = parseFloat(params.param1);
var param2 = parseFloat(params.param2);
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).vignette(param1, param2).update(); //effect
return this.fx_filter;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {
param1: 0.5,
param2: 0.5,
};
var data = this.change(canvas_thumb, params);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_vignette_class;
@@ -0,0 +1,77 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import Vintage_class from './../../libs/vintage.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_vintage_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Vintage = new Vintage_class(config.WIDTH, config.HEIGHT);
}
vintage() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
this.Vintage.reset_random_values(config.WIDTH, config.HEIGHT);
var settings = {
title: 'Vintage',
preview: true,
effects: true,
params: [
{name: "level", title: "Level:", value: 50, range: [0, 100]},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
_this.change(canvas_, params);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
this.change(canvas, params);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
var level = parseInt(params.level);
this.Vintage.apply_all(canvas, level);
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
ctx.drawImage(canvas_thumb, 0, 0);
//now update
var params = {
level: 50,
};
this.change(canvas, params);
}
}
export default Effects_vintage_class;
@@ -0,0 +1,102 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import glfx from './../../libs/glfx.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Effects_zoomBlur_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.fx_filter = false;
}
zoom_blur() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get layer size
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var settings = {
title: 'Zoom blur',
preview: true,
effects: true,
params: [
{name: "param1", title: "Strength:", value: "0.3", range: [0, 1], step: 0.01},
{name: "param2", title: "Center x:", value: Math.round(canvas.width / 2), range: [0, canvas.width]},
{name: "param3", title: "Center y:", value: Math.round(canvas.height / 2), range: [0, canvas.height]},
],
on_change: function (params, canvas_preview, w, h, canvas_) {
//recalc param by size
params.param2 = params.param2 / canvas.width * w;
params.param3 = params.param3 / canvas.height * h;
var data = _this.change(canvas_, params);
canvas_preview.clearRect(0, 0, canvas_.width, canvas_.height);
canvas_preview.drawImage(data, 0, 0);
},
on_finish: function (params) {
_this.save(params);
},
};
this.POP.show(settings);
}
save(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var data = this.change(canvas, params);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var param1 = parseFloat(params.param1);
var param2 = parseInt(params.param2);
var param3 = parseInt(params.param3);
var texture = this.fx_filter.texture(canvas);
this.fx_filter.draw(texture).zoomBlur(param2, param3, param1).update(); //effect
return this.fx_filter;
}
demo(canvas_id, canvas_thumb){
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext("2d");
//modify
var params = {
param1: 0.3,
param2: Math.round(canvas_thumb.width / 2),
param3: Math.round(canvas_thumb.height / 2),
};
var data = this.change(canvas_thumb, params);
//draw
ctx.drawImage(data, 0, 0);
}
}
export default Effects_zoomBlur_class;
+137
View File
@@ -0,0 +1,137 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_gui_class from './../../core/base-gui.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import Dialog_class from './../../libs/popup.js';
import Tools_settings_class from './../tools/settings.js';
/**
* manages files / new
*
* @author ViliusL
*/
class File_new_class {
constructor() {
this.Base_gui = new Base_gui_class();
this.Base_layers = new Base_layers_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
this.Tools_settings = new Tools_settings_class();
}
new () {
var _this = this;
var width = config.WIDTH;
var height = config.HEIGHT;
var common_dimensions = this.Base_gui.common_dimensions;
var resolution_types = ['Custom'];
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
for (var i in common_dimensions) {
var value = common_dimensions[i];
resolution_types.push(value[0] + 'x' + value[1] + ' - ' + value[2]);
}
var transparency_cookie = this.Helper.getCookie('transparency');
if (transparency_cookie === null) {
//default
transparency_cookie = false;
}
if (transparency_cookie) {
var transparency = true;
}
else {
var transparency = false;
}
//convert units
width = this.Helper.get_user_unit(width, units, resolution);
height = this.Helper.get_user_unit(height, units, resolution);
var settings = {
title: 'New file',
params: [
{name: "width", title: "Width:", value: width, comment: units},
{name: "height", title: "Height:", value: height, comment: units},
{name: "resolution_type", title: "Resolution:", values: resolution_types},
{name: "layout", title: "Layout:", value: "Custom", values: ["Custom", "Landscape", "Portrait"]},
{name: "transparency", title: "Transparent:", value: transparency},
],
on_finish: function (params) {
_this.new_handler(params);
},
};
this.POP.show(settings);
}
async new_handler(response) {
var width = parseFloat(response.width);
var height = parseFloat(response.height);
var resolution_type = response.resolution_type;
var transparency = response.transparency;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
if (resolution_type != 'Custom') {
var dim = resolution_type.split(" ");
dim = dim[0].split("x");
width = parseInt(dim[0]);
height = parseInt(dim[1]);
if(response.layout == 'Portrait'){
var tmp = width;
width = height;
height = tmp;
}
}
else {
//convert units
width = this.Helper.get_internal_unit(width, units, resolution);
height = this.Helper.get_internal_unit(height, units, resolution);
}
// Prepare layers
app.State.do_action(
new app.Actions.Bundle_action('new_file', 'New File', [
new app.Actions.Refresh_action_attributes_action('undo'),
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
TRANSPARENCY: !!transparency,
WIDTH: parseInt(width),
HEIGHT: parseInt(height),
ALPHA: 255,
COLOR: '#008000',
mouse: {},
visible_width: null,
visible_height: null,
user_fonts: {}
}),
new app.Actions.Prepare_canvas_action('do'),
new app.Actions.Refresh_action_attributes_action('do'),
new app.Actions.Reset_layers_action(),
new app.Actions.Init_canvas_zoom_action(),
new app.Actions.Insert_layer_action({})
])
);
//sleep, lets wait till DOM is finished
await new Promise(r => setTimeout(r, 10));
//fit to screen?
this.Base_gui.GUI_preview.zoom_auto(true);
// Save transparency
if (transparency) {
this.Helper.setCookie('transparency', 1);
}
else {
this.Helper.setCookie('transparency', 0);
}
}
}
export default File_new_class;
+708
View File
@@ -0,0 +1,708 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Base_gui_class from './../../core/base-gui.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import Clipboard_class from './../../libs/clipboard.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import EXIF from './../../../../node_modules/exif-js/exif.js';
import GUI_tools_class from "../../core/gui/gui-tools";
import semver_compare from './../../../../node_modules/semver-compare/';
var instance = null;
/**
* manages files / open
*
* @author ViliusL
*/
class File_open_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
var _this = this;
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.GUI_tools = new GUI_tools_class();
//clipboard class
this.Clipboard_class = new Clipboard_class(function (data, w, h) {
_this.on_paste(data, w, h);
});
this.events();
this.maybe_file_open_url_handler();
}
events() {
var _this = this;
window.ondrop = function (e) {
//drop
e.preventDefault();
_this.open_handler(e);
};
window.ondragover = function (e) {
e.preventDefault();
};
document.addEventListener('keydown', (event) => {
var code = event.key.toLowerCase();
if (this.Helper.is_input(event.target))
return;
if (code == "o") {
//open
this.open_file();
event.preventDefault();
}
}, false);
}
on_paste(data, width, height) {
var new_layer = {
name: 'Paste',
type: 'image',
data: data,
};
app.State.do_action(
new app.Actions.Insert_layer_action(new_layer)
);
}
open_file() {
var _this = this;
alertify.success('You can also drag and drop items into browser.');
document.getElementById("tmp").innerHTML = '';
var a = document.createElement('input');
a.setAttribute("id", "file_open");
a.type = 'file';
a.multiple = 'multiple';
document.getElementById("tmp").appendChild(a);
document.getElementById('file_open').addEventListener('change', function (e) {
_this.open_handler(e);
}, false);
//force click
document.querySelector('#file_open').click();
}
open_webcam(){
var _this = this;
var video = document.createElement('video');
video.autoplay = true;
video.style.maxWidth = '100%';
var track = null;
function handleSuccess(stream) {
track = stream.getTracks()[0];
video.srcObject = stream;
}
function handleError(error) {
alertify.error('Sorry, cold not load getUserMedia() data: ' + error);
}
var settings = {
title: 'Webcam',
params: [
{title: "Stream:", html: '<div id="webcam_container"></div>'},
],
on_load: function(params){
document.getElementById('webcam_container').appendChild(video);
},
on_finish: function(params){
//capture data
var width = video.videoWidth;
var height = video.videoHeight;
var tmpCanvas = document.createElement('canvas');
var tmpCanvasCtx = tmpCanvas.getContext("2d");
tmpCanvas.width = width;
tmpCanvas.height = height;
tmpCanvasCtx.drawImage(video, 0, 0);
//create requested layer
var new_layer = {
name: "Webcam #" + _this.Base_layers.auto_increment,
type: 'image',
data: tmpCanvas.toDataURL("image/png"),
width: width,
height: height,
width_original: width,
height_original: height,
};
app.State.do_action(
new app.Actions.Bundle_action('open_file_webcam', 'Open File Webcam', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(width, height, null, true, true)
])
);
//destroy
if(track != null){
track.stop();
}
video.pause();
video.src = "";
video.load();
},
on_cancel: function(params){
if(track != null){
track.stop();
}
video.pause();
video.src = "";
video.load();
},
};
this.POP.show(settings);
navigator.mediaDevices.getUserMedia({audio: false, video: true})
.then(handleSuccess)
.catch(handleError);
}
open_dir() {
var _this = this;
document.getElementById("tmp").innerHTML = '';
var a = document.createElement('input');
a.setAttribute("id", "file_open_dir");
a.type = 'file';
a.webkitdirectory = 'webkitdirectory';
document.getElementById("tmp").appendChild(a);
document.getElementById('file_open_dir').addEventListener('change', function (e) {
_this.open_handler(e);
}, false);
//force click
document.querySelector('#file_open_dir').click();
}
/**
* opens data URLs, like: "data:image/png;base64,xxxxxx"
*
* data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAG0lEQVQYV2N89+7df0FBQQbG/////3///j0DAF9wCsg9spQfAAAAAElFTkSuQmCC
*/
open_data_url() {
var _this = this;
var settings = {
title: 'Open data URL',
params: [
{name: "data", title: "Data URL:", type: "textarea", value: ""},
],
on_finish: function (params) {
_this.file_open_data_url_handler(params.data);
},
};
this.POP.show(settings);
}
file_open_data_url_handler(data) {
var _this = this;
if (data == '')
return;
var img = new Image();
img.crossOrigin = "Anonymous";
img.onload = function () {
var new_layer = {
name: "Data URL",
type: 'image',
link: img,
width: img.width,
height: img.height,
width_original: img.width,
height_original: img.height,
};
app.State.do_action(
new app.Actions.Bundle_action('open_file_data_url', 'Open File Data URL', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(img.width, img.height, null, true, true)
])
);
img.onload = function () {
config.need_render = true;
};
};
img.onerror = function (ex) {
alertify.error('Sorry, image could not be loaded. Try copy image and paste it.');
};
img.src = data;
}
open_url() {
var _this = this;
var settings = {
title: 'Open URL',
params: [
{name: "url", title: "URL:", value: ""},
],
on_finish: function (params) {
_this.file_open_url_handler(params);
},
};
this.POP.show(settings);
}
async open_handler(e) {
var _this = this;
var files = e.target.files;
var auto_increment = this.Base_layers.auto_increment;
if (files == undefined) {
//drag and drop
files = e.dataTransfer.files;
}
//sort
var orders = [];
for (var i = 0, f; i < files.length; i++) {
orders.push(files[i].name);
}
orders.sort();
var order_map = [];
for (var i in orders) {
order_map[orders[i]] = parseInt(i);
}
//check if dropped directory
var dir_opened = false;
if (e.dataTransfer && e.dataTransfer.items) {
var items = e.dataTransfer.items;
for (var i=0; i<items.length; i++) {
var item = items[i].webkitGetAsEntry();
if(item && item.isDirectory){
dir_opened = true;
}
}
}
for (var i = 0, f; i < files.length; i++) {
f = files[i];
if (!f.type.match('image.*') && !f.name.match('.json')) {
if(dir_opened == false) {
alertify.error('Wrong file type, must be image or json.');
}
continue;
}
if (files.length == 1) {
this.SAVE_NAME = f.name.split('.')[f.name.split('.').length - 2];
}
var FR = new FileReader();
FR.file = files[i];
FR.onload = function (event) {
if (this.file.type.match('image.*')) {
var order = auto_increment + order_map[this.file.name];
//image
var new_layer = {
name: this.file.name,
type: 'image',
data: event.target.result,
order: order,
_exif: _this.extract_exif(this.file)
};
app.State.do_action(
new app.Actions.Bundle_action('open_image', 'Open Image', [
new app.Actions.Insert_layer_action(new_layer)
])
);
}
else {
//json
var response = _this.load_json(event.target.result);
if (response === true) {
return false;
}
}
};
if (f.type == "text/plain")
FR.readAsText(f);
else if (f.name.match('.json'))
FR.readAsText(f);
else
FR.readAsDataURL(f);
//sleep after last image import, it maybe not be finished yet
await new Promise(r => setTimeout(r, 10));
}
//try to open dropped directory
if (e.dataTransfer && e.dataTransfer.items) {
var items = e.dataTransfer.items;
for (var i=0; i<items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item && item.isDirectory == true) {
this.traverseFileTree(item);
}
}
}
}
traverseFileTree(item, path) {
var _this = this;
var auto_increment = this.Base_layers.auto_increment;
path = path || "";
if (item.isFile) {
item.file(async function(file) {
var FR = new FileReader();
FR.file = file;
FR.onload = function (event) {
if (this.file.type.match('image.*')
//below is fix for firefox, it has empty type
|| (this.file.type == '' && this.file.name.match(/\.(png|jpg|jpeg|webp|gif|avif)/g))) {
//image
var new_layer = {
name: this.file.name,
type: 'image',
data: event.target.result,
_exif: _this.extract_exif(this.file)
};
app.State.do_action(
new app.Actions.Bundle_action('open_image', 'Open Image', [
new app.Actions.Insert_layer_action(new_layer)
])
);
}
};
FR.readAsDataURL(file);
//sleep after last image import, it maybe not be finished yet
await new Promise(r => setTimeout(r, 10));
});
}
else if (item.isDirectory) {
// Get folder contents
var dirReader = item.createReader();
dirReader.readEntries(function(entries) {
for (var i=0; i<entries.length; i++) {
_this.traverseFileTree(entries[i], path + item.name + "/");
}
});
}
}
open_template_test(){
var _this = this;
this.Base_layers.debug_rendering = true;
window.fetch("images/test-collection.json").then(function(response) {
return response.json();
}).then(function(json) {
_this.load_json(json, false);
}).catch(function(ex) {
alertify.error('Sorry, image could not be loaded.');
});
}
/**
* check if url has url params, for example: https://viliusle.github.io/miniPaint/?image=http://i.imgur.com/ATda8Ae.jpg
*/
maybe_file_open_url_handler() {
var _this = this;
var url_params = this.Helper.get_url_parameters();
if (url_params.image != undefined) {
this.open_resource(url_params.image);
}
}
/**
* includes provided resource (image or json)
*
* @param string resource_url
*/
open_resource(resource_url) {
var _this = this;
if(resource_url.toLowerCase().indexOf('.json') == resource_url.length - 5){
//load json
window.fetch(resource_url).then(function(response) {
return response.json();
}).then(function(json) {
_this.load_json(json, false);
}).catch(function(ex) {
alertify.error('Sorry, image could not be loaded.');
});
}
else{
//load image
var data = {
url: resource_url,
};
this.file_open_url_handler(data);
}
}
//handler for open url. Example url: http://i.imgur.com/ATda8Ae.jpg
file_open_url_handler(user_response) {
var _this = this;
var url = user_response.url;
if (url == '')
return;
var layer_name = url.replace(/^.*[\\\/]/, '');
var img = new Image();
img.crossOrigin = "Anonymous";
img.onload = function () {
var new_layer = {
name: layer_name,
type: 'image',
link: img,
width: img.width,
height: img.height,
width_original: img.width,
height_original: img.height,
};
img.onload = function () {
config.need_render = true;
};
app.State.do_action(
new app.Actions.Bundle_action('open_file_url', 'Open File URL', [
new app.Actions.Insert_layer_action(new_layer),
new app.Actions.Autoresize_canvas_action(img.width, img.height, null, true, true)
])
);
};
img.onerror = function (ex) {
alertify.error('Sorry, image could not be loaded. Try copy image and paste it.');
};
img.src = url;
}
async load_json(data) {
var json;
if(typeof data == 'string')
json = JSON.parse(data);
else
json = data;
if (json.info.version == undefined) {
json.info.version = "3.0.0";
}
//migration
if(semver_compare(json.info.version, '4.0.0') < 0) {
//convert from v3 to v4
for (var i in json.layers) {
//layers data
json.layers[i].id = (parseInt(i) + 1);
json.layers[i].opacity = json.layers[i].opacity * 100 || 100;
json.layers[i].type = "image";
json.layers[i].width = json.info.width;
json.layers[i].height = json.info.height;
json.layers[i].visible = (json.layers[i].visible == true); //convert to boolean
delete json.layers[i].title;
}
json.data = [];
for (var i in json.image_data) {
//image data
var new_id = null;
for (var j in json.layers) {
if (json.layers[j].name == json.image_data[i].name) {
new_id = json.layers[j].id;
}
}
if (new_id == null)
continue;
json.data.push(
{
id: new_id,
data: json.image_data[i].data,
}
);
}
}
if(semver_compare(json.info.version, '4.5.0') < 0) {
//migrate "rectangle", "circle" and "line" types to "shape"
for (var i in json.layers) {
var old_type = json.layers[i].type;
if(old_type == 'line' && json.layers[i].params.type.value == "Arrow"){
//migrate line (type=arrow) to arrow.
json.layers[i].type = 'arrow';
delete json.layers[i].params.type;
json.layers[i].render_function = ["arrow", "render"];
}
if(old_type == 'rectangle' || old_type == 'circle'){
//migrate params
json.layers[i].params.border_size = json.layers[i].params.size;
delete json.layers[i].params.size;
if(json.layers[i].params.fill == true) {
json.layers[i].params.border = false;
}
else{
json.layers[i].params.border = true;
}
json.layers[i].params.border_color = json.layers[i].color;
json.layers[i].params.fill_color = json.layers[i].color;
json.layers[i].color = null;
}
if(old_type == 'circle'){
//rename circle to ellipse
json.layers[i].type = 'ellipse';
json.layers[i].render_function = ["ellipse", "render"];
}
}
}
if(semver_compare(json.info.version, '4.8.0') < 0) {
//migrate "borders" layer to rectangle
for (var i in json.layers) {
var old_type = json.layers[i].type;
if(old_type == 'borders'){
json.layers[i].type = 'rectangle';
json.layers[i].name += ' (legacy)';
json.layers[i].params = {
radius: 0,
fill: false,
square: false,
border_size: json.layers[i].params.size,
border: true,
border_color: json.layers[i].color,
fill_color: "#000000",
};
json.layers[i].render_function = ["rectangle", "render"];
}
}
}
if(semver_compare(json.info.version, '4.11.0') < 0) {
//migrate star and star24 objects
for (var i in json.layers) {
var old_type = json.layers[i].type;
if(old_type == 'star' && typeof json.layers[i].params.corners == "undefined"){
json.layers[i].params.corners = 5;
json.layers[i].params.inner_radius = 40;
json.layers[i].render_function = ["star", "render"];
}
else if(old_type == 'star24'){
json.layers[i].type = 'star';
json.layers[i].params.corners = 24;
json.layers[i].params.inner_radius = 80;
json.layers[i].render_function = ["star", "render"];
}
}
}
const actions = [];
//reset zoom
await this.Base_gui.GUI_preview.zoom(100); //reset zoom
//set attributes
actions.push(
new app.Actions.Refresh_action_attributes_action('undo'),
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
ZOOM: 1,
WIDTH: parseInt(json.info.width),
HEIGHT: parseInt(json.info.height),
user_fonts: json.user_fonts || {}
}),
new app.Actions.Reset_layers_action(),
new app.Actions.Prepare_canvas_action('do'),
new app.Actions.Refresh_action_attributes_action('do')
);
var max_id_order = 0;
for (var i in json.layers) {
var value = json.layers[i];
if(value.id > max_id_order)
max_id_order = value.id;
if(typeof value.order != undefined && value.order > max_id_order)
max_id_order = value.order;
if (value.type == 'image') {
//add image data
value.link = null;
for (var j in json.data) {
if (json.data[j].id == value.id) {
value.data = json.data[j].data;
}
}
}
actions.push(
new app.Actions.Insert_layer_action(value, false)
);
}
if (json.info.layer_active != undefined) {
actions.push(
new app.Actions.Select_layer_action(json.info.layer_active, true)
);
}
if (json.info.guides != undefined) {
config.guides = json.info.guides;
}
actions.push(
new app.Actions.Set_object_property_action(this.Base_layers, 'auto_increment', max_id_order + 1),
new app.Actions.Update_config_action({
WIDTH: parseInt(json.info.width),
HEIGHT: parseInt(json.info.height),
}),
new app.Actions.Prepare_canvas_action('do')
);
await app.State.do_action(
new app.Actions.Bundle_action('open_json_file', 'Open JSON File', actions)
);
}
/**
* Returns an action that saves the exif data of the provided object to the current layer
*/
extract_exif(object) {
var exif_data = {
general: [],
exif: [],
};
//exif data
EXIF.getData(object, function () {
exif_data.exif = this.exifdata;
delete this.exifdata.thumbnail;
});
//general
if (object.name != undefined)
exif_data.general.Name = object.name;
if (object.size != undefined)
exif_data.general.Size = this.Helper.number_format(object.size / 1000, 2) + ' KB';
if (object.type != undefined)
exif_data.general.Type = object.type;
if (object.lastModified != undefined)
exif_data.general['Last modified'] = this.Helper.format_time(object.lastModified);
return exif_data;
}
search(){
this.GUI_tools.activate_tool('media');
}
}
export default File_open_class;
+14
View File
@@ -0,0 +1,14 @@
/**
* manages files / print
*
* @author ViliusL
*/
class File_print_class {
print() {
window.print();
}
}
export default File_print_class;
+46
View File
@@ -0,0 +1,46 @@
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import File_open_class from './open.js';
/**
* manages files / quick-load
*
* @author ViliusL
*/
class File_quickload_class {
constructor() {
this.Base_layers = new Base_layers_class();
this.File_open = new File_open_class();
this.set_events();
}
set_events() {
var _this = this;
document.addEventListener('keydown', function (event) {
var code = event.keyCode;
if (code == 121) {
//F10
_this.quickload();
event.preventDefault();
}
}, false);
}
quickload() {
//load image data
var json = localStorage.getItem('quicksave_data');
if (json == '' || json == null) {
//nothing was found
return false;
}
this.File_open.load_json(json);
}
}
export default File_quickload_class;
+45
View File
@@ -0,0 +1,45 @@
import config from './../../config.js';
import File_save_class from './save.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
/**
* manages files / quick-save
*
* @author ViliusL
*/
class File_quicksave_class {
constructor() {
this.POP = new Dialog_class();
this.File_save = new File_save_class();
this.set_events();
}
set_events() {
var _this = this;
document.addEventListener('keydown', function (event) {
var code = event.keyCode;
if (code == 120) {
//F9
_this.quicksave();
}
}, false);
}
quicksave() {
//save image data
var data_json = this.File_save.export_as_json();
if (data_json.length > 5000000) {
alertify.error('Sorry, image is too big, max 5 MB.');
return false;
}
localStorage.setItem('quicksave_data', data_json);
}
}
export default File_quicksave_class;
+736
View File
@@ -0,0 +1,736 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import canvasToBlob from './../../../../node_modules/blueimp-canvas-to-blob/js/canvas-to-blob.min.js';
import filesaver from './../../../../node_modules/file-saver/dist/FileSaver.min.js';
import GIF from './../../../../node_modules/gif.js.optimized/';
import CanvasToTIFF from './../../libs/canvastotiff.js';
import Tools_settings_class from "../tools/settings";
var instance = null;
/**
* manages files / save
*
* @author ViliusL
*/
class File_save_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.POP = new Dialog_class();
this.Tools_settings = new Tools_settings_class();
this.set_events();
//save types config
this.SAVE_TYPES = {
PNG: "Portable Network Graphics",
JPG: "JPG/JPEG Format",
//AVIF: "AV1 Image File Format", //just uncomment it in future to make it work
JSON: "Full layers data",
WEBP: "Weppy File Format",
GIF: "Graphics Interchange Format",
BMP: "Windows Bitmap",
TIFF: "Tag Image File Format",
};
this.default_extension = 'PNG';
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.key.toLowerCase();
if (this.Helper.is_input(event.target))
return;
if (code == "s") {
if(event.shiftKey){
//export
this.save();
}
else{
//save
this.export();
}
event.preventDefault();
}
}, false);
}
/**
* saves as non destructive mode (including layers, RAW)
*/
save(){
var types = JSON.parse(JSON.stringify(this.SAVE_TYPES));
for(var i in types){
if(i != 'JSON'){
delete types[i];
}
}
this.save_general(types, 'Save as');
}
/**
* save as encoded image
*/
export(){
var types = JSON.parse(JSON.stringify(this.SAVE_TYPES));
delete types.JSON;
this.save_general(types, 'Export');
}
save_general(file_types, title) {
var _this = this;
//find default format
var save_default = null;
var save_default_cookie = this.Helper.getCookie('save_default');
for(var i in file_types) {
if(save_default_cookie == i){
save_default = i;
break;
}
}
if(save_default == null){
save_default = Object.keys(file_types)[0];
}
save_default = save_default + " - " + file_types[save_default];
var calc_size_value = false;
var calc_size = false;
if (config.WIDTH * config.HEIGHT < 1000000) {
calc_size_value = true;
calc_size = true;
}
var file_name = config.layers[0].name;
var parts = file_name.split('.');
if (parts.length > 1)
file_name = parts[parts.length - 2];
file_name = file_name.replace(/ /g, "-");
file_name = this.Helper.escapeHtml(file_name);
var save_types = [];
for(var i in file_types) {
save_types.push(i + " - " + file_types[i]);
}
var save_layers_types = [
'All',
'Selected',
'Separated',
'Separated (original types)',
];
var resolution = this.Tools_settings.get_setting('resolution');
var settings = {
title: title,
params: [
{name: "name", title: "File name:", value: file_name},
{name: "type", title: "Save as type:", values: save_types, value: save_default},
{name: "quality", title: "Quality:", value: 90, range: [1, 100]},
{title: "File size:", html: '<span id="file_size">-</span>'},
{title: "Resolution:", value: resolution},
{name: "calc_size", title: "Show file size:", value: calc_size_value},
{name: "layers", title: "Save layers:", values: save_layers_types},
{name: "delay", title: "Gif delay:", value: 400},
],
on_change: function (params, canvas_preview, w, h) {
_this.save_dialog_onchange(true);
},
on_finish: function (params) {
if (params.layers == 'Separated' || params.layers == 'Separated (original types)') {
var active_layer = config.layer.id;
var original_layer_type = params.layers;
//alter params
params.layers = 'Selected';
for (var i in config.layers) {
if (config.layers[i].visible == false)
continue;
//detect type
if (original_layer_type == 'Separated (original types)') {
//detect type from file name
params.type = _this.SAVE_TYPES[_this.default_extension];
for (var j in _this.SAVE_TYPES) {
if (_this.Helper.strpos(config.layers[i].name.toLowerCase(), '.' + j.toLowerCase()) !== false) {
params.type = j;
break;
}
}
}
new app.Actions.Select_layer_action(config.layers[i].id, true).do();
_this.save_action(params, true);
}
new app.Actions.Select_layer_action(active_layer, true).do();
}
else {
_this.save_action(params);
}
},
};
this.POP.show(settings);
document.getElementById("pop_data_name").select();
if (calc_size == true) {
//calc size once
this.save_dialog_onchange(true);
}
else{
this.save_dialog_onchange(false);
}
}
save_data_url() {
var max = 10 * 1000 * 1000;
if (config.WIDTH * config.WIDTH > 10 * 1000 * 1000) {
alertify.error('Size is too big, max ' + this.Helper.number_format(max, 0) + ' pixels.');
return;
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
this.disable_canvas_smooth(ctx);
//ask data
this.Base_layers.convert_layers_to_canvas(ctx, null, false);
var data_url = canvas.toDataURL();
max = 1000 * 1000;
if (data_url.length > max) {
alertify.error('Size is too big, max ' + this.Helper.number_format(max, 0) + ' bytes.');
return;
}
var settings = {
title: 'Data URL',
params: [
{name: "url", title: "URL:", type: "textarea", value: data_url},
],
};
this.POP.show(settings);
}
update_file_size(file_size) {
if (typeof file_size == 'string') {
document.getElementById('file_size').innerHTML = file_size;
return;
}
if (file_size > 1024 * 1024)
file_size = this.Helper.number_format(file_size / 1024 / 1024, 2) + ' MB';
else if (file_size > 1024)
file_size = this.Helper.number_format(file_size / 1024, 2) + ' KB';
else
file_size = (file_size) + ' B';
document.getElementById('file_size').innerHTML = file_size;
}
/**
* /activated on save dialog parameters change - used for calculating file size
*
* @param {boolean} calculate_file_size
*/
save_dialog_onchange(calculate_file_size) {
var _this = this;
var user_response = this.POP.get_params();
var quality = parseInt(user_response.quality);
if (quality > 100 || quality < 1 || isNaN(quality) == true)
quality = 90;
quality = quality / 100;
//detect type
var type = user_response.type;
var parts = type.split(" ");
type = parts[0];
if (type == 'JPG' || type == 'WEBP')
document.getElementById('popup-tr-quality').style.display = '';
else
document.getElementById('popup-tr-quality').style.display = 'none';
if (type == 'GIF')
document.getElementById('popup-tr-delay').style.display = '';
else
document.getElementById('popup-tr-delay').style.display = 'none';
if (type == 'JSON' || type == 'GIF')
document.getElementById('popup-tr-layers').style.display = 'none';
else
document.getElementById('popup-tr-layers').style.display = '';
if (user_response.layers == 'Separated')
document.getElementById('pop_data_name').disabled = true;
else
document.getElementById('pop_data_name').disabled = false;
if (user_response.layers == 'Separated (original types)') {
if(document.getElementById('popup-group-type')) {
document.getElementById('popup-group-type').style.opacity = "0.5";
}
document.getElementById('popup-tr-quality').style.display = '';
}
else {
if(document.getElementById('popup-group-type')) {
document.getElementById('popup-group-type').style.opacity = "1";
}
}
if(calculate_file_size == false){
return;
}
this.update_file_size('...');
if (user_response.calc_size == false || user_response.layers == 'Separated'
|| user_response.layers == 'Separated (original types)') {
document.getElementById('file_size').innerHTML = '-';
return;
}
if (type != 'JSON') {
//create temp canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
this.disable_canvas_smooth(ctx);
//ask data
if (user_response.layers == 'Selected' && type != 'GIF' && config.layer.type != null) {
//only current layer !!!
var layer = config.layer;
var initial_x = null;
var initial_y = null;
if (layer.x != null && layer.y != null && layer.width != null && layer.height != null) {
//change position to top left corner
initial_x = layer.x;
initial_y = layer.y;
layer.x = 0;
layer.y = 0;
canvas.width = layer.width;
canvas.height = layer.height;
}
this.Base_layers.convert_layers_to_canvas(ctx, layer.id, false);
if (initial_x != null) {
//restore position
layer.x = initial_x;
layer.y = initial_y;
}
}
else {
this.Base_layers.convert_layers_to_canvas(ctx, null, false);
}
}
if (type != 'JSON' && (type == 'JPG' || config.TRANSPARENCY == false)) {
//add white background
ctx.globalCompositeOperation = 'destination-over';
this.fillCanvasBackground(ctx, '#ffffff');
ctx.globalCompositeOperation = 'source-over';
}
//calc size
if (type == 'PNG') {
//png
canvas.toBlob(function (blob) {
_this.update_file_size(blob.size);
});
}
else if (type == 'JPG') {
//jpg
canvas.toBlob(function (blob) {
_this.update_file_size(blob.size);
}, "image/jpeg", quality);
}
else if (type == 'WEBP') {
//WEBP
var data_header = "image/webp";
//check support
if (this.check_format_support(canvas, data_header, false) == false) {
this.update_file_size('-');
return;
}
canvas.toBlob(function (blob) {
_this.update_file_size(blob.size);
}, data_header, quality);
}
else if (type == 'AVIF') {
//AVIF
var data_header = "image/avif";
//check support
if (this.check_format_support(canvas, data_header, false) == false) {
this.update_file_size('-');
return;
}
canvas.toBlob(function (blob) {
_this.update_file_size(blob.size);
}, data_header, quality);
}
else if (type == 'BMP') {
//bmp
var data_header = "image/bmp";
//check support
if (this.check_format_support(canvas, data_header, false) == false) {
this.update_file_size('-');
return;
}
canvas.toBlob(function (blob) {
_this.update_file_size(blob.size);
}, data_header);
}
else if (type == 'TIFF') {
//tiff
var data_header = "image/tiff";
CanvasToTIFF.toBlob(canvas, function(blob) {
_this.update_file_size(blob.size);
}, data_header);
}
else if (type == 'JSON') {
//json
var data_json = this.export_as_json();
var blob = new Blob([data_json], {type: "text/plain"});
this.update_file_size(blob.size);
}
else if (type == 'GIF') {
//gif
this.update_file_size('-');
}
}
/**
* saves data in requested way
*
* @param {object} user_response parameters
* @param {boolean} autoname if use name from layer, false by default
*/
save_action(user_response, autoname) {
var fname = user_response.name;
if(autoname === true && user_response.layers == 'Selected'){
fname = config.layer.name;
}
var quality = parseInt(user_response.quality);
if (quality > 100 || quality < 1 || isNaN(quality) == true)
quality = 90;
quality = quality / 100;
var delay = parseInt(user_response.delay);
if (delay < 0 || isNaN(delay) == true)
delay = 400;
//detect type
var type = user_response.type;
var parts = type.split(" ");
type = parts[0];
//detect type from file name
for(var i in this.SAVE_TYPES) {
if (this.Helper.strpos(fname, '.' + i.toLowerCase()) !== false) {
type = i;
}
}
//save default type as cookie
if(this.Helper.getCookie('save_default') == '' || this.Helper.getCookie('save_default') != type){
this.Helper.setCookie('save_default', type);
}
if (type != 'JSON') {
//temp canvas
var canvas;
var ctx;
//get data
if (user_response.layers == 'Selected' && type != 'GIF') {
canvas = this.Base_layers.convert_layer_to_canvas();
ctx = canvas.getContext("2d");
}
else {
canvas = document.createElement('canvas');
ctx = canvas.getContext("2d");
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
this.disable_canvas_smooth(ctx);
this.Base_layers.convert_layers_to_canvas(ctx, null, false);
}
}
if (type != 'JSON' && (type == 'JPG' || config.TRANSPARENCY == false)) {
//add white background
ctx.globalCompositeOperation = 'destination-over';
this.fillCanvasBackground(ctx, '#ffffff');
ctx.globalCompositeOperation = 'source-over';
}
if (type == 'PNG') {
//png - default format
if (this.Helper.strpos(fname, '.png') == false)
fname = fname + ".png";
//simple save example
//var link = document.createElement('a');
//link.download = fname;
//link.href = canvas.toDataURL();
//link.click();
//save using lib
canvas.toBlob(function (blob) {
filesaver.saveAs(blob, fname);
});
}
else if (type == 'JPG') {
//jpg
if (this.Helper.strpos(fname, '.jpg') == false)
fname = fname + ".jpg";
canvas.toBlob(function (blob) {
filesaver.saveAs(blob, fname);
}, "image/jpeg", quality);
}
else if (type == 'WEBP') {
//WEBP
if (this.Helper.strpos(fname, '.webp') == false)
fname = fname + ".webp";
var data_header = "image/webp";
//check support
if (this.check_format_support(canvas, data_header) == false)
return false;
canvas.toBlob(function (blob) {
filesaver.saveAs(blob, fname);
}, data_header, quality);
}
else if (type == 'AVIF') {
//AVIF
if (this.Helper.strpos(fname, '.avif') == false)
fname = fname + ".avif";
var data_header = "image/avif";
//check support
if (this.check_format_support(canvas, data_header) == false)
return false;
canvas.toBlob(function (blob) {
filesaver.saveAs(blob, fname);
}, data_header, quality);
}
else if (type == 'BMP') {
//bmp
if (this.Helper.strpos(fname, '.bmp') == false)
fname = fname + ".bmp";
var data_header = "image/bmp";
//check support
if (this.check_format_support(canvas, data_header) == false)
return false;
canvas.toBlob(function (blob) {
filesaver.saveAs(blob, fname);
}, data_header);
}
else if (type == 'TIFF') {
//tiff
if (this.Helper.strpos(fname, '.tiff') == false)
fname = fname + ".tiff";
var data_header = "image/tiff";
CanvasToTIFF.toBlob(canvas, function(blob) {
filesaver.saveAs(blob, fname);
}, data_header);
}
else if (type == 'JSON') {
//json - full data with layers
if (this.Helper.strpos(fname, '.json') == false)
fname = fname + ".json";
var data_json = this.export_as_json();
var blob = new Blob([data_json], {type: "text/plain"});
//var data = window.URL.createObjectURL(blob); //html5
filesaver.saveAs(blob, fname);
}
else if (type == 'GIF') {
//gif
var cores = navigator.hardwareConcurrency || 4;
var gif_settings = {
workers: cores,
quality: 10, //1-30, lower is better
repeat: 0,
width: config.WIDTH,
height: config.HEIGHT,
dither: 'FloydSteinberg-serpentine',
workerScript: './src/js/libs/gifjs/gif.worker.js',
};
if (config.TRANSPARENCY == true) {
gif_settings.transparent = 'rgba(0,0,0,0)';
}
var gif = new GIF(gif_settings);
//add frames
for (var i = 0; i < config.layers.length; i++) {
if (config.layers[i].visible == false)
continue;
ctx.clearRect(0, 0, config.WIDTH, config.HEIGHT);
if (config.TRANSPARENCY == false) {
this.fillCanvasBackground(ctx, '#ffffff');
}
this.Base_layers.convert_layers_to_canvas(ctx, config.layers[i].id, false);
gif.addFrame(ctx, {copy: true, delay: delay});
}
gif.render();
gif.on('finished', function (blob) {
filesaver.saveAs(blob, fname);
});
}
}
fillCanvasBackground(ctx, color, width = config.WIDTH, height = config.HEIGHT) {
ctx.beginPath();
ctx.rect(0, 0, width, height);
ctx.fillStyle = color;
ctx.fill();
}
check_format_support(canvas, data_header, show_error) {
var data = canvas.toDataURL(data_header);
var actualType = data.replace(/^data:([^;]*).*/, '$1');
if (data_header != actualType && data_header != "text/plain") {
if (show_error == undefined || show_error == true) {
//error - no support
alertify.error('Your browser does not support this format.');
}
return false;
}
return true;
}
/**
* exports all layers to JSON
*/
export_as_json() {
//get date
var today = new Date();
var yyyy = today.getFullYear();
var mm = today.getMonth() + 1; //January is 0!
var dd = today.getDate();
if (dd < 10)
dd = '0' + dd;
if (mm < 10)
mm = '0' + mm;
var today = yyyy + '-' + mm + '-' + dd;
//data
var export_data = {};
export_data.info = {
width: config.WIDTH,
height: config.HEIGHT,
about: 'Image data with multi-layers. Can be opened using miniPaint - '
+ 'https://github.com/viliusle/miniPaint',
date: today,
version: VERSION,
layer_active: config.layer.id,
guides: config.guides,
};
//fonts
export_data.user_fonts = config.user_fonts;
//layers
export_data.layers = [];
for (var i in config.layers) {
var layer = {};
for (var j in config.layers[i]) {
if (j[0] == '_' || j == 'link_canvas') {
//private data
continue;
}
layer[j] = config.layers[i][j];
}
export_data.layers.push(layer);
}
//image data
export_data.data = [];
for (var i in config.layers) {
if (config.layers[i].type != 'image')
continue;
var canvas = document.createElement('canvas');
canvas.width = config.layers[i].width_original;
canvas.height = config.layers[i].height_original;
this.disable_canvas_smooth(canvas.getContext("2d"));
canvas.getContext('2d').drawImage(config.layers[i].link, 0, 0);
var data_tmp = canvas.toDataURL("image/png");
export_data.data.push(
{
id: config.layers[i].id,
data: data_tmp,
}
);
canvas.width = 1;
canvas.height = 1;
}
return JSON.stringify(export_data, null, "\t");
}
/**
* removes smoothing, because it look ugly during zoom
*
* @param {ctx} ctx
*/
disable_canvas_smooth(ctx) {
ctx.webkitImageSmoothingEnabled = false;
ctx.oImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
}
}
export default File_save_class;
+32
View File
@@ -0,0 +1,32 @@
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
class Help_about_class {
constructor() {
this.POP = new Dialog_class();
}
//about
about() {
var email = 'www.viliusl@gmail.com';
var settings = {
title: 'About',
params: [
{title: "", html: '<img style="width:64px;" class="about-logo" alt="" src="images/logo-colors.png" />'},
{title: "Name:", html: '<span class="about-name">miniPaint</span>'},
{title: "Version:", value: VERSION},
{title: "Description:", value: "Online image editor."},
{title: "Author:", value: 'ViliusL'},
{title: "Email:", html: '<a href="mailto:' + email + '">' + email + '</a>'},
{title: "GitHub:", html: '<a href="https://github.com/viliusle/miniPaint">https://github.com/viliusle/miniPaint</a>'},
{title: "Website:", html: '<a href="https://viliusle.github.io/miniPaint/">https://viliusle.github.io/miniPaint/</a>'},
],
};
this.POP.show(settings);
}
}
export default Help_about_class;
+44
View File
@@ -0,0 +1,44 @@
import Dialog_class from './../../libs/popup.js';
class Help_shortcuts_class {
constructor() {
this.POP = new Dialog_class();
}
//shortcuts
shortcuts() {
var settings = {
title: 'Keyboard Shortcuts',
className: 'shortcuts',
params: [
{title: "F", value: 'Auto Adjust Colors'},
{title: "F3 / &#8984; + F", value: 'Search'},
{title: "Ctrl + C", value: 'Copy to Clipboard'},
{title: "D", value: 'Duplicate'},
{title: "S", value: 'Export'},
{title: "G", value: 'Grid on/off'},
{title: "I", value: 'Information'},
{title: "N", value: 'New layer'},
{title: "O", value: 'Open'},
{title: "CTRL + V", value: 'Paste'},
{title: "F10", value: 'Quick Load'},
{title: "F9", value: 'Quick Save'},
{title: "R", value: 'Resize'},
{title: "L", value: 'Rotate left'},
{title: "U", value: 'Ruler'},
{title: "Shift + S", value: 'Save As'},
{title: "CTRL + A", value: 'Select All'},
{title: "H", value: 'Shapes'},
{title: "T", value: 'Trim'},
{title: "CTRL + Z", value: 'Undo'},
{title: "Scroll up", value: 'Zoom in'},
{title: "Scroll down", value: 'Zoom out'},
],
};
this.POP.show(settings);
}
}
export default Help_shortcuts_class;
@@ -0,0 +1,168 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Image_autoAdjust_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 70 && event.ctrlKey != true && event.metaKey != true) {
//F - adjust
this.auto_adjust();
event.preventDefault();
}
}, false);
}
auto_adjust() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.get_adjust_data(img);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
get_adjust_data(data) {
//settings
var white = 240; //white color min
var black = 30; //black color max
var target_white = 1; //how much % white colors should take
var target_black = 0.5; //how much % black colors should take
var modify = 1.1; //color modify strength
var cycles_count = 10; //how much iteration to change colors
var imgData = data.data;
var W = data.width;
var H = data.height;
var n = 0; //pixels count without transparent
//make sure we have white
var n_valid = 0;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 > white)
n_valid++;
n++;
}
var target = target_white;
var n_fix_white = 0;
var done = false;
for (var j = 0; j < cycles_count; j++) {
if (n_valid * 100 / n >= target)
done = true;
if (done == true)
break;
n_fix_white++;
//adjust
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
for (var c = 0; c < 3; c++) {
var x = i + c;
if (imgData[x] < 10)
continue;
//increase white
imgData[x] *= modify;
imgData[x] = Math.round(imgData[x]);
if (imgData[x] > 255)
imgData[x] = 255;
}
}
//recheck
n_valid = 0;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 > white)
n_valid++;
}
}
//make sure we have black
n_valid = 0;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 < black)
n_valid++;
}
target = target_black;
var n_fix_black = 0;
var done = false;
for (var j = 0; j < cycles_count; j++) {
if (n_valid * 100 / n >= target)
done = true;
if (done == true)
break;
n_fix_black++;
//adjust
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
for (var c = 0; c < 3; c++) {
var x = i + c;
if (imgData[x] > 240)
continue;
//increase black
imgData[x] -= (255 - imgData[x]) * modify - (255 - imgData[x]);
imgData[x] = Math.round(imgData[x]);
}
}
//recheck
n_valid = 0;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 < black)
n_valid++;
}
}
//log('Iterations: brighten='+n_fix_white+", darken="+n_fix_black);
return data;
}
}
export default Image_autoAdjust_class;
@@ -0,0 +1,134 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import ImageFilters_class from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Image_colorCorrections_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ImageFilters = ImageFilters_class;
}
color_corrections() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Color Corrections',
preview: true,
on_change: function (params, canvas_preview, w, h, canvas) {
//destructive effects
var img = this.layer_active_small_ctx.getImageData(0, 0, w, h);
var data = _this.do_corrections(img, params, false);
canvas_preview.putImageData(data, 0, 0);
//non-destructive
canvas_preview.filter = "brightness(" + (1 + (params.param_b / 100)) + ")";
canvas_preview.filter += " contrast(" + (1 + (params.param_c / 100)) + ")";
canvas_preview.filter += " saturate(" + (1 + (params.param_s / 100)) + ")";
canvas_preview.filter += " hue-rotate(" + params.param_h + "deg)";
canvas_preview.drawImage(canvas, 0, 0);
},
params: [
{name: "param_b", title: "Brightness:", value: "0", range: [-100, 100]},
{name: "param_c", title: "Contrast:", value: "0", range: [-100, 100]},
{name: "param_s", title: "Saturation:", value: "0", range: [-100, 100]},
{name: "param_h", title: "Hue:", value: "0", range: [-180, 180]},
{},
{name: "param_l", title: "Luminance:", value: "0", range: [-100, 100]},
{},
{name: "param_red", title: "Red channel:", value: "0", range: [-255, 255]},
{name: "param_green", title: "Green channel:", value: "0", range: [-255, 255]},
{name: "param_blue", title: "Blue channel:", value: "0", range: [-255, 255]},
],
on_finish: function (params) {
_this.save_changes(params);
},
};
this.POP.show(settings);
}
save_changes(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.do_corrections(img, params);
ctx.putImageData(data, 0, 0);
//save
app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
//non-destructive filters
//multiple do_action() + do_corrections() does not work together yet.
if(params.param_b != 0) {
var parameters = {value: params.param_b};
var filter_id = null;
app.State.do_action(
new app.Actions.Add_layer_filter_action(null, 'brightness', parameters, filter_id)
);
}
if(params.param_c != 0) {
var parameters = {value: params.param_c};
var filter_id = null;
app.State.do_action(
new app.Actions.Add_layer_filter_action(null, 'contrast', parameters, filter_id)
);
}
if(params.param_s != 0) {
var parameters = {value: params.param_s};
var filter_id = null;
app.State.do_action(
new app.Actions.Add_layer_filter_action(null, 'saturate', parameters, filter_id)
);
}
if(params.param_h != 0) {
var parameters = {value: params.param_h};
var filter_id = null;
app.State.do_action(
new app.Actions.Add_layer_filter_action(null, 'hue-rotate', parameters, filter_id)
);
}
}
/**
* corrections (destructive)
*
* @param data
* @param params
* @returns {*}
*/
do_corrections(data, params) {
//luminance
if(params.param_l != 0) {
var data = this.ImageFilters.HSLAdjustment(data, 0, 0, params.param_l);
}
//RGB corrections
if(params.param_red != 0 || params.param_green != 0 || params.param_blue != 0) {
var data = this.ImageFilters.ColorTransformFilter(data, 1, 1, 1, 1,
params.param_red, params.param_green, params.param_blue, 1);
}
return data;
}
}
export default Image_colorCorrections_class;
@@ -0,0 +1,174 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import ImageFilters_class from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Image_decreaseColors_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.ImageFilters = ImageFilters_class;
}
decrease_colors() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Decrease Color Depth',
preview: true,
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.get_decreased_data(img, params.colors, params.greyscale);
canvas_preview.putImageData(data, 0, 0);
},
params: [
{name: "colors", title: "Colors:", value: 10, range: [1, 256]},
{name: "greyscale", title: "Greyscale:", value: false},
],
on_finish: function (params) {
_this.execute(params);
},
};
this.POP.show(settings);
}
execute(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.get_decreased_data(img, params.colors, params.greyscale);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
get_decreased_data(data, colors, greyscale) {
var img = data.data;
var imgData = data.data;
var W = data.width;
var H = data.height;
var palette = [];
var block_size = 10;
//create tmp canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = W;
canvas.height = H;
//collect top colors
ctx.drawImage(config.layer.link, 0, 0, Math.ceil(W / block_size), Math.ceil(H / block_size));
var img_p = ctx.getImageData(0, 0, Math.ceil(W / block_size), Math.ceil(H / block_size));
var imgData_p = img_p.data;
ctx.clearRect(0, 0, W, H);
for (var i = 0; i < imgData_p.length; i += 4) {
if (imgData_p[i + 3] == 0)
continue; //transparent
var grey = Math.round(0.2126 * imgData_p[i] + 0.7152 * imgData_p[i + 1]
+ 0.0722 * imgData_p[i + 2]);
palette.push([imgData_p[i], imgData_p[i + 1], imgData_p[i + 2], grey]);
}
//calculate weights
var grey_palette = [];
for (var i = 0; i < 256; i++)
grey_palette[i] = 0;
for (var i = 0; i < palette.length; i++)
grey_palette[palette[i][3]]++;
//remove similar colors
for (var max = 10 * 3; max < 100 * 3; max = max + 10 * 3) {
if (palette.length <= colors)
break;
for (var i = 0; i < palette.length; i++) {
if (palette.length <= colors)
break;
var valid = true;
for (var j = 0; j < palette.length; j++) {
if (palette.length <= colors)
break;
if (i == j)
continue;
if (Math.abs(palette[i][0] - palette[j][0])
+ Math.abs(palette[i][1] - palette[j][1])
+ Math.abs(palette[i][2] - palette[j][2]) < max) {
if (grey_palette[palette[i][3]] > grey_palette[palette[j][3]]) {
//remove color
palette.splice(j, 1);
j--;
}
else {
valid = false;
break;
}
}
}
//remove color
if (valid == false) {
palette.splice(i, 1);
i--;
}
}
}
palette = palette.slice(0, colors);
//change
var p_n = palette.length;
for (var j = 0; j < H; j++) {
for (var i = 0; i < W; i++) {
var k = ((j * (W * 4)) + (i * 4));
if (imgData[k + 3] == 0)
continue; //transparent
//find closest color
var index1 = 0;
var min = 999999;
var diff1;
for (var m = 0; m < p_n; m++) {
var diff = Math.abs(palette[m][0] - imgData[k])
+ Math.abs(palette[m][1] - imgData[k + 1])
+ Math.abs(palette[m][2] - imgData[k + 2]);
if (diff < min) {
min = diff;
index1 = m;
diff1 = diff;
}
}
imgData[k] = palette[index1][0];
imgData[k + 1] = palette[index1][1];
imgData[k + 2] = palette[index1][2];
if (greyscale == true) {
var mid = Math.round(0.2126 * imgData[k] + 0.7152 * imgData[k + 1]
+ 0.0722 * imgData[k + 2]);
imgData[k] = mid;
imgData[k + 1] = mid;
imgData[k + 2] = mid;
}
}
}
return data;
}
}
export default Image_decreaseColors_class;
+56
View File
@@ -0,0 +1,56 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Image_flip_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
vertical() {
this.flip('vertical');
}
horizontal() {
this.flip('horizontal');
}
flip(mode) {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//create destination canvas
var canvas2 = document.createElement('canvas');
canvas2.width = canvas.width;
canvas2.height = canvas.height;
var ctx2 = canvas2.getContext("2d");
canvas2.dataset.x = canvas.dataset.x;
canvas2.dataset.y = canvas.dataset.y;
//flip
if (mode == 'vertical') {
ctx2.scale(1, -1);
ctx2.drawImage(canvas, 0, canvas2.height * -1);
}
else if (mode == 'horizontal') {
ctx2.scale(-1, 1);
ctx2.drawImage(canvas, canvas2.width * -1, 0);
}
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas2)
);
}
}
export default Image_flip_class;
+122
View File
@@ -0,0 +1,122 @@
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
class Image_histogram_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
}
histogram() {
var _this = this;
var settings = {
title: 'Histogram',
on_change: function (params) {
_this.histogram_onload(params);
},
params: [
{name: "channel", title: "Channel:", values: ["Gray", "Red", "Green", "Blue"], },
{title: 'Histogram:', function: function () {
var html = '<canvas style="position:relative;" id="c_h" width="256" height="100"></canvas>';
return html;
}},
{title: "Total pixels:", value: ""},
{title: "Average:", value: ""},
],
};
this.POP.show(settings);
this.histogram_onload({});
}
histogram_onload(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(config.layer.id);
var ctx = canvas.getContext("2d");
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var imgData = img.data;
var channel = 0;
if (params.channel == 'Red')
channel = 1;
else if (params.channel == 'Green')
channel = 2;
else if (params.channel == 'Blue')
channel = 3;
var hist_data = [[], [], [], []]; //grey, red, green, blue
var total = imgData.length / 4;
var sum = 0;
var grey;
for (var i = 0; i < imgData.length; i += 4) {
//collect grey
grey = Math.round((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3);
sum = sum + imgData[i] + imgData[i + 1] + imgData[i + 2];
if (hist_data[0][grey] == undefined)
hist_data[0][grey] = 1;
else
hist_data[0][grey]++;
//collect colors
for (var c = 0; c < 3; c++) {
if (c + 1 != channel)
continue;
if (hist_data[c + 1][imgData[i + c]] == undefined)
hist_data[c + 1][imgData[i + c]] = 1;
else
hist_data[c + 1][imgData[i + c]]++;
}
}
var c = document.getElementById("c_h").getContext("2d");
c.rect(0, 0, 256, 100);
c.fillStyle = "#ffffff";
c.fill();
var opacity = 1;
//draw histogram
for (var h in hist_data) {
for (var i = 0; i <= 255; i++) {
if (h != channel)
continue;
if (hist_data[h][i] == 0)
continue;
c.beginPath();
if (h == 0)
c.strokeStyle = "rgba(64, 64, 64, " + opacity * 2 + ")";
else if (h == 1)
c.strokeStyle = "rgba(255, 0, 0, " + opacity + ")";
else if (h == 2)
c.strokeStyle = "rgba(0, 255, 0, " + opacity + ")";
else if (h == 3)
c.strokeStyle = "rgba(0, 0, 255, " + opacity + ")";
c.lineWidth = 1;
c.moveTo(i + 0.5, 100 + 0.5);
c.lineTo(i + 0.5, 100 - Math.round(hist_data[h][i] * 255 * 100 / total / 6) + 0.5);
c.stroke();
}
}
document.getElementById("pop_data_totalpixel").innerHTML = this.Helper.number_format(total, 0);
var average;
if (total > 0)
average = Math.round(sum * 10 / total / 3) / 10;
else
average = '-';
document.getElementById("pop_data_average").innerHTML = average;
canvas.width = 1;
canvas.height = 1;
}
}
export default Image_histogram_class;
@@ -0,0 +1,144 @@
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import Base_layers_class from './../../core/base-layers.js';
import Tools_settings_class from './../tools/settings.js';
var instance = null;
class Image_information_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
this.Tools_settings = new Tools_settings_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.key.toLowerCase();
if (this.Helper.is_input(event.target))
return;
if (code == "i") {
this.information();
event.preventDefault();
}
}, false);
}
information() {
var _this = this;
var pixels = config.WIDTH * config.HEIGHT;
pixels = this.Helper.number_format(pixels, 0);
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var width = this.Helper.get_user_unit(config.WIDTH, units, resolution);
var height = this.Helper.get_user_unit(config.HEIGHT, units, resolution);
var settings = {
title: 'Information',
params: [
{title: "Width:", value: width + ' ' + units},
{title: "Height:", value: height + ' ' + units},
{title: "Pixels:", value: pixels},
{title: "Layers:", value: config.layers.length},
{title: "Unique colors:", value: '...'},
],
};
if(units != 'pixels'){
settings.params[0].value += " (" + config.WIDTH + " pixels)";
settings.params[1].value += " (" + config.HEIGHT + " pixels)";
}
//exif data
if (config.layer._exif != undefined) {
//show exif and general data
var exif_data = config.layer._exif;
//show general data
for (var i in exif_data.general) {
settings.params.push({title: i + ":", value: exif_data.general[i]});
}
//show exif data
var n = 0;
for (var i in exif_data.exif) {
if (i == 'undefined')
continue;
if (n == 0)
settings.params.push({title: "==== EXIF ====", value: ''});
settings.params.push({title: i + ":", value: exif_data.exif[i]});
n++;
}
}
this.POP.show(settings);
//calc colors
setTimeout(function () {
var colors = _this.unique_colors_count();
colors = _this.Helper.number_format(colors, 0);
document.getElementById('pop_data_uniquecolo').innerHTML = colors;
}, 50);
}
unique_colors_count() {
var method = 'v2'; //v1 or v2
if (config.WIDTH * config.HEIGHT > 20 * 1000 * 1000) {
return '-';
}
var canvas = this.Base_layers.convert_layer_to_canvas();
var ctx = canvas.getContext("2d");
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var imgData = img.data;
//v1 - simple, slow
if (method == 'v1') {
var colors = [];
var n = 0;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
var key = imgData[i] + "." + imgData[i + 1] + "." + imgData[i + 2];
if (colors[key] == undefined) {
colors[key] = 1;
n++;
}
}
}
//v2 - 30% faster
else if (method == 'v2') {
var buffer32 = new Uint32Array(imgData.buffer);
var len = buffer32.length;
var stats = {};
var n = 0;
for (var i = 0; i < len; i++) {
var key = "" + (buffer32[i] & 0xffffff);
if (stats[key] == undefined) {
stats[key] = 0;
n++;
}
}
}
return n;
}
}
export default Image_information_class;
+56
View File
@@ -0,0 +1,56 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
class Image_opacity_class {
constructor() {
this.POP = new Dialog_class();
}
opacity() {
var _this = this;
var initial_opacity = config.layer.opacity;
var settings = {
title: 'Opacity',
params: [
{name: "opacity", title: "Alpha:", value: config.layer.opacity, range: [0, 100]},
],
on_change: function (params, canvas_preview, w, h) {
_this.opacity_handler(params, false);
},
on_finish: function (params) {
config.layer.opacity = initial_opacity;
_this.opacity_handler(params);
},
on_cancel: function (params) {
config.layer.opacity = initial_opacity;
config.need_render = true;
},
};
this.POP.show(settings);
}
opacity_handler(data, is_final = true) {
var value = parseInt(data.opacity);
if (value < 0)
value = 0;
if (value > 100)
value = 100;
if (is_final) {
app.State.do_action(
new app.Actions.Bundle_action('change_opacity', 'Change Opacity', [
new app.Actions.Update_layer_action(config.layer.id, {
opacity: value
})
])
);
} else {
config.layer.opacity = value;
config.need_render = true;
}
}
}
export default Image_opacity_class;
+53
View File
@@ -0,0 +1,53 @@
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import colorThief_class from './../../libs/color-thief.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
class Image_color_class {
constructor() {
this.Base_layers = new Base_layers_class();
this.alertify = new colorThief_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
}
palette() {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var palette = this.alertify.getPalette(config.layer.link);
var dominant = this.alertify.getColor(config.layer.link);
dominant = this.Helper.rgbToHex(dominant[0], dominant[1], dominant[2]);
var settings = {
title: 'Palette',
params: [
{title: "Dominant color:", html: this.generate_color_box(dominant, 200)},
],
};
for (var i in palette) {
var rgb = this.Helper.rgbToHex(palette[i][0], palette[i][1], palette[i][2]);
i = parseInt(i);
settings.params.push(
{title: "Color #" + (i + 1) + ":", html: this.generate_color_box(rgb, 100)}
);
}
this.POP.show(settings);
}
generate_color_box(color, width) {
var html = '';
html += '<input style="width:100px;margin-right:10px;" type="text" value="' + color + '" />';
html += '<span style="display:inline-block;width:' + width + 'px;height:21px;margin-bottom:-6px;border:1px solid black;background-color:' + color + '"></span>';
return html;
}
}
export default Image_color_class;
+329
View File
@@ -0,0 +1,329 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Base_gui_class from './../../core/base-gui.js';
import Dialog_class from './../../libs/popup.js';
import ImageFilters_class from './../../libs/imagefilters.js';
import Hermite_class from 'hermite-resize';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Pica from './../../../../node_modules/pica/dist/pica.js';
import Helper_class from './../../libs/helpers.js';
import Tools_settings_class from './../tools/settings.js';
import { metaDefaults as textMetaDefaults } from '../../tools/text.js';
var instance = null;
class Image_resize_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.POP = new Dialog_class();
this.ImageFilters = ImageFilters_class;
this.Hermite = new Hermite_class();
this.Tools_settings = new Tools_settings_class();
this.pica = Pica();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 82 && event.ctrlKey != true && event.metaKey != true) {
//R - resize
this.resize();
event.preventDefault();
}
}, false);
}
resize() {
var _this = this;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
//convert units
var width = this.Helper.get_user_unit(config.WIDTH, units, resolution);
var height = this.Helper.get_user_unit(config.HEIGHT, units, resolution);
var settings = {
title: 'Resize',
params: [
{name: "width", title: "Width:", value: '', placeholder: width, comment: units},
{name: "height", title: "Height:", value: '', placeholder: height, comment: units},
{name: "width_percent", title: "Width (%):", value: '', placeholder: 100, comment: "%"},
{name: "height_percent", title: "Height (%):", value: '', placeholder: 100, comment: "%"},
{name: "mode", title: "Mode:", values: ["Lanczos", "Hermite", "Basic"]},
{name: "sharpen", title: "Sharpen:", value: false},
{name: "layers", title: "Layers:", values: ["All", "Active"], value: "All"},
],
on_finish: function (params) {
_this.do_resize(params);
},
};
this.POP.show(settings);
document.getElementById("pop_data_width").select();
}
async do_resize(params) {
//validate
if (isNaN(params.width) && isNaN(params.height) && isNaN(params.width_percent) && isNaN(params.height_percent)) {
alertify.error('Missing at least 1 size parameter.');
return false;
}
// Build a list of actions to execute for resize
let actions = [];
if (params.layers == 'All') {
//resize all layers
var skips = 0;
for (var i in config.layers) {
try {
actions = actions.concat(await this.resize_layer(config.layers[i], params));
} catch (error) {
skips++;
}
}
if (skips > 0) {
alertify.error(skips + ' layer(s) were skipped.');
}
actions = actions.concat(this.resize_gui(params));
}
else {
//only active
actions = actions.concat(await this.resize_layer(config.layer, params));
}
return app.State.do_action(
new app.Actions.Bundle_action('resize_layers', 'Resize Layers', actions)
);
}
/**
* Generates actions that will resize layer (image, text, vector), returns a promise that rejects on failure.
*
* @param {object} layer
* @param {object} params
* @returns {Promise<object>} Returns array of actions to perform
*/
async resize_layer(layer, params) {
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var mode = params.mode;
var width = parseFloat(params.width);
var height = parseFloat(params.height);
var width_100 = parseInt(params.width_percent);
var height_100 = parseInt(params.height_percent);
var canvas_width = layer.width;
var canvas_height = layer.height;
var sharpen = params.sharpen;
var _this = this;
//convert units
if (isNaN(width) == false){
width = this.Helper.get_internal_unit(width, units, resolution);
}
if (isNaN(height) == false){
height = this.Helper.get_internal_unit(height, units, resolution);
}
//if dimension with percent provided
if (isNaN(width) && isNaN(height)) {
if (isNaN(width_100) == false) {
width = Math.round(config.WIDTH * width_100 / 100);
canvas_width = Math.round(config.WIDTH * width_100 / 100);
}
if (isNaN(height_100) == false) {
height = Math.round(config.HEIGHT * height_100 / 100);
canvas_height = Math.round(config.HEIGHT * height_100 / 100);
}
}
//if only 1 dimension was provided
if (isNaN(width) || isNaN(height)) {
var ratio = layer.width / layer.height;
var canvas_ratio = config.WIDTH / config.HEIGHT;
if (isNaN(width))
width = Math.round(height * ratio);
canvas_width = Math.round(canvas_height * canvas_ratio);
if (isNaN(height))
height = Math.round(width / ratio);
canvas_height = Math.round(canvas_width / canvas_ratio);
}
let new_x = params.layers == 'All' ? Math.round(layer.x * width / config.WIDTH) : layer.x;
let new_y = params.layers == 'All' ? Math.round(layer.y * height / config.HEIGHT) : layer.y;
let xratio = width / config.WIDTH;
let yratio = height / config.HEIGHT;
//is text
if (layer.type == 'text') {
let data = JSON.parse(JSON.stringify(layer.data));
for (let line of data) {
for (let span of line) {
span.meta.size = Math.ceil((span.meta.size || textMetaDefaults.size) * xratio);
span.meta.stroke_size = parseFloat((0.1 * Math.round((span.meta.stroke_size != null ? span.meta.stroke_size : textMetaDefaults.stroke_size) * xratio / 0.1)).toFixed(1));
span.meta.kerning = Math.ceil((span.meta.kerning || textMetaDefaults.kerning) * xratio);
}
}
// Return actions
return [
new app.Actions.Update_layer_action(layer.id, {
x: new_x,
y: new_y,
data,
width: layer.width * xratio,
height: layer.height * yratio
})
];
}
//is vector
else if (layer.is_vector == true && layer.width != null && layer.height != null) {
// Return actions
return [
new app.Actions.Update_layer_action(layer.id, {
x: new_x,
y: new_y,
width: layer.width * xratio,
height: layer.height * yratio
})
];
}
//only images supported at this point
else if (layer.type != 'image') {
//error - no support
alertify.error('Layer must be vector or image (convert it to raster).');
throw new Error('Layer is not compatible with resize');
}
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(layer.id, true, false);
var ctx = canvas.getContext("2d");
//validate
if (mode == "Hermite" && (width > canvas.width || height > canvas.height)) {
alertify.warning('Scaling up is not supported in Hermite, using Lanczos.');
mode = "Lanczos";
}
//resize
if (mode == "Lanczos") {
//Pica resize with max quality
var tmp_data = document.createElement("canvas");
tmp_data.width = width;
tmp_data.height = height;
await this.pica.resize(canvas, tmp_data, {
alpha: true,
})
.then((result) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = width;
canvas.height = height;
ctx.drawImage(tmp_data, 0, 0, width, height);
});
}
else if (mode == "Hermite") {
//Hermite resample
this.Hermite.resample_single(canvas, width, height, true);
}
else {
//simple resize
var tmp_data = document.createElement("canvas");
tmp_data.width = canvas.width;
tmp_data.height = canvas.height;
tmp_data.getContext("2d").drawImage(canvas, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = width;
canvas.height = height;
ctx.drawImage(tmp_data, 0, 0, width, height);
}
if (sharpen == true) {
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var filtered = _this.ImageFilters.Sharpen(imageData, 1); //add effect
ctx.putImageData(filtered, 0, 0);
}
// Return actions
return [
new app.Actions.Update_layer_image_action(canvas, layer.id),
new app.Actions.Update_layer_action(layer.id, {
x: new_x,
y: new_y,
width: canvas.width,
height: canvas.height,
width_original: canvas.width,
height_original: canvas.height
})
];
}
resize_gui(params) {
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var width = parseFloat(params.width);
var height = parseFloat(params.height);
var width_100 = parseInt(params.width_percent);
var height_100 = parseInt(params.height_percent);
//convert units
if (isNaN(width) == false){
width = this.Helper.get_internal_unit(width, units, resolution);
}
if (isNaN(height) == false){
height = this.Helper.get_internal_unit(height, units, resolution);
}
//if dimension with percent provided
if (isNaN(width) && isNaN(height)) {
if (isNaN(width_100) == false) {
width = Math.round(config.WIDTH * width_100 / 100);
}
if (isNaN(height_100) == false) {
height = Math.round(config.HEIGHT * height_100 / 100);
}
}
//if only 1 dimension was provided
if (isNaN(width) || isNaN(height)) {
var ratio = config.WIDTH / config.HEIGHT;
if (isNaN(width))
width = Math.round(height * ratio);
if (isNaN(height))
height = Math.round(width / ratio);
}
return [
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: parseInt(width),
HEIGHT: parseInt(height)
}),
new app.Actions.Prepare_canvas_action('do')
];
}
}
export default Image_resize_class;
+180
View File
@@ -0,0 +1,180 @@
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_layers_class from './../../core/base-layers.js';
import Base_gui_class from './../../core/base-gui.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import app from '../../app.js';
var instance = null;
class Image_rotate_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.Dialog = new Dialog_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 76) {
//L - rotate left
this.left();
event.preventDefault();
}
}, false);
}
rotate() {
var _this = this;
if (config.layer.rotate === null) {
alertify.error('Rotate is not supported on this type of object. Convert to raster?');
return;
}
var angles = ['Custom', '0', '90', '180', '270'];
var initial_angle = config.layer.rotate;
var settings = {
title: 'Rotate',
params: [
{name: "rotate", title: "Rotate:", value: config.layer.rotate, range: [0, 360]},
{name: "right_angle", title: "Right angle:", values: angles},
],
on_change: function (params, canvas_preview, w, h) {
_this.rotate_handler(params, false);
},
on_finish: function (params) {
config.layer.rotate = initial_angle;
_this.rotate_handler(params);
},
on_cancel: function (params) {
config.layer.rotate = initial_angle;
config.need_render = true;
},
};
this.Dialog.show(settings);
}
rotate_handler(data, can_resize = true) {
var value = parseInt(data.rotate);
if (data.right_angle != 'Custom') {
value = parseInt(data.right_angle);
}
if (value < 0)
value = 360 + value;
if (value >= 360)
value = value - 360;
let new_rotate = value;
if (can_resize == true) {
app.State.do_action(
new app.Actions.Bundle_action('rotate_layer', 'Rotate Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
rotate: new_rotate
}),
...this.check_sizes(new_rotate)
])
);
} else {
config.layer.rotate = new_rotate;
config.need_render = true;
}
}
left() {
let new_rotate = config.layer.rotate;
new_rotate -= 90;
if (new_rotate < 0)
new_rotate = 360 + new_rotate;
app.State.do_action(
new app.Actions.Bundle_action('rotate_layer', 'Rotate Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
rotate: new_rotate
}),
...this.check_sizes(new_rotate)
])
);
}
right() {
let new_rotate = config.layer.rotate;
new_rotate += 90;
if (new_rotate >= 360)
new_rotate = new_rotate - 360;
app.State.do_action(
new app.Actions.Bundle_action('rotate_layer', 'Rotate Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
rotate: new_rotate
}),
...this.check_sizes(new_rotate)
])
);
}
/**
* Makes sure image fits all after rotation
* @returns {array} actions to perform
*/
check_sizes(new_rotate) {
let actions = [];
var w = config.layer.width;
var h = config.layer.height;
var o = new_rotate * Math.PI / 180;
var new_x = w * Math.abs(Math.cos(o)) + h * Math.abs(Math.sin(o));
var new_y = w * Math.abs(Math.sin(o)) + h * Math.abs(Math.cos(o));
//round values
new_x = Math.ceil(Math.round(new_x * 1000) / 1000);
new_y = Math.ceil(Math.round(new_y * 1000) / 1000);
if (new_x > config.WIDTH || new_y > config.HEIGHT) {
var dx = 0;
var dy = 0;
let new_width = config.WIDTH;
let new_height = config.HEIGHT;
if (new_x > config.WIDTH) {
dx = Math.ceil(new_x - new_width) / 2;
new_width = new_x;
}
if (new_y > config.HEIGHT) {
dy = Math.ceil(new_y - new_height) / 2;
new_height = new_y;
}
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_layer_action(config.layer.id, {
x: config.layer.x + dx,
y: config.layer.y + dy
}),
new app.Actions.Update_config_action({
WIDTH: new_width,
HEIGHT: new_height
}),
new app.Actions.Prepare_canvas_action('do')
);
}
return actions;
}
}
export default Image_rotate_class;
+140
View File
@@ -0,0 +1,140 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_gui_class from './../../core/base-gui.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Tools_settings_class from './../tools/settings.js';
import Helper_class from './../../libs/helpers.js';
class Image_size_class {
constructor() {
this.Base_gui = new Base_gui_class();
this.POP = new Dialog_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
}
size() {
var _this = this;
var common_dimensions = this.Base_gui.common_dimensions;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var enable_autoresize = this.Tools_settings.get_setting('enable_autoresize');
var resolutions = ['Custom'];
for (var i in common_dimensions) {
var value = common_dimensions[i];
resolutions.push(value[0] + 'x' + value[1] + ' - ' + value[2]);
}
//convert units
var width = this.Helper.get_user_unit(config.WIDTH, units, resolution);
var height = this.Helper.get_user_unit(config.HEIGHT, units, resolution);
var settings = {
title: 'Canvas Size',
params: [
{name: "w", title: "Width:", value: width, placeholder: width, comment: units},
{name: "h", title: "Height:", value: height, placeholder: height, comment: units},
{name: "resolution", title: "Resolution:", values: resolutions},
{name: "layout", title: "Layout:", value: "Custom", values: ["Custom", "Landscape", "Portrait"]},
{name: "enable_autoresize", title: "Enable autoresize:", value: enable_autoresize},
{name: "in_proportion", title: "In proportion:", value: false},
],
on_finish: function (params) {
_this.size_handler(params);
},
};
this.POP.show(settings);
}
size_handler(data) {
var width = parseFloat(data.w);
var height = parseFloat(data.h);
var ratio = config.WIDTH / config.HEIGHT;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
if (width < 0){
width = 1;
}
if (height < 0){
height = 1;
}
this.Tools_settings.save_setting('enable_autoresize', data.enable_autoresize);
//aspect ratio
if (isNaN(width) && isNaN(height)){
alertify.error('Wrong dimensions');
return;
}
if (isNaN(width)){
width = height * ratio;
}
if (isNaN(height)){
height = width / ratio;
}
if (data.resolution != 'Custom') {
var dim = data.resolution.split(" ");
dim = dim[0].split("x");
width = parseInt(dim[0]);
height = parseInt(dim[1]);
if(data.layout == 'Portrait'){
var tmp = width;
width = height;
height = tmp;
}
}
else{
//convert units
width = this.Helper.get_internal_unit(width, units, resolution);
height = this.Helper.get_internal_unit(height, units, resolution);
}
var actions = [
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: parseInt(width),
HEIGHT: parseInt(height)
}),
];
if(data.in_proportion == true) {
//resize object and change coordinates
var width_ratio = config.WIDTH / width;
var height_ratio = config.HEIGHT / height;
var ratio = Math.max(width_ratio, height_ratio);
for (var i in config.layers) {
var layer = config.layers[i];
if(layer.x != null && layer.y != null) {
var data_new = {
x: Math.round(layer.x / width_ratio),
y: Math.round(layer.y / height_ratio),
};
actions.push(new app.Actions.Update_layer_action(layer.id, data_new));
}
if(layer.width != null && layer.height != null) {
var data_new = {
width: Math.round(layer.width / ratio),
height: Math.round(layer.height / ratio),
};
actions.push(new app.Actions.Update_layer_action(layer.id, data_new));
}
}
}
actions.push(new app.Actions.Prepare_canvas_action('do'));
//execute
app.State.do_action(
new app.Actions.Bundle_action('set_image_size', 'Set Image Size', actions)
);
}
}
export default Image_size_class;
@@ -0,0 +1,47 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Tools_settings_class from './../tools/settings.js';
import Helper_class from './../../libs/helpers.js';
class Image_translate_class {
constructor() {
this.POP = new Dialog_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
}
translate() {
var _this = this;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var pos_x = this.Helper.get_user_unit(config.layer.x, units, resolution);
var pos_y = this.Helper.get_user_unit(config.layer.y, units, resolution);
var settings = {
title: 'Translate',
params: [
{name: "x", title: "X position:", value: pos_x},
{name: "y", title: "Y position:", value: pos_y},
],
on_finish: function (params) {
var pos_x = _this.Helper.get_internal_unit(params.x, units, resolution);
var pos_y = _this.Helper.get_internal_unit(params.y, units, resolution);
app.State.do_action(
new app.Actions.Bundle_action('translate_layer', 'Translate Layer', [
new app.Actions.Update_layer_action(config.layer.id, {
x: pos_x,
y: pos_y,
})
])
);
},
};
this.POP.show(settings);
}
}
export default Image_translate_class;
+305
View File
@@ -0,0 +1,305 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_gui_class from './../../core/base-gui.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Image_trim_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.Dialog = new Dialog_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 84) {
//trim
this.trim();
event.preventDefault();
}
}, false);
}
trim() {
var _this = this;
var removeWhiteColor = false;
if(config.TRANSPARENCY == false)
removeWhiteColor = true;
var settings = {
title: 'Trim',
params: [
{name: "trim_layer", title: "Trim layer:", value: true},
{name: "trim_all", title: "Trim borders:", value: true},
{name: "power", title: "Power:", value: 0, max: 255},
{name: "remove_white", title: "Trim white color?", value: removeWhiteColor},
],
on_finish: async (params) => {
if (params.trim_layer == true) {
//first trim
let actions = [];
actions = actions.concat(this.trim_layer(config.layer.id, params.remove_white, params.power));
await app.State.do_action(
new app.Actions.Bundle_action('trim_layers', 'Trim Layers', actions)
);
}
if (params.trim_all == true) {
//second trim
let actions = [];
actions = actions.concat(_this.trim_all(params.remove_white, params.power));
app.State.do_action(
new app.Actions.Bundle_action('trim_layers', 'Trim Layers', actions)
);
}
},
};
this.Dialog.show(settings);
}
/**
* removes empty (white/transparent) area from top, right, bottom and left sides
* This affects layer data
*
* @param layer_id
* @param removeWhiteColor
* @param {int} power
*/
trim_layer(layer_id, removeWhiteColor = false, power = 0) {
var layer = this.Base_layers.get_layer(layer_id);
if (layer.type != 'image') {
alertify.error('Skip - layer must be image.');
return false;
}
var trim = this.get_trim_info(layer_id, removeWhiteColor, power);
trim = trim.relative;
//if image was stretched
var width_ratio = (layer.width / layer.width_original);
var height_ratio = (layer.height / layer.height_original);
//create smaller canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = trim.width / width_ratio;
canvas.height = trim.height / height_ratio;
//cut required part
ctx.translate(-trim.left / width_ratio, -trim.top / height_ratio);
canvas.getContext("2d").drawImage(layer.link, 0, 0);
ctx.translate(0, 0);
return [
new app.Actions.Update_layer_image_action(canvas, layer.id),
new app.Actions.Update_layer_action(layer.id, {
x: layer.x + trim.left,
y: layer.y + trim.top,
width: Math.ceil(canvas.width * width_ratio),
height: Math.ceil(canvas.height * height_ratio),
width_original: canvas.width,
height_original: canvas.height
})
];
}
/**
* change canvas size, so there is no empty (white/transparent) areas on top, right, bottom and left sides
* this affect canvas size and all layers positions
*
* @param removeWhiteColor
* @param {int} power
*/
trim_all(removeWhiteColor = false, power = 0) {
let actions = [];
var all_top = config.HEIGHT;
var all_left = config.WIDTH;
var all_bottom = config.HEIGHT;
var all_right = config.WIDTH;
if (removeWhiteColor == undefined) {
removeWhiteColor = false;
if (config.TRANSPARENCY == false) {
removeWhiteColor = true;
}
}
//collect info
for (let i = 0; i < config.layers.length; i++) {
let layer = config.layers[i];
if (layer.width == null || layer.height == null || layer.x == null || layer.y == null) {
//layer without dimensions
const trim_info = this.get_trim_info(layer.id, removeWhiteColor, power);
all_top = Math.min(all_top, trim_info.top);
all_left = Math.min(all_left, trim_info.left);
all_bottom = Math.min(all_bottom, trim_info.bottom);
all_right = Math.min(all_right, trim_info.right);
}
else{
all_top = Math.min(all_top, layer.y);
all_left = Math.min(all_left, layer.x);
all_bottom = Math.min(all_bottom, config.HEIGHT - layer.height - layer.y);
all_right = Math.min(all_right, config.WIDTH - layer.width - layer.x);
}
}
//move every layer
for (let i = 0; i < config.layers.length; i++) {
let layer = config.layers[i];
if (layer.x == null || layer.y == null || layer.type == null)
continue;
actions.push(
new app.Actions.Update_layer_action(layer.id, {
x: layer.x - all_left,
y: layer.y - all_top
})
);
}
//resize
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: Math.max(1, config.WIDTH - all_left - all_right),
HEIGHT: Math.max(1, config.HEIGHT - all_top - all_bottom)
}),
new app.Actions.Prepare_canvas_action('do')
);
return actions;
}
/**
* get painted area coords
*
* @param {int} layer_id
* @param {boolean} trim_white
* @param {int} power
* @returns {object} keys: top, left, bottom, right, width, height, relative
*/
get_trim_info(layer_id, trim_white, power) {
if (trim_white == undefined) {
trim_white = false;
if (config.TRANSPARENCY == false) {
trim_white = true;
}
}
if (power == undefined) {
power = 0;
}
var layer = this.Base_layers.get_layer(layer_id);
var canvas = this.Base_layers.convert_layer_to_canvas(layer_id, null, false);
var ctx = canvas.getContext("2d");
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var imgData = img.data;
var top = 0;
var left = 0;
var bottom = 0;
var right = 0;
//check top
main1:
for (var y = 0; y < img.height; y++) {
for (var x = 0; x < img.width; x++) {
var k = ((y * (img.width * 4)) + (x * 4));
if (imgData[k + 3] <= power)
continue; //transparent
if (trim_white == true && imgData[k] >= 255 - power && imgData[k + 1] >= 255 - power
&& imgData[k + 2] >= 255 - power)
continue; //white
break main1;
}
top++;
}
//check left
main2:
for (var x = 0; x < img.width; x++) {
for (var y = 0; y < img.height; y++) {
var k = ((y * (img.width * 4)) + (x * 4));
if (imgData[k + 3] <= power)
continue; //transparent
if (trim_white == true && imgData[k] >= 255 - power && imgData[k + 1] >= 255 - power
&& imgData[k + 2] >= 255 - power)
continue; //white
break main2;
}
left++;
}
//check bottom
main3:
for (var y = img.height - 1; y >= 0; y--) {
for (var x = img.width - 1; x >= 0; x--) {
var k = ((y * (img.width * 4)) + (x * 4));
if (imgData[k + 3] <= power)
continue; //transparent
if (trim_white == true && imgData[k] >= 255 - power && imgData[k + 1] >= 255 - power
&& imgData[k + 2] >= 255 - power)
continue; //white
break main3;
}
bottom++;
}
//check right
main4:
for (var x = img.width - 1; x >= 0; x--) {
for (var y = img.height - 1; y >= 0; y--) {
var k = ((y * (img.width * 4)) + (x * 4));
if (imgData[k + 3] <= power)
continue; //transparent
if (trim_white == true && imgData[k] >= 255 - power && imgData[k + 1] >= 255 - power
&& imgData[k + 2] >= 255 - power)
continue; //white
break main4;
}
right++;
}
var top_rel = top - layer.y;
var left_rel = left - layer.x;
var bottom_rel = bottom - (config.HEIGHT - layer.y - layer.height);
var right_rel = right - (config.WIDTH - layer.x - layer.width);
return {
top: top,
left: left,
bottom: bottom,
right: right,
width: canvas.width - left - right,
height: canvas.height - top - bottom,
relative: {
top: top_rel,
left: left_rel,
bottom: bottom_rel,
right: right_rel,
width: canvas.width - left - right,
height: canvas.height - top - bottom,
},
};
}
}
export default Image_trim_class;
+19
View File
@@ -0,0 +1,19 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
class Layer_clear_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
clear() {
return app.State.do_action(
new app.Actions.Clear_layer_action(config.layer.id)
);
}
}
export default Layer_clear_class;
@@ -0,0 +1,86 @@
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Base_gui_class from "../../core/base-gui.js";
class Layer_composition_class {
constructor() {
this.POP = new Dialog_class();
this.Base_gui_class = new Base_gui_class();
}
composition() {
var compositions = [
"-- Default --",
"color",
"color-burn",
"color-dodge",
"copy",
"darken",
"darker",
"destination-atop",
"destination-in",
"destination-out",
"destination-over",
"difference",
"exclusion",
"hard-light",
"hue",
"lighten",
"lighter",
"luminosity",
"multiply",
"overlay",
"saturation",
"screen",
"soft-light",
"source-atop",
"source-in",
"source-out",
"source-over",
"xor",
];
var initial_composition = config.layer.composition;
var _this = this;
var settings = {
title: 'Composition',
//preview: true,
params: [
{name: "composition", title: "Composition:", value: config.layer.composition, values: compositions},
],
on_change: function (params, canvas_preview, w, h) {
//redraw preview
if (params.composition == '-- Default --') {
params.composition = 'source-over';
}
config.layer.composition = params.composition;
config.need_render = true;
_this.Base_gui_class.GUI_layers.render_layers();
},
on_finish: function (params) {
config.layer.composition = initial_composition;
if (params.composition == '-- Default --') {
params.composition = 'source-over';
}
app.State.do_action(
new app.Actions.Bundle_action('change_composition', 'Change Composition', [
new app.Actions.Update_layer_action(config.layer.id, {
composition: params.composition
})
])
);
},
on_cancel: function (params) {
config.layer.composition = initial_composition;
config.need_render = true;
_this.Base_gui_class.GUI_layers.render_layers();
},
};
this.POP.show(settings);
}
}
export default Layer_composition_class;
+19
View File
@@ -0,0 +1,19 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
class Layer_delete_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
delete() {
app.State.do_action(
new app.Actions.Delete_layer_action(config.layer.id)
);
}
}
export default Layer_delete_class;
@@ -0,0 +1,105 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Layer_differences_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
differences() {
var _this = this;
if (this.Base_layers.find_previous(config.layer.id) == null) {
alertify.error('There are no layers behind.');
return false;
}
var settings = {
title: 'Differences',
preview: true,
params: [
{name: "sensitivity", title: "Sensitivity:", value: "0", range: [0, 255]},
],
on_change: function (params, canvas_preview, w, h) {
_this.calc_differences(params.sensitivity, canvas_preview, w, h);
},
on_finish: function (params) {
_this.calc_differences(params.sensitivity);
},
};
this.POP.show(settings);
}
calc_differences(sensitivity, canvas_preview, w, h) {
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
var ctx = canvas.getContext("2d");
//get source data
this.Base_layers.render_object(ctx, config.layer);
var imgData1 = ctx.getImageData(0, 0, config.WIDTH, config.HEIGHT).data;
ctx.clearRect(0, 0, canvas.width, canvas.height);
//get target data
var next_layer = this.Base_layers.find_previous(config.layer.id);
this.Base_layers.render_object(ctx, next_layer);
var imgData2 = ctx.getImageData(0, 0, config.WIDTH, config.HEIGHT).data;
//prepare background
ctx.rect(0, 0, config.WIDTH, config.HEIGHT);
ctx.fillStyle = "#ffffff";
ctx.fill();
//generate diff
var img3 = ctx.getImageData(0, 0, config.WIDTH, config.HEIGHT);
var imgData3 = img3.data;
for (var xx = 0; xx < config.WIDTH; xx++) {
for (var yy = 0; yy < config.HEIGHT; yy++) {
var x = (xx + yy * config.WIDTH) * 4;
if (Math.abs(imgData1[x] - imgData2[x]) > sensitivity
|| Math.abs(imgData1[x + 1] - imgData2[x + 1]) > sensitivity
|| Math.abs(imgData1[x + 2] - imgData2[x + 2]) > sensitivity
|| Math.abs(imgData1[x + 3] - imgData2[x + 3]) > sensitivity) {
imgData3[x] = 255;
imgData3[x + 1] = 0;
imgData3[x + 2] = 0;
imgData3[x + 3] = 255;
}
}
}
ctx.putImageData(img3, 0, 0);
//show
if (canvas_preview == undefined) {
//main
var params = [];
params.type = 'image';
params.name = 'Differences';
params.data = canvas.toDataURL("image/png");
app.State.do_action(
new app.Actions.Insert_layer_action(params)
);
}
else {
//preview
canvas_preview.save();
canvas_preview.scale(w / config.WIDTH, h / config.HEIGHT);
canvas_preview.drawImage(canvas, 0, 0);
canvas_preview.restore();
}
canvas.width = 1;
canvas.height = 1;
}
}
export default Layer_differences_class;
@@ -0,0 +1,78 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
var instance = null;
class Layer_duplicate_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 68) {
//D - duplicate
this.duplicate();
event.preventDefault();
}
}, false);
}
duplicate() {
var params = JSON.parse(JSON.stringify(config.layer));
delete params.id;
delete params.order;
//generate name
var name_number = params.name.match(/^(.*) #([0-9]+)$/);
if(name_number == null){
//first duplicate
params.name = params.name + " #2";
}
else{
//nth duplicate - name like "query #17"
params.name = name_number[1] + " #" + (parseInt(name_number[2]) + 1)
}
if(params.x != 0 || params.y != 0 || params.width != config.WIDTH || params.height != config.HEIGHT){
params.x += 10;
params.y += 10;
}
for (var i in params) {
//remove private attributes
if (i[0] == '_')
delete params[i];
}
if (params.type == 'image') {
//image
params.link = config.layer.link.cloneNode(true);
}
app.State.do_action(
new app.Actions.Bundle_action('duplicate_layer', 'Duplicate Layer', [
new app.Actions.Insert_layer_action(params)
])
);
}
}
export default Layer_duplicate_class;
+56
View File
@@ -0,0 +1,56 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Layer_flatten_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
flatten() {
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
var ctx = canvas.getContext("2d");
var layers_sorted = this.Base_layers.get_sorted_layers();
//paint layers
for (var i = layers_sorted.length - 1; i >= 0; i--) {
var layer = layers_sorted[i];
ctx.globalAlpha = layer.opacity / 100;
ctx.globalCompositeOperation = layer.composition;
this.Base_layers.render_object(ctx, layer);
}
//create requested layer
var params = [];
params.type = 'image';
params.name = 'Merged';
params.data = canvas.toDataURL("image/png");
//remove rest of layers
let delete_actions = [];
for (var i = config.layers.length - 1; i >= 0; i--) {
delete_actions.push(new app.Actions.Delete_layer_action(config.layers[i].id));
}
// Run actions
app.State.do_action(
new app.Actions.Bundle_action('flatten_image', 'Flatten Image', [
new app.Actions.Insert_layer_action(params),
...delete_actions
])
);
canvas.width = 1;
canvas.height = 1;
}
}
export default Layer_flatten_class;
+59
View File
@@ -0,0 +1,59 @@
import app from './../../app.js';
import config from './../../config.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Base_layers_class from './../../core/base-layers.js';
class Layer_merge_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
merge() {
if (this.Base_layers.find_previous(config.layer.id) == null) {
alertify.error('There are no layers behind.');
return false;
}
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
var ctx = canvas.getContext("2d");
//first layer
var previous_layer = this.Base_layers.find_previous(config.layer.id);
var previous_id = previous_layer.id;
ctx.globalAlpha = previous_layer.opacity / 100;
ctx.globalCompositeOperation = previous_layer.composition;
this.Base_layers.render_object(ctx, previous_layer);
//second layer
var current_id = config.layer.id;
var current_order = config.layer.order;
ctx.globalAlpha = config.layer.opacity / 100;
ctx.globalCompositeOperation = config.layer.composition;
this.Base_layers.render_object(ctx, config.layer);
//create requested layer
var params = [];
params.type = 'image';
params.name = config.layer.name + ' + merged';
params.order = current_order;
params.data = canvas.toDataURL("image/png");
app.State.do_action(
new app.Actions.Bundle_action('merge_layers', 'Merge Layers', [
new app.Actions.Insert_layer_action(params),
new app.Actions.Delete_layer_action(current_id),
new app.Actions.Delete_layer_action(previous_id)
])
);
//free canvas data
canvas.width = 1;
canvas.height = 1;
}
}
export default Layer_merge_class;
+24
View File
@@ -0,0 +1,24 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
class Layer_move_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
up() {
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, 1)
);
}
down() {
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, -1)
);
}
}
export default Layer_move_class;
+97
View File
@@ -0,0 +1,97 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import GUI_tools_class from './../../core/gui/gui-tools.js';
import Base_selection_class from './../../core/base-selection.js';
import Selection_class from './../../tools/selection.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Layer_new_class {
constructor() {
this.Base_layers = new Base_layers_class();
this.Selection = new Selection_class();
this.Base_selection = new Base_selection_class(this.Base_layers.ctx);
this.GUI_tools = new GUI_tools_class();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 78 && event.ctrlKey != true && event.metaKey != true) {
//N
this.new();
}
}, false);
}
new() {
app.State.do_action(
new app.Actions.Insert_layer_action()
);
}
new_selection() {
var selection = this.Base_selection.get_selection();
var layer = config.layer;
if (selection.width === null || config.layer.type != 'image') {
alertify.error('Empty selection or type not image.');
return;
}
if (config.TOOL.name != 'selection') {
alertify.error('Empty selection or type not image.');
return;
}
//if image was stretched
var width_ratio = (layer.width / layer.width_original);
var height_ratio = (layer.height / layer.height_original);
var left = selection.x - layer.x;
var top = selection.y - layer.y;
//adapt to origin size
selection.width = selection.width / width_ratio;
selection.height = selection.height / height_ratio;
//create new layer
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = Math.round(selection.width);
canvas.height = Math.round(selection.height);
ctx.translate(-left / width_ratio, -top / height_ratio);
ctx.drawImage(config.layer.link, 0, 0);
ctx.translate(0, 0);
//register it
var params = {
x: Math.round(selection.x),
y: Math.round(selection.y),
width: Math.round(selection.width * width_ratio),
height: Math.round(selection.height * height_ratio),
width_original: Math.round(selection.width),
height_original: Math.round(selection.height),
type: 'image',
data: canvas.toDataURL("image/png"),
};
app.State.do_action(
new app.Actions.Bundle_action('new_layer', 'New Layer', [
new app.Actions.Insert_layer_action(params, false),
...this.Selection.on_leave(),
new app.Actions.Activate_tool_action('select')
])
);
}
}
export default Layer_new_class;
+38
View File
@@ -0,0 +1,38 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Layer_raster_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
raster() {
var canvas = this.Base_layers.convert_layer_to_canvas();
var current_layer = config.layer;
var current_id = current_layer.id;
//show
var params = {
type: 'image',
name: config.layer.name + ' + raster',
data: canvas.toDataURL("image/png"),
x: parseInt(canvas.dataset.x),
y: parseInt(canvas.dataset.y),
width: canvas.width,
height: canvas.height,
opacity: current_layer.opacity,
};
app.State.do_action(
new app.Actions.Bundle_action('convert_to_raster', 'Convert to Raster', [
new app.Actions.Insert_layer_action(params, false),
new app.Actions.Delete_layer_action(current_id)
])
);
}
}
export default Layer_raster_class;
+55
View File
@@ -0,0 +1,55 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
class Layer_rename_class {
constructor() {
this.Base_layers = new Base_layers_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
}
rename(id = null) {
var _this = this;
var name_ = this.Helper.escapeHtml(config.layer.name);
var settings = {
title: 'Rename',
params: [
{name: "name", title: "Name:", value: name_},
],
on_load: function () {
document.querySelector('#pop_data_name').select();
},
on_finish: function (params) {
app.State.do_action(
new app.Actions.Bundle_action('rename_layer', 'Rename Layer', [
new app.Actions.Refresh_layers_gui_action('undo'),
new app.Actions.Update_layer_action(id || config.layer.id, {
name: _this.validate_name(params.name)
}),
new app.Actions.Refresh_layers_gui_action('do')
])
);
},
};
this.POP.show(settings);
}
validate_name(text) {
text = text
.replace(/&/g, "-")
.replace(/</g, "-")
.replace(/>/g, "-")
.replace(/"/g, "-")
.replace(/'/g, "-");
return text;
}
}
export default Layer_rename_class;
@@ -0,0 +1,19 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
class Layer_visibility_class {
constructor() {
this.Base_layers = new Base_layers_class();
}
toggle() {
app.State.do_action(
new app.Actions.Toggle_layer_visibility_action(config.layer.id)
);
}
}
export default Layer_visibility_class;
@@ -0,0 +1,82 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Tools_colorToAlpha_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
}
color_to_alpha() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Color to Alpha',
preview: true,
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params.color);
canvas_preview.putImageData(data, 0, 0);
},
params: [
{name: "color", title: "Color:", value: config.COLOR, type: 'color'},
],
on_finish: function (params) {
_this.apply_affect(params.color);
},
};
this.POP.show(settings);
}
apply_affect(color) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, color);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, color) {
var imgData = data.data;
var back_color = this.Helper.hexToRgb(color);
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
//calculate difference from requested color, and change alpha
var diff = Math.abs(imgData[i] - back_color.r) + Math.abs(imgData[i + 1] - back_color.g) + Math.abs(imgData[i + 2] - back_color.b) / 3;
imgData[i + 3] = Math.round(diff);
//combining 2 layers in future will change colors, so make changes to get same colors in final image
//color_result = color_1 * (alpha_1 / 255) * (1 - A2 / 255) + color_2 * (alpha_2 / 255)
//color_2 = (color_result - color_1 * (alpha_1 / 255) * (1 - A2 / 255)) / (alpha_2 / 255)
imgData[i] = Math.ceil((imgData[i] - back_color.r * (1 - imgData[i + 3] / 255)) / (imgData[i + 3] / 255));
imgData[i + 1] = Math.ceil((imgData[i + 1] - back_color.g * (1 - imgData[i + 3] / 255)) / (imgData[i + 3] / 255));
imgData[i + 2] = Math.ceil((imgData[i + 2] - back_color.b * (1 - imgData[i + 3] / 255)) / (imgData[i + 3] / 255));
}
return data;
}
}
export default Tools_colorToAlpha_class;
@@ -0,0 +1,83 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Tools_colorZoom_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
color_zoom() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Color zoom',
preview: true,
params: [
{name: "zoom", title: "Zoom:", value: "2", range: [2, 20], },
{name: "center", title: "Center:", value: "128", range: [0, 255]},
],
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.change(img, params.zoom, params.center);
canvas_preview.putImageData(data, 0, 0);
},
on_finish: function (params) {
_this.save_zoom(params.zoom, params.center);
},
};
this.POP.show(settings);
}
save_zoom(zoom, center) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.change(img, zoom, center);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, zoom, center) {
var imgData = data.data;
var grey;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
for (var j = 0; j < 3; j++) {
var k = i + j;
if (grey > center)
imgData[k] += (imgData[k] - center) * zoom;
else if (grey < center)
imgData[k] -= (center - imgData[k]) * zoom;
if (imgData[k] < 0)
imgData[k] = 0;
if (imgData[k] > 255)
imgData[k] = 255;
}
}
return data;
}
}
export default Tools_colorZoom_class;
@@ -0,0 +1,266 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import ImageFilters from './../../libs/imagefilters.js';
import Image_trim_class from './../image/trim.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Tools_contentFill_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Image_trim = new Image_trim_class();
}
content_fill() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
if (config.layer.x == 0 && config.layer.y == 0 && config.layer.width == config.WIDTH
&& config.layer.height == config.HEIGHT) {
alertify.error('Can not use this tool on current layer: image already takes all area.');
return;
}
var settings = {
title: 'Content Fill',
preview: true,
on_change: function (params, canvas_preview, w, h, canvasElement) {
canvas_preview.clearRect(0, 0, w, h);
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
//change data
_this.change(canvas, params);
//add to preview
canvas_preview.drawImage(canvas, 0, 0, w, h);
},
params: [
{name: "mode", title: "Mode:", values: ['Expand edges', 'Cloned edges', 'Resized as background'], },
{name: "blur_power", title: "Blur power:", value: 5, range: [1, 20]},
{name: "blur_h", title: "Horizontal blur:", value: 5, range: [0, 30]},
{name: "blur_v", title: "Vertical blur:", value: 5, range: [0, 30]},
{name: "clone_count", title: "Clone count:", value: 15, range: [10, 50]},
],
on_finish: function (params) {
_this.apply_affect(params);
},
};
this.POP.show(settings);
}
apply_affect(params) {
//create tmp canvas
var canvas = document.createElement('canvas');
canvas.width = config.WIDTH;
canvas.height = config.HEIGHT;
//change data
this.change(canvas, params);
//save
return app.State.do_action(
new app.Actions.Bundle_action('content_fill', 'Content Fill', [
new app.Actions.Update_layer_action(config.layer.id, {
x: 0,
y: 0,
width: config.WIDTH,
height: config.HEIGHT
}),
new app.Actions.Update_layer_image_action(canvas)
])
);
}
change(canvas, params) {
var ctx = canvas.getContext("2d");
var mode = params.mode;
//generate background
if (mode == 'Expand edges')
this.add_edge_background(canvas, params);
else if (mode == 'Resized as background')
this.add_resized_background(canvas, params);
else if (mode == 'Cloned edges')
this.add_cloned_background(canvas, params);
//draw original image
this.Base_layers.render_object(ctx, config.layer);
}
add_edge_background(canvas, params) {
var ctx = canvas.getContext("2d");
var trim_info = this.Image_trim.get_trim_info(config.layer.id);
var original = this.Base_layers.convert_layer_to_canvas();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(original, trim_info.left, trim_info.top);
//draw top
ctx.drawImage(original,
0, 0, original.width, 1, //source
trim_info.left, 0, original.width, trim_info.top); //target
//bottom
ctx.drawImage(original,
0, original.height - 1, original.width, 1,
trim_info.left, trim_info.top + original.height, original.width, canvas.height);
//left
ctx.drawImage(original,
0, 0, 1, original.height,
0, trim_info.top, trim_info.left, original.height);
//right
ctx.drawImage(original,
original.width - 1, 0, 1, original.height,
trim_info.left + original.width, trim_info.top, canvas.width, original.height);
//fill corners
//left top
ctx.drawImage(original,
0, 0, 1, 1,
0, 0, trim_info.left, trim_info.top);
//right top
ctx.drawImage(original,
original.width - 1, 0, 1, 1,
trim_info.left + original.width, 0, canvas.width, trim_info.top);
//left bottom
ctx.drawImage(original,
0, original.height - 1, 1, 1,
0, trim_info.top + original.height, trim_info.left, trim_info.bottom);
//right bottom
ctx.drawImage(original,
original.width - 1, original.height - 1, 1, 1,
trim_info.left + original.width, trim_info.top + original.height, trim_info.right, trim_info.bottom);
//add blur
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var blurred = ImageFilters.BoxBlur(img, params.blur_h, params.blur_v, params.blur_power);
ctx.putImageData(blurred, 0, 0);
}
add_resized_background(canvas, params) {
var ctx = canvas.getContext("2d");
//draw original resized
var original = this.Base_layers.convert_layer_to_canvas();
ctx.drawImage(original, 0, 0, canvas.width, canvas.height);
//add blur
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var blurred = ImageFilters.BoxBlur(img, params.blur_h, params.blur_v, params.blur_power);
ctx.putImageData(blurred, 0, 0);
}
add_cloned_background(canvas, params) {
var blocks = params.clone_count;
var ctx = canvas.getContext("2d");
var trim_info = this.Image_trim.get_trim_info(config.layer.id);
var original = this.Base_layers.convert_layer_to_canvas();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(original, trim_info.left, trim_info.top);
//top
var bsize = Math.ceil(original.width / blocks);
for (var i = 0; i < original.width; i = i + bsize) {
for (var j = 0; j < trim_info.top; j = j + bsize) {
ctx.drawImage(original,
i, 0, bsize, bsize,
trim_info.left + i, 0 + j, bsize, bsize);
}
}
//bottom
var bsize = Math.ceil(original.width / blocks);
for (var i = 0; i < original.width; i = i + bsize) {
for (var j = 0; j < canvas.height; j = j + bsize) {
ctx.drawImage(original,
i, original.height - bsize, bsize, bsize,
trim_info.left + i, trim_info.top + original.height + j, bsize, bsize);
}
}
//left
var bsize = Math.ceil(original.height / blocks);
for (var i = 0; i < trim_info.left; i = i + bsize) {
for (var j = trim_info.top; j < trim_info.top + original.height; j = j + bsize) {
ctx.drawImage(original,
0, j - trim_info.top, bsize, bsize,
i, j, bsize, bsize);
}
}
//right
var bsize = Math.ceil(original.height / blocks);
for (var i = trim_info.left + original.width; i < canvas.width; i = i + bsize) {
for (var j = trim_info.top; j < trim_info.top + original.height; j = j + bsize) {
ctx.drawImage(original,
original.width - bsize, j - trim_info.top, bsize, bsize,
i, j, bsize, bsize);
}
}
//corners
var bsize = Math.ceil(Math.min(original.width, original.height) / blocks);
//top left
for (var i = 0; i < trim_info.left; i = i + bsize) {
for (var j = 0; j < trim_info.top; j = j + bsize) {
ctx.drawImage(original,
0, 0, bsize, bsize,
i, j, bsize, bsize);
}
}
//top right
for (var i = trim_info.left + original.width; i < canvas.width; i = i + bsize) {
for (var j = 0; j < trim_info.top; j = j + bsize) {
ctx.drawImage(original,
original.width - bsize, 0, bsize, bsize,
i, j, bsize, bsize);
}
}
//bottom left
for (var i = 0; i < trim_info.left; i = i + bsize) {
for (var j = trim_info.top + original.height; j < canvas.height; j = j + bsize) {
ctx.drawImage(original,
0, original.height - bsize, bsize, bsize,
i, j, bsize, bsize);
}
}
//bottom right
for (var i = trim_info.left + original.width; i < canvas.width; i = i + bsize) {
for (var j = trim_info.top + original.height; j < canvas.height; j = j + bsize) {
ctx.drawImage(original,
original.width - bsize, original.height - bsize, bsize, bsize,
i, j, bsize, bsize);
}
}
//add blur
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var blurred = ImageFilters.BoxBlur(img, params.blur_h, params.blur_v, params.blur_power);
ctx.putImageData(blurred, 0, 0);
}
}
export default Tools_contentFill_class;
+225
View File
@@ -0,0 +1,225 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Helper_class from './../../libs/helpers.js';
import ImageFilters_class from './../../libs/imagefilters.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
/**
* SIFT: scale-invariant-feature-transform, keypoints
*
* @author ViliusL
*/
class Tools_keypoints_class {
constructor() {
this.Helper = new Helper_class();
this.Base_layers = new Base_layers_class();
this.ImageFilters = ImageFilters_class;
//contrast check, smaller - more points, better accuracy, but slower
this.avg_offset = 50;
/**
* how much pixels to check for each side to get average
*/
this.avg_step = 4;
}
//generate key points for image
keypoints(return_data) {
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var W = config.layer.width;
var H = config.layer.height;
//get canvas from layer
var clone = this.Base_layers.convert_layer_to_canvas();
var ctx = clone.getContext("2d");
//greyscale
var imageData = ctx.getImageData(0, 0, W, H);
var data = this.convert_to_grayscale(imageData);
ctx.putImageData(data, 0, 0);
//make few copies and blur each
var n = 5;
var copies = [];
for (var i = 0; i < n; i++) {
var tmp_canvas = document.createElement('canvas');
tmp_canvas.width = W;
tmp_canvas.height = H;
var ctx_i = tmp_canvas.getContext("2d");
ctx_i.drawImage(clone, 0, 0);
//Gausian blur
var imageData = ctx_i.getImageData(0, 0, W, H);
var filtered = this.ImageFilters.GaussianBlur(imageData, i + 0.5); //add effect
ctx_i.putImageData(filtered, 0, 0);
copies.push(tmp_canvas);
}
//find extreme points
var points = [];
var n0 = this.avg_step * 2 + 1;
for (var c = 1; c < copies.length - 1; c++) {
var imageData = copies[c].getContext("2d").getImageData(0, 0, W, H).data;
var imageData0 = copies[c - 1].getContext("2d").getImageData(0, 0, W, H).data;
var imageData2 = copies[c + 1].getContext("2d").getImageData(0, 0, W, H).data;
for (var j = this.avg_step; j < H - this.avg_step; j++) {
for (var i = this.avg_step; i < W - this.avg_step; i++) {
var x = (i + j * W) * 4;
if (imageData[x + 3] == 0)
continue; //transparent
if (imageData[x] < imageData[x - 4] || imageData[x] < imageData[x + 4] || imageData[x] > imageData[x - 4] || imageData[x] > imageData[x + 4]) {
var x_pre = (i + (j - 1) * W) * 4;
var x_post = (i + (j + 1) * W) * 4;
//calc average
var area_average = 0;
for (var l = -this.avg_step; l <= this.avg_step; l++) {
var avgi = (i + (j - l) * W) * 4;
for (var a = -this.avg_step; a <= this.avg_step; a++) {
area_average += imageData[avgi + 4 * a];
}
}
area_average = area_average / (n0 * n0);
//max
if (imageData[x] + this.avg_offset < area_average) {
var min = Math.min(imageData[x_pre - 4], imageData[x_pre], imageData[x_pre + 4], imageData[x - 4], imageData[x + 4], imageData[x_post - 4], imageData[x_post], imageData[x_post + 4]);
if (imageData[x] <= min) {
var min0 = Math.min(imageData0[x_pre - 4], imageData0[x_pre], imageData0[x_pre + 4], imageData0[x - 4], imageData0[x + 4], imageData0[x_post - 4], imageData0[x_post], imageData0[x_post + 4]);
if (imageData[x] <= min0) {
var min2 = Math.min(imageData2[x_pre - 4], imageData2[x_pre], imageData2[x_pre + 4], imageData2[x - 4], imageData2[x + 4], imageData2[x_post - 4], imageData2[x_post], imageData2[x_post + 4]);
if (imageData[x] <= min2)
points.push({
x: i,
y: j,
w: Math.round(area_average - imageData[x] - this.avg_offset)
});
}
}
continue;
}
//min
if (imageData[x] - this.avg_offset > area_average) {
var max = Math.max(imageData[x_pre - 4], imageData[x_pre], imageData[x_pre + 4], imageData[x - 4], imageData[x + 4], imageData[x_post - 4], imageData[x_post], imageData[x_post + 4]);
if (imageData[x] >= max) {
var max0 = Math.max(imageData0[x_pre - 4], imageData0[x_pre], imageData0[x_pre + 4], imageData0[x - 4], imageData0[x + 4], imageData0[x_post - 4], imageData0[x_post], imageData0[x_post + 4]);
if (imageData[x] >= max0) {
var max2 = Math.max(imageData2[x_pre - 4], imageData2[x_pre], imageData2[x_pre + 4], imageData2[x - 4], imageData2[x + 4], imageData2[x_post - 4], imageData2[x_post], imageData2[x_post + 4]);
if (imageData[x] >= max2) {
points.push({
x: i,
y: j,
w: Math.round(imageData[x] - area_average - this.avg_offset)
});
}
}
}
}
}
}
}
}
//make unique
for (var i = 0; i < points.length; i++) {
for (var j = 0; j < points.length; j++) {
if (i != j && points[i].x == points[j].x && points[i].y == points[j].y) {
points.splice(i, 1);
i--;
break;
}
}
}
//show points?
if (return_data === undefined || return_data !== true) {
alertify.success('key points: ' + points.length);
var size = 3;
ctx.clearRect(0, 0, clone.width, clone.height);
ctx.fillStyle = "#ff0000";
for (var i in points) {
var point = points[i];
ctx.beginPath();
ctx.rect(point.x - Math.floor(size / 2) + 1, point.y - Math.floor(size / 2) + 1, size, size);
ctx.fill();
}
//show
var params = [];
params.type = 'image';
params.name = config.layer.name + ' + key points';
params.data = clone.toDataURL("image/png");
params.x = parseInt(clone.dataset.x);
params.y = parseInt(clone.dataset.y);
params.width = clone.width;
params.height = clone.height;
app.State.do_action(
new app.Actions.Bundle_action('keypoints', 'Key-Points', [
new app.Actions.Insert_layer_action(params)
])
);
clone.width = 1;
clone.height = 1;
}
else {
//sort by weights
points.sort(function (a, b) {
return parseFloat(b.w) - parseFloat(a.w);
});
clone.width = 1;
clone.height = 1;
return {
points: points,
};
}
}
//returns average value of requested area from greyscale image
//area = {x, y, w, h}
get_area_average(area, imageData, i, j, size) {
var imgData = imageData.data;
var sum = 0;
var n = 0;
size = size / 100; //prepare to use 1-100% values
var stop_x = i + Math.round(size * area.x) + Math.round(size * area.w);
var stop_y = j + Math.round(size * area.y) + Math.round(size * area.h);
var img_width4 = imageData.width * 4;
var k0, k;
for (var y = j + Math.round(size * area.y); y < stop_y; y++) {
k0 = y * img_width4;
for (var x = i + Math.round(size * area.x); x < stop_x; x++) {
k = k0 + (x * 4);
sum = sum + imgData[k];
n++;
}
}
return Math.round(sum / n);
}
convert_to_grayscale(data) {
var imgData = data.data;
var grey;
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
grey = Math.round(0.2126 * imgData[i] + 0.7152 * imgData[i + 1] + 0.0722 * imgData[i + 2]);
imgData[i] = grey;
imgData[i + 1] = grey;
imgData[i + 2] = grey;
}
return data;
}
}
export default Tools_keypoints_class;
@@ -0,0 +1,127 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Tools_replaceColor_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
}
replace_color() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Replace color',
preview: true,
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.do_replace(img, params);
canvas_preview.putImageData(data, 0, 0);
},
params: [
{name: "target", title: "Target:", value: config.COLOR, type: 'color'},
{name: "replacement", title: "Replacement:", value: '#ff0000', type: 'color'},
{name: "power", title: "Power:", value: "20", range: [0, 255]},
{name: "alpha", title: "Alpha:", value: "255", range: [0, 255]},
{name: "mode", title: "Mode:", values: ['Advanced', 'Simple']},
],
on_finish: function (params) {
_this.save_alpha(params);
},
};
this.POP.show(settings);
}
save_alpha(params) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.do_replace(img, params);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
do_replace(data, params) {
var target = params.target;
var replacement = params.replacement;
var power = params.power;
var alpha = params.alpha;
var mode = params.mode;
var imgData = data.data;
var target_rgb = this.Helper.hexToRgb(target);
var target_hsl = this.Helper.rgbToHsl(target_rgb.r, target_rgb.g, target_rgb.b);
var target_normalized = this.Helper.hslToRgb(target_hsl.h, target_hsl.s, 0.5);
var replacement_rgb = this.Helper.hexToRgb(replacement);
var replacement_hsl = this.Helper.rgbToHsl(replacement_rgb.r, replacement_rgb.g, replacement_rgb.b);
for (var i = 0; i < imgData.length; i += 4) {
if (imgData[i + 3] == 0)
continue; //transparent
if (mode == 'Simple') {
//simple replace
//calculate difference from requested color, and change alpha
var diff = (Math.abs(imgData[i] - target_rgb.r)
+ Math.abs(imgData[i + 1] - target_rgb.g)
+ Math.abs(imgData[i + 2] - target_rgb.b)) / 3;
if (diff > power)
continue;
imgData[i] = replacement_rgb.r;
imgData[i + 1] = replacement_rgb.g;
imgData[i + 2] = replacement_rgb.b;
if (alpha < 255)
imgData[i + 3] = alpha;
}
else {
//advanced replace using HSL
var hsl = this.Helper.rgbToHsl(imgData[i], imgData[i + 1], imgData[i + 2]);
var normalized = this.Helper.hslToRgb(hsl.h, hsl.s, 0.5);
var diff = (Math.abs(normalized.r - target_normalized.r)
+ Math.abs(normalized.g - target_normalized.g)
+ Math.abs(normalized.b - target_normalized.b)) / 3;
if (diff > power)
continue;
//change to new color with existing luminance
var normalized_final = this.Helper.hslToRgb(
replacement_hsl.h,
replacement_hsl.s,
hsl.l * (replacement_hsl.l)
);
imgData[i] = normalized_final.r;
imgData[i + 1] = normalized_final.g;
imgData[i + 2] = normalized_final.b;
if (alpha < 255)
imgData[i + 3] = alpha;
}
}
return data;
}
}
export default Tools_replaceColor_class;
@@ -0,0 +1,72 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
class Tools_restoreAlpha_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
}
restore_alpha() {
var _this = this;
if (config.layer.type != 'image') {
alertify.error('This layer must contain an image. Please convert it to raster to apply this tool.');
return;
}
var settings = {
title: 'Restore Alpha',
preview: true,
on_change: function (params, canvas_preview, w, h) {
var img = canvas_preview.getImageData(0, 0, w, h);
var data = _this.recover_alpha(img, params.level);
canvas_preview.putImageData(data, 0, 0);
},
params: [
{name: "level", title: "Level:", value: "128", range: [0, 255]},
],
on_finish: function (params) {
_this.save_alpha(params.level);
},
};
this.POP.show(settings);
}
save_alpha(level) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
//change data
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = this.recover_alpha(img, level);
ctx.putImageData(data, 0, 0);
//save
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
recover_alpha(data, level) {
var imgData = data.data;
var tmp;
level = parseInt(level);
for (var i = 0; i < imgData.length; i += 4) {
tmp = imgData[i + 3] + level;
if (tmp > 255) {
tmp = 255;
}
imgData[i + 3] = tmp;
}
return data;
}
}
export default Tools_restoreAlpha_class;
+15
View File
@@ -0,0 +1,15 @@
import Base_search_class from './../../core/base-search.js';
class Tools_search_class {
constructor() {
this.Base_search = new Base_search_class();
}
search() {
this.Base_search.search();
}
}
export default Tools_search_class;
+171
View File
@@ -0,0 +1,171 @@
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import Base_gui_class from './../../core/base-gui.js';
class Tools_settings_class {
constructor() {
this.Base_gui = new Base_gui_class();
this.POP = new Dialog_class();
this.Helper = new Helper_class();
this.default_units_config = {
pixels: 'px',
inches: '"',
centimeters: 'cm',
millimetres: 'mm',
};
}
settings() {
var _this = this;
var transparency_values = ['squares', 'green', 'grey'];
var resolutions_values = [72, 150, 300, 600];
var default_units_all = Object.keys(this.default_units_config);
var transparency = this.get_setting('transparency');
var theme = this.get_setting('theme');
var snap = this.get_setting('snap');
var guides = this.get_setting('guides');
var safe_search = this.get_setting('safe_search');
var exit_confirm = this.get_setting('exit_confirm');
var default_units = this.get_setting('default_units');
var resolution = this.get_setting('resolution');
var thick_guides = this.get_setting('thick_guides');
var enable_autoresize = this.get_setting('enable_autoresize');
var settings = {
title: 'Settings',
params: [
{name: "transparency", title: "Transparent:", value: transparency},
{name: "transparency_type", title: "Transparency background:", type: "select",
value: config.TRANSPARENCY_TYPE, values: transparency_values},
{name: "theme", title: "Theme", values: config.themes, value: theme, type: "select"},
{name: "default_units", title: "Units", values: default_units_all, value: default_units, type: "select"},
{name: "resolution", title: "Resolution:", type: "select",
value: resolution, values: resolutions_values},
{name: "snap", title: "Enable snap:", value: snap},
{name: "guides", title: "Enable guides:", value: guides},
{name: "safe_search", title: "Safe search:", value: safe_search},
{name: "exit_confirm", title: "Exit confirmation:", value: exit_confirm},
{name: "thick_guides", title: "Thick guides:", value: thick_guides},
{name: "enable_autoresize", title: "Enable autoresize:", value: enable_autoresize},
],
on_change: function (params) {
this.Base_gui.change_theme(params.theme);
},
on_cancel: function (params) {
this.Base_gui.change_theme(theme);
},
on_finish: function (params) {
_this.save_values(params);
},
};
this.POP.show(settings);
}
save_values(params) {
//save
this.save_setting('theme', params.theme);
this.save_setting('transparency', params.transparency);
this.save_setting('transparency_type', params.transparency_type);
this.save_setting('snap', params.snap);
this.save_setting('guides', params.guides);
this.save_setting('safe_search', params.safe_search);
this.save_setting('exit_confirm', params.exit_confirm);
this.save_setting('default_units', params.default_units);
this.save_setting('default_units_short', this.default_units_config[params.default_units]);
this.save_setting('resolution', params.resolution);
this.save_setting('thick_guides', params.thick_guides);
this.save_setting('enable_autoresize', params.enable_autoresize);
//update config
config.TRANSPARENCY = this.get_setting('transparency');
config.TRANSPARENCY_TYPE = this.get_setting('transparency_type');
config.SNAP = this.get_setting('snap');
config.guides_enabled = this.get_setting('guides');
this.Base_gui.change_theme(this.get_setting('theme'));
this.Base_gui.GUI_information.update_units();
//finish
this.Base_gui.prepare_canvas();
config.need_render = true;
}
/**
* set global setting. Values can be string(1 or 0 will be converted to boolean) or boolean
*
* @param key
* @param value
*/
save_setting(key, value) {
//prepare
if(value === true){
value = 1;
}
if(value === false){
value = 0;
}
this.Helper.setCookie(key, value);
}
/**
* get global setting. If settings does not exists, default valye will be used.
*
* @param key
* @returns {Object|string}
*/
get_setting(key) {
var default_values = {
'theme': null,
'transparency': false,
'snap': true,
'guides': true,
'safe_search': true,
'exit_confirm': true,
'default_units': Object.keys(this.default_units_config)[0],
'default_units_short': Object.values(this.default_units_config)[0],
'resolution': 72,
'thick_guides': false,
'enable_autoresize': config.enable_autoresize_by_default,
};
var value = this.Helper.getCookie(key);
if(value == null && default_values[key] != undefined){
//set default value
value = default_values[key];
}
if(key == 'safe_search' && config.safe_search_can_be_disabled === false){
//not allowed
value = 1;
}
if(key == 'theme' && value == null) {
value = config.themes[0];
/*if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
&& config.themes.includes('dark')) {
//dark mode
value = 'dark';
}
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches
&& config.themes.includes('light')) {
//light mode
value = 'light';
}*/
}
//finalize values
if(value === 1){
value = true;
}
if(value === 0){
value = false;
}
return value;
}
}
export default Tools_settings_class;
+123
View File
@@ -0,0 +1,123 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
import Dialog_class from './../../libs/popup.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Image_trim_class from './../image/trim.js';
import Base_gui_class from './../../core/base-gui.js';
class Tools_sprites_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Image_trim = new Image_trim_class();
this.Base_gui = new Base_gui_class();
}
sprites() {
var _this = this;
var settings = {
title: 'Sprites',
params: [
{name: "gap", title: "Gap:", value: "50", values: ["0", "10", "50", "100"]},
],
on_finish: function (params) {
_this.generate_sprites(params.gap);
},
};
this.POP.show(settings);
}
generate_sprites(gap, sprite_width) {
gap = parseInt(gap);
if (config.layers.length == 1) {
alertify.error('There is only 1 layer.');
return false;
}
var xx = 0;
var yy = 0;
var max_height = 0;
let actions = [];
let new_height = config.HEIGHT;
let new_width = config.WIDTH;
//collect trim info
var trim_details_array = [];
for (var i = 0; i < config.layers.length; i++) {
var layer = config.layers[i];
if (layer.visible == false)
continue;
trim_details_array[layer.id] = this.Image_trim.get_trim_info(layer.id);
}
//move layers
for (var i = 0; i < config.layers.length; i++) {
var layer = config.layers[i];
if (layer.visible == false)
continue;
var trim_details = trim_details_array[layer.id];
if (new_width == trim_details.left) {
//empty layer
continue;
}
var width = new_width - trim_details.left - trim_details.right;
var height = config.HEIGHT - trim_details.top - trim_details.bottom;
if (xx + width > new_width) {
xx = 0;
yy += max_height;
max_height = 0;
}
if (yy % gap > 0 && gap > 0) {
yy = yy - yy % gap + gap;
}
if (yy + height > new_height) {
new_height = parseInt(yy + height);
this.Base_gui.prepare_canvas();
}
actions.push(
new app.Actions.Update_layer_action(layer.id, {
x: layer.x + xx - trim_details.left,
y: layer.y + yy - trim_details.top
})
);
xx += width;
if (gap > 0) {
xx = xx - xx % gap + gap;
}
if (height > max_height) {
max_height = height;
}
if (xx > new_width) {
xx = 0;
yy += max_height;
max_height = 0;
}
}
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: new_width,
HEIGHT: new_height
}),
new app.Actions.Prepare_canvas_action('do')
);
app.State.do_action(
new app.Actions.Bundle_action('sprites', 'Sprites', actions)
);
}
}
export default Tools_sprites_class;
@@ -0,0 +1,70 @@
import config from './../../config.js';
import Helper_class from './../../libs/helpers.js';
import Translate_class from './../../libs/jquery.translate.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Tools_translate_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Helper = new Helper_class();
this.translations = {};
this.trans_lang_codes = [];
this.load_translations();
}
//change language
translate(lang_code, element) {
if (lang_code == undefined) {
lang_code = this.Helper.getCookie('language');
if (!lang_code) {
return;
}
}
if (lang_code != undefined && lang_code != config.LANG) {
//save cookie
this.Helper.setCookie('language', lang_code);
}
if (this.trans_lang_codes.includes(lang_code) || lang_code == 'en') {
//translate
$(element || 'body').translate({lang: lang_code, t: this.translations});
config.LANG = lang_code;
}
else {
alertify.error('Translate error, can not find dictionary: ' + lang_code);
}
}
load_translations() {
var _this = this;
var modules_context = require.context("./../../languages/", true, /\.json$/);
modules_context.keys().forEach(function (key) {
if (key.indexOf('Base' + '/') < 0 && key.indexOf('empty') < 0) {
var moduleKey = key.replace('./', '').replace('.json', '');
var classObj = modules_context(key);
for(var i in classObj){
if(_this.translations[i] == undefined){
_this.translations[i] = {
en: i,
};
}
_this.translations[i][moduleKey] = classObj[i];
}
_this.trans_lang_codes.push(moduleKey);
}
});
}
}
export default Tools_translate_class;
@@ -0,0 +1,20 @@
class View_fullScreen_class {
constructor() {}
/**
* toggle full-screen
*/
fs() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
}
else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
}
}
export default View_fullScreen_class;
+48
View File
@@ -0,0 +1,48 @@
import config from './../../config.js';
import Helper_class from './../../libs/helpers.js';
import Base_gui_class from './../../core/base-gui.js';
var instance = null;
class View_grid_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.GUI = new Base_gui_class();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
var code = event.keyCode;
if (this.Helper.is_input(event.target))
return;
if (code == 71 && event.ctrlKey != true && event.metaKey != true) {
//G - grid
this.grid({visible: !this.GUI.grid});
event.preventDefault();
}
}, false);
}
grid() {
if (this.GUI.grid == false) {
this.GUI.grid = true;
}
else {
this.GUI.grid = false;
}
config.need_render = true;
}
}
export default View_grid_class;
+146
View File
@@ -0,0 +1,146 @@
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Helper_class from './../../libs/helpers.js';
import Base_layers_class from './../../core/base-layers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Tools_settings_class from './../tools/settings.js';
import app from './../../app.js';
class View_guides_class {
constructor() {
this.POP = new Dialog_class();
this.Base_layers = new Base_layers_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
}
insert() {
var _this = this;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
//convert units
var position = 20;
var position = this.Helper.get_user_unit(position, units, resolution);
var settings = {
title: 'Insert guides',
params: [
{name: "type", title: "Type:", values: ["Vertical", "Horizontal"], value :"Vertical"},
{name: "position", title: "Position:", value: position},
],
on_finish: function (params) {
_this.insert_handler(params);
},
};
this.POP.show(settings);
}
insert_handler(data){
var type = data.type;
var position = parseFloat(data.position);
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
//convert units
position = this.Helper.get_internal_unit(position, units, resolution);
var x = null;
var y = null;
if(type == 'Vertical')
x = position;
if(type == 'Horizontal')
y = position;
//update
config.guides.push({x: x, y: y});
if(config.guides_enabled == false){
//was disabled
config.guides_enabled = true;
this.Helper.setCookie('guides', 1);
alertify.warning('Guides enabled.');
}
config.need_render = true;
}
update(){
var _this = this;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var params = [];
for(var i in config.guides){
var guide = config.guides[i];
//convert units
var value = guide.x;
var value = this.Helper.get_user_unit(value, units, resolution);
if(guide.y === null) {
params.push({name: i, title: "Vertical:", value: value});
}
}
for(var i in config.guides){
var guide = config.guides[i];
//convert units
var value = guide.y;
var value = this.Helper.get_user_unit(value, units, resolution);
if(guide.x === null) {
params.push({name: i, title: "Horizontal:", value: value});
}
}
var settings = {
title: 'Update guides',
params: params,
on_finish: function (params) {
_this.update_handler(params);
},
};
this.POP.show(settings);
}
update_handler(data){
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
//update
for (var i in data) {
var key = parseInt(i);
var value = parseFloat(data[i]);
//convert units
value = this.Helper.get_internal_unit(value, units, resolution);
if (config.guides[key].x === null)
config.guides[key].y = value;
else
config.guides[key].x = value;
}
//remove empty
for (var i = 0; i < config.guides.length; i++) {
if(config.guides[i].x === 0 || config.guides[i].y === 0
|| isNaN(config.guides[i].x) || isNaN( config.guides[i].y)){
config.guides.splice(i, 1);
i--;
}
}
config.need_render = true;
}
remove(params) {
config.guides = [];
config.need_render = true;
}
}
export default View_guides_class;
+205
View File
@@ -0,0 +1,205 @@
import config from './../../config.js';
import Helper_class from './../../libs/helpers.js';
import Base_gui_class from './../../core/base-gui.js';
import Base_layers_class from './../../core/base-layers.js';
import Tools_settings_class from './../tools/settings.js';
var instance = null;
class View_ruler_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.GUI = new Base_gui_class();
this.Base_layers = new Base_layers_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
this.set_events();
}
set_events() {
var _this = this;
window.addEventListener('resize', function (event) {
//resize
_this.prepare_ruler();
_this.render_ruler();
}, false);
document.addEventListener('keydown', (event) => {
var code = event.code;
if (this.Helper.is_input(event.target))
return;
if (event.code == "KeyU" && event.ctrlKey != true && event.metaKey != true) {
_this.ruler();
event.preventDefault();
}
}, false);
}
ruler() {
var ruler_left = document.getElementById('ruler_left');
var ruler_top = document.getElementById('ruler_top');
var middle_area = document.getElementById('middle_area');
if(config.ruler_active == false){
//activate
config.ruler_active = true;
document.getElementById('middle_area').classList.add('has-ruler');
ruler_left.style.display = 'block';
ruler_top.style.display = 'block';
this.prepare_ruler();
this.render_ruler();
}
else{
//deactivate
config.ruler_active = false;
document.getElementById('middle_area').classList.remove('has-ruler');
ruler_left.style.display = 'none';
ruler_top.style.display = 'none';
}
this.GUI.prepare_canvas();
config.need_render = true;
}
prepare_ruler(){
if(config.ruler_active == false)
return;
var ruler_left = document.getElementById('ruler_left');
var ruler_top = document.getElementById('ruler_top');
var middle_area = document.getElementById('middle_area');
var middle_area_width = middle_area.clientWidth;
var middle_area_height = middle_area.clientHeight;
ruler_left.width = 15;
ruler_left.height = middle_area_height - 20;
ruler_top.width = middle_area_width - 20;
ruler_top.height = 15;
}
render_ruler(){
if(config.ruler_active == false)
return;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
var ruler_left = document.getElementById('ruler_left');
var ruler_top = document.getElementById('ruler_top');
var ctx_left = ruler_left.getContext("2d");
var ctx_top = ruler_top.getContext("2d");
var color = '#111';
var size = 15;
//calc step
var step = Math.ceil(10 * config.ZOOM);
while (step < 5) {
step = step * 2;
}
while (step > 10) {
step = Math.ceil(step / 2);
}
var step_big = step * 10;
//calc begin/end point
var begin_x = Math.max(0, ruler_top.width / 2 - config.WIDTH * config.ZOOM / 2);
var begin_y = Math.max(0, ruler_left.height / 2 - config.HEIGHT * config.ZOOM / 2);
var end_x = Math.min(ruler_top.width, ruler_top.width / 2 + config.WIDTH * config.ZOOM / 2);
var end_y = Math.min(ruler_left.height, ruler_left.height / 2 + config.HEIGHT * config.ZOOM / 2);
//left
ctx_left.strokeStyle = color;
ctx_left.lineWidth = 1;
ctx_left.font = "11px Arial";
ctx_left.clearRect(0, 0, ruler_left.width, ruler_left.height);
ctx_left.beginPath();
for (var i = begin_y; i < end_y; i += step) {
ctx_left.moveTo(10, i + 0.5);
ctx_left.lineTo(size, i + 0.5);
}
ctx_left.stroke();
ctx_left.beginPath();
for (var i = begin_y; i <= end_y; i += step_big) {
ctx_left.moveTo(0, i + 0.5);
ctx_left.lineTo(size, i + 0.5);
var global_pos = this.Base_layers.get_world_coords(0, i - begin_y);
var value = this.Helper.get_user_unit(global_pos.y, units, resolution);
if(units == 'inches'){
//more decimals value
var text = this.Helper.number_format(value, 1);
}
else{
var text = Math.ceil(value);
}
text = text.toString();
//text
for (var j = 0; j < text.length; j++) {
var letter = text.charAt(j);
var line_height = 10;
ctx_left.fillText(letter, 1, i + 11 + j * line_height);
}
}
ctx_left.stroke();
//top
ctx_top.strokeStyle = color;
ctx_top.lineWidth = 1;
ctx_top.font = "11px Arial";
ctx_top.clearRect(0, 0, ruler_top.width, ruler_top.height);
ctx_top.beginPath();
for (var i = begin_x; i < end_x; i += step) {
var y = (i / step_big == parseInt(i / step_big)) ? 0 : step;
ctx_top.moveTo(i + 0.5, 10);
ctx_top.lineTo(i + 0.5, size);
}
ctx_top.stroke();
ctx_top.beginPath();
for (var i = begin_x; i <= end_x; i += step_big) {
ctx_top.moveTo(i + 0.5, 0);
ctx_top.lineTo(i + 0.5, size);
var global_pos = this.Base_layers.get_world_coords(i - begin_x, 0);
var value = this.Helper.get_user_unit(global_pos.x, units, resolution);
if(units == 'inches'){
//more decimals value
var text = this.Helper.number_format(value, 1);
}
else{
var text = Math.ceil(value);
}
text = text.toString();
//text
ctx_top.fillText(text, i + 3, 9);
}
ctx_top.stroke();
}
}
export default View_ruler_class;
+26
View File
@@ -0,0 +1,26 @@
import GUI_preview_class from './../../core/gui/gui-preview.js';
class View_zoom_class {
constructor() {
this.GUI_preview = new GUI_preview_class();
}
in() {
this.GUI_preview.zoom(1);
}
out() {
this.GUI_preview.zoom(-1);
}
original() {
this.GUI_preview.zoom(100);
}
auto() {
this.GUI_preview.zoom_auto();
}
}
export default View_zoom_class;