forked from lewisd32/avr-hsl2rgb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhsl2rgb.cpp
153 lines (128 loc) · 3.25 KB
/
hsl2rgb.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "hsl2rgb.h"
/*
* Original implementation by Johngineer.
* From: http://www.johngineer.com/blog/?p=1022
*
* Optimized by Derek Lewis.
* http://www.surprisingedge.com
*
* == Functional Differences ==
*
* Some comprimises were made in the new version in order to be able to do an
* efficient divide by 256, instead of the slower divide by 255, so the exact
* numeric output is slightly different, but never off by more than one.
*
* == Performance ==
*
* The new implementation is 13x faster on an ATmega328.
* May not have as much benifit on an ATtiny, with the lack of the hardware
* multiplier.
*
* == Size ==
*
* The hsl2rgb method adds only 152 bytes to the program.
* The hsl2rgb_orig method adds 390 bytes.
*
* == Examples ==
*
* See the "test" example sketch for a series of tests that verify results
* against the original algorithm.
*
* See the "perftest" example stetch for a comparison of performance.
*
*/
void hsl2rgb(uint16_t hue, uint8_t sat, uint8_t lum, uint8_t rgb[3]) {
uint8_t inverse_sat = (sat ^ 255);
uint8_t r_temp, g_temp, b_temp;
uint8_t hue_mod;
hue_mod = hue % 256; // same performance as hue & 0xff
if (hue < 256) {
r_temp = hue_mod ^ 255;
g_temp = hue_mod;
b_temp = 0;
} else if (hue < 512) {
r_temp = 0;
g_temp = hue_mod ^ 255;
b_temp = hue_mod;
} else if (hue < 768) {
r_temp = hue_mod;
g_temp = 0;
b_temp = hue_mod ^ 255;
} else {
r_temp = 0;
g_temp = 0;
b_temp = 0;
}
rgb[0] = r_temp;
rgb[1] = g_temp;
rgb[2] = b_temp;
for (int i = 0; i < 3; ++i) {
/*
* Original:
* rgb[i] = ((((rgb[i] * sat) / 255) + inverse_sat) * lum) / 255;
* Changed to:
* rgb[i] = ((((rgb[i] * (sat+1)) / 256) + inverse_sat) * (lum+1)) / 256;
* Produces slightly different output, but is much faster.
*
* Code below is functionally the same, but optimized to make better
* use of the ATmega's hardware multiplier.
*
*/
uint8_t t8 = rgb[i];
uint16_t t16 = t8 * sat;
t16 = t16 + t8; // Instead of * (sat+1)
t8 = t16 / 256; // same performance as t16 >> 8
t8 = t8 + inverse_sat;
t16 = t8 * lum;
t16 = t16 + t8; // Instead of * (lum+1)
t8 = t16 / 256; // same performance as t16 >> 8
rgb[i] = t8;
}
}
void hsl2rgb(uint16_t hue, uint8_t sat, uint8_t lum, uint8_t rgb_fraction[3], uint8_t rgb[3]) {
hsl2rgb(hue, sat, lum, rgb);
for (int i = 0; i < 3; ++i) {
uint16_t t16 = rgb[i] * (rgb_fraction[i]+1);
rgb[i] = t16 / 256;
}
}
void hsl2rgb_orig(uint16_t hue, uint8_t sat, uint8_t lum, uint8_t rgb[3]) {
uint16_t r_temp, g_temp, b_temp;
uint8_t hue_mod;
uint8_t inverse_sat = (sat ^ 255);
hue = hue % 768;
hue_mod = hue % 256;
if (hue < 256)
{
r_temp = hue_mod ^ 255;
g_temp = hue_mod;
b_temp = 0;
}
else if (hue < 512)
{
r_temp = 0;
g_temp = hue_mod ^ 255;
b_temp = hue_mod;
}
else if ( hue < 768)
{
r_temp = hue_mod;
g_temp = 0;
b_temp = hue_mod ^ 255;
}
else
{
r_temp = 0;
g_temp = 0;
b_temp = 0;
}
r_temp = ((r_temp * sat) / 255) + inverse_sat;
g_temp = ((g_temp * sat) / 255) + inverse_sat;
b_temp = ((b_temp * sat) / 255) + inverse_sat;
r_temp = (r_temp * lum) / 255;
g_temp = (g_temp * lum) / 255;
b_temp = (b_temp * lum) / 255;
rgb[0] = (uint8_t)r_temp;
rgb[1] = (uint8_t)g_temp;
rgb[2] = (uint8_t)b_temp;
}