From 897ed43cee2d4416fefedfa3243138c6a38d3d9e Mon Sep 17 00:00:00 2001 From: Igor Zinken Date: Wed, 13 Jan 2021 21:36:48 +0100 Subject: [PATCH] Paint operations are now also stored into state history --- .../document-canvas/document-canvas.vue | 1 + src/components/ui/zcanvas/layer-sprite.js | 45 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/components/document-canvas/document-canvas.vue b/src/components/document-canvas/document-canvas.vue index 5735dc7..aec4f7a 100644 --- a/src/components/document-canvas/document-canvas.vue +++ b/src/components/document-canvas/document-canvas.vue @@ -114,6 +114,7 @@ export default { handler( document, oldValue = null ) { // no active document or no document content if ( !document?.layers ) { + this.resetHistory(); if ( getCanvasInstance() ) { getCanvasInstance().dispose(); setCanvasInstance( null ); diff --git a/src/components/ui/zcanvas/layer-sprite.js b/src/components/ui/zcanvas/layer-sprite.js index 36c250c..83c3665 100644 --- a/src/components/ui/zcanvas/layer-sprite.js +++ b/src/components/ui/zcanvas/layer-sprite.js @@ -22,7 +22,7 @@ */ import Vue from "vue"; import { sprite } from "zcanvas" -import { createCanvas, resizeImage, globalToLocal } from "@/utils/canvas-util"; +import { createCanvas, cloneCanvas, resizeImage, globalToLocal } from "@/utils/canvas-util"; import { renderCross, renderMasked } from "@/utils/render-util"; import { LAYER_GRAPHIC, LAYER_MASK, LAYER_TEXT } from "@/definitions/layer-types"; import { translatePointerRotation, rotatePoints } from "@/math/image-math"; @@ -155,6 +155,9 @@ class LayerSprite extends sprite { this._selection = null; this._toolOptions = null; + // store pending paint states (if there were any) + this.storePaintState(); + if ( !this._interactive ) { return; } @@ -203,6 +206,10 @@ class LayerSprite extends sprite { } paint( x, y ) { + if ( !this._pendingPaintState ) { + this.preparePendingPaintState(); + } + // translate pointer to translated space, when layer is rotated or mirrored const { mirrorX, mirrorY, rotation } = this.layer.effects; const rotCenterX = this._bounds.left + this._bounds.width / 2; @@ -292,6 +299,37 @@ class LayerSprite extends sprite { this.resetFilterAndRecache(); } + /** + * As storing Bitmaps will consume a lot of memory fast we debounce this by + * a larger interval to prevent creating a big bitmap per brush stroke. + * Note that upon switching tools the state is enqueued immediately to + * not delay to history state UI from updating more than necessary. + */ + preparePendingPaintState() { + this._orgSourceToStore = cloneCanvas( this.layer.source ); + this._pendingPaintState = setTimeout( this.storePaintState.bind( this ), 5000 ); + } + + storePaintState() { + if ( !this._pendingPaintState ) { + return; + } + clearTimeout( this._pendingPaintState ); + this._pendingPaintState = null; + const layer = this.layer; + const orgState = this._orgSourceToStore; + const newState = cloneCanvas( layer.source ); + enqueueState( `spritePaint_${layer.id}`, { + undo() { + restorePaintFromHistory( layer, orgState ); + }, + redo() { + restorePaintFromHistory( layer, newState); + } + }); + this._orgSourceToStore = null; + } + /* the following override zCanvas.sprite */ setBounds( x, y, width = 0, height = 0 ) { @@ -478,3 +516,8 @@ function positionSpriteFromHistory( layer, x, y ) { sprite.invalidate(); } } + +function restorePaintFromHistory( layer, orgState ) { + layer.source = orgState; + getSpriteForLayer( layer )?.resetFilterAndRecache(); +}