Skip to content

Commit 0c5ae90

Browse files
Fix contrast color calculation
1 parent 4d5a8af commit 0c5ae90

File tree

1 file changed

+50
-16
lines changed

1 file changed

+50
-16
lines changed

webapp/src/Twig/TwigExtension.php

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,12 +1145,38 @@ public function fileTypeIcon(string $type): string
11451145
return 'fas fa-file-' . $iconName;
11461146
}
11471147

1148-
public function problemBadge(ContestProblem $problem, bool $grayedOut = false): string
1148+
private function relativeLuminance(string $rgb): float
1149+
{
1150+
// See https://en.wikipedia.org/wiki/Relative_luminance
1151+
[$r, $g, $b] = Utils::parseHexColor($rgb);
1152+
1153+
[$lr, $lg, $lb] = [
1154+
pow($r / 255, 2.4),
1155+
pow($g / 255, 2.4),
1156+
pow($b / 255, 2.4),
1157+
];
1158+
1159+
return 0.2126 * $lr + 0.7152 * $lg + 0.0722 * $lb;
1160+
}
1161+
1162+
private function apcaContrast(string $fgColor, string $bgColor): float
1163+
{
1164+
// Based on WCAG 3.x (https://www.w3.org/TR/wcag-3.0/)
1165+
$luminanceForeground = $this->relativeLuminance($fgColor);
1166+
$luminanceBackground = $this->relativeLuminance($bgColor);
1167+
1168+
$contrast = ($luminanceBackground > $luminanceForeground)
1169+
? (pow($luminanceBackground, 0.56) - pow($luminanceForeground, 0.57)) * 1.14
1170+
: (pow($luminanceBackground, 0.65) - pow($luminanceForeground, 0.62)) * 1.14;
1171+
1172+
return round($contrast * 100, 2);
1173+
}
1174+
1175+
/**
1176+
* @return array{string, string}
1177+
*/
1178+
private function hexToForegroundAndBorder(string $rgb): array
11491179
{
1150-
$rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff');
1151-
if ($grayedOut || empty($rgb)) {
1152-
$rgb = Utils::convertToHex('whitesmoke');
1153-
}
11541180
$background = Utils::parseHexColor($rgb);
11551181

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

1163-
// Pick the foreground text color based on the background color.
1164-
$foreground = ($background[0] + $background[1] + $background[2] > 450) ? '#000000' : '#ffffff';
1189+
// Pick the text color with the biggest absolute contrast.
1190+
$contrastWithWhite = $this->apcaContrast('#ffffff', $rgb);
1191+
$contrastWithBlack = $this->apcaContrast('#000000', $rgb);
1192+
1193+
$foreground = (abs($contrastWithBlack) > abs($contrastWithWhite)) ? '#000000' : '#ffffff';
1194+
1195+
return [$foreground, $border];
1196+
}
1197+
1198+
public function problemBadge(ContestProblem $problem, bool $grayedOut = false): string
1199+
{
1200+
$rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff');
1201+
if ($grayedOut || empty($rgb)) {
1202+
$rgb = Utils::convertToHex('whitesmoke');
1203+
}
1204+
1205+
[$foreground, $border] = $this->hexToForegroundAndBorder($rgb);
1206+
11651207
if ($grayedOut) {
11661208
$foreground = 'silver';
11671209
$border = 'linen';
@@ -1181,17 +1223,9 @@ public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem
11811223
if (!$matrixItem->isCorrect || empty($rgb)) {
11821224
$rgb = Utils::convertToHex('whitesmoke');
11831225
}
1184-
$background = Utils::parseHexColor($rgb);
11851226

1186-
// Pick a border that's a bit darker.
1187-
$darker = $background;
1188-
$darker[0] = max($darker[0] - 64, 0);
1189-
$darker[1] = max($darker[1] - 64, 0);
1190-
$darker[2] = max($darker[2] - 64, 0);
1191-
$border = Utils::rgbToHex($darker);
1227+
[$foreground, $border] = $this->hexToForegroundAndBorder($rgb);
11921228

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

0 commit comments

Comments
 (0)