Get most filters working with history and some tools

This commit is contained in:
acer 2020-12-06 00:13:14 -05:00
parent 2a67ea7782
commit 209524648c
80 changed files with 1282 additions and 414 deletions

View File

@ -72,13 +72,11 @@ function save_image(){
//update image using blob (faster)
tempCanvas.toBlob(function (blob) {
alert('Data length: ' + blob.size);
console.log(blob);
}, 'image/png');
}
else{
//slow way for IE, Edge
var data = tempCanvas.toDataURL();
console.log(data);
alert('Data length: ' + data.length);
}
}

View File

@ -0,0 +1,55 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Add_layer_filter_action extends Base_action {
/**
* register new live filter
*
* @param {int} layer_id
* @param {string} name
* @param {object} params
*/
constructor(layer_id, name, params) {
super('add_layer_filter', 'Add Layer Filter');
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.filter_id = Math.floor(Math.random() * 999999999) + 1; // A good UUID library would
this.name = name;
this.params = params;
this.reference_layer = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
var filter = {
id: this.filter_id,
name: this.name,
params: this.params,
};
this.reference_layer.filters.push(filter);
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer) {
this.reference_layer.filters.pop();
this.reference_layer = null;
}
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.params = null;
}
}

View File

@ -4,12 +4,14 @@ export class Base_action {
this.action_id = action_id;
this.action_description = action_description;
this.is_done = false;
this.memory_estimate = 0; // Estimate of how much memory will be freed when the free() method is called (in bytes)
}
do() {
this.is_done = true;
}
undo() {
this.is_done = false;
this.memory_estimate = 0;
}
free() {
// Override if need to run tasks to free memory when action is discarded from history

View File

@ -41,6 +41,11 @@ export class Bundle_action extends Base_action {
}
free() {
this.actions_to_do = null;
if (this.actions_to_do) {
for (let action of this.actions_to_do) {
action.free();
}
this.actions_to_do = null;
}
}
}

View File

@ -0,0 +1,82 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Clear_layer_action extends Base_action {
/**
* clear layer data
*
* @param {int} layer_id
*/
constructor(layer_id) {
super('clear_layer', 'Clear Layer');
this.layer_id = parseInt(layer_id);
this.update_layer_action = null;
this.delete_layer_settings_action = null;
}
async do() {
super.do();
let layer = app.Layers.get_layer(this.layer_id);
if (!layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
let new_settings = {
x: 0,
y: 0,
width: 0,
height: 0,
visible: true,
opacity: 100,
composition: null,
rotate: 0,
data: null,
params: {},
status: null,
render_function: null,
type: null
};
if (layer.type == 'image') {
//clean image
new_settings.link = null;
}
this.update_layer_action = new app.Actions.Update_layer_action(this.layer_id, new_settings);
await this.update_layer_action.do();
let delete_setting_names = [];
for (let prop_name in layer) {
//remove private attributes
if (prop_name[0] == '_') {
delete_setting_names.push(prop_name);
}
}
if (delete_setting_names.length > 0) {
this.delete_layer_settings_action = new app.Actions.Delete_layer_settings_action(this.layer_id, delete_setting_names);
await this.delete_layer_settings_action.do();
}
}
async undo() {
super.undo();
if (this.delete_layer_settings_action) {
await this.delete_layer_settings_action.undo();
this.delete_layer_settings_action.free();
this.delete_layer_settings_action = null;
}
if (this.update_layer_action) {
await this.update_layer_action.undo();
this.update_layer_action.free();
this.update_layer_action = null;
}
}
free() {
if (this.update_layer_action) {
this.update_layer_action.free();
this.update_layer_action = null;
}
if (this.delete_layer_settings_action) {
this.delete_layer_settings_action.free();
this.delete_layer_settings_action = null;
}
}
}

View File

@ -0,0 +1,60 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Delete_layer_filter_action extends Base_action {
/**
* delete live filter
*
* @param {int} layer_id
* @param {string} filter_id
*/
constructor(layer_id, filter_id) {
super('delete_layer_filter', 'Delete Layer Filter');
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.filter_id = filter_id;
this.reference_layer = null;
this.filter_remove_index = null;
this.old_filter = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
this.old_filter = null;
for (let i in this.reference_layer.filters) {
if (this.reference_layer.filters[i].id == this.filter_id) {
this.filter_remove_index = i;
this.old_filter = this.reference_layer.filters.splice(i, 1)[0];
break;
}
}
if (!this.old_filter) {
throw new Error('Aborted - filter with specified id doesn\'t exist in layer');
}
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer && this.old_filter) {
this.reference_layer.filters.splice(this.filter_remove_index, 0, this.old_filter);
}
this.reference_layer = null;
this.old_filter = null;
this.filter_remove_index = null;
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.old_filter = null;
}
}

View File

@ -0,0 +1,50 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Delete_layer_settings_action extends Base_action {
/**
* Deletes the specified settings in a layer
*
* @param {int} layer_id
* @param {array} setting_names
*/
constructor(layer_id, setting_names) {
super('delete_layer_settings', 'Delete Layer Settings');
this.layer_id = parseInt(layer_id);
this.setting_names = setting_names;
this.reference_layer = null;
this.old_settings = {};
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
for (let name in this.setting_names) {
this.old_settings[name] = this.reference_layer[name];
delete this.reference_layer[name];
}
config.need_render = true;
}
async undo() {
super.undo();
if (this.reference_layer) {
for (let i in this.old_settings) {
this.reference_layer[i] = this.old_settings[i];
}
this.old_settings = {};
}
this.reference_layer = null;
config.need_render = true;
}
free() {
this.setting_names = null;
this.reference_layer = null;
this.old_settings = null;
}
}

View File

@ -76,10 +76,12 @@ export class Delete_layer_action extends Base_action {
}
if (this.select_layer_action) {
await this.select_layer_action.undo();
this.select_layer_action.free();
this.select_layer_action = null;
}
if (this.insert_layer_action) {
await this.insert_layer_action.undo();
this.insert_layer_action.free();
this.insert_layer_action = null;
}
@ -91,8 +93,14 @@ export class Delete_layer_action extends Base_action {
if (this.deleted_layer) {
delete this.deleted_layer.link;
}
this.insert_layer_action = null;
this.select_layer_action = null;
if (this.insert_layer_action) {
this.insert_layer_action.free();
this.insert_layer_action = null;
}
if (this.select_layer_action) {
this.select_layer_action.free();
this.select_layer_action = null;
}
this.deleted_layer = null;
}
}

View File

@ -1,14 +1,21 @@
export { Add_layer_filter_action } from './add-layer-filter.js';
export { Autoresize_canvas_action } from './autoresize-canvas.js';
export { Bundle_action } from './bundle.js';
export { Clear_layer_action } from './clear-layer.js';
export { Delete_layer_action } from './delete-layer.js';
export { Delete_layer_filter_action } from './delete-layer-filter.js';
export { Delete_layer_settings_action } from './delete-layer-settings.js';
export { Init_canvas_zoom_action } from './init-canvas-zoom.js';
export { Insert_layer_action } from './insert-layer.js';
export { Prepare_canvas_action } from './prepare-canvas.js';
export { Reorder_layer_action } from './reorder-layer.js';
export { Reset_layers_action } from './reset-layers.js';
export { Reset_selection_action } from './reset-selection.js';
export { Select_layer_action } from './select-layer.js';
export { Select_next_layer_action } from './select-next-layer.js';
export { Select_previous_layer_action } from './select-previous-layer.js';
export { Set_selection_action } from './set-selection.js';
export { Toggle_layer_visibility_action } from './toggle-layer-visibility.js';
export { Update_config_action } from './update-config.js';
export { Update_layer_image_action } from './update-layer-image.js';
export { Update_layer_action } from './update-layer.js';

View File

@ -1,6 +1,7 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
export class Insert_layer_action extends Base_action {
/**
@ -23,6 +24,7 @@ export class Insert_layer_action extends Base_action {
async do() {
super.do();
this.previous_auto_increment = app.Layers.auto_increment;
this.previous_selected_layer = config.layer;
let autoresize_as = null;
@ -78,7 +80,7 @@ export class Insert_layer_action extends Base_action {
// Remove first empty layer
this.delete_layer_action = new app.Actions.Delete_layer_action(config.layer.id, true);
this.delete_layer_action.do();
await this.delete_layer_action.do();
}
if (layer.link == null) {
@ -179,15 +181,17 @@ export class Insert_layer_action extends Base_action {
this.autoresize_canvas_action = null;
}
if (this.inserted_layer_id) {
await new app.Actions.Delete_layer_action(this.inserted_layer_id, true).do();
config.layers.pop();
this.inserted_layer_id = null;
}
if (this.update_layer_action) {
await this.update_layer_action.undo();
this.update_layer_action.free();
this.update_layer_action = null;
}
if (this.delete_layer_action) {
await this.delete_layer_action.undo();
this.delete_layer_action.free();
this.delete_layer_action = null;
}
config.layer = this.previous_selected_layer;
@ -198,8 +202,14 @@ export class Insert_layer_action extends Base_action {
}
free() {
this.delete_layer_action = null;
this.update_layer_action = null;
if (this.delete_layer_action) {
this.delete_layer_action.free();
this.delete_layer_action = null;
}
if (this.update_layer_action) {
this.update_layer_action.free();
this.update_layer_action = null;
}
this.previous_selected_layer = null;
}
}

View File

@ -0,0 +1,64 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reorder_layer_action extends Base_action {
/**
* Reorder layer up or down in the layer stack
*
* @param {int} layer_id
* @param {int} direction
*/
constructor(layer_id, direction) {
super('reorder_layer', 'Reorder Layer');
this.layer_id = parseInt(layer_id);
this.direction = direction;
this.reference_layer = null;
this.reference_target = null;
this.old_layer_order = null;
this.old_target_order = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
if (this.direction < 0) {
this.reference_target = app.Layers.find_previous(this.layer_id);
}
else {
this.reference_target = app.Layers.find_next(this.layer_id);
}
if (!this.reference_target) {
throw new Error('Aborted - layer has nowhere to move');
}
this.old_layer_order = this.reference_layer.order;
this.old_target_order = this.reference_target.order;
this.reference_layer.order = this.old_target_order;
this.reference_target.order = this.old_layer_order;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer) {
this.reference_layer.order = this.old_layer_order;
this.reference_layer = null;
}
if (this.reference_target) {
this.reference_target.order = this.old_target_order;
this.reference_target = null;
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.reference_target = null;
}
}

View File

@ -39,15 +39,28 @@ export class Reset_layers_action extends Base_action {
super.undo();
if (this.insert_action) {
await this.insert_action.undo();
this.insert_action.free();
this.insert_action = null;
}
for (let i = this.delete_actions.length - 1; i >= 0; i--) {
await this.delete_actions[i].undo();
this.delete_actions[i].free();
}
app.Layers.auto_increment = this.previous_auto_increment;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
if (this.insert_action) {
this.insert_action.free();
this.insert_action = null;
}
if (this.delete_actions) {
for (let action of this.delete_actions) {
action.free();
}
this.delete_actions = null;
}
}
}

View File

@ -15,19 +15,23 @@ export class Reset_selection_action extends Base_action {
async do() {
super.do();
this.settings_reference = app.Layers.Base_selection.find_settings();
this.old_settings_data = this.settings_reference.data;
this.settings_reference.data = {
x: null,
y: null,
width: null,
height: null
};
this.old_settings_data = JSON.parse(JSON.stringify(this.settings_reference.data));
this.settings_reference.data = {
x: null,
y: null,
width: null,
height: null
}
config.need_render = true;
}
async undo() {
super.undo();
this.settings_reference.data = this.old_settings_data;
if (this.old_settings_data) {
for (let prop in this.old_settings_data) {
this.settings_reference.data[prop] = this.old_settings_data[prop];
}
}
this.settings_reference = null;
this.old_settings_data = null;
config.need_render = true;

View File

@ -41,6 +41,7 @@ export class Select_layer_action extends Base_action {
if (this.reset_selection_action) {
await this.reset_selection_action.undo();
this.reset_selection_action.free();
this.reset_selection_action = null;
}
@ -53,5 +54,9 @@ export class Select_layer_action extends Base_action {
free() {
this.old_layer = null;
if (this.reset_selection_action) {
this.reset_selection_action.free();
this.reset_selection_action = null;
}
}
}

View File

@ -0,0 +1,57 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Set_selection_action extends Base_action {
/**
* Sets the selection to the specified position and dimensions
*/
constructor(x, y, width, height, old_settings_override) {
super('set_selection', 'Set Selection');
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.settings_reference = null;
this.old_settings_data = null;
this.old_settings_override = old_settings_override || null;
}
async do() {
super.do();
this.settings_reference = app.Layers.Base_selection.find_settings();
this.old_settings_data = JSON.parse(JSON.stringify(this.settings_reference.data));
if (this.x != null)
this.settings_reference.data.x = this.x;
if (this.y != null)
this.settings_reference.data.y = this.y;
if (this.width != null)
this.settings_reference.data.width = this.width;
if (this.height != null)
this.settings_reference.data.height = this.height;
config.need_render = true;
}
async undo() {
super.undo();
if (this.old_settings_override) {
for (let prop in this.old_settings_override) {
this.settings_reference.data[prop] = this.old_settings_override[prop];
}
} else {
for (let prop in this.old_settings_data) {
this.settings_reference.data[prop] = this.old_settings_data[prop];
}
}
this.settings_reference = null;
this.old_settings_data = null;
config.need_render = true;
}
free() {
this.settings_reference = null;
this.old_settings_override = null;
this.old_settings_data = null;
}
}

View File

@ -0,0 +1,143 @@
import { get } from "jquery";
let idCounter = 0;
let database = null;
let databaseInitPromise = null;
export default {
/**
* Initializes the database
*/
async init() {
if (!databaseInitPromise) {
databaseInitPromise = new Promise(async (resolveInit) => {
try {
if (window.indexedDB) {
// Delete database from a previous page load
await new Promise((resolve, reject) => {
let deleteRequest = window.indexedDB.deleteDatabase('undoHistoryImageStore');
deleteRequest.onerror = () => {
reject(deleteRequest.error);
};
deleteRequest.onsuccess = () => {
resolve();
};
});
// Initialize database
await new Promise((resolve, reject) => {
let openRequest = window.indexedDB.open('undoHistoryImageStore', 1);
openRequest.onupgradeneeded = function(event) {
database = openRequest.result;
switch (event.oldVersion) {
case 0:
database.createObjectStore('images', { keyPath: 'id' });
break;
}
};
openRequest.onerror = () => {
reject(openRequest.error);
}
openRequest.onsuccess = () => {
resolve();
database = openRequest.result;
}
});
if (!database) {
throw new Error('indexedDB not initialized');
}
}
} catch (error) {
database = {
isMemory: true,
images: {}
};
}
resolveInit();
});
await databaseInitPromise;
} else if (!database) {
await databaseInitPromise;
}
},
/**
* Adds the specified image to the database. Returns a promise that is resolved with an id that can be used to retrieve it again.
*
* @param {string | canvas | ImageData} imageData the image data to store
* @returns {Promise<string>} resolves with retrieval id
*/
async add(imageData) {
await this.init();
let imageId = (idCounter++) + '';
if (database.isMemory) {
database.images[imageId] = imageData;
} else {
await new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readwrite');
const images = transaction.objectStore('images');
const image = {
id: imageId,
data: imageData
}
const request = images.add(image);
request.onsuccess = function() {
resolve();
};
request.onerror = function() {
reject(request.error);
};
});
}
return imageId;
},
/**
* Gets the specified image from the database, by imageId retrieved from "add()" method.
*
* @param {string} imageId the id of the image to get
* @returns {Promise<string | canvas | ImageData>} resolves with the image
*/
async get(imageId) {
await this.init();
if (database.isMemory) {
return database.images[imageId];
} else {
return new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readonly');
const images = transaction.objectStore('images');
const request = images.get(imageId);
request.onsuccess = function() {
resolve(request.result.data);
};
request.onerror = function() {
reject(request.error);
};
});
}
},
/**
* Deletes the specified image from the database, by imageId retrieved from "add()" method.
*
* @param {string} imageId the id of the image to delete
* @returns {Promise<string | canvas | ImageData>} resolves with the image
*/
async delete(imageId) {
await this.init();
if (database.isMemory) {
delete database.images[imageId];
} else {
return new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readwrite');
const images = transaction.objectStore('images');
const request = images.delete(imageId);
request.onsuccess = function() {
resolve();
};
request.onerror = function() {
reject(request.error);
};
});
}
}
};

View File

@ -0,0 +1,98 @@
import app from './../app.js';
import config from './../config.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import image_store from './store/image-store.js';
import { Base_action } from './base.js';
const Helper = new Helper_class();
export class Update_layer_image_action extends Base_action {
/**
* updates layer image data
*
* @param {canvas} canvas
* @param {int} layer_id (optional)
*/
constructor(canvas, layer_id) {
super('update_layer_image', 'Update Layer Image');
this.canvas = canvas;
this.canvas_data_url = null;
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.reference_layer = null;
this.old_image_id = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
if (this.reference_layer.type != 'image'){
alertify.error('Error: layer must be image.');
throw new Error('Aborted - layer is not an image');
}
if (!this.canvas_data_url) {
if (Helper.is_edge_or_ie() == false) {
// Update image using blob (faster)
await new Promise((resolve) => {
this.canvas.toBlob((blob) => {
this.canvas_data_url = window.URL.createObjectURL(blob);
resolve();
}, 'image/png');
});
}
else {
// Slow way for IE, Edge
this.canvas_data_url = this.canvas.toDataURL();
}
this.canvas = null;
}
try {
this.old_image_id = await image_store.add(this.reference_layer.link.src);
} catch (error) {
console.log(error);
// TODO - need to delete undo history, how to handle this?
}
this.reference_layer.link.src = this.canvas_data_url;
config.need_render = true;
}
async undo() {
super.undo();
if (this.old_image_id != null) {
try {
this.reference_layer.link.src = await image_store.get(this.old_image_id);
} catch (error) {
throw new Error('Failed to retrieve image from store');
}
try {
await image_store.delete(this.old_image_id);
} catch (error) {
// TODO - Reduce assumed total memory storage by image size
}
this.old_image_id = null;
}
this.reference_layer = null;
config.need_render = true;
}
async free() {
if (this.old_image_id != null) {
try {
await image_store.delete(this.old_image_id);
} catch (error) {
// TODO - Reduce assumed total memory storage by image size
}
this.old_image_id = null;
}
this.reference_layer = null;
this.canvas_data_url = null;
}
}

View File

@ -5,6 +5,7 @@ import { Base_action } from './base.js';
export class Update_layer_action extends Base_action {
/**
* Updates an existing layer with the provided settings
* WARNING: If passing objects or arrays into settings, make sure these are new objects, and not a modified existing object!
*
* @param {string} layer_id
* @param {object} settings
@ -20,11 +21,9 @@ export class Update_layer_action extends Base_action {
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
for (let i in this.settings) {
if (i == 'id')
continue;
@ -33,6 +32,10 @@ export class Update_layer_action extends Base_action {
this.old_settings[i] = this.reference_layer[i];
this.reference_layer[i] = this.settings[i];
}
if (this.settings.data && this.reference_layer.type === 'text') {
this.reference_layer._needs_update_data = true;
}
config.need_render = true;
}
async undo() {
@ -41,9 +44,13 @@ export class Update_layer_action extends Base_action {
for (let i in this.old_settings) {
this.reference_layer[i] = this.old_settings[i];
}
if (this.old_settings.data && this.reference_layer[i] === 'text') {
this.reference_layer._needs_update_data = true;
}
this.old_settings = {};
}
this.reference_layer = null;
config.need_render = true;
}
free() {

View File

@ -411,16 +411,15 @@ class Base_layers_class {
* @param {int} id
* @param {int} value 0-100
*/
set_opacity(id, value) {
id = parseInt(id);
async set_opacity(id, value) {
value = parseInt(value);
if (value < 0 || value > 100) {
//reset
value = 100;
}
var link = this.get_layer(id);
link.opacity = value;
return app.State.do_action(
new app.Actions.Update_layer_action(id, { opacity: value })
);
}
/**
@ -428,36 +427,10 @@ class Base_layers_class {
*
* @param {int} id
*/
layer_clear(id) {
id = parseInt(id);
var link = this.get_layer(id);
if (link.type == 'image') {
//clean image
link.link = null;
}
for (var i in link) {
//remove private attributes
if (i[0] == '_')
delete link[i];
}
link.x = 0;
link.y = 0;
link.width = 0;
link.height = 0;
link.visible = true;
link.opacity = 100;
link.composition = null;
link.rotate = 0;
link.data = null;
link.params = {};
link.status = null;
link.render_function = null;
link.type = null;
config.need_render = true;
async layer_clear(id) {
return app.State.do_action(
new app.Actions.Clear_layer_action(id)
);
}
/**
@ -466,24 +439,10 @@ class Base_layers_class {
* @param {int} id
* @param {int} direction
*/
move(id, direction) {
id = parseInt(id);
var link = this.get_layer(id);
if (direction < 0) {
var target = this.find_previous(id);
}
else {
var target = this.find_next(id);
}
if (target != null) {
var current_order = link.order;
link.order = target.order;
target.order = current_order;
}
this.render();
this.Base_gui.GUI_layers.render_layers();
async move(id, direction) {
return app.State.do_action(
new app.Actions.Reorder_layer_action(id, direction)
);
}
/**
@ -579,18 +538,9 @@ class Base_layers_class {
* @param {object} params
*/
add_filter(layer_id, name, params) {
if (layer_id == null)
layer_id = config.layer.id;
var link = this.get_layer(layer_id);
var filter = {
id: this.Helper.getRandomInt(1, 999999999),
name: name,
params: params,
};
link.filters.push(filter);
config.need_render = true;
this.Base_gui.GUI_layers.render_layers();
return app.State.do_action(
new app.Actions.Add_layer_filter_action(layer_id, name, params)
);
}
/**
@ -600,18 +550,9 @@ class Base_layers_class {
* @param {string} filter_id
*/
delete_filter(layer_id, filter_id) {
if (layer_id == null)
layer_id = config.layer.id;
var link = this.get_layer(layer_id);
for (var i in link.filters) {
if (link.filters[i].id == filter_id) {
link.filters.splice(i, 1);
}
}
config.need_render = true;
this.Base_gui.GUI_layers.render_layers();
return app.State.do_action(
new app.Actions.Delete_layer_filter_action(layer_id, filter_id)
);
}
/**
@ -704,28 +645,9 @@ class Base_layers_class {
* @param {int} layer_id (optional)
*/
update_layer_image(canvas, layer_id) {
if (layer_id == null)
layer_id = config.layer.id;
var link = this.get_layer(layer_id);
if (link.type != 'image'){
alertify.error('Error: layer must be image.');
return null;
}
if(this.Helper.is_edge_or_ie() == false){
//update image using blob (faster)
canvas.toBlob(function (blob) {
link.link.src = window.URL.createObjectURL(blob);
config.need_render = true;
}, 'image/png');
}
else{
//slow way for IE, Edge
link.link.src = canvas.toDataURL();
}
config.need_render = true;
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas, layer_id)
);
}
/**

View File

@ -45,39 +45,47 @@ class Base_selection_class {
this.selected_object_drag_type = null;
this.click_details = {};
this.is_touch = false;
// True if dragging from inside canvas area
this.is_drag = false;
this.events();
}
events() {
var _this = this;
document.addEventListener('mousedown', function (e) {
if(_this.is_touch == true)
document.addEventListener('mousedown', (e) => {
this.is_drag = false;
if(this.is_touch == true)
return;
_this.selected_object_actions(e);
if (!e.target.closest('#main_wrapper'))
return;
this.is_drag = true;
this.selected_object_actions(e);
});
document.addEventListener('mousemove', function (e) {
if(_this.is_touch == true)
document.addEventListener('mousemove', (e) => {
if(this.is_touch == true)
return;
_this.selected_object_actions(e);
this.selected_object_actions(e);
});
document.addEventListener('mouseup', function (e) {
if(_this.is_touch == true)
document.addEventListener('mouseup', (e) => {
if(this.is_touch == true)
return;
_this.selected_object_actions(e);
this.selected_object_actions(e);
});
// touch
document.addEventListener('touchstart', function (event) {
_this.is_touch = true;
_this.selected_object_actions(event);
document.addEventListener('touchstart', (event) => {
this.is_drag = false;
this.is_touch = true;
if (!e.target.closest('#main_wrapper'))
return;
this.is_drag = true;
this.selected_object_actions(event);
});
document.addEventListener('touchmove', function (event) {
_this.selected_object_actions(event);
document.addEventListener('touchmove', (event) => {
this.selected_object_actions(event);
}, {passive: false});
document.addEventListener('touchend', function (event) {
_this.selected_object_actions(event);
document.addEventListener('touchend', (event) => {
this.selected_object_actions(event);
});
}
@ -264,6 +272,9 @@ class Base_selection_class {
if(event_type == 'touchmove') event_type = 'mousemove';
if(event_type == 'touchend') event_type = 'mouseup';
if (!this.is_drag && ['mousedown', 'mouseup'].includes(event_type))
return;
const mainWrapper = document.getElementById('main_wrapper');
const defaultCursor = config.TOOL && config.TOOL.name === 'text' ? 'text' : 'default';
if (mainWrapper.style.cursor != defaultCursor) {
@ -288,7 +299,7 @@ class Base_selection_class {
height: settings.data.height,
};
}
if (event_type == 'mousemove' && this.mouse_lock == 'selected_object_actions') {
if (event_type == 'mousemove' && this.mouse_lock == 'selected_object_actions' && this.is_drag) {
const allowNegativeDimensions = settings.data.render_function
&& ['line', 'gradient'].includes(settings.data.render_function[0]);

View File

@ -61,6 +61,7 @@ class Base_state_class {
await action.do();
} catch (error) {
// Action aborted. This could be expected behavior if detected that the action shouldn't run.
console.info(error);
return { status: 'aborted', reason: error };
}
// Remove all redo actions from history
@ -106,6 +107,7 @@ class Base_state_class {
save() {
/*
this.optimize();
if (this.enabled == false) {
@ -155,6 +157,7 @@ class Base_state_class {
}
);
}
*/
}
/**

View File

@ -341,7 +341,6 @@ class GUI_colors_class {
// Initialize hex entry
this.inputs.hex
.on('input', (event) => {
console.log(event);
const value = this.inputs.hex.val();
const trimmedValue = value.trim();
if (value !== trimmedValue) {

View File

@ -50,13 +50,15 @@ class GUI_layers_class {
}
else if (target.id == 'layer_up') {
//move layer up
window.State.save();
_this.Base_layers.move(config.layer.id, 1);
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, 1)
);
}
else if (target.id == 'layer_down') {
//move layer down
window.State.save();
_this.Base_layers.move(config.layer.id, -1);
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, -1)
);
}
else if (target.id == 'visibility') {
//change visibility
@ -80,8 +82,9 @@ class GUI_layers_class {
}
else if (target.id == 'delete_filter') {
//delete filter
window.State.save();
_this.Base_layers.delete_filter(target.dataset.pid, target.dataset.id);
app.State.do_action(
new app.Actions.Delete_layer_filter_action(target.dataset.pid, target.dataset.id)
);
}
});
@ -107,38 +110,40 @@ class GUI_layers_class {
document.getElementById(target_id).innerHTML = '';
var html = '';
if (config.layer) {
for (var i in layers) {
var value = layers[i];
for (var i in layers) {
var value = layers[i];
if (value.id == config.layer.id)
html += '<div class="item active">';
else
html += '<div class="item">';
if (value.visible == true)
html += ' <span class="visibility visible" id="visibility" data-id="' + value.id + '" title="hide"></span>';
else
html += ' <span class="visibility" id="visibility" data-id="' + value.id + '" title="show"></span>';
html += ' <span class="delete" id="delete" data-id="' + value.id + '" title="delete"></span>';
html += ' <span class="layer_name" id="layer_name" data-id="' + value.id + '">' + value.name + '</span>';
html += ' <div class="clear"></div>';
html += '</div>';
if (value.id == config.layer.id)
html += '<div class="item active">';
else
html += '<div class="item">';
if (value.visible == true)
html += ' <span class="visibility visible" id="visibility" data-id="' + value.id + '" title="hide"></span>';
else
html += ' <span class="visibility" id="visibility" data-id="' + value.id + '" title="show"></span>';
html += ' <span class="delete" id="delete" data-id="' + value.id + '" title="delete"></span>';
html += ' <span class="layer_name" id="layer_name" data-id="' + value.id + '">' + value.name + '</span>';
html += ' <div class="clear"></div>';
html += '</div>';
//show filters
if (layers[i].filters.length > 0) {
html += '<div class="filters">';
for (var j in layers[i].filters) {
var filter = layers[i].filters[j];
var title = this.Helper.ucfirst(filter.name);
title = title.replace(/-/g, ' ');
//show filters
if (layers[i].filters.length > 0) {
html += '<div class="filters">';
for (var j in layers[i].filters) {
var filter = layers[i].filters[j];
var title = this.Helper.ucfirst(filter.name);
title = title.replace(/-/g, ' ');
html += '<div class="filter">';
html += ' <span class="delete" id="delete_filter" data-pid="' + layers[i].id + '" data-id="' + filter.id + '" title="delete"></span>';
html += ' <span class="layer_name" id="filter_name" data-pid="' + layers[i].id + '" data-id="' + filter.id + '">' + title + '</span>';
html += ' <div class="clear"></div>';
html += '<div class="filter">';
html += ' <span class="delete" id="delete_filter" data-pid="' + layers[i].id + '" data-id="' + filter.id + '" title="delete"></span>';
html += ' <span class="layer_name" id="filter_name" data-pid="' + layers[i].id + '" data-id="' + filter.id + '">' + title + '</span>';
html += ' <div class="clear"></div>';
html += '</div>';
}
html += '</div>';
}
html += '</div>';
}
}

View File

@ -1,3 +1,4 @@
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';
@ -38,7 +39,9 @@ class Effects_common_class {
}
save(params, type) {
this.Base_layers.add_filter(null, type, params);
return app.State.do_action(
new app.Actions.Add_layer_filter_action(null, type, params)
);
}
preview(params, type) {

View File

@ -1,3 +1,4 @@
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';
@ -66,7 +67,9 @@ class Effects_backAndWhite_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -32,7 +33,9 @@ class Effects_blueprint_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -52,7 +53,9 @@ class Effects_boxBlur_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -51,7 +52,9 @@ class Effects_denoise_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -50,7 +51,9 @@ class Effects_dither_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -51,7 +52,9 @@ class Effects_dotScreen_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -29,7 +30,9 @@ class Effects_edge_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {

View File

@ -1,3 +1,4 @@
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';
@ -29,7 +30,9 @@ class Effects_emboss_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {

View File

@ -1,3 +1,4 @@
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';
@ -48,7 +49,9 @@ class Effects_enrich_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -51,7 +52,9 @@ class Effects_grains_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -28,7 +29,9 @@ class Effects_heatmap_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {

View File

@ -1,3 +1,4 @@
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';
@ -29,7 +30,9 @@ class Effects_1977_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,4 +1,4 @@
import config from '../../../config.js';
import app from '../../../app.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';
@ -16,8 +16,6 @@ class Effects_aden_class {
return;
}
window.State.save();
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas(null, true);
var ctx = canvas.getContext("2d");
@ -28,7 +26,9 @@ class Effects_aden_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -29,7 +30,9 @@ class Effects_clarendon_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -28,7 +29,9 @@ class Effects_gingham_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -33,7 +34,9 @@ class Effects_inkwell_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -27,7 +28,9 @@ class Effects_lofi_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -32,7 +33,9 @@ class Effects_toaster_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -28,7 +29,9 @@ class Effects_valencia_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -28,7 +29,9 @@ class Effects_xpro2_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -50,7 +51,9 @@ class Effects_mosaic_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -32,7 +33,9 @@ class Effects_nightVision_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -51,7 +52,9 @@ class Effects_oil_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -28,7 +29,9 @@ class Effects_pencil_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, width, height) {

View File

@ -1,3 +1,4 @@
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';
@ -50,7 +51,9 @@ class Effects_sharpen_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -29,7 +30,9 @@ class Effects_solarize_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data) {

View File

@ -1,3 +1,4 @@
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';
@ -73,7 +74,9 @@ class Effects_tiltShift_class {
this.change(canvas, params);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -51,7 +52,9 @@ class Effects_vibrance_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -52,7 +53,9 @@ class Effects_vignette_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -49,7 +50,9 @@ class Effects_vintage_class {
this.change(canvas, params);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -60,7 +61,9 @@ class Effects_zoomBlur_class {
ctx.drawImage(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -54,7 +55,9 @@ class Image_autoAdjust_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
get_adjust_data(data) {

View File

@ -1,3 +1,4 @@
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';
@ -61,7 +62,9 @@ class Image_colorCorrections_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
do_corrections(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -53,7 +54,9 @@ class Image_decreaseColors_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
get_decreased_data(data, colors, greyscale) {

View File

@ -1,3 +1,4 @@
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';
@ -47,7 +48,9 @@ class Image_flip_class {
}
//save
this.Base_layers.update_layer_image(canvas2);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas2)
);
}
}

View File

@ -1,3 +1,4 @@
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';
@ -58,7 +59,7 @@ class Image_resize_class {
{name: "mode", title: "Mode:", values: ["Lanczos", "Hermite", "Basic"]},
{name: "sharpen", title: "Sharpen:", value: false},
{name: "layers", title: "Layers:", values: ["Active", "All"], value: "Active"},
{name: "layers", title: "Layers:", values: ["All", "Active"], value: "All"},
],
on_finish: function (params) {
_this.do_resize(params);
@ -67,50 +68,61 @@ class Image_resize_class {
this.POP.show(settings);
}
do_resize(params) {
async do_resize(params) {
//validate
if (isNaN(params.width) && isNaN(params.height) && isNaN(params.width_percent) && isNaN(params.height_percent)){
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;
}
if (params.width == config.WIDTH && params.height == config.HEIGHT){
if (params.layers == 'All' && params.width == config.WIDTH && params.height == config.HEIGHT) {
return false;
}
window.State.save();
// 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) {
var response = this.resize_layer(config.layers[i], params);
if(response === false){
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
this.resize_layer(config.layer, params);
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)
);
}
/**
* it will try to resize layer (image, text, vector), returns false on failure.
* Generates actions that will resize layer (image, text, vector), returns a promise that rejects on failure.
*
* @param {object} layer
* @param {object} params
* @returns {undefined|Boolean}
* @returns {Promise<object>} Returns array of actions to perform
*/
resize_layer(layer, params) {
async resize_layer(layer, params) {
var mode = params.mode;
var width = parseInt(params.width);
var height = parseInt(params.height);
var width_100 = parseInt(params.width_percent);
var height_100 = parseInt(params.height_percent);
var canvas_width;
var canvas_height;
var sharpen = params.sharpen;
var _this = this;
@ -118,52 +130,71 @@ class Image_resize_class {
if (isNaN(width) && isNaN(height)) {
if (isNaN(width_100) == false) {
width = Math.round(layer.width * width_100 / 100);
canvas_width = Math.round(config.WIDTH * width_100 / 100);
}
if (isNaN(height_100) == false) {
height = Math.round(layer.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 * canvas_width / config.WIDTH) : layer.x;
let new_y = params.layers == 'All' ? Math.round(layer.y * canvas_height / config.HEIGHT) : layer.y;
//is text
if(layer.type == 'text'){
if (layer.type == 'text') {
var xratio = width / layer.width;
for (let line of layer.data) {
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);
}
}
layer.width = width;
layer.height = height;
this.resize_gui();
config.need_render = true;
return true;
// Return actions
return [
new app.Actions.Update_layer_action(layer.id, {
x: new_x,
y: new_y,
data,
width,
height
})
];
}
//is vector
if(layer.is_vector == true && layer.width != null && layer.height != null){
layer.width = width;
layer.height = height;
this.resize_gui();
config.need_render = true;
return true;
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,
height
})
];
}
//only images supported at this point
if (layer.type != 'image') {
else if (layer.type != 'image') {
//error - no support
alertify.error('Layer must be vector or image (convert it to raster).');
return false;
throw new Error('Layer is not compatible with resize');
}
//get canvas from layer
@ -184,19 +215,15 @@ class Image_resize_class {
tmp_data.width = width;
tmp_data.height = height;
this.pica.resize(canvas, tmp_data, {
await this.pica.resize(canvas, tmp_data, {
alpha: true,
})
.then(function(result) {
.then((result) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = width;
canvas.height = height;
ctx.drawImage(tmp_data, 0, 0, width, height);
finish_resize();
});
return;
}
else if (mode == "Hermite") {
//Hermite resample
@ -216,48 +243,59 @@ class Image_resize_class {
ctx.drawImage(tmp_data, 0, 0, width, height);
}
finish_resize();
//private finish action
function finish_resize(){
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);
}
//save
_this.Base_layers.update_layer_image(canvas, layer.id);
layer.width = canvas.width;
layer.height = canvas.height;
layer.width_original = canvas.width;
layer.height_original = canvas.height;
config.need_render = true;
_this.resize_gui();
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() {
var max_x = 0;
var max_y = 0;
for (var i = 0; i < config.layers.length; i++) {
var layer = config.layers[i];
if(layer.width == null || layer.height == null || layer.x == null || layer.y == null){
//layer without dimensions
continue;
}
resize_gui(params) {
var width = parseInt(params.width);
var height = parseInt(params.height);
var width_100 = parseInt(params.width_percent);
var height_100 = parseInt(params.height_percent);
max_x = Math.max(max_x, layer.x + layer.width);
max_y = Math.max(max_y, layer.y + layer.height);
//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);
}
}
config.WIDTH = parseInt(max_x);
config.HEIGHT = parseInt(max_y);
this.Base_gui.prepare_canvas();
config.need_render = true;
//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')
];
}
}

View File

@ -1,3 +1,4 @@
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';
@ -38,9 +39,7 @@ class Image_trim_class {
}, false);
}
trim(){
var _this = this;
trim() {
var removeWhiteColor = false;
if(config.TRANSPARENCY == false)
removeWhiteColor = true;
@ -53,58 +52,64 @@ class Image_trim_class {
{}, //gap
{name: "remove_white", title: "Trim white color?", value: removeWhiteColor},
],
on_finish: function (params) {
window.State.save();
if(params.trim_layer == true)
_this.trim_layer(config.layer.id, params.remove_white);
if(params.trim_all == true)
_this.trim_all(params.remove_white);
on_finish: (params) => {
let actions = [];
if (params.trim_layer == true)
actions = actions.concat(this.trim_layer(config.layer.id, params.remove_white));
if (params.trim_all == true)
actions = actions.concat(this.trim_all(params.remove_white));
if (actions.length > 0) {
app.State.do_action(
new app.Actions.Bundle_action('trim_layers', 'Trim Layers', actions)
);
}
},
};
this.Dialog.show(settings);
}
trim_layer(layer_id, removeWhiteColor = false){
trim_layer(layer_id, removeWhiteColor = false) {
var layer = this.Base_layers.get_layer(layer_id);
if (config.layer.type != 'image') {
if (layer.type != 'image') {
alertify.error('Skip - layer must be image.');
return false;
}
var trim = this.get_trim_info(layer_id, removeWhiteColor);
trim = trim.relative;
if(layer.type == 'image'){
//if image was stretched
var width_ratio = (layer.width / layer.width_original);
var height_ratio = (layer.height / layer.height_original);
//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;
//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);
this.Base_layers.update_layer_image(canvas, layer.id);
//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);
//update attributes
layer.width = Math.ceil(canvas.width * width_ratio);
layer.height = Math.ceil(canvas.height * height_ratio);
layer.x += trim.left;
layer.y += trim.top;
layer.width_original = canvas.width;
layer.height_original = canvas.height;
}
config.need_render = true;
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
})
];
}
trim_all(removeWhiteColor = false) {
let actions = [];
var all_top = config.HEIGHT;
var all_left = config.WIDTH;
var all_bottom = config.HEIGHT;
@ -118,12 +123,12 @@ class Image_trim_class {
}
//collect info
for (var i = 0; i < config.layers.length; i++) {
var layer = config.layers[i];
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){
if (layer.width == null || layer.height == null || layer.x == null || layer.y == null) {
//layer without dimensions
var trim_info = this.get_trim_info(layer.id, removeWhiteColor);
const trim_info = this.get_trim_info(layer.id, removeWhiteColor);
all_top = Math.min(all_top, trim_info.top);
all_left = Math.min(all_left, trim_info.left);
@ -139,25 +144,29 @@ class Image_trim_class {
}
//move every layer
for (var i = 0; i < config.layers.length; i++) {
var layer = config.layers[i];
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;
layer.x = layer.x - all_left;
layer.y = layer.y - all_top;
actions.push(
new app.Actions.Update_layer_action(layer.id, {
x: layer.x - all_left,
y: layer.y - all_top
})
);
}
//resize
config.WIDTH = config.WIDTH - all_left - all_right;
config.HEIGHT = config.HEIGHT - all_top - all_bottom;
if (config.WIDTH < 1)
config.WIDTH = 1;
if (config.HEIGHT < 1)
config.HEIGHT = 1;
this.Base_gui.prepare_canvas();
config.need_render = true;
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;
}
/**

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
@ -8,9 +9,9 @@ class Layer_clear_class {
}
clear() {
window.State.save();
this.Base_layers.layer_clear(config.layer.id);
return app.State.do_action(
new app.Actions.Clear_layer_action(config.layer.id)
);
}
}

View File

@ -46,7 +46,6 @@ class Layer_flatten_class {
for (var i = config.layers.length - 1; i >= 0; i--) {
delete_actions.push(new app.Actions.Delete_layer_action(config.layers[i].id));
}
console.log(delete_actions);
// Run actions
app.State.do_action(
new app.Actions.Bundle_action('flatten_image', 'Flatten Image', [

View File

@ -1,3 +1,4 @@
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../../core/base-layers.js';
@ -8,13 +9,15 @@ class Layer_move_class {
}
up() {
window.State.save();
this.Base_layers.move(config.layer.id, 1);
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, 1)
);
}
down() {
window.State.save();
this.Base_layers.move(config.layer.id, -1);
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, -1)
);
}
}

View File

@ -1,3 +1,4 @@
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';
@ -50,7 +51,9 @@ class Tools_colorToAlpha_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, color) {

View File

@ -1,3 +1,4 @@
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';
@ -49,7 +50,9 @@ class Tools_colorZoom_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
change(data, zoom, center) {

View File

@ -1,3 +1,4 @@
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';
@ -63,11 +64,17 @@ class Tools_contentFill_class {
this.change(canvas, params);
//save
config.layer.x = 0;
config.layer.y = 0;
config.layer.width = config.WIDTH;
config.layer.height = config.HEIGHT;
this.Base_layers.update_layer_image(canvas);
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_data(canvas)
])
);
}
change(canvas, params) {

View File

@ -1,3 +1,4 @@
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';
@ -54,7 +55,9 @@ class Tools_replaceColor_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
do_replace(data, params) {

View File

@ -1,3 +1,4 @@
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';
@ -48,7 +49,9 @@ class Tools_restoreAlpha_class {
ctx.putImageData(data, 0, 0);
//save
this.Base_layers.update_layer_image(canvas);
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas)
);
}
recover_alpha(data, level) {

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -128,7 +129,11 @@ class Blur_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('blur_tool', 'Blur Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -105,7 +106,11 @@ class BulgePinch_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('bulge_pinch_tool', 'Bulge/Pinch Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -259,7 +260,11 @@ class Clone_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('clone_tool', 'Clone Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -30,28 +31,30 @@ class Crop_class extends Base_tools_class {
return _this.selection;
},
};
this.mousedown_selection = null;
this.Base_selection = new Base_selection_class(ctx, sel_config, this.name);
}
dragStart(event) {
var _this = this;
if (config.TOOL.name != _this.name)
this.is_mousedown_canvas = false;
if (config.TOOL.name != this.name)
return;
_this.mousedown(event);
if (!event.target.closest('#main_wrapper'))
return;
this.is_mousedown_canvas = true;
this.mousedown(event);
}
dragMove(event) {
var _this = this;
if (config.TOOL.name != _this.name)
if (config.TOOL.name != this.name)
return;
_this.mousemove(event);
this.mousemove(event);
}
dragEnd(event) {
var _this = this;
if (config.TOOL.name != _this.name)
if (config.TOOL.name != this.name)
return;
_this.mouseup(event);
this.mouseup(event);
}
load() {
@ -82,9 +85,11 @@ class Crop_class extends Base_tools_class {
mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.valid == false || mouse.click_valid == false)
if (this.Base_selection.is_drag == false || mouse.valid == false || mouse.click_valid == false)
return;
this.mousedown_selection = JSON.parse(JSON.stringify(this.selection));
if (this.Base_selection.mouse_lock !== null) {
return;
}
@ -95,7 +100,7 @@ class Crop_class extends Base_tools_class {
mousemove(e) {
var mouse = this.get_mouse_info(e);
if (mouse.is_drag == false) {
if (this.Base_selection.is_drag == false || mouse.is_drag == false) {
return;
}
if (e.type == 'mousedown' && (mouse.valid == false || mouse.click_valid == false)) {
@ -108,7 +113,7 @@ class Crop_class extends Base_tools_class {
var width = mouse.x - mouse.click_x;
var height = mouse.y - mouse.click_y;
if(event.ctrlKey == true || event.metaKey){
if(e.ctrlKey == true || e.metaKey){
//ctrl is pressed - crop will be calculated based on global width and height ratio
var ratio = config.WIDTH / config.HEIGHT;
var width_new = Math.round(height * ratio);
@ -134,6 +139,9 @@ class Crop_class extends Base_tools_class {
mouseup(e) {
var mouse = this.get_mouse_info(e);
if (!this.Base_selection.is_drag) {
return;
}
if (e.type == 'mousedown' && mouse.click_valid == false) {
return;
}
@ -183,7 +191,9 @@ class Crop_class extends Base_tools_class {
this.selection.height = config.HEIGHT - this.selection.y;
}
config.need_render = true;
app.State.do_action(
new app.Actions.Set_selection_action(this.selection.x, this.selection.y, this.selection.width, this.selection.height, this.mousedown_selection)
);
}
render(ctx, layer) {
@ -193,7 +203,7 @@ class Crop_class extends Base_tools_class {
/**
* do actual crop
*/
on_params_update() {
async on_params_update() {
var params = this.getParams();
var selection = this.selection;
params.crop = true;
@ -229,69 +239,98 @@ class Crop_class extends Base_tools_class {
selection.width = Math.min(selection.width, config.WIDTH);
selection.height = Math.min(selection.height, config.HEIGHT);
let actions = [];
for (var i in config.layers) {
var link = config.layers[i];
if (link.type == null)
continue;
let x = link.x;
let y = link.y;
let width = link.width;
let height = link.height;
let width_original = link.width_original;
let height_original = link.height_original;
//move
link.x -= parseInt(selection.x);
link.y -= parseInt(selection.y);
x -= parseInt(selection.x);
y -= parseInt(selection.y);
if (link.type == 'image') {
//also remove unvisible data
var left = 0;
if (link.x < 0)
left = -link.x;
var top = 0;
if (link.y < 0)
top = -link.y;
var right = 0;
if (link.x + link.width > selection.width)
right = link.x + link.width - selection.width;
var bottom = 0;
if (link.y + link.height > selection.height)
bottom = link.y + link.height - selection.height;
var width = link.width - left - right;
var height = link.height - top - bottom;
let left = 0;
if (x < 0)
left = -x;
let top = 0;
if (y < 0)
top = -y;
let right = 0;
if (x + width > selection.width)
right = x + width - selection.width;
let bottom = 0;
if (y + height > selection.height)
bottom = y + height - selection.height;
let crop_width = width - left - right;
let crop_height = height - top - bottom;
//if image was streched
var width_ratio = (link.width / link.width_original);
var height_ratio = (link.height / link.height_original);
let width_ratio = (width / width_original);
let height_ratio = (height / height_original);
//create smaller canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.width = width / width_ratio;
canvas.height = height / height_ratio;
let canvas = document.createElement('canvas');
let ctx = canvas.getContext("2d");
canvas.width = crop_width / width_ratio;
canvas.height = crop_height / height_ratio;
//cut required part
ctx.translate(-left / width_ratio, -top / height_ratio);
canvas.getContext("2d").drawImage(link.link, 0, 0);
ctx.translate(0, 0);
this.Base_layers.update_layer_image(canvas, link.id);
actions.push(
new app.Actions.Update_layer_image_action(canvas, link.id)
);
//update attributes
link.width = Math.ceil(canvas.width * width_ratio);
link.height = Math.ceil(canvas.height * height_ratio);
link.x += left;
link.y += top;
link.width_original = canvas.width;
link.height_original = canvas.height;
width = Math.ceil(canvas.width * width_ratio);
height = Math.ceil(canvas.height * height_ratio);
x += left;
y += top;
width_original = canvas.width;
height_original = canvas.height;
}
actions.push(
new app.Actions.Update_layer_action(link.id, {
x,
y,
width,
height,
width_original,
height_original
})
);
}
config.WIDTH = parseInt(selection.width);
config.HEIGHT = parseInt(selection.height);
this.Base_gui.prepare_canvas();
actions.push(
new app.Actions.Prepare_canvas_action('undo'),
new app.Actions.Update_config_action({
WIDTH: parseInt(selection.width),
HEIGHT: parseInt(selection.height)
}),
new app.Actions.Prepare_canvas_action('do'),
new app.Actions.Reset_selection_action()
);
await app.State.do_action(
new app.Actions.Bundle_action('crop_tool', 'Crop Tool', actions)
);
this.selection = {
x: null,
y: null,
width: null,
height: null,
};
this.Base_selection.reset_selection();
}
on_leave() {

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -129,7 +130,11 @@ class Desaturate_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('desaturate_tool', 'Desaturate Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -155,7 +156,11 @@ class Erase_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('erase_tool', 'Erase Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -106,7 +106,11 @@ class Fill_class extends Base_tools_class {
if (config.layer.type != null) {
//update
this.Base_layers.update_layer_image(canvas);
app.State.do_action(
new app.Actions.Bundle_action('fill_tool', 'Fill Tool', [
new app.Actions.Update_layer_image_action(canvas)
])
);
}
else {
//create new
@ -119,7 +123,7 @@ class Fill_class extends Base_tools_class {
params.width = canvas.width;
params.height = canvas.height;
app.State.do_action(
new app.Actions.Bundle_action('fill', 'Fill', [
new app.Actions.Bundle_action('fill_tool', 'Fill Tool', [
new app.Actions.Insert_layer_action(params)
])
);

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -88,7 +89,11 @@ class Magic_erase_class extends Base_tools_class {
this.magic_erase_general(ctx, config.WIDTH, config.HEIGHT,
mouse_x, mouse_y, params.power, params.anti_aliasing, params.contiguous);
this.Base_layers.update_layer_image(canvas);
app.State.do_action(
new app.Actions.Bundle_action('magic_erase_tool', 'Magic Eraser Tool', [
new app.Actions.Update_layer_image_action(canvas)
])
);
//prevent crash bug on touch screen - hard to explain and debug
await new Promise(r => setTimeout(r, 10));
this.working = false;

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -249,7 +250,11 @@ class Selection_class extends Base_tools_class {
return;
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('selection_tool', 'Selection Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
this.reset_tmp_canvas();
config.need_render = true;
@ -283,7 +288,12 @@ class Selection_class extends Base_tools_class {
//do erase
this.tmpCanvasCtx.clearRect(mouse_x, mouse_y, selection.width, selection.height);
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('selection_tool', 'Selection Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
this.selection = {
x: null,
y: null,

View File

@ -1,3 +1,4 @@
import app from './../app.js';
import config from './../config.js';
import Base_tools_class from './../core/base-tools.js';
import Base_layers_class from './../core/base-layers.js';
@ -128,7 +129,11 @@ class Sharpen_class extends Base_tools_class {
}
delete config.layer.link_canvas;
this.Base_layers.update_layer_image(this.tmpCanvas);
app.State.do_action(
new app.Actions.Bundle_action('sharpen_tool', 'Sharpen Tool', [
new app.Actions.Update_layer_image_action(this.tmpCanvas)
])
);
//decrease memory
this.tmpCanvas.width = 1;

View File

@ -367,12 +367,6 @@ class Text_document_class {
for (let i = 0; i < insertLine.length; i++) {
const span = insertLine[i];
const spanLength = span.text.length;
if (span === insertedSpan) {
console.log(
(character > characterCount || character === 0),
character <= characterCount + spanLength
);
}
if (!modifyingSpan && (character > characterCount || character === 0) && character <= characterCount + spanLength) {
if (insertLine[i + 1] && insertLine[i + 1].text === '') {
modifyingSpan = insertLine[i + 1];
@ -2391,6 +2385,10 @@ class Text_class extends Base_tools_class {
editor.layer = layer;
layerEditors.set(layer, editor);
}
if (layer._needs_update_data) {
delete layer._needs_update_data;
editor.set_lines(layer.data);
}
return editor;
}