From 42222c19ae93ce6af2b52c6122c7865102907248 Mon Sep 17 00:00:00 2001 From: ChiaNing Date: Wed, 20 Sep 2023 17:10:15 +0800 Subject: [PATCH] remove shared label info --- cocos/2d/assembler/label/bmfontUtils.ts | 97 ++++++------------- cocos/2d/assembler/label/font-utils.ts | 70 +++++--------- cocos/2d/assembler/label/letter-font.ts | 93 +++++++++++++------ cocos/2d/assembler/label/text-output-data.ts | 5 +- cocos/2d/assembler/label/text-processing.ts | 98 ++++++++++---------- cocos/2d/assembler/label/text-style.ts | 5 +- cocos/2d/assembler/label/ttfUtils.ts | 43 +++++---- cocos/2d/components/label.ts | 87 ++--------------- cocos/2d/components/rich-text.ts | 4 +- 9 files changed, 205 insertions(+), 297 deletions(-) diff --git a/cocos/2d/assembler/label/bmfontUtils.ts b/cocos/2d/assembler/label/bmfontUtils.ts index 4e1a4901385..36ebbb69254 100644 --- a/cocos/2d/assembler/label/bmfontUtils.ts +++ b/cocos/2d/assembler/label/bmfontUtils.ts @@ -23,13 +23,12 @@ */ import { JSB } from 'internal:constants'; -import { IConfig, FontAtlas } from '../../assets/bitmap-font'; +import { FontAtlas, BitmapFont } from '../../assets/bitmap-font'; import { SpriteFrame } from '../../assets/sprite-frame'; import { Rect, Vec2 } from '../../../core'; import { error } from '@base/debug'; import { Label, Overflow, CacheMode } from '../../components/label'; import { UITransform } from '../../framework/ui-transform'; -import { LetterAtlas, shareLabelInfo } from './font-utils'; import { dynamicAtlasManager } from '../../utils/dynamic-atlas/atlas-manager'; import { TextProcessing } from './text-processing'; import { TextOutputLayoutData, TextOutputRenderData } from './text-output-data'; @@ -37,11 +36,8 @@ import { TextStyle } from './text-style'; import { TextLayout } from './text-layout'; import { view } from '../../../ui/view'; -const _defaultLetterAtlas = new LetterAtlas(64, 64); const _defaultFontAtlas = new FontAtlas(null); -let _fntConfig: IConfig | null = null; -let _spriteFrame: SpriteFrame|null = null; let QUAD_INDICES; export const bmfontUtils = { @@ -55,7 +51,6 @@ export const bmfontUtils = { ): void { style.fontSize = comp.fontSize; //both style.actualFontSize = comp.fontSize; //both - style.originFontSize = _fntConfig ? _fntConfig.fontSize : comp.fontSize; //both layout.horizontalAlign = comp.horizontalAlign; //both layout.verticalAlign = comp.verticalAlign; //both layout.spacingX = comp.spacingX; // layout only @@ -69,20 +64,23 @@ export const bmfontUtils = { // should wrap text if (overflow === Overflow.NONE) { layout.wrapping = false; // both - outputLayoutData.nodeContentSize.width += shareLabelInfo.margin * 2; - outputLayoutData.nodeContentSize.height += shareLabelInfo.margin * 2; } else if (overflow === Overflow.RESIZE_HEIGHT) { layout.wrapping = true; - outputLayoutData.nodeContentSize.height += shareLabelInfo.margin * 2; } else { layout.wrapping = comp.enableWrapText; } + const fontAsset = comp.font as BitmapFont; + style.fntConfig = fontAsset.fntConfig; // layout only + style.originFontSize = fontAsset.fntConfig?.fontSize; //both + style.fontAtlas = fontAsset.fontDefDictionary; + if (!style.fontAtlas) { + style.fontAtlas = _defaultFontAtlas; + } - shareLabelInfo.lineHeight = comp.lineHeight; - shareLabelInfo.fontSize = comp.fontSize; + style.isOutlined = false; + style.outlineWidth = 0; - style.fntConfig = _fntConfig; // layout only - style.fontFamily = shareLabelInfo.fontFamily; // layout only + style.hash = ''; }, // render Only @@ -96,33 +94,30 @@ export const bmfontUtils = { outputRenderData.uiTransAnchorX = anchor.x; outputRenderData.uiTransAnchorY = anchor.y; - style.spriteFrame = _spriteFrame; // render only + if (comp.font instanceof BitmapFont) { + const fontAsset = comp.font; + style.spriteFrame = fontAsset.spriteFrame; + dynamicAtlasManager.packToDynamicAtlas(comp, style.spriteFrame); + } style.color.set(comp.color); // render only }, - // 进行统一调用 updateLayoutData (comp: Label): void { - if (comp.layoutDirty) { - const trans = comp.node._uiProps.uiTransformComp!; - const processing = TextProcessing.instance; - const style = comp.textStyle; - const layout = comp.textLayout; - const outputLayoutData = comp.textLayoutData; - style.fontScale = view.getScaleX(); - this._updateFontFamily(comp); - - this.updateLayoutProcessingData(style, layout, outputLayoutData, comp, trans); - - this._updateLabelInfo(comp); + // Todo: dirtyFlag + const trans = comp.node._uiProps.uiTransformComp!; + const processing = TextProcessing.instance; + const style = comp.textStyle; + const layout = comp.textLayout; + const outputLayoutData = comp.textLayoutData; + style.fontScale = view.getScaleX(); - style.fontDesc = shareLabelInfo.fontDesc; + this.updateLayoutProcessingData(style, layout, outputLayoutData, comp, trans); - // TextProcessing - processing.processingString(true, style, layout, outputLayoutData, comp.string); + // TextProcessing + processing.processingString(true, style, layout, outputLayoutData, comp.string); - comp.actualFontSize = style.actualFontSize; - trans.setContentSize(outputLayoutData.nodeContentSize); - } + comp.actualFontSize = style.actualFontSize; + trans.setContentSize(outputLayoutData.nodeContentSize); }, updateRenderData (comp: Label): void { @@ -131,7 +126,7 @@ export const bmfontUtils = { } if (comp.renderData.vertDirty) { - this.updateLayoutData(comp);// 需要注意的是要防止在两个函数中间被修改 // 但是这里的修改应该是不会影响到排版的 + this.updateLayoutData(comp);// Todo: move to layout manager const renderData = comp.renderData; const processing = TextProcessing.instance; const style = comp.textStyle; @@ -169,8 +164,6 @@ export const bmfontUtils = { this.updateColor(comp); // dirty need renderData.vertDirty = false; - - this._resetProperties(); } if (comp.spriteFrame) { @@ -288,37 +281,7 @@ export const bmfontUtils = { dataList[dataOffset + 3].y = y; }, - _updateFontFamily (comp): void { - const fontAsset = comp.font; - _spriteFrame = fontAsset.spriteFrame; - _fntConfig = fontAsset.fntConfig; - shareLabelInfo.fontAtlas = fontAsset.fontDefDictionary; - if (!shareLabelInfo.fontAtlas) { - if (comp.cacheMode === CacheMode.CHAR) { - shareLabelInfo.fontAtlas = _defaultLetterAtlas; - } else { - shareLabelInfo.fontAtlas = _defaultFontAtlas; - } - } - - dynamicAtlasManager.packToDynamicAtlas(comp, _spriteFrame); - // TODO update material and uv - }, - - _updateLabelInfo (comp): void { - // clear - shareLabelInfo.hash = ''; - shareLabelInfo.margin = 0; - }, - - _resetProperties (): void { - _fntConfig = null; - _spriteFrame = null; - shareLabelInfo.hash = ''; - shareLabelInfo.margin = 0; - }, - - createQuadIndices (indexCount): void { + createQuadIndices (indexCount: number): void { if (indexCount % 6 !== 0) { error('illegal index count!'); return; diff --git a/cocos/2d/assembler/label/font-utils.ts b/cocos/2d/assembler/label/font-utils.ts index 9f177c7f433..f9337192918 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -22,6 +22,7 @@ THE SOFTWARE. */ +import { ccwindow } from '@base/global'; import { FontAtlas } from '../../assets/bitmap-font'; import { Color, macro, ImageData } from '../../../core'; import { warn, warnID } from '@base/debug'; @@ -30,7 +31,7 @@ import { PixelFormat } from '../../../asset/assets/asset-enum'; import { BufferTextureCopy } from '../../../gfx'; import { safeMeasureText, BASELINE_RATIO, MIDDLE_RATIO, getBaselineOffset } from '../../utils/text-utils'; import { director, Director } from '../../../game/director'; -import { ccwindow } from '@base/global'; +import { TextStyle } from './text-style'; export interface ISharedLabelData { canvas: HTMLCanvasElement; @@ -88,12 +89,8 @@ export class CanvasPool { interface ILabelInfo { fontSize: number; - lineHeight: number; - hash: string; fontFamily: string; fontDesc: string; - hAlign: number; - vAlign: number; color: Color; isOutlined: boolean; out: Color; @@ -131,10 +128,10 @@ class LetterTexture { public height = 0; public offsetY = 0; public hash: string; - constructor (char: string, labelInfo: ILabelInfo) { + constructor (char: string, labelInfo: ILabelInfo, hash: string) { this.char = char; this.labelInfo = labelInfo; - this.hash = `${char.charCodeAt(0)}${labelInfo.hash}`; + this.hash = `${char.charCodeAt(0)}${hash}`; } public updateRenderData (): void { @@ -400,11 +397,22 @@ export class LetterAtlas { return this.fontDefDictionary.letterDefinitions[key]; } - public getLetterDefinitionForChar (char: string, labelInfo: ILabelInfo): any { - const hash = char.charCodeAt(0).toString() + labelInfo.hash; + public getLetterDefinitionForChar (char: string, style: TextStyle, fontScale: number): any { + const styleHash = style.hash; + const hash = char.charCodeAt(0).toString() + styleHash; let letter = this.fontDefDictionary.letterDefinitions[hash]; if (!letter) { - const temp = new LetterTexture(char, labelInfo); + const sharedLabelData: ILabelInfo = { + fontSize: style.fontSize, + fontFamily: style.fontFamily, + fontDesc: style.fontDesc, + color: style.color.clone(), + isOutlined: style.isOutlined, + out: style.outlineColor.clone(), + margin: style.outlineWidth, + fontScale, + }; + const temp = new LetterTexture(char, sharedLabelData, styleHash); temp.updateRenderData(); letter = this.insertLetterTexture(temp); temp.destroy(); @@ -414,45 +422,13 @@ export class LetterAtlas { } } -export interface IShareLabelInfo { - fontAtlas: FontAtlas | LetterAtlas | null; - fontSize: number; - lineHeight: number; - hAlign: number; - vAlign: number; - hash: string; - fontFamily: string; - fontDesc: string; - color: Color; - isOutlined: boolean; - out: Color; - margin: number; - fontScale: number; -} - -export const shareLabelInfo: IShareLabelInfo = { - fontAtlas: null, - fontSize: 0, - lineHeight: 0, - hAlign: 0, - vAlign: 0, - hash: '', - fontFamily: '', - fontDesc: 'Arial', - color: Color.WHITE.clone(), - isOutlined: false, - out: Color.WHITE.clone(), - margin: 0, - fontScale: 1, -}; - -export function computeHash (labelInfo: IShareLabelInfo): string { +export function computeHash (color: Color, isOutlined: boolean, margin: number, outlineColor: Color, fontSize: number, fontFamily: string): string { const hashData = ''; - const color = labelInfo.color.toHEX(); + const colorHex = color.toHEX(); let out = ''; - if (labelInfo.isOutlined && labelInfo.margin > 0) { - out = out + labelInfo.margin.toString() + labelInfo.out.toHEX(); + if (isOutlined && margin > 0) { + out = out + margin.toString() + outlineColor.toHEX(); } - return hashData + labelInfo.fontSize.toString() + labelInfo.fontFamily + color + out; + return hashData + fontSize.toString() + fontFamily + colorHex + out; } diff --git a/cocos/2d/assembler/label/letter-font.ts b/cocos/2d/assembler/label/letter-font.ts index 3acef2a0bcf..570b73d5277 100644 --- a/cocos/2d/assembler/label/letter-font.ts +++ b/cocos/2d/assembler/label/letter-font.ts @@ -23,13 +23,16 @@ */ import { js } from '../../../core'; -import { Label, LabelOutline } from '../../components'; +import { Label, LabelOutline, Overflow } from '../../components'; +import { UITransform } from '../../framework/ui-transform'; import { bmfontUtils } from './bmfontUtils'; -import { shareLabelInfo, LetterAtlas, computeHash } from './font-utils'; +import { LetterAtlas, computeHash } from './font-utils'; +import { TextLayout } from './text-layout'; +import { TextOutputLayoutData } from './text-output-data'; +import { TextStyle } from './text-style'; const _atlasWidth = 1024; const _atlasHeight = 1024; -const _isBold = false; let _shareAtlas: LetterAtlas | null = null; @@ -42,23 +45,6 @@ export const letterFont = js.mixin(bmfontUtils, { return _shareAtlas.getTexture(); }, - _updateFontFamily (comp) { - shareLabelInfo.fontAtlas = _shareAtlas; - shareLabelInfo.fontFamily = this._getFontFamily(comp); - - // outline - const outline = comp.getComponent(LabelOutline); - if (outline && outline.enabled) { - shareLabelInfo.isOutlined = true; - shareLabelInfo.margin = outline.width; - shareLabelInfo.out = outline.color.clone(); - shareLabelInfo.out.a = outline.color.a * comp.color.a / 255.0; - } else { - shareLabelInfo.isOutlined = false; - shareLabelInfo.margin = 0; - } - }, - _getFontFamily (comp: Label) { let fontFamily = 'Arial'; if (!comp.useSystemFont) { @@ -72,19 +58,66 @@ export const letterFont = js.mixin(bmfontUtils, { return fontFamily; }, - _updateLabelInfo (comp) { - shareLabelInfo.fontDesc = this._getFontDesc(); - shareLabelInfo.color = comp.color; - shareLabelInfo.hash = computeHash(shareLabelInfo); + _getFontDesc (fontSize: number, fontFamily: string) { + let fontDesc = `${fontSize.toString()}px `; + fontDesc += fontFamily; + + return fontDesc; }, - _getFontDesc () { - let fontDesc = `${shareLabelInfo.fontSize.toString()}px `; - fontDesc += shareLabelInfo.fontFamily; - if (_isBold) { - fontDesc = `bold ${fontDesc}`; + updateLayoutProcessingData ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + comp: Label, + trans: UITransform, + ): void { + style.fontSize = comp.fontSize; + style.actualFontSize = comp.fontSize; + layout.horizontalAlign = comp.horizontalAlign; + layout.verticalAlign = comp.verticalAlign; + layout.spacingX = comp.spacingX; + const overflow = comp.overflow; + + outputLayoutData.nodeContentSize.width = trans.width; + outputLayoutData.nodeContentSize.height = trans.height; + layout.overFlow = overflow; + layout.lineHeight = comp.lineHeight; + + style.fontAtlas = _shareAtlas; + style.fontFamily = this._getFontFamily(comp); + + // outline + let margin = 0; + const outline = comp.getComponent(LabelOutline); + if (outline && outline.enabled) { + style.isOutlined = true; + margin = outline.width; + style.outlineWidth = outline.width; + style.outlineColor = outline.color.clone(); + style.outlineColor.a = outline.color.a * comp.color.a / 255.0; + } else { + style.outlineWidth = 0; + style.isOutlined = false; + margin = 0; } - return fontDesc; + // should wrap text + if (overflow === Overflow.NONE) { + layout.wrapping = false; // both + outputLayoutData.nodeContentSize.width += margin * 2; + outputLayoutData.nodeContentSize.height += margin * 2; + } else if (overflow === Overflow.RESIZE_HEIGHT) { + layout.wrapping = true; + outputLayoutData.nodeContentSize.height += margin * 2; + } else { + layout.wrapping = comp.enableWrapText; + } + style.originFontSize = comp.fontSize; + style.fntConfig = null; + + style.fontDesc = this._getFontDesc(style.fontSize, style.fontFamily); + style.color.set(comp.color); + style.hash = computeHash(style.color, style.isOutlined, style.outlineWidth, style.outlineColor, style.fontSize, style.fontFamily); }, }); diff --git a/cocos/2d/assembler/label/text-output-data.ts b/cocos/2d/assembler/label/text-output-data.ts index 30086182eb3..10db72c3430 100644 --- a/cocos/2d/assembler/label/text-output-data.ts +++ b/cocos/2d/assembler/label/text-output-data.ts @@ -25,7 +25,7 @@ import { Texture2D } from '../../../asset/assets'; import { Rect, Size, Vec2 } from '../../../core'; import { SpriteFrame } from '../../assets'; -import { IRenderData } from './text-processing'; +import { IRenderData, LetterInfo } from './text-processing'; export class TextOutputLayoutData { // public parsedStringStyle; // Prepare for merging richtext @@ -43,6 +43,8 @@ export class TextOutputLayoutData { public startPosition = Vec2.ZERO.clone(); // ttf + public lettersInfo: LetterInfo[] = []; // only bmfont use + public reset (): void { this.parsedString.length = 0; this.nodeContentSize.set(0, 0); @@ -50,6 +52,7 @@ export class TextOutputLayoutData { this.canvasPadding.set(); this.contentSizeExtend.set(); this.startPosition.set(); + this.lettersInfo.length = 0; } } diff --git a/cocos/2d/assembler/label/text-processing.ts b/cocos/2d/assembler/label/text-processing.ts index e329032c583..2e440b552b1 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -22,16 +22,16 @@ THE SOFTWARE. */ import { ANDROID, JSB } from 'internal:constants'; +import { cclegacy } from '@base/global'; import { Texture2D } from '../../../asset/assets'; import { WrapMode } from '../../../asset/assets/asset-enum'; -import { cclegacy } from '@base/global'; -import { Color, Pool, Rect, Vec2 } from '../../../core'; +import { Color, Rect, Vec2 } from '../../../core'; import { log, logID, warn } from '@base/debug'; import { SpriteFrame } from '../../assets'; import { FontLetterDefinition } from '../../assets/bitmap-font'; import { HorizontalTextAlignment, Overflow, VerticalTextAlignment } from '../../components/label'; import { BASELINE_RATIO, fragmentText, getBaselineOffset, isUnicodeCJK, isUnicodeSpace, safeMeasureText } from '../../utils/text-utils'; -import { CanvasPool, ISharedLabelData, shareLabelInfo } from './font-utils'; +import { CanvasPool, ISharedLabelData } from './font-utils'; import { TextOutputLayoutData, TextOutputRenderData } from './text-output-data'; import { TextStyle } from './text-style'; import { TextLayout } from './text-layout'; @@ -55,7 +55,7 @@ export interface IRenderData { color: Color; } -class LetterInfo { +export class LetterInfo { public char = ''; public valid = true; public x = 0; @@ -75,7 +75,6 @@ export class TextProcessing { public destroy (): void { CanvasPool.getInstance().put(this._canvasData!); - this._lettersInfo.length = 0; } public processingString ( @@ -98,7 +97,10 @@ export class TextProcessing { if (loopTime > MAX_CALCULATION_NUM) { this._fontScale = 1; } else { - const maxValue = Math.max(outputLayoutData.canvasSize.width, outputLayoutData.canvasSize.height); // Current Canvas Size max dimension + const maxValue = Math.max( + outputLayoutData.canvasSize.width, + outputLayoutData.canvasSize.height, + ); // Current Canvas Size max dimension const canvasScaleToMaxSizeRatio = MAX_SIZE / maxValue; this._fontScale *= canvasScaleToMaxSizeRatio; this._fontScale = Math.max(1, this._fontScale); @@ -113,7 +115,6 @@ export class TextProcessing { } else { this._fontScale = 1; } - shareLabelInfo.fontScale = this._fontScale; this._setupBMFontOverflowMetrics(layout, outputLayoutData); this._updateFontScale(style); this._computeHorizontalKerningForText(style, layout, inputString); @@ -156,7 +157,6 @@ export class TextProcessing { private _canvas: HTMLCanvasElement | null = null; private _canvasData: ISharedLabelData | null = null; - private _lettersInfo: LetterInfo[] = []; private _tmpRect = new Rect(); private _maxFontSize = 100; @@ -721,7 +721,7 @@ export class TextProcessing { let textFragment = ''; for (let i = 0, line = 0, l = inputString.length; i < l; ++i) { - const letterInfo = this._lettersInfo[i]; + const letterInfo = outputLayoutData.lettersInfo[i]; if (!letterInfo.valid) { continue; } if (line === letterInfo.line) { textFragment += letterInfo.char; @@ -768,12 +768,12 @@ export class TextProcessing { lineIndex++; nextTokenX = 0; nextTokenY -= layout.lineHeight * this._getFontScale(style, layout) + _lineSpacing; - this._recordPlaceholderInfo(index, character); + this._recordPlaceholderInfo(index, character, style.hash, outputLayoutData.lettersInfo); index++; continue; } - const tokenLen = nextTokenFunc(style, layout, _string, index, textLen); + const tokenLen = nextTokenFunc(style, layout, _string, index, textLen, this._fontScale); let tokenHighestY = highestY; let tokenLowestY = lowestY; let tokenRight = letterRight; @@ -785,18 +785,18 @@ export class TextProcessing { const letterIndex = index + tmp; character = _string.charAt(letterIndex); if (character === '\r') { - this._recordPlaceholderInfo(letterIndex, character); + this._recordPlaceholderInfo(letterIndex, character, style.hash, outputLayoutData.lettersInfo); continue; } - letterDef = shareLabelInfo.fontAtlas!.getLetterDefinitionForChar(character, shareLabelInfo); + letterDef = style.fontAtlas!.getLetterDefinitionForChar(character, style, this._fontScale); if (!letterDef) { - this._recordPlaceholderInfo(letterIndex, character); + this._recordPlaceholderInfo(letterIndex, character, style.hash, outputLayoutData.lettersInfo); log(`Can't find letter definition in texture atlas ${ style.fntConfig!.atlasName} for letter:${character}`); continue; } - const letterX = nextLetterX + letterDef.offsetX * style.bmfontScale - shareLabelInfo.margin; + const letterX = nextLetterX + letterDef.offsetX * style.bmfontScale - style.outlineWidth; if (layout.wrapping && layout.maxLineWidth > 0 @@ -815,7 +815,7 @@ export class TextProcessing { } letterPosition.y = nextTokenY - letterDef.offsetY * style.bmfontScale; - this._recordLetterInfo(letterPosition, character, letterIndex, lineIndex); + this._recordLetterInfo(letterPosition, character, letterIndex, lineIndex, style.hash, style.fontAtlas, outputLayoutData.lettersInfo); if (letterIndex + 1 < layout.horizontalKerning.length && letterIndex < textLen - 1) { nextLetterX += layout.horizontalKerning[letterIndex + 1] * style.bmfontScale; @@ -863,10 +863,10 @@ export class TextProcessing { outputLayoutData.nodeContentSize.width = layout.textWidthTemp; outputLayoutData.nodeContentSize.height = layout.textHeightTemp; if (layout.textWidthTemp <= 0) { - outputLayoutData.nodeContentSize.width = parseFloat(longestLine.toFixed(2)) + shareLabelInfo.margin * 2; + outputLayoutData.nodeContentSize.width = parseFloat(longestLine.toFixed(2)) + style.outlineWidth * 2; } if (layout.textHeightTemp <= 0) { - outputLayoutData.nodeContentSize.height = parseFloat(layout.textDesiredHeight.toFixed(2)) + shareLabelInfo.margin * 2; + outputLayoutData.nodeContentSize.height = parseFloat(layout.textDesiredHeight.toFixed(2)) + style.outlineWidth * 2; } layout.tailoredTopY = outputLayoutData.nodeContentSize.height; @@ -881,36 +881,44 @@ export class TextProcessing { return true; } - private _recordPlaceholderInfo (letterIndex: number, char: string): void { - if (letterIndex >= this._lettersInfo.length) { + private _recordPlaceholderInfo (letterIndex: number, char: string, hash: string, lettersInfo: LetterInfo[]): void { + if (letterIndex >= lettersInfo.length) { const tmpInfo = new LetterInfo(); - this._lettersInfo.push(tmpInfo); + lettersInfo.push(tmpInfo); } - this._lettersInfo[letterIndex].char = char; - this._lettersInfo[letterIndex].hash = `${char.charCodeAt(0)}${shareLabelInfo.hash}`; - this._lettersInfo[letterIndex].valid = false; + lettersInfo[letterIndex].char = char; + lettersInfo[letterIndex].hash = `${char.charCodeAt(0)}${hash}`; + lettersInfo[letterIndex].valid = false; } - private _recordLetterInfo (letterPosition: Vec2, character: string, letterIndex: number, lineIndex: number): void { - if (letterIndex >= this._lettersInfo.length) { + private _recordLetterInfo ( + letterPosition: Vec2, + character: string, + letterIndex: number, + lineIndex: number, + hash: string, + fontAtlas, + lettersInfo, + ): void { + if (letterIndex >= lettersInfo.length) { const tmpInfo = new LetterInfo(); - this._lettersInfo.push(tmpInfo); + lettersInfo.push(tmpInfo); } const char = character.charCodeAt(0); - const key = `${char}${shareLabelInfo.hash}`; - - this._lettersInfo[letterIndex].line = lineIndex; - this._lettersInfo[letterIndex].char = character; - this._lettersInfo[letterIndex].hash = key; - this._lettersInfo[letterIndex].valid = shareLabelInfo.fontAtlas!.getLetter(key).valid; - this._lettersInfo[letterIndex].x = letterPosition.x; - this._lettersInfo[letterIndex].y = letterPosition.y; + const key = `${char}${hash}`; + + lettersInfo[letterIndex].line = lineIndex; + lettersInfo[letterIndex].char = character; + lettersInfo[letterIndex].hash = key; + lettersInfo[letterIndex].valid = fontAtlas.getLetter(key).valid; + lettersInfo[letterIndex].x = letterPosition.x; + lettersInfo[letterIndex].y = letterPosition.y; } // processingString - private _getFirstWordLen (style: TextStyle, layout: TextLayout, text: string, startIndex: number, textLen: number): number { + private _getFirstWordLen (style: TextStyle, layout: TextLayout, text: string, startIndex: number, textLen: number, fontScale: number): number { let character = text.charAt(startIndex); if (isUnicodeCJK(character) || character === '\n' @@ -919,7 +927,7 @@ export class TextProcessing { } let len = 1; - let letterDef = shareLabelInfo.fontAtlas!.getLetterDefinitionForChar(character, shareLabelInfo); + let letterDef = style.fontAtlas!.getLetterDefinitionForChar(character, style, fontScale); if (!letterDef) { return len; } @@ -928,12 +936,11 @@ export class TextProcessing { for (let index = startIndex + 1; index < textLen; ++index) { character = text.charAt(index); - letterDef = shareLabelInfo.fontAtlas!.getLetterDefinitionForChar(character, shareLabelInfo); + letterDef = style.fontAtlas!.getLetterDefinitionForChar(character, style, fontScale); if (!letterDef) { break; } letterX = nextLetterX + letterDef.offsetX * style.bmfontScale; - if (letterX + letterDef.w * style.bmfontScale > layout.maxLineWidth && !isUnicodeSpace(character) && layout.maxLineWidth > 0) { @@ -947,7 +954,6 @@ export class TextProcessing { } len++; } - return len; } @@ -1018,13 +1024,12 @@ export class TextProcessing { let letterClamp = false; const _string = inputString; for (let ctr = 0, l = _string.length; ctr < l; ++ctr) { - const letterInfo = process._lettersInfo[ctr]; + const letterInfo = outputLayoutData.lettersInfo[ctr]; if (letterInfo.valid) { - const letterDef = shareLabelInfo.fontAtlas!.getLetterDefinitionForChar(letterInfo.char, shareLabelInfo); + const letterDef = style.fontAtlas!.getLetterDefinitionForChar(letterInfo.char, style, process._fontScale); if (!letterDef) { continue; } - const px = letterInfo.x + letterDef.w * style.bmfontScale; const lineIndex = letterInfo.line; if (layout.textWidthTemp > 0) { @@ -1043,7 +1048,6 @@ export class TextProcessing { } } } - return letterClamp; } @@ -1119,16 +1123,16 @@ export class TextProcessing { inputString: string, callback, ): boolean { - const texture = style.spriteFrame ? style.spriteFrame.texture : shareLabelInfo.fontAtlas!.getTexture(); + const texture = style.spriteFrame ? style.spriteFrame.texture : style.fontAtlas!.getTexture(); const appX = outputRenderData.uiTransAnchorX * outputLayoutData.nodeContentSize.width; const appY = outputRenderData.uiTransAnchorY * outputLayoutData.nodeContentSize.height; const ret = true; for (let ctr = 0, l = inputString.length; ctr < l; ++ctr) { - const letterInfo = this._lettersInfo[ctr]; + const letterInfo = outputLayoutData.lettersInfo[ctr]; if (!letterInfo.valid) { continue; } - const letterDef = shareLabelInfo.fontAtlas!.getLetter(letterInfo.hash); + const letterDef = style.fontAtlas!.getLetter(letterInfo.hash); if (!letterDef) { warn('Can\'t find letter in this bitmap-font'); continue; diff --git a/cocos/2d/assembler/label/text-style.ts b/cocos/2d/assembler/label/text-style.ts index 590f4c03842..b665da041ff 100644 --- a/cocos/2d/assembler/label/text-style.ts +++ b/cocos/2d/assembler/label/text-style.ts @@ -23,8 +23,9 @@ */ import { Color } from '../../../core'; -import { IConfig } from '../../assets/bitmap-font'; +import { FontAtlas, IConfig } from '../../assets/bitmap-font'; import { SpriteFrame } from '../../assets/sprite-frame'; +import { LetterAtlas } from './font-utils'; export class TextStyle { // ---------------ttf extra part----------------- @@ -67,6 +68,8 @@ export class TextStyle { // font info // todo remove public fntConfig: IConfig | null = null; // For char mode,not have asset public spriteFrame: SpriteFrame | null = null; // For char mode,not have spriteFrame in asset + public hash = ''; // For char mode,not have hash in asset + public fontAtlas: FontAtlas | LetterAtlas | null = null; // Just for bm & char public fontScale = 1; diff --git a/cocos/2d/assembler/label/ttfUtils.ts b/cocos/2d/assembler/label/ttfUtils.ts index ff74e950a7a..706e9059290 100644 --- a/cocos/2d/assembler/label/ttfUtils.ts +++ b/cocos/2d/assembler/label/ttfUtils.ts @@ -44,25 +44,25 @@ export const ttfUtils = { trans: UITransform, ): void { // font info // both - style.isSystemFontUsed = comp.useSystemFont; // 都会影响 - style.fontSize = comp.fontSize; // 都会影响 + style.isSystemFontUsed = comp.useSystemFont; // both + style.fontSize = comp.fontSize; // both // layout info - layout.lineHeight = comp.lineHeight; // both // 都影响 - layout.overFlow = comp.overflow; // layout only // but change render // 在 bmfont 里会和渲染相关,ttf 不会 + layout.lineHeight = comp.lineHeight; // both + layout.overFlow = comp.overflow; // layout only if (comp.overflow === Overflow.NONE) { layout.wrapping = false; } else if (comp.overflow === Overflow.RESIZE_HEIGHT) { layout.wrapping = true; } else { - layout.wrapping = comp.enableWrapText; // layout only // but change render // 在 bmfont 里会和渲染相关,ttf 不会 + layout.wrapping = comp.enableWrapText; // layout only } // effect info // both - style.isBold = comp.isBold; // 可能会影响到 context 的测量,所以和排版相关 // 和渲染相关 - style.isItalic = comp.isItalic; // 可能会影响到 context 的测量,所以和排版相关 // 和渲染相关 + style.isBold = comp.isBold; + style.isItalic = comp.isItalic; - // outline// both + // outline // both let outlineComp = LabelOutline && comp.getComponent(LabelOutline); outlineComp = (outlineComp && outlineComp.enabled && outlineComp.width > 0) ? outlineComp : null; if (outlineComp) { @@ -70,10 +70,10 @@ export const ttfUtils = { style.outlineColor.set(outlineComp.color); style.outlineWidth = outlineComp.width; } else { - style.isOutlined = false; // 由于影响到了canvas 的宽度,所以和排版相关 // 和渲染相关 + style.isOutlined = false; } - // shadow// both + // shadow // both let shadowComp = LabelShadow && comp.getComponent(LabelShadow); shadowComp = (shadowComp && shadowComp.enabled) ? shadowComp : null; if (shadowComp) { @@ -83,15 +83,15 @@ export const ttfUtils = { style.shadowOffsetX = shadowComp.offset.x; style.shadowOffsetY = shadowComp.offset.y; } else { - style.hasShadow = false; // 由于影响到了canvas 的宽度,所以和排版相关 //和渲染相关 + style.hasShadow = false; } - layout.horizontalAlign = comp.horizontalAlign; // render Only // 由于影响起始位置的计算,所以和排版相关 // 和渲染相关 - layout.verticalAlign = comp.verticalAlign; // render Only // 由于影响起始位置的计算,所以和排版相关 // 和渲染相关 + layout.horizontalAlign = comp.horizontalAlign; // both + layout.verticalAlign = comp.verticalAlign; // both - // node info // both // 怎么触发 dirty - outputLayoutData.nodeContentSize.width = outputLayoutData.canvasSize.width = trans.width; // 这儿的更新一定都会影响的 - outputLayoutData.nodeContentSize.height = outputLayoutData.canvasSize.height = trans.height; // 这儿的更新一定都会影响的 + // node info // both + outputLayoutData.nodeContentSize.width = outputLayoutData.canvasSize.width = trans.width; + outputLayoutData.nodeContentSize.height = outputLayoutData.canvasSize.height = trans.height; }, // render Only @@ -123,9 +123,9 @@ export const ttfUtils = { } }, - // 进行统一调用 updateLayoutData (comp: Label): void { - if (comp.layoutDirty) { + // Todo: dirtyFlag + if (comp.assemblerData) { const trans = comp.node._uiProps.uiTransformComp!; const processing = TextProcessing.instance; const style = comp.textStyle; @@ -134,7 +134,7 @@ export const ttfUtils = { style.fontScale = view.getScaleX(); this.updateLayoutProcessingData(style, layout, outputLayoutData, comp, trans); // use canvas in assemblerData // to do to optimize - processing.setCanvasUsed(comp.assemblerData!.canvas, comp.assemblerData!.context); + processing.setCanvasUsed(comp.assemblerData.canvas, comp.assemblerData.context); style.fontFamily = this._updateFontFamily(comp); // TextProcessing @@ -142,7 +142,6 @@ export const ttfUtils = { comp.actualFontSize = style.actualFontSize; trans.setContentSize(outputLayoutData.nodeContentSize); comp.contentWidth = outputLayoutData.nodeContentSize.width; - comp._resetLayoutDirty(); } }, @@ -150,8 +149,7 @@ export const ttfUtils = { if (!comp.renderData) { return; } if (comp.renderData.vertDirty) { - this.updateLayoutData(comp); // 需要注意的是要防止在两个函数中间被修改 // 但是这里的修改应该是不会影响到排版的 - + this.updateLayoutData(comp);// Todo: move to layout manager const processing = TextProcessing.instance; const style = comp.textStyle; const layout = comp.textLayout; @@ -162,6 +160,7 @@ export const ttfUtils = { this._resetDynamicAtlas(comp); + processing.setCanvasUsed(comp.assemblerData!.canvas, comp.assemblerData!.context); processing.generateRenderInfo(false, style, layout, outputLayoutData, outputRenderData, comp.string, this.generateVertexData); const renderData = comp.renderData; diff --git a/cocos/2d/components/label.ts b/cocos/2d/components/label.ts index 40bc3f02dca..adbc6cc8ad3 100644 --- a/cocos/2d/components/label.ts +++ b/cocos/2d/components/label.ts @@ -26,10 +26,10 @@ import { ccclass, help, executionOrder, menu, tooltip, displayOrder, visible, multiline, type, serializable, editable } from 'cc.decorator'; import { BYTEDANCE, EDITOR, JSB } from 'internal:constants'; import { minigame } from 'pal/minigame'; +import { cclegacy } from '@base/global'; import { BitmapFont, Font, SpriteFrame } from '../assets'; import { ImageAsset, Texture2D } from '../../asset/assets'; import { ccenum, Color } from '../../core'; -import { cclegacy } from '@base/global'; import { IBatcher } from '../renderer/i-batcher'; import { FontAtlas } from '../assets/bitmap-font'; import { CanvasPool, ISharedLabelData, LetterRenderTexture } from '../assembler/label/font-utils'; @@ -40,7 +40,6 @@ import { BlendFactor } from '../../gfx'; import { TextStyle } from '../assembler/label/text-style'; import { TextLayout } from '../assembler/label/text-layout'; import { TextOutputLayoutData, TextOutputRenderData } from '../assembler/label/text-output-data'; -import { TransformBit } from '../../scene-graph'; const tempColor = Color.WHITE.clone(); /** @@ -231,8 +230,6 @@ export class Label extends UIRenderer { } this._string = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -255,8 +252,6 @@ export class Label extends UIRenderer { } this._horizontalAlign = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -279,8 +274,6 @@ export class Label extends UIRenderer { } this._verticalAlign = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -316,8 +309,6 @@ export class Label extends UIRenderer { } this._fontSize = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -339,8 +330,6 @@ export class Label extends UIRenderer { } this._lineHeight = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -365,8 +354,6 @@ export class Label extends UIRenderer { } this._spacingX = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -389,8 +376,6 @@ export class Label extends UIRenderer { } this._overflow = value; - this._markLayoutDirty(); // 其实只影响 bm - this._markLayoutDirty(); // 其实只影响 bm this.markForUpdateRenderData(); } @@ -412,8 +397,6 @@ export class Label extends UIRenderer { } this._enableWrapText = value; - this._markLayoutDirty(); // 其实只影响 bm - this._markLayoutDirty(); // 其实只影响 bm this.markForUpdateRenderData(); } @@ -449,8 +432,6 @@ export class Label extends UIRenderer { this.font = null; } this._flushAssembler(); - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -473,8 +454,6 @@ export class Label extends UIRenderer { } this._fontFamily = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -513,11 +492,7 @@ export class Label extends UIRenderer { this.destroyRenderData(); this._fontAtlas = null; - this._markLayoutDirty(); - this.updateRenderData(true);//为了 flushAssembler this.updateRenderData(true); - this._markLayoutDirty(); - this.updateRenderData(true);//为了 flushAssembler } /** @@ -546,11 +521,7 @@ export class Label extends UIRenderer { } this._cacheMode = value; - this._markLayoutDirty(); - this.updateRenderData(true); //为了 flushAssembler this.updateRenderData(true); - this._markLayoutDirty(); - this.updateRenderData(true); //为了 flushAssembler } /** @@ -571,8 +542,6 @@ export class Label extends UIRenderer { } this._isBold = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -594,8 +563,6 @@ export class Label extends UIRenderer { } this._isItalic = value; - this._markLayoutDirty(); - this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -704,12 +671,6 @@ export class Label extends UIRenderer { get textLayoutData (): TextOutputLayoutData { return this._textLayoutData!; } - /** - * @engineInternal - */ - get layoutDirty (): boolean { - return this._layoutDirty; - } @serializable protected _string = 'label'; @@ -724,7 +685,7 @@ export class Label extends UIRenderer { @serializable protected _fontFamily = 'Arial'; @serializable - protected _lineHeight = 40;//实际上影响排版的位置,而不影响排版 + protected _lineHeight = 40; @serializable protected _overflow: Overflow = Overflow.NONE; @serializable @@ -736,13 +697,13 @@ export class Label extends UIRenderer { @serializable protected _spacingX = 0; @serializable - protected _isItalic = false;// 只会影响宽度 + protected _isItalic = false; @serializable - protected _isBold = false;// 只会影响宽度 + protected _isBold = false; @serializable - protected _isUnderline = false;// 不影响,但是要同步 + protected _isUnderline = false; @serializable - protected _underlineHeight = 2;// 不影响,但是要同步 + protected _underlineHeight = 2; @serializable protected _cacheMode = CacheMode.NONE; @@ -763,8 +724,6 @@ export class Label extends UIRenderer { protected _textRenderData: TextOutputRenderData | null = null; protected _textLayoutData: TextOutputLayoutData | null = null; - protected _layoutDirty = true; // 是否重新计算文本布局 - /** * @engineInternal */ @@ -844,7 +803,7 @@ export class Label extends UIRenderer { * @zh 更新渲染相关数据。 * @param force @en Whether to force an immediate update. @zh 是否立马强制更新渲染数据。 */ - public updateRenderData (force = false): void { // 此接口应当有限使用,仅有几种情况才使用,且其他情况也该直接 markDirty 即可 + public updateRenderData (force = false): void { if (force) { this._flushAssembler(); // Hack: Fixed the bug that richText wants to get the label length by _measureText, @@ -994,38 +953,6 @@ export class Label extends UIRenderer { } super._updateBlendFunc(); } - - /** - * @engineInternal - */ - public _markLayoutDirty (): void { - if (this._layoutDirty) return; - this._layoutDirty = true; - if (this.enabled) { - // 加入队列,统一进行更新 - // 在排版时进行 renderDirty 的触发 - // 还是在上层直接进行触发? - } - } - - /** - * @engineInternal - */ - public _resetLayoutDirty (): void { - this._layoutDirty = false; - } - - protected _nodeStateChange (transformType: TransformBit): void { - super._nodeStateChange(transformType); - this._markLayoutDirty(); - for (let i = 0; i < this.node.children.length; ++i) { - const child = this.node.children[i]; - const renderComp = child.getComponent(Label); - if (renderComp) { - renderComp._markLayoutDirty(); - } - } - } } cclegacy.Label = Label; diff --git a/cocos/2d/components/rich-text.ts b/cocos/2d/components/rich-text.ts index ff07bf62d01..2420483bd1e 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -25,11 +25,11 @@ import { ccclass, executeInEditMode, executionOrder, help, menu, tooltip, multiline, type, displayOrder, serializable } from 'cc.decorator'; import { DEBUG, DEV, EDITOR } from 'internal:constants'; +import { cclegacy } from '@base/global'; import { Font, SpriteAtlas, TTFFont, SpriteFrame } from '../assets'; import { EventTouch } from '../../input/types'; import { assert, warnID } from '@base/debug'; import { Color, Vec2, CCObject, js, Size } from '../../core'; -import { cclegacy } from '@base/global'; import { BASELINE_RATIO, fragmentText, isUnicodeCJK, isUnicodeSpace, getEnglishWordPartAtFirst, getEnglishWordPartAtLast } from '../utils/text-utils'; import { HtmlTextParser, IHtmlTextParserResultObj, IHtmlTextParserStack } from '../utils/html-text-parser'; import { Node } from '../../scene-graph'; @@ -1331,7 +1331,7 @@ export class RichText extends Component { label.useSystemFont = this._isSystemFontUsed; label.lineHeight = this._lineHeight; - label.updateRenderData(true);// 此处的强制更新是为了这个步骤之后马上要使用的宽度 + label.updateRenderData(true); } protected _applyLayer (): void {