Support clip path on any draw definition

This commit is contained in:
Daniel Imms
2025-12-24 04:25:59 -08:00
parent 2114abe88c
commit 11dae84ff7
3 changed files with 32 additions and 28 deletions
@@ -501,22 +501,22 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi
'\u{1FB9B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1', type: CustomGlyphVectorType.FILL } }, // LEFT AND RIGHT TRIANGULAR HALF BLOCK
// Triangular shade characters (1FB9C-1FB9F)
'\u{1FB9C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // UPPER LEFT TRIANGULAR MEDIUM SHADE
'\u{1FB9C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // UPPER LEFT TRIANGULAR MEDIUM SHADE
[1, 0],
[0, 1]
], 'M0,0 L1,0 L0,1 Z'] },
'\u{1FB9D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // UPPER RIGHT TRIANGULAR MEDIUM SHADE
], clipPath: 'M0,0 L1,0 L0,1 Z' },
'\u{1FB9D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // UPPER RIGHT TRIANGULAR MEDIUM SHADE
[1, 0],
[0, 1]
], 'M0,0 L1,0 L1,1 Z'] },
'\u{1FB9E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // LOWER RIGHT TRIANGULAR MEDIUM SHADE
], clipPath: 'M0,0 L1,0 L1,1 Z' },
'\u{1FB9E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LOWER RIGHT TRIANGULAR MEDIUM SHADE
[1, 0],
[0, 1]
], 'M1,0 L1,1 L0,1 Z'] },
'\u{1FB9F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // LOWER LEFT TRIANGULAR MEDIUM SHADE
], clipPath: 'M1,0 L1,1 L0,1 Z' },
'\u{1FB9F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LOWER LEFT TRIANGULAR MEDIUM SHADE
[1, 0],
[0, 1]
], 'M0,0 L1,1 L0,1 Z'] },
], clipPath: 'M0,0 L1,1 L0,1 Z' },
// Character cell diagonals (1FBA0-1FBAE)
'\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT
@@ -46,6 +46,12 @@ function drawDefinitionPart(
devicePixelRatio: number,
backgroundColor?: string
): void {
// Handle clipPath generically for any definition type
if (part.clipPath) {
ctx.save();
applyClipPath(ctx, part.clipPath, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
}
switch (part.type) {
case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR:
drawBlockVectorChar(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
@@ -56,9 +62,6 @@ function drawDefinitionPart(
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);
@@ -73,6 +76,10 @@ function drawDefinitionPart(
drawPathDefinitionCharacterWithWeight(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
break;
}
if (part.clipPath) {
ctx.restore();
}
}
function drawBlockVectorChar(
@@ -491,21 +498,16 @@ function drawPathDefinitionCharacterWithWeight(
}
/**
* Draws a pattern clipped to an arbitrary path (for triangular shades, etc.)
* Applies a clip path to the canvas context from SVG-like path instructions.
*/
function drawBlockPatternWithClipPath(
function applyClipPath(
ctx: CanvasRenderingContext2D,
definition: [pattern: CustomGlyphPatternDefinition, clipPath: string],
clipPath: string,
xOffset: number,
yOffset: number,
deviceCellWidth: number,
deviceCellHeight: number
): void {
const [pattern, clipPath] = definition;
ctx.save();
// Build clip path from SVG-like instructions
ctx.beginPath();
for (const instruction of clipPath.split(' ')) {
const type = instruction[0];
@@ -526,11 +528,6 @@ function drawBlockPatternWithClipPath(
}
}
ctx.clip();
// Draw the pattern
drawPatternChar(ctx, pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
ctx.restore();
}
function drawVectorShape(
+11 -4
View File
@@ -34,7 +34,6 @@ export const enum CustomGlyphDefinitionType {
SOLID_OCTANT_BLOCK_VECTOR,
BLOCK_PATTERN,
BLOCK_PATTERN_WITH_REGION,
BLOCK_PATTERN_WITH_CLIP_PATH,
PATH_FUNCTION,
PATH_FUNCTION_WITH_WEIGHT,
PATH,
@@ -44,18 +43,26 @@ export const enum CustomGlyphDefinitionType {
export type CustomGlyphRegionDefinition = [x: number, y: number, w: number, h: number];
export type CustomGlyphDefinitionPart = (
export type CustomGlyphDefinitionPartRaw = (
{ 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] } |
{ 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 } } |
{ type: CustomGlyphDefinitionType.PATH, data: string } |
{ type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: ICustomGlyphVectorShape } |
{ type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape }
{ type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape}
);
export interface ICustomGlyphDefinitionCommon {
/**
* A custom clip path for the draw definition, restricting the area it can draw to.
*/
clipPath?: string;
}
export type CustomGlyphDefinitionPart = CustomGlyphDefinitionPartRaw & ICustomGlyphDefinitionCommon;
/**
* A character definition that can be a single part or an array of parts drawn in sequence.
*/