diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fc263c7..487b066c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,9 @@ message(STATUS "ENABLE_X11 = " ${ENABLE_X11}) option(ENABLE_QT5 "Enable QT5" OFF) message(STATUS "ENABLE_QT5 = " ${ENABLE_QT5}) +option(ENABLE_TESTS "Compile additional test applications" OFF) +message(STATUS "ENABLE_TESTS = " ${ENABLE_TESTS}) + if(ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) message(FATAL_ERROR "V4L2 grabber requires PROTOBUF. Disable V4L2 or enable PROTOBUF") endif(ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) @@ -59,19 +62,26 @@ if(ENABLE_OSX AND ENABLE_DISPMANX) message(FATAL_ERROR "dispmanx grabber and osx grabber cannot be used at the same time") endif(ENABLE_OSX AND ENABLE_DISPMANX) + +SET ( PROTOBUF_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/proto ) +SET ( PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto ) + #if(ENABLE_QT5) # TODO vs ENABLE_QT4? #endif(ENABLE_QT5) # Createt the configuration file + +# Add project specific cmake modules (find, etc) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +find_package(GitVersion) + # configure a header file to pass some of the CMake settings # to the source code configure_file("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h") include_directories("${PROJECT_BINARY_DIR}") -# Add project specific cmake modules (find, etc) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - if(ENABLE_QT5) ADD_DEFINITIONS ( -DENABLE_QT5 ) #find_package(Qt5Widgets) @@ -136,7 +146,10 @@ configure_file(config/hyperion_x86.config.json ${LIBRARY_OUTPUT_PATH} @ONLY) add_subdirectory(dependencies) add_subdirectory(libsrc) add_subdirectory(src) -add_subdirectory(test) +if (ENABLE_TESTS) + add_subdirectory(test) +endif (ENABLE_TESTS) + # Add the doxygen generation directory add_subdirectory(doc) diff --git a/CompileHowto.txt b/CompileHowto.txt index da8f38c9..0aa3f059 100644 --- a/CompileHowto.txt +++ b/CompileHowto.txt @@ -27,7 +27,7 @@ cd "$HYPERION_DIR/build" cmake -DCMAKE_BUILD_TYPE=Release -Wno-dev .. # run cmake to generate make files on the raspberry pi WITH PWM SUPPORT cmake -DENABLE_WS2812BPWM=ON -DENABLE_WS281XPWM=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev .. -# or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices +# or if you are not compiling on the raspberry pi (e.g. OrangePi) and need to disable the Dispmanx grabber and support for spi devices cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_X11=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev .. # as an alternative for the dispmanx grabber on non-rpi devices (e.g. cubox-i) you could try the framebuffer grabber cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF -DENABLE_FB=ON -DCMAKE_BUILD_TYPE=Release -Wno-dev .. diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 69147bb2..ddfff53c 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -30,3 +30,4 @@ // Define to enable the osx grabber #cmakedefine ENABLE_OSX +#define HYPERION_VERSION_ID "${HYPERION_VERSION_ID}" diff --git a/ISSUE_TEMPLATE b/ISSUE_TEMPLATE index 7b2f9f22..e8f12f0a 100644 --- a/ISSUE_TEMPLATE +++ b/ISSUE_TEMPLATE @@ -4,5 +4,5 @@ Please check the wiki in case your problem is already known/feature requested.** **1.** Used hardware and sofware (Wetek,RPi1,Rpi2,... Ubuntu 14.04(64bit),OSX,OpenELEC,OSMC,XBian,...) **2.** Your LED device and additional hardware (if used) (WS2801,APA102,WS2812B,... connected through (direct,arduino uno,...)) **3.** Please upload your Hyperion log to pastebin.com and insert the link. Have a look at the wiki how you get one. -**4.** Please upload your "Hyperion Configuration File" to www.jsoneditoronline.org and insert the link. +**4.** Please upload your "Hyperion Configuration File" to pastebin.com and insert the link. diff --git a/bin/create_oe_depedencies.sh b/bin/create_oe_depedencies.sh index dd2883e4..194da7a8 100644 --- a/bin/create_oe_depedencies.sh +++ b/bin/create_oe_depedencies.sh @@ -23,5 +23,6 @@ tar --create --verbose --gzip --absolute-names --show-transformed-names --derefe "$IMX6_ROOTFS/usr/lib/arm-linux-gnueabihf/libXrender.so.1" \ "$IMX6_ROOTFS/usr/lib/arm-linux-gnueabihf/libXt.so.6" \ "./openelec/hyperiond.sh" \ + "./openelec/hyperion-v4l2.sh" \ "./openelec/hyperion-remote.sh" diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index 5bdc5c2a..40ae503a 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -4,8 +4,18 @@ # Make sure /sbin is on the path (for service to find sub scripts) PATH="/sbin:$PATH" +#Check which arguments are used +if [ "$1" = "HyperConInstall" ] || [ "$2" = "HyperConInstall" ]; then + HCInstall=1 +else HCInstall=0 +fi +if [ "$1" = "WS281X" ] || [ "$2" = "WS281X" ]; then + PWM=1 +else PWM=0 +fi + #Check if HyperCon is logged in as root -if [ $(id -u) != 0 ] && [ "$1" = "HyperConInstall" ]; then +if [ $(id -u) != 0 ] && [ $HCInstall -eq 1 ]; then echo '---> Critical Error: Please connect as user "root" through HyperCon' echo '---> We need admin privileges to install/update your Hyperion! -> abort' exit 1 @@ -78,7 +88,7 @@ if [ $OS_OPENELEC -ne 1 ]; then fi #Check, if dtparam=spi=on is in place (not for OPENELEC) -if [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -ne 1 ]; then +if [ $PWM -ne 1 ] && [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -ne 1 ]; then SPIOK=`grep '^\dtparam=spi=on' /boot/config.txt | wc -l` if [ $SPIOK -ne 1 ]; then echo '---> Raspberry Pi found, but SPI is not ready, we write "dtparam=spi=on" to /boot/config.txt' @@ -88,7 +98,7 @@ if [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -ne 1 ]; then fi #Check, if dtparam=spi=on is in place (just for OPENELEC) -if [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -eq 1 ]; then +if [ $PWM -ne 1 ] && [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -eq 1 ]; then SPIOK=`grep '^\dtparam=spi=on' /flash/config.txt | wc -l` if [ $SPIOK -ne 1 ]; then mount -o remount,rw /flash @@ -98,6 +108,28 @@ if [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -eq 1 ]; then REBOOTMESSAGE="echo Please reboot your OpenELEC, we inserted dtparam=spi=on to /flash/config.txt" fi fi + +#Check, if dtoverlay=pwm is in place (not for OPENELEC) +#if [ $PWM -eq 1 ] && [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -ne 1 ]; then +# PWMOK=`grep '^\dtoverlay=pwm' /boot/config.txt | wc -l` +# if [ $PWMOK -ne 1 ]; then +# echo '---> Raspberry Pi found, but PWM is not ready, we write "dtoverlay=pwm" to /boot/config.txt' +# sed -i '$a dtoverlay=pwm' /boot/config.txt +# PWMREBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtoverlay=pwm to /boot/config.txt" +# fi +#fi + +#Check, if dtoverlay=pwm is in place (just for OPENELEC) +if [ $PWM -eq 1 ] && [ $CPU_RPI -eq 1 ] && [ $OS_OPENELEC -eq 1 ]; then + PWMOK=`grep '^\dtoverlay=pwm' /flash/config.txt | wc -l` + if [ $PWMOK -ne 1 ]; then + mount -o remount,rw /flash + echo '---> Raspberry Pi with OpenELEC found, but PWM is not ready, we write "dtoverlay=pwm" to /flash/config.txt' + sed -i '$a dtoverlay=pwm' /flash/config.txt + mount -o remount,ro /flash + PWMREBOOTMESSAGE="echo Please reboot your OpenELEC, we inserted dtoverlay=pwm to /flash/config.txt" + fi +fi #Backup the .conf files, if present echo '---> Backup Hyperion configuration(s), if present' rm -f /tmp/*.json 2>/dev/null @@ -177,16 +209,29 @@ elif [ $OS_OPENELEC -eq 1 ]; then echo "/storage/hyperion/bin/hyperiond.sh /storage/.config/hyperion.config.json > /dev/null 2>&1 &" >> /storage/.config/autostart.sh chmod +x /storage/.config/autostart.sh fi + # only add hyperion-x11 to startup, if not found and x32x64 detected + if [ $CPU_X32X64 -eq 1 ] && [ `cat /storage/.config/autostart.sh 2>/dev/null | grep hyperion-x11 | wc -l` -eq 0 ]; then + echo '---> Adding Hyperion-x11 to OpenELEC autostart.sh' + echo "DISPLAY=:0.0 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/storage/hyperion/bin /storage/hyperion/bin/hyperion-x11 /dev/null 2>&1 &" >> /storage/.config/autostart.sh + fi elif [ $USE_SYSTEMD -eq 1 ]; then echo '---> Installing systemd script' #place startup script for systemd and activate #Problem with systemd to enable symlinks - Bug? Workaround cp -n (overwrite never) - #Bad workaround for Jessie users that used the official script for install + #Bad workaround for Jessie (systemd) users that used the official script for install update-rc.d -f hyperion remove 2>/dev/null rm /etc/init.d/hyperion 2>/dev/null cp -n /opt/hyperion/init.d/hyperion.systemd.sh /etc/systemd/system/hyperion.service systemctl -q enable hyperion.service - if [ $OS_OSMC -eq 1 ]; then + if [ $PWM -eq 1 ] && [ $OS_OSMC -eq 1 ]; then + echo '---> Modify systemd script for OSMC usage (PWM Support)' + # Wait until kodi is sarted (for xbmc checker) and FIX user in case it is wrong (need root for access to pwm!)! + sed -i '/After = mediacenter.service/d' /etc/systemd/system/hyperion.service + sed -i '/Unit/a After = mediacenter.service' /etc/systemd/system/hyperion.service + sed -i 's/User=osmc/User=root/g' /etc/systemd/system/hyperion.service + sed -i 's/Group=osmc/Group=root/g' /etc/systemd/system/hyperion.service + systemctl -q daemon-reload + elif [ $OS_OSMC -eq 1 ]; then echo '---> Modify systemd script for OSMC usage' # Wait until kodi is sarted (for xbmc checker) and replace user (for remote control through osmc) sed -i '/After = mediacenter.service/d' /etc/systemd/system/hyperion.service @@ -226,15 +271,16 @@ echo 'Please get a new HyperCon version to benefit from the latest features!' echo 'Create a new config file, if you encounter problems!' $HINTMESSAGE $REBOOTMESSAGE +$PWMREBOOTMESSAGE echo '*******************************************************************************' ## Force reboot and prevent prompt if spi is added during a HyperCon Install -if [ "$1" = "HyperConInstall" ] && [ $CPU_RPI -eq 1 ] && [ $SPIOK -ne 1 ]; then - echo "Rebooting now, we added dtparam=spi=on to config.txt" +if ( [ "$HCInstall" = "1" ] && [ "$CPU_RPI" = "1" ] ) && ( [ "$SPIOK" = "0" ] || [ "$PWMOK" = "0" ] ); then + echo "Rebooting now, we added dtparam=spi=on and/or dtoverlay=pwm to config.txt" reboot exit 0 fi #Prompt for reboot, if spi added to config.txt -if [ $CPU_RPI -eq 1 ] && [ $SPIOK -ne 1 ];then +if ( [ "$CPU_RPI" = "1" ] ) && ( [ "$SPIOK" = "0" ] || [ "$PWMOK" = "0" ] ); then while true do echo -n "---> Do you want to reboot your Raspberry Pi now? (y or n) :" diff --git a/bin/remove_hyperion.sh b/bin/remove_hyperion.sh index 1b56b84c..8ce5fd38 100644 --- a/bin/remove_hyperion.sh +++ b/bin/remove_hyperion.sh @@ -70,6 +70,7 @@ elif [ $OS_OPENELEC -eq 1 ]; then # Remove Hyperion from OpenELEC autostart.sh echo "---> Remove Hyperion from OpenELEC autostart.sh" sed -i "/hyperiond/d" /storage/.config/autostart.sh 2>/dev/null + sed -i "/hyperion-x11/d" /storage/.config/autostart.sh 2>/dev/null elif [ $USE_SYSTEMD -eq 1 ]; then # Delete and disable Hyperion systemd script echo '---> Delete and disable Hyperion systemd script' diff --git a/cmake/FindGitVersion.cmake b/cmake/FindGitVersion.cmake new file mode 100644 index 00000000..3ce5fe93 --- /dev/null +++ b/cmake/FindGitVersion.cmake @@ -0,0 +1,9 @@ + +execute_process( COMMAND git log -1 --format=%cn-%t/%h-%ct WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE BUILD_ID ERROR_QUIET ) +execute_process( COMMAND sh -c "git branch | grep '^*' | sed 's;^*;;g' " WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE VERSION_ID ERROR_QUIET ) + +STRING ( STRIP "${BUILD_ID}" BUILD_ID ) +STRING ( STRIP "${VERSION_ID}" VERSION_ID ) +SET ( HYPERION_VERSION_ID "${VERSION_ID} (${BUILD_ID}" ) +message ( STATUS "Current Version: ${HYPERION_VERSION_ID})" ) + 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/deploy/hyperion_rpi.tar.gz.REMOVED.git-id b/deploy/hyperion_rpi.tar.gz.REMOVED.git-id index df62cd1b..59522dc2 100644 --- a/deploy/hyperion_rpi.tar.gz.REMOVED.git-id +++ b/deploy/hyperion_rpi.tar.gz.REMOVED.git-id @@ -1 +1 @@ -63bda72fdb3fb504564f067795f74e9b45eb99ba \ No newline at end of file +bc3bead4aa43a0f90fb15fb300e63eb6ba6885bf \ No newline at end of file diff --git a/deploy/hyperion_wetek.tar.gz.REMOVED.git-id b/deploy/hyperion_wetek.tar.gz.REMOVED.git-id index 9cc7dee0..e5d26a68 100644 --- a/deploy/hyperion_wetek.tar.gz.REMOVED.git-id +++ b/deploy/hyperion_wetek.tar.gz.REMOVED.git-id @@ -1 +1 @@ -03e78585af86a18889ecd1d8b910d76155608cd6 \ No newline at end of file +e3cbd5bb82a29a50ee85a2fab6a1a0d610938f37 \ No newline at end of file diff --git a/deploy/hyperion_x32x64.tar.gz.REMOVED.git-id b/deploy/hyperion_x32x64.tar.gz.REMOVED.git-id index d2485017..977b46b5 100644 --- a/deploy/hyperion_x32x64.tar.gz.REMOVED.git-id +++ b/deploy/hyperion_x32x64.tar.gz.REMOVED.git-id @@ -1 +1 @@ -9a00f3dc5684cab0fede56348df91c046634ff11 \ No newline at end of file +dc2a99908af86a6e3a2b81721bc69e7d614623c3 \ No newline at end of file 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/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 49b197f6..3c925e8d 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -180,7 +180,7 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) device = deviceLightpack; } - else if (type == "multi-lightpack" || type == "multi_lightpack") + else if (type == "multi-lightpack") { LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack(); deviceLightpack->open(); @@ -327,8 +327,9 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) const int leds = deviceConfig.get("leds", 12).asInt(); const uint32_t freq = deviceConfig.get("freq", (Json::UInt)800000ul).asInt(); const int dmanum = deviceConfig.get("dmanum", 5).asInt(); + const int pwmchannel = deviceConfig.get("pwmchannel", 0).asInt(); - LedDeviceWS281x * ledDeviceWS281x = new LedDeviceWS281x(gpio, leds, freq, dmanum); + LedDeviceWS281x * ledDeviceWS281x = new LedDeviceWS281x(gpio, leds, freq, dmanum, pwmchannel); device = ledDeviceWS281x; } #endif 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/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 332176ad..eec1c824 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -216,7 +216,7 @@ int LedDevicePhilipsHue::switchOff() { } void LedDevicePhilipsHue::put(QString route, QString content) { - QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); + QString url = getUrl(route); // Perfrom request QNetworkRequest request(url); QNetworkReply* reply = manager->put(request, content.toLatin1()); @@ -225,10 +225,12 @@ void LedDevicePhilipsHue::put(QString route, QString content) { loop.connect(reply, SIGNAL(finished()), SLOT(quit())); // Go into the loop until the request is finished. loop.exec(); + // Free space. + reply->deleteLater(); } QByteArray LedDevicePhilipsHue::get(QString route) { - QString url = QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); + QString url = getUrl(route); // Perfrom request QNetworkRequest request(url); QNetworkReply* reply = manager->get(request); @@ -238,7 +240,11 @@ QByteArray LedDevicePhilipsHue::get(QString route) { // Go into the loop until the request is finished. loop.exec(); // Read all data of the response. - return reply->readAll(); + QByteArray response = reply->readAll(); + // Free space. + reply->deleteLater(); + // Return response + return response; } QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) { @@ -249,6 +255,10 @@ QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { return QString("lights/%1").arg(lightId); } +QString LedDevicePhilipsHue::getUrl(QString route) { + return QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); +} + void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Clear saved lamps. lights.clear(); @@ -257,18 +267,24 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { Json::FastWriter writer; // Read light ids if none have been supplied by the user. if (lightIds.size() != nLights) { + lightIds.clear(); + // QByteArray response = get("lights"); Json::Value json; if (!reader.parse(QString(response).toStdString(), json)) { - throw std::runtime_error("No lights found"); + throw std::runtime_error(("No lights found at " + getUrl("lights")).toStdString()); } // Loop over all children. - for (Json::ValueIterator it = json.begin(); it != json.end() && lightIds.size() <= nLights; it++) { + for (Json::ValueIterator it = json.begin(); it != json.end() && lightIds.size() < nLights; it++) { int lightId = atoi(it.key().asCString()); lightIds.push_back(lightId); std::cout << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): found light with id " << lightId << "." << std::endl; } + // Check if we found enough lights. + if (lightIds.size() != nLights) { + throw std::runtime_error(("Not enough lights found at " + getUrl("lights")).toStdString()); + } } // Iterate lights. for (unsigned int i = 0; i < nLights; i++) { @@ -278,12 +294,22 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { Json::Value json; if (!reader.parse(QString(response).toStdString(), json)) { // Error occured, break loop. - std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights - << "): got invalid response from light with id " << lightIds.at(i) << "." << std::endl; + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got invalid response from light " + << getUrl(getRoute(lightIds.at(i))).toStdString() << "." << std::endl; break; } // Get state object values which are subject to change. Json::Value state(Json::objectValue); + if (!json.isMember("state")) { + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no state for light from " + << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; + break; + } + if (!json["state"].isMember("on")) { + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no valid state from light " + << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; + break; + } state["on"] = json["state"]["on"]; if (json["state"]["on"] == true) { state["xy"] = json["state"]["xy"]; diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 61047062..5e5409b6 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -207,6 +207,13 @@ private: /// QString getRoute(unsigned int lightId); + /// + /// @param route + /// + /// @return the full URL of the request. + /// + QString getUrl(QString route); + /// /// Queries the status of all lights and saves it. /// diff --git a/libsrc/leddevice/LedDeviceWS281x.cpp b/libsrc/leddevice/LedDeviceWS281x.cpp index 10b5d2e0..3871b553 100644 --- a/libsrc/leddevice/LedDeviceWS281x.cpp +++ b/libsrc/leddevice/LedDeviceWS281x.cpp @@ -3,22 +3,27 @@ #include "LedDeviceWS281x.h" // Constructor -LedDeviceWS281x::LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, const int dmanum) +LedDeviceWS281x::LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, const int dmanum, const int pwmchannel) { initialized = false; led_string.freq = freq; led_string.dmanum = dmanum; - led_string.channel[0].gpionum = gpio; - led_string.channel[0].invert = 0; - led_string.channel[0].count = leds; - led_string.channel[0].brightness = 255; - led_string.channel[0].strip_type = WS2811_STRIP_RGB; + if (pwmchannel != 0 && pwmchannel != 1) { + std::cout << "WS281x: invalid PWM channel; must be 0 or 1." << std::endl; + throw -1; + } + chan = pwmchannel; + led_string.channel[chan].gpionum = gpio; + led_string.channel[chan].invert = 0; + led_string.channel[chan].count = leds; + led_string.channel[chan].brightness = 255; + led_string.channel[chan].strip_type = WS2811_STRIP_RGB; - led_string.channel[1].gpionum = 0; - led_string.channel[1].invert = 0; - led_string.channel[1].count = 0; - led_string.channel[1].brightness = 0; - led_string.channel[0].strip_type = WS2811_STRIP_RGB; + led_string.channel[!chan].gpionum = 0; + led_string.channel[!chan].invert = 0; + led_string.channel[!chan].count = 0; + led_string.channel[!chan].brightness = 0; + led_string.channel[!chan].strip_type = WS2811_STRIP_RGB; if (ws2811_init(&led_string) < 0) { std::cout << "Unable to initialize ws281x library." << std::endl; throw -1; @@ -35,12 +40,12 @@ int LedDeviceWS281x::write(const std::vector &ledValues) int idx = 0; for (const ColorRgb& color : ledValues) { - if (idx >= led_string.channel[0].count) + if (idx >= led_string.channel[chan].count) break; - led_string.channel[0].leds[idx++] = ((uint32_t)color.red << 16) + ((uint32_t)color.green << 8) + color.blue; + led_string.channel[chan].leds[idx++] = ((uint32_t)color.red << 16) + ((uint32_t)color.green << 8) + color.blue; } - while (idx < led_string.channel[0].count) - led_string.channel[0].leds[idx++] = 0; + while (idx < led_string.channel[chan].count) + led_string.channel[chan].leds[idx++] = 0; if (ws2811_render(&led_string)) return -1; @@ -57,8 +62,8 @@ int LedDeviceWS281x::switchOff() return -1; int idx = 0; - while (idx < led_string.channel[0].count) - led_string.channel[0].leds[idx++] = 0; + while (idx < led_string.channel[chan].count) + led_string.channel[chan].leds[idx++] = 0; if (ws2811_render(&led_string)) return -1; diff --git a/libsrc/leddevice/LedDeviceWS281x.h b/libsrc/leddevice/LedDeviceWS281x.h index 429a092f..61a58800 100644 --- a/libsrc/leddevice/LedDeviceWS281x.h +++ b/libsrc/leddevice/LedDeviceWS281x.h @@ -17,7 +17,7 @@ public: /// @param freq The target frequency for the data line, default is 800000 /// @param dmanum The DMA channel to use, default is 5 /// - LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, int dmanum); + LedDeviceWS281x(const int gpio, const int leds, const uint32_t freq, int dmanum, int pwmchannel); /// /// Destructor of the LedDevice, waits for DMA to complete and then cleans up @@ -37,6 +37,7 @@ public: private: ws2811_t led_string; + int chan; bool initialized; }; 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-aml/CMakeLists.txt b/src/hyperion-aml/CMakeLists.txt index 9d365560..9445b3ec 100644 --- a/src/hyperion-aml/CMakeLists.txt +++ b/src/hyperion-aml/CMakeLists.txt @@ -50,8 +50,15 @@ target_link_libraries(${PROJECT_NAME} pthread ) +qt4_use_modules(${PROJECT_NAME} + Core + Gui + Network) + if(ENABLE_QT5) qt5_use_modules(${PROJECT_NAME} Widgets Core Gui Network) else(ENABLE_QT5) qt4_use_modules(${PROJECT_NAME} Core Gui Network ) endif(ENABLE_QT5) + +install ( TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index 42870455..4932c0dc 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -10,6 +10,9 @@ #include #include "AmlogicWrapper.h" +#include "HyperionConfig.h" + + using namespace vlofgren; // save the image as screenshot @@ -22,6 +25,11 @@ void saveScreenshot(const char * filename, const Image & image) int main(int argc, char ** argv) { + std::cout + << "hyperion-aml:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; + QCoreApplication app(argc, argv); try diff --git a/src/hyperion-dispmanx/CMakeLists.txt b/src/hyperion-dispmanx/CMakeLists.txt index 6252943c..ae4c02d2 100644 --- a/src/hyperion-dispmanx/CMakeLists.txt +++ b/src/hyperion-dispmanx/CMakeLists.txt @@ -59,3 +59,6 @@ if(ENABLE_QT5) else(ENABLE_QT5) qt4_use_modules(${PROJECT_NAME} Core Gui Network ) endif(ENABLE_QT5) + + +install ( TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) diff --git a/src/hyperion-dispmanx/hyperion-dispmanx.cpp b/src/hyperion-dispmanx/hyperion-dispmanx.cpp index e5ebf429..6f1f66ad 100644 --- a/src/hyperion-dispmanx/hyperion-dispmanx.cpp +++ b/src/hyperion-dispmanx/hyperion-dispmanx.cpp @@ -9,6 +9,8 @@ #include #include "DispmanxWrapper.h" +#include "HyperionConfig.h" + using namespace vlofgren; // save the image as screenshot @@ -21,6 +23,11 @@ void saveScreenshot(const char * filename, const Image & image) int main(int argc, char ** argv) { + std::cout + << "hyperion-dispmanx:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; + QCoreApplication app(argc, argv); try diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index 3e36d2b2..36ca8cc2 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -41,3 +41,6 @@ if(ENABLE_QT5) else(ENABLE_QT5) qt4_use_modules(${PROJECT_NAME} Core Gui Network ) endif(ENABLE_QT5) + +install ( TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) + 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 3636721f..57f8448a 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -13,6 +13,9 @@ #include "CustomParameter.h" #include "JsonConnection.h" +#include "HyperionConfig.h" + + using namespace vlofgren; /// Count the number of true values in a list of booleans @@ -28,6 +31,11 @@ int count(std::initializer_list values) int main(int argc, char * argv[]) { + std::cout + << "hyperion-remote:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; + QCoreApplication app(argc, argv); // force the locale @@ -53,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()); @@ -85,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; } @@ -121,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; @@ -141,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) { diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 22e512b2..ee1882a1 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -59,3 +59,6 @@ if(ENABLE_QT5) else(ENABLE_QT5) qt4_use_modules(${PROJECT_NAME} Core Gui Network ) endif(ENABLE_QT5) + + +install ( TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index ed56dbb7..22add6c7 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -24,6 +24,8 @@ #include "PixelFormatParameter.h" #include "ScreenshotHandler.h" +#include "HyperionConfig.h" + using namespace vlofgren; // save the image as screenshot @@ -36,6 +38,11 @@ void saveScreenshot(void *, const Image & image) int main(int argc, char** argv) { + std::cout + << "hyperion-v4l2:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; + QCoreApplication app(argc, argv); // force the locale diff --git a/src/hyperion-x11/CMakeLists.txt b/src/hyperion-x11/CMakeLists.txt index 13e1e1a6..69744f4f 100644 --- a/src/hyperion-x11/CMakeLists.txt +++ b/src/hyperion-x11/CMakeLists.txt @@ -60,3 +60,5 @@ if(ENABLE_QT5) else(ENABLE_QT5) qt4_use_modules(${PROJECT_NAME} Core Gui Network ) endif(ENABLE_QT5) + +install ( TARGETS ${PROJECT_NAME} DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 42853af1..776b3326 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -8,6 +8,7 @@ #include "protoserver/ProtoConnectionWrapper.h" #include "X11Wrapper.h" +#include "HyperionConfig.h" using namespace vlofgren; @@ -21,6 +22,11 @@ void saveScreenshot(const char * filename, const Image & image) int main(int argc, char ** argv) { + std::cout + << "hyperion-x11:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; + QCoreApplication app(argc, argv); try diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 70c2381b..5aef74d2 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -33,3 +33,5 @@ endif (ENABLE_AMLOGIC) if (ENABLE_PROTOBUF) target_link_libraries(hyperiond protoserver) endif (ENABLE_PROTOBUF) + +install ( TARGETS hyperiond DESTINATION "${CMAKE_SOURCE_DIR}/deploy/bin" ) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 7f658e9d..6a883ea7 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -92,11 +92,13 @@ Json::Value loadConfig(const std::string & configFile) int main(int argc, char** argv) { - std::cout << "Application build time: " << __DATE__ << " " << __TIME__ << std::endl; + std::cout + << "Hyperiond:" << std::endl + << "\tversion : " << HYPERION_VERSION_ID << std::endl + << "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl; // Initialising QCoreApplication QCoreApplication app(argc, argv); - std::cout << "QCoreApplication initialised" << std::endl; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler);