FastLED  3.1
scale8.h
1 #ifndef __INC_LIB8TION_SCALE_H
2 #define __INC_LIB8TION_SCALE_H
3 
5 
15 
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 
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 
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 
189 
190 LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY( uint8_t& i, fract8 scale)
191 {
192 #if SCALE8_C == 1
193 #if (FASTLED_SCALE8_FIXED == 1)
194  i = (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
195 #else
196  i = ((int)i * (int)(scale) ) >> 8;
197 #endif
198 #elif SCALE8_AVRASM == 1
199  asm volatile(
200  #if (FASTLED_SCALE8_FIXED==1)
201  // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
202  "mul %0, %1 \n\t"
203  // Add i to r0, possibly setting the carry flag
204  "add r0, %0 \n\t"
205  // load the immediate 0 into i (note, this does _not_ touch any flags)
206  "ldi %0, 0x00 \n\t"
207  // walk and chew gum at the same time
208  "adc %0, r1 \n\t"
209  #else
210  /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
211  "mul %0, %1 \n\t"
212  /* Move the high 8-bits of the product (r1) back to i */
213  "mov %0, r1 \n\t"
214  #endif
215  /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
216  /* "clr __zero_reg__ \n\t" */
217 
218  : "+a" (i) /* writes to i */
219  : "a" (scale) /* uses scale */
220  : "r0", "r1" /* clobbers r0, r1 */ );
221 #else
222 #error "No implementation for nscale8_LEAVING_R1_DIRTY available."
223 #endif
224 }
225 
226 
230 LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
231 {
232 #if SCALE8_C == 1 || defined(LIB8_ATTINY)
233  uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
234  // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
235  // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
236  return j;
237 #elif SCALE8_AVRASM == 1
238  uint8_t j=0;
239  asm volatile(
240  " tst %[i]\n\t"
241  " breq L_%=\n\t"
242  " mul %[i], %[scale]\n\t"
243  " mov %[j], r1\n\t"
244  " breq L_%=\n\t"
245  " subi %[j], 0xFF\n\t"
246  "L_%=: \n\t"
247  : [j] "+a" (j)
248  : [i] "a" (i), [scale] "a" (scale)
249  : "r0", "r1");
250 
251  return j;
252  // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
253  // asm volatile(
254  // " tst %0 \n"
255  // " breq L_%= \n"
256  // " mul %0, %1 \n"
257  // " mov %0, r1 \n"
258  // " add %0, %2 \n"
259  // " clr __zero_reg__ \n"
260  // "L_%=: \n"
261 
262  // : "+a" (i)
263  // : "a" (scale), "a" (nonzeroscale)
264  // : "r0", "r1");
265 
266  // // Return the result
267  // return i;
268 #else
269 #error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
270 #endif
271 }
272 
277 LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY( uint8_t & i, fract8 scale)
278 {
279 #if SCALE8_C == 1 || defined(LIB8_ATTINY)
280  i = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
281 #elif SCALE8_AVRASM == 1
282  asm volatile(
283  " tst %[i]\n\t"
284  " breq L_%=\n\t"
285  " mul %[i], %[scale]\n\t"
286  " mov %[i], r1\n\t"
287  " breq L_%=\n\t"
288  " subi %[i], 0xFF\n\t"
289  "L_%=: \n\t"
290  : [i] "+a" (i)
291  : [scale] "a" (scale)
292  : "r0", "r1");
293 #else
294 #error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
295 #endif
296 }
297 
299 LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
300 {
301 #if CLEANUP_R1_AVRASM == 1
302  // Restore r1 to "0"; it's expected to always be that
303  asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
304 #endif
305 }
306 
307 
313 
314 LIB8STATIC void nscale8x3( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
315 {
316 #if SCALE8_C == 1
317 #if (FASTLED_SCALE8_FIXED == 1)
318  uint16_t scale_fixed = scale + 1;
319  r = (((uint16_t)r) * scale_fixed) >> 8;
320  g = (((uint16_t)g) * scale_fixed) >> 8;
321  b = (((uint16_t)b) * scale_fixed) >> 8;
322 #else
323  r = ((int)r * (int)(scale) ) >> 8;
324  g = ((int)g * (int)(scale) ) >> 8;
325  b = ((int)b * (int)(scale) ) >> 8;
326 #endif
327 #elif SCALE8_AVRASM == 1
328  r = scale8_LEAVING_R1_DIRTY(r, scale);
329  g = scale8_LEAVING_R1_DIRTY(g, scale);
330  b = scale8_LEAVING_R1_DIRTY(b, scale);
331  cleanup_R1();
332 #else
333 #error "No implementation for nscale8x3 available."
334 #endif
335 }
336 
344 LIB8STATIC void nscale8x3_video( uint8_t& r, uint8_t& g, uint8_t& b, fract8 scale)
345 {
346 #if SCALE8_C == 1
347  uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
348  r = (r == 0) ? 0 : (((int)r * (int)(scale) ) >> 8) + nonzeroscale;
349  g = (g == 0) ? 0 : (((int)g * (int)(scale) ) >> 8) + nonzeroscale;
350  b = (b == 0) ? 0 : (((int)b * (int)(scale) ) >> 8) + nonzeroscale;
351 #elif SCALE8_AVRASM == 1
355  cleanup_R1();
356 #else
357 #error "No implementation for nscale8x3 available."
358 #endif
359 }
360 
366 
367 LIB8STATIC void nscale8x2( uint8_t& i, uint8_t& j, fract8 scale)
368 {
369 #if SCALE8_C == 1
370 #if FASTLED_SCALE8_FIXED == 1
371  uint16_t scale_fixed = scale + 1;
372  i = (((uint16_t)i) * scale_fixed ) >> 8;
373  j = (((uint16_t)j) * scale_fixed ) >> 8;
374 #else
375  i = ((uint16_t)i * (uint16_t)(scale) ) >> 8;
376  j = ((uint16_t)j * (uint16_t)(scale) ) >> 8;
377 #endif
378 #elif SCALE8_AVRASM == 1
379  i = scale8_LEAVING_R1_DIRTY(i, scale);
380  j = scale8_LEAVING_R1_DIRTY(j, scale);
381  cleanup_R1();
382 #else
383 #error "No implementation for nscale8x2 available."
384 #endif
385 }
386 
394 
395 
396 LIB8STATIC void nscale8x2_video( uint8_t& i, uint8_t& j, fract8 scale)
397 {
398 #if SCALE8_C == 1
399  uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
400  i = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
401  j = (j == 0) ? 0 : (((int)j * (int)(scale) ) >> 8) + nonzeroscale;
402 #elif SCALE8_AVRASM == 1
405  cleanup_R1();
406 #else
407 #error "No implementation for nscale8x2 available."
408 #endif
409 }
410 
411 
415 
416 LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )
417 {
418 #if SCALE16BY8_C == 1
419  uint16_t result;
420 #if FASTLED_SCALE8_FIXED == 1
421  result = (i * (1+((uint16_t)scale))) >> 8;
422 #else
423  result = (i * scale) / 256;
424 #endif
425  return result;
426 #elif SCALE16BY8_AVRASM == 1
427  uint16_t result = 0;
428  asm volatile(
429  // result.A = HighByte(i.A x j )
430  " mul %A[i], %[scale] \n\t"
431  " mov %A[result], r1 \n\t"
432  //" clr %B[result] \n\t"
433 
434  // result.A-B += i.B x j
435  " mul %B[i], %[scale] \n\t"
436  " add %A[result], r0 \n\t"
437  " adc %B[result], r1 \n\t"
438 
439  // cleanup r1
440  " clr __zero_reg__ \n\t"
441 
442  : [result] "+r" (result)
443  : [i] "r" (i), [scale] "r" (scale)
444  : "r0", "r1"
445  );
446  return result;
447 #else
448  #error "No implementation for scale16by8 available."
449 #endif
450 }
451 
455 
456 LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
457 {
458  #if SCALE16_C == 1
459  uint16_t result;
460 #if FASTLED_SCALE8_FIXED == 1
461  result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;
462 #else
463  result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
464 #endif
465  return result;
466 #elif SCALE16_AVRASM == 1
467  uint32_t result;
468  asm volatile(
469  // result.A-B = i.A x scale.A
470  " mul %A[i], %A[scale] \n\t"
471  // save results...
472  // basic idea:
473  //" mov %A[result], r0 \n\t"
474  //" mov %B[result], r1 \n\t"
475  // which can be written as...
476  " movw %A[result], r0 \n\t"
477  // We actually need to do anything with r0,
478  // as result.A is never used again here, so we
479  // could just move the high byte, but movw is
480  // one clock cycle, just like mov, so might as
481  // well, in case we want to use this code for
482  // a generic 16x16 multiply somewhere.
483 
484  : [result] "=r" (result)
485  : [i] "r" (i),
486  [scale] "r" (scale)
487  : "r0", "r1"
488  );
489 
490  asm volatile(
491  // result.C-D = i.B x scale.B
492  " mul %B[i], %B[scale] \n\t"
493  //" mov %C[result], r0 \n\t"
494  //" mov %D[result], r1 \n\t"
495  " movw %C[result], r0 \n\t"
496  : [result] "+r" (result)
497  : [i] "r" (i),
498  [scale] "r" (scale)
499  : "r0", "r1"
500  );
501 
502  const uint8_t zero = 0;
503  asm volatile(
504  // result.B-D += i.B x scale.A
505  " mul %B[i], %A[scale] \n\t"
506 
507  " add %B[result], r0 \n\t"
508  " adc %C[result], r1 \n\t"
509  " adc %D[result], %[zero] \n\t"
510 
511  // result.B-D += i.A x scale.B
512  " mul %A[i], %B[scale] \n\t"
513 
514  " add %B[result], r0 \n\t"
515  " adc %C[result], r1 \n\t"
516  " adc %D[result], %[zero] \n\t"
517 
518  // cleanup r1
519  " clr r1 \n\t"
520 
521  : [result] "+r" (result)
522  : [i] "r" (i),
523  [scale] "r" (scale),
524  [zero] "r" (zero)
525  : "r0", "r1"
526  );
527 
528  result = result >> 16;
529  return result;
530 #else
531  #error "No implementation for scale16 available."
532 #endif
533 }
535 
548 
550 LIB8STATIC uint8_t dim8_raw( uint8_t x)
551 {
552  return scale8( x, x);
553 }
554 
556 LIB8STATIC uint8_t dim8_video( uint8_t x)
557 {
558  return scale8_video( x, x);
559 }
560 
562 LIB8STATIC uint8_t dim8_lin( uint8_t x )
563 {
564  if( x & 0x80 ) {
565  x = scale8( x, x);
566  } else {
567  x += 1;
568  x /= 2;
569  }
570  return x;
571 }
572 
574 LIB8STATIC uint8_t brighten8_raw( uint8_t x)
575 {
576  uint8_t ix = 255 - x;
577  return 255 - scale8( ix, ix);
578 }
579 
581 LIB8STATIC uint8_t brighten8_video( uint8_t x)
582 {
583  uint8_t ix = 255 - x;
584  return 255 - scale8_video( ix, ix);
585 }
586 
588 LIB8STATIC uint8_t brighten8_lin( uint8_t x )
589 {
590  uint8_t ix = 255 - x;
591  if( ix & 0x80 ) {
592  ix = scale8( ix, ix);
593  } else {
594  ix += 1;
595  ix /= 2;
596  }
597  return 255 - ix;
598 }
599 
601 #endif
LIB8STATIC uint8_t dim8_raw(uint8_t x)
Adjust a scaling value for dimming.
Definition: scale8.h:550
LIB8STATIC uint8_t brighten8_lin(uint8_t x)
inverse of the dimming function, brighten a value
Definition: scale8.h:588
LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY(uint8_t &i, fract8 scale)
In place modifying version of scale8_video, also this version of nscale8_video does not clean up the ...
Definition: scale8.h:277
LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls.
Definition: scale8.h:299
uint16_t fract16
ANSI: unsigned _Fract.
Definition: lib8tion.h:343
LIB8STATIC uint8_t dim8_lin(uint8_t x)
Linear version of the dimming function that halves for values < 128.
Definition: scale8.h:562
LIB8STATIC void nscale8x2_video(uint8_t &i, uint8_t &j, fract8 scale)
scale two one byte values by a third one, which is treated as the numerator of a fraction whose demom...
Definition: scale8.h:396
LIB8STATIC uint16_t scale16(uint16_t i, fract16 scale)
scale a 16-bit unsigned value by a 16-bit value, considered as numerator of a fraction whose denomina...
Definition: scale8.h:456
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY(uint8_t i, fract8 scale)
This version of scale8_video does not clean up the R1 register on AVR If you are doing several 'scale...
Definition: scale8.h:230
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video(uint8_t i, fract8 scale)
The "video" version of scale8 guarantees that the output will be only be zero if one or both of the i...
Definition: scale8.h:98
LIB8STATIC uint8_t brighten8_raw(uint8_t x)
inverse of the dimming function, brighten a value
Definition: scale8.h:574
LIB8STATIC void nscale8x3_video(uint8_t &r, uint8_t &g, uint8_t &b, fract8 scale)
scale three one byte values by a fourth one, which is treated as the numerator of a fraction whose de...
Definition: scale8.h:344
LIB8STATIC uint8_t brighten8_video(uint8_t x)
inverse of the dimming function, brighten a value
Definition: scale8.h:581
uint8_t fract8
ANSI unsigned short _Fract.
Definition: lib8tion.h:335
LIB8STATIC void nscale8x2(uint8_t &i, uint8_t &j, fract8 scale)
scale two one byte values by a third one, which is treated as the numerator of a fraction whose demom...
Definition: scale8.h:367
LIB8STATIC void nscale8x3(uint8_t &r, uint8_t &g, uint8_t &b, fract8 scale)
scale three one byte values by a fourth one, which is treated as the numerator of a fraction whose de...
Definition: scale8.h:314
LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY(uint8_t &i, fract8 scale)
In place modifying version of scale8, also this version of nscale8 does not clean up the R1 register ...
Definition: scale8.h:190
LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8(uint16_t i, fract8 scale)
scale a 16-bit unsigned value by an 8-bit value, considered as numerator of a fraction whose denomina...
Definition: scale8.h:416
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale)
scale one byte by a second one, which is treated as the numerator of a fraction whose denominator is ...
Definition: scale8.h:20
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY(uint8_t i, fract8 scale)
This version of scale8 does not clean up the R1 register on AVR If you are doing several 'scale8's in...
Definition: scale8.h:146
LIB8STATIC uint8_t dim8_video(uint8_t x)
Adjust a scaling value for dimming for video (value will never go below 1)
Definition: scale8.h:556