diff --git a/.gitignore b/.gitignore index 28305334..6a5b347a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /*.user /build +/build-x86 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e8ad3ff..1e09ab45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Define the main-project name -project(Hyperion) +project(hyperion) # define the minimum cmake version (as required by cmake) cmake_minimum_required(VERSION 2.8) @@ -35,6 +35,7 @@ include_directories(${CMAKE_SOURCE_DIR}/include) # Prefer static linking over dynamic #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") +#set(CMAKE_BUILD_TYPE "Debug") set(CMAKE_BUILD_TYPE "Release") # enable C++11 @@ -43,14 +44,13 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") # Configure the use of QT4 find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED QUIET) -#SET(QT_DONT_USE_QTGUI TRUE) -#SET(QT_USE_QTCONSOLE TRUE) include(${QT_USE_FILE}) add_definitions(${QT_DEFINITIONS}) link_directories(${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnueabihf) configure_file(bin/install_hyperion.sh ${LIBRARY_OUTPUT_PATH} @ONLY) configure_file(config/hyperion.config.json ${LIBRARY_OUTPUT_PATH} @ONLY) +configure_file(config/hyperion_x86.config.json ${LIBRARY_OUTPUT_PATH} @ONLY) # Add the source/lib directories add_subdirectory(dependencies) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 5f569a43..1c5963ce 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -2,7 +2,7 @@ // Generated by: HyperCon (The Hyperion deamon configuration file builder { - /// Device configuration contains the following fields: + /// Device configuration contains the following fields: /// * 'name' : The user friendly name of the device (only used for display purposes) /// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'ldp8806', /// 'lpd6803', 'sedu', 'adalight', 'lightpack', 'test' and 'none') @@ -34,50 +34,87 @@ /// - 'updateFrequency' The update frequency of the leds in Hz "color" : { - "hsv" : - { - "saturationGain" : 1.0000, - "valueGain" : 1.5000 - }, - "red" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 0.8000 - }, - "green" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 1.0000 - }, - "blue" : - { - "threshold" : 0.1000, - "gamma" : 2.0000, - "blacklevel" : 0.0000, - "whitelevel" : 1.0000 - }, + "transform" : + [ + { + "id" : "device_1", + "leds" : "0-24", + "hsv" : + { + "saturationGain" : 1.0000, + "valueGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + } + }, + { + "id" : "device_2", + "leds" : "25-49", + "hsv" : + { + "saturationGain" : 1.0000, + "valueGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + } + } + ], "smoothing" : { - "type" : "none", + "type" : "linear", "time_ms" : 200, "updateFrequency" : 20.0000 } }, - /// The configuration for each individual led. This contains the specification of the area - /// averaged of an input image for each led to determine its color. Each item in the list + /// The configuration for each individual led. This contains the specification of the area + /// averaged of an input image for each led to determine its color. Each item in the list /// contains the following fields: - /// * index: The index of the led. This determines its location in the string of leds; zero + /// * index: The index of the led. This determines its location in the string of leds; zero /// being the first led. - /// * hscan: The fractional part of the image along the horizontal used for the averaging + /// * hscan: The fractional part of the image along the horizontal used for the averaging /// (minimum and maximum inclusive) - /// * vscan: The fractional part of the image along the vertical used for the averaging + /// * vscan: The fractional part of the image along the vertical used for the averaging /// (minimum and maximum inclusive) - "leds" : + "leds" : [ { "index" : 0, @@ -331,15 +368,15 @@ } ], - /// The black border configuration, contains the following items: + /// The black border configuration, contains the following items: /// * enable : true if the detector should be activated "blackborderdetector" : { "enable" : true }, - /// The boot-sequence configuration, contains the following items: - /// * type : The type of the boot-sequence ('rainbow', 'knightrider', 'none') + /// The boot-sequence configuration, contains the following items: + /// * type : The type of the boot-sequence ('rainbow', 'knight_rider', 'none') /// * duration_ms : The length of the boot-sequence [ms] "bootsequence" : { @@ -347,7 +384,7 @@ "duration_ms" : 3000 }, - /// The configuration for the frame-grabber, contains the following items: + /// The configuration for the frame-grabber, contains the following items: /// * width : The width of the grabbed frames [pixels] /// * height : The height of the grabbed frames [pixels] /// * frequency_Hz : The frequency of the frame grab [Hz] @@ -358,7 +395,7 @@ "frequency_Hz" : 10.0 }, - /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: + /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: /// * xbmcAddress : The IP address of the XBMC-host /// * xbmcTcpPort : The TCP-port of the XBMC-server /// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback diff --git a/config/hyperion_x86.config.json b/config/hyperion_x86.config.json new file mode 100644 index 00000000..807177e4 --- /dev/null +++ b/config/hyperion_x86.config.json @@ -0,0 +1,438 @@ +// Automatically generated configuration file for 'Hyperion daemon' +// Generated by: HyperCon (The Hyperion deamon configuration file builder + +{ + /// Device configuration contains the following fields: + /// * 'name' : The user friendly name of the device (only used for display purposes) + /// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'ldp8806', + /// 'lpd6803', 'sedu', 'adalight', 'lightpack', 'test' and 'none') + /// * 'output' : The output specification depends on selected device. This can for example be the + /// device specifier, device serial number, or the output file name + /// * 'rate' : The baudrate of the output to the device + /// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). + "device" : + { + "name" : "MyPi", + "type" : "test", + "output" : "./hyperiond.test.out", + "rate" : 500000, + "colorOrder" : "rgb" + }, + + /// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields: + /// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters: + /// - 'saturationGain' The gain adjustement of the saturation + /// - 'valueGain' The gain adjustement of the value + /// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel: + /// - 'threshold' The minimum required input value for the channel to be on (else zero) + /// - 'gamma' The gamma-curve correction factor + /// - 'blacklevel' The lowest possible value (when the channel is black) + /// - 'whitelevel' The highest possible value (when the channel is white) + /// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning parameters: + /// - 'type' The type of smoothing algorithm ('linear' or 'none') + /// - 'time_ms' The time constant for smoothing algorithm in milliseconds + /// - 'updateFrequency' The update frequency of the leds in Hz + "color" : + { + "transform" : + [ + { + "id" : "device_1", + "leds" : "0,1,2,3-10, 12-32, 33, 34, 35-49", + "hsv" : + { + "saturationGain" : 1.0000, + "valueGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + } + }, + { + "id" : "device_2", + "leds" : "11", + "hsv" : + { + "saturationGain" : 1.0000, + "valueGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + } + } + ], + + "smoothing" : + { + "type" : "none", + "time_ms" : 200, + "updateFrequency" : 20.0000 + } + }, + + /// The configuration for each individual led. This contains the specification of the area + /// averaged of an input image for each led to determine its color. Each item in the list + /// contains the following fields: + /// * index: The index of the led. This determines its location in the string of leds; zero + /// being the first led. + /// * hscan: The fractional part of the image along the horizontal used for the averaging + /// (minimum and maximum inclusive) + /// * vscan: The fractional part of the image along the vertical used for the averaging + /// (minimum and maximum inclusive) + "leds" : + [ + { + "index" : 0, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 1, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 2, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 3, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 4, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 5, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 6, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 7, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 8, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 9, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 10, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 11, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 12, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 13, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 14, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 15, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 16, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 17, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 18, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 19, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 20, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 21, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 22, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 23, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 24, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 25, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 26, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 27, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 28, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 29, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 30, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 31, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 32, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 33, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 34, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 35, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 36, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 37, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 38, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 39, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 40, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 41, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 42, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 43, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 44, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 45, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 46, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 47, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 48, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 49, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + } + ], + + /// The black border configuration, contains the following items: + /// * enable : true if the detector should be activated + "blackborderdetector" : + { + "enable" : true + }, + + /// The boot-sequence configuration, contains the following items: + /// * type : The type of the boot-sequence ('rainbow', 'knight_rider', 'none') + /// * duration_ms : The length of the boot-sequence [ms] + "bootsequence" : + { + "type" : "Rainbow", + "duration_ms" : 3000 + }, + + /// The configuration for the frame-grabber, contains the following items: + /// * width : The width of the grabbed frames [pixels] + /// * height : The height of the grabbed frames [pixels] + /// * frequency_Hz : The frequency of the frame grab [Hz] + "framegrabber" : + { + "width" : 64, + "height" : 64, + "frequency_Hz" : 10.0 + }, + + /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: + /// * xbmcAddress : The IP address of the XBMC-host + /// * xbmcTcpPort : The TCP-port of the XBMC-server + /// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback + /// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show + /// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback + /// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu +// "xbmcVideoChecker" : +// { +// "xbmcAddress" : "127.0.0.1", +// "xbmcTcpPort" : 9090, +// "grabVideo" : true, +// "grabPictures" : true, +// "grabAudio" : true, +// "grabMenu" : false +// }, + + /// The configuration of the Json server which enables the json remote interface + /// * port : Port at which the json server is started + "jsonServer" : + { + "port" : 19444 + }, + + /// The configuration of the Proto server which enables the protobuffer remote interface + /// * port : Port at which the protobuffer server is started + "protoServer" : + { + "port" : 19445 + }, + + /// The configuration of the boblight server which enables the boblight remote interface + /// * port : Port at which the boblight server is started +// "boblightServer" : +// { +// "port" : 19333 +// }, + + "end-of-json" : "end-of-json" +} diff --git a/include/hyperion/ColorTransform.h b/include/hyperion/ColorTransform.h new file mode 100644 index 00000000..50cbedba --- /dev/null +++ b/include/hyperion/ColorTransform.h @@ -0,0 +1,26 @@ +#pragma once + +// STL includes +#include + +// Utils includes +#include +#include + +class ColorTransform +{ +public: + + /// Unique identifier for this color transform + std::string _id; + + /// The RED-Channel (RGB) transform + RgbChannelTransform _rgbRedTransform; + /// The GREEN-Channel (RGB) transform + RgbChannelTransform _rgbGreenTransform; + /// The BLUE-Channel (RGB) transform + RgbChannelTransform _rgbBlueTransform; + + /// The HSV Transform for applying Saturation and Value transforms + HsvTransform _hsvTransform; +}; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 5026ef08..605ce6a1 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -16,8 +16,10 @@ #include // Forward class declaration -class HsvTransform; class ColorTransform; +class HsvTransform; +class RgbChannelTransform; +class MultiColorTransform; /// /// The main class of Hyperion. This gives other 'users' access to the attached LedDevice through @@ -33,7 +35,7 @@ public: /// /// RGB-Color channel enumeration /// - enum Color + enum RgbChannel { RED, GREEN, BLUE, INVALID }; @@ -88,15 +90,16 @@ public: void setColors(int priority, const std::vector &ledColors, const int timeout_ms); /// - /// Sets/Updates a part of the color transformation. + /// Returns the list with unique transform identifiers + /// @return The list with transform identifiers /// - /// @param[in] transform The type of transform to configure - /// @param[in] color The color channel to which the transform applies (only applicable for - /// Transform::THRESHOLD, Transform::GAMMA, Transform::BLACKLEVEL, - /// Transform::WHITELEVEL) - /// @param[in] value The new value for the given transform + const std::vector & getTransformIds() const; + /// - void setTransform(Transform transform, Color color, double value); + /// 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); /// /// Clears the given priority channel. This will switch the led-colors to the colors of the next @@ -111,18 +114,6 @@ public: /// void clearall(); - /// - /// Returns the value of a specific color transform - /// - /// @param[in] transform The type of transform - /// @param[in] color The color channel to which the transform applies (only applicable for - /// Transform::THRESHOLD, Transform::GAMMA, Transform::BLACKLEVEL, - /// Transform::WHITELEVEL) - /// - /// @return The value of the specified color transform - /// - double getTransform(Transform transform, Color color) const; - /// /// Returns a list of active priorities /// @@ -144,8 +135,12 @@ public: static LedDevice * createDevice(const Json::Value & deviceConfig); static ColorOrder createColorOrder(const Json::Value & deviceConfig); static LedString createLedString(const Json::Value & ledsConfig); + + static MultiColorTransform * createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorTransformConfig); + static ColorTransform * createColorTransform(const Json::Value & transformConfig); static HsvTransform * createHsvTransform(const Json::Value & hsvConfig); - static ColorTransform * createColorTransform(const Json::Value & colorConfig); + static RgbChannelTransform * createRgbChannelTransform(const Json::Value& colorConfig); + static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); private slots: @@ -156,28 +151,14 @@ private slots: void update(); private: - /// - /// Applies all color transmforms to the given list of colors. The transformation is performed - /// in place. - /// - /// @param colors The colors to be transformed - /// - void applyTransform(std::vector& colors) const; - /// The specifiation of the led frame construction and picture integration LedString _ledString; /// The priority muxer PriorityMuxer _muxer; - /// The HSV Transform for applying Saturation and Value transforms - HsvTransform * _hsvTransform; - /// The RED-Channel (RGB) transform - ColorTransform * _redTransform; - /// The GREEN-Channel (RGB) transform - ColorTransform * _greenTransform; - /// The BLUE-Channel (RGB) transform - ColorTransform * _blueTransform; + /// The transformation from raw colors to led colors + MultiColorTransform * _raw2ledTransform; /// Value with the desired color byte order ColorOrder _colorOrder; diff --git a/include/utils/ColorTransform.h b/include/utils/RgbChannelTransform.h similarity index 81% rename from include/utils/ColorTransform.h rename to include/utils/RgbChannelTransform.h index c89b3916..7f524d81 100644 --- a/include/utils/ColorTransform.h +++ b/include/utils/RgbChannelTransform.h @@ -12,21 +12,21 @@ /// 4) finally, in case of a weird choice of parameters, the output is clamped between [0:1] /// /// All configuration values are doubles and assume the color value to be between 0 and 1 -class ColorTransform +class RgbChannelTransform { public: /// Default constructor - ColorTransform(); + RgbChannelTransform(); /// Constructor - /// @param threshold - /// @param gamma - /// @param blacklevel - /// @param whitelevel - ColorTransform(double threshold, double gamma, double blacklevel, double whitelevel); + /// @param threshold The minimum threshold + /// @param gamma The gamma of the gamma-curve correction + /// @param blacklevel The minimum value for the RGB-Channel + /// @param whitelevel The maximum value for the RGB-Channel + RgbChannelTransform(double threshold, double gamma, double blacklevel, double whitelevel); /// Destructor - ~ColorTransform(); + ~RgbChannelTransform(); /// @return The current threshold value double getThreshold() const; diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 5462fc85..8a70c46a 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -39,6 +39,8 @@ SET(Hyperion_HEADERS ${CURRENT_HEADER_DIR}/BlackBorderDetector.h ${CURRENT_HEADER_DIR}/BlackBorderProcessor.h + ${CURRENT_SOURCE_DIR}/MultiColorTransform.h + ${CURRENT_SOURCE_DIR}/device/LedSpiDevice.h ${CURRENT_SOURCE_DIR}/device/LedRs232Device.h ${CURRENT_SOURCE_DIR}/device/LedDeviceTest.h @@ -60,6 +62,7 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/BlackBorderDetector.cpp ${CURRENT_SOURCE_DIR}/BlackBorderProcessor.cpp ${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp + ${CURRENT_SOURCE_DIR}/MultiColorTransform.cpp ${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp ${CURRENT_SOURCE_DIR}/device/LedSpiDevice.cpp diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index dbfd123d..d0adf982 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -4,6 +4,9 @@ // QT includes #include +#include +#include +#include // JsonSchema include #include @@ -22,11 +25,9 @@ #include "device/LedDeviceLightpack.h" #include "device/LedDeviceMultiLightpack.h" +#include "MultiColorTransform.h" #include "LinearColorSmoothing.h" -#include -#include - LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) { std::cout << "Device configuration: " << deviceConfig << std::endl; @@ -155,6 +156,98 @@ Hyperion::ColorOrder Hyperion::createColorOrder(const Json::Value &deviceConfig) return ORDER_RGB; } +ColorTransform * Hyperion::createColorTransform(const Json::Value & transformConfig) +{ + const std::string id = transformConfig.get("id", "default").asString(); + + RgbChannelTransform * redTransform = createRgbChannelTransform(transformConfig["red"]); + RgbChannelTransform * greenTransform = createRgbChannelTransform(transformConfig["green"]); + RgbChannelTransform * blueTransform = createRgbChannelTransform(transformConfig["blue"]); + + HsvTransform * hsvTransform = createHsvTransform(transformConfig["hsv"]); + + ColorTransform * transform = new ColorTransform(); + transform->_id = id; + transform->_rgbRedTransform = *redTransform; + transform->_rgbGreenTransform = *greenTransform; + transform->_rgbBlueTransform = *blueTransform; + transform->_hsvTransform = *hsvTransform; + + // Cleanup the allocated individual transforms + delete redTransform; + delete greenTransform; + delete blueTransform; + delete hsvTransform; + + return transform; +} + +MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorConfig) +{ + // Create the result, the transforms are added to this + MultiColorTransform * transform = new MultiColorTransform(ledCnt); + + const Json::Value transformConfig = colorConfig.get("transform", Json::nullValue); + if (transformConfig.isNull()) + { + // Old style color transformation config (just one for all leds) + ColorTransform * colorTransform = createColorTransform(colorConfig); + transform->addTransform(colorTransform); + transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1); + } + else if (!transformConfig.isArray()) + { + ColorTransform * colorTransform = createColorTransform(transformConfig); + transform->addTransform(colorTransform); + transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1); + } + else + { + const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*"); + + for (Json::UInt i = 0; i < transformConfig.size(); ++i) + { + const Json::Value & config = transformConfig[i]; + ColorTransform * colorTransform = createColorTransform(config); + transform->addTransform(colorTransform); + + const QString ledIndicesStr = config.get("leds", "").asCString(); + if (!overallExp.exactMatch(ledIndicesStr)) + { + std::cerr << "Given led indices " << i << " not correct format: " << ledIndicesStr.toStdString() << std::endl; + continue; + } + + std::cout << "ColorTransform '" << colorTransform->_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(); + + transform->setTransformForLed(colorTransform->_id, startInd, endInd); + std::cout << startInd << "-" << endInd; + } + else + { + int index = ledIndexList[i].toInt(); + transform->setTransformForLed(colorTransform->_id, index, index); + std::cout << index; + } + } + std::cout << "]" << std::endl; + } + } + return transform; +} + HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig) { const double saturationGain = hsvConfig.get("saturationGain", 1.0).asDouble(); @@ -163,14 +256,14 @@ HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig) return new HsvTransform(saturationGain, valueGain); } -ColorTransform* Hyperion::createColorTransform(const Json::Value& colorConfig) +RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colorConfig) { const double threshold = colorConfig.get("threshold", 0.0).asDouble(); const double gamma = colorConfig.get("gamma", 1.0).asDouble(); const double blacklevel = colorConfig.get("blacklevel", 0.0).asDouble(); const double whitelevel = colorConfig.get("whitelevel", 1.0).asDouble(); - ColorTransform* transform = new ColorTransform(threshold, gamma, blacklevel, whitelevel); + RgbChannelTransform* transform = new RgbChannelTransform(threshold, gamma, blacklevel, whitelevel); return transform; } @@ -246,14 +339,15 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, Hyperion::Hyperion(const Json::Value &jsonConfig) : _ledString(createLedString(jsonConfig["leds"])), _muxer(_ledString.leds().size()), - _hsvTransform(createHsvTransform(jsonConfig["color"]["hsv"])), - _redTransform(createColorTransform(jsonConfig["color"]["red"])), - _greenTransform(createColorTransform(jsonConfig["color"]["green"])), - _blueTransform(createColorTransform(jsonConfig["color"]["blue"])), + _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"])), _colorOrder(createColorOrder(jsonConfig["device"])), _device(createDevice(jsonConfig["device"])), _timer() { + if (!_raw2ledTransform->verifyTransforms()) + { + throw std::runtime_error("Color transformation incorrectly set"); + } // initialize the image processor factory ImageProcessorFactory::getInstance().init(_ledString, jsonConfig["blackborderdetector"].get("enable", true).asBool()); @@ -278,13 +372,8 @@ Hyperion::~Hyperion() // Delete the Led-String delete _device; - // delete he hsv transform - delete _hsvTransform; - - // Delete the color-transform - delete _blueTransform; - delete _greenTransform; - delete _redTransform; + // delete the color transform + delete _raw2ledTransform; } unsigned Hyperion::getLedCount() const @@ -319,56 +408,14 @@ void Hyperion::setColors(int priority, const std::vector& ledColors, c } } -void Hyperion::setTransform(Hyperion::Transform transform, Hyperion::Color color, double value) +const std::vector & Hyperion::getTransformIds() const { - // select the transform of the requested color - ColorTransform * t = nullptr; - switch (color) - { - case RED: - t = _redTransform; - break; - case GREEN: - t = _greenTransform; - break; - case BLUE: - t = _blueTransform; - break; - default: - break; - } + return _raw2ledTransform->getTransformIds(); +} - // set transform value - switch (transform) - { - case SATURATION_GAIN: - _hsvTransform->setSaturationGain(value); - break; - case VALUE_GAIN: - _hsvTransform->setValueGain(value); - break; - case THRESHOLD: - assert (t != nullptr); - t->setThreshold(value); - break; - case GAMMA: - assert (t != nullptr); - t->setGamma(value); - break; - case BLACKLEVEL: - assert (t != nullptr); - t->setBlacklevel(value); - break; - case WHITELEVEL: - assert (t != nullptr); - t->setWhitelevel(value); - break; - default: - assert(false); - } - - // update the led output - update(); +ColorTransform * Hyperion::getTransform(const std::string& id) +{ + return _raw2ledTransform->getTransform(id); } void Hyperion::clear(int priority) @@ -393,51 +440,6 @@ void Hyperion::clearall() update(); } -double Hyperion::getTransform(Hyperion::Transform transform, Hyperion::Color color) const -{ - // select the transform of the requested color - ColorTransform * t = nullptr; - switch (color) - { - case RED: - t = _redTransform; - break; - case GREEN: - t = _greenTransform; - break; - case BLUE: - t = _blueTransform; - break; - default: - break; - } - - // set transform value - switch (transform) - { - case SATURATION_GAIN: - return _hsvTransform->getSaturationGain(); - case VALUE_GAIN: - return _hsvTransform->getValueGain(); - case THRESHOLD: - assert (t != nullptr); - return t->getThreshold(); - case GAMMA: - assert (t != nullptr); - return t->getGamma(); - case BLACKLEVEL: - assert (t != nullptr); - return t->getBlacklevel(); - case WHITELEVEL: - assert (t != nullptr); - return t->getWhitelevel(); - default: - assert(false); - } - - return 999.0; -} - QList Hyperion::getActivePriorities() const { return _muxer.getPriorities(); @@ -458,14 +460,9 @@ void Hyperion::update() const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority); // Apply the transform to each led and color-channel - std::vector ledColors(priorityInfo.ledColors); + std::vector ledColors = _raw2ledTransform->applyTransform(priorityInfo.ledColors); for (ColorRgb& color : ledColors) { - _hsvTransform->transform(color.red, color.green, color.blue); - color.red = _redTransform->transform(color.red); - color.green = _greenTransform->transform(color.green); - color.blue = _blueTransform->transform(color.blue); - // correct the color byte order switch (_colorOrder) { diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index d3bfba3d..a0af2077 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -4,8 +4,6 @@ #include #include -#include - using namespace hyperion; ImageProcessor::ImageProcessor(const LedString& ledString, bool enableBlackBorderDetector) : diff --git a/libsrc/hyperion/MultiColorTransform.cpp b/libsrc/hyperion/MultiColorTransform.cpp new file mode 100644 index 00000000..d15f020e --- /dev/null +++ b/libsrc/hyperion/MultiColorTransform.cpp @@ -0,0 +1,97 @@ + +// STL includes +#include + +// Hyperion includes +#include "MultiColorTransform.h" + +MultiColorTransform::MultiColorTransform(const unsigned ledCnt) : + _ledTransforms(ledCnt, nullptr) +{ +} + +MultiColorTransform::~MultiColorTransform() +{ + // Clean up all the transforms + for (ColorTransform * transform : _transform) + { + delete transform; + } +} + +void MultiColorTransform::addTransform(ColorTransform * transform) +{ + _transformIds.push_back(transform->_id); + _transform.push_back(transform); +} + +void MultiColorTransform::setTransformForLed(const std::string& id, const unsigned startLed, const unsigned endLed) +{ + assert(startLed <= endLed); + assert(endLed < _ledTransforms.size()); + + // Get the identified transform (don't care if is nullptr) + ColorTransform * transform = getTransform(id); + for (unsigned iLed=startLed; iLed<=endLed; ++iLed) + { + _ledTransforms[iLed] = transform; + } +} + +bool MultiColorTransform::verifyTransforms() const +{ + bool allLedsSet = true; + for (unsigned iLed=0; iLed<_ledTransforms.size(); ++iLed) + { + if (_ledTransforms[iLed] == nullptr) + { + std::cerr << "No transform set for " << iLed << std::endl; + allLedsSet = false; + } + } + return allLedsSet; +} + +const std::vector & MultiColorTransform::getTransformIds() +{ + return _transformIds; +} + +ColorTransform* MultiColorTransform::getTransform(const std::string& id) +{ + // Iterate through the unique transforms until we find the one with the given id + for (ColorTransform* transform : _transform) + { + if (transform->_id == id) + { + return transform; + } + } + + // The ColorTransform was not found + return nullptr; +} + +std::vector MultiColorTransform::applyTransform(const std::vector& rawColors) +{ + // Create a copy, as we will do the rest of the transformation in place + std::vector ledColors(rawColors); + + const size_t itCnt = std::min(_ledTransforms.size(), rawColors.size()); + for (size_t i=0; i_hsvTransform.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); + } + return ledColors; +} diff --git a/libsrc/hyperion/MultiColorTransform.h b/libsrc/hyperion/MultiColorTransform.h new file mode 100644 index 00000000..05bd3704 --- /dev/null +++ b/libsrc/hyperion/MultiColorTransform.h @@ -0,0 +1,66 @@ +#pragma once + +// STL includes +#include + +// Utils includes +#include + +// Hyperion includes +#include + +/// +/// The LedColorTransform is responsible for performing color transformation from 'raw' colors +/// received as input to colors mapped to match the color-properties of the leds. +/// +class MultiColorTransform +{ +public: + MultiColorTransform(const unsigned ledCnt); + ~MultiColorTransform(); + + /** + * Adds a new ColorTransform to this MultiColorTransform + * + * @param transform The new ColorTransform (ownership is transfered) + */ + void addTransform(ColorTransform * transform); + + void setTransformForLed(const std::string& id, const unsigned startLed, const unsigned endLed); + + bool verifyTransforms() const; + + /// + /// Returns the identifier of all the unique ColorTransform + /// + /// @return The list with unique id's of the ColorTransforms + const std::vector & getTransformIds(); + + /// + /// Returns the pointer to the ColorTransform with the given id + /// + /// @param id The identifier of the ColorTransform + /// + /// @return The ColorTransform with the given id (or nullptr if it does not exist) + /// + ColorTransform* getTransform(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 applyTransform(const std::vector& rawColors); + +private: + /// List with transform ids + std::vector _transformIds; + + /// List with unique ColorTransforms + std::vector _transform; + + /// List with a pointer to the ColorTransform for each individual led + std::vector _ledTransforms; +}; diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index 1c99b13d..cd5894db 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -12,9 +12,10 @@ #include // hyperion util includes -#include "hyperion/ImageProcessorFactory.h" -#include "hyperion/ImageProcessor.h" -#include "utils/ColorRgb.h" +#include +#include +#include +#include // project includes #include "JsonClientConnection.h" @@ -173,25 +174,39 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &message) } // collect transform information - Json::Value & transform = info["transform"]; - transform["saturationGain"] = _hyperion->getTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID); - transform["valueGain"] = _hyperion->getTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID); - Json::Value & threshold = transform["threshold"]; - threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::RED)); - threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::GREEN)); - threshold.append(_hyperion->getTransform(Hyperion::THRESHOLD, Hyperion::BLUE)); - Json::Value & gamma = transform["gamma"]; - gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::RED)); - gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::GREEN)); - gamma.append(_hyperion->getTransform(Hyperion::GAMMA, Hyperion::BLUE)); - Json::Value & blacklevel = transform["blacklevel"]; - blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::RED)); - blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN)); - blacklevel.append(_hyperion->getTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE)); - Json::Value & whitelevel = transform["whitelevel"]; - whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::RED)); - whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::GREEN)); - whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::BLUE)); + Json::Value & transformArray = info["transform"]; + for (const std::string& transformId : _hyperion->getTransformIds()) + { + const ColorTransform * colorTransform = _hyperion->getTransform(transformId); + if (colorTransform == nullptr) + { + std::cerr << "Incorrect color transform id: " << transformId << std::endl; + continue; + } + + Json::Value & transform = transformArray.append(Json::Value()); + transform["id"] = transformId; + + transform["saturationGain"] = colorTransform->_hsvTransform.getSaturationGain(); + transform["valueGain"] = colorTransform->_hsvTransform.getValueGain(); + + Json::Value & threshold = transform["threshold"]; + threshold.append(colorTransform->_rgbRedTransform.getThreshold()); + threshold.append(colorTransform->_rgbGreenTransform.getThreshold()); + threshold.append(colorTransform->_rgbBlueTransform.getThreshold()); + Json::Value & gamma = transform["gamma"]; + gamma.append(colorTransform->_rgbRedTransform.getGamma()); + gamma.append(colorTransform->_rgbGreenTransform.getGamma()); + gamma.append(colorTransform->_rgbBlueTransform.getGamma()); + Json::Value & blacklevel = transform["blacklevel"]; + blacklevel.append(colorTransform->_rgbRedTransform.getBlacklevel()); + blacklevel.append(colorTransform->_rgbGreenTransform.getBlacklevel()); + blacklevel.append(colorTransform->_rgbBlueTransform.getBlacklevel()); + Json::Value & whitelevel = transform["whitelevel"]; + whitelevel.append(colorTransform->_rgbRedTransform.getWhitelevel()); + whitelevel.append(colorTransform->_rgbGreenTransform.getWhitelevel()); + whitelevel.append(colorTransform->_rgbBlueTransform.getWhitelevel()); + } // send the result sendMessage(result); @@ -222,46 +237,54 @@ void JsonClientConnection::handleTransformCommand(const Json::Value &message) { const Json::Value & transform = message["transform"]; + const std::string transformId = transform.get("id", _hyperion->getTransformIds().front()).asString(); + ColorTransform * colorTransform = _hyperion->getTransform(transformId); + if (colorTransform == nullptr) + { + //sendErrorReply(std::string("Incorrect transform identifier: ") + transformId); + return; + } + if (transform.isMember("saturationGain")) { - _hyperion->setTransform(Hyperion::SATURATION_GAIN, Hyperion::INVALID, transform["saturationGain"].asDouble()); + colorTransform->_hsvTransform.setSaturationGain(transform["saturationGain"].asDouble()); } if (transform.isMember("valueGain")) { - _hyperion->setTransform(Hyperion::VALUE_GAIN, Hyperion::INVALID, transform["valueGain"].asDouble()); + colorTransform->_hsvTransform.setValueGain(transform["valueGain"].asDouble()); } if (transform.isMember("threshold")) { - const Json::Value & threshold = transform["threshold"]; - _hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::RED, threshold[0u].asDouble()); - _hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::GREEN, threshold[1u].asDouble()); - _hyperion->setTransform(Hyperion::THRESHOLD, Hyperion::BLUE, threshold[2u].asDouble()); + const Json::Value & values = transform["threshold"]; + colorTransform->_rgbRedTransform .setThreshold(values[0u].asDouble()); + colorTransform->_rgbGreenTransform.setThreshold(values[1u].asDouble()); + colorTransform->_rgbBlueTransform .setThreshold(values[2u].asDouble()); } if (transform.isMember("gamma")) { - const Json::Value & threshold = transform["gamma"]; - _hyperion->setTransform(Hyperion::GAMMA, Hyperion::RED, threshold[0u].asDouble()); - _hyperion->setTransform(Hyperion::GAMMA, Hyperion::GREEN, threshold[1u].asDouble()); - _hyperion->setTransform(Hyperion::GAMMA, Hyperion::BLUE, threshold[2u].asDouble()); + const Json::Value & values = transform["gamma"]; + colorTransform->_rgbRedTransform .setGamma(values[0u].asDouble()); + colorTransform->_rgbGreenTransform.setGamma(values[1u].asDouble()); + colorTransform->_rgbBlueTransform .setGamma(values[2u].asDouble()); } if (transform.isMember("blacklevel")) { - const Json::Value & threshold = transform["blacklevel"]; - _hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::RED, threshold[0u].asDouble()); - _hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::GREEN, threshold[1u].asDouble()); - _hyperion->setTransform(Hyperion::BLACKLEVEL, Hyperion::BLUE, threshold[2u].asDouble()); + const Json::Value & values = transform["blacklevel"]; + colorTransform->_rgbRedTransform .setBlacklevel(values[0u].asDouble()); + colorTransform->_rgbGreenTransform.setBlacklevel(values[1u].asDouble()); + colorTransform->_rgbBlueTransform .setBlacklevel(values[2u].asDouble()); } if (transform.isMember("whitelevel")) { - const Json::Value & threshold = transform["whitelevel"]; - _hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::RED, threshold[0u].asDouble()); - _hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::GREEN, threshold[1u].asDouble()); - _hyperion->setTransform(Hyperion::WHITELEVEL, Hyperion::BLUE, threshold[2u].asDouble()); + const Json::Value & values = transform["whitelevel"]; + colorTransform->_rgbRedTransform .setWhitelevel(values[0u].asDouble()); + colorTransform->_rgbGreenTransform.setWhitelevel(values[1u].asDouble()); + colorTransform->_rgbBlueTransform .setWhitelevel(values[2u].asDouble()); } sendSuccessReply(); diff --git a/libsrc/jsonserver/schema/schema-transform.json b/libsrc/jsonserver/schema/schema-transform.json index 26fdf787..a135e12f 100644 --- a/libsrc/jsonserver/schema/schema-transform.json +++ b/libsrc/jsonserver/schema/schema-transform.json @@ -1,68 +1,72 @@ { - "type":"object", - "required":true, - "properties":{ - "command": { - "type" : "string", - "required" : true, - "enum" : ["transform"] - }, - "transform": { - "type": "object", - "required": true, - "properties": { - "saturationGain" : { - "type" : "double", - "required" : false, - "minimum" : 0.0 - }, - "valueGain" : { - "type" : "double", - "required" : false, - "minimum" : 0.0 - }, - "threshold": { - "type": "array", - "required": false, - "items" : { - "type": "double", - "minimum": 0.0, - "maximum": 1.0 - }, - "minItems": 3, - "maxItems": 3 - }, - "gamma": { - "type": "array", - "required": false, - "items" : { - "type": "double", - "minimum": 0.0 - }, - "minItems": 3, - "maxItems": 3 - }, - "blacklevel": { - "type": "array", - "required": false, - "items" : { - "type": "double" - }, - "minItems": 3, - "maxItems": 3 - }, - "whitelevel": { - "type": "array", - "required": false, - "items" : { - "type": "double" - }, - "minItems": 3, - "maxItems": 3 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "type":"object", + "required":true, + "properties":{ + "command": { + "type" : "string", + "required" : true, + "enum" : ["transform"] + }, + "transform": { + "type": "object", + "required": true, + "properties": { + "id" : { + "type" : "string", + "required" : false + }, + "saturationGain" : { + "type" : "double", + "required" : false, + "minimum" : 0.0 + }, + "valueGain" : { + "type" : "double", + "required" : false, + "minimum" : 0.0 + }, + "threshold": { + "type": "array", + "required": false, + "items" : { + "type": "double", + "minimum": 0.0, + "maximum": 1.0 + }, + "minItems": 3, + "maxItems": 3 + }, + "gamma": { + "type": "array", + "required": false, + "items" : { + "type": "double", + "minimum": 0.0 + }, + "minItems": 3, + "maxItems": 3 + }, + "blacklevel": { + "type": "array", + "required": false, + "items" : { + "type": "double" + }, + "minItems": 3, + "maxItems": 3 + }, + "whitelevel": { + "type": "array", + "required": false, + "items" : { + "type": "double" + }, + "minItems": 3, + "maxItems": 3 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index 18dfd98c..52094a61 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -11,11 +11,11 @@ add_library(hyperion-utils ${CURRENT_HEADER_DIR}/ColorRgba.h ${CURRENT_SOURCE_DIR}/ColorRgba.cpp ${CURRENT_HEADER_DIR}/Image.h - ${CURRENT_HEADER_DIR}/ColorTransform.h - ${CURRENT_HEADER_DIR}/HsvTransform.h - ${CURRENT_SOURCE_DIR}/ColorTransform.cpp + ${CURRENT_HEADER_DIR}/HsvTransform.h ${CURRENT_SOURCE_DIR}/HsvTransform.cpp + ${CURRENT_HEADER_DIR}/RgbChannelTransform.h + ${CURRENT_SOURCE_DIR}/RgbChannelTransform.cpp ${CURRENT_HEADER_DIR}/jsonschema/JsonFactory.h ${CURRENT_HEADER_DIR}/jsonschema/JsonSchemaChecker.h diff --git a/libsrc/utils/ColorTransform.cpp b/libsrc/utils/RgbChannelTransform.cpp similarity index 60% rename from libsrc/utils/ColorTransform.cpp rename to libsrc/utils/RgbChannelTransform.cpp index abe7bded..bdd4ec1b 100644 --- a/libsrc/utils/ColorTransform.cpp +++ b/libsrc/utils/RgbChannelTransform.cpp @@ -1,9 +1,10 @@ // STL includes #include -#include +// Utils includes +#include -ColorTransform::ColorTransform() : +RgbChannelTransform::RgbChannelTransform() : _threshold(0), _gamma(1.0), _blacklevel(0.0), @@ -12,7 +13,7 @@ ColorTransform::ColorTransform() : initializeMapping(); } -ColorTransform::ColorTransform(double threshold, double gamma, double blacklevel, double whitelevel) : +RgbChannelTransform::RgbChannelTransform(double threshold, double gamma, double blacklevel, double whitelevel) : _threshold(threshold), _gamma(gamma), _blacklevel(blacklevel), @@ -21,55 +22,55 @@ ColorTransform::ColorTransform(double threshold, double gamma, double blacklevel initializeMapping(); } -ColorTransform::~ColorTransform() +RgbChannelTransform::~RgbChannelTransform() { } -double ColorTransform::getThreshold() const +double RgbChannelTransform::getThreshold() const { return _threshold; } -void ColorTransform::setThreshold(double threshold) +void RgbChannelTransform::setThreshold(double threshold) { _threshold = threshold; initializeMapping(); } -double ColorTransform::getGamma() const +double RgbChannelTransform::getGamma() const { return _gamma; } -void ColorTransform::setGamma(double gamma) +void RgbChannelTransform::setGamma(double gamma) { _gamma = gamma; initializeMapping(); } -double ColorTransform::getBlacklevel() const +double RgbChannelTransform::getBlacklevel() const { return _blacklevel; } -void ColorTransform::setBlacklevel(double blacklevel) +void RgbChannelTransform::setBlacklevel(double blacklevel) { _blacklevel = blacklevel; initializeMapping(); } -double ColorTransform::getWhitelevel() const +double RgbChannelTransform::getWhitelevel() const { return _whitelevel; } -void ColorTransform::setWhitelevel(double whitelevel) +void RgbChannelTransform::setWhitelevel(double whitelevel) { _whitelevel = whitelevel; initializeMapping(); } -void ColorTransform::initializeMapping() +void RgbChannelTransform::initializeMapping() { // initialize the mapping as a linear array for (int i = 0; i < 256; ++i) diff --git a/libsrc/utils/jsonschema/JsonSchemaChecker.cpp b/libsrc/utils/jsonschema/JsonSchemaChecker.cpp index 2ace25a5..da0c51d8 100644 --- a/libsrc/utils/jsonschema/JsonSchemaChecker.cpp +++ b/libsrc/utils/jsonschema/JsonSchemaChecker.cpp @@ -154,6 +154,8 @@ void JsonSchemaChecker::checkType(const Json::Value & value, const Json::Value & wrongType = !value.isNumeric(); else if (type == "integer") wrongType = !value.isIntegral(); + else if (type == "double") + wrongType = !value.isDouble(); else if (type == "boolean") wrongType = !value.isBool(); else if (type == "object") @@ -162,6 +164,8 @@ void JsonSchemaChecker::checkType(const Json::Value & value, const Json::Value & wrongType = !value.isArray(); else if (type == "null") wrongType = !value.isNull(); + else if (type == "enum") + wrongType = !value.isString(); else if (type == "any") wrongType = false; else diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/ConfigurationFile.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/ConfigurationFile.java index 285978f8..1abd46c1 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/ConfigurationFile.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/ConfigurationFile.java @@ -8,19 +8,19 @@ import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; import java.util.Properties; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; +import java.util.Vector; public class ConfigurationFile { - private final Properties pProps = new Properties(); + private final Properties mProps = new Properties(); public void load(String pFilename) { - pProps.clear(); + mProps.clear(); // try (InputStream in = new InflaterInputStream(new FileInputStream(pFilename))){ - try (InputStream in = new GZIPInputStream(new FileInputStream(pFilename))){ -// try (InputStream in = new FileInputStream(pFilename)) { - pProps.load(in); +// try (InputStream in = new GZIPInputStream(new FileInputStream(pFilename))){ + try (InputStream in = new FileInputStream(pFilename)) { + mProps.load(in); } catch (Throwable t) { // TODO Auto-generated catch block t.printStackTrace(); @@ -29,15 +29,18 @@ public class ConfigurationFile { public void save(String pFilename) { // try (OutputStream out = new DeflaterOutputStream(new FileOutputStream(pFilename))) { - try (OutputStream out = new GZIPOutputStream(new FileOutputStream(pFilename))) { -// try (OutputStream out = (new FileOutputStream(pFilename))) { - pProps.store(out, "Pesistent settings file for HyperCon"); +// try (OutputStream out = new GZIPOutputStream(new FileOutputStream(pFilename))) { + try (OutputStream out = (new FileOutputStream(pFilename))) { + mProps.store(out, "Pesistent settings file for HyperCon"); } catch (IOException e) { e.printStackTrace(); } } public void store(Object pObj) { + store(pObj, pObj.getClass().getSimpleName(), ""); + } + public void store(Object pObj, String preamble, String postamble) { String className = pObj.getClass().getSimpleName(); // Retrieve the member variables Field[] fields = pObj.getClass().getDeclaredFields(); @@ -48,27 +51,99 @@ public class ConfigurationFile { continue; } - String key = className + "." + field.getName(); + String key = preamble + "." + field.getName() + postamble; try { Object value = field.get(pObj); if (value.getClass().isEnum()) { - pProps.setProperty(key, ((Enum)value).name()); + mProps.setProperty(key, ((Enum)value).name()); + } else if (value.getClass().isAssignableFrom(Vector.class)) { + @SuppressWarnings("unchecked") + Vector v = (Vector) value; + for (int i=0; i vector; + try { + vector = (Vector)field.get(pObj); + } catch (Throwable t) { + t.printStackTrace(); + break; + } + // Clear existing elements from the vector + vector.clear(); + + // Iterate through the properties to find the indices of the vector + int i=0; + while (true) { + String curIndexKey = pPreamble + field.getName() + "[" + i + "]"; + Properties elemProps = new Properties(); + // Find all the elements for the current vector index + for (Object keyObj : pProps.keySet()) { + String keyStr = (String)keyObj; + if (keyStr.startsWith(curIndexKey)) { + // Remove the name and dot + elemProps.put(keyStr.substring(curIndexKey.length()+1), pProps.get(keyStr)); + } + } + if (elemProps.isEmpty()) { + // Found no more elements for the vector + break; + } + + // Construct new instance of vectors generic type + ParameterizedType vectorElementType = (ParameterizedType) field.getGenericType(); + Class vectorElementClass = (Class) vectorElementType.getActualTypeArguments()[0]; + // Find the constructor with no arguments and create a new instance + Object newElement = null; + try { + newElement = vectorElementClass.getConstructor().newInstance(); + } catch (Throwable t) { + System.err.println("Failed to find empty default constructor for " + vectorElementClass.getName()); + break; + } + if (newElement == null) { + System.err.println("Failed to construct instance for " + vectorElementClass.getName()); + break; + } + + // Restore the instance members from the collected properties + restore(newElement, elemProps, ""); + + // Add the instance to the vector + vector.addElement(newElement); + + ++i; + } + + continue; + } + + String key = pPreamble + field.getName(); String value = pProps.getProperty(key); if (value == null) { System.out.println("Persistent settings does not contain value for " + key); @@ -97,6 +172,6 @@ public class ConfigurationFile { @Override public String toString() { - return pProps.toString(); + return mProps.toString(); } } diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorPanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorTransformPanel.java similarity index 82% rename from src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorPanel.java rename to src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorTransformPanel.java index 531aad0f..e7dd3278 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorPanel.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorTransformPanel.java @@ -1,5 +1,6 @@ package org.hyperion.hypercon.gui; +import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.beans.Transient; @@ -11,20 +12,27 @@ import javax.swing.GroupLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; +import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; -import org.hyperion.hypercon.spec.ColorConfig; +import org.hyperion.hypercon.spec.TransformConfig; /** * Configuration panel for the ColorConfig. * * NB This has not been integrated in the GUI jet! */ -public class ColorPanel extends JPanel { +public class ColorTransformPanel extends JPanel { - private final ColorConfig mColorConfig; + private final TransformConfig mColorConfig; + + private JPanel mIndexPanel; + private JLabel mIndexLabel; + private JTextField mIndexField; private JPanel mRgbTransformPanel; private JLabel mThresholdLabel; @@ -53,10 +61,10 @@ public class ColorPanel extends JPanel { private JLabel mValueAdjustLabel; private JSpinner mValueAdjustSpinner; - public ColorPanel(ColorConfig pColorConfig) { + public ColorTransformPanel(TransformConfig pTransformConfig) { super(); - mColorConfig = pColorConfig; + mColorConfig = pTransformConfig; initialise(); } @@ -70,12 +78,32 @@ public class ColorPanel extends JPanel { } private void initialise() { - setBorder(BorderFactory.createTitledBorder("Color transform")); + setBorder(BorderFactory.createTitledBorder("Transform [" + mColorConfig.mId + "]")); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + add(getIndexPanel()); + add(Box.createVerticalStrut(10)); add(getRgbPanel()); add(Box.createVerticalStrut(10)); add(getHsvPanel()); + add(Box.createVerticalGlue()); + } + + private JPanel getIndexPanel() { + if (mIndexPanel == null) { + mIndexPanel = new JPanel(); + mIndexPanel.setMaximumSize(new Dimension(1024, 25)); + mIndexPanel.setLayout(new BorderLayout(10,10)); + + mIndexLabel = new JLabel("Indices:"); + mIndexPanel.add(mIndexLabel, BorderLayout.WEST); + + mIndexField = new JTextField(mColorConfig.mLedIndexString); + mIndexField.setToolTipText("Comma seperated indices or index ranges (eg '1-10, 13, 14, 17-19')"); + mIndexField.getDocument().addDocumentListener(mDocumentListener); + mIndexPanel.add(mIndexField, BorderLayout.CENTER); + } + return mIndexPanel; } private JPanel getRgbPanel() { @@ -216,4 +244,18 @@ public class ColorPanel extends JPanel { mColorConfig.mValueGain = (Double)mValueAdjustSpinner.getValue(); } }; + private final DocumentListener mDocumentListener = new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + mColorConfig.mLedIndexString = mIndexField.getText(); + } + @Override + public void insertUpdate(DocumentEvent e) { + mColorConfig.mLedIndexString = mIndexField.getText(); + } + @Override + public void changedUpdate(DocumentEvent e) { + mColorConfig.mLedIndexString = mIndexField.getText(); + } + }; } diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorsPanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorsPanel.java new file mode 100644 index 00000000..29f50840 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ColorsPanel.java @@ -0,0 +1,130 @@ +package org.hyperion.hypercon.gui; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import org.hyperion.hypercon.spec.ColorConfig; +import org.hyperion.hypercon.spec.TransformConfig; + +public class ColorsPanel extends JPanel { + + private final ColorConfig mColorConfig; + private final DefaultComboBoxModel mTransformsModel; + + private JPanel mControlPanel; + private JComboBox mTransformCombo; + private JButton mAddTransformButton; + private JButton mDelTransformButton; + + private JPanel mTransformPanel; + + private final Map mTransformPanels = new HashMap<>(); + + + public ColorsPanel(ColorConfig pColorConfig) { + super(); + + mColorConfig = pColorConfig; + mTransformsModel = new DefaultComboBoxModel(mColorConfig.mTransforms); + + initialise(); + } + + private void initialise() { + setLayout(new BorderLayout(10,10)); + setBorder(BorderFactory.createTitledBorder("Colors")); + + add(getControlPanel(), BorderLayout.NORTH); + + mTransformPanel = new JPanel(); + mTransformPanel.setLayout(new BorderLayout()); + add(mTransformPanel, BorderLayout.CENTER); + + for (TransformConfig config : mColorConfig.mTransforms) { + mTransformPanels.put(config, new ColorTransformPanel(config)); + } + ColorTransformPanel currentPanel = mTransformPanels.get(mColorConfig.mTransforms.get(0)); + mTransformPanel.add(currentPanel, BorderLayout.CENTER); + } + + private JPanel getControlPanel() { + if (mControlPanel == null) { + mControlPanel = new JPanel(); + mControlPanel.setLayout(new BoxLayout(mControlPanel, BoxLayout.LINE_AXIS)); + + mTransformCombo = new JComboBox<>(mTransformsModel); + mTransformCombo.addActionListener(mComboListener); + mControlPanel.add(mTransformCombo); + + mAddTransformButton = new JButton(mAddAction); + mControlPanel.add(mAddTransformButton); + + mDelTransformButton = new JButton(mDelAction); + mDelTransformButton.setEnabled(mTransformCombo.getItemCount() > 1); + mControlPanel.add(mDelTransformButton); + } + return mControlPanel; + } + + private final Action mAddAction = new AbstractAction("Add") { + @Override + public void actionPerformed(ActionEvent e) { + String newId = JOptionPane.showInputDialog("Give an identifier for the new color-transform:"); + if (newId == null || newId.isEmpty()) { + // No proper value given + return; + } + + TransformConfig config = new TransformConfig(); + config.mId = newId; + + ColorTransformPanel panel = new ColorTransformPanel(config); + mTransformPanels.put(config, panel); + + mTransformsModel.addElement(config); + mTransformsModel.setSelectedItem(config); + + mDelTransformButton.setEnabled(true); + } + }; + private final Action mDelAction = new AbstractAction("Del") { + @Override + public void actionPerformed(ActionEvent e) { + TransformConfig config = (TransformConfig) mTransformCombo.getSelectedItem(); + mTransformPanels.remove(config); + mTransformsModel.removeElement(config); + + mDelTransformButton.setEnabled(mTransformCombo.getItemCount() > 1); + } + }; + + private final ActionListener mComboListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + TransformConfig selConfig = (TransformConfig) mTransformsModel.getSelectedItem(); + if (selConfig == null) { + // Something went wrong here, there should always be a selection! + return; + } + + ColorTransformPanel panel = mTransformPanels.get(selConfig); + mTransformPanel.removeAll(); + mTransformPanel.add(panel, BorderLayout.CENTER); + mTransformPanel.revalidate(); + mTransformPanel.repaint(); + } + }; +} diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java index 6c56bc44..84e1636d 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/ConfigPanel.java @@ -174,7 +174,7 @@ public class ConfigPanel extends JPanel { mProcessPanel.add(new BootSequencePanel(ledString.mMiscConfig)); mProcessPanel.add(new FrameGrabberPanel(ledString.mMiscConfig)); mProcessPanel.add(new ColorSmoothingPanel(ledString.mColorConfig)); - mProcessPanel.add(new ColorPanel(ledString.mColorConfig)); + mProcessPanel.add(new ColorsPanel(ledString.mColorConfig)); mProcessPanel.add(Box.createVerticalGlue()); } return mProcessPanel; diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/LedDivideDialog.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/LedDivideDialog.java new file mode 100644 index 00000000..d0072df1 --- /dev/null +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/LedDivideDialog.java @@ -0,0 +1,48 @@ +package org.hyperion.hypercon.gui; + +import java.awt.GridLayout; + +import javax.swing.ButtonGroup; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; + +public class LedDivideDialog extends JFrame { + + private final int mLedCount; + private final int mTransformCount; + + private JPanel mContentPanel; + + public LedDivideDialog(int pLedCnt, int pTransformCnt) { + super(); + + mLedCount = pLedCnt; + mTransformCount = pTransformCnt; + + initialise(); + } + + private void initialise() { + mContentPanel = new JPanel(); + mContentPanel.setLayout(new GridLayout(mLedCount, mTransformCount, 5, 5)); + + for (int iLed=0; iLed mTransforms = new Vector<>(); + { + mTransforms.add(new TransformConfig()); + } public boolean mSmoothingEnabled = false; /** The type of smoothing algorithm */ @@ -54,81 +30,53 @@ public class ColorConfig { public String toJsonString() { StringBuffer strBuf = new StringBuffer(); - strBuf.append("\t/// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields:\n"); - strBuf.append("\t/// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters:\n"); + strBuf.append("\t/// Color manipulation configuration used to tune the output colors to specific surroundings. \n"); + strBuf.append("\t/// The configuration contains a list of color-transforms. Each transform contains the \n"); + strBuf.append("\t/// following fields:\n"); + strBuf.append("\t/// * 'id' : The unique identifier of the color transformation (eg 'device_1')"); + strBuf.append("\t/// * 'leds' : The indices (or index ranges) of the leds to which this color transform applies\n"); + strBuf.append("\t/// (eg '0-5, 9, 11, 12-17'). The indices are zero based."); + strBuf.append("\t/// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following \n"); + strBuf.append("\t/// tuning parameters:\n"); strBuf.append("\t/// - 'saturationGain' The gain adjustement of the saturation\n"); strBuf.append("\t/// - 'valueGain' The gain adjustement of the value\n"); - strBuf.append("\t/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel:\n"); - strBuf.append("\t/// - 'threshold' The minimum required input value for the channel to be on (else zero)\n"); + strBuf.append("\t/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the \n"); + strBuf.append("\t/// following tuning parameters for each channel:\n"); + strBuf.append("\t/// - 'threshold' The minimum required input value for the channel to be on \n"); + strBuf.append("\t/// (else zero)\n"); strBuf.append("\t/// - 'gamma' The gamma-curve correction factor\n"); strBuf.append("\t/// - 'blacklevel' The lowest possible value (when the channel is black)\n"); strBuf.append("\t/// - 'whitelevel' The highest possible value (when the channel is white)\n"); - strBuf.append("\t/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning parameters:\n"); + strBuf.append("\t///"); + strBuf.append("\t/// Next to the list with color transforms there is also a smoothing option."); + strBuf.append("\t/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning \n"); + strBuf.append("\t/// parameters:\n"); strBuf.append("\t/// - 'type' The type of smoothing algorithm ('linear' or 'none')\n"); strBuf.append("\t/// - 'time_ms' The time constant for smoothing algorithm in milliseconds\n"); strBuf.append("\t/// - 'updateFrequency' The update frequency of the leds in Hz\n"); strBuf.append("\t\"color\" :\n"); strBuf.append("\t{\n"); - strBuf.append(hsvToJsonString() + ",\n"); - strBuf.append(rgbToJsonString() + ",\n"); + + strBuf.append("\t\t\"transform\" :\n"); + strBuf.append("\t\t[\n"); + for (int i=0; i & 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"); TransformParameter & argGamma = parameters.add('g', "gamma" , "Set the gamma of the leds (requires 3 space seperated values)"); @@ -83,6 +84,7 @@ int main(int argc, char * argv[]) 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 << " " << argId.usageLine() << std::endl; std::cerr << " " << argSaturation.usageLine() << std::endl; std::cerr << " " << argValue.usageLine() << std::endl; std::cerr << " " << argThreshold.usageLine() << std::endl; @@ -119,9 +121,11 @@ int main(int argc, char * argv[]) } else if (colorTransform) { + std::string transId; double saturation, value; ColorTransformValues threshold, gamma, blacklevel, whitelevel; + if (argId.isSet()) transId = argId.getValue(); if (argSaturation.isSet()) saturation = argSaturation.getValue(); if (argValue.isSet()) value = argValue.getValue(); if (argThreshold.isSet()) threshold = argThreshold.getValue(); @@ -130,6 +134,7 @@ int main(int argc, char * argv[]) if (argWhitelevel.isSet()) whitelevel = argWhitelevel.getValue(); connection.setTransform( + argId.isSet() ? &transId : nullptr, argSaturation.isSet() ? &saturation : nullptr, argValue.isSet() ? &value : nullptr, argThreshold.isSet() ? &threshold : nullptr, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5e2844e3..22957155 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,5 +41,9 @@ add_executable(test_blackborderprocessor target_link_libraries(test_blackborderprocessor hyperion) +add_executable(test_qregexp TestQRegExp.cpp) +target_link_libraries(test_qregexp + ${QT_LIBRARIES}) + add_executable(spidev_test spidev_test.c) add_executable(gpio2spi switchPinCtrl.c) diff --git a/test/TestBlackBorderProcessor.cpp b/test/TestBlackBorderProcessor.cpp index beb018f0..f5317f05 100644 --- a/test/TestBlackBorderProcessor.cpp +++ b/test/TestBlackBorderProcessor.cpp @@ -148,7 +148,7 @@ int main() // Switch back (in one shot) to no border assert(processor.process(noBorderImage)); - assert(processor.getCurrentBorder().type == BlackBorder::none); + assert(processor.getCurrentBorder().verticalSize == 0 && processor.getCurrentBorder().horizontalSize == 0); return 0; } diff --git a/test/TestColorTransform.cpp b/test/TestColorTransform.cpp index b52c941c..21af454b 100644 --- a/test/TestColorTransform.cpp +++ b/test/TestColorTransform.cpp @@ -2,13 +2,14 @@ #include #include -#include +// Utils includes +#include int main() { { std::cout << "Testing linear transform" << std::endl; - ColorTransform t; + RgbChannelTransform t; for (int i = 0; i < 256; ++i) { uint8_t input = i; @@ -29,7 +30,7 @@ int main() { std::cout << "Testing threshold" << std::endl; - ColorTransform t(.10, 1.0, 0.0, 1.0); + RgbChannelTransform t(.10, 1.0, 0.0, 1.0); for (int i = 0; i < 256; ++i) { uint8_t input = i; @@ -50,7 +51,7 @@ int main() { std::cout << "Testing blacklevel and whitelevel" << std::endl; - ColorTransform t(0, 1.0, 0.2, 0.8); + RgbChannelTransform t(0, 1.0, 0.2, 0.8); for (int i = 0; i < 256; ++i) { uint8_t input = i; @@ -71,7 +72,7 @@ int main() { std::cout << "Testing gamma" << std::endl; - ColorTransform t(0, 2.0, 0.0, 1.0); + RgbChannelTransform t(0, 2.0, 0.0, 1.0); for (int i = 0; i < 256; ++i) { uint8_t input = i; diff --git a/test/TestQRegExp.cpp b/test/TestQRegExp.cpp new file mode 100644 index 00000000..efc74227 --- /dev/null +++ b/test/TestQRegExp.cpp @@ -0,0 +1,50 @@ + +// STL includes +#include + +// QT includes +#include +#include +#include + +int main() +{ + QString testString = "1-9, 11, 12,13,16-17"; + + QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*"); + { + + std::cout << "[1] Match found: " << (overallExp.exactMatch("5")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("4-")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("-4")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("3-9")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("1-90")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("1-90,100")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("1-90, 100")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("1-90, 100-200")?"true":"false") << std::endl; + std::cout << "[1] Match found: " << (overallExp.exactMatch("1-90, 100-200, 100")?"true":"false") << std::endl; + } + { + if (!overallExp.exactMatch(testString)) { + std::cout << "No correct match" << std::endl; + return -1; + } + QStringList splitString = testString.split(QChar(',')); + for (int i=0; i " << startInd << "-" << endInd << std::endl; + } + else + { + int index = splitString[i].toInt(); + std::cout << "==> " << index << std::endl; + } + } + } + + return 0; +}