@@ -1179,9 +1179,59 @@ public function fileTypeIcon(string $type): string
1179
1179
return 'fas fa-file- ' . $ iconName ;
1180
1180
}
1181
1181
1182
- public function problemBadge ( ContestProblem $ problem , bool $ grayedOut = false ): string
1182
+ private function relativeLuminance ( string $ rgb ): float
1183
1183
{
1184
- $ rgb = Utils::convertToHex ($ problem ->getColor () ?? '#ffffff ' );
1184
+ // See https://en.wikipedia.org/wiki/Relative_luminance
1185
+ [$ r , $ g , $ b ] = Utils::parseHexColor ($ rgb );
1186
+
1187
+ [$ lr , $ lg , $ lb ] = [
1188
+ pow ($ r / 255 , 2.4 ),
1189
+ pow ($ g / 255 , 2.4 ),
1190
+ pow ($ b / 255 , 2.4 ),
1191
+ ];
1192
+
1193
+ return 0.2126 * $ lr + 0.7152 * $ lg + 0.0722 * $ lb ;
1194
+ }
1195
+
1196
+ private function apcaContrast (string $ fgColor , string $ bgColor ): float
1197
+ {
1198
+ // Based on WCAG 3.x (https://www.w3.org/TR/wcag-3.0/)
1199
+ $ luminanceForeground = $ this ->relativeLuminance ($ fgColor );
1200
+ $ luminanceBackground = $ this ->relativeLuminance ($ bgColor );
1201
+
1202
+ $ contrast = ($ luminanceBackground > $ luminanceForeground )
1203
+ ? (pow ($ luminanceBackground , 0.56 ) - pow ($ luminanceForeground , 0.57 )) * 1.14
1204
+ : (pow ($ luminanceBackground , 0.65 ) - pow ($ luminanceForeground , 0.62 )) * 1.14 ;
1205
+
1206
+ return round ($ contrast * 100 , 2 );
1207
+ }
1208
+
1209
+ /**
1210
+ * @return array{string, string}
1211
+ */
1212
+ private function hexToForegroundAndBorder (string $ rgb ): array
1213
+ {
1214
+ $ background = Utils::parseHexColor ($ rgb );
1215
+
1216
+ // Pick a border that's a bit darker.
1217
+ $ darker = $ background ;
1218
+ $ darker [0 ] = max ($ darker [0 ] - 64 , 0 );
1219
+ $ darker [1 ] = max ($ darker [1 ] - 64 , 0 );
1220
+ $ darker [2 ] = max ($ darker [2 ] - 64 , 0 );
1221
+ $ border = Utils::rgbToHex ($ darker );
1222
+
1223
+ // Pick the text color with the biggest absolute contrast.
1224
+ $ contrastWithWhite = $ this ->apcaContrast ('#ffffff ' , $ rgb );
1225
+ $ contrastWithBlack = $ this ->apcaContrast ('#000000 ' , $ rgb );
1226
+
1227
+ $ foreground = (abs ($ contrastWithBlack ) > abs ($ contrastWithWhite )) ? '#000000 ' : '#ffffff ' ;
1228
+
1229
+ return [$ foreground , $ border ];
1230
+ }
1231
+
1232
+ public function problemBadge (?ContestProblem $ problem , bool $ grayedOut = false ): string
1233
+ {
1234
+ $ rgb = Utils::convertToHex ($ problem ?->getColor() ?? '#ffffff ' );
1185
1235
if ($ grayedOut || empty ($ rgb )) {
1186
1236
$ rgb = Utils::convertToHex ('whitesmoke ' );
1187
1237
}
@@ -1197,7 +1247,7 @@ public function problemBadge(ContestProblem $problem, bool $grayedOut = false):
1197
1247
$ rgb ,
1198
1248
$ border ,
1199
1249
$ foreground ,
1200
- $ problem ->getShortname ()
1250
+ $ problem? ->getShortname() ?? ' ? '
1201
1251
);
1202
1252
}
1203
1253
0 commit comments