From bf253707f9375d00fa0c829ea7855354146a483a Mon Sep 17 00:00:00 2001 From: ChiaNing Date: Thu, 27 Jul 2023 14:52:39 +0800 Subject: [PATCH 1/4] label process string add --- cocos/2d/assembler/label/bmfont.ts | 62 ---------- cocos/2d/assembler/label/bmfontUtils.ts | 123 +++++++++++++------- cocos/2d/assembler/label/text-processing.ts | 106 +++++++++++++---- cocos/2d/assembler/label/ttfUtils.ts | 101 +++++++++------- cocos/2d/components/label.ts | 85 +++++++++++++- cocos/2d/components/rich-text.ts | 16 ++- 6 files changed, 314 insertions(+), 179 deletions(-) diff --git a/cocos/2d/assembler/label/bmfont.ts b/cocos/2d/assembler/label/bmfont.ts index 78431f155a0..8865b66878b 100644 --- a/cocos/2d/assembler/label/bmfont.ts +++ b/cocos/2d/assembler/label/bmfont.ts @@ -50,68 +50,6 @@ export const bmfont: IAssembler = { // Fill All fillMeshVertices3D(node, renderer, comp.renderData!, tempColor); }, - - appendQuad (comp: Label, spriteFrame: SpriteFrame, rect: Rect, rotated: boolean, x: number, y: number, scale: number) { - const renderData = comp.renderData; - if (!renderData) { - return; - } - - const dataOffset = renderData.dataLength; - - renderData.dataLength += 4; - renderData.resize(renderData.dataLength, renderData.dataLength / 2 * 3); - - const dataList = renderData.data; - const texW = spriteFrame.width; - const texH = spriteFrame.height; - - const rectWidth = rect.width; - const rectHeight = rect.height; - - let l = 0; - let b = 0; - let t = 0; - let r = 0; - if (!rotated) { - l = (rect.x) / texW; - r = (rect.x + rectWidth) / texW; - b = (rect.y + rectHeight) / texH; - t = (rect.y) / texH; - - dataList[dataOffset].u = l; - dataList[dataOffset].v = b; - dataList[dataOffset + 1].u = r; - dataList[dataOffset + 1].v = b; - dataList[dataOffset + 2].u = l; - dataList[dataOffset + 2].v = t; - dataList[dataOffset + 3].u = r; - dataList[dataOffset + 3].v = t; - } else { - l = (rect.x) / texW; - r = (rect.x + rectHeight) / texW; - b = (rect.y + rectWidth) / texH; - t = (rect.y) / texH; - - dataList[dataOffset].u = l; - dataList[dataOffset].v = t; - dataList[dataOffset + 1].u = l; - dataList[dataOffset + 1].v = b; - dataList[dataOffset + 2].u = r; - dataList[dataOffset + 2].v = t; - dataList[dataOffset + 3].u = r; - dataList[dataOffset + 3].v = b; - } - - dataList[dataOffset].x = x; - dataList[dataOffset].y = y - rectHeight * scale; - dataList[dataOffset + 1].x = x + rectWidth * scale; - dataList[dataOffset + 1].y = y - rectHeight * scale; - dataList[dataOffset + 2].x = x; - dataList[dataOffset + 2].y = y; - dataList[dataOffset + 3].x = x + rectWidth * scale; - dataList[dataOffset + 3].y = y; - }, }; js.addon(bmfont, bmfontUtils); diff --git a/cocos/2d/assembler/label/bmfontUtils.ts b/cocos/2d/assembler/label/bmfontUtils.ts index 6a49f279eb9..0d98a2efb51 100644 --- a/cocos/2d/assembler/label/bmfontUtils.ts +++ b/cocos/2d/assembler/label/bmfontUtils.ts @@ -26,7 +26,7 @@ import { JSB } from 'internal:constants'; import { error } from '@base/debug'; import { IConfig, FontAtlas } from '../../assets/bitmap-font'; import { SpriteFrame } from '../../assets/sprite-frame'; -import { Rect } from '../../../core'; +import { Rect, Vec2 } from '../../../core'; import { Label, Overflow, CacheMode } from '../../components/label'; import { UITransform } from '../../framework/ui-transform'; import { LetterAtlas, shareLabelInfo } from './font-utils'; @@ -40,34 +40,35 @@ import { view } from '../../../ui/view'; const _defaultLetterAtlas = new LetterAtlas(64, 64); const _defaultFontAtlas = new FontAtlas(null); -let _comp: Label | null = null; -let _uiTrans: UITransform | null = null; - let _fntConfig: IConfig | null = null; let _spriteFrame: SpriteFrame|null = null; let QUAD_INDICES; export const bmfontUtils = { - updateProcessingData (style: TextStyle, layout: TextLayout, - outputLayoutData: TextOutputLayoutData, outputRenderData: TextOutputRenderData, - comp: Label, trans: UITransform): void { - style.fontSize = comp.fontSize; - style.actualFontSize = comp.fontSize; - style.originFontSize = _fntConfig ? _fntConfig.fontSize : comp.fontSize; - layout.horizontalAlign = comp.horizontalAlign; - layout.verticalAlign = comp.verticalAlign; - layout.spacingX = comp.spacingX; + updateLayoutProcessingData ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + comp: Label, + trans: UITransform, + ): 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 const overflow = comp.overflow; - layout.overFlow = overflow; - layout.lineHeight = comp.lineHeight; outputLayoutData.nodeContentSize.width = trans.width; outputLayoutData.nodeContentSize.height = trans.height; + layout.overFlow = overflow; // both + layout.lineHeight = comp.lineHeight; // both // should wrap text if (overflow === Overflow.NONE) { - layout.wrapping = false; + layout.wrapping = false; // both outputLayoutData.nodeContentSize.width += shareLabelInfo.margin * 2; outputLayoutData.nodeContentSize.height += shareLabelInfo.margin * 2; } else if (overflow === Overflow.RESIZE_HEIGHT) { @@ -76,40 +77,41 @@ export const bmfontUtils = { } else { layout.wrapping = comp.enableWrapText; } - outputRenderData.uiTransAnchorX = trans.anchorX; - outputRenderData.uiTransAnchorY = trans.anchorY; shareLabelInfo.lineHeight = comp.lineHeight; shareLabelInfo.fontSize = comp.fontSize; - style.spriteFrame = _spriteFrame; - style.fntConfig = _fntConfig; - style.fontFamily = shareLabelInfo.fontFamily; - - style.color.set(comp.color); + style.fntConfig = _fntConfig; // layout only + style.fontFamily = shareLabelInfo.fontFamily; // layout only }, - updateRenderData (comp: Label): void { - if (!comp.renderData) { - return; - } - - if (_comp === comp) { return; } - - if (comp.renderData.vertDirty) { - _comp = comp; - _uiTrans = _comp.node._uiProps.uiTransformComp!; - const renderData = comp.renderData; + // render Only + updateRenderProcessingData ( + style: TextStyle, + outputRenderData: TextOutputRenderData, + comp: Label, + anchor: Readonly, + ): void { + // render info + outputRenderData.uiTransAnchorX = anchor.x; + outputRenderData.uiTransAnchorY = anchor.y; + + style.spriteFrame = _spriteFrame; // render only + 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; - const outputRenderData = comp.textRenderData; style.fontScale = view.getScaleX(); this._updateFontFamily(comp); - this.updateProcessingData(style, layout, outputLayoutData, outputRenderData, comp, _uiTrans); + this.updateLayoutProcessingData(style, layout, outputLayoutData, comp, trans); this._updateLabelInfo(comp); @@ -117,11 +119,40 @@ export const bmfontUtils = { // TextProcessing processing.processingString(true, style, layout, outputLayoutData, comp.string); + + comp.actualFontSize = style.actualFontSize; + trans.setContentSize(outputLayoutData.nodeContentSize); + } + }, + + updateRenderData (comp: Label): void { + if (!comp.renderData) { + return; + } + + if (comp.renderData.vertDirty) { + this.updateLayoutData(comp);// 需要注意的是要防止在两个函数中间被修改 // 但是这里的修改应该是不会影响到排版的 + const renderData = comp.renderData; + const processing = TextProcessing.instance; + const style = comp.textStyle; + const layout = comp.textLayout; + const outputLayoutData = comp.textLayoutData; + const outputRenderData = comp.textRenderData; + const anchor = comp.node._uiProps.uiTransformComp!.anchorPoint; + this.updateRenderProcessingData(style, outputRenderData, comp, anchor); + // generateVertex this.resetRenderData(comp); outputRenderData.quadCount = 0; - processing.generateRenderInfo(true, style, layout, outputLayoutData, outputRenderData, - comp.string, this.generateVertexData); + processing.generateRenderInfo( + true, + style, + layout, + outputLayoutData, + outputRenderData, + comp.string, + this.generateVertexData, + ); renderData.dataLength = outputRenderData.quadCount; renderData.resize(renderData.dataLength, renderData.dataLength / 2 * 3); @@ -134,13 +165,10 @@ export const bmfontUtils = { this.createQuadIndices(indexCount); renderData.chunk.setIndexBuffer(QUAD_INDICES); - _comp.actualFontSize = style.actualFontSize; - _uiTrans.setContentSize(outputLayoutData.nodeContentSize); this.updateUVs(comp);// dirty need this.updateColor(comp); // dirty need renderData.vertDirty = false; - _comp = null; this._resetProperties(); } @@ -195,8 +223,17 @@ export const bmfontUtils = { }, // callBack function - generateVertexData (style: TextStyle, outputLayoutData: TextOutputLayoutData, outputRenderData: TextOutputRenderData, offset: number, - spriteFrame: SpriteFrame, rect: Rect, rotated: boolean, x: number, y: number): void { + generateVertexData ( + style: TextStyle, + outputLayoutData: TextOutputLayoutData, + outputRenderData: TextOutputRenderData, + offset: number, + spriteFrame: SpriteFrame, + rect: Rect, + rotated: boolean, + x: number, + y: number, + ): void { const dataOffset = offset; const scale = style.bmfontScale; diff --git a/cocos/2d/assembler/label/text-processing.ts b/cocos/2d/assembler/label/text-processing.ts index 7aaab7f0d4d..c34f6cc2c32 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -78,8 +78,14 @@ export class TextProcessing { this._lettersInfo.length = 0; } - public processingString (isBmFont: boolean, style: TextStyle, layout: TextLayout, - outputLayoutData: TextOutputLayoutData, inputString: string, out?: string[]): void { + public processingString ( + isBmFont: boolean, + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, + out?: string[], + ): void { if (!isBmFont) { let loopTime = 0; this._fontScale = this._getStyleFontScale(style.fontSize, style.fontScale); @@ -118,8 +124,15 @@ export class TextProcessing { } } - public generateRenderInfo (isBmFont: boolean, style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - outputRenderData: TextOutputRenderData, inputString: string, callback: AnyFunction): void { + public generateRenderInfo ( + isBmFont: boolean, + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + outputRenderData: TextOutputRenderData, + inputString: string, + callback: AnyFunction, + ): void { if (!isBmFont) { this._updateLabelDimensions(style, layout, outputLayoutData); this._updateTexture(style, layout, outputLayoutData, outputRenderData); @@ -158,8 +171,13 @@ export class TextProcessing { return scale; } - private _calculateLabelFont (style: TextStyle, layout: TextLayout, - outputLayoutData: TextOutputLayoutData, inputString: string): void { + // processingString + private _calculateLabelFont ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, + ): void { if (!this._context) { return; } @@ -292,10 +310,12 @@ export class TextProcessing { totalHeight = 0; for (i = 0; i < paragraphedStrings.length; ++i) { const allWidth = safeMeasureText(this._context, paragraphedStrings[i], _fontDesc); - textFragment = fragmentText(paragraphedStrings[i], + textFragment = fragmentText( + paragraphedStrings[i], allWidth, canvasWidthNoMargin, - this._measureText(this._context, _fontDesc)); + this._measureText(this._context, _fontDesc), + ); totalHeight += textFragment.length * lineHeight; } @@ -342,10 +362,12 @@ export class TextProcessing { this._context.font = _fontDesc; for (let i = 0; i < paragraphedStrings.length; ++i) { const allWidth = safeMeasureText(this._context, paragraphedStrings[i], _fontDesc); - const textFragment = fragmentText(paragraphedStrings[i], + const textFragment = fragmentText( + paragraphedStrings[i], allWidth, canvasWidthNoMargin, - this._measureText(this._context, _fontDesc)); + this._measureText(this._context, _fontDesc), + ); _splitStrings = _splitStrings.concat(textFragment); } outputLayoutData.parsedString = _splitStrings; @@ -367,6 +389,7 @@ export class TextProcessing { return paragraphLength; } + // processingString private _updatePaddingRect (style: TextStyle, outputLayoutData: TextOutputLayoutData): void { let top = 0; let bottom = 0; let left = 0; let right = 0; let outlineWidth = 0; @@ -588,8 +611,15 @@ export class TextProcessing { // -------------------- Render Processing Part -------------------------- - private generateVertexData (isBmFont: boolean, style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - outputRenderData: TextOutputRenderData, inputString: string, callback: AnyFunction): void { + private generateVertexData ( + isBmFont: boolean, + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + outputRenderData: TextOutputRenderData, + inputString: string, + callback: AnyFunction, + ): void { if (!isBmFont) { this.updateQuatCount(outputRenderData); // update vbBuffer count callback(style, outputLayoutData, outputRenderData); @@ -620,6 +650,7 @@ export class TextProcessing { // -------------------- Canvas Mode Part --------------------------- // -------------------- Multiple Quad Mode Part -------------------- + // processingString private _setupBMFontOverflowMetrics (layout: TextLayout, outputLayoutData: TextOutputLayoutData): void { let newWidth = outputLayoutData.nodeContentSize.width; let newHeight = outputLayoutData.nodeContentSize.height; @@ -669,6 +700,7 @@ export class TextProcessing { } } + // processingString private _alignText (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, inputString: string): void { this._multilineTextWrap(style, layout, outputLayoutData, inputString, this._getFirstWordLen); @@ -703,8 +735,14 @@ export class TextProcessing { outputLayoutData.parsedString = _splitStrings; } - private _multilineTextWrap (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - inputString: string, nextTokenFunc: (arg0: TextStyle, arg1: TextLayout, arg2: string, arg3: number, arg4: number) => number): boolean { + // processingString + private _multilineTextWrap ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, + nextTokenFunc: (arg0: TextStyle, arg1: TextLayout, arg2: string, arg3: number, arg4: number) => number, + ): boolean { layout.linesWidth.length = 0; const _string = inputString; @@ -871,6 +909,7 @@ export class TextProcessing { this._lettersInfo[letterIndex].y = letterPosition.y; } + // processingString private _getFirstWordLen (style: TextStyle, layout: TextLayout, text: string, startIndex: number, textLen: number): number { let character = text.charAt(startIndex); if (isUnicodeCJK(character) @@ -955,8 +994,13 @@ export class TextProcessing { return layout.overFlow === Overflow.SHRINK ? style.bmfontScale : 1; } - private _isVerticalClamp (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - inputString: string, process: TextProcessing): boolean { + private _isVerticalClamp ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, + process: TextProcessing, + ): boolean { if (layout.textDesiredHeight > outputLayoutData.nodeContentSize.height) { return true; } else { @@ -964,8 +1008,13 @@ export class TextProcessing { } } - private _isHorizontalClamp (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - inputString: string, process: TextProcessing): boolean { + private _isHorizontalClamp ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, + process: TextProcessing, + ): boolean { let letterClamp = false; const _string = inputString; for (let ctr = 0, l = _string.length; ctr < l; ++ctr) { @@ -1007,9 +1056,15 @@ export class TextProcessing { return false; } - private _shrinkLabelToContentSize (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, inputString: string, + // processingString + private _shrinkLabelToContentSize ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + inputString: string, lambda: (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - inputString: string, process: TextProcessing) => boolean): void { + inputString: string, process: TextProcessing) => boolean, + ): void { const fontSize = style.actualFontSize; let left = 0; @@ -1041,6 +1096,7 @@ export class TextProcessing { } } + // processingString private _scaleFontSizeDown (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, inputString: string, fontSize: number): void { let shouldUpdateContent = true; if (!fontSize) { @@ -1055,8 +1111,14 @@ export class TextProcessing { } } - private _updateQuads (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - outputRenderData: TextOutputRenderData, inputString: string, callback): boolean { + private _updateQuads ( + style: TextStyle, + layout: TextLayout, + outputLayoutData: TextOutputLayoutData, + outputRenderData: TextOutputRenderData, + inputString: string, + callback, + ): boolean { const texture = style.spriteFrame ? style.spriteFrame.texture : shareLabelInfo.fontAtlas!.getTexture(); const appX = outputRenderData.uiTransAnchorX * outputLayoutData.nodeContentSize.width; diff --git a/cocos/2d/assembler/label/ttfUtils.ts b/cocos/2d/assembler/label/ttfUtils.ts index c4ff09c4aa0..ef6773c35ab 100644 --- a/cocos/2d/assembler/label/ttfUtils.ts +++ b/cocos/2d/assembler/label/ttfUtils.ts @@ -31,6 +31,7 @@ import { TextStyle } from './text-style'; import { TextLayout } from './text-layout'; import { view } from '../../../ui/view'; import { approx } from '../../../core'; +import { Vec2 } from '../../../core/math'; const Overflow = Label.Overflow; @@ -45,28 +46,23 @@ export const ttfUtils = { trans: UITransform, ): void { // font info // both - style.isSystemFontUsed = comp.useSystemFont; - style.fontSize = comp.fontSize; + style.isSystemFontUsed = comp.useSystemFont; // 都会影响 + style.fontSize = comp.fontSize; // 都会影响 - // node info // both - outputLayoutData.nodeContentSize.width = outputLayoutData.canvasSize.width = trans.width; - outputLayoutData.nodeContentSize.height = outputLayoutData.canvasSize.height = trans.height; // layout info - layout.lineHeight = comp.lineHeight; // both - layout.overFlow = comp.overflow; // layout only // but change render + layout.lineHeight = comp.lineHeight; // both // 都影响 + layout.overFlow = comp.overflow; // layout only // but change render // 在 bmfont 里会和渲染相关,ttf 不会 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 + layout.wrapping = comp.enableWrapText; // layout only // but change render // 在 bmfont 里会和渲染相关,ttf 不会 } // effect info // both - style.isBold = comp.isBold; - style.isItalic = comp.isItalic; - style.isUnderline = comp.isUnderline; - style.underlineHeight = comp.underlineHeight; + style.isBold = comp.isBold; // 可能会影响到 context 的测量,所以和排版相关 // 和渲染相关 + style.isItalic = comp.isItalic; // 可能会影响到 context 的测量,所以和排版相关 // 和渲染相关 // outline// both const isOutlined = comp.enableOutline && comp.outlineWidth > 0; @@ -75,7 +71,7 @@ export const ttfUtils = { style.outlineColor.set(comp.outlineColor); style.outlineWidth = comp.outlineWidth; } else { - style.isOutlined = false; + style.isOutlined = false; // 由于影响到了canvas 的宽度,所以和排版相关 // 和渲染相关 } // shadow// both @@ -87,17 +83,32 @@ export const ttfUtils = { style.shadowOffsetX = comp.shadowOffset.x; style.shadowOffsetY = comp.shadowOffset.y; } else { - style.hasShadow = false; + style.hasShadow = false; // 由于影响到了canvas 的宽度,所以和排版相关 //和渲染相关 } - // render info - style.color.set(comp.color);// may opacity bug // render Only - outputRenderData.texture = comp.spriteFrame; // render Only - outputRenderData.uiTransAnchorX = trans.anchorX; // render Only - outputRenderData.uiTransAnchorY = trans.anchorY; // render Only + layout.horizontalAlign = comp.horizontalAlign; // render Only // 由于影响起始位置的计算,所以和排版相关 // 和渲染相关 + layout.verticalAlign = comp.verticalAlign; // render Only // 由于影响起始位置的计算,所以和排版相关 // 和渲染相关 - layout.horizontalAlign = comp.horizontalAlign; // render Only - layout.verticalAlign = comp.verticalAlign; // render Only + // node info // both // 怎么触发 dirty + outputLayoutData.nodeContentSize.width = outputLayoutData.canvasSize.width = trans.width; // 这儿的更新一定都会影响的 + outputLayoutData.nodeContentSize.height = outputLayoutData.canvasSize.height = trans.height; // 这儿的更新一定都会影响的 + }, + + // render Only + updateRenderProcessingData ( + style: TextStyle, + outputRenderData: TextOutputRenderData, + comp: Label, + anchor: Readonly, + ): void { + style.isUnderline = comp.isUnderline; + style.underlineHeight = comp.underlineHeight; + + // render info + style.color.set(comp.color); + outputRenderData.texture = comp.spriteFrame; + outputRenderData.uiTransAnchorX = anchor.x; + outputRenderData.uiTransAnchorY = anchor.y; }, getAssemblerData (): ISharedLabelData { @@ -112,42 +123,51 @@ export const ttfUtils = { } }, - updateRenderData (comp: Label): void { - if (!comp.renderData) { return; } - - if (comp.renderData.vertDirty) { + // 进行统一调用 + 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; - const outputRenderData = comp.textRenderData; style.fontScale = view.getScaleX(); - this.updateProcessingData(style, layout, outputLayoutData, outputRenderData, comp, trans); + this.updateLayoutProcessingData(style, layout, outputLayoutData, comp, trans); // use canvas in assemblerData // to do to optimize processing.setCanvasUsed(comp.assemblerData!.canvas, comp.assemblerData!.context); style.fontFamily = this._updateFontFamily(comp); - this._resetDynamicAtlas(comp); // TextProcessing processing.processingString(false, style, layout, outputLayoutData, comp.string); - processing.generateRenderInfo( - false, - style, - layout, - outputLayoutData, - outputRenderData, - comp.string, - this.generateVertexData, - ); + comp.actualFontSize = style.actualFontSize; + trans.setContentSize(outputLayoutData.nodeContentSize); + comp.contentWidth = outputLayoutData.nodeContentSize.width; + comp._resetLayoutDirty(); + } + }, + + updateRenderData (comp: Label): void { + if (!comp.renderData) { return; } + + if (comp.renderData.vertDirty) { + this.updateLayoutData(comp); // 需要注意的是要防止在两个函数中间被修改 // 但是这里的修改应该是不会影响到排版的 + + const processing = TextProcessing.instance; + const style = comp.textStyle; + const layout = comp.textLayout; + const outputLayoutData = comp.textLayoutData; + const outputRenderData = comp.textRenderData; + const anchor = comp.node._uiProps.uiTransformComp!.anchorPoint; + this.updateRenderProcessingData(style, outputRenderData, comp, anchor); + + this._resetDynamicAtlas(comp); + + processing.generateRenderInfo(false, style, layout, outputLayoutData, outputRenderData, comp.string, this.generateVertexData); const renderData = comp.renderData; renderData.textureDirty = true; this._calDynamicAtlas(comp, outputLayoutData); - comp.actualFontSize = style.actualFontSize; - trans.setContentSize(outputLayoutData.nodeContentSize); - const datalist = renderData.data; datalist[0] = outputRenderData.vertexBuffer[0]; datalist[1] = outputRenderData.vertexBuffer[1]; @@ -156,7 +176,6 @@ export const ttfUtils = { this.updateUVs(comp); comp.renderData.vertDirty = false; - comp.contentWidth = outputLayoutData.nodeContentSize.width; } if (comp.spriteFrame) { diff --git a/cocos/2d/components/label.ts b/cocos/2d/components/label.ts index 7f1c3c8ff29..f9095d59052 100644 --- a/cocos/2d/components/label.ts +++ b/cocos/2d/components/label.ts @@ -40,6 +40,7 @@ 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(); /** @@ -230,6 +231,8 @@ export class Label extends UIRenderer { } this._string = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -252,6 +255,8 @@ export class Label extends UIRenderer { } this._horizontalAlign = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -274,6 +279,8 @@ export class Label extends UIRenderer { } this._verticalAlign = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -309,6 +316,8 @@ export class Label extends UIRenderer { } this._fontSize = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -330,6 +339,8 @@ export class Label extends UIRenderer { } this._lineHeight = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -354,6 +365,8 @@ export class Label extends UIRenderer { } this._spacingX = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -376,6 +389,8 @@ export class Label extends UIRenderer { } this._overflow = value; + this._markLayoutDirty(); // 其实只影响 bm + this._markLayoutDirty(); // 其实只影响 bm this.markForUpdateRenderData(); } @@ -397,6 +412,8 @@ export class Label extends UIRenderer { } this._enableWrapText = value; + this._markLayoutDirty(); // 其实只影响 bm + this._markLayoutDirty(); // 其实只影响 bm this.markForUpdateRenderData(); } @@ -432,6 +449,8 @@ export class Label extends UIRenderer { this.font = null; } this._flushAssembler(); + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -454,6 +473,8 @@ export class Label extends UIRenderer { } this._fontFamily = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -492,7 +513,11 @@ 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 } /** @@ -521,7 +546,11 @@ export class Label extends UIRenderer { } this._cacheMode = value; + this._markLayoutDirty(); + this.updateRenderData(true); //为了 flushAssembler this.updateRenderData(true); + this._markLayoutDirty(); + this.updateRenderData(true); //为了 flushAssembler } /** @@ -542,6 +571,8 @@ export class Label extends UIRenderer { } this._isBold = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -563,6 +594,8 @@ export class Label extends UIRenderer { } this._isItalic = value; + this._markLayoutDirty(); + this._markLayoutDirty(); this.markForUpdateRenderData(); } @@ -808,6 +841,12 @@ export class Label extends UIRenderer { get textLayoutData (): TextOutputLayoutData { return this._textLayoutData!; } + /** + * @engineInternal + */ + get layoutDirty (): boolean { + return this._layoutDirty; + } @serializable protected _string = 'label'; @@ -822,7 +861,7 @@ export class Label extends UIRenderer { @serializable protected _fontFamily = 'Arial'; @serializable - protected _lineHeight = 40; + protected _lineHeight = 40;//实际上影响排版的位置,而不影响排版 @serializable protected _overflow: Overflow = Overflow.NONE; @serializable @@ -834,13 +873,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; @serializable @@ -875,6 +914,8 @@ export class Label extends UIRenderer { protected _textRenderData: TextOutputRenderData | null = null; protected _textLayoutData: TextOutputLayoutData | null = null; + protected _layoutDirty = true; // 是否重新计算文本布局 + /** * @engineInternal */ @@ -954,7 +995,7 @@ export class Label extends UIRenderer { * @zh 更新渲染相关数据。 * @param force @en Whether to force an immediate update. @zh 是否立马强制更新渲染数据。 */ - public updateRenderData (force = false): void { + public updateRenderData (force = false): void { // 此接口应当有限使用,仅有几种情况才使用,且其他情况也该直接 markDirty 即可 if (force) { this._flushAssembler(); // Hack: Fixed the bug that richText wants to get the label length by _measureText, @@ -1104,6 +1145,38 @@ 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 c0ed9d7cc9e..e5dddb89798 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -897,8 +897,12 @@ export class RichText extends Component { } } if (fragmentWidth > this._maxWidth) { - const fragments = fragmentText(labelString, fragmentWidth, this._maxWidth, - this._measureText(styleIndex) as unknown as (s: string) => number); + const fragments = fragmentText( + labelString, + fragmentWidth, + this._maxWidth, + this._measureText(styleIndex) as unknown as (s: string) => number, + ); for (let k = 0; k < fragments.length; ++k) { const splitString = fragments[k]; labelSegment = this._addLabelSegment(splitString, styleIndex); @@ -1203,9 +1207,11 @@ export class RichText extends Component { } const pos = segment.node.position; - segment.node.setPosition(nextTokenX + lineOffsetX, + segment.node.setPosition( + nextTokenX + lineOffsetX, this._lineHeight * (totalLineCount - lineCount) - this._labelHeight * anchorY, - pos.z); + pos.z, + ); if (lineCount === nextLineIndex) { nextTokenX += segment.node._uiProps.uiTransformComp!.width; @@ -1325,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 { From 6814c3340aa683e1a6d04486fd75cf6d76fb8580 Mon Sep 17 00:00:00 2001 From: ChiaNing Date: Wed, 20 Sep 2023 17:10:15 +0800 Subject: [PATCH 2/4] remove shared label info --- cocos/2d/assembler/label/bmfontUtils.ts | 97 ++++++-------------- cocos/2d/assembler/label/font-utils.ts | 68 +++++--------- cocos/2d/assembler/label/letter-font.ts | 93 +++++++++++++------ cocos/2d/assembler/label/text-output-data.ts | 5 +- cocos/2d/assembler/label/text-processing.ts | 96 +++++++++---------- cocos/2d/assembler/label/text-style.ts | 5 +- cocos/2d/assembler/label/ttfUtils.ts | 39 ++++---- cocos/2d/components/label.ts | 85 ++--------------- cocos/2d/components/rich-text.ts | 2 +- 9 files changed, 199 insertions(+), 291 deletions(-) diff --git a/cocos/2d/assembler/label/bmfontUtils.ts b/cocos/2d/assembler/label/bmfontUtils.ts index 0d98a2efb51..63af5c678ba 100644 --- a/cocos/2d/assembler/label/bmfontUtils.ts +++ b/cocos/2d/assembler/label/bmfontUtils.ts @@ -24,12 +24,11 @@ import { JSB } from 'internal:constants'; import { error } from '@base/debug'; -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 { 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 251848eed96..e074a54bbf6 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -31,6 +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 { 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 c189b39fb2a..5b5ddf0cb54 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, LetterRenderTexture } 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() as LetterRenderTexture | null; }, - _updateFontFamily (comp) { - shareLabelInfo.fontAtlas = _shareAtlas; - shareLabelInfo.fontFamily = this._getFontFamily(comp); - - // outline - const isOutlined = comp.enableOutline && comp.outlineWidth > 0; - if (isOutlined) { - shareLabelInfo.isOutlined = true; - shareLabelInfo.margin = comp.outlineWidth; - shareLabelInfo.out = comp.outlineColor.clone(); - shareLabelInfo.out.a = comp.outlineColor.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 c34f6cc2c32..c3b1117b057 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -26,12 +26,12 @@ import { cclegacy } from '@base/global'; import { log, logID, warn } from '@base/debug'; import { Texture2D } from '../../../asset/assets'; import { WrapMode } from '../../../asset/assets/asset-enum'; -import { Color, Pool, Rect, Vec2 } from '../../../core'; +import { Color, Rect, Vec2 } from '../../../core'; 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 ef6773c35ab..18cd5416d56 100644 --- a/cocos/2d/assembler/label/ttfUtils.ts +++ b/cocos/2d/assembler/label/ttfUtils.ts @@ -46,23 +46,23 @@ 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 const isOutlined = comp.enableOutline && comp.outlineWidth > 0; @@ -71,7 +71,7 @@ export const ttfUtils = { style.outlineColor.set(comp.outlineColor); style.outlineWidth = comp.outlineWidth; } else { - style.isOutlined = false; // 由于影响到了canvas 的宽度,所以和排版相关 // 和渲染相关 + style.isOutlined = false; } // shadow// both @@ -83,15 +83,15 @@ export const ttfUtils = { style.shadowOffsetX = comp.shadowOffset.x; style.shadowOffsetY = comp.shadowOffset.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 f9095d59052..7f1c3c8ff29 100644 --- a/cocos/2d/components/label.ts +++ b/cocos/2d/components/label.ts @@ -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(); } @@ -841,12 +808,6 @@ export class Label extends UIRenderer { get textLayoutData (): TextOutputLayoutData { return this._textLayoutData!; } - /** - * @engineInternal - */ - get layoutDirty (): boolean { - return this._layoutDirty; - } @serializable protected _string = 'label'; @@ -861,7 +822,7 @@ export class Label extends UIRenderer { @serializable protected _fontFamily = 'Arial'; @serializable - protected _lineHeight = 40;//实际上影响排版的位置,而不影响排版 + protected _lineHeight = 40; @serializable protected _overflow: Overflow = Overflow.NONE; @serializable @@ -873,13 +834,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; @serializable @@ -914,8 +875,6 @@ export class Label extends UIRenderer { protected _textRenderData: TextOutputRenderData | null = null; protected _textLayoutData: TextOutputLayoutData | null = null; - protected _layoutDirty = true; // 是否重新计算文本布局 - /** * @engineInternal */ @@ -995,7 +954,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, @@ -1145,38 +1104,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 e5dddb89798..3f85b879a3e 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -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 { From 24fa515040451a8a4946bbf93e71227c26654ebd Mon Sep 17 00:00:00 2001 From: ChiaNing Date: Fri, 22 Sep 2023 14:28:22 +0800 Subject: [PATCH 3/4] after rebase --- cocos/2d/assembler/label/font-utils.ts | 1 + cocos/2d/assembler/label/letter-font.ts | 14 +++++++------- cocos/2d/assembler/label/text-processing.ts | 2 +- cocos/2d/assembler/label/ttfUtils.ts | 3 +-- cocos/2d/components/rich-text.ts | 1 + 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cocos/2d/assembler/label/font-utils.ts b/cocos/2d/assembler/label/font-utils.ts index e074a54bbf6..c6f90c2692c 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -24,6 +24,7 @@ import { warn, warnID } from '@base/debug'; import { ccwindow } from '@base/global'; +import { warn, warnID } from '@base/debug'; import { FontAtlas } from '../../assets/bitmap-font'; import { Color, macro, ImageData } from '../../../core'; import { ImageAsset, Texture2D } from '../../../asset/assets'; diff --git a/cocos/2d/assembler/label/letter-font.ts b/cocos/2d/assembler/label/letter-font.ts index 5b5ddf0cb54..5a226450b62 100644 --- a/cocos/2d/assembler/label/letter-font.ts +++ b/cocos/2d/assembler/label/letter-font.ts @@ -42,7 +42,7 @@ export const letterFont = js.mixin(bmfontUtils, { _shareAtlas = new LetterAtlas(_atlasWidth, _atlasHeight); } - return _shareAtlas.getTexture() as LetterRenderTexture | null; + return _shareAtlas.getTexture(); }, _getFontFamily (comp: Label) { @@ -89,13 +89,13 @@ export const letterFont = js.mixin(bmfontUtils, { // outline let margin = 0; - const outline = comp.getComponent(LabelOutline); - if (outline && outline.enabled) { + const isOutlined = comp.enableOutline && comp.outlineWidth > 0; + if (isOutlined) { 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; + margin = comp.outlineWidth; + style.outlineWidth = comp.outlineWidth; + style.outlineColor = comp.outlineColor.clone(); + style.outlineColor.a = comp.outlineColor.a * comp.color.a / 255.0; } else { style.outlineWidth = 0; style.isOutlined = false; diff --git a/cocos/2d/assembler/label/text-processing.ts b/cocos/2d/assembler/label/text-processing.ts index c3b1117b057..8b4d95d74b8 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -741,7 +741,7 @@ export class TextProcessing { layout: TextLayout, outputLayoutData: TextOutputLayoutData, inputString: string, - nextTokenFunc: (arg0: TextStyle, arg1: TextLayout, arg2: string, arg3: number, arg4: number) => number, + nextTokenFunc: (arg0: TextStyle, arg1: TextLayout, arg2: string, arg3: number, arg4: number, arg5: number) => number, ): boolean { layout.linesWidth.length = 0; diff --git a/cocos/2d/assembler/label/ttfUtils.ts b/cocos/2d/assembler/label/ttfUtils.ts index 18cd5416d56..67655b67c41 100644 --- a/cocos/2d/assembler/label/ttfUtils.ts +++ b/cocos/2d/assembler/label/ttfUtils.ts @@ -37,11 +37,10 @@ const Overflow = Label.Overflow; export const ttfUtils = { - updateProcessingData ( + updateLayoutProcessingData ( style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData, - outputRenderData: TextOutputRenderData, comp: Label, trans: UITransform, ): void { diff --git a/cocos/2d/components/rich-text.ts b/cocos/2d/components/rich-text.ts index 3f85b879a3e..6ae3c6095b0 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -27,6 +27,7 @@ import { ccclass, executeInEditMode, executionOrder, help, menu, tooltip, multil import { DEBUG, DEV, EDITOR } from 'internal:constants'; import { assert, warnID } from '@base/debug'; import { cclegacy } from '@base/global'; +import { assert, warnID } from '@base/debug'; import { Font, SpriteAtlas, TTFFont, SpriteFrame } from '../assets'; import { EventTouch } from '../../input/types'; import { Color, Vec2, CCObject, js, Size } from '../../core'; From 1ea3c75721c72d24bf8fcb52bd0ebf98d707198c Mon Sep 17 00:00:00 2001 From: ChiaNing Date: Mon, 25 Sep 2023 11:12:10 +0800 Subject: [PATCH 4/4] fix rebase --- cocos/2d/assembler/label/bmfontUtils.ts | 4 +-- cocos/2d/assembler/label/font-utils.ts | 1 - cocos/2d/assembler/label/letter-font.ts | 5 ++-- cocos/2d/assembler/label/text-processing.ts | 30 ++++++++++----------- cocos/2d/components/rich-text.ts | 1 - 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cocos/2d/assembler/label/bmfontUtils.ts b/cocos/2d/assembler/label/bmfontUtils.ts index 63af5c678ba..8ec4ce1194f 100644 --- a/cocos/2d/assembler/label/bmfontUtils.ts +++ b/cocos/2d/assembler/label/bmfontUtils.ts @@ -38,7 +38,7 @@ import { view } from '../../../ui/view'; const _defaultFontAtlas = new FontAtlas(null); -let QUAD_INDICES; +let QUAD_INDICES: Uint16Array | null = null; export const bmfontUtils = { @@ -158,7 +158,7 @@ export const bmfontUtils = { const indexCount = renderData.indexCount; this.createQuadIndices(indexCount); - renderData.chunk.setIndexBuffer(QUAD_INDICES); + renderData.chunk.setIndexBuffer(QUAD_INDICES!); this.updateUVs(comp);// dirty need this.updateColor(comp); // dirty need diff --git a/cocos/2d/assembler/label/font-utils.ts b/cocos/2d/assembler/label/font-utils.ts index c6f90c2692c..a0bfce1d34b 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -22,7 +22,6 @@ THE SOFTWARE. */ -import { warn, warnID } from '@base/debug'; import { ccwindow } from '@base/global'; import { warn, warnID } from '@base/debug'; import { FontAtlas } from '../../assets/bitmap-font'; diff --git a/cocos/2d/assembler/label/letter-font.ts b/cocos/2d/assembler/label/letter-font.ts index 5a226450b62..a9c8962b884 100644 --- a/cocos/2d/assembler/label/letter-font.ts +++ b/cocos/2d/assembler/label/letter-font.ts @@ -22,8 +22,9 @@ THE SOFTWARE. */ +import { TextureBase } from '../../../asset/assets/texture-base'; import { js } from '../../../core'; -import { Label, LabelOutline, Overflow } from '../../components'; +import { Label, Overflow } from '../../components'; import { UITransform } from '../../framework/ui-transform'; import { bmfontUtils } from './bmfontUtils'; import { LetterAtlas, computeHash } from './font-utils'; @@ -42,7 +43,7 @@ export const letterFont = js.mixin(bmfontUtils, { _shareAtlas = new LetterAtlas(_atlasWidth, _atlasHeight); } - return _shareAtlas.getTexture(); + return _shareAtlas.getTexture() as TextureBase; }, _getFontFamily (comp: Label) { diff --git a/cocos/2d/assembler/label/text-processing.ts b/cocos/2d/assembler/label/text-processing.ts index 8b4d95d74b8..05acfcb1ded 100644 --- a/cocos/2d/assembler/label/text-processing.ts +++ b/cocos/2d/assembler/label/text-processing.ts @@ -374,7 +374,7 @@ export class TextProcessing { style.fontDesc = _fontDesc; } - private _measureText (ctx: CanvasRenderingContext2D, fontDesc): (str: string) => number { + private _measureText (ctx: CanvasRenderingContext2D, fontDesc: string): (str: string) => number { return (str: string): number => safeMeasureText(ctx, str, fontDesc); } @@ -439,9 +439,9 @@ export class TextProcessing { private _calculateFillTextStartPosition (style: TextStyle, layout: TextLayout, outputLayoutData: TextOutputLayoutData): void { let labelX = 0; - if (layout.horizontalAlign === HorizontalTextAlignment.RIGHT) { + if (layout.horizontalAlign === HorizontalTextAlignment.RIGHT as number) { labelX = outputLayoutData.canvasSize.width - outputLayoutData.canvasPadding.width; - } else if (layout.horizontalAlign === HorizontalTextAlignment.CENTER) { + } else if (layout.horizontalAlign === HorizontalTextAlignment.CENTER as number) { labelX = (outputLayoutData.canvasSize.width - outputLayoutData.canvasPadding.width) / 2; } @@ -449,10 +449,10 @@ export class TextProcessing { const drawStartY = lineHeight * (outputLayoutData.parsedString.length - 1); // TOP let firstLinelabelY = style.actualFontSize * (1 - BASELINE_RATIO / 2); - if (layout.verticalAlign !== VerticalTextAlignment.TOP) { + if (layout.verticalAlign !== VerticalTextAlignment.TOP as number) { // free space in vertical direction let blank = drawStartY + outputLayoutData.canvasPadding.height + style.actualFontSize - outputLayoutData.canvasSize.height; - if (layout.verticalAlign === VerticalTextAlignment.BOTTOM) { + if (layout.verticalAlign === VerticalTextAlignment.BOTTOM as number) { // Unlike BMFont, needs to reserve space below. blank += BASELINE_RATIO / 2 * style.actualFontSize; // BOTTOM @@ -579,9 +579,9 @@ export class TextProcessing { if (style.isUnderline) { const _drawUnderlineWidth = measureText(outputLayoutData.parsedString[i]); const _drawUnderlinePos = new Vec2(); - if (layout.horizontalAlign === HorizontalTextAlignment.RIGHT) { + if (layout.horizontalAlign === HorizontalTextAlignment.RIGHT as number) { _drawUnderlinePos.x = startPosition.x - _drawUnderlineWidth; - } else if (layout.horizontalAlign === HorizontalTextAlignment.CENTER) { + } else if (layout.horizontalAlign === HorizontalTextAlignment.CENTER as number) { _drawUnderlinePos.x = startPosition.x - (_drawUnderlineWidth / 2); } else { _drawUnderlinePos.x = startPosition.x; @@ -655,11 +655,11 @@ export class TextProcessing { let newWidth = outputLayoutData.nodeContentSize.width; let newHeight = outputLayoutData.nodeContentSize.height; - if (layout.overFlow === Overflow.RESIZE_HEIGHT) { + if (layout.overFlow === Overflow.RESIZE_HEIGHT as number) { newHeight = 0; } - if (layout.overFlow === Overflow.NONE) { + if (layout.overFlow === Overflow.NONE as number) { newWidth = 0; newHeight = 0; } @@ -705,7 +705,7 @@ export class TextProcessing { this._multilineTextWrap(style, layout, outputLayoutData, inputString, this._getFirstWordLen); // shrink - if (layout.overFlow === Overflow.SHRINK) { + if (layout.overFlow === Overflow.SHRINK as number) { if (style.fontSize > 0 && this._isVerticalClamp(style, layout, outputLayoutData, inputString, this)) { this._shrinkLabelToContentSize(style, layout, outputLayoutData, inputString, this._isVerticalClamp); } @@ -983,10 +983,10 @@ export class TextProcessing { // TOP layout.letterOffsetY = outputLayoutData.nodeContentSize.height; - if (layout.verticalAlign !== VerticalTextAlignment.TOP) { + if (layout.verticalAlign !== VerticalTextAlignment.TOP as number) { const blank = outputLayoutData.nodeContentSize.height - layout.textDesiredHeight + layout.lineHeight * this._getFontScale(style, layout) - style.originFontSize * this._fontScale * style.bmfontScale; - if (layout.verticalAlign === VerticalTextAlignment.BOTTOM) { + if (layout.verticalAlign === VerticalTextAlignment.BOTTOM as number) { // BOTTOM layout.letterOffsetY -= blank; } else { @@ -997,7 +997,7 @@ export class TextProcessing { } private _getFontScale (style: TextStyle, layout: TextLayout): number { - return layout.overFlow === Overflow.SHRINK ? style.bmfontScale : 1; + return layout.overFlow === Overflow.SHRINK as number ? style.bmfontScale : 1; } private _isVerticalClamp ( @@ -1153,7 +1153,7 @@ export class TextProcessing { py -= clipTop; } - if ((py - this._tmpRect.height * style.bmfontScale < layout.tailoredBottomY) && layout.overFlow === Overflow.CLAMP) { + if ((py - this._tmpRect.height * style.bmfontScale < layout.tailoredBottomY) && layout.overFlow === Overflow.CLAMP as number) { this._tmpRect.height = (py < layout.tailoredBottomY) ? 0 : (py - layout.tailoredBottomY) / style.bmfontScale; } } @@ -1163,7 +1163,7 @@ export class TextProcessing { if (layout.textWidthTemp > 0) { if (this._isHorizontalClamped(layout, outputLayoutData, px, lineIndex)) { - if (layout.overFlow === Overflow.CLAMP) { + if (layout.overFlow === Overflow.CLAMP as number) { this._tmpRect.width = 0; } } diff --git a/cocos/2d/components/rich-text.ts b/cocos/2d/components/rich-text.ts index 6ae3c6095b0..b0e3c8ba407 100644 --- a/cocos/2d/components/rich-text.ts +++ b/cocos/2d/components/rich-text.ts @@ -25,7 +25,6 @@ import { ccclass, executeInEditMode, executionOrder, help, menu, tooltip, multiline, type, displayOrder, serializable } from 'cc.decorator'; import { DEBUG, DEV, EDITOR } from 'internal:constants'; -import { assert, warnID } from '@base/debug'; import { cclegacy } from '@base/global'; import { assert, warnID } from '@base/debug'; import { Font, SpriteAtlas, TTFFont, SpriteFrame } from '../assets';