Support multiple glyphs with multiple draw calls

This commit is contained in:
Daniel Imms
2025-12-24 04:19:14 -08:00
parent 269d8c20ae
commit 2114abe88c
3 changed files with 67 additions and 91 deletions
@@ -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 <reserved>
'\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
@@ -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) },
+6 -5
View File
@@ -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[];