Initial commit of Arduino libraries
This commit is contained in:
125
FastLED/src/CMakeLists.txt
Normal file
125
FastLED/src/CMakeLists.txt
Normal file
@@ -0,0 +1,125 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Efficiently compiles the libfastled.a archive to link against.
|
||||
# Optionally, you can copy the header tree to a specified include path.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Set FastLED source directory (this is where the FastLED sources live)
|
||||
set(FASTLED_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
message(STATUS "FASTLED_SOURCE_DIR: ${FASTLED_SOURCE_DIR}")
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
message(STATUS "CMAKE_CXX_STANDARD not defined. Setting C++ standard to 17.")
|
||||
else()
|
||||
message(STATUS "CMAKE_CXX_STANDARD already defined as: ${CMAKE_CXX_STANDARD}")
|
||||
endif()
|
||||
|
||||
# Retrieve and print the flags passed from the parent (e.g. tests/CMakeLists.txt)
|
||||
message(STATUS "Using compile flags from parent CMakeLists.txt")
|
||||
message(STATUS "COMMON_COMPILE_FLAGS: ${COMMON_COMPILE_FLAGS}")
|
||||
message(STATUS "COMMON_COMPILE_DEFINITIONS: ${COMMON_COMPILE_DEFINITIONS}")
|
||||
|
||||
# Verify the directory exists
|
||||
if(NOT EXISTS ${FASTLED_SOURCE_DIR})
|
||||
message(FATAL_ERROR "Error: FASTLED_SOURCE_DIR does not exist! Check directory path.")
|
||||
endif()
|
||||
|
||||
# Include FastLED headers (assumed to be in this directory)
|
||||
include_directories(${FASTLED_SOURCE_DIR})
|
||||
|
||||
# === Step 1: Get all the source files ===
|
||||
file(GLOB_RECURSE FASTLED_SOURCES "${FASTLED_SOURCE_DIR}/*.c*")
|
||||
message(STATUS "Found source files: ${FASTLED_SOURCES}")
|
||||
|
||||
if(FASTLED_SOURCES STREQUAL "")
|
||||
message(FATAL_ERROR "Error: No source files found in ${FASTLED_SOURCE_DIR}!")
|
||||
endif()
|
||||
|
||||
# Exclude platform-specific files (e.g. esp, arm, avr)
|
||||
list(FILTER FASTLED_SOURCES EXCLUDE REGEX ".*esp.*")
|
||||
list(FILTER FASTLED_SOURCES EXCLUDE REGEX ".*arm.*")
|
||||
list(FILTER FASTLED_SOURCES EXCLUDE REGEX ".*avr.*")
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Partition helper: create object libraries and add their names to a global list
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Initialize a global list to store the names of the created object libraries.
|
||||
set(FASTLED_OBJECT_LIBS "")
|
||||
|
||||
# Using a macro (instead of a function) ensures we are in the global scope.
|
||||
macro(add_partitioned_library lib_name source_regex)
|
||||
set(matched_sources "")
|
||||
foreach(source IN LISTS FASTLED_SOURCES)
|
||||
if(source MATCHES "${source_regex}")
|
||||
list(APPEND matched_sources "${source}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(matched_sources)
|
||||
# Remove the matched sources from the global list so they won't be processed again.
|
||||
foreach(source IN LISTS matched_sources)
|
||||
list(REMOVE_ITEM FASTLED_SOURCES "${source}")
|
||||
endforeach()
|
||||
|
||||
# Create an object library from the matched sources.
|
||||
add_library(${lib_name} OBJECT ${matched_sources})
|
||||
target_compile_options(${lib_name} PRIVATE ${COMMON_COMPILE_FLAGS})
|
||||
target_compile_definitions(${lib_name} PRIVATE ${COMMON_COMPILE_DEFINITIONS})
|
||||
message(STATUS "Created ${lib_name} with sources: ${matched_sources}")
|
||||
|
||||
# Append the library name to the global list.
|
||||
list(APPEND FASTLED_OBJECT_LIBS ${lib_name})
|
||||
else()
|
||||
message(WARNING "No sources found for library ${lib_name} using filter: ${source_regex}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Step 2: Partition the sources into libraries with one-line calls.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Partition some of the sources into separate object libraries, removing them from the global list FASTLED_SOURCES.
|
||||
add_partitioned_library(FastLED_fx ".*/fx/")
|
||||
add_partitioned_library(FastLED_platforms ".*/platforms/")
|
||||
# Uncomment the next line if you wish to partition lib8tion sources:
|
||||
# add_partitioned_library(fastled8tion ".*/lib8tion/")
|
||||
add_partitioned_library(FastLED_fl ".*/fl/")
|
||||
add_partitioned_library(FastLED_sensors ".*/sensors/")
|
||||
add_partitioned_library(FastLED_third_party ".*/third_party/")
|
||||
# Anything else will be considered the "core" library
|
||||
|
||||
# The remaining files become the "core" library.
|
||||
if(FASTLED_SOURCES)
|
||||
add_library(FastLED_core OBJECT ${FASTLED_SOURCES})
|
||||
target_compile_options(FastLED_core PRIVATE ${COMMON_COMPILE_FLAGS})
|
||||
target_compile_definitions(FastLED_core PRIVATE ${COMMON_COMPILE_DEFINITIONS})
|
||||
list(APPEND FASTLED_OBJECT_LIBS FastLED_core)
|
||||
else()
|
||||
message(FATAL_ERROR "Error: FastLED_core has no sources! This should not happen.")
|
||||
endif()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Create the main FastLED library by aggregating all object libraries.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
add_library(fastled STATIC)
|
||||
|
||||
# Loop over the global list of object libraries and add their object files.
|
||||
foreach(obj_lib IN LISTS FASTLED_OBJECT_LIBS)
|
||||
target_sources(fastled PRIVATE $<TARGET_OBJECTS:${obj_lib}>)
|
||||
endforeach()
|
||||
|
||||
# Forward the compile flags and definitions from the parent.
|
||||
target_compile_options(fastled PRIVATE ${COMMON_COMPILE_FLAGS})
|
||||
target_compile_definitions(fastled PRIVATE ${COMMON_COMPILE_DEFINITIONS})
|
||||
|
||||
# Ensure full archive linking: force inclusion of all object files.
|
||||
target_link_options(fastled PRIVATE "-Wl,--whole-archive" "-Wl,--no-whole-archive")
|
||||
|
||||
393
FastLED/src/FastLED.cpp
Normal file
393
FastLED/src/FastLED.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
#include "fl/singleton.h"
|
||||
#include "fl/engine_events.h"
|
||||
|
||||
/// @file FastLED.cpp
|
||||
/// Central source file for FastLED, implements the CFastLED class/object
|
||||
|
||||
#ifndef MAX_CLED_CONTROLLERS
|
||||
#ifdef __AVR__
|
||||
// if mega or leonardo, allow more controllers
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__)
|
||||
#define MAX_CLED_CONTROLLERS 16
|
||||
#else
|
||||
#define MAX_CLED_CONTROLLERS 8
|
||||
#endif
|
||||
#else
|
||||
#define MAX_CLED_CONTROLLERS 64
|
||||
#endif // __AVR__
|
||||
#endif // MAX_CLED_CONTROLLERS
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
volatile uint32_t fuckit;
|
||||
#endif
|
||||
|
||||
// Disable to fix build breakage.
|
||||
// #ifndef FASTLED_DEFINE_WEAK_YEILD_FUNCTION
|
||||
// #if defined(__AVR_ATtiny13__)
|
||||
// // Arduino.h also defines this as a weak function on this platform.
|
||||
// #define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 0
|
||||
// #else
|
||||
// #define FASTLED_DEFINE_WEAK_YEILD_FUNCTION 1
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
/// Has to be declared outside of any namespaces.
|
||||
/// Called at program exit when run in a desktop environment.
|
||||
/// Extra C definition that some environments may need.
|
||||
/// @returns 0 to indicate success
|
||||
extern "C" __attribute__((weak)) int atexit(void (* /*func*/ )()) { return 0; }
|
||||
|
||||
#ifdef FASTLED_NEEDS_YIELD
|
||||
extern "C" void yield(void) { }
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
uint16_t cled_contoller_size() {
|
||||
return sizeof(CLEDController);
|
||||
}
|
||||
|
||||
uint8_t get_brightness();
|
||||
|
||||
/// Pointer to the matrix object when using the Smart Matrix Library
|
||||
/// @see https://github.com/pixelmatix/SmartMatrix
|
||||
void *pSmartMatrix = NULL;
|
||||
|
||||
CFastLED FastLED;
|
||||
|
||||
CLEDController *CLEDController::m_pHead = NULL;
|
||||
CLEDController *CLEDController::m_pTail = NULL;
|
||||
static uint32_t lastshow = 0;
|
||||
|
||||
/// Global frame counter, used for debugging ESP implementations
|
||||
/// @todo Include in FASTLED_DEBUG_COUNT_FRAME_RETRIES block?
|
||||
uint32_t _frame_cnt=0;
|
||||
|
||||
/// Global frame retry counter, used for debugging ESP implementations
|
||||
/// @todo Include in FASTLED_DEBUG_COUNT_FRAME_RETRIES block?
|
||||
uint32_t _retry_cnt=0;
|
||||
|
||||
// uint32_t CRGB::Squant = ((uint32_t)((__TIME__[4]-'0') * 28))<<16 | ((__TIME__[6]-'0')*50)<<8 | ((__TIME__[7]-'0')*28);
|
||||
|
||||
CFastLED::CFastLED() {
|
||||
// clear out the array of led controllers
|
||||
// m_nControllers = 0;
|
||||
m_Scale = 255;
|
||||
m_nFPS = 0;
|
||||
m_pPowerFunc = NULL;
|
||||
m_nPowerData = 0xFFFFFFFF;
|
||||
m_nMinMicros = 0;
|
||||
}
|
||||
|
||||
int CFastLED::size() {
|
||||
return (*this)[0].size();
|
||||
}
|
||||
|
||||
CRGB* CFastLED::leds() {
|
||||
return (*this)[0].leds();
|
||||
}
|
||||
|
||||
CLEDController &CFastLED::addLeds(CLEDController *pLed,
|
||||
struct CRGB *data,
|
||||
int nLedsOrOffset, int nLedsIfOffset) {
|
||||
int nOffset = (nLedsIfOffset > 0) ? nLedsOrOffset : 0;
|
||||
int nLeds = (nLedsIfOffset > 0) ? nLedsIfOffset : nLedsOrOffset;
|
||||
|
||||
pLed->init();
|
||||
pLed->setLeds(data + nOffset, nLeds);
|
||||
FastLED.setMaxRefreshRate(pLed->getMaxRefreshRate(),true);
|
||||
fl::EngineEvents::onStripAdded(pLed, nLedsOrOffset - nOffset);
|
||||
return *pLed;
|
||||
}
|
||||
|
||||
static void* gControllersData[MAX_CLED_CONTROLLERS];
|
||||
|
||||
void CFastLED::show(uint8_t scale) {
|
||||
fl::EngineEvents::onBeginFrame();
|
||||
while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
|
||||
lastshow = micros();
|
||||
|
||||
// If we have a function for computing power, use it!
|
||||
if(m_pPowerFunc) {
|
||||
scale = (*m_pPowerFunc)(scale, m_nPowerData);
|
||||
}
|
||||
|
||||
// static uninitialized gControllersData produces the smallest binary on attiny85.
|
||||
int length = 0;
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
|
||||
while(pCur && length < MAX_CLED_CONTROLLERS) {
|
||||
if (pCur->getEnabled()) {
|
||||
gControllersData[length] = pCur->beginShowLeds(pCur->size());
|
||||
} else {
|
||||
gControllersData[length] = nullptr;
|
||||
}
|
||||
length++;
|
||||
if (m_nFPS < 100) { pCur->setDither(0); }
|
||||
pCur = pCur->next();
|
||||
}
|
||||
|
||||
pCur = CLEDController::head();
|
||||
for (length = 0; length < MAX_CLED_CONTROLLERS && pCur; length++) {
|
||||
if (pCur->getEnabled()) {
|
||||
pCur->showLedsInternal(scale);
|
||||
}
|
||||
pCur = pCur->next();
|
||||
|
||||
}
|
||||
|
||||
length = 0; // Reset length to 0 and iterate again.
|
||||
pCur = CLEDController::head();
|
||||
while(pCur && length < MAX_CLED_CONTROLLERS) {
|
||||
if (pCur->getEnabled()) {
|
||||
pCur->endShowLeds(gControllersData[length]);
|
||||
}
|
||||
length++;
|
||||
pCur = pCur->next();
|
||||
}
|
||||
countFPS();
|
||||
fl::EngineEvents::onEndShowLeds();
|
||||
fl::EngineEvents::onEndFrame();
|
||||
}
|
||||
|
||||
int CFastLED::count() {
|
||||
int x = 0;
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while( pCur) {
|
||||
++x;
|
||||
pCur = pCur->next();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
CLEDController & CFastLED::operator[](int x) {
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(x-- && pCur) {
|
||||
pCur = pCur->next();
|
||||
}
|
||||
if(pCur == NULL) {
|
||||
return *(CLEDController::head());
|
||||
} else {
|
||||
return *pCur;
|
||||
}
|
||||
}
|
||||
|
||||
void CFastLED::showColor(const struct CRGB & color, uint8_t scale) {
|
||||
while(m_nMinMicros && ((micros()-lastshow) < m_nMinMicros));
|
||||
lastshow = micros();
|
||||
|
||||
// If we have a function for computing power, use it!
|
||||
if(m_pPowerFunc) {
|
||||
scale = (*m_pPowerFunc)(scale, m_nPowerData);
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(pCur && length < MAX_CLED_CONTROLLERS) {
|
||||
if (pCur->getEnabled()) {
|
||||
gControllersData[length] = pCur->beginShowLeds(pCur->size());
|
||||
} else {
|
||||
gControllersData[length] = nullptr;
|
||||
}
|
||||
length++;
|
||||
pCur = pCur->next();
|
||||
}
|
||||
|
||||
pCur = CLEDController::head();
|
||||
while(pCur && length < MAX_CLED_CONTROLLERS) {
|
||||
if(m_nFPS < 100) { pCur->setDither(0); }
|
||||
if (pCur->getEnabled()) {
|
||||
pCur->showColorInternal(color, scale);
|
||||
}
|
||||
pCur = pCur->next();
|
||||
}
|
||||
|
||||
pCur = CLEDController::head();
|
||||
length = 0; // Reset length to 0 and iterate again.
|
||||
while(pCur && length < MAX_CLED_CONTROLLERS) {
|
||||
if (pCur->getEnabled()) {
|
||||
pCur->endShowLeds(gControllersData[length]);
|
||||
}
|
||||
length++;
|
||||
pCur = pCur->next();
|
||||
}
|
||||
countFPS();
|
||||
}
|
||||
|
||||
void CFastLED::clear(bool writeData) {
|
||||
if(writeData) {
|
||||
showColor(CRGB(0,0,0), 0);
|
||||
}
|
||||
clearData();
|
||||
}
|
||||
|
||||
void CFastLED::clearData() {
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(pCur) {
|
||||
pCur->clearLedDataInternal();
|
||||
pCur = pCur->next();
|
||||
}
|
||||
}
|
||||
|
||||
void CFastLED::delay(unsigned long ms) {
|
||||
unsigned long start = millis();
|
||||
do {
|
||||
#ifndef FASTLED_ACCURATE_CLOCK
|
||||
// make sure to allow at least one ms to pass to ensure the clock moves
|
||||
// forward
|
||||
::delay(1);
|
||||
#endif
|
||||
show();
|
||||
yield();
|
||||
}
|
||||
while((millis()-start) < ms);
|
||||
}
|
||||
|
||||
void CFastLED::setTemperature(const struct CRGB & temp) {
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(pCur) {
|
||||
pCur->setTemperature(temp);
|
||||
pCur = pCur->next();
|
||||
}
|
||||
}
|
||||
|
||||
void CFastLED::setCorrection(const struct CRGB & correction) {
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(pCur) {
|
||||
pCur->setCorrection(correction);
|
||||
pCur = pCur->next();
|
||||
}
|
||||
}
|
||||
|
||||
void CFastLED::setDither(uint8_t ditherMode) {
|
||||
CLEDController *pCur = CLEDController::head();
|
||||
while(pCur) {
|
||||
pCur->setDither(ditherMode);
|
||||
pCur = pCur->next();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// template<int m, int n> void transpose8(unsigned char A[8], unsigned char B[8]) {
|
||||
// uint32_t x, y, t;
|
||||
//
|
||||
// // Load the array and pack it into x and y.
|
||||
// y = *(unsigned int*)(A);
|
||||
// x = *(unsigned int*)(A+4);
|
||||
//
|
||||
// // x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
|
||||
// // y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
|
||||
//
|
||||
// // pre-transform x
|
||||
// t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
// t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
|
||||
//
|
||||
// // pre-transform y
|
||||
// t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
// t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
|
||||
//
|
||||
// // final transform
|
||||
// t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
// y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
// x = t;
|
||||
//
|
||||
// B[7*n] = y; y >>= 8;
|
||||
// B[6*n] = y; y >>= 8;
|
||||
// B[5*n] = y; y >>= 8;
|
||||
// B[4*n] = y;
|
||||
//
|
||||
// B[3*n] = x; x >>= 8;
|
||||
// B[2*n] = x; x >>= 8;
|
||||
// B[n] = x; x >>= 8;
|
||||
// B[0] = x;
|
||||
// // B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
|
||||
// // B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
|
||||
// }
|
||||
//
|
||||
// void transposeLines(Lines & out, Lines & in) {
|
||||
// transpose8<1,2>(in.bytes, out.bytes);
|
||||
// transpose8<1,2>(in.bytes + 8, out.bytes + 1);
|
||||
// }
|
||||
|
||||
|
||||
/// Unused value
|
||||
/// @todo Remove?
|
||||
extern int noise_min;
|
||||
|
||||
/// Unused value
|
||||
/// @todo Remove?
|
||||
extern int noise_max;
|
||||
|
||||
void CFastLED::countFPS(int nFrames) {
|
||||
static int br = 0;
|
||||
static uint32_t lastframe = 0; // millis();
|
||||
|
||||
if(br++ >= nFrames) {
|
||||
uint32_t now = millis();
|
||||
now -= lastframe;
|
||||
if(now == 0) {
|
||||
now = 1; // prevent division by zero below
|
||||
}
|
||||
m_nFPS = (br * 1000) / now;
|
||||
br = 0;
|
||||
lastframe = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void CFastLED::setMaxRefreshRate(uint16_t refresh, bool constrain) {
|
||||
if(constrain) {
|
||||
// if we're constraining, the new value of m_nMinMicros _must_ be higher than previously (because we're only
|
||||
// allowed to slow things down if constraining)
|
||||
if(refresh > 0) {
|
||||
m_nMinMicros = ((1000000 / refresh) > m_nMinMicros) ? (1000000 / refresh) : m_nMinMicros;
|
||||
}
|
||||
} else if(refresh > 0) {
|
||||
m_nMinMicros = 1000000 / refresh;
|
||||
} else {
|
||||
m_nMinMicros = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t get_brightness() {
|
||||
return FastLED.getBrightness();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NEED_CXX_BITS
|
||||
namespace __cxxabiv1
|
||||
{
|
||||
#if !defined(ESP8266) && !defined(ESP32)
|
||||
extern "C" void __cxa_pure_virtual (void) {}
|
||||
#endif
|
||||
|
||||
/* guard variables */
|
||||
|
||||
/* The ABI requires a 64-bit type. */
|
||||
__extension__ typedef int __guard __attribute__((mode(__DI__)));
|
||||
|
||||
extern "C" int __cxa_guard_acquire (__guard *) __attribute__((weak));
|
||||
extern "C" void __cxa_guard_release (__guard *) __attribute__((weak));
|
||||
extern "C" void __cxa_guard_abort (__guard *) __attribute__((weak));
|
||||
|
||||
extern "C" int __cxa_guard_acquire (__guard *g)
|
||||
{
|
||||
return !*(char *)(g);
|
||||
}
|
||||
|
||||
extern "C" void __cxa_guard_release (__guard *g)
|
||||
{
|
||||
*(char *)g = 1;
|
||||
}
|
||||
|
||||
extern "C" void __cxa_guard_abort (__guard *)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
863
FastLED/src/FastLED.h
Normal file
863
FastLED/src/FastLED.h
Normal file
@@ -0,0 +1,863 @@
|
||||
#pragma once
|
||||
#ifndef __INC_FASTSPI_LED2_H
|
||||
#define __INC_FASTSPI_LED2_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/// @file FastLED.h
|
||||
/// central include file for FastLED, defines the CFastLED class/object
|
||||
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
|
||||
#define FASTLED_HAS_PRAGMA_MESSAGE
|
||||
#endif
|
||||
|
||||
/// Current FastLED version number, as an integer.
|
||||
/// E.g. 3007001 for version "3.7.1", with:
|
||||
/// * 1 digit for the major version
|
||||
/// * 3 digits for the minor version
|
||||
/// * 3 digits for the patch version
|
||||
#define FASTLED_VERSION 3009016
|
||||
#ifndef FASTLED_INTERNAL
|
||||
# ifdef FASTLED_SHOW_VERSION
|
||||
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
|
||||
# pragma message "FastLED version 3.009.016"
|
||||
# else
|
||||
# warning FastLED version 3.009.016 (Not really a warning, just telling you here.)
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __PROG_TYPES_COMPAT__
|
||||
/// avr-libc define to expose __progmem__ typedefs.
|
||||
/// @note These typedefs are now deprecated!
|
||||
/// @see https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
|
||||
#define __PROG_TYPES_COMPAT__
|
||||
#endif
|
||||
|
||||
#ifdef SmartMatrix_h
|
||||
#include <SmartMatrix.h>
|
||||
#endif
|
||||
|
||||
#ifdef DmxSimple_h
|
||||
#include <DmxSimple.h>
|
||||
#endif
|
||||
|
||||
#ifdef DmxSerial_h
|
||||
#include <DMXSerial.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_OCTOWS2811
|
||||
#include <OctoWS2811.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "cpp_compat.h"
|
||||
|
||||
#include "fastled_config.h"
|
||||
#include "led_sysdefs.h"
|
||||
|
||||
// Utility functions
|
||||
#include "fastled_delay.h"
|
||||
#include "bitswap.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "fastpin.h"
|
||||
#include "fastspi_types.h"
|
||||
#include "dmx.h"
|
||||
|
||||
#include "platforms.h"
|
||||
#include "fastled_progmem.h"
|
||||
|
||||
#include "lib8tion.h"
|
||||
#include "pixeltypes.h"
|
||||
#include "hsv2rgb.h"
|
||||
#include "colorutils.h"
|
||||
#include "pixelset.h"
|
||||
#include "colorpalettes.h"
|
||||
|
||||
#include "noise.h"
|
||||
#include "power_mgt.h"
|
||||
|
||||
#include "fastspi.h"
|
||||
#include "chipsets.h"
|
||||
#include "fl/engine_events.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Backdoor to get the size of the CLedController object. The one place
|
||||
// that includes this just uses extern to declare the function.
|
||||
uint16_t cled_contoller_size();
|
||||
|
||||
/// LED chipsets with SPI interface
|
||||
enum ESPIChipsets {
|
||||
LPD6803, ///< LPD6803 LED chipset
|
||||
LPD8806, ///< LPD8806 LED chipset
|
||||
WS2801, ///< WS2801 LED chipset
|
||||
WS2803, ///< WS2803 LED chipset
|
||||
SM16716, ///< SM16716 LED chipset
|
||||
P9813, ///< P9813 LED chipset
|
||||
APA102, ///< APA102 LED chipset
|
||||
SK9822, ///< SK9822 LED chipset
|
||||
SK9822HD, ///< SK9822 LED chipset with 5-bit gamma correction
|
||||
DOTSTAR, ///< APA102 LED chipset alias
|
||||
DOTSTARHD, ///< APA102HD LED chipset alias
|
||||
APA102HD, ///< APA102 LED chipset with 5-bit gamma correction
|
||||
HD107, /// Same as APA102, but in turbo 40-mhz mode.
|
||||
HD107HD, /// Same as APA102HD, but in turbo 40-mhz mode.
|
||||
|
||||
};
|
||||
|
||||
/// Smart Matrix Library controller type
|
||||
/// @see https://github.com/pixelmatix/SmartMatrix
|
||||
enum ESM { SMART_MATRIX };
|
||||
|
||||
/// Octo WS2811 LED Library controller types
|
||||
/// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||
/// @see https://github.com/PaulStoffregen/OctoWS2811
|
||||
enum OWS2811 { OCTOWS2811,OCTOWS2811_400, OCTOWS2813};
|
||||
|
||||
/// WS2812Serial Library controller type
|
||||
/// @see https://www.pjrc.com/non-blocking-ws2812-led-library/
|
||||
/// @see https://github.com/PaulStoffregen/WS2812Serial
|
||||
enum SWS2812 { WS2812SERIAL };
|
||||
|
||||
#ifdef HAS_PIXIE
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class PIXIE : public PixieController<DATA_PIN, RGB_ORDER> {};
|
||||
#endif
|
||||
|
||||
#ifdef FASTLED_HAS_CLOCKLESS
|
||||
/// @addtogroup Chipsets
|
||||
/// @{
|
||||
/// @addtogroup ClocklessChipsets
|
||||
/// @{
|
||||
|
||||
/// LED controller for WS2812 LEDs with GRB color order
|
||||
/// @see WS2812Controller800Khz
|
||||
template<uint8_t DATA_PIN> class NEOPIXEL : public WS2812Controller800Khz<DATA_PIN, GRB> {};
|
||||
|
||||
/// @brief SM16703 controller class.
|
||||
/// @copydetails SM16703Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class SM16703 : public SM16703Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief TM1829 controller class.
|
||||
/// @copydetails TM1829Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class TM1829 : public TM1829Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief TM1812 controller class.
|
||||
/// @copydetails TM1809Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class TM1812 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief TM1809 controller class.
|
||||
/// @copydetails TM1809Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class TM1809 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief TM1804 controller class.
|
||||
/// @copydetails TM1809Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class TM1804 : public TM1809Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief TM1803 controller class.
|
||||
/// @copydetails TM1803Controller400Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class TM1803 : public TM1803Controller400Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief UCS1903 controller class.
|
||||
/// @copydetails UCS1903Controller400Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class UCS1903 : public UCS1903Controller400Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief UCS1903B controller class.
|
||||
/// @copydetails UCS1903BController800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class UCS1903B : public UCS1903BController800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief UCS1904 controller class.
|
||||
/// @copydetails UCS1904Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class UCS1904 : public UCS1904Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief UCS2903 controller class.
|
||||
/// @copydetails UCS2903Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class UCS2903 : public UCS2903Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2812 controller class.
|
||||
/// @copydetails WS2812Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2812 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2815 controller class.
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2815 : public WS2815Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2816 controller class.
|
||||
template <uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2816 : public WS2816Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2852 controller class.
|
||||
/// @copydetails WS2812Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2852 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2812B controller class.
|
||||
/// @copydetails WS2812Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2812B : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief GS1903 controller class.
|
||||
/// @copydetails WS2812Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class GS1903 : public WS2812Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief SK6812 controller class.
|
||||
/// @copydetails SK6812Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class SK6812 : public SK6812Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief SK6822 controller class.
|
||||
/// @copydetails SK6822Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class SK6822 : public SK6822Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief APA106 controller class.
|
||||
/// @copydetails SK6822Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class APA106 : public SK6822Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief PL9823 controller class.
|
||||
/// @copydetails PL9823Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class PL9823 : public PL9823Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2811 controller class.
|
||||
/// @copydetails WS2811Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2811 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2813 controller class.
|
||||
/// @copydetails WS2813Controller
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2813 : public WS2813Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief APA104 controller class.
|
||||
/// @copydetails WS2811Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class APA104 : public WS2811Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief WS2811_400 controller class.
|
||||
/// @copydetails WS2811Controller400Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class WS2811_400 : public WS2811Controller400Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief GE8822 controller class.
|
||||
/// @copydetails GE8822Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class GE8822 : public GE8822Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief GW6205 controller class.
|
||||
/// @copydetails GW6205Controller800Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class GW6205 : public GW6205Controller800Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief GW6205_400 controller class.
|
||||
/// @copydetails GW6205Controller400Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class GW6205_400 : public GW6205Controller400Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief LPD1886 controller class.
|
||||
/// @copydetails LPD1886Controller1250Khz
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class LPD1886 : public LPD1886Controller1250Khz<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief LPD1886_8BIT controller class.
|
||||
/// @copydetails LPD1886Controller1250Khz_8bit
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class LPD1886_8BIT : public LPD1886Controller1250Khz_8bit<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
/// @brief UCS1912 controller class.
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
class UCS1912 : public UCS1912Controller<DATA_PIN, RGB_ORDER> {};
|
||||
|
||||
#if defined(DmxSimple_h) || defined(FASTLED_DOXYGEN)
|
||||
/// @copydoc DMXSimpleController
|
||||
template<uint8_t DATA_PIN, EOrder RGB_ORDER> class DMXSIMPLE : public DMXSimpleController<DATA_PIN, RGB_ORDER> {};
|
||||
#endif
|
||||
#if defined(DmxSerial_h) || defined(FASTLED_DOXYGEN)
|
||||
/// @copydoc DMXSerialController
|
||||
template<EOrder RGB_ORDER> class DMXSERIAL : public DMXSerialController<RGB_ORDER> {};
|
||||
#endif
|
||||
#endif
|
||||
/// @} ClocklessChipsets
|
||||
/// @} Chipsets
|
||||
|
||||
|
||||
/// Blockless output port enum
|
||||
enum EBlockChipsets {
|
||||
#ifdef PORTA_FIRST_PIN
|
||||
WS2811_PORTA,
|
||||
WS2813_PORTA,
|
||||
WS2811_400_PORTA,
|
||||
TM1803_PORTA,
|
||||
UCS1903_PORTA,
|
||||
#endif
|
||||
#ifdef PORTB_FIRST_PIN
|
||||
WS2811_PORTB,
|
||||
WS2813_PORTB,
|
||||
WS2811_400_PORTB,
|
||||
TM1803_PORTB,
|
||||
UCS1903_PORTB,
|
||||
#endif
|
||||
#ifdef PORTC_FIRST_PIN
|
||||
WS2811_PORTC,
|
||||
WS2813_PORTC,
|
||||
WS2811_400_PORTC,
|
||||
TM1803_PORTC,
|
||||
UCS1903_PORTC,
|
||||
#endif
|
||||
#ifdef PORTD_FIRST_PIN
|
||||
WS2811_PORTD,
|
||||
WS2813_PORTD,
|
||||
WS2811_400_PORTD,
|
||||
TM1803_PORTD,
|
||||
UCS1903_PORTD,
|
||||
#endif
|
||||
#ifdef HAS_PORTDC
|
||||
WS2811_PORTDC,
|
||||
WS2813_PORTDC,
|
||||
WS2811_400_PORTDC,
|
||||
TM1803_PORTDC,
|
||||
UCS1903_PORTDC,
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Typedef for a power consumption calculation function. Used within
|
||||
/// CFastLED for rescaling brightness before sending the LED data to
|
||||
/// the strip with CFastLED::show().
|
||||
/// @param scale the initial brightness scale value
|
||||
/// @param data max power data, in milliwatts
|
||||
/// @returns the brightness scale, limited to max power
|
||||
typedef uint8_t (*power_func)(uint8_t scale, uint32_t data);
|
||||
|
||||
/// High level controller interface for FastLED.
|
||||
/// This class manages controllers, global settings, and trackings such as brightness
|
||||
/// and refresh rates, and provides access functions for driving led data to controllers
|
||||
/// via the show() / showColor() / clear() methods.
|
||||
/// This is instantiated as a global object with the name FastLED.
|
||||
/// @nosubgrouping
|
||||
class CFastLED {
|
||||
// int m_nControllers;
|
||||
uint8_t m_Scale; ///< the current global brightness scale setting
|
||||
uint16_t m_nFPS; ///< tracking for current frames per second (FPS) value
|
||||
uint32_t m_nMinMicros; ///< minimum µs between frames, used for capping frame rates
|
||||
uint32_t m_nPowerData; ///< max power use parameter
|
||||
power_func m_pPowerFunc; ///< function for overriding brightness when using FastLED.show();
|
||||
|
||||
public:
|
||||
CFastLED();
|
||||
|
||||
// Useful when you want to know when an event like onFrameBegin or onFrameEnd is happening.
|
||||
// This is disabled on AVR to save space.
|
||||
void addListener(fl::EngineEvents::Listener *listener) { fl::EngineEvents::addListener(listener); }
|
||||
void removeListener(fl::EngineEvents::Listener *listener) { fl::EngineEvents::removeListener(listener); }
|
||||
|
||||
/// Add a CLEDController instance to the world. Exposed to the public to allow people to implement their own
|
||||
/// CLEDController objects or instances. There are two ways to call this method (as well as the other addLeds()
|
||||
/// variations). The first is with 3 arguments, in which case the arguments are the controller, a pointer to
|
||||
/// led data, and the number of leds used by this controller. The second is with 4 arguments, in which case
|
||||
/// the first two arguments are the same, the third argument is an offset into the CRGB data where this controller's
|
||||
/// CRGB data begins, and the fourth argument is the number of leds for this controller object.
|
||||
/// @param pLed the led controller being added
|
||||
/// @param data base pointer to an array of CRGB data structures
|
||||
/// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array
|
||||
/// @param nLedsIfOffset number of leds (4 argument version)
|
||||
/// @returns a reference to the added controller
|
||||
static CLEDController &addLeds(CLEDController *pLed, struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0);
|
||||
|
||||
/// @name Adding SPI-based controllers
|
||||
/// Add an SPI based CLEDController instance to the world.
|
||||
///
|
||||
/// There are two ways to call this method (as well as the other addLeds()
|
||||
/// variations). The first is with 2 arguments, in which case the arguments are a pointer to
|
||||
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
|
||||
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
|
||||
/// CRGB data begins, and the third argument is the number of leds for this controller object.
|
||||
///
|
||||
/// @param data base pointer to an array of CRGB data structures
|
||||
/// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array
|
||||
/// @param nLedsIfOffset number of leds (4 argument version)
|
||||
/// @tparam CHIPSET the chipset type
|
||||
/// @tparam DATA_PIN the optional data pin for the leds (if omitted, will default to the first hardware SPI MOSI pin)
|
||||
/// @tparam CLOCK_PIN the optional clock pin for the leds (if omitted, will default to the first hardware SPI clock pin)
|
||||
/// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
|
||||
/// @tparam SPI_DATA_RATE the data rate to drive the SPI clock at, defined using DATA_RATE_MHZ or DATA_RATE_KHZ macros
|
||||
/// @returns a reference to the added controller
|
||||
/// @{
|
||||
|
||||
|
||||
// Base template: Causes a compile-time error if an unsupported CHIPSET is used
|
||||
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN>
|
||||
struct ClockedChipsetHelper {
|
||||
// Default implementation, will be specialized for supported chipsets
|
||||
static const bool IS_VALID = false;
|
||||
};
|
||||
|
||||
// Macro to define a mapping from the ESPIChipeset enum to the controller class
|
||||
// in it's various template configurations.
|
||||
#define _FL_MAP_CLOCKED_CHIPSET(CHIPSET_ENUM, CONTROLLER_CLASS) \
|
||||
template<uint8_t DATA_PIN, uint8_t CLOCK_PIN> \
|
||||
struct ClockedChipsetHelper<CHIPSET_ENUM, DATA_PIN, CLOCK_PIN> { \
|
||||
static const bool IS_VALID = true; \
|
||||
typedef CONTROLLER_CLASS<DATA_PIN, CLOCK_PIN> ControllerType; \
|
||||
/* Controller type with RGB_ORDER specified */ \
|
||||
template<EOrder RGB_ORDER> \
|
||||
struct CONTROLLER_CLASS_WITH_ORDER { \
|
||||
typedef CONTROLLER_CLASS<DATA_PIN, CLOCK_PIN, RGB_ORDER> ControllerType; \
|
||||
}; \
|
||||
/* Controller type with RGB_ORDER and spi frequency specified */ \
|
||||
template<EOrder RGB_ORDER, uint32_t FREQ> \
|
||||
struct CONTROLLER_CLASS_WITH_ORDER_AND_FREQ { \
|
||||
typedef CONTROLLER_CLASS<DATA_PIN, CLOCK_PIN, RGB_ORDER, FREQ> ControllerType; \
|
||||
}; \
|
||||
};
|
||||
|
||||
// Define specializations for each supported CHIPSET
|
||||
_FL_MAP_CLOCKED_CHIPSET(LPD6803, LPD6803Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(LPD8806, LPD8806Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(WS2801, WS2801Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(WS2803, WS2803Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(SM16716, SM16716Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(P9813, P9813Controller)
|
||||
|
||||
// Both DOTSTAR and APA102 use the same controller class
|
||||
_FL_MAP_CLOCKED_CHIPSET(DOTSTAR, APA102Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(APA102, APA102Controller)
|
||||
|
||||
// Both DOTSTARHD and APA102HD use the same controller class
|
||||
_FL_MAP_CLOCKED_CHIPSET(DOTSTARHD, APA102ControllerHD)
|
||||
_FL_MAP_CLOCKED_CHIPSET(APA102HD, APA102ControllerHD)
|
||||
|
||||
_FL_MAP_CLOCKED_CHIPSET(HD107, APA102Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(HD107HD, APA102ControllerHD)
|
||||
|
||||
_FL_MAP_CLOCKED_CHIPSET(SK9822, SK9822Controller)
|
||||
_FL_MAP_CLOCKED_CHIPSET(SK9822HD, SK9822ControllerHD)
|
||||
|
||||
|
||||
/// Add an SPI based CLEDController instance to the world.
|
||||
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER, uint32_t SPI_DATA_RATE > CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
// Instantiate the controller using ClockedChipsetHelper
|
||||
typedef ClockedChipsetHelper<CHIPSET, DATA_PIN, CLOCK_PIN> CHIP;
|
||||
typedef typename CHIP::template CONTROLLER_CLASS_WITH_ORDER_AND_FREQ<RGB_ORDER, SPI_DATA_RATE>::ControllerType ControllerTypeWithFreq;
|
||||
static_assert(CHIP::IS_VALID, "Unsupported chipset");
|
||||
static ControllerTypeWithFreq c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
/// Add an SPI based CLEDController instance to the world.
|
||||
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN > static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
typedef ClockedChipsetHelper<CHIPSET, DATA_PIN, CLOCK_PIN> CHIP;
|
||||
typedef typename CHIP::ControllerType ControllerType;
|
||||
static_assert(CHIP::IS_VALID, "Unsupported chipset");
|
||||
static ControllerType c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
|
||||
// The addLeds function using ChipsetHelper
|
||||
template<ESPIChipsets CHIPSET, uint8_t DATA_PIN, uint8_t CLOCK_PIN, EOrder RGB_ORDER>
|
||||
CLEDController& addLeds(struct CRGB* data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
typedef ClockedChipsetHelper<CHIPSET, DATA_PIN, CLOCK_PIN> CHIP;
|
||||
static_assert(CHIP::IS_VALID, "Unsupported chipset");
|
||||
typedef typename CHIP::template CONTROLLER_CLASS_WITH_ORDER<RGB_ORDER>::ControllerType ControllerTypeWithOrder;
|
||||
static ControllerTypeWithOrder c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
|
||||
#ifdef SPI_DATA
|
||||
template<ESPIChipsets CHIPSET> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB>(data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
template<ESPIChipsets CHIPSET, EOrder RGB_ORDER> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER>(data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
template<ESPIChipsets CHIPSET, EOrder RGB_ORDER, uint32_t SPI_DATA_RATE> static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
return addLeds<CHIPSET, SPI_DATA, SPI_CLOCK, RGB_ORDER, SPI_DATA_RATE>(data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
#endif
|
||||
/// @} Adding SPI based controllers
|
||||
|
||||
#ifdef FASTLED_HAS_CLOCKLESS
|
||||
/// @name Adding 3-wire led controllers
|
||||
/// Add a clockless (aka 3-wire, also DMX) based CLEDController instance to the world.
|
||||
///
|
||||
/// There are two ways to call this method (as well as the other addLeds()
|
||||
/// variations). The first is with 2 arguments, in which case the arguments are a pointer to
|
||||
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
|
||||
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
|
||||
/// CRGB data begins, and the third argument is the number of leds for this controller object.
|
||||
///
|
||||
/// This method also takes 2 to 3 template parameters for identifying the specific chipset, data pin,
|
||||
/// RGB ordering, and SPI data rate
|
||||
///
|
||||
/// @param data base pointer to an array of CRGB data structures
|
||||
/// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array
|
||||
/// @param nLedsIfOffset number of leds (4 argument version)
|
||||
/// @tparam CHIPSET the chipset type (required)
|
||||
/// @tparam DATA_PIN the data pin for the leds (required)
|
||||
/// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
|
||||
/// @returns a reference to the added controller
|
||||
/// @{
|
||||
|
||||
/// Add a clockless based CLEDController instance to the world.
|
||||
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
static CHIPSET<DATA_PIN, RGB_ORDER> c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
/// Add a clockless based CLEDController instance to the world.
|
||||
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
static CHIPSET<DATA_PIN, RGB> c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
/// Add a clockless based CLEDController instance to the world.
|
||||
template<template<uint8_t DATA_PIN> class CHIPSET, uint8_t DATA_PIN>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
static CHIPSET<DATA_PIN> c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
#if defined(__FASTLED_HAS_FIBCC) && (__FASTLED_HAS_FIBCC == 1)
|
||||
template<uint8_t NUM_LANES, template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLeds) {
|
||||
static __FIBCC<CHIPSET, DATA_PIN, NUM_LANES, RGB_ORDER> c;
|
||||
return addLeds(&c, data, nLeds);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FASTSPI_USE_DMX_SIMPLE
|
||||
template<EClocklessChipsets CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER=RGB>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
|
||||
{
|
||||
switch(CHIPSET) {
|
||||
case DMX: { static DMXController<DATA_PIN> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// @} Adding 3-wire led controllers
|
||||
#endif
|
||||
|
||||
/// @name Adding 3rd party library controllers
|
||||
/// Add a 3rd party library based CLEDController instance to the world.
|
||||
///
|
||||
/// There are two ways to call this method (as well as the other addLeds()
|
||||
/// variations). The first is with 2 arguments, in which case the arguments are a pointer to
|
||||
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
|
||||
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
|
||||
/// CRGB data begins, and the third argument is the number of leds for this controller object. This class includes the SmartMatrix
|
||||
/// and OctoWS2811 based controllers
|
||||
///
|
||||
/// This method also takes 1 to 2 template parameters for identifying the specific chipset and
|
||||
/// RGB ordering.
|
||||
///
|
||||
/// @param data base pointer to an array of CRGB data structures
|
||||
/// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array
|
||||
/// @param nLedsIfOffset number of leds (4 argument version)
|
||||
/// @tparam CHIPSET the chipset type (required)
|
||||
/// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
|
||||
/// @returns a reference to the added controller
|
||||
/// @{
|
||||
|
||||
/// Add a 3rd party library based CLEDController instance to the world.
|
||||
template<template<EOrder RGB_ORDER> class CHIPSET, EOrder RGB_ORDER>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
static CHIPSET<RGB_ORDER> c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
/// Add a 3rd party library based CLEDController instance to the world.
|
||||
template<template<EOrder RGB_ORDER> class CHIPSET>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
static CHIPSET<RGB> c;
|
||||
return addLeds(&c, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
|
||||
#ifdef USE_OCTOWS2811
|
||||
/// Add a OCTOWS2811 based CLEDController instance to the world.
|
||||
/// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||
/// @see https://github.com/PaulStoffregen/OctoWS2811
|
||||
template<OWS2811 CHIPSET, EOrder RGB_ORDER>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
|
||||
{
|
||||
switch(CHIPSET) {
|
||||
case OCTOWS2811: { static COctoWS2811Controller<RGB_ORDER,WS2811_800kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
|
||||
case OCTOWS2811_400: { static COctoWS2811Controller<RGB_ORDER,WS2811_400kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
|
||||
#ifdef WS2813_800kHz
|
||||
case OCTOWS2813: { static COctoWS2811Controller<RGB_ORDER,WS2813_800kHz> controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a OCTOWS2811 library based CLEDController instance to the world.
|
||||
/// @see https://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||
/// @see https://github.com/PaulStoffregen/OctoWS2811
|
||||
template<OWS2811 CHIPSET>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
|
||||
{
|
||||
return addLeds<CHIPSET,GRB>(data,nLedsOrOffset,nLedsIfOffset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_WS2812SERIAL
|
||||
/// Add a WS2812Serial library based CLEDController instance to the world.
|
||||
/// @see https://www.pjrc.com/non-blocking-ws2812-led-library/
|
||||
/// @see https://github.com/PaulStoffregen/WS2812Serial
|
||||
template<SWS2812 CHIPSET, int DATA_PIN, EOrder RGB_ORDER>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
|
||||
{
|
||||
static CWS2812SerialController<DATA_PIN,RGB_ORDER> controller;
|
||||
return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SmartMatrix_h
|
||||
/// Add a SmartMatrix library based CLEDController instance to the world.
|
||||
/// @see https://github.com/pixelmatix/SmartMatrix
|
||||
template<ESM CHIPSET>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0)
|
||||
{
|
||||
switch(CHIPSET) {
|
||||
case SMART_MATRIX: { static CSmartMatrixController controller; return addLeds(&controller, data, nLedsOrOffset, nLedsIfOffset); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// @} Adding 3rd party library controllers
|
||||
|
||||
|
||||
#ifdef FASTLED_HAS_BLOCKLESS
|
||||
|
||||
/// @name Adding parallel output controllers
|
||||
/// Add a block based CLEDController instance to the world.
|
||||
///
|
||||
/// There are two ways to call this method (as well as the other addLeds()
|
||||
/// variations). The first is with 2 arguments, in which case the arguments are a pointer to
|
||||
/// led data, and the number of leds used by this controller. The second is with 3 arguments, in which case
|
||||
/// the first argument is the same, the second argument is an offset into the CRGB data where this controller's
|
||||
/// CRGB data begins, and the third argument is the number of leds for this controller object.
|
||||
///
|
||||
/// This method also takes a 2 to 3 template parameters for identifying the specific chipset and rgb ordering
|
||||
/// RGB ordering, and SPI data rate
|
||||
///
|
||||
/// @param data base pointer to an array of CRGB data structures
|
||||
/// @param nLedsOrOffset number of leds (3 argument version) or offset into the data array
|
||||
/// @param nLedsIfOffset number of leds (4 argument version)
|
||||
/// @tparam CHIPSET the chipset/port type (required)
|
||||
/// @tparam NUM_LANES how many parallel lanes of output to write
|
||||
/// @tparam RGB_ORDER the rgb ordering for the leds (e.g. what order red, green, and blue data is written out in)
|
||||
/// @returns a reference to the added controller
|
||||
/// @{
|
||||
|
||||
/// Add a block based parallel output CLEDController instance to the world.
|
||||
template<EBlockChipsets CHIPSET, int NUM_LANES, EOrder RGB_ORDER>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
switch(CHIPSET) {
|
||||
#ifdef PORTA_FIRST_PIN
|
||||
case WS2811_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2811_400_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2813_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case TM1803_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case UCS1903_PORTA: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTA_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
#endif
|
||||
#ifdef PORTB_FIRST_PIN
|
||||
case WS2811_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2811_400_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2813_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case TM1803_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case UCS1903_PORTB: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTB_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
#endif
|
||||
#ifdef PORTC_FIRST_PIN
|
||||
case WS2811_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2811_400_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2813_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case TM1803_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case UCS1903_PORTC: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTC_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
#endif
|
||||
#ifdef PORTD_FIRST_PIN
|
||||
case WS2811_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2811_400_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2813_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case TM1803_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case UCS1903_PORTD: return addLeds(new InlineBlockClocklessController<NUM_LANES, PORTD_FIRST_PIN, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
#endif
|
||||
#ifdef HAS_PORTDC
|
||||
case WS2811_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(320), NS(320), NS(640), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2811_400_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES,NS(800), NS(800), NS(900), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case WS2813_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(320), NS(320), NS(640), RGB_ORDER, 0, false, 300>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case TM1803_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(700), NS(1100), NS(700), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
case UCS1903_PORTDC: return addLeds(new SixteenWayInlineBlockClocklessController<NUM_LANES, NS(500), NS(1500), NS(500), RGB_ORDER>(), data, nLedsOrOffset, nLedsIfOffset);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a block based parallel output CLEDController instance to the world.
|
||||
template<EBlockChipsets CHIPSET, int NUM_LANES>
|
||||
static CLEDController &addLeds(struct CRGB *data, int nLedsOrOffset, int nLedsIfOffset = 0) {
|
||||
return addLeds<CHIPSET,NUM_LANES,GRB>(data,nLedsOrOffset,nLedsIfOffset);
|
||||
}
|
||||
/// @} Adding parallel output controllers
|
||||
#endif
|
||||
|
||||
/// Set the global brightness scaling
|
||||
/// @param scale a 0-255 value for how much to scale all leds before writing them out
|
||||
void setBrightness(uint8_t scale) { m_Scale = scale; }
|
||||
|
||||
/// Get the current global brightness setting
|
||||
/// @returns the current global brightness value
|
||||
uint8_t getBrightness() { return m_Scale; }
|
||||
|
||||
/// Set the maximum power to be used, given in volts and milliamps.
|
||||
/// @param volts how many volts the leds are being driven at (usually 5)
|
||||
/// @param milliamps the maximum milliamps of power draw you want
|
||||
inline void setMaxPowerInVoltsAndMilliamps(uint8_t volts, uint32_t milliamps) { setMaxPowerInMilliWatts(volts * milliamps); }
|
||||
|
||||
/// Set the maximum power to be used, given in milliwatts
|
||||
/// @param milliwatts the max power draw desired, in milliwatts
|
||||
inline void setMaxPowerInMilliWatts(uint32_t milliwatts) { m_pPowerFunc = &calculate_max_brightness_for_power_mW; m_nPowerData = milliwatts; }
|
||||
|
||||
/// Update all our controllers with the current led colors, using the passed in brightness
|
||||
/// @param scale the brightness value to use in place of the stored value
|
||||
void show(uint8_t scale);
|
||||
|
||||
/// Update all our controllers with the current led colors
|
||||
void show() { show(m_Scale); }
|
||||
|
||||
/// Clear the leds, wiping the local array of data. Optionally you can also
|
||||
/// send the cleared data to the LEDs.
|
||||
/// @param writeData whether or not to write out to the leds as well
|
||||
void clear(bool writeData = false);
|
||||
|
||||
/// Clear out the local data array
|
||||
void clearData();
|
||||
|
||||
/// Set all leds on all controllers to the given color/scale.
|
||||
/// @param color what color to set the leds to
|
||||
/// @param scale what brightness scale to show at
|
||||
void showColor(const struct CRGB & color, uint8_t scale);
|
||||
|
||||
/// Set all leds on all controllers to the given color
|
||||
/// @param color what color to set the leds to
|
||||
void showColor(const struct CRGB & color) { showColor(color, m_Scale); }
|
||||
|
||||
/// Delay for the given number of milliseconds. Provided to allow the library to be used on platforms
|
||||
/// that don't have a delay function (to allow code to be more portable).
|
||||
/// @note This will call show() constantly to drive the dithering engine (and will call show() at least once).
|
||||
/// @param ms the number of milliseconds to pause for
|
||||
void delay(unsigned long ms);
|
||||
|
||||
/// Set a global color temperature. Sets the color temperature for all added led strips,
|
||||
/// overriding whatever previous color temperature those controllers may have had.
|
||||
/// @param temp A CRGB structure describing the color temperature
|
||||
void setTemperature(const struct CRGB & temp);
|
||||
|
||||
/// Set a global color correction. Sets the color correction for all added led strips,
|
||||
/// overriding whatever previous color correction those controllers may have had.
|
||||
/// @param correction A CRGB structure describin the color correction.
|
||||
void setCorrection(const struct CRGB & correction);
|
||||
|
||||
/// Set the dithering mode. Sets the dithering mode for all added led strips, overriding
|
||||
/// whatever previous dithering option those controllers may have had.
|
||||
/// @param ditherMode what type of dithering to use, either BINARY_DITHER or DISABLE_DITHER
|
||||
void setDither(uint8_t ditherMode = BINARY_DITHER);
|
||||
|
||||
/// Set the maximum refresh rate. This is global for all leds. Attempts to
|
||||
/// call show() faster than this rate will simply wait.
|
||||
/// @note The refresh rate defaults to the slowest refresh rate of all the leds added through addLeds().
|
||||
/// If you wish to set/override this rate, be sure to call setMaxRefreshRate() _after_
|
||||
/// adding all of your leds.
|
||||
/// @param refresh maximum refresh rate in hz
|
||||
/// @param constrain constrain refresh rate to the slowest speed yet set
|
||||
void setMaxRefreshRate(uint16_t refresh, bool constrain=false);
|
||||
|
||||
/// For debugging, this will keep track of time between calls to countFPS(). Every
|
||||
/// `nFrames` calls, it will update an internal counter for the current FPS.
|
||||
/// @todo Make this a rolling counter
|
||||
/// @param nFrames how many frames to time for determining FPS
|
||||
void countFPS(int nFrames=25);
|
||||
|
||||
/// Get the number of frames/second being written out
|
||||
/// @returns the most recently computed FPS value
|
||||
uint16_t getFPS() { return m_nFPS; }
|
||||
|
||||
/// Get how many controllers have been registered
|
||||
/// @returns the number of controllers (strips) that have been added with addLeds()
|
||||
int count();
|
||||
|
||||
/// Get a reference to a registered controller
|
||||
/// @returns a reference to the Nth controller
|
||||
CLEDController & operator[](int x);
|
||||
|
||||
/// Get the number of leds in the first controller
|
||||
/// @returns the number of LEDs in the first controller
|
||||
int size();
|
||||
|
||||
/// Get a pointer to led data for the first controller
|
||||
/// @returns pointer to the CRGB buffer for the first controller
|
||||
CRGB *leds();
|
||||
};
|
||||
|
||||
/// Alias of the FastLED instance for legacy purposes
|
||||
#define FastSPI_LED FastLED
|
||||
/// Alias of the FastLED instance for legacy purposes
|
||||
#define FastSPI_LED2 FastLED
|
||||
#ifndef LEDS
|
||||
/// Alias of the FastLED instance for legacy purposes
|
||||
#define LEDS FastLED
|
||||
#endif
|
||||
|
||||
/// Global LED strip management instance
|
||||
extern CFastLED FastLED;
|
||||
|
||||
/// If no pin/port mappings are found, sends a warning message to the user
|
||||
/// during compilation.
|
||||
/// @see fastpin.h
|
||||
#ifndef HAS_HARDWARE_PIN_SUPPORT
|
||||
#warning "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FASTLED_UI
|
||||
// As a convenience, include the UI headers and bring them into the global namespace
|
||||
#include "fl/ui.h"
|
||||
#include "fl/xymap.h"
|
||||
using fl::UIButton;
|
||||
using fl::UICheckbox;
|
||||
using fl::UINumberField;
|
||||
using fl::UISlider;
|
||||
using fl::XYMap;
|
||||
#define FASTLED_TITLE(text) fl::UITitle g_title(text)
|
||||
#define FASTLED_DESCRIPTION(text) fl::UIDescription g_description(text)
|
||||
#endif // FASTLED_UI
|
||||
|
||||
#if defined(FASTLED_FORCE_USE_NAMESPACE) && FASTLED_FORCE_USE_NAMESPACE==1
|
||||
using namespace fl;
|
||||
#endif
|
||||
|
||||
|
||||
5
FastLED/src/allocator.h
Normal file
5
FastLED/src/allocator.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#warning "This header is deprecated. Please use allocator.h, this header will go away in the version 4.0."
|
||||
|
||||
#include "fl/allocator.h"
|
||||
5
FastLED/src/bitswap.cpp
Normal file
5
FastLED/src/bitswap.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
/// @file bitswap.cpp
|
||||
/// Functions for doing a rotation of bits/bytes used by parallel output
|
||||
|
||||
/// Disables pragma messages and warnings
|
||||
#define FASTLED_INTERNAL
|
||||
293
FastLED/src/bitswap.h
Normal file
293
FastLED/src/bitswap.h
Normal file
@@ -0,0 +1,293 @@
|
||||
#ifndef __INC_BITSWAP_H
|
||||
#define __INC_BITSWAP_H
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
/// @file bitswap.h
|
||||
/// Functions for doing a rotation of bits/bytes used by parallel output
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
#if defined(FASTLED_ARM) || defined(FASTLED_ESP8266) || defined(FASTLED_DOXYGEN)
|
||||
/// Structure representing 8 bits of access
|
||||
typedef union {
|
||||
uint8_t raw; ///< the entire byte
|
||||
struct {
|
||||
uint32_t a0:1; ///< bit 0 (0x01)
|
||||
uint32_t a1:1; ///< bit 1 (0x02)
|
||||
uint32_t a2:1; ///< bit 2 (0x04)
|
||||
uint32_t a3:1; ///< bit 3 (0x08)
|
||||
uint32_t a4:1; ///< bit 4 (0x10)
|
||||
uint32_t a5:1; ///< bit 5 (0x20)
|
||||
uint32_t a6:1; ///< bit 6 (0x40)
|
||||
uint32_t a7:1; ///< bit 7 (0x80)
|
||||
};
|
||||
} just8bits;
|
||||
|
||||
/// Structure representing 32 bits of access
|
||||
typedef struct {
|
||||
uint32_t a0:1; ///< byte 'a', bit 0 (0x00000000)
|
||||
uint32_t a1:1; ///< byte 'a', bit 1 (0x00000002)
|
||||
uint32_t a2:1; ///< byte 'a', bit 2 (0x00000004)
|
||||
uint32_t a3:1; ///< byte 'a', bit 3 (0x00000008)
|
||||
uint32_t a4:1; ///< byte 'a', bit 4 (0x00000010)
|
||||
uint32_t a5:1; ///< byte 'a', bit 5 (0x00000020)
|
||||
uint32_t a6:1; ///< byte 'a', bit 6 (0x00000040)
|
||||
uint32_t a7:1; ///< byte 'a', bit 7 (0x00000080)
|
||||
uint32_t b0:1; ///< byte 'b', bit 0 (0x00000100)
|
||||
uint32_t b1:1; ///< byte 'b', bit 1 (0x00000200)
|
||||
uint32_t b2:1; ///< byte 'b', bit 2 (0x00000400)
|
||||
uint32_t b3:1; ///< byte 'b', bit 3 (0x00000800)
|
||||
uint32_t b4:1; ///< byte 'b', bit 4 (0x00001000)
|
||||
uint32_t b5:1; ///< byte 'b', bit 5 (0x00002000)
|
||||
uint32_t b6:1; ///< byte 'b', bit 6 (0x00004000)
|
||||
uint32_t b7:1; ///< byte 'b', bit 7 (0x00008000)
|
||||
uint32_t c0:1; ///< byte 'c', bit 0 (0x00010000)
|
||||
uint32_t c1:1; ///< byte 'c', bit 1 (0x00020000)
|
||||
uint32_t c2:1; ///< byte 'c', bit 2 (0x00040000)
|
||||
uint32_t c3:1; ///< byte 'c', bit 3 (0x00080000)
|
||||
uint32_t c4:1; ///< byte 'c', bit 4 (0x00100000)
|
||||
uint32_t c5:1; ///< byte 'c', bit 5 (0x00200000)
|
||||
uint32_t c6:1; ///< byte 'c', bit 6 (0x00400000)
|
||||
uint32_t c7:1; ///< byte 'c', bit 7 (0x00800000)
|
||||
uint32_t d0:1; ///< byte 'd', bit 0 (0x01000000)
|
||||
uint32_t d1:1; ///< byte 'd', bit 1 (0x02000000)
|
||||
uint32_t d2:1; ///< byte 'd', bit 2 (0x04000000)
|
||||
uint32_t d3:1; ///< byte 'd', bit 3 (0x08000000)
|
||||
uint32_t d4:1; ///< byte 'd', bit 4 (0x10000000)
|
||||
uint32_t d5:1; ///< byte 'd', bit 5 (0x20000000)
|
||||
uint32_t d6:1; ///< byte 'd', bit 6 (0x40000000)
|
||||
uint32_t d7:1; ///< byte 'd', bit 7 (0x80000000)
|
||||
} sub4;
|
||||
|
||||
/// Union containing a full 8 bytes to swap the bit orientation on
|
||||
typedef union {
|
||||
uint32_t word[2]; ///< two 32-bit values to load for swapping
|
||||
uint8_t bytes[8]; ///< eight 8-bit values to load for swapping
|
||||
struct {
|
||||
sub4 a; ///< 32-bit access struct for bit swapping, upper four bytes (word[0] or bytes[0-3])
|
||||
sub4 b; ///< 32-bit access struct for bit swapping, lower four bytes (word[1] or bytes[4-7])
|
||||
};
|
||||
} bitswap_type;
|
||||
|
||||
|
||||
/// Set `out.X` bits 0, 1, 2, and 3 to bit N
|
||||
/// of `in.a.a`, `in.a.b`, `in.a.b`, `in.a.c`, and `in.a.d`
|
||||
/// @param X the sub4 of `out` to set
|
||||
/// @param N the bit of each byte to retrieve
|
||||
/// @see bitswap_type
|
||||
#define SWAPSA(X,N) out. X ## 0 = in.a.a ## N; \
|
||||
out. X ## 1 = in.a.b ## N; \
|
||||
out. X ## 2 = in.a.c ## N; \
|
||||
out. X ## 3 = in.a.d ## N;
|
||||
|
||||
/// Set `out.X` bits 0, 1, 2, and 3 to bit N
|
||||
/// of `in.b.a`, `in.b.b`, `in.b.b`, `in.b.c`, and `in.b.d`
|
||||
/// @param X the sub4 of `out` to set
|
||||
/// @param N the bit of each byte to retrieve
|
||||
/// @see bitswap_type
|
||||
#define SWAPSB(X,N) out. X ## 0 = in.b.a ## N; \
|
||||
out. X ## 1 = in.b.b ## N; \
|
||||
out. X ## 2 = in.b.c ## N; \
|
||||
out. X ## 3 = in.b.d ## N;
|
||||
|
||||
/// Set `out.X` bits to bit N of both `in.a` and `in.b`
|
||||
/// in order
|
||||
/// @param X the sub4 of `out` to set
|
||||
/// @param N the bit of each byte to retrieve
|
||||
/// @see bitswap_type
|
||||
#define SWAPS(X,N) out. X ## 0 = in.a.a ## N; \
|
||||
out. X ## 1 = in.a.b ## N; \
|
||||
out. X ## 2 = in.a.c ## N; \
|
||||
out. X ## 3 = in.a.d ## N; \
|
||||
out. X ## 4 = in.b.a ## N; \
|
||||
out. X ## 5 = in.b.b ## N; \
|
||||
out. X ## 6 = in.b.c ## N; \
|
||||
out. X ## 7 = in.b.d ## N;
|
||||
|
||||
|
||||
/// Do an 8-byte by 8-bit rotation
|
||||
FASTLED_FORCE_INLINE void swapbits8(bitswap_type in, bitswap_type & out) {
|
||||
|
||||
// SWAPS(a.a,7);
|
||||
// SWAPS(a.b,6);
|
||||
// SWAPS(a.c,5);
|
||||
// SWAPS(a.d,4);
|
||||
// SWAPS(b.a,3);
|
||||
// SWAPS(b.b,2);
|
||||
// SWAPS(b.c,1);
|
||||
// SWAPS(b.d,0);
|
||||
|
||||
// SWAPSA(a.a,7);
|
||||
// SWAPSA(a.b,6);
|
||||
// SWAPSA(a.c,5);
|
||||
// SWAPSA(a.d,4);
|
||||
//
|
||||
// SWAPSB(a.a,7);
|
||||
// SWAPSB(a.b,6);
|
||||
// SWAPSB(a.c,5);
|
||||
// SWAPSB(a.d,4);
|
||||
//
|
||||
// SWAPSA(b.a,3);
|
||||
// SWAPSA(b.b,2);
|
||||
// SWAPSA(b.c,1);
|
||||
// SWAPSA(b.d,0);
|
||||
// //
|
||||
// SWAPSB(b.a,3);
|
||||
// SWAPSB(b.b,2);
|
||||
// SWAPSB(b.c,1);
|
||||
// SWAPSB(b.d,0);
|
||||
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
just8bits work;
|
||||
work.a3 = in.word[0] >> 31;
|
||||
work.a2 = in.word[0] >> 23;
|
||||
work.a1 = in.word[0] >> 15;
|
||||
work.a0 = in.word[0] >> 7;
|
||||
in.word[0] <<= 1;
|
||||
work.a7 = in.word[1] >> 31;
|
||||
work.a6 = in.word[1] >> 23;
|
||||
work.a5 = in.word[1] >> 15;
|
||||
work.a4 = in.word[1] >> 7;
|
||||
in.word[1] <<= 1;
|
||||
out.bytes[i] = work.raw;
|
||||
}
|
||||
}
|
||||
|
||||
/// Slow version of the 8 byte by 8 bit rotation
|
||||
FASTLED_FORCE_INLINE void slowswap(unsigned char *A, unsigned char *B) {
|
||||
|
||||
for(int row = 0; row < 7; ++row) {
|
||||
uint8_t x = A[row];
|
||||
|
||||
uint8_t bit = (1<<row);
|
||||
unsigned char *p = B;
|
||||
for(uint32_t mask = 1<<7 ; mask ; mask >>= 1) {
|
||||
if(x & mask) {
|
||||
*p++ |= bit;
|
||||
} else {
|
||||
*p++ &= ~bit;
|
||||
}
|
||||
}
|
||||
// B[7] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[6] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[5] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[4] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[3] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[2] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[1] |= (x & 0x01) << row; x >>= 1;
|
||||
// B[0] |= (x & 0x01) << row; x >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified form of bits rotating function.
|
||||
/// This rotates data into LSB for a faster write (the code using this data can happily walk the array backwards).
|
||||
/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
|
||||
void transpose8x1_noinline(unsigned char *A, unsigned char *B);
|
||||
|
||||
/// @copydoc transpose8x1_noinline()
|
||||
FASTLED_FORCE_INLINE void transpose8x1(unsigned char *A, unsigned char *B) {
|
||||
uint32_t x, y, t;
|
||||
|
||||
// Load the array and pack it into x and y.
|
||||
y = *(unsigned int*)(A);
|
||||
x = *(unsigned int*)(A+4);
|
||||
|
||||
// pre-transform x
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
|
||||
|
||||
// pre-transform y
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
|
||||
|
||||
// final transform
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
*((uint32_t*)B) = y;
|
||||
*((uint32_t*)(B+4)) = x;
|
||||
}
|
||||
|
||||
/// Simplified form of bits rotating function.
|
||||
/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
|
||||
FASTLED_FORCE_INLINE void transpose8x1_MSB(unsigned char *A, unsigned char *B) {
|
||||
uint32_t x, y, t;
|
||||
|
||||
// Load the array and pack it into x and y.
|
||||
y = *(unsigned int*)(A);
|
||||
x = *(unsigned int*)(A+4);
|
||||
|
||||
// pre-transform x
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
|
||||
|
||||
// pre-transform y
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
|
||||
|
||||
// final transform
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
B[7] = y; y >>= 8;
|
||||
B[6] = y; y >>= 8;
|
||||
B[5] = y; y >>= 8;
|
||||
B[4] = y;
|
||||
|
||||
B[3] = x; x >>= 8;
|
||||
B[2] = x; x >>= 8;
|
||||
B[1] = x; x >>= 8;
|
||||
B[0] = x; /* */
|
||||
}
|
||||
|
||||
/// Templated bit-rotating function.
|
||||
/// Based on code found here: https://web.archive.org/web/20190108225554/http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
|
||||
template<int m, int n>
|
||||
FASTLED_FORCE_INLINE void transpose8(unsigned char *A, unsigned char *B) {
|
||||
uint32_t x, y, t;
|
||||
|
||||
// Load the array and pack it into x and y.
|
||||
if(m == 1) {
|
||||
y = *(unsigned int*)(A);
|
||||
x = *(unsigned int*)(A+4);
|
||||
} else {
|
||||
x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
|
||||
y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
|
||||
}
|
||||
|
||||
// pre-transform x
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
|
||||
|
||||
// pre-transform y
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
|
||||
|
||||
// final transform
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
B[7*n] = y; y >>= 8;
|
||||
B[6*n] = y; y >>= 8;
|
||||
B[5*n] = y; y >>= 8;
|
||||
B[4*n] = y;
|
||||
|
||||
B[3*n] = x; x >>= 8;
|
||||
B[2*n] = x; x >>= 8;
|
||||
B[n] = x; x >>= 8;
|
||||
B[0] = x;
|
||||
// B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x>>0;
|
||||
// B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y>>0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
1234
FastLED/src/chipsets.h
Normal file
1234
FastLED/src/chipsets.h
Normal file
File diff suppressed because it is too large
Load Diff
110
FastLED/src/chsv.h
Normal file
110
FastLED/src/chsv.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/// @file chsv.h
|
||||
/// Defines the hue, saturation, and value (HSV) pixel struct
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// @addtogroup PixelTypes Pixel Data Types (CRGB/CHSV)
|
||||
/// @{
|
||||
|
||||
/// Representation of an HSV pixel (hue, saturation, value (aka brightness)).
|
||||
struct CHSV {
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
/// Color hue.
|
||||
/// This is an 8-bit value representing an angle around
|
||||
/// the color wheel. Where 0 is 0°, and 255 is 358°.
|
||||
uint8_t hue;
|
||||
uint8_t h; ///< @copydoc hue
|
||||
};
|
||||
union {
|
||||
/// Color saturation.
|
||||
/// This is an 8-bit value representing a percentage.
|
||||
uint8_t saturation;
|
||||
uint8_t sat; ///< @copydoc saturation
|
||||
uint8_t s; ///< @copydoc saturation
|
||||
};
|
||||
union {
|
||||
/// Color value (brightness).
|
||||
/// This is an 8-bit value representing a percentage.
|
||||
uint8_t value;
|
||||
uint8_t val; ///< @copydoc value
|
||||
uint8_t v; ///< @copydoc value
|
||||
};
|
||||
};
|
||||
/// Access the hue, saturation, and value data as an array.
|
||||
/// Where:
|
||||
/// * `raw[0]` is the hue
|
||||
/// * `raw[1]` is the saturation
|
||||
/// * `raw[2]` is the value
|
||||
uint8_t raw[3];
|
||||
};
|
||||
|
||||
/// Array access operator to index into the CHSV object
|
||||
/// @param x the index to retrieve (0-2)
|
||||
/// @returns the CHSV::raw value for the given index
|
||||
inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline))
|
||||
{
|
||||
return raw[x];
|
||||
}
|
||||
|
||||
/// @copydoc operator[]
|
||||
inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline))
|
||||
{
|
||||
return raw[x];
|
||||
}
|
||||
|
||||
/// Default constructor
|
||||
/// @warning Default values are UNITIALIZED!
|
||||
constexpr inline CHSV() __attribute__((always_inline)): h(0), s(0), v(0) { }
|
||||
|
||||
/// Allow construction from hue, saturation, and value
|
||||
/// @param ih input hue
|
||||
/// @param is input saturation
|
||||
/// @param iv input value
|
||||
constexpr inline CHSV( uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
|
||||
: h(ih), s(is), v(iv)
|
||||
{
|
||||
}
|
||||
|
||||
/// Allow copy construction
|
||||
constexpr inline CHSV(const CHSV& rhs) noexcept : h(rhs.h), s(rhs.s), v(rhs.v) { }
|
||||
|
||||
/// Allow copy construction
|
||||
inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default;
|
||||
|
||||
/// Assign new HSV values
|
||||
/// @param ih input hue
|
||||
/// @param is input saturation
|
||||
/// @param iv input value
|
||||
/// @returns reference to the CHSV object
|
||||
inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline))
|
||||
{
|
||||
h = ih;
|
||||
s = is;
|
||||
v = iv;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// Pre-defined hue values for CHSV objects
|
||||
typedef enum {
|
||||
HUE_RED = 0, ///< Red (0°)
|
||||
HUE_ORANGE = 32, ///< Orange (45°)
|
||||
HUE_YELLOW = 64, ///< Yellow (90°)
|
||||
HUE_GREEN = 96, ///< Green (135°)
|
||||
HUE_AQUA = 128, ///< Aqua (180°)
|
||||
HUE_BLUE = 160, ///< Blue (225°)
|
||||
HUE_PURPLE = 192, ///< Purple (270°)
|
||||
HUE_PINK = 224 ///< Pink (315°)
|
||||
} HSVHue;
|
||||
|
||||
/// @} PixelTypes
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
48
FastLED/src/cled_controller.cpp
Normal file
48
FastLED/src/cled_controller.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/// @file cled_controller.cpp
|
||||
/// base definitions used by led controllers for writing out led data
|
||||
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "cled_controller.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
CLEDController::~CLEDController() = default;
|
||||
|
||||
/// Create an led controller object, add it to the chain of controllers
|
||||
CLEDController::CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
|
||||
m_pNext = NULL;
|
||||
if(m_pHead==NULL) { m_pHead = this; }
|
||||
if(m_pTail != NULL) { m_pTail->m_pNext = this; }
|
||||
m_pTail = this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLEDController::clearLedDataInternal(int nLeds) {
|
||||
if(m_Data) {
|
||||
nLeds = (nLeds < 0) ? m_nLeds : nLeds;
|
||||
nLeds = (nLeds > m_nLeds) ? m_nLeds : nLeds;
|
||||
memset((void*)m_Data, 0, sizeof(struct CRGB) * nLeds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColorAdjustment CLEDController::getAdjustmentData(uint8_t brightness) {
|
||||
// *premixed = getAdjustment(brightness);
|
||||
// if (color_correction) {
|
||||
// *color_correction = getAdjustment(255);
|
||||
// }
|
||||
#if FASTLED_HD_COLOR_MIXING
|
||||
ColorAdjustment out = {this->getAdjustment(brightness), this->getAdjustment(255), brightness};
|
||||
#else
|
||||
ColorAdjustment out = {getAdjustment(brightness)};
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
279
FastLED/src/cled_controller.h
Normal file
279
FastLED/src/cled_controller.h
Normal file
@@ -0,0 +1,279 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/// @file cled_controller.h
|
||||
/// base definitions used by led controllers for writing out led data
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "led_sysdefs.h"
|
||||
#include "pixeltypes.h"
|
||||
#include "color.h"
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/unused.h"
|
||||
#include "pixel_controller.h"
|
||||
#include "dither_mode.h"
|
||||
#include "pixel_iterator.h"
|
||||
#include "fl/engine_events.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/virtual_if_not_avr.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LED Controller interface definition
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available.
|
||||
/// If you want to pass LED controllers around to methods, make them references to this type, keeps your code saner. However,
|
||||
/// most people won't be seeing/using these objects directly at all.
|
||||
/// @note That the methods for eventual checking of background writing of data (I'm looking at you, Teensy 3.0 DMA controller!)
|
||||
/// are not yet implemented.
|
||||
class CLEDController {
|
||||
protected:
|
||||
friend class CFastLED;
|
||||
CRGB *m_Data; ///< pointer to the LED data used by this controller
|
||||
CLEDController *m_pNext; ///< pointer to the next LED controller in the linked list
|
||||
CRGB m_ColorCorrection; ///< CRGB object representing the color correction to apply to the strip on show() @see setCorrection
|
||||
CRGB m_ColorTemperature; ///< CRGB object representing the color temperature to apply to the strip on show() @see setTemperature
|
||||
EDitherMode m_DitherMode; ///< the current dither mode of the controller
|
||||
bool m_enabled = true;
|
||||
int m_nLeds; ///< the number of LEDs in the LED data array
|
||||
static CLEDController *m_pHead; ///< pointer to the first LED controller in the linked list
|
||||
static CLEDController *m_pTail; ///< pointer to the last LED controller in the linked list
|
||||
|
||||
public:
|
||||
|
||||
/// Set all the LEDs to a given color.
|
||||
/// @param data the CRGB color to set the LEDs to
|
||||
/// @param nLeds the number of LEDs to set to this color
|
||||
/// @param scale the rgb scaling value for outputting color
|
||||
virtual void showColor(const CRGB & data, int nLeds, uint8_t brightness) = 0;
|
||||
|
||||
/// Write the passed in RGB data out to the LEDs managed by this controller.
|
||||
/// @param data the rgb data to write out to the strip
|
||||
/// @param nLeds the number of LEDs being written out
|
||||
/// @param scale the rgb scaling to apply to each led before writing it out
|
||||
virtual void show(const struct CRGB *data, int nLeds, uint8_t brightness) = 0;
|
||||
|
||||
|
||||
Rgbw mRgbMode = RgbwInvalid::value();
|
||||
CLEDController& setRgbw(const Rgbw& arg = RgbwDefault::value()) {
|
||||
// Note that at this time (Sept 13th, 2024) this is only implemented in the ESP32 driver
|
||||
// directly. For an emulated version please see RGBWEmulatedController in chipsets.h
|
||||
mRgbMode = arg;
|
||||
return *this; // builder pattern.
|
||||
}
|
||||
|
||||
void setEnabled(bool enabled) { m_enabled = enabled; }
|
||||
bool getEnabled() { return m_enabled; }
|
||||
|
||||
CLEDController();
|
||||
// If we added virtual to the AVR boards then we are going to add 600 bytes of memory to the binary
|
||||
// flash size. This is because the virtual destructor pulls in malloc and free, which are the largest
|
||||
// Testing shows that this virtual destructor adds a 600 bytes to the binary on
|
||||
// attiny85 and about 1k for the teensy 4.X series.
|
||||
// Attiny85:
|
||||
// With CLEDController destructor virtual: 11018 bytes to binary.
|
||||
// Without CLEDController destructor virtual: 10666 bytes to binary.
|
||||
VIRTUAL_IF_NOT_AVR ~CLEDController();
|
||||
|
||||
Rgbw getRgbw() const { return mRgbMode; }
|
||||
|
||||
/// Initialize the LED controller
|
||||
virtual void init() = 0;
|
||||
|
||||
/// Clear out/zero out the given number of LEDs.
|
||||
/// @param nLeds the number of LEDs to clear
|
||||
VIRTUAL_IF_NOT_AVR void clearLeds(int nLeds = -1) {
|
||||
clearLedDataInternal(nLeds);
|
||||
showLeds(0);
|
||||
}
|
||||
|
||||
// Compatibility with the 3.8.x codebase.
|
||||
VIRTUAL_IF_NOT_AVR void showLeds(uint8_t brightness) {
|
||||
void* data = beginShowLeds(m_nLeds);
|
||||
showLedsInternal(brightness);
|
||||
endShowLeds(data);
|
||||
}
|
||||
|
||||
ColorAdjustment getAdjustmentData(uint8_t brightness);
|
||||
|
||||
/// @copybrief show(const struct CRGB*, int, CRGB)
|
||||
///
|
||||
/// Will scale for color correction and temperature. Can accept LED data not attached to this controller.
|
||||
/// @param data the LED data to write to the strip
|
||||
/// @param nLeds the number of LEDs in the data array
|
||||
/// @param brightness the brightness of the LEDs
|
||||
/// @see show(const struct CRGB*, int, CRGB)
|
||||
void showInternal(const struct CRGB *data, int nLeds, uint8_t brightness) {
|
||||
if (m_enabled) {
|
||||
show(data, nLeds,brightness);
|
||||
}
|
||||
}
|
||||
|
||||
/// @copybrief showColor(const struct CRGB&, int, CRGB)
|
||||
///
|
||||
/// Will scale for color correction and temperature. Can accept LED data not attached to this controller.
|
||||
/// @param data the CRGB color to set the LEDs to
|
||||
/// @param nLeds the number of LEDs in the data array
|
||||
/// @param brightness the brightness of the LEDs
|
||||
/// @see showColor(const struct CRGB&, int, CRGB)
|
||||
void showColorInternal(const struct CRGB &data, int nLeds, uint8_t brightness) {
|
||||
if (m_enabled) {
|
||||
showColor(data, nLeds, brightness);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the data to the LEDs managed by this controller
|
||||
/// @param brightness the brightness of the LEDs
|
||||
/// @see show(const struct CRGB*, int, uint8_t)
|
||||
void showLedsInternal(uint8_t brightness) {
|
||||
if (m_enabled) {
|
||||
show(m_Data, m_nLeds, brightness);
|
||||
}
|
||||
}
|
||||
|
||||
/// @copybrief showColor(const struct CRGB&, int, CRGB)
|
||||
///
|
||||
/// @param data the CRGB color to set the LEDs to
|
||||
/// @param brightness the brightness of the LEDs
|
||||
/// @see showColor(const struct CRGB&, int, CRGB)
|
||||
void showColorInternal(const struct CRGB & data, uint8_t brightness) {
|
||||
if (m_enabled) {
|
||||
showColor(data, m_nLeds, brightness);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the first LED controller in the linked list of controllers
|
||||
/// @returns CLEDController::m_pHead
|
||||
static CLEDController *head() { return m_pHead; }
|
||||
|
||||
/// Get the next controller in the linked list after this one. Will return NULL at the end of the linked list.
|
||||
/// @returns CLEDController::m_pNext
|
||||
CLEDController *next() { return m_pNext; }
|
||||
|
||||
/// Set the default array of LEDs to be used by this controller
|
||||
/// @param data pointer to the LED data
|
||||
/// @param nLeds the number of LEDs in the LED data
|
||||
CLEDController & setLeds(CRGB *data, int nLeds) {
|
||||
m_Data = data;
|
||||
m_nLeds = nLeds;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Zero out the LED data managed by this controller
|
||||
void clearLedDataInternal(int nLeds = -1);
|
||||
|
||||
/// How many LEDs does this controller manage?
|
||||
/// @returns CLEDController::m_nLeds
|
||||
virtual int size() { return m_nLeds; }
|
||||
|
||||
/// How many Lanes does this controller manage?
|
||||
/// @returns 1 for a non-Parallel controller
|
||||
virtual int lanes() { return 1; }
|
||||
|
||||
/// Pointer to the CRGB array for this controller
|
||||
/// @returns CLEDController::m_Data
|
||||
CRGB* leds() { return m_Data; }
|
||||
|
||||
/// Reference to the n'th LED managed by the controller
|
||||
/// @param x the LED number to retrieve
|
||||
/// @returns reference to CLEDController::m_Data[x]
|
||||
CRGB &operator[](int x) { return m_Data[x]; }
|
||||
|
||||
/// Set the dithering mode for this controller to use
|
||||
/// @param ditherMode the dithering mode to set
|
||||
/// @returns a reference to the controller
|
||||
inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
|
||||
|
||||
CLEDController& setScreenMap(const fl::XYMap& map) {
|
||||
// EngineEvents::onCanvasUiSet(this, map);
|
||||
fl::ScreenMap screenmap = map.toScreenMap();
|
||||
fl::EngineEvents::onCanvasUiSet(this, screenmap);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CLEDController& setScreenMap(const fl::ScreenMap& map) {
|
||||
fl::EngineEvents::onCanvasUiSet(this, map);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CLEDController& setScreenMap(uint16_t width, uint16_t height) {
|
||||
return setScreenMap(fl::XYMap::constructRectangularGrid(width, height));
|
||||
}
|
||||
|
||||
/// Get the dithering option currently set for this controller
|
||||
/// @return the currently set dithering option (CLEDController::m_DitherMode)
|
||||
inline uint8_t getDither() { return m_DitherMode; }
|
||||
|
||||
virtual void* beginShowLeds(int size) {
|
||||
FASTLED_UNUSED(size);
|
||||
// By default, emit an integer. This integer will, by default, be passed back.
|
||||
// If you override beginShowLeds() then
|
||||
// you should also override endShowLeds() to match the return state.
|
||||
//
|
||||
// For async led controllers this should be used as a sync point to block
|
||||
// the caller until the leds from the last draw frame have completed drawing.
|
||||
// for each controller:
|
||||
// beginShowLeds();
|
||||
// for each controller:
|
||||
// showLeds();
|
||||
// for each controller:
|
||||
// endShowLeds();
|
||||
uintptr_t d = getDither();
|
||||
void* out = reinterpret_cast<void*>(d);
|
||||
return out;
|
||||
}
|
||||
|
||||
virtual void endShowLeds(void* data) {
|
||||
// By default recieves the integer that beginShowLeds() emitted.
|
||||
//For async controllers this should be used to signal the controller
|
||||
// to begin transmitting the current frame to the leds.
|
||||
uintptr_t d = reinterpret_cast<uintptr_t>(data);
|
||||
setDither(static_cast<uint8_t>(d));
|
||||
}
|
||||
|
||||
/// The color corrction to use for this controller, expressed as a CRGB object
|
||||
/// @param correction the color correction to set
|
||||
/// @returns a reference to the controller
|
||||
CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
|
||||
|
||||
/// @copydoc setCorrection()
|
||||
CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
|
||||
|
||||
/// Get the correction value used by this controller
|
||||
/// @returns the current color correction (CLEDController::m_ColorCorrection)
|
||||
CRGB getCorrection() { return m_ColorCorrection; }
|
||||
|
||||
/// Set the color temperature, aka white point, for this controller
|
||||
/// @param temperature the color temperature to set
|
||||
/// @returns a reference to the controller
|
||||
CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
|
||||
|
||||
/// @copydoc setTemperature()
|
||||
CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
|
||||
|
||||
/// Get the color temperature, aka white point, for this controller
|
||||
/// @returns the current color temperature (CLEDController::m_ColorTemperature)
|
||||
CRGB getTemperature() { return m_ColorTemperature; }
|
||||
|
||||
/// Get the combined brightness/color adjustment for this controller
|
||||
/// @param scale the brightness scale to get the correction for
|
||||
/// @returns a CRGB object representing the total adjustment, including color correction and color temperature
|
||||
CRGB getAdjustment(uint8_t scale) {
|
||||
return CRGB::computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
|
||||
}
|
||||
|
||||
/// Gets the maximum possible refresh rate of the strip
|
||||
/// @returns the maximum refresh rate, in frames per second (FPS)
|
||||
virtual uint16_t getMaxRefreshRate() const { return 0; }
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
100
FastLED/src/color.h
Normal file
100
FastLED/src/color.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// @file color.h
|
||||
/// Contains definitions for color correction and temperature
|
||||
|
||||
/// @defgroup ColorEnums Color Correction/Temperature
|
||||
/// Definitions for color correction and light temperatures
|
||||
/// @{
|
||||
|
||||
/// @brief Color correction starting points
|
||||
typedef enum {
|
||||
/// Typical values for SMD5050 LEDs
|
||||
TypicalSMD5050=0xFFB0F0 /* 255, 176, 240 */,
|
||||
/// @copydoc TypicalSMD5050
|
||||
TypicalLEDStrip=0xFFB0F0 /* 255, 176, 240 */,
|
||||
|
||||
/// Typical values for 8 mm "pixels on a string".
|
||||
/// Also for many through-hole 'T' package LEDs.
|
||||
Typical8mmPixel=0xFFE08C /* 255, 224, 140 */,
|
||||
/// @copydoc Typical8mmPixel
|
||||
TypicalPixelString=0xFFE08C /* 255, 224, 140 */,
|
||||
|
||||
/// Uncorrected color (0xFFFFFF)
|
||||
UncorrectedColor=0xFFFFFF /* 255, 255, 255 */
|
||||
|
||||
} LEDColorCorrection;
|
||||
|
||||
|
||||
/// @brief Color temperature values
|
||||
/// @details These color values are separated into two groups: black body radiators
|
||||
/// and gaseous light sources.
|
||||
///
|
||||
/// Black body radiators emit a (relatively) continuous spectrum,
|
||||
/// and can be described as having a Kelvin 'temperature'. This includes things
|
||||
/// like candles, tungsten lightbulbs, and sunlight.
|
||||
///
|
||||
/// Gaseous light sources emit discrete spectral bands, and while we can
|
||||
/// approximate their aggregate hue with RGB values, they don't actually
|
||||
/// have a proper Kelvin temperature.
|
||||
///
|
||||
/// @see https://en.wikipedia.org/wiki/Color_temperature
|
||||
typedef enum {
|
||||
// Black Body Radiators
|
||||
// @{
|
||||
/// 1900 Kelvin
|
||||
Candle=0xFF9329 /* 1900 K, 255, 147, 41 */,
|
||||
/// 2600 Kelvin
|
||||
Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */,
|
||||
/// 2850 Kelvin
|
||||
Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */,
|
||||
/// 3200 Kelvin
|
||||
Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */,
|
||||
/// 5200 Kelvin
|
||||
CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */,
|
||||
/// 5400 Kelvin
|
||||
HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */,
|
||||
/// 6000 Kelvin
|
||||
DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */,
|
||||
/// 7000 Kelvin
|
||||
OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */,
|
||||
/// 20000 Kelvin
|
||||
ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */,
|
||||
// @}
|
||||
|
||||
// Gaseous Light Sources
|
||||
// @{
|
||||
/// Warm (yellower) flourescent light bulbs
|
||||
WarmFluorescent=0xFFF4E5 /* 0 K, 255, 244, 229 */,
|
||||
/// Standard flourescent light bulbs
|
||||
StandardFluorescent=0xF4FFFA /* 0 K, 244, 255, 250 */,
|
||||
/// Cool white (bluer) flourescent light bulbs
|
||||
CoolWhiteFluorescent=0xD4EBFF /* 0 K, 212, 235, 255 */,
|
||||
/// Full spectrum flourescent light bulbs
|
||||
FullSpectrumFluorescent=0xFFF4F2 /* 0 K, 255, 244, 242 */,
|
||||
/// Grow light flourescent light bulbs
|
||||
GrowLightFluorescent=0xFFEFF7 /* 0 K, 255, 239, 247 */,
|
||||
/// Black light flourescent light bulbs
|
||||
BlackLightFluorescent=0xA700FF /* 0 K, 167, 0, 255 */,
|
||||
/// Mercury vapor light bulbs
|
||||
MercuryVapor=0xD8F7FF /* 0 K, 216, 247, 255 */,
|
||||
/// Sodium vapor light bulbs
|
||||
SodiumVapor=0xFFD1B2 /* 0 K, 255, 209, 178 */,
|
||||
/// Metal-halide light bulbs
|
||||
MetalHalide=0xF2FCFF /* 0 K, 242, 252, 255 */,
|
||||
/// High-pressure sodium light bulbs
|
||||
HighPressureSodium=0xFFB74C /* 0 K, 255, 183, 76 */,
|
||||
// @}
|
||||
|
||||
/// Uncorrected temperature (0xFFFFFF)
|
||||
UncorrectedTemperature=0xFFFFFF /* 255, 255, 255 */
|
||||
} ColorTemperature;
|
||||
|
||||
/// @} ColorEnums
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
190
FastLED/src/colorpalettes.cpp
Normal file
190
FastLED/src/colorpalettes.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
/// Disables pragma messages and warnings
|
||||
#define FASTLED_INTERNAL
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "colorutils.h"
|
||||
#include "colorpalettes.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_USING_NAMESPACE
|
||||
|
||||
/// @file colorpalettes.cpp
|
||||
/// Definitions for the predefined color palettes supplied by FastLED.
|
||||
/// @note The documentation is in the source file instead of the header
|
||||
/// because it allows Doxygen to automatically inline the values that
|
||||
/// make up each palette.
|
||||
|
||||
/// @addtogroup ColorPalettes
|
||||
/// @{
|
||||
|
||||
/// @defgroup PredefinedPalettes Predefined Color Palettes
|
||||
/// Stock color palettes, only included when used.
|
||||
/// These palettes are all declared as `PROGMEM`, meaning
|
||||
/// that they won't take up SRAM on AVR chips until used.
|
||||
/// Furthermore, the compiler won't even include these
|
||||
/// in your PROGMEM (flash) storage unless you specifically
|
||||
/// use each one, so you only "pay for" those you actually use.
|
||||
/// @{
|
||||
|
||||
/// Cloudy color palette
|
||||
extern const TProgmemRGBPalette16 CloudColors_p FL_PROGMEM =
|
||||
{
|
||||
CRGB::Blue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::DarkBlue,
|
||||
|
||||
CRGB::DarkBlue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::DarkBlue,
|
||||
|
||||
CRGB::Blue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::SkyBlue,
|
||||
CRGB::SkyBlue,
|
||||
|
||||
CRGB::LightBlue,
|
||||
CRGB::White,
|
||||
CRGB::LightBlue,
|
||||
CRGB::SkyBlue
|
||||
};
|
||||
|
||||
/// Lava color palette
|
||||
extern const TProgmemRGBPalette16 LavaColors_p FL_PROGMEM =
|
||||
{
|
||||
CRGB::Black,
|
||||
CRGB::Maroon,
|
||||
CRGB::Black,
|
||||
CRGB::Maroon,
|
||||
|
||||
CRGB::DarkRed,
|
||||
CRGB::DarkRed,
|
||||
CRGB::Maroon,
|
||||
CRGB::DarkRed,
|
||||
|
||||
CRGB::DarkRed,
|
||||
CRGB::DarkRed,
|
||||
CRGB::Red,
|
||||
CRGB::Orange,
|
||||
|
||||
CRGB::White,
|
||||
CRGB::Orange,
|
||||
CRGB::Red,
|
||||
CRGB::DarkRed
|
||||
};
|
||||
|
||||
|
||||
/// Ocean colors, blues and whites
|
||||
extern const TProgmemRGBPalette16 OceanColors_p FL_PROGMEM =
|
||||
{
|
||||
CRGB::MidnightBlue,
|
||||
CRGB::DarkBlue,
|
||||
CRGB::MidnightBlue,
|
||||
CRGB::Navy,
|
||||
|
||||
CRGB::DarkBlue,
|
||||
CRGB::MediumBlue,
|
||||
CRGB::SeaGreen,
|
||||
CRGB::Teal,
|
||||
|
||||
CRGB::CadetBlue,
|
||||
CRGB::Blue,
|
||||
CRGB::DarkCyan,
|
||||
CRGB::CornflowerBlue,
|
||||
|
||||
CRGB::Aquamarine,
|
||||
CRGB::SeaGreen,
|
||||
CRGB::Aqua,
|
||||
CRGB::LightSkyBlue
|
||||
};
|
||||
|
||||
/// Forest colors, greens
|
||||
extern const TProgmemRGBPalette16 ForestColors_p FL_PROGMEM =
|
||||
{
|
||||
CRGB::DarkGreen,
|
||||
CRGB::DarkGreen,
|
||||
CRGB::DarkOliveGreen,
|
||||
CRGB::DarkGreen,
|
||||
|
||||
CRGB::Green,
|
||||
CRGB::ForestGreen,
|
||||
CRGB::OliveDrab,
|
||||
CRGB::Green,
|
||||
|
||||
CRGB::SeaGreen,
|
||||
CRGB::MediumAquamarine,
|
||||
CRGB::LimeGreen,
|
||||
CRGB::YellowGreen,
|
||||
|
||||
CRGB::LightGreen,
|
||||
CRGB::LawnGreen,
|
||||
CRGB::MediumAquamarine,
|
||||
CRGB::ForestGreen
|
||||
};
|
||||
|
||||
/// HSV Rainbow
|
||||
extern const TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM =
|
||||
{
|
||||
0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00,
|
||||
0xABAB00, 0x56D500, 0x00FF00, 0x00D52A,
|
||||
0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5,
|
||||
0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B
|
||||
};
|
||||
|
||||
/// Alias of RainbowStripeColors_p
|
||||
#define RainbowStripesColors_p RainbowStripeColors_p
|
||||
|
||||
/// HSV Rainbow colors with alternatating stripes of black
|
||||
extern const TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM =
|
||||
{
|
||||
0xFF0000, 0x000000, 0xAB5500, 0x000000,
|
||||
0xABAB00, 0x000000, 0x00FF00, 0x000000,
|
||||
0x00AB55, 0x000000, 0x0000FF, 0x000000,
|
||||
0x5500AB, 0x000000, 0xAB0055, 0x000000
|
||||
};
|
||||
|
||||
/// HSV color ramp: blue, purple, pink, red, orange, yellow (and back).
|
||||
/// Basically, everything but the greens, which tend to make
|
||||
/// people's skin look unhealthy. This palette is good for
|
||||
/// lighting at a club or party, where it'll be shining on people.
|
||||
extern const TProgmemRGBPalette16 PartyColors_p FL_PROGMEM =
|
||||
{
|
||||
0x5500AB, 0x84007C, 0xB5004B, 0xE5001B,
|
||||
0xE81700, 0xB84700, 0xAB7700, 0xABAB00,
|
||||
0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E,
|
||||
0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9
|
||||
};
|
||||
|
||||
/// Approximate "black body radiation" palette, akin to
|
||||
/// the FastLED HeatColor() function.
|
||||
/// It's recommended that you use values 0-240 rather than
|
||||
/// the usual 0-255, as the last 15 colors will be
|
||||
/// "wrapping around" from the hot end to the cold end,
|
||||
/// which looks wrong.
|
||||
extern const TProgmemRGBPalette16 HeatColors_p FL_PROGMEM =
|
||||
{
|
||||
0x000000,
|
||||
0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000,
|
||||
0xFF3300, 0xFF6600, 0xFF9900, 0xFFCC00, 0xFFFF00,
|
||||
0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFFFFFF
|
||||
};
|
||||
|
||||
|
||||
/// Rainbow gradient. Provided for situations where you're going
|
||||
/// to use a number of other gradient palettes, AND you want a
|
||||
/// "standard" FastLED rainbow as well.
|
||||
DEFINE_GRADIENT_PALETTE( Rainbow_gp ) {
|
||||
0, 255, 0, 0, // Red
|
||||
32, 171, 85, 0, // Orange
|
||||
64, 171, 171, 0, // Yellow
|
||||
96, 0, 255, 0, // Green
|
||||
128, 0, 171, 85, // Aqua
|
||||
160, 0, 0, 255, // Blue
|
||||
192, 85, 0, 171, // Purple
|
||||
224, 171, 0, 85, // Pink
|
||||
255, 255, 0, 0};// and back to Red
|
||||
|
||||
/// @}
|
||||
/// @}
|
||||
36
FastLED/src/colorpalettes.h
Normal file
36
FastLED/src/colorpalettes.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef __INC_COLORPALETTES_H
|
||||
#define __INC_COLORPALETTES_H
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "colorutils.h"
|
||||
|
||||
/// @file colorpalettes.h
|
||||
/// Declarations for the predefined color palettes supplied by FastLED.
|
||||
|
||||
// Have Doxygen ignore these declarations
|
||||
/// @cond
|
||||
|
||||
// For historical reasons, TProgmemRGBPalette and others may be
|
||||
// defined in sketches. Therefore we treat these as special
|
||||
// and bind to the global namespace.
|
||||
extern const ::TProgmemRGBPalette16 CloudColors_p FL_PROGMEM;
|
||||
extern const ::TProgmemRGBPalette16 LavaColors_p FL_PROGMEM;
|
||||
extern const ::TProgmemRGBPalette16 OceanColors_p FL_PROGMEM;
|
||||
extern const ::TProgmemRGBPalette16 ForestColors_p FL_PROGMEM;
|
||||
|
||||
extern const ::TProgmemRGBPalette16 RainbowColors_p FL_PROGMEM;
|
||||
|
||||
/// Alias of RainbowStripeColors_p
|
||||
#define RainbowStripesColors_p RainbowStripeColors_p
|
||||
extern const ::TProgmemRGBPalette16 RainbowStripeColors_p FL_PROGMEM;
|
||||
|
||||
extern const ::TProgmemRGBPalette16 PartyColors_p FL_PROGMEM;
|
||||
|
||||
extern const ::TProgmemRGBPalette16 HeatColors_p FL_PROGMEM;
|
||||
|
||||
|
||||
DECLARE_GRADIENT_PALETTE( Rainbow_gp);
|
||||
|
||||
/// @endcond
|
||||
|
||||
#endif
|
||||
1453
FastLED/src/colorutils.cpp
Normal file
1453
FastLED/src/colorutils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2308
FastLED/src/colorutils.h
Normal file
2308
FastLED/src/colorutils.h
Normal file
File diff suppressed because it is too large
Load Diff
9
FastLED/src/controller.h
Normal file
9
FastLED/src/controller.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __INC_CONTROLLER_H
|
||||
#define __INC_CONTROLLER_H
|
||||
|
||||
/// @file controller.h
|
||||
/// deprecated: base definitions used by led controllers for writing out led data
|
||||
|
||||
#include "cpixel_ledcontroller.h"
|
||||
|
||||
#endif
|
||||
74
FastLED/src/cpixel_ledcontroller.h
Normal file
74
FastLED/src/cpixel_ledcontroller.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
/// @file cpixel_ledcontroller.h
|
||||
/// defines the templated version of the CLEDController class
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "led_sysdefs.h"
|
||||
#include "pixeltypes.h"
|
||||
#include "color.h"
|
||||
#include "eorder.h"
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "pixel_controller.h"
|
||||
#include "cled_controller.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
|
||||
/// Template extension of the CLEDController class
|
||||
/// @tparam RGB_ORDER the rgb ordering for the LEDs (e.g. what order red, green, and blue data is written out in)
|
||||
/// @tparam LANES how many parallel lanes of output to write
|
||||
/// @tparam MASK bitmask for the output lanes
|
||||
template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLEDController : public CLEDController {
|
||||
protected:
|
||||
|
||||
|
||||
/// Set all the LEDs on the controller to a given color
|
||||
/// @param data the CRGB color to set the LEDs to
|
||||
/// @param nLeds the number of LEDs to set to this color
|
||||
/// @param scale_pre_mixed the RGB scaling of color adjustment + global brightness to apply to each LED (in RGB8 mode).
|
||||
virtual void showColor(const CRGB& data, int nLeds, uint8_t brightness) override {
|
||||
// CRGB premixed, color_correction;
|
||||
// getAdjustmentData(brightness, &premixed, &color_correction);
|
||||
// ColorAdjustment color_adjustment = {premixed, color_correction, brightness};
|
||||
ColorAdjustment color_adjustment = getAdjustmentData(brightness);
|
||||
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, color_adjustment, getDither());
|
||||
showPixels(pixels);
|
||||
}
|
||||
|
||||
/// Write the passed in RGB data out to the LEDs managed by this controller
|
||||
/// @param data the RGB data to write out to the strip
|
||||
/// @param nLeds the number of LEDs being written out
|
||||
/// @param scale_pre_mixed the RGB scaling of color adjustment + global brightness to apply to each LED (in RGB8 mode).
|
||||
virtual void show(const struct CRGB *data, int nLeds, uint8_t brightness) override {
|
||||
ColorAdjustment color_adjustment = getAdjustmentData(brightness);
|
||||
PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds < 0 ? -nLeds : nLeds, color_adjustment, getDither());
|
||||
if(nLeds < 0) {
|
||||
// nLeds < 0 implies that we want to show them in reverse
|
||||
pixels.mAdvance = -pixels.mAdvance;
|
||||
}
|
||||
showPixels(pixels);
|
||||
}
|
||||
|
||||
public:
|
||||
static const EOrder RGB_ORDER_VALUE = RGB_ORDER; ///< The RGB ordering for this controller
|
||||
static const int LANES_VALUE = LANES; ///< The number of lanes for this controller
|
||||
static const uint32_t MASK_VALUE = MASK; ///< The mask for the lanes for this controller
|
||||
CPixelLEDController() : CLEDController() {}
|
||||
|
||||
/// Send the LED data to the strip
|
||||
/// @param pixels the PixelController object for the LED data
|
||||
virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;
|
||||
|
||||
/// Get the number of lanes of the Controller
|
||||
/// @returns LANES from template
|
||||
int lanes() override { return LANES; }
|
||||
};
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
26
FastLED/src/cpp_compat.h
Normal file
26
FastLED/src/cpp_compat.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/// @file cpp_compat.h
|
||||
/// Compatibility functions based on C++ version
|
||||
|
||||
#ifndef __INC_CPP_COMPAT_H
|
||||
#define __INC_CPP_COMPAT_H
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#if __cplusplus <= 199711L
|
||||
|
||||
/// Compile-time assertion checking, introduced in C++11
|
||||
/// @see https://en.cppreference.com/w/cpp/language/static_assert
|
||||
#define static_assert(expression, message)
|
||||
|
||||
/// Declares that it is possible to evaluate a value at compile time, introduced in C++11
|
||||
/// @see https://en.cppreference.com/w/cpp/language/constexpr
|
||||
#define constexpr const
|
||||
|
||||
#else
|
||||
|
||||
// things that we can turn on if we're in a C++11 environment
|
||||
#endif
|
||||
|
||||
#include "fl/register.h"
|
||||
|
||||
#endif
|
||||
79
FastLED/src/crgb.cpp
Normal file
79
FastLED/src/crgb.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/// @file crgb.cpp
|
||||
/// Utility functions for the red, green, and blue (RGB) pixel struct
|
||||
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
#include "crgb.h"
|
||||
#include "lib8tion/math8.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
fl::Str CRGB::toString() const {
|
||||
fl::Str out;
|
||||
out.append("CRGB(");
|
||||
out.append(int16_t(r));
|
||||
out.append(",");
|
||||
out.append(int16_t(g));
|
||||
out.append(",");
|
||||
out.append(int16_t(b));
|
||||
out.append(")");
|
||||
return out;
|
||||
}
|
||||
|
||||
CRGB CRGB::computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
|
||||
#if defined(NO_CORRECTION) && (NO_CORRECTION==1)
|
||||
return CRGB(scale,scale,scale);
|
||||
#else
|
||||
CRGB adj(0,0,0);
|
||||
if(scale > 0) {
|
||||
for(uint8_t i = 0; i < 3; ++i) {
|
||||
uint8_t cc = colorCorrection.raw[i];
|
||||
uint8_t ct = colorTemperature.raw[i];
|
||||
if(cc > 0 && ct > 0) {
|
||||
// Optimized for AVR size. This function is only called very infrequently so size
|
||||
// matters more than speed.
|
||||
uint32_t work = (((uint16_t)cc)+1);
|
||||
work *= (((uint16_t)ct)+1);
|
||||
work *= scale;
|
||||
work /= 0x10000L;
|
||||
adj.raw[i] = work & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
return adj;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CRGB CRGB::blend(const CRGB& p1, const CRGB& p2, fract8 amountOfP2) {
|
||||
return CRGB(
|
||||
blend8(p1.r, p2.r, amountOfP2),
|
||||
blend8(p1.g, p2.g, amountOfP2),
|
||||
blend8(p1.b, p2.b, amountOfP2)
|
||||
);
|
||||
}
|
||||
|
||||
CRGB CRGB::blendAlphaMaxChannel(const CRGB& upper, const CRGB& lower) {
|
||||
// Use luma of upper pixel as alpha (0..255)
|
||||
uint8_t max_component = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (upper.raw[i] > max_component) {
|
||||
max_component = upper.raw[i];
|
||||
}
|
||||
}
|
||||
// uint8_t alpha = upper.getLuma();
|
||||
// blend(lower, upper, alpha) → (lower * (255−alpha) + upper * alpha) / 256
|
||||
uint8_t amountOf2 = 255 - max_component;
|
||||
return CRGB::blend(upper, lower, amountOf2);
|
||||
}
|
||||
|
||||
CRGB& CRGB::nscale8 (uint8_t scaledown )
|
||||
{
|
||||
nscale8x3( r, g, b, scaledown);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
755
FastLED/src/crgb.h
Normal file
755
FastLED/src/crgb.h
Normal file
@@ -0,0 +1,755 @@
|
||||
/// @file crgb.h
|
||||
/// Defines the red, green, and blue (RGB) pixel struct
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "chsv.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "color.h"
|
||||
#include "lib8tion/types.h"
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/template_magic.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
class Str;
|
||||
}
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Whether to allow HD_COLOR_MIXING
|
||||
#ifndef FASTLED_HD_COLOR_MIXING
|
||||
#ifdef __AVR__
|
||||
// Saves some memory on these constrained devices.
|
||||
#define FASTLED_HD_COLOR_MIXING 0
|
||||
#else
|
||||
#define FASTLED_HD_COLOR_MIXING 1
|
||||
#endif // __AVR__
|
||||
#endif // FASTLED_HD_COLOR_MIXING
|
||||
|
||||
|
||||
struct CRGB;
|
||||
|
||||
/// @defgroup PixelTypes Pixel Data Types (CRGB/CHSV)
|
||||
/// @brief Structs that hold pixel color data
|
||||
/// @{
|
||||
|
||||
/// Forward declaration of hsv2rgb_rainbow here,
|
||||
/// to avoid circular dependencies.
|
||||
///
|
||||
/// Convert an HSV value to RGB using a visually balanced rainbow.
|
||||
/// This "rainbow" yields better yellow and orange than a straight
|
||||
/// mathematical "spectrum".
|
||||
///
|
||||
/// 
|
||||
///
|
||||
/// @param hsv CHSV struct to convert to RGB. Max hue supported is HUE_MAX_RAINBOW
|
||||
/// @param rgb CRGB struct to store the result of the conversion (will be modified)
|
||||
void hsv2rgb_rainbow( const struct CHSV& hsv, struct CRGB& rgb);
|
||||
|
||||
|
||||
/// Representation of an RGB pixel (Red, Green, Blue)
|
||||
struct CRGB {
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
uint8_t r; ///< Red channel value
|
||||
uint8_t red; ///< @copydoc r
|
||||
};
|
||||
union {
|
||||
uint8_t g; ///< Green channel value
|
||||
uint8_t green; ///< @copydoc g
|
||||
};
|
||||
union {
|
||||
uint8_t b; ///< Blue channel value
|
||||
uint8_t blue; ///< @copydoc b
|
||||
};
|
||||
};
|
||||
/// Access the red, green, and blue data as an array.
|
||||
/// Where:
|
||||
/// * `raw[0]` is the red value
|
||||
/// * `raw[1]` is the green value
|
||||
/// * `raw[2]` is the blue value
|
||||
uint8_t raw[3];
|
||||
};
|
||||
|
||||
static CRGB blend(const CRGB& p1, const CRGB& p2, fract8 amountOfP2);
|
||||
static CRGB blendAlphaMaxChannel(const CRGB& upper, const CRGB& lower);
|
||||
|
||||
/// Array access operator to index into the CRGB object
|
||||
/// @param x the index to retrieve (0-2)
|
||||
/// @returns the CRGB::raw value for the given index
|
||||
FASTLED_FORCE_INLINE uint8_t& operator[] (uint8_t x)
|
||||
{
|
||||
return raw[x];
|
||||
}
|
||||
|
||||
/// Array access operator to index into the CRGB object
|
||||
/// @param x the index to retrieve (0-2)
|
||||
/// @returns the CRGB::raw value for the given index
|
||||
FASTLED_FORCE_INLINE const uint8_t& operator[] (uint8_t x) const
|
||||
{
|
||||
return raw[x];
|
||||
}
|
||||
|
||||
/// Default constructor
|
||||
/// @warning Default values are UNITIALIZED!
|
||||
FASTLED_FORCE_INLINE CRGB() = default;
|
||||
|
||||
/// Allow construction from red, green, and blue
|
||||
/// @param ir input red value
|
||||
/// @param ig input green value
|
||||
/// @param ib input blue value
|
||||
constexpr CRGB(uint8_t ir, uint8_t ig, uint8_t ib) noexcept
|
||||
: r(ir), g(ig), b(ib)
|
||||
{
|
||||
}
|
||||
|
||||
/// Allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code
|
||||
/// @param colorcode a packed 24 bit color code
|
||||
constexpr CRGB(uint32_t colorcode) noexcept
|
||||
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uint32_t as_uint32_t() const noexcept {
|
||||
return uint32_t(0xff000000) |
|
||||
(uint32_t{r} << 16) |
|
||||
(uint32_t{g} << 8) |
|
||||
uint32_t{b};
|
||||
}
|
||||
|
||||
/// Allow construction from a LEDColorCorrection enum
|
||||
/// @param colorcode an LEDColorCorrect enumeration value
|
||||
constexpr CRGB(LEDColorCorrection colorcode) noexcept
|
||||
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
|
||||
{
|
||||
}
|
||||
|
||||
/// Allow construction from a ColorTemperature enum
|
||||
/// @param colorcode an ColorTemperature enumeration value
|
||||
constexpr CRGB(ColorTemperature colorcode) noexcept
|
||||
: r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF)
|
||||
{
|
||||
}
|
||||
|
||||
/// Allow copy construction
|
||||
FASTLED_FORCE_INLINE CRGB(const CRGB& rhs) = default;
|
||||
|
||||
/// Allow construction from a CHSV color
|
||||
FASTLED_FORCE_INLINE CRGB(const CHSV& rhs)
|
||||
{
|
||||
hsv2rgb_rainbow( rhs, *this);
|
||||
}
|
||||
|
||||
/// Allow assignment from one RGB struct to another
|
||||
FASTLED_FORCE_INLINE CRGB& operator= (const CRGB& rhs) = default;
|
||||
|
||||
/// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
|
||||
/// @param colorcode a packed 24 bit color code
|
||||
FASTLED_FORCE_INLINE CRGB& operator= (const uint32_t colorcode)
|
||||
{
|
||||
r = (colorcode >> 16) & 0xFF;
|
||||
g = (colorcode >> 8) & 0xFF;
|
||||
b = (colorcode >> 0) & 0xFF;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow assignment from red, green, and blue
|
||||
/// @param nr new red value
|
||||
/// @param ng new green value
|
||||
/// @param nb new blue value
|
||||
FASTLED_FORCE_INLINE CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb)
|
||||
{
|
||||
r = nr;
|
||||
g = ng;
|
||||
b = nb;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow assignment from hue, saturation, and value
|
||||
/// @param hue color hue
|
||||
/// @param sat color saturation
|
||||
/// @param val color value (brightness)
|
||||
FASTLED_FORCE_INLINE CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val)
|
||||
{
|
||||
hsv2rgb_rainbow( CHSV(hue, sat, val), *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow assignment from just a hue.
|
||||
/// Saturation and value (brightness) are set automatically to max.
|
||||
/// @param hue color hue
|
||||
FASTLED_FORCE_INLINE CRGB& setHue (uint8_t hue)
|
||||
{
|
||||
hsv2rgb_rainbow( CHSV(hue, 255, 255), *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow assignment from HSV color
|
||||
FASTLED_FORCE_INLINE CRGB& operator= (const CHSV& rhs)
|
||||
{
|
||||
hsv2rgb_rainbow( rhs, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code
|
||||
/// @param colorcode a packed 24 bit color code
|
||||
FASTLED_FORCE_INLINE CRGB& setColorCode (uint32_t colorcode)
|
||||
{
|
||||
r = (colorcode >> 16) & 0xFF;
|
||||
g = (colorcode >> 8) & 0xFF;
|
||||
b = (colorcode >> 0) & 0xFF;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Add one CRGB to another, saturating at 0xFF for each channel
|
||||
FASTLED_FORCE_INLINE CRGB& operator+= (const CRGB& rhs);
|
||||
|
||||
/// Add a constant to each channel, saturating at 0xFF.
|
||||
/// @note This is NOT an operator+= overload because the compiler
|
||||
/// can't usefully decide when it's being passed a 32-bit
|
||||
/// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
|
||||
FASTLED_FORCE_INLINE CRGB& addToRGB (uint8_t d);
|
||||
|
||||
/// Subtract one CRGB from another, saturating at 0x00 for each channel
|
||||
FASTLED_FORCE_INLINE CRGB& operator-= (const CRGB& rhs);
|
||||
|
||||
/// Subtract a constant from each channel, saturating at 0x00.
|
||||
/// @note This is NOT an operator+= overload because the compiler
|
||||
/// can't usefully decide when it's being passed a 32-bit
|
||||
/// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue)
|
||||
FASTLED_FORCE_INLINE CRGB& subtractFromRGB(uint8_t d);
|
||||
|
||||
/// Subtract a constant of '1' from each channel, saturating at 0x00
|
||||
FASTLED_FORCE_INLINE CRGB& operator-- ();
|
||||
|
||||
/// @copydoc operator--
|
||||
FASTLED_FORCE_INLINE CRGB operator-- (int );
|
||||
|
||||
/// Add a constant of '1' from each channel, saturating at 0xFF
|
||||
FASTLED_FORCE_INLINE CRGB& operator++ ();
|
||||
|
||||
/// @copydoc operator++
|
||||
FASTLED_FORCE_INLINE CRGB operator++ (int );
|
||||
|
||||
/// Divide each of the channels by a constant
|
||||
FASTLED_FORCE_INLINE CRGB& operator/= (uint8_t d )
|
||||
{
|
||||
r /= d;
|
||||
g /= d;
|
||||
b /= d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Right shift each of the channels by a constant
|
||||
FASTLED_FORCE_INLINE CRGB& operator>>= (uint8_t d)
|
||||
{
|
||||
r >>= d;
|
||||
g >>= d;
|
||||
b >>= d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply each of the channels by a constant,
|
||||
/// saturating each channel at 0xFF.
|
||||
FASTLED_FORCE_INLINE CRGB& operator*= (uint8_t d);
|
||||
|
||||
/// Scale down a RGB to N/256ths of it's current brightness using
|
||||
/// "video" dimming rules. "Video" dimming rules means that unless the scale factor
|
||||
/// is ZERO each channel is guaranteed NOT to dim down to zero. If it's already
|
||||
/// nonzero, it'll stay nonzero, even if that means the hue shifts a little
|
||||
/// at low brightness levels.
|
||||
/// @see nscale8x3_video
|
||||
FASTLED_FORCE_INLINE CRGB& nscale8_video (uint8_t scaledown);
|
||||
|
||||
/// %= is a synonym for nscale8_video(). Think of it is scaling down
|
||||
/// by "a percentage"
|
||||
FASTLED_FORCE_INLINE CRGB& operator%= (uint8_t scaledown);
|
||||
|
||||
/// fadeLightBy is a synonym for nscale8_video(), as a fade instead of a scale
|
||||
/// @param fadefactor the amount to fade, sent to nscale8_video() as (255 - fadefactor)
|
||||
FASTLED_FORCE_INLINE CRGB& fadeLightBy (uint8_t fadefactor );
|
||||
|
||||
/// Scale down a RGB to N/256ths of its current brightness, using
|
||||
/// "plain math" dimming rules. "Plain math" dimming rules means that the low light
|
||||
/// levels may dim all the way to 100% black.
|
||||
/// @see nscale8x3
|
||||
CRGB& nscale8 (uint8_t scaledown );
|
||||
|
||||
/// Scale down a RGB to N/256ths of its current brightness, using
|
||||
/// "plain math" dimming rules. "Plain math" dimming rules means that the low light
|
||||
/// levels may dim all the way to 100% black.
|
||||
/// @see ::scale8
|
||||
FASTLED_FORCE_INLINE CRGB& nscale8 (const CRGB & scaledown );
|
||||
|
||||
constexpr CRGB nscale8_constexpr (const CRGB scaledown ) const;
|
||||
|
||||
|
||||
/// Return a CRGB object that is a scaled down version of this object
|
||||
FASTLED_FORCE_INLINE CRGB scale8 (uint8_t scaledown ) const;
|
||||
|
||||
/// Return a CRGB object that is a scaled down version of this object
|
||||
FASTLED_FORCE_INLINE CRGB scale8 (const CRGB & scaledown ) const;
|
||||
|
||||
/// fadeToBlackBy is a synonym for nscale8(), as a fade instead of a scale
|
||||
/// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor)
|
||||
FASTLED_FORCE_INLINE CRGB& fadeToBlackBy (uint8_t fadefactor );
|
||||
|
||||
/// "or" operator brings each channel up to the higher of the two values
|
||||
FASTLED_FORCE_INLINE CRGB& operator|= (const CRGB& rhs )
|
||||
{
|
||||
if( rhs.r > r) r = rhs.r;
|
||||
if( rhs.g > g) g = rhs.g;
|
||||
if( rhs.b > b) b = rhs.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @copydoc operator|=
|
||||
FASTLED_FORCE_INLINE CRGB& operator|= (uint8_t d )
|
||||
{
|
||||
if( d > r) r = d;
|
||||
if( d > g) g = d;
|
||||
if( d > b) b = d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// "and" operator brings each channel down to the lower of the two values
|
||||
FASTLED_FORCE_INLINE CRGB& operator&= (const CRGB& rhs )
|
||||
{
|
||||
if( rhs.r < r) r = rhs.r;
|
||||
if( rhs.g < g) g = rhs.g;
|
||||
if( rhs.b < b) b = rhs.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @copydoc operator&=
|
||||
FASTLED_FORCE_INLINE CRGB& operator&= (uint8_t d )
|
||||
{
|
||||
if( d < r) r = d;
|
||||
if( d < g) g = d;
|
||||
if( d < b) b = d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// This allows testing a CRGB for zero-ness
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return r || g || b;
|
||||
}
|
||||
|
||||
/// Converts a CRGB to a 32-bit color having an alpha of 255.
|
||||
constexpr explicit operator uint32_t() const
|
||||
{
|
||||
return uint32_t(0xff000000) |
|
||||
(uint32_t{r} << 16) |
|
||||
(uint32_t{g} << 8) |
|
||||
uint32_t{b};
|
||||
}
|
||||
|
||||
/// Invert each channel
|
||||
constexpr CRGB operator-() const
|
||||
{
|
||||
return CRGB(255 - r, 255 - g, 255 - b);
|
||||
}
|
||||
|
||||
#if (defined SmartMatrix_h || defined SmartMatrix3_h)
|
||||
/// Convert to an rgb24 object, used with the SmartMatrix library
|
||||
/// @see https://github.com/pixelmatix/SmartMatrix
|
||||
operator rgb24() const {
|
||||
rgb24 ret;
|
||||
ret.red = r;
|
||||
ret.green = g;
|
||||
ret.blue = b;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
fl::Str toString() const;
|
||||
|
||||
/// Get the "luma" of a CRGB object. In other words, roughly how much
|
||||
/// light the CRGB pixel is putting out (from 0 to 255).
|
||||
uint8_t getLuma() const;
|
||||
|
||||
|
||||
|
||||
/// Get the average of the R, G, and B values
|
||||
FASTLED_FORCE_INLINE uint8_t getAverageLight() const;
|
||||
|
||||
/// Maximize the brightness of this CRGB object.
|
||||
/// This makes the individual color channels as bright as possible
|
||||
/// while keeping the same value differences between channels.
|
||||
/// @note This does not keep the same ratios between channels,
|
||||
/// just the same difference in absolute values.
|
||||
FASTLED_FORCE_INLINE void maximizeBrightness( uint8_t limit = 255 ) {
|
||||
uint8_t max = red;
|
||||
if( green > max) max = green;
|
||||
if( blue > max) max = blue;
|
||||
|
||||
// stop div/0 when color is black
|
||||
if(max > 0) {
|
||||
uint16_t factor = ((uint16_t)(limit) * 256) / max;
|
||||
red = (red * factor) / 256;
|
||||
green = (green * factor) / 256;
|
||||
blue = (blue * factor) / 256;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the combined color adjustment to the LEDs at a given scale, color correction, and color temperature
|
||||
/// @param scale the scale value for the RGB data (i.e. brightness)
|
||||
/// @param colorCorrection color correction to apply
|
||||
/// @param colorTemperature color temperature to apply
|
||||
/// @returns a CRGB object representing the adjustment, including color correction and color temperature
|
||||
static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature);
|
||||
|
||||
/// Return a new CRGB object after performing a linear interpolation between this object and the passed in object
|
||||
FASTLED_FORCE_INLINE CRGB lerp8( const CRGB& other, fract8 frac) const;
|
||||
|
||||
/// @copydoc lerp8
|
||||
FASTLED_FORCE_INLINE CRGB lerp16( const CRGB& other, fract16 frac) const;
|
||||
/// Returns 0 or 1, depending on the lowest bit of the sum of the color components.
|
||||
FASTLED_FORCE_INLINE uint8_t getParity()
|
||||
{
|
||||
uint8_t sum = r + g + b;
|
||||
return (sum & 0x01);
|
||||
}
|
||||
|
||||
/// Adjusts the color in the smallest way possible
|
||||
/// so that the parity of the coloris now the desired value.
|
||||
/// This allows you to "hide" one bit of information in the color.
|
||||
///
|
||||
/// Ideally, we find one color channel which already
|
||||
/// has data in it, and modify just that channel by one.
|
||||
/// We don't want to light up a channel that's black
|
||||
/// if we can avoid it, and if the pixel is 'grayscale',
|
||||
/// (meaning that R==G==B), we modify all three channels
|
||||
/// at once, to preserve the neutral hue.
|
||||
///
|
||||
/// There's no such thing as a free lunch; in many cases
|
||||
/// this "hidden bit" may actually be visible, but this
|
||||
/// code makes reasonable efforts to hide it as much
|
||||
/// as is reasonably possible.
|
||||
///
|
||||
/// Also, an effort is made to make it such that
|
||||
/// repeatedly setting the parity to different values
|
||||
/// will not cause the color to "drift". Toggling
|
||||
/// the parity twice should generally result in the
|
||||
/// original color again.
|
||||
///
|
||||
FASTLED_FORCE_INLINE void setParity( uint8_t parity)
|
||||
{
|
||||
uint8_t curparity = getParity();
|
||||
|
||||
if( parity == curparity) return;
|
||||
|
||||
if( parity ) {
|
||||
// going 'up'
|
||||
if( (b > 0) && (b < 255)) {
|
||||
if( r == g && g == b) {
|
||||
++r;
|
||||
++g;
|
||||
}
|
||||
++b;
|
||||
} else if( (r > 0) && (r < 255)) {
|
||||
++r;
|
||||
} else if( (g > 0) && (g < 255)) {
|
||||
++g;
|
||||
} else {
|
||||
if( r == g && g == b) {
|
||||
r ^= 0x01;
|
||||
g ^= 0x01;
|
||||
}
|
||||
b ^= 0x01;
|
||||
}
|
||||
} else {
|
||||
// going 'down'
|
||||
if( b > 1) {
|
||||
if( r == g && g == b) {
|
||||
--r;
|
||||
--g;
|
||||
}
|
||||
--b;
|
||||
} else if( g > 1) {
|
||||
--g;
|
||||
} else if( r > 1) {
|
||||
--r;
|
||||
} else {
|
||||
if( r == g && g == b) {
|
||||
r ^= 0x01;
|
||||
g ^= 0x01;
|
||||
}
|
||||
b ^= 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Predefined RGB colors
|
||||
typedef enum {
|
||||
AliceBlue=0xF0F8FF, ///< @htmlcolorblock{F0F8FF}
|
||||
Amethyst=0x9966CC, ///< @htmlcolorblock{9966CC}
|
||||
AntiqueWhite=0xFAEBD7, ///< @htmlcolorblock{FAEBD7}
|
||||
Aqua=0x00FFFF, ///< @htmlcolorblock{00FFFF}
|
||||
Aquamarine=0x7FFFD4, ///< @htmlcolorblock{7FFFD4}
|
||||
Azure=0xF0FFFF, ///< @htmlcolorblock{F0FFFF}
|
||||
Beige=0xF5F5DC, ///< @htmlcolorblock{F5F5DC}
|
||||
Bisque=0xFFE4C4, ///< @htmlcolorblock{FFE4C4}
|
||||
Black=0x000000, ///< @htmlcolorblock{000000}
|
||||
BlanchedAlmond=0xFFEBCD, ///< @htmlcolorblock{FFEBCD}
|
||||
Blue=0x0000FF, ///< @htmlcolorblock{0000FF}
|
||||
BlueViolet=0x8A2BE2, ///< @htmlcolorblock{8A2BE2}
|
||||
Brown=0xA52A2A, ///< @htmlcolorblock{A52A2A}
|
||||
BurlyWood=0xDEB887, ///< @htmlcolorblock{DEB887}
|
||||
CadetBlue=0x5F9EA0, ///< @htmlcolorblock{5F9EA0}
|
||||
Chartreuse=0x7FFF00, ///< @htmlcolorblock{7FFF00}
|
||||
Chocolate=0xD2691E, ///< @htmlcolorblock{D2691E}
|
||||
Coral=0xFF7F50, ///< @htmlcolorblock{FF7F50}
|
||||
CornflowerBlue=0x6495ED, ///< @htmlcolorblock{6495ED}
|
||||
Cornsilk=0xFFF8DC, ///< @htmlcolorblock{FFF8DC}
|
||||
Crimson=0xDC143C, ///< @htmlcolorblock{DC143C}
|
||||
Cyan=0x00FFFF, ///< @htmlcolorblock{00FFFF}
|
||||
DarkBlue=0x00008B, ///< @htmlcolorblock{00008B}
|
||||
DarkCyan=0x008B8B, ///< @htmlcolorblock{008B8B}
|
||||
DarkGoldenrod=0xB8860B, ///< @htmlcolorblock{B8860B}
|
||||
DarkGray=0xA9A9A9, ///< @htmlcolorblock{A9A9A9}
|
||||
DarkGrey=0xA9A9A9, ///< @htmlcolorblock{A9A9A9}
|
||||
DarkGreen=0x006400, ///< @htmlcolorblock{006400}
|
||||
DarkKhaki=0xBDB76B, ///< @htmlcolorblock{BDB76B}
|
||||
DarkMagenta=0x8B008B, ///< @htmlcolorblock{8B008B}
|
||||
DarkOliveGreen=0x556B2F, ///< @htmlcolorblock{556B2F}
|
||||
DarkOrange=0xFF8C00, ///< @htmlcolorblock{FF8C00}
|
||||
DarkOrchid=0x9932CC, ///< @htmlcolorblock{9932CC}
|
||||
DarkRed=0x8B0000, ///< @htmlcolorblock{8B0000}
|
||||
DarkSalmon=0xE9967A, ///< @htmlcolorblock{E9967A}
|
||||
DarkSeaGreen=0x8FBC8F, ///< @htmlcolorblock{8FBC8F}
|
||||
DarkSlateBlue=0x483D8B, ///< @htmlcolorblock{483D8B}
|
||||
DarkSlateGray=0x2F4F4F, ///< @htmlcolorblock{2F4F4F}
|
||||
DarkSlateGrey=0x2F4F4F, ///< @htmlcolorblock{2F4F4F}
|
||||
DarkTurquoise=0x00CED1, ///< @htmlcolorblock{00CED1}
|
||||
DarkViolet=0x9400D3, ///< @htmlcolorblock{9400D3}
|
||||
DeepPink=0xFF1493, ///< @htmlcolorblock{FF1493}
|
||||
DeepSkyBlue=0x00BFFF, ///< @htmlcolorblock{00BFFF}
|
||||
DimGray=0x696969, ///< @htmlcolorblock{696969}
|
||||
DimGrey=0x696969, ///< @htmlcolorblock{696969}
|
||||
DodgerBlue=0x1E90FF, ///< @htmlcolorblock{1E90FF}
|
||||
FireBrick=0xB22222, ///< @htmlcolorblock{B22222}
|
||||
FloralWhite=0xFFFAF0, ///< @htmlcolorblock{FFFAF0}
|
||||
ForestGreen=0x228B22, ///< @htmlcolorblock{228B22}
|
||||
Fuchsia=0xFF00FF, ///< @htmlcolorblock{FF00FF}
|
||||
Gainsboro=0xDCDCDC, ///< @htmlcolorblock{DCDCDC}
|
||||
GhostWhite=0xF8F8FF, ///< @htmlcolorblock{F8F8FF}
|
||||
Gold=0xFFD700, ///< @htmlcolorblock{FFD700}
|
||||
Goldenrod=0xDAA520, ///< @htmlcolorblock{DAA520}
|
||||
Gray=0x808080, ///< @htmlcolorblock{808080}
|
||||
Grey=0x808080, ///< @htmlcolorblock{808080}
|
||||
Green=0x008000, ///< @htmlcolorblock{008000}
|
||||
GreenYellow=0xADFF2F, ///< @htmlcolorblock{ADFF2F}
|
||||
Honeydew=0xF0FFF0, ///< @htmlcolorblock{F0FFF0}
|
||||
HotPink=0xFF69B4, ///< @htmlcolorblock{FF69B4}
|
||||
IndianRed=0xCD5C5C, ///< @htmlcolorblock{CD5C5C}
|
||||
Indigo=0x4B0082, ///< @htmlcolorblock{4B0082}
|
||||
Ivory=0xFFFFF0, ///< @htmlcolorblock{FFFFF0}
|
||||
Khaki=0xF0E68C, ///< @htmlcolorblock{F0E68C}
|
||||
Lavender=0xE6E6FA, ///< @htmlcolorblock{E6E6FA}
|
||||
LavenderBlush=0xFFF0F5, ///< @htmlcolorblock{FFF0F5}
|
||||
LawnGreen=0x7CFC00, ///< @htmlcolorblock{7CFC00}
|
||||
LemonChiffon=0xFFFACD, ///< @htmlcolorblock{FFFACD}
|
||||
LightBlue=0xADD8E6, ///< @htmlcolorblock{ADD8E6}
|
||||
LightCoral=0xF08080, ///< @htmlcolorblock{F08080}
|
||||
LightCyan=0xE0FFFF, ///< @htmlcolorblock{E0FFFF}
|
||||
LightGoldenrodYellow=0xFAFAD2, ///< @htmlcolorblock{FAFAD2}
|
||||
LightGreen=0x90EE90, ///< @htmlcolorblock{90EE90}
|
||||
LightGrey=0xD3D3D3, ///< @htmlcolorblock{D3D3D3}
|
||||
LightPink=0xFFB6C1, ///< @htmlcolorblock{FFB6C1}
|
||||
LightSalmon=0xFFA07A, ///< @htmlcolorblock{FFA07A}
|
||||
LightSeaGreen=0x20B2AA, ///< @htmlcolorblock{20B2AA}
|
||||
LightSkyBlue=0x87CEFA, ///< @htmlcolorblock{87CEFA}
|
||||
LightSlateGray=0x778899, ///< @htmlcolorblock{778899}
|
||||
LightSlateGrey=0x778899, ///< @htmlcolorblock{778899}
|
||||
LightSteelBlue=0xB0C4DE, ///< @htmlcolorblock{B0C4DE}
|
||||
LightYellow=0xFFFFE0, ///< @htmlcolorblock{FFFFE0}
|
||||
Lime=0x00FF00, ///< @htmlcolorblock{00FF00}
|
||||
LimeGreen=0x32CD32, ///< @htmlcolorblock{32CD32}
|
||||
Linen=0xFAF0E6, ///< @htmlcolorblock{FAF0E6}
|
||||
Magenta=0xFF00FF, ///< @htmlcolorblock{FF00FF}
|
||||
Maroon=0x800000, ///< @htmlcolorblock{800000}
|
||||
MediumAquamarine=0x66CDAA, ///< @htmlcolorblock{66CDAA}
|
||||
MediumBlue=0x0000CD, ///< @htmlcolorblock{0000CD}
|
||||
MediumOrchid=0xBA55D3, ///< @htmlcolorblock{BA55D3}
|
||||
MediumPurple=0x9370DB, ///< @htmlcolorblock{9370DB}
|
||||
MediumSeaGreen=0x3CB371, ///< @htmlcolorblock{3CB371}
|
||||
MediumSlateBlue=0x7B68EE, ///< @htmlcolorblock{7B68EE}
|
||||
MediumSpringGreen=0x00FA9A, ///< @htmlcolorblock{00FA9A}
|
||||
MediumTurquoise=0x48D1CC, ///< @htmlcolorblock{48D1CC}
|
||||
MediumVioletRed=0xC71585, ///< @htmlcolorblock{C71585}
|
||||
MidnightBlue=0x191970, ///< @htmlcolorblock{191970}
|
||||
MintCream=0xF5FFFA, ///< @htmlcolorblock{F5FFFA}
|
||||
MistyRose=0xFFE4E1, ///< @htmlcolorblock{FFE4E1}
|
||||
Moccasin=0xFFE4B5, ///< @htmlcolorblock{FFE4B5}
|
||||
NavajoWhite=0xFFDEAD, ///< @htmlcolorblock{FFDEAD}
|
||||
Navy=0x000080, ///< @htmlcolorblock{000080}
|
||||
OldLace=0xFDF5E6, ///< @htmlcolorblock{FDF5E6}
|
||||
Olive=0x808000, ///< @htmlcolorblock{808000}
|
||||
OliveDrab=0x6B8E23, ///< @htmlcolorblock{6B8E23}
|
||||
Orange=0xFFA500, ///< @htmlcolorblock{FFA500}
|
||||
OrangeRed=0xFF4500, ///< @htmlcolorblock{FF4500}
|
||||
Orchid=0xDA70D6, ///< @htmlcolorblock{DA70D6}
|
||||
PaleGoldenrod=0xEEE8AA, ///< @htmlcolorblock{EEE8AA}
|
||||
PaleGreen=0x98FB98, ///< @htmlcolorblock{98FB98}
|
||||
PaleTurquoise=0xAFEEEE, ///< @htmlcolorblock{AFEEEE}
|
||||
PaleVioletRed=0xDB7093, ///< @htmlcolorblock{DB7093}
|
||||
PapayaWhip=0xFFEFD5, ///< @htmlcolorblock{FFEFD5}
|
||||
PeachPuff=0xFFDAB9, ///< @htmlcolorblock{FFDAB9}
|
||||
Peru=0xCD853F, ///< @htmlcolorblock{CD853F}
|
||||
Pink=0xFFC0CB, ///< @htmlcolorblock{FFC0CB}
|
||||
Plaid=0xCC5533, ///< @htmlcolorblock{CC5533}
|
||||
Plum=0xDDA0DD, ///< @htmlcolorblock{DDA0DD}
|
||||
PowderBlue=0xB0E0E6, ///< @htmlcolorblock{B0E0E6}
|
||||
Purple=0x800080, ///< @htmlcolorblock{800080}
|
||||
Red=0xFF0000, ///< @htmlcolorblock{FF0000}
|
||||
RosyBrown=0xBC8F8F, ///< @htmlcolorblock{BC8F8F}
|
||||
RoyalBlue=0x4169E1, ///< @htmlcolorblock{4169E1}
|
||||
SaddleBrown=0x8B4513, ///< @htmlcolorblock{8B4513}
|
||||
Salmon=0xFA8072, ///< @htmlcolorblock{FA8072}
|
||||
SandyBrown=0xF4A460, ///< @htmlcolorblock{F4A460}
|
||||
SeaGreen=0x2E8B57, ///< @htmlcolorblock{2E8B57}
|
||||
Seashell=0xFFF5EE, ///< @htmlcolorblock{FFF5EE}
|
||||
Sienna=0xA0522D, ///< @htmlcolorblock{A0522D}
|
||||
Silver=0xC0C0C0, ///< @htmlcolorblock{C0C0C0}
|
||||
SkyBlue=0x87CEEB, ///< @htmlcolorblock{87CEEB}
|
||||
SlateBlue=0x6A5ACD, ///< @htmlcolorblock{6A5ACD}
|
||||
SlateGray=0x708090, ///< @htmlcolorblock{708090}
|
||||
SlateGrey=0x708090, ///< @htmlcolorblock{708090}
|
||||
Snow=0xFFFAFA, ///< @htmlcolorblock{FFFAFA}
|
||||
SpringGreen=0x00FF7F, ///< @htmlcolorblock{00FF7F}
|
||||
SteelBlue=0x4682B4, ///< @htmlcolorblock{4682B4}
|
||||
Tan=0xD2B48C, ///< @htmlcolorblock{D2B48C}
|
||||
Teal=0x008080, ///< @htmlcolorblock{008080}
|
||||
Thistle=0xD8BFD8, ///< @htmlcolorblock{D8BFD8}
|
||||
Tomato=0xFF6347, ///< @htmlcolorblock{FF6347}
|
||||
Turquoise=0x40E0D0, ///< @htmlcolorblock{40E0D0}
|
||||
Violet=0xEE82EE, ///< @htmlcolorblock{EE82EE}
|
||||
Wheat=0xF5DEB3, ///< @htmlcolorblock{F5DEB3}
|
||||
White=0xFFFFFF, ///< @htmlcolorblock{FFFFFF}
|
||||
WhiteSmoke=0xF5F5F5, ///< @htmlcolorblock{F5F5F5}
|
||||
Yellow=0xFFFF00, ///< @htmlcolorblock{FFFF00}
|
||||
YellowGreen=0x9ACD32, ///< @htmlcolorblock{9ACD32}
|
||||
|
||||
// LED RGB color that roughly approximates
|
||||
// the color of incandescent fairy lights,
|
||||
// assuming that you're using FastLED
|
||||
// color correction on your LEDs (recommended).
|
||||
FairyLight=0xFFE42D, ///< @htmlcolorblock{FFE42D}
|
||||
|
||||
// If you are using no color correction, use this
|
||||
FairyLightNCC=0xFF9D2A ///< @htmlcolorblock{FFE42D}
|
||||
|
||||
} HTMLColorCode;
|
||||
};
|
||||
|
||||
|
||||
/// Check if two CRGB objects have the same color data
|
||||
FASTLED_FORCE_INLINE bool operator== (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b);
|
||||
}
|
||||
|
||||
/// Check if two CRGB objects do *not* have the same color data
|
||||
FASTLED_FORCE_INLINE bool operator!= (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// Check if two CHSV objects have the same color data
|
||||
FASTLED_FORCE_INLINE bool operator== (const CHSV& lhs, const CHSV& rhs)
|
||||
{
|
||||
return (lhs.h == rhs.h) && (lhs.s == rhs.s) && (lhs.v == rhs.v);
|
||||
}
|
||||
|
||||
/// Check if two CHSV objects do *not* have the same color data
|
||||
FASTLED_FORCE_INLINE bool operator!= (const CHSV& lhs, const CHSV& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// Check if the sum of the color channels in one CRGB object is less than another
|
||||
FASTLED_FORCE_INLINE bool operator< (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
uint16_t sl, sr;
|
||||
sl = lhs.r + lhs.g + lhs.b;
|
||||
sr = rhs.r + rhs.g + rhs.b;
|
||||
return sl < sr;
|
||||
}
|
||||
|
||||
/// Check if the sum of the color channels in one CRGB object is greater than another
|
||||
FASTLED_FORCE_INLINE bool operator> (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
uint16_t sl, sr;
|
||||
sl = lhs.r + lhs.g + lhs.b;
|
||||
sr = rhs.r + rhs.g + rhs.b;
|
||||
return sl > sr;
|
||||
}
|
||||
|
||||
/// Check if the sum of the color channels in one CRGB object is greater than or equal to another
|
||||
FASTLED_FORCE_INLINE bool operator>= (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
uint16_t sl, sr;
|
||||
sl = lhs.r + lhs.g + lhs.b;
|
||||
sr = rhs.r + rhs.g + rhs.b;
|
||||
return sl >= sr;
|
||||
}
|
||||
|
||||
/// Check if the sum of the color channels in one CRGB object is less than or equal to another
|
||||
FASTLED_FORCE_INLINE bool operator<= (const CRGB& lhs, const CRGB& rhs)
|
||||
{
|
||||
uint16_t sl, sr;
|
||||
sl = lhs.r + lhs.g + lhs.b;
|
||||
sr = rhs.r + rhs.g + rhs.b;
|
||||
return sl <= sr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// @copydoc CRGB::operator/=
|
||||
FASTLED_FORCE_INLINE CRGB operator/( const CRGB& p1, uint8_t d)
|
||||
{
|
||||
return CRGB( p1.r/d, p1.g/d, p1.b/d);
|
||||
}
|
||||
|
||||
|
||||
/// Combine two CRGB objects, taking the smallest value of each channel
|
||||
FASTLED_FORCE_INLINE CRGB operator&( const CRGB& p1, const CRGB& p2)
|
||||
{
|
||||
return CRGB( p1.r < p2.r ? p1.r : p2.r,
|
||||
p1.g < p2.g ? p1.g : p2.g,
|
||||
p1.b < p2.b ? p1.b : p2.b);
|
||||
}
|
||||
|
||||
/// Combine two CRGB objects, taking the largest value of each channel
|
||||
FASTLED_FORCE_INLINE CRGB operator|( const CRGB& p1, const CRGB& p2)
|
||||
{
|
||||
return CRGB( p1.r > p2.r ? p1.r : p2.r,
|
||||
p1.g > p2.g ? p1.g : p2.g,
|
||||
p1.b > p2.b ? p1.b : p2.b);
|
||||
}
|
||||
|
||||
/// @copydoc CRGB::operator+=
|
||||
FASTLED_FORCE_INLINE CRGB operator+( const CRGB& p1, const CRGB& p2);
|
||||
|
||||
/// @copydoc CRGB::operator-=
|
||||
FASTLED_FORCE_INLINE CRGB operator-( const CRGB& p1, const CRGB& p2);
|
||||
|
||||
/// @copydoc CRGB::operator*=
|
||||
FASTLED_FORCE_INLINE CRGB operator*( const CRGB& p1, uint8_t d);
|
||||
|
||||
/// Scale using CRGB::nscale8_video()
|
||||
FASTLED_FORCE_INLINE CRGB operator%( const CRGB& p1, uint8_t d);
|
||||
|
||||
/// @} PixelTypes
|
||||
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
233
FastLED/src/crgb.hpp
Normal file
233
FastLED/src/crgb.hpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/// @file crgb.hpp
|
||||
/// Defines utility functions for the red, green, and blue (RGB) pixel struct
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "chsv.h"
|
||||
#include "crgb.h"
|
||||
#include "lib8tion.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/str.h"
|
||||
|
||||
#if FASTLED_IS_USING_NAMESPACE
|
||||
#define FUNCTION_SCALE8(a,b) FASTLED_NAMESPACE::scale8(a,b)
|
||||
#else
|
||||
#define FUNCTION_SCALE8(a,b) ::scale8(a,b)
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// Add one CRGB to another, saturating at 0xFF for each channel
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator+= (const CRGB& rhs )
|
||||
{
|
||||
r = qadd8( r, rhs.r);
|
||||
g = qadd8( g, rhs.g);
|
||||
b = qadd8( b, rhs.b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::addToRGB (uint8_t d )
|
||||
{
|
||||
r = qadd8( r, d);
|
||||
g = qadd8( g, d);
|
||||
b = qadd8( b, d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator-= (const CRGB& rhs )
|
||||
{
|
||||
r = qsub8( r, rhs.r);
|
||||
g = qsub8( g, rhs.g);
|
||||
b = qsub8( b, rhs.b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add a constant of '1' from each channel, saturating at 0xFF
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator++ ()
|
||||
{
|
||||
addToRGB(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @copydoc operator++
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::operator++ (int )
|
||||
{
|
||||
CRGB retval(*this);
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::subtractFromRGB(uint8_t d)
|
||||
{
|
||||
r = qsub8( r, d);
|
||||
g = qsub8( g, d);
|
||||
b = qsub8( b, d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator*= (uint8_t d )
|
||||
{
|
||||
r = qmul8( r, d);
|
||||
g = qmul8( g, d);
|
||||
b = qmul8( b, d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::nscale8_video(uint8_t scaledown )
|
||||
{
|
||||
nscale8x3_video( r, g, b, scaledown);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator%= (uint8_t scaledown )
|
||||
{
|
||||
nscale8x3_video( r, g, b, scaledown);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::fadeLightBy (uint8_t fadefactor )
|
||||
{
|
||||
nscale8x3_video( r, g, b, 255 - fadefactor);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Subtract a constant of '1' from each channel, saturating at 0x00
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::operator-- ()
|
||||
{
|
||||
subtractFromRGB(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @copydoc operator--
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::operator-- (int )
|
||||
{
|
||||
CRGB retval(*this);
|
||||
--(*this);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
constexpr CRGB CRGB::nscale8_constexpr(const CRGB scaledown) const
|
||||
{
|
||||
return CRGB(
|
||||
scale8_constexpr(r, scaledown.r),
|
||||
scale8_constexpr(g, scaledown.g),
|
||||
scale8_constexpr(b, scaledown.b)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB& CRGB::nscale8 (const CRGB & scaledown )
|
||||
{
|
||||
r = FUNCTION_SCALE8(r, scaledown.r);
|
||||
g = FUNCTION_SCALE8(g, scaledown.g);
|
||||
b = FUNCTION_SCALE8(b, scaledown.b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::scale8 (uint8_t scaledown ) const
|
||||
{
|
||||
CRGB out = *this;
|
||||
nscale8x3( out.r, out.g, out.b, scaledown);
|
||||
return out;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::scale8 (const CRGB & scaledown ) const
|
||||
{
|
||||
CRGB out;
|
||||
out.r = FUNCTION_SCALE8(r, scaledown.r);
|
||||
out.g = FUNCTION_SCALE8(g, scaledown.g);
|
||||
out.b = FUNCTION_SCALE8(b, scaledown.b);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline CRGB& CRGB::fadeToBlackBy (uint8_t fadefactor )
|
||||
{
|
||||
nscale8x3( r, g, b, 255 - fadefactor);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE uint8_t CRGB::getLuma( ) const {
|
||||
//Y' = 0.2126 R' + 0.7152 G' + 0.0722 B'
|
||||
// 54 183 18 (!)
|
||||
|
||||
uint8_t luma = scale8_LEAVING_R1_DIRTY( r, 54) + \
|
||||
scale8_LEAVING_R1_DIRTY( g, 183) + \
|
||||
scale8_LEAVING_R1_DIRTY( b, 18);
|
||||
cleanup_R1();
|
||||
return luma;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE uint8_t CRGB::getAverageLight( ) const {
|
||||
#if FASTLED_SCALE8_FIXED == 1
|
||||
const uint8_t eightyfive = 85;
|
||||
#else
|
||||
const uint8_t eightyfive = 86;
|
||||
#endif
|
||||
uint8_t avg = scale8_LEAVING_R1_DIRTY( r, eightyfive) + \
|
||||
scale8_LEAVING_R1_DIRTY( g, eightyfive) + \
|
||||
scale8_LEAVING_R1_DIRTY( b, eightyfive);
|
||||
cleanup_R1();
|
||||
return avg;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::lerp8( const CRGB& other, fract8 frac) const
|
||||
{
|
||||
CRGB ret;
|
||||
|
||||
ret.r = lerp8by8(r,other.r,frac);
|
||||
ret.g = lerp8by8(g,other.g,frac);
|
||||
ret.b = lerp8by8(b,other.b,frac);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FASTLED_FORCE_INLINE CRGB CRGB::lerp16( const CRGB& other, fract16 frac) const
|
||||
{
|
||||
CRGB ret;
|
||||
|
||||
ret.r = lerp16by16(r<<8,other.r<<8,frac)>>8;
|
||||
ret.g = lerp16by16(g<<8,other.g<<8,frac)>>8;
|
||||
ret.b = lerp16by16(b<<8,other.b<<8,frac)>>8;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/// @copydoc CRGB::operator+=
|
||||
FASTLED_FORCE_INLINE CRGB operator+( const CRGB& p1, const CRGB& p2)
|
||||
{
|
||||
return CRGB( qadd8( p1.r, p2.r),
|
||||
qadd8( p1.g, p2.g),
|
||||
qadd8( p1.b, p2.b));
|
||||
}
|
||||
|
||||
/// @copydoc CRGB::operator-=
|
||||
FASTLED_FORCE_INLINE CRGB operator-( const CRGB& p1, const CRGB& p2)
|
||||
{
|
||||
return CRGB( qsub8( p1.r, p2.r),
|
||||
qsub8( p1.g, p2.g),
|
||||
qsub8( p1.b, p2.b));
|
||||
}
|
||||
|
||||
/// @copydoc CRGB::operator*=
|
||||
FASTLED_FORCE_INLINE CRGB operator*( const CRGB& p1, uint8_t d)
|
||||
{
|
||||
return CRGB( qmul8( p1.r, d),
|
||||
qmul8( p1.g, d),
|
||||
qmul8( p1.b, d));
|
||||
}
|
||||
|
||||
/// Scale using CRGB::nscale8_video()
|
||||
FASTLED_FORCE_INLINE CRGB operator%( const CRGB& p1, uint8_t d)
|
||||
{
|
||||
CRGB retval( p1);
|
||||
retval.nscale8_video( d);
|
||||
return retval;
|
||||
}
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#undef FUNCTION_SCALE8
|
||||
17
FastLED/src/dither_mode.h
Normal file
17
FastLED/src/dither_mode.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/// @file dither_mode.h
|
||||
/// Declares dithering options and types
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
/// Disable dithering
|
||||
#define DISABLE_DITHER 0x00
|
||||
/// Enable dithering using binary dithering (only option)
|
||||
#define BINARY_DITHER 0x01
|
||||
/// The dither setting, either DISABLE_DITHER or BINARY_DITHER
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
typedef uint8_t EDitherMode;
|
||||
FASTLED_NAMESPACE_END
|
||||
88
FastLED/src/dmx.h
Normal file
88
FastLED/src/dmx.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/// @file dmx.h
|
||||
/// Defines the DMX512-based LED controllers.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
/// @addtogroup Chipsets
|
||||
/// @{
|
||||
|
||||
/// @addtogroup ClocklessChipsets
|
||||
/// @{
|
||||
|
||||
#if defined(DmxSimple_h) || defined(FASTLED_DOXYGEN)
|
||||
#include <DmxSimple.h>
|
||||
|
||||
/// Flag set when the DmxSimple library is included
|
||||
#define HAS_DMX_SIMPLE
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// DMX512 based LED controller class, using the DmxSimple library
|
||||
/// @tparam DATA_PIN the data pin for the output of the DMX bus
|
||||
/// @tparam RGB_ORDER the RGB ordering for these LEDs
|
||||
/// @see https://www.pjrc.com/teensy/td_libs_DmxSimple.html
|
||||
/// @see https://github.com/PaulStoffregen/DmxSimple
|
||||
/// @see https://en.wikipedia.org/wiki/DMX512
|
||||
template <uint8_t DATA_PIN, EOrder RGB_ORDER = RGB> class DMXSimpleController : public CPixelLEDController<RGB_ORDER> {
|
||||
public:
|
||||
/// Initialize the LED controller
|
||||
virtual void init() { DmxSimple.usePin(DATA_PIN); }
|
||||
|
||||
protected:
|
||||
/// @copydoc CPixelLEDController::showPixels()
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
int iChannel = 1;
|
||||
while(pixels.has(1)) {
|
||||
DmxSimple.write(iChannel++, pixels.loadAndScale0());
|
||||
DmxSimple.write(iChannel++, pixels.loadAndScale1());
|
||||
DmxSimple.write(iChannel++, pixels.loadAndScale2());
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(DmxSerial_h) || defined(FASTLED_DOXYGEN)
|
||||
#include <DMXSerial.h>
|
||||
|
||||
/// Flag set when the DMXSerial library is included
|
||||
#define HAS_DMX_SERIAL
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// DMX512 based LED controller class, using the DMXSerial library
|
||||
/// @tparam RGB_ORDER the RGB ordering for these LEDs
|
||||
/// @see http://www.mathertel.de/Arduino/DMXSerial.aspx
|
||||
/// @see https://github.com/mathertel/DMXSerial
|
||||
/// @see https://en.wikipedia.org/wiki/DMX512
|
||||
template <EOrder RGB_ORDER = RGB> class DMXSerialController : public CPixelLEDController<RGB_ORDER> {
|
||||
public:
|
||||
/// Initialize the LED controller
|
||||
virtual void init() { DMXSerial.init(DMXController); }
|
||||
|
||||
/// @copydoc CPixelLEDController::showPixels()
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
int iChannel = 1;
|
||||
while(pixels.has(1)) {
|
||||
DMXSerial.write(iChannel++, pixels.loadAndScale0());
|
||||
DMXSerial.write(iChannel++, pixels.loadAndScale1());
|
||||
DMXSerial.write(iChannel++, pixels.loadAndScale2());
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
/// @} DMXControllers
|
||||
/// @} Chipsets
|
||||
|
||||
#endif
|
||||
|
||||
5
FastLED/src/engine_events.h
Normal file
5
FastLED/src/engine_events.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#warning "This header is deprecated. Please use engine_events.h, this header will go away in the version 4.0."
|
||||
|
||||
#include "fl/engine_events.h"
|
||||
33
FastLED/src/eorder.h
Normal file
33
FastLED/src/eorder.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/// @file eorder.h
|
||||
/// Defines color channel ordering enumerations
|
||||
|
||||
#pragma once
|
||||
#include "fl/namespace.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// RGB color channel orderings, used when instantiating controllers to determine
|
||||
/// what order the controller should send data out in. The default ordering
|
||||
/// is RGB.
|
||||
/// Within this enum, the red channel is 0, the green channel is 1, and the
|
||||
/// blue chanel is 2.
|
||||
enum EOrder {
|
||||
RGB=0012, ///< Red, Green, Blue (0012)
|
||||
RBG=0021, ///< Red, Blue, Green (0021)
|
||||
GRB=0102, ///< Green, Red, Blue (0102)
|
||||
GBR=0120, ///< Green, Blue, Red (0120)
|
||||
BRG=0201, ///< Blue, Red, Green (0201)
|
||||
BGR=0210 ///< Blue, Green, Red (0210)
|
||||
};
|
||||
|
||||
// After EOrder is applied this is where W is inserted for RGBW.
|
||||
enum EOrderW {
|
||||
W3 = 0x3, ///< White is fourth
|
||||
W2 = 0x2, ///< White is third
|
||||
W1 = 0x1, ///< White is second
|
||||
W0 = 0x0, ///< White is first
|
||||
WDefault = W3
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
88
FastLED/src/fastled_config.h
Normal file
88
FastLED/src/fastled_config.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
/// @file fastled_config.h
|
||||
/// Contains definitions that can be used to configure FastLED at compile time
|
||||
|
||||
/// @def FASTLED_FORCE_SOFTWARE_PINS
|
||||
/// Use this option only for debugging pin access and forcing software pin access. Forces use of `digitalWrite()`
|
||||
/// methods for pin access vs. direct hardware port access.
|
||||
/// @note Software pin access only works in Arduino-based environments.
|
||||
// #define FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
/// @def FASTLED_FORCE_SOFTWARE_SPI
|
||||
/// Use this option only for debugging bitbang'd SPI access or to work around bugs in hardware
|
||||
/// SPI access. Forces use of bit-banged SPI, even on pins that have hardware SPI available.
|
||||
// #define FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
/// @def FASTLED_ALLOW_INTERRUPTS
|
||||
/// Use this to force FastLED to allow interrupts in the clockless chipsets (or to force it to
|
||||
/// disallow), overriding the default on platforms that support this. Set the value to 1 to
|
||||
/// allow interrupts or 0 to disallow them.
|
||||
// #define FASTLED_ALLOW_INTERRUPTS 1
|
||||
// #define FASTLED_ALLOW_INTERRUPTS 0
|
||||
|
||||
/// @def FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW
|
||||
/// Use this to allow some integer overflows/underflows in the inoise() functions.
|
||||
/// The original implementions allowed this, and had some discontinuties in the noise
|
||||
/// output. It's technically an implementation bug, and was fixed, but you may wish
|
||||
/// to preserve the old look and feel of the inoise() functions in your existing animations.
|
||||
/// The default is 0: NO overflow, and 'continuous' noise output, aka the fixed way.
|
||||
// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0
|
||||
// #define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 1
|
||||
|
||||
/// @def FASTLED_SCALE8_FIXED
|
||||
/// Use this to toggle whether or not to use the "fixed" FastLED scale8(). The initial scale8()
|
||||
/// had a problem where scale8(255,255) would give you 254. This is now fixed, and that
|
||||
/// fix is enabled by default. However, if for some reason you have code that is not
|
||||
/// working right as a result of this (e.g. code that was expecting the old scale8() behavior)
|
||||
/// you can disable it here.
|
||||
#define FASTLED_SCALE8_FIXED 1
|
||||
// #define FASTLED_SCALE8_FIXED 0
|
||||
|
||||
/// @def FASTLED_BLEND_FIXED
|
||||
/// Use this to toggle whether to use "fixed" FastLED pixel blending, including ColorFromPalette.
|
||||
/// The prior pixel blend functions had integer-rounding math errors that led to
|
||||
/// small errors being inadvertently added to the low bits of blended colors, including colors
|
||||
/// retrieved from color palettes using LINEAR_BLEND. This is now fixed, and the
|
||||
/// fix is enabled by default. However, if for some reason you wish to run with the old
|
||||
/// blending, including the integer rounding and color errors, you can disable the bugfix here.
|
||||
#define FASTLED_BLEND_FIXED 1
|
||||
// #define FASTLED_BLEND_FIXED 0
|
||||
|
||||
/// @def FASTLED_NOISE_FIXED
|
||||
/// Use this to toggle whether to use "fixed" FastLED 8-bit and 16-bit noise functions.
|
||||
/// The prior noise functions had some math errors that led to "discontinuities" in the
|
||||
/// output, which by definition should be smooth and continuous. The bug led to
|
||||
/// noise function output that had "edges" and glitches in it. This is now fixed, and the
|
||||
/// fix is enabled by default. However, if for some reason you wish to run with the old
|
||||
/// noise code, including the glitches, you can disable the bugfix here.
|
||||
#define FASTLED_NOISE_FIXED 1
|
||||
//#define FASTLED_NOISE_FIXED 0
|
||||
|
||||
/// @def FASTLED_INTERRUPT_RETRY_COUNT
|
||||
/// Use this to determine how many times FastLED will attempt to re-transmit a frame if interrupted
|
||||
/// for too long by interrupts.
|
||||
#ifndef FASTLED_INTERRUPT_RETRY_COUNT
|
||||
#define FASTLED_INTERRUPT_RETRY_COUNT 2
|
||||
#endif
|
||||
|
||||
/// @def FASTLED_USE_GLOBAL_BRIGHTNESS
|
||||
/// Use this toggle to enable global brightness in contollers that support is (e.g. ADA102 and SK9822).
|
||||
/// It changes how color scaling works and uses global brightness before scaling down color values.
|
||||
/// This enables much more accurate color control on low brightness settings.
|
||||
//#define FASTLED_USE_GLOBAL_BRIGHTNESS 1
|
||||
|
||||
|
||||
// The defines are used for Doxygen documentation generation.
|
||||
// They're commented out above and repeated here so the Doxygen parser
|
||||
// will be able to find them. They will not affect your own configuration,
|
||||
// and you do *NOT* need to modify them.
|
||||
#ifdef FASTLED_DOXYGEN
|
||||
#define FASTLED_FORCE_SOFTWARE_PINS
|
||||
#define FASTLED_FORCE_SOFTWARE_SPI
|
||||
#define FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW 0
|
||||
#define FASTLED_INTERRUPT_RETRY_COUNT 2
|
||||
#define FASTLED_USE_GLOBAL_BRIGHTNESS 0
|
||||
#endif
|
||||
|
||||
218
FastLED/src/fastled_delay.h
Normal file
218
FastLED/src/fastled_delay.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#ifndef __INC_FL_DELAY_H
|
||||
#define __INC_FL_DELAY_H
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/types.h"
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
/// @file fastled_delay.h
|
||||
/// Utility functions and classes for managing delay cycles
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
#if (!defined(NO_MINIMUM_WAIT) || (NO_MINIMUM_WAIT==0))
|
||||
|
||||
/// Class to ensure that a minimum amount of time has kicked since the last time run - and delay if not enough time has passed yet.
|
||||
/// @tparam WAIT The amount of time to wait, in microseconds
|
||||
template<int WAIT> class CMinWait {
|
||||
/// Timestamp of the last time this was run, in microseconds
|
||||
uint16_t mLastMicros;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
CMinWait() { mLastMicros = 0; }
|
||||
|
||||
/// Blocking delay until WAIT time since mark() has passed
|
||||
void wait() {
|
||||
uint16_t diff;
|
||||
do {
|
||||
diff = (micros() & 0xFFFF) - mLastMicros;
|
||||
} while(diff < WAIT);
|
||||
}
|
||||
|
||||
/// Reset the timestamp that marks the start of the wait period
|
||||
void mark() { mLastMicros = micros() & 0xFFFF; }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// if you keep your own FPS (and therefore don't call show() too quickly for pixels to latch), you may not want a minimum wait.
|
||||
template<int WAIT> class CMinWait {
|
||||
public:
|
||||
CMinWait() { }
|
||||
void wait() { }
|
||||
void mark() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// @name Clock cycle counted delay loop
|
||||
///
|
||||
/// @{
|
||||
|
||||
// Default is now just 'nop', with special case for AVR
|
||||
|
||||
// ESP32 core has it's own definition of NOP, so undef it first
|
||||
#ifdef ESP32
|
||||
#undef NOP
|
||||
#undef NOP2
|
||||
#endif
|
||||
|
||||
#if defined(__AVR__)
|
||||
# define FL_NOP __asm__ __volatile__ ("cp r0,r0\n");
|
||||
# define FL_NOP2 __asm__ __volatile__ ("rjmp .+0");
|
||||
#else
|
||||
/// Single no operation ("no-op") instruction for delay
|
||||
# define FL_NOP __asm__ __volatile__ ("nop\n");
|
||||
/// Double no operation ("no-op") instruction for delay
|
||||
# define FL_NOP2 __asm__ __volatile__ ("nop\n\t nop\n");
|
||||
#endif
|
||||
|
||||
// predeclaration to not upset the compiler
|
||||
|
||||
|
||||
/// Delay N clock cycles.
|
||||
/// @tparam CYCLES the number of clock cycles to delay
|
||||
/// @note No delay is applied if CYCLES is less than or equal to zero.
|
||||
template<fl::cycle_t CYCLES> inline void delaycycles();
|
||||
|
||||
/// A variant of ::delaycycles that will always delay
|
||||
/// at least one cycle.
|
||||
template<fl::cycle_t CYCLES> inline void delaycycles_min1() {
|
||||
delaycycles<1>();
|
||||
delaycycles<CYCLES-1>();
|
||||
}
|
||||
|
||||
|
||||
// TODO: ARM version of _delaycycles_
|
||||
|
||||
// usable definition
|
||||
#if defined(FASTLED_AVR)
|
||||
// worker template - this will nop for LOOP * 3 + PAD cycles total
|
||||
template<int LOOP, fl::cycle_t PAD> inline void _delaycycles_AVR() {
|
||||
delaycycles<PAD>();
|
||||
// the loop below is 3 cycles * LOOP. the LDI is one cycle,
|
||||
// the DEC is 1 cycle, the BRNE is 2 cycles if looping back and
|
||||
// 1 if not (the LDI balances out the BRNE being 1 cycle on exit)
|
||||
__asm__ __volatile__ (
|
||||
" LDI R16, %0\n"
|
||||
"L_%=: DEC R16\n"
|
||||
" BRNE L_%=\n"
|
||||
: /* no outputs */
|
||||
: "M" (LOOP)
|
||||
: "r16"
|
||||
);
|
||||
}
|
||||
|
||||
template<fl::cycle_t CYCLES> FASTLED_FORCE_INLINE void delaycycles() {
|
||||
_delaycycles_AVR<CYCLES / 3, CYCLES % 3>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
// template<int LOOP, fl::cycle_t PAD> inline void _delaycycles_ARM() {
|
||||
// delaycycles<PAD>();
|
||||
// // the loop below is 3 cycles * LOOP. the LDI is one cycle,
|
||||
// // the DEC is 1 cycle, the BRNE is 2 cycles if looping back and
|
||||
// // 1 if not (the LDI balances out the BRNE being 1 cycle on exit)
|
||||
// __asm__ __volatile__ (
|
||||
// " mov.w r9, %0\n"
|
||||
// "L_%=: subs.w r9, r9, #1\n"
|
||||
// " bne.n L_%=\n"
|
||||
// : /* no outputs */
|
||||
// : "M" (LOOP)
|
||||
// : "r9"
|
||||
// );
|
||||
// }
|
||||
|
||||
|
||||
template<fl::cycle_t CYCLES> FASTLED_FORCE_INLINE void delaycycles() {
|
||||
// _delaycycles_ARM<CYCLES / 3, CYCLES % 3>();
|
||||
FL_NOP; delaycycles<CYCLES-1>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// pre-instantiations for values small enough to not need the loop, as well as sanity holders
|
||||
// for some negative values.
|
||||
|
||||
// These are hidden from Doxygen because they match the expected behavior of the class.
|
||||
/// @cond
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-10>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-9>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-8>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-7>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-6>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-5>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-4>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-3>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-2>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<-1>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<0>() {}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<1>() {FL_NOP;}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<2>() {FL_NOP2;}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<3>() {FL_NOP;FL_NOP2;}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<4>() {FL_NOP2;FL_NOP2;}
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<5>() {FL_NOP2;FL_NOP2;FL_NOP;}
|
||||
#if defined(ESP32)
|
||||
template<> FASTLED_FORCE_INLINE void delaycycles<4294966398>() {
|
||||
// specialization for a gigantic amount of cycles, apparently this is needed
|
||||
// or esp32 will blow the stack with cycles = 4294966398.
|
||||
const uint32_t termination = 4294966398 / 10;
|
||||
const uint32_t remainder = 4294966398 % 10;
|
||||
for (uint32_t i = 0; i < termination; i++) {
|
||||
FL_NOP; FL_NOP; FL_NOP; FL_NOP; FL_NOP;
|
||||
FL_NOP; FL_NOP; FL_NOP; FL_NOP; FL_NOP;
|
||||
}
|
||||
|
||||
// remainder
|
||||
switch (remainder) {
|
||||
case 9: FL_NOP;
|
||||
case 8: FL_NOP;
|
||||
case 7: FL_NOP;
|
||||
case 6: FL_NOP;
|
||||
case 5: FL_NOP;
|
||||
case 4: FL_NOP;
|
||||
case 3: FL_NOP;
|
||||
case 2: FL_NOP;
|
||||
case 1: FL_NOP;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// @name Some timing related macros/definitions
|
||||
/// @{
|
||||
|
||||
// Macro to convert from nano-seconds to clocks and clocks to nano-seconds
|
||||
// #define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
|
||||
|
||||
/// CPU speed, in megahertz (MHz)
|
||||
#define F_CPU_MHZ (F_CPU / 1000000L)
|
||||
|
||||
// #define NS(_NS) ( (_NS * (F_CPU / 1000000L))) / 1000
|
||||
|
||||
/// Convert from nanoseconds to number of clock cycles
|
||||
#define NS(_NS) (((_NS * F_CPU_MHZ) + 999) / 1000)
|
||||
/// Convert from number of clock cycles to microseconds
|
||||
#define CLKS_TO_MICROS(_CLKS) ((long)(_CLKS)) / (F_CPU / 1000000L)
|
||||
|
||||
/// Macro for making sure there's enough time available
|
||||
#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 3 || NS(C) < 6)
|
||||
|
||||
/// @}
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
103
FastLED/src/fastled_progmem.h
Normal file
103
FastLED/src/fastled_progmem.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef __INC_FL_PROGMEM_H
|
||||
#define __INC_FL_PROGMEM_H
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
/// @file fastled_progmem.h
|
||||
/// Wrapper definitions to allow seamless use of PROGMEM in environments that have it
|
||||
///
|
||||
/// This is a compatibility layer for devices that do or don't
|
||||
/// have "PROGMEM" and the associated pgm_ accessors.
|
||||
///
|
||||
/// If a platform supports PROGMEM, it should define
|
||||
/// `FASTLED_USE_PROGMEM` as 1, otherwise FastLED will
|
||||
/// fall back to NOT using PROGMEM.
|
||||
///
|
||||
/// Whether or not pgmspace.h is \#included is separately
|
||||
/// controllable by FASTLED_INCLUDE_PGMSPACE, if needed.
|
||||
|
||||
|
||||
|
||||
// This block is used for Doxygen documentation generation,
|
||||
// so that the Doxygen parser will be able to find the macros
|
||||
// included without a defined platform
|
||||
#ifdef FASTLED_DOXYGEN
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
/// @def FASTLED_USE_PROGMEM
|
||||
/// Determine whether the current platform supports PROGMEM.
|
||||
/// If FASTLED_USE_PROGMEM is 1, we'll map FL_PROGMEM
|
||||
/// and the FL_PGM_* accessors to the Arduino equivalents.
|
||||
|
||||
|
||||
#if (FASTLED_USE_PROGMEM == 1) || defined(FASTLED_DOXYGEN)
|
||||
#ifndef FASTLED_INCLUDE_PGMSPACE
|
||||
#define FASTLED_INCLUDE_PGMSPACE 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_INCLUDE_PGMSPACE == 1
|
||||
#include <avr/pgmspace.h>
|
||||
#endif
|
||||
|
||||
/// PROGMEM keyword for storage
|
||||
#define FL_PROGMEM PROGMEM
|
||||
|
||||
/// @name PROGMEM Read Functions
|
||||
/// Functions for reading data from PROGMEM memory.
|
||||
///
|
||||
/// Note that only the "near" memory wrappers are provided.
|
||||
/// If you're using "far" memory, you already have
|
||||
/// portability issues to work through, but you could
|
||||
/// add more support here if needed.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Read a byte (8-bit) from PROGMEM memory
|
||||
#define FL_PGM_READ_BYTE_NEAR(x) (pgm_read_byte_near(x))
|
||||
/// Read a word (16-bit) from PROGMEM memory
|
||||
#define FL_PGM_READ_WORD_NEAR(x) (pgm_read_word_near(x))
|
||||
/// Read a double word (32-bit) from PROGMEM memory
|
||||
#define FL_PGM_READ_DWORD_NEAR(x) (pgm_read_dword_near(x))
|
||||
|
||||
/// @} PROGMEM
|
||||
|
||||
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
|
||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6))
|
||||
#ifdef FASTLED_AVR
|
||||
#ifdef PROGMEM
|
||||
#undef PROGMEM
|
||||
#define PROGMEM __attribute__((section(".progmem.data")))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
// If FASTLED_USE_PROGMEM is 0 or undefined,
|
||||
// we'll use regular memory (RAM) access.
|
||||
|
||||
//empty PROGMEM simulation
|
||||
#define FL_PROGMEM
|
||||
#define FL_PGM_READ_BYTE_NEAR(x) (*((const uint8_t*)(x)))
|
||||
#define FL_PGM_READ_WORD_NEAR(x) (*((const uint16_t*)(x)))
|
||||
#define FL_PGM_READ_DWORD_NEAR(x) (*((const uint32_t*)(x)))
|
||||
|
||||
#endif
|
||||
|
||||
/// @def FL_ALIGN_PROGMEM
|
||||
/// Helps to force 4-byte alignment for platforms with unaligned access
|
||||
///
|
||||
/// On some platforms, most notably ARM M0, unaligned access
|
||||
/// to 'PROGMEM' for multibyte values (e.g. read dword) is
|
||||
/// not allowed and causes a crash. This macro can help
|
||||
/// force 4-byte alignment as needed. The FastLED gradient
|
||||
/// palette code uses 'read dword', and now uses this macro
|
||||
/// to make sure that gradient palettes are 4-byte aligned.
|
||||
|
||||
#if defined(FASTLED_ARM) || defined(ESP32) || defined(ESP8266) || defined(FASTLED_DOXYGEN)
|
||||
#define FL_ALIGN_PROGMEM __attribute__ ((aligned (4)))
|
||||
#else
|
||||
#define FL_ALIGN_PROGMEM
|
||||
#endif
|
||||
|
||||
#endif
|
||||
396
FastLED/src/fastpin.h
Normal file
396
FastLED/src/fastpin.h
Normal file
@@ -0,0 +1,396 @@
|
||||
#ifndef __INC_FASTPIN_H
|
||||
#define __INC_FASTPIN_H
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "led_sysdefs.h"
|
||||
#include <stddef.h>
|
||||
#include "fl/unused.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#ifdef ESP32
|
||||
// Get rid of the endless volatile warnings in ESP32
|
||||
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||
#pragma GCC diagnostic ignored "-Wvolatile"
|
||||
#endif
|
||||
|
||||
/// @file fastpin.h
|
||||
/// Class base definitions for defining fast pin access
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// Constant for "not a pin"
|
||||
/// @todo Unused, remove?
|
||||
#define NO_PIN 255
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Pin access class - needs to tune for various platforms (naive fallback solution?)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Abstract class for "selectable" things
|
||||
class Selectable {
|
||||
public:
|
||||
#ifndef __AVR__
|
||||
virtual ~Selectable() {}
|
||||
#endif
|
||||
virtual void select() = 0; ///< Select this object
|
||||
virtual void release() = 0; ///< Release this object
|
||||
virtual bool isSelected() = 0; ///< Check if this object is currently selected
|
||||
};
|
||||
|
||||
#if !defined(FASTLED_NO_PINMAP)
|
||||
|
||||
/// Naive fallback solution for low level pin access
|
||||
class Pin : public Selectable {
|
||||
volatile RwReg *mPort; ///< Output register for the pin
|
||||
volatile RoReg *mInPort; ///< Input register for the pin
|
||||
RwReg mPinMask; ///< Bitmask for the pin within its register
|
||||
uint8_t mPin; ///< Arduino digital pin number
|
||||
|
||||
/// Initialize the class by retrieving the register
|
||||
/// pointers and bitmask.
|
||||
void _init() {
|
||||
mPinMask = digitalPinToBitMask(mPin);
|
||||
mPort = (volatile RwReg*)portOutputRegister(digitalPinToPort(mPin));
|
||||
mInPort = (volatile RoReg*)portInputRegister(digitalPinToPort(mPin));
|
||||
}
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// @param pin Arduino digital pin number
|
||||
Pin(int pin) : mPin(pin) { _init(); }
|
||||
#ifndef __AVR__
|
||||
virtual ~Pin() {} // Shut up the compiler warning, but don't steal bytes from AVR.
|
||||
#endif
|
||||
|
||||
typedef volatile RwReg * port_ptr_t; ///< type for a pin read/write register, volatile
|
||||
typedef RwReg port_t; ///< type for a pin read/write register, non-volatile
|
||||
|
||||
/// Set the pin mode as `OUTPUT`
|
||||
inline void setOutput() { pinMode(mPin, OUTPUT); }
|
||||
|
||||
/// Set the pin mode as `INPUT`
|
||||
inline void setInput() { pinMode(mPin, INPUT); }
|
||||
|
||||
/// Set the pin state to `HIGH`
|
||||
inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
|
||||
/// Set the pin state to `LOW`
|
||||
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
|
||||
|
||||
/// Toggle the pin twice to create a short pulse
|
||||
inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
/// Toggle the pin.
|
||||
/// If the pin was high, set it low. If was low, set it high.
|
||||
inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
|
||||
|
||||
/// Set the same pin on another port to `HIGH`
|
||||
/// @param port the port to modify
|
||||
inline void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
|
||||
/// Set the same pin on another port to `LOW`
|
||||
/// @param port the port to modify
|
||||
inline void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
|
||||
/// Set the state of the output register
|
||||
/// @param val the state to set the output register to
|
||||
/// @note This function is not limited to the current pin! It modifies the entire register.
|
||||
inline void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *mPort = val; }
|
||||
|
||||
/// Set the state of a port
|
||||
/// @param port the port to modify
|
||||
/// @param val the state to set the port to
|
||||
inline void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
/// Gets the state of the port with this pin `HIGH`
|
||||
port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; }
|
||||
/// Gets the state of the port with this pin `LOW`
|
||||
port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
|
||||
/// Get the output state of the port
|
||||
port_ptr_t port() __attribute__ ((always_inline)) { return mPort; }
|
||||
/// Get the pin mask
|
||||
port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
|
||||
|
||||
/// @copydoc Pin::hi()
|
||||
virtual void select() override { hi(); }
|
||||
/// @copydoc Pin::lo()
|
||||
virtual void release() override { lo(); }
|
||||
/// Checks if the pin is currently `HIGH`
|
||||
virtual bool isSelected() override { return (*mPort & mPinMask) == mPinMask; }
|
||||
};
|
||||
|
||||
/// I/O pin initially set to OUTPUT
|
||||
class OutputPin : public Pin {
|
||||
public:
|
||||
/// @copydoc Pin::Pin(int)
|
||||
OutputPin(int pin) : Pin(pin) { setOutput(); }
|
||||
};
|
||||
|
||||
/// I/O pin initially set to INPUT
|
||||
class InputPin : public Pin {
|
||||
public:
|
||||
/// @copydoc Pin::Pin(int)
|
||||
InputPin(int pin) : Pin(pin) { setInput(); }
|
||||
};
|
||||
|
||||
#else
|
||||
// This is the empty code version of the raw pin class, method bodies should be filled in to Do The Right Thing[tm] when making this
|
||||
// available on a new platform
|
||||
class Pin : public Selectable {
|
||||
volatile RwReg *mPort;
|
||||
volatile RoReg *mInPort;
|
||||
RwReg mPinMask;
|
||||
uint8_t mPin;
|
||||
|
||||
|
||||
|
||||
void _init() {
|
||||
// TODO: fill in init on a new platform
|
||||
mPinMask = 0;
|
||||
mPort = NULL;
|
||||
mInPort = NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
Pin(int pin) : mPin(pin) { _init(); }
|
||||
#ifndef __AVR__
|
||||
virtual ~Pin() {} // Shut up the compiler warning, but don't steal bytes from AVR.
|
||||
#endif
|
||||
|
||||
void setPin(int pin) { mPin = pin; _init(); }
|
||||
|
||||
typedef volatile RwReg * port_ptr_t;
|
||||
typedef RwReg port_t;
|
||||
|
||||
inline void setOutput() { /* TODO: Set pin output */ }
|
||||
inline void setInput() { /* TODO: Set pin input */ }
|
||||
|
||||
inline void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; }
|
||||
inline void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; }
|
||||
|
||||
inline void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
inline void toggle() __attribute__ ((always_inline)) { *mInPort = mPinMask; }
|
||||
|
||||
inline void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; }
|
||||
inline void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; }
|
||||
inline void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *mPort = val; }
|
||||
|
||||
inline void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; }
|
||||
port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; }
|
||||
port_ptr_t port() __attribute__ ((always_inline)) { return mPort; }
|
||||
port_t mask() __attribute__ ((always_inline)) { return mPinMask; }
|
||||
|
||||
virtual void select() override { hi(); }
|
||||
virtual void release() override { lo(); }
|
||||
virtual bool isSelected() override { return (*mPort & mPinMask) == mPinMask; }
|
||||
};
|
||||
|
||||
class OutputPin : public Pin {
|
||||
public:
|
||||
OutputPin(int pin) : Pin(pin) { setOutput(); }
|
||||
};
|
||||
|
||||
class InputPin : public Pin {
|
||||
public:
|
||||
InputPin(int pin) : Pin(pin) { setInput(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// The simplest level of Pin class. This relies on runtime functions during initialization to get the port/pin mask for the pin. Most
|
||||
/// of the accesses involve references to these static globals that get set up. This won't be the fastest set of pin operations, but it
|
||||
/// will provide pin level access on pretty much all Arduino environments. In addition, it includes some methods to help optimize access in
|
||||
/// various ways. Namely, the versions of hi(), lo(), and fastset() that take the port register as a passed in register variable (saving a global
|
||||
/// dereference), since these functions are aggressively inlined, that can help collapse out a lot of extraneous memory loads/dereferences.
|
||||
///
|
||||
/// In addition, if, while writing a bunch of data to a pin, you know no other pins will be getting written to, you can get/cache a value of
|
||||
/// the pin's port register and use that to do a full set to the register. This results in one being able to simply do a store to the register,
|
||||
/// vs. the load, and/or, and store that would be done normally.
|
||||
///
|
||||
/// There are platform specific instantiations of this class that provide direct i/o register access to pins for much higher speed pin twiddling.
|
||||
///
|
||||
/// Note that these classes are all static functions. So the proper usage is Pin<13>::hi(); or such. Instantiating objects is not recommended,
|
||||
/// as passing Pin objects around will likely -not- have the effect you're expecting.
|
||||
#ifdef FASTLED_FORCE_SOFTWARE_PINS
|
||||
template<uint8_t PIN> class FastPin {
|
||||
static RwReg sPinMask; ///< Bitmask for the pin within its register
|
||||
static volatile RwReg *sPort; ///< Output register for the pin
|
||||
static volatile RoReg *sInPort; ///< Input register for the pin
|
||||
static void _init() {
|
||||
#if !defined(FASTLED_NO_PINMAP)
|
||||
sPinMask = digitalPinToBitMask(PIN);
|
||||
sPort = portOutputRegister(digitalPinToPort(PIN));
|
||||
sInPort = portInputRegister(digitalPinToPort(PIN));
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
typedef volatile RwReg * port_ptr_t; ///< @copydoc Pin::port_ptr_t
|
||||
typedef RwReg port_t; ///< @copydoc Pin::port_t
|
||||
|
||||
/// @copydoc Pin::setOutput()
|
||||
inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); }
|
||||
/// @copydoc Pin::setInput()
|
||||
inline static void setInput() { _init(); pinMode(PIN, INPUT); }
|
||||
|
||||
/// @copydoc Pin::hi()
|
||||
inline static void hi() __attribute__ ((always_inline)) { *sPort |= sPinMask; }
|
||||
/// @copydoc Pin::lo()
|
||||
inline static void lo() __attribute__ ((always_inline)) { *sPort &= ~sPinMask; }
|
||||
|
||||
/// @copydoc Pin::strobe()
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
/// @copydoc Pin::toggle()
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *sInPort = sPinMask; }
|
||||
|
||||
/// @copydoc Pin::hi(FASTLED_REGISTER port_ptr_t)
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port |= sPinMask; }
|
||||
/// @copydoc Pin::lo(FASTLED_REGISTER port_ptr_t)
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~sPinMask; }
|
||||
/// @copydoc Pin::set(FASTLED_REGISTER port_t)
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *sPort = val; }
|
||||
|
||||
/// @copydoc Pin::fastset()
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
/// @copydoc Pin::hival()
|
||||
static port_t hival() __attribute__ ((always_inline)) { return *sPort | sPinMask; }
|
||||
/// @copydoc Pin::loval()
|
||||
static port_t loval() __attribute__ ((always_inline)) { return *sPort & ~sPinMask; }
|
||||
/// @copydoc Pin::port()
|
||||
static port_ptr_t port() __attribute__ ((always_inline)) { return sPort; }
|
||||
/// @copydoc Pin::mask()
|
||||
static port_t mask() __attribute__ ((always_inline)) { return sPinMask; }
|
||||
};
|
||||
|
||||
template<uint8_t PIN> RwReg FastPin<PIN>::sPinMask;
|
||||
template<uint8_t PIN> volatile RwReg *FastPin<PIN>::sPort;
|
||||
template<uint8_t PIN> volatile RoReg *FastPin<PIN>::sInPort;
|
||||
|
||||
#else
|
||||
|
||||
template<uint8_t PIN> class FastPin {
|
||||
// This is a default implementation. If you are hitting this then FastPin<> is either:
|
||||
// 1) Not defined -or-
|
||||
// 2) Not part of the set of defined FastPin<> specializations for your platform
|
||||
// You need to define a FastPin<> specialization
|
||||
// or change what get's included for your particular build target.
|
||||
// Keep in mind that these messages are cryptic, so it's best to define an invalid in type.
|
||||
constexpr static bool validpin() { return false; }
|
||||
constexpr static bool LowSpeedOnlyRecommended() { // Some implementations assume this exists.
|
||||
// Caller must always determine if high speed use if allowed on a given pin,
|
||||
// because it depends on more than just the chip packaging ... it depends on entire board (and even system) design.
|
||||
return false; // choosing default to be FALSE, to allow users to ATTEMPT to use high-speed on pins where support is not known
|
||||
}
|
||||
|
||||
static_assert(validpin(), "Invalid pin specified");
|
||||
|
||||
static void _init() { }
|
||||
|
||||
public:
|
||||
typedef volatile RwReg * port_ptr_t; ///< @copydoc Pin::port_ptr_t
|
||||
typedef RwReg port_t; ///< @copydoc Pin::port_t
|
||||
|
||||
/// @copydoc Pin::setOutput()
|
||||
inline static void setOutput() { }
|
||||
/// @copydoc Pin::setInput()
|
||||
inline static void setInput() { }
|
||||
|
||||
/// @copydoc Pin::hi()
|
||||
inline static void hi() __attribute__ ((always_inline)) { }
|
||||
/// @copydoc Pin::lo()
|
||||
inline static void lo() __attribute__ ((always_inline)) { }
|
||||
|
||||
/// @copydoc Pin::strobe()
|
||||
inline static void strobe() __attribute__ ((always_inline)) { }
|
||||
|
||||
/// @copydoc Pin::toggle()
|
||||
inline static void toggle() __attribute__ ((always_inline)) { }
|
||||
|
||||
/// @copydoc Pin::hi(FASTLED_REGISTER port_ptr_t)
|
||||
inline static void hi(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) {
|
||||
FASTLED_UNUSED(port);
|
||||
}
|
||||
/// @copydoc Pin::lo(FASTLED_REGISTER port_ptr_t)
|
||||
inline static void lo(FASTLED_REGISTER port_ptr_t port) __attribute__ ((always_inline)) {
|
||||
FASTLED_UNUSED(port);
|
||||
}
|
||||
/// @copydoc Pin::set(FASTLED_REGISTER port_t)
|
||||
inline static void set(FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) {
|
||||
FASTLED_UNUSED(val);
|
||||
}
|
||||
|
||||
/// @copydoc Pin::fastset()
|
||||
inline static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val) __attribute__ ((always_inline)) {
|
||||
FASTLED_UNUSED(port);
|
||||
FASTLED_UNUSED(val);
|
||||
}
|
||||
|
||||
/// @copydoc Pin::hival()
|
||||
static port_t hival() __attribute__ ((always_inline)) { return 0; }
|
||||
/// @copydoc Pin::loval()
|
||||
static port_t loval() __attribute__ ((always_inline)) { return 0;}
|
||||
/// @copydoc Pin::port()
|
||||
static port_ptr_t port() __attribute__ ((always_inline)) { return NULL; }
|
||||
/// @copydoc Pin::mask()
|
||||
static port_t mask() __attribute__ ((always_inline)) { return 0; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// FastPin implementation for bit-banded access.
|
||||
/// Only for MCUs that support bitbanding.
|
||||
/// @note This bitband class is optional!
|
||||
template<uint8_t PIN> class FastPinBB : public FastPin<PIN> {};
|
||||
|
||||
typedef volatile uint32_t & reg32_t; ///< Reference to a 32-bit register, volatile
|
||||
typedef volatile uint32_t * ptr_reg32_t; ///< Pointer to a 32-bit register, volatile
|
||||
|
||||
/// Utility template for tracking down information about pins and ports
|
||||
/// @tparam port the port to check information for
|
||||
template<uint8_t port> struct __FL_PORT_INFO {
|
||||
/// Checks whether a port exists
|
||||
static bool hasPort() { return 0; }
|
||||
/// Gets the name of the port, as a C-string
|
||||
static const char *portName() { return "--"; }
|
||||
/// Gets the raw address of the port
|
||||
static const void *portAddr() { return NULL; }
|
||||
};
|
||||
|
||||
|
||||
/// Macro to create the instantiations for defined ports.
|
||||
/// We're going to abuse this later for auto discovery of pin/port mappings
|
||||
/// for new variants.
|
||||
/// Use this for ports that are numeric in nature, e.g. GPIO0, GPIO1, etc.
|
||||
/// @param L the number of the port
|
||||
/// @param BASE the data type for the register
|
||||
#define _FL_DEFINE_PORT(L, BASE) template<> struct __FL_PORT_INFO<L> { \
|
||||
static bool hasPort() { return 1; } \
|
||||
static const char *portName() { return #L; } \
|
||||
typedef BASE __t_baseType; \
|
||||
static const void *portAddr() { return (void*)&__t_baseType::r(); } };
|
||||
|
||||
/// Macro to create the instantiations for defined ports.
|
||||
/// We're going to abuse this later for auto discovery of pin/port mappings
|
||||
/// for new variants.
|
||||
/// Use this for ports that are letters. The first parameter will be the
|
||||
/// letter, the second parameter will be an integer/counter of some kind.
|
||||
/// This is because attempts to turn macro parameters into character constants
|
||||
/// break in some compilers.
|
||||
/// @param L the letter of the port
|
||||
/// @param LC an integer counter
|
||||
/// @param BASE the data type for the register
|
||||
#define _FL_DEFINE_PORT3(L, LC, BASE) template<> struct __FL_PORT_INFO<LC> { \
|
||||
static bool hasPort() { return 1; } \
|
||||
static const char *portName() { return #L; } \
|
||||
typedef BASE __t_baseType; \
|
||||
static const void *portAddr() { return (void*)&__t_baseType::r(); } };
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif // __INC_FASTPIN_H
|
||||
197
FastLED/src/fastspi.h
Normal file
197
FastLED/src/fastspi.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/// @file fastspi.h
|
||||
/// Serial peripheral interface (SPI) definitions per platform
|
||||
|
||||
#ifndef __INC_FASTSPI_H
|
||||
#define __INC_FASTSPI_H
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "lib8tion.h"
|
||||
|
||||
#include "fastspi_bitbang.h"
|
||||
|
||||
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && (F_CPU > 48000000)
|
||||
#define DATA_RATE_MHZ(X) (((48000000L / 1000000L) / X))
|
||||
#define DATA_RATE_KHZ(X) (((48000000L / 1000L) / X))
|
||||
#elif defined(FASTLED_TEENSY4) || (defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)) || (defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI) || defined(FASTLED_STUB_IMPL))
|
||||
// just use clocks
|
||||
#define DATA_RATE_MHZ(X) (1000000 * (X))
|
||||
#define DATA_RATE_KHZ(X) (1000 * (X))
|
||||
#else
|
||||
/// Convert data rate from megahertz (MHz) to clock cycles per bit
|
||||
#define DATA_RATE_MHZ(X) ((F_CPU / 1000000L) / X)
|
||||
/// Convert data rate from kilohertz (KHz) to clock cycles per bit
|
||||
#define DATA_RATE_KHZ(X) ((F_CPU / 1000L) / X)
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// External SPI template definition with partial instantiation(s) to map to hardware SPI ports on platforms/builds where the pin
|
||||
// mappings are known at compile time.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_STUB_IMPL)
|
||||
|
||||
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public StubSPIOutput {};
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#if !defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
/// Hardware SPI output
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
/// Software SPI output
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SoftwareSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
#if defined(NRF51) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public NRF51SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
#if defined(NRF52_SERIES) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
#if defined(FASTLED_APOLLO3) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public APOLLO3HardwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public ESP32SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SPIOutput : public ESP8266SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(SPI_DATA) && defined(SPI_CLOCK)
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
|
||||
|
||||
#if defined(SPI2_DATA)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI_DATA, SPI2_CLOCK, SPI_SPEED, 0x4002C000> {};
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<SPI2_DATA, SPI_CLOCK, SPI_SPEED, 0x4002C000> {};
|
||||
#endif
|
||||
|
||||
#elif defined(FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI, 0> {};
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI1_DATA, SPI1_CLOCK, SPI_SPEED, SPI1, 1> {};
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED> : public Teensy4HardwareSPIOutput<SPI2_DATA, SPI2_CLOCK, SPI_SPEED, SPI2, 2> {};
|
||||
|
||||
#elif defined(FASTLED_TEENSYLC) && defined(ARM_HARDWARE_SPI)
|
||||
|
||||
#define DECLARE_SPI0(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
|
||||
class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40076000> {};
|
||||
#define DECLARE_SPI1(__DATA,__CLOCK) template<uint32_t SPI_SPEED>\
|
||||
class SPIOutput<__DATA, __CLOCK, SPI_SPEED> : public ARMHardwareSPIOutput<__DATA, __CLOCK, SPI_SPEED, 0x40077000> {};
|
||||
|
||||
DECLARE_SPI0(7,13);
|
||||
DECLARE_SPI0(8,13);
|
||||
DECLARE_SPI0(11,13);
|
||||
DECLARE_SPI0(12,13);
|
||||
DECLARE_SPI0(7,14);
|
||||
DECLARE_SPI0(8,14);
|
||||
DECLARE_SPI0(11,14);
|
||||
DECLARE_SPI0(12,14);
|
||||
DECLARE_SPI1(0,20);
|
||||
DECLARE_SPI1(1,20);
|
||||
DECLARE_SPI1(21,20);
|
||||
|
||||
#elif defined(__SAM3X8E__)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public SAMHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
|
||||
|
||||
#elif defined(AVR_HARDWARE_SPI)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> {};
|
||||
|
||||
#if defined(SPI_UART0_DATA)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> : public AVRUSART0SPIOutput<SPI_UART0_DATA, SPI_UART0_CLOCK, SPI_SPEED> {};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(SPI_UART1_DATA)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> : public AVRUSART1SPIOutput<SPI_UART1_DATA, SPI_UART1_CLOCK, SPI_SPEED> {};
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(ARDUNIO_CORE_SPI)
|
||||
|
||||
template<uint32_t SPI_SPEED>
|
||||
class SPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED> : public ArdunioCoreSPIOutput<SPI_DATA, SPI_CLOCK, SPI_SPEED, SPI> {};
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI) && !defined(ESP32)
|
||||
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
|
||||
# pragma message "No hardware SPI pins defined. All SPI access will default to bitbanged output"
|
||||
# else
|
||||
# warning "No hardware SPI pins defined. All SPI access will default to bitbanged output"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// #if defined(USART_DATA) && defined(USART_CLOCK)
|
||||
// template<uint32_t SPI_SPEED>
|
||||
// class AVRSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, SPI_SPEED> {};
|
||||
// #endif
|
||||
|
||||
#else
|
||||
# if !defined(FASTLED_INTERNAL) && !defined(FASTLED_ALL_PINS_HARDWARE_SPI)
|
||||
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
|
||||
# pragma message "Forcing software SPI - no hardware SPI for you!"
|
||||
# else
|
||||
# warning "Forcing software SPI - no hardware SPI for you!"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif // !defined(FASTLED_STUB_IMPL)
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
421
FastLED/src/fastspi_bitbang.h
Normal file
421
FastLED/src/fastspi_bitbang.h
Normal file
@@ -0,0 +1,421 @@
|
||||
/// @file fastspi_bitbang.h
|
||||
/// Software SPI (aka bit-banging) support
|
||||
|
||||
#ifndef __INC_FASTSPI_BITBANG_H
|
||||
#define __INC_FASTSPI_BITBANG_H
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "fastled_delay.h"
|
||||
#include "fl/force_inline.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// Software SPI (aka bit-banging) support
|
||||
/// Includes aggressive optimizations for when the clock and data pin are on the same port.
|
||||
/// @tparam DATA_PIN pin number of the SPI data pin.
|
||||
/// @tparam CLOCK_PIN pin number of the SPI clock pin.
|
||||
/// @tparam SPI_SPEED speed of the bus. Determines the delay times between pin writes.
|
||||
/// @note Although this is named with the "AVR" prefix, this will work on any platform. Theoretically.
|
||||
/// @todo Replace the select pin definition with a set of pins, to allow using mux hardware for routing in the future.
|
||||
template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
|
||||
class AVRSoftwareSPIOutput {
|
||||
// The data types for pointers to the pin port - typedef'd here from the ::Pin definition because on AVR these
|
||||
// are pointers to 8 bit values, while on ARM they are 32 bit
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<CLOCK_PIN>::port_ptr_t clock_ptr_t;
|
||||
|
||||
// The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
|
||||
// are 8 bits wide while on arm they are 32.
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
typedef typename FastPin<CLOCK_PIN>::port_t clock_t;
|
||||
Selectable *m_pSelect; ///< SPI chip select
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
AVRSoftwareSPIOutput() { m_pSelect = NULL; }
|
||||
/// Constructor with selectable for SPI chip select
|
||||
AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
/// Set the pointer for the SPI chip select
|
||||
/// @param pSelect pointer to chip select control
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
/// Set the clock/data pins to output and make sure the chip select is released.
|
||||
void init() {
|
||||
// set the pins to output and make sure the select is released (which apparently means hi? This is a bit
|
||||
// confusing to me)
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
FastPin<CLOCK_PIN>::setOutput();
|
||||
release();
|
||||
}
|
||||
|
||||
/// Stop the SPI output.
|
||||
/// Pretty much a NOP with software, as there's no registers to kick
|
||||
static void stop() { }
|
||||
|
||||
/// Wait until the SPI subsystem is ready for more data to write.
|
||||
/// A NOP when bitbanging.
|
||||
static void wait() __attribute__((always_inline)) { }
|
||||
/// @copydoc AVRSoftwareSPIOutput::wait()
|
||||
static void waitFully() __attribute__((always_inline)) { wait(); }
|
||||
|
||||
/// Write a single byte over SPI without waiting.
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
|
||||
/// Write a single byte over SPI and wait afterwards.
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
|
||||
|
||||
/// Write a word (two bytes) over SPI.
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
||||
|
||||
/// Write a single byte over SPI.
|
||||
/// Naive implelentation, simply calls writeBit() on the 8 bits in the byte.
|
||||
static void writeByte(uint8_t b) {
|
||||
writeBit<7>(b);
|
||||
writeBit<6>(b);
|
||||
writeBit<5>(b);
|
||||
writeBit<4>(b);
|
||||
writeBit<3>(b);
|
||||
writeBit<2>(b);
|
||||
writeBit<1>(b);
|
||||
writeBit<0>(b);
|
||||
}
|
||||
|
||||
private:
|
||||
/// writeByte() implementation with data/clock registers passed in.
|
||||
static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
|
||||
writeBit<7>(b, clockpin, datapin);
|
||||
writeBit<6>(b, clockpin, datapin);
|
||||
writeBit<5>(b, clockpin, datapin);
|
||||
writeBit<4>(b, clockpin, datapin);
|
||||
writeBit<3>(b, clockpin, datapin);
|
||||
writeBit<2>(b, clockpin, datapin);
|
||||
writeBit<1>(b, clockpin, datapin);
|
||||
writeBit<0>(b, clockpin, datapin);
|
||||
}
|
||||
|
||||
/// writeByte() implementation with the data register passed in and prebaked values for data hi w/clock hi and
|
||||
/// low and data lo w/clock hi and lo. This is to be used when clock and data are on the same GPIO register,
|
||||
/// can get close to getting a bit out the door in 2 clock cycles!
|
||||
static void writeByte(uint8_t b, data_ptr_t datapin,
|
||||
data_t hival, data_t loval,
|
||||
clock_t hiclock, clock_t loclock) {
|
||||
writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
|
||||
}
|
||||
|
||||
/// writeByte() implementation with not just registers passed in, but pre-baked values for said registers for
|
||||
/// data hi/lo and clock hi/lo values.
|
||||
/// @note Weird things will happen if this method is called in cases where
|
||||
/// the data and clock pins are on the same port! Don't do that!
|
||||
static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
|
||||
data_t hival, data_t loval,
|
||||
clock_t hiclock, clock_t loclock) {
|
||||
writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
#if defined(FASTLED_TEENSY4)
|
||||
#define DELAY_NS (1000 / (SPI_SPEED/1000000))
|
||||
#define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
|
||||
#define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
|
||||
#else
|
||||
/// We want to make sure that the clock pulse is held high for a minimum of 35 ns.
|
||||
#define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1)
|
||||
|
||||
/// Delay for the clock signal 'high' period
|
||||
#define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0);
|
||||
/// Delay for the clock signal 'low' period
|
||||
#define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0);
|
||||
#endif
|
||||
|
||||
/// Write the BIT'th bit out via SPI, setting the data pin then strobing the clock
|
||||
/// @tparam BIT the bit index in the byte
|
||||
/// @param b the byte to read the bit from
|
||||
template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
|
||||
//cli();
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<DATA_PIN>::hi();
|
||||
#ifdef ESP32
|
||||
// try to ensure we never have adjacent write opcodes to the same register
|
||||
FastPin<CLOCK_PIN>::lo();
|
||||
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::toggle(); CLOCK_LO_DELAY;
|
||||
#else
|
||||
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
|
||||
#endif
|
||||
} else {
|
||||
FastPin<DATA_PIN>::lo();
|
||||
FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
|
||||
#ifdef ESP32
|
||||
// try to ensure we never have adjacent write opcodes to the same register
|
||||
FastPin<CLOCK_PIN>::toggle(); CLOCK_HI_DELAY;
|
||||
#else
|
||||
FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
|
||||
#endif
|
||||
}
|
||||
//sei();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Write the BIT'th bit out via SPI, setting the data pin then strobing the clock, using the passed in pin registers to accelerate access if needed
|
||||
template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<DATA_PIN>::hi(datapin);
|
||||
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
|
||||
} else {
|
||||
FastPin<DATA_PIN>::lo(datapin);
|
||||
FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The version of writeBit() to use when clock and data are on separate pins with precomputed values for setting
|
||||
/// the clock and data pins
|
||||
template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
|
||||
data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
|
||||
// // only need to explicitly set clock hi if clock and data are on different ports
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<DATA_PIN>::fastset(datapin, hival);
|
||||
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
|
||||
} else {
|
||||
// FL_NOP;
|
||||
FastPin<DATA_PIN>::fastset(datapin, loval);
|
||||
FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
|
||||
FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/// The version of writeBit() to use when clock and data are on the same port with precomputed values for the various
|
||||
/// combinations
|
||||
template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, data_ptr_t clockdatapin,
|
||||
data_t datahiclockhi, data_t dataloclockhi,
|
||||
data_t datahiclocklo, data_t dataloclocklo) {
|
||||
#if 0
|
||||
writeBit<BIT>(b);
|
||||
#else
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
|
||||
} else {
|
||||
// FL_NOP;
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
|
||||
FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Select the SPI output (chip select)
|
||||
/// @todo Research whether this really means 'hi' or 'lo'.
|
||||
/// @par
|
||||
/// @todo Move select responsibility out of the SPI classes entirely,
|
||||
/// make it up to the caller to remember to lock/select the line?
|
||||
void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
|
||||
|
||||
/// Release the SPI chip select line
|
||||
void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
|
||||
|
||||
/// Write multiple bytes of the given value over SPI.
|
||||
/// Useful for quickly flushing, say, a line of 0's down the line.
|
||||
/// @param value the value to write to the bus
|
||||
/// @param len how many copies of the value to write
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
writeBytesValueRaw(value, len);
|
||||
release();
|
||||
}
|
||||
|
||||
/// Write multiple bytes of the given value over SPI, without selecting the interface.
|
||||
/// @copydetails AVRSoftwareSPIOutput::writeBytesValue(uint8_t, int)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
|
||||
// TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
|
||||
// to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
|
||||
// back to the degenerative code below
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
#else
|
||||
FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
|
||||
|
||||
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
|
||||
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
|
||||
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
|
||||
FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
|
||||
FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
|
||||
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
|
||||
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
|
||||
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
|
||||
while(len--) {
|
||||
writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
}
|
||||
|
||||
} else {
|
||||
// If data and clock are on the same port then we can combine setting the data and clock pins
|
||||
FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
|
||||
|
||||
while(len--) {
|
||||
writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Write an array of data to the SPI interface.
|
||||
/// @tparam D Per-byte modifier class, e.g. ::DATA_NOP
|
||||
/// @param data pointer to data to write
|
||||
/// @param len number of bytes to write
|
||||
/// @todo Need to type this better so that explicit casts into the call aren't required.
|
||||
template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
|
||||
select();
|
||||
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
|
||||
uint8_t *end = data + len;
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
#else
|
||||
FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
|
||||
FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
|
||||
|
||||
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
|
||||
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
|
||||
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
|
||||
FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
|
||||
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
|
||||
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
|
||||
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
|
||||
uint8_t *end = data + len;
|
||||
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
}
|
||||
|
||||
} else {
|
||||
// FastPin<CLOCK_PIN>::hi();
|
||||
// If data and clock are on the same port then we can combine setting the data and clock pins
|
||||
FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
|
||||
|
||||
uint8_t *end = data + len;
|
||||
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
}
|
||||
// FastPin<CLOCK_PIN>::lo();
|
||||
}
|
||||
#endif
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
/// Write an array of data to the SPI interface.
|
||||
/// @param data pointer to data to write
|
||||
/// @param len number of bytes to write
|
||||
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
|
||||
/// Write LED pixel data to the SPI interface.
|
||||
/// Data is written in groups of three, re-ordered per the RGB_ORDER.
|
||||
/// @tparam FLAGS Option flags, such as ::FLAG_START_BIT
|
||||
/// @tparam D Per-byte modifier class, e.g. ::DATA_NOP
|
||||
/// @tparam RGB_ORDER the rgb ordering for the LED data (e.g. what order red, green, and blue data is written out in)
|
||||
/// @param pixels a ::PixelController with the LED data and modifier options
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
FASTLED_UNUSED(context);
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
|
||||
// If interrupts or other things may be generating output while we're working on things, then we need
|
||||
// to use this block
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
#else
|
||||
// If we can guaruntee that no one else will be writing data while we are running (namely, changing the values of the PORT/PDOR pins)
|
||||
// then we can use a bunch of optimizations in here
|
||||
FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
|
||||
|
||||
if(FastPin<DATA_PIN>::port() != FastPin<CLOCK_PIN>::port()) {
|
||||
FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
|
||||
// If data and clock are on different ports, then writing a bit will consist of writing the value foor
|
||||
// the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
|
||||
FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
|
||||
FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
|
||||
FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
|
||||
FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
|
||||
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
|
||||
} else {
|
||||
// If data and clock are on the same port then we can combine setting the data and clock pins
|
||||
FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
|
||||
FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
|
||||
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
3
FastLED/src/fastspi_dma.h
Normal file
3
FastLED/src/fastspi_dma.h
Normal file
@@ -0,0 +1,3 @@
|
||||
/// @file fastspi_dma.h
|
||||
/// Direct memory access (DMA) functions for SPI interfaces
|
||||
/// @deprecated This header file is empty.
|
||||
71
FastLED/src/fastspi_nop.h
Normal file
71
FastLED/src/fastspi_nop.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/// @file fastspi_nop.h
|
||||
/// Example of a NOP/stub class to show the SPI methods required by a chipset implementation
|
||||
/// @note Example for developers. Not a functional part of the library.
|
||||
|
||||
#ifndef __INC_FASTSPI_NOP_H
|
||||
#define __INC_FASTSPI_NOP_H
|
||||
|
||||
#if FASTLED_DOXYGEN // Guard against the arduino ide idiotically including every header file
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
|
||||
/// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
|
||||
/// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NOPSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
public:
|
||||
/// Default Constructor
|
||||
NOPSPIOutput() { m_pSelect = NULL; }
|
||||
|
||||
/// Constructor with selectable
|
||||
NOPSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
/// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
/// initialize the SPI subssytem
|
||||
void init() { /* TODO */ }
|
||||
|
||||
/// latch the CS select
|
||||
void select() { /* TODO */ }
|
||||
|
||||
/// release the CS select
|
||||
void release() { /* TODO */ }
|
||||
|
||||
/// wait until all queued up data has been written
|
||||
void waitFully();
|
||||
|
||||
/// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
|
||||
|
||||
/// write a byte out via SPI (returns immediately on writing register)
|
||||
void writeByte(uint8_t b) { /* TODO */ }
|
||||
/// write a word out via SPI (returns immediately on writing register)
|
||||
void writeWord(uint16_t w) { /* TODO */ }
|
||||
|
||||
/// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) { /* TODO */ }
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) { /* TODO */ }
|
||||
|
||||
/// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
void writeBytes(uint8_t *data, int len) { /* TODO */ }
|
||||
|
||||
/// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
|
||||
|
||||
/// write out pixel data from the given PixelController object
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) { /* TODO */ }
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
#endif
|
||||
103
FastLED/src/fastspi_ref.h
Normal file
103
FastLED/src/fastspi_ref.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/// @file fastspi_ref.h
|
||||
/// Example of a hardware SPI support class.
|
||||
/// @note Example for developers. Not a functional part of the library.
|
||||
|
||||
#ifndef __INC_FASTSPI_ARM_SAM_H
|
||||
#define __INC_FASTSPI_ARM_SAM_H
|
||||
|
||||
#if FASTLED_DOXYGEN // guard against the arduino ide idiotically including every header file
|
||||
#include "FastLED.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// A skeletal implementation of hardware SPI support. Fill in the necessary code for init, waiting, and writing. The rest of
|
||||
/// the method implementations should provide a starting point, even if they're not the most efficient to start with
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class REFHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
public:
|
||||
/// Default Constructor
|
||||
SAMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
|
||||
/// Constructor with selectable
|
||||
SAMHArdwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
/// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
/// initialize the SPI subssytem
|
||||
void init() { /* TODO */ }
|
||||
|
||||
/// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
|
||||
|
||||
/// release the CS select
|
||||
void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
|
||||
|
||||
/// wait until all queued up data has been written
|
||||
static void waitFully() { /* TODO */ }
|
||||
|
||||
/// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) { /* TODO */ }
|
||||
|
||||
/// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) { /* TODO */ }
|
||||
|
||||
/// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select(); writeBytesValueRaw(value, len); release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
/// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
|
||||
|
||||
/// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
/// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
|
||||
select();
|
||||
while(data != end) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
data += (3+skip);
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
87
FastLED/src/fastspi_types.h
Normal file
87
FastLED/src/fastspi_types.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/// @file fastspi_types.h
|
||||
/// Data types and constants used by SPI interfaces
|
||||
|
||||
#ifndef __INC_FASTSPI_TYPES_H
|
||||
#define __INC_FASTSPI_TYPES_H
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
/// @name Byte Re-Order Macros
|
||||
/// Some helper macros for getting at mis-ordered byte values.
|
||||
/// @todo Unused. Remove?
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Get SPI byte 0 offset
|
||||
#define SPI_B0 (RGB_BYTE0(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
|
||||
/// Get SPI byte 1 offset
|
||||
#define SPI_B1 (RGB_BYTE1(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
|
||||
/// Get SPI byte 2 offset
|
||||
#define SPI_B2 (RGB_BYTE2(RGB_ORDER) + (MASK_SKIP_BITS & SKIP))
|
||||
/// Advance SPI data pointer
|
||||
#define SPI_ADVANCE (3 + (MASK_SKIP_BITS & SKIP))
|
||||
/// @}
|
||||
|
||||
/// Dummy class for output controllers that need no data transformations.
|
||||
/// Some of the SPI controllers will need to perform a transform on each byte before doing
|
||||
/// anything with it. Creating a class of this form and passing it in as a template parameter to
|
||||
/// writeBytes()/writeBytes3() will ensure that the body of this method will get called on every
|
||||
/// byte worked on.
|
||||
/// @note Recommendation: make the adjust method aggressively inlined.
|
||||
/// @todo Convinience macro for building these
|
||||
class DATA_NOP {
|
||||
public:
|
||||
/// Hook called to adjust a byte of data before writing it to the output.
|
||||
/// In this dummy version, no adjustment is made.
|
||||
static FASTLED_FORCE_INLINE uint8_t adjust(FASTLED_REGISTER uint8_t data) { return data; }
|
||||
|
||||
/// @copybrief adjust(FASTLED_REGISTER uint8_t)
|
||||
/// @param data input byte
|
||||
/// @param scale scale value
|
||||
/// @returns input byte rescaled using ::scale8(uint8_t, uint8_t)
|
||||
static FASTLED_FORCE_INLINE uint8_t adjust(FASTLED_REGISTER uint8_t data, FASTLED_REGISTER uint8_t scale) { return scale8(data, scale); }
|
||||
|
||||
/// Hook called after a block of data is written to the output.
|
||||
/// In this dummy version, no action is performed.
|
||||
static FASTLED_FORCE_INLINE void postBlock(int /* len */, void* context = NULL) {
|
||||
FASTLED_UNUSED(context);
|
||||
}
|
||||
};
|
||||
|
||||
/// Flag for the start of an SPI transaction
|
||||
#define FLAG_START_BIT 0x80
|
||||
|
||||
/// Bitmask for the lower 6 bits of a byte
|
||||
/// @todo Unused. Remove?
|
||||
#define MASK_SKIP_BITS 0x3F
|
||||
|
||||
/// @name Clock speed dividers
|
||||
/// @{
|
||||
|
||||
/// Divisor for clock speed by 2
|
||||
#define SPEED_DIV_2 2
|
||||
/// Divisor for clock speed by 4
|
||||
#define SPEED_DIV_4 4
|
||||
/// Divisor for clock speed by 8
|
||||
#define SPEED_DIV_8 8
|
||||
/// Divisor for clock speed by 16
|
||||
#define SPEED_DIV_16 16
|
||||
/// Divisor for clock speed by 32
|
||||
#define SPEED_DIV_32 32
|
||||
/// Divisor for clock speed by 64
|
||||
#define SPEED_DIV_64 64
|
||||
/// Divisor for clock speed by 128
|
||||
#define SPEED_DIV_128 128
|
||||
/// @}
|
||||
|
||||
/// Max SPI data rate
|
||||
/// @todo Unused. Remove?
|
||||
#define MAX_DATA_RATE 0
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
5
FastLED/src/file_system.h
Normal file
5
FastLED/src/file_system.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#warning "This header is deprecated. Please use file_system.h, this header will go away in the version 4.0."
|
||||
|
||||
#include "fl/file_system.h"
|
||||
12
FastLED/src/fl/_readme
Normal file
12
FastLED/src/fl/_readme
Normal file
@@ -0,0 +1,12 @@
|
||||
This directory holds core functionality of FastLED.
|
||||
|
||||
Every class/struct/function in this directory must be under the
|
||||
namespace fl, except for special cases.
|
||||
|
||||
This is done because according to the the Arduino build system,
|
||||
all headers/cpp/hpp files in the root the src directory will be in global
|
||||
scope and other libraries can #include them by mistake.
|
||||
|
||||
This has happened so many times that I've just gone ahead and migrated all
|
||||
the new code to this directory, to prevent name collisions.
|
||||
|
||||
55
FastLED/src/fl/allocator.cpp
Normal file
55
FastLED/src/fl/allocator.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fl/allocator.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
#ifdef ESP32
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_system.h"
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef ESP32
|
||||
// On esp32, attempt to always allocate in psram first.
|
||||
void *DefaultAlloc(size_t size) {
|
||||
void *out = heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
|
||||
if (out == nullptr) {
|
||||
// Fallback to default allocator.
|
||||
out = heap_caps_malloc(size, MALLOC_CAP_DEFAULT);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
void DefaultFree(void *ptr) { heap_caps_free(ptr); }
|
||||
#else
|
||||
void *DefaultAlloc(size_t size) { return malloc(size); }
|
||||
void DefaultFree(void *ptr) { free(ptr); }
|
||||
#endif
|
||||
|
||||
void *(*Alloc)(size_t) = DefaultAlloc;
|
||||
void (*Free)(void *) = DefaultFree;
|
||||
} // namespace
|
||||
|
||||
void SetLargeBlockAllocator(void *(*alloc)(size_t), void (*free)(void *)) {
|
||||
Alloc = alloc;
|
||||
Free = free;
|
||||
}
|
||||
|
||||
void* LargeBlockAllocate(size_t size, bool zero) {
|
||||
void* ptr = Alloc(size);
|
||||
if (zero) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void LargeBlockDeallocate(void* ptr) {
|
||||
Free(ptr);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
|
||||
31
FastLED/src/fl/allocator.h
Normal file
31
FastLED/src/fl/allocator.h
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
void SetLargeBlockAllocator(void *(*alloc)(size_t), void (*free)(void *));
|
||||
void* LargeBlockAllocate(size_t size, bool zero = true);
|
||||
void LargeBlockDeallocate(void* ptr);
|
||||
|
||||
template<typename T>
|
||||
class LargeBlockAllocator {
|
||||
public:
|
||||
static T* Alloc(size_t n) {
|
||||
void* ptr = LargeBlockAllocate(sizeof(T) * n, true);
|
||||
return reinterpret_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
static void Free(T* p) {
|
||||
if (p == nullptr) {
|
||||
return;
|
||||
}
|
||||
LargeBlockDeallocate(p);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
25
FastLED/src/fl/assert.h
Normal file
25
FastLED/src/fl/assert.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include "fl/warn.h"
|
||||
#include "fl/strstream.h"
|
||||
|
||||
#ifndef DEBUG
|
||||
#define FASTLED_ASSERT(x, MSG) FASTLED_WARN_IF(!(x), MSG)
|
||||
#else
|
||||
|
||||
#ifdef ESP32
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#define FASTLED_ASSERT(x, MSG) \
|
||||
{ \
|
||||
if (!(x)) { \
|
||||
ESP_LOGE("#### FastLED", "%s", (fl::StrStream() << MSG).c_str()); \
|
||||
ESP_ERROR_CHECK(ESP_FAIL); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define FASTLED_ASSERT(x, MSG) FASTLED_WARN_IF(!(x), MSG)
|
||||
#endif
|
||||
#endif
|
||||
72
FastLED/src/fl/bilinear_compression.cpp
Normal file
72
FastLED/src/fl/bilinear_compression.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
#include "bilinear_compression.h"
|
||||
|
||||
#include "crgb.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
void downscaleBilinear(const CRGB *src, uint16_t srcWidth, uint16_t srcHeight,
|
||||
CRGB *dst, uint16_t dstWidth, uint16_t dstHeight) {
|
||||
// Use 8 bits for fixed-point fractional precision.
|
||||
const uint16_t SHIFT = 8;
|
||||
const uint16_t FP_ONE = 1 << SHIFT; // 256 in fixed-point
|
||||
const uint16_t WEIGHT_SHIFT =
|
||||
SHIFT * 2; // 16 bits (for the product of two fixed-point numbers)
|
||||
const uint16_t rounding = 1 << (WEIGHT_SHIFT - 1);
|
||||
|
||||
// Compute scale factors in fixed-point: factor = (srcDimension << SHIFT) /
|
||||
// dstDimension.
|
||||
uint16_t scaleX = (srcWidth << SHIFT) / dstWidth;
|
||||
uint16_t scaleY = (srcHeight << SHIFT) / dstHeight;
|
||||
|
||||
// Loop over each pixel in the destination image.
|
||||
for (int y = 0; y < dstHeight; ++y) {
|
||||
// Compute the corresponding source y coordinate in fixed-point.
|
||||
int srcYFixed = y * scaleY;
|
||||
int y0 = srcYFixed >> SHIFT; // integer part for row index
|
||||
int yFrac = srcYFixed & (FP_ONE - 1); // fractional part
|
||||
int y1 =
|
||||
MIN(y0 + 1, srcHeight - 1); // ensure we don't exceed bounds
|
||||
|
||||
for (int x = 0; x < dstWidth; ++x) {
|
||||
// Compute the corresponding source x coordinate in fixed-point.
|
||||
int srcXFixed = x * scaleX;
|
||||
int x0 = srcXFixed >> SHIFT; // integer part for column index
|
||||
int xFrac = srcXFixed & (FP_ONE - 1); // fractional part
|
||||
int x1 = MIN(x0 + 1, srcWidth - 1);
|
||||
|
||||
// Compute the weights for the four neighboring pixels.
|
||||
int w00 = (FP_ONE - xFrac) * (FP_ONE - yFrac);
|
||||
int w10 = xFrac * (FP_ONE - yFrac);
|
||||
int w01 = (FP_ONE - xFrac) * yFrac;
|
||||
int w11 = xFrac * yFrac;
|
||||
|
||||
// Apply fixed-point weighted sum for each color channel.
|
||||
int r = (w00 * src[y0 * srcWidth + x0].r +
|
||||
w10 * src[y0 * srcWidth + x1].r +
|
||||
w01 * src[y1 * srcWidth + x0].r +
|
||||
w11 * src[y1 * srcWidth + x1].r + rounding) >>
|
||||
WEIGHT_SHIFT;
|
||||
|
||||
int g = (w00 * src[y0 * srcWidth + x0].g +
|
||||
w10 * src[y0 * srcWidth + x1].g +
|
||||
w01 * src[y1 * srcWidth + x0].g +
|
||||
w11 * src[y1 * srcWidth + x1].g + rounding) >>
|
||||
WEIGHT_SHIFT;
|
||||
|
||||
int b = (w00 * src[y0 * srcWidth + x0].b +
|
||||
w10 * src[y0 * srcWidth + x1].b +
|
||||
w01 * src[y1 * srcWidth + x0].b +
|
||||
w11 * src[y1 * srcWidth + x1].b + rounding) >>
|
||||
WEIGHT_SHIFT;
|
||||
|
||||
// Store the computed pixel in the destination image.
|
||||
dst[y * dstWidth + x] = CRGB(static_cast<unsigned char>(r),
|
||||
static_cast<unsigned char>(g),
|
||||
static_cast<unsigned char>(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
17
FastLED/src/fl/bilinear_compression.h
Normal file
17
FastLED/src/fl/bilinear_compression.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/*
|
||||
Experimental bilinearn downscaling algorithm. Not tested yet and completely
|
||||
"vibe-coded" by ai.
|
||||
|
||||
If you use this and find an issue then please report it.
|
||||
*/
|
||||
|
||||
|
||||
#include "crgb.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
void downscaleBilinear(const CRGB *src, uint16_t srcWidth, uint16_t srcHeight,
|
||||
CRGB *dst, uint16_t dstWidth, uint16_t dstHeight);
|
||||
|
||||
} // namespace fl
|
||||
273
FastLED/src/fl/bilinear_expansion.cpp
Normal file
273
FastLED/src/fl/bilinear_expansion.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/// @file bilinear_expansion.cpp
|
||||
/// @brief Demonstrates how to mix noise generation with color palettes on a
|
||||
/// 2D LED matrix
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bilinear_expansion.h"
|
||||
#include "crgb.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/xymap.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01,
|
||||
uint8_t v11, uint16_t dx, uint16_t dy);
|
||||
|
||||
uint8_t bilinearInterpolatePowerOf2(uint8_t v00, uint8_t v10, uint8_t v01,
|
||||
uint8_t v11, uint8_t dx, uint8_t dy);
|
||||
|
||||
|
||||
void bilinearExpandArbitrary(const CRGB *input, CRGB *output, uint16_t inputWidth,
|
||||
uint16_t inputHeight, XYMap xyMap) {
|
||||
uint16_t n = xyMap.getTotal();
|
||||
uint16_t outputWidth = xyMap.getWidth();
|
||||
uint16_t outputHeight = xyMap.getHeight();
|
||||
const uint16_t scale_factor = 256; // Using 8 bits for the fractional part
|
||||
|
||||
for (uint16_t y = 0; y < outputHeight; y++) {
|
||||
for (uint16_t x = 0; x < outputWidth; x++) {
|
||||
// Calculate the corresponding position in the input grid
|
||||
uint32_t fx = ((uint32_t)x * (inputWidth - 1) * scale_factor) /
|
||||
(outputWidth - 1);
|
||||
uint32_t fy = ((uint32_t)y * (inputHeight - 1) * scale_factor) /
|
||||
(outputHeight - 1);
|
||||
|
||||
uint16_t ix = fx / scale_factor; // Integer part of x
|
||||
uint16_t iy = fy / scale_factor; // Integer part of y
|
||||
uint16_t dx = fx % scale_factor; // Fractional part of x
|
||||
uint16_t dy = fy % scale_factor; // Fractional part of y
|
||||
|
||||
uint16_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
|
||||
uint16_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
|
||||
|
||||
uint16_t i00 = iy * inputWidth + ix;
|
||||
uint16_t i10 = iy * inputWidth + ix1;
|
||||
uint16_t i01 = iy1 * inputWidth + ix;
|
||||
uint16_t i11 = iy1 * inputWidth + ix1;
|
||||
|
||||
CRGB c00 = input[i00];
|
||||
CRGB c10 = input[i10];
|
||||
CRGB c01 = input[i01];
|
||||
CRGB c11 = input[i11];
|
||||
|
||||
CRGB result;
|
||||
result.r = bilinearInterpolate(c00.r, c10.r, c01.r, c11.r, dx, dy);
|
||||
result.g = bilinearInterpolate(c00.g, c10.g, c01.g, c11.g, dx, dy);
|
||||
result.b = bilinearInterpolate(c00.b, c10.b, c01.b, c11.b, dx, dy);
|
||||
|
||||
uint16_t idx = xyMap.mapToIndex(x, y);
|
||||
if (idx < n) {
|
||||
output[idx] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01, uint8_t v11,
|
||||
uint16_t dx, uint16_t dy) {
|
||||
uint16_t dx_inv = 256 - dx;
|
||||
uint16_t dy_inv = 256 - dy;
|
||||
|
||||
uint32_t w00 = (uint32_t)dx_inv * dy_inv;
|
||||
uint32_t w10 = (uint32_t)dx * dy_inv;
|
||||
uint32_t w01 = (uint32_t)dx_inv * dy;
|
||||
uint32_t w11 = (uint32_t)dx * dy;
|
||||
|
||||
uint32_t sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
|
||||
|
||||
// Normalize the result by dividing by 65536 (shift right by 16 bits),
|
||||
// with rounding
|
||||
uint8_t result = (uint8_t)((sum + 32768) >> 16);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void bilinearExpandPowerOf2(const CRGB *input, CRGB *output, uint8_t inputWidth, uint8_t inputHeight, XYMap xyMap) {
|
||||
uint8_t width = xyMap.getWidth();
|
||||
uint8_t height = xyMap.getHeight();
|
||||
if (width != xyMap.getWidth() || height != xyMap.getHeight()) {
|
||||
// xyMap has width and height that do not fit in an uint16_t.
|
||||
return;
|
||||
}
|
||||
uint16_t n = xyMap.getTotal();
|
||||
|
||||
for (uint8_t y = 0; y < height; y++) {
|
||||
for (uint8_t x = 0; x < width; x++) {
|
||||
// Use 8-bit fixed-point arithmetic with 8 fractional bits
|
||||
// (scale factor of 256)
|
||||
uint16_t fx = ((uint16_t)x * (inputWidth - 1) * 256) / (width - 1);
|
||||
uint16_t fy =
|
||||
((uint16_t)y * (inputHeight - 1) * 256) / (height - 1);
|
||||
|
||||
uint8_t ix = fx >> 8; // Integer part
|
||||
uint8_t iy = fy >> 8;
|
||||
uint8_t dx = fx & 0xFF; // Fractional part
|
||||
uint8_t dy = fy & 0xFF;
|
||||
|
||||
uint8_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
|
||||
uint8_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
|
||||
|
||||
uint16_t i00 = iy * inputWidth + ix;
|
||||
uint16_t i10 = iy * inputWidth + ix1;
|
||||
uint16_t i01 = iy1 * inputWidth + ix;
|
||||
uint16_t i11 = iy1 * inputWidth + ix1;
|
||||
|
||||
CRGB c00 = input[i00];
|
||||
CRGB c10 = input[i10];
|
||||
CRGB c01 = input[i01];
|
||||
CRGB c11 = input[i11];
|
||||
|
||||
CRGB result;
|
||||
result.r = bilinearInterpolatePowerOf2(c00.r, c10.r, c01.r, c11.r, dx, dy);
|
||||
result.g = bilinearInterpolatePowerOf2(c00.g, c10.g, c01.g, c11.g, dx, dy);
|
||||
result.b = bilinearInterpolatePowerOf2(c00.b, c10.b, c01.b, c11.b, dx, dy);
|
||||
|
||||
uint16_t idx = xyMap.mapToIndex(x, y);
|
||||
if (idx < n) {
|
||||
output[idx] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t bilinearInterpolatePowerOf2(uint8_t v00, uint8_t v10, uint8_t v01,
|
||||
uint8_t v11, uint8_t dx, uint8_t dy) {
|
||||
uint16_t dx_inv = 256 - dx; // 0 to 256
|
||||
uint16_t dy_inv = 256 - dy; // 0 to 256
|
||||
|
||||
// Scale down weights to fit into uint16_t
|
||||
uint16_t w00 = (dx_inv * dy_inv) >> 8; // Max value 256
|
||||
uint16_t w10 = (dx * dy_inv) >> 8;
|
||||
uint16_t w01 = (dx_inv * dy) >> 8;
|
||||
uint16_t w11 = (dx * dy) >> 8;
|
||||
|
||||
// Sum of weights should be approximately 256
|
||||
uint16_t weight_sum = w00 + w10 + w01 + w11;
|
||||
|
||||
// Compute the weighted sum of pixel values
|
||||
uint16_t sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
|
||||
|
||||
// Normalize the result
|
||||
uint8_t result = (sum + (weight_sum >> 1)) / weight_sum;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point version of bilinear interpolation
|
||||
uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
|
||||
uint8_t v11, float dx, float dy) {
|
||||
float dx_inv = 1.0f - dx;
|
||||
float dy_inv = 1.0f - dy;
|
||||
|
||||
// Calculate the weights for each corner
|
||||
float w00 = dx_inv * dy_inv;
|
||||
float w10 = dx * dy_inv;
|
||||
float w01 = dx_inv * dy;
|
||||
float w11 = dx * dy;
|
||||
|
||||
// Compute the weighted sum
|
||||
float sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
|
||||
|
||||
// Clamp the result to [0, 255] and round
|
||||
uint8_t result = static_cast<uint8_t>(sum + 0.5f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Floating-point version for arbitrary grid sizes
|
||||
void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
|
||||
uint16_t inputWidth, uint16_t inputHeight,
|
||||
XYMap xyMap) {
|
||||
uint16_t n = xyMap.getTotal();
|
||||
uint16_t outputWidth = xyMap.getWidth();
|
||||
uint16_t outputHeight = xyMap.getHeight();
|
||||
|
||||
for (uint16_t y = 0; y < outputHeight; y++) {
|
||||
for (uint16_t x = 0; x < outputWidth; x++) {
|
||||
// Map output pixel to input grid position
|
||||
float fx = static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
|
||||
float fy = static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
|
||||
|
||||
uint16_t ix = static_cast<uint16_t>(fx);
|
||||
uint16_t iy = static_cast<uint16_t>(fy);
|
||||
float dx = fx - ix;
|
||||
float dy = fy - iy;
|
||||
|
||||
uint16_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
|
||||
uint16_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
|
||||
|
||||
uint16_t i00 = iy * inputWidth + ix;
|
||||
uint16_t i10 = iy * inputWidth + ix1;
|
||||
uint16_t i01 = iy1 * inputWidth + ix;
|
||||
uint16_t i11 = iy1 * inputWidth + ix1;
|
||||
|
||||
CRGB c00 = input[i00];
|
||||
CRGB c10 = input[i10];
|
||||
CRGB c01 = input[i01];
|
||||
CRGB c11 = input[i11];
|
||||
|
||||
CRGB result;
|
||||
result.r = bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
|
||||
result.g = bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
|
||||
result.b = bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
|
||||
|
||||
uint16_t idx = xyMap.mapToIndex(x, y);
|
||||
if (idx < n) {
|
||||
output[idx] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Floating-point version for power-of-two grid sizes
|
||||
void bilinearExpandFloat(const CRGB *input, CRGB *output,
|
||||
uint8_t inputWidth, uint8_t inputHeight,
|
||||
XYMap xyMap) {
|
||||
uint8_t outputWidth = xyMap.getWidth();
|
||||
uint8_t outputHeight = xyMap.getHeight();
|
||||
if (outputWidth != xyMap.getWidth() || outputHeight != xyMap.getHeight()) {
|
||||
// xyMap has width and height that do not fit in a uint8_t.
|
||||
return;
|
||||
}
|
||||
uint16_t n = xyMap.getTotal();
|
||||
|
||||
for (uint8_t y = 0; y < outputHeight; y++) {
|
||||
for (uint8_t x = 0; x < outputWidth; x++) {
|
||||
// Map output pixel to input grid position
|
||||
float fx = static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
|
||||
float fy = static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
|
||||
|
||||
uint8_t ix = static_cast<uint8_t>(fx);
|
||||
uint8_t iy = static_cast<uint8_t>(fy);
|
||||
float dx = fx - ix;
|
||||
float dy = fy - iy;
|
||||
|
||||
uint8_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
|
||||
uint8_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
|
||||
|
||||
uint16_t i00 = iy * inputWidth + ix;
|
||||
uint16_t i10 = iy * inputWidth + ix1;
|
||||
uint16_t i01 = iy1 * inputWidth + ix;
|
||||
uint16_t i11 = iy1 * inputWidth + ix1;
|
||||
|
||||
CRGB c00 = input[i00];
|
||||
CRGB c10 = input[i10];
|
||||
CRGB c01 = input[i01];
|
||||
CRGB c11 = input[i11];
|
||||
|
||||
CRGB result;
|
||||
result.r = bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
|
||||
result.g = bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
|
||||
result.b = bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
|
||||
|
||||
uint16_t idx = xyMap.mapToIndex(x, y);
|
||||
if (idx < n) {
|
||||
output[idx] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
62
FastLED/src/fl/bilinear_expansion.h
Normal file
62
FastLED/src/fl/bilinear_expansion.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/// @file bilinear_expansion.h
|
||||
/// @brief Demonstrates how to mix noise generation with color palettes on a
|
||||
/// 2D LED matrix
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "crgb.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/xymap.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @brief Performs bilinear interpolation for upscaling an image.
|
||||
/// @param output The output grid to write into the interpolated values.
|
||||
/// @param input The input grid to read from.
|
||||
/// @param inputWidth The width of the input grid.
|
||||
/// @param inputHeight The height of the input grid.
|
||||
/// @param xyMap The XYMap to use to determine where to write the pixel. If the
|
||||
/// pixel is mapped outside of the range then it is clipped.
|
||||
void bilinearExpandArbitrary(const CRGB *input, CRGB *output,
|
||||
uint16_t inputWidth, uint16_t inputHeight,
|
||||
fl::XYMap xyMap);
|
||||
|
||||
/// @brief Performs bilinear interpolation for upscaling an image.
|
||||
/// @param output The output grid to write into the interpolated values.
|
||||
/// @param input The input grid to read from.
|
||||
/// @param inputWidth The width of the input grid.
|
||||
/// @param inputHeight The height of the input grid.
|
||||
/// @param xyMap The XYMap to use to determine where to write the pixel. If the
|
||||
/// pixel is mapped outside of the range then it is clipped.
|
||||
void bilinearExpandPowerOf2(const CRGB *input, CRGB *output, uint8_t inputWidth, uint8_t inputHeight, fl::XYMap xyMap);
|
||||
|
||||
//
|
||||
inline void bilinearExpand(const CRGB *input, CRGB *output, uint16_t inputWidth,
|
||||
uint16_t inputHeight, fl::XYMap xyMap) {
|
||||
uint16_t outputWidth = xyMap.getWidth();
|
||||
uint16_t outputHeight = xyMap.getHeight();
|
||||
const bool wontFit = (outputWidth != xyMap.getWidth() || outputHeight != xyMap.getHeight());
|
||||
// if the input dimensions are not a power of 2 then we can't use the
|
||||
// optimized version.
|
||||
if (wontFit || (inputWidth & (inputWidth - 1)) || (inputHeight & (inputHeight - 1))) {
|
||||
bilinearExpandArbitrary(input, output, inputWidth, inputHeight, xyMap);
|
||||
} else {
|
||||
bilinearExpandPowerOf2(input, output, inputWidth, inputHeight, xyMap);
|
||||
}
|
||||
}
|
||||
|
||||
void bilinearExpandFloat(const CRGB *input, CRGB *output,
|
||||
uint8_t inputWidth, uint8_t inputHeight,
|
||||
fl::XYMap xyMap);
|
||||
|
||||
void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
|
||||
uint16_t inputWidth, uint16_t inputHeight,
|
||||
fl::XYMap xyMap);
|
||||
|
||||
uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
|
||||
uint8_t v11, float dx, float dy);
|
||||
|
||||
} // namespace fl
|
||||
31
FastLED/src/fl/bytestream.h
Normal file
31
FastLED/src/fl/bytestream.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/ptr.h"
|
||||
|
||||
#include "crgb.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(ByteStream);
|
||||
|
||||
// An abstract class that represents a stream of bytes.
|
||||
class ByteStream : public fl::Referent {
|
||||
public:
|
||||
virtual ~ByteStream() {}
|
||||
virtual bool available(size_t) const = 0;
|
||||
virtual size_t read(uint8_t *dst, size_t bytesToRead) = 0;
|
||||
virtual const char *path() const = 0;
|
||||
virtual void close() {} // default is do nothing on close.
|
||||
// convenience functions
|
||||
size_t readCRGB(CRGB *dst, size_t n) {
|
||||
return read((uint8_t *)dst, n * 3) / 3;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace fl
|
||||
68
FastLED/src/fl/bytestreammemory.cpp
Normal file
68
FastLED/src/fl/bytestreammemory.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "fl/bytestreammemory.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
ByteStreamMemory::ByteStreamMemory(uint32_t size_buffer)
|
||||
: mReadBuffer(size_buffer) {}
|
||||
|
||||
ByteStreamMemory::~ByteStreamMemory() = default;
|
||||
|
||||
bool ByteStreamMemory::available(size_t n) const {
|
||||
return mReadBuffer.size() >= n;
|
||||
}
|
||||
|
||||
size_t ByteStreamMemory::read(uint8_t *dst, size_t bytesToRead) {
|
||||
if (!available(bytesToRead) || dst == nullptr) {
|
||||
FASTLED_WARN("ByteStreamMemory::read: !available(bytesToRead): " << bytesToRead << " mReadBuffer.size(): " << mReadBuffer.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t actualBytesToRead = MIN(bytesToRead, mReadBuffer.size());
|
||||
size_t bytesRead = 0;
|
||||
|
||||
while (bytesRead < actualBytesToRead) {
|
||||
uint8_t& b = dst[bytesRead];
|
||||
mReadBuffer.pop_front(&b);
|
||||
bytesRead++;
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
FASTLED_WARN("ByteStreamMemory::read: bytesRead == 0");
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
size_t ByteStreamMemory::write(const uint8_t* src, size_t n) {
|
||||
if (src == nullptr || mReadBuffer.capacity() == 0) {
|
||||
FASTLED_WARN_IF(src == nullptr, "ByteStreamMemory::write: src == nullptr");
|
||||
FASTLED_WARN_IF(mReadBuffer.capacity() == 0, "ByteStreamMemory::write: mReadBuffer.capacity() == 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (mReadBuffer.full()) {
|
||||
FASTLED_WARN("ByteStreamMemory::write: mReadBuffer.full(): " << mReadBuffer.size());
|
||||
break;
|
||||
}
|
||||
mReadBuffer.push_back(src[i]);
|
||||
++written;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
size_t ByteStreamMemory::writeCRGB(const CRGB* src, size_t n) {
|
||||
size_t bytes_written = write(reinterpret_cast<const uint8_t*>(src), n * 3);
|
||||
size_t pixels_written = bytes_written / 3;
|
||||
return pixels_written;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
34
FastLED/src/fl/bytestreammemory.h
Normal file
34
FastLED/src/fl/bytestreammemory.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/ptr.h"
|
||||
|
||||
#include "fl/bytestream.h"
|
||||
#include "fl/circular_buffer.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(ByteStreamMemory);
|
||||
|
||||
class ByteStreamMemory : public ByteStream {
|
||||
public:
|
||||
ByteStreamMemory(uint32_t size_buffer);
|
||||
~ByteStreamMemory() override;
|
||||
bool available(size_t n) const override;
|
||||
size_t read(uint8_t *dst, size_t bytesToRead) override;
|
||||
void clear() {
|
||||
mReadBuffer.clear();
|
||||
}
|
||||
const char *path() const override { return "ByteStreamMemory"; }
|
||||
size_t write(const uint8_t* src, size_t n);
|
||||
size_t writeCRGB(const CRGB* src, size_t n);
|
||||
|
||||
private:
|
||||
CircularBuffer<uint8_t> mReadBuffer;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fl
|
||||
36
FastLED/src/fl/callback.h
Normal file
36
FastLED/src/fl/callback.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
/// @brief A void returning callback that can be used to call a function with a variable number of arguments. If this binds
|
||||
/// to a non-static member function then self must be none null. If it's a free function then set self to
|
||||
/// nullptr.
|
||||
/// @tparam ...Args The types of the arguments to the function.
|
||||
/// @example Callback<const char*> callback(this, [](void* self, const char* str) { static_cast<MyClass*>(self)->myFunction(str); });
|
||||
template<typename ...Args>
|
||||
class Callback {
|
||||
public:
|
||||
Callback() = default;
|
||||
// Member function constructor.
|
||||
Callback(void* self, void (*callback)(void* self, Args... args)) : self(self), callback(callback) {}
|
||||
// Free function constructor.
|
||||
explicit Callback(void* (*callback)(Args... args)) : self(nullptr), callback((void (*)(void*, Args...))callback) {}
|
||||
Callback(const Callback&) = default;
|
||||
operator bool() const { return callback != nullptr; }
|
||||
void operator()(Args... args) const { if (callback) callback(self, args...); }
|
||||
void clear() { callback = nullptr; self = nullptr; }
|
||||
bool operator==(const Callback& other) const { return self == other.self && callback == other.callback; }
|
||||
bool operator!=(const Callback& other) const { return !(*this == other); }
|
||||
Callback& operator=(const Callback& other) { self = other.self; callback = other.callback; return *this; }
|
||||
Callback& operator=(void* (*other)(Args... args)) { self = nullptr; callback = (void (*)(void*, Args...))other; return *this; }
|
||||
Callback& operator=(void (*other)(void* self, Args... args)) { self = nullptr; callback = other; return *this; }
|
||||
// operator < for set/map
|
||||
bool operator<(const Callback& other) const { return self < other.self || (self == other.self && callback < other.callback); }
|
||||
private:
|
||||
void* self = nullptr;
|
||||
void (*callback)(void* self, Args... args) = nullptr;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
122
FastLED/src/fl/circular_buffer.h
Normal file
122
FastLED/src/fl/circular_buffer.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // For size_t
|
||||
#include <stdint.h> // For standard integer types
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/ptr.h" // Assuming this provides `scoped_array` or similar
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
template <typename T>
|
||||
class CircularBuffer {
|
||||
public:
|
||||
CircularBuffer(size_t capacity)
|
||||
: mCapacity(capacity + 1), mHead(0), mTail(0) { // Extra space for distinguishing full/empty
|
||||
mBuffer.reset(new T[mCapacity]);
|
||||
}
|
||||
|
||||
CircularBuffer(const CircularBuffer&) = delete;
|
||||
CircularBuffer& operator=(const CircularBuffer&) = delete;
|
||||
|
||||
bool push_back(const T& value) {
|
||||
if (full()) {
|
||||
mTail = increment(mTail); // Overwrite the oldest element
|
||||
}
|
||||
mBuffer[mHead] = value;
|
||||
mHead = increment(mHead);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop_front(T* dst = nullptr) {
|
||||
if (empty()) {
|
||||
return false;
|
||||
}
|
||||
if (dst) {
|
||||
*dst = mBuffer[mTail];
|
||||
}
|
||||
mTail = increment(mTail);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push_front(const T& value) {
|
||||
if (full()) {
|
||||
mHead = decrement(mHead); // Overwrite the oldest element
|
||||
}
|
||||
mTail = decrement(mTail);
|
||||
mBuffer[mTail] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop_back(T* dst = nullptr) {
|
||||
if (empty()) {
|
||||
return false;
|
||||
}
|
||||
mHead = decrement(mHead);
|
||||
if (dst) {
|
||||
*dst = mBuffer[mHead];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
T& front() {
|
||||
return mBuffer[mTail];
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return mBuffer[mTail];
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return mBuffer[(mHead + mCapacity - 1) % mCapacity];
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return mBuffer[(mHead + mCapacity - 1) % mCapacity];
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
return mBuffer[(mTail + index) % mCapacity];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
return mBuffer[(mTail + index) % mCapacity];
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return (mHead + mCapacity - mTail) % mCapacity;
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return mCapacity - 1;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return mHead == mTail;
|
||||
}
|
||||
|
||||
bool full() const {
|
||||
return increment(mHead) == mTail;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mHead = mTail = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t increment(size_t index) const {
|
||||
return (index + 1) % mCapacity;
|
||||
}
|
||||
|
||||
size_t decrement(size_t index) const {
|
||||
return (index + mCapacity - 1) % mCapacity;
|
||||
}
|
||||
|
||||
fl::scoped_array<T> mBuffer;
|
||||
size_t mCapacity;
|
||||
size_t mHead;
|
||||
size_t mTail;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
13
FastLED/src/fl/convert.h
Normal file
13
FastLED/src/fl/convert.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Conversion from FastLED timings to the type found in datasheets.
|
||||
inline void convert_fastled_timings_to_timedeltas(
|
||||
uint16_t T1, uint16_t T2, uint16_t T3,
|
||||
uint16_t* T0H, uint16_t* T0L, uint16_t* T1H, uint16_t* T1L) {
|
||||
*T0H = T1;
|
||||
*T0L = T2 + T3;
|
||||
*T1H = T1 + T2;
|
||||
*T1L = T3;
|
||||
}
|
||||
69
FastLED/src/fl/dbg.h
Normal file
69
FastLED/src/fl/dbg.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/strstream.h"
|
||||
|
||||
namespace fl {
|
||||
// ".build/src/fl/dbg.h" -> "src/fl/dbg.h"
|
||||
// "blah/blah/blah.h" -> "blah.h"
|
||||
inline const char* fastled_file_offset(const char* file) {
|
||||
const char* p = file;
|
||||
const char* last_slash = nullptr;
|
||||
|
||||
while (*p) {
|
||||
if (p[0] == 's' && p[1] == 'r' && p[2] == 'c' && p[3] == '/') {
|
||||
return p; // Skip past "src/"
|
||||
}
|
||||
if (*p == '/') { // fallback to using last slash
|
||||
last_slash = p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
// If "src/" not found but we found at least one slash, return after the last slash
|
||||
if (last_slash) {
|
||||
return last_slash + 1;
|
||||
}
|
||||
return file; // If no slashes found at all, return original path
|
||||
}
|
||||
} // namespace fl
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define FASTLED_DBG_USE_PRINTF 1
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_DBG_USE_PRINTF
|
||||
#if defined(DEBUG) && (defined(__IMXRT1062__) || defined(ESP32))
|
||||
#define FASTLED_DBG_USE_PRINTF 1
|
||||
#else
|
||||
#define FASTLED_DBG_USE_PRINTF 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if FASTLED_DBG_USE_PRINTF
|
||||
#define FASTLED_HAS_DBG 1
|
||||
#include <stdio.h> // ok include
|
||||
namespace fl {
|
||||
|
||||
} // namespace fl
|
||||
#define _FASTLED_DGB(X) \
|
||||
printf("%s", \
|
||||
(fl::StrStream() << \
|
||||
(fl::fastled_file_offset(__FILE__)) << "(" << __LINE__ << "): " << X << "\n") \
|
||||
.c_str())
|
||||
|
||||
#define FASTLED_DBG(X) _FASTLED_DGB(X)
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef FASTLED_HAS_DBG
|
||||
// FASTLED_DBG is a macro that can be defined to enable debug printing.
|
||||
#define FASTLED_DBG(X) (fl::FakeStrStream() << X)
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_DBG_IF
|
||||
#ifdef FASTLED_HAS_DBG
|
||||
#define FASTLED_DBG_IF(COND, MSG) if (COND) FASTLED_DBG(MSG)
|
||||
#else
|
||||
#define FASTLED_DBG_IF(COND, MSG) while(false && (COND)) { FASTLED_DBG(MSG); }
|
||||
#endif // FASTLED_HAS_DBG
|
||||
#endif // FASTLED_DBG_IF
|
||||
13
FastLED/src/fl/deprecated.h
Normal file
13
FastLED/src/fl/deprecated.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#if defined(__GNUC__) // GCC or Clang
|
||||
# ifndef FASTLED_DEPRECATED
|
||||
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
||||
# define FASTLED_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
# else
|
||||
# define FASTLED_DEPRECATED(msg) __attribute__((deprecated))
|
||||
# endif
|
||||
# endif
|
||||
#else // Other compilers
|
||||
# ifndef FASTLED_DEPRECATED
|
||||
# define FASTLED_DEPRECATED(msg)
|
||||
# endif
|
||||
#endif
|
||||
132
FastLED/src/fl/engine_events.cpp
Normal file
132
FastLED/src/fl/engine_events.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "fl/engine_events.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
using namespace fl;
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
|
||||
EngineEvents::Listener::Listener() {
|
||||
}
|
||||
|
||||
EngineEvents::Listener::~Listener() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents* ptr = EngineEvents::getInstance();
|
||||
const bool has_listener = ptr && ptr->_hasListener(this);
|
||||
if (has_listener) {
|
||||
// Warning, the listener should be removed by the subclass. If we are here
|
||||
// then the subclass did not remove the listener and we are now in a partial
|
||||
// state of destruction and the results may be undefined for multithreaded
|
||||
// applications. However, for single threaded (the only option as of 2024, October)
|
||||
// this will be ok.
|
||||
ptr->_removeListener(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EngineEvents* EngineEvents::getInstance() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
return &Singleton<EngineEvents>::instance();
|
||||
#else
|
||||
return nullptr; // strip out when engine events are disabled.
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
void EngineEvents::_onPlatformPreLoop() {
|
||||
for (auto& item : mListeners) {
|
||||
auto listener = item.listener;
|
||||
listener->onPlatformPreLoop();
|
||||
}
|
||||
for (auto& item : mListeners) {
|
||||
auto listener = item.listener;
|
||||
listener->onPlatformPreLoop2();
|
||||
}
|
||||
}
|
||||
|
||||
bool EngineEvents::_hasListener(Listener* listener) {
|
||||
auto predicate = [listener](const Pair& pair) {
|
||||
return pair.listener == listener;
|
||||
};
|
||||
return mListeners.find_if(predicate) != mListeners.end();
|
||||
}
|
||||
|
||||
void EngineEvents::_addListener(Listener* listener, int priority) {
|
||||
if (_hasListener(listener)) {
|
||||
return;
|
||||
}
|
||||
for (auto it = mListeners.begin(); it != mListeners.end(); ++it) {
|
||||
if (it->priority < priority) {
|
||||
// this is now the highest priority in this spot.
|
||||
EngineEvents::Pair pair = EngineEvents::Pair(listener, priority);
|
||||
mListeners.insert(it, pair);
|
||||
return;
|
||||
}
|
||||
}
|
||||
EngineEvents::Pair pair = EngineEvents::Pair(listener, priority);
|
||||
mListeners.push_back(pair);
|
||||
}
|
||||
|
||||
void EngineEvents::_removeListener(Listener* listener) {
|
||||
auto predicate = [listener](const Pair& pair) {
|
||||
return pair.listener == listener;
|
||||
};
|
||||
auto it = mListeners.find_if(predicate);
|
||||
if (it != mListeners.end()) {
|
||||
mListeners.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void EngineEvents::_onBeginFrame() {
|
||||
// Make the copy of the listener list to avoid issues with listeners being added or removed during the loop.
|
||||
ListenerList copy = mListeners;
|
||||
for (auto& item : copy) {
|
||||
auto listener = item.listener;
|
||||
listener->onBeginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void EngineEvents::_onEndShowLeds() {
|
||||
// Make the copy of the listener list to avoid issues with listeners being added or removed during the loop.
|
||||
ListenerList copy = mListeners;
|
||||
for (auto& item : copy) {
|
||||
auto listener = item.listener;
|
||||
listener->onEndShowLeds();
|
||||
}
|
||||
}
|
||||
|
||||
void EngineEvents::_onEndFrame() {
|
||||
// Make the copy of the listener list to avoid issues with listeners being added or removed during the loop.
|
||||
ListenerList copy = mListeners;
|
||||
for (auto& item : copy) {
|
||||
auto listener = item.listener;
|
||||
listener->onEndFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void EngineEvents::_onStripAdded(CLEDController* strip, uint32_t num_leds) {
|
||||
// Make the copy of the listener list to avoid issues with listeners being added or removed during the loop.
|
||||
ListenerList copy = mListeners;
|
||||
for (auto& item : copy) {
|
||||
auto listener = item.listener;
|
||||
listener->onStripAdded(strip, num_leds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EngineEvents::_onCanvasUiSet(CLEDController *strip, const ScreenMap& screenmap) {
|
||||
// Make the copy of the listener list to avoid issues with listeners being added or removed during the loop.
|
||||
ListenerList copy = mListeners;
|
||||
for (auto& item : copy) {
|
||||
auto listener = item.listener;
|
||||
listener->onCanvasUiSet(strip, screenmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // FASTLED_HAS_ENGINE_EVENTS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
153
FastLED/src/fl/engine_events.h
Normal file
153
FastLED/src/fl/engine_events.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/vector.h"
|
||||
#include "fl/singleton.h"
|
||||
#include "fl/xymap.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
|
||||
#ifndef FASTLED_ENGINE_EVENTS_MAX_LISTENERS
|
||||
#define FASTLED_ENGINE_EVENTS_MAX_LISTENERS 8
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_ENGINE_EVENTS
|
||||
#ifdef __AVR__
|
||||
#define FASTLED_HAS_ENGINE_EVENTS 0
|
||||
#else
|
||||
#define FASTLED_HAS_ENGINE_EVENTS 1
|
||||
#endif
|
||||
#endif // FASTLED_HAS_ENGINE_EVENTS
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
class CLEDController;
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
namespace fl {
|
||||
|
||||
class EngineEvents {
|
||||
public:
|
||||
class Listener {
|
||||
public:
|
||||
// Note that the subclass must call EngineEvents::addListener(this) to
|
||||
// start listening. In the subclass destructor, the subclass should call
|
||||
// EngineEvents::removeListener(this).
|
||||
Listener();
|
||||
virtual ~Listener();
|
||||
virtual void onBeginFrame() {}
|
||||
virtual void onEndShowLeds() {}
|
||||
virtual void onEndFrame() {}
|
||||
virtual void onStripAdded(CLEDController *strip, uint32_t num_leds) {
|
||||
(void)strip;
|
||||
(void)num_leds;
|
||||
}
|
||||
// Called to set the canvas for UI elements for a particular strip.
|
||||
virtual void onCanvasUiSet(CLEDController *strip, const ScreenMap& screenmap) {
|
||||
(void)strip;
|
||||
(void)screenmap;
|
||||
}
|
||||
virtual void onPlatformPreLoop() {}
|
||||
virtual void onPlatformPreLoop2() {}
|
||||
};
|
||||
|
||||
|
||||
static void addListener(Listener *listener, int priority = 0) {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_addListener(listener, priority);
|
||||
#else
|
||||
(void)listener;
|
||||
(void)priority;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void removeListener(Listener *listener) {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_removeListener(listener);
|
||||
#else
|
||||
(void)listener;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool hasListener(Listener *listener) {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
return EngineEvents::getInstance()->_hasListener(listener);
|
||||
#else
|
||||
(void)listener;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onBeginFrame() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onBeginFrame();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onEndShowLeds() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onEndShowLeds();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onEndFrame() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onEndFrame();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onStripAdded(CLEDController *strip, uint32_t num_leds) {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onStripAdded(strip, num_leds);
|
||||
#else
|
||||
(void)strip;
|
||||
(void)num_leds;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void onCanvasUiSet(CLEDController *strip, const ScreenMap& xymap) {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onCanvasUiSet(strip, xymap);
|
||||
#else
|
||||
(void)strip;
|
||||
(void)xymap;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onPlatformPreLoop() {
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
EngineEvents::getInstance()->_onPlatformPreLoop();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
// Safe to add a listeners during a callback.
|
||||
void _addListener(Listener *listener, int priority);
|
||||
// Safe to remove self during a callback.
|
||||
void _removeListener(Listener *listener);
|
||||
void _onBeginFrame();
|
||||
void _onEndShowLeds();
|
||||
void _onEndFrame();
|
||||
void _onStripAdded(CLEDController *strip, uint32_t num_leds);
|
||||
void _onCanvasUiSet(CLEDController *strip, const ScreenMap& xymap);
|
||||
void _onPlatformPreLoop();
|
||||
bool _hasListener(Listener *listener);
|
||||
#if FASTLED_HAS_ENGINE_EVENTS
|
||||
struct Pair {
|
||||
Listener *listener = nullptr;
|
||||
int priority = 0;
|
||||
Pair(Listener *listener, int priority) : listener(listener), priority(priority) {}
|
||||
};
|
||||
typedef fl::FixedVector<Pair, FASTLED_ENGINE_EVENTS_MAX_LISTENERS>
|
||||
ListenerList;
|
||||
ListenerList mListeners;
|
||||
#endif
|
||||
|
||||
static EngineEvents *getInstance();
|
||||
|
||||
EngineEvents() = default;
|
||||
friend class fl::Singleton<EngineEvents>;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
197
FastLED/src/fl/file_system.cpp
Normal file
197
FastLED/src/fl/file_system.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "fl/file_system.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include "platforms/wasm/fs_wasm.h"
|
||||
#elif __has_include(<SD.h>)
|
||||
// work in progress.
|
||||
//#include "platforms/fs_sdcard_arduino.hpp"
|
||||
#endif
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/json.h"
|
||||
#include "fl/unused.h"
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
class NullFileHandle : public FileHandle {
|
||||
public:
|
||||
NullFileHandle() = default;
|
||||
~NullFileHandle() override {}
|
||||
|
||||
bool available() const override { return false; }
|
||||
size_t size() const override { return 0; }
|
||||
size_t read(uint8_t *dst, size_t bytesToRead) override {
|
||||
FASTLED_UNUSED(dst);
|
||||
FASTLED_UNUSED(bytesToRead);
|
||||
return 0;
|
||||
}
|
||||
size_t pos() const override { return 0; }
|
||||
const char *path() const override {
|
||||
return "NULL FILE HANDLE";
|
||||
}
|
||||
bool seek(size_t pos) override {
|
||||
FASTLED_UNUSED(pos);
|
||||
return false;
|
||||
}
|
||||
void close() override {}
|
||||
bool valid() const override {
|
||||
FASTLED_WARN("NullFileHandle is not valid");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace fl;
|
||||
|
||||
class NullFileSystem : public FsImpl {
|
||||
public:
|
||||
NullFileSystem() {
|
||||
FASTLED_WARN("NullFileSystem instantiated as a placeholder, please implement a file system for your platform.");
|
||||
}
|
||||
~NullFileSystem() override {}
|
||||
|
||||
bool begin() override { return true; }
|
||||
void end() override {}
|
||||
|
||||
void close(FileHandlePtr file) override {
|
||||
// No need to do anything for in-memory files
|
||||
FASTLED_UNUSED(file);
|
||||
FASTLED_WARN("NullFileSystem::close");
|
||||
}
|
||||
|
||||
FileHandlePtr openRead(const char *_path) override {
|
||||
FASTLED_UNUSED(_path);
|
||||
FileHandlePtr out = FileHandlePtr::TakeOwnership(new NullFileHandle());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool FileSystem::beginSd(int cs_pin) {
|
||||
mFs = make_sdcard_filesystem(cs_pin);
|
||||
if (!mFs) {
|
||||
return false;
|
||||
}
|
||||
mFs->begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSystem::begin(FsImplPtr platform_filesystem) {
|
||||
mFs = platform_filesystem;
|
||||
if (!mFs) {
|
||||
return false;
|
||||
}
|
||||
mFs->begin();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t FileHandle::bytesLeft() const { return size() - pos(); }
|
||||
|
||||
FileSystem::FileSystem() : mFs() {}
|
||||
|
||||
|
||||
void FileSystem::end() {
|
||||
if (mFs) {
|
||||
mFs->end();
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSystem::readJson(const char *path, JsonDocument* doc) {
|
||||
Str text;
|
||||
if (!readText(path, &text)) {
|
||||
return false;
|
||||
}
|
||||
return parseJson(text.c_str(), doc);
|
||||
}
|
||||
|
||||
bool FileSystem::readScreenMaps(const char *path, FixedMap<Str, ScreenMap, 16>* out, Str* error) {
|
||||
Str text;
|
||||
if (!readText(path, &text)) {
|
||||
FASTLED_WARN("Failed to read file: " << path);
|
||||
if (error) {
|
||||
*error = "Failed to read file: ";
|
||||
error->append(path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Str err;
|
||||
bool ok = ScreenMap::ParseJson(text.c_str(), out, &err);
|
||||
if (!ok) {
|
||||
FASTLED_WARN("Failed to parse screen map: " << err.c_str());
|
||||
*error = err;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSystem::readScreenMap(const char *path, const char* name, ScreenMap* out, Str* error) {
|
||||
Str text;
|
||||
if (!readText(path, &text)) {
|
||||
FASTLED_WARN("Failed to read file: " << path);
|
||||
if (error) {
|
||||
*error = "Failed to read file: ";
|
||||
error->append(path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Str err;
|
||||
bool ok = ScreenMap::ParseJson(text.c_str(), name, out, &err);
|
||||
if (!ok) {
|
||||
FASTLED_WARN("Failed to parse screen map: " << err.c_str());
|
||||
*error = err;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSystem::close(FileHandlePtr file) { mFs->close(file); }
|
||||
|
||||
FileHandlePtr FileSystem::openRead(const char *path) { return mFs->openRead(path); }
|
||||
Video FileSystem::openVideo(const char *path, size_t pixelsPerFrame, float fps, size_t nFrameHistory) {
|
||||
Video video(pixelsPerFrame, fps, nFrameHistory);
|
||||
FileHandlePtr file = openRead(path);
|
||||
if (!file) {
|
||||
video.setError(fl::Str("Could not open file: ").append(path));
|
||||
return video;
|
||||
}
|
||||
video.begin(file);
|
||||
return video;
|
||||
}
|
||||
|
||||
bool FileSystem::readText(const char *path, fl::Str* out) {
|
||||
FileHandlePtr file = openRead(path);
|
||||
if (!file) {
|
||||
FASTLED_WARN("Failed to open file: " << path);
|
||||
return false;
|
||||
}
|
||||
size_t size = file->size();
|
||||
out->reserve(size + out->size());
|
||||
bool wrote = false;
|
||||
while (file->available()) {
|
||||
uint8_t buf[64];
|
||||
size_t n = file->read(buf, sizeof(buf));
|
||||
// out->append(buf, n);
|
||||
out->append((const char*)buf, n);
|
||||
wrote = true;
|
||||
}
|
||||
file->close();
|
||||
FASTLED_DBG_IF(!wrote, "Failed to write any data to the output string.");
|
||||
return wrote;
|
||||
}
|
||||
} // namespace fl
|
||||
|
||||
namespace fl {
|
||||
__attribute__((weak)) FsImplPtr make_sdcard_filesystem(int cs_pin) {
|
||||
FASTLED_UNUSED(cs_pin);
|
||||
FsImplPtr out = FsImplPtr::TakeOwnership(new NullFileSystem());
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
|
||||
106
FastLED/src/fl/file_system.h
Normal file
106
FastLED/src/fl/file_system.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
// Note, fs.h breaks ESPAsyncWebServer so we use file_system.h instead.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/ptr.h"
|
||||
#include "fx/video.h"
|
||||
#include "fl/str.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(FsImpl);
|
||||
// PLATFORM INTERFACE
|
||||
// You need to define this for your platform.
|
||||
// Otherwise a null filesystem will be used that will do nothing but spew warnings, but otherwise
|
||||
// won't crash the system.
|
||||
FsImplPtr make_sdcard_filesystem(int cs_pin);
|
||||
}
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
struct CRGB;
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
namespace fl {
|
||||
|
||||
class ScreenMap;
|
||||
FASTLED_SMART_PTR(FileSystem);
|
||||
FASTLED_SMART_PTR(FileHandle);
|
||||
class Video;
|
||||
template<typename Key, typename Value, size_t N> class FixedMap;
|
||||
|
||||
|
||||
|
||||
|
||||
class JsonDocument;
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
FileSystem();
|
||||
bool beginSd(int cs_pin); // Signal to begin using the filesystem resource.
|
||||
bool begin(FsImplPtr platform_filesystem); // Signal to begin using the filesystem resource.
|
||||
void end(); // Signal to end use of the file system.
|
||||
|
||||
|
||||
FileHandlePtr openRead(const char *path); // Null if file could not be opened.
|
||||
Video openVideo(const char *path, size_t pixelsPerFrame, float fps = 30.0f, size_t nFrameHistory = 0); // Null if video could not be opened.
|
||||
bool readText(const char *path, Str* out);
|
||||
bool readJson(const char *path, JsonDocument* doc);
|
||||
bool readScreenMaps(const char *path, FixedMap<Str, ScreenMap, 16>* out, Str* error = nullptr);
|
||||
bool readScreenMap(const char *path, const char* name, ScreenMap* out, Str* error = nullptr);
|
||||
void close(FileHandlePtr file);
|
||||
|
||||
private:
|
||||
FsImplPtr mFs; // System dependent filesystem.
|
||||
};
|
||||
|
||||
|
||||
|
||||
// An abstract class that represents a file handle.
|
||||
// Devices like the SD card will return one of these.
|
||||
class FileHandle: public Referent {
|
||||
public:
|
||||
virtual ~FileHandle() {}
|
||||
virtual bool available() const = 0;
|
||||
virtual size_t bytesLeft() const;
|
||||
virtual size_t size() const = 0;
|
||||
virtual size_t read(uint8_t *dst, size_t bytesToRead) = 0;
|
||||
virtual size_t pos() const = 0;
|
||||
virtual const char* path() const = 0;
|
||||
virtual bool seek(size_t pos) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual bool valid() const = 0;
|
||||
|
||||
// convenience functions
|
||||
size_t readCRGB(CRGB* dst, size_t n) {
|
||||
return read((uint8_t*)dst, n * 3) / 3;
|
||||
}
|
||||
};
|
||||
|
||||
// Platforms will subclass this to implement the filesystem.
|
||||
class FsImpl : public Referent {
|
||||
public:
|
||||
struct Visitor {
|
||||
virtual ~Visitor() {}
|
||||
virtual void accept(const char* path) = 0;
|
||||
};
|
||||
FsImpl() = default;
|
||||
virtual ~FsImpl() {} // Use default pins for spi.
|
||||
virtual bool begin() = 0;
|
||||
// End use of card
|
||||
virtual void end() = 0;
|
||||
virtual void close(FileHandlePtr file) = 0;
|
||||
virtual FileHandlePtr openRead(const char *path) = 0;
|
||||
|
||||
virtual bool ls(Visitor &visitor) {
|
||||
// todo: implement.
|
||||
(void)visitor;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
161
FastLED/src/fl/five_bit_hd_gamma.cpp
Normal file
161
FastLED/src/fl/five_bit_hd_gamma.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/// @file five_bit_hd_gamma.cpp
|
||||
/// Defines functions for five-bit gamma correction
|
||||
|
||||
#define FASTLED_INTERNAL 1
|
||||
#include "five_bit_hd_gamma.h"
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "fastled_progmem.h"
|
||||
#include "lib8tion/intmap.h"
|
||||
#include "lib8tion/math8.h"
|
||||
#include "lib8tion/scale8.h"
|
||||
#include "lib8tion/brightness_bitshifter.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
// Author: Zach Vorhies
|
||||
namespace fl {
|
||||
|
||||
namespace {
|
||||
template <typename T> T mymax(T a, T b) { return (a > b) ? a : b; }
|
||||
|
||||
template <typename T> T max3(T a, T b, T c) { return mymax(mymax(a, b), c); }
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifndef FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_2_8
|
||||
// Fast a memory efficient gamma=2 function.
|
||||
void five_bit_hd_gamma_function(CRGB color, uint16_t *r16, uint16_t *g16,
|
||||
uint16_t *b16) {
|
||||
uint16_t _r16 = map8_to_16(color.r);
|
||||
uint16_t _g16 = map8_to_16(color.g);
|
||||
uint16_t _b16 = map8_to_16(color.b);
|
||||
*r16 = scale16by8(_r16, color.r);
|
||||
*g16 = scale16by8(_g16, color.g);
|
||||
*b16 = scale16by8(_b16, color.b);
|
||||
}
|
||||
#else
|
||||
// Using look up table for gamma16 correction at power of 2.8
|
||||
static const uint16_t PROGMEM _gamma_2_8[256] = {
|
||||
0, 0, 0, 1, 1, 2, 4, 6, 8, 11, 14,
|
||||
18, 23, 29, 35, 41, 49, 57, 67, 77, 88, 99,
|
||||
112, 126, 141, 156, 173, 191, 210, 230, 251, 274, 297,
|
||||
322, 348, 375, 404, 433, 464, 497, 531, 566, 602, 640,
|
||||
680, 721, 763, 807, 853, 899, 948, 998, 1050, 1103, 1158,
|
||||
1215, 1273, 1333, 1394, 1458, 1523, 1590, 1658, 1729, 1801, 1875,
|
||||
1951, 2029, 2109, 2190, 2274, 2359, 2446, 2536, 2627, 2720, 2816,
|
||||
2913, 3012, 3114, 3217, 3323, 3431, 3541, 3653, 3767, 3883, 4001,
|
||||
4122, 4245, 4370, 4498, 4627, 4759, 4893, 5030, 5169, 5310, 5453,
|
||||
5599, 5747, 5898, 6051, 6206, 6364, 6525, 6688, 6853, 7021, 7191,
|
||||
7364, 7539, 7717, 7897, 8080, 8266, 8454, 8645, 8838, 9034, 9233,
|
||||
9434, 9638, 9845, 10055, 10267, 10482, 10699, 10920, 11143, 11369, 11598,
|
||||
11829, 12064, 12301, 12541, 12784, 13030, 13279, 13530, 13785, 14042, 14303,
|
||||
14566, 14832, 15102, 15374, 15649, 15928, 16209, 16493, 16781, 17071, 17365,
|
||||
17661, 17961, 18264, 18570, 18879, 19191, 19507, 19825, 20147, 20472, 20800,
|
||||
21131, 21466, 21804, 22145, 22489, 22837, 23188, 23542, 23899, 24260, 24625,
|
||||
24992, 25363, 25737, 26115, 26496, 26880, 27268, 27659, 28054, 28452, 28854,
|
||||
29259, 29667, 30079, 30495, 30914, 31337, 31763, 32192, 32626, 33062, 33503,
|
||||
33947, 34394, 34846, 35300, 35759, 36221, 36687, 37156, 37629, 38106, 38586,
|
||||
39071, 39558, 40050, 40545, 41045, 41547, 42054, 42565, 43079, 43597, 44119,
|
||||
44644, 45174, 45707, 46245, 46786, 47331, 47880, 48432, 48989, 49550, 50114,
|
||||
50683, 51255, 51832, 52412, 52996, 53585, 54177, 54773, 55374, 55978, 56587,
|
||||
57199, 57816, 58436, 59061, 59690, 60323, 60960, 61601, 62246, 62896, 63549,
|
||||
64207, 64869, 65535};
|
||||
|
||||
void five_bit_hd_gamma_function(CRGB rgb, uint16_t *r16, uint16_t *g16,
|
||||
uint16_t *b16) {
|
||||
*r16 = _gamma_2_8[rgb.r];
|
||||
*g16 = _gamma_2_8[rgb.g];
|
||||
*b16 = _gamma_2_8[rgb.b];
|
||||
}
|
||||
#endif // FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_2_8
|
||||
|
||||
uint8_t five_bit_bitshift(uint16_t r16, uint16_t g16, uint16_t b16,
|
||||
uint8_t brightness, CRGB *out, uint8_t *out_power_5bit) {
|
||||
if (brightness == 0) {
|
||||
*out = CRGB(0, 0, 0);
|
||||
*out_power_5bit = 0;
|
||||
return 0;
|
||||
}
|
||||
if (r16 == 0 && g16 == 0 && b16 == 0) {
|
||||
*out = CRGB(0, 0, 0);
|
||||
*out_power_5bit = (brightness <= 31) ? brightness : 31;
|
||||
return brightness;
|
||||
}
|
||||
|
||||
// Note: One day someone smarter than me will come along and invent a closed
|
||||
// form solution for this. However, the numerical method works extremely
|
||||
// well and has been optimized to avoid division performance penalties as
|
||||
// much as possible.
|
||||
|
||||
// Step 1: Initialize brightness
|
||||
static const uint8_t kStartBrightness = 0b00010000;
|
||||
uint8_t v5 = kStartBrightness;
|
||||
// Step 2: Boost brightness by swapping power with the driver brightness.
|
||||
brightness_bitshifter8(&v5, &brightness, 4);
|
||||
|
||||
// Step 3: Boost brightness of the color channels by swapping power with the
|
||||
// driver brightness.
|
||||
uint16_t max_component = max3(r16, g16, b16);
|
||||
// five_bit_color_bitshift(&r16, &g16, &b16, &v5);
|
||||
uint8_t shifts = brightness_bitshifter16(&v5, &max_component, 4, 2);
|
||||
if (shifts) {
|
||||
r16 = r16 << shifts;
|
||||
g16 = g16 << shifts;
|
||||
b16 = b16 << shifts;
|
||||
}
|
||||
|
||||
// Step 4: scale by final brightness factor.
|
||||
if (brightness != 0xff) {
|
||||
r16 = scale16by8(r16, brightness);
|
||||
g16 = scale16by8(g16, brightness);
|
||||
b16 = scale16by8(b16, brightness);
|
||||
}
|
||||
|
||||
|
||||
// brighten hardware brightness by turning on low order bits
|
||||
if (v5 > 1) {
|
||||
// since v5 is a power of two, subtracting one will invert the leading bit
|
||||
// and invert all the bits below it.
|
||||
// Example: 0b00010000 -1 = 0b00001111
|
||||
// So 0b00010000 | 0b00001111 = 0b00011111
|
||||
v5 = v5 | (v5 - 1);
|
||||
}
|
||||
// Step 5: Convert back to 8-bit and output.
|
||||
*out = CRGB(map16_to_8(r16), map16_to_8(g16), map16_to_8(b16));
|
||||
*out_power_5bit = v5;
|
||||
return brightness;
|
||||
}
|
||||
|
||||
void __builtin_five_bit_hd_gamma_bitshift(CRGB colors, CRGB colors_scale,
|
||||
uint8_t global_brightness,
|
||||
CRGB *out_colors,
|
||||
uint8_t *out_power_5bit) {
|
||||
|
||||
if (global_brightness == 0) {
|
||||
*out_colors = CRGB(0, 0, 0);
|
||||
*out_power_5bit = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Gamma Correction
|
||||
uint16_t r16, g16, b16;
|
||||
five_bit_hd_gamma_function(colors, &r16, &g16, &b16);
|
||||
|
||||
// Step 2: Color correction step comes after gamma correction. These values
|
||||
// are assumed to be be relatively close to 255.
|
||||
if (colors_scale.r != 0xff) {
|
||||
r16 = scale16by8(r16, colors_scale.r);
|
||||
}
|
||||
if (colors_scale.g != 0xff) {
|
||||
g16 = scale16by8(g16, colors_scale.g);
|
||||
}
|
||||
if (colors_scale.b != 0xff) {
|
||||
b16 = scale16by8(b16, colors_scale.b);
|
||||
}
|
||||
|
||||
five_bit_bitshift(r16, g16, b16, global_brightness, out_colors,
|
||||
out_power_5bit);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
94
FastLED/src/fl/five_bit_hd_gamma.h
Normal file
94
FastLED/src/fl/five_bit_hd_gamma.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/// @file five_bit_hd_gamma.h
|
||||
/// Declares functions for five-bit gamma correction
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/force_inline.h"
|
||||
#include "crgb.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
enum FiveBitGammaCorrectionMode {
|
||||
kFiveBitGammaCorrectionMode_Null = 0,
|
||||
kFiveBitGammaCorrectionMode_BitShift = 1
|
||||
};
|
||||
|
||||
// Applies gamma correction for the RGBV(8, 8, 8, 5) color space, where
|
||||
// the last byte is the brightness byte at 5 bits.
|
||||
// To override this five_bit_hd_gamma_bitshift you'll need to define
|
||||
// FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE in your build settings
|
||||
// then define the function anywhere in your project.
|
||||
// Example:
|
||||
// FASTLED_NAMESPACE_BEGIN
|
||||
// void five_bit_hd_gamma_bitshift(
|
||||
// uint8_t r8, uint8_t g8, uint8_t b8,
|
||||
// uint8_t r8_scale, uint8_t g8_scale, uint8_t b8_scale,
|
||||
// uint8_t* out_r8,
|
||||
// uint8_t* out_g8,
|
||||
// uint8_t* out_b8,
|
||||
// uint8_t* out_power_5bit) {
|
||||
// cout << "hello world\n";
|
||||
// }
|
||||
// FASTLED_NAMESPACE_END
|
||||
|
||||
void __builtin_five_bit_hd_gamma_bitshift(CRGB colors,
|
||||
CRGB colors_scale,
|
||||
uint8_t global_brightness,
|
||||
CRGB* out_colors,
|
||||
uint8_t *out_power_5bit);
|
||||
|
||||
|
||||
// Exposed for testing.
|
||||
uint8_t five_bit_bitshift(uint16_t r16, uint16_t g16, uint16_t b16, uint8_t brightness, CRGB* out, uint8_t* out_power_5bit);
|
||||
|
||||
#ifdef FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
|
||||
// This function is located somewhere else in your project, so it's declared
|
||||
// extern here.
|
||||
extern void five_bit_hd_gamma_bitshift(CRGB colors,
|
||||
CRGB colors_scale,
|
||||
uint8_t global_brightness,
|
||||
CRGB* out_colors,
|
||||
uint8_t *out_power_5bit);
|
||||
#else
|
||||
FASTLED_FORCE_INLINE void
|
||||
five_bit_hd_gamma_bitshift(CRGB colors,
|
||||
CRGB colors_scale,
|
||||
uint8_t global_brightness,
|
||||
CRGB* out_colors,
|
||||
uint8_t *out_power_5bit) {
|
||||
__builtin_five_bit_hd_gamma_bitshift(colors,
|
||||
colors_scale,
|
||||
global_brightness,
|
||||
out_colors,
|
||||
out_power_5bit);
|
||||
}
|
||||
#endif // FASTLED_FIVE_BIT_HD_BITSHIFT_FUNCTION_OVERRIDE
|
||||
|
||||
// Simple gamma correction function that converts from
|
||||
// 8-bit color component and converts it to gamma corrected 16-bit
|
||||
// color component. Fast and no memory overhead!
|
||||
// To override this function you'll need to define
|
||||
// FASTLED_FIVE_BIT_HD_GAMMA_BITSHIFT_FUNCTION_OVERRIDE in your build settings
|
||||
// and then define your own version anywhere in your project. Example:
|
||||
// FASTLED_NAMESPACE_BEGIN
|
||||
// void five_bit_hd_gamma_function(
|
||||
// uint8_t r8, uint8_t g8, uint8_t b8,
|
||||
// uint16_t* r16, uint16_t* g16, uint16_t* b16) {
|
||||
// cout << "hello world\n";
|
||||
// }
|
||||
// FASTLED_NAMESPACE_END
|
||||
#ifdef FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_OVERRIDE
|
||||
// This function is located somewhere else in your project, so it's declared
|
||||
// extern here.
|
||||
extern void five_bit_hd_gamma_function(CRGB color,
|
||||
uint16_t *r16, uint16_t *g16, uint16_t *b16);
|
||||
#else
|
||||
void five_bit_hd_gamma_function(CRGB color,
|
||||
uint16_t *r16, uint16_t *g16, uint16_t *b16);
|
||||
#endif // FASTLED_FIVE_BIT_HD_GAMMA_FUNCTION_OVERRIDE
|
||||
|
||||
} // namespace fl
|
||||
|
||||
8
FastLED/src/fl/force_inline.h
Normal file
8
FastLED/src/fl/force_inline.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifdef FASTLED_NO_FORCE_INLINE
|
||||
#define FASTLED_FORCE_INLINE inline
|
||||
#else
|
||||
#define FASTLED_FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
5
FastLED/src/fl/has_define.h
Normal file
5
FastLED/src/fl/has_define.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(x) 0
|
||||
#endif
|
||||
21
FastLED/src/fl/inplacenew.h
Normal file
21
FastLED/src/fl/inplacenew.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
// This file must not be in the fl namespace, it must be in the global namespace.
|
||||
|
||||
#if (defined(__AVR__) || !defined(__has_include)) && (!defined(FASTLED_HAS_NEW))
|
||||
#ifndef __has_include
|
||||
#define _NO_EXCEPT
|
||||
#else
|
||||
#define _NO_EXCEPT noexcept
|
||||
#endif
|
||||
inline void* operator new(size_t, void* ptr) _NO_EXCEPT {
|
||||
return ptr;
|
||||
}
|
||||
#elif __has_include(<new>)
|
||||
#include <new>
|
||||
#elif __has_include(<new.h>)
|
||||
#include <new.h>
|
||||
#elif __has_include("new.h")
|
||||
#include "new.h"
|
||||
#endif
|
||||
|
||||
16
FastLED/src/fl/insert_result.h
Normal file
16
FastLED/src/fl/insert_result.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Because of the fixed size nature of a lot of FastLED's containers we
|
||||
// need to provide additional feedback to the caller about the nature of
|
||||
// why an insert did or did not happen. Specifically, we want to differentiate
|
||||
// between failing to insert because the item already existed and when the container
|
||||
// was full.
|
||||
enum InsertResult {
|
||||
kInserted = 0,
|
||||
kExists = 1,
|
||||
kMaxSize = 2,
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
32
FastLED/src/fl/json.cpp
Normal file
32
FastLED/src/fl/json.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "fl/json.h"
|
||||
#include "fl/str.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
bool parseJson(const char* json, fl::JsonDocument* doc, Str* _error) {
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
if (_error) {
|
||||
*_error = "JSON not enabled";
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
FLArduinoJson::DeserializationError error = deserializeJson(*doc, json);
|
||||
if (error) {
|
||||
if (_error) {
|
||||
*_error = error.c_str();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void toJson(const fl::JsonDocument& doc, Str* jsonBuffer) {
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
return;
|
||||
#else
|
||||
serializeJson(doc, *jsonBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
29
FastLED/src/fl/json.h
Normal file
29
FastLED/src/fl/json.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifndef FASTLED_ENABLE_JSON
|
||||
#ifdef __AVR__
|
||||
#define FASTLED_ENABLE_JSON 0
|
||||
#else
|
||||
#define FASTLED_ENABLE_JSON 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if FASTLED_ENABLE_JSON
|
||||
#include "third_party/arduinojson/json.h"
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
class Str;
|
||||
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
class JsonDocument {};
|
||||
#else
|
||||
class JsonDocument: public ::FLArduinoJson::JsonDocument {};
|
||||
#endif
|
||||
|
||||
bool parseJson(const char* json, JsonDocument* doc, Str* error = nullptr);
|
||||
void toJson(const JsonDocument& doc, Str* jsonBuffer);
|
||||
|
||||
} // namespace fl
|
||||
75
FastLED/src/fl/lut.h
Normal file
75
FastLED/src/fl/lut.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
LUT - Look up table implementation for various types.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fl/ptr.h"
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/allocator.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
template<typename T>
|
||||
struct pair_xy {
|
||||
T x = 0;
|
||||
T y = 0;
|
||||
constexpr pair_xy() = default;
|
||||
constexpr pair_xy(T x, T y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
using pair_xy_float = pair_xy<float>; // It's just easier if we allow negative values.
|
||||
|
||||
// LUT holds a look up table to map data from one
|
||||
// value to another. This can be quite big (1/3rd of the frame buffer)
|
||||
// so a Referent is used to allow memory sharing.
|
||||
|
||||
template<typename T>
|
||||
class LUT;
|
||||
|
||||
typedef LUT<uint16_t> LUT16;
|
||||
typedef LUT<pair_xy_float> LUTXYFLOAT;
|
||||
|
||||
FASTLED_SMART_PTR_NO_FWD(LUT16);
|
||||
FASTLED_SMART_PTR_NO_FWD(LUTXYFLOAT);
|
||||
|
||||
// Templated lookup table.
|
||||
template<typename T>
|
||||
class LUT : public fl::Referent {
|
||||
public:
|
||||
LUT(uint32_t length) : length(length) {
|
||||
T* ptr = LargeBlockAllocator<T>::Alloc(length);
|
||||
mDataHandle.reset(ptr);
|
||||
data = ptr;
|
||||
}
|
||||
// In this version the data is passed in but not managed by this object.
|
||||
LUT(uint32_t length, T* data) : length(length) {
|
||||
this->data = data;
|
||||
}
|
||||
~LUT() {
|
||||
LargeBlockAllocator<T>::Free(mDataHandle.release());
|
||||
data = mDataHandle.get();
|
||||
}
|
||||
|
||||
const T& operator[](uint32_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
const T& operator[](uint16_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
T* getData() const {
|
||||
return data;
|
||||
}
|
||||
private:
|
||||
fl::scoped_ptr<T> mDataHandle;
|
||||
T* data = nullptr;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
|
||||
407
FastLED/src/fl/map.h
Normal file
407
FastLED/src/fl/map.h
Normal file
@@ -0,0 +1,407 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/template_magic.h"
|
||||
#include "fl/insert_result.h"
|
||||
#include "fl/pair.h"
|
||||
#include "fl/assert.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
template<typename T>
|
||||
struct DefaultLess {
|
||||
bool operator()(const T& a, const T& b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A simple unordered map implementation with a fixed size.
|
||||
// The user is responsible for making sure that the inserts
|
||||
// do not exceed the capacity of the set, otherwise they will
|
||||
// fail. Because of this limitation, this set is not a drop in
|
||||
// replacement for std::map.
|
||||
template<typename Key, typename Value, size_t N>
|
||||
class FixedMap {
|
||||
public:
|
||||
using PairKV = fl::Pair<Key, Value>;
|
||||
|
||||
typedef FixedVector<PairKV, N> VectorType;
|
||||
typedef typename VectorType::iterator iterator;
|
||||
typedef typename VectorType::const_iterator const_iterator;
|
||||
|
||||
|
||||
// Constructor
|
||||
constexpr FixedMap() = default;
|
||||
|
||||
iterator begin() {
|
||||
return data.begin();
|
||||
}
|
||||
iterator end() {
|
||||
return data.end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return data.begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return data.end();
|
||||
}
|
||||
|
||||
iterator find(const Key& key) {
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
if (it->first == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const Key& key) const {
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
if (it->first == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Less>
|
||||
iterator lowest(Less less_than = Less()) {
|
||||
iterator lowest = end();
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (lowest == end() || less_than(it->first, lowest->first)) {
|
||||
lowest = it;
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
template<typename Less>
|
||||
const_iterator lowest(Less less_than = Less()) const {
|
||||
const_iterator lowest = end();
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if (lowest == end() || less_than(it->first, lowest->first)) {
|
||||
lowest = it;
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
template<typename Less>
|
||||
iterator highest(Less less_than = Less()) {
|
||||
iterator highest = end();
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (highest == end() || less_than(highest->first, it->first)) {
|
||||
highest = it;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
template<typename Less>
|
||||
const_iterator highest(Less less_than = Less()) const {
|
||||
const_iterator highest = end();
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if (highest == end() || less_than(highest->first, it->first)) {
|
||||
highest = it;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
// We differ from the std standard here so that we don't allow
|
||||
// dereferencing the end iterator.
|
||||
bool get(const Key& key, Value* value) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
*value = it->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Value get(const Key& key, bool* has=nullptr) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
if (has) {
|
||||
*has = true;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
if (has) {
|
||||
*has = false;
|
||||
}
|
||||
return Value();
|
||||
}
|
||||
|
||||
Pair<bool, iterator> insert(const Key& key, const Value& value, InsertResult* result = nullptr) {
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
if (result) {
|
||||
*result = InsertResult::kExists;
|
||||
}
|
||||
// return false;
|
||||
return {false, it};
|
||||
}
|
||||
if (data.size() < N) {
|
||||
data.push_back(PairKV(key, value));
|
||||
if (result) {
|
||||
*result = InsertResult::kInserted;
|
||||
}
|
||||
// return true;
|
||||
return {true, data.end() - 1};
|
||||
}
|
||||
if (result) {
|
||||
*result = InsertResult::kMaxSize;
|
||||
}
|
||||
//return false;
|
||||
return {false, end()};
|
||||
}
|
||||
|
||||
bool update(const Key& key, const Value& value, bool insert_if_missing = true) {
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
it->second = value;
|
||||
return true;
|
||||
} else if (insert_if_missing) {
|
||||
return insert(key, value).first;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Value& operator[](const Key& key) {
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
return it->second;
|
||||
}
|
||||
data.push_back(PairKV(key, Value()));
|
||||
return data.back().second;
|
||||
}
|
||||
|
||||
const Value& operator[](const Key& key) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
return it->second;
|
||||
}
|
||||
static Value default_value;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
bool next(const Key& key, Key* next_key, bool allow_rollover = false) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
++it;
|
||||
if (it != end()) {
|
||||
*next_key = it->first;
|
||||
return true;
|
||||
} else if (allow_rollover && !empty()) {
|
||||
*next_key = begin()->first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool prev(const Key& key, Key* prev_key, bool allow_rollover = false) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
if (it != begin()) {
|
||||
--it;
|
||||
*prev_key = it->first;
|
||||
return true;
|
||||
} else if (allow_rollover && !empty()) {
|
||||
*prev_key = data[data.size() - 1].first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Get the current size of the vector
|
||||
constexpr size_t size() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
return data.empty();
|
||||
}
|
||||
|
||||
// Get the capacity of the vector
|
||||
constexpr size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
// Clear the vector
|
||||
void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
bool has(const Key& it) const {
|
||||
return find(it) != end();
|
||||
}
|
||||
|
||||
bool contains(const Key& key) const {
|
||||
return has(key);
|
||||
}
|
||||
private:
|
||||
VectorType data;
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename Value, typename Less = fl::DefaultLess<Key>>
|
||||
class SortedHeapMap {
|
||||
private:
|
||||
struct Pair {
|
||||
Key first;
|
||||
Value second;
|
||||
|
||||
Pair(const Key& k = Key(), const Value& v = Value())
|
||||
: first(k), second(v) {}
|
||||
};
|
||||
|
||||
struct PairLess {
|
||||
Less less;
|
||||
bool operator()(const Pair& a, const Pair& b) const {
|
||||
return less(a.first, b.first);
|
||||
}
|
||||
};
|
||||
|
||||
SortedHeapVector<Pair, PairLess> data;
|
||||
|
||||
public:
|
||||
|
||||
typedef typename SortedHeapVector<Pair, PairLess>::iterator iterator;
|
||||
typedef typename SortedHeapVector<Pair, PairLess>::const_iterator const_iterator;
|
||||
typedef Pair value_type;
|
||||
|
||||
SortedHeapMap(Less less = Less())
|
||||
: data(PairLess{less}) {
|
||||
}
|
||||
|
||||
void setMaxSize(size_t n) {
|
||||
data.setMaxSize(n);
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
data.reserve(n);
|
||||
}
|
||||
|
||||
bool insert(const Key& key, const Value& value, InsertResult* result = nullptr) {
|
||||
return data.insert(Pair(key, value), result);
|
||||
}
|
||||
|
||||
void update(const Key& key, const Value& value) {
|
||||
if (!insert(key, value)) {
|
||||
iterator it = find(key);
|
||||
it->second = value;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(SortedHeapMap& other) {
|
||||
data.swap(other.data);
|
||||
}
|
||||
|
||||
Value& at(const Key& key) {
|
||||
iterator it = find(key);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool has(const Key& key) const {
|
||||
return data.has(Pair(key));
|
||||
}
|
||||
|
||||
bool contains(const Key& key) const {
|
||||
return has(key);
|
||||
}
|
||||
|
||||
bool operator==(const SortedHeapMap& other) const {
|
||||
if (size() != other.size()) {
|
||||
return false;
|
||||
}
|
||||
for (const_iterator it = begin(), other_it = other.begin();
|
||||
it != end(); ++it, ++other_it) {
|
||||
if (it->first != other_it->first || it->second != other_it->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const SortedHeapMap& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size_t size() const { return data.size(); }
|
||||
bool empty() const { return data.empty(); }
|
||||
bool full() const { return data.full(); }
|
||||
size_t capacity() const { return data.capacity(); }
|
||||
void clear() { data.clear(); }
|
||||
|
||||
// begin, dend
|
||||
iterator begin() { return data.begin(); }
|
||||
iterator end() { return data.end(); }
|
||||
const_iterator begin() const { return data.begin(); }
|
||||
const_iterator end() const { return data.end(); }
|
||||
|
||||
iterator find(const Key& key) {
|
||||
return data.find(Pair(key));
|
||||
}
|
||||
const_iterator find(const Key& key) const {
|
||||
return data.find(Pair(key));
|
||||
}
|
||||
|
||||
bool erase(const Key& key) {
|
||||
return data.erase(Pair(key));
|
||||
}
|
||||
bool erase(iterator it) {
|
||||
return data.erase(it);
|
||||
}
|
||||
|
||||
iterator lower_bound(const Key& key) {
|
||||
return data.lower_bound(Pair(key));
|
||||
}
|
||||
|
||||
const_iterator lower_bound(const Key& key) const {
|
||||
return data.lower_bound(Pair(key));
|
||||
}
|
||||
|
||||
iterator upper_bound(const Key& key) {
|
||||
iterator it = lower_bound(key);
|
||||
if (it != end() && it->first == key) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
const_iterator upper_bound(const Key& key) const {
|
||||
const_iterator it = lower_bound(key);
|
||||
if (it != end() && it->first == key) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
Pair& front() { return data.front(); }
|
||||
const Pair& front() const { return data.front(); }
|
||||
Pair& back() { return data.back(); }
|
||||
const Pair& back() const { return data.back(); }
|
||||
|
||||
|
||||
Value& operator[](const Key& key) {
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
return it->second;
|
||||
}
|
||||
Pair pair(key, Value());
|
||||
bool ok = data.insert(pair);
|
||||
FASTLED_ASSERT(ok, "Failed to insert into SortedHeapMap");
|
||||
return data.find(pair)->second; // TODO: optimize.
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
21
FastLED/src/fl/math_macros.h
Normal file
21
FastLED/src/fl/math_macros.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef ABS
|
||||
#define ABS(x) ((x)>0?(x):-(x))
|
||||
#endif
|
||||
|
||||
#ifndef ALMOST_EQUAL
|
||||
#define ALMOST_EQUAL(a,b,small) (ABS((a)-(b))<small)
|
||||
#endif
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#endif
|
||||
31
FastLED/src/fl/namespace.h
Normal file
31
FastLED/src/fl/namespace.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/// @file namespace.h
|
||||
/// Implements the FastLED namespace macros
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(FASTLED_FORCE_NAMESPACE) && !defined(FASTLED_IS_USING_NAMESPACE) && !defined(FASTLED_NAMESPACE)
|
||||
#define FASTLED_NAMESPACE fl
|
||||
#define FASTLED_IS_USING_NAMESPACE 1
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_NAMESPACE
|
||||
#define FASTLED_IS_USING_NAMESPACE 0
|
||||
/// Start of the FastLED namespace
|
||||
#define FASTLED_NAMESPACE_BEGIN
|
||||
/// End of the FastLED namespace
|
||||
#define FASTLED_NAMESPACE_END
|
||||
/// "Using" directive for the namespace
|
||||
#define FASTLED_USING_NAMESPACE
|
||||
#else
|
||||
#define FASTLED_IS_USING_NAMESPACE 1
|
||||
#define FASTLED_NAMESPACE_BEGIN namespace FASTLED_NAMESPACE {
|
||||
#define FASTLED_NAMESPACE_END }
|
||||
|
||||
// We need to create an empty instance of the namespace before we can
|
||||
// declare that we are using it.
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#define FASTLED_USING_NAMESPACE using namespace FASTLED_NAMESPACE;
|
||||
#endif
|
||||
|
||||
12
FastLED/src/fl/pair.h
Normal file
12
FastLED/src/fl/pair.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace fl {
|
||||
|
||||
template<typename Key, typename Value>
|
||||
struct Pair {
|
||||
Key first = Key();
|
||||
Value second = Value();
|
||||
Pair(const Key& k, const Value& v) : first(k), second(v) {}
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
434
FastLED/src/fl/ptr.h
Normal file
434
FastLED/src/fl/ptr.h
Normal file
@@ -0,0 +1,434 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// FastLED smart pointer. This was originally called Ptr<T> but that conflicts with
|
||||
// ArduinoJson::Ptr<T> so it was renamed to Ptr<T>.
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/scoped_ptr.h"
|
||||
#include "fl/template_magic.h"
|
||||
|
||||
|
||||
// Declares a smart pointer. FASTLED_SMART_PTR(Foo) will declare a class FooPtr
|
||||
// which will be a typedef of Ptr<Foo>. After this FooPtr::New(...args) can be
|
||||
// used to create a new instance of Ptr<Foo>.
|
||||
#define FASTLED_SMART_PTR(type) \
|
||||
class type; \
|
||||
using type##Ptr = fl::Ptr<type>;
|
||||
|
||||
#define FASTLED_SMART_PTR_NO_FWD(type) using type##Ptr = fl::Ptr<type>;
|
||||
|
||||
// If you have an interface class that you want to create a smart pointer for,
|
||||
// then you need to use this to bind it to a constructor.
|
||||
#define FASTLED_SMART_PTR_CONSTRUCTOR(type, constructor) \
|
||||
template <> class PtrTraits<type> { \
|
||||
public: \
|
||||
template <typename... Args> static Ptr<type> New(Args... args) { \
|
||||
fl::Ptr<type> ptr = constructor(args...); \
|
||||
return ptr; \
|
||||
} \
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
class Referent; // Inherit this if you want your object to be able to go into a
|
||||
// Ptr, or WeakPtr.
|
||||
template <typename T> class Ptr; // Reference counted smart pointer base class.
|
||||
template <typename T> class WeakPtr; // Weak reference smart pointer base class.
|
||||
|
||||
|
||||
template <typename T> class Ptr;
|
||||
template <typename T> class WeakPtr;
|
||||
|
||||
template <typename T> class PtrTraits {
|
||||
public:
|
||||
using element_type = T;
|
||||
using ptr_type = Ptr<T>;
|
||||
|
||||
template <typename... Args> static Ptr<T> New(Args... args) {
|
||||
T *ptr = new T(args...);
|
||||
return Ptr<T>::TakeOwnership(ptr);
|
||||
}
|
||||
|
||||
static Ptr<T> New() {
|
||||
T *ptr = new T();
|
||||
return Ptr<T>::TakeOwnership(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// Ptr is a reference-counted smart pointer that manages the lifetime of an
|
||||
// object.
|
||||
//
|
||||
// It will work with any class implementing ref(), unref() and destroy().
|
||||
//
|
||||
// Please note that this Ptr class is "sticky" to it's referent, that is, no
|
||||
// automatic conversion from raw pointers to Ptr or vice versa is allowed and
|
||||
// must be done explicitly, see the Ptr::TakeOwnership() and Ptr::NoTracking()
|
||||
// methods.
|
||||
//
|
||||
// To create a Ptr to a concrete object, it's best to use FASTLED_SMART_PTR(Foo)
|
||||
// and then use FooPtr::New(...) to create a new instance of Ptr<Foo>.
|
||||
//
|
||||
// To create a Ptr of an interface bound to a subclass (common for driver code
|
||||
// or when hiding implementation) use the Ptr<InterfaceClass>::TakeOwnership(new
|
||||
// Subclass()) method.
|
||||
//
|
||||
// For objects created statically, use Ptr<Referent>::NoTracking(referent) to
|
||||
// create a Ptr, as this will disable reference tracking but still allow it to
|
||||
// be used as a Ptr.
|
||||
//
|
||||
// Example:
|
||||
// FASTLED_SMART_PTR(Foo);
|
||||
// class Foo: public fl::Referent {};
|
||||
// FooPtr foo = FooPtr::New();
|
||||
//
|
||||
// Example 2: (Manual binding to constructor)
|
||||
// class FooSubclass: public Foo {};
|
||||
// Ptr<Foo> bar = Ptr<FooSubclass>::TakeOwnership(new FooSubclass());
|
||||
//
|
||||
// Example 3: (Provide your own constructor so that FooPtr::New() works to
|
||||
// create a FooSubclass)
|
||||
// class FooSubclass: public Foo { // Foo is an interface, FooSubclass is an
|
||||
// implementation.
|
||||
// public:
|
||||
// static FooPtr New(int a, int b);
|
||||
// };
|
||||
// FASTLED_SMART_PTR_CONSTRUCTOR(Foo, FooSubclass::New);
|
||||
// FooPtr foo = FooPtr::New(1, 2); // this will now work.
|
||||
template <typename T> class Ptr : public PtrTraits<T> {
|
||||
public:
|
||||
friend class PtrTraits<T>;
|
||||
|
||||
template <typename... Args> static Ptr<T> New(Args... args) {
|
||||
return PtrTraits<T>::New(args...);
|
||||
}
|
||||
// Used for low level allocations, typically for pointer to an
|
||||
// implementation where it needs to convert to a Ptr of a base class.
|
||||
static Ptr TakeOwnership(T *ptr) { return Ptr(ptr, true); }
|
||||
|
||||
// Used for low level allocations, typically to handle memory that is
|
||||
// statically allocated where the destructor should not be called when
|
||||
// the refcount reaches 0.
|
||||
static Ptr NoTracking(T &referent) { return Ptr(&referent, false); }
|
||||
|
||||
|
||||
// Allow upcasting of Refs.
|
||||
template <typename U, typename = fl::is_derived<T, U>>
|
||||
Ptr(const Ptr<U>& refptr) : referent_(refptr.get()) {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->ref();
|
||||
}
|
||||
}
|
||||
|
||||
static Ptr<T> Null() { return Ptr<T>(); }
|
||||
|
||||
Ptr() : referent_(nullptr) {}
|
||||
|
||||
// Forbidden to convert a raw pointer to a Referent into a Ptr, because
|
||||
// it's possible that the raw pointer comes from the stack or static memory.
|
||||
Ptr(T *referent) = delete;
|
||||
Ptr &operator=(T *referent) = delete;
|
||||
|
||||
Ptr(const Ptr &other) : referent_(other.referent_) {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->ref();
|
||||
}
|
||||
}
|
||||
|
||||
Ptr(Ptr &&other) noexcept : referent_(other.referent_) {
|
||||
other.referent_ = nullptr;
|
||||
}
|
||||
|
||||
~Ptr() {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->unref();
|
||||
}
|
||||
}
|
||||
|
||||
Ptr &operator=(const Ptr &other) {
|
||||
if (this != &other) {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->unref();
|
||||
}
|
||||
referent_ = other.referent_;
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->ref();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Either returns the weakptr if it exists, or an empty weakptr.
|
||||
WeakPtr<T> weakRefNoCreate() const;
|
||||
WeakPtr<T> weakPtr() const { return WeakPtr<T>(*this); }
|
||||
|
||||
bool operator==(const T *other) const { return referent_ == other; }
|
||||
|
||||
bool operator!=(const T *other) const { return referent_ != other; }
|
||||
|
||||
bool operator==(const Ptr &other) const {
|
||||
return referent_ == other.referent_;
|
||||
}
|
||||
bool operator!=(const Ptr &other) const {
|
||||
return referent_ != other.referent_;
|
||||
}
|
||||
|
||||
bool operator<(const Ptr &other) const {
|
||||
return referent_ < other.referent_;
|
||||
}
|
||||
|
||||
Ptr &operator=(Ptr &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->unref();
|
||||
}
|
||||
referent_ = other.referent_;
|
||||
other.referent_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
T *get() const { return referent_; }
|
||||
|
||||
T *operator->() const { return referent_; }
|
||||
|
||||
T &operator*() const { return *referent_; }
|
||||
|
||||
explicit operator bool() const noexcept { return referent_ != nullptr; }
|
||||
|
||||
void reset() {
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->unref();
|
||||
}
|
||||
referent_ = nullptr;
|
||||
}
|
||||
|
||||
void reset(Ptr<T> &refptr) {
|
||||
if (refptr.referent_ != referent_) {
|
||||
if (refptr.referent_ && refptr.isOwned()) {
|
||||
refptr.referent_->ref();
|
||||
}
|
||||
if (referent_ && isOwned()) {
|
||||
referent_->unref();
|
||||
}
|
||||
referent_ = refptr.referent_;
|
||||
}
|
||||
}
|
||||
|
||||
// Releases the pointer from reference counting from this Ptr.
|
||||
T *release() {
|
||||
T *temp = referent_;
|
||||
referent_ = nullptr;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void swap(Ptr &other) noexcept {
|
||||
T *temp = referent_;
|
||||
referent_ = other.referent_;
|
||||
other.referent_ = temp;
|
||||
}
|
||||
|
||||
bool isOwned() const { return referent_ && referent_->ref_count() > 0; }
|
||||
|
||||
private:
|
||||
Ptr(T *referent, bool from_heap) : referent_(referent) {
|
||||
if (referent_ && from_heap) {
|
||||
referent_->ref();
|
||||
}
|
||||
}
|
||||
T *referent_;
|
||||
};
|
||||
|
||||
// Don't inherit from this, this is an internal object.
|
||||
class WeakReferent {
|
||||
public:
|
||||
WeakReferent() : mRefCount(0), mReferent(nullptr) {}
|
||||
~WeakReferent() {}
|
||||
|
||||
void ref() { mRefCount++; }
|
||||
int ref_count() const { return mRefCount; }
|
||||
void unref() {
|
||||
if (--mRefCount == 0) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
void destroy() { delete this; }
|
||||
void setReferent(Referent *referent) { mReferent = referent; }
|
||||
Referent *getReferent() const { return mReferent; }
|
||||
|
||||
protected:
|
||||
WeakReferent(const WeakReferent &) = default;
|
||||
WeakReferent &operator=(const WeakReferent &) = default;
|
||||
WeakReferent(WeakReferent &&) = default;
|
||||
WeakReferent &operator=(WeakReferent &&) = default;
|
||||
|
||||
private:
|
||||
mutable int mRefCount;
|
||||
Referent *mReferent;
|
||||
};
|
||||
|
||||
template <typename T> class WeakPtr {
|
||||
public:
|
||||
WeakPtr() : mWeakPtr() {}
|
||||
|
||||
WeakPtr(const Ptr<T> &ptr) {
|
||||
if (ptr) {
|
||||
WeakPtr weakRefNoCreate = ptr.weakRefNoCreate();
|
||||
bool expired = weakRefNoCreate.expired();
|
||||
if (expired) {
|
||||
Ptr<WeakReferent> weakRefNoCreate = Ptr<WeakReferent>::New();
|
||||
ptr->setWeakPtr(weakRefNoCreate);
|
||||
weakRefNoCreate->setReferent(ptr.get());
|
||||
}
|
||||
mWeakPtr = ptr->mWeakPtr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U> WeakPtr(const Ptr<U> &ptr) : mWeakPtr(ptr->mWeakPtr) {
|
||||
if (ptr) {
|
||||
WeakPtr weakRefNoCreate = ptr.weakRefNoCreate();
|
||||
bool expired = weakRefNoCreate.expired();
|
||||
if (expired) {
|
||||
Ptr<WeakReferent> weakRefNoCreate = Ptr<WeakReferent>::New();
|
||||
ptr->setWeakPtr(weakRefNoCreate);
|
||||
weakRefNoCreate->setReferent(ptr.get());
|
||||
}
|
||||
mWeakPtr = ptr->mWeakPtr;
|
||||
}
|
||||
}
|
||||
|
||||
WeakPtr(const WeakPtr &other) : mWeakPtr(other.mWeakPtr) {}
|
||||
|
||||
template <typename U>
|
||||
WeakPtr(const WeakPtr<U> &other) : mWeakPtr(other.mWeakPtr) {}
|
||||
|
||||
WeakPtr(WeakPtr &&other) noexcept : mWeakPtr(other.mWeakPtr) {}
|
||||
|
||||
~WeakPtr() { reset(); }
|
||||
|
||||
operator bool() const { return mWeakPtr && mWeakPtr->getReferent(); }
|
||||
|
||||
bool operator!() const {
|
||||
bool ok = *this;
|
||||
return !ok;
|
||||
}
|
||||
|
||||
bool operator==(const WeakPtr &other) const {
|
||||
return mWeakPtr == other.mWeakPtr;
|
||||
}
|
||||
|
||||
bool operator!=(const WeakPtr &other) const {
|
||||
return !(mWeakPtr != other.mWeakPtr);
|
||||
}
|
||||
|
||||
bool operator==(const T *other) const { return lock().get() == other; }
|
||||
|
||||
bool operator==(T *other) const {
|
||||
if (!mWeakPtr) {
|
||||
return other == nullptr;
|
||||
}
|
||||
return mWeakPtr->getReferent() == other;
|
||||
}
|
||||
|
||||
bool operator==(const Ptr<T> &other) const {
|
||||
if (!mWeakPtr) {
|
||||
return !other;
|
||||
}
|
||||
return mWeakPtr->getReferent() == other.get();
|
||||
}
|
||||
|
||||
bool operator!=(const T *other) const {
|
||||
bool equal = *this == other;
|
||||
return !equal;
|
||||
}
|
||||
|
||||
WeakPtr &operator=(const WeakPtr &other) {
|
||||
this->mWeakPtr = other.mWeakPtr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ptr<T> lock() const {
|
||||
if (!mWeakPtr) {
|
||||
return Ptr<T>();
|
||||
}
|
||||
T* out = static_cast<T*>(mWeakPtr->getReferent());
|
||||
if (out->ref_count() == 0) {
|
||||
// This is a static object, so the refcount is 0.
|
||||
return Ptr<T>::NoTracking(*out);
|
||||
}
|
||||
// This is a heap object, so we need to ref it.
|
||||
return Ptr<T>::TakeOwnership(static_cast<T *>(out));
|
||||
}
|
||||
|
||||
bool expired() const {
|
||||
if (!mWeakPtr) {
|
||||
return true;
|
||||
}
|
||||
if (!mWeakPtr->getReferent()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (mWeakPtr) {
|
||||
mWeakPtr.reset();
|
||||
}
|
||||
}
|
||||
Ptr<WeakReferent> mWeakPtr;
|
||||
};
|
||||
|
||||
// Objects that inherit this class can be reference counted and put into
|
||||
// a Ptr object. They can also be put into a WeakPtr object.
|
||||
class Referent {
|
||||
public:
|
||||
virtual int ref_count() const;
|
||||
|
||||
protected:
|
||||
Referent();
|
||||
virtual ~Referent();
|
||||
Referent(const Referent &);
|
||||
Referent &operator=(const Referent &);
|
||||
Referent(Referent &&);
|
||||
Referent &operator=(Referent &&);
|
||||
|
||||
virtual void ref();
|
||||
virtual void unref();
|
||||
virtual void destroy();
|
||||
|
||||
private:
|
||||
friend class WeakReferent;
|
||||
template <typename T> friend class Ptr;
|
||||
template <typename T> friend class WeakPtr;
|
||||
void setWeakPtr(Ptr<WeakReferent> weakRefNoCreate) {
|
||||
mWeakPtr = weakRefNoCreate;
|
||||
}
|
||||
mutable int mRefCount;
|
||||
Ptr<WeakReferent> mWeakPtr; // Optional weak reference to this object.
|
||||
};
|
||||
|
||||
template <typename T> inline WeakPtr<T> Ptr<T>::weakRefNoCreate() const {
|
||||
if (!referent_) {
|
||||
return WeakPtr<T>();
|
||||
}
|
||||
WeakReferent *tmp = get()->mWeakPtr.get();
|
||||
if (!tmp) {
|
||||
return WeakPtr<T>();
|
||||
}
|
||||
T *referent = static_cast<T *>(tmp->getReferent());
|
||||
if (!referent) {
|
||||
return WeakPtr<T>();
|
||||
}
|
||||
// At this point, we know that our weak referent is valid.
|
||||
// However, the template parameter ensures that either we have
|
||||
// an exact type, or are at least down-castable of it.
|
||||
WeakPtr<T> out;
|
||||
out.mWeakPtr = get()->mWeakPtr;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
100
FastLED/src/fl/rectangular_draw_buffer.cpp
Normal file
100
FastLED/src/fl/rectangular_draw_buffer.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
#include "fl/rectangular_draw_buffer.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "rgbw.h"
|
||||
#include "fl/allocator.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
DrawItem::DrawItem(uint8_t pin, uint16_t numLeds, bool is_rgbw)
|
||||
: mPin(pin), mIsRgbw(is_rgbw) {
|
||||
if (is_rgbw) {
|
||||
numLeds = Rgbw::size_as_rgb(numLeds);
|
||||
}
|
||||
mNumBytes = numLeds * 3;
|
||||
}
|
||||
|
||||
Slice<uint8_t> RectangularDrawBuffer::getLedsBufferBytesForPin(uint8_t pin, bool clear_first) {
|
||||
auto it = mPinToLedSegment.find(pin);
|
||||
if (it == mPinToLedSegment.end()) {
|
||||
FASTLED_ASSERT(false, "Pin not found in RectangularDrawBuffer");
|
||||
return fl::Slice<uint8_t>();
|
||||
}
|
||||
fl::Slice<uint8_t> slice = it->second;
|
||||
if (clear_first) {
|
||||
memset(slice.data(), 0, slice.size() * sizeof(slice[0]));
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
bool RectangularDrawBuffer::onQueuingStart() {
|
||||
if (mQueueState == QUEUEING) {
|
||||
return false;
|
||||
}
|
||||
mQueueState = QUEUEING;
|
||||
mPinToLedSegment.clear();
|
||||
mDrawList.swap(mPrevDrawList);
|
||||
mDrawList.clear();
|
||||
if (mAllLedsBufferUint8Size > 0) {
|
||||
memset(mAllLedsBufferUint8.get(), 0, mAllLedsBufferUint8Size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RectangularDrawBuffer::queue(const DrawItem &item) {
|
||||
mDrawList.push_back(item);
|
||||
}
|
||||
|
||||
bool RectangularDrawBuffer::onQueuingDone() {
|
||||
if (mQueueState == QUEUE_DONE) {
|
||||
return false;
|
||||
}
|
||||
mQueueState = QUEUE_DONE;
|
||||
mDrawListChangedThisFrame = mDrawList != mPrevDrawList;
|
||||
// iterator through the current draw objects and calculate the total
|
||||
// number of bytes (representing RGB or RGBW) that will be drawn this frame.
|
||||
uint32_t total_bytes = 0;
|
||||
uint32_t max_bytes_in_strip = 0;
|
||||
uint32_t num_strips = 0;
|
||||
getBlockInfo(&num_strips, &max_bytes_in_strip, &total_bytes);
|
||||
if (total_bytes > mAllLedsBufferUint8Size) {
|
||||
uint8_t* old_ptr = mAllLedsBufferUint8.release();
|
||||
fl::LargeBlockAllocator<uint8_t>::Free(old_ptr);
|
||||
uint8_t* ptr = fl::LargeBlockAllocator<uint8_t>::Alloc(total_bytes);
|
||||
mAllLedsBufferUint8.reset(ptr);
|
||||
}
|
||||
mAllLedsBufferUint8Size = total_bytes;
|
||||
uint32_t offset = 0;
|
||||
for (auto it = mDrawList.begin(); it != mDrawList.end(); ++it) {
|
||||
uint8_t pin = it->mPin;
|
||||
Slice<uint8_t> slice(mAllLedsBufferUint8.get() + offset,
|
||||
max_bytes_in_strip);
|
||||
mPinToLedSegment[pin] = slice;
|
||||
offset += max_bytes_in_strip;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t RectangularDrawBuffer::getMaxBytesInStrip() const {
|
||||
uint32_t max_bytes = 0;
|
||||
for (auto it = mDrawList.begin(); it != mDrawList.end(); ++it) {
|
||||
max_bytes = MAX(max_bytes, it->mNumBytes);
|
||||
}
|
||||
return max_bytes;
|
||||
}
|
||||
|
||||
uint32_t RectangularDrawBuffer::getTotalBytes() const {
|
||||
uint32_t num_strips = mDrawList.size();
|
||||
uint32_t max_bytes = getMaxBytesInStrip();
|
||||
return num_strips * max_bytes;
|
||||
}
|
||||
|
||||
void RectangularDrawBuffer::getBlockInfo(uint32_t *num_strips,
|
||||
uint32_t *bytes_per_strip,
|
||||
uint32_t *total_bytes) const {
|
||||
*num_strips = mDrawList.size();
|
||||
*bytes_per_strip = getMaxBytesInStrip();
|
||||
*total_bytes = (*num_strips) * (*bytes_per_strip);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
65
FastLED/src/fl/rectangular_draw_buffer.h
Normal file
65
FastLED/src/fl/rectangular_draw_buffer.h
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/map.h"
|
||||
#include "fl/scoped_ptr.h"
|
||||
#include "fl/slice.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
struct DrawItem {
|
||||
DrawItem() = default;
|
||||
DrawItem(uint8_t pin, uint16_t numLeds, bool is_rgbw);
|
||||
uint8_t mPin = 0;
|
||||
uint32_t mNumBytes = 0;
|
||||
bool mIsRgbw = false;
|
||||
bool operator!=(const DrawItem &other) const {
|
||||
return mPin != other.mPin || mNumBytes != other.mNumBytes ||
|
||||
mIsRgbw != other.mIsRgbw;
|
||||
}
|
||||
};
|
||||
|
||||
// Needed by controllers that require a compact, rectangular buffer of pixel data.
|
||||
// Namely, ObjectFLED and the I2S controllers.
|
||||
// This class handles using multiple independent strips of LEDs, each with their own
|
||||
// buffer of pixel data. The strips are not necessarily contiguous in memory.
|
||||
// One or more DrawItems containing the pin number and number are queued
|
||||
// up. When the queue-ing is done, the buffers are compacted into the rectangular buffer.
|
||||
// Data access is achieved through a Slice<uint8_t> representing the pixel data for that pin.
|
||||
class RectangularDrawBuffer {
|
||||
public:
|
||||
typedef fl::HeapVector<DrawItem> DrawList;
|
||||
// We manually manage the memory for the buffer of all LEDs so that it can go
|
||||
// into psram on ESP32S3, which is managed by fl::LargeBlockAllocator.
|
||||
scoped_array<uint8_t> mAllLedsBufferUint8;
|
||||
uint32_t mAllLedsBufferUint8Size = 0;
|
||||
fl::FixedMap<uint8_t, fl::Slice<uint8_t>, 50> mPinToLedSegment;
|
||||
DrawList mDrawList;
|
||||
DrawList mPrevDrawList;
|
||||
bool mDrawListChangedThisFrame = false;
|
||||
|
||||
enum QueueState { IDLE, QUEUEING, QUEUE_DONE };
|
||||
QueueState mQueueState = IDLE;
|
||||
|
||||
RectangularDrawBuffer() = default;
|
||||
~RectangularDrawBuffer() = default;
|
||||
|
||||
fl::Slice<uint8_t> getLedsBufferBytesForPin(uint8_t pin, bool clear_first=true);
|
||||
|
||||
// Safe to call multiple times before calling queue() once. Returns true on the first call, false after.
|
||||
bool onQueuingStart();
|
||||
void queue(const DrawItem &item);
|
||||
|
||||
// Safe to call multiple times before calling onQueueingStart() again. Returns true on the first call, false after.
|
||||
bool onQueuingDone();
|
||||
uint32_t getMaxBytesInStrip() const;
|
||||
uint32_t getTotalBytes() const;
|
||||
void getBlockInfo(uint32_t *num_strips, uint32_t *bytes_per_strip,
|
||||
uint32_t *total_bytes) const;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
30
FastLED/src/fl/ref.cpp
Normal file
30
FastLED/src/fl/ref.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "fl/ptr.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
Referent::Referent() : mRefCount(0) {}
|
||||
Referent::~Referent() = default;
|
||||
void Referent::ref() { mRefCount++; }
|
||||
|
||||
int Referent::ref_count() const { return mRefCount; }
|
||||
|
||||
void Referent::unref() {
|
||||
if (--mRefCount == 0) {
|
||||
if (mWeakPtr) {
|
||||
mWeakPtr->setReferent(nullptr);
|
||||
mWeakPtr.reset();
|
||||
}
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void Referent::destroy() { delete this; }
|
||||
|
||||
Referent::Referent(const Referent &) = default;
|
||||
Referent &Referent::operator=(const Referent &) = default;
|
||||
Referent::Referent(Referent &&) = default;
|
||||
Referent &Referent::operator=(Referent &&) = default;
|
||||
|
||||
} // namespace fl
|
||||
15
FastLED/src/fl/register.h
Normal file
15
FastLED/src/fl/register.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/// @def FASTLED_REGISTER
|
||||
/// Helper macro to replace the deprecated 'register' keyword if we're
|
||||
/// using modern C++ where it's been removed entirely.
|
||||
|
||||
#if __cplusplus < 201703L
|
||||
#define FASTLED_REGISTER register
|
||||
#else
|
||||
#ifdef FASTLED_REGISTER
|
||||
#undef FASTLED_REGISTER
|
||||
#endif
|
||||
#define FASTLED_REGISTER
|
||||
#endif
|
||||
157
FastLED/src/fl/scoped_ptr.h
Normal file
157
FastLED/src/fl/scoped_ptr.h
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct ArrayDeleter {
|
||||
void operator()(T* ptr) {
|
||||
delete[] ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct PointerDeleter {
|
||||
void operator()(T* ptr) {
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Deleter = PointerDeleter<T>>
|
||||
class scoped_ptr {
|
||||
public:
|
||||
// Constructor
|
||||
explicit scoped_ptr(T *ptr = nullptr, Deleter deleter = Deleter())
|
||||
: ptr_(ptr), deleter_(deleter) {}
|
||||
|
||||
// Destructor
|
||||
~scoped_ptr() { deleter_(ptr_); }
|
||||
|
||||
// Disable copy semantics (no copying allowed)
|
||||
scoped_ptr(const scoped_ptr &) = delete;
|
||||
scoped_ptr &operator=(const scoped_ptr &) = delete;
|
||||
|
||||
// Move constructor
|
||||
scoped_ptr(scoped_ptr &&other) noexcept
|
||||
: ptr_(other.ptr_), deleter_(other.deleter_) {
|
||||
other.ptr_ = nullptr;
|
||||
other.deleter_ = {};
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
scoped_ptr &operator=(scoped_ptr &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset(other.ptr_);
|
||||
deleter_ = other.deleter_;
|
||||
other.ptr_ = nullptr;
|
||||
other.deleter_ = {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Access the managed object
|
||||
T *operator->() const { return ptr_; }
|
||||
|
||||
// Dereference the managed object
|
||||
T &operator*() const { return *ptr_; }
|
||||
|
||||
// Get the raw pointer
|
||||
T *get() const { return ptr_; }
|
||||
|
||||
// Boolean conversion operator
|
||||
explicit operator bool() const noexcept { return ptr_ != nullptr; }
|
||||
|
||||
// Logical NOT operator
|
||||
bool operator!() const noexcept { return ptr_ == nullptr; }
|
||||
|
||||
// Release the managed object and reset the pointer
|
||||
void reset(T *ptr = nullptr) {
|
||||
if (ptr_ != ptr) {
|
||||
deleter_(ptr_);
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr_;
|
||||
ptr_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
T *ptr_; // Managed pointer
|
||||
Deleter deleter_; // Custom deleter
|
||||
};
|
||||
|
||||
template <typename T, typename Deleter=ArrayDeleter<T>> class scoped_array {
|
||||
public:
|
||||
// Constructor
|
||||
explicit scoped_array(T *arr = nullptr) : arr_(arr) {}
|
||||
scoped_array(T *arr, Deleter deleter) : arr_(arr), deleter_(deleter) {}
|
||||
|
||||
// Destructor
|
||||
~scoped_array() { deleter_(arr_); }
|
||||
|
||||
// Disable copy semantics (no copying allowed)
|
||||
scoped_array(const scoped_array &) = delete;
|
||||
scoped_array &operator=(const scoped_array &) = delete;
|
||||
|
||||
// Move constructor
|
||||
scoped_array(scoped_array &&other) noexcept : arr_(other.arr_), deleter_(other.deleter_) {
|
||||
other.arr_ = nullptr;
|
||||
other.deleter_ = {};
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
scoped_array &operator=(scoped_array &&other) noexcept {
|
||||
if (this != &other) {
|
||||
reset(other.arr_);
|
||||
other.arr_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Array subscript operator
|
||||
T &operator[](size_t i) const { return arr_[i]; }
|
||||
|
||||
// Get the raw pointer
|
||||
T *get() const { return arr_; }
|
||||
|
||||
// Boolean conversion operator
|
||||
explicit operator bool() const noexcept { return arr_ != nullptr; }
|
||||
|
||||
// Logical NOT operator
|
||||
bool operator!() const noexcept { return arr_ == nullptr; }
|
||||
|
||||
// Release the managed array and reset the pointer
|
||||
void reset(T *arr = nullptr) {
|
||||
if (arr_ == arr) {
|
||||
return;
|
||||
}
|
||||
deleter_(arr_);
|
||||
arr_ = arr;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
reset();
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = arr_;
|
||||
arr_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
T *arr_; // Managed array pointer
|
||||
Deleter deleter_ = {};
|
||||
};
|
||||
|
||||
|
||||
} // namespace fl
|
||||
230
FastLED/src/fl/screenmap.cpp
Normal file
230
FastLED/src/fl/screenmap.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/* Screenmap maps strip indexes to x,y coordinates. This is used for FastLED Web
|
||||
* to map the 1D strip to a 2D grid. Note that the strip can have arbitrary
|
||||
* size. this was first motivated during the (attempted? Oct. 19th 2024) port of
|
||||
* the Chromancer project to FastLED Web.
|
||||
*/
|
||||
|
||||
#include "fl/screenmap.h"
|
||||
|
||||
#include "fl/json.h"
|
||||
#include "fl/map.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/str.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/namespace.h"
|
||||
#include <math.h>
|
||||
|
||||
namespace fl {
|
||||
|
||||
ScreenMap ScreenMap::Circle(int numLeds, float cm_between_leds,
|
||||
float cm_led_diameter) {
|
||||
ScreenMap screenMap = ScreenMap(numLeds);
|
||||
float circumference = numLeds * cm_between_leds;
|
||||
float radius = circumference / (2 * PI);
|
||||
|
||||
for (int i = 0; i < numLeds; i++) {
|
||||
float angle = i * 2 * PI / numLeds;
|
||||
float x = radius * cos(angle) * 2;
|
||||
float y = radius * sin(angle) * 2;
|
||||
screenMap[i] = {x, y};
|
||||
}
|
||||
screenMap.setDiameter(cm_led_diameter);
|
||||
return screenMap;
|
||||
}
|
||||
|
||||
bool ScreenMap::ParseJson(const char *jsonStrScreenMap,
|
||||
FixedMap<Str, ScreenMap, 16> *segmentMaps, Str *err) {
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
if (err) {
|
||||
*err = "JSON not enabled";
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
JsonDocument doc;
|
||||
Str _err;
|
||||
if (!err) {
|
||||
err = &_err;
|
||||
}
|
||||
|
||||
bool ok = parseJson(jsonStrScreenMap, &doc, err);
|
||||
if (!ok) {
|
||||
FASTLED_WARN("Failed to parse json: " << err->c_str());
|
||||
return false;
|
||||
}
|
||||
auto map = doc["map"];
|
||||
for (auto kv : map.as<FLArduinoJson::JsonObject>()) {
|
||||
auto segment = kv.value();
|
||||
auto x = segment["x"];
|
||||
auto y = segment["y"];
|
||||
auto obj = segment["diameter"];
|
||||
float diameter = -1.0f;
|
||||
if (obj.is<float>()) {
|
||||
float d = obj.as<float>();
|
||||
if (d > 0.0f) {
|
||||
diameter = d;
|
||||
}
|
||||
}
|
||||
auto n = x.size();
|
||||
ScreenMap segment_map(n, diameter);
|
||||
for (uint16_t j = 0; j < n; j++) {
|
||||
segment_map.set(j, pair_xy_float{x[j], y[j]});
|
||||
}
|
||||
segmentMaps->insert(kv.key().c_str(), segment_map);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ScreenMap::ParseJson(const char *jsonStrScreenMap,
|
||||
const char *screenMapName, ScreenMap *screenmap,
|
||||
Str *err) {
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
if (err) {
|
||||
*err = "JSON not enabled";
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
FixedMap<Str, ScreenMap, 16> segmentMaps;
|
||||
bool ok = ParseJson(jsonStrScreenMap, &segmentMaps, err);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
if (segmentMaps.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (segmentMaps.has(screenMapName)) {
|
||||
*screenmap = segmentMaps[screenMapName];
|
||||
return true;
|
||||
}
|
||||
Str _err = "ScreenMap not found: ";
|
||||
_err.append(screenMapName);
|
||||
if (err) {
|
||||
*err = _err;
|
||||
}
|
||||
FASTLED_WARN(_err.c_str());
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScreenMap::toJson(const FixedMap<Str, ScreenMap, 16> &segmentMaps,
|
||||
JsonDocument *_doc) {
|
||||
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
return;
|
||||
#else
|
||||
auto &doc = *_doc;
|
||||
auto map = doc["map"].to<FLArduinoJson::JsonObject>();
|
||||
for (auto kv : segmentMaps) {
|
||||
auto segment = map[kv.first].to<FLArduinoJson::JsonObject>();
|
||||
auto x_array = segment["x"].to<FLArduinoJson::JsonArray>();
|
||||
auto y_array = segment["y"].to<FLArduinoJson::JsonArray>();
|
||||
for (uint16_t i = 0; i < kv.second.getLength(); i++) {
|
||||
const pair_xy_float &xy = kv.second[i];
|
||||
x_array.add(xy.x);
|
||||
y_array.add(xy.y);
|
||||
}
|
||||
float diameter = kv.second.getDiameter();
|
||||
if (diameter < 0.0f) {
|
||||
diameter = .5f; // 5mm.
|
||||
}
|
||||
if (diameter > 0.0f) {
|
||||
segment["diameter"] = diameter;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScreenMap::toJsonStr(const FixedMap<Str, ScreenMap, 16> &segmentMaps,
|
||||
Str *jsonBuffer) {
|
||||
#if !FASTLED_ENABLE_JSON
|
||||
return;
|
||||
#else
|
||||
JsonDocument doc;
|
||||
toJson(segmentMaps, &doc);
|
||||
fl::toJson(doc, jsonBuffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
ScreenMap::ScreenMap(uint32_t length, float mDiameter)
|
||||
: length(length), mDiameter(mDiameter) {
|
||||
mLookUpTable = LUTXYFLOATPtr::New(length);
|
||||
LUTXYFLOAT &lut = *mLookUpTable.get();
|
||||
pair_xy_float *data = lut.getData();
|
||||
for (uint32_t x = 0; x < length; x++) {
|
||||
data[x] = {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
ScreenMap::ScreenMap(const pair_xy_float *lut, uint32_t length, float diameter)
|
||||
: length(length), mDiameter(diameter) {
|
||||
mLookUpTable = LUTXYFLOATPtr::New(length);
|
||||
LUTXYFLOAT &lut16xy = *mLookUpTable.get();
|
||||
pair_xy_float *data = lut16xy.getData();
|
||||
for (uint32_t x = 0; x < length; x++) {
|
||||
data[x] = lut[x];
|
||||
}
|
||||
}
|
||||
|
||||
ScreenMap::ScreenMap(const ScreenMap &other) {
|
||||
mDiameter = other.mDiameter;
|
||||
length = other.length;
|
||||
mLookUpTable = other.mLookUpTable;
|
||||
}
|
||||
|
||||
void ScreenMap::set(uint16_t index, const pair_xy_float &p) {
|
||||
if (mLookUpTable) {
|
||||
LUTXYFLOAT &lut = *mLookUpTable.get();
|
||||
auto *data = lut.getData();
|
||||
data[index] = p;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenMap::setDiameter(float diameter) { mDiameter = diameter; }
|
||||
|
||||
pair_xy_float ScreenMap::mapToIndex(uint32_t x) const {
|
||||
if (x >= length || !mLookUpTable) {
|
||||
return {0, 0};
|
||||
}
|
||||
LUTXYFLOAT &lut = *mLookUpTable.get();
|
||||
pair_xy_float screen_coords = lut[x];
|
||||
return screen_coords;
|
||||
}
|
||||
|
||||
uint32_t ScreenMap::getLength() const { return length; }
|
||||
|
||||
float ScreenMap::getDiameter() const { return mDiameter; }
|
||||
|
||||
const pair_xy_float &ScreenMap::empty() {
|
||||
static const pair_xy_float s_empty = pair_xy_float(0, 0);
|
||||
return s_empty;
|
||||
}
|
||||
|
||||
const pair_xy_float &ScreenMap::operator[](uint32_t x) const {
|
||||
if (x >= length || !mLookUpTable) {
|
||||
return empty(); // better than crashing.
|
||||
}
|
||||
LUTXYFLOAT &lut = *mLookUpTable.get();
|
||||
return lut[x];
|
||||
}
|
||||
|
||||
pair_xy_float &ScreenMap::operator[](uint32_t x) {
|
||||
if (x >= length || !mLookUpTable) {
|
||||
return const_cast<pair_xy_float &>(empty()); // better than crashing.
|
||||
}
|
||||
LUTXYFLOAT &lut = *mLookUpTable.get();
|
||||
auto *data = lut.getData();
|
||||
return data[x];
|
||||
}
|
||||
|
||||
ScreenMap &ScreenMap::operator=(const ScreenMap &other) {
|
||||
if (this != &other) {
|
||||
mDiameter = other.mDiameter;
|
||||
length = other.length;
|
||||
mLookUpTable = other.mLookUpTable;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
86
FastLED/src/fl/screenmap.h
Normal file
86
FastLED/src/fl/screenmap.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/force_inline.h"
|
||||
#include "fl/lut.h"
|
||||
#include "fl/ptr.h"
|
||||
|
||||
|
||||
#include "fl/map.h"
|
||||
#include "fl/str.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
/* Screenmap maps strip indexes to x,y coordinates. This is used for FastLED Web
|
||||
* to map the 1D strip to a 2D screen. Note that the strip can have arbitrary
|
||||
* size. this was first motivated by the effort to port theChromancer project to
|
||||
* FastLED for the browser.
|
||||
*/
|
||||
|
||||
namespace fl {
|
||||
|
||||
class Str;
|
||||
class JsonDocument;
|
||||
|
||||
// ScreenMap screen map maps strip indexes to x,y coordinates for a ui
|
||||
// canvas in float format.
|
||||
// This class is cheap to copy as it uses smart pointers for shared data.
|
||||
class ScreenMap {
|
||||
public:
|
||||
static ScreenMap Circle(int numLeds, float cm_between_leds = 1.5f,
|
||||
float cm_led_diameter = 0.5f);
|
||||
|
||||
ScreenMap() = default;
|
||||
|
||||
// is_reverse is false by default for linear layout
|
||||
ScreenMap(uint32_t length, float mDiameter = -1.0f);
|
||||
|
||||
ScreenMap(const pair_xy_float *lut, uint32_t length,
|
||||
float diameter = -1.0);
|
||||
|
||||
template <uint32_t N>
|
||||
ScreenMap(const pair_xy_float (&lut)[N], float diameter = -1.0)
|
||||
: ScreenMap(lut, N, diameter) {}
|
||||
|
||||
ScreenMap(const ScreenMap &other);
|
||||
|
||||
const pair_xy_float &operator[](uint32_t x) const;
|
||||
|
||||
void set(uint16_t index, const pair_xy_float &p);
|
||||
|
||||
pair_xy_float &operator[](uint32_t x);
|
||||
|
||||
// TODO: change this name to setDiameterLed. Default should be .5f
|
||||
// for 5 mm ws lense.
|
||||
void setDiameter(float diameter);
|
||||
|
||||
// define the assignment operator
|
||||
ScreenMap &operator=(const ScreenMap &other);
|
||||
|
||||
pair_xy_float mapToIndex(uint32_t x) const;
|
||||
|
||||
uint32_t getLength() const;
|
||||
// The diameter each point represents.
|
||||
float getDiameter() const;
|
||||
|
||||
static bool ParseJson(const char *jsonStrScreenMap,
|
||||
FixedMap<Str, ScreenMap, 16> *segmentMaps,
|
||||
Str *err = nullptr);
|
||||
|
||||
static bool ParseJson(const char *jsonStrScreenMap,
|
||||
const char *screenMapName, ScreenMap *screenmap,
|
||||
Str *err = nullptr);
|
||||
|
||||
static void toJsonStr(const FixedMap<Str, ScreenMap, 16> &,
|
||||
Str *jsonBuffer);
|
||||
static void toJson(const FixedMap<Str, ScreenMap, 16> &,
|
||||
JsonDocument *doc);
|
||||
|
||||
private:
|
||||
static const pair_xy_float &empty();
|
||||
uint32_t length = 0;
|
||||
float mDiameter = -1.0f; // Only serialized if it's not > 0.0f.
|
||||
LUTXYFLOATPtr mLookUpTable;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
153
FastLED/src/fl/set.h
Normal file
153
FastLED/src/fl/set.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/vector.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// A simple unordered set implementation with a fixed size.
|
||||
// The user is responsible for making sure that the inserts
|
||||
// do not exceed the capacity of the set, otherwise they will
|
||||
// fail. Because of this limitation, this set is not a drop in
|
||||
// replacement for std::set.
|
||||
template<typename Key, size_t N>
|
||||
class FixedSet {
|
||||
public:
|
||||
typedef FixedVector<Key, N> VectorType;
|
||||
typedef typename VectorType::iterator iterator;
|
||||
typedef typename VectorType::const_iterator const_iterator;
|
||||
|
||||
// Constructor
|
||||
constexpr FixedSet() = default;
|
||||
|
||||
iterator begin() {
|
||||
return data.begin();
|
||||
}
|
||||
iterator end() {
|
||||
return data.end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return data.begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return data.end();
|
||||
}
|
||||
|
||||
iterator find(const Key& key) {
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
if (*it == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const Key& key) const {
|
||||
for (auto it = begin(); it != end(); ++it) {
|
||||
if (*it == key) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
bool insert(const Key& key) {
|
||||
if (data.size() < N) {
|
||||
auto it = find(key);
|
||||
if (it == end()) {
|
||||
data.push_back(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool erase(const Key& key) {
|
||||
auto it = find(key);
|
||||
if (it != end()) {
|
||||
data.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool erase(iterator pos) {
|
||||
if (pos != end()) {
|
||||
data.erase(pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool next(const Key& key, Key* next_key, bool allow_rollover = false) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
++it;
|
||||
if (it != end()) {
|
||||
*next_key = *it;
|
||||
return true;
|
||||
} else if (allow_rollover && !empty()) {
|
||||
*next_key = *begin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool prev(const Key& key, Key* prev_key, bool allow_rollover = false) const {
|
||||
const_iterator it = find(key);
|
||||
if (it != end()) {
|
||||
if (it != begin()) {
|
||||
--it;
|
||||
*prev_key = *it;
|
||||
return true;
|
||||
} else if (allow_rollover && !empty()) {
|
||||
*prev_key = data[data.size() - 1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current size of the set
|
||||
constexpr size_t size() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
return data.empty();
|
||||
}
|
||||
|
||||
// Get the capacity of the set
|
||||
constexpr size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
// Clear the set
|
||||
void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
bool has(const Key& key) const {
|
||||
return find(key) != end();
|
||||
}
|
||||
|
||||
// Return the first element of the set
|
||||
const Key& front() const {
|
||||
return data.front();
|
||||
}
|
||||
|
||||
// Return the last element of the set
|
||||
const Key& back() const {
|
||||
return data.back();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
VectorType data;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
35
FastLED/src/fl/sin32.cpp
Normal file
35
FastLED/src/fl/sin32.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fl {
|
||||
|
||||
const int16_t sinLut[] =
|
||||
{
|
||||
0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793,
|
||||
12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594,
|
||||
23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956,
|
||||
30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757,
|
||||
32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285, 32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571,
|
||||
30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683, 27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731,
|
||||
23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868, 18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279,
|
||||
12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179, 6393, 5602, 4808, 4011, 3212, 2410, 1608, 804,
|
||||
0, -804, -1608, -2410, -3212, -4011, -4808, -5602, -6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793,
|
||||
-12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530, -18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594,
|
||||
-23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790, -27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956,
|
||||
-30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971, -32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757,
|
||||
-32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285, -32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571,
|
||||
-30273, -29956, -29621, -29268, -28898, -28510, -28105, -27683, -27245, -26790, -26319, -25832, -25329, -24811, -24279, -23731,
|
||||
-23170, -22594, -22005, -21403, -20787, -20159, -19519, -18868, -18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279,
|
||||
-12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179, -6393, -5602, -4808, -4011, -3212, -2410, -1608, -804,
|
||||
0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793,
|
||||
12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594,
|
||||
23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956,
|
||||
30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757,
|
||||
32767};
|
||||
|
||||
const int16_t *sinArray = &sinLut[0];
|
||||
|
||||
const int16_t *cosArray = &sinLut[64];
|
||||
|
||||
} // namespace fl
|
||||
70
FastLED/src/fl/sin32.h
Normal file
70
FastLED/src/fl/sin32.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "namespace.h"
|
||||
#include "force_inline.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// fast and accurate sin and cos approximations
|
||||
// they differ only 0.01 % from actual sinf and cosf funtions
|
||||
// sin32 and cos32 use 24 bit unsigned integer arguments
|
||||
// 0 to 16777216 is one cycle
|
||||
// they output an integer between -2147418112 and 2147418112
|
||||
// that's 32767 * 65536
|
||||
// this is because I use int16_t look up table to interpolate the results
|
||||
//
|
||||
// sin16 and cos16 are faster and more accurate funtions that take uint16_t as arguments
|
||||
// and return int16_t as output
|
||||
// they can replace the older implementation and are 62 times more accurate and twice as fast
|
||||
// the downside with these funtions is that they use 640 bytes for the look up table
|
||||
// thats a lot for old microcontrollers but nothing for modern ones
|
||||
//
|
||||
// sin32 and cos32 take about 13 cyces to execute on an esp32
|
||||
// they're 32 times faster than sinf and cosf
|
||||
// you can use choose to use these new by writing
|
||||
// #define USE_SIN_32 before #include "FastLED.h"
|
||||
|
||||
extern const int16_t *sinArray;
|
||||
|
||||
extern const int16_t *cosArray;
|
||||
|
||||
// 0 to 16777216 is a full circle
|
||||
// output is between -2147418112 and 2147418112
|
||||
FASTLED_FORCE_INLINE static int32_t sin32(uint32_t angle)
|
||||
{
|
||||
uint8_t angle256 = angle / 65536;
|
||||
int32_t subAngle = angle % 65536;
|
||||
return sinArray[angle256] * (65536 - subAngle) + sinArray[angle256 + 1] * subAngle;
|
||||
}
|
||||
|
||||
// 0 to 16777216 is a full circle
|
||||
// output is between -2147418112 and 2147418112
|
||||
FASTLED_FORCE_INLINE static int32_t cos32(uint32_t angle)
|
||||
{
|
||||
uint8_t angle256 = angle / 65536;
|
||||
int32_t subAngle = angle % 65536;
|
||||
return cosArray[angle256] * (65536 - subAngle) + cosArray[angle256 + 1] * subAngle;
|
||||
}
|
||||
|
||||
// 0 to 65536 is a full circle
|
||||
// output is between -32767 and 32767
|
||||
FASTLED_FORCE_INLINE static int16_t sin16lut(uint16_t angle)
|
||||
{
|
||||
uint8_t angle256 = angle / 256;
|
||||
int32_t subAngle = angle % 256;
|
||||
return (sinArray[angle256] * (256 - subAngle) + sinArray[angle256 + 1] * subAngle) / 256;
|
||||
}
|
||||
|
||||
// 0 to 65536 is a full circle
|
||||
// output is between -32767 and 32767
|
||||
FASTLED_FORCE_INLINE static int16_t cos16lut(uint16_t angle)
|
||||
{
|
||||
uint8_t angle256 = angle / 256;
|
||||
int32_t subAngle = angle % 256;
|
||||
return (cosArray[angle256] * (256 - subAngle) + cosArray[angle256 + 1] * subAngle) / 256;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
25
FastLED/src/fl/singleton.h
Normal file
25
FastLED/src/fl/singleton.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// A templated singleton class, parameterized by the type of the singleton and an optional integer.
|
||||
template<typename T, int N = 0>
|
||||
class Singleton {
|
||||
public:
|
||||
static T& instance() {
|
||||
static T instance;
|
||||
return instance;
|
||||
}
|
||||
static T* instanceRef() {
|
||||
return &instance();
|
||||
}
|
||||
Singleton(const Singleton&) = delete;
|
||||
Singleton& operator=(const Singleton&) = delete;
|
||||
private:
|
||||
Singleton() = default;
|
||||
~Singleton() = default;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
129
FastLED/src/fl/slice.h
Normal file
129
FastLED/src/fl/slice.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Slice<int> is equivalent to int* with a length. It is used to pass around
|
||||
// arrays of integers with a length, without needing to pass around a separate
|
||||
// length parameter.
|
||||
// It works just like an array of objects, but it also knows its length.
|
||||
template<typename T>
|
||||
class Slice {
|
||||
public:
|
||||
Slice() : mData(nullptr), mSize(0) {}
|
||||
Slice(T* data, size_t size) : mData(data), mSize(size) {}
|
||||
|
||||
Slice(const Slice& other) : mData(other.mData), mSize(other.mSize) {}
|
||||
|
||||
|
||||
Slice& operator=(const Slice& other) {
|
||||
mData = other.mData;
|
||||
mSize = other.mSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Automatic promotion to const Slice<const T>
|
||||
operator Slice<const T>() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
// No bounds checking in embedded environment
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
// No bounds checking in embedded environment
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
T* begin() const {
|
||||
return mData;
|
||||
}
|
||||
|
||||
T* end() const {
|
||||
return mData + mSize;
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
const T* data() const {
|
||||
return mData;
|
||||
}
|
||||
|
||||
T* data() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
Slice<T> slice(size_t start, size_t end) const {
|
||||
// No bounds checking in embedded environment
|
||||
return Slice<T>(mData + start, end - start);
|
||||
}
|
||||
|
||||
Slice<T> slice(size_t start) const {
|
||||
// No bounds checking in embedded environment
|
||||
return Slice<T>(mData + start, mSize - start);
|
||||
}
|
||||
|
||||
// Find the first occurrence of a value in the slice
|
||||
// Returns the index of the first occurrence if found, or size_t(-1) if not found
|
||||
size_t find(const T& value) const {
|
||||
for (size_t i = 0; i < mSize; ++i) {
|
||||
if (mData[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
bool pop_front() {
|
||||
if (mSize == 0) {
|
||||
return false;
|
||||
}
|
||||
++mData;
|
||||
--mSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop_back() {
|
||||
if (mSize == 0) {
|
||||
return false;
|
||||
}
|
||||
--mSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
T& front() {
|
||||
return *mData;
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return *mData;
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return *(mData + mSize - 1);
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return *(mData + mSize - 1);
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
return mSize == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T* mData;
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
171
FastLED/src/fl/str.cpp
Normal file
171
FastLED/src/fl/str.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fl/str.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
namespace string_functions {
|
||||
|
||||
static int itoa(int value, char *sp, int radix) {
|
||||
char tmp[16]; // be careful with the length of the buffer
|
||||
char *tp = tmp;
|
||||
int i;
|
||||
unsigned v;
|
||||
|
||||
int sign = (radix == 10 && value < 0);
|
||||
if (sign)
|
||||
v = -value;
|
||||
else
|
||||
v = (unsigned)value;
|
||||
|
||||
while (v || tp == tmp) {
|
||||
i = v % radix;
|
||||
v = radix ? v / radix : 0;
|
||||
if (i < 10)
|
||||
*tp++ = i + '0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
int len = tp - tmp;
|
||||
|
||||
if (sign) {
|
||||
*sp++ = '-';
|
||||
len++;
|
||||
}
|
||||
|
||||
while (tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static float atoff(const char *str, size_t len) {
|
||||
float result = 0.0f; // The resulting number
|
||||
float sign = 1.0f; // Positive or negative
|
||||
float fraction = 0.0f; // Fractional part
|
||||
float divisor = 1.0f; // Divisor for the fractional part
|
||||
int isFractional = 0; // Whether the current part is fractional
|
||||
|
||||
size_t pos = 0; // Current position in the string
|
||||
|
||||
// Handle empty input
|
||||
if (len == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Skip leading whitespace (manual check instead of isspace)
|
||||
while (pos < len && (str[pos] == ' ' || str[pos] == '\t' || str[pos] == '\n' || str[pos] == '\r' || str[pos] == '\f' || str[pos] == '\v')) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Handle optional sign
|
||||
if (pos < len && str[pos] == '-') {
|
||||
sign = -1.0f;
|
||||
pos++;
|
||||
} else if (pos < len && str[pos] == '+') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Main parsing loop
|
||||
while (pos < len) {
|
||||
if (str[pos] >= '0' && str[pos] <= '9') {
|
||||
if (isFractional) {
|
||||
divisor *= 10.0f;
|
||||
fraction += (str[pos] - '0') / divisor;
|
||||
} else {
|
||||
result = result * 10.0f + (str[pos] - '0');
|
||||
}
|
||||
} else if (str[pos] == '.' && !isFractional) {
|
||||
isFractional = 1;
|
||||
} else {
|
||||
// Stop parsing at invalid characters
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
// Combine integer and fractional parts
|
||||
result = result + fraction;
|
||||
|
||||
// Apply the sign
|
||||
return sign * result;
|
||||
}
|
||||
|
||||
} // namespace string_functions
|
||||
|
||||
void StringFormatter::append(int32_t val, StrN<64> *dst) {
|
||||
char buf[63] = {0};
|
||||
string_functions::itoa(val, buf, 10);
|
||||
dst->write(buf, strlen(buf));
|
||||
}
|
||||
StringHolder::StringHolder(const char *str) {
|
||||
mLength = strlen(str); // Don't include null terminator in length
|
||||
mCapacity = mLength + 1; // Capacity includes null terminator
|
||||
mData = new char[mCapacity];
|
||||
memcpy(mData, str, mLength);
|
||||
mData[mLength] = '\0';
|
||||
}
|
||||
StringHolder::StringHolder(size_t length) {
|
||||
mData = (char *)malloc(length + 1);
|
||||
if (mData) {
|
||||
mLength = length;
|
||||
mData[mLength] = '\0';
|
||||
} else {
|
||||
mLength = 0;
|
||||
}
|
||||
mCapacity = mLength;
|
||||
}
|
||||
|
||||
|
||||
StringHolder::StringHolder(const char *str, size_t length) {
|
||||
mData = (char *)malloc(length + 1);
|
||||
if (mData) {
|
||||
mLength = length;
|
||||
memcpy(mData, str, mLength);
|
||||
mData[mLength] = '\0';
|
||||
} else {
|
||||
mLength = 0;
|
||||
}
|
||||
mCapacity = mLength;
|
||||
}
|
||||
|
||||
StringHolder::~StringHolder() {
|
||||
free(mData); // Release the memory
|
||||
}
|
||||
|
||||
void StringHolder::grow(size_t newLength) {
|
||||
if (newLength <= mCapacity) {
|
||||
// New length must be greater than current length
|
||||
mLength = newLength;
|
||||
return;
|
||||
}
|
||||
char *newData = (char *)realloc(mData, newLength + 1);
|
||||
if (newData) {
|
||||
mData = newData;
|
||||
mLength = newLength;
|
||||
mCapacity = newLength;
|
||||
mData[mLength] = '\0'; // Ensure null-termination
|
||||
} else {
|
||||
// handle re-allocation failure.
|
||||
char *newData = (char *)malloc(newLength + 1);
|
||||
if (newData) {
|
||||
memcpy(newData, mData, mLength + 1);
|
||||
free(mData);
|
||||
mData = newData;
|
||||
mLength = newLength;
|
||||
mCapacity = mLength;
|
||||
} else {
|
||||
// memory failure.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float StringFormatter::parseFloat(const char *str, size_t len) {
|
||||
return string_functions::atoff(str, len);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
443
FastLED/src/fl/str.h
Normal file
443
FastLED/src/fl/str.h
Normal file
@@ -0,0 +1,443 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/ptr.h"
|
||||
#include "fl/template_magic.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
#ifndef FASTLED_STR_INLINED_SIZE
|
||||
#define FASTLED_STR_INLINED_SIZE 64
|
||||
#endif
|
||||
|
||||
namespace fl { // Mandatory namespace for this class since it has name collisions.
|
||||
|
||||
template <size_t N> class StrN;
|
||||
|
||||
// A copy on write string class. Fast to copy from another
|
||||
// Str object as read only pointers are shared. If the size
|
||||
// of the string is below FASTLED_STR_INLINED_SIZE then the
|
||||
// the entire string fits in the object and no heap allocation occurs.
|
||||
// When the string is large enough it will overflow into heap
|
||||
// allocated memory. Copy on write means that if the Str has
|
||||
// a heap buffer that is shared with another Str object, then
|
||||
// a copy is made and then modified in place.
|
||||
// If write() or append() is called then the internal data structure
|
||||
// will grow to accomodate the new data with extra space for future,
|
||||
// like a vector.
|
||||
class Str;
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Implementation details.
|
||||
|
||||
FASTLED_SMART_PTR(StringHolder);
|
||||
|
||||
class StringFormatter {
|
||||
public:
|
||||
static void append(int32_t val, StrN<64> *dst);
|
||||
static bool isSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }
|
||||
static float parseFloat(const char *str, size_t len);
|
||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
|
||||
};
|
||||
|
||||
class StringHolder : public fl::Referent {
|
||||
public:
|
||||
StringHolder(const char *str);
|
||||
StringHolder(size_t length);
|
||||
StringHolder(const char *str, size_t length);
|
||||
StringHolder(const StringHolder &other) = delete;
|
||||
StringHolder &operator=(const StringHolder &other) = delete;
|
||||
~StringHolder();
|
||||
|
||||
bool isShared() const { return ref_count() > 1; }
|
||||
void grow(size_t newLength);
|
||||
bool hasCapacity(size_t newLength) const { return newLength <= mCapacity; }
|
||||
const char *data() const { return mData; }
|
||||
char *data() { return mData; }
|
||||
size_t length() const { return mLength; }
|
||||
size_t capacity() const { return mCapacity; }
|
||||
bool copy(const char *str, size_t len) {
|
||||
if ((len+1) > mCapacity) {
|
||||
return false;
|
||||
}
|
||||
memcpy(mData, str, len);
|
||||
mData[len] = '\0';
|
||||
mLength = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
char *mData = nullptr;
|
||||
size_t mLength = 0;
|
||||
size_t mCapacity = 0;
|
||||
};
|
||||
|
||||
template <size_t SIZE = 64> class StrN {
|
||||
private:
|
||||
size_t mLength = 0;
|
||||
char mInlineData[SIZE] = {0};
|
||||
StringHolderPtr mHeapData;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
StrN() = default;
|
||||
|
||||
// cppcheck-suppress-begin [operatorEqVarError]
|
||||
template <size_t M> StrN(const StrN<M> &other) { copy(other); }
|
||||
StrN(const char *str) {
|
||||
size_t len = strlen(str);
|
||||
mLength = len; // Length is without null terminator
|
||||
if (len + 1 <= SIZE) { // Check capacity including null
|
||||
memcpy(mInlineData, str, len + 1); // Copy including null
|
||||
mHeapData.reset();
|
||||
} else {
|
||||
mHeapData = StringHolderPtr::New(str);
|
||||
}
|
||||
}
|
||||
StrN(const StrN &other) { copy(other); }
|
||||
void copy(const char *str) {
|
||||
size_t len = strlen(str);
|
||||
mLength = len;
|
||||
if (len + 1 <= SIZE) {
|
||||
memcpy(mInlineData, str, len + 1);
|
||||
mHeapData.reset();
|
||||
} else {
|
||||
if (mHeapData && !mHeapData->isShared()) {
|
||||
// We are the sole owners of this data so we can modify it
|
||||
mHeapData->copy(str, len);
|
||||
return;
|
||||
}
|
||||
mHeapData.reset();
|
||||
mHeapData = StringHolderPtr::New(str);
|
||||
}
|
||||
}
|
||||
|
||||
template<int N> StrN(const char (&str)[N]) {
|
||||
copy(str, N-1); // Subtract 1 to not count null terminator
|
||||
}
|
||||
template<int N> StrN &operator=(const char (&str)[N]) {
|
||||
assign(str, N);
|
||||
return *this;
|
||||
}
|
||||
StrN &operator=(const StrN &other) {
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
template <size_t M> StrN &operator=(const StrN<M> &other) {
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
// cppcheck-suppress-end
|
||||
|
||||
bool operator==(const StrN &other) const {
|
||||
return strcmp(c_str(), other.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const StrN &other) const {
|
||||
return strcmp(c_str(), other.c_str()) != 0;
|
||||
}
|
||||
|
||||
void copy(const char* str, size_t len) {
|
||||
mLength = len;
|
||||
if (len + 1 <= SIZE) {
|
||||
memcpy(mInlineData, str, len + 1);
|
||||
mHeapData.reset();
|
||||
} else {
|
||||
mHeapData = StringHolderPtr::New(str, len);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t M> void copy(const StrN<M> &other) {
|
||||
size_t len = other.size();
|
||||
if (len + 1 <= SIZE) {
|
||||
memcpy(mInlineData, other.c_str(), len + 1);
|
||||
mHeapData.reset();
|
||||
} else {
|
||||
if (other.mHeapData) {
|
||||
mHeapData = other.mHeapData;
|
||||
} else {
|
||||
mHeapData = StringHolderPtr::New(other.c_str());
|
||||
}
|
||||
}
|
||||
mLength = len;
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return mHeapData ? mHeapData->capacity() : SIZE;
|
||||
}
|
||||
|
||||
|
||||
size_t write(const uint8_t *data, size_t n) {
|
||||
const char *str = reinterpret_cast<const char *>(data);
|
||||
return write(str, n);
|
||||
}
|
||||
|
||||
size_t write(const char *str, size_t n) {
|
||||
size_t newLen = mLength + n;
|
||||
if (mHeapData && !mHeapData->isShared()) {
|
||||
if (!mHeapData->hasCapacity(newLen)) {
|
||||
size_t grow_length = MAX(3, newLen * 3 / 2);
|
||||
mHeapData->grow(grow_length); // Grow by 50%
|
||||
}
|
||||
memcpy(mHeapData->data() + mLength, str, n);
|
||||
mLength = newLen;
|
||||
mHeapData->data()[mLength] = '\0';
|
||||
return mLength;
|
||||
}
|
||||
if (newLen + 1 <= SIZE) {
|
||||
memcpy(mInlineData + mLength, str, n);
|
||||
mLength = newLen;
|
||||
mInlineData[mLength] = '\0';
|
||||
return mLength;
|
||||
}
|
||||
mHeapData.reset();
|
||||
StringHolderPtr newData = StringHolderPtr::New(newLen);
|
||||
if (newData) {
|
||||
memcpy(newData->data(), c_str(), mLength);
|
||||
memcpy(newData->data() + mLength, str, n);
|
||||
newData->data()[newLen] = '\0';
|
||||
mHeapData = newData;
|
||||
mLength = newLen;
|
||||
}
|
||||
mHeapData = newData;
|
||||
return mLength;
|
||||
}
|
||||
|
||||
size_t write(char c) { return write(&c, 1); }
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
const char *str = reinterpret_cast<const char *>(&c);
|
||||
return write(str, 1);
|
||||
}
|
||||
|
||||
size_t write(const uint16_t& n) {
|
||||
StrN<64> dst;
|
||||
StringFormatter::append(n, &dst); // Inlined size should suffice
|
||||
return write(dst.c_str(), dst.size());
|
||||
}
|
||||
|
||||
size_t write(const uint32_t& val) {
|
||||
StrN<64> dst;
|
||||
StringFormatter::append(val, &dst); // Inlined size should suffice
|
||||
return write(dst.c_str(), dst.size());
|
||||
}
|
||||
|
||||
size_t write(const int32_t& val) {
|
||||
StrN<64> dst;
|
||||
StringFormatter::append(val, &dst); // Inlined size should suffice
|
||||
return write(dst.c_str(), dst.size());
|
||||
}
|
||||
|
||||
size_t write(const int8_t val) {
|
||||
StrN<64> dst;
|
||||
StringFormatter::append(int16_t(val), &dst); // Inlined size should suffice
|
||||
return write(dst.c_str(), dst.size());
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~StrN() {}
|
||||
|
||||
// Accessors
|
||||
size_t size() const { return mLength; }
|
||||
size_t length() const { return size(); }
|
||||
const char *c_str() const {
|
||||
return mHeapData ? mHeapData->data() : mInlineData;
|
||||
}
|
||||
|
||||
char *c_str_mutable() {
|
||||
return mHeapData ? mHeapData->data() : mInlineData;
|
||||
}
|
||||
|
||||
char &operator[](size_t index) {
|
||||
if (index >= mLength) {
|
||||
static char dummy = '\0';
|
||||
return dummy;
|
||||
}
|
||||
return c_str_mutable()[index];
|
||||
}
|
||||
|
||||
const char &operator[](size_t index) const {
|
||||
if (index >= mLength) {
|
||||
static char dummy = '\0';
|
||||
return dummy;
|
||||
}
|
||||
return c_str()[index];
|
||||
}
|
||||
|
||||
bool empty() const { return mLength == 0; }
|
||||
|
||||
// Append method
|
||||
|
||||
|
||||
bool operator<(const StrN &other) const {
|
||||
return strcmp(c_str(), other.c_str()) < 0;
|
||||
}
|
||||
|
||||
template<size_t M> bool operator<(const StrN<M> &other) const {
|
||||
return strcmp(c_str(), other.c_str()) < 0;
|
||||
}
|
||||
|
||||
void reserve(size_t newCapacity) {
|
||||
// If capacity is less than current length, do nothing
|
||||
if (newCapacity <= mLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If new capacity fits in inline buffer, no need to allocate
|
||||
if (newCapacity + 1 <= SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we already have unshared heap data with sufficient capacity, do nothing
|
||||
if (mHeapData && !mHeapData->isShared() && mHeapData->hasCapacity(newCapacity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to allocate new storage
|
||||
StringHolderPtr newData = StringHolderPtr::New(newCapacity);
|
||||
if (newData) {
|
||||
// Copy existing content
|
||||
memcpy(newData->data(), c_str(), mLength);
|
||||
newData->data()[mLength] = '\0';
|
||||
mHeapData = newData;
|
||||
}
|
||||
}
|
||||
|
||||
void clear(bool freeMemory = false) {
|
||||
mLength = 0;
|
||||
if (freeMemory && mHeapData) {
|
||||
mHeapData.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int16_t find(const char &value) const {
|
||||
for (size_t i = 0; i < mLength; ++i) {
|
||||
if (c_str()[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
StrN substring(size_t start, size_t end) const {
|
||||
if (start >= mLength) {
|
||||
return StrN();
|
||||
}
|
||||
if (end > mLength) {
|
||||
end = mLength;
|
||||
}
|
||||
if (start >= end) {
|
||||
return StrN();
|
||||
}
|
||||
StrN out;
|
||||
out.copy(c_str() + start, end - start);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
StrN trim() const {
|
||||
StrN out;
|
||||
size_t start = 0;
|
||||
size_t end = mLength;
|
||||
while (start < mLength && StringFormatter::isSpace(c_str()[start])) {
|
||||
start++;
|
||||
}
|
||||
while (end > start && StringFormatter::isSpace(c_str()[end - 1])) {
|
||||
end--;
|
||||
}
|
||||
return substring(start, end);
|
||||
}
|
||||
|
||||
float toFloat() const {
|
||||
return StringFormatter::parseFloat(c_str(), mLength);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
StringHolderPtr mData;
|
||||
};
|
||||
|
||||
class Str : public StrN<FASTLED_STR_INLINED_SIZE> {
|
||||
public:
|
||||
Str() : StrN<FASTLED_STR_INLINED_SIZE>() {}
|
||||
Str(const char *str) : StrN<FASTLED_STR_INLINED_SIZE>(str) {}
|
||||
Str(const Str &other) : StrN<FASTLED_STR_INLINED_SIZE>(other) {}
|
||||
template <size_t M>
|
||||
Str(const StrN<M> &other) : StrN<FASTLED_STR_INLINED_SIZE>(other) {}
|
||||
Str &operator=(const Str &other) {
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator>(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) > 0;
|
||||
}
|
||||
|
||||
bool operator>=(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) >= 0;
|
||||
}
|
||||
|
||||
bool operator<(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) < 0;
|
||||
}
|
||||
|
||||
bool operator<=(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) <= 0;
|
||||
}
|
||||
|
||||
bool operator==(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Str &other) const {
|
||||
return strcmp(c_str(), other.c_str()) != 0;
|
||||
}
|
||||
|
||||
Str& operator+=(const Str &other) {
|
||||
append(other.c_str(), other.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str& append(const char *str) { write(str, strlen(str)); return *this; }
|
||||
Str& append(const char *str, size_t len) { write(str, len); return *this; }
|
||||
//Str& append(char c) { write(&c, 1); return *this; }
|
||||
Str& append(const int8_t& c) {
|
||||
const char* str = reinterpret_cast<const char*>(&c);
|
||||
write(str, 1); return *this;
|
||||
}
|
||||
Str& append(const uint8_t& c) { write(uint16_t(c)); return *this; }
|
||||
Str& append(const uint16_t& val) { write(val); return *this; }
|
||||
Str& append(const int16_t& val) { write(uint32_t(val)); return *this; }
|
||||
Str& append(const uint32_t& val) { write(val); return *this; }
|
||||
Str& append(const int32_t& c) { write(c); return *this; }
|
||||
|
||||
Str& append(const float& val) {
|
||||
int32_t i = static_cast<int32_t>(val * 100);
|
||||
// append the integer part
|
||||
append(i / 100);
|
||||
append(".");
|
||||
// append the decimal part
|
||||
append(i % 100);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str& append(const double& val) {
|
||||
return append(float(val));
|
||||
}
|
||||
|
||||
Str& append(const StrN &str) { write(str.c_str(), str.size()); return *this; }
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace fl
|
||||
208
FastLED/src/fl/strstream.h
Normal file
208
FastLED/src/fl/strstream.h
Normal file
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include "crgb.h"
|
||||
#include "str.h"
|
||||
|
||||
|
||||
#ifndef FASTLED_STRSTREAM_USES_SIZE_T
|
||||
#if defined(__AVR__) || defined(ESP8266) || defined(ESP32)
|
||||
#define FASTLED_STRSTREAM_USES_SIZE_T 0
|
||||
#else
|
||||
#define FASTLED_STRSTREAM_USES_SIZE_T 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
template <typename T> struct StrStreamHelper {
|
||||
static void append(Str &str, const T& n) { str.append(n); }
|
||||
};
|
||||
|
||||
template <> struct StrStreamHelper<int> {
|
||||
static void append(Str &str, const int& n) { str.append(int32_t(n)); }
|
||||
};
|
||||
|
||||
template <> struct StrStreamHelper<uint8_t> {
|
||||
static void append(Str &str, const uint8_t& n) { str.append(uint16_t(n)); }
|
||||
};
|
||||
|
||||
template <> struct StrStreamHelper<char> {
|
||||
static void append(Str &str, const char& n) { str.append(uint16_t(n)); }
|
||||
};
|
||||
|
||||
template <> struct StrStreamHelper<unsigned int> {
|
||||
static void append(Str &str, const unsigned int& n) { str.append(uint32_t(n)); }
|
||||
};
|
||||
|
||||
class StrStream {
|
||||
public:
|
||||
StrStream() = default;
|
||||
StrStream(const Str &str) : mStr(str) {}
|
||||
|
||||
void setTreatCharAsInt(bool treatCharAsInt) {
|
||||
mTreatCharAsInt = treatCharAsInt;
|
||||
}
|
||||
|
||||
const Str &str() const { return mStr; }
|
||||
const char *c_str() const { return mStr.c_str(); }
|
||||
|
||||
StrStream &operator<<(const CRGB &rgb) {
|
||||
mStr.append("CRGB(");
|
||||
mStr.append(rgb.r);
|
||||
mStr.append(",");
|
||||
mStr.append(rgb.g);
|
||||
mStr.append(",");
|
||||
mStr.append(rgb.b);
|
||||
mStr.append(")");
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator=(uint16_t n) {
|
||||
mStr.clear();
|
||||
(*this) << n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator=(uint8_t n) {
|
||||
mStr.clear();
|
||||
(*this) << n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator=(char c) {
|
||||
mStr.clear();
|
||||
(*this) << c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// << operator section
|
||||
StrStream &operator<<(const Str &str) {
|
||||
mStr.append(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(const char *str) {
|
||||
mStr.append(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream& operator<<(const float& f) {
|
||||
// multiply by 100 and round to get 2 decimal places
|
||||
mStr.append(f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream& operator<<(const double& f) {
|
||||
// multiply by 100 and round to get 2 decimal places
|
||||
mStr.append(f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(char c) {
|
||||
if (mTreatCharAsInt) {
|
||||
StrStreamHelper<int>::append(mStr, c);
|
||||
} else {
|
||||
StrStreamHelper<char>::append(mStr, c);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if FASTLED_STRSTREAM_USES_SIZE_T
|
||||
StrStream &operator<<(size_t n) {
|
||||
mStr.append(uint32_t(n));
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
StrStream &operator<<(T n) {
|
||||
StrStreamHelper<T>::append(mStr, n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(uint8_t n) {
|
||||
if (mTreatCharAsInt) {
|
||||
mStr.append(uint16_t(n));
|
||||
} else {
|
||||
mStr.append(n);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(uint16_t n) {
|
||||
mStr.append(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(int16_t n) {
|
||||
mStr.append(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(uint32_t n) {
|
||||
mStr.append(uint32_t(n));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator<<(int32_t n) {
|
||||
mStr.append(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// assignment operator completely replaces the current string
|
||||
StrStream &operator=(const Str &str) {
|
||||
mStr = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
StrStream &operator=(const char *str) {
|
||||
mStr = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// crgb
|
||||
StrStream &operator=(const CRGB &rgb) {
|
||||
mStr.clear();
|
||||
(*this) << rgb;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Str mStr;
|
||||
bool mTreatCharAsInt = true;
|
||||
};
|
||||
|
||||
class FakeStrStream {
|
||||
public:
|
||||
template<typename T>
|
||||
FakeStrStream& operator<<(const T&) { return *this; }
|
||||
|
||||
FakeStrStream& operator<<(const char*) { return *this; }
|
||||
|
||||
template<typename T>
|
||||
FakeStrStream& operator=(const T&) { return *this; }
|
||||
|
||||
FakeStrStream& operator<<(const CRGB&) { return *this; }
|
||||
FakeStrStream& operator<<(const Str&) { return *this; }
|
||||
FakeStrStream& operator<<(char) { return *this; }
|
||||
|
||||
#if FASTLED_STRSTREAM_USES_SIZE_T
|
||||
FakeStrStream& operator<<(size_t) { return *this; }
|
||||
#endif
|
||||
|
||||
FakeStrStream& operator<<(uint8_t) { return *this; }
|
||||
FakeStrStream& operator<<(uint16_t) { return *this; }
|
||||
FakeStrStream& operator<<(int16_t) { return *this; }
|
||||
FakeStrStream& operator<<(uint32_t) { return *this; }
|
||||
FakeStrStream& operator<<(int32_t) { return *this; }
|
||||
|
||||
FakeStrStream& operator=(const Str&) { return *this; }
|
||||
FakeStrStream& operator=(const CRGB&) { return *this; }
|
||||
FakeStrStream& operator=(uint16_t) { return *this; }
|
||||
FakeStrStream& operator=(uint8_t) { return *this; }
|
||||
FakeStrStream& operator=(char) { return *this; }
|
||||
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
36
FastLED/src/fl/stub_main.cpp
Normal file
36
FastLED/src/fl/stub_main.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This is a stub implementation of main that can be used to include an *.ino
|
||||
* file which is so close to C++ that many of them can be compiled as C++. The
|
||||
* notable difference between a *.ino file and a *.cpp file is that the *.ino
|
||||
* file does not need to include function prototypes, and are instead
|
||||
* auto-generated.
|
||||
*/
|
||||
|
||||
// This can't be in the namespace fl. It needs to be in the global namespace.
|
||||
|
||||
|
||||
#if defined(FASTLED_STUB_MAIN) || defined(FASTLED_STUB_MAIN_INCLUDE_INO)
|
||||
|
||||
#ifndef _FASTLED_STRINGIFY
|
||||
#define _FASTLED_STRINGIFY_HELPER(x) #x
|
||||
#define _FASTLED_STRINGIFY(x) _FASTLED_STRINGIFY_HELPER(x)
|
||||
#endif
|
||||
|
||||
#ifdef FASTLED_STUB_MAIN_INCLUDE_INO
|
||||
// Correctly include the file by expanding and stringifying the macro value
|
||||
#include _FASTLED_STRINGIFY(FASTLED_STUB_MAIN_INCLUDE_INO)
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#endif // FASTLED_STUB_MAIN_INCLUDE_INO
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
// Super simple main function that just calls the setup and loop functions.
|
||||
setup();
|
||||
while(1) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
#endif // FASTLED_STUB_MAIN_INCLUDE_INO
|
||||
10
FastLED/src/fl/supersample.h
Normal file
10
FastLED/src/fl/supersample.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace fl {
|
||||
enum SuperSample {
|
||||
SUPER_SAMPLE_NONE = 1, // 1x supersampling (no supersampling)
|
||||
SUPER_SAMPLE_2X = 2, // 2x supersampling
|
||||
SUPER_SAMPLE_4X = 4, // 4x supersampling
|
||||
SUPER_SAMPLE_8X = 8 // 8x supersampling
|
||||
};
|
||||
}
|
||||
134
FastLED/src/fl/template_magic.h
Normal file
134
FastLED/src/fl/template_magic.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Provides eanble_if and is_derived for compilers before C++14.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl { // mandatory namespace to prevent name collision with std::enable_if.
|
||||
|
||||
// Define enable_if for SFINAE
|
||||
template <bool Condition, typename T = void>
|
||||
struct enable_if {};
|
||||
|
||||
// Specialization for true condition
|
||||
template <typename T>
|
||||
struct enable_if<true, T> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
// if enable_if<Condition, T> is true, then there will be a member type
|
||||
// called type. Otherwise it will not exist. This is (ab)used to enable
|
||||
// constructors and other functions based on template parameters. If there
|
||||
// is no member type, then the compiler will not fail to bind to the target
|
||||
// function or operation.
|
||||
template <bool Condition, typename T = void>
|
||||
using enable_if_t = typename enable_if<Condition, T>::type;
|
||||
|
||||
// Define is_base_of to check inheritance relationship
|
||||
template <typename Base, typename Derived>
|
||||
struct is_base_of {
|
||||
private:
|
||||
typedef uint8_t yes;
|
||||
typedef uint16_t no;
|
||||
static yes test(Base*); // Matches if Derived is convertible to Base*
|
||||
static no test(...); // Fallback if not convertible
|
||||
enum {
|
||||
kSizeDerived = sizeof(test(static_cast<Derived*>(nullptr))),
|
||||
};
|
||||
public:
|
||||
static constexpr bool value = (kSizeDerived == sizeof(yes));
|
||||
};
|
||||
|
||||
// Define is_base_of_v for compatibility with pre-C++14
|
||||
// Replaced variable template with a constant static member
|
||||
template <typename Base, typename Derived>
|
||||
struct is_base_of_v_helper {
|
||||
static constexpr bool value = is_base_of<Base, Derived>::value;
|
||||
};
|
||||
|
||||
// Define is_same trait
|
||||
template <typename T, typename U>
|
||||
struct is_same {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
// Specialization for when T and U are the same type
|
||||
template <typename T>
|
||||
struct is_same<T, T> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
// Define is_same_v for compatibility with variable templates
|
||||
template <typename T, typename U>
|
||||
struct is_same_v_helper {
|
||||
static constexpr bool value = is_same<T, U>::value;
|
||||
};
|
||||
|
||||
// Define is_pod trait (basic implementation)
|
||||
template <typename T>
|
||||
struct is_pod {
|
||||
static constexpr bool value = false; // Default to false for safety
|
||||
};
|
||||
|
||||
// Specializations for fundamental types
|
||||
template<> struct is_pod<bool> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<char> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<signed char> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<unsigned char> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<short> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<unsigned short> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<int> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<unsigned int> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<long> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<unsigned long> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<long long> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<unsigned long long> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<float> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<double> { static constexpr bool value = true; };
|
||||
template<> struct is_pod<long double> { static constexpr bool value = true; };
|
||||
|
||||
// Helper struct for is_pod_v (similar to other _v helpers)
|
||||
template <typename T>
|
||||
struct is_pod_v_helper {
|
||||
static constexpr bool value = is_pod<T>::value;
|
||||
};
|
||||
|
||||
// This uses template magic to maybe generate a type for the given condition. If that type
|
||||
// doesn't exist then a type will fail to be generated, and the compiler will skip the
|
||||
// consideration of a target function. This is useful for enabling template constructors
|
||||
// that only become available if the class can be upcasted to the desired type.
|
||||
//
|
||||
// Example:
|
||||
// This is an optional upcasting constructor for a Ref<T>. If the type U is not derived from T
|
||||
// then the constructor will not be generated, and the compiler will skip it.
|
||||
//
|
||||
// template <typename U, typename = fl::is_derived<T, U>>
|
||||
// Ref(const Ref<U>& refptr) : referent_(refptr.get());
|
||||
template <typename Base, typename Derived>
|
||||
using is_derived = enable_if_t<is_base_of<Base, Derived>::value>;
|
||||
|
||||
} // namespace fl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// For comparison operators that return bool against pod data. The class obj will need
|
||||
// to supply the comparison operator for the pod type.
|
||||
// This example will show how to define a comparison operator for a class that can be
|
||||
// compared against a pod type.
|
||||
// Example:
|
||||
// FASTLED_DEFINE_POD_COMPARISON_OPERATOR(Myclass, >=) will allow MyClass to be compared
|
||||
// MyClass obj;
|
||||
// return obj >= 0;
|
||||
#define FASTLED_DEFINE_POD_COMPARISON_OPERATOR(CLASS, OP) \
|
||||
template <typename T, typename U> \
|
||||
typename fl::enable_if<fl::is_same<U, CLASS>::value && fl::is_pod<T>::value, bool>::type \
|
||||
operator OP (const T& pod, const CLASS& obj) { return pod OP obj; } \
|
||||
template <typename T> \
|
||||
typename fl::enable_if<fl::is_pod<T>::value, bool>::type \
|
||||
operator OP (const CLASS& obj, const T& pod) { return obj OP pod; }
|
||||
108
FastLED/src/fl/time_alpha.cpp
Normal file
108
FastLED/src/fl/time_alpha.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
#include "fl/time_alpha.h"
|
||||
#include "fl/warn.h"
|
||||
#include "math_macros.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
uint8_t time_alpha8(uint32_t now, uint32_t start, uint32_t end) {
|
||||
if (now < start) {
|
||||
return 0;
|
||||
}
|
||||
if (now > end) {
|
||||
return 255;
|
||||
}
|
||||
uint32_t elapsed = now - start;
|
||||
uint32_t total = end - start;
|
||||
uint32_t out = (elapsed * 255) / total;
|
||||
if (out > 255) {
|
||||
out = 255;
|
||||
}
|
||||
return static_cast<uint8_t>(out);
|
||||
}
|
||||
|
||||
uint16_t time_alpha16(uint32_t now, uint32_t start, uint32_t end) {
|
||||
if (now < start) {
|
||||
return 0;
|
||||
}
|
||||
if (now > end) {
|
||||
return 65535;
|
||||
}
|
||||
uint32_t elapsed = now - start;
|
||||
uint32_t total = end - start;
|
||||
uint32_t out = (elapsed * 65535) / total;
|
||||
if (out > 65535) {
|
||||
out = 65535;
|
||||
}
|
||||
return static_cast<uint16_t>(out);
|
||||
}
|
||||
|
||||
TimeRamp::TimeRamp(uint32_t risingTime, uint32_t latchMs,
|
||||
uint32_t fallingTime)
|
||||
: mLatchMs(latchMs), mRisingTime(risingTime), mFallingTime(fallingTime) {}
|
||||
|
||||
void TimeRamp::trigger(uint32_t now) {
|
||||
mStart = now;
|
||||
// mLastValue = 0;
|
||||
|
||||
mFinishedRisingTime = mStart + mRisingTime;
|
||||
mFinishedPlateauTime = mFinishedRisingTime + mLatchMs;
|
||||
mFinishedFallingTime = mFinishedPlateauTime + mFallingTime;
|
||||
}
|
||||
|
||||
void TimeRamp::trigger(uint32_t now, uint32_t risingTime,
|
||||
uint32_t latchMs, uint32_t fallingTime) {
|
||||
mRisingTime = risingTime;
|
||||
mLatchMs = latchMs;
|
||||
mFallingTime = fallingTime;
|
||||
trigger(now);
|
||||
}
|
||||
|
||||
bool TimeRamp::isActive(uint32_t now) const {
|
||||
|
||||
bool not_started = (mFinishedRisingTime == 0) && (mFinishedPlateauTime == 0) &&
|
||||
(mFinishedFallingTime == 0);
|
||||
if (not_started) {
|
||||
// if we have not started, we are not active
|
||||
return false;
|
||||
}
|
||||
|
||||
if (now < mStart) {
|
||||
// if the time is before the start, we are not active
|
||||
return false;
|
||||
}
|
||||
|
||||
if (now > mFinishedFallingTime) {
|
||||
// if the time is after the finished rising, we are not active
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t TimeRamp::update(uint32_t now) {
|
||||
if (!isActive(now)) {
|
||||
return 0;
|
||||
}
|
||||
// uint32_t elapsed = now - mStart;
|
||||
uint8_t out = 0;
|
||||
if (now < mFinishedRisingTime) {
|
||||
out = time_alpha8(now, mStart, mFinishedRisingTime);
|
||||
} else if (now < mFinishedPlateauTime) {
|
||||
// plateau
|
||||
out = 255;
|
||||
} else if (now < mFinishedFallingTime) {
|
||||
// ramp down
|
||||
uint8_t alpha =
|
||||
time_alpha8(now, mFinishedPlateauTime, mFinishedFallingTime);
|
||||
out = 255 - alpha;
|
||||
} else {
|
||||
// finished
|
||||
out = 0;
|
||||
}
|
||||
|
||||
mLastValue = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
132
FastLED/src/fl/time_alpha.h
Normal file
132
FastLED/src/fl/time_alpha.h
Normal file
@@ -0,0 +1,132 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Use this function to compute the alpha value based on the time elapsed
|
||||
// 0 -> 255
|
||||
uint8_t time_alpha8(uint32_t now, uint32_t start, uint32_t end);
|
||||
// 0 -> 65535
|
||||
uint16_t time_alpha16(uint32_t now, uint32_t start, uint32_t end);
|
||||
|
||||
|
||||
class TimeAlpha {
|
||||
public:
|
||||
virtual ~TimeAlpha() = default;
|
||||
virtual void trigger(uint32_t now) = 0;
|
||||
virtual uint8_t update(uint32_t now) = 0;
|
||||
virtual uint16_t update16(uint32_t now) {
|
||||
return static_cast<uint16_t>(update(now) << 8);
|
||||
}
|
||||
virtual bool isActive(uint32_t now) const = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* amplitude
|
||||
* ^
|
||||
* 255 ───────────────────────
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* 0 ────────────┴ ┴──────────────────> time (ms)
|
||||
* t0 t1 t2 t4
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class TimeRamp: public TimeAlpha {
|
||||
public:
|
||||
|
||||
|
||||
/// @param latchMs total active time (ms)
|
||||
/// @param risingTime time to ramp from 0→255 (ms)
|
||||
/// @param fallingTime time to ramp from 255→0 (ms)
|
||||
TimeRamp(uint32_t risingTime, uint32_t latchMs, uint32_t fallingTime);
|
||||
|
||||
/// Call this when you want to (re)start the ramp cycle.
|
||||
void trigger(uint32_t now) override;
|
||||
|
||||
void trigger(uint32_t now, uint32_t risingTime, uint32_t latchMs,
|
||||
uint32_t fallingTime);
|
||||
|
||||
/// @return true iff we're still within the latch period.
|
||||
bool isActive(uint32_t now) const override;
|
||||
|
||||
/// Compute current 0–255 output based on how much time has elapsed since
|
||||
/// trigger().
|
||||
uint8_t update(uint32_t now) override;
|
||||
|
||||
private:
|
||||
uint32_t mLatchMs;
|
||||
uint32_t mRisingTime;
|
||||
uint32_t mFallingTime;
|
||||
|
||||
uint32_t mFinishedRisingTime = 0;
|
||||
uint32_t mFinishedPlateauTime = 0;
|
||||
uint32_t mFinishedFallingTime = 0;
|
||||
|
||||
uint32_t mStart = 0;
|
||||
uint8_t mLastValue = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* amplitude
|
||||
* ^
|
||||
* 255 ──────────────────────────────────────
|
||||
* /
|
||||
* /
|
||||
* /
|
||||
* /
|
||||
* 0 ────────────┴ --> time (ms)
|
||||
* t0 t1
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class TimeLinear: TimeAlpha {
|
||||
public:
|
||||
TimeLinear(uint32_t duration) : mDuration(duration) {}
|
||||
|
||||
void trigger(uint32_t now) override {
|
||||
mStart = now;
|
||||
mEnd = now + mDuration;
|
||||
}
|
||||
|
||||
bool isActive(uint32_t now) const override {
|
||||
bool not_started = (mEnd == 0) && (mStart == 0);
|
||||
if (not_started) {
|
||||
// if we have not started, we are not active
|
||||
return false;
|
||||
}
|
||||
if (now < mStart) {
|
||||
// if the time is before the start, we are not active
|
||||
return false;
|
||||
}
|
||||
if (now > mEnd) {
|
||||
// if the time is after the finished rising, we are not active
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t update(uint32_t now) override {
|
||||
bool not_started = (mEnd == 0) && (mStart == 0);
|
||||
if (not_started) {
|
||||
// if we have not started, we are not active
|
||||
return 0;
|
||||
}
|
||||
uint8_t out = time_alpha8(now, mStart, mEnd);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mStart = 0;
|
||||
uint32_t mDuration = 0;
|
||||
uint32_t mEnd = 0;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
15
FastLED/src/fl/types.h
Normal file
15
FastLED/src/fl/types.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "led_sysdefs.h"
|
||||
#include "fl/namespace.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
#if defined(__AVR__)
|
||||
typedef int cycle_t; ///< 8.8 fixed point (signed) value
|
||||
#else
|
||||
typedef int64_t cycle_t; ///< 8.8 fixed point (signed) value
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
196
FastLED/src/fl/ui.h
Normal file
196
FastLED/src/fl/ui.h
Normal file
@@ -0,0 +1,196 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/template_magic.h"
|
||||
#include "fl/unused.h"
|
||||
#include "platforms/ui_defs.h"
|
||||
|
||||
#ifndef FASTLED_HAS_UI_SLIDER
|
||||
#define FASTLED_HAS_UI_SLIDER 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_UI_BUTTON
|
||||
#define FASTLED_HAS_UI_BUTTON 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_UI_CHECKBOX
|
||||
#define FASTLED_HAS_UI_CHECKBOX 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_UI_NUMBER_FIELD
|
||||
#define FASTLED_HAS_UI_NUMBER_FIELD 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_UI_TITLE
|
||||
#define FASTLED_HAS_UI_TITLE 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_HAS_UI_DESCRIPTION
|
||||
#define FASTLED_HAS_UI_DESCRIPTION 0
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
// If the platform is missing ui components, provide stubs.
|
||||
|
||||
#if !FASTLED_HAS_UI_SLIDER
|
||||
|
||||
class UISlider {
|
||||
public:
|
||||
// If step is -1, it will be calculated as (max - min) / 100
|
||||
UISlider(const char *name, float value = 128.0f, float min = 1,
|
||||
float max = 255, float step = -1.f)
|
||||
: mValue(value), mMin(MIN(min, max)), mMax(MAX(min, max)) {
|
||||
FASTLED_UNUSED(name);
|
||||
FASTLED_UNUSED(step);
|
||||
if (value < min) {
|
||||
mValue = min;
|
||||
}
|
||||
if (value > max) {
|
||||
mValue = max;
|
||||
}
|
||||
}
|
||||
~UISlider() {}
|
||||
float value() const { return mValue; }
|
||||
float value_normalized() const {
|
||||
if (ALMOST_EQUAL(mMax, mMin, 0.0001f)) {
|
||||
return 0;
|
||||
}
|
||||
return (mValue - mMin) / (mMax - mMin);
|
||||
}
|
||||
float max_value() const { return mMax; }
|
||||
void setValue(float value) { mValue = MAX(mMin, MIN(mMax, value)); }
|
||||
operator float() const { return mValue; }
|
||||
operator uint8_t() const { return static_cast<uint8_t>(mValue); }
|
||||
operator uint16_t() const { return static_cast<uint16_t>(mValue); }
|
||||
operator int() const { return static_cast<int>(mValue); }
|
||||
template <typename T> T as() const { return static_cast<T>(mValue); }
|
||||
|
||||
UISlider &operator=(float value) {
|
||||
setValue(value);
|
||||
return *this;
|
||||
}
|
||||
UISlider &operator=(int value) {
|
||||
setValue(static_cast<float>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
float mValue;
|
||||
float mMin;
|
||||
float mMax;
|
||||
};
|
||||
|
||||
// template operator for >= against a jsSlider
|
||||
|
||||
#endif
|
||||
|
||||
#if !FASTLED_HAS_UI_BUTTON
|
||||
|
||||
class UIButton {
|
||||
public:
|
||||
UIButton(const char *name) { FASTLED_UNUSED(name); }
|
||||
~UIButton() {}
|
||||
bool isPressed() const { return false; }
|
||||
bool clicked() const { return false; }
|
||||
int clickedCount() const { return 0; }
|
||||
operator bool() const { return false; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !FASTLED_HAS_UI_CHECKBOX
|
||||
|
||||
class UICheckbox {
|
||||
public:
|
||||
UICheckbox(const char *name, bool value = false) : mValue(value) {
|
||||
FASTLED_UNUSED(name);
|
||||
}
|
||||
~UICheckbox() {}
|
||||
operator bool() const { return mValue; }
|
||||
operator int() const { return mValue ? 1 : 0; }
|
||||
UICheckbox &operator=(bool value) {
|
||||
setValue(value);
|
||||
return *this;
|
||||
}
|
||||
UICheckbox &operator=(int value) {
|
||||
setValue(value != 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void setValue(bool value) { mValue = value; }
|
||||
bool mValue;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !FASTLED_HAS_UI_NUMBER_FIELD
|
||||
|
||||
class UINumberField {
|
||||
public:
|
||||
UINumberField(const char *name, double value, double min = 0,
|
||||
double max = 100)
|
||||
: mValue(value), mMin(MIN(min, max)), mMax(MAX(min, max)) {
|
||||
FASTLED_UNUSED(name);
|
||||
}
|
||||
~UINumberField() {}
|
||||
double value() const { return mValue; }
|
||||
void setValue(double value) { mValue = MAX(mMin, MIN(mMax, value)); }
|
||||
operator double() const { return mValue; }
|
||||
operator int() const { return static_cast<int>(mValue); }
|
||||
UINumberField &operator=(double value) {
|
||||
setValue(value);
|
||||
return *this;
|
||||
}
|
||||
UINumberField &operator=(int value) {
|
||||
setValue(static_cast<double>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
double mValue;
|
||||
double mMin;
|
||||
double mMax;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !FASTLED_HAS_UI_TITLE
|
||||
|
||||
class UITitle {
|
||||
public:
|
||||
UITitle(const char *name) { FASTLED_UNUSED(name); }
|
||||
~UITitle() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !FASTLED_HAS_UI_DESCRIPTION
|
||||
|
||||
class UIDescription {
|
||||
public:
|
||||
UIDescription(const char *name) { FASTLED_UNUSED(name); }
|
||||
~UIDescription() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#define FASTLED_UI_DEFINE_OPERATORS(UI_CLASS) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, >=) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, <=) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, >) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, <) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, ==) \
|
||||
FASTLED_DEFINE_POD_COMPARISON_OPERATOR(UI_CLASS, !=)
|
||||
|
||||
FASTLED_UI_DEFINE_OPERATORS(UISlider);
|
||||
FASTLED_UI_DEFINE_OPERATORS(UINumberField);
|
||||
FASTLED_UI_DEFINE_OPERATORS(UICheckbox);
|
||||
FASTLED_UI_DEFINE_OPERATORS(UIButton);
|
||||
|
||||
} // end namespace fl
|
||||
3
FastLED/src/fl/unused.h
Normal file
3
FastLED/src/fl/unused.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define FASTLED_UNUSED(x) (void)(x)
|
||||
659
FastLED/src/fl/vector.h
Normal file
659
FastLED/src/fl/vector.h
Normal file
@@ -0,0 +1,659 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "inplacenew.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/scoped_ptr.h"
|
||||
#include "fl/insert_result.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
// A fixed sized vector. The user is responsible for making sure that the
|
||||
// inserts do not exceed the capacity of the vector, otherwise they will fail.
|
||||
// Because of this limitation, this vector is not a drop in replacement for
|
||||
// std::vector.
|
||||
template<typename T, size_t N>
|
||||
class FixedVector {
|
||||
private:
|
||||
union {
|
||||
char mRaw[N * sizeof(T)];
|
||||
T mData[N];
|
||||
};
|
||||
size_t current_size = 0;
|
||||
|
||||
public:
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
// Constructor
|
||||
constexpr FixedVector() : current_size(0) {}
|
||||
|
||||
FixedVector(const T (&values)[N]) : current_size(N) {
|
||||
assign(values, N);
|
||||
}
|
||||
|
||||
template<size_t M>
|
||||
FixedVector(const T (&values)[M]) : current_size(M) {
|
||||
static_assert(M <= N, "Too many elements for FixedVector");
|
||||
assign(values, M);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~FixedVector() {
|
||||
clear();
|
||||
}
|
||||
|
||||
// Array subscript operator
|
||||
T& operator[](size_t index) {
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
// Const array subscript operator
|
||||
const T& operator[](size_t index) const {
|
||||
if (index >= current_size) {
|
||||
const T* out = nullptr;
|
||||
return *out; // Cause a nullptr dereference
|
||||
}
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
// Get the current size of the vector
|
||||
constexpr size_t size() const {
|
||||
return current_size;
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
return current_size == 0;
|
||||
}
|
||||
|
||||
// Get the capacity of the vector
|
||||
constexpr size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
// Add an element to the end of the vector
|
||||
void push_back(const T& value) {
|
||||
if (current_size < N) {
|
||||
void* mem = &mData[current_size];
|
||||
new (mem) T(value);
|
||||
++current_size;
|
||||
}
|
||||
}
|
||||
|
||||
void assign(const T* values, size_t count) {
|
||||
clear();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
push_back(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void assign(const_iterator begin, const_iterator end) {
|
||||
clear();
|
||||
for (const_iterator it = begin; it != end; ++it) {
|
||||
push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the last element from the vector
|
||||
void pop_back() {
|
||||
if (current_size > 0) {
|
||||
--current_size;
|
||||
mData[current_size].~T();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the vector
|
||||
void clear() {
|
||||
while (current_size > 0) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Erase the element at the given iterator position
|
||||
iterator erase(iterator pos) {
|
||||
if (pos != end()) {
|
||||
pos->~T();
|
||||
// shift all elements to the left
|
||||
for (iterator p = pos; p != end() - 1; ++p) {
|
||||
new (p) T(*(p + 1)); // Use copy constructor instead of std::move
|
||||
(p + 1)->~T();
|
||||
}
|
||||
--current_size;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
iterator erase(const T& value) {
|
||||
iterator it = find(value);
|
||||
if (it != end()) {
|
||||
erase(it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
iterator find(const T& value) {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (*it == value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Predicate>
|
||||
iterator find_if(Predicate pred) {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (pred(*it)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
bool insert(iterator pos, const T& value) {
|
||||
if (current_size < N) {
|
||||
// shift all elements to the right
|
||||
for (iterator p = end(); p != pos; --p) {
|
||||
new (p) T(*(p - 1)); // Use copy constructor instead of std::move
|
||||
(p - 1)->~T();
|
||||
}
|
||||
new (pos) T(value);
|
||||
++current_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const_iterator find(const T& value) const {
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if (*it == value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
iterator data() {
|
||||
return begin();
|
||||
}
|
||||
|
||||
const_iterator data() const {
|
||||
return begin();
|
||||
}
|
||||
|
||||
bool has(const T& value) const {
|
||||
return find(value) != end();
|
||||
}
|
||||
|
||||
// Access to first and last elements
|
||||
T& front() {
|
||||
return mData[0];
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return mData[0];
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return mData[current_size - 1];
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return mData[current_size - 1];
|
||||
}
|
||||
|
||||
// Iterator support
|
||||
iterator begin() { return &mData[0]; }
|
||||
const_iterator begin() const { return &mData[0]; }
|
||||
iterator end() { return &mData[current_size]; }
|
||||
const_iterator end() const { return &mData[current_size]; }
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class HeapVector {
|
||||
private:
|
||||
fl::scoped_array<T> mArray;
|
||||
|
||||
size_t mCapacity = 0;
|
||||
size_t mSize = 0;
|
||||
|
||||
public:
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
|
||||
struct reverse_iterator {
|
||||
iterator it;
|
||||
reverse_iterator(iterator i): it(i) {}
|
||||
T& operator*() { return *(it - 1); }
|
||||
reverse_iterator& operator++() { --it; return *this; }
|
||||
bool operator!=(const reverse_iterator& other) const { return it != other.it; }
|
||||
};
|
||||
|
||||
// Constructor
|
||||
HeapVector(size_t size = 0, const T& value = T()): mCapacity(size) {
|
||||
mArray.reset(new T[mCapacity]);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
mArray[i] = value;
|
||||
}
|
||||
mSize = size;
|
||||
}
|
||||
HeapVector(const HeapVector<T>& other): mSize(other.size()) {
|
||||
assign(other.begin(), other.end());
|
||||
}
|
||||
HeapVector& operator=(const HeapVector<T>& other) { // cppcheck-suppress operatorEqVarError
|
||||
if (this != &other) {
|
||||
assign(other.begin(), other.end());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~HeapVector() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void ensure_size(size_t n) {
|
||||
if (n > mCapacity) {
|
||||
size_t new_capacity = (3*mCapacity) / 2;
|
||||
if (new_capacity < n) {
|
||||
new_capacity = n;
|
||||
}
|
||||
fl::scoped_array<T> new_array(new T[new_capacity]);
|
||||
for (size_t i = 0; i < mSize; ++i) {
|
||||
new_array[i] = mArray[i];
|
||||
}
|
||||
// mArray = std::move(new_array);
|
||||
mArray.reset();
|
||||
mArray.reset(new_array.release());
|
||||
mCapacity = new_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
if (n > mCapacity) {
|
||||
ensure_size(n);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t n) {
|
||||
if (mSize == n) {
|
||||
return;
|
||||
}
|
||||
HeapVector<T> temp(n);
|
||||
for (size_t i = 0; i < n && i < mSize; ++i) {
|
||||
temp.mArray[i] = mArray[i];
|
||||
}
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
void resize(size_t n, const T& value) {
|
||||
mArray.reset();
|
||||
mArray.reset(new T[n]);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
mArray[i] = value;
|
||||
}
|
||||
mCapacity = n;
|
||||
}
|
||||
|
||||
// Array access operators
|
||||
T& operator[](size_t index) {
|
||||
return mArray[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
return mArray[index];
|
||||
}
|
||||
|
||||
// Capacity and size methods
|
||||
size_t size() const {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return mSize == 0;
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
// Element addition/removal
|
||||
void push_back(const T& value) {
|
||||
ensure_size(mSize + 1);
|
||||
if (mSize < mCapacity) {
|
||||
mArray[mSize] = value;
|
||||
++mSize;
|
||||
}
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
if (mSize > 0) {
|
||||
--mSize;
|
||||
mArray[mSize] = T();
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
while (mSize > 0) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator methods
|
||||
iterator begin() { return &mArray[0]; }
|
||||
const_iterator begin() const { return &mArray[0]; }
|
||||
iterator end() { return &mArray[mSize]; }
|
||||
const_iterator end() const { return &mArray[mSize]; }
|
||||
|
||||
|
||||
reverse_iterator rbegin() {
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
reverse_iterator rend() {
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
// Element access
|
||||
T& front() {
|
||||
return mArray[0];
|
||||
}
|
||||
|
||||
const T& front() const {
|
||||
return mArray[0];
|
||||
}
|
||||
|
||||
T& back() {
|
||||
return mArray[mSize - 1];
|
||||
}
|
||||
|
||||
const T& back() const {
|
||||
return mArray[mSize - 1];
|
||||
}
|
||||
|
||||
// Search and modification
|
||||
iterator find(const T& value) {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (*it == value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const T& value) const {
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if (*it == value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Predicate>
|
||||
iterator find_if(Predicate pred) {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (pred(*it)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
bool has(const T& value) const {
|
||||
return find(value) != end();
|
||||
}
|
||||
|
||||
bool erase(iterator pos, T* out_value = nullptr) {
|
||||
if (pos == end() || empty()) {
|
||||
return false;
|
||||
}
|
||||
if (out_value) {
|
||||
*out_value = *pos;
|
||||
}
|
||||
while (pos != end() - 1) {
|
||||
*pos = *(pos + 1);
|
||||
++pos;
|
||||
}
|
||||
back() = T();
|
||||
--mSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
void erase(const T& value) {
|
||||
iterator it = find(value);
|
||||
if (it != end()) {
|
||||
erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(HeapVector<T>& other) {
|
||||
T* temp = mArray.release();
|
||||
size_t temp_size = mSize;
|
||||
size_t temp_capacity = mCapacity;
|
||||
T* temp2 = other.mArray.release();
|
||||
mArray.reset(temp2);
|
||||
other.mArray.reset(temp);
|
||||
mSize = other.mSize;
|
||||
mCapacity = other.mCapacity;
|
||||
other.mSize = temp_size;
|
||||
other.mCapacity = temp_capacity;
|
||||
}
|
||||
|
||||
void swap(iterator a, iterator b) {
|
||||
T temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
}
|
||||
|
||||
bool full() const {
|
||||
return mSize >= mCapacity;
|
||||
}
|
||||
|
||||
bool insert(iterator pos, const T& value) {
|
||||
// TODO: Introduce mMaxSize (and move it from SortedVector to here)
|
||||
// push back and swap into place.
|
||||
size_t target_idx = pos - begin();
|
||||
push_back(value);
|
||||
auto last = end() - 1;
|
||||
for (size_t curr_idx = last - begin(); curr_idx > target_idx; --curr_idx) {
|
||||
auto first = begin() + curr_idx - 1;
|
||||
auto second = begin() + curr_idx;
|
||||
swap(first, second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void assign(const T* values, size_t count) {
|
||||
assign(values, values + count);
|
||||
}
|
||||
|
||||
void assign(const_iterator begin, const_iterator end) {
|
||||
clear();
|
||||
reserve(end - begin);
|
||||
for (const_iterator it = begin; it != end; ++it) {
|
||||
push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
T* data() {
|
||||
return mArray.get();
|
||||
}
|
||||
|
||||
const T* data() const {
|
||||
return mArray.get();
|
||||
}
|
||||
|
||||
bool operator==(const HeapVector<T>& other) const {
|
||||
if (size() != other.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
if (mArray[i] != other.mArray[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const HeapVector<T>& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T, typename LessThan>
|
||||
class SortedHeapVector {
|
||||
private:
|
||||
HeapVector<T> mArray;
|
||||
LessThan mLess;
|
||||
size_t mMaxSize = size_t(-1);
|
||||
|
||||
public:
|
||||
typedef typename HeapVector<T>::iterator iterator;
|
||||
typedef typename HeapVector<T>::const_iterator const_iterator;
|
||||
|
||||
SortedHeapVector(LessThan less=LessThan()): mLess(less) {}
|
||||
|
||||
void setMaxSize(size_t n) {
|
||||
if (mMaxSize == n) {
|
||||
return;
|
||||
}
|
||||
mMaxSize = n;
|
||||
const bool needs_adjustment = mArray.size() > mMaxSize;
|
||||
if (needs_adjustment) {
|
||||
mArray.resize(n);
|
||||
} else {
|
||||
mArray.reserve(n);
|
||||
}
|
||||
}
|
||||
|
||||
~SortedHeapVector() {
|
||||
mArray.clear();
|
||||
}
|
||||
|
||||
void reserve(size_t n) {
|
||||
mArray.reserve(n);
|
||||
}
|
||||
|
||||
// Insert while maintaining sort order
|
||||
bool insert(const T& value, InsertResult* result = nullptr) {
|
||||
// Find insertion point using binary search
|
||||
iterator pos = lower_bound(value);
|
||||
if (pos != end() && !mLess(value, *pos) && !mLess(*pos, value)) {
|
||||
// return false; // Already inserted.
|
||||
if (result) {
|
||||
// *result = kExists;
|
||||
*result = InsertResult::kExists;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
if (mArray.size() >= mMaxSize) {
|
||||
// return false; // Too full
|
||||
if (result) {
|
||||
*result = InsertResult::kMaxSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
mArray.insert(pos, value);
|
||||
if (result) {
|
||||
*result = kInserted;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the first position where we should insert value to maintain sort order
|
||||
iterator lower_bound(const T& value) {
|
||||
iterator first = mArray.begin();
|
||||
iterator last = mArray.end();
|
||||
|
||||
while (first != last) {
|
||||
iterator mid = first + (last - first) / 2;
|
||||
|
||||
if (mLess(*mid, value)) {
|
||||
first = mid + 1;
|
||||
} else {
|
||||
last = mid;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
const_iterator lower_bound(const T& value) const {
|
||||
return const_cast<SortedHeapVector*>(this)->lower_bound(value);
|
||||
}
|
||||
|
||||
// Lookup operations
|
||||
iterator find(const T& value) {
|
||||
iterator pos = lower_bound(value);
|
||||
if (pos != end() && !mLess(value, *pos) && !mLess(*pos, value)) {
|
||||
return pos;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
void swap(SortedHeapVector& other) {
|
||||
mArray.swap(other.mArray);
|
||||
}
|
||||
|
||||
const_iterator find(const T& value) const {
|
||||
return const_cast<SortedHeapVector*>(this)->find(value);
|
||||
}
|
||||
|
||||
bool has(const T& value) const {
|
||||
return find(value) != end();
|
||||
}
|
||||
|
||||
// Removal operations
|
||||
bool erase(const T& value) {
|
||||
iterator it = find(value);
|
||||
if (it != end()) {
|
||||
return mArray.erase(it);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool erase(iterator pos) {
|
||||
return mArray.erase(pos);
|
||||
}
|
||||
|
||||
// Basic container operations
|
||||
size_t size() const { return mArray.size(); }
|
||||
bool empty() const { return mArray.empty(); }
|
||||
size_t capacity() const { return mArray.capacity(); }
|
||||
void clear() { mArray.clear(); }
|
||||
bool full() const {
|
||||
if (mArray.size() >= mMaxSize) {
|
||||
return true;
|
||||
}
|
||||
return mArray.full();
|
||||
}
|
||||
|
||||
// Element access
|
||||
T& operator[](size_t index) { return mArray[index]; }
|
||||
const T& operator[](size_t index) const { return mArray[index]; }
|
||||
|
||||
T& front() { return mArray.front(); }
|
||||
const T& front() const { return mArray.front(); }
|
||||
|
||||
T& back() { return mArray.back(); }
|
||||
const T& back() const { return mArray.back(); }
|
||||
|
||||
// Iterators
|
||||
iterator begin() { return mArray.begin(); }
|
||||
const_iterator begin() const { return mArray.begin(); }
|
||||
iterator end() { return mArray.end(); }
|
||||
const_iterator end() const { return mArray.end(); }
|
||||
|
||||
// Raw data access
|
||||
T* data() { return mArray.data(); }
|
||||
const T* data() const { return mArray.data(); }
|
||||
};
|
||||
|
||||
|
||||
} // namespace fl
|
||||
7
FastLED/src/fl/virtual_if_not_avr.h
Normal file
7
FastLED/src/fl/virtual_if_not_avr.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __AVR__
|
||||
#define VIRTUAL_IF_NOT_AVR
|
||||
#else
|
||||
#define VIRTUAL_IF_NOT_AVR virtual
|
||||
#endif
|
||||
9
FastLED/src/fl/warn.h
Normal file
9
FastLED/src/fl/warn.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/dbg.h"
|
||||
|
||||
#ifndef FASTLED_WARN
|
||||
// in the future this will do something
|
||||
#define FASTLED_WARN FASTLED_DBG
|
||||
#define FASTLED_WARN_IF FASTLED_DBG_IF
|
||||
#endif
|
||||
289
FastLED/src/fl/wave_simulation.cpp
Normal file
289
FastLED/src/fl/wave_simulation.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
// Based on works and code by Shawn Silverman.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/wave_simulation.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
uint8_t half_duplex_blend_sqrt_q15(uint16_t x) {
|
||||
x = MIN(x, 32767); // Q15
|
||||
const int Q = 15;
|
||||
uint32_t X = (uint32_t)x << Q; // promote to Q30
|
||||
uint32_t y = (1u << Q); // start at “1.0” in Q15
|
||||
|
||||
// 3–4 iterations is plenty for 15‑bit precision:
|
||||
for (int i = 0; i < 4; i++) {
|
||||
y = ( y + (X / y) ) >> 1;
|
||||
}
|
||||
return static_cast<int16_t>(y) >> 8;
|
||||
}
|
||||
|
||||
uint8_t half_duplex_blend_linear(uint16_t x) {
|
||||
x = MIN(x, 32767); // Q15
|
||||
x *= 2;
|
||||
return x >> 8;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
void WaveSimulation2D::setSpeed(float speed) { sim->setSpeed(speed); }
|
||||
|
||||
WaveSimulation2D::WaveSimulation2D(uint32_t W, uint32_t H, SuperSample factor,
|
||||
float speed, float dampening) {
|
||||
init(W, H, factor, speed, dampening);
|
||||
}
|
||||
|
||||
void WaveSimulation2D::init(uint32_t width, uint32_t height, SuperSample factor, float speed, int dampening) {
|
||||
outerWidth = width;
|
||||
outerHeight = height;
|
||||
multiplier = static_cast<uint32_t>(factor);
|
||||
sim.reset(new WaveSimulation2D_Real(width * multiplier, height * multiplier, speed, dampening));
|
||||
// Extra frames are needed because the simulation slows down in
|
||||
// proportion to the supersampling factor.
|
||||
extraFrames = uint8_t(factor) - 1;
|
||||
}
|
||||
|
||||
void WaveSimulation2D::setDampening(int damp) { sim->setDampening(damp); }
|
||||
|
||||
int WaveSimulation2D::getDampenening() const { return sim->getDampenening(); }
|
||||
|
||||
float WaveSimulation2D::getSpeed() const { return sim->getSpeed(); }
|
||||
|
||||
float WaveSimulation2D::getf(size_t x, size_t y) const {
|
||||
if (!has(x, y))
|
||||
return 0.0f;
|
||||
float sum = 0.0f;
|
||||
for (uint32_t j = 0; j < multiplier; ++j) {
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->getf(x * multiplier + i, y * multiplier + j);
|
||||
}
|
||||
}
|
||||
return sum / static_cast<float>(multiplier * multiplier);
|
||||
}
|
||||
|
||||
int16_t WaveSimulation2D::geti16(size_t x, size_t y) const {
|
||||
if (!has(x, y))
|
||||
return 0;
|
||||
int32_t sum = 0;
|
||||
for (uint32_t j = 0; j < multiplier; ++j) {
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->geti16(x * multiplier + i, y * multiplier + j);
|
||||
}
|
||||
}
|
||||
return static_cast<int16_t>(sum / (multiplier * multiplier));
|
||||
}
|
||||
|
||||
int16_t WaveSimulation2D::geti16Previous(size_t x, size_t y) const {
|
||||
if (!has(x, y))
|
||||
return 0;
|
||||
int32_t sum = 0;
|
||||
for (uint32_t j = 0; j < multiplier; ++j) {
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->geti16Previous(x * multiplier + i, y * multiplier + j);
|
||||
}
|
||||
}
|
||||
return static_cast<int16_t>(sum / (multiplier * multiplier));
|
||||
}
|
||||
|
||||
bool WaveSimulation2D::geti16All(size_t x, size_t y, int16_t *curr,
|
||||
int16_t *prev, int16_t *diff) const {
|
||||
if (!has(x, y))
|
||||
return false;
|
||||
*curr = geti16(x, y);
|
||||
*prev = geti16Previous(x, y);
|
||||
*diff = *curr - *prev;
|
||||
return true;
|
||||
}
|
||||
|
||||
int8_t WaveSimulation2D::geti8(size_t x, size_t y) const {
|
||||
return static_cast<int8_t>(geti16(x, y) >> 8);
|
||||
}
|
||||
|
||||
uint8_t WaveSimulation2D::getu8(size_t x, size_t y) const {
|
||||
int16_t value = geti16(x, y);
|
||||
if (sim->getHalfDuplex()) {
|
||||
uint16_t v2 = static_cast<uint16_t>(value);
|
||||
switch (mU8Mode) {
|
||||
case WAVE_U8_MODE_LINEAR:
|
||||
return half_duplex_blend_linear(v2);
|
||||
case WAVE_U8_MODE_SQRT:
|
||||
return half_duplex_blend_sqrt_q15(v2);
|
||||
}
|
||||
}
|
||||
return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
|
||||
}
|
||||
|
||||
bool WaveSimulation2D::has(size_t x, size_t y) const {
|
||||
return (x < outerWidth) && (y < outerHeight);
|
||||
}
|
||||
|
||||
void WaveSimulation2D::seti16(size_t x, size_t y, int16_t v16) {
|
||||
if (!has(x, y))
|
||||
return;
|
||||
|
||||
// radius in pixels of your diamond
|
||||
int rad = static_cast<int>(multiplier) / 2;
|
||||
|
||||
for (size_t j = 0; j < multiplier; ++j) {
|
||||
for (size_t i = 0; i < multiplier; ++i) {
|
||||
// compute offset from the center of this block
|
||||
int dx = static_cast<int>(i) - rad;
|
||||
int dy = static_cast<int>(j) - rad;
|
||||
|
||||
// keep only those points whose Manhattan distance ≤ rad
|
||||
if (ABS(dx) + ABS(dy) > rad)
|
||||
continue;
|
||||
|
||||
size_t xx = x * multiplier + i;
|
||||
size_t yy = y * multiplier + j;
|
||||
if (sim->has(xx, yy)) {
|
||||
sim->seti16(xx, yy, v16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaveSimulation2D::setf(size_t x, size_t y, float value) {
|
||||
if (!has(x, y))
|
||||
return;
|
||||
|
||||
int16_t v16 = wave_detail::float_to_fixed(value);
|
||||
seti16(x, y, v16);
|
||||
}
|
||||
|
||||
void WaveSimulation2D::update() {
|
||||
sim->update();
|
||||
for (uint8_t i = 0; i < extraFrames; ++i) {
|
||||
sim->update();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t WaveSimulation2D::getWidth() const { return outerWidth; }
|
||||
uint32_t WaveSimulation2D::getHeight() const { return outerHeight; }
|
||||
|
||||
void WaveSimulation2D::setExtraFrames(uint8_t extra) { extraFrames = extra; }
|
||||
|
||||
WaveSimulation1D::WaveSimulation1D(uint32_t length, SuperSample factor,
|
||||
float speed, int dampening) {
|
||||
init(length, factor, speed, dampening);
|
||||
}
|
||||
|
||||
void WaveSimulation1D::init(uint32_t length, SuperSample factor,
|
||||
float speed, int dampening) {
|
||||
outerLength = length;
|
||||
multiplier = static_cast<uint32_t>(factor);
|
||||
sim.reset(new WaveSimulation1D_Real(length * multiplier, speed, dampening));
|
||||
// Extra updates (frames) are applied because the simulation slows down in
|
||||
// proportion to the supersampling factor.
|
||||
extraFrames = static_cast<uint8_t>(factor) - 1;
|
||||
}
|
||||
|
||||
void WaveSimulation1D::setSpeed(float speed) { sim->setSpeed(speed); }
|
||||
|
||||
void WaveSimulation1D::setDampening(int damp) { sim->setDampening(damp); }
|
||||
|
||||
int WaveSimulation1D::getDampenening() const { return sim->getDampenening(); }
|
||||
|
||||
void WaveSimulation1D::setExtraFrames(uint8_t extra) { extraFrames = extra; }
|
||||
|
||||
float WaveSimulation1D::getSpeed() const { return sim->getSpeed(); }
|
||||
|
||||
float WaveSimulation1D::getf(size_t x) const {
|
||||
if (!has(x))
|
||||
return 0.0f;
|
||||
float sum = 0.0f;
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->getf(x * multiplier + i);
|
||||
}
|
||||
return sum / static_cast<float>(multiplier);
|
||||
}
|
||||
|
||||
int16_t WaveSimulation1D::geti16(size_t x) const {
|
||||
if (!has(x))
|
||||
return 0;
|
||||
int32_t sum = 0;
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->geti16(x * multiplier + i);
|
||||
}
|
||||
return static_cast<int16_t>(sum / multiplier);
|
||||
}
|
||||
|
||||
int16_t WaveSimulation1D::geti16Previous(size_t x) const {
|
||||
if (!has(x))
|
||||
return 0;
|
||||
int32_t sum = 0;
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sum += sim->geti16Previous(x * multiplier + i);
|
||||
}
|
||||
return static_cast<int16_t>(sum / multiplier);
|
||||
}
|
||||
|
||||
bool WaveSimulation1D::geti16All(size_t x, int16_t *curr, int16_t *prev,
|
||||
int16_t *diff) const {
|
||||
if (!has(x))
|
||||
return false;
|
||||
*curr = geti16(x);
|
||||
*prev = geti16Previous(x);
|
||||
*diff = *curr - *prev;
|
||||
return true;
|
||||
}
|
||||
|
||||
int8_t WaveSimulation1D::geti8(size_t x) const {
|
||||
return static_cast<int8_t>(geti16(x) >> 8);
|
||||
}
|
||||
|
||||
// uint8_t WaveSimulation2D::getu8(size_t x, size_t y) const {
|
||||
// int16_t value = geti16(x, y);
|
||||
// if (sim->getHalfDuplex()) {
|
||||
// uint16_t v2 = static_cast<uint16_t>(value);
|
||||
// switch (mU8Mode) {
|
||||
// case WAVE_U8_MODE_LINEAR:
|
||||
// return half_duplex_blend_linear(v2);
|
||||
// case WAVE_U8_MODE_SQRT:
|
||||
// return half_duplex_blend_sqrt_q15(v2);
|
||||
// }
|
||||
// }
|
||||
// return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
|
||||
// }
|
||||
|
||||
uint8_t WaveSimulation1D::getu8(size_t x) const {
|
||||
int16_t value = geti16(x);
|
||||
if (sim->getHalfDuplex()) {
|
||||
uint16_t v2 = static_cast<uint16_t>(value);
|
||||
switch (mU8Mode) {
|
||||
case WAVE_U8_MODE_LINEAR:
|
||||
return half_duplex_blend_linear(v2);
|
||||
case WAVE_U8_MODE_SQRT:
|
||||
return half_duplex_blend_sqrt_q15(v2);
|
||||
}
|
||||
}
|
||||
return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
|
||||
}
|
||||
|
||||
bool WaveSimulation1D::has(size_t x) const { return (x < outerLength); }
|
||||
|
||||
void WaveSimulation1D::setf(size_t x, float value) {
|
||||
if (!has(x))
|
||||
return;
|
||||
for (uint32_t i = 0; i < multiplier; ++i) {
|
||||
sim->set(x * multiplier + i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void WaveSimulation1D::update() {
|
||||
sim->update();
|
||||
for (uint8_t i = 0; i < extraFrames; ++i) {
|
||||
sim->update();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t WaveSimulation1D::getLength() const { return outerLength; }
|
||||
|
||||
} // namespace fl
|
||||
217
FastLED/src/fl/wave_simulation.h
Normal file
217
FastLED/src/fl/wave_simulation.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
This is the WaveSimulation API. These classes allow flexible super sampling
|
||||
to achieve much better visual quality. They will also run the simulator
|
||||
update multiple times in order to achieve consistent speed between super
|
||||
sampling factors.
|
||||
|
||||
A super sampling value of 2x will give the best results for the CPU consumption
|
||||
as most artifacts will be averaged out at this resolution.
|
||||
|
||||
Based on works and code by Shawn Silverman.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/math_macros.h" // if needed for MAX/MIN macros
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/scoped_ptr.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/wave_simulation_real.h"
|
||||
|
||||
#include "fl/ptr.h"
|
||||
#include "fl/supersample.h"
|
||||
#include "fl/xymap.h"
|
||||
#include "fx/fx.h"
|
||||
#include "fx/fx2d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
enum U8EasingFunction {
|
||||
WAVE_U8_MODE_LINEAR,
|
||||
WAVE_U8_MODE_SQRT
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// New supersampled 1D simulation class.
|
||||
//
|
||||
// This class mimics the supersampling logic of WaveSimulation2D.
|
||||
// The constructor accepts the desired downsampled length and a supersampling
|
||||
// multiplier (via a SuperSample enum). Internally, it creates a high-resolution
|
||||
// simulation of size (multiplier * length), and its accessor methods average
|
||||
// over or replicate across the corresponding block of high-res cells.
|
||||
class WaveSimulation1D {
|
||||
public:
|
||||
// Constructor:
|
||||
// - length: desired downsampled grid length.
|
||||
// - factor: supersampling multiplier (e.g., 1x, 2x, 4x, or 8x).
|
||||
// Higher values yield better quality but are more cpu intensive.
|
||||
// - speed and dampening are passed on to the internal simulation.
|
||||
WaveSimulation1D(uint32_t length,
|
||||
SuperSample factor = SuperSample::SUPER_SAMPLE_NONE,
|
||||
float speed = 0.16f, int dampening = 6);
|
||||
|
||||
void init(uint32_t length, SuperSample factor, float speed, int dampening);
|
||||
|
||||
void setSuperSample(SuperSample factor) {
|
||||
if (uint32_t(factor) == multiplier) {
|
||||
return;
|
||||
}
|
||||
init(outerLength, factor, sim->getSpeed(), sim->getDampenening());
|
||||
}
|
||||
|
||||
// Only applies to getu8().
|
||||
void setEasingMode(U8EasingFunction mode) { mU8Mode = mode; }
|
||||
|
||||
~WaveSimulation1D() = default;
|
||||
|
||||
// Delegate methods to the internal simulation.
|
||||
void setSpeed(float speed);
|
||||
void setDampening(int damp);
|
||||
int getDampenening() const;
|
||||
float getSpeed() const;
|
||||
|
||||
// Runs the simulator faster by updating it multiple times.
|
||||
void setExtraFrames(uint8_t extra);
|
||||
|
||||
// Downsampled getter for the floating point value at index x.
|
||||
// It averages over the corresponding 'multiplier'-sized block in the
|
||||
// high-res simulation.
|
||||
float getf(size_t x) const;
|
||||
|
||||
// Downsampled getter for the Q15 (fixed point) value at index x.
|
||||
// It averages the multiplier cells of Q15 values.
|
||||
int16_t geti16(size_t x) const;
|
||||
int16_t geti16Previous(size_t x) const;
|
||||
|
||||
bool geti16All(size_t x, int16_t *curr, int16_t *prev, int16_t *diff) const;
|
||||
|
||||
// Downsampled getters for the 8-bit representations.
|
||||
int8_t geti8(size_t x) const;
|
||||
|
||||
uint8_t getu8(size_t x) const;
|
||||
|
||||
// Check if x is within the bounds of the outer (downsampled) simulation.
|
||||
bool has(size_t x) const;
|
||||
|
||||
// Upsampling setter: set the value at an outer grid cell x by replicating
|
||||
// it to the corresponding multiplier cells in the high-res simulation.
|
||||
void setf(size_t x, float value);
|
||||
|
||||
|
||||
void setHalfDuplex(bool on) {
|
||||
sim->setHalfDuplex(on);
|
||||
}
|
||||
|
||||
// Advance the simulation one time step.
|
||||
void update();
|
||||
|
||||
// Get the outer (downsampled) grid length.
|
||||
uint32_t getLength() const;
|
||||
|
||||
WaveSimulation1D_Real &real() { return *sim; }
|
||||
|
||||
private:
|
||||
uint32_t outerLength; // Length of the downsampled simulation.
|
||||
uint8_t extraFrames = 0;
|
||||
uint32_t multiplier; // Supersampling multiplier (e.g., 2, 4, or 8).
|
||||
U8EasingFunction mU8Mode = WAVE_U8_MODE_LINEAR;
|
||||
// Internal high-resolution simulation.
|
||||
fl::scoped_ptr<WaveSimulation1D_Real> sim;
|
||||
};
|
||||
|
||||
class WaveSimulation2D {
|
||||
public:
|
||||
// Constructor:
|
||||
// - W and H specify the desired inner grid size of the downsampled
|
||||
// simulation.
|
||||
// - 'factor' selects the supersampling multiplier (e.g., 2x, 4x, or 8x).
|
||||
// Higher values yield better quality but are more cpu intensive.
|
||||
// - Internally, the simulation is created with dimensions (factor*W x
|
||||
// factor*H).
|
||||
// - 'speed' and 'dampening' parameters are passed on to the internal
|
||||
// simulation.
|
||||
WaveSimulation2D(uint32_t W, uint32_t H,
|
||||
SuperSample factor = SuperSample::SUPER_SAMPLE_NONE,
|
||||
float speed = 0.16f, float dampening = 6.0f);
|
||||
|
||||
void init(uint32_t width, uint32_t height, SuperSample factor, float speed, int dampening);
|
||||
|
||||
~WaveSimulation2D() = default;
|
||||
|
||||
// Delegated simulation methods.
|
||||
void setSpeed(float speed);
|
||||
|
||||
void setExtraFrames(uint8_t extra);
|
||||
|
||||
void setDampening(int damp);
|
||||
|
||||
void setEasingMode(U8EasingFunction mode) { mU8Mode = mode; }
|
||||
|
||||
int getDampenening() const;
|
||||
|
||||
float getSpeed() const;
|
||||
|
||||
void setSuperSample(SuperSample factor) {
|
||||
if (uint32_t(factor) == multiplier) {
|
||||
return;
|
||||
}
|
||||
init(outerWidth, outerHeight, factor, sim->getSpeed(),
|
||||
sim->getDampenening());
|
||||
}
|
||||
|
||||
// Downsampled getter for the floating point value at (x,y) in the outer
|
||||
// grid. It averages over the corresponding multiplier×multiplier block in
|
||||
// the high-res simulation.
|
||||
float getf(size_t x, size_t y) const;
|
||||
|
||||
// Downsampled getter for the Q15 (fixed point) value at (x,y).
|
||||
// It averages the multiplier×multiplier block of Q15 values.
|
||||
int16_t geti16(size_t x, size_t y) const;
|
||||
int16_t geti16Previous(size_t x, size_t y) const;
|
||||
|
||||
bool geti16All(size_t x, size_t y, int16_t *curr, int16_t *prev,
|
||||
int16_t *diff) const;
|
||||
|
||||
// Downsampled getters for the 8-bit representations.
|
||||
int8_t geti8(size_t x, size_t y) const;
|
||||
|
||||
// Special function to get the value as a uint8_t for drawing / gradients.
|
||||
// Ease out functions are applied to this when in half duplex mode.
|
||||
uint8_t getu8(size_t x, size_t y) const;
|
||||
|
||||
// Check if (x,y) is within the bounds of the outer (downsampled) grid.
|
||||
bool has(size_t x, size_t y) const;
|
||||
|
||||
// Upsampling setter: set the value at an outer grid cell (x,y) by
|
||||
// replicating it to all cells of the corresponding multiplier×multiplier
|
||||
// block in the high-res simulation.
|
||||
void setf(size_t x, size_t y, float value);
|
||||
|
||||
void seti16(size_t x, size_t y, int16_t value);
|
||||
|
||||
void setHalfDuplex(bool on) {
|
||||
sim->setHalfDuplex(on);
|
||||
}
|
||||
|
||||
// Advance the simulation one time step.
|
||||
void update();
|
||||
|
||||
// Get the outer grid dimensions.
|
||||
uint32_t getWidth() const;
|
||||
uint32_t getHeight() const;
|
||||
|
||||
WaveSimulation2D_Real &real() { return *sim; }
|
||||
|
||||
private:
|
||||
uint32_t outerWidth; // Width of the downsampled (outer) grid.
|
||||
uint32_t outerHeight; // Height of the downsampled (outer) grid.
|
||||
uint8_t extraFrames = 0;
|
||||
uint32_t multiplier; // Supersampling multiplier (e.g., 2, 4, or 8).
|
||||
U8EasingFunction mU8Mode = WAVE_U8_MODE_LINEAR;
|
||||
// Internal high-resolution simulation.
|
||||
fl::scoped_ptr<WaveSimulation2D_Real> sim;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
272
FastLED/src/fl/wave_simulation_real.cpp
Normal file
272
FastLED/src/fl/wave_simulation_real.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
// Based on works and code by Shawn Silverman.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/wave_simulation_real.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
// Define Q15 conversion constants.
|
||||
#define FIXED_SCALE (1 << 15) // 32768: 1.0 in Q15
|
||||
#define FIXED_ONE (FIXED_SCALE)
|
||||
|
||||
namespace wave_detail { // Anonymous namespace for internal linkage
|
||||
// Convert float to fixed Q15.
|
||||
int16_t float_to_fixed(float f) { return (int16_t)(f * FIXED_SCALE); }
|
||||
|
||||
// Convert fixed Q15 to float.
|
||||
float fixed_to_float(int16_t f) { return ((float)f) / FIXED_SCALE; }
|
||||
|
||||
// // Multiply two Q15 fixed point numbers.
|
||||
// int16_t fixed_mul(int16_t a, int16_t b) {
|
||||
// return (int16_t)(((int32_t)a * b) >> 15);
|
||||
// }
|
||||
} // namespace
|
||||
|
||||
using namespace wave_detail;
|
||||
|
||||
WaveSimulation1D_Real::WaveSimulation1D_Real(uint32_t len, float courantSq,
|
||||
int dampening)
|
||||
: length(len),
|
||||
grid1(new int16_t[length + 2]()), // Allocate and zero-initialize with
|
||||
// length+2 elements.
|
||||
grid2(new int16_t[length + 2]()), whichGrid(0),
|
||||
mCourantSq(float_to_fixed(courantSq)), mDampenening(dampening) {
|
||||
// Additional initialization can be added here if needed.
|
||||
}
|
||||
|
||||
void WaveSimulation1D_Real::setSpeed(float something) {
|
||||
mCourantSq = float_to_fixed(something);
|
||||
}
|
||||
|
||||
void WaveSimulation1D_Real::setDampening(int damp) { mDampenening = damp; }
|
||||
|
||||
int WaveSimulation1D_Real::getDampenening() const { return mDampenening; }
|
||||
|
||||
float WaveSimulation1D_Real::getSpeed() const {
|
||||
return fixed_to_float(mCourantSq);
|
||||
}
|
||||
|
||||
int16_t WaveSimulation1D_Real::geti16(size_t x) const {
|
||||
if (x >= length) {
|
||||
FASTLED_WARN("Out of range.");
|
||||
return 0;
|
||||
}
|
||||
const int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
|
||||
return curr[x + 1];
|
||||
}
|
||||
|
||||
int16_t WaveSimulation1D_Real::geti16Previous(size_t x) const {
|
||||
if (x >= length) {
|
||||
FASTLED_WARN("Out of range.");
|
||||
return 0;
|
||||
}
|
||||
const int16_t *prev = (whichGrid == 0) ? grid2.get() : grid1.get();
|
||||
return prev[x + 1];
|
||||
}
|
||||
|
||||
float WaveSimulation1D_Real::getf(size_t x) const {
|
||||
if (x >= length) {
|
||||
FASTLED_WARN("Out of range.");
|
||||
return 0.0f;
|
||||
}
|
||||
// Retrieve value from the active grid (offset by 1 for boundary).
|
||||
const int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
|
||||
return fixed_to_float(curr[x + 1]);
|
||||
}
|
||||
|
||||
bool WaveSimulation1D_Real::has(size_t x) const { return (x < length); }
|
||||
|
||||
void WaveSimulation1D_Real::set(size_t x, float value) {
|
||||
if (x >= length) {
|
||||
FASTLED_WARN("warning X value too high");
|
||||
return;
|
||||
}
|
||||
int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
|
||||
curr[x + 1] = float_to_fixed(value);
|
||||
}
|
||||
|
||||
void WaveSimulation1D_Real::update() {
|
||||
int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
|
||||
int16_t *next = (whichGrid == 0) ? grid2.get() : grid1.get();
|
||||
|
||||
// Update boundaries with a Neumann (zero-gradient) condition:
|
||||
curr[0] = curr[1];
|
||||
curr[length + 1] = curr[length];
|
||||
|
||||
// Compute dampening factor as an integer value: 2^(mDampenening)
|
||||
int32_t dampening_factor = 1 << mDampenening;
|
||||
|
||||
int32_t mCourantSq32 = static_cast<int32_t>(mCourantSq);
|
||||
// Iterate over each inner cell.
|
||||
for (size_t i = 1; i < length + 1; i++) {
|
||||
// Compute the 1D Laplacian:
|
||||
// lap = curr[i+1] - 2 * curr[i] + curr[i-1]
|
||||
int32_t lap =
|
||||
(int32_t)curr[i + 1] - ((int32_t)curr[i] << 1) + curr[i - 1];
|
||||
|
||||
// Multiply the Laplacian by the simulation speed using Q15 arithmetic:
|
||||
int32_t term = (mCourantSq32 * lap) >> 15;
|
||||
|
||||
// Compute the new value:
|
||||
// f = -next[i] + 2 * curr[i] + term
|
||||
int32_t f = -(int32_t)next[i] + ((int32_t)curr[i] << 1) + term;
|
||||
|
||||
// Apply damping:
|
||||
f = f - (f / dampening_factor);
|
||||
|
||||
// Clamp the result to the Q15 range [-32768, 32767].
|
||||
if (f > 32767)
|
||||
f = 32767;
|
||||
else if (f < -32768)
|
||||
f = -32768;
|
||||
|
||||
next[i] = (int16_t)f;
|
||||
}
|
||||
|
||||
if (mHalfDuplex) {
|
||||
// Set the negative values to zero.
|
||||
for (size_t i = 1; i < length + 1; i++) {
|
||||
if (next[i] < 0) {
|
||||
next[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the active grid.
|
||||
whichGrid ^= 1;
|
||||
}
|
||||
|
||||
WaveSimulation2D_Real::WaveSimulation2D_Real(uint32_t W, uint32_t H,
|
||||
float speed, float dampening)
|
||||
: width(W), height(H), stride(W + 2),
|
||||
grid1(new int16_t[(W + 2) * (H + 2)]()),
|
||||
grid2(new int16_t[(W + 2) * (H + 2)]()), whichGrid(0),
|
||||
// Initialize speed 0.16 in fixed Q15
|
||||
mCourantSq(float_to_fixed(speed)),
|
||||
// Dampening exponent; e.g., 6 means a factor of 2^6 = 64.
|
||||
mDampening(dampening) {}
|
||||
|
||||
void WaveSimulation2D_Real::setSpeed(float something) {
|
||||
mCourantSq = float_to_fixed(something);
|
||||
}
|
||||
|
||||
void WaveSimulation2D_Real::setDampening(int damp) { mDampening = damp; }
|
||||
|
||||
int WaveSimulation2D_Real::getDampenening() const { return mDampening; }
|
||||
|
||||
float WaveSimulation2D_Real::getSpeed() const {
|
||||
return fixed_to_float(mCourantSq);
|
||||
}
|
||||
|
||||
float WaveSimulation2D_Real::getf(size_t x, size_t y) const {
|
||||
if (x >= width || y >= height) {
|
||||
FASTLED_WARN("Out of range: " << x << ", " << y);
|
||||
return 0.0f;
|
||||
}
|
||||
const int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
|
||||
return fixed_to_float(curr[(y + 1) * stride + (x + 1)]);
|
||||
}
|
||||
|
||||
int16_t WaveSimulation2D_Real::geti16(size_t x, size_t y) const {
|
||||
if (x >= width || y >= height) {
|
||||
FASTLED_WARN("Out of range: " << x << ", " << y);
|
||||
return 0;
|
||||
}
|
||||
const int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
|
||||
return curr[(y + 1) * stride + (x + 1)];
|
||||
}
|
||||
|
||||
int16_t WaveSimulation2D_Real::geti16Previous(size_t x, size_t y) const {
|
||||
if (x >= width || y >= height) {
|
||||
FASTLED_WARN("Out of range: " << x << ", " << y);
|
||||
return 0;
|
||||
}
|
||||
const int16_t *prev = (whichGrid == 0 ? grid2.get() : grid1.get());
|
||||
return prev[(y + 1) * stride + (x + 1)];
|
||||
}
|
||||
|
||||
bool WaveSimulation2D_Real::has(size_t x, size_t y) const {
|
||||
return (x < width && y < height);
|
||||
}
|
||||
|
||||
void WaveSimulation2D_Real::setf(size_t x, size_t y, float value) {
|
||||
int16_t v = float_to_fixed(value);
|
||||
return seti16(x, y, v);
|
||||
}
|
||||
|
||||
void WaveSimulation2D_Real::seti16(size_t x, size_t y, int16_t value) {
|
||||
if (x >= width || y >= height) {
|
||||
FASTLED_WARN("Out of range: " << x << ", " << y);
|
||||
return;
|
||||
}
|
||||
int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
|
||||
curr[(y + 1) * stride + (x + 1)] = value;
|
||||
}
|
||||
|
||||
void WaveSimulation2D_Real::update() {
|
||||
int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
|
||||
int16_t *next = (whichGrid == 0 ? grid2.get() : grid1.get());
|
||||
|
||||
// Update horizontal boundaries.
|
||||
for (size_t j = 0; j < height + 2; ++j) {
|
||||
curr[j * stride + 0] = curr[j * stride + 1];
|
||||
curr[j * stride + (width + 1)] = curr[j * stride + width];
|
||||
}
|
||||
|
||||
// Update vertical boundaries.
|
||||
for (size_t i = 0; i < width + 2; ++i) {
|
||||
curr[0 * stride + i] = curr[1 * stride + i];
|
||||
curr[(height + 1) * stride + i] = curr[height * stride + i];
|
||||
}
|
||||
|
||||
// Compute the dampening factor as an integer: 2^(dampening).
|
||||
int32_t dampening_factor = 1 << mDampening; // e.g., 6 -> 64
|
||||
int32_t mCourantSq32 = static_cast<int32_t>(mCourantSq);
|
||||
|
||||
// Update each inner cell.
|
||||
for (size_t j = 1; j <= height; ++j) {
|
||||
for (size_t i = 1; i <= width; ++i) {
|
||||
int index = j * stride + i;
|
||||
// Laplacian: sum of four neighbors minus 4 times the center.
|
||||
int32_t laplacian = (int32_t)curr[index + 1] + curr[index - 1] +
|
||||
curr[index + stride] + curr[index - stride] -
|
||||
((int32_t)curr[index] << 2);
|
||||
// Compute the new value:
|
||||
// f = - next[index] + 2 * curr[index] + mCourantSq * laplacian
|
||||
// The multiplication is in Q15, so we shift right by 15.
|
||||
int32_t term = (mCourantSq32 * laplacian) >> 15;
|
||||
int32_t f =
|
||||
-(int32_t)next[index] + ((int32_t)curr[index] << 1) + term;
|
||||
|
||||
// Apply damping:
|
||||
f = f - (f / dampening_factor);
|
||||
|
||||
// Clamp f to the Q15 range.
|
||||
if (f > 32767)
|
||||
f = 32767;
|
||||
else if (f < -32768)
|
||||
f = -32768;
|
||||
|
||||
next[index] = (int16_t)f;
|
||||
}
|
||||
}
|
||||
|
||||
if (mHalfDuplex) {
|
||||
// Set negative values to zero.
|
||||
for (size_t j = 1; j <= height; ++j) {
|
||||
for (size_t i = 1; i <= width; ++i) {
|
||||
int index = j * stride + i;
|
||||
if (next[index] < 0) {
|
||||
next[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swap the roles of the grids.
|
||||
whichGrid ^= 1;
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
203
FastLED/src/fl/wave_simulation_real.h
Normal file
203
FastLED/src/fl/wave_simulation_real.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Wave simulation classes for 1D and 2D simulations. This is called _Real because
|
||||
there is a one to one mapping between the simulation and the LED grid. For
|
||||
flexible super sampling see wave_simluation.h.
|
||||
|
||||
Based on works and code by Shawn Silverman.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fl/math_macros.h" // if needed for MAX/MIN macros
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/scoped_ptr.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
#include "fl/ptr.h"
|
||||
#include "fl/supersample.h"
|
||||
#include "fl/xymap.h"
|
||||
#include "fx/fx.h"
|
||||
#include "fx/fx2d.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
namespace wave_detail {
|
||||
int16_t float_to_fixed(float f);
|
||||
|
||||
// Convert fixed Q15 to float.
|
||||
float fixed_to_float(int16_t f);
|
||||
} // namespace wave_detail
|
||||
|
||||
class WaveSimulation1D_Real {
|
||||
public:
|
||||
// Constructor:
|
||||
// - length: inner simulation grid length (excluding the 2 boundary cells).
|
||||
// - speed: simulation speed (in float, will be stored in Q15).
|
||||
// - dampening: exponent so that the effective damping factor is
|
||||
// 2^(dampening).
|
||||
WaveSimulation1D_Real(uint32_t length, float speed = 0.16f,
|
||||
int dampening = 6);
|
||||
~WaveSimulation1D_Real() = default;
|
||||
|
||||
// Set simulation speed (courant parameter) using a float.
|
||||
void setSpeed(float something);
|
||||
|
||||
// Set the dampening exponent (effective damping factor is 2^(dampening)).
|
||||
void setDampening(int damp);
|
||||
|
||||
// Get the current dampening exponent.
|
||||
int getDampenening() const;
|
||||
|
||||
// Get the simulation speed as a float.
|
||||
float getSpeed() const;
|
||||
|
||||
void setHalfDuplex(bool on) { mHalfDuplex = on; }
|
||||
|
||||
bool getHalfDuplex() const { return mHalfDuplex; }
|
||||
|
||||
// Get the simulation value at the inner grid cell x (converted to float in
|
||||
// the range [-1.0, 1.0]).
|
||||
float getf(size_t x) const;
|
||||
|
||||
int16_t geti16(size_t x) const;
|
||||
int16_t geti16Previous(size_t x) const;
|
||||
|
||||
int8_t geti8(size_t x) const { return static_cast<int8_t>(geti16(x) >> 8); }
|
||||
|
||||
// If mHalfDuplex is set then the the values are adjusted so that negative
|
||||
// values will instead be represented by zero.
|
||||
uint8_t getu8(size_t x) const {
|
||||
int16_t value = geti16(x);
|
||||
// Rebase the range from [-32768, 32767] to [0, 65535] then extract the
|
||||
// upper 8 bits.
|
||||
// return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768))
|
||||
// >>
|
||||
// 8);
|
||||
if (mHalfDuplex) {
|
||||
uint16_t v2 = static_cast<uint16_t>(value);
|
||||
v2 *= 2;
|
||||
return static_cast<uint8_t>(v2 >> 8);
|
||||
} else {
|
||||
return static_cast<uint8_t>(
|
||||
((static_cast<uint16_t>(value) + 32768)) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether x is within the inner grid bounds.
|
||||
bool has(size_t x) const;
|
||||
|
||||
// Set the value at grid cell x (expected range: [-1.0, 1.0]); the value is
|
||||
// stored in Q15.
|
||||
void set(size_t x, float value);
|
||||
|
||||
// Advance the simulation one time step.
|
||||
void update();
|
||||
|
||||
private:
|
||||
uint32_t length; // Length of the inner simulation grid.
|
||||
// Two grids stored in fixed Q15 format, each with length+2 entries
|
||||
// (including boundary cells).
|
||||
fl::scoped_array<int16_t> grid1;
|
||||
fl::scoped_array<int16_t> grid2;
|
||||
size_t whichGrid; // Indicates the active grid (0 or 1).
|
||||
|
||||
int16_t mCourantSq; // Simulation speed (courant squared) stored in Q15.
|
||||
int mDampenening; // Dampening exponent (damping factor = 2^(mDampenening)).
|
||||
bool mHalfDuplex =
|
||||
true; // Flag to restrict values to positive range during update.
|
||||
};
|
||||
|
||||
class WaveSimulation2D_Real {
|
||||
public:
|
||||
// Constructor: Initializes the simulation with inner grid size (W x H).
|
||||
// The grid dimensions include a 1-cell border on each side.
|
||||
// Here, 'speed' is specified as a float (converted to fixed Q15)
|
||||
// and 'dampening' is given as an exponent so that the damping factor is
|
||||
// 2^dampening.
|
||||
WaveSimulation2D_Real(uint32_t W, uint32_t H, float speed = 0.16f,
|
||||
float dampening = 6.0f);
|
||||
~WaveSimulation2D_Real() = default;
|
||||
|
||||
// Set the simulation speed (courantSq) using a float value.
|
||||
void setSpeed(float something);
|
||||
|
||||
// Set the dampening factor exponent.
|
||||
// The dampening factor used is 2^(dampening).
|
||||
void setDampening(int damp);
|
||||
|
||||
// Get the current dampening exponent.
|
||||
int getDampenening() const;
|
||||
|
||||
// Get the simulation speed as a float (converted from fixed Q15).
|
||||
float getSpeed() const;
|
||||
|
||||
// Return the value at an inner grid cell (x,y), converted to float.
|
||||
// The value returned is in the range [-1.0, 1.0].
|
||||
float getf(size_t x, size_t y) const;
|
||||
|
||||
// Return the value at an inner grid cell (x,y) as a fixed Q15 integer
|
||||
// in the range [-32768, 32767].
|
||||
int16_t geti16(size_t x, size_t y) const;
|
||||
int16_t geti16Previous(size_t x, size_t y) const;
|
||||
|
||||
int8_t geti8(size_t x, size_t y) const {
|
||||
return static_cast<int8_t>(geti16(x, y) >> 8);
|
||||
}
|
||||
|
||||
uint8_t getu8(size_t x, size_t y) const {
|
||||
int16_t value = geti16(x, y);
|
||||
// Rebase the range from [-32768, 32767] to [0, 65535] then extract the
|
||||
// upper 8 bits.
|
||||
// return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768))
|
||||
// >>
|
||||
// 8);
|
||||
if (mHalfDuplex) {
|
||||
uint16_t v2 = static_cast<uint16_t>(value);
|
||||
v2 *= 2;
|
||||
return static_cast<uint8_t>(v2 >> 8);
|
||||
} else {
|
||||
return static_cast<uint8_t>(
|
||||
((static_cast<uint16_t>(value) + 32768)) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if (x,y) is within the inner grid.
|
||||
bool has(size_t x, size_t y) const;
|
||||
|
||||
// Set the value at an inner grid cell using a float;
|
||||
// the value is stored in fixed Q15 format.
|
||||
// value shoudl be between -1.0 and 1.0.
|
||||
void setf(size_t x, size_t y, float value);
|
||||
|
||||
void seti16(size_t x, size_t y, int16_t value);
|
||||
|
||||
void setHalfDuplex(bool on) { mHalfDuplex = on; }
|
||||
|
||||
bool getHalfDuplex() const { return mHalfDuplex; }
|
||||
|
||||
// Advance the simulation one time step using fixed-point arithmetic.
|
||||
void update();
|
||||
|
||||
uint32_t getWidth() const { return width; }
|
||||
uint32_t getHeight() const { return height; }
|
||||
|
||||
private:
|
||||
uint32_t width; // Width of the inner grid.
|
||||
uint32_t height; // Height of the inner grid.
|
||||
uint32_t stride; // Row length (width + 2 for the borders).
|
||||
|
||||
// Two separate grids stored in fixed Q15 format.
|
||||
fl::scoped_array<int16_t> grid1;
|
||||
fl::scoped_array<int16_t> grid2;
|
||||
|
||||
size_t whichGrid; // Indicates the active grid (0 or 1).
|
||||
|
||||
int16_t mCourantSq; // Fixed speed parameter in Q15.
|
||||
int mDampening; // Dampening exponent; used as 2^(dampening).
|
||||
bool mHalfDuplex =
|
||||
true; // Flag to restrict values to positive range during update.
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user