From 23df51cd2597a9a3bd98406ae6d1851bd8a65867 Mon Sep 17 00:00:00 2001 From: Thomas Sommer Date: Mon, 10 Jan 2022 10:10:05 +0100 Subject: [PATCH 1/2] rewrite modm::ui::color --- examples/arduino_nano/color/main.cpp | 2 +- examples/avr/display/dogm128/image/main.cpp | 10 +- examples/nucleo_f446re/color/main.cpp | 2 +- .../stm32f469_discovery/game_of_life/main.cpp | 6 +- .../stm32f4_discovery/colour_tcs3414/main.cpp | 2 +- src/modm/board/disco_f469ni/board_display.cpp | 10 +- src/modm/driver/color/tcs3414.hpp | 2 +- src/modm/driver/color/tcs3472.hpp | 2 +- src/modm/driver/display/ili9341_impl.hpp | 20 +- src/modm/driver/display/parallel_tft_impl.hpp | 8 +- src/modm/driver/display/st7789.hpp | 4 +- src/modm/driver/pwm/apa102.hpp | 11 +- src/modm/driver/pwm/sk6812w.hpp | 10 +- src/modm/driver/pwm/ws2812b.hpp | 6 +- .../math/filter/s_curve_controller_impl.hpp | 6 +- src/modm/math/saturated.hpp | 373 ++++++++++++++++++ src/modm/math/{saturation => }/saturation.lb | 6 +- src/modm/math/saturation/saturated.hpp | 327 --------------- src/modm/math/utils/integer_traits.hpp | 66 ++-- src/modm/math/utils/misc.hpp | 109 ++--- src/modm/ui/color.cpp | 53 --- src/modm/ui/color.hpp | 13 +- src/modm/ui/color/brightness.hpp | 105 ----- src/modm/ui/color/color.lb | 20 +- src/modm/ui/color/color.md | 90 +++++ src/modm/ui/color/concepts.hpp | 75 ++++ src/modm/ui/color/gray.hpp | 335 ++++++++++++++++ src/modm/ui/color/hsv.hpp | 207 ++++++---- src/modm/ui/color/hsv_impl.hpp | 71 ---- src/modm/ui/color/rgb.hpp | 278 ++++++++----- src/modm/ui/color/rgb565.hpp | 129 ------ src/modm/ui/color/rgb_html.hpp | 191 +++++++++ src/modm/ui/color/rgb_impl.hpp | 50 --- src/modm/ui/color/rgb_pallete.hpp | 212 ++++++++++ src/modm/ui/color/rgbhtml.hpp | 192 --------- src/modm/ui/color_impl.hpp | 85 ---- .../ui/display/virtual_graphic_display.cpp | 4 +- src/modm/ui/gui/colorpalette.hpp | 2 +- src/modm/ui/led/module.md | 2 +- src/modm/ui/led/rgb.hpp | 22 +- test/modm/math/saturation/saturation_test.cpp | 266 ------------- test/modm/math/saturation_test.cpp | 282 +++++++++++++ .../math/{saturation => }/saturation_test.hpp | 13 +- test/modm/ui/color/color_test.cpp | 195 +++++---- test/modm/ui/color/color_test.hpp | 23 +- 45 files changed, 2164 insertions(+), 1733 deletions(-) create mode 100644 src/modm/math/saturated.hpp rename src/modm/math/{saturation => }/saturation.lb (84%) delete mode 100644 src/modm/math/saturation/saturated.hpp delete mode 100644 src/modm/ui/color.cpp delete mode 100644 src/modm/ui/color/brightness.hpp create mode 100644 src/modm/ui/color/color.md create mode 100644 src/modm/ui/color/concepts.hpp create mode 100644 src/modm/ui/color/gray.hpp delete mode 100644 src/modm/ui/color/hsv_impl.hpp delete mode 100644 src/modm/ui/color/rgb565.hpp create mode 100644 src/modm/ui/color/rgb_html.hpp delete mode 100644 src/modm/ui/color/rgb_impl.hpp create mode 100644 src/modm/ui/color/rgb_pallete.hpp delete mode 100644 src/modm/ui/color/rgbhtml.hpp delete mode 100644 src/modm/ui/color_impl.hpp delete mode 100644 test/modm/math/saturation/saturation_test.cpp create mode 100644 test/modm/math/saturation_test.cpp rename test/modm/math/{saturation => }/saturation_test.hpp (77%) diff --git a/examples/arduino_nano/color/main.cpp b/examples/arduino_nano/color/main.cpp index 80d7cd9d52..a6a79cb688 100644 --- a/examples/arduino_nano/color/main.cpp +++ b/examples/arduino_nano/color/main.cpp @@ -65,7 +65,7 @@ class Sensorthread : public modm::pt::Protothread if (PT_CALL(sensor.readColor())) { const auto rgb = data.getColor(); - MODM_LOG_INFO << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv(rgb) << modm::endl; + MODM_LOG_INFO << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv888(rgb) << modm::endl; } } diff --git a/examples/avr/display/dogm128/image/main.cpp b/examples/avr/display/dogm128/image/main.cpp index 77a9d55fd2..833e7c51e5 100644 --- a/examples/avr/display/dogm128/image/main.cpp +++ b/examples/avr/display/dogm128/image/main.cpp @@ -203,13 +203,9 @@ drawNumber(modm::glcd::Point cursor, uint8_t number) int main() { - led::R::set(); - led::G::set(); - led::B::reset(); - - led::R::setOutput(); - led::G::setOutput(); - led::B::setOutput(); + led::R::setOutput(true); + led::G::setOutput(true); + led::B::setOutput(false); lcd::SPI::connect(); lcd::SPI::initialize(); diff --git a/examples/nucleo_f446re/color/main.cpp b/examples/nucleo_f446re/color/main.cpp index ac80a406ce..228c3c73b1 100644 --- a/examples/nucleo_f446re/color/main.cpp +++ b/examples/nucleo_f446re/color/main.cpp @@ -64,7 +64,7 @@ class ThreadOne : public modm::pt::Protothread if (PT_CALL(sensor.readColor())) { const auto rgb = data.getColor(); - MODM_LOG_INFO << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv(rgb) << modm::endl; + MODM_LOG_INFO << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv888(rgb) << modm::endl; } timeout.restart(500ms); PT_WAIT_UNTIL(timeout.isExpired()); diff --git a/examples/stm32f469_discovery/game_of_life/main.cpp b/examples/stm32f469_discovery/game_of_life/main.cpp index 1b65cf0366..9fbc0fe01c 100644 --- a/examples/stm32f469_discovery/game_of_life/main.cpp +++ b/examples/stm32f469_discovery/game_of_life/main.cpp @@ -72,7 +72,7 @@ uint16_t * displayBuffer; #define TRAIL_LENGTH ((1 << TRAIL_POWER) + 1) constexpr uint8_t alive = (1 << TRAIL_POWER); -#define COLOR_SHADE(red, green, blue, fraction) modm::color::Rgb(\ +#define COLOR_SHADE(red, green, blue, fraction) modm::color::Rgb888(\ uint8_t(uint32_t(red) * (fraction) / TRAIL_LENGTH), \ uint8_t(uint32_t(green) * (fraction) / TRAIL_LENGTH), \ uint8_t(uint32_t(blue) * (fraction) / TRAIL_LENGTH) ) @@ -137,7 +137,7 @@ static inline void touch(framebuffer_t buffer) static inline void setPixel(int x, int y, uint8_t color) { -#define DRAW(x, y) displayBuffer[(y) * 800 + (x)] = GET_TRAIL_COLOR(color).color; +#define DRAW(x, y) displayBuffer[(y) * 800 + (x)] = GET_TRAIL_COLOR(color).value(); #if SCALE >= 8 // >:v x:y // 0 | | @@ -148,7 +148,7 @@ static inline void setPixel(int x, int y, uint8_t color) // 5 | x x | // 6 | xxxx | // 7 | | -GET_TRAIL_COLOR(color).color; +GET_TRAIL_COLOR(color).value(); // 1 DRAW(x+2, y+1); DRAW(x+3, y+1); diff --git a/examples/stm32f4_discovery/colour_tcs3414/main.cpp b/examples/stm32f4_discovery/colour_tcs3414/main.cpp index c8cc5a5e65..f28d40f70c 100644 --- a/examples/stm32f4_discovery/colour_tcs3414/main.cpp +++ b/examples/stm32f4_discovery/colour_tcs3414/main.cpp @@ -99,7 +99,7 @@ class ThreadOne : public modm::pt::Protothread { if (PT_CALL(sensor.readColor())) { const auto rgb = data.getColor(); - stream << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv(rgb) << modm::endl; + stream << "RGB: " << rgb << "\tHSV: " << modm::color::Hsv888(rgb) << modm::endl; } timeout.restart(500ms); PT_WAIT_UNTIL(timeout.isExpired()); diff --git a/src/modm/board/disco_f469ni/board_display.cpp b/src/modm/board/disco_f469ni/board_display.cpp index 664a544484..c411b218cb 100644 --- a/src/modm/board/disco_f469ni/board_display.cpp +++ b/src/modm/board/disco_f469ni/board_display.cpp @@ -49,7 +49,7 @@ class DsiDisplay : public modm::ColorGraphicDisplay void clear() final { - std::fill(buffer, buffer + this->getBufferWidth()*this->getBufferHeight(), this->backgroundColor.color); + std::fill(buffer, buffer + this->getBufferWidth()*this->getBufferHeight(), this->backgroundColor.value()); } void @@ -62,21 +62,21 @@ class DsiDisplay : public modm::ColorGraphicDisplay setPixel(int16_t x, int16_t y) final { if (x < 0 or 800 <= x or y < 0 or 480 <= y) return; - buffer[y * 800 + x] = this->foregroundColor.color; + buffer[y * 800 + x] = this->foregroundColor.value(); } void clearPixel(int16_t x, int16_t y) final { if (x < 0 or 800 <= x or y < 0 or 480 <= y) return; - buffer[y * 800 + x] = this->backgroundColor.color; + buffer[y * 800 + x] = this->backgroundColor.value(); } modm::color::Rgb565 getPixel(int16_t x, int16_t y) const final { - if (x < 0 or 800 <= x or y < 0 or 480 <= y) return false; - return buffer[y * 800 + x]; + if (x < 0 or 800 <= x or y < 0 or 480 <= y) return modm::color::html::Black; + return modm::color::Rgb565(buffer[y * 800 + x]); } protected: diff --git a/src/modm/driver/color/tcs3414.hpp b/src/modm/driver/color/tcs3414.hpp index 6505957ed6..825d2fb7f5 100644 --- a/src/modm/driver/color/tcs3414.hpp +++ b/src/modm/driver/color/tcs3414.hpp @@ -123,7 +123,7 @@ struct tcs3414 }; - using Rgb = color::RgbT; + using Rgb = color::Rgb161616; struct modm_packed Data diff --git a/src/modm/driver/color/tcs3472.hpp b/src/modm/driver/color/tcs3472.hpp index 1a20c34ba7..2e6af1593d 100644 --- a/src/modm/driver/color/tcs3472.hpp +++ b/src/modm/driver/color/tcs3472.hpp @@ -139,7 +139,7 @@ struct tcs3472 addr(uint8_t version=5) { return version < 5 ? 0x39 : 0x29; } - using Rgb = color::RgbT; + using Rgb = color::Rgb161616; struct modm_packed Data diff --git a/src/modm/driver/display/ili9341_impl.hpp b/src/modm/driver/display/ili9341_impl.hpp index d44ce85c69..a3c415ee95 100644 --- a/src/modm/driver/display/ili9341_impl.hpp +++ b/src/modm/driver/display/ili9341_impl.hpp @@ -202,7 +202,7 @@ void Ili9341::drawHorizontalLine( glcd::Point start, uint16_t length) { - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; + uint16_t const pixelValue { modm::toBigEndian(foregroundColor.value()) }; auto minLength { std::min(std::size_t(length), BufferSize) }; uint16_t *buffer16 { reinterpret_cast(buffer) }; std::fill(buffer16, buffer16+minLength, pixelValue); @@ -223,7 +223,7 @@ void Ili9341::drawVerticalLine( glcd::Point start, uint16_t length) { - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; + uint16_t const pixelValue { modm::toBigEndian(foregroundColor.value()) }; auto minLength { std::min(std::size_t(length), BufferSize) }; uint16_t *buffer16 { reinterpret_cast(buffer) }; std::fill(buffer16, buffer16+minLength, pixelValue); @@ -248,7 +248,7 @@ Ili9341::fillRectangle( auto const y { upperLeft.getY() }; std::size_t pixelCount { std::size_t(width) * std::size_t(height) }; - uint16_t const pixelValue { modm::toBigEndian(foregroundColor.color) }; + uint16_t const pixelValue { modm::toBigEndian(foregroundColor.value()) }; auto minLength { std::min(std::size_t(pixelCount), BufferSize) }; uint16_t *buffer16 { reinterpret_cast(buffer) }; std::fill(buffer16, buffer16+minLength, pixelValue); @@ -270,8 +270,8 @@ void Ili9341::fillCircle( glcd::Point center, uint16_t radius) { - uint8_t const setColor[] { uint8_t((foregroundColor.color >> 8) & 0xff), - uint8_t(foregroundColor.color & 0xff) }; + uint8_t const setColor[] { uint8_t((foregroundColor.value() >> 8) & 0xff), + uint8_t(foregroundColor.value() & 0xff) }; int16_t f = 1 - radius; int16_t ddF_x = 0; @@ -317,10 +317,10 @@ void Ili9341::drawImageRaw(glcd::Point upperLeft, uint16_t width, uint16_t height, modm::accessor::Flash data) { - uint8_t const setColor[] { uint8_t((foregroundColor.color >> 8) & 0xff), - uint8_t(foregroundColor.color & 0xff) }; - uint8_t const clearColor[] { uint8_t((backgroundColor.color >> 8) & 0xff), - uint8_t(backgroundColor.color & 0xff) }; + uint8_t const setColor[] { uint8_t((foregroundColor.value() >> 8) & 0xff), + uint8_t(foregroundColor.value() & 0xff) }; + uint8_t const clearColor[] { uint8_t((backgroundColor.value() >> 8) & 0xff), + uint8_t(backgroundColor.value() & 0xff) }; BatchHandle h(*this); @@ -392,7 +392,7 @@ Ili9341::setColoredPixel( int16_t x, int16_t y, color::Rgb565 const &color) { auto const pixelColor { color }; - uint8_t const setColor[] { uint8_t((pixelColor.color >> 8) & 0xff), uint8_t(pixelColor.color & 0xff) }; + uint8_t const setColor[] { uint8_t((pixelColor.value() >> 8) & 0xff), uint8_t(pixelColor.value() & 0xff) }; BatchHandle h(*this); diff --git a/src/modm/driver/display/parallel_tft_impl.hpp b/src/modm/driver/display/parallel_tft_impl.hpp index be3f5c6579..dfb850c2ff 100644 --- a/src/modm/driver/display/parallel_tft_impl.hpp +++ b/src/modm/driver/display/parallel_tft_impl.hpp @@ -110,7 +110,7 @@ modm::ParallelTft::clear() interface.writeIndex(0x0022); for (uint32_t i = 0; i < MAX_X * MAX_Y; i++) { - interface.writeData(backgroundColor.color); + interface.writeData(backgroundColor.value()); } } @@ -123,7 +123,7 @@ modm::ParallelTft::setPixel(int16_t x, int16_t y) } writeCursor(x, y); - interface.writeRegister(0x0022, foregroundColor.color); + interface.writeRegister(0x0022, foregroundColor.value()); } template @@ -138,7 +138,7 @@ modm::ParallelTft::clearPixel(int16_t x, int16_t y) // } // // writeCursor(x, y); -// interface.writeRegister(0x0022, color.getValue()); +// interface.writeRegister(0x0022, color.value()); } template @@ -148,7 +148,7 @@ modm::ParallelTft::getPixel(int16_t x, int16_t y) const (void) x; (void) y; - return false; + return modm::color::Rgb565(); } // ---------------------------------------------------------------------------- diff --git a/src/modm/driver/display/st7789.hpp b/src/modm/driver/display/st7789.hpp index 32a4d3ecda..0f7a6a81e2 100644 --- a/src/modm/driver/display/st7789.hpp +++ b/src/modm/driver/display/st7789.hpp @@ -82,14 +82,14 @@ class St7789 : public ColorGraphicDisplay, public St7789Driver &color, uint8_t brightness) { if (index >= LEDs) return false; - uint32_t value = (color.red << 24) | (color.green << 16) | - (color.blue << 8) | (brightness | 0xe0); + uint32_t value = color.value() << 8 | (brightness | 0xe0); reinterpret_cast(data)[1+index] = value; return true; } bool - setColor(size_t index, const color::Rgb &color) + setColor(size_t index, const color::RgbPallete<8,8,8> &color) { if (index >= LEDs) return false; // read the brightness value and clear all colors uint32_t value = reinterpret_cast(data)[1+index] & 0xfful; // set all colors - value |= (color.red << 24) | (color.green << 16) | (color.blue << 8); + value |= color.value() << 8; // write back entire value reinterpret_cast(data)[1+index] = value; return true; } - color::Rgb + color::Rgb888 getColor(size_t index) const { if (index >= LEDs) return {}; diff --git a/src/modm/driver/pwm/sk6812w.hpp b/src/modm/driver/pwm/sk6812w.hpp index 995e409c6f..08f9728934 100644 --- a/src/modm/driver/pwm/sk6812w.hpp +++ b/src/modm/driver/pwm/sk6812w.hpp @@ -72,11 +72,11 @@ class Sk6812w } void - setColorBrightness(size_t index, const color::Rgb &color, uint8_t brightness) + setColorBrightness(size_t index, const color::Rgb888 &color, uint8_t brightness) { if (index >= LEDs) return; - const uint8_t colors[] = {color.green, color.red, color.blue, brightness}; + const uint8_t colors[] = {color.green(), color.red(), color.blue(), brightness}; for (size_t ii = 0; ii < 4; ii++) { const uint32_t c = (spread(colors[ii]) << 12) | spread(colors[ii] >> 4); @@ -86,11 +86,11 @@ class Sk6812w } void - setColor(size_t index, const color::Rgb &color) + setColor(size_t index, const color::Rgb888 &color) { if (index >= LEDs) return; - const uint8_t colors[] = {color.green, color.red, color.blue}; + const uint8_t colors[] = {color.green(), color.red(), color.blue()}; for (size_t ii = 0; ii < 3; ii++) { const uint32_t c = (spread(colors[ii]) << 12) | spread(colors[ii] >> 4); @@ -99,7 +99,7 @@ class Sk6812w } } - color::Rgb + color::Rgb888 getColor(size_t index) const { if (index >= LEDs) return {}; diff --git a/src/modm/driver/pwm/ws2812b.hpp b/src/modm/driver/pwm/ws2812b.hpp index 0f064f712d..a2ebf87fd0 100644 --- a/src/modm/driver/pwm/ws2812b.hpp +++ b/src/modm/driver/pwm/ws2812b.hpp @@ -72,11 +72,11 @@ class Ws2812b } void - setColor(size_t index, const color::Rgb &color) + setColor(size_t index, const color::Rgb888 &color) { if (index >= LEDs) return; - const uint8_t colors[3] = {color.green, color.red, color.blue}; + const uint8_t colors[3] = {color.green(), color.red(), color.blue()}; for (size_t ii = 0; ii < 3; ii++) { const uint32_t c = (spread(colors[ii]) << 12) | spread(colors[ii] >> 4); @@ -85,7 +85,7 @@ class Ws2812b } } - color::Rgb + color::Rgb888 getColor(size_t index) const { if (index >= LEDs) return {}; diff --git a/src/modm/math/filter/s_curve_controller_impl.hpp b/src/modm/math/filter/s_curve_controller_impl.hpp index e2bd72fe5a..dd651dc8fc 100644 --- a/src/modm/math/filter/s_curve_controller_impl.hpp +++ b/src/modm/math/filter/s_curve_controller_impl.hpp @@ -19,7 +19,7 @@ #error "Don't include this file directly, use 's_curve_controller.hpp' instead!" #endif - +#include #include // ---------------------------------------------------------------------------- @@ -113,9 +113,9 @@ modm::SCurveController::update(T error, const T& speed) outputDecrement = std::sqrt(error * parameter.decreaseFactor * 2); } - output = modm::min(outputIncrement, outputDecrement); + output = std::min(outputIncrement, outputDecrement); // TODO smooth breaking if the speedMaximum has changed to a lower value - output = modm::min(output, parameter.speedMaximum); + output = std::min(output, parameter.speedMaximum); if (output < parameter.speedMinimum) { output = parameter.speedMinimum; diff --git a/src/modm/math/saturated.hpp b/src/modm/math/saturated.hpp new file mode 100644 index 0000000000..8e510088bd --- /dev/null +++ b/src/modm/math/saturated.hpp @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2022-2023, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace modm +{ + +/** + * @brief Saturation arithmetic building on 'Integer-Overflow-Builtins' + * Implementation works with integer, unsigned integer and float or reference to them. + * Operators work with the same types or Saturated types of them. + * @see https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html + * @see https://en.wikipedia.org/wiki/Saturation_arithmetic + * + * @author Thomas Sommer + * + * @ingroup modm_math_saturation + */ +template +requires std::integral> +class Saturated +{ +protected: + using TP = std::remove_reference_t; + using TS = std::conditional_t, T, std::make_signed_t>>; + + T value_{0}; + +private: + static constexpr TP min = std::numeric_limits::min(); + static constexpr TP max = std::numeric_limits::max(); + +public: + Saturated() = default; + + constexpr Saturated(const T& value) : value_(value){}; + constexpr Saturated(const Saturated& other) : value_(other.value_){}; + + template + requires std::integral> + constexpr Saturated(const U& value) + { value_ = std::clamp< modm::fits_any >(value, min, max); } + + template + requires std::floating_point> + constexpr Saturated(const U& value) + { value_ = std::clamp(value, min, max); } + + template + requires std::integral> + constexpr Saturated(const Saturated& other) + { value_ = std::clamp< modm::fits_any >(other.value, min, max); } + + T value() const + { return value_; } + + // Cast to underlying type. No more comparison operators required. + // @see https://en.cppreference.com/w/cpp/language/cast_operator + operator T() const + { return value_; } + + // operator= + void + operator=(const Saturated& other) + { value_ = other.value_; } + + template + requires std::integral> + void + operator=(const U& other) + { value_ = std::clamp< modm::fits_any >(other, min, max); } + + template + requires std::integral> + void + operator=(const Saturated& other) + { value_ = std::clamp< modm::fits_any >(other.value_, min, max); } + + // Post: operator++, operator-- + Saturated& + operator++() + { + if (value_ < max) value_++; + return *this; + } + + Saturated& + operator--() + { + if (value_ > min) value_--; + return *this; + } + + // Pre: operator++(int), operator--(int) + Saturated + operator++(int) + { + Saturated ret(*this); + if (value_ < max) value_++; + return ret; + } + + Saturated + operator--(int) + { + Saturated ret(*this); + if (value_ > min) value_--; + return ret; + } + + // operator+=, operator-=, operator*= + template + requires std::unsigned_integral> + Saturated& + operator+=(const U& other) + { + if (__builtin_add_overflow(value_, other, &value_)) + value_ = max; + + return *this; + } + + template + requires std::signed_integral> + Saturated& + operator+=(const U& other) + { + if (other < 0) { + if (__builtin_sub_overflow(value_, -other, &value_)) + value_ = min; + } else { + if (__builtin_add_overflow(value_, other, &value_)) + value_ = max; + } + + return *this; + } + + template + requires std::integral> + Saturated& + operator+=(const Saturated& other) + { return this->operator+=(other.value_); } + + template + requires std::unsigned_integral> + Saturated& + operator-=(const U& other) + { + if (__builtin_sub_overflow(value_, other, &value_)) + value_ = min; + + return *this; + } + + template + requires std::signed_integral> + Saturated& + operator-=(const U& other) + { + if (other < 0) { + if (__builtin_add_overflow(value_, -other, &value_)) + value_ = max; + } else { + if (__builtin_sub_overflow(value_, other, &value_)) + value_ = min; + } + + return *this; + } + + template + requires std::integral> + Saturated& + operator-=(const Saturated& other) + { return this->operator-=(other.value_); } + + template + requires std::unsigned_integral> + Saturated& + operator*=(const U& other) + { + if (__builtin_mul_overflow(value_, other, &value_)) + value_ = max; + + return *this; + } + + template + requires std::signed_integral> + Saturated& + operator*=(const U& other) + { + if (other < 0) { + if (__builtin_mul_overflow(value_, -other, &value_)) + value_ = max; + value_ = -value_; + } else { + if (__builtin_mul_overflow(value_, other, &value_)) + value_ = max; + } + + return *this; + } + + template + requires std::integral> + Saturated& + operator*=(const Saturated& other) + { return this->operator*=(other.value_); } + + // operator+, operator-, operator* + template + requires std::unsigned_integral> + TP + operator+(const U& other) const + { + Saturated ret; + + if (__builtin_add_overflow(value_, other, &ret.value_)) + ret.value_ = max; + + return ret; + } + + template + requires std::signed_integral> + TP + operator+(const U& other) const + { + Saturated ret; + + if (other < 0) { + if (__builtin_sub_overflow(value_, -other, &ret.value_)) + ret.value_ = min; + } else { + if (__builtin_add_overflow(value_, other, &ret.value_)) + ret.value_ = max; + } + + return ret; + } + + template + requires std::integral> + Saturated + operator+(const Saturated& other) const + { return this->operator+(other.value_); } + + template + requires std::unsigned_integral> + Saturated + operator-(const U& other) const + { + Saturated ret; + + if (__builtin_sub_overflow(value_, other, &ret.value_)) + ret.value_ = min; + + return ret; + } + + template + requires std::signed_integral> + Saturated + operator-(const U& other) const + { + Saturated ret; + + if (other < 0) { + if (__builtin_add_overflow(value_, -other, &ret.value_)) + ret.value_ = max; + } else { + if (__builtin_sub_overflow(value_, other, &ret.value_)) + ret.value_ = min; + } + + return ret; + } + + template + requires std::integral> + Saturated + operator-(const Saturated& other) const + { return this->operator-(other.value_); } + + template + requires std::unsigned_integral> + Saturated + operator*(const U& other) const + { + Saturated ret; + + if (__builtin_mul_overflow(value_, other, &ret.value_)) + ret.value_ = max; + + return ret; + } + + template + requires std::signed_integral> + Saturated + operator*(const U& other) const + { + Saturated ret; + + if (other < 0) { + if (__builtin_mul_overflow(value_, -other, &ret.value_)) + ret.value_ = max; + ret.value_ = -ret.value_; + } else { + if (__builtin_mul_overflow(value_, other, &ret.value_)) + ret.value_ = max; + } + + return ret; + } + + template + requires std::integral> + Saturated + operator*(const Saturated& other) const + { return this->operator*(other.value_); } + + template + requires std::integral> + Saturated + operator/(const U& other) const + { + return Saturated(value_ / other); + } + + template + requires std::integral> + Saturated + operator/(const Saturated& other) const + { return this->operator/(other.value_); } + + + TS + operator-() const + { return -TS(value_); } + + void + absolute() + // Should be std::abs but that's troubelous for avr-gcc + // @see: https://stackoverflow.com/questions/1374037/ambiguous-overload-call-to-absdouble + { value_ = abs(value_); } + + template + requires std::integral> + friend class Saturated; +}; + +} // namespace modm diff --git a/src/modm/math/saturation/saturation.lb b/src/modm/math/saturation.lb similarity index 84% rename from src/modm/math/saturation/saturation.lb rename to src/modm/math/saturation.lb index 70dc85992f..acef2e58ce 100644 --- a/src/modm/math/saturation/saturation.lb +++ b/src/modm/math/saturation.lb @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# Copyright (c) 2018, Thomas Sommer +# Copyright (c) 2022, Thomas Sommer # # This file is part of the modm project. # @@ -19,5 +19,5 @@ def prepare(module, options): return True def build(env): - env.outbasepath = "modm/src/modm/math/saturation" - env.copy(".") + env.outbasepath = "modm/src/modm/math" + env.copy("saturated.hpp") diff --git a/src/modm/math/saturation/saturated.hpp b/src/modm/math/saturation/saturated.hpp deleted file mode 100644 index 3cfbbb781c..0000000000 --- a/src/modm/math/saturation/saturated.hpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -namespace modm -{ - -/** - * @brief Saturation arithmetic building on 'Integer-Overflow-Builtins' - * Implementation works with integer, unsigned integer and float or reference to them. - * Operators work with the same types or Saturated types of them. - * @see https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html - * @see https://en.wikipedia.org/wiki/Saturation_arithmetic - * - * @author Thomas Sommer - * - * @ingroup modm_math_saturation - */ -template -requires std::integral> -class Saturated -{ -protected: - using TP = std::remove_reference_t; - using TS = std::conditional_t, T, std::make_signed_t>>; - - T value = 0; -private: - static constexpr TP min = std::numeric_limits::min(); - static constexpr TP max = std::numeric_limits::max(); - -public: - Saturated() = default; - - constexpr Saturated(const T& value) : value(value){}; - constexpr Saturated(const Saturated& other) : value(other.value){}; - - template - requires std::integral> - constexpr Saturated(const U& v) - { value = std::clamp< modm::fits_any_t >(v, min, max); } - - template - requires std::floating_point> - constexpr Saturated(const U& v) - { value = std::clamp(v, min, max); } - - template - requires std::integral> - constexpr Saturated(const Saturated& other) - { value = std::clamp< modm::fits_any_t >(other.value, min, max); } - - TP - getValue() const - { return value; } - - // Implicitely serve underlying type so you can f.e. pass Saturated to std::abs() - operator T&() { return value; } - operator T() const { return value; } - - // comparison operators - constexpr auto - operator<=>(const Saturated&) const = default; - - // operator= - void - operator=(const Saturated& other) - { value = other.value; } - - template - requires std::integral> - void - operator=(const Saturated& other) - { value = std::clamp< modm::fits_any_t >(other.value, min, max); } - - // Post: operator++, operator-- - Saturated& - operator++() - { - if (value < max) value++; - return *this; - } - - Saturated& - operator--() - { - if (value > min) value--; - return *this; - } - - // Pre: operator++(int), operator--(int) - Saturated - operator++(int) - { - Saturated tmp(*this); - if (value < max) value++; - return tmp; - } - - Saturated - operator--(int) - { - Saturated tmp(*this); - if (value > min) value--; - return tmp; - } - - // operator+=, operator-=, operator*= - template - requires std::unsigned_integral> - Saturated& - operator+=(const Saturated& other) - { - if (__builtin_add_overflow(value, other.value, &value)) - value = max; - - return *this; - } - - template - requires std::signed_integral> - Saturated& - operator+=(const Saturated& other) - { - if (other.value < 0) { - if (__builtin_sub_overflow(value, -other.value, &value)) - value = min; - } else { - if (__builtin_add_overflow(value, other.value, &value)) - value = max; - } - - return *this; - } - - template - requires std::unsigned_integral> - Saturated& - operator-=(const Saturated& other) - { - if (__builtin_sub_overflow(value, other.value, &value)) - value = min; - - return *this; - } - - template - requires std::signed_integral> - Saturated& - operator-=(const Saturated& other) - { - if (other.value < 0) { - if (__builtin_add_overflow(value, -other.value, &value)) - value = max; - } else { - if (__builtin_sub_overflow(value, other.value, &value)) - value = min; - } - - return *this; - } - - template - requires std::unsigned_integral> - Saturated& - operator*=(const Saturated& other) - { - if (__builtin_mul_overflow(value, other.value, &value)) - value = max; - - return *this; - } - - template - requires std::signed_integral> - Saturated& - operator*=(const Saturated& other) - { - if (other.value < 0) { - if (__builtin_mul_overflow(value, -other.value, &value)) - value = max; - value = -value; - } else { - if (__builtin_mul_overflow(value, other.value, &value)) - value = max; - } - - return *this; - } - - // OPTIMIZE By whatever reason, for operator*= the compiler doesn't implicitly construct Saturated types. - // Overload plain types for now: - template - requires std::unsigned_integral> - Saturated& - operator*=(const U& v) - { - if (__builtin_mul_overflow(value, v, &value)) - value = max; - - return *this; - } - - template - requires std::signed_integral> - Saturated& - operator*=(const U& v) - { - if (v < 0) { - if (__builtin_mul_overflow(value, -v, &value)) - value = max; - value = -value; - } else { - if (__builtin_mul_overflow(value, v, &value)) - value = max; - } - - return *this; - } - - // operator+, operator-, operator* - template - requires std::unsigned_integral> - TP - operator+(const Saturated& other) - { - Saturated tmp; - - if (__builtin_add_overflow(value, other.value, &tmp.value)) - tmp.value = max; - - return tmp.value; - } - - template - requires std::signed_integral> - TP - operator+(const Saturated& other) - { - Saturated tmp; - - if (other.value < 0) { - if (__builtin_sub_overflow(value, -other.value, &tmp.value)) - tmp.value = min; - } else { - if (__builtin_add_overflow(value, other.value, &tmp.value)) - tmp.value = max; - } - - return tmp.value; - } - - template - requires std::unsigned_integral> - TP - operator-(const Saturated& other) - { - Saturated tmp; - - if (__builtin_sub_overflow(value, other.value, &tmp.value)) - tmp.value = min; - - return tmp.value; - } - - template - requires std::signed_integral> - TP - operator-(const Saturated& other) - { - Saturated tmp; - - if (other.value < 0) { - if (__builtin_add_overflow(value, -other.value, &tmp.value)) - tmp.value = max; - } else { - if (__builtin_sub_overflow(value, other.value, &tmp.value)) - tmp.value = min; - } - - return tmp.value; - } - - TP - operator*(const Saturated& other) - { - Saturated tmp; - - if (__builtin_mul_overflow(value, other.value, &tmp.value)) - tmp.value = max; - - return tmp.value; - } - - TS - operator-() - { return -TS(value); } - - void - absolute() - // Should be std::abs but that's troubelous for avr-gcc - // @see: https://stackoverflow.com/questions/1374037/ambiguous-overload-call-to-absdouble - { value = abs(value); } - - template - friend class Saturated; -}; - -} // namespace modm diff --git a/src/modm/math/utils/integer_traits.hpp b/src/modm/math/utils/integer_traits.hpp index 4be8caea30..0a5ce723c9 100644 --- a/src/modm/math/utils/integer_traits.hpp +++ b/src/modm/math/utils/integer_traits.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2021-2023, Thomas Sommer * * This file is part of the modm project. * @@ -13,60 +13,48 @@ #include #include - #include +#include +#include + +#include "arithmetic_traits.hpp" + namespace modm { /// @ingroup modm_math_utils /// @{ +using builtin_int_t = std::tuple; +using builtin_uint_t = std::tuple; -/// Trait the smallest unsigned type that fits n Bits +/// Trait the smallest unsigned integral fitting n [D]igits /// @author Thomas Sommer -template -struct uint_t -{ - using least = std::conditional_t< - (Bits <= 8), uint8_t, std::conditional_t< - (Bits <= 16), uint16_t, std::conditional_t< - (Bits <= 32), uint32_t, std::enable_if_t< - (Bits <= 64), uint64_t>>>>; -}; - -template -using least_uint = typename modm::uint_t::least; +template +requires(0 < D and D <= 64) +using least_uint = std::tuple_element_t(D, 8))>>3)), builtin_uint_t>; /// @cond namespace detail { - template - constexpr int most_digits() { - return std::numeric_limits::digits; + template + consteval int max_digits() { + return std::max({std::numeric_limits::digits...}); } - template - constexpr std::enable_if_t - most_digits() { - return std::max(std::numeric_limits::digits, most_digits()); - } + template + struct fits_any { + static constexpr int max_digits = max_digits(); + static constexpr bool is_any_signed = std::disjunction_v...>; + + using type = std::conditional_t>>, + least_uint + >; + }; } /// @endcond /// Trait the smallest integral - signed or unsigned - fitting any Ts /// @author Thomas Sommer template -struct fits_any { - static constexpr int most_dig = detail::most_digits(); - - using type = std::conditional_t< - std::conjunction_v...>, - typename uint_t::least, - // An odd most_dig means: type with most digits is signed integral - std::make_signed_t::least> - >; -}; - -template -using fits_any_t = typename fits_any::type; - -/// @} -} +using fits_any = typename detail::fits_any::type; +} // namespace modm \ No newline at end of file diff --git a/src/modm/math/utils/misc.hpp b/src/modm/math/utils/misc.hpp index 919f2fb05a..39be29b81f 100644 --- a/src/modm/math/utils/misc.hpp +++ b/src/modm/math/utils/misc.hpp @@ -4,6 +4,7 @@ * Copyright (c) 2011-2012, 2014-2015, Niklas Hauser * Copyright (c) 2015, Sascha Schade * Copyright (c) 2020, Christopher Durand + * Copyright (c) 2023, Thomas Sommer * * This file is part of the modm project. * @@ -13,13 +14,13 @@ */ // ---------------------------------------------------------------------------- -#ifndef MODM_MATH_UTILS_MISC_HPP -#define MODM_MATH_UTILS_MISC_HPP +#pragma once #include #include #include #include +#include #include @@ -56,74 +57,58 @@ pow(uint32_t base, uint8_t exponent) } /** - * This does what you think it does. + * @brief Variadic min for 2-∞ objects + * + * @param a first object to compare + * @param b second object to compare + * @param cs Further objects for comparison * - * @param a A thing of arbitrary type. - * @param b Another thing of arbitrary type. - * @return The lesser of the parameters. + * @return The smallest object * - * This is the simple classic generic implementation. It will work on - * temporary expressions, since they are only evaluated once, unlike a - * preprocessor macro. + * @see https://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions */ template -inline const T& -min(const T& a, const T& b) +constexpr T vmin(const T& a, const T& b) { - if (b < a) - return b; - else - return a; + return a < b ? a : b; } -/** - * This does what you think it does. - * - * @param a A thing of arbitrary type. - * @param b Another thing of arbitrary type. - * @return The greater of the parameters. - * - * This is the simple classic generic implementation. It will work on - * temporary expressions, since they are only evaluated once, unlike a - * preprocessor macro. - */ -template -inline const T& -max(const T& a, const T& b) +template +constexpr T vmin(const T& a, const T& b, const Ts&... cs) { - if (a < b) - return b; - else - return a; + return a < b ? vmin(a, cs...) : vmin(b, cs...); } /** * This does what you think it does. * - * @param a A thing of arbitrary type. - * @param b Another thing of arbitrary type. - * @param c Something else of arbitrary type. - * @return The greater of the three parameters. + * @param a first object to compare + * @param b second object to compare + * @param cs Further objects to compare * - * This is the simple classic generic implementation. It will work on - * temporary expressions, since they are only evaluated once, unlike a - * preprocessor macro. + * @return The biggest object + * + * @see https://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions */ template -constexpr T -max(const T a, const T b, const T c) +constexpr T vmax(const T& a, const T& b) { - return ( ( (b > c) ? b : c ) > a ) ? - ( (b > c) ? b : c) : a; + return a > b ? a : b; +} + +template +constexpr T vmax(const T& a, const T& b, const Ts&... cs) +{ + return a > b ? vmax(a, cs...) : vmax(b, cs...); } /** * This does what you think it does. * - * @param a A thing of arbitrary type. - * @param b Another thing of arbitrary type. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. * @param compare A comparison functor. - * @return The lesser of the parameters. + * @return The lesser of the parameters. * * This will work on temporary expressions, since they are only evaluated * once, unlike a preprocessor macro. @@ -132,19 +117,16 @@ template inline const T& min(const T& a, const T& b, Compare compare) { - if (compare(b, a)) - return b; - else - return a; + return compare(b, a) ? b : a; } /** * This does what you think it does. * - * @param a A thing of arbitrary type. - * @param b Another thing of arbitrary type. - * @param compare A comparison functor. - * @return The greater of the parameters. + * @param a A thing of arbitrary type. + * @param b Another thing of arbitrary type. + * @param compare A comparison functor. + * @return The greater of the parameters. * * This will work on temporary expressions, since they are only evaluated * once, unlike a preprocessor macro. @@ -153,25 +135,16 @@ template inline const T& max(const T& a, const T& b, Compare compare) { - if (compare(a, b)) - return b; - else - return a; + return compare(a, b) ? b : a; } /// constexpr implementation of fabs -template - requires std::is_floating_point_v +template constexpr Float constexpr_fabs(Float number) { - if (number >= 0) { - return number; - } else { - return -number; - } + return number >= 0 ? number : -number; } /// @} -} // namespace modm -#endif +} // namespace modm diff --git a/src/modm/ui/color.cpp b/src/modm/ui/color.cpp deleted file mode 100644 index 7e19a0655f..0000000000 --- a/src/modm/ui/color.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2009-2010, 2012, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2013, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include "color.hpp" - -// ---------------------------------------------------------------------------- -namespace modm { -namespace color { -template<> template<> -void -HsvT::toRgb(RgbT* color) const -{ - uint16_t vs = value * saturation; - uint16_t h6 = 6 * hue; - - uint8_t p = ((value << 8) - vs) >> 8; - uint8_t i = h6 >> 8; - uint16_t f = ((i | 1) << 8) - h6; - if (i & 1) { - f = -f; - } - - uint8_t u = (((uint32_t) value << 16) - (uint32_t) vs * f) >> 16; - uint8_t r = value; - uint8_t g = value; - uint8_t b = value; - switch(i) - { - case 0: g = u; b = p; break; - case 1: r = u; b = p; break; - case 2: r = p; b = u; break; - case 3: r = p; g = u; break; - case 4: r = u; g = p; break; - case 5: g = p; b = u; break; - } - - color->red = r; - color->green = g; - color->blue = b; -} -} // namespace color -} // namespace modm diff --git a/src/modm/ui/color.hpp b/src/modm/ui/color.hpp index 64e4ef53ce..db256e9310 100644 --- a/src/modm/ui/color.hpp +++ b/src/modm/ui/color.hpp @@ -1,9 +1,5 @@ /* - * Copyright (c) 2009, Martin Rosekeit - * Copyright (c) 2009-2013, Fabian Greif - * Copyright (c) 2012-2013, 2015, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2021-2022, Thomas Sommer * * This file is part of the modm project. * @@ -12,10 +8,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- +#pragma once +#include "color/gray.hpp" #include "color/rgb.hpp" #include "color/hsv.hpp" -#include "color/brightness.hpp" -#include "color/rgb565.hpp" -#include "color/rgbhtml.hpp" +#include "color/rgb_html.hpp" +#include "color/rgb_pallete.hpp" \ No newline at end of file diff --git a/src/modm/ui/color/brightness.hpp b/src/modm/ui/color/brightness.hpp deleted file mode 100644 index e82fa98e24..0000000000 --- a/src/modm/ui/color/brightness.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include - -#include - -#include "hsv.hpp" -#include "rgb.hpp" -#include "rgb565.hpp" - -namespace modm::color -{ - -// forward declarations for convertion constructors -template -class RgbT; - -template -class HsvT; - -class Rgb565; - -/** - * @brief Brightness as unsigned integral. Add's the math for conversion to and from - * Color-Types. Use with: Grayscale Buffers, Dimmed LEDs, Brightness sensors - * - * @author Thomas Sommer - * - * @tparam T Unsigned integral for the brightness-value - * @ingroup modm_ui_color - */ -template -class BrightnessT -{ -public: - T value{0}; - - constexpr BrightnessT() = default; - - constexpr BrightnessT(T value) : value(value) {} - - /** - * Copy Constructor 8bit->16bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr BrightnessT(const BrightnessT &brightness_other) - : value(brightness_other.value << 8) - {} - - /** - * Copy Constructor 16bit->8bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr BrightnessT(const BrightnessT &brightness_other) - : value(brightness_other.value >> 8) - {} - - /** - * Convertion Constructor for RGB Color - * - * @param rgb RGB Color - */ - template - constexpr BrightnessT(RgbT rgb) - : value((0.2125f * float(rgb.red)) + (0.7154f * float(rgb.green)) + - (0.0721f * float(rgb.blue))) - {} - - /** - * Convertion Constructor for HSV Color - * - * @param hsv HSV Color - */ - template - constexpr BrightnessT(HsvT hsv) : value(hsv.value) - {} - - /** - * Convertion Constructor for RGB565 Color - * - * @param rgb565 RGB565 Color - */ - constexpr BrightnessT(Rgb565 rgb565) : BrightnessT(RgbT(rgb565)) {} - - constexpr bool - operator==(const BrightnessT &other) const = default; -}; - -/// @ingroup modm_ui_color -using Brightness = BrightnessT; - -} // namespace modm::color diff --git a/src/modm/ui/color/color.lb b/src/modm/ui/color/color.lb index f0d4dcf5f1..00c40126a8 100644 --- a/src/modm/ui/color/color.lb +++ b/src/modm/ui/color/color.lb @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# Copyright (c) 2018, Niklas Hauser +# Copyright (c) 2021-2023, Thomas Sommer # # This file is part of the modm project. # @@ -12,17 +12,21 @@ def init(module): module.name = ":ui:color" - module.description = """ -# Color - -Color containers and converters in various formats: RGB, HSV, Brightness, Rgb565 -""" + module.description = FileReader("color.md") def prepare(module, options): - module.depends(":math:utils") + module.depends( + ":math:utils", + ":math:uintn_t", + ":math:saturation" + ) return True + def build(env): env.outbasepath = "modm/src/modm/ui/color" - env.copy(".") + + ignore = ["*pattern*"] + env.copy(".", ignore=env.ignore_paths(*ignore)) + env.copy("../color.hpp") diff --git a/src/modm/ui/color/color.md b/src/modm/ui/color/color.md new file mode 100644 index 0000000000..6b51614136 --- /dev/null +++ b/src/modm/ui/color/color.md @@ -0,0 +1,90 @@ +# Color + +Color containers and converters in various formats: RGB, HSV, Brightness, Rgb565. You can 'invent' your own types by choosing the digits used for each component use template arguments. + +## Construction and Assigment +The types are quite flexible and thanks to constexpr, the following constructions of red are resolved at compile time. + +All following assignments create the same plain Red: #FF0000, Rgb(255, 0, 0) + +Declare a RGB color with 8bits per channel +```cpp +#include + +modm::color::Rgb888 red; // Rgb888 is shorthand for Rgb<8, 8, 8> +``` + +Obligatory construction +```cpp +red = modm::color::Rgb888(255, 0, 0); +``` + +modm supports html named colors +```cpp +red = modm::color::html::Red; +``` + +Changing resolution results in scaled values + +Here, the red-component has just **3** bits and a value of **7** makes it **full saturated**. +After conversion to Rgb<**8**, 8, 8> it's still **fully saturated**. +Red now equals **255**. +```cpp +red = modm::color::Rgb<3, 2, 1>(7, 0, 0); +``` + +Convert from different ColorType +```cpp +red = modm::color::Hsv888(0, 255, 255); +``` + +Colors components are essentially color::Gray<> +This makes plain red as well +```cpp +modm::color::Gray2 white(3); +modm::color::Gray2 black(0); + +red = (white, black, black); +``` + +This is a conversion from Gray to Rgb. +It mixes white in rgb colorspace +```cpp +red = white; // -> Rgb888(255, 255, 255) +``` +## Saturation Arithmetics + +Every arithmetic operations yields in burned out or underexposed colors. + +Only the Hue component of Hsv ColorType wraps around just like integers do. + +## Flexible Widgets +- [ ] Complete this snippet and make the code actually working + +The flexible colortypes open the doors for flexible UI components as well. +You can write one codebase that has all the supports the cheapest to full color TFTs: + +```cpp +#include + +template +drawAlertButton() { + // @warning: Pseudocode. These methods do not yet exist! + drawFilledRectangle( static_cast(Hsv888(0, 40, 200)) ); // Button Background + drawRectangle( static_cast(modm::color::DarkRed) ); // Button Border + drawText( static_cast(modm::color::DarkRed), "Alert, Alert!"); // Button Text +} + +// Draws a red alert button +drawAlertButton(); + +// Draws a similar red alert button +drawAlertButton(); + +// Also draws a similar red alert button +drawAlertButton(); + +// In Monochrome colorspace at least you get the Button +drawAlertButton(); // Monochrome = shorthand for Gray<1> + +``` \ No newline at end of file diff --git a/src/modm/ui/color/concepts.hpp b/src/modm/ui/color/concepts.hpp new file mode 100644 index 0000000000..904f070283 --- /dev/null +++ b/src/modm/ui/color/concepts.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include + +/** + * @brief Concepts for colortypes and groups of colortypes. + * Key applications: + * - Color conversion constructors + * - Specialisation of buffer manipulation algorithms + * + * @author Thomas Sommer + * @ingroup modm_ui_color + */ +namespace modm::color +{ + +template +requires (D > 0) +class Gray; + +template +requires (DR > 0) && (DG > 0) && (DB > 0) +class Rgb; + +template +requires (DR > 0) && (DG > 0) && (DB > 0) +class RgbPallete; + +template +requires (DH > 0) && (DS > 0) && (DV > 0) +class Hsv; + +/** + * @brief Identify template class instance + * @see: https://stackoverflow.com/questions/44012938/how-to-tell-if-template-type-is-an-instance-of-a-template-class + */ +template class> +struct is_instance : public std::false_type {}; + +template class U> +struct is_instance, U> : public std::true_type {}; + +/** + * @brief Concepts for ColorType templates + */ +template +concept ColorGray = is_instance::value; + +template +concept ColorRgb = is_instance::value; + +template +concept ColorRgbPallete = is_instance::value; + +template +concept ColorHsv = is_instance::value; + +/** + * @brief Concepts for any ColorType + */ +template +concept Color = ColorGray || ColorRgb || ColorHsv || ColorRgbPallete; // conjunction +// concept Color = std::convertible_to >; // more tolerant alternative: convertability + +} // namespace modm::color \ No newline at end of file diff --git a/src/modm/ui/color/gray.hpp b/src/modm/ui/color/gray.hpp new file mode 100644 index 0000000000..18e89c9a13 --- /dev/null +++ b/src/modm/ui/color/gray.hpp @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2023, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +#include "concepts.hpp" + +namespace modm::color +{ + +/** + * @brief Unsigned integer with arbitrary number of digits, proportional conversion + * and saturating arithemtics. + * + * @tparam D Number of Digits + * + * @author Thomas Sommer + * + * @ingroup modm_ui_color + */ +template +requires (D > 0) +class Gray +{ +public: + using T = least_uint; + static constexpr T max = std::pow(2, D) - 1; + static constexpr int digits = D; + + /// constructors + constexpr Gray() = default; + + constexpr Gray(T value) + { +#ifdef MODM_DEBUG_BUILD + if (not modm_assert_continue_ignore_debug(value <= max, "Gray.ctor", + "value exceeded max", max)) + { + value_ = std::min(max, value); + return; + } +#endif + + value_ = value; + } + + // Construct from Gray with less digits: scale up + template + requires (E < D) + constexpr Gray(const Gray& other) + : value_(other.value_ * max / other.max) + {} + + // Construct from Gray with more digits: scale down + /// @warning Scaling down may infer loss of accuracy! + template + requires (E > D) + constexpr Gray(const Gray& other) + : value_(other.value_ >> (E - D)) + {} + + // OPTIMIZE Check the colors continuoty of this conversion algorithm, + // once buffers can be viewed on displays. + // @details: https://github.com/modm-io/modm/pull/781#discussion_r820142167 + + // @see: https://en.wikipedia.org/wiki/Grayscale#Converting_colour_to_grayscale + template + constexpr Gray(const C& rgb) + : value_( + ( 1742 * Gray(rgb.red()) + + 5859 * Gray(rgb.green()) + + 591 * Gray(rgb.blue()) + ) >> 13) + {} + + template + constexpr Gray(const C& rgbpallete) + : Gray(Rgb(rgbpallete)) + {} + + // Conversion from Hsv does not reflect human brightness perception like conversion from Rgb above + // e.g. converting Hsv->Rgb->Gray will not produce the same result like Hsv->Gray + // However, using hsv serves the purpose + template + constexpr Gray(const C& hsv) : value_(hsv.value()) + {} + + + /// assignment operator + void + operator=(T value) + { +#ifdef MODM_DEBUG_BUILD + if (not modm_assert_continue_ignore_debug(value <= max, "Gray.assign", + "value exceeded max", max)) + { + value_ = std::min(max, value); + return; + } +#endif + + value_ = value; + } + + // Assign from Gray with less digits: scale up + template + requires (E < D) + constexpr void operator=(const Gray& other) { + value_ = other.value_ * max / other.max; + } + + // Assign from Gray with more digits: scale down + /// @warning Scaling down may infer loss of accuracy! + template + requires (E > D) + constexpr void operator=(const Gray& other) { + value_ = other.value_ >> (E - D); + } + + // operator +=, -=, *=, /= + template + Gray& + operator+=(const I integral) { + modm::Saturated saturated(value_); + saturated += integral; + + value_ = std::min(value_, max); + + return *this; + } + + template + Gray& + operator+=(const Gray& other) + { + return this->operator+=(other.value_); + } + + template + Gray& + operator-=(const I integral) { + modm::Saturated saturated(value_); + saturated -= integral; + + value_ = std::min(value_, max); + + return *this; + } + + template + Gray& + operator-=(const Gray& other) + { + return this->operator-=(other.value_); + } + + template + Gray& + operator*=(const I scale) { + modm::Saturated saturated(value_); + saturated *= scale; + + value_ = std::min(value_, max); + + return *this; + } + + template + Gray& + operator*=(const Gray& other) + { + return this->operator*=(other.value_); + } + + template + Gray& + operator/=(const I scale) { + modm::Saturated saturated(value_); + saturated /= scale; + + value_ = std::min(value_, max); + + return *this; + } + + template + Gray& + operator/=(const Gray& other) + { + return this->operator*=(other.value_); + } + + // operator +, -, *, / + template + constexpr Gray + operator+(const I integral) const { + modm::Saturated saturated(value_); + saturated += integral; + + return std::min(saturated.value(), max); + } + + constexpr Gray + operator+(const Gray& other) const + { + return this->operator+(other.value_); + } + + template + constexpr Gray + operator-(const I integral) const { + modm::Saturated saturated(value_); + saturated -= integral; + + return std::min(saturated.value(), max); + } + + constexpr Gray + operator-(const Gray& other) const + { + return this->operator-(other.value_); + } + + template + constexpr Gray + operator*(const I scale) const { + modm::Saturated saturated(value_); + saturated *= scale; + + return std::min(saturated.value(), max); + } + + constexpr Gray + operator*(const Gray& other) const + { + return this->operator*(other.value_); + } + + template + constexpr Gray + operator/(const I scale) const { + modm::Saturated saturated(value_); + saturated /= scale; + return saturated.value(); + } + + constexpr Gray + operator/(const Gray& other) const + { + return this->operator/(other.value_); + } + + template + constexpr Gray + operator*(const F scale) const + { + // OPTIMIZE develop optimal decimals from D + static constexpr int decimals = 10; + + using WideType = modm::WideType; + WideType saturated = value_ * T(scale * decimals) / decimals; + return {T(std::min(saturated, max))}; + } + + void + invert() { + value_ ^= T(std::pow(2, D) - 1); + } + + bool + constexpr isSaturated() const + { + return value_ == max; + } + + constexpr operator T() const { + return value_; + } + +protected: + T value_{0}; + +private: + template + requires (E > 0) + friend class Gray; + + template + friend modm::IOStream & + operator<<(modm::IOStream &, const Gray &); +}; + +// If the user wants to provide a type rather than digits +template +using GrayT = Gray::digits>; + +using Monochrome = Gray<1>; +using Gray2 = Gray<2>; +using Gray4 = Gray<4>; +using Gray8 = GrayT; +using Gray16 = GrayT; +// Also fine +// using Gray8 = Gray<8>; +// using Gray16 = Gray<16>; + +#if __has_include() +#include + +template +modm::IOStream & +operator<<(modm::IOStream &os, const Gray &color) +{ + os << color.value_; + return os; +} +#endif + +} // namespace modm::color \ No newline at end of file diff --git a/src/modm/ui/color/hsv.hpp b/src/modm/ui/color/hsv.hpp index 76de4a6b28..f867f75749 100644 --- a/src/modm/ui/color/hsv.hpp +++ b/src/modm/ui/color/hsv.hpp @@ -1,9 +1,5 @@ /* - * Copyright (c) 2009, Martin Rosekeit - * Copyright (c) 2009-2013, Fabian Greif - * Copyright (c) 2012-2013, 2015, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2021-2023, Thomas Sommer * * This file is part of the modm project. * @@ -12,117 +8,172 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- - -#ifndef MODM_COLOR_HSV_HPP -#define MODM_COLOR_HSV_HPP +#pragma once #include #include -#include "brightness.hpp" -#include "rgb.hpp" -#include "rgb565.hpp" +#include "concepts.hpp" +#include "gray.hpp" +#include + +#include namespace modm::color { -// forward declarations for convertion constructors -template -class RgbT; - -template -class BrightnessT; - -class Rgb565; - /** - * @brief Color in HSV Colorspace + * @brief Color in HSV space. Each channel has a memoryaddress on its own. * - * @author Martin Rosekeit, Fabian Greif, Niklas Hauser, David Hebbeker, Thomas Sommer - * @ingroup modm_ui_color + * @tparam DH Digits for hue + * @tparam DS Digits for saturation + * @tparam DV Digits for value + * + * @author Thomas Sommer + * @ingroup modm_ui_color */ -template -class HsvT +template +requires (DH > 0) && (DS > 0) && (DV > 0) +class Hsv { public: - T hue{0}; - T saturation{0}; - T value{0}; + // Hue should wrap rather than saturate, thus an uintn_t is used + using HueType = uintn_t; + + using SaturationType = Gray; + using ValueType = Gray; - constexpr HsvT() = default; + constexpr Hsv() = default; - constexpr HsvT(T hue, T saturation, T value) : hue(hue), saturation(saturation), value(value) {} + // TODO Support for https://en.cppreference.com/w/cpp/utility/initializer_list - /** - * Copy Constructor 8bit->16bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr HsvT(const HsvT &hsv_other) - : hue(hsv_other.hue << 8), saturation(hsv_other.saturation << 8), value(hsv_other.value << 8) + constexpr Hsv(HueType hue, SaturationType saturation, ValueType value) + : hue_(hue), saturation_(saturation), value_(value) {} - /** - * Copy Constructor 16bit->8bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr HsvT(const HsvT &hsv_other) - : hue(hsv_other.hue >> 8), saturation(hsv_other.saturation >> 8), value(hsv_other.value >> 8) + template + constexpr Hsv(const C &other) + : hue_(other.hue_), saturation_(other.saturation_), value_(other) {} - /** - * Convertion Constructor for RGB Color - * - * @param rgb RGB Color - */ - template - constexpr HsvT(const RgbT& rgb); - - /** - * Convertion Constructor for Brightness - * - * @param brightness Brightness 'Color'-object - */ - template - constexpr HsvT(const BrightnessT gray) : hue(0), saturation(0), value(gray.value) + template + constexpr Hsv(const C &gray) + : hue_(0), saturation_(0), value_(gray) {} - /** - * Convertion Constructor for RGB565 Color - * - * @param rgb565 RGB565 Color - */ - constexpr HsvT(const Rgb565& rgb565) : HsvT(RgbT(rgb565)) {} + template + constexpr Hsv(const C& rgb) + { + // OPTIMIZE No need to calculate sharper than the output + // Develop CalcType from target types: HueType, SaturationType and ValueType + using CalcType = float; + + const CalcType maxValue = ValueType::max; + + const CalcType red = CalcType(ValueType(rgb.red())) / maxValue; + const CalcType green = CalcType(ValueType(rgb.green())) / maxValue; + const CalcType blue = CalcType(ValueType(rgb.blue())) / maxValue; + const CalcType max = modm::vmax(red, green, blue); + const CalcType min = modm::vmin(red, green, blue); + const CalcType diff = max - min; + + CalcType hue_temp; + + if (max == min) { + // all three color values are the same + hue_temp = 0; + value_ = max * maxValue; + } else if (max == red) { + hue_temp = 60 * (0 + (green - blue) / diff); + value_ = rgb.red(); + } else if (max == green) { + hue_temp = 60 * (2 + (blue - red) / diff); + value_ = rgb.green(); + } else { + // max == blue + hue_temp = 60 * (4 + (red - green) / diff); + value_ = rgb.blue(); + } + + hue_ = hue_temp < 0 ? (hue_temp + 360) * (maxValue / 360) : (hue_temp) * (maxValue / 360); + saturation_ = max ? diff / max * maxValue : 0; + } + + template + constexpr Hsv(const C& rgbstacked) + : Hsv(Rgb(rgbstacked)) + {} + + // accesors + constexpr HueType hue() const { return hue_; } + constexpr SaturationType saturation() const { return saturation_; } + constexpr ValueType value() const { return value_; } + + HueType& hue() { return hue_; } + SaturationType& saturation() { return saturation_; } + ValueType& value() { return value_; } + // TODO operator +=, -=, *=, /= + // @see: https://gamedev.stackexchange.com/questions/26525/how-do-you-blend-multiple-colors-in-hsv-polar-color-space + + // TODO operator +, -, *, / + + // comparison operators constexpr bool - operator==(const HsvT& other) const = default; + operator==(const Hsv& other) const = default; + + // Compare perceived brightness. For performance and simplicity reasons, + // the intermediate brightness type is hardcoded to Gray8. + constexpr auto + operator<=>(const Gray8& gray) const noexcept { + return value_ <=> gray; + }; + + constexpr void invert() + { hue_.invert(); } private: - template - friend IOStream& - operator<<(IOStream&, const HsvT&); + HueType hue_{0}; + SaturationType saturation_{0}; + ValueType value_{0}; + + template + requires (EH > 0) && (ES > 0) && (EV > 0) + friend class Hsv; }; -/// @ingroup modm_ui_color -using Hsv = HsvT; +template +using HsvT = Hsv::digits>; +/// @ingroup modm_ui_color +using Hsv888 = HsvT; +using Hsv161616 = HsvT; #if __has_include() #include /// @ingroup modm_ui_color -template +template IOStream& -operator<<(IOStream& os, const color::HsvT& color) +operator<<(IOStream& os, const C& hsv) { - os << color.hue << "\t" << color.saturation << "\t" << color.value; + os << hsv.hue() << "\t" << hsv.saturation() << "\t" << hsv.value(); return os; } + +// Human friendly output: +// Hue in deg, Sat in pct, Value in pct +/// @ingroup modm_ui_color +template +void ostream_human_friendly(IOStream& os, const C& hsv) { + using CalcTypeHue = modm::WideType; + using CalcTypeSaturation = modm::WideType; + using CalcTypeValue = modm::WideType; + + os << (CalcTypeHue(hsv.hue()) * 360 / hsv.hue().max) << "deg\t"; + os << (CalcTypeSaturation(hsv.saturation()) * 100 / hsv.saturation().max) << "%\t"; + os << (CalcTypeValue(hsv.value()) * 100 / hsv.value().max) << "%"; +} #endif } // namespace modm::color - -#include "hsv_impl.hpp" - -#endif // MODM_COLOR_HSV_HPP diff --git a/src/modm/ui/color/hsv_impl.hpp b/src/modm/ui/color/hsv_impl.hpp deleted file mode 100644 index c9851b787f..0000000000 --- a/src/modm/ui/color/hsv_impl.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2009-2010, 2012, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2013, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_COLOR_HSV_HPP -#error "Don't include this file directly, use 'hsv.hpp' instead!" -#endif - -#include - -/** - * @see http://de.wikipedia.org/wiki/HSV-Farbraum#Umrechnung_RGB_in_HSV.2FHSL - * @param rgb - */ -template -template -constexpr modm::color::HsvT::HsvT(const modm::color::RgbT &rgb) -{ - using CalcType = float; - const CalcType maxValue = std::numeric_limits::max(); - const CalcType _red = CalcType(rgb.red) / maxValue; - const CalcType _blue = CalcType(rgb.blue) / maxValue; - const CalcType _green = CalcType(rgb.green) / maxValue; - const CalcType _max = std::max(_red, std::max(_green, _blue)); - const CalcType _min = std::min(_red, std::min(_green, _blue)); - const CalcType _diff = _max - _min; - - CalcType hue_temp; - - // CALCULATE HUE - if (_max == _min) - { - // all three color values are the same - hue_temp = 0; - value = _max * maxValue; - } else if (_max == _red) - { - hue_temp = 60 * (0 + (_green - _blue) / _diff); - value = rgb.red; - } else if (_max == _green) - { - hue_temp = 60 * (2 + (_blue - _red) / _diff); - value = rgb.green; - } else /*if(_max == _blue)*/ - { - hue_temp = 60 * (4 + (_red - _green) / _diff); - value = rgb.blue; - } - - if (hue_temp < 0) - hue = (hue_temp + 360) * (maxValue / 360); - else - hue = (hue_temp) * (maxValue / 360); - - // CALCULATE SATURATION - if (_max == 0) - saturation = 0; - else - saturation = _diff / _max * maxValue; -} diff --git a/src/modm/ui/color/rgb.hpp b/src/modm/ui/color/rgb.hpp index 788043766a..4277fe8550 100644 --- a/src/modm/ui/color/rgb.hpp +++ b/src/modm/ui/color/rgb.hpp @@ -1,9 +1,5 @@ /* - * Copyright (c) 2009, Martin Rosekeit - * Copyright (c) 2009-2013, Fabian Greif - * Copyright (c) 2012-2013, 2015, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2021-2023, Thomas Sommer * * This file is part of the modm project. * @@ -12,9 +8,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ---------------------------------------------------------------------------- - -#ifndef MODM_COLOR_RGB_HPP -#define MODM_COLOR_RGB_HPP +#pragma once #include #include @@ -24,102 +18,200 @@ #include #include -#include "brightness.hpp" -#include "hsv.hpp" -#include "rgb565.hpp" +#include "concepts.hpp" +#include "gray.hpp" namespace modm::color { -// forward declarations for convertion constructors -template -class HsvT; - -template -class BrightnessT; - -class Rgb565; - /** - * Color in HSV Colorspace + * @brief Color in RGB space. Each channel has a memoryaddress on its own. * - * @author Martin Rosekeit, Fabian Greif, Niklas Hauser, David Hebbeker, Thomas Sommer - * @ingroup modm_ui_color + * @tparam DR Digits for red channel + * @tparam DG Digits for green channel + * @tparam DB Digits for blue channel + * + * @author Thomas Sommer + * @ingroup modm_ui_color */ -template -class RgbT +template +requires (DR > 0) && (DG > 0) && (DB > 0) +class Rgb { public: - T red{0}; - T green{0}; - T blue{0}; + using RedType = Gray; + using GreenType = Gray; + using BlueType = Gray; - using TSum = modm::WideType; + // using RgbSumValueType = modm::fits_any; - constexpr RgbT() = default; + constexpr Rgb() = default; - constexpr RgbT(T red, T green, T blue) : red(red), green(green), blue(blue) {} + // TODO Support for https://en.cppreference.com/w/cpp/utility/initializer_list - /** - * Copy Constructor 8bit->16bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr RgbT(const RgbT &rgb_other) - : red(rgb_other.red << 8), green(rgb_other.green << 8), blue(rgb_other.blue << 8) + constexpr Rgb(RedType red, GreenType green, BlueType blue) + : red_(red), green_(green), blue_(blue) {} - /** - * Copy Constructor 16bit->8bit - */ - template - requires std::is_same_v && std::is_same_v - constexpr RgbT(const RgbT &rgb_other) - : red(rgb_other.red >> 8), green(rgb_other.green >> 8), blue(rgb_other.blue >> 8) + template + requires ColorRgb || ColorRgbPallete + constexpr Rgb(const C& other) + : red_(other.red()), green_(other.green()), blue_(other.blue()) {} - /** - * Convertion Constructor for HSV Color - * - * @param hsv HSV Color - */ - template - constexpr RgbT(const HsvT& hsv); - - /** - * Convertion Constructor for Brightness - * - * @param brightness Brightness 'Color'-object - */ - // TODO Plump conversion, implement the right way - template - constexpr RgbT(const BrightnessT brightness) - : red(brightness), green(brightness), blue(brightness) + template + constexpr Rgb(const C &gray) + : red_(gray), green_(gray), blue_(gray) {} - /** - * Convertion Constructor for RGB565 Color - * - * @param rgb565 RGB565 Color - */ - constexpr RgbT(const Rgb565& rgb565) - : red((rgb565.color >> 8) & 0xF8), - green((rgb565.color >> 3) & 0xFC), - blue(rgb565.color << 3) - {} + template + constexpr Rgb(const C& hsv) + { + // No need to calculate sharper than the output + // OPTIMIZE Develop CalcType from types of conversion target: RedType, GreenType and BlueType. + using CalcType = C::ValueType; + using T = CalcType::T; + + using WideType = modm::WideType; + using WideWideType = modm::WideType; + static_assert(!std::is_same_v, "C::T too big"); + + const T hue = CalcType(Gray::digits>(hsv.hue())); + const T saturation = CalcType(hsv.saturation()); + const T value = CalcType(hsv.value()); + + const WideType vs = value * saturation; + const WideType h6 = 6 * hue; + + T i = h6 >> CalcType::digits; + WideType f = ((i | 1) << CalcType::digits) - h6; + if (i & 1) f = -f; + + CalcType p(((value << CalcType::digits) - vs) >> CalcType::digits); + CalcType u(((WideWideType(value) << 2 * CalcType::digits) - WideWideType(vs) * f) >> 2 * CalcType::digits); + + switch (i) + { + case 0: red_ = hsv.value(); green_ = u; blue_ = p; break; + case 1: red_ = u; green_ = hsv.value(); blue_ = p; break; + case 2: red_ = p; green_ = hsv.value(); blue_ = u; break; + case 3: red_ = p; green_ = u; blue_ = hsv.value(); break; + case 4: red_ = u; green_ = p; blue_ = hsv.value(); break; + case 5: red_ = hsv.value(); green_ = p; blue_ = u; break; + } + } + + // accessors + constexpr RedType red() const { return red_; } + constexpr GreenType green() const { return green_; } + constexpr BlueType blue() const { return blue_; } + + RedType& red() { return red_; } + GreenType& green() { return green_; } + BlueType& blue() { return blue_; } + + // operator +=, -=, *=, /= + Rgb& operator+=(const Rgb& other) { + red_ += other.red(); + green_ += other.green(); + blue_ += other.blue(); + return *this; + } + + Rgb& operator-=(const Rgb& other) { + red_ -= other.red(); + green_ -= other.green(); + blue_ -= other.blue(); + return *this; + } + + template + Rgb operator*= (S scale) { + red_ *= scale; + green_ *= scale; + blue_ *= scale; + return *this; + } + + template + Rgb operator/= (S scale) { + red_ *= scale; + green_ *= scale; + blue_ *= scale; + return *this; + } + + // operator +, -, *, / + constexpr Rgb + operator+(const Rgb& rgb) const { + return { + red_ + rgb.red(), + green_ + rgb.green(), + blue_ + rgb.blue() + }; + } + + constexpr Rgb + operator-(const Rgb& rgb) const { + return { + red_ - rgb.red(), + green_ - rgb.green(), + blue_ - rgb.blue() + }; + } + + template + constexpr Rgb + operator*(S scale) const { + return { + red_ * scale, + green_ * scale, + blue_ * scale + }; + } + + template + constexpr Rgb + operator/(S scale) const { + return { + red_ / scale, + green_ / scale, + blue_ / scale + }; + } constexpr bool - operator==(const RgbT& other) const = default; + operator==(const Rgb& other) const = default; + + // Compare perceived brightness. For performance and simplicity reasons, + // the intermediate brightness type is hardcoded to Gray8. + constexpr auto + operator<=>(const Gray8& gray) const noexcept { + return Gray8(*this) <=> gray; + }; + + void invert() { + red_.invert(); + green_.invert(); + blue_.invert(); + } private: - template - friend IOStream& - operator<<(IOStream&, const RgbT&); + RedType red_{0}; + GreenType green_{0}; + BlueType blue_{0}; + + template + requires (ER > 0) && (EG > 0) && (EB > 0) + friend class Rgb; }; -/// @ingroup modm_ui_color -using Rgb = RgbT; +template +using RgbT = Rgb::digits>; +/// @ingroup modm_ui_color +using Rgb888 = RgbT; +using Rgb161616 = RgbT; /** * Normalize color values based on a clear value @@ -133,33 +225,31 @@ using Rgb = RgbT; * * @ingroup modm_ui_color */ -template +template requires std::is_fundamental_v -constexpr RgbT -normalizeColor(RgbT rgb, IntermediateType multiplier = 1) +constexpr ReturnColor +normalizeColor(C rgb, IntermediateType multiplier = 1) { - const IntermediateType sum = IntermediateType(rgb.red) + rgb.green + rgb.blue; - return RgbT(IntermediateType(rgb.red) * multiplier / sum, - IntermediateType(rgb.green) * multiplier / sum, - IntermediateType(rgb.blue) * multiplier / sum); + // OPTIMIZE This should also work with fixed point and Colors integrated operator* and operator/ + const IntermediateType sum = IntermediateType(rgb.red() + rgb.green() + rgb.blue()); + return { + IntermediateType(rgb.red()) * multiplier / sum, + IntermediateType(rgb.green()) * multiplier / sum, + IntermediateType(rgb.blue()) * multiplier / sum + }; } - #if __has_include() #include /// @ingroup modm_ui_color -template +template IOStream& -operator<<(IOStream& os, const color::RgbT& color) +operator<<(IOStream& os, const C& rgb) { - os << color.red << "\t" << color.green << "\t" << color.blue; + os << rgb.red() << "\t" << rgb.green() << "\t" << rgb.blue(); return os; } #endif -} // namespace modm::color - -#include "rgb_impl.hpp" - -#endif // MODM_COLOR_RGB_HPP +} // namespace modm::color \ No newline at end of file diff --git a/src/modm/ui/color/rgb565.hpp b/src/modm/ui/color/rgb565.hpp deleted file mode 100644 index dbb19aba56..0000000000 --- a/src/modm/ui/color/rgb565.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include - -#include "brightness.hpp" -#include "hsv.hpp" -#include "rgb.hpp" - -namespace modm::color -{ - -// forward declarations for convertion constructors -template -class RgbT; - -template -class HsvT; - -template -class BrightnessT; - -/** - * Color in RGB Colorspace, 16 bits: RRRR RGGG GGGB BBBB - * - * @author Fabian Greif, Thomas Sommer - * @ingroup modm_ui_color - */ -class Rgb565 -{ -public: - uint16_t color{0x0000}; - - using RgbCalcType = RgbT; - - constexpr Rgb565() = default; - - /** - * Constructor for preformatted Rgb565 Color - * - * @param color Preformatted RGB-color in 565-format: RRRR RGGG GGGB BBBB - */ - constexpr Rgb565(uint16_t color) : color(color) {} - - /** - * Constructor for components: red, green, blue - * - * @param red Red color component - * @param green Green color component - * @param blue Blue color component - */ - constexpr Rgb565(uint8_t red, uint8_t green, uint8_t blue) - : color(uint16_t(red & 0xF8) << 8 | uint16_t(green & 0xFC) << 3 | uint16_t(blue >> 3)) - {} - - /** - * Convertion Constructor for RGB Color - * - * @param rgb RGB Color - */ - template - constexpr Rgb565(const RgbT &rgb) : Rgb565(rgb.red, rgb.green, rgb.blue) - {} - - /** - * Convertion Constructor for HSV Color - * - * @param hsv HSV Color - */ - template - constexpr Rgb565(const HsvT &hsv) : Rgb565(RgbCalcType(hsv)) - {} - - /** - * Convertion Constructor for Brightness - * - * @param brightness Brightness 'Color'-object - */ - template - constexpr Rgb565(const BrightnessT brightness) : Rgb565(RgbCalcType(brightness)) - {} - - constexpr bool - operator==(const Rgb565 &other) const = default; - - /// Saturated addition ⊕ - Rgb565 - operator+(const Rgb565 other) const - { - const int8_t red_raw = (color >> 11) + (other.color >> 11); - const uint16_t red = std::clamp(red_raw, 0, 31) << 11; - - const int8_t green_raw = (color >> 5 & 0x3F) + (other.color >> 5 & 0x3F); - const uint16_t green = std::clamp(green_raw, 0, 63) << 5; - - const int8_t blue_raw = (color & 0x1F) + (other.color & 0x1F); - const uint16_t blue = std::clamp(blue_raw, 0, 31); - - return Rgb565(red | green | blue); - } - - /// Saturated substraction ⊖ - Rgb565 - operator-(const Rgb565 other) const - { - const int8_t red_raw = (color >> 11) - (other.color >> 11); - const uint16_t red = std::clamp(red_raw, 0, 0x1F) << 11; - - const int8_t green_raw = (color >> 5 & 0x3F) - (other.color >> 5 & 0x3F); - const uint16_t green = std::clamp(green_raw, 0, 0x3F) << 5; - - const int8_t blue_raw = (color & 0x1F) - (other.color & 0x1F); - const uint16_t blue = std::clamp(blue_raw, 0, 0x1F); - - return Rgb565(red | green | blue); - } -}; - -} // namespace modm::color diff --git a/src/modm/ui/color/rgb_html.hpp b/src/modm/ui/color/rgb_html.hpp new file mode 100644 index 0000000000..ade5e2d971 --- /dev/null +++ b/src/modm/ui/color/rgb_html.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include "rgb.hpp" + +/// @ingroup modm_ui_color +namespace modm::color::html +{ + +/** + * Constant HTML Colornames in RGB Colorspace + * + * @see https://htmlcolorcodes.com/color-names/ + * @see modm:color:RgbT + * @ingroup modm_ui_color + * @{ + */ + +// Red HTML Color Names +static constexpr Rgb888 IndianRed(205, 92, 92); +static constexpr Rgb888 LightCoral(240, 128, 128); +static constexpr Rgb888 Salmon(250, 128, 114); +static constexpr Rgb888 DarkSalmon(233, 150, 122); +static constexpr Rgb888 LightSalmon(255, 160, 122); +static constexpr Rgb888 Crimson(220, 20, 60); +static constexpr Rgb888 Red(255, 0, 0); +static constexpr Rgb888 FireBrick(178, 34, 34); +static constexpr Rgb888 DarkRed(139, 0, 0); + +// Pink HTML Color Names +static constexpr Rgb888 Pink(255, 192, 203); +static constexpr Rgb888 LightPink(255, 182, 193); +static constexpr Rgb888 HotPink(255, 105, 180); +static constexpr Rgb888 DeepPink(255, 20, 147); +static constexpr Rgb888 MediumVioletRed(199, 21, 133); +static constexpr Rgb888 PaleVioletRed(219, 112, 147); + +// Orange HTML Color Names +static constexpr Rgb888 Coral(255, 127, 80); +static constexpr Rgb888 Tomato(255, 99, 71); +static constexpr Rgb888 OrangeRed(255, 69, 0); +static constexpr Rgb888 DarkOrange(255, 140, 0); +static constexpr Rgb888 Orange(25, 165, 0); + +// Yellow HTML Color Names +static constexpr Rgb888 Gold(255, 215, 0); +static constexpr Rgb888 Yellow(255, 255, 0); +static constexpr Rgb888 LightYellow(255, 255, 224); +static constexpr Rgb888 LemonChiffon(255, 250, 205); +static constexpr Rgb888 LightGoldenrodYellow(250, 250, 210); +static constexpr Rgb888 PapayaWhip(255, 239, 213); +static constexpr Rgb888 Moccasin(255, 228, 181); +static constexpr Rgb888 PeachPuff(255, 218, 185); +static constexpr Rgb888 PaleGoldenrod(238, 232, 170); +static constexpr Rgb888 Khaki(240, 230, 140); +static constexpr Rgb888 DarkKhaki(189, 183, 107); + +// Purple HTML Color Names +static constexpr Rgb888 Lavender(230, 230, 250); +static constexpr Rgb888 Thistle(216, 191, 216); +static constexpr Rgb888 Plum(221, 160, 221); +static constexpr Rgb888 Violet(238, 130, 238); +static constexpr Rgb888 Orchid(218, 112, 214); +static constexpr Rgb888 Fuchsia(255, 0, 255); +static constexpr Rgb888 Magenta(255, 0, 255); +static constexpr Rgb888 MediumOrchid(186, 85, 211); +static constexpr Rgb888 MediumPurple(147, 112, 219); +static constexpr Rgb888 RebeccaPurple(102, 51, 153); +static constexpr Rgb888 BlueViolet(138, 43, 226); +static constexpr Rgb888 DarkViolet(148, 0, 211); +static constexpr Rgb888 DarkOrchid(153, 50, 204); +static constexpr Rgb888 DarkMagenta(139, 0, 139); +static constexpr Rgb888 Purple(128, 0, 128); +static constexpr Rgb888 Indigo(75, 0, 130); +static constexpr Rgb888 SlateBlue(106, 90, 205); +static constexpr Rgb888 DarkSlateBlue(72, 61, 139); + +// Green HTML Color Names +static constexpr Rgb888 GreenYellow(173, 255, 47); +static constexpr Rgb888 Chartreuse(127, 255, 0); +static constexpr Rgb888 LawnGreen(124, 252, 0); +static constexpr Rgb888 Lime(0, 255, 0); +static constexpr Rgb888 LimeGreen(50, 205, 50); +static constexpr Rgb888 PaleGreen(152, 251, 152); +static constexpr Rgb888 LightGreen(144, 238, 144); +static constexpr Rgb888 MediumSpringGreen(0, 250, 154); +static constexpr Rgb888 SpringGreen(0, 255, 127); +static constexpr Rgb888 MediumSeaGreen(60, 179, 113); +static constexpr Rgb888 SeaGreen(46, 139, 87); +static constexpr Rgb888 ForestGreen(34, 139, 34); +static constexpr Rgb888 Green(0, 128, 0); +static constexpr Rgb888 DarkGreen(0, 100, 0); +static constexpr Rgb888 YellowGreen(154, 205, 50); +static constexpr Rgb888 OliveDrab(107, 142, 35); +static constexpr Rgb888 Olive(128, 128, 0); +static constexpr Rgb888 DarkOliveGreen(85, 107, 47); +static constexpr Rgb888 MediumAquamarine(102, 205, 170); +static constexpr Rgb888 DarkSeaGreen(143, 188, 139); +static constexpr Rgb888 LightSeaGreen(32, 178, 170); +static constexpr Rgb888 DarkCyan(0, 139, 139); +static constexpr Rgb888 Teal(0, 128, 128); + +// Blue HTML Color Names +static constexpr Rgb888 Aqua(0, 255, 255); +static constexpr Rgb888 Cyan(0, 255, 255); +static constexpr Rgb888 LightCyan(224, 255, 255); +static constexpr Rgb888 PaleTurquoise(175, 238, 238); +static constexpr Rgb888 Aquamarine(127, 255, 212); +static constexpr Rgb888 Turquoise(64, 224, 208); +static constexpr Rgb888 MediumTurquoise(72, 209, 204); +static constexpr Rgb888 DarkTurquoise(0, 206, 209); +static constexpr Rgb888 CadetBlue(95, 158, 160); +static constexpr Rgb888 SteelBlue(70, 130, 180); +static constexpr Rgb888 LightSteelBlue(176, 196, 222); +static constexpr Rgb888 PowderBlue(176, 224, 230); +static constexpr Rgb888 LightBlue(173, 216, 230); +static constexpr Rgb888 SkyBlue(135, 206, 235); +static constexpr Rgb888 LightSkyBlue(135, 206, 250); +static constexpr Rgb888 DeepSkyBlue(0, 191, 255); +static constexpr Rgb888 DodgerBlue(30, 144, 255); +static constexpr Rgb888 CornflowerBlue(100, 149, 237); +static constexpr Rgb888 MediumSlateBlue(123, 104, 238); +static constexpr Rgb888 RoyalBlue(65, 105, 225); +static constexpr Rgb888 Blue(0, 0, 255); +static constexpr Rgb888 MediumBlue(0, 0, 205); +static constexpr Rgb888 DarkBlue(0, 0, 139); +static constexpr Rgb888 Navy(0, 0, 128); +static constexpr Rgb888 MidnightBlue(25, 25, 112); + +// Brown HTML Color Names +static constexpr Rgb888 Cornsilk(255, 248, 220); +static constexpr Rgb888 BlanchedAlmond(255, 235, 205); +static constexpr Rgb888 Bisque(255, 228, 196); +static constexpr Rgb888 NavajoWhite(255, 222, 173); +static constexpr Rgb888 Wheat(245, 222, 179); +static constexpr Rgb888 BurlyWood(222, 184, 135); +static constexpr Rgb888 Tan(210, 180, 140); +static constexpr Rgb888 RosyBrown(188, 143, 143); +static constexpr Rgb888 SandyBrown(244, 164, 96); +static constexpr Rgb888 Goldenrod(218, 165, 32); +static constexpr Rgb888 DarkGoldenrod(184, 134, 11); +static constexpr Rgb888 Peru(205, 133, 63); +static constexpr Rgb888 Chocolate(210, 105, 30); +static constexpr Rgb888 SaddleBrown(139, 69, 19); +static constexpr Rgb888 Sienna(160, 82, 45); +static constexpr Rgb888 Brown(165, 42, 42); +static constexpr Rgb888 Maroon(128, 0, 0); + +// White HTML Color Names +static constexpr Rgb888 White(255, 255, 255); +static constexpr Rgb888 Snow(255, 250, 250); +static constexpr Rgb888 HoneyDew(240, 255, 240); +static constexpr Rgb888 MintCream(245, 255, 250); +static constexpr Rgb888 Azure(240, 255, 255); +static constexpr Rgb888 AliceBlue(240, 248, 255); +static constexpr Rgb888 GhostWhite(248, 248, 255); +static constexpr Rgb888 WhiteSmoke(245, 245, 245); +static constexpr Rgb888 SeaShell(255, 245, 238); +static constexpr Rgb888 Beige(245, 245, 220); +static constexpr Rgb888 OldLace(253, 245, 230); +static constexpr Rgb888 FloralWhite(255, 250, 230); +static constexpr Rgb888 Ivory(255, 255, 240); +static constexpr Rgb888 AntiqueWhite(250, 235, 215); +static constexpr Rgb888 Linen(250, 240, 230); +static constexpr Rgb888 LavenderBlush(255, 240, 245); +static constexpr Rgb888 MistyRose(255, 228, 225); + +// Gray HTML Color Names +static constexpr Rgb888 Gainsboro(220, 220, 220); +static constexpr Rgb888 LightGray(211, 211, 211); +static constexpr Rgb888 Silver(192, 192, 192); +static constexpr Rgb888 DarkGray(169, 169, 169); +static constexpr Rgb888 Gray(128, 128, 128); +static constexpr Rgb888 DimGray(105, 105, 105); +static constexpr Rgb888 LightSlateGray(119, 136, 153); +static constexpr Rgb888 SlateGray(112, 128, 144); +static constexpr Rgb888 DarkSlateGray(47, 79, 79); +static constexpr Rgb888 Black(0, 0, 0); + +/// @} + +} // namespace modm::color::html diff --git a/src/modm/ui/color/rgb_impl.hpp b/src/modm/ui/color/rgb_impl.hpp deleted file mode 100644 index b8b9a17841..0000000000 --- a/src/modm/ui/color/rgb_impl.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2009-2010, 2012, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012-2013, Niklas Hauser - * Copyright (c) 2013, David Hebbeker - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_COLOR_RGB_HPP -#error "Don't include this file directly, use 'rgb.hpp' instead!" -#endif - -#include - -namespace modm::color -{ - -// TODO Finish generalisation for uint16_t -template -template -constexpr RgbT::RgbT(const HsvT &hsv) -{ - uint16_t vs = hsv.value * hsv.saturation; - uint16_t h6 = 6 * hsv.hue; - - T p = ((hsv.value << 8) - vs) >> 8; - T i = h6 >> 8; - uint16_t f = ((i | 1) << 8) - h6; - if (i & 1) { f = -f; } - T u = (((uint32_t)hsv.value << 16) - (uint32_t)vs * f) >> 16; - - switch (i) - { - case 0: red = hsv.value; green = u; blue = p; break; - case 1: red = u; green = hsv.value; blue = p; break; - case 2: red = p; green = hsv.value; blue = u; break; - case 3: red = p; green = u; blue = hsv.value; break; - case 4: red = u; green = p; blue = hsv.value; break; - case 5: red = hsv.value; green = p; blue = u; break; - } -} - -} // namespace modm::color diff --git a/src/modm/ui/color/rgb_pallete.hpp b/src/modm/ui/color/rgb_pallete.hpp new file mode 100644 index 0000000000..6929b477d8 --- /dev/null +++ b/src/modm/ui/color/rgb_pallete.hpp @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2023, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- +#pragma once + +#include "concepts.hpp" + +namespace modm::color +{ + +/** + * @brief Color in RGB space - mandatory for most graphic displays and stored images. + * Requires less space than unstacked rgb types. Calculations without dedicated graphics + * acceleration hardware are very inefficient. + * If theres no dedicated graphics acceleration but lots of RAM, using modm::Rgb<> for + * calculations and afterwards conversion to modm::RgbPallete<> may be an option. + * + * @tparam DR Digits for red channel + * @tparam DG Digits for green channel + * @tparam DB Digits for blue channel + * + * @author Thomas Sommer + * @ingroup modm_ui_color + */ +template +requires (DR > 0) && (DG > 0) && (DB > 0) +class RgbPallete +{ +public: + using RedType = Gray; + using GreenType = Gray; + using BlueType = Gray; + + using T = least_uint; + + constexpr RgbPallete() = default; + + constexpr RgbPallete(T value) + : value_(value) + {} + + constexpr RgbPallete(RedType red, GreenType green, BlueType blue) + : value_(red << (DG + DB) | green << DB | blue) + {} + + constexpr RgbPallete(const RgbPallete &rgbstacked) + : value_(rgbstacked.value_) + {} + + template + constexpr RgbPallete(const C &gray) + : RgbPallete(Rgb(gray)) + {} + + template + constexpr RgbPallete(const C &rgb) + : RgbPallete(rgb.red(), rgb.green(), rgb.blue()) + {} + + template + constexpr RgbPallete(const C &rgstacked) + : RgbPallete(rgstacked.red(), rgstacked.green(), rgstacked.blue()) + {} + + template + constexpr RgbPallete(const C &hsv) + : RgbPallete(Rgb<5,6,5>(hsv)) + {} + + /** + * @brief Interface to channels of stacked color types + * May be used for StackedHsv as well + * + * @tparam TS unsigned integral type of stacked color value + * @tparam TC Gray<> type of channel + * @tparam Shift lshift of channel in stacked color value + * + * @ingroup modm_ui_color + */ + template + struct channel_accessor { + TS &value_; + + void operator= (const TC new_value) + { value_ = (value_ & ~(TC::max << Shift)) | new_value << Shift; } + + TC value() const { return (value_ >> Shift) & TC::max; } + }; + + // accessors + constexpr RedType red() const { return value_ >> (DG + DB); } + constexpr GreenType green() const { return value_ >> DB & GreenType::max;} + constexpr BlueType blue() const { return value_ & BlueType::max; } + + auto red() { return channel_accessor{value_}; } + auto green() { return channel_accessor{value_}; } + auto blue() { return channel_accessor{value_}; } + + // assignment + void operator=(const RgbPallete other) { + value_ = other.value_; + } + + RgbPallete& operator+=(const Rgb& rgb) { + operator=({ + red() + rgb.red(), + green() + rgb.green(), + blue() + rgb.blue() + }); + return *this; + } + + RgbPallete& operator-=(const Rgb& rgb) { + operator=({ + red() - rgb.red(), + green() - rgb.green(), + blue() - rgb.blue() + }); + return *this; + } + + RgbPallete& operator*=(const Rgb& rgb) { + operator=({ + red() * rgb.red(), + green() * rgb.green(), + blue() * rgb.blue() + }); + return *this; + } + + RgbPallete& operator/=(const Rgb& rgb) { + operator=({ + red() * rgb.red(), + green() * rgb.green(), + blue() * rgb.blue() + }); + return *this; + } + + constexpr RgbPallete + operator+(const Rgb& rgb) { + return { + red() + rgb.red(), + green() + rgb.green(), + blue() + rgb.blue() + }; + } + + constexpr RgbPallete + operator-(const Rgb& rgb) { + return { + red() - rgb.red(), + green() - rgb.green(), + blue() - rgb.blue() + }; + } + + constexpr RgbPallete + operator*(const Rgb& rgb) { + return { + red() * rgb.red(), + green() * rgb.green(), + blue() * rgb.blue() + }; + } + + constexpr RgbPallete + operator/(const Rgb& rgb) { + return { + red() / rgb.red(), + green() / rgb.green(), + blue() / rgb.blue() + }; + } + + constexpr bool + operator==(const RgbPallete& other) const = default; + + // Remaining comparison operators are server by color::Rgb and implicit type conversion + + void invert() { + value_ ^= T(std::pow(2, DR + DG + DB) - 1); + } + + constexpr T value() const { return value_; } +private: + T value_{0}; +}; + +using Rgb565 = RgbPallete<5,6,5>; +using Rgb666 = RgbPallete<6,6,6>; + +#if __has_include() +#include + +template +IOStream& +operator<<(IOStream& os, const C& rgb) +{ + os << rgb.red() << "\t" << rgb.green() << "\t" << rgb.blue(); + return os; +} +#endif + +} // namespace modm::color \ No newline at end of file diff --git a/src/modm/ui/color/rgbhtml.hpp b/src/modm/ui/color/rgbhtml.hpp deleted file mode 100644 index 7b282fadf8..0000000000 --- a/src/modm/ui/color/rgbhtml.hpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once - -#include "rgb.hpp" - -/// @ingroup modm_ui_color -namespace modm::color::html -{ - -/** - * Constant HTML Colornames in RGB Colorspace - * - * @see https://htmlcolorcodes.com/color-names/ - * @see modm:color:RgbT - * @ingroup modm_ui_color - * @{ - */ - -// Red HTML Color Names -static constexpr Rgb IndianRed(205, 92, 92); -static constexpr Rgb LightCoral(240, 128, 128); -static constexpr Rgb Salmon(250, 128, 114); -static constexpr Rgb DarkSalmon(233, 150, 122); -static constexpr Rgb LightSalmon(255, 160, 122); -static constexpr Rgb Crimson(220, 20, 60); -static constexpr Rgb Red(255, 0, 0); -static constexpr Rgb FireBrick(178, 34, 34); -static constexpr Rgb DarkRed(139, 0, 0); - -// Pink HTML Color Names -static constexpr Rgb Pink(255, 192, 203); -static constexpr Rgb LightPink(255, 182, 193); -static constexpr Rgb HotPink(255, 105, 180); -static constexpr Rgb DeepPink(255, 20, 147); -static constexpr Rgb MediumVioletRed(199, 21, 133); -static constexpr Rgb PaleVioletRed(219, 112, 147); - -// Orange HTML Color Names -static constexpr Rgb Coral(255, 127, 80); -static constexpr Rgb Tomato(255, 99, 71); -static constexpr Rgb OrangeRed(255, 69, 0); -static constexpr Rgb DarkOrange(255, 140, 0); -static constexpr Rgb Orange(25, 165, 0); - -// Yellow HTML Color Names -static constexpr Rgb Gold(255, 215, 0); -static constexpr Rgb Yellow(255, 255, 0); -static constexpr Rgb LightYellow(255, 255, 224); -static constexpr Rgb LemonChiffon(255, 250, 205); -static constexpr Rgb LightGoldenrodYellow(250, 250, 210); -static constexpr Rgb PapayaWhip(255, 239, 213); -static constexpr Rgb Moccasin(255, 228, 181); -static constexpr Rgb PeachPuff(255, 218, 185); -static constexpr Rgb PaleGoldenrod(238, 232, 170); -static constexpr Rgb Khaki(240, 230, 140); -static constexpr Rgb DarkKhaki(189, 183, 107); - -// Purple HTML Color Names -static constexpr Rgb Lavender(230, 230, 250); -static constexpr Rgb Thistle(216, 191, 216); -static constexpr Rgb Plum(221, 160, 221); -static constexpr Rgb Violet(238, 130, 238); -static constexpr Rgb Orchid(218, 112, 214); -static constexpr Rgb Fuchsia(255, 0, 255); -static constexpr Rgb Magenta(255, 0, 255); -static constexpr Rgb MediumOrchid(186, 85, 211); -static constexpr Rgb MediumPurple(147, 112, 219); -static constexpr Rgb RebeccaPurple(102, 51, 153); -static constexpr Rgb BlueViolet(138, 43, 226); -static constexpr Rgb DarkViolet(148, 0, 211); -static constexpr Rgb DarkOrchid(153, 50, 204); -static constexpr Rgb DarkMagenta(139, 0, 139); -static constexpr Rgb Purple(128, 0, 128); -static constexpr Rgb Indigo(75, 0, 130); -static constexpr Rgb SlateBlue(106, 90, 205); -static constexpr Rgb DarkSlateBlue(72, 61, 139); - -// Green HTML Color Names -static constexpr Rgb GreenYellow(173, 255, 47); -static constexpr Rgb Chartreuse(127, 255, 0); -static constexpr Rgb LawnGreen(124, 252, 0); -static constexpr Rgb Lime(0, 255, 0); -static constexpr Rgb LimeGreen(50, 205, 50); -static constexpr Rgb PaleGreen(152, 251, 152); -static constexpr Rgb LightGreen(144, 238, 144); -static constexpr Rgb MediumSpringGreen(0, 250, 154); -static constexpr Rgb SpringGreen(0, 255, 127); -static constexpr Rgb MediumSeaGreen(60, 179, 113); -static constexpr Rgb SeaGreen(46, 139, 87); -static constexpr Rgb ForestGreen(34, 139, 34); -static constexpr Rgb Green(0, 128, 0); -static constexpr Rgb DarkGreen(0, 100, 0); -static constexpr Rgb YellowGreen(154, 205, 50); -static constexpr Rgb OliveDrab(107, 142, 35); -static constexpr Rgb Olive(128, 128, 0); -static constexpr Rgb DarkOliveGreen(85, 107, 47); -static constexpr Rgb MediumAquamarine(102, 205, 170); -static constexpr Rgb DarkSeaGreen(143, 188, 139); -static constexpr Rgb LightSeaGreen(32, 178, 170); -static constexpr Rgb DarkCyan(0, 139, 139); -static constexpr Rgb Teal(0, 128, 128); - -// Blue HTML Color Names -static constexpr Rgb Aqua(0, 255, 255); -static constexpr Rgb Cyan(0, 255, 255); -static constexpr Rgb LightCyan(224, 255, 255); -static constexpr Rgb PaleTurquoise(175, 238, 238); -static constexpr Rgb Aquamarine(127, 255, 212); -static constexpr Rgb Turquoise(64, 224, 208); -static constexpr Rgb MediumTurquoise(72, 209, 204); -static constexpr Rgb DarkTurquoise(0, 206, 209); -static constexpr Rgb CadetBlue(95, 158, 160); -static constexpr Rgb SteelBlue(70, 130, 180); -static constexpr Rgb LightSteelBlue(176, 196, 222); -static constexpr Rgb PowderBlue(176, 224, 230); -static constexpr Rgb LightBlue(173, 216, 230); -static constexpr Rgb SkyBlue(135, 206, 235); -static constexpr Rgb LightSkyBlue(135, 206, 250); -static constexpr Rgb DeepSkyBlue(0, 191, 255); -static constexpr Rgb DodgerBlue(30, 144, 255); -static constexpr Rgb CornflowerBlue(100, 149, 237); -static constexpr Rgb MediumSlateBlue(123, 104, 238); -static constexpr Rgb RoyalBlue(65, 105, 225); -static constexpr Rgb Blue(0, 0, 255); -static constexpr Rgb MediumBlue(0, 0, 205); -static constexpr Rgb DarkBlue(0, 0, 139); -static constexpr Rgb Navy(0, 0, 128); -static constexpr Rgb MidnightBlue(25, 25, 112); - -// Brown HTML Color Names -static constexpr Rgb Cornsilk(255, 248, 220); -static constexpr Rgb BlanchedAlmond(255, 235, 205); -static constexpr Rgb Bisque(255, 228, 196); -static constexpr Rgb NavajoWhite(255, 222, 173); -static constexpr Rgb Wheat(245, 222, 179); -static constexpr Rgb BurlyWood(222, 184, 135); -static constexpr Rgb Tan(210, 180, 140); -static constexpr Rgb RosyBrown(188, 143, 143); -static constexpr Rgb SandyBrown(244, 164, 96); -static constexpr Rgb Goldenrod(218, 165, 32); -static constexpr Rgb DarkGoldenrod(184, 134, 11); -static constexpr Rgb Peru(205, 133, 63); -static constexpr Rgb Chocolate(210, 105, 30); -static constexpr Rgb SaddleBrown(139, 69, 19); -static constexpr Rgb Sienna(160, 82, 45); -static constexpr Rgb Brown(165, 42, 42); -static constexpr Rgb Maroon(128, 0, 0); - -// White HTML Color Names -static constexpr Rgb White(255, 255, 255); -static constexpr Rgb Snow(255, 250, 250); -static constexpr Rgb HoneyDew(240, 255, 240); -static constexpr Rgb MintCream(245, 255, 250); -static constexpr Rgb Azure(240, 255, 255); -static constexpr Rgb AliceBlue(240, 248, 255); -static constexpr Rgb GhostWhite(248, 248, 255); -static constexpr Rgb WhiteSmoke(245, 245, 245); -static constexpr Rgb SeaShell(255, 245, 238); -static constexpr Rgb Beige(245, 245, 220); -static constexpr Rgb OldLace(253, 245, 230); -static constexpr Rgb FloralWhite(255, 250, 230); -static constexpr Rgb Ivory(255, 255, 240); -static constexpr Rgb AntiqueWhite(250, 235, 215); -static constexpr Rgb Linen(250, 240, 230); -static constexpr Rgb LavenderBlush(255, 240, 245); -static constexpr Rgb MistyRose(255, 228, 225); - -// Gray HTML Color Names -static constexpr Rgb Gainsboro(220, 220, 220); -static constexpr Rgb LightGray(211, 211, 211); -static constexpr Rgb Silver(192, 192, 192); -static constexpr Rgb DarkGray(169, 169, 169); -static constexpr Rgb Gray(128, 128, 128); -static constexpr Rgb DimGray(105, 105, 105); -static constexpr Rgb LightSlateGray(119, 136, 153); -static constexpr Rgb SlateGray(112, 128, 144); -static constexpr Rgb DarkSlateGray(47, 79, 79); -static constexpr Rgb Black(0, 0, 0); - -/// @} - -} // namespace modm::color::html diff --git a/src/modm/ui/color_impl.hpp b/src/modm/ui/color_impl.hpp deleted file mode 100644 index 5cf8aa4439..0000000000 --- a/src/modm/ui/color_impl.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2013-2015, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_COLOR_HPP -# error "Don't include this file directly, use 'color.hpp' instead!" -#endif - -#include -#include - -/** - * @see http://de.wikipedia.org/wiki/HSV-Farbraum#Umrechnung_RGB_in_HSV.2FHSL - * @param color - */ -template template -inline void -modm::color::RgbT::toHsv(HsvT* color) const -{ - typedef float CalcType; - const CalcType maxValue = std::numeric_limits::max(); - const CalcType _red = static_cast(red) / maxValue; - const CalcType _blue = static_cast(blue) / maxValue; - const CalcType _green = static_cast(green) / maxValue; - const CalcType _max = std::max(_red, std::max(_green, _blue)); - const CalcType _min = std::min(_red, std::min(_green, _blue)); - const CalcType _diff = _max - _min; - - CalcType hue_temp; - - // CALCULATE HUE - if(_max == _min) { // all three color values are the same - hue_temp = 0; - color->value = _max * maxValue; - } - else if(_max == _red) { - hue_temp = 60 * (0 + (_green - _blue) / _diff ); - color->value = red; - } - else if(_max == _green) { - hue_temp = 60 * (2 + (_blue - _red) / _diff ); - color->value = green; - } - else /*if(_max == _blue)*/ { - hue_temp = 60 * (4 + (_red - _green) / _diff ); - color->value = blue; - } - - if(hue_temp < 0) { - color->hue = (hue_temp + 360 ) * (maxValue / 360); - } - else { - color->hue = (hue_temp ) * (maxValue / 360); - } - - // CALCULATE SATURATION - if(_max == 0) { - color->saturation = 0; - } else { - color->saturation = _diff / _max * maxValue; - } -} - -template -modm::IOStream& -modm::color::operator << ( modm::IOStream& os, const modm::color::RgbT& color) -{ - os << color.red << "\t" << color.green << "\t" << color.blue; - return os; -} - -template -modm::IOStream& -modm::color::operator << ( modm::IOStream& os, const modm::color::HsvT& color) -{ - os << color.hue << "\t" << color.saturation << "\t" << color.value; - return os; -} \ No newline at end of file diff --git a/src/modm/ui/display/virtual_graphic_display.cpp b/src/modm/ui/display/virtual_graphic_display.cpp index 936629c168..05915d14d2 100644 --- a/src/modm/ui/display/virtual_graphic_display.cpp +++ b/src/modm/ui/display/virtual_graphic_display.cpp @@ -31,9 +31,9 @@ void modm::VirtualGraphicDisplay::clear() { //TODO switch black , white - this->display->setColor(color::Rgb(0, 0, 0)); + this->display->setColor(color::Rgb888(0, 0, 0)); this->display->fillRectangle(this->leftUpper, width, height); - this->display->setColor(color::Rgb(255, 255, 255)); + this->display->setColor(color::Rgb888(255, 255, 255)); } void diff --git a/src/modm/ui/gui/colorpalette.hpp b/src/modm/ui/gui/colorpalette.hpp index a276fffbcf..b49e60e881 100644 --- a/src/modm/ui/gui/colorpalette.hpp +++ b/src/modm/ui/gui/colorpalette.hpp @@ -86,7 +86,7 @@ class ColorPalette getColor(Color name) const { if (name >= Color::PALETTE_SIZE) - return modm::color::Rgb565(0xffff); + return modm::color::html::White; return colors[name]; } diff --git a/src/modm/ui/led/module.md b/src/modm/ui/led/module.md index 02efb86ade..7e00b9d646 100644 --- a/src/modm/ui/led/module.md +++ b/src/modm/ui/led/module.md @@ -188,7 +188,7 @@ modm::ui::Led leds[3] = // Group them together as one RGB LED modm::ui::RgbLed rgb(leds[1], leds[0], leds[2]); // animate to orange within 2 seconds -rgb.fadeTo(modm::ui::Rgb(95, 177, 147), 2000); +rgb.fadeTo(modm::ui::Rgb888(95, 177, 147), 2000); ``` diff --git a/src/modm/ui/led/rgb.hpp b/src/modm/ui/led/rgb.hpp index 4386e283b2..b18ccc9f53 100644 --- a/src/modm/ui/led/rgb.hpp +++ b/src/modm/ui/led/rgb.hpp @@ -35,26 +35,26 @@ class RgbLed Led& green; Led& blue; - ::modm::color::Rgb absolute; + ::modm::color::Rgb888 absolute; public: RgbLed(Led& red, Led& green, Led& blue): red(red), green(green), blue(blue) {} inline void - setColor(::modm::color::Rgb color) + setColor(::modm::color::Rgb888 color) { absolute = color; - red.setBrightness(color.red); - green.setBrightness(color.green); - blue.setBrightness(color.blue); + red.setBrightness(color.red().value()); + green.setBrightness(color.green().value()); + blue.setBrightness(color.blue().value()); } - inline ::modm::color::Rgb + inline ::modm::color::Rgb888 getColor() { - return ::modm::color::Rgb( + return ::modm::color::Rgb888( red.getBrightness(), green.getBrightness(), blue.getBrightness()); } @@ -65,13 +65,13 @@ class RgbLed } inline void - fadeTo(::modm::color::Rgb color, uint16_t time) + fadeTo(::modm::color::Rgb888 color, uint16_t time) { absolute = color; - red.fadeTo(absolute.red, time); - green.fadeTo(absolute.green, time); - blue.fadeTo(absolute.blue, time); + red.fadeTo(absolute.red().value(), time); + green.fadeTo(absolute.green().value(), time); + blue.fadeTo(absolute.blue().value(), time); } /// should be called every 1ms or more. diff --git a/test/modm/math/saturation/saturation_test.cpp b/test/modm/math/saturation/saturation_test.cpp deleted file mode 100644 index 60d559a454..0000000000 --- a/test/modm/math/saturation/saturation_test.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2009, Martin Rosekeit - * Copyright (c) 2009-2010, Fabian Greif - * Copyright (c) 2012, Niklas Hauser - * Copyright (c) 2021, Thomas Sommer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include - -#include "saturation_test.hpp" -#include - -void -SaturationTest::testSigned8bit() -{ - modm::Saturated x; - modm::Saturated y(100); - - TEST_ASSERT_EQUALS(x.getValue(), 0); - TEST_ASSERT_EQUALS(y.getValue(), 100); - - x = 110; - - TEST_ASSERT_EQUALS(x.getValue(), 110); - - x += y; - - TEST_ASSERT_EQUALS(x.getValue(), 127); - - x = -100; - y = 50; - x -= y; - - TEST_ASSERT_EQUALS(x.getValue(), -128); - - modm::Saturated z; - - x = 20; - y = 10; - - z = x + y; - - TEST_ASSERT_EQUALS(x.getValue(), 20); - TEST_ASSERT_EQUALS(y.getValue(), 10); - TEST_ASSERT_EQUALS(z.getValue(), 30); - - z = x - y; - - TEST_ASSERT_EQUALS(x.getValue(), 20); - TEST_ASSERT_EQUALS(y.getValue(), 10); - TEST_ASSERT_EQUALS(z.getValue(), 10); - - y = z - x; - - TEST_ASSERT_EQUALS(x.getValue(), 20); - TEST_ASSERT_EQUALS(y.getValue(), 0); - TEST_ASSERT_EQUALS(z.getValue(), 10); - - x = -z; - - TEST_ASSERT_EQUALS(x.getValue(), -10); - - y = -100; - x = abs(y); - TEST_ASSERT_EQUALS(x.getValue(), 100); - - x = y; - x.absolute(); - TEST_ASSERT_EQUALS(x.getValue(), 100); -} - -void -SaturationTest::testUnsigned8bit() -{ - modm::Saturated x; - modm::Saturated y(100); - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - TEST_ASSERT_EQUALS(y.getValue(), 100U); - - x = 200; - - TEST_ASSERT_EQUALS(x.getValue(), 200U); - - x += y; - - TEST_ASSERT_EQUALS(x.getValue(), 255U); - - x = 10; - y = 20; - x -= y; - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - - x = 100; - y = 3; - x *= y; - - TEST_ASSERT_EQUALS(x.getValue(), 255U); - - modm::Saturated z; - - x = 20; - y = 10; - - z = x + y; - - TEST_ASSERT_EQUALS(x.getValue(), 20U); - TEST_ASSERT_EQUALS(y.getValue(), 10U); - TEST_ASSERT_EQUALS(z.getValue(), 30U); - - z = x - y; - - TEST_ASSERT_EQUALS(x.getValue(), 20U); - TEST_ASSERT_EQUALS(y.getValue(), 10U); - TEST_ASSERT_EQUALS(z.getValue(), 10U); - - y = z - x; - - TEST_ASSERT_EQUALS(x.getValue(), 20U); - TEST_ASSERT_EQUALS(y.getValue(), 0U); - TEST_ASSERT_EQUALS(z.getValue(), 10U); - - x = -z; - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - - y = 200; - x = abs(y); - TEST_ASSERT_EQUALS(x.getValue(), 200U); - - x = y; - x.absolute(); - TEST_ASSERT_EQUALS(x.getValue(), 200U); -} - -void -SaturationTest::testSigned16bit() -{ - modm::Saturated x; - modm::Saturated y(30000); - - TEST_ASSERT_EQUALS(x.getValue(), 0); - TEST_ASSERT_EQUALS(y.getValue(), 30000); - - x = 20000; - - TEST_ASSERT_EQUALS(x.getValue(), 20000); - - x += y; - - TEST_ASSERT_EQUALS(x.getValue(), 32767); - - x = 1000; - y = 2000; - x -= y; - - TEST_ASSERT_EQUALS(x.getValue(), -1000); - - modm::Saturated z; - - x = 10000; - y = 20000; - - z = x + y; - - TEST_ASSERT_EQUALS(x.getValue(), 10000); - TEST_ASSERT_EQUALS(y.getValue(), 20000); - TEST_ASSERT_EQUALS(z.getValue(), 30000); - - z = x - y; - - TEST_ASSERT_EQUALS(x.getValue(), 10000); - TEST_ASSERT_EQUALS(y.getValue(), 20000); - TEST_ASSERT_EQUALS(z.getValue(), -10000); - - y = z - x; - - // ??? - TEST_ASSERT_EQUALS(x.getValue(), 10000); - TEST_ASSERT_EQUALS(y.getValue(), -20000); - TEST_ASSERT_EQUALS(z.getValue(), -10000); - - x = -z; - - TEST_ASSERT_EQUALS(x.getValue(), 10000); - - y = -20000; - x = abs(y); - TEST_ASSERT_EQUALS(x.getValue(), 20000); - - x = y; - x.absolute(); - TEST_ASSERT_EQUALS(x.getValue(), 20000); -} - -void -SaturationTest::testUnsigned16bit() -{ - modm::Saturated x; - modm::Saturated y(30000); - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - TEST_ASSERT_EQUALS(y.getValue(), 30000U); - - x = 40000; - - TEST_ASSERT_EQUALS(x.getValue(), 40000U); - - x += y; - - TEST_ASSERT_EQUALS(x.getValue(), 65535U); - - x = 1000; - y = 2000; - x -= y; - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - - x = 20000; - x *= 5; - - TEST_ASSERT_EQUALS(x.getValue(), 65535U); - - modm::Saturated z; - - x = 20000; - y = 10000; - - z = x + y; - - TEST_ASSERT_EQUALS(x.getValue(), 20000U); - TEST_ASSERT_EQUALS(y.getValue(), 10000U); - TEST_ASSERT_EQUALS(z.getValue(), 30000U); - - z = x - y; - - TEST_ASSERT_EQUALS(x.getValue(), 20000U); - TEST_ASSERT_EQUALS(y.getValue(), 10000U); - TEST_ASSERT_EQUALS(z.getValue(), 10000U); - - y = z - x; - - TEST_ASSERT_EQUALS(x.getValue(), 20000U); - TEST_ASSERT_EQUALS(y.getValue(), 0U); - TEST_ASSERT_EQUALS(z.getValue(), 10000U); - - x = -z; - - TEST_ASSERT_EQUALS(x.getValue(), 0U); - - y = 20000; - x = abs(y); - TEST_ASSERT_EQUALS(x.getValue(), 20000U); - - x = y; - x.absolute(); - TEST_ASSERT_EQUALS(x.getValue(), 20000U); -} \ No newline at end of file diff --git a/test/modm/math/saturation_test.cpp b/test/modm/math/saturation_test.cpp new file mode 100644 index 0000000000..d90a53bb10 --- /dev/null +++ b/test/modm/math/saturation_test.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2009, Martin Rosekeit + * Copyright (c) 2009-2010, Fabian Greif + * Copyright (c) 2012, Niklas Hauser + * Copyright (c) 2022, Thomas Sommer + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +#include "saturation_test.hpp" +#include + +void +SaturationTest::testSaturated_int8_t() +{ + modm::Saturated x; + TEST_ASSERT_EQUALS(x, 0); + + modm::Saturated y(100); + TEST_ASSERT_EQUALS(y, 100); + + x = 110; + + TEST_ASSERT_EQUALS(x, 110); + + x += y; + + TEST_ASSERT_EQUALS(x, 127); + + x = -100; + y = 50; + x -= y; + + TEST_ASSERT_EQUALS(x, -128); + + modm::Saturated z; + + x = 20; + y = 10; + + z = x + y; + + TEST_ASSERT_EQUALS(x, 20); + TEST_ASSERT_EQUALS(y, 10); + TEST_ASSERT_EQUALS(z, 30); + + z = x - y; + + TEST_ASSERT_EQUALS(x, 20); + TEST_ASSERT_EQUALS(y, 10); + TEST_ASSERT_EQUALS(z, 10); + + y = z - x; + + TEST_ASSERT_EQUALS(x, 20); + TEST_ASSERT_EQUALS(y, 0); + TEST_ASSERT_EQUALS(z, 10); + + x = -z; + + TEST_ASSERT_EQUALS(x, -10); + + y = -100; + x = abs(y); + TEST_ASSERT_EQUALS(x, 100); + + x = y; + x.absolute(); + TEST_ASSERT_EQUALS(x, 100); +} + +void +SaturationTest::testSaturated_uint8_t() +{ + modm::Saturated x; + TEST_ASSERT_EQUALS(x, 0U); + + modm::Saturated y(100); + TEST_ASSERT_EQUALS(y, 100U); + + x = 200; + + TEST_ASSERT_EQUALS(x, 200U); + + x += y; + + TEST_ASSERT_EQUALS(x, 255U); + + x = 10; + y = 20; + x -= y; + + TEST_ASSERT_EQUALS(x, 0U); + + x = 100; + y = 3; + x *= y; + + TEST_ASSERT_EQUALS(x, 255U); + + modm::Saturated z; + + x = 20; + y = 10; + + z = x + y; + + TEST_ASSERT_EQUALS(x, 20U); + TEST_ASSERT_EQUALS(y, 10U); + TEST_ASSERT_EQUALS(z, 30U); + + z = x - y; + + TEST_ASSERT_EQUALS(x, 20U); + TEST_ASSERT_EQUALS(y, 10U); + TEST_ASSERT_EQUALS(z, 10U); + + y = z - x; + + TEST_ASSERT_EQUALS(x, 20U); + TEST_ASSERT_EQUALS(y, 0U); + TEST_ASSERT_EQUALS(z, 10U); + + x = -z; + + TEST_ASSERT_EQUALS(x, 0U); + + y = 200; + x = abs(y); + TEST_ASSERT_EQUALS(x, 200U); + + x = y; + x.absolute(); + TEST_ASSERT_EQUALS(x, 200U); +} + +void +SaturationTest::testSaturated_int16_t() +{ + modm::Saturated x; + TEST_ASSERT_EQUALS(x, 0); + + modm::Saturated y(30000); + TEST_ASSERT_EQUALS(y, 30000); + + x = 20000; + TEST_ASSERT_EQUALS(x, 20000); + + x += y; + TEST_ASSERT_EQUALS(x, 32767); + + x = 1000; + y = 2000; + x -= y; + TEST_ASSERT_EQUALS(x, -1000); + + modm::Saturated z; + + x = 10000; + y = 20000; + z = x + y; + TEST_ASSERT_EQUALS(x, 10000); + TEST_ASSERT_EQUALS(y, 20000); + TEST_ASSERT_EQUALS(z, 30000); + + z = x - y; + TEST_ASSERT_EQUALS(x, 10000); + TEST_ASSERT_EQUALS(y, 20000); + TEST_ASSERT_EQUALS(z, -10000); + + y = z - x; + TEST_ASSERT_EQUALS(x, 10000); + TEST_ASSERT_EQUALS(y, -20000); + TEST_ASSERT_EQUALS(z, -10000); + + x = -z; + TEST_ASSERT_EQUALS(x, 10000); + + y = -20000; + x = abs(y); + TEST_ASSERT_EQUALS(x, 20000); + + x = y; + x.absolute(); + TEST_ASSERT_EQUALS(x, 20000); +} + +void +SaturationTest::testSaturated_uint16_t() +{ + modm::Saturated x; + TEST_ASSERT_EQUALS(x, 0U); + + modm::Saturated y(30000); + TEST_ASSERT_EQUALS(y, 30000U); + + x = 40000; + TEST_ASSERT_EQUALS(x, 40000U); + + x += y; + TEST_ASSERT_EQUALS(x, 65535U); + + x = 1000; + y = 2000; + x -= y; + TEST_ASSERT_EQUALS(x, 0U); + + x = 20000; + x *= 5; + TEST_ASSERT_EQUALS(x, 65535U); + + modm::Saturated z; + + x = 20000; + y = 10000; + z = x + y; + TEST_ASSERT_EQUALS(x, 20000U); + TEST_ASSERT_EQUALS(y, 10000U); + TEST_ASSERT_EQUALS(z, 30000U); + + z = x - y; + TEST_ASSERT_EQUALS(x, 20000U); + TEST_ASSERT_EQUALS(y, 10000U); + TEST_ASSERT_EQUALS(z, 10000U); + + y = z - x; + TEST_ASSERT_EQUALS(x, 20000U); + TEST_ASSERT_EQUALS(y, 0U); + TEST_ASSERT_EQUALS(z, 10000U); + + x = -z; + TEST_ASSERT_EQUALS(x, 0U); + + y = 20000; + x = abs(y); + TEST_ASSERT_EQUALS(x, 20000U); + + x = y; + x.absolute(); + TEST_ASSERT_EQUALS(x, 20000U); +} + +void +SaturationTest::testSaturated_uint8_t_ref() { + uint8_t x = 100; + + modm::Saturated xRef = x; + TEST_ASSERT_EQUALS(xRef, 100U); + + x += 10; + TEST_ASSERT_EQUALS(xRef, 110U); + + xRef += 200; + TEST_ASSERT_EQUALS(x, 255U); + + xRef = 30000U; + TEST_ASSERT_EQUALS(x, 255U); + + x = 20; + xRef -= 200; + TEST_ASSERT_EQUALS(x, 0U); + + x = 42; + uint8_t y = xRef; + TEST_ASSERT_EQUALS(y, 42U); + + x = 20; + y = xRef - 100; + TEST_ASSERT_EQUALS(y, 0U); + TEST_ASSERT_EQUALS(x, 20U); + + y = xRef + 3000; + TEST_ASSERT_EQUALS(y, 255U); + TEST_ASSERT_EQUALS(x, 20U); +} \ No newline at end of file diff --git a/test/modm/math/saturation/saturation_test.hpp b/test/modm/math/saturation_test.hpp similarity index 77% rename from test/modm/math/saturation/saturation_test.hpp rename to test/modm/math/saturation_test.hpp index 4012c2afea..2ce921ad18 100644 --- a/test/modm/math/saturation/saturation_test.hpp +++ b/test/modm/math/saturation_test.hpp @@ -2,7 +2,7 @@ * Copyright (c) 2009, Martin Rosekeit * Copyright (c) 2009-2010, Fabian Greif * Copyright (c) 2012, Niklas Hauser - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2022, Thomas Sommer * * This file is part of the modm project. * @@ -19,14 +19,17 @@ class SaturationTest : public unittest::TestSuite { public: void - testSigned8bit(); + testSaturated_int8_t(); void - testUnsigned8bit(); + testSaturated_uint8_t(); void - testSigned16bit(); + testSaturated_int16_t(); void - testUnsigned16bit(); + testSaturated_uint16_t(); + + void + testSaturated_uint8_t_ref(); }; diff --git a/test/modm/ui/color/color_test.cpp b/test/modm/ui/color/color_test.cpp index 0864e30282..9af070f909 100644 --- a/test/modm/ui/color/color_test.cpp +++ b/test/modm/ui/color/color_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2022, Thomas Sommer * * This file is part of the modm project. * @@ -15,101 +15,152 @@ #include #include +using namespace modm; using namespace modm::color; -void ColorTest::testRgbCopyConstructors() { - RgbT rgb8(html::Orchid); - RgbT rgb8_b(rgb8); - TEST_ASSERT_EQUALS(rgb8, rgb8_b); +void ColorTest::testGray() { + Gray8 gray8(127); + TEST_ASSERT_EQUALS(gray8.max, 255u); - RgbT rgb16(rgb8); - TEST_ASSERT_EQUALS(uint16_t(rgb8.red) << 8, rgb16.red); - TEST_ASSERT_EQUALS(uint16_t(rgb8.green) << 8, rgb16.green); - TEST_ASSERT_EQUALS(uint16_t(rgb8.blue) << 8, rgb16.blue); + Gray8 gray8_B(gray8); + TEST_ASSERT_EQUALS(gray8, gray8_B); - RgbT rgb8_c(rgb16); - TEST_ASSERT_EQUALS(rgb8, rgb8_c); + Gray4 gray4(7); + TEST_ASSERT_EQUALS(gray4.max, 15u); + + gray4 += 3; + TEST_ASSERT_EQUALS(gray4, 10u); + + gray4 -= 1; + TEST_ASSERT_EQUALS(gray4, 9u); + + gray4 -= 44; // under-saturation + TEST_ASSERT_EQUALS(gray4, 0u); + + gray4 += 66; // over-saturation + TEST_ASSERT_EQUALS(gray4, uint8_t(0b1111)); + + gray4 -= 3; + TEST_ASSERT_EQUALS(gray4, uint8_t(0b1100)); + + // IMPLEMENT -= - + // gray4 -= -2; + + // IMPLEMENT += - + // gray4 += -3; + + gray8 = gray4; // upscaling + TEST_ASSERT_EQUALS(gray8, uint8_t(0b11001100)); + + Gray<13> gray13 = gray4; // further upscaling + TEST_ASSERT_EQUALS(gray13, uint16_t(0b0001100110011000)); // last digit rounds down for odd D + + gray4 = gray13; // downscaling + TEST_ASSERT_EQUALS(gray4, uint8_t(0b00001100)); } -void ColorTest::testHsvCopyConstructors() { - HsvT hsv8(html::Orchid); - HsvT hsv8_b(hsv8); - TEST_ASSERT_EQUALS(hsv8, hsv8_b); +void ColorTest::testRgb() { + Rgb888 rgb888_A(0, 100, 200); + Rgb888 rgb888_B(rgb888_A); + TEST_ASSERT_EQUALS(rgb888_A, rgb888_B); + + Rgb161616 rgb16(rgb888_A); + + Rgb888 rgb888_C(rgb16); + TEST_ASSERT_EQUALS(rgb888_A, rgb888_C); + + Rgb888 rgb888_d(1, 2, 3); - HsvT hsv16(hsv8); - TEST_ASSERT_EQUALS(uint16_t(hsv8.hue) << 8, hsv16.hue); - TEST_ASSERT_EQUALS(uint16_t(hsv8.saturation) << 8, hsv16.saturation); - TEST_ASSERT_EQUALS(uint16_t(hsv8.value) << 8, hsv16.value); + rgb888_A += rgb888_d; + TEST_ASSERT_EQUALS(rgb888_A, Rgb888(1, 102, 203)); - HsvT hsv8_c(hsv16); - TEST_ASSERT_EQUALS(hsv8, hsv8_c); + Rgb666 rgb666_A(1, 2, 3); } -void ColorTest::testBrightnessCopyConstructors() { - BrightnessT brightness8(127); - BrightnessT brightness8_b(brightness8); - TEST_ASSERT_EQUALS(brightness8.value, brightness8_b.value); +void ColorTest::testHsv() { + Hsv888 hsv888(html::Orchid); + Hsv888 hsv888_B(hsv888); + TEST_ASSERT_EQUALS(hsv888, hsv888_B); - BrightnessT brightness16(brightness8); - TEST_ASSERT_EQUALS(uint16_t(brightness8.value) << 8, brightness16.value); + Hsv161616 hsv161616(hsv888); - BrightnessT brightness8_c(brightness16); - TEST_ASSERT_EQUALS(brightness8.value, brightness8_c.value); + Hsv888 hsv888_C(hsv161616); + TEST_ASSERT_EQUALS(hsv888, hsv888_C); + + // Test wrapping of hue component + Hsv<6, 6, 6> hsv666(60, 0, 0); + + hsv666.hue() += 10; + TEST_ASSERT_EQUALS(hsv666.hue(), 6); + + hsv666.hue() -= 20; + TEST_ASSERT_EQUALS(hsv666.hue(), 50); } void ColorTest::testConvertion_8bit() { - RgbT rgb(124, 128, 10); + Rgb888 rgb(124, 128, 10); - HsvT hsv(rgb); - TEST_ASSERT_EQUALS(hsv.hue, 43); - TEST_ASSERT_EQUALS(hsv.saturation, 235); - TEST_ASSERT_EQUALS(hsv.value, 128); + Hsv888 hsv(rgb); + TEST_ASSERT_EQUALS(hsv.hue(), 43); + TEST_ASSERT_EQUALS(hsv.saturation(), 235); + TEST_ASSERT_EQUALS(hsv.value(), 128); - BrightnessT brightness(rgb); - TEST_ASSERT_EQUALS(brightness.value, 118); + Gray8 gray(rgb); + TEST_ASSERT_EQUALS(gray, 118); } -// TODO 16bit convertion not yet working -// see hsv_impl.hpp and rgb_impl.hpp -// void ColorTest::testConvertion_16bit() -// { -// RgbT rgb8(html::Orchid); -// HsvT hsv8(rgb8); -// HsvT hsv16(hsv8); +void ColorTest::testConvertion_16bit() +{ + Rgb888 rgb888(html::Orchid); + Hsv888 hsv888(rgb888); + Hsv161616 hsv161616(hsv888); -// RgbT rgb16(rgb8); -// HsvT hsv16_b(rgb16); + Rgb161616 rgb16(rgb888); + Hsv161616 hsv161616_B(rgb16); -// // Test, if rgb->hsv conversion produces the same result for 8 and 16bits -// TEST_ASSERT_EQUALS(hsv16, hsv16_b); -// } + // Test, if rgb->hsv conversion produces the same result for 8 and 16bits + // FIXME test fails + // TEST_ASSERT_EQUALS(hsv161616, hsv161616_B); +} void ColorTest::testRgbHsvPingPongConvertion_8bit() { - RgbT rgb8(html::Orchid); - HsvT hsv8(rgb8); - RgbT rgb8_b(hsv8); - - // Convertion can distort - allow some tolerance. - using namespace modm; - TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb8.red, rgb8_b.red, 1_pct)); - TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb8.green, rgb8_b.green, 1_pct)); - TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb8.blue, rgb8_b.blue, 1_pct)); + Rgb888 rgb888(html::Orchid); + Hsv888 hsv888(rgb888); + Rgb888 rgb888_B(hsv888); + + // Convertion may distort - allow some tolerance. + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb888.red(), rgb888_B.red(), 1_pct)); + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb888.green(), rgb888_B.green(), 1_pct)); + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb888.blue(), rgb888_B.blue(), 1_pct)); +} + +void ColorTest::testRgbHsvPingPongConvertion_16bit() +{ + // Rgb->Hsv->Rgb, both 16 bit + Rgb161616 rgb16(html::Orchid); + Hsv161616 hsv16(rgb16); + Rgb161616 rgb16_B(hsv16); + + // Convertion may distort - allow some tolerance. + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb16.red(), rgb16_B.red(), 1_pct)); + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb16.green(), rgb16_B.green(), 1_pct)); + TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb16.blue(), rgb16_B.blue(), 1_pct)); } -// TODO 16bit convertion not yet working -// see hsv_impl.hpp and rgb_impl.hpp -// void ColorTest::testRgbHsvPingPongConvertion_16bit() -// { -// // Rgb->Hsv->Rgb, both 16 bit -// RgbT rgb16(html::Orchid); -// HsvT hsv16(rgb16); -// RgbT rgb16_b(hsv16); - -// // Convertion can distort - allow some tolerance. -// using namespace modm; -// TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb.red, rgb16_b.red, 1_pct)); -// TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb.green, rgb16_b.green, 1_pct)); -// TEST_ASSERT_TRUE(modm::isValueInTolerance(rgb.blue, rgb16_b.blue, 1_pct)); -// } +void ColorTest::testConstevalOperators() { + + Rgb888 rgb_a(10, 20, 30); + Rgb888 rgb_b(40, 50, 60); + + const Rgb888 rgb_c = rgb_a + rgb_b; + TEST_ASSERT_EQUALS(rgb_c.red(), 50); + TEST_ASSERT_EQUALS(rgb_c.green(), 70); + TEST_ASSERT_EQUALS(rgb_c.blue(), 90); + + const Rgb888 rgb_d = rgb_a - rgb_b; + TEST_ASSERT_EQUALS(rgb_d.red(), 0); + TEST_ASSERT_EQUALS(rgb_d.green(), 0); + TEST_ASSERT_EQUALS(rgb_d.blue(), 0); +} diff --git a/test/modm/ui/color/color_test.hpp b/test/modm/ui/color/color_test.hpp index bb9bb1cc0a..ffcedf9ef9 100644 --- a/test/modm/ui/color/color_test.hpp +++ b/test/modm/ui/color/color_test.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Thomas Sommer + * Copyright (c) 2022, Thomas Sommer * * This file is part of the modm project. * @@ -19,29 +19,28 @@ class ColorTest : public unittest::TestSuite { public: void - testRgbCopyConstructors(); + testGray(); void - testHsvCopyConstructors(); + testRgb(); void - testBrightnessCopyConstructors(); + testHsv(); void testConvertion_8bit(); - // TODO 16bit convertion not yet working - // see hsv_impl.hpp and rgb_impl.hpp - // void - // testConvertion_16bit(); + void + testConvertion_16bit(); void testRgbHsvPingPongConvertion_8bit(); - // TODO 16bit convertion not yet working - // see hsv_impl.hpp and rgb_impl.hpp - // void - // testRgbHsvPingPongConvertion_16bit(); + void + testRgbHsvPingPongConvertion_16bit(); + + void + testConstevalOperators(); }; #endif // COLOR_TEST_HPP From eed697940030a0ecb75ba65317fdb3d753d51993 Mon Sep 17 00:00:00 2001 From: Thomas Sommer Date: Mon, 4 Mar 2024 10:10:36 +0100 Subject: [PATCH 2/2] rgb implicit conversion --- src/modm/ui/led/rgb.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modm/ui/led/rgb.hpp b/src/modm/ui/led/rgb.hpp index b18ccc9f53..e4ddd8831c 100644 --- a/src/modm/ui/led/rgb.hpp +++ b/src/modm/ui/led/rgb.hpp @@ -46,9 +46,9 @@ class RgbLed { absolute = color; - red.setBrightness(color.red().value()); - green.setBrightness(color.green().value()); - blue.setBrightness(color.blue().value()); + red.setBrightness(color.red()); + green.setBrightness(color.green()); + blue.setBrightness(color.blue()); } inline ::modm::color::Rgb888