Show result during direct-to-mask drawing operations

This commit is contained in:
Igor Zinken
2025-03-15 21:31:24 +01:00
parent 5f191cf9cc
commit cfbfbfae1f
2 changed files with 53 additions and 5 deletions

View File

@@ -43,6 +43,7 @@ import { clipContextToSelection } from "@/rendering/clipping";
import { renderClonedStroke, setCloneSource } from "@/rendering/cloning";
import { renderBrushStroke } from "@/rendering/drawing";
import { floodFill } from "@/rendering/fill";
import { getMaskComposite, disposeMaskComposite } from "@/rendering/masking";
import { snapSpriteToGuide } from "@/rendering/snapping";
import { applyTransformation } from "@/rendering/transforming";
import { flushLayerCache, clearCacheProperty } from "@/rendering/cache/bitmap-cache";
@@ -625,6 +626,7 @@ export default class LayerSprite extends ZoomableSprite {
this.layer, this.getPaintSource(), this.getPaintSize(), this.canvas, this._brush.options.opacity,
this._toolType === ToolTypes.ERASER ? "destination-out" : undefined
);
disposeMaskComposite();
disposeDrawableCanvas();
this.resetFilterAndRecache();
@@ -711,7 +713,10 @@ export default class LayerSprite extends ZoomableSprite {
}
let drawContext: CanvasRenderingContext2D = documentContext;
const applyBlending = enabled && blendMode !== BlendModes.NORMAL;
const isPainting = this.isPainting();
const isDrawingOnMask = isPainting && this.isMaskable() && this._toolType !== ToolTypes.ERASER; // erasing from mask needs some work ;-)
const applyBlending = enabled && blendMode !== BlendModes.NORMAL && !isDrawingOnMask;
if ( applyBlending ) {
drawContext = getBlendContext( documentContext.canvas );
@@ -719,6 +724,12 @@ export default class LayerSprite extends ZoomableSprite {
drawContext.scale( scaleFactor, scaleFactor );
}
let maskComposite: CanvasContextPairing | undefined;
if ( isDrawingOnMask ) {
maskComposite = getMaskComposite( this.getPaintSize() ); // temporary canvas to combine paintCanvas with source
drawContext = maskComposite.ctx;
}
drawContext.save(); // transformation save()
const transformedBounds = applyTransformation( drawContext, this.layer, viewport );
@@ -730,7 +741,7 @@ export default class LayerSprite extends ZoomableSprite {
// invoke base class behaviour to render bitmap
super.draw( drawContext, transformCanvas ? undefined : viewport, drawBounds );
if ( applyBlending ) {
blendLayer( documentContext, drawContext, blendMode );
}
@@ -738,11 +749,14 @@ export default class LayerSprite extends ZoomableSprite {
drawContext.restore(); // transformation restore()
// user is currently drawing on this layer, render contents of drawableCanvas onto screen
if ( this.isPainting()) {
if ( isPainting ) {
renderDrawableCanvas(
documentContext, this.getPaintSize(), this.canvas, this._brush.options.opacity,
isDrawingOnMask ? drawContext : documentContext, this.getPaintSize(), this.canvas, this._brush.options.opacity,
this._toolType === ToolTypes.ERASER || this.isMaskable() ? "destination-out" : undefined,
);
if ( isDrawingOnMask ) {
documentContext.drawImage( maskComposite.cvs, 0, 0 ); // draw temporary masked canvas onto underlying document
}
}
if ( altOpacity ) {

View File

@@ -20,6 +20,40 @@
* 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 Size } from "zcanvas";
import type { CanvasContextPairing } from "@/definitions/editor";
import { createCanvas, setCanvasDimensions } from "@/utils/canvas-util";
let tempCanvas: CanvasContextPairing;
/**
* Get a reference to a temporary Canvas used to make a composite of the
* source layer and the mask layer while drawing (to be rendered above
* the background for instant previewing purposes).
*/
export const getMaskComposite = ( size: Size ): CanvasContextPairing => {
if ( !tempCanvas ) {
tempCanvas = createCanvas( size.width, size.height );
} else {
setCanvasDimensions( tempCanvas, size.width, size.height );
}
return tempCanvas;
};
/**
* Free memory allocated to the temporary Canvas. The Canvas will
* remained pooled but by shrinking its size it will reduce memory usage.
*/
export const disposeMaskComposite = (): void => {
if ( tempCanvas ) {
setCanvasDimensions( tempCanvas, 1, 1 );
}
};
/**
* Apply a mask defined in provided mask property onto provided image source where
* the output is drawn onto provided destinationContext.
*/
export const maskImage = (
destinationContext: CanvasRenderingContext2D, image: HTMLCanvasElement, mask: HTMLCanvasElement,
width: number, height: number, maskOffsetX = 0, maskOffsetY = 0
@@ -33,4 +67,4 @@ export const maskImage = (
destinationContext.drawImage( mask, maskOffsetX, maskOffsetY );
destinationContext.restore();
};
};