Skip to content

Commit

Permalink
Canvas2D Text: Use a managed child node to render text
Browse files Browse the repository at this point in the history
- Canvas Text Textures are now managed properly by the texture manager
- Also add willReadFrequently option to text drawing canvas element context
  • Loading branch information
frank-weindel committed Jul 5, 2024
1 parent aff3a4a commit f443716
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 769 deletions.
2 changes: 0 additions & 2 deletions examples/tests/text-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ export default async function test(settings: ExampleSettings) {
{
duration: 3000,
easing: 'ease-out',
// loop: true,
stopMethod: 'reverse',
},
)
.start();
Expand Down
2 changes: 1 addition & 1 deletion src/core/CoreNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ export type CoreNodeAnimatableProps = {
export class CoreNode extends EventEmitter {
readonly children: CoreNode[] = [];
protected _id: number = getNewId();
protected props: Required<CoreNodeWritableProps>;
readonly props: Required<CoreNodeWritableProps>;

public updateType = UpdateType.All;

Expand Down
11 changes: 10 additions & 1 deletion src/core/CoreTextNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,15 @@ export class CoreTextNode
override renderQuads(renderer: CoreRenderer) {
assertTruthy(this.globalTransform);

// If the text renderer does not support rendering quads, fallback to the
// default renderQuads method
if (!this.textRenderer.renderQuads) {
super.renderQuads(renderer);
return;
}

// If the text renderer does support rendering quads, use it...

// Prevent quad rendering if parent has a render texture
// and this node is not the render texture
if (this.parentHasRenderTexture) {
Expand Down Expand Up @@ -444,7 +453,7 @@ export class CoreTextNode
this._textRendererOverride,
);

const textRendererState = resolvedTextRenderer.createState(props);
const textRendererState = resolvedTextRenderer.createState(props, this);

textRendererState.emitter.on('loaded', this.onTextLoaded);
textRendererState.emitter.on('failed', this.onTextFailed);
Expand Down
111 changes: 110 additions & 1 deletion src/core/Stage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { startLoop, getTimeStamp } from './platform.js';
import { WebGlCoreRenderer } from './renderers/webgl/WebGlCoreRenderer.js';
import { assertTruthy, setPremultiplyMode } from '../utils.js';
import { AnimationManager } from './animations/AnimationManager.js';
import { CoreNode } from './CoreNode.js';
import { CoreNode, type CoreNodeWritableProps } from './CoreNode.js';
import { CoreTextureManager } from './CoreTextureManager.js';
import { TrFontManager } from './text-rendering/TrFontManager.js';
import { CoreShaderManager } from './CoreShaderManager.js';
Expand All @@ -46,6 +46,11 @@ import type {
CoreRendererOptions,
} from './renderers/CoreRenderer.js';
import { CanvasCoreRenderer } from './renderers/canvas/CanvasCoreRenderer.js';
import { santizeCustomDataMap } from '../main-api/utils.js';
import {
CoreTextNode,
type CoreTextNodeWritableProps,
} from './CoreTextNode.js';

export interface StageOptions {
appWidth: number;
Expand Down Expand Up @@ -448,4 +453,108 @@ export class Stage {
// the covariant state argument in the setter method map
return resolvedTextRenderer as unknown as TextRenderer;
}

createNode(props: Partial<CoreNodeWritableProps>) {
const resolvedProps = this.resolveNodeDefaults(props);
const node = new CoreNode(this, {
...resolvedProps,
shaderProps: null,
});
return node;
}

createTextNode(props: Partial<CoreTextNodeWritableProps>) {
const fontSize = props.fontSize ?? 16;
const resolvedProps = {
...this.resolveNodeDefaults(props),
text: props.text ?? '',
textRendererOverride: props.textRendererOverride ?? null,
fontSize,
fontFamily: props.fontFamily ?? 'sans-serif',
fontStyle: props.fontStyle ?? 'normal',
fontWeight: props.fontWeight ?? 'normal',
fontStretch: props.fontStretch ?? 'normal',
textAlign: props.textAlign ?? 'left',
contain: props.contain ?? 'none',
scrollable: props.scrollable ?? false,
scrollY: props.scrollY ?? 0,
offsetY: props.offsetY ?? 0,
letterSpacing: props.letterSpacing ?? 0,
lineHeight: props.lineHeight, // `undefined` is a valid value
maxLines: props.maxLines ?? 0,
textBaseline: props.textBaseline ?? 'alphabetic',
verticalAlign: props.verticalAlign ?? 'middle',
overflowSuffix: props.overflowSuffix ?? '...',
debug: props.debug ?? {},
shaderProps: null,
};

return new CoreTextNode(this, resolvedProps);
}

/**
* Resolves the default property values for a Node
*
* @remarks
* This method is used internally by the RendererMain to resolve the default
* property values for a Node. It is exposed publicly so that it can be used
* by Core Driver implementations.
*
* @param props
* @returns
*/
protected resolveNodeDefaults(
props: Partial<CoreNodeWritableProps>,
): CoreNodeWritableProps {
const color = props.color ?? 0xffffffff;
const colorTl = props.colorTl ?? props.colorTop ?? props.colorLeft ?? color;
const colorTr =
props.colorTr ?? props.colorTop ?? props.colorRight ?? color;
const colorBl =
props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color;
const colorBr =
props.colorBr ?? props.colorBottom ?? props.colorRight ?? color;
const data = santizeCustomDataMap(props.data ?? {});

return {
x: props.x ?? 0,
y: props.y ?? 0,
width: props.width ?? 0,
height: props.height ?? 0,
alpha: props.alpha ?? 1,
autosize: props.autosize ?? false,
clipping: props.clipping ?? false,
color,
colorTop: props.colorTop ?? color,
colorBottom: props.colorBottom ?? color,
colorLeft: props.colorLeft ?? color,
colorRight: props.colorRight ?? color,
colorBl,
colorBr,
colorTl,
colorTr,
zIndex: props.zIndex ?? 0,
zIndexLocked: props.zIndexLocked ?? 0,
parent: props.parent ?? null,
texture: props.texture ?? null,
textureOptions: props.textureOptions ?? {},
shader: props.shader ?? null,
shaderProps: props.shaderProps ?? null,
// Since setting the `src` will trigger a texture load, we need to set it after
// we set the texture. Otherwise, problems happen.
src: props.src ?? '',
scale: props.scale ?? null,
scaleX: props.scaleX ?? props.scale ?? 1,
scaleY: props.scaleY ?? props.scale ?? 1,
mount: props.mount ?? 0,
mountX: props.mountX ?? props.mount ?? 0,
mountY: props.mountY ?? props.mount ?? 0,
pivot: props.pivot ?? 0.5,
pivotX: props.pivotX ?? props.pivot ?? 0.5,
pivotY: props.pivotY ?? props.pivot ?? 0.5,
rotation: props.rotation ?? 0,
rtt: props.rtt ?? false,
data: data,
};
}
}
Loading

0 comments on commit f443716

Please sign in to comment.