diff options
Diffstat (limited to 'lib/lib8tion/scale8.h')
-rw-r--r-- | lib/lib8tion/scale8.h | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/lib/lib8tion/scale8.h b/lib/lib8tion/scale8.h new file mode 100644 index 000000000..9895fd4d7 --- /dev/null +++ b/lib/lib8tion/scale8.h | |||
@@ -0,0 +1,542 @@ | |||
1 | #ifndef __INC_LIB8TION_SCALE_H | ||
2 | #define __INC_LIB8TION_SCALE_H | ||
3 | |||
4 | ///@ingroup lib8tion | ||
5 | |||
6 | ///@defgroup Scaling Scaling functions | ||
7 | /// Fast, efficient 8-bit scaling functions specifically | ||
8 | /// designed for high-performance LED programming. | ||
9 | /// | ||
10 | /// Because of the AVR(Arduino) and ARM assembly language | ||
11 | /// implementations provided, using these functions often | ||
12 | /// results in smaller and faster code than the equivalent | ||
13 | /// program using plain "C" arithmetic and logic. | ||
14 | ///@{ | ||
15 | |||
16 | /// scale one byte by a second one, which is treated as | ||
17 | /// the numerator of a fraction whose denominator is 256 | ||
18 | /// In other words, it computes i * (scale / 256) | ||
19 | /// 4 clocks AVR with MUL, 2 clocks ARM | ||
20 | LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale) | ||
21 | { | ||
22 | #if SCALE8_C == 1 | ||
23 | #if (FASTLED_SCALE8_FIXED == 1) | ||
24 | return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8; | ||
25 | #else | ||
26 | return ((uint16_t)i * (uint16_t)(scale) ) >> 8; | ||
27 | #endif | ||
28 | #elif SCALE8_AVRASM == 1 | ||
29 | #if defined(LIB8_ATTINY) | ||
30 | #if (FASTLED_SCALE8_FIXED == 1) | ||
31 | uint8_t work=i; | ||
32 | #else | ||
33 | uint8_t work=0; | ||
34 | #endif | ||
35 | uint8_t cnt=0x80; | ||
36 | asm volatile( | ||
37 | #if (FASTLED_SCALE8_FIXED == 1) | ||
38 | " inc %[scale] \n\t" | ||
39 | " breq DONE_%= \n\t" | ||
40 | " clr %[work] \n\t" | ||
41 | #endif | ||
42 | "LOOP_%=: \n\t" | ||
43 | /*" sbrc %[scale], 0 \n\t" | ||
44 | " add %[work], %[i] \n\t" | ||
45 | " ror %[work] \n\t" | ||
46 | " lsr %[scale] \n\t" | ||
47 | " clc \n\t"*/ | ||
48 | " sbrc %[scale], 0 \n\t" | ||
49 | " add %[work], %[i] \n\t" | ||
50 | " ror %[work] \n\t" | ||
51 | " lsr %[scale] \n\t" | ||
52 | " lsr %[cnt] \n\t" | ||
53 | "brcc LOOP_%= \n\t" | ||
54 | "DONE_%=: \n\t" | ||
55 | : [work] "+r" (work), [cnt] "+r" (cnt) | ||
56 | : [scale] "r" (scale), [i] "r" (i) | ||
57 | : | ||
58 | ); | ||
59 | return work; | ||
60 | #else | ||
61 | asm volatile( | ||
62 | #if (FASTLED_SCALE8_FIXED==1) | ||
63 | // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 | ||
64 | "mul %0, %1 \n\t" | ||
65 | // Add i to r0, possibly setting the carry flag | ||
66 | "add r0, %0 \n\t" | ||
67 | // load the immediate 0 into i (note, this does _not_ touch any flags) | ||
68 | "ldi %0, 0x00 \n\t" | ||
69 | // walk and chew gum at the same time | ||
70 | "adc %0, r1 \n\t" | ||
71 | #else | ||
72 | /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ | ||
73 | "mul %0, %1 \n\t" | ||
74 | /* Move the high 8-bits of the product (r1) back to i */ | ||
75 | "mov %0, r1 \n\t" | ||
76 | /* Restore r1 to "0"; it's expected to always be that */ | ||
77 | #endif | ||
78 | "clr __zero_reg__ \n\t" | ||
79 | |||
80 | : "+a" (i) /* writes to i */ | ||
81 | : "a" (scale) /* uses scale */ | ||
82 | : "r0", "r1" /* clobbers r0, r1 */ ); | ||
83 | |||
84 | /* Return the result */ | ||
85 | return i; | ||
86 | #endif | ||
87 | #else | ||
88 | #error "No implementation for scale8 available." | ||
89 | #endif | ||
90 | } | ||
91 | |||
92 | |||
93 | /// The "video" version of scale8 guarantees that the output will | ||
94 | /// be only be zero if one or both of the inputs are zero. If both | ||
95 | /// inputs are non-zero, the output is guaranteed to be non-zero. | ||
96 | /// This makes for better 'video'/LED dimming, at the cost of | ||
97 | /// several additional cycles. | ||
98 | LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale) | ||
99 | { | ||
100 | #if SCALE8_C == 1 || defined(LIB8_ATTINY) | ||
101 | uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); | ||
102 | // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; | ||
103 | // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale; | ||
104 | return j; | ||
105 | #elif SCALE8_AVRASM == 1 | ||
106 | uint8_t j=0; | ||
107 | asm volatile( | ||
108 | " tst %[i]\n\t" | ||
109 | " breq L_%=\n\t" | ||
110 | " mul %[i], %[scale]\n\t" | ||
111 | " mov %[j], r1\n\t" | ||
112 | " clr __zero_reg__\n\t" | ||
113 | " cpse %[scale], r1\n\t" | ||
114 | " subi %[j], 0xFF\n\t" | ||
115 | "L_%=: \n\t" | ||
116 | : [j] "+a" (j) | ||
117 | : [i] "a" (i), [scale] "a" (scale) | ||
118 | : "r0", "r1"); | ||
119 | |||
120 | return j; | ||
121 | // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; | ||
122 | // asm volatile( | ||
123 | // " tst %0 \n" | ||
124 | // " breq L_%= \n" | ||
125 | // " mul %0, %1 \n" | ||
126 | // " mov %0, r1 \n" | ||
127 | // " add %0, %2 \n" | ||
128 | // " clr __zero_reg__ \n" | ||
129 | // "L_%=: \n" | ||
130 | |||
131 | // : "+a" (i) | ||
132 | // : "a" (scale), "a" (nonzeroscale) | ||
133 | // : "r0", "r1"); | ||
134 | |||
135 | // // Return the result | ||
136 | // return i; | ||
137 | #else | ||
138 | #error "No implementation for scale8_video available." | ||
139 | #endif | ||
140 | } | ||
141 | |||
142 | |||
143 | /// This version of scale8 does not clean up the R1 register on AVR | ||
144 | /// If you are doing several 'scale8's in a row, use this, and | ||
145 | /// then explicitly call cleanup_R1. | ||
146 | LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale) | ||
147 | { | ||
148 | #if SCALE8_C == 1 | ||
149 | #if (FASTLED_SCALE8_FIXED == 1) | ||
150 | return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8; | ||
151 | #else | ||
152 | return ((int)i * (int)(scale) ) >> 8; | ||
153 | #endif | ||
154 | #elif SCALE8_AVRASM == 1 | ||
155 | asm volatile( | ||
156 | #if (FASTLED_SCALE8_FIXED==1) | ||
157 | // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 | ||
158 | "mul %0, %1 \n\t" | ||
159 | // Add i to r0, possibly setting the carry flag | ||
160 | "add r0, %0 \n\t" | ||
161 | // load the immediate 0 into i (note, this does _not_ touch any flags) | ||
162 | "ldi %0, 0x00 \n\t" | ||
163 | // walk and chew gum at the same time | ||
164 | "adc %0, r1 \n\t" | ||
165 | #else | ||
166 | /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */ | ||
167 | "mul %0, %1 \n\t" | ||
168 | /* Move the high 8-bits of the product (r1) back to i */ | ||
169 | "mov %0, r1 \n\t" | ||
170 | #endif | ||
171 | /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */ | ||
172 | /* "clr __zero_reg__ \n\t" */ | ||
173 | |||
174 | : "+a" (i) /* writes to i */ | ||
175 | : "a" (scale) /* uses scale */ | ||
176 | : "r0", "r1" /* clobbers r0, r1 */ ); | ||
177 | |||
178 | // Return the result | ||
179 | return i; | ||
180 | #else | ||
181 | #error "No implementation for scale8_LEAVING_R1_DIRTY available." | ||
182 | #endif | ||
183 | } | ||
184 | |||
185 | |||
186 | /// This version of scale8_video does not clean up the R1 register on AVR | ||
187 | /// If you are doing several 'scale8_video's in a row, use this, and | ||
188 | /// then explicitly call cleanup_R1. | ||
189 | LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale) | ||
190 | { | ||
191 | #if SCALE8_C == 1 || defined(LIB8_ATTINY) | ||
192 | uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); | ||
193 | // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; | ||
194 | // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale; | ||
195 | return j; | ||
196 | #elif SCALE8_AVRASM == 1 | ||
197 | uint8_t j=0; | ||
198 | asm volatile( | ||
199 | " tst %[i]\n\t" | ||
200 | " breq L_%=\n\t" | ||
201 | " mul %[i], %[scale]\n\t" | ||
202 | " mov %[j], r1\n\t" | ||
203 | " breq L_%=\n\t" | ||
204 | " subi %[j], 0xFF\n\t" | ||
205 | "L_%=: \n\t" | ||
206 | : [j] "+a" (j) | ||
207 | : [i] "a" (i), [scale] "a" (scale) | ||
208 | : "r0", "r1"); | ||
209 | |||
210 | return j; | ||
211 | // uint8_t nonzeroscale = (scale != 0) ? 1 : 0; | ||
212 | // asm volatile( | ||
213 | // " tst %0 \n" | ||
214 | // " breq L_%= \n" | ||
215 | // " mul %0, %1 \n" | ||
216 | // " mov %0, r1 \n" | ||
217 | // " add %0, %2 \n" | ||
218 | // " clr __zero_reg__ \n" | ||
219 | // "L_%=: \n" | ||
220 | |||
221 | // : "+a" (i) | ||
222 | // : "a" (scale), "a" (nonzeroscale) | ||
223 | // : "r0", "r1"); | ||
224 | |||
225 | // // Return the result | ||
226 | // return i; | ||
227 | #else | ||
228 | #error "No implementation for scale8_video_LEAVING_R1_DIRTY available." | ||
229 | #endif | ||
230 | } | ||
231 | |||
232 | /// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls | ||
233 | LIB8STATIC_ALWAYS_INLINE void cleanup_R1(void) | ||
234 | { | ||
235 | #if CLEANUP_R1_AVRASM == 1 | ||
236 | // Restore r1 to "0"; it's expected to always be that | ||
237 | asm volatile( "clr __zero_reg__ \n\t" : : : "r1" ); | ||
238 | #endif | ||
239 | } | ||
240 | |||
241 | |||
242 | /// scale a 16-bit unsigned value by an 8-bit value, | ||
243 | /// considered as numerator of a fraction whose denominator | ||
244 | /// is 256. In other words, it computes i * (scale / 256) | ||
245 | |||
246 | LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale ) | ||
247 | { | ||
248 | #if SCALE16BY8_C == 1 | ||
249 | uint16_t result; | ||
250 | #if FASTLED_SCALE8_FIXED == 1 | ||
251 | result = (i * (1+((uint16_t)scale))) >> 8; | ||
252 | #else | ||
253 | result = (i * scale) / 256; | ||
254 | #endif | ||
255 | return result; | ||
256 | #elif SCALE16BY8_AVRASM == 1 | ||
257 | #if FASTLED_SCALE8_FIXED == 1 | ||
258 | uint16_t result = 0; | ||
259 | asm volatile( | ||
260 | // result.A = HighByte( (i.A x scale) + i.A ) | ||
261 | " mul %A[i], %[scale] \n\t" | ||
262 | " add r0, %A[i] \n\t" | ||
263 | // " adc r1, [zero] \n\t" | ||
264 | // " mov %A[result], r1 \n\t" | ||
265 | " adc %A[result], r1 \n\t" | ||
266 | |||
267 | // result.A-B += i.B x scale | ||
268 | " mul %B[i], %[scale] \n\t" | ||
269 | " add %A[result], r0 \n\t" | ||
270 | " adc %B[result], r1 \n\t" | ||
271 | |||
272 | // cleanup r1 | ||
273 | " clr __zero_reg__ \n\t" | ||
274 | |||
275 | // result.A-B += i.B | ||
276 | " add %A[result], %B[i] \n\t" | ||
277 | " adc %B[result], __zero_reg__ \n\t" | ||
278 | |||
279 | : [result] "+r" (result) | ||
280 | : [i] "r" (i), [scale] "r" (scale) | ||
281 | : "r0", "r1" | ||
282 | ); | ||
283 | return result; | ||
284 | #else | ||
285 | uint16_t result = 0; | ||
286 | asm volatile( | ||
287 | // result.A = HighByte(i.A x j ) | ||
288 | " mul %A[i], %[scale] \n\t" | ||
289 | " mov %A[result], r1 \n\t" | ||
290 | //" clr %B[result] \n\t" | ||
291 | |||
292 | // result.A-B += i.B x j | ||
293 | " mul %B[i], %[scale] \n\t" | ||
294 | " add %A[result], r0 \n\t" | ||
295 | " adc %B[result], r1 \n\t" | ||
296 | |||
297 | // cleanup r1 | ||
298 | " clr __zero_reg__ \n\t" | ||
299 | |||
300 | : [result] "+r" (result) | ||
301 | : [i] "r" (i), [scale] "r" (scale) | ||
302 | : "r0", "r1" | ||
303 | ); | ||
304 | return result; | ||
305 | #endif | ||
306 | #else | ||
307 | #error "No implementation for scale16by8 available." | ||
308 | #endif | ||
309 | } | ||
310 | |||
311 | /// scale a 16-bit unsigned value by a 16-bit value, | ||
312 | /// considered as numerator of a fraction whose denominator | ||
313 | /// is 65536. In other words, it computes i * (scale / 65536) | ||
314 | |||
315 | LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale ) | ||
316 | { | ||
317 | #if SCALE16_C == 1 | ||
318 | uint16_t result; | ||
319 | #if FASTLED_SCALE8_FIXED == 1 | ||
320 | result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536; | ||
321 | #else | ||
322 | result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536; | ||
323 | #endif | ||
324 | return result; | ||
325 | #elif SCALE16_AVRASM == 1 | ||
326 | #if FASTLED_SCALE8_FIXED == 1 | ||
327 | // implemented sort of like | ||
328 | // result = ((i * scale) + i ) / 65536 | ||
329 | // | ||
330 | // why not like this, you may ask? | ||
331 | // result = (i * (scale+1)) / 65536 | ||
332 | // the answer is that if scale is 65535, then scale+1 | ||
333 | // will be zero, which is not what we want. | ||
334 | uint32_t result; | ||
335 | asm volatile( | ||
336 | // result.A-B = i.A x scale.A | ||
337 | " mul %A[i], %A[scale] \n\t" | ||
338 | // save results... | ||
339 | // basic idea: | ||
340 | //" mov %A[result], r0 \n\t" | ||
341 | //" mov %B[result], r1 \n\t" | ||
342 | // which can be written as... | ||
343 | " movw %A[result], r0 \n\t" | ||
344 | // Because we're going to add i.A-B to | ||
345 | // result.A-D, we DO need to keep both | ||
346 | // the r0 and r1 portions of the product | ||
347 | // UNlike in the 'unfixed scale8' version. | ||
348 | // So the movw here is needed. | ||
349 | : [result] "=r" (result) | ||
350 | : [i] "r" (i), | ||
351 | [scale] "r" (scale) | ||
352 | : "r0", "r1" | ||
353 | ); | ||
354 | |||
355 | asm volatile( | ||
356 | // result.C-D = i.B x scale.B | ||
357 | " mul %B[i], %B[scale] \n\t" | ||
358 | //" mov %C[result], r0 \n\t" | ||
359 | //" mov %D[result], r1 \n\t" | ||
360 | " movw %C[result], r0 \n\t" | ||
361 | : [result] "+r" (result) | ||
362 | : [i] "r" (i), | ||
363 | [scale] "r" (scale) | ||
364 | : "r0", "r1" | ||
365 | ); | ||
366 | |||
367 | const uint8_t zero = 0; | ||
368 | asm volatile( | ||
369 | // result.B-D += i.B x scale.A | ||
370 | " mul %B[i], %A[scale] \n\t" | ||
371 | |||
372 | " add %B[result], r0 \n\t" | ||
373 | " adc %C[result], r1 \n\t" | ||
374 | " adc %D[result], %[zero] \n\t" | ||
375 | |||
376 | // result.B-D += i.A x scale.B | ||
377 | " mul %A[i], %B[scale] \n\t" | ||
378 | |||
379 | " add %B[result], r0 \n\t" | ||
380 | " adc %C[result], r1 \n\t" | ||
381 | " adc %D[result], %[zero] \n\t" | ||
382 | |||
383 | // cleanup r1 | ||
384 | " clr r1 \n\t" | ||
385 | |||
386 | : [result] "+r" (result) | ||
387 | : [i] "r" (i), | ||
388 | [scale] "r" (scale), | ||
389 | [zero] "r" (zero) | ||
390 | : "r0", "r1" | ||
391 | ); | ||
392 | |||
393 | asm volatile( | ||
394 | // result.A-D += i.A-B | ||
395 | " add %A[result], %A[i] \n\t" | ||
396 | " adc %B[result], %B[i] \n\t" | ||
397 | " adc %C[result], %[zero] \n\t" | ||
398 | " adc %D[result], %[zero] \n\t" | ||
399 | : [result] "+r" (result) | ||
400 | : [i] "r" (i), | ||
401 | [zero] "r" (zero) | ||
402 | ); | ||
403 | |||
404 | result = result >> 16; | ||
405 | return result; | ||
406 | #else | ||
407 | uint32_t result; | ||
408 | asm volatile( | ||
409 | // result.A-B = i.A x scale.A | ||
410 | " mul %A[i], %A[scale] \n\t" | ||
411 | // save results... | ||
412 | // basic idea: | ||
413 | //" mov %A[result], r0 \n\t" | ||
414 | //" mov %B[result], r1 \n\t" | ||
415 | // which can be written as... | ||
416 | " movw %A[result], r0 \n\t" | ||
417 | // We actually don't need to do anything with r0, | ||
418 | // as result.A is never used again here, so we | ||
419 | // could just move the high byte, but movw is | ||
420 | // one clock cycle, just like mov, so might as | ||
421 | // well, in case we want to use this code for | ||
422 | // a generic 16x16 multiply somewhere. | ||
423 | |||
424 | : [result] "=r" (result) | ||
425 | : [i] "r" (i), | ||
426 | [scale] "r" (scale) | ||
427 | : "r0", "r1" | ||
428 | ); | ||
429 | |||
430 | asm volatile( | ||
431 | // result.C-D = i.B x scale.B | ||
432 | " mul %B[i], %B[scale] \n\t" | ||
433 | //" mov %C[result], r0 \n\t" | ||
434 | //" mov %D[result], r1 \n\t" | ||
435 | " movw %C[result], r0 \n\t" | ||
436 | : [result] "+r" (result) | ||
437 | : [i] "r" (i), | ||
438 | [scale] "r" (scale) | ||
439 | : "r0", "r1" | ||
440 | ); | ||
441 | |||
442 | const uint8_t zero = 0; | ||
443 | asm volatile( | ||
444 | // result.B-D += i.B x scale.A | ||
445 | " mul %B[i], %A[scale] \n\t" | ||
446 | |||
447 | " add %B[result], r0 \n\t" | ||
448 | " adc %C[result], r1 \n\t" | ||
449 | " adc %D[result], %[zero] \n\t" | ||
450 | |||
451 | // result.B-D += i.A x scale.B | ||
452 | " mul %A[i], %B[scale] \n\t" | ||
453 | |||
454 | " add %B[result], r0 \n\t" | ||
455 | " adc %C[result], r1 \n\t" | ||
456 | " adc %D[result], %[zero] \n\t" | ||
457 | |||
458 | // cleanup r1 | ||
459 | " clr r1 \n\t" | ||
460 | |||
461 | : [result] "+r" (result) | ||
462 | : [i] "r" (i), | ||
463 | [scale] "r" (scale), | ||
464 | [zero] "r" (zero) | ||
465 | : "r0", "r1" | ||
466 | ); | ||
467 | |||
468 | result = result >> 16; | ||
469 | return result; | ||
470 | #endif | ||
471 | #else | ||
472 | #error "No implementation for scale16 available." | ||
473 | #endif | ||
474 | } | ||
475 | ///@} | ||
476 | |||
477 | ///@defgroup Dimming Dimming and brightening functions | ||
478 | /// | ||
479 | /// Dimming and brightening functions | ||
480 | /// | ||
481 | /// The eye does not respond in a linear way to light. | ||
482 | /// High speed PWM'd LEDs at 50% duty cycle appear far | ||
483 | /// brighter then the 'half as bright' you might expect. | ||
484 | /// | ||
485 | /// If you want your midpoint brightness leve (128) to | ||
486 | /// appear half as bright as 'full' brightness (255), you | ||
487 | /// have to apply a 'dimming function'. | ||
488 | ///@{ | ||
489 | |||
490 | /// Adjust a scaling value for dimming | ||
491 | LIB8STATIC uint8_t dim8_raw( uint8_t x) | ||
492 | { | ||
493 | return scale8( x, x); | ||
494 | } | ||
495 | |||
496 | /// Adjust a scaling value for dimming for video (value will never go below 1) | ||
497 | LIB8STATIC uint8_t dim8_video( uint8_t x) | ||
498 | { | ||
499 | return scale8_video( x, x); | ||
500 | } | ||
501 | |||
502 | /// Linear version of the dimming function that halves for values < 128 | ||
503 | LIB8STATIC uint8_t dim8_lin( uint8_t x ) | ||
504 | { | ||
505 | if( x & 0x80 ) { | ||
506 | x = scale8( x, x); | ||
507 | } else { | ||
508 | x += 1; | ||
509 | x /= 2; | ||
510 | } | ||
511 | return x; | ||
512 | } | ||
513 | |||
514 | /// inverse of the dimming function, brighten a value | ||
515 | LIB8STATIC uint8_t brighten8_raw( uint8_t x) | ||
516 | { | ||
517 | uint8_t ix = 255 - x; | ||
518 | return 255 - scale8( ix, ix); | ||
519 | } | ||
520 | |||
521 | /// inverse of the dimming function, brighten a value | ||
522 | LIB8STATIC uint8_t brighten8_video( uint8_t x) | ||
523 | { | ||
524 | uint8_t ix = 255 - x; | ||
525 | return 255 - scale8_video( ix, ix); | ||
526 | } | ||
527 | |||
528 | /// inverse of the dimming function, brighten a value | ||
529 | LIB8STATIC uint8_t brighten8_lin( uint8_t x ) | ||
530 | { | ||
531 | uint8_t ix = 255 - x; | ||
532 | if( ix & 0x80 ) { | ||
533 | ix = scale8( ix, ix); | ||
534 | } else { | ||
535 | ix += 1; | ||
536 | ix /= 2; | ||
537 | } | ||
538 | return 255 - ix; | ||
539 | } | ||
540 | |||
541 | ///@} | ||
542 | #endif | ||