11import * as PIXI from 'pixi.js' ;
22
33import { aggregate } from '../utilities/aggregate' ;
4+ import { BLUR_LAYER_NAME } from './constants' ;
45import { getApplicationScale } from './misc' ;
56
67//
7- // Global constants used for crop effects
8+ // Global constants used for blur effects
89//
910
1011// Define the radius (pixel) of the brush used to apply blur
@@ -19,10 +20,27 @@ const CURSOR_NAME = 'BRUSH_CURSOR';
1920//
2021
2122/**
22- * This function draw brush graphical object for each points
23+ * Get or create the blur layer Container in the stage.
24+ * Using a Container (not a Sprite) avoids the v8 deprecation warning
25+ * "Only Containers will be allowed to add children".
26+ */
27+ function getOrCreateBlurLayer ( application : PIXI . Application ) : PIXI . Container {
28+ let layer = application . stage . getChildByLabel (
29+ BLUR_LAYER_NAME ,
30+ ) as PIXI . Container | null ;
31+ if ( ! layer ) {
32+ layer = new PIXI . Container ( ) ;
33+ layer . label = BLUR_LAYER_NAME ;
34+ application . stage . addChild ( layer ) ;
35+ }
36+ return layer ;
37+ }
38+
39+ /**
40+ * This function draws brush graphical objects for each point
2341 *
2442 * @param points a list of PIXI.Point used to draw brush
25- * @returns a graphical object that draw brush for each of theses points
43+ * @returns a graphical object that draws brush for each of these points
2644 */
2745function drawBrush (
2846 points : Array < PIXI . Point | undefined > ,
@@ -31,22 +49,20 @@ function drawBrush(
3149 const container = new PIXI . Graphics ( ) ;
3250 for ( const point of points ) {
3351 if ( point ) {
34- container . beginFill ( 0xffffff , 1 ) ;
35- container . drawCircle ( point . x , point . y , BRUSH_SIZE / scale ) ;
36- container . lineStyle ( 0 ) ;
37- container . endFill ( ) ;
52+ container . circle ( point . x , point . y , BRUSH_SIZE / scale ) ;
53+ container . fill ( { color : 0xffffff , alpha : 1 } ) ;
3854 }
3955 }
4056 return container ;
4157}
4258
4359/**
44- * This function create a mouse event listener that merge and aggregate mouse events
45- * The aggregated events are used to draw brush and apply blur filter to the stage
46- * If the spriteName has not been found in the context, the listener do nothing
60+ * This function creates a mouse event listener that merges and aggregates mouse events.
61+ * The aggregated events are used to draw brush and apply blur filter to the stage.
62+ * If the spriteName has not been found in the context, the listener does nothing.
4763 *
4864 * @param application The PIXI.Application context
49- * @param { spriteName } arg The name of the sprite identifying the original image
65+ * @param spriteName The name of the sprite identifying the original image
5066 * @returns A mouse event listener
5167 */
5268function drawBlurListener (
@@ -60,32 +76,34 @@ function drawBlurListener(
6076 } ,
6177 ( points : Array < PIXI . Point | undefined > ) => {
6278 // Search for sprite
63- const child = application . stage . getChildByName ( spriteName ) ;
79+ const child = application . stage . getChildByLabel ( spriteName ) ;
6480 const scale = getApplicationScale ( application ) ;
6581 if ( ! child ) return ;
82+ const sprite = child as PIXI . Sprite ;
6683 // Create a sprite by copying texture and apply blurFilter
67- const newSprite = new PIXI . Sprite ( ( child as PIXI . Sprite ) . texture ) ;
68- // Apply blur filter to the new sprite, quality for big image (fix lag issue)
69-
84+ const newSprite = new PIXI . Sprite ( sprite . texture ) ;
7085 newSprite . filters = [
7186 new PIXI . BlurFilter ( {
7287 strength : 8 ,
7388 quality : Math . min ( Math . round ( 4 * scale ) , 4 ) ,
7489 resolution : Math . min ( scale , 1 ) ,
7590 } ) ,
7691 ] ;
77- newSprite . width = ( child as PIXI . Sprite ) . width ;
78- newSprite . height = ( child as PIXI . Sprite ) . height ;
79- // Resize the new sprite to match the original
92+ newSprite . width = sprite . width ;
93+ newSprite . height = sprite . height ;
8094 newSprite . scale = new PIXI . Point ( 1 , 1 ) ;
81- newSprite . anchor = ( child as PIXI . Sprite ) . anchor ;
82- newSprite . mask = drawBrush ( points , scale ) ;
83- ( child as PIXI . Sprite ) . addChild ( newSprite ) ;
95+ newSprite . anchor = sprite . anchor ;
96+ // Add blur sprite and its mask to the blur layer Container
97+ const blurLayer = getOrCreateBlurLayer ( application ) ;
98+ const brushMask = drawBrush ( points , scale ) ;
99+ blurLayer . addChild ( brushMask ) ;
100+ newSprite . mask = brushMask ;
101+ blurLayer . addChild ( newSprite ) ;
84102 } ,
85103 ) ;
86104}
87105/**
88- * This function draw the graphical cursor use to apply the blur effect
106+ * This function draws the graphical cursor used to apply the blur effect
89107 *
90108 * @param application The PIXI.Application context
91109 * @returns the PIXI.Graphics object representing the cursor
@@ -95,26 +113,25 @@ function drawCursor(application: PIXI.Application): PIXI.Graphics {
95113 removeCursor ( application ) ;
96114 const scale = getApplicationScale ( application ) ;
97115 const circle = new PIXI . Graphics ( ) ;
98- circle . lineStyle ( Math . max ( 1 , 1 / scale ) , 0xff0000 ) ;
99- circle . drawCircle ( 0 , 0 , BRUSH_SIZE / scale ) ;
100- circle . endFill ( ) ;
101- circle . name = CURSOR_NAME ;
116+ circle . circle ( 0 , 0 , BRUSH_SIZE / scale ) ;
117+ circle . stroke ( { width : Math . max ( 1 , 1 / scale ) , color : 0xff0000 } ) ;
118+ circle . label = CURSOR_NAME ;
102119 application . stage . addChild ( circle ) ;
103120 return circle ;
104121}
105122/**
106- * This function remove cursor if exists in context
123+ * This function removes cursor if it exists in context
107124 *
108125 * @param application The PIXI.Application context
109126 */
110127function removeCursor ( application : PIXI . Application ) {
111- const child = application . stage . getChildByName ( CURSOR_NAME ) ;
128+ const child = application . stage . getChildByLabel ( CURSOR_NAME ) ;
112129 if ( child ) {
113130 child . removeFromParent ( ) ;
114131 }
115132}
116133/**
117- * Move the cursor graphical controler while mouse is moving
134+ * Move the cursor graphical controller while mouse is moving
118135 *
119136 * @param application the PIXI.Application context
120137 * @returns A mouse event listener
@@ -123,21 +140,21 @@ function moveCursorListener(application: PIXI.Application) {
123140 return ( event : PIXI . FederatedMouseEvent ) => {
124141 if ( ! application ) return ;
125142 const point = application . stage . toLocal ( event . global ) ;
126- const child = application . stage . getChildByName ( CURSOR_NAME , true ) ;
143+ const child = application . stage . getChildByLabel ( CURSOR_NAME , true ) ;
127144 if ( child ) {
128145 child . position . x = point . x ;
129146 child . position . y = point . y ;
130147 }
131148 } ;
132149}
133150/**
134- * This function start the blur controler
151+ * This function starts the blur controller:
135152 * - drawing cursor
136153 * - listening mouse move to move cursor
137- * - listening
154+ * - listening pointer events to apply blur
138155 *
139- * @param application
140- * @param param1
156+ * @param application The PIXI.Application context
157+ * @param spriteName The name of the sprite identifying the original image
141158 */
142159export function start (
143160 application : PIXI . Application ,
@@ -151,6 +168,8 @@ export function start(
151168 application . stage . on ( 'pointermove' , cursorListener ) ;
152169 const blurListener = drawBlurListener ( application , { spriteName } ) ;
153170 application . stage . on ( 'pointerdown' , ( ) => {
171+ // Remove first to avoid accumulating duplicate listeners on repeated clicks
172+ application . stage . off ( 'pointermove' , blurListener ) ;
154173 application . stage . on ( 'pointermove' , blurListener ) ;
155174 } ) ;
156175 // Stop listening move when cursor is up
@@ -164,7 +183,9 @@ export function start(
164183 } ) ;
165184}
166185/**
167- * This function remove cursor and all mouse event listeners
186+ * This function removes cursor and all mouse event listeners.
187+ * The blur layer is kept on stage so it gets captured by toBlob/generateTexture
188+ * when the image is saved or another operation is applied.
168189 *
169190 * @param application the PIXI.Application context
170191 */
0 commit comments