@@ -12,6 +12,51 @@ const { Telegram } = require('telegraf')
1212
1313const emojiDb = new EmojiDbLib ( { useDefaultDb : true } )
1414
15+ // Canvas pool for reusing canvases
16+ const canvasPool = {
17+ small : [ ] , // For canvases < 1000x1000
18+ medium : [ ] , // For canvases < 2000x2000
19+ large : [ ] // For larger canvases
20+ }
21+
22+ const getPooledCanvas = ( width , height ) => {
23+ const size = width * height
24+ let pool
25+
26+ if ( size < 1000000 ) pool = canvasPool . small
27+ else if ( size < 4000000 ) pool = canvasPool . medium
28+ else pool = canvasPool . large
29+
30+ // Try to find a suitable canvas from pool
31+ for ( let i = 0 ; i < pool . length ; i ++ ) {
32+ const canvas = pool [ i ]
33+ if ( canvas . width >= width && canvas . height >= height ) {
34+ pool . splice ( i , 1 ) // Remove from pool
35+ return canvas
36+ }
37+ }
38+
39+ // Create new canvas if none suitable found
40+ return createCanvas ( width , height )
41+ }
42+
43+ const returnCanvasToPool = ( canvas ) => {
44+ const size = canvas . width * canvas . height
45+ let pool
46+
47+ if ( size < 1000000 ) pool = canvasPool . small
48+ else if ( size < 4000000 ) pool = canvasPool . medium
49+ else pool = canvasPool . large
50+
51+ // Only keep reasonable number of canvases in each pool
52+ if ( pool . length < 5 ) {
53+ // Clear the canvas
54+ const ctx = canvas . getContext ( '2d' )
55+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
56+ pool . push ( canvas )
57+ }
58+ }
59+
1560function loadFont ( ) {
1661 console . log ( 'font load start' )
1762 const fontsDir = 'assets/fonts/'
@@ -113,7 +158,7 @@ class QuoteGenerate {
113158
114159 async avatarImageLatters ( letters , color ) {
115160 const size = 500
116- const canvas = createCanvas ( size , size )
161+ const canvas = getPooledCanvas ( size , size )
117162 const context = canvas . getContext ( '2d' )
118163
119164 const gradient = context . createLinearGradient ( 0 , 0 , canvas . width , canvas . height )
@@ -364,7 +409,7 @@ class QuoteGenerate {
364409 const fallbackEmojiImageJson = emojiImageByBrand [ fallbackEmojiBrand ]
365410
366411 // Pre-calculate text dimensions to avoid creating oversized canvas
367- const canvas = createCanvas ( maxWidth + fontSize , maxHeight + fontSize )
412+ const canvas = getPooledCanvas ( maxWidth + fontSize , maxHeight + fontSize )
368413 const canvasCtx = canvas . getContext ( '2d' )
369414
370415 // text = text.slice(0, 4096)
@@ -679,12 +724,15 @@ class QuoteGenerate {
679724 if ( breakWrite ) break
680725 }
681726
682- const canvasResize = createCanvas ( textWidth , lineY + fontSize )
727+ const canvasResize = getPooledCanvas ( textWidth , lineY + fontSize )
683728 const canvasResizeCtx = canvasResize . getContext ( '2d' )
684729
685730 let dx = ( lineDirection === 'rtl' ) ? textWidth - maxWidth + fontSize * 2 : 0
686731 canvasResizeCtx . drawImage ( canvas , dx , 0 )
687732
733+ // Return original canvas to pool
734+ returnCanvasToPool ( canvas )
735+
688736 return canvasResize
689737 }
690738
@@ -693,7 +741,7 @@ class QuoteGenerate {
693741 const x = 0
694742 const y = 0
695743
696- const canvas = createCanvas ( w , h )
744+ const canvas = getPooledCanvas ( w , h )
697745 const canvasCtx = canvas . getContext ( '2d' )
698746
699747 canvasCtx . fillStyle = color
@@ -717,7 +765,7 @@ class QuoteGenerate {
717765 const x = 0
718766 const y = 0
719767
720- const canvas = createCanvas ( w , h )
768+ const canvas = getPooledCanvas ( w , h )
721769 const canvasCtx = canvas . getContext ( '2d' )
722770
723771 const gradient = canvasCtx . createLinearGradient ( 0 , 0 , w , h )
@@ -765,7 +813,7 @@ class QuoteGenerate {
765813 const w = image . width
766814 const h = image . height
767815
768- const canvas = createCanvas ( w , h )
816+ const canvas = getPooledCanvas ( w , h )
769817 const canvasCtx = canvas . getContext ( '2d' )
770818
771819 const x = 0
@@ -789,7 +837,7 @@ class QuoteGenerate {
789837 }
790838
791839 drawReplyLine ( lineWidth , height , color ) {
792- const canvas = createCanvas ( 20 , height )
840+ const canvas = getPooledCanvas ( 20 , height )
793841 const context = canvas . getContext ( '2d' )
794842 context . beginPath ( )
795843 context . moveTo ( 10 , 0 )
@@ -809,7 +857,7 @@ class QuoteGenerate {
809857 if ( avatarImage ) {
810858 const avatarSize = avatarImage . naturalHeight || avatarImage . height
811859
812- const canvas = createCanvas ( avatarSize , avatarSize )
860+ const canvas = getPooledCanvas ( avatarSize , avatarSize )
813861 const canvasCtx = canvas . getContext ( '2d' )
814862
815863 const avatarX = 0
@@ -848,7 +896,7 @@ class QuoteGenerate {
848896 drawWaveform ( data ) {
849897 const normalizedData = data . map ( i => i / 32 )
850898
851- const canvas = createCanvas ( 4500 , 500 )
899+ const canvas = getPooledCanvas ( 4500 , 500 )
852900 const padding = 50
853901 canvas . height = ( canvas . height + padding * 2 )
854902 const ctx = canvas . getContext ( '2d' )
@@ -976,7 +1024,7 @@ class QuoteGenerate {
9761024 backgroundColorOne = backgroundColorTwo = 'rgba(0, 0, 0, 0.5)'
9771025 }
9781026
979- const canvas = createCanvas ( width , height )
1027+ const canvas = getPooledCanvas ( width , height )
9801028 const canvasCtx = canvas . getContext ( '2d' )
9811029
9821030 const rectPosX = blockPosX
@@ -1021,12 +1069,13 @@ class QuoteGenerate {
10211069 return this . colorCache . get ( color )
10221070 }
10231071
1024- const canvas = createCanvas ( 0 , 0 )
1072+ const canvas = getPooledCanvas ( 1 , 1 ) // Minimal size for color normalization
10251073 const canvasCtx = canvas . getContext ( '2d' )
10261074
10271075 canvasCtx . fillStyle = color
10281076 const normalizedColor = canvasCtx . fillStyle
10291077
1078+ returnCanvasToPool ( canvas )
10301079 this . colorCache . set ( color , normalizedColor )
10311080 return normalizedColor
10321081 }
0 commit comments