Browse Source

Added f.lux like color temperature adjustment

Thomas Buck 7 years ago
parent
commit
afd12b5b2a
No account linked to committer's email address
2 changed files with 173 additions and 2 deletions
  1. 172
    1
      DisplayBacklight/AppDelegate.m
  2. 1
    1
      DisplayBacklight/Info.plist

+ 172
- 1
DisplayBacklight/AppDelegate.m View File

@@ -82,7 +82,7 @@ struct LEDStrand strands[] = {
82 82
 // but will probably cause high CPU-Usage.
83 83
 // (Run-Time of the algorithm is ignored here, so real speed will be
84 84
 // slightly lower.)
85
-#define DISPLAY_DELAY (1.0 / 30.0)
85
+#define DISPLAY_DELAY (1.0 / 20.0)
86 86
 
87 87
 // How many pixels to skip when calculating the average color.
88 88
 // Slightly increases performance and doesn't really alter the result.
@@ -100,6 +100,9 @@ struct LEDStrand strands[] = {
100 100
 // If this is defined it will print the FPS every DEBUG_PRINT_FPS seconds
101 101
 //#define DEBUG_PRINT_FPS 10
102 102
 
103
+// ToDo Change color-temperature depending on time of day to match f.lux adjustments
104
+#define TARGET_COLOR_TEMPERATURE 2800.0
105
+
103 106
 // ------------------------ Config ends here ------------------------
104 107
 
105 108
 @interface AppDelegate ()
@@ -455,9 +458,18 @@ UInt8 ledColorData[LED_COUNT * 3];
455 458
                 // Calculate average color for this led
456 459
                 UInt32 color = [self calculateAverage:data Width:width Height:height SPP:spp Alpha:alpha StartX:x StartY:y EndX:endX EndY:endY];
457 460
                 
461
+#ifdef TARGET_COLOR_TEMPERATURE
462
+                struct Color3 c = { ((color & 0xFF0000) >> 16) / 255.0, ((color & 0x00FF00) >> 8) / 255.0, (color & 0x0000FF) / 255.0 };
463
+                c = getRGBfromTemperature(TARGET_COLOR_TEMPERATURE, c);
464
+                
465
+                ledColorData[led * 3] = (int)(c.r * 255.0);
466
+                ledColorData[(led * 3) + 1] = (int)(c.g * 255.0);
467
+                ledColorData[(led * 3) + 2] = (int)(c.b * 255.0);
468
+#else
458 469
                 ledColorData[led * 3] = (color & 0xFF0000) >> 16;
459 470
                 ledColorData[(led * 3) + 1] = (color & 0x00FF00) >> 8;
460 471
                 ledColorData[(led * 3) + 2] = color & 0x0000FF;
472
+#endif
461 473
                 
462 474
                 // Move to next LED
463 475
                 if ((strands[i].direction == DIR_LEFT) || (strands[i].direction == DIR_RIGHT)) {
@@ -526,4 +538,163 @@ UInt8 ledColorData[LED_COUNT * 3];
526 538
     timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:NO];
527 539
 }
528 540
 
541
+// ----------------------------------------------------
542
+// ----------- Color Temperature Adjustment -----------
543
+// ----------------------------------------------------
544
+
545
+#define LUMINANCE_PRESERVATION 0.75
546
+#define EPSILON 1e-10
547
+#define SATURATION_FACTOR 0.9
548
+
549
+struct Color3 {
550
+    float r, g, b;
551
+};
552
+
553
+float saturateFloat(float v) {
554
+    if (v < 0.0f) {
555
+        return 0.0f;
556
+    } else if (v > 1.0f) {
557
+        return 1.0f;
558
+    } else {
559
+        return v;
560
+    }
561
+}
562
+
563
+struct Color3 saturateColor(struct Color3 v) {
564
+    v.r = saturateFloat(v.r);
565
+    v.g = saturateFloat(v.g);
566
+    v.b = saturateFloat(v.b);
567
+    return v;
568
+}
569
+
570
+struct Color3 colorTemperatureToRGB(float temperatureInKelvins) {
571
+    struct Color3 retColor;
572
+    
573
+    if (temperatureInKelvins < 1000.0) {
574
+        temperatureInKelvins = 1000.0;
575
+    } else if (temperatureInKelvins > 40000) {
576
+        temperatureInKelvins = 40000.0;
577
+    }
578
+    
579
+    temperatureInKelvins /= 100.0;
580
+    
581
+    if (temperatureInKelvins <= 66.0) {
582
+        retColor.r = 1.0;
583
+        retColor.g = saturateFloat(0.39008157876901960784 * log(temperatureInKelvins) - 0.63184144378862745098);
584
+    } else {
585
+        float t = temperatureInKelvins - 60.0;
586
+        retColor.r = saturateFloat(1.29293618606274509804 * pow(t, -0.1332047592));
587
+        retColor.g = saturateFloat(1.12989086089529411765 * pow(t, -0.0755148492));
588
+    }
589
+    
590
+    if (temperatureInKelvins >= 66.0) {
591
+        retColor.b = 1.0;
592
+    } else if (temperatureInKelvins <= 19.0) {
593
+        retColor.b = 0.0;
594
+    } else {
595
+        retColor.b = saturateFloat(0.54320678911019607843 * log(temperatureInKelvins - 10.0) - 1.19625408914);
596
+    }
597
+    
598
+    return retColor;
599
+}
600
+
601
+float luminance(struct Color3 color) {
602
+    float min = fmin(fmin(color.r, color.g), color.b);
603
+    float max = fmax(fmax(color.r, color.g), color.b);
604
+    return (max + min) / 2.0;
605
+}
606
+
607
+struct Color3 HUEtoRGB(float h) {
608
+    float r = fabs(h * 6.0 - 3.0) - 1.0;
609
+    float g = 2.0 - fabs(h * 6.0 - 2.0);
610
+    float b = 2.0 - fabs(h * 6.0 - 4.0);
611
+    struct Color3 ret = { r, g, b };
612
+    return saturateColor(ret);
613
+}
614
+
615
+struct Color3 HSLtoRGB(struct Color3 hsl) {
616
+    struct Color3 rgb = HUEtoRGB(hsl.r);
617
+    float c = (1.0 - fabs(2.0 * hsl.b - 1.0)) * hsl.g;
618
+    struct Color3 ret = { (rgb.r - 0.5) * c + hsl.b, (rgb.g - 0.5) * c + hsl.b, (rgb.b - 0.5) * c + hsl.b };
619
+    return ret;
620
+}
621
+
622
+struct Color3 RGBtoHCV(struct Color3 rgb) {
623
+    // Based on work by Sam Hocevar and Emil Persson
624
+    
625
+    struct Color3 p;
626
+    float pw;
627
+    if (rgb.g < rgb.b) {
628
+        p.r = rgb.b;
629
+        p.g = rgb.g;
630
+        p.b = -1.0;
631
+        pw = 2.0 / 3.0;
632
+    } else {
633
+        p.r = rgb.g;
634
+        p.g = rgb.b;
635
+        p.b = 0.0;
636
+        pw = -1.0 / 3.0;
637
+    }
638
+    
639
+    struct Color3 q;
640
+    float qw;
641
+    if (rgb.r < p.r) {
642
+        q.r = p.r;
643
+        q.g = p.g;
644
+        q.b = pw;
645
+        qw = rgb.r;
646
+    } else {
647
+        q.r = rgb.r;
648
+        q.g = p.g;
649
+        q.b = p.b;
650
+        qw = p.r;
651
+    }
652
+    
653
+    float c = q.r - fmin(qw, q.g);
654
+    float h = fabs((qw - q.g) / (6.0 * c + EPSILON) + q.b);
655
+    
656
+    struct Color3 res = { h, c, q.r };
657
+    return res;
658
+}
659
+
660
+struct Color3 RGBtoHSL(struct Color3 rgb) {
661
+    struct Color3 hcv = RGBtoHCV(rgb);
662
+    float l = hcv.b - hcv.g * 0.5;
663
+    float s = hcv.g / (1.0 - fabs(l * 2.0 - 1.0) + EPSILON);
664
+    struct Color3 res = { hcv.r, s, l };
665
+    return res;
666
+}
667
+
668
+float mixFloat(float a, float b, float factor) {
669
+    return a + ((b - a) * factor);
670
+}
671
+
672
+struct Color3 mixColor(struct Color3 a, struct Color3 b, float factor) {
673
+    struct Color3 res;
674
+    res.r = mixFloat(a.r, b.r, factor);
675
+    res.g = mixFloat(a.g, b.g, factor);
676
+    res.b = mixFloat(a.b, b.b, factor);
677
+    return res;
678
+}
679
+
680
+struct Color3 blendColor(struct Color3 a, struct Color3 b) {
681
+    struct Color3 res;
682
+    res.r = a.r * b.r;
683
+    res.g = a.g * b.g;
684
+    res.b = a.b * b.b;
685
+    return res;
686
+}
687
+
688
+// http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
689
+// Given a temperature (in Kelvin), estimate an RGB equivalent
690
+struct Color3 getRGBfromTemperature(float temperature, struct Color3 color) {
691
+    struct Color3 tempColor = colorTemperatureToRGB(temperature);
692
+    float originalLuminance = luminance(color);
693
+    struct Color3 blended = mixColor(color, blendColor(color, tempColor), SATURATION_FACTOR);
694
+    struct Color3 resultHSL = RGBtoHSL(blended);
695
+    struct Color3 converted = { resultHSL.r, resultHSL.g, originalLuminance };
696
+    struct Color3 luminancePreservedRGB = HSLtoRGB(converted);
697
+    return mixColor(blended, luminancePreservedRGB, LUMINANCE_PRESERVATION);
698
+}
699
+
529 700
 @end

+ 1
- 1
DisplayBacklight/Info.plist View File

@@ -21,7 +21,7 @@
21 21
 	<key>CFBundleSignature</key>
22 22
 	<string>????</string>
23 23
 	<key>CFBundleVersion</key>
24
-	<string>81</string>
24
+	<string>99</string>
25 25
 	<key>LSApplicationCategoryType</key>
26 26
 	<string>public.app-category.utilities</string>
27 27
 	<key>LSMinimumSystemVersion</key>

Loading…
Cancel
Save