Add miniPaint as new frontend base
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
//Floyd–Steinberg 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* manages files / print
|
||||
*
|
||||
* @author ViliusL
|
||||
*/
|
||||
class File_print_class {
|
||||
|
||||
print() {
|
||||
window.print();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default File_print_class;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 / ⌘ + 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user