initial commit
This commit is contained in:
760
libraries/FastLED/src/lib8tion/scale8.h
Normal file
760
libraries/FastLED/src/lib8tion/scale8.h
Normal file
@@ -0,0 +1,760 @@
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lib8tion/config.h"
|
||||
#include "crgb.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fastled_config.h"
|
||||
#include "lib8static.h"
|
||||
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING_UNUSED_PARAMETER
|
||||
FL_DISABLE_WARNING_RETURN_TYPE
|
||||
FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// @file scale8.h
|
||||
/// Fast, efficient 8-bit scaling functions specifically
|
||||
/// designed for high-performance LED programming.
|
||||
|
||||
/// @addtogroup lib8tion
|
||||
/// @{
|
||||
|
||||
/// @defgroup Scaling Scaling Functions
|
||||
/// Fast, efficient 8-bit scaling functions specifically
|
||||
/// designed for high-performance LED programming.
|
||||
///
|
||||
/// Because of the AVR(Arduino) and ARM assembly language
|
||||
/// implementations provided, using these functions often
|
||||
/// results in smaller and faster code than the equivalent
|
||||
/// program using plain "C" arithmetic and logic.
|
||||
/// @{
|
||||
|
||||
/// Scale one byte by a second one, which is treated as
|
||||
/// the numerator of a fraction whose denominator is 256.
|
||||
///
|
||||
/// In other words, it computes i * (scale / 256)
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @returns scaled value
|
||||
/// @note Takes 4 clocks on AVR with MUL, 2 clocks on ARM
|
||||
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
return (((uint16_t)i) * (1 + (uint16_t)(scale))) >> 8;
|
||||
#else
|
||||
return ((uint16_t)i * (uint16_t)(scale)) >> 8;
|
||||
#endif
|
||||
#elif SCALE8_AVRASM == 1
|
||||
#if defined(LIB8_ATTINY)
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
uint8_t work = i;
|
||||
#else
|
||||
uint8_t work = 0;
|
||||
#endif
|
||||
uint8_t cnt = 0x80;
|
||||
asm volatile(
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
" inc %[scale] \n\t"
|
||||
" breq DONE_%= \n\t"
|
||||
" clr %[work] \n\t"
|
||||
#endif
|
||||
"LOOP_%=: \n\t"
|
||||
/*" sbrc %[scale], 0 \n\t"
|
||||
" add %[work], %[i] \n\t"
|
||||
" ror %[work] \n\t"
|
||||
" lsr %[scale] \n\t"
|
||||
" clc \n\t"*/
|
||||
" sbrc %[scale], 0 \n\t"
|
||||
" add %[work], %[i] \n\t"
|
||||
" ror %[work] \n\t"
|
||||
" lsr %[scale] \n\t"
|
||||
" lsr %[cnt] \n\t"
|
||||
"brcc LOOP_%= \n\t"
|
||||
"DONE_%=: \n\t"
|
||||
: [work] "+r"(work), [cnt] "+r"(cnt)
|
||||
: [scale] "r"(scale), [i] "r"(i)
|
||||
:);
|
||||
return work;
|
||||
#else
|
||||
asm volatile(
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
|
||||
"mul %0, %1 \n\t"
|
||||
// Add i to r0, possibly setting the carry flag
|
||||
"add r0, %0 \n\t"
|
||||
// load the immediate 0 into i (note, this does _not_ touch any flags)
|
||||
"ldi %0, 0x00 \n\t"
|
||||
// walk and chew gum at the same time
|
||||
"adc %0, r1 \n\t"
|
||||
#else
|
||||
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
|
||||
"mul %0, %1 \n\t"
|
||||
/* Move the high 8-bits of the product (r1) back to i */
|
||||
"mov %0, r1 \n\t"
|
||||
/* Restore r1 to "0"; it's expected to always be that */
|
||||
#endif
|
||||
"clr __zero_reg__ \n\t"
|
||||
|
||||
: "+d"(i) /* writes to i; r16-r31, restricted by ldi */
|
||||
: "r"(scale) /* uses scale */
|
||||
: "r0", "r1" /* clobbers r0, r1 */
|
||||
);
|
||||
/* Return the result */
|
||||
return i;
|
||||
#endif
|
||||
#else
|
||||
#error "No implementation for scale8 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr uint8_t scale8_constexpr(uint8_t i, fract8 scale) {
|
||||
return (((uint16_t)i) * (1 + (uint16_t)(scale))) >> 8;
|
||||
}
|
||||
|
||||
/// The "video" version of scale8() guarantees that the output will
|
||||
/// be only be zero if one or both of the inputs are zero.
|
||||
/// If both inputs are non-zero, the output is guaranteed to be non-zero.
|
||||
/// This makes for better "video"/LED dimming, at the cost of
|
||||
/// several additional cycles.
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @returns scaled value
|
||||
/// @see scale8()
|
||||
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video(uint8_t i, fract8 scale) {
|
||||
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
|
||||
uint8_t j = (((int)i * (int)scale) >> 8) + ((i && scale) ? 1 : 0);
|
||||
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) +
|
||||
// nonzeroscale;
|
||||
return j;
|
||||
#elif SCALE8_AVRASM == 1
|
||||
uint8_t j = 0;
|
||||
asm volatile(" tst %[i]\n\t"
|
||||
" breq L_%=\n\t"
|
||||
" mul %[i], %[scale]\n\t"
|
||||
" mov %[j], r1\n\t"
|
||||
" clr __zero_reg__\n\t"
|
||||
" cpse %[scale], r1\n\t"
|
||||
" subi %[j], 0xFF\n\t"
|
||||
"L_%=: \n\t"
|
||||
: [j] "+d"(j) // r16-r31, restricted by subi
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
return j;
|
||||
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
// asm volatile(
|
||||
// " tst %0 \n"
|
||||
// " breq L_%= \n"
|
||||
// " mul %0, %1 \n"
|
||||
// " mov %0, r1 \n"
|
||||
// " add %0, %2 \n"
|
||||
// " clr __zero_reg__ \n"
|
||||
// "L_%=: \n"
|
||||
// : "+a" (i)
|
||||
// : "a" (scale), "a" (nonzeroscale)
|
||||
// : "r0", "r1");
|
||||
// // Return the result
|
||||
// return i;
|
||||
#else
|
||||
#error "No implementation for scale8_video available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @defgroup ScalingDirty Scaling Functions that Leave R1 Dirty
|
||||
/// These functions are more efficient for scaling multiple
|
||||
/// bytes at once, but require calling cleanup_R1() afterwards.
|
||||
/// @{
|
||||
|
||||
/// This version of scale8() does not clean up the R1 register on AVR.
|
||||
/// If you are doing several "scale8()'s" in a row, use this, and
|
||||
/// then explicitly call cleanup_R1().
|
||||
/// @warning You **MUST** call cleanup_R1() after using this function!
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @returns scaled value
|
||||
/// @see scale8()
|
||||
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY(uint8_t i,
|
||||
fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
return (((uint16_t)i) * ((uint16_t)(scale) + 1)) >> 8;
|
||||
#else
|
||||
return ((int)i * (int)(scale)) >> 8;
|
||||
#endif
|
||||
#elif SCALE8_AVRASM == 1
|
||||
asm volatile(
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
|
||||
"mul %0, %1 \n\t"
|
||||
// Add i to r0, possibly setting the carry flag
|
||||
"add r0, %0 \n\t"
|
||||
// load the immediate 0 into i (note, this does _not_ touch any flags)
|
||||
"ldi %0, 0x00 \n\t"
|
||||
// walk and chew gum at the same time
|
||||
"adc %0, r1 \n\t"
|
||||
#else
|
||||
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
|
||||
"mul %0, %1 \n\t"
|
||||
/* Move the high 8-bits of the product (r1) back to i */
|
||||
"mov %0, r1 \n\t"
|
||||
#endif
|
||||
/* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
|
||||
/* "clr __zero_reg__ \n\t" */
|
||||
: "+d"(i) /* writes to i; r16-r31, restricted by ldi */
|
||||
: "r"(scale) /* uses scale */
|
||||
: "r0", "r1" /* clobbers r0, r1 */
|
||||
);
|
||||
// Return the result
|
||||
return i;
|
||||
#else
|
||||
#error "No implementation for scale8_LEAVING_R1_DIRTY available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// In place modifying version of scale8() that does not clean up the R1
|
||||
/// register on AVR. If you are doing several "scale8()'s" in a row, use this,
|
||||
/// and then explicitly call cleanup_R1().
|
||||
/// @warning You **MUST** call cleanup_R1() after using this function!
|
||||
/// @par
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @see scale8()
|
||||
LIB8STATIC_ALWAYS_INLINE void nscale8_LEAVING_R1_DIRTY(uint8_t &i,
|
||||
fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
i = (((uint16_t)i) * ((uint16_t)(scale) + 1)) >> 8;
|
||||
#else
|
||||
i = ((int)i * (int)(scale)) >> 8;
|
||||
#endif
|
||||
#elif SCALE8_AVRASM == 1
|
||||
asm volatile(
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
|
||||
"mul %0, %1 \n\t"
|
||||
// Add i to r0, possibly setting the carry flag
|
||||
"add r0, %0 \n\t"
|
||||
// load the immediate 0 into i (note, this does _not_ touch any flags)
|
||||
"ldi %0, 0x00 \n\t"
|
||||
// walk and chew gum at the same time
|
||||
"adc %0, r1 \n\t"
|
||||
#else
|
||||
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
|
||||
"mul %0, %1 \n\t"
|
||||
/* Move the high 8-bits of the product (r1) back to i */
|
||||
"mov %0, r1 \n\t"
|
||||
#endif
|
||||
/* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
|
||||
/* "clr __zero_reg__ \n\t" */
|
||||
|
||||
: "+d"(i) /* writes to i; r16-r31, restricted by ldi */
|
||||
: "r"(scale) /* uses scale */
|
||||
: "r0", "r1" /* clobbers r0, r1 */
|
||||
);
|
||||
#else
|
||||
#error "No implementation for nscale8_LEAVING_R1_DIRTY available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// This version of scale8_video() does not clean up the R1 register on AVR.
|
||||
/// If you are doing several "scale8_video()'s" in a row, use this, and
|
||||
/// then explicitly call cleanup_R1().
|
||||
/// @warning You **MUST** call cleanup_R1() after using this function!
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @returns scaled value
|
||||
/// @see scale8_video()
|
||||
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY(uint8_t i,
|
||||
fract8 scale) {
|
||||
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
|
||||
uint8_t j = (((int)i * (int)scale) >> 8) + ((i && scale) ? 1 : 0);
|
||||
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) +
|
||||
// nonzeroscale;
|
||||
return j;
|
||||
#elif SCALE8_AVRASM == 1
|
||||
uint8_t j = 0;
|
||||
asm volatile(" tst %[i]\n\t"
|
||||
" breq L_%=\n\t"
|
||||
" mul %[i], %[scale]\n\t"
|
||||
" mov %[j], r1\n\t"
|
||||
" breq L_%=\n\t"
|
||||
" subi %[j], 0xFF\n\t"
|
||||
"L_%=: \n\t"
|
||||
: [j] "+d"(j) // r16-r31, restricted by subi
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
return j;
|
||||
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
// asm volatile(
|
||||
// " tst %0 \n"
|
||||
// " breq L_%= \n"
|
||||
// " mul %0, %1 \n"
|
||||
// " mov %0, r1 \n"
|
||||
// " add %0, %2 \n"
|
||||
// " clr __zero_reg__ \n"
|
||||
// "L_%=: \n"
|
||||
// : "+a" (i)
|
||||
// : "a" (scale), "a" (nonzeroscale)
|
||||
// : "r0", "r1");
|
||||
// // Return the result
|
||||
// return i;
|
||||
#else
|
||||
#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// In place modifying version of scale8_video() that does not clean up the R1
|
||||
/// register on AVR. If you are doing several "scale8_video()'s" in a row, use
|
||||
/// this, and then explicitly call cleanup_R1().
|
||||
/// @warning You **MUST** call cleanup_R1() after using this function!
|
||||
/// @par
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @see scale8_video()
|
||||
LIB8STATIC_ALWAYS_INLINE void nscale8_video_LEAVING_R1_DIRTY(uint8_t &i,
|
||||
fract8 scale) {
|
||||
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
|
||||
i = (((int)i * (int)scale) >> 8) + ((i && scale) ? 1 : 0);
|
||||
#elif SCALE8_AVRASM == 1
|
||||
asm volatile(" tst %[i]\n\t"
|
||||
" breq L_%=\n\t"
|
||||
" mul %[i], %[scale]\n\t"
|
||||
" mov %[i], r1\n\t"
|
||||
" breq L_%=\n\t"
|
||||
" subi %[i], 0xFF\n\t"
|
||||
"L_%=: \n\t"
|
||||
: [i] "+d"(i) // r16-r31, restricted by subi
|
||||
: [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
#else
|
||||
#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
|
||||
/// @ingroup ScalingDirty
|
||||
LIB8STATIC_ALWAYS_INLINE void cleanup_R1() {
|
||||
#if CLEANUP_R1_AVRASM == 1
|
||||
// Restore r1 to "0"; it's expected to always be that
|
||||
asm volatile("clr __zero_reg__ \n\t" : : : "r1");
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr CRGB nscale8x3_constexpr(uint8_t r, uint8_t g, uint8_t b, fract8 scale) {
|
||||
return CRGB(((int)r * (int)(scale)) >> 8, ((int)g * (int)(scale)) >> 8,
|
||||
((int)b * (int)(scale)) >> 8);
|
||||
}
|
||||
|
||||
/// @} ScalingDirty
|
||||
|
||||
/// Scale three one-byte values by a fourth one, which is treated as
|
||||
/// the numerator of a fraction whose demominator is 256.
|
||||
///
|
||||
/// In other words, it computes r,g,b * (scale / 256)
|
||||
///
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param r first value to scale
|
||||
/// @param g second value to scale
|
||||
/// @param b third value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
LIB8STATIC void nscale8x3(uint8_t &r, uint8_t &g, uint8_t &b, fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
uint16_t scale_fixed = scale + 1;
|
||||
r = (((uint16_t)r) * scale_fixed) >> 8;
|
||||
g = (((uint16_t)g) * scale_fixed) >> 8;
|
||||
b = (((uint16_t)b) * scale_fixed) >> 8;
|
||||
#else
|
||||
r = ((int)r * (int)(scale)) >> 8;
|
||||
g = ((int)g * (int)(scale)) >> 8;
|
||||
b = ((int)b * (int)(scale)) >> 8;
|
||||
#endif
|
||||
#elif SCALE8_AVRASM == 1
|
||||
r = scale8_LEAVING_R1_DIRTY(r, scale);
|
||||
g = scale8_LEAVING_R1_DIRTY(g, scale);
|
||||
b = scale8_LEAVING_R1_DIRTY(b, scale);
|
||||
cleanup_R1();
|
||||
#else
|
||||
#error "No implementation for nscale8x3 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Scale three one-byte values by a fourth one, which is treated as
|
||||
/// the numerator of a fraction whose demominator is 256.
|
||||
///
|
||||
/// In other words, it computes r,g,b * (scale / 256), ensuring
|
||||
/// that non-zero values passed in remain non-zero, no matter how low the scale
|
||||
/// argument.
|
||||
///
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param r first value to scale
|
||||
/// @param g second value to scale
|
||||
/// @param b third value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
LIB8STATIC void nscale8x3_video(uint8_t &r, uint8_t &g, uint8_t &b,
|
||||
fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
r = (r == 0) ? 0 : (((int)r * (int)(scale)) >> 8) + nonzeroscale;
|
||||
g = (g == 0) ? 0 : (((int)g * (int)(scale)) >> 8) + nonzeroscale;
|
||||
b = (b == 0) ? 0 : (((int)b * (int)(scale)) >> 8) + nonzeroscale;
|
||||
#elif SCALE8_AVRASM == 1
|
||||
nscale8_video_LEAVING_R1_DIRTY(r, scale);
|
||||
nscale8_video_LEAVING_R1_DIRTY(g, scale);
|
||||
nscale8_video_LEAVING_R1_DIRTY(b, scale);
|
||||
cleanup_R1();
|
||||
#else
|
||||
#error "No implementation for nscale8x3 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Scale two one-byte values by a third one, which is treated as
|
||||
/// the numerator of a fraction whose demominator is 256.
|
||||
///
|
||||
/// In other words, it computes i,j * (scale / 256).
|
||||
///
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param i first value to scale
|
||||
/// @param j second value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
LIB8STATIC void nscale8x2(uint8_t &i, uint8_t &j, fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
uint16_t scale_fixed = scale + 1;
|
||||
i = (((uint16_t)i) * scale_fixed) >> 8;
|
||||
j = (((uint16_t)j) * scale_fixed) >> 8;
|
||||
#else
|
||||
i = ((uint16_t)i * (uint16_t)(scale)) >> 8;
|
||||
j = ((uint16_t)j * (uint16_t)(scale)) >> 8;
|
||||
#endif
|
||||
#elif SCALE8_AVRASM == 1
|
||||
i = scale8_LEAVING_R1_DIRTY(i, scale);
|
||||
j = scale8_LEAVING_R1_DIRTY(j, scale);
|
||||
cleanup_R1();
|
||||
#else
|
||||
#error "No implementation for nscale8x2 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Scale two one-byte values by a third one, which is treated as
|
||||
/// the numerator of a fraction whose demominator is 256.
|
||||
///
|
||||
/// In other words, it computes i,j * (scale / 256), ensuring
|
||||
/// that non-zero values passed in remain non zero, no matter how low the scale
|
||||
/// argument.
|
||||
///
|
||||
/// @warning This function always modifies its arguments in place!
|
||||
/// @param i first value to scale
|
||||
/// @param j second value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
LIB8STATIC void nscale8x2_video(uint8_t &i, uint8_t &j, fract8 scale) {
|
||||
#if SCALE8_C == 1
|
||||
uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
|
||||
i = (i == 0) ? 0 : (((int)i * (int)(scale)) >> 8) + nonzeroscale;
|
||||
j = (j == 0) ? 0 : (((int)j * (int)(scale)) >> 8) + nonzeroscale;
|
||||
#elif SCALE8_AVRASM == 1
|
||||
nscale8_video_LEAVING_R1_DIRTY(i, scale);
|
||||
nscale8_video_LEAVING_R1_DIRTY(j, scale);
|
||||
cleanup_R1();
|
||||
#else
|
||||
#error "No implementation for nscale8x2 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Scale a 16-bit unsigned value by an 8-bit value, which is treated
|
||||
/// as the numerator of a fraction whose denominator is 256.
|
||||
///
|
||||
/// In other words, it computes i * (scale / 256)
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/256 units
|
||||
/// @returns scaled value
|
||||
LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8(uint16_t i, fract8 scale) {
|
||||
if (scale == 0) {
|
||||
return 0; // Fixes non zero output when scale == 0 and
|
||||
// FASTLED_SCALE8_FIXED==1
|
||||
}
|
||||
#if SCALE16BY8_C == 1
|
||||
uint16_t result;
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
result = (((uint32_t)(i) * (1 + ((uint32_t)scale))) >> 8);
|
||||
#else
|
||||
result = (i * scale) / 256;
|
||||
#endif
|
||||
return result;
|
||||
#elif SCALE16BY8_AVRASM == 1
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
uint16_t result = 0;
|
||||
asm volatile(
|
||||
// result.A = HighByte( (i.A x scale) + i.A )
|
||||
" mul %A[i], %[scale] \n\t"
|
||||
" add r0, %A[i] \n\t"
|
||||
// " adc r1, [zero] \n\t"
|
||||
// " mov %A[result], r1 \n\t"
|
||||
" adc %A[result], r1 \n\t"
|
||||
|
||||
// result.A-B += i.B x scale
|
||||
" mul %B[i], %[scale] \n\t"
|
||||
" add %A[result], r0 \n\t"
|
||||
" adc %B[result], r1 \n\t"
|
||||
|
||||
// cleanup r1
|
||||
" clr __zero_reg__ \n\t"
|
||||
|
||||
// result.A-B += i.B
|
||||
" add %A[result], %B[i] \n\t"
|
||||
" adc %B[result], __zero_reg__ \n\t"
|
||||
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
return result;
|
||||
#else
|
||||
uint16_t result = 0;
|
||||
asm volatile(
|
||||
// result.A = HighByte(i.A x j )
|
||||
" mul %A[i], %[scale] \n\t"
|
||||
" mov %A[result], r1 \n\t"
|
||||
//" clr %B[result] \n\t"
|
||||
|
||||
// result.A-B += i.B x j
|
||||
" mul %B[i], %[scale] \n\t"
|
||||
" add %A[result], r0 \n\t"
|
||||
" adc %B[result], r1 \n\t"
|
||||
|
||||
// cleanup r1
|
||||
" clr __zero_reg__ \n\t"
|
||||
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
return result;
|
||||
#endif
|
||||
#else
|
||||
#error "No implementation for scale16by8 available."
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Scale a 16-bit unsigned value by an 16-bit value, which is treated
|
||||
/// as the numerator of a fraction whose denominator is 65536.
|
||||
/// In other words, it computes i * (scale / 65536)
|
||||
/// @param i input value to scale
|
||||
/// @param scale scale factor, in n/65536 units
|
||||
/// @returns scaled value
|
||||
LIB8STATIC uint16_t scale16(uint16_t i, fract16 scale) {
|
||||
#if SCALE16_C == 1
|
||||
uint16_t result;
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
result = ((uint32_t)(i) * (1 + (uint32_t)(scale))) / 65536;
|
||||
#else
|
||||
result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
|
||||
#endif
|
||||
return result;
|
||||
#elif SCALE16_AVRASM == 1
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
// implemented sort of like
|
||||
// result = ((i * scale) + i ) / 65536
|
||||
//
|
||||
// why not like this, you may ask?
|
||||
// result = (i * (scale+1)) / 65536
|
||||
// the answer is that if scale is 65535, then scale+1
|
||||
// will be zero, which is not what we want.
|
||||
uint32_t result;
|
||||
asm volatile(
|
||||
// result.A-B = i.A x scale.A
|
||||
" mul %A[i], %A[scale] \n\t"
|
||||
// save results...
|
||||
// basic idea:
|
||||
//" mov %A[result], r0 \n\t"
|
||||
//" mov %B[result], r1 \n\t"
|
||||
// which can be written as...
|
||||
" movw %A[result], r0 \n\t"
|
||||
// Because we're going to add i.A-B to
|
||||
// result.A-D, we DO need to keep both
|
||||
// the r0 and r1 portions of the product
|
||||
// UNlike in the 'unfixed scale8' version.
|
||||
// So the movw here is needed.
|
||||
: [result] "=r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
|
||||
asm volatile(
|
||||
// result.C-D = i.B x scale.B
|
||||
" mul %B[i], %B[scale] \n\t"
|
||||
//" mov %C[result], r0 \n\t"
|
||||
//" mov %D[result], r1 \n\t"
|
||||
" movw %C[result], r0 \n\t"
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
|
||||
const uint8_t zero = 0;
|
||||
asm volatile(
|
||||
// result.B-D += i.B x scale.A
|
||||
" mul %B[i], %A[scale] \n\t"
|
||||
|
||||
" add %B[result], r0 \n\t"
|
||||
" adc %C[result], r1 \n\t"
|
||||
" adc %D[result], %[zero] \n\t"
|
||||
|
||||
// result.B-D += i.A x scale.B
|
||||
" mul %A[i], %B[scale] \n\t"
|
||||
|
||||
" add %B[result], r0 \n\t"
|
||||
" adc %C[result], r1 \n\t"
|
||||
" adc %D[result], %[zero] \n\t"
|
||||
|
||||
// cleanup r1
|
||||
" clr r1 \n\t"
|
||||
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale), [zero] "r"(zero)
|
||||
: "r0", "r1");
|
||||
|
||||
asm volatile(
|
||||
// result.A-D += i.A-B
|
||||
" add %A[result], %A[i] \n\t"
|
||||
" adc %B[result], %B[i] \n\t"
|
||||
" adc %C[result], %[zero] \n\t"
|
||||
" adc %D[result], %[zero] \n\t"
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [zero] "r"(zero));
|
||||
|
||||
result = result >> 16;
|
||||
return result;
|
||||
#else
|
||||
uint32_t result;
|
||||
asm volatile(
|
||||
// result.A-B = i.A x scale.A
|
||||
" mul %A[i], %A[scale] \n\t"
|
||||
// save results...
|
||||
// basic idea:
|
||||
//" mov %A[result], r0 \n\t"
|
||||
//" mov %B[result], r1 \n\t"
|
||||
// which can be written as...
|
||||
" movw %A[result], r0 \n\t"
|
||||
// We actually don't need to do anything with r0,
|
||||
// as result.A is never used again here, so we
|
||||
// could just move the high byte, but movw is
|
||||
// one clock cycle, just like mov, so might as
|
||||
// well, in case we want to use this code for
|
||||
// a generic 16x16 multiply somewhere.
|
||||
|
||||
: [result] "=r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
|
||||
asm volatile(
|
||||
// result.C-D = i.B x scale.B
|
||||
" mul %B[i], %B[scale] \n\t"
|
||||
//" mov %C[result], r0 \n\t"
|
||||
//" mov %D[result], r1 \n\t"
|
||||
" movw %C[result], r0 \n\t"
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale)
|
||||
: "r0", "r1");
|
||||
|
||||
const uint8_t zero = 0;
|
||||
asm volatile(
|
||||
// result.B-D += i.B x scale.A
|
||||
" mul %B[i], %A[scale] \n\t"
|
||||
|
||||
" add %B[result], r0 \n\t"
|
||||
" adc %C[result], r1 \n\t"
|
||||
" adc %D[result], %[zero] \n\t"
|
||||
|
||||
// result.B-D += i.A x scale.B
|
||||
" mul %A[i], %B[scale] \n\t"
|
||||
|
||||
" add %B[result], r0 \n\t"
|
||||
" adc %C[result], r1 \n\t"
|
||||
" adc %D[result], %[zero] \n\t"
|
||||
|
||||
// cleanup r1
|
||||
" clr r1 \n\t"
|
||||
|
||||
: [result] "+r"(result)
|
||||
: [i] "r"(i), [scale] "r"(scale), [zero] "r"(zero)
|
||||
: "r0", "r1");
|
||||
|
||||
result = result >> 16;
|
||||
return result;
|
||||
#endif
|
||||
#else
|
||||
#error "No implementation for scale16 available."
|
||||
#endif
|
||||
}
|
||||
/// @} Scaling
|
||||
|
||||
/// @defgroup Dimming Dimming and Brightening Functions
|
||||
/// Functions to dim or brighten data.
|
||||
///
|
||||
/// The eye does not respond in a linear way to light.
|
||||
/// High speed PWM'd LEDs at 50% duty cycle appear far
|
||||
/// brighter then the "half as bright" you might expect.
|
||||
///
|
||||
/// If you want your midpoint brightness LEDs (128) to
|
||||
/// appear half as bright as "full" brightness (255), you
|
||||
/// have to apply a "dimming function".
|
||||
///
|
||||
/// @note These are approximations of gamma correction with
|
||||
/// a gamma value of 2.0.
|
||||
/// @see @ref GammaFuncs
|
||||
/// @{
|
||||
|
||||
/// Adjust a scaling value for dimming.
|
||||
/// @see scale8()
|
||||
LIB8STATIC uint8_t dim8_raw(uint8_t x) { return scale8(x, x); }
|
||||
|
||||
/// Adjust a scaling value for dimming for video (value will never go below 1)
|
||||
/// @see scale8_video()
|
||||
LIB8STATIC uint8_t dim8_video(uint8_t x) { return scale8_video(x, x); }
|
||||
|
||||
/// Linear version of the dimming function that halves for values < 128
|
||||
LIB8STATIC uint8_t dim8_lin(uint8_t x) {
|
||||
if (x & 0x80) {
|
||||
x = scale8(x, x);
|
||||
} else {
|
||||
x += 1;
|
||||
x /= 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/// Brighten a value (inverse of dim8_raw())
|
||||
LIB8STATIC uint8_t brighten8_raw(uint8_t x) {
|
||||
uint8_t ix = 255 - x;
|
||||
return 255 - scale8(ix, ix);
|
||||
}
|
||||
|
||||
/// Brighten a value (inverse of dim8_video())
|
||||
LIB8STATIC uint8_t brighten8_video(uint8_t x) {
|
||||
uint8_t ix = 255 - x;
|
||||
return 255 - scale8_video(ix, ix);
|
||||
}
|
||||
|
||||
/// Brighten a value (inverse of dim8_lin())
|
||||
LIB8STATIC uint8_t brighten8_lin(uint8_t x) {
|
||||
uint8_t ix = 255 - x;
|
||||
if (ix & 0x80) {
|
||||
ix = scale8(ix, ix);
|
||||
} else {
|
||||
ix += 1;
|
||||
ix /= 2;
|
||||
}
|
||||
return 255 - ix;
|
||||
}
|
||||
|
||||
/// @} Dimming
|
||||
/// @} lib8tion
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
Reference in New Issue
Block a user