diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index a5779afc..07488dd6 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -514,7 +514,7 @@ export class TextureAtlas implements ITextureAtlas { // Draw custom characters if applicable let customGlyph = false; if (this._config.customGlyphs !== false) { - customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.fontSize, this._config.devicePixelRatio); + customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css); } // Whether to clear pixels based on a threshold difference between the glyph color and the diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 61b6b659..e062d5d6 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -549,9 +549,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Consider implementing // Negative terminal graphic characters (1FBBD-1FBBF) - '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH, data: 'M.07,0 L.93,0 L.5,.43 Z M1,.07 L1,.93 L.57,.5 Z M.93,1 L.07,1 L.5,.57 Z M0,.93 L0,.07 L.43,.5 Z' }, // NEGATIVE DIAGONAL CROSS - '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.43 L.43,1 L0,1 Z M1,.57 L.57,1 L1,1 Z' }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L.43,0 L0,.43 Z M.57,0 L1,0 L1,.43 Z M1,.57 L1,1 L.57,1 Z M0,.57 L.43,1 L0,1 Z M.5,.07 L.93,.5 L.5,.93 L.07,.5 Z' }, // NEGATIVE DIAGONAL DIAMOND + '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS + '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M1,.5 L.5,1', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 495bc70e..41994760 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -19,7 +19,8 @@ export function tryDrawCustomGlyph( deviceCellWidth: number, deviceCellHeight: number, fontSize: number, - devicePixelRatio: number + devicePixelRatio: number, + backgroundColor?: string ): boolean { const unifiedCharDefinition = customGlyphDefinitions[c]; if (unifiedCharDefinition) { @@ -44,6 +45,9 @@ export function tryDrawCustomGlyph( 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; @@ -111,6 +115,75 @@ function drawPathDefinitionCharacter( ctx.fill(); } +/** + * Draws a "negative" path where the background color is used to draw the shape on top of a + * foreground-filled cell. This creates the appearance of a cutout without using actual + * transparency, which allows SPAA (subpixel anti-aliasing) to work correctly. + * + * @param ctx The canvas rendering context (fillStyle should be set to foreground color) + * @param charDefinition The vector shape definition for the negative shape + * @param xOffset The x offset to draw at + * @param yOffset The y offset to draw at + * @param deviceCellWidth The width of the cell in device pixels + * @param deviceCellHeight The height of the cell in device pixels + * @param devicePixelRatio The device pixel ratio + * @param backgroundColor The background color to use for the "cutout" portion + */ +function drawPathNegativeDefinitionCharacter( + ctx: CanvasRenderingContext2D, + charDefinition: ICustomGlyphVectorShape, + xOffset: number, + yOffset: number, + deviceCellWidth: number, + deviceCellHeight: number, + devicePixelRatio: number, + backgroundColor?: string +): void { + ctx.save(); + + // First, fill the entire cell with foreground color + ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); + + // Then draw the "negative" shape with the background color + if (backgroundColor) { + ctx.fillStyle = backgroundColor; + ctx.strokeStyle = backgroundColor; + } + + ctx.lineWidth = devicePixelRatio; + ctx.lineCap = 'square'; + ctx.beginPath(); + for (const instruction of charDefinition.d.split(' ')) { + const type = instruction[0]; + const args: string[] = instruction.substring(1).split(','); + if (!args[0] || !args[1]) { + if (type === 'Z') { + ctx.closePath(); + } + continue; + } + const translatedArgs = args.map((e, i) => { + const val = parseFloat(e); + return i % 2 === 0 + ? xOffset + val * deviceCellWidth + : yOffset + val * deviceCellHeight; + }); + if (type === 'M') { + ctx.moveTo(translatedArgs[0], translatedArgs[1]); + } else if (type === 'L') { + ctx.lineTo(translatedArgs[0], translatedArgs[1]); + } + } + + if (charDefinition.type === CustomGlyphVectorType.STROKE) { + ctx.stroke(); + } else { + ctx.fill(); + } + + ctx.restore(); +} + const cachedPatterns: Map> = new Map(); function drawPatternChar( diff --git a/addons/addon-webgl/src/customGlyphs/Types.ts b/addons/addon-webgl/src/customGlyphs/Types.ts index 4623ea46..0cc8bdc8 100644 --- a/addons/addon-webgl/src/customGlyphs/Types.ts +++ b/addons/addon-webgl/src/customGlyphs/Types.ts @@ -39,6 +39,7 @@ export const enum CustomGlyphDefinitionType { PATH_FUNCTION, PATH_FUNCTION_WITH_WEIGHT, PATH, + PATH_NEGATIVE, VECTOR_SHAPE, } @@ -55,5 +56,6 @@ export type CustomGlyphCharacterDefinition = ( { 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 } );