Skip to content

Commit

Permalink
Fix contrast color calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
nickygerritsen committed Mar 2, 2025
1 parent 4d5a8af commit 0c5ae90
Showing 1 changed file with 50 additions and 16 deletions.
66 changes: 50 additions & 16 deletions webapp/src/Twig/TwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -1145,12 +1145,38 @@ public function fileTypeIcon(string $type): string
return 'fas fa-file-' . $iconName;
}

public function problemBadge(ContestProblem $problem, bool $grayedOut = false): string
private function relativeLuminance(string $rgb): float
{
// See https://en.wikipedia.org/wiki/Relative_luminance
[$r, $g, $b] = Utils::parseHexColor($rgb);

[$lr, $lg, $lb] = [
pow($r / 255, 2.4),
pow($g / 255, 2.4),
pow($b / 255, 2.4),
];

return 0.2126 * $lr + 0.7152 * $lg + 0.0722 * $lb;
}

private function apcaContrast(string $fgColor, string $bgColor): float
{
// Based on WCAG 3.x (https://www.w3.org/TR/wcag-3.0/)
$luminanceForeground = $this->relativeLuminance($fgColor);
$luminanceBackground = $this->relativeLuminance($bgColor);

$contrast = ($luminanceBackground > $luminanceForeground)
? (pow($luminanceBackground, 0.56) - pow($luminanceForeground, 0.57)) * 1.14
: (pow($luminanceBackground, 0.65) - pow($luminanceForeground, 0.62)) * 1.14;

return round($contrast * 100, 2);
}

/**
* @return array{string, string}
*/
private function hexToForegroundAndBorder(string $rgb): array
{
$rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff');
if ($grayedOut || empty($rgb)) {
$rgb = Utils::convertToHex('whitesmoke');
}
$background = Utils::parseHexColor($rgb);

// Pick a border that's a bit darker.
Expand All @@ -1160,8 +1186,24 @@ public function problemBadge(ContestProblem $problem, bool $grayedOut = false):
$darker[2] = max($darker[2] - 64, 0);
$border = Utils::rgbToHex($darker);

// Pick the foreground text color based on the background color.
$foreground = ($background[0] + $background[1] + $background[2] > 450) ? '#000000' : '#ffffff';
// Pick the text color with the biggest absolute contrast.
$contrastWithWhite = $this->apcaContrast('#ffffff', $rgb);
$contrastWithBlack = $this->apcaContrast('#000000', $rgb);

$foreground = (abs($contrastWithBlack) > abs($contrastWithWhite)) ? '#000000' : '#ffffff';

return [$foreground, $border];
}

public function problemBadge(ContestProblem $problem, bool $grayedOut = false): string
{
$rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff');
if ($grayedOut || empty($rgb)) {
$rgb = Utils::convertToHex('whitesmoke');
}

[$foreground, $border] = $this->hexToForegroundAndBorder($rgb);

if ($grayedOut) {
$foreground = 'silver';
$border = 'linen';
Expand All @@ -1181,17 +1223,9 @@ public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem
if (!$matrixItem->isCorrect || empty($rgb)) {
$rgb = Utils::convertToHex('whitesmoke');
}
$background = Utils::parseHexColor($rgb);

// Pick a border that's a bit darker.
$darker = $background;
$darker[0] = max($darker[0] - 64, 0);
$darker[1] = max($darker[1] - 64, 0);
$darker[2] = max($darker[2] - 64, 0);
$border = Utils::rgbToHex($darker);
[$foreground, $border] = $this->hexToForegroundAndBorder($rgb);

// Pick the foreground text color based on the background color.
$foreground = ($background[0] + $background[1] + $background[2] > 450) ? '#000000' : '#ffffff';
if (!$matrixItem->isCorrect) {
$foreground = 'silver';
$border = 'linen';
Expand Down

0 comments on commit 0c5ae90

Please sign in to comment.