Skip to content

Commit

Permalink
Merge branch 'main' into zlw-poi-ts
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeLonewolf authored Jan 12, 2024
2 parents 8a613f9 + 71c9d4c commit 09b28a6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 67 deletions.
4 changes: 3 additions & 1 deletion shieldlib/src/screen_gfx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import rgba from "color-rgba";
const defaultFontFamily = '"sans-serif-condensed", "Arial Narrow", sans-serif';
export const shieldFont = (size: number, fontFamily: string) =>
`condensed 500 ${size}px ${fontFamily || defaultFontFamily}`;
export const fontSizeThreshold = 12;

//If a computed shield font size is below this value, choose a wider shield if possible
export const fontSizeThreshold = 11.8;

// Replaces `sourceVal` with a blend of `lightenVal` and `darkenVal` proportional to the brightness;
// i.e. white becomes `darkenVal`, black becomes `lightenVal`, and anit-aliased pixels remain anit-aliased
Expand Down
74 changes: 31 additions & 43 deletions shieldlib/src/shield.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,49 +76,14 @@ function getDrawFunc(shieldDef) {
return ShieldDraw.blank;
}

function drawShield(r, ctx, shieldDef, routeDef) {
let bannerCount = getBannerCount(shieldDef);
let yOffset = bannerCount * r.px(r.options.bannerHeight);

//Shift canvas to draw shield below banner
ctx.save();
ctx.translate(0, yOffset);
let drawFunc = getDrawFunc(shieldDef);
drawFunc(r, ctx, routeDef.ref);
ctx.restore();
}

function getDrawHeight(r, shieldDef) {
if (typeof shieldDef.shapeBlank != "undefined") {
return ShieldDraw.shapeHeight(r, shieldDef.shapeBlank.drawFunc);
}
return r.shieldSize();
}

function drawShieldText(r, ctx, shieldDef, routeDef) {
var bannerCount = getBannerCount(shieldDef);
var shieldBounds = null;

var shieldArtwork = getRasterShieldBlank(r, shieldDef, routeDef);
let yOffset = bannerCount * r.px(r.options.bannerHeight);

if (shieldArtwork == null) {
ctx.translate(0, yOffset);
let drawFunc = getDrawFunc(shieldDef);
drawFunc(r, ctx, routeDef.ref);
ctx.translate(0, -yOffset);

shieldBounds = {
width: ctx.canvas.width,
height: getDrawHeight(r, shieldDef),
};
} else {
shieldBounds = {
width: shieldArtwork.data.width,
height: shieldArtwork.data.height,
};
}

function drawShieldText(r, ctx, shieldDef, routeDef, shieldBounds) {
if (shieldDef.notext) {
//If the shield definition says not to draw a ref, ignore ref
return ctx;
Expand All @@ -132,8 +97,6 @@ function drawShieldText(r, ctx, shieldDef, routeDef) {
shieldBounds
);

textLayout.yBaseline += bannerCount * r.px(r.options.bannerHeight);

if (typeof r.options.SHIELD_TEXT_HALO_COLOR_OVERRIDE !== "undefined") {
ctx.strokeStyle = options.SHIELD_TEXT_HALO_COLOR_OVERRIDE;
ShieldText.drawShieldHaloText(r, ctx, routeDef.ref, textLayout);
Expand All @@ -150,8 +113,7 @@ function drawShieldText(r, ctx, shieldDef, routeDef) {
ctx.lineWidth = r.px(1);
ctx.strokeRect(
r.px(shieldDef.padding.left - 0.5),
bannerCount * r.px(r.options.bannerHeight) +
r.px(shieldDef.padding.top - 0.5),
r.px(shieldDef.padding.top - 0.5),
shieldBounds.width -
r.px(shieldDef.padding.left + shieldDef.padding.right - 1),
shieldBounds.height -
Expand Down Expand Up @@ -299,6 +261,17 @@ function getDrawnShieldBounds(r, shieldDef, ref) {
return { width, height };
}

function bannerAreaHeight(r, bannerCount) {
if (bannerCount === 0) {
return 0;
}
return (
bannerCount * r.px(r.options.bannerHeight) +
//No padding after last banner
(bannerCount - 1) * r.px(r.options.bannerPadding)
);
}

export function generateShieldCtx(r, routeDef) {
let shieldDef = getShieldDef(r.shieldDef, routeDef);

Expand All @@ -316,18 +289,25 @@ export function generateShieldCtx(r, routeDef) {
let width = r.shieldSize();
let height = r.shieldSize();

let shieldBounds = null;

if (sourceSprite == null) {
if (typeof shieldDef.shapeBlank != "undefined") {
let bounds = getDrawnShieldBounds(r, shieldDef, routeDef.ref);
width = bounds.width;
height = bounds.height;
}
shieldBounds = {
width: width,
height: getDrawHeight(r, shieldDef),
};
} else {
width = sourceSprite.data.width;
height = sourceSprite.data.height;
shieldBounds = { width, height };
}

let bannerHeight = bannerCount * r.px(r.options.bannerHeight);
let bannerHeight = bannerAreaHeight(r, bannerCount);
height += bannerHeight;

//Generate empty canvas sized to the graphic
Expand All @@ -343,9 +323,15 @@ export function generateShieldCtx(r, routeDef) {
// Add the halo around modifier plaque text
drawBannerHalos(r, ctx, shieldDef);

//Shift canvas to draw shield below banner
ctx.save();
ctx.translate(0, bannerHeight);

if (sourceSprite == null) {
drawShield(r, ctx, shieldDef, routeDef);
let drawFunc = getDrawFunc(shieldDef);
drawFunc(r, ctx, routeDef.ref);
} else {
//This is a raw copy, so the yOffset (bannerHeight) is needed
Gfx.transposeImageData(
ctx,
sourceSprite,
Expand All @@ -357,7 +343,9 @@ export function generateShieldCtx(r, routeDef) {
}

// Draw the shield text
drawShieldText(r, ctx, shieldDef, routeDef);
drawShieldText(r, ctx, shieldDef, routeDef, shieldBounds);

ctx.restore();

// Add modifier plaque text
drawBanners(r, ctx, shieldDef);
Expand Down
6 changes: 3 additions & 3 deletions shieldlib/src/shield_banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function drawBannerTextComponent(
textComponent: boolean
): void {
const bannerPadding = {
top: r.options.bannerPadding,
top: 0,
bottom: 0,
left: 0,
right: 0,
Expand Down Expand Up @@ -161,7 +161,7 @@ function drawBannerTextComponent(
text,
textLayout.xBaseline,
textLayout.yBaseline +
bannerIndex * r.px(r.options.bannerHeight - r.options.bannerPadding)
bannerIndex * r.px(r.options.bannerHeight + r.options.bannerPadding)
);
} else {
ctx.shadowBlur = 0;
Expand All @@ -170,7 +170,7 @@ function drawBannerTextComponent(
text,
textLayout.xBaseline,
textLayout.yBaseline +
bannerIndex * r.px(r.options.bannerHeight - r.options.bannerPadding)
bannerIndex * r.px(r.options.bannerHeight + r.options.bannerPadding)
);

ctx.shadowColor = null;
Expand Down
67 changes: 47 additions & 20 deletions shieldlib/src/shield_text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ function triangleDownTextConstraint(
};
}

// Warning!!! Hack!!!
function isRunningInWebKit(): boolean {
if (typeof window === "undefined") {
return false;
}
const userAgent = window.navigator.userAgent;
return /WebKit/i.test(userAgent) && !/Chrome/i.test(userAgent);
}

/**
* Determines the position and font size to draw text so that it fits within
* a bounding box.
Expand All @@ -157,34 +166,49 @@ export function layoutShieldText(
textLayoutDef: TextLayout,
maxFontSize: number = 14
): TextPlacement {
var padTop = r.px(padding.top) || 0;
var padBot = r.px(padding.bottom) || 0;
var padLeft = r.px(padding.left) || 0;
var padRight = r.px(padding.right) || 0;
let padTop = r.px(padding.top) || 0;
let padBot = r.px(padding.bottom) || 0;
let padLeft = r.px(padding.left) || 0;
let padRight = r.px(padding.right) || 0;

var maxFont = r.px(maxFontSize);
let maxFont = r.px(maxFontSize);
//Temporary canvas for text measurment
var ctx = r.gfxFactory.createGraphics(bounds);
let ctx: CanvasRenderingContext2D = r.gfxFactory.createGraphics(bounds);

ctx.font = Gfx.shieldFont(Gfx.fontSizeThreshold, r.options.shieldFont);
ctx.textAlign = "center";
ctx.textAlign = "left";
ctx.textBaseline = "top";

var metrics = ctx.measureText(text);
let metrics: TextMetrics = ctx.measureText(text);

var textWidth = metrics.width;
var textHeight = metrics.actualBoundingBoxDescent;
let textWidth: number =
Math.abs(metrics.actualBoundingBoxLeft) +
Math.abs(metrics.actualBoundingBoxRight);
let textHeight: number =
Math.abs(metrics.actualBoundingBoxDescent) +
Math.abs(metrics.actualBoundingBoxAscent);

var availHeight = bounds.height - padTop - padBot;
var availWidth = bounds.width - padLeft - padRight;
//Adjust for excess descender text height across browsers
textHeight *= 0.9;

var xBaseline = padLeft + availWidth / 2;
//Adjust for excess text height measured in Webkit engine specifically
if (isRunningInWebKit()) {
textHeight *= 0.54;
}

let availHeight: number = bounds.height - padTop - padBot;
let availWidth: number = bounds.width - padLeft - padRight;

let xBaseline: number = padLeft + availWidth / 2;

let textLayoutFunc = drawTextFunctions[textLayoutDef.constraintFunc];

let textConstraint = textLayoutFunc(
{ height: availHeight, width: availWidth },
{ height: textHeight, width: textWidth },
let spaceAvail: Dimension = { height: availHeight, width: availWidth };
let measuredTextBounds: Dimension = { height: textHeight, width: textWidth };

let textConstraint: TextTransform = textLayoutFunc(
spaceAvail,
measuredTextBounds,
textLayoutDef.options
);

Expand All @@ -195,20 +219,23 @@ export function layoutShieldText(
);

ctx.font = Gfx.shieldFont(fontSize, r.options.shieldFont);
ctx.textAlign = "center";
ctx.textAlign = "left";
ctx.textBaseline = "top";

metrics = ctx.measureText(text);
textHeight = metrics.actualBoundingBoxDescent;
textHeight =
Math.abs(metrics.actualBoundingBoxDescent) +
Math.abs(metrics.actualBoundingBoxAscent);

let yBaseline: number;

switch (textConstraint.valign) {
case VerticalAlignment.Top:
yBaseline = padTop;
yBaseline = padTop + metrics.actualBoundingBoxAscent;
break;
case VerticalAlignment.Bottom:
yBaseline = padTop + availHeight - textHeight;
yBaseline =
padTop + availHeight - textHeight + metrics.actualBoundingBoxAscent;
break;
case VerticalAlignment.Middle:
default:
Expand Down
2 changes: 2 additions & 0 deletions src/js/shield_defs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3320,6 +3320,7 @@ export function loadShields() {

shields["omt-ie-national"] = roundedRectShield(
Color.shields.green,
Color.shields.white,
Color.shields.yellow
);

Expand Down Expand Up @@ -3535,6 +3536,7 @@ export function loadShields() {

shields["omt-gb-trunk"] = roundedRectShield(
Color.shields.green,
Color.shields.white,
Color.shields.yellow
);

Expand Down

0 comments on commit 09b28a6

Please sign in to comment.