Skip to content

Commit c48f6ab

Browse files
committed
fix: optimize emoji loading and canvas rendering in quote generation
1 parent 6e53e07 commit c48f6ab

File tree

1 file changed

+77
-25
lines changed

1 file changed

+77
-25
lines changed

utils/quote-generate.js

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ class QuoteGenerate {
383383

384384
const fallbackEmojiImageJson = emojiImageByBrand[fallbackEmojiBrand]
385385

386+
// Pre-calculate text dimensions to avoid creating oversized canvas
386387
const canvas = createCanvas(maxWidth + fontSize, maxHeight + fontSize)
387388
const canvasCtx = canvas.getContext('2d')
388389

@@ -396,6 +397,44 @@ class QuoteGenerate {
396397

397398
const emojis = emojiDb.searchFromText({ input: text, fixCodePoints: true })
398399

400+
// Pre-load all emojis for better performance
401+
const emojiCache = new Map()
402+
const emojiLoadPromises = []
403+
404+
for (let emojiIndex = 0; emojiIndex < emojis.length; emojiIndex++) {
405+
const emoji = emojis[emojiIndex]
406+
if (!emojiCache.has(emoji.found)) {
407+
emojiLoadPromises.push(
408+
(async () => {
409+
const emojiImageBase = emojiImageJson[emoji.found]
410+
if (emojiImageBase) {
411+
try {
412+
const image = await loadImage(Buffer.from(emojiImageBase, 'base64'))
413+
emojiCache.set(emoji.found, image)
414+
} catch {
415+
try {
416+
const fallbackImage = await loadImage(Buffer.from(fallbackEmojiImageJson[emoji.found], 'base64'))
417+
emojiCache.set(emoji.found, fallbackImage)
418+
} catch {
419+
// Skip if both fail
420+
}
421+
}
422+
} else {
423+
try {
424+
const fallbackImage = await loadImage(Buffer.from(fallbackEmojiImageJson[emoji.found], 'base64'))
425+
emojiCache.set(emoji.found, fallbackImage)
426+
} catch {
427+
// Skip if fails
428+
}
429+
}
430+
})()
431+
)
432+
}
433+
}
434+
435+
// Wait for all emojis to load
436+
await Promise.all(emojiLoadPromises)
437+
399438
for (let charIndex = 0; charIndex < chars.length; charIndex++) {
400439
const char = chars[charIndex]
401440

@@ -534,6 +573,11 @@ class QuoteGenerate {
534573

535574
let breakWrite = false
536575
let lineDirection = this.getLineDirection(styledWords, 0)
576+
577+
// Pre-set font to avoid repeated font changes
578+
let currentFont = null
579+
let currentFillStyle = null
580+
537581
for (let index = 0; index < styledWords.length; index++) {
538582
const styledWord = styledWords[index]
539583

@@ -543,17 +587,8 @@ class QuoteGenerate {
543587
if (styledWord.customEmojiId && customEmojiStickers[styledWord.customEmojiId]) {
544588
emojiImage = customEmojiStickers[styledWord.customEmojiId]
545589
} else {
546-
const emojiImageBase = emojiImageJson[styledWord.emoji.code]
547-
if (emojiImageBase) {
548-
emojiImage = await loadImage(
549-
Buffer.from(emojiImageBase, 'base64')
550-
).catch(() => {})
551-
}
552-
if (!emojiImage) {
553-
emojiImage = await loadImage(
554-
Buffer.from(fallbackEmojiImageJson[styledWord.emoji.code], 'base64')
555-
).catch(() => {})
556-
}
590+
// Use pre-loaded emoji from cache
591+
emojiImage = emojiCache.get(styledWord.emoji.code)
557592
}
558593
}
559594

@@ -578,20 +613,37 @@ class QuoteGenerate {
578613
const rbaColor = this.hexToRgb(this.normalizeColor(fontColor))
579614
fillStyle = `rgba(${rbaColor[0]}, ${rbaColor[1]}, ${rbaColor[2]}, 0.15)`
580615
}
581-
// else {
582-
// canvasCtx.font = `${fontSize}px OpenSans`
583-
// canvasCtx.fillStyle = fontColor
584-
// }
585-
586-
canvasCtx.font = `${fontType} ${fontSize}px ${fontName}`
587-
canvasCtx.fillStyle = fillStyle
588-
589-
if (canvasCtx.measureText(styledWord.word).width > maxWidth - fontSize * 3) {
590-
while (canvasCtx.measureText(styledWord.word).width > maxWidth - fontSize * 3) {
591-
styledWord.word = styledWord.word.substr(0, styledWord.word.length - 1)
592-
if (styledWord.word.length <= 0) break
616+
617+
const newFont = `${fontType} ${fontSize}px ${fontName}`
618+
619+
// Only change font if different from current
620+
if (currentFont !== newFont) {
621+
canvasCtx.font = newFont
622+
currentFont = newFont
623+
}
624+
625+
// Only change fill style if different from current
626+
if (currentFillStyle !== fillStyle) {
627+
canvasCtx.fillStyle = fillStyle
628+
currentFillStyle = fillStyle
629+
}
630+
631+
// Pre-truncate long words before measurement
632+
let wordToMeasure = styledWord.word
633+
const maxWordWidth = maxWidth - fontSize * 3
634+
635+
if (wordToMeasure.length > 50) { // Quick length check before expensive measurement
636+
while (canvasCtx.measureText(wordToMeasure).width > maxWordWidth && wordToMeasure.length > 0) {
637+
wordToMeasure = wordToMeasure.substr(0, wordToMeasure.length - 1)
638+
}
639+
if (wordToMeasure.length < styledWord.word.length) {
640+
styledWord.word = wordToMeasure + '…'
641+
}
642+
} else if (canvasCtx.measureText(wordToMeasure).width > maxWordWidth) {
643+
while (canvasCtx.measureText(wordToMeasure).width > maxWordWidth && wordToMeasure.length > 0) {
644+
wordToMeasure = wordToMeasure.substr(0, wordToMeasure.length - 1)
593645
}
594-
styledWord.word += '…'
646+
styledWord.word = wordToMeasure + '…'
595647
}
596648

597649
let lineWidth
@@ -631,7 +683,7 @@ class QuoteGenerate {
631683
if (lineWidth > textWidth) textWidth = lineWidth
632684
if (textWidth > maxWidth) textWidth = maxWidth
633685

634-
let wordX = (lineDirection == 'rtl') ? maxWidth - lineX - wordlWidth - fontSize * 2 : lineX
686+
let wordX = (lineDirection === 'rtl') ? maxWidth - lineX - wordlWidth - fontSize * 2 : lineX
635687

636688
if (emojiImage) {
637689
canvasCtx.drawImage(emojiImage, wordX, lineY - fontSize + (fontSize * 0.15), fontSize + (fontSize * 0.22), fontSize + (fontSize * 0.22))

0 commit comments

Comments
 (0)