Skip to content

Commit 5f974d2

Browse files
committed
fix: implement canvas pooling for optimized canvas management and rendering
1 parent 98edfa6 commit 5f974d2

2 files changed

Lines changed: 69 additions & 19 deletions

File tree

methods/generate.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ const returnCanvasToPool = (canvas) => {
5353
}
5454

5555
const normalizeColor = (color) => {
56-
const canvas = createCanvas(0, 0)
56+
const canvas = getPooledCanvas(1, 1) // Minimal size for color normalization
5757
const canvasCtx = canvas.getContext('2d')
5858

5959
canvasCtx.fillStyle = color
60-
color = canvasCtx.fillStyle
60+
const normalizedColor = canvasCtx.fillStyle
6161

62-
return color
62+
returnCanvasToPool(canvas)
63+
return normalizedColor
6364
}
6465

6566
const colorLuminance = (hex, lum) => {
@@ -83,7 +84,7 @@ const colorLuminance = (hex, lum) => {
8384
}
8485

8586
const imageAlpha = (image, alpha) => {
86-
const canvas = createCanvas(image.width, image.height)
87+
const canvas = getPooledCanvas(image.width, image.height)
8788
const canvasCtx = canvas.getContext('2d')
8889

8990
canvasCtx.globalAlpha = alpha
@@ -238,7 +239,7 @@ module.exports = async (parm) => {
238239

239240
const quoteMargin = 5 * parm.scale
240241

241-
const canvas = createCanvas(width, height + (quoteMargin * quoteImages.length))
242+
const canvas = getPooledCanvas(width, height + (quoteMargin * quoteImages.length))
242243
const canvasCtx = canvas.getContext('2d')
243244

244245
let imageY = 0
@@ -271,7 +272,7 @@ module.exports = async (parm) => {
271272

272273
const canvasImage = await loadImage(await imageQuoteSharp.toBuffer())
273274

274-
const canvasPadding = createCanvas(canvasImage.width, canvasImage.height + downPadding)
275+
const canvasPadding = getPooledCanvas(canvasImage.width, canvasImage.height + downPadding)
275276
const canvasPaddingCtx = canvasPadding.getContext('2d')
276277

277278
canvasPaddingCtx.drawImage(canvasImage, 0, 0)
@@ -289,7 +290,7 @@ module.exports = async (parm) => {
289290

290291
const canvasImage = await loadImage(canvasQuote.toBuffer())
291292

292-
const canvasPic = createCanvas(canvasImage.width + widthPadding, canvasImage.height + heightPadding)
293+
const canvasPic = getPooledCanvas(canvasImage.width + widthPadding, canvasImage.height + heightPadding)
293294
const canvasPicCtx = canvasPic.getContext('2d')
294295

295296
// Optimized gradient background creation
@@ -320,7 +321,7 @@ module.exports = async (parm) => {
320321

321322
quoteImage = await sharp(canvasPic.toBuffer()).png({ lossless: true, force: true }).toBuffer()
322323
} else if (type === 'stories') {
323-
const canvasPic = createCanvas(720, 1280)
324+
const canvasPic = getPooledCanvas(720, 1280)
324325
const canvasPicCtx = canvasPic.getContext('2d')
325326

326327
// Optimized gradient background creation for stories

utils/quote-generate.js

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,51 @@ const { Telegram } = require('telegraf')
1212

1313
const 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+
1560
function 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

Comments
 (0)