From 2114abe88ca85390832a221a62cf2fc823eee0c7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 24 Dec 2025 04:19:14 -0800 Subject: [PATCH] Support multiple glyphs with multiple draw calls --- .../customGlyphs/CustomGlyphDefinitions.ts | 30 ++--- .../src/customGlyphs/CustomGlyphRasterizer.ts | 117 +++++++----------- addons/addon-webgl/src/customGlyphs/Types.ts | 11 +- 3 files changed, 67 insertions(+), 91 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 9340212c..11b6f9ac 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -452,28 +452,28 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi [0, 1], [1, 0] ], [0, 0, 1, 1]] }, - '\u{1FB91}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE - pattern: [[ + '\u{1FB91}': [ // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE + { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ [0, 1], [1, 0] - ], [0, 0.5, 1, 0.5]], - vectors: [{ x: 0, y: 0, w: 8, h: 4 }] - } }, - '\u{1FB92}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK - pattern: [[ + ], [0, 0.5, 1, 0.5]] }, + { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 4 }] } + ], + '\u{1FB92}': [ // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK + { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ [0, 1], [1, 0] - ], [0, 0, 1, 0.5]], - vectors: [{ x: 0, y: 4, w: 8, h: 4 }] - } }, + ], [0, 0, 1, 0.5]] }, + { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 4 }] } + ], // 1FB93 is - '\u{1FB94}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK - pattern: [[ + '\u{1FB94}': [ // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK + { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [[ [0, 1], [1, 0] - ], [0, 0, 0.5, 1]], - vectors: [{ x: 4, y: 0, w: 4, h: 8 }] - } }, + ], [0, 0, 0.5, 1]] }, + { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 8 }] } + ], // Fill characters (1FB95-1FB97) '\u{1FB95}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [ // CHECKER BOARD FILL diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 5e557016..614941c3 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -5,7 +5,7 @@ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; -import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type CustomGlyphRegionDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; +import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type CustomGlyphRegionDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; /** * Try drawing a custom block element or box drawing character, returning whether it was @@ -24,42 +24,57 @@ export function tryDrawCustomGlyph( ): boolean { const unifiedCharDefinition = customGlyphDefinitions[c]; if (unifiedCharDefinition) { - switch (unifiedCharDefinition.type) { - case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR: - drawBlockVectorChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.BLOCK_PATTERN: - drawPatternChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION: - drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR: - drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data.pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - drawBlockVectorChar(ctx, unifiedCharDefinition.data.vectors, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH: - drawBlockPatternWithClipPath(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.PATH_FUNCTION: - case CustomGlyphDefinitionType.PATH: - drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); - return true; - case CustomGlyphDefinitionType.PATH_NEGATIVE: - drawPathNegativeDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, backgroundColor); - return true; - case CustomGlyphDefinitionType.VECTOR_SHAPE: - drawVectorShape(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio); - return true; - case CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT: - drawPathDefinitionCharacterWithWeight(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio); - return true; + // Normalize to array for uniform handling + const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition]; + for (const part of parts) { + drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio, backgroundColor); } + return true; } return false; } +function drawDefinitionPart( + ctx: CanvasRenderingContext2D, + part: CustomGlyphDefinitionPart, + xOffset: number, + yOffset: number, + deviceCellWidth: number, + deviceCellHeight: number, + fontSize: number, + devicePixelRatio: number, + backgroundColor?: string +): void { + switch (part.type) { + case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR: + drawBlockVectorChar(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + break; + case CustomGlyphDefinitionType.BLOCK_PATTERN: + drawPatternChar(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + break; + case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION: + drawBlockPatternWithRegion(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + break; + case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH: + drawBlockPatternWithClipPath(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + break; + case CustomGlyphDefinitionType.PATH_FUNCTION: + case CustomGlyphDefinitionType.PATH: + drawPathDefinitionCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + break; + case CustomGlyphDefinitionType.PATH_NEGATIVE: + drawPathNegativeDefinitionCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, backgroundColor); + break; + case CustomGlyphDefinitionType.VECTOR_SHAPE: + drawVectorShape(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio); + break; + case CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT: + drawPathDefinitionCharacterWithWeight(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio); + break; + } +} + function drawBlockVectorChar( ctx: CanvasRenderingContext2D, charDefinition: ICustomGlyphSolidOctantBlockVector[], @@ -432,46 +447,6 @@ function drawBlockPatternWithRegion( ctx.restore(); } -/** - * Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to - * canvas draw calls. - * - * Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐ - * ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤ - * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘ - * ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐ - * │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤ - * └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘ - * - * Other: - * ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈ - * │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉ - * ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋ - * - * All box drawing characters: - * ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏ - * ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟ - * ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯ - * ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿ - * ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏ - * ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ - * ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯ - * ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿ - * - * --- - * - * Box drawing alignment tests: █ - * ▉ - * ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ - * ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ - * ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ - * ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ - * ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ - * ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ - * ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ - * - * Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html - */ function drawPathDefinitionCharacterWithWeight( ctx: CanvasRenderingContext2D, charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) }, diff --git a/addons/addon-webgl/src/customGlyphs/Types.ts b/addons/addon-webgl/src/customGlyphs/Types.ts index 0cc8bdc8..52e81b55 100644 --- a/addons/addon-webgl/src/customGlyphs/Types.ts +++ b/addons/addon-webgl/src/customGlyphs/Types.ts @@ -34,7 +34,6 @@ export const enum CustomGlyphDefinitionType { SOLID_OCTANT_BLOCK_VECTOR, BLOCK_PATTERN, BLOCK_PATTERN_WITH_REGION, - BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, BLOCK_PATTERN_WITH_CLIP_PATH, PATH_FUNCTION, PATH_FUNCTION_WITH_WEIGHT, @@ -45,13 +44,10 @@ export const enum CustomGlyphDefinitionType { export type CustomGlyphRegionDefinition = [x: number, y: number, w: number, h: number]; -export type CustomGlyphCharacterDefinition = ( +export type CustomGlyphDefinitionPart = ( { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: ICustomGlyphSolidOctantBlockVector[] } | { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: CustomGlyphPatternDefinition } | { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition] } | - // TODO: Consolidate these, draws should be possible via regions/clipping instead of special - // casing - { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { pattern: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition], vectors: ICustomGlyphSolidOctantBlockVector[] } } | { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [pattern: CustomGlyphPatternDefinition, clipPath: string] } | { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } | { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [fontWeight: number]: string | CustomGlyphPathDrawFunctionDefinition } } | @@ -59,3 +55,8 @@ export type CustomGlyphCharacterDefinition = ( { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: ICustomGlyphVectorShape } | { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape } ); + +/** + * A character definition that can be a single part or an array of parts drawn in sequence. + */ +export type CustomGlyphCharacterDefinition = CustomGlyphDefinitionPart | CustomGlyphDefinitionPart[];