Use GIF files for effects (#477)

* Add new Effects

* add schema file for gif based effects

* add background color to snake effect

* add background color to snake effect

* Update schema file for snake effect

* Add function getImage

* add function getImage

* optimize lights.gif

* Add format to GIF schema
This commit is contained in:
Paulchen Panther 2017-10-12 12:00:32 +02:00 committed by brindosch
parent 838008568a
commit 9cc6c75633
11 changed files with 179 additions and 7 deletions

BIN
effects/fire.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

10
effects/fire.json Normal file
View File

@ -0,0 +1,10 @@
{
"name" : "Fire",
"script" : "gif.py",
"args" :
{
"image" : ":fire.gif",
"fps" :25,
"reverse" : false
}
}

16
effects/gif.py Normal file
View File

@ -0,0 +1,16 @@
import os, hyperion, time
# Get the parameters
imageFile = hyperion.args.get('image')
framesPerSecond = float(hyperion.args.get('fps', 25))
reverse = bool(hyperion.args.get('reverse', False))
sleepTime = 1./framesPerSecond
if imageFile:
imageList = list(reversed(hyperion.getImage(imageFile))) if reverse else hyperion.getImage(imageFile)
# Start the write data loop
while not hyperion.abort() and imageList:
for image in imageList:
hyperion.setImage(image.imageWidth, image.imageHeight, image.imageData)
time.sleep(sleepTime)

BIN
effects/lights.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

10
effects/lights.json Normal file
View File

@ -0,0 +1,10 @@
{
"name" : "Lights",
"script" : "gif.py",
"args" :
{
"image" : ":lights.gif",
"fps" :25,
"reverse" : false
}
}

View File

@ -0,0 +1,31 @@
{
"type":"object",
"script" : "gif.py",
"title":"edt_eff_gif_header",
"required":true,
"properties":{
"image": {
"type": "string",
"title":"edt_eff_image",
"format" : "file",
"default": "",
"propertyOrder" : 1
},
"fps": {
"type": "integer",
"title":"edt_eff_fps",
"default": 25,
"minimum" : 1,
"maximum" : 100,
"step" : 1,
"propertyOrder" : 2
},
"reverse": {
"type": "boolean",
"title":"edt_eff_reversedirection",
"default": false,
"propertyOrder" : 3
}
},
"additionalProperties": false
}

View File

@ -18,20 +18,34 @@
"maxItems": 3, "maxItems": 3,
"propertyOrder" : 1 "propertyOrder" : 1
}, },
"background-color": {
"type": "array",
"title":"edt_eff_color",
"format":"colorpicker",
"default": [0,0,0],
"items" : {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"propertyOrder" : 2
},
"rotation-time": { "rotation-time": {
"type": "number", "type": "number",
"title":"edt_eff_rotationtime", "title":"edt_eff_rotationtime",
"default": 12.0, "default": 12.0,
"minimum" : 0.1, "minimum" : 0.1,
"append" : "edt_append_s", "append" : "edt_append_s",
"propertyOrder" : 2 "propertyOrder" : 3
}, },
"percentage": { "percentage": {
"type": "integer", "type": "integer",
"title":"edt_eff_length", "title":"edt_eff_length",
"default": 10, "default": 10,
"append" : "edt_append_percent", "append" : "edt_append_percent",
"propertyOrder" : 3 "propertyOrder" : 4
} }
}, },
"additionalProperties": false "additionalProperties": false

View File

@ -5,6 +5,7 @@
{ {
"rotation-time" : 12.0, "rotation-time" : 12.0,
"color" : [255, 0, 0], "color" : [255, 0, 0],
"background-color" : [0, 0, 0],
"percentage" : 10 "percentage" : 10
} }
} }

View File

@ -5,6 +5,7 @@ import colorsys
# Get the parameters # Get the parameters
rotationTime = float(hyperion.args.get('rotation-time', 10.0)) rotationTime = float(hyperion.args.get('rotation-time', 10.0))
color = hyperion.args.get('color', (255,0,0)) color = hyperion.args.get('color', (255,0,0))
backgroundColor = hyperion.args.get('background-color', (0,0,0))
percentage = int(hyperion.args.get('percentage', 10)) percentage = int(hyperion.args.get('percentage', 10))
# Check parameters # Check parameters
@ -13,17 +14,23 @@ percentage = max(1, min(percentage, 100))
# Process parameters # Process parameters
factor = percentage/100.0 factor = percentage/100.0
hsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
# Initialize the led data # Initialize the led data
snakeLeds = max(1, int(hyperion.ledCount*factor)) snakeLeds = max(1, int(hyperion.ledCount*factor))
ledData = bytearray() ledData = bytearray()
for i in range(hyperion.ledCount-snakeLeds): color_hsv = colorsys.rgb_to_hsv(color[0]/255.0,color[1]/255.0,color[2]/255.0)
ledData += bytearray((0, 0, 0)) backgroundColor_hsv = colorsys.rgb_to_hsv(backgroundColor[0]/255.0,backgroundColor[1]/255.0,backgroundColor[2]/255.0)
for i in range(1,snakeLeds+1): for i in range(hyperion.ledCount-snakeLeds):
rgb = colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2]*(snakeLeds-i)/snakeLeds) ledData += bytearray((int(backgroundColor[0]), int(backgroundColor[1]), int(backgroundColor[2])))
def lerp(a, b, t):
return (a[0]*(1-t)+b[0]*t, a[1]*(1-t)+b[1]*t, a[2]*(1-t)+b[2]*t)
for i in range(0, snakeLeds):
hsv = lerp(color_hsv, backgroundColor_hsv, 1.0/(snakeLeds-1)*i)
rgb = colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2])
ledData += bytearray((int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))) ledData += bytearray((int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255)))
# Calculate the sleep time and rotation increment # Calculate the sleep time and rotation increment

View File

@ -14,6 +14,8 @@
#include <QConicalGradient> #include <QConicalGradient>
#include <QRadialGradient> #include <QRadialGradient>
#include <QRect> #include <QRect>
#include <QImageReader>
#include <QResource>
// effect engin eincludes // effect engin eincludes
#include "Effect.h" #include "Effect.h"
@ -24,6 +26,7 @@
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."},
{"getImage" , Effect::wrapGetImage , METH_VARARGS, "get image data from file."},
{"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_VARARGS, "set current effect image to hyperion core."}, {"imageShow" , Effect::wrapImageShow , METH_VARARGS, "set current effect image to hyperion core."},
{"imageLinearGradient" , Effect::wrapImageLinearGradient , METH_VARARGS, ""}, {"imageLinearGradient" , Effect::wrapImageLinearGradient , METH_VARARGS, ""},
@ -465,6 +468,85 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
return nullptr; return nullptr;
} }
PyObject* Effect::wrapGetImage(PyObject *self, PyObject *args)
{
Q_INIT_RESOURCE(EffectEngine);
char *source;
if(!PyArg_ParseTuple(args, "s", &source))
{
PyErr_SetString(PyExc_TypeError, "String required");
return NULL;
}
QString file = QString::fromUtf8(source);
if (file.mid(0, 1) == ":")
file = ":/effects/"+file.mid(1);
QImageReader reader(file);
PyObject* prop;
static PyObject* imageClass = NULL;
if (imageClass == NULL)
imageClass = PyClass_New(NULL, PyDict_New(), PyString_FromString("ImageClass"));
if (reader.canRead())
{
PyObject* result = PyList_New(reader.imageCount());
for (int i = 0; i < reader.imageCount(); ++i)
{
reader.jumpToImage(i);
if (reader.canRead())
{
QImage qimage = reader.read();
PyObject* imageDict = PyDict_New();
int width = qimage.width();
int height = qimage.height();
prop = Py_BuildValue("i", width);
PyDict_SetItemString(imageDict, "imageWidth", prop);
Py_XDECREF(prop);
prop = Py_BuildValue("i", height);
PyDict_SetItemString(imageDict, "imageHeight", prop);
Py_XDECREF(prop);
QByteArray binaryImage;
for (int i = 0; i<height; ++i)
{
const QRgb * scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i));
for (int j = 0; j< width; ++j)
{
binaryImage.append((char) qRed(scanline[j]));
binaryImage.append((char) qGreen(scanline[j]));
binaryImage.append((char) qBlue(scanline[j]));
}
}
prop = Py_BuildValue("O", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size()));
PyDict_SetItemString(imageDict, "imageData", prop);
Py_XDECREF(prop);
PyList_SET_ITEM(result, i, Py_BuildValue("O", PyInstance_NewRaw(imageClass, imageDict)));
}
else
{
PyErr_SetString(PyExc_TypeError, reader.errorString().toUtf8().constData());
return NULL;
}
}
return result;
}
else
{
PyErr_SetString(PyExc_TypeError, reader.errorString().toUtf8().constData());
return NULL;
}
}
PyObject* Effect::wrapAbort(PyObject *self, PyObject *) PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
{ {
Effect * effect = getEffect(); Effect * effect = getEffect();

View File

@ -46,6 +46,7 @@ private:
static PyMethodDef effectMethods[]; static PyMethodDef effectMethods[];
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* wrapGetImage (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* wrapImageShow (PyObject *self, PyObject *args);
static PyObject* wrapImageLinearGradient (PyObject *self, PyObject *args); static PyObject* wrapImageLinearGradient (PyObject *self, PyObject *args);