99
1010#include " randomcolor.h"
1111#include < cassert>
12+ #include < cmath>
13+ #include < algorithm>
1214
1315/* *
1416 * The map is formed with the help of the following sources:
@@ -273,3 +275,113 @@ int RandomColor::HSBtoRGB( double h, double s, double v ) const
273275 // Scale r, g, b to [0; 255] and pack them into a number 0xRRGGBB
274276 return (int (r * 255 ) << 16 ) + (int (g * 255 ) << 8 ) + int (b * 255 );
275277}
278+
279+ int RandomColor::generateWithContrast ( int textColor, Color color, Luminosity luminosity )
280+ {
281+ double textLuminance = calculateRelativeLuminance (textColor);
282+ double targetLuminance;
283+
284+ if (luminosity == Dark) {
285+ // Dark mode - need dark background: (textLum + 0.05) / (bgLum + 0.05) >= 4.5
286+ targetLuminance = (textLuminance + 0.05 ) / 4.5 - 0.05 ;
287+ targetLuminance = std::max (0.05 , std::min (0.4 , targetLuminance)); // Keep backgrounds reasonably dark
288+ } else {
289+ // Light mode - need bright background: (bgLum + 0.05) / (textLum + 0.05) >= 4.5
290+ double minRequired = (textLuminance + 0.05 ) * 4.5 - 0.05 ;
291+ targetLuminance = std::max (0.6 , std::min (0.9 , minRequired)); // Keep backgrounds reasonably bright but not blinding
292+ }
293+
294+ // Generate base hue and saturation
295+ const ColorInfo& info = (color == RandomHue) ? getColorInfo (randomWithin ({0 , 359 })) : colorMap[color];
296+ const int h = randomWithin (info.hRange );
297+ const int s = pickSaturation (info, luminosity);
298+
299+ // Generate color with exact target luminance
300+ return generateColorWithTargetLuminance (h, s, targetLuminance);
301+ }
302+
303+ double RandomColor::calculateContrastRatio ( int color1, int color2 )
304+ {
305+ double lum1 = calculateRelativeLuminance (color1);
306+ double lum2 = calculateRelativeLuminance (color2);
307+
308+ if (lum1 < lum2) {
309+ std::swap (lum1, lum2);
310+ }
311+
312+ return (lum1 + 0.05 ) / (lum2 + 0.05 );
313+ }
314+
315+ double RandomColor::calculateRelativeLuminance ( int rgbColor )
316+ {
317+ double r = ((rgbColor >> 16 ) & 0xFF ) / 255.0 ;
318+ double g = ((rgbColor >> 8 ) & 0xFF ) / 255.0 ;
319+ double b = (rgbColor & 0xFF ) / 255.0 ;
320+
321+ r = sRGBtoLinear (r);
322+ g = sRGBtoLinear (g);
323+ b = sRGBtoLinear (b);
324+
325+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
326+ }
327+
328+ double RandomColor::sRGBtoLinear ( double value )
329+ {
330+ if (value <= 0.03928 ) {
331+ return value / 12.92 ;
332+ } else {
333+ return pow ((value + 0.055 ) / 1.055 , 2.4 );
334+ }
335+ }
336+
337+ double RandomColor::linearTosRGB ( double value )
338+ {
339+ if (value <= 0.0031308 ) {
340+ return value * 12.92 ;
341+ } else {
342+ return 1.055 * pow (value, 1.0 /2.4 ) - 0.055 ;
343+ }
344+ }
345+
346+ int RandomColor::generateColorWithTargetLuminance ( int h, int s, double targetLuminance ) const
347+ {
348+ // Convert HSV to RGB to get the hue and saturation ratios
349+ double tempColor = HSBtoRGB (h, s, 50 ); // Use 50% brightness as baseline
350+ int tempR = (int (tempColor) >> 16 ) & 0xFF ;
351+ int tempG = (int (tempColor) >> 8 ) & 0xFF ;
352+ int tempB = int (tempColor) & 0xFF ;
353+
354+ // Calculate the relative contributions of R, G, B to luminance
355+ // L = 0.2126*R + 0.7152*G + 0.0722*B (in linear space)
356+ // We need to find a scaling factor that achieves target luminance
357+
358+ // Binary search for the right brightness multiplier
359+ double minBrightness = 0.0 ;
360+ double maxBrightness = 1.0 ;
361+
362+ for (int i = 0 ; i < 20 ; ++i) { // 20 iterations gives us good precision
363+ double testBrightness = (minBrightness + maxBrightness) / 2.0 ;
364+
365+ // Scale the RGB values by brightness
366+ int r = std::min (255 , (int )(tempR * testBrightness * 2.0 )); // *2 because we used 50% baseline
367+ int g = std::min (255 , (int )(tempG * testBrightness * 2.0 ));
368+ int b = std::min (255 , (int )(tempB * testBrightness * 2.0 ));
369+
370+ int testColor = (r << 16 ) | (g << 8 ) | b;
371+ double actualLuminance = calculateRelativeLuminance (testColor);
372+
373+ if (actualLuminance < targetLuminance) {
374+ minBrightness = testBrightness;
375+ } else {
376+ maxBrightness = testBrightness;
377+ }
378+ }
379+
380+ // Generate final color with calculated brightness
381+ double finalBrightness = (minBrightness + maxBrightness) / 2.0 ;
382+ int r = std::min (255 , (int )(tempR * finalBrightness * 2.0 ));
383+ int g = std::min (255 , (int )(tempG * finalBrightness * 2.0 ));
384+ int b = std::min (255 , (int )(tempB * finalBrightness * 2.0 ));
385+
386+ return (r << 16 ) | (g << 8 ) | b;
387+ }
0 commit comments