Make medium shade consistent, make all shades less heavy

This commit is contained in:
Daniel Imms
2025-12-22 10:57:59 -08:00
parent f049c42acc
commit b64543aa59
2 changed files with 43 additions and 40 deletions
@@ -291,21 +291,32 @@ export const symbolsForLegacyComputingDefinitions: { [index: string]: CustomGlyp
'\u{1FB6F}': () => 'M0,1 L1,1 L0.5,0.5 Z' // LOWER TRIANGULAR ONE QUARTER BLOCK
};
/**
* Rectangular shade characters - these use medium shade pattern (50%) like 0x2592.
* Each entry is [pattern, region] where region is [x, y, width, height] in 0-1 normalized
* coordinates. Pattern is the medium shade checkerboard pattern.
*/
const mediumShadePattern: CustomGlyphPatternDefinition = [
[1, 0],
[0, 1]
];
const inverseMediumShadePattern: CustomGlyphPatternDefinition = [
[0, 1],
[1, 0]
];
export const rectangularShadeDefinitions: { [index: string]: [CustomGlyphPatternDefinition, [number, number, number, number]] | undefined } = {
'\u{1FB8C}': [mediumShadePattern, [0, 0, 0.5, 1]], // LEFT HALF MEDIUM SHADE
'\u{1FB8D}': [mediumShadePattern, [0.5, 0, 0.5, 1]], // RIGHT HALF MEDIUM SHADE
'\u{1FB8E}': [mediumShadePattern, [0, 0, 1, 0.5]], // UPPER HALF MEDIUM SHADE
'\u{1FB8F}': [mediumShadePattern, [0, 0.5, 1, 0.5]], // LOWER HALF MEDIUM SHADE
'\u{1FB90}': [inverseMediumShadePattern, [0, 0, 1, 1]] // INVERSE MEDIUM SHADE
};
/** Region defined as [x, y, width, height] in 0-1 normalized coordinates. */
type RegionDefinition = [number, number, number, number];
/**
* Rectangular shade characters - these use medium shade pattern with region bounds.
* Pattern is a checkerboard that shifts 1px each row (same as medium shade).
*/
export const rectangularShadeDefinitions: { [index: string]: RegionDefinition | undefined } = {
'\u{1FB8C}': [0, 0, 0.5, 1], // LEFT HALF MEDIUM SHADE
'\u{1FB8D}': [0.5, 0, 0.5, 1], // RIGHT HALF MEDIUM SHADE
'\u{1FB8E}': [0, 0, 1, 0.5], // UPPER HALF MEDIUM SHADE
'\u{1FB8F}': [0, 0.5, 1, 0.5], // LOWER HALF MEDIUM SHADE
'\u{1FB90}': [0, 0, 1, 1] // INVERSE MEDIUM SHADE
};
/** [solidRegion, shadeRegion] where shade region uses inverse medium shade pattern. */
type BlockShadeComboDefinition = [RegionDefinition, RegionDefinition];
@@ -326,22 +337,16 @@ export const blockShadeComboDefinitions: { [index: string]: BlockShadeComboDefin
export const patternCharacterDefinitions: { [key: string]: CustomGlyphPatternDefinition | undefined } = {
// Shade characters (0x2591-0x2593)
'░': [ // LIGHT SHADE (25%)
[1, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 0]
[1, 0],
[0, 0]
],
'▒': [ // MEDIUM SHADE (50%)
[1, 0],
[0, 0],
[0, 1],
[0, 0]
[0, 1]
],
'▓': [ // DARK SHADE (75%)
[0, 1],
[1, 1],
[1, 0],
[1, 1]
[1, 0]
]
};
@@ -61,7 +61,7 @@ export function tryDrawCustomGlyph(
const rectangularShadeDefinition = rectangularShadeDefinitions[c];
if (rectangularShadeDefinition) {
drawRectangularShadeChar(ctx, rectangularShadeDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, c === '\u{1FB90}');
drawRectangularShadeChar(ctx, rectangularShadeDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
return true;
}
@@ -214,34 +214,32 @@ function drawPatternChar(
*/
function drawRectangularShadeChar(
ctx: CanvasRenderingContext2D,
region: [number, number, number, number],
definition: [CustomGlyphPatternDefinition, [number, number, number, number]],
xOffset: number,
yOffset: number,
deviceCellWidth: number,
deviceCellHeight: number,
isInverse: boolean
deviceCellHeight: number
): void {
const [pattern, region] = definition;
const [rx, ry, rw, rh] = region;
const regionX = Math.round(xOffset + rx * deviceCellWidth);
const regionY = Math.round(yOffset + ry * deviceCellHeight);
const regionW = Math.round(rw * deviceCellWidth);
const regionH = Math.round(rh * deviceCellHeight);
// For inverse medium shade, we use the opposite pattern (fill where medium shade doesn't)
// Medium shade pattern: fills at (x+y) % 2 === 0, so inverse fills at (x+y) % 2 === 1
const patternOffset = isInverse ? 1 : 0;
// Save context state
ctx.save();
for (let py = 0; py < regionH; py++) {
// Calculate the absolute y position for pattern calculation
const absY = regionY + py - yOffset;
for (let px = 0; px < regionW; px++) {
const absX = regionX + px - xOffset;
// Checkerboard pattern: fill if (x + y) % 2 matches the offset
if (((absX + absY) % 2) === patternOffset) {
ctx.fillRect(regionX + px, regionY + py, 1, 1);
}
}
}
// Clip to the region
ctx.beginPath();
ctx.rect(regionX, regionY, regionW, regionH);
ctx.clip();
// Draw the pattern
drawPatternChar(ctx, pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
// Restore context state
ctx.restore();
}
/**