mirror of
https://github.com/igorski/bitmappery.git
synced 2026-07-05 23:09:12 +02:00
Show result during direct-to-mask drawing operations
This commit is contained in:
@@ -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 ) {
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user