diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 8c9d1ecc..0b8c5d8c 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -42,6 +42,32 @@ /// - 'updateDelay' The delay of the output to leds (in periods of smoothing) "color" : { + "correction" : + [ + { + "id" : "default", + "leds" : "*", + "correctionValues" : + { + "red" : 255, + "green" : 255, + "blue" : 255 + } + } + ], + "temperature" : + [ + { + "id" : "default", + "leds" : "*", + "temperatureValues" : + { + "red" : 255, + "green" : 255, + "blue" : 255 + } + } + ], "transform" : [ { @@ -52,6 +78,11 @@ "saturationGain" : 1.0000, "valueGain" : 1.0000 }, + "hsl" : + { + "saturationGain" : 1.0000, + "luminanceGain" : 1.0000 + }, "red" : { "threshold" : 0.0000, diff --git a/effects/knight-rider.json b/effects/knight-rider.json index 9801d491..b4644387 100644 --- a/effects/knight-rider.json +++ b/effects/knight-rider.json @@ -1,10 +1,10 @@ -{ - "name" : "Knight rider", - "script" : "knight-rider.py", - "args" : - { - "speed" : 1.0, - "fadeFactor" : 0.7, - "color" : [255,0,0] - } -} +{ + "name" : "Knight rider", + "script" : "knight-rider.py", + "args" : + { + "speed" : 1.0, + "fadeFactor" : 0.7, + "color" : [255,0,0] + } +} diff --git a/effects/mood-blobs-blue.json b/effects/mood-blobs-blue.json index 32280b46..1aa188ab 100644 --- a/effects/mood-blobs-blue.json +++ b/effects/mood-blobs-blue.json @@ -1,12 +1,12 @@ -{ - "name" : "Blue mood blobs", - "script" : "mood-blobs.py", - "args" : - { - "rotationTime" : 60.0, - "color" : [0,0,255], - "hueChange" : 60.0, - "blobs" : 5, - "reverse" : false - } -} +{ + "name" : "Blue mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,0,255], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/mood-blobs-green.json b/effects/mood-blobs-green.json index aabd536c..c0c104fe 100644 --- a/effects/mood-blobs-green.json +++ b/effects/mood-blobs-green.json @@ -1,12 +1,12 @@ -{ - "name" : "Green mood blobs", - "script" : "mood-blobs.py", - "args" : - { - "rotationTime" : 60.0, - "color" : [0,255,0], - "hueChange" : 60.0, - "blobs" : 5, - "reverse" : false - } -} +{ + "name" : "Green mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,255,0], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/mood-blobs-red.json b/effects/mood-blobs-red.json index ac47af53..3272dded 100644 --- a/effects/mood-blobs-red.json +++ b/effects/mood-blobs-red.json @@ -1,12 +1,12 @@ -{ - "name" : "Red mood blobs", - "script" : "mood-blobs.py", - "args" : - { - "rotationTime" : 60.0, - "color" : [255,0,0], - "hueChange" : 60.0, - "blobs" : 5, - "reverse" : false - } -} +{ + "name" : "Red mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [255,0,0], + "hueChange" : 60.0, + "blobs" : 5, + "reverse" : false + } +} diff --git a/effects/rainbow-mood.json b/effects/rainbow-mood.json index c9208b60..fe754287 100644 --- a/effects/rainbow-mood.json +++ b/effects/rainbow-mood.json @@ -1,10 +1,10 @@ -{ - "name" : "Rainbow mood", - "script" : "rainbow-mood.py", - "args" : - { - "rotation-time" : 60.0, - "brightness" : 1.0, - "reverse" : false - } -} +{ + "name" : "Rainbow mood", + "script" : "rainbow-mood.py", + "args" : + { + "rotation-time" : 60.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/effects/rainbow-swirl-fast.json b/effects/rainbow-swirl-fast.json index 88e8d79d..19fec89c 100644 --- a/effects/rainbow-swirl-fast.json +++ b/effects/rainbow-swirl-fast.json @@ -1,10 +1,10 @@ -{ - "name" : "Rainbow swirl fast", - "script" : "rainbow-swirl.py", - "args" : - { - "rotation-time" : 3.0, - "brightness" : 1.0, - "reverse" : false - } -} +{ + "name" : "Rainbow swirl fast", + "script" : "rainbow-swirl.py", + "args" : + { + "rotation-time" : 3.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/effects/rainbow-swirl.json b/effects/rainbow-swirl.json index 43a80a8d..3f7b7243 100644 --- a/effects/rainbow-swirl.json +++ b/effects/rainbow-swirl.json @@ -1,10 +1,10 @@ -{ - "name" : "Rainbow swirl", - "script" : "rainbow-swirl.py", - "args" : - { - "rotation-time" : 20.0, - "brightness" : 1.0, - "reverse" : false - } -} +{ + "name" : "Rainbow swirl", + "script" : "rainbow-swirl.py", + "args" : + { + "rotation-time" : 20.0, + "brightness" : 1.0, + "reverse" : false + } +} diff --git a/effects/snake.json b/effects/snake.json index d5a7674f..18f2b17c 100644 --- a/effects/snake.json +++ b/effects/snake.json @@ -1,10 +1,10 @@ -{ - "name" : "Snake", - "script" : "snake.py", - "args" : - { - "rotation-time" : 12.0, - "color" : [255, 0, 0], - "percentage" : 10 - } -} +{ + "name" : "Snake", + "script" : "snake.py", + "args" : + { + "rotation-time" : 12.0, + "color" : [255, 0, 0], + "percentage" : 10 + } +} diff --git a/effects/udp.json b/effects/udp.json index defd4f89..29df0971 100644 --- a/effects/udp.json +++ b/effects/udp.json @@ -1,8 +1,8 @@ -{ - "name" : "UDP listener", - "script" : "udp.py", - "args" : - { - "udpPort" : 2391 - } -} +{ + "name" : "UDP listener", + "script" : "udp.py", + "args" : + { + "udpPort" : 2391 + } +} diff --git a/include/hyperion/ColorCorrection.h b/include/hyperion/ColorCorrection.h new file mode 100644 index 00000000..51ae0605 --- /dev/null +++ b/include/hyperion/ColorCorrection.h @@ -0,0 +1,18 @@ +#pragma once + +// STL includes +#include + +// Utils includes +#include + +class ColorCorrection +{ +public: + + /// Unique identifier for this color correction + std::string _id; + + /// The RGB correction + RgbChannelCorrection _rgbCorrection; +}; diff --git a/include/hyperion/ColorTransform.h b/include/hyperion/ColorTransform.h index 50cbedba..998d6a60 100644 --- a/include/hyperion/ColorTransform.h +++ b/include/hyperion/ColorTransform.h @@ -6,6 +6,7 @@ // Utils includes #include #include +#include class ColorTransform { @@ -23,4 +24,7 @@ public: /// The HSV Transform for applying Saturation and Value transforms HsvTransform _hsvTransform; + + /// The HSL Transform for applying Saturation and Value transforms + HslTransform _hslTransform; }; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 811f0199..88c480d3 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -13,6 +13,8 @@ // Hyperion includes #include #include +#include +#include #include // Effect engine includes @@ -23,8 +25,12 @@ class LedDevice; class ColorTransform; class EffectEngine; class HsvTransform; +class HslTransform; class RgbChannelTransform; +class RgbChannelCorrection; class MultiColorTransform; +class MultiColorCorrection; +class MultiColorTemperature; /// /// The main class of Hyperion. This gives other 'users' access to the attached LedDevice through @@ -116,19 +122,51 @@ public slots: /// @return The list with transform identifiers /// const std::vector & getTransformIds() const; - + + /// + /// Returns the list with unique correction identifiers + /// @return The list with correction identifiers + /// + const std::vector & getCorrectionIds() const; + + /// + /// Returns the list with unique correction identifiers + /// @return The list with correction identifiers + /// + const std::vector & getTemperatureIds() const; + /// /// Returns the ColorTransform with the given identifier /// @return The transform with the given identifier (or nullptr if the identifier does not exist) /// ColorTransform * getTransform(const std::string& id); + + /// + /// Returns the ColorCorrection with the given identifier + /// @return The correction with the given identifier (or nullptr if the identifier does not exist) + /// + ColorCorrection * getCorrection(const std::string& id); + + /// + /// Returns the ColorCorrection with the given identifier + /// @return The correction with the given identifier (or nullptr if the identifier does not exist) + /// + ColorCorrection * getTemperature(const std::string& id); + + /// + /// Returns MessageForwarder Object + /// @return instance of message forwarder object + /// + MessageForwarder * getForwarder(); /// Tell Hyperion that the transforms have changed and the leds need to be updated void transformsUpdated(); - - /// Returns MessageForwarder Object - /// @return instance of message forwarder object - MessageForwarder * getForwarder(); + + /// Tell Hyperion that the corrections have changed and the leds need to be updated + void correctionsUpdated(); + + /// Tell Hyperion that the corrections have changed and the leds need to be updated + void temperaturesUpdated(); /// /// Clears the given priority channel. This will switch the led-colors to the colors of the next @@ -168,9 +206,14 @@ public: static LedString createLedString(const Json::Value & ledsConfig, const ColorOrder deviceOrder); static MultiColorTransform * createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorTransformConfig); + static MultiColorCorrection * createLedColorsCorrection(const unsigned ledCnt, const Json::Value & colorCorrectionConfig); + static MultiColorCorrection * createLedColorsTemperature(const unsigned ledCnt, const Json::Value & colorTemperatureConfig); static ColorTransform * createColorTransform(const Json::Value & transformConfig); + static ColorCorrection * createColorCorrection(const Json::Value & correctionConfig); static HsvTransform * createHsvTransform(const Json::Value & hsvConfig); + static HslTransform * createHslTransform(const Json::Value & hslConfig); static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig); + static RgbChannelCorrection * createRgbChannelCorrection(const Json::Value& colorConfig); static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig); @@ -198,15 +241,21 @@ private: /// The priority muxer PriorityMuxer _muxer; - /// The transformation from raw colors to led colors + /// The transformation from corrected colors to led colors MultiColorTransform * _raw2ledTransform; - + + /// The correction from raw colors to led colors + MultiColorCorrection * _raw2ledCorrection; + + /// The temperature from corrected colors to led colors + MultiColorCorrection * _raw2ledTemperature; + /// The actual LedDevice LedDevice * _device; /// Effect engine EffectEngine * _effectEngine; - + // proto and json Message forwarder MessageForwarder * _messageForwarder; diff --git a/include/utils/HslTransform.h b/include/utils/HslTransform.h new file mode 100644 index 00000000..f2151e65 --- /dev/null +++ b/include/utils/HslTransform.h @@ -0,0 +1,100 @@ +#pragma once + +// STL includes +#include + +/// +/// Color transformation to adjust the saturation and luminance of a RGB color value +/// +class HslTransform +{ +public: + /// + /// Default constructor + /// + HslTransform(); + + /// + /// Constructor + /// + /// @param saturationGain The used saturation gain + /// @param luminanceGain The used luminance gain + /// + HslTransform(double saturationGain, double luminanceGain); + + /// + /// Destructor + /// + ~HslTransform(); + + /// + /// Updates the saturation gain + /// + /// @param saturationGain New saturationGain + /// + void setSaturationGain(double saturationGain); + + /// + /// Returns the saturation gain + /// + /// @return The current Saturation gain + /// + double getSaturationGain() const; + + /// + /// Updates the luminance gain + /// + /// @param luminanceGain New luminance gain + /// + void setLuminanceGain(double luminanceGain); + + /// + /// Returns the luminance gain + /// + /// @return The current luminance gain + /// + double getLuminanceGain() const; + + /// + /// Apply the transform the the given RGB values. + /// + /// @param red The red color component + /// @param green The green color component + /// @param blue The blue color component + /// + /// @note The values are updated in place. + /// + void transform(uint8_t & red, uint8_t & green, uint8_t & blue) const; + + /// + /// Translates an RGB (red, green, blue) color to an HSL (hue, saturation, luminance) color + /// + /// @param[in] red The red RGB-component + /// @param[in] green The green RGB-component + /// @param[in] blue The blue RGB-component + /// @param[out] hue The hue HSL-component + /// @param[out] saturation The saturation HSL-component + /// @param[out] luminance The luminance HSL-component + /// + + static void rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, float & saturation, float & luminance); + + /// + /// Translates an HSL (hue, saturation, luminance) color to an RGB (red, green, blue) color + /// + /// @param[in] hue The hue HSL-component + /// @param[in] saturation The saturation HSL-component + /// @param[in] luminance The luminance HSL-component + /// @param[out] red The red RGB-component + /// @param[out] green The green RGB-component + /// @param[out] blue The blue RGB-component + /// + + static void hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t & red, uint8_t & green, uint8_t & blue); + +private: + /// The saturation gain + double _saturationGain; + /// The luminance gain + double _luminanceGain; +}; diff --git a/include/utils/RgbChannelCorrection.h b/include/utils/RgbChannelCorrection.h new file mode 100644 index 00000000..f09b9cef --- /dev/null +++ b/include/utils/RgbChannelCorrection.h @@ -0,0 +1,66 @@ +#pragma once + +// STL includes +#include + +/// Correction for a single color byte value +/// All configuration values are unsigned int and assume the color value to be between 0 and 255 +class RgbChannelCorrection +{ +public: + /// Default constructor + RgbChannelCorrection(); + + /// Constructor + /// @param correctionR + /// @param correctionG + /// @param correctionB + + RgbChannelCorrection(int correctionR, int correctionG, int correctionB); + + /// Destructor + ~RgbChannelCorrection(); + + /// @return The current correctionR value + uint8_t getcorrectionR() const; + + /// @param threshold New correctionR value + void setcorrectionR(uint8_t correctionR); + + /// @return The current correctionG value + uint8_t getcorrectionG() const; + + /// @param gamma New correctionG value + void setcorrectionG(uint8_t correctionG); + + /// @return The current correctionB value + uint8_t getcorrectionB() const; + + /// @param blacklevel New correctionB value + void setcorrectionB(uint8_t correctionB); + + /// Transform the given array value + /// @param input The input color bytes + /// @return The corrected byte value + uint8_t correctionR(uint8_t inputR) const; + uint8_t correctionG(uint8_t inputG) const; + uint8_t correctionB(uint8_t inputB) const; + + +private: + /// (re)-initilize the color mapping + void initializeMapping(); + +private: + /// The correction of R channel + int _correctionR; + /// The correction of G channel + int _correctionG; + /// The correction of B channel + int _correctionB; + + /// The mapping from input color to output color + int _mappingR[256]; + int _mappingG[256]; + int _mappingB[256]; +}; diff --git a/libsrc/grabber/dispmanx/CMakeLists.txt b/libsrc/grabber/dispmanx/CMakeLists.txt index 27c1f11c..4eb59e64 100644 --- a/libsrc/grabber/dispmanx/CMakeLists.txt +++ b/libsrc/grabber/dispmanx/CMakeLists.txt @@ -1,41 +1,41 @@ - -# Find the BCM-package (VC control) -find_package(BCM REQUIRED) -include_directories(${BCM_INCLUDE_DIRS}) - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx) - -# Group the headers that go through the MOC compiler -SET(DispmanxGrabberQT_HEADERS - ${CURRENT_HEADER_DIR}/DispmanxWrapper.h -) - -SET(DispmanxGrabberHEADERS - ${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h -) - -SET(DispmanxGrabberSOURCES - ${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp - ${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp -) - -if(ENABLE_QT5) - QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) -else(ENABLE_QT5) - QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) -endif(ENABLE_QT5) - -add_library(dispmanx-grabber - ${DispmanxGrabberHEADERS} - ${DispmanxGrabberQT_HEADERS} - ${DispmanxGrabberHEADERS_MOC} - ${DispmanxGrabberSOURCES} -) - -target_link_libraries(dispmanx-grabber - hyperion - ${QT_LIBRARIES} - ${BCM_LIBRARIES} -) + +# Find the BCM-package (VC control) +find_package(BCM REQUIRED) +include_directories(${BCM_INCLUDE_DIRS}) + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx) + +# Group the headers that go through the MOC compiler +SET(DispmanxGrabberQT_HEADERS + ${CURRENT_HEADER_DIR}/DispmanxWrapper.h +) + +SET(DispmanxGrabberHEADERS + ${CURRENT_HEADER_DIR}/DispmanxFrameGrabber.h +) + +SET(DispmanxGrabberSOURCES + ${CURRENT_SOURCE_DIR}/DispmanxWrapper.cpp + ${CURRENT_SOURCE_DIR}/DispmanxFrameGrabber.cpp +) + +if(ENABLE_QT5) + QT5_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) +else(ENABLE_QT5) + QT4_WRAP_CPP(DispmanxGrabberHEADERS_MOC ${DispmanxGrabberQT_HEADERS}) +endif(ENABLE_QT5) + +add_library(dispmanx-grabber + ${DispmanxGrabberHEADERS} + ${DispmanxGrabberQT_HEADERS} + ${DispmanxGrabberHEADERS_MOC} + ${DispmanxGrabberSOURCES} +) + +target_link_libraries(dispmanx-grabber + hyperion + ${QT_LIBRARIES} + ${BCM_LIBRARIES} +) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index ebd25254..a2188127 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -1,68 +1,70 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) - -# Group the headers that go through the MOC compiler -SET(Hyperion_QT_HEADERS - ${CURRENT_HEADER_DIR}/Hyperion.h - - ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h -) - -SET(Hyperion_HEADERS - ${CURRENT_HEADER_DIR}/ImageProcessor.h - ${CURRENT_HEADER_DIR}/ImageProcessorFactory.h - ${CURRENT_HEADER_DIR}/ImageToLedsMap.h - ${CURRENT_HEADER_DIR}/LedString.h - ${CURRENT_HEADER_DIR}/PriorityMuxer.h - - ${CURRENT_SOURCE_DIR}/MultiColorTransform.h - ${CURRENT_HEADER_DIR}/MessageForwarder.h -) - -SET(Hyperion_SOURCES - ${CURRENT_SOURCE_DIR}/Hyperion.cpp - ${CURRENT_SOURCE_DIR}/ImageProcessor.cpp - ${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp - ${CURRENT_SOURCE_DIR}/LedString.cpp - ${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp - - ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp - ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp - ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp - ${CURRENT_SOURCE_DIR}/MessageForwarder.cpp -) - -set(Hyperion_RESOURCES - ${CURRENT_SOURCE_DIR}/resource.qrc -) - -if(ENABLE_QT5) -QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) -QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") -else(ENABLE_QT5) -QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) -QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") -endif(ENABLE_QT5) - -add_library(hyperion - ${Hyperion_HEADERS} - ${Hyperion_QT_HEADERS} - ${Hyperion_HEADERS_MOC} - ${Hyperion_SOURCES} - ${Hyperion_RESOURCES_RCC} -) - -if(ENABLE_QT5) -qt5_use_modules(hyperion Widgets) -endif(ENABLE_QT5) - -target_link_libraries(hyperion - blackborder - hyperion-utils - leddevice - effectengine - serialport - ${QT_LIBRARIES} -) + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) + +# Group the headers that go through the MOC compiler +SET(Hyperion_QT_HEADERS + ${CURRENT_HEADER_DIR}/Hyperion.h + + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h +) + +SET(Hyperion_HEADERS + ${CURRENT_HEADER_DIR}/ImageProcessor.h + ${CURRENT_HEADER_DIR}/ImageProcessorFactory.h + ${CURRENT_HEADER_DIR}/ImageToLedsMap.h + ${CURRENT_HEADER_DIR}/LedString.h + ${CURRENT_HEADER_DIR}/PriorityMuxer.h + + ${CURRENT_SOURCE_DIR}/MultiColorTransform.h + ${CURRENT_SOURCE_DIR}/MultiColorCorrection.h + ${CURRENT_HEADER_DIR}/MessageForwarder.h +) + +SET(Hyperion_SOURCES + ${CURRENT_SOURCE_DIR}/Hyperion.cpp + ${CURRENT_SOURCE_DIR}/ImageProcessor.cpp + ${CURRENT_SOURCE_DIR}/ImageProcessorFactory.cpp + ${CURRENT_SOURCE_DIR}/LedString.cpp + ${CURRENT_SOURCE_DIR}/PriorityMuxer.cpp + + ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp + ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp + ${CURRENT_SOURCE_DIR}/MultiColorCorrection.cpp + ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp + ${CURRENT_SOURCE_DIR}/MessageForwarder.cpp +) + +set(Hyperion_RESOURCES + ${CURRENT_SOURCE_DIR}/resource.qrc +) + +if(ENABLE_QT5) +QT5_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) +QT5_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") +else(ENABLE_QT5) +QT4_WRAP_CPP(Hyperion_HEADERS_MOC ${Hyperion_QT_HEADERS}) +QT4_ADD_RESOURCES(Hyperion_RESOURCES_RCC ${Hyperion_RESOURCES} OPTIONS "-no-compress") +endif(ENABLE_QT5) + +add_library(hyperion + ${Hyperion_HEADERS} + ${Hyperion_QT_HEADERS} + ${Hyperion_HEADERS_MOC} + ${Hyperion_SOURCES} + ${Hyperion_RESOURCES_RCC} +) + +if(ENABLE_QT5) +qt5_use_modules(hyperion Widgets) +endif(ENABLE_QT5) + +target_link_libraries(hyperion + blackborder + hyperion-utils + leddevice + effectengine + serialport + ${QT_LIBRARIES} +) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 2463ed15..b564b3a6 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -15,12 +15,15 @@ // hyperion include #include #include +#include +#include // Leddevice includes #include #include #include "MultiColorTransform.h" +#include "MultiColorCorrection.h" #include "LinearColorSmoothing.h" // effect engine includes @@ -77,6 +80,7 @@ ColorTransform * Hyperion::createColorTransform(const Json::Value & transformCon RgbChannelTransform * blueTransform = createRgbChannelTransform(transformConfig["blue"]); HsvTransform * hsvTransform = createHsvTransform(transformConfig["hsv"]); + HslTransform * hslTransform = createHslTransform(transformConfig["hsl"]); ColorTransform * transform = new ColorTransform(); transform->_id = id; @@ -84,16 +88,35 @@ ColorTransform * Hyperion::createColorTransform(const Json::Value & transformCon transform->_rgbGreenTransform = *greenTransform; transform->_rgbBlueTransform = *blueTransform; transform->_hsvTransform = *hsvTransform; + transform->_hslTransform = *hslTransform; + // Cleanup the allocated individual transforms delete redTransform; delete greenTransform; delete blueTransform; delete hsvTransform; + delete hslTransform; return transform; } +ColorCorrection * Hyperion::createColorCorrection(const Json::Value & correctionConfig) +{ + const std::string id = correctionConfig.get("id", "default").asString(); + + RgbChannelCorrection * rgbCorrection = createRgbChannelCorrection(correctionConfig["correctionValues"]); + + ColorCorrection * correction = new ColorCorrection(); + correction->_id = id; + correction->_rgbCorrection = *rgbCorrection; + + // Cleanup the allocated individual transforms + delete rgbCorrection; + + return correction; +} + MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorConfig) { // Create the result, the transforms are added to this @@ -168,6 +191,154 @@ MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, return transform; } +MultiColorCorrection * Hyperion::createLedColorsCorrection(const unsigned ledCnt, const Json::Value & colorConfig) +{ + // Create the result, the corrections are added to this + MultiColorCorrection * correction = new MultiColorCorrection(ledCnt); + + const Json::Value correctionConfig = colorConfig.get("correction", Json::nullValue); + if (correctionConfig.isNull()) + { + // Old style color correction config (just one for all leds) + ColorCorrection * colorCorrection = createColorCorrection(colorConfig); + correction->addCorrection(colorCorrection); + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + } + else if (!correctionConfig.isArray()) + { + ColorCorrection * colorCorrection = createColorCorrection(colorConfig); + correction->addCorrection(colorCorrection); + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + } + else + { + const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*"); + + for (Json::UInt i = 0; i < correctionConfig.size(); ++i) + { + const Json::Value & config = correctionConfig[i]; + ColorCorrection * colorCorrection = createColorCorrection(config); + correction->addCorrection(colorCorrection); + + const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed(); + if (ledIndicesStr.compare("*") == 0) + { + // Special case for indices '*' => all leds + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [0; "<< ledCnt-1 << "]" << std::endl; + continue; + } + + if (!overallExp.exactMatch(ledIndicesStr)) + { + std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl; + continue; + } + + std::cout << "ColorCorrection '" << colorCorrection->_id << "' => ["; + + const QStringList ledIndexList = ledIndicesStr.split(","); + for (int i=0; i 0) + { + std::cout << ", "; + } + if (ledIndexList[i].contains("-")) + { + QStringList ledIndices = ledIndexList[i].split("-"); + int startInd = ledIndices[0].toInt(); + int endInd = ledIndices[1].toInt(); + + correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd); + std::cout << startInd << "-" << endInd; + } + else + { + int index = ledIndexList[i].toInt(); + correction->setCorrectionForLed(colorCorrection->_id, index, index); + std::cout << index; + } + } + std::cout << "]" << std::endl; + } + } + return correction; +} + +MultiColorCorrection * Hyperion::createLedColorsTemperature(const unsigned ledCnt, const Json::Value & colorConfig) +{ + // Create the result, the corrections are added to this + MultiColorCorrection * correction = new MultiColorCorrection(ledCnt); + + const Json::Value correctionConfig = colorConfig.get("temperature", Json::nullValue); + if (correctionConfig.isNull()) + { + // Old style color correction config (just one for all leds) + ColorCorrection * colorCorrection = createColorCorrection(colorConfig); + correction->addCorrection(colorCorrection); + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + } + else if (!correctionConfig.isArray()) + { + ColorCorrection * colorCorrection = createColorCorrection(colorConfig); + correction->addCorrection(colorCorrection); + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + } + else + { + const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*"); + + for (Json::UInt i = 0; i < correctionConfig.size(); ++i) + { + const Json::Value & config = correctionConfig[i]; + ColorCorrection * colorCorrection = createColorCorrection(config); + correction->addCorrection(colorCorrection); + + const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed(); + if (ledIndicesStr.compare("*") == 0) + { + // Special case for indices '*' => all leds + correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1); + std::cout << "ColorCorrection '" << colorCorrection->_id << "' => [0; "<< ledCnt-1 << "]" << std::endl; + continue; + } + + if (!overallExp.exactMatch(ledIndicesStr)) + { + std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl; + continue; + } + + std::cout << "ColorCorrection '" << colorCorrection->_id << "' => ["; + + const QStringList ledIndexList = ledIndicesStr.split(","); + for (int i=0; i 0) + { + std::cout << ", "; + } + if (ledIndexList[i].contains("-")) + { + QStringList ledIndices = ledIndexList[i].split("-"); + int startInd = ledIndices[0].toInt(); + int endInd = ledIndices[1].toInt(); + + correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd); + std::cout << startInd << "-" << endInd; + } + else + { + int index = ledIndexList[i].toInt(); + correction->setCorrectionForLed(colorCorrection->_id, index, index); + std::cout << index; + } + } + std::cout << "]" << std::endl; + } + } + return correction; +} + HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig) { const double saturationGain = hsvConfig.get("saturationGain", 1.0).asDouble(); @@ -176,6 +347,14 @@ HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig) return new HsvTransform(saturationGain, valueGain); } +HslTransform * Hyperion::createHslTransform(const Json::Value & hslConfig) +{ + const double saturationGain = hslConfig.get("saturationGain", 1.0).asDouble(); + const double luminanceGain = hslConfig.get("luminanceGain", 1.0).asDouble(); + + return new HslTransform(saturationGain, luminanceGain); +} + RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colorConfig) { const double threshold = colorConfig.get("threshold", 0.0).asDouble(); @@ -187,6 +366,16 @@ RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colo return transform; } +RgbChannelCorrection* Hyperion::createRgbChannelCorrection(const Json::Value& colorConfig) +{ + const int varR = colorConfig.get("red", 255).asInt(); + const int varG = colorConfig.get("green", 255).asInt(); + const int varB = colorConfig.get("blue", 255).asInt(); + + RgbChannelCorrection* correction = new RgbChannelCorrection(varR, varG, varB); + return correction; +} + LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOrder deviceOrder) { LedString ledString; @@ -266,7 +455,6 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, return ledDevice; } - MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig) { MessageForwarder * forwarder = new MessageForwarder(); @@ -302,12 +490,22 @@ MessageForwarder * Hyperion::getForwarder() Hyperion::Hyperion(const Json::Value &jsonConfig) : _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"]))), _muxer(_ledString.leds().size()), + _raw2ledCorrection(createLedColorsCorrection(_ledString.leds().size(), jsonConfig["color"])), + _raw2ledTemperature(createLedColorsTemperature(_ledString.leds().size(), jsonConfig["color"])), _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _device(LedDeviceFactory::construct(jsonConfig["device"])), _effectEngine(nullptr), _messageForwarder(createMessageForwarder(jsonConfig["forwarder"])), _timer() { + if (!_raw2ledCorrection->verifyCorrections()) + { + throw std::runtime_error("Color correction incorrectly set"); + } + if (!_raw2ledTemperature->verifyCorrections()) + { + throw std::runtime_error("Color temperature incorrectly set"); + } if (!_raw2ledTransform->verifyTransforms()) { throw std::runtime_error("Color transformation incorrectly set"); @@ -347,6 +545,12 @@ Hyperion::~Hyperion() // delete the color transform delete _raw2ledTransform; + + // delete the color correction + delete _raw2ledCorrection; + + // delete the color temperature correction + delete _raw2ledTemperature; // delete the message forwarder delete _messageForwarder; @@ -395,16 +599,46 @@ const std::vector & Hyperion::getTransformIds() const return _raw2ledTransform->getTransformIds(); } +const std::vector & Hyperion::getCorrectionIds() const +{ + return _raw2ledCorrection->getCorrectionIds(); +} + +const std::vector & Hyperion::getTemperatureIds() const +{ + return _raw2ledTemperature->getCorrectionIds(); +} + ColorTransform * Hyperion::getTransform(const std::string& id) { return _raw2ledTransform->getTransform(id); } +ColorCorrection * Hyperion::getCorrection(const std::string& id) +{ + return _raw2ledCorrection->getCorrection(id); +} + +ColorCorrection * Hyperion::getTemperature(const std::string& id) +{ + return _raw2ledTemperature->getCorrection(id); +} + void Hyperion::transformsUpdated() { update(); } +void Hyperion::correctionsUpdated() +{ + update(); +} + +void Hyperion::temperaturesUpdated() +{ + update(); +} + void Hyperion::clear(int priority) { if (_muxer.hasPriority(priority)) @@ -468,8 +702,10 @@ void Hyperion::update() int priority = _muxer.getCurrentPriority(); const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority); - // Apply the transform to each led and color-channel - std::vector ledColors = _raw2ledTransform->applyTransform(priorityInfo.ledColors); + // Apply the correction and the transform to each led and color-channel + std::vector correctedColors = _raw2ledCorrection->applyCorrection(priorityInfo.ledColors); + std::vector temperatureColors = _raw2ledTemperature->applyCorrection(correctedColors); + std::vector ledColors =_raw2ledTransform->applyTransform(temperatureColors); const std::vector& leds = _ledString.leds(); int i = 0; for (ColorRgb& color : ledColors) diff --git a/libsrc/hyperion/MultiColorCorrection.cpp b/libsrc/hyperion/MultiColorCorrection.cpp new file mode 100644 index 00000000..6388d27e --- /dev/null +++ b/libsrc/hyperion/MultiColorCorrection.cpp @@ -0,0 +1,96 @@ + +// STL includes +#include + +// Hyperion includes +#include "MultiColorCorrection.h" + +MultiColorCorrection::MultiColorCorrection(const unsigned ledCnt) : + _ledCorrections(ledCnt, nullptr) +{ +} + +MultiColorCorrection::~MultiColorCorrection() +{ + // Clean up all the correctinos + for (ColorCorrection * correction : _correction) + { + delete correction; + } +} + +void MultiColorCorrection::addCorrection(ColorCorrection * correction) +{ + _correctionIds.push_back(correction->_id); + _correction.push_back(correction); +} + +void MultiColorCorrection::setCorrectionForLed(const std::string& id, const unsigned startLed, const unsigned endLed) +{ + assert(startLed <= endLed); + assert(endLed < _ledCorrections.size()); + + // Get the identified correction (don't care if is nullptr) + ColorCorrection * correction = getCorrection(id); + for (unsigned iLed=startLed; iLed<=endLed; ++iLed) + { + _ledCorrections[iLed] = correction; + } +} + +bool MultiColorCorrection::verifyCorrections() const +{ + bool allLedsSet = true; + for (unsigned iLed=0; iLed<_ledCorrections.size(); ++iLed) + { + if (_ledCorrections[iLed] == nullptr) + { + std::cerr << "No correction set for " << iLed << std::endl; + allLedsSet = false; + } + } + return allLedsSet; +} + +const std::vector & MultiColorCorrection::getCorrectionIds() +{ + return _correctionIds; +} + +ColorCorrection* MultiColorCorrection::getCorrection(const std::string& id) +{ + // Iterate through the unique corrections until we find the one with the given id + for (ColorCorrection * correction : _correction) + { + if (correction->_id == id) + { + return correction; + } + } + + // The ColorCorrection was not found + return nullptr; +} + +std::vector MultiColorCorrection::applyCorrection(const std::vector& rawColors) +{ + // Create a copy, as we will do the rest of the correction in place + std::vector ledColors(rawColors); + + const size_t itCnt = std::min(_ledCorrections.size(), rawColors.size()); + for (size_t i=0; i_rgbCorrection.correctionR(color.red); + color.green = correction->_rgbCorrection.correctionG(color.green); + color.blue = correction->_rgbCorrection.correctionB(color.blue); + } + return ledColors; +} diff --git a/libsrc/hyperion/MultiColorCorrection.h b/libsrc/hyperion/MultiColorCorrection.h new file mode 100644 index 00000000..cdefce17 --- /dev/null +++ b/libsrc/hyperion/MultiColorCorrection.h @@ -0,0 +1,66 @@ +#pragma once + +// STL includes +#include + +// Utils includes +#include + +// Hyperion includes +#include + +/// +/// The LedColorCorrection is responsible for performing color correction from 'raw' colors +/// received as input to colors mapped to match the color-properties of the leds. +/// +class MultiColorCorrection +{ +public: + MultiColorCorrection(const unsigned ledCnt); + ~MultiColorCorrection(); + + /** + * Adds a new ColorCorrection to this MultiColorCorrection + * + * @param Correction The new ColorCorrection (ownership is transfered) + */ + void addCorrection(ColorCorrection * correction); + + void setCorrectionForLed(const std::string& id, const unsigned startLed, const unsigned endLed); + + bool verifyCorrections() const; + + /// + /// Returns the identifier of all the unique ColorCorrection + /// + /// @return The list with unique id's of the ColorCorrections + const std::vector & getCorrectionIds(); + + /// + /// Returns the pointer to the ColorCorrection with the given id + /// + /// @param id The identifier of the ColorCorrection + /// + /// @return The ColorCorrection with the given id (or nullptr if it does not exist) + /// + ColorCorrection* getCorrection(const std::string& id); + + /// + /// Performs the color transoformation from raw-color to led-color + /// + /// @param rawColors The list with raw colors + /// + /// @return The list with led-colors + /// + std::vector applyCorrection(const std::vector& rawColors); + +private: + /// List with Correction ids + std::vector _correctionIds; + + /// List with unique ColorCorrections + std::vector _correction; + + /// List with a pointer to the ColorCorrection for each individual led + std::vector _ledCorrections; +}; diff --git a/libsrc/hyperion/MultiColorTransform.cpp b/libsrc/hyperion/MultiColorTransform.cpp index d15f020e..0b524acf 100644 --- a/libsrc/hyperion/MultiColorTransform.cpp +++ b/libsrc/hyperion/MultiColorTransform.cpp @@ -89,6 +89,7 @@ std::vector MultiColorTransform::applyTransform(const std::vector_hsvTransform.transform(color.red, color.green, color.blue); + transform->_hslTransform.transform(color.red, color.green, color.blue); color.red = transform->_rgbRedTransform.transform(color.red); color.green = transform->_rgbGreenTransform.transform(color.green); color.blue = transform->_rgbBlueTransform.transform(color.blue); diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index e459425b..f30e1a27 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -38,7 +38,7 @@ "type":"object", "required":false, "properties": { - "hsv" : { + "hsv" : { "type" : "object", "required" : false, "properties" : { @@ -54,6 +54,23 @@ } }, "additionalProperties" : false + }, + "hsl" : { + "type" : "object", + "required" : false, + "properties" : { + "saturationGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + }, + "luminanceGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + } + }, + "additionalProperties" : false }, "red": { "type":"object", diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index e4dbc096..2d3cfa45 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include // project includes @@ -231,7 +232,7 @@ void JsonClientConnection::handleMessage(const std::string &messageString) sendErrorReply("Error while validating json: " + errors); return; } - + // switch over all possible commands and handle them if (command == "color") handleColorCommand(message); @@ -247,6 +248,10 @@ void JsonClientConnection::handleMessage(const std::string &messageString) handleClearallCommand(message); else if (command == "transform") handleTransformCommand(message); + else if (command == "correction") + handleCorrectionCommand(message); + else if (command == "temperature") + handleTemperatureCommand(message); else handleNotImplemented(); } @@ -387,6 +392,47 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &) item["duration_ms"] = Json::Value::UInt(priorityInfo.timeoutTime_ms - now); } } + + // collect correction information + Json::Value & correctionArray = info["correction"]; + for (const std::string& correctionId : _hyperion->getCorrectionIds()) + { + const ColorCorrection * colorCorrection = _hyperion->getCorrection(correctionId); + if (colorCorrection == nullptr) + { + std::cerr << "Incorrect color correction id: " << correctionId << std::endl; + continue; + } + + Json::Value & correction = correctionArray.append(Json::Value()); + correction["id"] = correctionId; + + Json::Value & corrValues = correction["correctionValues"]; + corrValues.append(colorCorrection->_rgbCorrection.getcorrectionR()); + corrValues.append(colorCorrection->_rgbCorrection.getcorrectionG()); + corrValues.append(colorCorrection->_rgbCorrection.getcorrectionB()); + } + + // collect temperature correction information + Json::Value & temperatureArray = info["temperature"]; + for (const std::string& tempId : _hyperion->getTemperatureIds()) + { + const ColorCorrection * colorTemp = _hyperion->getTemperature(tempId); + if (colorTemp == nullptr) + { + std::cerr << "Incorrect color temperature correction id: " << tempId << std::endl; + continue; + } + + Json::Value & temperature = temperatureArray.append(Json::Value()); + temperature["id"] = tempId; + + Json::Value & tempValues = temperature["correctionValues"]; + tempValues.append(colorTemp->_rgbCorrection.getcorrectionR()); + tempValues.append(colorTemp->_rgbCorrection.getcorrectionG()); + tempValues.append(colorTemp->_rgbCorrection.getcorrectionB()); + } + // collect transform information Json::Value & transformArray = info["transform"]; @@ -404,6 +450,8 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &) transform["saturationGain"] = colorTransform->_hsvTransform.getSaturationGain(); transform["valueGain"] = colorTransform->_hsvTransform.getValueGain(); + transform["saturationLGain"] = colorTransform->_hslTransform.getSaturationGain(); + transform["luminanceGain"] = colorTransform->_hslTransform.getLuminanceGain(); Json::Value & threshold = transform["threshold"]; threshold.append(colorTransform->_rgbRedTransform.getThreshold()); @@ -476,7 +524,7 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message) //sendErrorReply(std::string("Incorrect transform identifier: ") + transformId); return; } - + if (transform.isMember("saturationGain")) { colorTransform->_hsvTransform.setSaturationGain(transform["saturationGain"].asDouble()); @@ -486,6 +534,16 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message) { colorTransform->_hsvTransform.setValueGain(transform["valueGain"].asDouble()); } + + if (transform.isMember("saturationLGain")) + { + colorTransform->_hslTransform.setSaturationGain(transform["saturationLGain"].asDouble()); + } + + if (transform.isMember("luminanceGain")) + { + colorTransform->_hslTransform.setLuminanceGain(transform["luminanceGain"].asDouble()); + } if (transform.isMember("threshold")) { @@ -518,13 +576,65 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message) colorTransform->_rgbGreenTransform.setWhitelevel(values[1u].asDouble()); colorTransform->_rgbBlueTransform .setWhitelevel(values[2u].asDouble()); } - + // commit the changes _hyperion->transformsUpdated(); sendSuccessReply(); } +void JsonClientConnection::handleCorrectionCommand(const Json::Value &message) +{ + const Json::Value & correction = message["correction"]; + + const std::string correctionId = correction.get("id", _hyperion->getCorrectionIds().front()).asString(); + ColorCorrection * colorCorrection = _hyperion->getCorrection(correctionId); + if (colorCorrection == nullptr) + { + //sendErrorReply(std::string("Incorrect correction identifier: ") + correctionId); + return; + } + + if (correction.isMember("correctionValues")) + { + const Json::Value & values = correction["correctionValues"]; + colorCorrection->_rgbCorrection.setcorrectionR(values[0u].asInt()); + colorCorrection->_rgbCorrection.setcorrectionG(values[1u].asInt()); + colorCorrection->_rgbCorrection.setcorrectionB(values[2u].asInt()); + } + + // commit the changes + _hyperion->correctionsUpdated(); + + sendSuccessReply(); +} + +void JsonClientConnection::handleTemperatureCommand(const Json::Value &message) +{ + const Json::Value & temperature = message["temperature"]; + + const std::string tempId = temperature.get("id", _hyperion->getTemperatureIds().front()).asString(); + ColorCorrection * colorTemperature = _hyperion->getTemperature(tempId); + if (colorTemperature == nullptr) + { + //sendErrorReply(std::string("Incorrect temperature identifier: ") + tempId); + return; + } + + if (temperature.isMember("correctionValues")) + { + const Json::Value & values = temperature["correctionValues"]; + colorTemperature->_rgbCorrection.setcorrectionR(values[0u].asInt()); + colorTemperature->_rgbCorrection.setcorrectionG(values[1u].asInt()); + colorTemperature->_rgbCorrection.setcorrectionB(values[2u].asInt()); + } + + // commit the changes + _hyperion->temperaturesUpdated(); + + sendSuccessReply(); +} + void JsonClientConnection::handleNotImplemented() { sendErrorReply("Command not implemented"); diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index 01db54b1..acf58d7d 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -112,6 +112,20 @@ private: /// @param message the incoming message /// void handleTransformCommand(const Json::Value & message); + + /// + /// Handle an incoming JSON Correction message + /// + /// @param message the incoming message + /// + void handleCorrectionCommand(const Json::Value & message); + + /// + /// Handle an incoming JSON Temperature message + /// + /// @param message the incoming message + /// + void handleTemperatureCommand(const Json::Value & message); /// /// Handle an incoming JSON message of unknown type diff --git a/libsrc/jsonserver/JsonSchemas.qrc b/libsrc/jsonserver/JsonSchemas.qrc index 7736e530..193624ba 100644 --- a/libsrc/jsonserver/JsonSchemas.qrc +++ b/libsrc/jsonserver/JsonSchemas.qrc @@ -7,6 +7,8 @@ schema/schema-clear.json schema/schema-clearall.json schema/schema-transform.json + schema/schema-correction.json + schema/schema-temperature.json schema/schema-effect.json diff --git a/libsrc/jsonserver/schema/schema-correction.json b/libsrc/jsonserver/schema/schema-correction.json new file mode 100644 index 00000000..ad7dbfca --- /dev/null +++ b/libsrc/jsonserver/schema/schema-correction.json @@ -0,0 +1,34 @@ +{ + "type":"object", + "required":true, + "properties":{ + "command": { + "type" : "string", + "required" : true, + "enum" : ["correction"] + }, + "correction": { + "type": "object", + "required": true, + "properties": { + "id" : { + "type" : "string", + "required" : false + }, + "correctionValues" : { + "type": "array", + "required": false, + "items" : { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "minItems": 3, + "maxItems": 3 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/libsrc/jsonserver/schema/schema-temperature.json b/libsrc/jsonserver/schema/schema-temperature.json new file mode 100644 index 00000000..b7be1f4f --- /dev/null +++ b/libsrc/jsonserver/schema/schema-temperature.json @@ -0,0 +1,34 @@ +{ + "type":"object", + "required":true, + "properties":{ + "command": { + "type" : "string", + "required" : true, + "enum" : ["temperature"] + }, + "temperature": { + "type": "object", + "required": true, + "properties": { + "id" : { + "type" : "string", + "required" : false + }, + "correctionValues" : { + "type": "array", + "required": false, + "items" : { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "minItems": 3, + "maxItems": 3 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/libsrc/jsonserver/schema/schema-transform.json b/libsrc/jsonserver/schema/schema-transform.json index 7b939839..8b194eab 100644 --- a/libsrc/jsonserver/schema/schema-transform.json +++ b/libsrc/jsonserver/schema/schema-transform.json @@ -25,6 +25,16 @@ "required" : false, "minimum" : 0.0 }, + "saturationLGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + }, + "luminanceGain" : { + "type" : "number", + "required" : false, + "minimum" : 0.0 + }, "threshold": { "type": "array", "required": false, diff --git a/libsrc/jsonserver/schema/schema.json b/libsrc/jsonserver/schema/schema.json index e931f7b7..061339d1 100644 --- a/libsrc/jsonserver/schema/schema.json +++ b/libsrc/jsonserver/schema/schema.json @@ -5,7 +5,7 @@ "command": { "type" : "string", "required" : true, - "enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform"] + "enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform", "correction", "temperature"] } } } diff --git a/libsrc/leddevice/LedDeviceFadeCandy.cpp b/libsrc/leddevice/LedDeviceFadeCandy.cpp old mode 100755 new mode 100644 diff --git a/libsrc/leddevice/LedDeviceFadeCandy.h b/libsrc/leddevice/LedDeviceFadeCandy.h old mode 100755 new mode 100644 diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index 1aad10cb..8de00707 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -23,8 +23,12 @@ add_library(hyperion-utils ${CURRENT_HEADER_DIR}/HsvTransform.h ${CURRENT_SOURCE_DIR}/HsvTransform.cpp + ${CURRENT_HEADER_DIR}/HslTransform.h + ${CURRENT_SOURCE_DIR}/HslTransform.cpp ${CURRENT_HEADER_DIR}/RgbChannelTransform.h ${CURRENT_SOURCE_DIR}/RgbChannelTransform.cpp + ${CURRENT_HEADER_DIR}/RgbChannelCorrection.h + ${CURRENT_SOURCE_DIR}/RgbChannelCorrection.cpp ${CURRENT_HEADER_DIR}/jsonschema/JsonFactory.h ${CURRENT_HEADER_DIR}/jsonschema/JsonSchemaChecker.h diff --git a/libsrc/utils/HslTransform.cpp b/libsrc/utils/HslTransform.cpp new file mode 100644 index 00000000..09056bb2 --- /dev/null +++ b/libsrc/utils/HslTransform.cpp @@ -0,0 +1,158 @@ +#include +#include +#include + +HslTransform::HslTransform() : + _saturationGain(1.0), + _luminanceGain(1.0) +{ +} + +HslTransform::HslTransform(double saturationGain, double luminanceGain) : + _saturationGain(saturationGain), + _luminanceGain(luminanceGain) +{ +} + +HslTransform::~HslTransform() +{ +} + +void HslTransform::setSaturationGain(double saturationGain) +{ + _saturationGain = saturationGain; +} + +double HslTransform::getSaturationGain() const +{ + return _saturationGain; +} + +void HslTransform::setLuminanceGain(double luminanceGain) +{ + _luminanceGain = luminanceGain; +} + +double HslTransform::getLuminanceGain() const +{ + return _luminanceGain; +} + +void HslTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) const +{ + if (_saturationGain != 1.0 || _luminanceGain != 1.0) + { + uint16_t hue; + float saturation, luminance; + rgb2hsl(red, green, blue, hue, saturation, luminance); + + float s = saturation * _saturationGain; + if (s > 1.0f) + saturation = 1.0f; + else + saturation = s; + + float l = luminance * _luminanceGain; + if (l > 1.0f) + luminance = 1.0f; + else + luminance = l; + + hsl2rgb(hue, saturation, luminance, red, green, blue); + } +} + +void HslTransform::rgb2hsl(uint8_t red, uint8_t green, uint8_t blue, uint16_t & hue, float & saturation, float & luminance) +{ + float r = red / 255.0f; + float g = green / 255.0f; + float b = blue / 255.0f; + + float rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b); + float rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b); + float diff = rgbMax - rgbMin; + + //luminance + luminance = (rgbMin + rgbMax) / 2.0f; + + if (diff == 0.0f) { + saturation = 0.0f; + hue = 0; + return; + } + + //saturation + if (luminance < 0.5f) + saturation = diff / (rgbMin + rgbMax); + else + saturation = diff / (2.0f - rgbMin - rgbMax); + + if (rgbMax == r) + { + // start from 360 to be sure that we won't assign a negative number to the unsigned hue value + hue = 360 + 60 * (g - b) / (rgbMax - rgbMin); + + if (hue > 359) + hue -= 360; + } + else if (rgbMax == g) + { + hue = 120 + 60 * (b - r) / (rgbMax - rgbMin); + } + else + { + hue = 240 + 60 * (r - g) / (rgbMax - rgbMin); + } + +} + +void HslTransform::hsl2rgb(uint16_t hue, float saturation, float luminance, uint8_t & red, uint8_t & green, uint8_t & blue) +{ + if (saturation == 0.0f){ + red = (uint8_t)(luminance * 255.0f); + green = (uint8_t)(luminance * 255.0f); + blue = (uint8_t)(luminance * 255.0f); + return; + } + + float q; + + if (luminance < 0.5f) + q = luminance * (1.0f + saturation); + else + q = (luminance + saturation) - (luminance * saturation); + + float p = (2.0f * luminance) - q; + float h = hue / 360.0f; + + float t[3]; + + t[0] = h + (1.0f / 3.0f); + t[1] = h; + t[2] = h - (1.0f / 3.0f); + + for (int i = 0; i < 3; i++) { + if (t[i] < 0.0f) + t[i] += 1.0f; + if (t[i] > 1.0f) + t[i] -= 1.0f; + } + + float out[3]; + + for (int i = 0; i < 3; i++) { + if (t[i] * 6.0f < 1.0f) + out[i] = p + (q - p) * 6.0f * t[i]; + else if (t[i] * 2.0f < 1.0f) + out[i] = q; + else if (t[i] * 3.0f < 2.0f) + out[i] = p + (q - p) * ((2.0f / 3.0f) - t[i]) * 6.0f; + else out[i] = p; + } + + //convert back to 0...255 range + red = (uint8_t)(out[0] * 255.0f); + green = (uint8_t)(out[1] * 255.0f); + blue = (uint8_t)(out[2] * 255.0f); + +} diff --git a/libsrc/utils/RgbChannelCorrection.cpp b/libsrc/utils/RgbChannelCorrection.cpp new file mode 100644 index 00000000..2cced20b --- /dev/null +++ b/libsrc/utils/RgbChannelCorrection.cpp @@ -0,0 +1,120 @@ +// STL includes +#include + +// Utils includes +#include + +RgbChannelCorrection::RgbChannelCorrection() : + _correctionR(255), + _correctionB(255), + _correctionG(255) +{ + initializeMapping(); +} + +RgbChannelCorrection::RgbChannelCorrection(int correctionR, int correctionG, int correctionB) : + _correctionR(correctionR), + _correctionG(correctionG), + _correctionB(correctionB) +{ + initializeMapping(); +} + +RgbChannelCorrection::~RgbChannelCorrection() +{ +} + +uint8_t RgbChannelCorrection::getcorrectionR() const +{ + return _correctionR; +} + +void RgbChannelCorrection::setcorrectionR(uint8_t correctionR) +{ + _correctionR = correctionR; + initializeMapping(); +} + +uint8_t RgbChannelCorrection::getcorrectionG() const +{ + return _correctionG; +} + +void RgbChannelCorrection::setcorrectionG(uint8_t correctionG) +{ + _correctionG = correctionG; + initializeMapping(); +} + +uint8_t RgbChannelCorrection::getcorrectionB() const +{ + return _correctionB; +} + +void RgbChannelCorrection::setcorrectionB(uint8_t correctionB) +{ + _correctionB = correctionB; + initializeMapping(); +} + +uint8_t RgbChannelCorrection::correctionR(uint8_t inputR) const +{ + return _mappingR[inputR]; +} + +uint8_t RgbChannelCorrection::correctionG(uint8_t inputG) const +{ + return _mappingG[inputG]; +} + +uint8_t RgbChannelCorrection::correctionB(uint8_t inputB) const +{ + return _mappingB[inputB]; +} + +void RgbChannelCorrection::initializeMapping() +{ + // initialize the mapping + for (int i = 0; i < 256; ++i) + { + int outputR = (i * _correctionR) / 255; + if (outputR < -255) + { + outputR = -255; + } + else if (outputR > 255) + { + outputR = 255; + } + _mappingR[i] = outputR; + } + for (int i = 0; i < 256; ++i) + { + int outputG = (i * _correctionG) / 255; + if (outputG < -255) + { + outputG = -255; + } + else if (outputG > 255) + { + outputG = 255; + } + _mappingG[i] = outputG; + } + for (int i = 0; i < 256; ++i) + { + int outputB = (i * _correctionB) / 255; + if (outputB < -255) + { + outputB = -255; + } + else if (outputB > 255) + { + outputB = 255; + } + _mappingB[i] = outputB; + } + + +} + diff --git a/src/hyperion-remote/ColorCorrectionValues.h b/src/hyperion-remote/ColorCorrectionValues.h new file mode 100644 index 00000000..0cd0e911 --- /dev/null +++ b/src/hyperion-remote/ColorCorrectionValues.h @@ -0,0 +1,12 @@ +#pragma once + +/// Simple structure to contain the values of a color transformation +struct ColorCorrectionValues +{ + /// The value for the red color-channel + int valueRed; + /// The value for the green color-channel + int valueGreen; + /// The value for the blue color-channel + int valueBlue; +}; diff --git a/src/hyperion-remote/CustomParameter.h b/src/hyperion-remote/CustomParameter.h index 4f336dd7..af1836a6 100644 --- a/src/hyperion-remote/CustomParameter.h +++ b/src/hyperion-remote/CustomParameter.h @@ -12,6 +12,7 @@ // hyperion-remote includes #include "ColorTransformValues.h" +#include "ColorCorrectionValues.h" /// Data parameter for a color typedef vlofgren::PODParameter> ColorParameter; @@ -22,6 +23,9 @@ typedef vlofgren::PODParameter ImageParameter; /// Data parameter for color transform values (list of three values) typedef vlofgren::PODParameter TransformParameter; +/// Data parameter for color correction values (list of three values) +typedef vlofgren::PODParameter CorrectionParameter; + namespace vlofgren { /// /// Translates a string (as passed on the commandline) to a vector of colors @@ -128,4 +132,33 @@ namespace vlofgren { return transform; } + + template<> + ColorCorrectionValues CorrectionParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + ColorCorrectionValues correction; + + // s should be split in 3 parts + // seperators are either a ',' or a space + QStringList components = QString(s.c_str()).split(" ", QString::SkipEmptyParts); + + if (components.size() == 3) + { + bool ok1, ok2, ok3; + correction.valueRed = components[0].toInt(&ok1); + correction.valueGreen = components[1].toInt(&ok2); + correction.valueBlue = components[2].toInt(&ok3); + + if (ok1 && ok2 && ok3) + { + return correction; + } + } + + std::stringstream errorMessage; + errorMessage << "Argument " << s << " can not be parsed to 3 integer values"; + throw Parameter::ParameterRejected(errorMessage.str()); + + return correction; + } } diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp index 652a3dfa..61367104 100644 --- a/src/hyperion-remote/JsonConnection.cpp +++ b/src/hyperion-remote/JsonConnection.cpp @@ -192,7 +192,7 @@ void JsonConnection::clearAll() parseReply(reply); } -void JsonConnection::setTransform(std::string * transformId, double * saturation, double * value, ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel) +void JsonConnection::setTransform(std::string * transformId, double * saturation, double * value, double * saturationL, double * luminance, ColorTransformValues *threshold, ColorTransformValues *gamma, ColorTransformValues *blacklevel, ColorTransformValues *whitelevel) { std::cout << "Set color transforms" << std::endl; @@ -215,7 +215,16 @@ void JsonConnection::setTransform(std::string * transformId, double * saturation { transform["valueGain"] = *value; } + + if (saturationL != nullptr) + { + transform["saturationLGain"] = *saturationL; + } + if (luminance != nullptr) + { + transform["luminanceGain"] = *luminance; + } if (threshold != nullptr) { Json::Value & v = transform["threshold"]; @@ -255,6 +264,64 @@ void JsonConnection::setTransform(std::string * transformId, double * saturation parseReply(reply); } +void JsonConnection::setCorrection(std::string * correctionId, ColorCorrectionValues *correction) +{ + std::cout << "Set color corrections" << std::endl; + + // create command + Json::Value command; + command["command"] = "correction"; + Json::Value & correct = command["correction"]; + + if (correctionId != nullptr) + { + correct["id"] = *correctionId; + } + + if (correction != nullptr) + { + Json::Value & v = correct["correctionValues"]; + v.append(correction->valueRed); + v.append(correction->valueGreen); + v.append(correction->valueBlue); + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + +void JsonConnection::setTemperature(std::string * temperatureId, ColorCorrectionValues *temperature) +{ + std::cout << "Set color temperature corrections" << std::endl; + + // create command + Json::Value command; + command["command"] = "temperature"; + Json::Value & temp = command["temperature"]; + + if (temperatureId != nullptr) + { + temp["id"] = *temperatureId; + } + + if (temperature != nullptr) + { + Json::Value & v = temp["correctionValues"]; + v.append(temperature->valueRed); + v.append(temperature->valueGreen); + v.append(temperature->valueBlue); + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} + Json::Value JsonConnection::sendMessage(const Json::Value & message) { // serialize message (FastWriter already appends a newline) diff --git a/src/hyperion-remote/JsonConnection.h b/src/hyperion-remote/JsonConnection.h index 472c5565..48455110 100644 --- a/src/hyperion-remote/JsonConnection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -14,6 +14,7 @@ // hyperion-remote includes #include "ColorTransformValues.h" +#include "ColorCorrectionValues.h" /// /// Connection class to setup an connection to the hyperion server and execute commands @@ -89,6 +90,8 @@ public: /// @param transformId The identifier of the transform to set /// @param saturation The HSV saturation gain /// @param value The HSV value gain + /// @param saturationL The HSL saturation gain + /// @param luminance The HSL luminance gain /// @param threshold The threshold /// @param gamma The gamma value /// @param blacklevel The blacklevel @@ -98,10 +101,34 @@ public: std::string * transformId, double * saturation, double * value, + double * saturationL, + double * luminance, ColorTransformValues * threshold, ColorTransformValues * gamma, ColorTransformValues * blacklevel, ColorTransformValues * whitelevel); + + /// + /// Set the color correction of the leds + /// + /// @note Note that providing a NULL will leave the settings on the server unchanged + /// + /// @param correctionId The identifier of the correction to set + /// @param correction The correction values + void setCorrection( + std::string * correctionId, + ColorCorrectionValues * correction); + + /// + /// Set the color temperature of the leds + /// + /// @note Note that providing a NULL will leave the settings on the server unchanged + /// + /// @param temperatureId The identifier of the correction to set + /// @param temperature The temperature correction values + void setTemperature( + std::string * temperatureId, + ColorCorrectionValues * temperature); private: /// diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 42df87e9..57f8448a 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -61,20 +61,26 @@ int main(int argc, char * argv[]) IntParameter & argDuration = parameters.add ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]"); ColorParameter & argColor = parameters.add ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name. The color may be repeated multiple time like: RRGGBBRRGGBB)"); ImageParameter & argImage = parameters.add ('i', "image" , "Set the leds to the colors according to the given image file"); - StringParameter & argEffect = parameters.add ('e', "effect" , "Enable the effect with the given name"); - StringParameter & argEffectArgs = parameters.add (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string."); + StringParameter & argEffect = parameters.add ('e', "effect" , "Enable the effect with the given name"); + StringParameter & argEffectArgs = parameters.add (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string."); SwitchParameter<> & argServerInfo = parameters.add >('l', "list" , "List server info"); SwitchParameter<> & argClear = parameters.add >('x', "clear" , "Clear data for the priority channel provided by the -p option"); SwitchParameter<> & argClearAll = parameters.add >(0x0, "clearall" , "Clear data for all active priority channels"); StringParameter & argId = parameters.add ('q', "qualifier" , "Identifier(qualifier) of the transform to set"); DoubleParameter & argSaturation = parameters.add ('s', "saturation", "Set the HSV saturation gain of the leds"); DoubleParameter & argValue = parameters.add ('v', "value" , "Set the HSV value gain of the leds"); + DoubleParameter & argSaturationL = parameters.add ('u', "saturationL", "Set the HSL saturation gain of the leds"); + DoubleParameter & argLuminance = parameters.add ('m', "luminance" , "Set the HSL luminance gain of the leds"); TransformParameter & argGamma = parameters.add('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated values)"); TransformParameter & argThreshold = parameters.add('t', "threshold" , "Set the threshold of the leds (requires 3 space seperated values between 0.0 and 1.0)"); TransformParameter & argBlacklevel = parameters.add('b', "blacklevel", "Set the blacklevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); TransformParameter & argWhitelevel = parameters.add('w', "whitelevel", "Set the whitelevel of the leds (requires 3 space seperated values which are normally between 0.0 and 1.0)"); SwitchParameter<> & argPrint = parameters.add >(0x0, "print" , "Print the json input and output messages on stdout"); SwitchParameter<> & argHelp = parameters.add >('h', "help" , "Show this help message and exit"); + StringParameter & argIdC = parameters.add ('y', "qualifier" , "Identifier(qualifier) of the correction to set"); + CorrectionParameter & argCorrection = parameters.add('Y', "correction" , "Set the correction of the leds (requires 3 space seperated values between 0 and 255)"); + StringParameter & argIdT = parameters.add ('z', "qualifier" , "Identifier(qualifier) of the temperature to set"); + CorrectionParameter & argTemperature = parameters.add('Z', "temperature" , "Set the temperature correction of the leds (requires 3 space seperated values between 0 and 255)"); // set the default values argAddress.setDefault(defaultServerAddress.toStdString()); @@ -93,27 +99,35 @@ int main(int argc, char * argv[]) } // check if at least one of the available color transforms is set - bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); + bool colorTransform = argSaturation.isSet() || argValue.isSet() || argSaturationL.isSet() || argLuminance.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); // check that exactly one command was given - int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); + int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform, argCorrection.isSet(), argTemperature.isSet()}); if (commandCount != 1) { std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl; std::cerr << " " << argColor.usageLine() << std::endl; std::cerr << " " << argImage.usageLine() << std::endl; - std::cerr << " " << argEffect.usageLine() << std::endl; + std::cerr << " " << argEffect.usageLine() << std::endl; std::cerr << " " << argServerInfo.usageLine() << std::endl; std::cerr << " " << argClear.usageLine() << std::endl; std::cerr << " " << argClearAll.usageLine() << std::endl; - std::cerr << "or one or more of the available color transformations:" << std::endl; + std::cerr << "one or more of the available color transformations:" << std::endl; std::cerr << " " << argId.usageLine() << std::endl; std::cerr << " " << argSaturation.usageLine() << std::endl; std::cerr << " " << argValue.usageLine() << std::endl; + std::cerr << " " << argSaturationL.usageLine() << std::endl; + std::cerr << " " << argLuminance.usageLine() << std::endl; std::cerr << " " << argThreshold.usageLine() << std::endl; std::cerr << " " << argGamma.usageLine() << std::endl; std::cerr << " " << argBlacklevel.usageLine() << std::endl; std::cerr << " " << argWhitelevel.usageLine() << std::endl; + std::cerr << "one or more of the available color corrections:" << std::endl; + std::cerr << " " << argIdC.usageLine() << std::endl; + std::cerr << " " << argCorrection.usageLine() << std::endl; + std::cerr << "or one or more of the available color temperature adjustment:" << std::endl; + std::cerr << " " << argIdT.usageLine() << std::endl; + std::cerr << " " << argTemperature.usageLine() << std::endl; return 1; } @@ -129,11 +143,11 @@ int main(int argc, char * argv[]) { connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue()); } - else if (argEffect.isSet()) - { - connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue()); - } - else if (argServerInfo.isSet()) + else if (argEffect.isSet()) + { + connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue()); + } + else if (argServerInfo.isSet()) { QString info = connection.getServerInfo(); std::cout << "Server info:\n" << info.toStdString() << std::endl; @@ -149,26 +163,54 @@ int main(int argc, char * argv[]) else if (colorTransform) { std::string transId; - double saturation, value; + double saturation, value, saturationL, luminance; ColorTransformValues threshold, gamma, blacklevel, whitelevel; if (argId.isSet()) transId = argId.getValue(); if (argSaturation.isSet()) saturation = argSaturation.getValue(); if (argValue.isSet()) value = argValue.getValue(); + if (argSaturationL.isSet()) saturationL = argSaturationL.getValue(); + if (argLuminance.isSet()) luminance = argLuminance.getValue(); if (argThreshold.isSet()) threshold = argThreshold.getValue(); if (argGamma.isSet()) gamma = argGamma.getValue(); if (argBlacklevel.isSet()) blacklevel = argBlacklevel.getValue(); if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue(); - + connection.setTransform( argId.isSet() ? &transId : nullptr, argSaturation.isSet() ? &saturation : nullptr, argValue.isSet() ? &value : nullptr, + argSaturationL.isSet() ? &saturationL : nullptr, + argLuminance.isSet() ? &luminance : nullptr, argThreshold.isSet() ? &threshold : nullptr, argGamma.isSet() ? &gamma : nullptr, argBlacklevel.isSet() ? &blacklevel : nullptr, argWhitelevel.isSet() ? &whitelevel : nullptr); } + else if (argCorrection.isSet()) + { + std::string corrId; + ColorCorrectionValues correction; + + if (argIdC.isSet()) corrId = argIdC.getValue(); + if (argCorrection.isSet()) correction = argCorrection.getValue(); + + connection.setCorrection( + argIdC.isSet() ? &corrId : nullptr, + argCorrection.isSet() ? &correction : nullptr); + } + else if (argTemperature.isSet()) + { + std::string tempId; + ColorCorrectionValues temperature; + + if (argIdT.isSet()) tempId = argIdT.getValue(); + if (argTemperature.isSet()) temperature = argTemperature.getValue(); + + connection.setTemperature( + argIdT.isSet() ? &tempId : nullptr, + argTemperature.isSet() ? &temperature : nullptr); + } } catch (const std::runtime_error & e) {