From 809d5ba17f2e55e4db0d0302d5af38bbb9c8922a Mon Sep 17 00:00:00 2001 From: David Luzar Date: Sun, 8 Jan 2023 16:22:04 +0100 Subject: [PATCH] fix: png-exporting does not preserve angles correctly for flipped images (#6085) * fix: png-exporting does not preserve angles correctly for flipped images * refactor related code * simplify further and comment --- src/renderer/renderElement.ts | 50 +++++++++++++++++++---------------- src/scene/scroll.ts | 4 +-- src/utils.ts | 5 ++-- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 0c7882ca..f77a8c48 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -713,22 +713,8 @@ const drawElementFromCanvas = ( const cx = ((x1 + x2) / 2 + renderConfig.scrollX) * window.devicePixelRatio; const cy = ((y1 + y2) / 2 + renderConfig.scrollY) * window.devicePixelRatio; - const _isPendingImageElement = isPendingImageElement(element, renderConfig); - - const scaleXFactor = - "scale" in elementWithCanvas.element && !_isPendingImageElement - ? elementWithCanvas.element.scale[0] - : 1; - const scaleYFactor = - "scale" in elementWithCanvas.element && !_isPendingImageElement - ? elementWithCanvas.element.scale[1] - : 1; - context.save(); - context.scale( - (1 / window.devicePixelRatio) * scaleXFactor, - (1 / window.devicePixelRatio) * scaleYFactor, - ); + context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio); const boundTextElement = getBoundTextElement(element); if (isArrowElement(element) && boundTextElement) { @@ -793,7 +779,7 @@ const drawElementFromCanvas = ( zoom, ); - context.translate(cx * scaleXFactor, cy * scaleYFactor); + context.translate(cx, cy); context.drawImage( tempCanvas, (-(x2 - x1) / 2) * window.devicePixelRatio - offsetX / zoom - padding, @@ -802,15 +788,30 @@ const drawElementFromCanvas = ( tempCanvas.height / zoom, ); } else { - context.translate(cx * scaleXFactor, cy * scaleYFactor); + // we translate context to element center so that rotation and scale + // originates from the element center + context.translate(cx, cy); - context.rotate(element.angle * scaleXFactor * scaleYFactor); + context.rotate(element.angle); + + if ( + "scale" in elementWithCanvas.element && + !isPendingImageElement(element, renderConfig) + ) { + context.scale( + elementWithCanvas.element.scale[0], + elementWithCanvas.element.scale[1], + ); + } + + // revert afterwards we don't have account for it during drawing + context.translate(-cx, -cy); context.drawImage( elementWithCanvas.canvas!, - (-(x2 - x1) / 2) * window.devicePixelRatio - + (x1 + renderConfig.scrollX) * window.devicePixelRatio - (padding * elementWithCanvas.canvasZoom) / elementWithCanvas.canvasZoom, - (-(y2 - y1) / 2) * window.devicePixelRatio - + (y1 + renderConfig.scrollY) * window.devicePixelRatio - (padding * elementWithCanvas.canvasZoom) / elementWithCanvas.canvasZoom, elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom, elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom, @@ -905,9 +906,6 @@ export const renderElement = ( } context.save(); context.translate(cx, cy); - if (element.type === "image") { - context.scale(element.scale[0], element.scale[1]); - } if (shouldResetImageFilter(element, renderConfig)) { context.filter = "none"; @@ -973,6 +971,12 @@ export const renderElement = ( ); } else { context.rotate(element.angle); + + if (element.type === "image") { + // note: scale must be applied *after* rotating + context.scale(element.scale[0], element.scale[1]); + } + context.translate(-shiftX, -shiftY); drawElementOnCanvas(element, rc, context, renderConfig); } diff --git a/src/scene/scroll.ts b/src/scene/scroll.ts index 4fce5e64..114d6db0 100644 --- a/src/scene/scroll.ts +++ b/src/scene/scroll.ts @@ -41,8 +41,8 @@ export const centerScrollOn = ({ zoom: Zoom; }) => { return { - scrollX: (viewportDimensions.width / 2) * (1 / zoom.value) - scenePoint.x, - scrollY: (viewportDimensions.height / 2) * (1 / zoom.value) - scenePoint.y, + scrollX: viewportDimensions.width / 2 / zoom.value - scenePoint.x, + scrollY: viewportDimensions.height / 2 / zoom.value - scenePoint.y, }; }; diff --git a/src/utils.ts b/src/utils.ts index ffaf414d..119eb7ae 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -352,9 +352,8 @@ export const viewportCoordsToSceneCoords = ( scrollY: number; }, ) => { - const invScale = 1 / zoom.value; - const x = (clientX - offsetLeft) * invScale - scrollX; - const y = (clientY - offsetTop) * invScale - scrollY; + const x = (clientX - offsetLeft) / zoom.value - scrollX; + const y = (clientY - offsetTop) / zoom.value - scrollY; return { x, y }; };