|
@@ -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
|