updated libraries

This commit is contained in:
stuce-bot 2025-06-30 21:41:49 +02:00
parent d5d5d000b3
commit 3b7e68065a
102 changed files with 3020 additions and 1624 deletions

View file

@ -57,7 +57,7 @@ uint8_t get_brightness();
void *pSmartMatrix = NULL;
FL_DISABLE_WARNING_PUSH
FL_DISABLE_WARNING(global-constructors)
FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
CFastLED FastLED; // global constructor allowed in this case.

View file

@ -16,13 +16,13 @@
/// * 1 digit for the major version
/// * 3 digits for the minor version
/// * 3 digits for the patch version
#define FASTLED_VERSION 3009020
#define FASTLED_VERSION 301001
#ifndef FASTLED_INTERNAL
# ifdef FASTLED_SHOW_VERSION
# ifdef FASTLED_HAS_PRAGMA_MESSAGE
# pragma message "FastLED version 3.009.020"
# pragma message "FastLED version 3.010.001"
# else
# warning FastLED version 3.009.020 (Not really a warning, just telling you here.)
# warning FastLED version 3.010.001 (Not really a warning, just telling you here.)
# endif
# endif
#endif
@ -968,3 +968,6 @@ using namespace fl;
void loop() { FASTLED_WARN("hijacked the loop"); real_loop(); } \
void real_loop()
#endif
#include "fl/sketch_macros.h"

View file

@ -6,7 +6,7 @@
#include "FastLED.h"
#include "fl/xymap.h"
#include "fl/bilinear_expansion.h"
#include "fl/upscale.h"
#include "fl/downscale.h"
#include "lib8tion/math8.h"
@ -82,7 +82,7 @@ void CRGB::upscale(const CRGB *src, const fl::XYMap &srcXY, CRGB *dst,
"Upscaling only works with a src matrix that is rectangular");
uint16_t w = srcXY.getWidth();
uint16_t h = srcXY.getHeight();
fl::bilinearExpand(src, dst, w, h, dstXY);
fl::upscale(src, dst, w, h, dstXY);
}
CRGB &CRGB::nscale8(uint8_t scaledown) {

View file

@ -11,4 +11,159 @@ void reverse(Iterator first, Iterator last) {
}
}
template <typename Iterator>
Iterator max_element(Iterator first, Iterator last) {
if (first == last) {
return last;
}
Iterator max_iter = first;
++first;
while (first != last) {
if (*max_iter < *first) {
max_iter = first;
}
++first;
}
return max_iter;
}
template <typename Iterator, typename Compare>
Iterator max_element(Iterator first, Iterator last, Compare comp) {
if (first == last) {
return last;
}
Iterator max_iter = first;
++first;
while (first != last) {
if (comp(*max_iter, *first)) {
max_iter = first;
}
++first;
}
return max_iter;
}
template <typename Iterator>
Iterator min_element(Iterator first, Iterator last) {
if (first == last) {
return last;
}
Iterator min_iter = first;
++first;
while (first != last) {
if (*first < *min_iter) {
min_iter = first;
}
++first;
}
return min_iter;
}
template <typename Iterator, typename Compare>
Iterator min_element(Iterator first, Iterator last, Compare comp) {
if (first == last) {
return last;
}
Iterator min_iter = first;
++first;
while (first != last) {
if (comp(*first, *min_iter)) {
min_iter = first;
}
++first;
}
return min_iter;
}
template <typename Iterator1, typename Iterator2>
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2) {
while (first1 != last1) {
if (*first1 != *first2) {
return false;
}
++first1;
++first2;
}
return true;
}
template <typename Iterator1, typename Iterator2, typename BinaryPredicate>
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, BinaryPredicate pred) {
while (first1 != last1) {
if (!pred(*first1, *first2)) {
return false;
}
++first1;
++first2;
}
return true;
}
template <typename Iterator1, typename Iterator2>
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) {
while (first1 != last1 && first2 != last2) {
if (*first1 != *first2) {
return false;
}
++first1;
++first2;
}
return first1 == last1 && first2 == last2;
}
template <typename Iterator1, typename Iterator2, typename BinaryPredicate>
bool equal(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, BinaryPredicate pred) {
while (first1 != last1 && first2 != last2) {
if (!pred(*first1, *first2)) {
return false;
}
++first1;
++first2;
}
return first1 == last1 && first2 == last2;
}
template <typename Container1, typename Container2>
bool equal_container(const Container1& c1, const Container2& c2) {
size_t size1 = c1.size();
size_t size2 = c2.size();
if (size1 != size2) {
return false;
}
return equal(c1.begin(), c1.end(), c2.begin(), c2.end());
}
template <typename Container1, typename Container2, typename BinaryPredicate>
bool equal_container(const Container1& c1, const Container2& c2, BinaryPredicate pred) {
size_t size1 = c1.size();
size_t size2 = c2.size();
if (size1 != size2) {
return false;
}
return equal(c1.begin(), c1.end(), c2.begin(), c2.end(), pred);
}
template <typename Iterator, typename T>
void fill(Iterator first, Iterator last, const T& value) {
while (first != last) {
*first = value;
++first;
}
}
} // namespace fl

View file

@ -175,7 +175,7 @@ void swap(array<T, N> &lhs,
TYPE *NAME = reinterpret_cast<TYPE *>(alloca(sizeof(TYPE) * (SIZE))); \
memset(NAME, 0, sizeof(TYPE) * (SIZE))
#elif __has_include(<cstdlib>)
#include <cstdlib>
#include <cstdlib> // ok include
#define FASTLED_STACK_ARRAY(TYPE, NAME, SIZE) \
TYPE *NAME = reinterpret_cast<TYPE *>(alloca(sizeof(TYPE) * (SIZE))); \
memset(NAME, 0, sizeof(TYPE) * (SIZE))

View file

@ -7,7 +7,9 @@
#include <stdint.h>
#include "crgb.h"
#include "fl/deprecated.h"
#include "fl/namespace.h"
#include "fl/upscale.h"
#include "fl/xymap.h"
namespace fl {
@ -19,47 +21,62 @@ namespace fl {
/// @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);
fl::XYMap xyMap)
FASTLED_DEPRECATED("use upscaleArbitrary from upscale.h");
/// @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);
uint8_t inputHeight, fl::XYMap xyMap)
FASTLED_DEPRECATED("use upscalePowerOf2 from upscale.h");
//
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 bilinearExpand(const CRGB *input, CRGB *output, uint16_t inputWidth,
uint16_t inputHeight, fl::XYMap xyMap)
FASTLED_DEPRECATED("use upscale from upscale.h");
// These are here for testing purposes and are slow. Their primary use
// is to test against the fixed integer version above.
void bilinearExpandFloat(const CRGB *input, CRGB *output, uint8_t inputWidth,
uint8_t inputHeight, fl::XYMap xyMap);
uint8_t inputHeight, fl::XYMap xyMap)
FASTLED_DEPRECATED("use upscaleFloat from upscale.h");
void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
uint16_t inputWidth, uint16_t inputHeight,
fl::XYMap xyMap);
fl::XYMap xyMap)
FASTLED_DEPRECATED("use upscaleArbitraryFloat from upscale.h");
uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
uint8_t v11, float dx, float dy);
uint8_t v11, float dx, float dy)
FASTLED_DEPRECATED("use upscaleFloat from upscale.h");
////////////////// Inline definitions for backward compatibility ///////////////////////
inline void bilinearExpandArbitrary(const CRGB *input, CRGB *output,
uint16_t inputWidth, uint16_t inputHeight,
fl::XYMap xyMap) {
upscaleArbitrary(input, output, inputWidth, inputHeight, xyMap);
}
inline void bilinearExpandPowerOf2(const CRGB *input, CRGB *output,
uint8_t inputWidth, uint8_t inputHeight,
fl::XYMap xyMap) {
upscalePowerOf2(input, output, inputWidth, inputHeight, xyMap);
}
inline void bilinearExpand(const CRGB *input, CRGB *output, uint16_t inputWidth,
uint16_t inputHeight, fl::XYMap xyMap) {
upscale(input, output, inputWidth, inputHeight, xyMap);
}
inline void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
uint16_t inputWidth,
uint16_t inputHeight,
fl::XYMap xyMap) {
upscaleArbitraryFloat(input, output, inputWidth, inputHeight, xyMap);
}
inline uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
uint8_t v11, float dx, float dy) {
return upscaleFloat(v00, v10, v01, v11, dx, dy);
}
} // namespace fl

View file

@ -3,7 +3,12 @@
#include "fl/leds.h"
#include "fl/stdint.h"
namespace fl {
template<typename T>
class Grid;
// Memory safe clear function for CRGB arrays.
template <int N> inline void clear(CRGB (&arr)[N]) {
for (int i = 0; i < N; ++i) {
@ -18,4 +23,17 @@ inline void clear(LedsXY<W, H> &leds) {
leds.fill(CRGB::Black);
}
template<typename T>
inline void clear(Grid<T> &grid) {
grid.clear();
}
// Default, when you don't know what do then call clear.
template<typename Container>
inline void clear(Container &container) {
container.clear();
}
} // namespace fl

View file

@ -19,3 +19,11 @@
#define FL_DISABLE_WARNING_POP
#define FL_DISABLE_WARNING(warning)
#endif
#if defined(__clang__)
#define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS \
FL_DISABLE_WARNING(global-constructors)
#else
#define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS /* nothing */
#endif

View file

@ -1,138 +1,142 @@
#include "fl/corkscrew.h"
#include "fl/algorithm.h"
#include "fl/assert.h"
#include "fl/math.h"
#include "fl/splat.h"
#include "fl/math_macros.h"
#define TWO_PI (PI * 2.0)
namespace fl {
void generateMap(const Corkscrew::Input &input, CorkscrewOutput &output);
void generateState(const Corkscrew::Input &input, CorkscrewState *output);
void generateMap(const Corkscrew::Input &input, CorkscrewOutput &output) {
// Calculate circumference per turn from height and total angle
float circumferencePerTurn = input.totalHeight * TWO_PI / input.totalAngle;
void generateState(const Corkscrew::Input &input, CorkscrewState *output) {
// Calculate vertical segments based on number of turns
// For a single turn (2π), we want exactly 1 vertical segment
// For two turns (4π), we want exactly 2 vertical segments
uint16_t verticalSegments = round(input.totalAngle / TWO_PI);
// uint16_t verticalSegments = ceil(input.totalTurns);
// Calculate width based on LED density per turn
float ledsPerTurn = static_cast<float>(input.numLeds) / verticalSegments;
// float ledsPerTurn = static_cast<float>(input.numLeds) / verticalSegments;
// Determine cylindrical dimensions
output.height = verticalSegments;
output.width = ceil(ledsPerTurn);
output.mapping.clear();
output->mapping.clear();
output->width = 0; // we will change this below.
output->height = 0;
// If numLeds is specified, use that for mapping size instead of grid
if (input.numLeds > 0) {
output.mapping.reserve(input.numLeds);
output->mapping.reserve(input.numLeds);
// Generate LED mapping based on numLeds
// Note that width_step should be 1.0f/float(input.numLeds) so last led in a
// turn does not wrap around.
const float width_step =
1.0f / float(input.numLeds); // Corkscrew reaches max width on last led.
const float height_step =
1.0f /
float(input.numLeds - 1); // Corkscrew reaches max height on last led.
// const float led_width_factor = circumferencePerTurn / TWO_PI;
const float length_per_turn = input.numLeds / input.totalTurns;
// Generate LED mapping based on numLeds
for (uint16_t i = 0; i < input.numLeds; ++i) {
// Calculate position along the corkscrew (0.0 to 1.0)
float position = static_cast<float>(i) / (input.numLeds - 1);
for (uint16_t i = 0; i < input.numLeds; ++i) {
// Calculate position along the corkscrew (0.0 to 1.0)
const float i_f = static_cast<float>(i);
const float alpha_width = i_f * width_step;
const float alpha_height = i_f * height_step;
const float width_before_mod = alpha_width * input.totalLength;
const float height = alpha_height * input.totalHeight;
const float width = fmodf(width_before_mod, length_per_turn);
output->mapping.push_back({width, height});
}
// Calculate angle and height
float angle = position * input.totalAngle;
float height = position * verticalSegments;
// Calculate circumference position
float circumference = fmodf(angle * circumferencePerTurn / TWO_PI,
circumferencePerTurn);
// Store the mapping
output.mapping.push_back({circumference, height});
}
} else {
// Original grid-based mapping
output.mapping.reserve(output.width * output.height);
// Corrected super sampling step size
float thetaStep = 0.5f / output.width;
float hStep = 0.5f / output.height;
// Precompute angle per segment
float anglePerSegment = input.totalAngle / verticalSegments;
// Loop over cylindrical pixels
for (uint16_t h = 0; h < output.height; ++h) {
float segmentOffset = input.offsetCircumference * h;
for (uint16_t w = 0; w < output.width; ++w) {
vec2f sample = {0, 0};
// 2x2 supersampling
for (uint8_t ssH = 0; ssH < 2; ++ssH) {
for (uint8_t ssW = 0; ssW < 2; ++ssW) {
float theta =
(w + 0.5f + ssW * thetaStep) / output.width;
float height = (h + 0.5f + ssH * hStep) / output.height;
// Corkscrew projection (θ,h)
float corkscrewTheta =
theta * TWO_PI + anglePerSegment * h;
float corkscrewH = height * verticalSegments;
// Apply circumference offset
float corkscrewCircumference = fmodf(
corkscrewTheta * circumferencePerTurn / TWO_PI +
segmentOffset,
circumferencePerTurn);
// Accumulate samples
sample.x += corkscrewCircumference;
sample.y += corkscrewH;
}
}
// Average the supersampled points
sample.x *= 0.25f;
sample.y *= 0.25f;
output.mapping.push_back(sample);
}
if (!output->mapping.empty()) {
float max_width = 0.0f;
float max_height = 0.0f;
for (const auto &point : output->mapping) {
max_width = MAX(max_width, point.x);
max_height = MAX(max_height, point.y);
}
output->width = static_cast<uint16_t>(ceilf(max_width)) + 1;
output->height = static_cast<uint16_t>(ceilf(max_height)) + 1;
}
// Apply inversion if requested
if (input.invert) {
fl::reverse(output.mapping.begin(), output.mapping.end());
fl::reverse(output->mapping.begin(), output->mapping.end());
}
}
Corkscrew::Corkscrew(const Corkscrew::Input &input) : mInput(input) {
fl::generateMap(mInput, mOutput);
fl::generateState(mInput, &mState);
}
vec2f Corkscrew::at(uint16_t i) const {
if (i >= mOutput.mapping.size()) {
vec2f Corkscrew::at_exact(uint16_t i) const {
if (i >= mState.mapping.size()) {
// Handle out-of-bounds access, possibly by returning a default value
return vec2f(0, 0);
}
// Convert the float position to integer
const vec2f &position = mOutput.mapping[i];
const vec2f &position = mState.mapping[i];
return position;
}
Tile2x2_u8 Corkscrew::at_splat(uint16_t i) const {
if (i >= mOutput.mapping.size()) {
Tile2x2_u8 Corkscrew::at_splat_extrapolate(float i) const {
// To finish this, we need to handle wrap around.
// To accomplish this we need a different data structure than the the
// Tile2x2_u8.
// 1. It will be called CorkscrewTile2x2_u8.
// 2. The four alpha values will each contain the index the LED is at,
// uint16_t.
// 3. There will be no origin, each pixel in the tile will contain a
// uint16_t origin. This is not supposed to be a storage format, but a
// convenient pre-computed value for rendering.
if (i >= mState.mapping.size()) {
// Handle out-of-bounds access, possibly by returning a default
// Tile2x2_u8
FASTLED_ASSERT(false, "Out of bounds access in Corkscrew at_splat: "
<< i << " size: " << mState.mapping.size());
return Tile2x2_u8();
}
// Use the splat function to convert the vec2f to a Tile2x2_u8
return splat(mOutput.mapping[i]);
float i_floor = floorf(i);
float i_ceil = ceilf(i);
if (ALMOST_EQUAL_FLOAT(i_floor, i_ceil)) {
// If the index is the same, just return the splat of that index
return splat(mState.mapping[static_cast<uint16_t>(i_floor)]);
} else {
// Interpolate between the two points and return the splat of the result
vec2f pos1 = mState.mapping[static_cast<uint16_t>(i_floor)];
vec2f pos2 = mState.mapping[static_cast<uint16_t>(i_ceil)];
if (pos2.x < pos1.x) {
// If the next point is on the other side of the cylinder, we need
// to wrap it around and bring it back into the positive direction so we can construct a Tile2x2_u8 wrap with it.
pos2.x += mState.width;
}
vec2f interpolated_pos =
pos1 * (1.0f - (i - i_floor)) + pos2 * (i - i_floor);
return splat(interpolated_pos);
}
}
size_t Corkscrew::size() const { return mOutput.mapping.size(); }
size_t Corkscrew::size() const { return mState.mapping.size(); }
CorkscrewOutput Corkscrew::generateMap(const Input &input) {
CorkscrewOutput output;
fl::generateMap(input, output);
Corkscrew::State Corkscrew::generateState(const Corkscrew::Input &input) {
CorkscrewState output;
fl::generateState(input, &output);
return output;
}
} // namespace fl
Tile2x2_u8_wrap Corkscrew::at_wrap(float i) const {
// This is a splatted pixel, but wrapped around the cylinder.
// This is useful for rendering the corkscrew in a cylindrical way.
Tile2x2_u8 tile = at_splat_extrapolate(i);
return Tile2x2_u8_wrap(tile, mState.width, mState.height);
}
} // namespace fl

View file

@ -4,15 +4,15 @@
* @file corkscrew.h
* @brief Corkscrew projection utilities
*
* Corkscrew projection maps from Corkscrew (θ, h) to Cylindrical cartesian (w,
* h) space, where w = one turn of the Corkscrew. The corkscrew at (0,0) will
* map to (0,0) in cylindrical space.
* You want to draw on a rectangular surface, and have it map to a GOD DAMN
* CORKSCREW! Well guess what, this is the file for you.
*
* Corkscrew projection maps from Corkscrew angle height, (θ, h) to Cylindrical
* cartesian (w, h) space, where w = one turn of the Corkscrew. The corkscrew at
* (0) will map to the first index in the cylinder map at (0, 0). The last value
* is probly not at the max pixel value at (width - 1, height - 1), but could
* be.
*
* The projection:
* - Super samples cylindrical space?
* - θ is normalized to [0, 1] or mapped to [0, W-1] for grid projection
* - Uses 2x2 super sampling for better visual quality
* - Works with XYPathRenderer's "Splat Rendering" for sub-pixel rendering
*
* Inputs:
* - Total Height of the Corkscrew in centimeters
@ -35,6 +35,7 @@
#include "fl/allocator.h"
#include "fl/geometry.h"
#include "fl/math_macros.h"
#include "fl/pair.h"
#include "fl/tile2x2.h"
#include "fl/vector.h"
@ -46,41 +47,43 @@ namespace fl {
* @return The resulting cylindrical mapping.
*/
struct CorkscrewInput {
float totalLength = 100; // Total length of the corkscrew in centimeters,
// set to dense 144 strips.
float totalHeight = 23.25; // Total height of the corkscrew in centimeters
// for 144 densly wrapped up over 19 turns
float totalAngle = 19.f * 2 * PI; // Default to 19 turns
float offsetCircumference = 0; // Optional offset for gap accounting
uint16_t numLeds = 144; // Default to dense 144 leds.
bool invert = false; // If true, reverse the mapping order
float totalTurns = 19.f; // Default to 19 turns
float offsetCircumference = 0; // Optional offset for gap accounting
uint16_t numLeds = 144; // Default to dense 144 leds.
bool invert = false; // If true, reverse the mapping order
CorkscrewInput() = default;
CorkscrewInput(float height, float total_angle, float offset = 0,
uint16_t leds = 144, bool invertMapping = false)
: totalHeight(height), totalAngle(total_angle),
offsetCircumference(offset), numLeds(leds), invert(invertMapping) {}
CorkscrewInput(float total_length, float height, float total_turns,
uint16_t leds, float offset = 0,
bool invertMapping = false)
: totalLength(total_length), totalHeight(height),
totalTurns(total_turns), offsetCircumference(offset), numLeds(leds),
invert(invertMapping) {}
};
struct CorkscrewOutput {
struct CorkscrewState {
uint16_t width = 0; // Width of cylindrical map (circumference of one turn)
uint16_t height = 0; // Height of cylindrical map (total vertical segments)
fl::vector<fl::vec2f, fl::allocator_psram<fl::vec2f>>
mapping; // Full precision mapping from corkscrew to cylindrical
CorkscrewOutput() = default;
CorkscrewState() = default;
class iterator {
public:
public:
using value_type = vec2f;
using difference_type = int32_t;
using pointer = vec2f*;
using reference = vec2f&;
using pointer = vec2f *;
using reference = vec2f &;
iterator(CorkscrewOutput* owner, size_t position)
iterator(CorkscrewState *owner, size_t position)
: owner_(owner), position_(position) {}
vec2f& operator*() const {
return owner_->mapping[position_];
}
vec2f &operator*() const { return owner_->mapping[position_]; }
iterator& operator++() {
iterator &operator++() {
++position_;
return *this;
}
@ -91,67 +94,82 @@ struct CorkscrewOutput {
return temp;
}
bool operator==(const iterator& other) const {
iterator &operator--() {
--position_;
return *this;
}
iterator operator--(int) {
iterator temp = *this;
--position_;
return temp;
}
bool operator==(const iterator &other) const {
return position_ == other.position_;
}
bool operator!=(const iterator& other) const {
bool operator!=(const iterator &other) const {
return position_ != other.position_;
}
private:
CorkscrewOutput* owner_;
difference_type operator-(const iterator &other) const {
return static_cast<difference_type>(position_) -
static_cast<difference_type>(other.position_);
}
private:
CorkscrewState *owner_;
size_t position_;
};
iterator begin() {
return iterator(this, 0);
}
iterator begin() { return iterator(this, 0); }
iterator end() {
return iterator(this, mapping.size());
}
fl::Tile2x2_u8 at(int16_t x, int16_t y) const;
iterator end() { return iterator(this, mapping.size()); }
};
// Maps a Corkscrew defined by the input to a cylindrical mapping for rendering
// a densly wrapped LED corkscrew.
class Corkscrew {
public:
using Input = CorkscrewInput;
using Output = CorkscrewOutput;
using iterator = CorkscrewOutput::iterator;
using State = CorkscrewState;
using iterator = CorkscrewState::iterator;
Corkscrew(const Input &input);
Corkscrew(const Corkscrew &) = default;
Corkscrew(Corkscrew &&) = default;
vec2f at_exact(uint16_t i) const;
// This is the future api.
Tile2x2_u8_wrap at_wrap(float i) const;
vec2f at(uint16_t i) const;
// This is a splatted pixel. This is will look way better than
// using at(), because it uses 2x2 neighboor sampling.
Tile2x2_u8 at_splat(uint16_t i) const;
size_t size() const;
iterator begin() {
return mOutput.begin();
}
iterator begin() { return mState.begin(); }
iterator end() {
return mOutput.end();
}
iterator end() { return mState.end(); }
/// For testing
static CorkscrewOutput generateMap(const Input &input);
static State generateState(const Input &input);
Output& access() {
return mOutput;
}
State &access() { return mState; }
const Output& access() const {
return mOutput;
}
const State &access() const { return mState; }
int16_t cylinder_width() const { return mState.width; }
int16_t cylinder_height() const { return mState.height; }
private:
Input mInput; // The input parameters defining the corkscrew
CorkscrewOutput mOutput; // The resulting cylindrical mapping
// For internal use. Splats the pixel on the surface which
// extends past the width. This extended Tile2x2 is designed
// to be wrapped around with a Tile2x2_u8_wrap.
Tile2x2_u8 at_splat_extrapolate(float i) const;
Input mInput; // The input parameters defining the corkscrew
State mState; // The resulting cylindrical mapping
};
} // namespace fl

View file

@ -13,19 +13,15 @@ template <typename T> class Grid {
Grid(uint32_t width, uint32_t height) { reset(width, height); }
void reset(uint32_t width, uint32_t height) {
clear();
if (width != mWidth || height != mHeight) {
mWidth = width;
mHeight = height;
// Only re-allocate if the size is now bigger.
mData.reserve(width * height);
// Fill with default objects.
while (mData.size() < width * height) {
mData.push_back(T());
}
mSlice = fl::MatrixSlice<T>(mData.data(), width, height, 0, 0,
width - 1, height - 1);
mData.resize(width * height);
}
clear();
mSlice = fl::MatrixSlice<T>(mData.data(), width, height, 0, 0,
width, height);
}
void clear() {
@ -60,6 +56,11 @@ template <typename T> class Grid {
uint32_t width() const { return mWidth; }
uint32_t height() const { return mHeight; }
T* data() { return mData.data(); }
const T* data() const { return mData.data(); }
size_t size() const { return mData.size(); }
private:
static T &NullValue() {
static T gNull;

View file

@ -0,0 +1,44 @@
#pragma once
#include "fl/stdint.h"
namespace fl {
// fl::begin for arrays
template <typename T, size_t N>
constexpr T* begin(T (&array)[N]) noexcept {
return array;
}
// fl::end for arrays
template <typename T, size_t N>
constexpr T* end(T (&array)[N]) noexcept {
return array + N;
}
// fl::begin for containers with begin() member function
template <typename Container>
constexpr auto begin(Container& c) -> decltype(c.begin()) {
return c.begin();
}
// fl::begin for const containers with begin() member function
template <typename Container>
constexpr auto begin(const Container& c) -> decltype(c.begin()) {
return c.begin();
}
// fl::end for containers with end() member function
template <typename Container>
constexpr auto end(Container& c) -> decltype(c.end()) {
return c.end();
}
// fl::end for const containers with end() member function
template <typename Container>
constexpr auto end(const Container& c) -> decltype(c.end()) {
return c.end();
}
} // namespace fl

View file

@ -9,6 +9,8 @@ namespace fl {
template <typename T, int N = 0> class Singleton {
public:
static T &instance() {
// We love function level singletons!! They don't get construction until first call.
// And they seem to have locks on them in most compilers. So yay.
static T instance;
return instance;
}

View file

@ -0,0 +1,7 @@
#pragma once
#if defined(__AVR__) || defined(ARDUINO_TEENSYLC) || defined(ARDUINO_TEENSY30) || defined(ARDUINO_TEENSY31)|| defined(STM32F1) || defined(ARDUINO_ARCH_RENESAS_UNO) || defined(ESP8266)
#define SKETCH_HAS_LOTS_OF_MEMORY 0
#else
#define SKETCH_HAS_LOTS_OF_MEMORY 1
#endif

View file

@ -1,16 +1,27 @@
#include "fl/tile2x2.h"
#include "crgb.h"
#include "fl/draw_visitor.h"
#include "fl/math_macros.h"
#include "fl/raster.h"
#include "fl/raster_sparse.h"
#include "fl/unused.h"
#include "fl/warn.h"
#include "fl/xymap.h"
using namespace fl;
namespace fl {
namespace {
static vec2i16 wrap(const vec2i16 &v, const vec2i16 &size) {
// Wrap the vector v around the size
return vec2i16(v.x % size.x, v.y % size.y);
}
static vec2i16 wrap_x(const vec2i16 &v, const uint16_t width) {
// Wrap the x component of the vector v around the size
return vec2i16(v.x % width, v.y);
}
} // namespace
void Tile2x2_u8::Rasterize(const Slice<const Tile2x2_u8> &tiles,
XYRasterU8Sparse *out_raster) {
out_raster->rasterize(tiles);
@ -34,4 +45,66 @@ void Tile2x2_u8::scale(uint8_t scale) {
}
}
Tile2x2_u8_wrap::Tile2x2_u8_wrap(const Tile2x2_u8 &from, uint16_t width) {
const vec2i16 origin = from.origin();
at(0, 0) = {wrap_x(vec2i16(origin.x, origin.y), width), from.at(0, 0)};
at(0, 1) = {wrap_x(vec2i16(origin.x, origin.y + 1), width), from.at(0, 1)};
at(1, 0) = {wrap_x(vec2i16(origin.x + 1, origin.y), width), from.at(1, 0)};
at(1, 1) = {wrap_x(vec2i16(origin.x + 1, origin.y + 1), width),
from.at(1, 1)};
}
Tile2x2_u8_wrap::Data &Tile2x2_u8_wrap::at(uint16_t x, uint16_t y) {
// Wrap around the edges
x = (x + 2) % 2;
y = (y + 2) % 2;
return tile[y][x];
}
const Tile2x2_u8_wrap::Data &Tile2x2_u8_wrap::at(uint16_t x, uint16_t y) const {
// Wrap around the edges
x = (x + 2) % 2;
y = (y + 2) % 2;
return tile[y][x];
}
Tile2x2_u8_wrap::Tile2x2_u8_wrap(const Tile2x2_u8 &from, uint16_t width,
uint16_t height) {
const vec2i16 origin = from.origin();
at(0, 0) = {wrap(vec2i16(origin.x, origin.y), vec2i16(width, height)),
from.at(0, 0)};
at(0, 1) = {wrap(vec2i16(origin.x, origin.y + 1), vec2i16(width, height)),
from.at(0, 1)};
at(1, 0) = {wrap(vec2i16(origin.x + 1, origin.y), vec2i16(width, height)),
from.at(1, 0)};
at(1,
1) = {wrap(vec2i16(origin.x + 1, origin.y + 1), vec2i16(width, height)),
from.at(1, 1)};
}
uint8_t Tile2x2_u8::maxValue() const {
uint8_t max = 0;
max = MAX(max, at(0, 0));
max = MAX(max, at(0, 1));
max = MAX(max, at(1, 0));
max = MAX(max, at(1, 1));
return max;
}
Tile2x2_u8 Tile2x2_u8::MaxTile(const Tile2x2_u8 &a, const Tile2x2_u8 &b) {
Tile2x2_u8 result;
for (int x = 0; x < 2; ++x) {
for (int y = 0; y < 2; ++y) {
result.at(x, y) = MAX(a.at(x, y), b.at(x, y));
}
}
return result;
}
rect<int16_t> Tile2x2_u8::bounds() const {
vec2<int16_t> min = mOrigin;
vec2<int16_t> max = mOrigin + vec2<int16_t>(2, 2);
return rect<int16_t>(min, max);
}
} // namespace fl

View file

@ -5,6 +5,7 @@
#include "fl/geometry.h"
#include "fl/namespace.h"
#include "fl/pair.h"
#include "fl/slice.h"
#include "fl/xymap.h"
@ -31,6 +32,8 @@ class Tile2x2_u8 {
void scale(uint8_t scale);
void setOrigin(int16_t x, int16_t y) { mOrigin = vec2<int16_t>(x, y); }
uint8_t &operator()(int x, int y) { return at(x, y); }
uint8_t &at(int x, int y) { return mTile[y][x]; }
const uint8_t &at(int x, int y) const { return mTile[y][x]; }
@ -40,33 +43,19 @@ class Tile2x2_u8 {
uint8_t &lower_right() { return at(1, 0); }
uint8_t &upper_right() { return at(1, 1); }
uint8_t maxValue() const {
uint8_t max = 0;
max = MAX(max, at(0, 0));
max = MAX(max, at(0, 1));
max = MAX(max, at(1, 0));
max = MAX(max, at(1, 1));
return max;
}
const uint8_t &lower_left() const { return at(0, 0); }
const uint8_t &upper_left() const { return at(0, 1); }
const uint8_t &lower_right() const { return at(1, 0); }
const uint8_t &upper_right() const { return at(1, 1); }
static Tile2x2_u8 Max(const Tile2x2_u8 &a, const Tile2x2_u8 &b) {
Tile2x2_u8 result;
for (int x = 0; x < 2; ++x) {
for (int y = 0; y < 2; ++y) {
result.at(x, y) = MAX(a.at(x, y), b.at(x, y));
}
}
return result;
}
uint8_t maxValue() const;
static Tile2x2_u8 MaxTile(const Tile2x2_u8 &a, const Tile2x2_u8 &b);
vec2<int16_t> origin() const { return mOrigin; }
/// bounds => [begin_x, end_x) (where end_x is exclusive)
rect<int16_t> bounds() const {
vec2<int16_t> min = mOrigin;
vec2<int16_t> max = mOrigin + vec2<int16_t>(2, 2);
return rect<int16_t>(min, max);
}
rect<int16_t> bounds() const;
// Draws the subpixel tile to the led array.
void draw(const CRGB &color, const XYMap &xymap, CRGB *out) const;
@ -96,4 +85,24 @@ class Tile2x2_u8 {
vec2<int16_t> mOrigin;
};
class Tile2x2_u8_wrap {
// This is a class that is like a Tile2x2_u8 but wraps around the edges.
// This is useful for cylinder mapping where the x-coordinate wraps around
// the width of the cylinder and the y-coordinate wraps around the height.
// This converts a tile2x2 to a wrapped x,y version.
public:
using Data = fl::pair<vec2i16, uint8_t>; // absolute position, alpha
Tile2x2_u8_wrap() = default;
Tile2x2_u8_wrap(const Tile2x2_u8 &from, uint16_t width);
Tile2x2_u8_wrap(const Tile2x2_u8 &from, uint16_t width, uint16_t height);
// Returns the absolute position and the alpha.
Data &at(uint16_t x, uint16_t y);
const Data &at(uint16_t x, uint16_t y) const;
private:
Data tile[2][2] = {}; // zero filled.
};
} // namespace fl

View file

@ -4,9 +4,9 @@
#include <stdint.h>
#include "bilinear_expansion.h"
#include "crgb.h"
#include "fl/namespace.h"
#include "fl/upscale.h"
#include "fl/xymap.h"
namespace fl {
@ -17,9 +17,8 @@ uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01, uint8_t v11,
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) {
void upscaleArbitrary(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();
@ -82,8 +81,8 @@ uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01, uint8_t v11,
return result;
}
void bilinearExpandPowerOf2(const CRGB *input, CRGB *output, uint8_t inputWidth,
uint8_t inputHeight, XYMap xyMap) {
void upscalePowerOf2(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()) {
@ -158,7 +157,7 @@ uint8_t bilinearInterpolatePowerOf2(uint8_t v00, uint8_t v10, uint8_t v01,
}
// Floating-point version of bilinear interpolation
uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
uint8_t upscaleFloat(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;
@ -179,9 +178,8 @@ uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
}
// Floating-point version for arbitrary grid sizes
void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
uint16_t inputWidth, uint16_t inputHeight,
XYMap xyMap) {
void upscaleArbitraryFloat(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();
@ -214,11 +212,11 @@ void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
CRGB result;
result.r =
bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
upscaleFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
result.g =
bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
upscaleFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
result.b =
bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
upscaleFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
uint16_t idx = xyMap.mapToIndex(x, y);
if (idx < n) {
@ -229,8 +227,8 @@ void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
}
// Floating-point version for power-of-two grid sizes
void bilinearExpandFloat(const CRGB *input, CRGB *output, uint8_t inputWidth,
uint8_t inputHeight, XYMap xyMap) {
void upscaleFloat(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()) {
@ -267,11 +265,11 @@ void bilinearExpandFloat(const CRGB *input, CRGB *output, uint8_t inputWidth,
CRGB result;
result.r =
bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
upscaleFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
result.g =
bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
upscaleFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
result.b =
bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
upscaleFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
uint16_t idx = xyMap.mapToIndex(x, y);
if (idx < n) {

View file

@ -0,0 +1,63 @@
/// @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 upscaleArbitrary(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 upscalePowerOf2(const CRGB *input, CRGB *output, uint8_t inputWidth,
uint8_t inputHeight, fl::XYMap xyMap);
//
inline void upscale(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))) {
upscaleArbitrary(input, output, inputWidth, inputHeight, xyMap);
} else {
upscalePowerOf2(input, output, inputWidth, inputHeight, xyMap);
}
}
// These are here for testing purposes and are slow. Their primary use
// is to test against the fixed integer version above.
void upscaleFloat(const CRGB *input, CRGB *output, uint8_t inputWidth,
uint8_t inputHeight, fl::XYMap xyMap);
void upscaleArbitraryFloat(const CRGB *input, CRGB *output, uint16_t inputWidth,
uint16_t inputHeight, fl::XYMap xyMap);
uint8_t upscaleFloat(uint8_t v00, uint8_t v10, uint8_t v01,
uint8_t v11, float dx, float dy);
} // namespace fl

View file

@ -237,7 +237,7 @@ XYPathPtr XYPath::NewCatmullRomPath(uint16_t width, uint16_t height,
}
XYPathPtr XYPath::NewCustomPath(const fl::function<vec2f(float)> &f,
const rect<int> &drawbounds,
const rect<int16_t> &drawbounds,
const TransformFloat &transform,
const char *name) {
@ -250,7 +250,7 @@ XYPathPtr XYPath::NewCustomPath(const fl::function<vec2f(float)> &f,
if (!transform.is_identity()) {
out->setTransform(transform);
}
rect<int> bounds;
rect<int16_t> bounds;
if (path->hasDrawBounds(&bounds)) {
if (!bounds.mMin.is_zero()) {
// Set the bounds to the path's bounds

View file

@ -18,8 +18,6 @@
#include "fl/transform.h"
#include "fl/xypath_impls.h"
#include "fl/avr_disallowed.h"
namespace fl {
class Gradient;
@ -37,7 +35,6 @@ namespace xypath_detail {
fl::Str unique_missing_name(const char *prefix = "XYCustomPath: ");
} // namespace xypath_detail
AVR_DISALLOWED
class XYPath : public Referent {
public:
/////////////////////////////////////////////
@ -57,7 +54,7 @@ class XYPath : public Referent {
// Custom path using just a function.
static XYPathPtr
NewCustomPath(const fl::function<vec2f(float)> &path,
const rect<int> &drawbounds = rect<int>(),
const rect<int16_t> &drawbounds = rect<int16_t>(),
const TransformFloat &transform = TransformFloat(),
const char *name = nullptr);
@ -148,10 +145,10 @@ class XYPathFunction : public XYPathGenerator {
const Str name() const override { return mName; }
void setName(const Str &name) { mName = name; }
fl::rect<int> drawBounds() const { return mDrawBounds; }
void setDrawBounds(const fl::rect<int> &bounds) { mDrawBounds = bounds; }
fl::rect<int16_t> drawBounds() const { return mDrawBounds; }
void setDrawBounds(const fl::rect<int16_t> &bounds) { mDrawBounds = bounds; }
bool hasDrawBounds(fl::rect<int> *bounds) override {
bool hasDrawBounds(fl::rect<int16_t> *bounds) override {
if (bounds) {
*bounds = mDrawBounds;
}
@ -161,7 +158,7 @@ class XYPathFunction : public XYPathGenerator {
private:
fl::function<vec2f(float)> mFunction;
fl::Str mName = "XYPathFunction Unnamed";
fl::rect<int> mDrawBounds;
fl::rect<int16_t> mDrawBounds;
};
} // namespace fl

View file

@ -57,7 +57,7 @@ class XYPathGenerator : public Referent {
virtual const Str name() const = 0;
virtual vec2f compute(float alpha) = 0;
// No writes when returning false.
virtual bool hasDrawBounds(rect<int> *bounds) {
virtual bool hasDrawBounds(rect<int16_t> *bounds) {
FASTLED_UNUSED(bounds);
return false;
}

View file

@ -14,6 +14,8 @@
#include "fl/scoped_ptr.h"
#include "fl/xymap.h"
#include "fx/fx2d.h"
#include "eorder.h"
#include "pixel_controller.h" // For RGB_BYTE_0, RGB_BYTE_1, RGB_BYTE_2
#define ANIMARTRIX_INTERNAL
#include "animartrix_detail.hpp"
@ -93,6 +95,8 @@ class Animartrix : public Fx2d {
int fxGet() const { return static_cast<int>(current_animation); }
Str fxName() const override { return "Animartrix:"; }
void fxNext(int fx = 1) { fxSet(fxGet() + fx); }
void setColorOrder(EOrder order) { color_order = order; }
EOrder getColorOrder() const { return color_order; }
private:
friend void AnimartrixLoop(Animartrix &self, uint32_t now);
@ -102,6 +106,7 @@ class Animartrix : public Fx2d {
fl::scoped_ptr<FastLEDANIMartRIX> impl;
CRGB *leds = nullptr; // Only set during draw, then unset back to nullptr.
AnimartrixAnim current_animation = RGB_BLOBS5;
EOrder color_order = RGB;
};
void AnimartrixLoop(Animartrix &self, uint32_t now);
@ -263,6 +268,17 @@ const char *Animartrix::getAnimationName(AnimartrixAnim animation) {
void Animartrix::draw(DrawContext ctx) {
this->leds = ctx.leds;
AnimartrixLoop(*this, ctx.now);
if (color_order != RGB) {
for (int i = 0; i < mXyMap.getTotal(); ++i) {
CRGB &pixel = ctx.leds[i];
const uint8_t b0_index = RGB_BYTE0(color_order);
const uint8_t b1_index = RGB_BYTE1(color_order);
const uint8_t b2_index = RGB_BYTE2(color_order);
pixel = CRGB(pixel.raw[b0_index], pixel.raw[b1_index],
pixel.raw[b2_index]);
}
}
this->leds = nullptr;
}

View file

@ -3,7 +3,7 @@
#define FASTLED_INTERNAL
#include "FastLED.h"
#include "fl/bilinear_expansion.h"
#include "fl/upscale.h"
#include "fl/ptr.h"
#include "fl/xymap.h"
#include "fx/fx2d.h"
@ -64,13 +64,13 @@ void ScaleUp::draw(DrawContext context) {
void ScaleUp::expand(const CRGB *input, CRGB *output, uint16_t width,
uint16_t height, XYMap mXyMap) {
#if FASTLED_SCALE_UP == FASTLED_SCALE_UP_ALWAYS_POWER_OF_2
bilinearExpandPowerOf2(input, output, width, height, mXyMap);
fl::upscalePowerOf2(input, output, static_cast<uint8_t>(width), static_cast<uint8_t>(height), mXyMap);
#elif FASTLED_SCALE_UP == FASTLED_SCALE_UP_HIGH_PRECISION
bilinearExpandArbitrary(input, output, width, height, mXyMap);
fl::upscaleArbitrary(input, output, width, height, mXyMap);
#elif FASTLED_SCALE_UP == FASTLED_SCALE_UP_DECIDE_AT_RUNTIME
bilinearExpand(input, output, width, height, mXyMap);
fl::upscale(input, output, width, height, mXyMap);
#elif FASTLED_SCALE_UP == FASTLED_SCALE_UP_FORCE_FLOATING_POINT
bilinearExpandFloat(input, output, width, height, mXyMap);
fl::upscaleFloat(input, output, static_cast<uint8_t>(width), static_cast<uint8_t>(height), mXyMap);
#else
#error "Invalid FASTLED_SCALE_UP"
#endif

View file

@ -9,7 +9,7 @@
#include <stdint.h>
#include "fl/bilinear_expansion.h"
#include "fl/upscale.h"
#include "fl/ptr.h"
#include "fl/vector.h"
#include "fl/xymap.h"

View file

@ -40,6 +40,8 @@ typedef fl::FixedVector<int, 16> PinList16;
typedef uint8_t Pin;
bool gPsramInited = false;
// Maps multiple pins and CRGB strips to a single I2S_Esp32 object.
@ -180,6 +182,13 @@ class Driver: public InternalI2SDriver {
};
InternalI2SDriver* InternalI2SDriver::create() {
if (!gPsramInited) {
gPsramInited = true;
bool ok = psramInit();
if (!ok) {
log_e("PSRAM initialization failed, I2S driver may crash.");
}
}
return new Driver();
}

View file

@ -14,6 +14,20 @@
#include "fl/vector.h"
#include "eorder.h"
#ifndef FASTLED_INTERNAL
// We need to do a check for the esp-idf version because very specific versions of the
// esp-idf arduino core are broken.
#include "platforms/esp/esp_version.h"
// Broken in 3.0.2 (esp-idf 5.1.0)
// Broken in 3.0.4 (esp-idf 5.1.0)
// Broken in 3.0.7 (esp-idf 5.1.0)
// Broken in 3.1.0 (esp-idf 5.3.2)
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 1, 0) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)
#error "I2S driver is known to not be compatible with ESP-IDF 5.1.0, upgrade to ESP-IDF 5.4.0 in Arduino core esp32 3.2.0+, see https://github.com/FastLED/FastLED/issues/1903"
#elif ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 3, 2)
#error "I2S driver is known to not be compatible with ESP-IDF 5.3.2, upgrade to ESP-IDF 5.4.0 in Arduino core esp32 3.2.0+, see https://github.com/FastLED/FastLED/issues/1903"
#endif
#endif // FASTLED_INTERNAL
namespace fl {

View file

@ -118,6 +118,11 @@ public:
// GPIO 20-22, 24-26 used by default for SPI flash.
#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(24) | _FL_BIT(25) | _FL_BIT(26) | _FL_BIT(28) | _FL_BIT(29) | _FL_BIT(30))
#elif CONFIG_IDF_TARGET_ESP32P4
// 55 GPIO pins. ESPIDF defines all pins as valid.
// NOTE: GPIO 24 & 25 commonly used for USB and may cause flashes when uploading.
#define FASTLED_UNUSABLE_PIN_MASK (0ULL | _FL_BIT(24) | _FL_BIT(25))
#elif CONFIG_IDF_TARGET_ESP32H2
// 22 GPIO pins. ESPIDF defines all pins as valid.
// ESP32-H2 datasheet not yet available, when it is, mask the pins commonly used by SPI flash.
@ -131,6 +136,7 @@ public:
#define FASTLED_UNUSABLE_PIN_MASK (0ULL)
#endif
#endif

View file

@ -25,11 +25,7 @@ private:
static RmtController5::DmaMode DefaultDmaMode()
{
#ifdef FASTLED_RMT_USE_DMA
return RmtController5::DMA_ENABLED;
#else
return RmtController5::DMA_AUTO;
#endif
}
public:

View file

@ -1,9 +1,16 @@
#pragma once
#if !defined(PROGMEM)
#define PROGMEM
#define FL_PROGMEM
#endif
#if !defined(FL_PROGMEM)
#define FL_PROGMEM PROGMEM
#endif
#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)))
#define FL_ALIGN_PROGMEM
#define FL_ALIGN_PROGMEM
#define FL_PROGMEM_USES_NULL 1

View file

@ -11,7 +11,7 @@
FL_DISABLE_WARNING_PUSH
FL_DISABLE_WARNING(global-constructors)
FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
static const auto start_time = std::chrono::system_clock::now();

View file

@ -42,7 +42,7 @@ class ActiveStripData : public fl::EngineEvents::Listener {
updateScreenMap(id, screenmap);
}
const bool hasScreenMap(int id) const { return mScreenMap.has(id); }
bool hasScreenMap(int id) const { return mScreenMap.has(id); }
private:
friend class fl::Singleton<ActiveStripData>;

View file

@ -45,7 +45,7 @@ class ActiveStripData2 : public fl::EngineEvents::Listener {
updateScreenMap(id, screenmap);
}
const bool hasScreenMap(int id) const { return mScreenMap.has(id); }
bool hasScreenMap(int id) const { return mScreenMap.has(id); }
private:
friend class fl::Singleton<ActiveStripData2>;