mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
extend effect engine with qt image effects (#249)
* - effects now can use qt image effects - rainbow swirtl is now suitable for any led layout - including matrix * fix rainbow effect * effect: add radialGradient * fix some js errors * optimize code * try fix travis test not working as expected * fix default config files * fix config
This commit is contained in:
parent
9340a9a7c8
commit
f88cd3a230
@ -22,6 +22,6 @@ before_install:
|
|||||||
- ./.travis/travis_install.sh
|
- ./.travis/travis_install.sh
|
||||||
script:
|
script:
|
||||||
- ./.travis/travis_build.sh
|
- ./.travis/travis_build.sh
|
||||||
after_success:
|
|
||||||
- ./test/testrunner.sh
|
- ./test/testrunner.sh
|
||||||
|
after_success:
|
||||||
- ./.travis/travis_deploy.sh
|
- ./.travis/travis_deploy.sh
|
@ -23,20 +23,6 @@ $(hyperion).one("cmd-config-getschema", function(event) {
|
|||||||
parsedConfSchemaJSON = event.response.result;
|
parsedConfSchemaJSON = event.response.result;
|
||||||
|
|
||||||
schema = parsedConfSchemaJSON.properties;
|
schema = parsedConfSchemaJSON.properties;
|
||||||
blackborderdetector = schema.blackborderdetector;
|
|
||||||
color = schema.color;
|
|
||||||
effects = schema.effects;
|
|
||||||
forwarder = schema.forwarder;
|
|
||||||
initialEffect = schema.initialEffect;
|
|
||||||
kodiVideoChecker = schema.kodiVideoChecker;
|
|
||||||
smoothing = schema.smoothing;
|
|
||||||
logger = schema.logger;
|
|
||||||
jsonServer = schema.jsonServer;
|
|
||||||
protoServer = schema.protoServer;
|
|
||||||
boblightServer = schema.boblightServer;
|
|
||||||
udpListener = schema.udpListener;
|
|
||||||
webConfig = schema.webConfig;
|
|
||||||
|
|
||||||
var element = document.getElementById('editor_container');
|
var element = document.getElementById('editor_container');
|
||||||
|
|
||||||
var general_conf_editor = new JSONEditor(element,{
|
var general_conf_editor = new JSONEditor(element,{
|
||||||
@ -50,19 +36,19 @@ $(hyperion).one("cmd-config-getschema", function(event) {
|
|||||||
schema: {
|
schema: {
|
||||||
title:'',
|
title:'',
|
||||||
properties: {
|
properties: {
|
||||||
blackborderdetector,
|
blackborderdetector: schema.blackborderdetector,
|
||||||
color,
|
color : schema.color,
|
||||||
effects,
|
effects : schema.effects,
|
||||||
forwarder,
|
forwarder : schema.forwarder,
|
||||||
initialEffect,
|
initialEffect : schema.initialEffect,
|
||||||
kodiVideoChecker,
|
kodiVideoChecker : schema.kodiVideoChecker,
|
||||||
smoothing,
|
smoothing : schema.smoothing,
|
||||||
logger,
|
logger : schema.logger,
|
||||||
jsonServer,
|
jsonServer : schema.jsonServer,
|
||||||
protoServer,
|
protoServer : schema.protoServer,
|
||||||
boblightServer,
|
boblightServer : schema.boblightServer,
|
||||||
udpListener,
|
udpListener : schema.udpListener,
|
||||||
webConfig
|
webConfig : schema.webConfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -80,7 +66,7 @@ $(hyperion).one("cmd-config-getschema", function(event) {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
//Alternative Function with submit button to get Values
|
//Alternative Function with submit button to get Values
|
||||||
$('btn_submit').off().on('click',function() {
|
$('#btn_submit').off().on('click',function() {
|
||||||
console.log(general_conf_editor.getValue());
|
console.log(general_conf_editor.getValue());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,15 +20,14 @@ function removeAdvanced(obj,searchStack)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var grabber_conf_editor = null;
|
||||||
$(hyperion).one("cmd-config-getschema", function(event) {
|
$(hyperion).one("cmd-config-getschema", function(event) {
|
||||||
parsedConfSchemaJSON = event.response.result;
|
parsedConfSchemaJSON = event.response.result;
|
||||||
schema = parsedConfSchemaJSON.properties;
|
schema = parsedConfSchemaJSON.properties;
|
||||||
schema_framegrabber = schema.framegrabber;
|
|
||||||
schema_grabberv4l2 = schema["grabber-v4l2"];
|
|
||||||
|
|
||||||
var element = document.getElementById('editor_container');
|
var element = document.getElementById('editor_container');
|
||||||
|
|
||||||
var grabber_conf_editor = new JSONEditor(element,{
|
grabber_conf_editor = new JSONEditor(element,{
|
||||||
theme: 'bootstrap3',
|
theme: 'bootstrap3',
|
||||||
iconlib: "fontawesome4",
|
iconlib: "fontawesome4",
|
||||||
disable_collapse: 'true',
|
disable_collapse: 'true',
|
||||||
@ -39,8 +38,8 @@ $(hyperion).one("cmd-config-getschema", function(event) {
|
|||||||
schema: {
|
schema: {
|
||||||
title:'',
|
title:'',
|
||||||
properties: {
|
properties: {
|
||||||
schema_framegrabber,
|
framegrabber: schema.framegrabber,
|
||||||
schema_grabberv4l2,
|
grabberV4L2 : schema["grabberV4L2"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -59,7 +58,7 @@ $(document).ready( function() {
|
|||||||
|
|
||||||
document.getElementById('btn_submit').addEventListener('click',function() {
|
document.getElementById('btn_submit').addEventListener('click',function() {
|
||||||
// Get the value from the editor
|
// Get the value from the editor
|
||||||
//console.log(general_conf_editor.getValue());
|
console.log(grabber_conf_editor.getValue());
|
||||||
});
|
});
|
||||||
// $("[type='checkbox']").bootstrapSwitch();
|
// $("[type='checkbox']").bootstrapSwitch();
|
||||||
});
|
});
|
||||||
|
@ -153,8 +153,8 @@ $(document).ready(function() {
|
|||||||
schema: {
|
schema: {
|
||||||
title:' ',
|
title:' ',
|
||||||
properties: {
|
properties: {
|
||||||
generalOptions,
|
generalOptions : generalOptions,
|
||||||
specificOptions,
|
specificOptions : specificOptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -96,6 +96,7 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
$('#cp2').colorpicker().on('changeColor', function(e) {
|
$('#cp2').colorpicker().on('changeColor', function(e) {
|
||||||
color = e.color.toRGB();
|
color = e.color.toRGB();
|
||||||
|
$("#effect_select").val("__none__");
|
||||||
requestSetColor(color.r, color.g, color.b);
|
requestSetColor(color.r, color.g, color.b);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -106,7 +107,10 @@ $(document).ready(function() {
|
|||||||
efx = $(this).val();
|
efx = $(this).val();
|
||||||
if(efx != "__none__")
|
if(efx != "__none__")
|
||||||
{
|
{
|
||||||
requestPlayEffect(efx);
|
requestPriorityClear();
|
||||||
|
$(hyperion).one("cmd-clear", function(event) {
|
||||||
|
setTimeout(function() {requestPlayEffect(efx)}, 100);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
/// * redSignalThreshold : Signal threshold for the red channel between 0.0 and 1.0 [default=0.0]
|
/// * redSignalThreshold : Signal threshold for the red channel between 0.0 and 1.0 [default=0.0]
|
||||||
/// * greenSignalThreshold : Signal threshold for the green channel between 0.0 and 1.0 [default=0.0]
|
/// * greenSignalThreshold : Signal threshold for the green channel between 0.0 and 1.0 [default=0.0]
|
||||||
/// * blueSignalThreshold : Signal threshold for the blue channel between 0.0 and 1.0 [default=0.0]
|
/// * blueSignalThreshold : Signal threshold for the blue channel between 0.0 and 1.0 [default=0.0]
|
||||||
"grabber-v4l2" :
|
"grabberV4L2" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"enable" : false,
|
"enable" : false,
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
"continuousOutput" : true
|
"continuousOutput" : true
|
||||||
},
|
},
|
||||||
|
|
||||||
"grabber-v4l2" :
|
"grabberV4L2" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"enable" : false,
|
"enable" : false,
|
||||||
@ -194,7 +194,7 @@
|
|||||||
|
|
||||||
"webConfig" :
|
"webConfig" :
|
||||||
{
|
{
|
||||||
"enable" : true,
|
"enable" : true
|
||||||
},
|
},
|
||||||
|
|
||||||
"effects" :
|
"effects" :
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
"script" : "rainbow-swirl.py",
|
"script" : "rainbow-swirl.py",
|
||||||
"args" :
|
"args" :
|
||||||
{
|
{
|
||||||
"rotation-time" : 3.0,
|
"rotation-time" : 4.0,
|
||||||
"brightness" : 1.0,
|
"center_x" : 0.5,
|
||||||
|
"center_y" : 0.5,
|
||||||
"reverse" : false
|
"reverse" : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
"args" :
|
"args" :
|
||||||
{
|
{
|
||||||
"rotation-time" : 20.0,
|
"rotation-time" : 20.0,
|
||||||
"brightness" : 1.0,
|
"center_x" : 0.5,
|
||||||
|
"center_y" : 0.5,
|
||||||
"reverse" : false
|
"reverse" : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,37 @@
|
|||||||
import hyperion
|
import hyperion, time
|
||||||
import time
|
|
||||||
import colorsys
|
|
||||||
|
|
||||||
# Get the parameters
|
# Get the parameters
|
||||||
rotationTime = float(hyperion.args.get('rotation-time', 3.0))
|
rotationTime = float(hyperion.args.get('rotation-time', 3.0))
|
||||||
brightness = float(hyperion.args.get('brightness', 1.0))
|
|
||||||
saturation = float(hyperion.args.get('saturation', 1.0))
|
|
||||||
reverse = bool(hyperion.args.get('reverse', False))
|
reverse = bool(hyperion.args.get('reverse', False))
|
||||||
|
centerX = float(hyperion.args.get('center_x', 0.5))
|
||||||
|
centerY = float(hyperion.args.get('center_y', 0.5))
|
||||||
|
|
||||||
# Check parameters
|
sleepTime = max(0.1, rotationTime) / 360
|
||||||
rotationTime = max(0.1, rotationTime)
|
angle = 0
|
||||||
brightness = max(0.0, min(brightness, 1.0))
|
centerX = int(round(hyperion.imageWidth)*centerX)
|
||||||
saturation = max(0.0, min(saturation, 1.0))
|
centerY = int(round(float(hyperion.imageHeight)*centerY))
|
||||||
|
increment = -1 if reverse else 1
|
||||||
|
|
||||||
# Initialize the led data
|
# table of stop colors for rainbow gradient, first is the position, next rgb, all values 0-255
|
||||||
ledData = bytearray()
|
rainbowColors = bytearray([
|
||||||
for i in range(hyperion.ledCount):
|
0 ,255,0 ,0,
|
||||||
hue = float(i)/hyperion.ledCount
|
25 ,255,230,0,
|
||||||
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
|
63 ,255,255,0,
|
||||||
ledData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
|
100,0 ,255,0,
|
||||||
|
127,0 ,255,200,
|
||||||
|
159,0 ,255,255,
|
||||||
|
191,0 ,0 ,255,
|
||||||
|
224,255,0 ,255,
|
||||||
|
255,255,0 ,127,
|
||||||
|
])
|
||||||
|
|
||||||
# Calculate the sleep time and rotation increment
|
# effect loop
|
||||||
increment = 3
|
|
||||||
sleepTime = rotationTime / hyperion.ledCount
|
|
||||||
while sleepTime < 0.05:
|
|
||||||
increment *= 2
|
|
||||||
sleepTime *= 2
|
|
||||||
increment %= hyperion.ledCount
|
|
||||||
|
|
||||||
# Switch direction if needed
|
|
||||||
if reverse:
|
|
||||||
increment = -increment
|
|
||||||
|
|
||||||
# Start the write data loop
|
|
||||||
while not hyperion.abort():
|
while not hyperion.abort():
|
||||||
hyperion.setColor(ledData)
|
angle += increment
|
||||||
ledData = ledData[-increment:] + ledData[:-increment]
|
if angle > 360: angle=0
|
||||||
|
if angle < 0: angle=360
|
||||||
|
|
||||||
|
hyperion.imageCanonicalGradient(centerX, centerY, angle, rainbowColors)
|
||||||
|
|
||||||
|
hyperion.imageShow()
|
||||||
time.sleep(sleepTime)
|
time.sleep(sleepTime)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
// QT includes
|
// QT includes
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QSize>
|
||||||
|
|
||||||
// hyperion-utils includes
|
// hyperion-utils includes
|
||||||
#include <utils/Image.h>
|
#include <utils/Image.h>
|
||||||
@ -86,6 +87,8 @@ public:
|
|||||||
///
|
///
|
||||||
unsigned getLedCount() const;
|
unsigned getLedCount() const;
|
||||||
|
|
||||||
|
QSize getLedGridSize() const { return _ledGridSize; }
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the current priority
|
/// Returns the current priority
|
||||||
///
|
///
|
||||||
@ -292,6 +295,7 @@ public:
|
|||||||
|
|
||||||
static LinearColorSmoothing * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice);
|
static LinearColorSmoothing * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice);
|
||||||
static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig);
|
static MessageForwarder * createMessageForwarder(const Json::Value & forwarderConfig);
|
||||||
|
static QSize getLedLayoutGridSize(const Json::Value& ledsConfig);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/// Signal which is emitted when a priority channel is actively cleared
|
/// Signal which is emitted when a priority channel is actively cleared
|
||||||
@ -390,4 +394,6 @@ private:
|
|||||||
int _currentSourcePriority;
|
int _currentSourcePriority;
|
||||||
|
|
||||||
QByteArray _configHash;
|
QByteArray _configHash;
|
||||||
|
|
||||||
|
QSize _ledGridSize;
|
||||||
};
|
};
|
||||||
|
@ -48,6 +48,8 @@ add_library(effectengine
|
|||||||
${EffectEngineSOURCES}
|
${EffectEngineSOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt5_use_modules(effectengine Core Gui)
|
||||||
|
|
||||||
target_link_libraries(effectengine
|
target_link_libraries(effectengine
|
||||||
hyperion
|
hyperion
|
||||||
jsoncpp
|
jsoncpp
|
||||||
|
@ -8,16 +8,27 @@
|
|||||||
// Qt includes
|
// Qt includes
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <Qt>
|
||||||
|
#include <QLinearGradient>
|
||||||
|
#include <QConicalGradient>
|
||||||
|
#include <QRadialGradient>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
// effect engin eincludes
|
// effect engin eincludes
|
||||||
#include "Effect.h"
|
#include "Effect.h"
|
||||||
#include <utils/Logger.h>
|
#include <utils/Logger.h>
|
||||||
|
#include <hyperion/Hyperion.h>
|
||||||
|
|
||||||
// Python method table
|
// Python method table
|
||||||
PyMethodDef Effect::effectMethods[] = {
|
PyMethodDef Effect::effectMethods[] = {
|
||||||
{"setColor", Effect::wrapSetColor, METH_VARARGS, "Set a new color for the leds."},
|
{"setColor", Effect::wrapSetColor, METH_VARARGS, "Set a new color for the leds."},
|
||||||
{"setImage", Effect::wrapSetImage, METH_VARARGS, "Set a new image to process and determine new led colors."},
|
{"setImage", Effect::wrapSetImage, METH_VARARGS, "Set a new image to process and determine new led colors."},
|
||||||
{"abort", Effect::wrapAbort, METH_NOARGS, "Check if the effect should abort execution."},
|
{"abort", Effect::wrapAbort, METH_NOARGS, "Check if the effect should abort execution."},
|
||||||
|
{"imageShow", Effect::wrapImageShow, METH_NOARGS, "set current effect image to hyperion core."},
|
||||||
|
{"imageCanonicalGradient", Effect::wrapImageCanonicalGradient, METH_VARARGS, ""},
|
||||||
|
{"imageRadialGradient" , Effect::wrapImageRadialGradient, METH_VARARGS, ""},
|
||||||
|
// {"imageSetPixel",Effect::wrapImageShow, METH_VARARGS, "set pixel color of image"},
|
||||||
|
// {"imageGetPixel",Effect::wrapImageShow, METH_VARARGS, "get pixel color of image"},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,6 +81,12 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const
|
|||||||
// disable the black border detector for effects
|
// disable the black border detector for effects
|
||||||
_imageProcessor->enableBlackBorderDetector(false);
|
_imageProcessor->enableBlackBorderDetector(false);
|
||||||
|
|
||||||
|
// init effect image for image based effects, size is based on led layout
|
||||||
|
_imageSize = Hyperion::getInstance()->getLedGridSize();
|
||||||
|
_image = new QImage(_imageSize, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_image->fill(Qt::black);
|
||||||
|
_painter = new QPainter(_image);
|
||||||
|
|
||||||
// connect the finished signal
|
// connect the finished signal
|
||||||
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
|
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
|
||||||
Q_INIT_RESOURCE(EffectEngine);
|
Q_INIT_RESOURCE(EffectEngine);
|
||||||
@ -77,6 +94,8 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const
|
|||||||
|
|
||||||
Effect::~Effect()
|
Effect::~Effect()
|
||||||
{
|
{
|
||||||
|
delete _painter;
|
||||||
|
delete _image;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Effect::run()
|
void Effect::run()
|
||||||
@ -96,6 +115,12 @@ void Effect::run()
|
|||||||
// add ledCount variable to the interpreter
|
// add ledCount variable to the interpreter
|
||||||
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
||||||
|
|
||||||
|
// add imageWidth variable to the interpreter
|
||||||
|
PyObject_SetAttrString(module, "imageWidth", Py_BuildValue("i", _imageSize.width()));
|
||||||
|
|
||||||
|
// add imageHeight variable to the interpreter
|
||||||
|
PyObject_SetAttrString(module, "imageHeight", Py_BuildValue("i", _imageSize.height()));
|
||||||
|
|
||||||
// add a args variable to the interpreter
|
// add a args variable to the interpreter
|
||||||
PyObject_SetAttrString(module, "args", json2python(_args));
|
PyObject_SetAttrString(module, "args", json2python(_args));
|
||||||
|
|
||||||
@ -357,6 +382,207 @@ PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
|||||||
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
|
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject* Effect::wrapImageShow(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
|
// determine the timeout
|
||||||
|
int timeout = effect->_timeout;
|
||||||
|
if (timeout > 0)
|
||||||
|
{
|
||||||
|
timeout = effect->_endTime - QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
|
// we are done if the time has passed
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = effect->_imageSize.width();
|
||||||
|
int height = effect->_imageSize.height();
|
||||||
|
QImage * qimage = effect->_image;
|
||||||
|
|
||||||
|
Image<ColorRgb> image(width, height);
|
||||||
|
QByteArray binaryImage;
|
||||||
|
|
||||||
|
for (int i = 0; i <qimage->height(); ++i)
|
||||||
|
{
|
||||||
|
const QRgb * scanline = reinterpret_cast<const QRgb *>(qimage->scanLine(i));
|
||||||
|
for (int j = 0; j < qimage->width(); ++j)
|
||||||
|
{
|
||||||
|
binaryImage.append((char) qRed(scanline[j]));
|
||||||
|
binaryImage.append((char) qGreen(scanline[j]));
|
||||||
|
binaryImage.append((char) qBlue(scanline[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(image.memptr(), binaryImage.data(), binaryImage.size());
|
||||||
|
effect->_imageProcessor->process(image, effect->_colors);
|
||||||
|
effect->setColors(effect->_priority, effect->_colors, timeout, false);
|
||||||
|
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject* Effect::wrapImageCanonicalGradient(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
|
int argCount = PyTuple_Size(args);
|
||||||
|
PyObject * bytearray = nullptr;
|
||||||
|
int centerX, centerY, angle;
|
||||||
|
int startX = 0;
|
||||||
|
int startY = 0;
|
||||||
|
int width = effect->_imageSize.width();
|
||||||
|
int height = effect->_imageSize.height();
|
||||||
|
|
||||||
|
bool argsOK = false;
|
||||||
|
|
||||||
|
if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &angle, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
}
|
||||||
|
if ( argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &angle, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
}
|
||||||
|
angle = std::max(std::min(angle,360),0);
|
||||||
|
|
||||||
|
if (argsOK)
|
||||||
|
{
|
||||||
|
if (PyByteArray_Check(bytearray))
|
||||||
|
{
|
||||||
|
int length = PyByteArray_Size(bytearray);
|
||||||
|
if (length % 4 == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
QPainter * painter = effect->_painter;
|
||||||
|
QRect myQRect(startX,startY,width,height);
|
||||||
|
QConicalGradient gradient(QPoint(centerX,centerY), angle );
|
||||||
|
char * data = PyByteArray_AS_STRING(bytearray);
|
||||||
|
|
||||||
|
for (int idx=0; idx<length; idx+=4)
|
||||||
|
{
|
||||||
|
gradient.setColorAt(
|
||||||
|
((uint8_t)data[idx])/255.0,
|
||||||
|
QColor(
|
||||||
|
(uint8_t)(data[idx+1]),
|
||||||
|
(uint8_t)(data[idx+2]),
|
||||||
|
(uint8_t)(data[idx+3])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
gradient.setSpread(QGradient::RepeatSpread);
|
||||||
|
painter->fillRect(myQRect, gradient);
|
||||||
|
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should multiple of 4");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Argument 8 is not a bytearray");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject* Effect::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
|
int argCount = PyTuple_Size(args);
|
||||||
|
PyObject * bytearray = nullptr;
|
||||||
|
int centerX, centerY, radius, focalX, focalY, focalRadius;
|
||||||
|
int startX = 0;
|
||||||
|
int startY = 0;
|
||||||
|
int width = effect->_imageSize.width();
|
||||||
|
int height = effect->_imageSize.height();
|
||||||
|
|
||||||
|
bool argsOK = false;
|
||||||
|
|
||||||
|
if ( argCount == 11 && PyArg_ParseTuple(args, "iiiiiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
}
|
||||||
|
if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
focalX = centerX;
|
||||||
|
focalY = centerY;
|
||||||
|
focalRadius = radius;
|
||||||
|
}
|
||||||
|
if ( argCount == 7 && PyArg_ParseTuple(args, "iiiiiiO", ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
}
|
||||||
|
if ( argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &radius, &bytearray) )
|
||||||
|
{
|
||||||
|
argsOK = true;
|
||||||
|
focalX = centerX;
|
||||||
|
focalY = centerY;
|
||||||
|
focalRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argsOK)
|
||||||
|
{
|
||||||
|
if (PyByteArray_Check(bytearray))
|
||||||
|
{
|
||||||
|
int length = PyByteArray_Size(bytearray);
|
||||||
|
if (length % 4 == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
QPainter * painter = effect->_painter;
|
||||||
|
QRect myQRect(startX,startY,width,height);
|
||||||
|
QRadialGradient gradient(QPoint(centerX,centerY), std::max(radius,0) );
|
||||||
|
char * data = PyByteArray_AS_STRING(bytearray);
|
||||||
|
|
||||||
|
for (int idx=0; idx<length; idx+=4)
|
||||||
|
{
|
||||||
|
gradient.setColorAt(
|
||||||
|
((uint8_t)data[idx])/255.0,
|
||||||
|
QColor(
|
||||||
|
(uint8_t)(data[idx+1]),
|
||||||
|
(uint8_t)(data[idx+2]),
|
||||||
|
(uint8_t)(data[idx+3])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
//gradient.setSpread(QGradient::ReflectSpread);
|
||||||
|
painter->fillRect(myQRect, gradient);
|
||||||
|
|
||||||
|
return Py_BuildValue("");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should multiple of 4");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Last argument is not a bytearray");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Effect * Effect::getEffect()
|
Effect * Effect::getEffect()
|
||||||
{
|
{
|
||||||
// extract the module from the runtime
|
// extract the module from the runtime
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
// Qt includes
|
// Qt includes
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
// Hyperion includes
|
// Hyperion includes
|
||||||
#include <hyperion/ImageProcessor.h>
|
#include <hyperion/ImageProcessor.h>
|
||||||
@ -52,6 +55,10 @@ private:
|
|||||||
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
||||||
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
||||||
static PyObject* wrapAbort(PyObject *self, PyObject *args);
|
static PyObject* wrapAbort(PyObject *self, PyObject *args);
|
||||||
|
static PyObject* wrapImageShow(PyObject *self, PyObject *args);
|
||||||
|
static PyObject* wrapImageCanonicalGradient(PyObject *self, PyObject *args);
|
||||||
|
static PyObject* wrapImageRadialGradient(PyObject *self, PyObject *args);
|
||||||
|
|
||||||
static Effect * getEffect();
|
static Effect * getEffect();
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
@ -84,5 +91,10 @@ private:
|
|||||||
|
|
||||||
/// Buffer for colorData
|
/// Buffer for colorData
|
||||||
std::vector<ColorRgb> _colors;
|
std::vector<ColorRgb> _colors;
|
||||||
|
|
||||||
|
QSize _imageSize;
|
||||||
|
|
||||||
|
QImage * _image;
|
||||||
|
QPainter * _painter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -423,6 +423,7 @@ LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOr
|
|||||||
LedString ledString;
|
LedString ledString;
|
||||||
const std::string deviceOrderStr = colorOrderToString(deviceOrder);
|
const std::string deviceOrderStr = colorOrderToString(deviceOrder);
|
||||||
int maxLedId = ledsConfig.size();
|
int maxLedId = ledsConfig.size();
|
||||||
|
|
||||||
for (const Json::Value& ledConfig : ledsConfig)
|
for (const Json::Value& ledConfig : ledsConfig)
|
||||||
{
|
{
|
||||||
Led led;
|
Led led;
|
||||||
@ -499,6 +500,51 @@ LedString Hyperion::createLedStringClone(const Json::Value& ledsConfig, const Co
|
|||||||
return ledString;
|
return ledString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize Hyperion::getLedLayoutGridSize(const Json::Value& ledsConfig)
|
||||||
|
{
|
||||||
|
std::vector<int> midPointsX;
|
||||||
|
std::vector<int> midPointsY;
|
||||||
|
|
||||||
|
for (const Json::Value& ledConfig : ledsConfig)
|
||||||
|
{
|
||||||
|
if ( ledConfig.get("clone",-1).asInt() < 0 )
|
||||||
|
{
|
||||||
|
const Json::Value& hscanConfig = ledConfig["hscan"];
|
||||||
|
const Json::Value& vscanConfig = ledConfig["vscan"];
|
||||||
|
double minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].asDouble()));
|
||||||
|
double maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].asDouble()));
|
||||||
|
double minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].asDouble()));
|
||||||
|
double maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].asDouble()));
|
||||||
|
// Fix if the user swapped min and max
|
||||||
|
if (minX_frac > maxX_frac)
|
||||||
|
{
|
||||||
|
std::swap(minX_frac, maxX_frac);
|
||||||
|
}
|
||||||
|
if (minY_frac > maxY_frac)
|
||||||
|
{
|
||||||
|
std::swap(minY_frac, maxY_frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate mid point and make grid calculation
|
||||||
|
midPointsX.push_back( int(1000.0*(minX_frac + maxX_frac) / 2.0) );
|
||||||
|
midPointsY.push_back( int(1000.0*(minY_frac + maxY_frac) / 2.0) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove duplicates
|
||||||
|
std::sort(midPointsX.begin(), midPointsX.end());
|
||||||
|
midPointsX.erase(std::unique(midPointsX.begin(), midPointsX.end()), midPointsX.end());
|
||||||
|
std::sort(midPointsY.begin(), midPointsY.end());
|
||||||
|
midPointsY.erase(std::unique(midPointsY.begin(), midPointsY.end()), midPointsY.end());
|
||||||
|
|
||||||
|
QSize gridSize( midPointsX.size(), midPointsY.size() );
|
||||||
|
Debug(Logger::getInstance("Core"), "led layout grid: %dx%d", gridSize.width(), gridSize.height());
|
||||||
|
|
||||||
|
return gridSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LinearColorSmoothing * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice)
|
LinearColorSmoothing * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice)
|
||||||
{
|
{
|
||||||
Logger * log = Logger::getInstance("Core");
|
Logger * log = Logger::getInstance("Core");
|
||||||
@ -578,6 +624,7 @@ Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile)
|
|||||||
, _hwLedCount(_ledString.leds().size())
|
, _hwLedCount(_ledString.leds().size())
|
||||||
, _sourceAutoSelectEnabled(true)
|
, _sourceAutoSelectEnabled(true)
|
||||||
, _configHash()
|
, _configHash()
|
||||||
|
, _ledGridSize(getLedLayoutGridSize(jsonConfig["leds"]))
|
||||||
{
|
{
|
||||||
registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY);
|
registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY);
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@
|
|||||||
},
|
},
|
||||||
"additionalProperties" : false
|
"additionalProperties" : false
|
||||||
},
|
},
|
||||||
"grabber-v4l2" :
|
"grabberV4L2" :
|
||||||
{
|
{
|
||||||
"type":"array",
|
"type":"array",
|
||||||
"title" : "USB Grabber",
|
"title" : "USB Grabber",
|
||||||
|
@ -548,13 +548,13 @@ void HyperionDaemon::createGrabberOsx(const QJsonObject & grabberConfig)
|
|||||||
void HyperionDaemon::createGrabberV4L2()
|
void HyperionDaemon::createGrabberV4L2()
|
||||||
{
|
{
|
||||||
// construct and start the v4l2 grabber if the configuration is present
|
// construct and start the v4l2 grabber if the configuration is present
|
||||||
bool v4lConfigured = _qconfig.contains("grabber-v4l2");
|
bool v4lConfigured = _qconfig.contains("grabberV4L2");
|
||||||
bool v4lStarted = false;
|
bool v4lStarted = false;
|
||||||
unsigned v4lEnableCount = 0;
|
unsigned v4lEnableCount = 0;
|
||||||
|
|
||||||
if (_qconfig["grabber-v4l2"].isArray())
|
if (_qconfig["grabberV4L2"].isArray())
|
||||||
{
|
{
|
||||||
const QJsonArray & v4lArray = _qconfig["grabber-v4l2"].toArray();
|
const QJsonArray & v4lArray = _qconfig["grabberV4L2"].toArray();
|
||||||
for ( signed idx=0; idx<v4lArray.size(); idx++)
|
for ( signed idx=0; idx<v4lArray.size(); idx++)
|
||||||
{
|
{
|
||||||
const QJsonObject & grabberConfig = v4lArray.at(idx).toObject();
|
const QJsonObject & grabberConfig = v4lArray.at(idx).toObject();
|
||||||
|
Loading…
Reference in New Issue
Block a user