Improved performance of selection copying

This commit is contained in:
Igor Zinken
2025-03-15 11:31:17 +01:00
parent 201ddbd9e5
commit 875edf0d10
5 changed files with 22 additions and 26 deletions

View File

@@ -142,4 +142,7 @@ export type WandToolOptions = {
sampleMerged: boolean;
};
export type CopiedSelection = SizedImage & { type: LayerTypes };
export type CopiedSelection = {
bitmap: HTMLCanvasElement;
type: LayerTypes;
};

View File

@@ -20,8 +20,8 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import type { Store } from "vuex";
import type { BitMapperyState } from "@/store";
import { type Store } from "vuex";
import { type BitMapperyState } from "@/store";
/**
* a history state should provide undo and redo functions and an optional
@@ -38,7 +38,7 @@ const stateQueue = new Map();
const ENQUEUE_TIMEOUT = 1000;
let timeout = 0;
let store: Store<BitMapperyState>;
let store: Store<BitMapperyState> | undefined;
export const initHistory = ( storeReference: Store<BitMapperyState> ): void => {
store = storeReference;
@@ -87,6 +87,6 @@ export const enqueueState = ( key: string, undoRedoState: UndoRedoState ): void
function processQueue(): void {
window.clearTimeout( timeout );
stateQueue.forEach( undoRedoState => store.commit( "saveState", undoRedoState ));
stateQueue.forEach( undoRedoState => store?.commit( "saveState", undoRedoState ));
stateQueue.clear();
}

View File

@@ -39,7 +39,7 @@ import history, { HistoryState } from "./modules/history-module";
import image, { ImageState } from "./modules/image-module";
import preferences, { PreferencesState } from "./modules/preferences-module";
import tool, { ToolState } from "./modules/tool-module";
import { cloneCanvas, imageToCanvas } from "@/utils/canvas-util";
import { cloneCanvas } from "@/utils/canvas-util";
import { copySelection, deleteSelectionContent } from "@/utils/document-util";
import { saveBlobAsFile, selectFile } from "@/utils/file-util";
import { replaceLayerSource } from "@/utils/layer-util";
@@ -281,13 +281,14 @@ export default {
},
pasteSelection({ commit, getters, dispatch, state }: ActionContext<BitMapperyState, any> ): void {
const selection = state.selectionContent;
const { image, size, type } = selection;
const { bitmap, type } = selection;
const layer = LayerFactory.create({
type: ( !type || type === LayerTypes.LAYER_TEXT ) ? LayerTypes.LAYER_GRAPHIC : type,
source: imageToCanvas( image, size.width, size.height ),
...size,
left: getters.activeDocument.width / 2 - size.width / 2,
top : getters.activeDocument.height / 2 - size.height / 2,
source: cloneCanvas( bitmap ),
width: bitmap.width,
height: bitmap.height,
left: getters.activeDocument.width / 2 - bitmap.width / 2,
top : getters.activeDocument.height / 2 - bitmap.height / 2,
});
const index = getters.activeDocument.layers.length;
const paste = () => {
@@ -295,7 +296,7 @@ export default {
dispatch( "clearSelection" );
};
paste();
enqueueState( `paste_${selection.size.width}_${selection.size.height}`, {
enqueueState( `paste_${type}_${bitmap.width}_${bitmap.height}`, {
undo() {
commit( "setSelectionContent", selection );
commit( "removeLayer", index );

View File

@@ -20,9 +20,7 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { canvas, loader } from "zcanvas";
import type { Rectangle } from "zcanvas";
import { PNG } from "@/definitions/image-types";
import { canvas, type Rectangle } from "zcanvas";
import type { Document, Shape, Layer } from "@/definitions/document";
import type { CopiedSelection } from "@/definitions/editor";
import { renderEffectsForLayer } from "@/services/render-service";
@@ -202,10 +200,8 @@ export const copySelection = async ( activeDocument: Document, activeLayer: Laye
);
zcvs.dispose();
const output = await loader.loadImage( selectionCanvas.cvs.toDataURL( PNG.mime ));
return {
...output,
bitmap: selectionCanvas.cvs,
type: activeLayer.type,
};
};

View File

@@ -1,5 +1,5 @@
import { it, beforeEach, describe, expect, afterAll, vi } from "vitest";
import { mockZCanvas } from "../mocks";
import { createMockCanvasElement, createState, mockZCanvas } from "../mocks";
import { type Layer } from "@/definitions/document";
import { type Dialog } from "@/definitions/editor";
import { SAVE_DOCUMENT } from "@/definitions/modal-windows";
@@ -10,7 +10,6 @@ import DocumentFactory from "@/factories/document-factory";
import LayerFactory from "@/factories/layer-factory";
import KeyboardService from "@/services/keyboard-service";
import store, { type BitMapperyState } from "@/store";
import { createState, createMockImageElement } from "../mocks";
const { getters, mutations, actions } = store;
@@ -28,6 +27,7 @@ vi.mock( "@/utils/file-util", () => ({
}));
vi.mock("@/utils/canvas-util", () => ({
createCanvas: vi.fn(() => ({ cvs: {}, ctx: {} })),
cloneCanvas: vi.fn( cvs => cvs ),
imageToBase64: vi.fn(),
imageToCanvas: vi.fn(),
base64ToLayerImage: vi.fn()
@@ -83,7 +83,7 @@ describe( "Vuex store", () => {
it( "should be able to set the current selection content", () => {
const state = createState({ selectionContent: null });
const selection = { image: createMockImageElement(), size: { width: 100, height: 50 }, type: LayerTypes.LAYER_GRAPHIC };
const selection = { bitmap: createMockCanvasElement(), type: LayerTypes.LAYER_GRAPHIC };
mutations.setSelectionContent( state, selection );
expect( state.selectionContent ).toEqual( selection );
});
@@ -392,11 +392,7 @@ describe( "Vuex store", () => {
beforeEach(() => {
state = createState({
selectionContent: {
image: createMockImageElement(),
size: {
width: 40,
height: 30
},
bitmap: createMockCanvasElement(),
type: LayerTypes.LAYER_GRAPHIC,
},
});