From 943c899288181988013dca3558a2382937f36f3f Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Tue, 28 Oct 2014 16:54:37 +0100 Subject: [PATCH 01/20] First cleanup of image loading code --- libcore/imageloader.c | 12 ++--- libcore/imageloader.h | 4 +- libcore/imagemagickwrapper.c | 92 +++--------------------------------- libcore/imagemagickwrapper.h | 5 -- views/view.c | 6 ++- 5 files changed, 16 insertions(+), 103 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 61e8076..07b324e 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -14,16 +14,12 @@ cImageLoader::cImageLoader() : cImageMagickWrapper() { cImageLoader::~cImageLoader() { } -cImage cImageLoader::GetImage() { - return CreateImageCopy(); +cImage *cImageLoader::GetImage(int width, int height) { + return CreateImage(width, height, false); } -bool cImageLoader::LoadImage(const char *path, int width, int height) { - if (cImageMagickWrapper::LoadImage(path)) { - buffer.sample(Geometry(width, height)); - return true; - } - return false; +bool cImageLoader::LoadImage(const char *path) { + return cImageMagickWrapper::LoadImage(path); } void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { diff --git a/libcore/imageloader.h b/libcore/imageloader.h index 2a148be..b2966c9 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -14,8 +14,8 @@ class cImageLoader : public cImageMagickWrapper { public: cImageLoader(); ~cImageLoader(); - cImage GetImage(); - bool LoadImage(const char *path, int width, int height); + cImage *GetImage(int width, int height); + bool LoadImage(const char *path); void DeterminateChannelLogoSize(int &width, int &height); private: }; diff --git a/libcore/imagemagickwrapper.c b/libcore/imagemagickwrapper.c index ab1bcba..b95aeb5 100644 --- a/libcore/imagemagickwrapper.c +++ b/libcore/imagemagickwrapper.c @@ -48,51 +48,6 @@ cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAsp return image; } -cImage cImageMagickWrapper::CreateImageCopy() { - int w, h; - w = buffer.columns(); - h = buffer.rows(); - cImage image (cSize(w, h)); - const PixelPacket *pixels = buffer.getConstPixels(0, 0, w, h); - for (int iy = 0; iy < h; ++iy) { - for (int ix = 0; ix < w; ++ix) { - tColor col = (~int(pixels->opacity * 255 / MaxRGB) << 24) - | (int(pixels->green * 255 / MaxRGB) << 8) - | (int(pixels->red * 255 / MaxRGB) << 16) - | (int(pixels->blue * 255 / MaxRGB) ); - image.SetPixel(cPoint(ix, iy), col); - ++pixels; - } - } - return image; -} - -bool cImageMagickWrapper::LoadImage(std::string FileName, std::string Path, std::string Extension) { - try { - std::stringstream sstrImgFile; - sstrImgFile << Path << FileName << "." << Extension; - std::string imgFile = sstrImgFile.str(); - if (config.debugImageLoading) - dsyslog("skindesigner: trying to load: %s", imgFile.c_str()); - buffer.read(imgFile.c_str()); - if (config.debugImageLoading) - dsyslog("skindesigner: %s sucessfully loaded", imgFile.c_str()); - } catch( Magick::Warning &warning ) { - if (config.debugImageLoading) - dsyslog("skindesigner: Magick Warning: %s", warning.what()); - return true; - } catch( Magick::Error &error ) { - if (config.debugImageLoading) - dsyslog("skindesigner: Magick Error: %s", error.what()); - return false; - } catch(...) { - if (config.debugImageLoading) - dsyslog("skindesigner: an unknown Magick error occured during image loading"); - return false; - } - return true; -} - bool cImageMagickWrapper::LoadImage(const char *fullpath) { if ((fullpath == NULL) || (strlen(fullpath) < 5)) return false; @@ -118,45 +73,10 @@ bool cImageMagickWrapper::LoadImage(const char *fullpath) { return true; } -Color cImageMagickWrapper::Argb2Color(tColor col) { - tIndex alpha = (col & 0xFF000000) >> 24; - tIndex red = (col & 0x00FF0000) >> 16; - tIndex green = (col & 0x0000FF00) >> 8; - tIndex blue = (col & 0x000000FF); - Color color(MaxRGB*red/255, MaxRGB*green/255, MaxRGB*blue/255, MaxRGB*(0xFF-alpha)/255); - return color; -} - -void cImageMagickWrapper::CreateGradient(tColor back, tColor blend, int width, int height, double wfactor, double hfactor) { - Color Back = Argb2Color(back); - Color Blend = Argb2Color(blend); - int maxw = MaxRGB * wfactor; - int maxh = MaxRGB * hfactor; - - Image imgblend(Geometry(width, height), Blend); - imgblend.modifyImage(); - imgblend.type(TrueColorMatteType); - PixelPacket *pixels = imgblend.getPixels(0, 0, width, height); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - PixelPacket *pixel = pixels + y * width + x; - int opacity = (maxw / width * x + maxh - maxh / height * y) / 2; - pixel->opacity = (opacity <= MaxRGB) ? opacity : MaxRGB; - } - } - imgblend.syncPixels(); - - Image imgback(Geometry(width, height), Back); - imgback.composite(imgblend, 0, 0, OverCompositeOp); - - buffer = imgback; -} - -void cImageMagickWrapper::CreateBackground(tColor back, tColor blend, int width, int height, bool mirror) { - CreateGradient(back, blend, width, height, 0.8, 0.8); - if (mirror) - buffer.flop(); -} -void cImageMagickWrapper::CreateBackgroundReverse(tColor back, tColor blend, int width, int height) { - CreateGradient(back, blend, width, height, 1.3, 0.7); +// Just a different way to call LoadImage. Calls the above one. +bool cImageMagickWrapper::LoadImage(std::string FileName, std::string Path, std::string Extension) { + std::stringstream sstrImgFile; + sstrImgFile << Path << FileName << "." << Extension; + std::string imgFile = sstrImgFile.str(); + return LoadImage(imgFile.c_str()); } diff --git a/libcore/imagemagickwrapper.h b/libcore/imagemagickwrapper.h index 5f9901e..da327a9 100644 --- a/libcore/imagemagickwrapper.h +++ b/libcore/imagemagickwrapper.h @@ -10,19 +10,14 @@ using namespace Magick; class cImageMagickWrapper { private: - void CreateGradient(tColor back, tColor blend, int width, int height, double wfactor, double hfactor); public: cImageMagickWrapper(); ~cImageMagickWrapper(); protected: Image buffer; - Color Argb2Color(tColor col); cImage *CreateImage(int width, int height, bool preserveAspect = true); - cImage CreateImageCopy(void); bool LoadImage(std::string FileName, std::string Path, std::string Extension); bool LoadImage(const char *fullpath); - void CreateBackground(tColor back, tColor blend, int width, int height, bool mirror = false); - void CreateBackgroundReverse(tColor back, tColor blend, int width, int height); }; #endif //__NOPACITY_IMAGEMAGICKWRAPPER_H diff --git a/views/view.c b/views/view.c index 75a24e0..8711136 100644 --- a/views/view.c +++ b/views/view.c @@ -711,8 +711,10 @@ void cView::DoDrawImage(int num, cTemplateFunction *func, int x0, int y0) { break; } case itImage: { cImageLoader imgLoader; - if (imgLoader.LoadImage(path.c_str(), width, height)) { - DrawImage(num, pos, imgLoader.GetImage()); + if (imgLoader.LoadImage(path.c_str())) { + cImage *image = imgLoader.GetImage(width, height); + DrawImage(num, pos, *image); + delete(image); } break; } default: From 8bf7b33c1dd3e131af1826e64a1bc51d1b0ac29e Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Tue, 28 Oct 2014 19:38:11 +0100 Subject: [PATCH 02/20] Use cairo to read PNG files --- Makefile | 3 ++ libcore/imagemagickwrapper.c | 81 +++++++++++++++++------------------- libcore/imagemagickwrapper.h | 5 +-- 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index 2160ae6..7b2b7d8 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,9 @@ else ifeq ($(IMAGELIB), graphicsmagick) LIBS += $(shell pkg-config --libs GraphicsMagick++) endif +INCLUDES += $(shell pkg-config --cflags cairo-png) +LIBS += $(shell pkg-config --libs cairo-png) + LIBS += $(shell xml2-config --libs) ### The object files: diff --git a/libcore/imagemagickwrapper.c b/libcore/imagemagickwrapper.c index b95aeb5..9823fab 100644 --- a/libcore/imagemagickwrapper.c +++ b/libcore/imagemagickwrapper.c @@ -12,64 +12,59 @@ cImageMagickWrapper::~cImageMagickWrapper() { } cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAspect) { + if (image == NULL) return NULL; + int w, h; - w = buffer.columns(); - h = buffer.rows(); + w = cairo_image_surface_get_width(image); + h = cairo_image_surface_get_height(image); if (width == 0) width = w; if (height == 0) height = h; + + cairo_surface_t *surface; + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + + cairo_t *cr; + cr = cairo_create(surface); + + double sx = width / w; + double sy = height / h; if (preserveAspect) { - unsigned scale_w = 1000 * width / w; - unsigned scale_h = 1000 * height / h; - if (scale_w > scale_h) - width = w * height / h; - else - height = h * width / w; + if (sx < sy) + sy = sx; + if (sy < sx) + sx = sy; } - const PixelPacket *pixels = buffer.getConstPixels(0, 0, w, h); - cImage *image = new cImage(cSize(width, height)); - tColor *imgData = (tColor *)image->Data(); - if (w != width || h != height) { - ImageScaler scaler; - scaler.SetImageParameters(imgData, width, width, height, w, h); - for (const void *pixels_end = &pixels[w*h]; pixels < pixels_end; ++pixels) - scaler.PutSourcePixel(pixels->blue / ((MaxRGB + 1) / 256), - pixels->green / ((MaxRGB + 1) / 256), - pixels->red / ((MaxRGB + 1) / 256), - ~((unsigned char)(pixels->opacity / ((MaxRGB + 1) / 256)))); - return image; - } - for (const void *pixels_end = &pixels[width*height]; pixels < pixels_end; ++pixels) - *imgData++ = ((~int(pixels->opacity / ((MaxRGB + 1) / 256)) << 24) | - (int(pixels->green / ((MaxRGB + 1) / 256)) << 8) | - (int(pixels->red / ((MaxRGB + 1) / 256)) << 16) | - (int(pixels->blue / ((MaxRGB + 1) / 256)) )); - return image; + cairo_scale(cr, sx, sy); + + cairo_set_source_surface(cr, image, 0, 0); + cairo_paint(cr); + + unsigned char *data = cairo_image_surface_get_data(surface); + cImage *cimage = new cImage(cSize(width, height), (tColor*)data); + + cairo_destroy(cr); + cairo_surface_destroy(image); + image = NULL; + + return cimage; } bool cImageMagickWrapper::LoadImage(const char *fullpath) { if ((fullpath == NULL) || (strlen(fullpath) < 5)) return false; - try { + + if (image != NULL) cairo_surface_destroy(image); + + image = cairo_image_surface_create_from_png(fullpath); + + if (cairo_surface_status(image)) { if (config.debugImageLoading) - dsyslog("skindesigner: trying to load: %s", fullpath); - buffer.read(fullpath); - if (config.debugImageLoading) - dsyslog("skindesigner: %s sucessfully loaded", fullpath); - } catch( Magick::Warning &warning ) { - if (config.debugImageLoading) - dsyslog("skindesigner: Magick Warning: %s", warning.what()); - return true; - } catch( Magick::Error &error ) { - if (config.debugImageLoading) - dsyslog("skindesigner: Magick Error: %s", error.what()); - return false; - } catch(...) { - if (config.debugImageLoading) - dsyslog("skindesigner: an unknown Magick error occured during image loading"); + dsyslog("skindesigner: Cairo Error: %s", cairo_status_to_string(cairo_surface_status(image))); return false; } + return true; } diff --git a/libcore/imagemagickwrapper.h b/libcore/imagemagickwrapper.h index da327a9..b0c19e8 100644 --- a/libcore/imagemagickwrapper.h +++ b/libcore/imagemagickwrapper.h @@ -3,10 +3,9 @@ #define X_DISPLAY_MISSING -#include +#include #include -using namespace Magick; class cImageMagickWrapper { private: @@ -14,7 +13,7 @@ public: cImageMagickWrapper(); ~cImageMagickWrapper(); protected: - Image buffer; + cairo_surface_t *image = NULL; cImage *CreateImage(int width, int height, bool preserveAspect = true); bool LoadImage(std::string FileName, std::string Path, std::string Extension); bool LoadImage(const char *fullpath); From 87aa10dc281627a06b588af79b1b32aa6b1ce8fb Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Tue, 28 Oct 2014 22:15:22 +0100 Subject: [PATCH 03/20] Fixed calculation of scale factors, more debug messages --- libcore/imagemagickwrapper.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libcore/imagemagickwrapper.c b/libcore/imagemagickwrapper.c index 9823fab..79e48e7 100644 --- a/libcore/imagemagickwrapper.c +++ b/libcore/imagemagickwrapper.c @@ -28,8 +28,8 @@ cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAsp cairo_t *cr; cr = cairo_create(surface); - double sx = width / w; - double sy = height / h; + double sx = width / (double)w; + double sy = height / (double)h; if (preserveAspect) { if (sx < sy) sy = sx; @@ -41,6 +41,10 @@ cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAsp cairo_set_source_surface(cr, image, 0, 0); cairo_paint(cr); + cairo_status_t status = cairo_status (cr); + if (status) + dsyslog("skindesigner: Cairo CreateImage Error %s", cairo_status_to_string(status)); + unsigned char *data = cairo_image_surface_get_data(surface); cImage *cimage = new cImage(cSize(width, height), (tColor*)data); @@ -57,11 +61,15 @@ bool cImageMagickWrapper::LoadImage(const char *fullpath) { if (image != NULL) cairo_surface_destroy(image); + if (config.debugImageLoading) + dsyslog("skindesigner: trying to load: %s", fullpath); + image = cairo_image_surface_create_from_png(fullpath); if (cairo_surface_status(image)) { if (config.debugImageLoading) - dsyslog("skindesigner: Cairo Error: %s", cairo_status_to_string(cairo_surface_status(image))); + dsyslog("skindesigner: Cairo LoadImage Error: %s", cairo_status_to_string(cairo_surface_status(image))); + image = NULL; return false; } From 94542945595f39866f868181f79583d20a019d1f Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Thu, 30 Oct 2014 16:41:06 +0100 Subject: [PATCH 04/20] Removed ImageMagick dependency. Final class names for Cairo backend --- Makefile | 13 ---- libcore/imagecache.c | 3 +- libcore/imagecache.h | 7 +- libcore/imageloader.c | 131 +++++++++++++++++++++++++++++++---- libcore/imageloader.h | 53 +++++++++++--- libcore/imagemagickwrapper.c | 85 ----------------------- libcore/imagemagickwrapper.h | 22 ------ views/view.c | 2 +- 8 files changed, 164 insertions(+), 152 deletions(-) delete mode 100644 libcore/imagemagickwrapper.c delete mode 100644 libcore/imagemagickwrapper.h diff --git a/Makefile b/Makefile index 7b2b7d8..8277229 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,6 @@ # # $Id$ Makefile 1.0 2014/07/24 louis Exp $ -# External image lib to use: imagemagick, graphicsmagick -IMAGELIB = imagemagick - # The official name of this plugin. PLUGIN = skindesigner @@ -47,14 +44,6 @@ DEFINES += $(shell xml2-config --cflags) INCLUDES += $(shell pkg-config --cflags freetype2 fontconfig) -ifeq ($(IMAGELIB), imagemagick) - INCLUDES += $(shell pkg-config --cflags Magick++) - LIBS += $(shell pkg-config --libs Magick++) -else ifeq ($(IMAGELIB), graphicsmagick) - INCLUDES += $(shell pkg-config --cflags GraphicsMagick++) - LIBS += $(shell pkg-config --libs GraphicsMagick++) -endif - INCLUDES += $(shell pkg-config --cflags cairo-png) LIBS += $(shell pkg-config --libs cairo-png) @@ -74,8 +63,6 @@ OBJS = $(PLUGIN).o \ libcore/pixmapcontainer.o \ libcore/fontmanager.o \ libcore/imagecache.o \ - libcore/imagemagickwrapper.o \ - libcore/imagescaler.o \ libcore/helpers.o \ libcore/imageloader.o \ libcore/recfolderinfo.o \ diff --git a/libcore/imagecache.c b/libcore/imagecache.c index 9ad9918..7c96438 100644 --- a/libcore/imagecache.c +++ b/libcore/imagecache.c @@ -7,14 +7,13 @@ #include "../config.h" #include "helpers.h" -using namespace Magick; cMutex cImageCache::mutex; string cImageCache::items[16] = { "Schedule", "Channels", "Timers", "Recordings", "Setup", "Commands", "OSD", "EPG", "DVB", "LNB", "CAM", "Recording", "Replay", "Miscellaneous", "Plugins", "Restart"}; -cImageCache::cImageCache() : cImageMagickWrapper() { +cImageCache::cImageCache() { tempStaticLogo = NULL; } diff --git a/libcore/imagecache.h b/libcore/imagecache.h index db56a67..4e88600 100644 --- a/libcore/imagecache.h +++ b/libcore/imagecache.h @@ -5,14 +5,11 @@ #include #include -#include #include -#include "imagemagickwrapper.h" +#include "imageloader.h" #include "../libtemplate/templatefunction.h" -using namespace Magick; - -class cImageCache : public cImageMagickWrapper { +class cImageCache : public cImageLoader { public: cImageCache(); ~cImageCache(); diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 07b324e..0c20fda 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -1,25 +1,84 @@ #include "../config.h" #include "helpers.h" #include "imageloader.h" -#include +//#include #include #include #include -using namespace Magick; - -cImageLoader::cImageLoader() : cImageMagickWrapper() { +cImageLoader::cImageLoader() { + importer = NULL; } cImageLoader::~cImageLoader() { + delete(importer); } -cImage *cImageLoader::GetImage(int width, int height) { - return CreateImage(width, height, false); +cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { + if (!importer) return NULL; + + int w, h; + importer->GetImageSize(w, h); + if (width == 0) + width = w; + if (height == 0) + height = h; + + cairo_surface_t *surface; + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + + cairo_t *cr; + cr = cairo_create(surface); + + double sx = width / (double)w; + double sy = height / (double)h; + if (preserveAspect) { + if (sx < sy) + sy = sx; + if (sy < sx) + sx = sy; + } + cairo_scale(cr, sx, sy); + + importer->DrawToCairo(cr); + cairo_paint(cr); + + cairo_status_t status = cairo_status(cr); + if (status) + dsyslog("skindesigner: Cairo CreateImage Error %s", cairo_status_to_string(status)); + + unsigned char *data = cairo_image_surface_get_data(surface); + cImage *image = new cImage(cSize(width, height), (tColor*)data); + + cairo_destroy(cr); + + return image; } -bool cImageLoader::LoadImage(const char *path) { - return cImageMagickWrapper::LoadImage(path); +bool cImageLoader::LoadImage(const char *fullpath) { + if ((fullpath == NULL) || (strlen(fullpath) < 5)) + return false; + + if (config.debugImageLoading) + dsyslog("skindesigner: trying to load: %s", fullpath); + + delete(importer); + importer = NULL; + + if (endswith(fullpath, ".png")) + importer = new cImageImporterPNG; + else + return false; + + return importer->LoadImage(fullpath); +} + +// Just a different way to call LoadImage. Calls the above one. +bool cImageLoader::LoadImage(std::string FileName, std::string Path, std::string Extension) { + std::stringstream sstrImgFile; + sstrImgFile << Path << FileName << "." << Extension; + std::string imgFile = sstrImgFile.str(); + return LoadImage(imgFile.c_str()); } void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { @@ -41,18 +100,60 @@ void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { if (endswith(file->d_name, *logoExt)) { std::stringstream filePath; filePath << *logoPath << file->d_name; - Image logo; - try { - logo.read(filePath.str().c_str()); - Geometry g = logo.size(); - int logoWidth = g.width(); - int logoHeight = g.height(); + if (LoadImage(filePath.str().c_str())) { + int logoWidth = 0; + int logoHeight = 0; + importer->GetImageSize(logoWidth, logoHeight); if (logoWidth > 0 && logoHeight > 0) { width = logoWidth; height = logoHeight; + delete(importer); + importer = NULL; return; } - } catch( ... ) { } + } } } } + + +// +// Image importer for PNG +// + +cImageImporterPNG::cImageImporterPNG() { + surface = NULL; +} + +cImageImporterPNG::~cImageImporterPNG() { + if (surface) + cairo_surface_destroy(surface); +} + +bool cImageImporterPNG::LoadImage(const char *path) { + if (surface) + cairo_surface_destroy(surface); + + surface = cairo_image_surface_create_from_png(path); + + if (cairo_surface_status(surface)) { + if (config.debugImageLoading) + dsyslog("skindesigner: Cairo LoadImage Error: %s", cairo_status_to_string(cairo_surface_status(surface))); + surface = NULL; + return false; + } + + return true; +} + +void cImageImporterPNG::DrawToCairo(cairo_t *cr) { + if (surface) + cairo_set_source_surface(cr, surface, 0, 0); +} + +void cImageImporterPNG::GetImageSize(int &width, int &height) { + if (surface) { + width = cairo_image_surface_get_width(surface); + height = cairo_image_surface_get_height(surface); + } +} diff --git a/libcore/imageloader.h b/libcore/imageloader.h index b2966c9..30e47b7 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -3,21 +3,56 @@ #define X_DISPLAY_MISSING +#include #include -#include -#include -#include "imagemagickwrapper.h" +#include -using namespace Magick; +// +// Image importers +// +class cImageImporter { +public: + cImageImporter() {}; + virtual ~cImageImporter() {}; + virtual bool LoadImage(const char *path) {}; + virtual void DrawToCairo(cairo_t *cr) {}; + virtual void GetImageSize(int &width, int &height) {}; +}; -class cImageLoader : public cImageMagickWrapper { +// Image importer for PNG +class cImageImporterPNG : public cImageImporter { +public: + cImageImporterPNG(); + ~cImageImporterPNG(); + bool LoadImage(const char *path); + void DrawToCairo(cairo_t *cr); + void GetImageSize(int &width, int &height); +private: + cairo_surface_t *surface; +}; + +// Image importer for SVG +/* +class cImageImporterSVG : public cImageImporter { +public: + ~cImageImporterSVG(); + bool LoadImage(const char *path); + bool RenderToCairo(cairo_t *cr); + void GetImageSize(int &width, int &height); +private: + RsvgHandle *handle = NULL; +}*/ + +class cImageLoader { +private: + cImageImporter *importer = NULL; public: cImageLoader(); - ~cImageLoader(); - cImage *GetImage(int width, int height); - bool LoadImage(const char *path); + virtual ~cImageLoader(); + cImage *CreateImage(int width, int height, bool preserveAspect = true); + bool LoadImage(std::string FileName, std::string Path, std::string Extension); + bool LoadImage(const char *fullpath); void DeterminateChannelLogoSize(int &width, int &height); -private: }; #endif //__NOPACITY_IMAGELOADER_H diff --git a/libcore/imagemagickwrapper.c b/libcore/imagemagickwrapper.c deleted file mode 100644 index 79e48e7..0000000 --- a/libcore/imagemagickwrapper.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include "imagemagickwrapper.h" -#include "../config.h" -#include "imagescaler.h" - -cImageMagickWrapper::cImageMagickWrapper() { - InitializeMagick(NULL); -} - -cImageMagickWrapper::~cImageMagickWrapper() { -} - -cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAspect) { - if (image == NULL) return NULL; - - int w, h; - w = cairo_image_surface_get_width(image); - h = cairo_image_surface_get_height(image); - if (width == 0) - width = w; - if (height == 0) - height = h; - - cairo_surface_t *surface; - surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - - cairo_t *cr; - cr = cairo_create(surface); - - double sx = width / (double)w; - double sy = height / (double)h; - if (preserveAspect) { - if (sx < sy) - sy = sx; - if (sy < sx) - sx = sy; - } - cairo_scale(cr, sx, sy); - - cairo_set_source_surface(cr, image, 0, 0); - cairo_paint(cr); - - cairo_status_t status = cairo_status (cr); - if (status) - dsyslog("skindesigner: Cairo CreateImage Error %s", cairo_status_to_string(status)); - - unsigned char *data = cairo_image_surface_get_data(surface); - cImage *cimage = new cImage(cSize(width, height), (tColor*)data); - - cairo_destroy(cr); - cairo_surface_destroy(image); - image = NULL; - - return cimage; -} - -bool cImageMagickWrapper::LoadImage(const char *fullpath) { - if ((fullpath == NULL) || (strlen(fullpath) < 5)) - return false; - - if (image != NULL) cairo_surface_destroy(image); - - if (config.debugImageLoading) - dsyslog("skindesigner: trying to load: %s", fullpath); - - image = cairo_image_surface_create_from_png(fullpath); - - if (cairo_surface_status(image)) { - if (config.debugImageLoading) - dsyslog("skindesigner: Cairo LoadImage Error: %s", cairo_status_to_string(cairo_surface_status(image))); - image = NULL; - return false; - } - - return true; -} - -// Just a different way to call LoadImage. Calls the above one. -bool cImageMagickWrapper::LoadImage(std::string FileName, std::string Path, std::string Extension) { - std::stringstream sstrImgFile; - sstrImgFile << Path << FileName << "." << Extension; - std::string imgFile = sstrImgFile.str(); - return LoadImage(imgFile.c_str()); -} diff --git a/libcore/imagemagickwrapper.h b/libcore/imagemagickwrapper.h deleted file mode 100644 index b0c19e8..0000000 --- a/libcore/imagemagickwrapper.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __NOPACITY_IMAGEMAGICKWRAPPER_H -#define __NOPACITY_IMAGEMAGICKWRAPPER_H - -#define X_DISPLAY_MISSING - -#include -#include - - -class cImageMagickWrapper { -private: -public: - cImageMagickWrapper(); - ~cImageMagickWrapper(); -protected: - cairo_surface_t *image = NULL; - cImage *CreateImage(int width, int height, bool preserveAspect = true); - bool LoadImage(std::string FileName, std::string Path, std::string Extension); - bool LoadImage(const char *fullpath); -}; - -#endif //__NOPACITY_IMAGEMAGICKWRAPPER_H diff --git a/views/view.c b/views/view.c index 8711136..864eb45 100644 --- a/views/view.c +++ b/views/view.c @@ -712,7 +712,7 @@ void cView::DoDrawImage(int num, cTemplateFunction *func, int x0, int y0) { case itImage: { cImageLoader imgLoader; if (imgLoader.LoadImage(path.c_str())) { - cImage *image = imgLoader.GetImage(width, height); + cImage *image = imgLoader.CreateImage(width, height); DrawImage(num, pos, *image); delete(image); } From f17af1f7136fb38cee6898dab514dd33f9363b4d Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Thu, 30 Oct 2014 21:07:26 +0100 Subject: [PATCH 05/20] Added SVG rendering support --- Makefile | 4 +-- libcore/imagecache.c | 40 +++++++++++++---------------- libcore/imageloader.c | 58 ++++++++++++++++++++++++++++++++++++++++--- libcore/imageloader.h | 12 +++++---- 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 8277229..e3c9144 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ DEFINES += $(shell xml2-config --cflags) INCLUDES += $(shell pkg-config --cflags freetype2 fontconfig) -INCLUDES += $(shell pkg-config --cflags cairo-png) -LIBS += $(shell pkg-config --libs cairo-png) +INCLUDES += $(shell pkg-config --cflags librsvg-2.0 cairo-png) +LIBS += $(shell pkg-config --libs librsvg-2.0 cairo-png) LIBS += $(shell xml2-config --libs) diff --git a/libcore/imagecache.c b/libcore/imagecache.c index 7c96438..62691e6 100644 --- a/libcore/imagecache.c +++ b/libcore/imagecache.c @@ -311,18 +311,17 @@ cImage *cImageCache::GetSkinpart(string name, int width, int height) { } bool cImageCache::LoadIcon(eImageType type, string name) { - bool success = false; cString subdir(""); if (type == itMenuIcon) subdir = "menuicons"; else if (type == itIcon) subdir = "icons"; cString subIconPath = cString::sprintf("%s%s/", iconPath.c_str(), *subdir); - success = LoadImage(name, *subIconPath, "png"); - if (success) { - return true; - } - return false; + + if (FileExists(*subIconPath, name, "svg")) + return LoadImage(*subIconPath, name, "svg"); + else + return LoadImage(*subIconPath, name, "png"); } bool cImageCache::LoadLogo(const cChannel *channel) { @@ -331,32 +330,27 @@ bool cImageCache::LoadLogo(const cChannel *channel) { string channelID = StrToLowerCase(*(channel->GetChannelID().ToString())); string logoLower = StrToLowerCase(channel->Name()); bool success = false; - success = LoadImage(channelID.c_str(), logoPath.c_str(), *config.logoExtension); - if (success) - return true; - success = LoadImage(logoLower.c_str(), logoPath.c_str(), *config.logoExtension); - if (success) - return true; + + if (FileExists(logoPath.c_str(), channelID.c_str(), *config.logoExtension)) + return LoadImage(logoPath.c_str(), channelID.c_str(), *config.logoExtension); + + if (FileExists(logoPath.c_str(), logoLower.c_str(), *config.logoExtension)) + return LoadImage(logoPath.c_str(), logoLower.c_str(), *config.logoExtension); + return false; } bool cImageCache::LoadSeparatorLogo(string name) { cString separatorPath = cString::sprintf("%sseparatorlogos/", logoPath.c_str()); string nameLower = StrToLowerCase(name.c_str()); - bool success = false; - success = LoadImage(nameLower.c_str(), *separatorPath, *config.logoExtension); - if (success) - return true; - return false; + return LoadImage(*separatorPath, nameLower.c_str(), *config.logoExtension); } bool cImageCache::LoadSkinpart(string name) { - bool success = false; - success = LoadImage(name, skinPartsPath.c_str(), "png"); - if (success) { - return true; - } - return false; + if (FileExists(skinPartsPath.c_str(), name, "svg")) + return LoadImage(skinPartsPath.c_str(), name, "svg"); + else + return LoadImage(skinPartsPath.c_str(), name, "png"); } void cImageCache::Clear(void) { diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 0c20fda..bb88b70 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -41,7 +41,6 @@ cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { cairo_scale(cr, sx, sy); importer->DrawToCairo(cr); - cairo_paint(cr); cairo_status_t status = cairo_status(cr); if (status) @@ -51,6 +50,7 @@ cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { cImage *image = new cImage(cSize(width, height), (tColor*)data); cairo_destroy(cr); + cairo_surface_destroy(surface); return image; } @@ -67,6 +67,8 @@ bool cImageLoader::LoadImage(const char *fullpath) { if (endswith(fullpath, ".png")) importer = new cImageImporterPNG; + else if (endswith(fullpath, ".svg")) + importer = new cImageImporterSVG; else return false; @@ -74,7 +76,7 @@ bool cImageLoader::LoadImage(const char *fullpath) { } // Just a different way to call LoadImage. Calls the above one. -bool cImageLoader::LoadImage(std::string FileName, std::string Path, std::string Extension) { +bool cImageLoader::LoadImage(std::string Path, std::string FileName, std::string Extension) { std::stringstream sstrImgFile; sstrImgFile << Path << FileName << "." << Extension; std::string imgFile = sstrImgFile.str(); @@ -147,8 +149,10 @@ bool cImageImporterPNG::LoadImage(const char *path) { } void cImageImporterPNG::DrawToCairo(cairo_t *cr) { - if (surface) + if (surface) { cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + } } void cImageImporterPNG::GetImageSize(int &width, int &height) { @@ -157,3 +161,51 @@ void cImageImporterPNG::GetImageSize(int &width, int &height) { height = cairo_image_surface_get_height(surface); } } + +// +// Image importer for SVG +// + +cImageImporterSVG::cImageImporterSVG() { + handle = NULL; +} + +cImageImporterSVG::~cImageImporterSVG() { + if (handle) { + rsvg_handle_close(handle, NULL); + g_object_unref(handle); + } +} + +bool cImageImporterSVG::LoadImage(const char *path) { + if (handle) { + rsvg_handle_close(handle, NULL); + g_object_unref(handle); + } + + GError *error = NULL; + handle = rsvg_handle_new_from_file(path, &error); + if (!handle) { + if (config.debugImageLoading) + dsyslog("skindesigner: RSVG Error: %s", error->message); + return false; + } + + rsvg_handle_set_dpi(handle, 90); + + return true; +} + +void cImageImporterSVG::DrawToCairo(cairo_t *cr) { + if (handle) + rsvg_handle_render_cairo(handle, cr); +} + +void cImageImporterSVG::GetImageSize(int &width, int &height) { + if (handle) { + RsvgDimensionData dim; + rsvg_handle_get_dimensions(handle, &dim); + width = dim.width; + height = dim.height; + } +} diff --git a/libcore/imageloader.h b/libcore/imageloader.h index 30e47b7..6caf19f 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -4,6 +4,7 @@ #define X_DISPLAY_MISSING #include +#include #include #include @@ -32,16 +33,17 @@ private: }; // Image importer for SVG -/* class cImageImporterSVG : public cImageImporter { public: + cImageImporterSVG(); ~cImageImporterSVG(); bool LoadImage(const char *path); - bool RenderToCairo(cairo_t *cr); + void DrawToCairo(cairo_t *cr); void GetImageSize(int &width, int &height); private: - RsvgHandle *handle = NULL; -}*/ + RsvgHandle *handle; +}; + class cImageLoader { private: @@ -50,7 +52,7 @@ public: cImageLoader(); virtual ~cImageLoader(); cImage *CreateImage(int width, int height, bool preserveAspect = true); - bool LoadImage(std::string FileName, std::string Path, std::string Extension); + bool LoadImage(std::string Path, std::string FileName, std::string Extension); bool LoadImage(const char *fullpath); void DeterminateChannelLogoSize(int &width, int &height); }; From 052bc504085a4be9fed4dfc54ab8af8a011e6984 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Thu, 30 Oct 2014 21:12:03 +0100 Subject: [PATCH 06/20] Dropped imagescaler source files --- libcore/imagescaler.c | 149 ------------------------------------------ libcore/imagescaler.h | 97 --------------------------- 2 files changed, 246 deletions(-) delete mode 100644 libcore/imagescaler.c delete mode 100644 libcore/imagescaler.h diff --git a/libcore/imagescaler.c b/libcore/imagescaler.c deleted file mode 100644 index 64fe3dc..0000000 --- a/libcore/imagescaler.c +++ /dev/null @@ -1,149 +0,0 @@ - -#include "imagescaler.h" - -#include -#include - -ImageScaler::ImageScaler() : - m_memory(NULL), - m_hor_filters(NULL), - m_ver_filters(NULL), - m_buffer(NULL), - m_dst_image(NULL), - m_dst_stride(0), - m_dst_width(0), - m_dst_height(0), - m_src_width(0), - m_src_height(0), - m_src_x(0), - m_src_y(0), - m_dst_x(0), - m_dst_y(0) { -} - -ImageScaler::~ImageScaler() { - if ( m_memory ) free( m_memory ); -} - -// sin(x)/(x) -static float sincf( float x ) { - if ( fabsf(x) < 0.05f ) return 1.0f - (1.0f/6.0f)*x*x; // taylor series approximation to avoid 0/0 - return sin(x)/x; -} - -static void CalculateFilters( ImageScaler::Filter *filters, int dst_size, int src_size ) { - const float fc = dst_size >= src_size ? 1.0f : ((float) dst_size)/((float) src_size); - - for (int i = 0; i < dst_size; i++) { - const int d = 2*dst_size; // sample position denominator - const int e = (2*i+1) * src_size - dst_size; // sample position enumerator - int offset = e / d; // truncated sample position - const float sub_offset = ((float) (e - offset*d)) / ((float) d); // exact sample position is (float) e/d = offset + sub_offset - - // calculate filter coefficients - float h[4]; - for (int j=0; j<4; j++) { - const float t = 3.14159265359f * (sub_offset+(1-j)); - h[j] = sincf( fc * t ) * cosf( 0.25f * t ); // sinc-lowpass and cos-window - } - - // ensure that filter does not reach out off image bounds: - while ( offset < 1 ) { - h[0] += h[1]; - h[1] = h[2]; - h[2] = h[3]; - h[3] = 0.0f; - offset++; - } - - while ( offset+3 > src_size ) { - h[3] += h[2]; - h[2] = h[1]; - h[1] = h[0]; - h[0] = 0.0f; - offset--; - } - - // coefficients are normalized to sum up to 2048 - const float norm = 2048.0f / ( h[0] + h[1] + h[2] + h[3] ); - - offset--; // offset of fist used pixel - - filters[i].m_offset = offset + 4; // store offset of first unused pixel - - for (int j=0; j<4; j++) { - const float t = norm * h[j]; - filters[i].m_coeff[(offset+j) & 3] = (int) ((t > 0.0f) ? (t+0.5f) : (t-0.5f)); // consider ring buffer index permutations - } - } - - // set end marker - filters[dst_size].m_offset = (unsigned) -1; - -} - -void ImageScaler::SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height ) { - m_src_x = 0; - m_src_y = 0; - m_dst_x = 0; - m_dst_y = 0; - - m_dst_image = dst_image; - m_dst_stride = dst_stride; - - // if image dimensions do not change we can keep the old filter coefficients - if ( (src_width == m_src_width) && (src_height == m_src_height) && (dst_width == m_dst_width) && (dst_height == m_dst_height) ) return; - - m_dst_width = dst_width; - m_dst_height = dst_height; - m_src_width = src_width; - m_src_height = src_height; - - if ( m_memory ) free( m_memory ); - - const unsigned hor_filters_size = (m_dst_width + 1) * sizeof(Filter); // reserve one extra position for end marker - const unsigned ver_filters_size = (m_dst_height + 1) * sizeof(Filter); - const unsigned buffer_size = 4 * m_dst_width * sizeof(TmpPixel); - - char *p = (char *) malloc( hor_filters_size + ver_filters_size + buffer_size ); - - m_memory = p; - - m_hor_filters = (Filter *) p; p += hor_filters_size; - m_ver_filters = (Filter *) p; p += ver_filters_size; - m_buffer = (TmpPixel *) p; - - CalculateFilters( m_hor_filters, m_dst_width , m_src_width ); - CalculateFilters( m_ver_filters, m_dst_height, m_src_height ); -} - -// shift range to 0..255 and clamp overflows -static unsigned shift_clamp( int x ) { - x = ( x + (1<<21) ) >> 22; - if ( x < 0 ) return 0; - if ( x > 255 ) return 255; - return x; -} - -void ImageScaler::NextSourceLine() { - m_dst_x = 0; - m_src_x = 0; - m_src_y++; - - while ( m_ver_filters[m_dst_y].m_offset == m_src_y ) { - const int h0 = m_ver_filters[m_dst_y].m_coeff[0]; - const int h1 = m_ver_filters[m_dst_y].m_coeff[1]; - const int h2 = m_ver_filters[m_dst_y].m_coeff[2]; - const int h3 = m_ver_filters[m_dst_y].m_coeff[3]; - const TmpPixel *src = m_buffer; - unsigned *dst = m_dst_image + m_dst_stride * m_dst_y; - - for (unsigned i=0; im_offset == m_src_x ) { - *bp = m_hbuf[0]*fh->m_coeff[0] + m_hbuf[1]*fh->m_coeff[1] + m_hbuf[2]*fh->m_coeff[2] + m_hbuf[3]*fh->m_coeff[3]; - m_dst_x++; - bp += 4; - } - - if ( m_src_x == m_src_width ) NextSourceLine(); - } - -private: - - //! temporary image pixel class - a 4-element integer vector - class TmpPixel { - public: - TmpPixel() { - } - - TmpPixel( int c0, int c1, int c2, int c3 ) { - Set(c0,c1,c2,c3); - } - - void Set( int c0, int c1, int c2, int c3 ) { - m_comp[0] = c0; - m_comp[1] = c1; - m_comp[2] = c2; - m_comp[3] = c3; - } - - TmpPixel operator*( int s ) const { - return TmpPixel( m_comp[0]*s, m_comp[1]*s, m_comp[2]*s, m_comp[3]*s ); - } - - TmpPixel operator+( const TmpPixel &x ) const { - return TmpPixel( m_comp[0] + x[0], m_comp[1] + x[1], m_comp[2] + x[2], m_comp[3] + x[3] ); - } - - // return component i=[0..3] - No range check! - int operator[](unsigned i) const { - return m_comp[i]; - } - - private: - int m_comp[4]; - }; - - //! this is called whenever one input line is processed completely - void NextSourceLine(); - - TmpPixel m_hbuf[4]; //! ring buffer for 4 input pixels - char *m_memory; //! buffer container - Filter *m_hor_filters; //! buffer for horizontal filters (one for each output image column) - Filter *m_ver_filters; //! buffer for vertical filters (one for each output image row) - TmpPixel *m_buffer; //! buffer contains 4 horizontally filtered input lines, multiplexed - unsigned *m_dst_image; //! pointer to destination image - unsigned m_dst_stride; //! destination image stride - unsigned m_dst_width; //! destination image width - unsigned m_dst_height; //! destination image height - unsigned m_src_width; //! source image width - unsigned m_src_height; //! source image height - unsigned m_src_x; //! x position of next source image pixel - unsigned m_src_y; //! y position of source image line currently beeing processed - unsigned m_dst_x; //! x position of next destination image pixel - unsigned m_dst_y; //! x position of next destination image line -}; - -#endif // _ImageScaler_h - From e07e56a3faced2f2ef7459cc13c2bda6f65f68ba Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 2 Nov 2014 13:41:21 +0100 Subject: [PATCH 07/20] Added JPEG support --- Makefile | 4 +- libcore/imageloader.c | 137 +++++++++++++++++++++++++++++++++++++++++- libcore/imageloader.h | 51 +++++++++++++++- 3 files changed, 187 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e3c9144..3f4b204 100644 --- a/Makefile +++ b/Makefile @@ -44,8 +44,8 @@ DEFINES += $(shell xml2-config --cflags) INCLUDES += $(shell pkg-config --cflags freetype2 fontconfig) -INCLUDES += $(shell pkg-config --cflags librsvg-2.0 cairo-png) -LIBS += $(shell pkg-config --libs librsvg-2.0 cairo-png) +INCLUDES += $(shell pkg-config --cflags librsvg-2.0 cairo-png) -ljpeg +LIBS += $(shell pkg-config --libs librsvg-2.0 cairo-png) -ljpeg LIBS += $(shell xml2-config --libs) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index bb88b70..527e5d6 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -1,7 +1,6 @@ #include "../config.h" #include "helpers.h" #include "imageloader.h" -//#include #include #include #include @@ -69,6 +68,8 @@ bool cImageLoader::LoadImage(const char *fullpath) { importer = new cImageImporterPNG; else if (endswith(fullpath, ".svg")) importer = new cImageImporterSVG; + else if (endswith(fullpath, ".jpg")) + importer = new cImageImporterJPG; else return false; @@ -209,3 +210,137 @@ void cImageImporterSVG::GetImageSize(int &width, int &height) { height = dim.height; } } + +// +// Image importer for JPG +// + +cImageImporterJPG::cImageImporterJPG() { + cinfo = NULL; +} + +cImageImporterJPG::~cImageImporterJPG() { + if (cinfo) { + jpeg_destroy_decompress(cinfo); + free(cinfo); + fclose(infile); + } +} + +bool cImageImporterJPG::LoadImage(const char *path) { + if (cinfo) { + jpeg_destroy_decompress(cinfo); + free(cinfo); + fclose(infile); + cinfo = NULL; + } + + // Open input file + if ((infile = fopen(path, "rb")) == NULL) { + if (config.debugImageLoading) + dsyslog("skindesigner: Can't open %s", path); + return false; + } + + // We set up the normal JPEG error routines, then override error_exit. + struct my_error_mgr jerr; + cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct)); + cinfo->err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + jerr.pub.output_message = my_output_message; + // Establish the setjmp return context for my_error_exit to use. + if (setjmp(jerr.setjmp_buffer)) { + // If we get here, the JPEG code has signaled an error. + jpeg_destroy_decompress(cinfo); + free(cinfo); + fclose(infile); + cinfo = NULL; + return false; + } + + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(cinfo); + + // Step 2: specify data source (eg, a file) + jpeg_stdio_src(cinfo, infile); + + // Step 3: read file parameters with jpeg_read_header() + (void) jpeg_read_header(cinfo, TRUE); + return true; +} + +void cImageImporterJPG::DrawToCairo(cairo_t *cr) { + if (!cinfo) + return; + + unsigned char *bmp_buffer = NULL; + + // Re-establish error handling + struct my_error_mgr jerr; + cinfo->err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + jerr.pub.output_message = my_output_message; + if (setjmp(jerr.setjmp_buffer)) { + jpeg_destroy_decompress(cinfo); + free(cinfo); + fclose(infile); + free(bmp_buffer); + cinfo = NULL; + return; + } + + // Step 4: set parameters for decompression + cinfo->out_color_space = JCS_EXT_ARGB; + + // Step 5: Start decompressor + (void) jpeg_start_decompress(cinfo); + + // Bytes per row in output buffer + int row_stride = cinfo->output_width * cinfo->output_components; + + // Allocate buffer + unsigned long bmp_size = row_stride * cinfo->output_height; + bmp_buffer = (unsigned char*)malloc(bmp_size); + + // Step 6: while (scan lines remain to be read) + while (cinfo->output_scanline < cinfo->output_height) { + unsigned char *buffer_array[1]; + buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * row_stride; + jpeg_read_scanlines(cinfo, buffer_array, 1); + } + + // Step 7: Finish decompression + (void)jpeg_finish_decompress(cinfo); + fclose(infile); + + // --> At this point we have raw RGB data in bmp_buffer + + // Create new Cairo surface from our raw image data + cairo_surface_t *surface; + surface = cairo_image_surface_create_for_data(bmp_buffer, + CAIRO_FORMAT_ARGB32, + cinfo->output_width, + cinfo->output_height, + row_stride); + + // Draw surface to Cairo + if (surface) { + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_surface_destroy(surface); + } + + // Cleanup. In this "ImageImporter" we clean up everything in "DrawToCairo" + // as I'm not really sure whether we are able to draw a second time. + free(bmp_buffer); + jpeg_destroy_decompress(cinfo); + free(cinfo); + cinfo = NULL; +} + +void cImageImporterJPG::GetImageSize(int &width, int &height) { + if (cinfo) { + width = cinfo->image_width; + height = cinfo->image_height; + } +} diff --git a/libcore/imageloader.h b/libcore/imageloader.h index 6caf19f..e90f459 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -1,10 +1,10 @@ #ifndef __NOPACITY_IMAGELOADER_H #define __NOPACITY_IMAGELOADER_H -#define X_DISPLAY_MISSING - #include #include +#include +#include #include #include @@ -44,6 +44,53 @@ private: RsvgHandle *handle; }; +// Image importer for JPG + +#if BITS_IN_JSAMPLE != 8 + #error libjpeg-turbo has to be compiled with 8-bit samples! +#endif +#ifndef JCS_EXTENSIONS + #error libjpeg-turbo with JCS_EXTENSIONS required! +#endif + +struct my_error_mgr { + struct jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller +}; + +METHODDEF(void) +my_error_exit(j_common_ptr cinfo) { + // cinfo->err really points to a my_error_mgr struct, so coerce pointer + my_error_mgr *myerr = (my_error_mgr*) cinfo->err; + + // Always display the message. + // We could postpone this until after returning, if we chose. + (*cinfo->err->output_message) (cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); +} + +METHODDEF(void) +my_output_message(j_common_ptr cinfo) { + char buf[JMSG_LENGTH_MAX]; + cinfo->err->format_message(cinfo, buf); + dsyslog("skindesigner: libjpeg error: %s", buf); +} + +class cImageImporterJPG : public cImageImporter { +public: + cImageImporterJPG(); + ~cImageImporterJPG(); + bool LoadImage(const char *path); + void DrawToCairo(cairo_t *cr); + void GetImageSize(int &width, int &height); +private: + j_decompress_ptr cinfo; + FILE *infile; +}; + + class cImageLoader { private: From 9511982fdd6f8a5e3e045e824b85d0e9998e6e0d Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 2 Nov 2014 18:34:40 +0100 Subject: [PATCH 08/20] Fixed JPEG importer, added support for "non-turbo" libjpeg --- libcore/imageloader.c | 52 +++++++++++++++++++++++++++---------------- libcore/imageloader.h | 3 --- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 527e5d6..0dc7ed8 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -290,38 +290,56 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { } // Step 4: set parameters for decompression - cinfo->out_color_space = JCS_EXT_ARGB; + cinfo->out_color_space = JCS_RGB; // Step 5: Start decompressor (void) jpeg_start_decompress(cinfo); - // Bytes per row in output buffer - int row_stride = cinfo->output_width * cinfo->output_components; - - // Allocate buffer - unsigned long bmp_size = row_stride * cinfo->output_height; - bmp_buffer = (unsigned char*)malloc(bmp_size); + // Allocate buffer. Directly allocate the space needed for ARGB + int width = cinfo->output_width; + int height = cinfo->output_height; + bmp_buffer = (unsigned char*)malloc(width * height * 4); // Step 6: while (scan lines remain to be read) - while (cinfo->output_scanline < cinfo->output_height) { + int jpg_stride = width * cinfo->output_components; + while (cinfo->output_scanline < height) { unsigned char *buffer_array[1]; - buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * row_stride; + buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * jpg_stride; jpeg_read_scanlines(cinfo, buffer_array, 1); } - // Step 7: Finish decompression + // Step 7: Finish decompression. Free all libjpeg stuff and close file. (void)jpeg_finish_decompress(cinfo); + + // Cleanup. In this "ImageImporter" we clean up everything in "DrawToCairo" + // as I'm not really sure whether we are able to draw a second time. fclose(infile); + jpeg_destroy_decompress(cinfo); + free(cinfo); + cinfo = NULL; // --> At this point we have raw RGB data in bmp_buffer + // Do some ugly byte shifting. + // Byte order in libjpeg: RGB + // Byte order in cairo and VDR: BGRA + for (int index = (width * height) - 1; index >= 0; index--) { + unsigned char temp[3]; + unsigned char *target = bmp_buffer + (index * 4); + unsigned char *source = bmp_buffer + (index * 3); + memcpy(&temp[0], source + 2, 1); + memcpy(&temp[1], source + 1, 1); + memcpy(&temp[2], source, 1); + memcpy(target, &temp, 3); + } + // Create new Cairo surface from our raw image data cairo_surface_t *surface; surface = cairo_image_surface_create_for_data(bmp_buffer, - CAIRO_FORMAT_ARGB32, - cinfo->output_width, - cinfo->output_height, - row_stride); + CAIRO_FORMAT_RGB24, + width, + height, + width * 4); // Draw surface to Cairo if (surface) { @@ -330,12 +348,8 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { cairo_surface_destroy(surface); } - // Cleanup. In this "ImageImporter" we clean up everything in "DrawToCairo" - // as I'm not really sure whether we are able to draw a second time. + // Free our memory free(bmp_buffer); - jpeg_destroy_decompress(cinfo); - free(cinfo); - cinfo = NULL; } void cImageImporterJPG::GetImageSize(int &width, int &height) { diff --git a/libcore/imageloader.h b/libcore/imageloader.h index e90f459..9b74f98 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -49,9 +49,6 @@ private: #if BITS_IN_JSAMPLE != 8 #error libjpeg-turbo has to be compiled with 8-bit samples! #endif -#ifndef JCS_EXTENSIONS - #error libjpeg-turbo with JCS_EXTENSIONS required! -#endif struct my_error_mgr { struct jpeg_error_mgr pub; // "public" fields From 2bd5a4057b11f7056cf346a6e4c897c03ed08f9a Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Tue, 4 Nov 2014 16:55:45 +0100 Subject: [PATCH 09/20] Some cleanup and minor bugfixes --- libcore/imageloader.c | 26 ++++++++++++++++---------- libcore/imageloader.h | 11 +++++------ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 0dc7ed8..a0247ac 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -14,7 +14,8 @@ cImageLoader::~cImageLoader() { } cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { - if (!importer) return NULL; + if (!importer) + return NULL; int w, h; importer->GetImageSize(w, h); @@ -87,18 +88,19 @@ bool cImageLoader::LoadImage(std::string Path, std::string FileName, std::string void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { cString logoPath; cString logoPathSkin = cString::sprintf("%s%s/themes/%s/logos/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme); - if (FolderExists(*logoPathSkin)) { + + if (FolderExists(*logoPathSkin)) logoPath = logoPathSkin; - } else { + else logoPath = config.logoPath; - } + cString logoExt = config.logoExtension; DIR *folder = NULL; struct dirent *file; folder = opendir(logoPath); - if (!folder) { + if (!folder) return; - } + while (file = readdir(folder)) { if (endswith(file->d_name, *logoExt)) { std::stringstream filePath; @@ -192,6 +194,7 @@ bool cImageImporterSVG::LoadImage(const char *path) { return false; } + // 90 dpi is the hardcoded default setting of the Inkscape SVG editor rsvg_handle_set_dpi(handle, 90); return true; @@ -242,9 +245,11 @@ bool cImageImporterJPG::LoadImage(const char *path) { return false; } + // Allocate space for our decompress struct + cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct)); + // We set up the normal JPEG error routines, then override error_exit. struct my_error_mgr jerr; - cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct)); cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; jerr.pub.output_message = my_output_message; @@ -275,7 +280,8 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { unsigned char *bmp_buffer = NULL; - // Re-establish error handling + // Re-establish error handling. We have to do this again as the saved + // calling environment of "LoadImage" is invalid if we reach here! struct my_error_mgr jerr; cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; @@ -308,7 +314,7 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { jpeg_read_scanlines(cinfo, buffer_array, 1); } - // Step 7: Finish decompression. Free all libjpeg stuff and close file. + // Step 7: Finish decompression. (void)jpeg_finish_decompress(cinfo); // Cleanup. In this "ImageImporter" we clean up everything in "DrawToCairo" @@ -323,8 +329,8 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { // Do some ugly byte shifting. // Byte order in libjpeg: RGB // Byte order in cairo and VDR: BGRA + unsigned char temp[3]; for (int index = (width * height) - 1; index >= 0; index--) { - unsigned char temp[3]; unsigned char *target = bmp_buffer + (index * 4); unsigned char *source = bmp_buffer + (index * 3); memcpy(&temp[0], source + 2, 1); diff --git a/libcore/imageloader.h b/libcore/imageloader.h index 9b74f98..0a19dd4 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -15,7 +15,7 @@ class cImageImporter { public: cImageImporter() {}; virtual ~cImageImporter() {}; - virtual bool LoadImage(const char *path) {}; + virtual bool LoadImage(const char *path) { return false; }; virtual void DrawToCairo(cairo_t *cr) {}; virtual void GetImageSize(int &width, int &height) {}; }; @@ -47,12 +47,12 @@ private: // Image importer for JPG #if BITS_IN_JSAMPLE != 8 - #error libjpeg-turbo has to be compiled with 8-bit samples! + #error libjpeg has to be compiled with 8-bit samples! #endif struct my_error_mgr { - struct jpeg_error_mgr pub; // "public" fields - jmp_buf setjmp_buffer; // for return to caller + struct jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller }; METHODDEF(void) @@ -61,7 +61,6 @@ my_error_exit(j_common_ptr cinfo) { my_error_mgr *myerr = (my_error_mgr*) cinfo->err; // Always display the message. - // We could postpone this until after returning, if we chose. (*cinfo->err->output_message) (cinfo); // Return control to the setjmp point @@ -91,7 +90,7 @@ private: class cImageLoader { private: - cImageImporter *importer = NULL; + cImageImporter *importer; public: cImageLoader(); virtual ~cImageLoader(); From 28413f7f2cd6299d3418a43a81b643ac04cdc67e Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Fri, 7 Nov 2014 17:31:27 +0100 Subject: [PATCH 10/20] Updated documentation - Updated information about dependencies - Fixed line lengths to look better when viewed on shell - Removed some trailing whitespaces --- HISTORY | 54 +++++++++++++++++++++++++++---------------- README | 71 ++++++++++++++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/HISTORY b/HISTORY index bcba075..ea58da5 100644 --- a/HISTORY +++ b/HISTORY @@ -7,14 +7,17 @@ VDR Plugin 'skindesigner' Revision History Version 0.0.2 -- added some more tokens with more detailed audio information in displaychannel -> statusinfo +- added some more tokens with more detailed audio information in + displaychannel -> statusinfo - added hasVPS for current scheduling in displaychannel -> epginfo - added common channel logo path for all skins - changed skin handling so that every skin is directly shown in VDR OSD Menu - added Theme support, each skin can now have various themes -- fixed a crash if no skindesigner skins are found and plugin setup menu is called from another skin +- fixed a crash if no skindesigner skins are found and plugin setup menu is + called from another skin - added {durationhours} and {durationminutes} tokens in several view elements -- added discusage icons to menu header view element so that it discusage can be displayed in every menu view +- added discusage icons to menu header view element so that it discusage can be + displayed in every menu view - added numeric day, month and year tokens in different view elements - support for global variables type "double" - added setup options to configure rerun display behaviour @@ -23,37 +26,48 @@ Version 0.0.2 - added vps token in menudetailepg - implemented cSDDisplayMenu::GetTextAreaFont() - introduced new viewelement audioinfo in displaychannel -- added setup option to choose Menu Item display method between "at one go" and "after one another" -- fixed bug that new skin was not properly loaded sometimes when skin was changed in OSD Setup menu -- fixed bug that new font was displayed first after VDR restart when font was changed in OSD Setup menu -- display always newest recording of folders in recordings list, thanks@ Lars Hanisch for providing the patch +- added setup option to choose Menu Item display method between "at one go" and + "after one another" +- fixed bug that new skin was not properly loaded sometimes when skin was + changed in OSD Setup menu +- fixed bug that new font was displayed first after VDR restart when font was + changed in OSD Setup menu +- display always newest recording of folders in recordings list, + thanks@ Lars Hanisch for providing the patch - added extented recording information -- added token {nummenuitem} as number of item for every list, value starts with 1 +- added token {nummenuitem} as number of item for every list, value starts + with 1 - fixed bug that x and y of subviews was not respected - if a subview is completely not set in a skin, the default menu is used -- fixed a bug if displaydetailedtext is called without correct menucat (mailbox plugin) +- fixed a bug if displaydetailedtext is called without correct menucat (mailbox + plugin) - implemented function drawslope, see Wiki for documentation - using default menu list in case an invalid MenuCategory is set - added device info in displaychannel, example in metrixHD - improved menu icon display, additionally using menu cat - changed devices list, device numbers start with 0 - fixed bug that hasposter is only true if poster really exists -- no absolute pathes allowed for image type "image", path has to start with {ressourcedir} +- no absolute pathes allowed for image type "image", path has to start with + {ressourcedir} Version 0.0.3 -- added tokens for current video and audio bitrate in displaychannel. Thx @rofafor for the original code +- added tokens for current video and audio bitrate in displaychannel. + Thx @rofafor for the original code in the femon plugin and _Martin_ for extracting the code in skinflatplus - changed skin metrixHD to display bitrate infos -- added "active" Token for cutting marks so that a mark can be displayed in a dedicated way if current position - in replay exactly hits the mark -- added {channelname}, {channelid}, {channellogoexists} for all schedules list and current views +- added "active" Token for cutting marks so that a mark can be displayed in a + dedicated way if current position in replay exactly hits the mark +- added {channelname}, {channelid}, {channellogoexists} for all schedules list + and current views - added printf function for , see Wiki for documentation -- removed code for displaying bitrates in displaychannel again because of incompatibility with dvbapi Plugin +- removed code for displaying bitrates in displaychannel again because of + incompatibility with dvbapi Plugin - optimized performance when creating a menu list -- fixed Bug that displaychannel was not shown after closing displaymenu with "backspace" (with active - menuorg plugin) +- fixed Bug that displaychannel was not shown after closing displaymenu with + "backspace" (with active menuorg plugin) - fixed Bug with menuselection Patch -- added tokens {month}, {monthname} and {year} in displaymenutimers listitem and currentitem -- added dedicated tokens for posters and banners in in displaychannel and displayreplay - +- added tokens {month}, {monthname} and {year} in displaymenutimers listitem + and currentitem +- added dedicated tokens for posters and banners in in + displaychannel and displayreplay diff --git a/README b/README index 14fcfac..15c1cbd 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ Written by: Louis Braun Project's homepage: http://projects.vdr-developer.org/projects/plg-skindesigner Latest version: http://projects.vdr-developer.org/projects/plg-skindesigner/files -GIT repository: git clone git://projects.vdr-developer.org/vdr-plugin-skindesigner.git +GIT repository: git clone git://projects.vdr-developer.org/vdr-plugin-skindesigner.git This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,9 +16,9 @@ See the file COPYING for more information. Description ----------- -Skindesigner is a VDR skin engine that displays XML based Skins. +Skindesigner is a VDR skin engine that displays XML based Skins. -Currently two XML Skins (MetrixHD and nOpacity freestyle) are included in +Currently two XML Skins (MetrixHD and nOpacity freestyle) are included in /skins/ Requirements @@ -26,19 +26,19 @@ Requirements - VDR version >= 2.0.0 -- Installed ImageMagick or GraphicsMagick for displaying png/jpg Icons, Channel Logos - and EPG Images (configurable during make via IMAGELIB = imagemagick|graphicsmagick - parameter) +- cairo + +- librsvg-2 - libxml2 -- for scaling the video picture to fit into the VDR menu window please use +- for scaling the video picture to fit into the VDR menu window please use softhddevice plugin revision 87c1c7be (2013-01-01) or newer. -- epgsearch Git since commit ba7c6277 (2013-01-03) to correctly replace the schedules - menu with epgsearch +- epgsearch Git since commit ba7c6277 (2013-01-03) to correctly replace the + schedules menu with epgsearch -Installation +Installation ------------ After "normal" Plugin installation you have to care about the paths for the @@ -53,10 +53,10 @@ XML skins and epg images. The following paths can be set at startup: -e path, --epgimages=path Path to the epgimages (Default: /epgimages/) -ResourceDirectory and CacheDirectory are taken from your VDR configuration (make.config -or vdr.pc). +ResourceDirectory and CacheDirectory are taken from your VDR configuration +(make.config or vdr.pc). -During a "make install" the included skins are automatically copied from +During a "make install" the included skins are automatically copied from /skins/ to the configured path. For S2-6400 Users: Disable High Level OSD, otherwise the plugin will not be @@ -67,20 +67,21 @@ an suitable true color OSD. For Xineliboutput Users: Start vdr-sxfe with the --hud option enabled -Since the default skin MetrixHD uses VDROpenSans as font which is not installed per -default, you may want to install this font (included in /fonts/) -first. Otherwise the inside VDRs OSD menu configured vdrOsd Font is used as default. +Since the default skin MetrixHD uses VDROpenSans as font which is not installed +per default, you may want to install this font (included in +/fonts/) first. Otherwise the inside VDRs OSD menu +configured vdrOsd Font is used as default. Channel Logos ------------- -Since each XML skin is responsible for it's used channel logos, skindesigner searches -for channel logos only in the skin dependend directory +Since each XML skin is responsible for it's used channel logos, skindesigner +searches for channel logos only in the skin dependend directory /plugins/skindesigner/skins//logos -Each copy your used logos directly to this directory or set a symbolic link to a common -channellogo directory. +Each copy your used logos directly to this directory or set a symbolic link to +a common channellogo directory. I recommend to use channel logos from https://github.com/3PO/Senderlogos To download them just change in the directory you want to place the logos @@ -89,17 +90,19 @@ git clone https://github.com/3PO/Senderlogos.git logos An update of the logos can then be done with a "git pull" just inside this directory. -In this logo pack all files are named only with lower case letters. -Skindesigner uses the channel name CONVERTED TO LOWER CASE LETTERS to search for an -appropriate channel logo. With this, approximately 90% of the channel logos should work -immediately after placing the channel logos in the correct place. So if you have to change -the name of a channel logo (may be by inserting a space or a hyphen) so that it fits to -the channel name, only use lower case letters, and not the name of the channel with upper -and lower letters as displayed inside VDR. -If no logo is found for the channel name, additionally a search for a logo named as the -ChannelID is performed. Analog to the channel name the ChannelID is also converted to lower -case letters. This allows channel logos for channels with changing names (for instance -Sky Feed Channels). -Additional hint: some channels have slashes in their name (in germany nick/comedy for instance). -In this example, as a dirty hack just create a folder in your channel logo directory named -"nick" and place an image named "comedy.png" inside this folder. +In this logo pack all files are named only with lower case letters. +Skindesigner uses the channel name CONVERTED TO LOWER CASE LETTERS to search +for an appropriate channel logo. With this, approximately 90% of the channel +logos should work immediately after placing the channel logos in the correct +place. So if you have to change the name of a channel logo (may be by inserting +a space or a hyphen) so that it fits to the channel name, only use lower case +letters, and not the name of the channel with upper and lower letters as +displayed inside VDR. +If no logo is found for the channel name, additionally a search for a logo +named as the ChannelID is performed. Analog to the channel name the ChannelID +is also converted to lower case letters. This allows channel logos for channels +with changing names (for instance Sky Feed Channels). +Additional hint: some channels have slashes in their name (in germany +nick/comedy for instance). +In this example, as a dirty hack just create a folder in your channel logo +directory named "nick" and place an image named "comedy.png" inside this folder. From 0d5773cb7f1748b5cf2f338ac41c1a9b8f99b664 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 9 Nov 2014 10:57:12 +0100 Subject: [PATCH 11/20] Center scaled image in the available target space when preserving aspect --- libcore/imageloader.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index a0247ac..d3f02fe 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -33,10 +33,17 @@ cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { double sx = width / (double)w; double sy = height / (double)h; if (preserveAspect) { - if (sx < sy) + double tx = 0; + double ty = 0; + if (sx < sy) { sy = sx; - if (sy < sx) + ty = (height - h * sy) / 2; + } + if (sy < sx) { sx = sy; + tx = (width - w * sx) / 2; + } + cairo_translate(cr, tx, ty); } cairo_scale(cr, sx, sy); From 66332dae29605015bf03b4c4cfabd8f8e30c6a1f Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 9 Nov 2014 11:03:33 +0100 Subject: [PATCH 12/20] Added 4 SVG alternatives for small PNG icons --- .../themes/default/icons/ico_hd_off.svg | 64 +++++++++++++++++++ .../themes/default/icons/ico_hd_on.svg | 64 +++++++++++++++++++ .../default/icons/ico_widescreen_off.svg | 58 +++++++++++++++++ .../default/icons/ico_widescreen_on.svg | 58 +++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 skins/metrixhd/themes/default/icons/ico_hd_off.svg create mode 100644 skins/metrixhd/themes/default/icons/ico_hd_on.svg create mode 100644 skins/metrixhd/themes/default/icons/ico_widescreen_off.svg create mode 100644 skins/metrixhd/themes/default/icons/ico_widescreen_on.svg diff --git a/skins/metrixhd/themes/default/icons/ico_hd_off.svg b/skins/metrixhd/themes/default/icons/ico_hd_off.svg new file mode 100644 index 0000000..897bdfc --- /dev/null +++ b/skins/metrixhd/themes/default/icons/ico_hd_off.svg @@ -0,0 +1,64 @@ + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/skins/metrixhd/themes/default/icons/ico_hd_on.svg b/skins/metrixhd/themes/default/icons/ico_hd_on.svg new file mode 100644 index 0000000..9c69401 --- /dev/null +++ b/skins/metrixhd/themes/default/icons/ico_hd_on.svg @@ -0,0 +1,64 @@ + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/skins/metrixhd/themes/default/icons/ico_widescreen_off.svg b/skins/metrixhd/themes/default/icons/ico_widescreen_off.svg new file mode 100644 index 0000000..dd91dad --- /dev/null +++ b/skins/metrixhd/themes/default/icons/ico_widescreen_off.svg @@ -0,0 +1,58 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/skins/metrixhd/themes/default/icons/ico_widescreen_on.svg b/skins/metrixhd/themes/default/icons/ico_widescreen_on.svg new file mode 100644 index 0000000..e6a91e5 --- /dev/null +++ b/skins/metrixhd/themes/default/icons/ico_widescreen_on.svg @@ -0,0 +1,58 @@ + + + + + + + + image/svg+xml + + + + + + + + + From 9a8ec36a35a7aa12b23e15efbe08b4fa846a24b6 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 9 Nov 2014 11:03:51 +0100 Subject: [PATCH 13/20] Added gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfa459b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.so +*.mo +*.pot +.dependencies From 955da7682808d806e5f967b68e02a50e61bf876c Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Sun, 9 Nov 2014 18:45:37 +0100 Subject: [PATCH 14/20] Added workaround for librsvg < 2.36.2 --- libcore/imageloader.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcore/imageloader.h b/libcore/imageloader.h index 0a19dd4..a1a11de 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -3,6 +3,9 @@ #include #include +#ifndef LIBRSVG_VERSION // Workaround for librsvg < 2.36.2 + #include +#endif #include #include #include From 529039e24cb61d038fb065f8026b2e97f79ccdf9 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Mon, 10 Nov 2014 16:32:38 +0100 Subject: [PATCH 15/20] Fixed all compiler warnings for imageloader code --- libcore/imageloader.c | 33 +++++++++++++++++++++++++++++---- libcore/imageloader.h | 30 +++--------------------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index d3f02fe..4a641c0 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -108,7 +108,7 @@ void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { if (!folder) return; - while (file = readdir(folder)) { + while ( (file = readdir(folder)) ) { if (endswith(file->d_name, *logoExt)) { std::stringstream filePath; filePath << *logoPath << file->d_name; @@ -225,6 +225,30 @@ void cImageImporterSVG::GetImageSize(int &width, int &height) { // Image importer for JPG // +struct my_error_mgr { + struct jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller +}; + +METHODDEF(void) +my_error_exit(j_common_ptr cinfo) { + // cinfo->err really points to a my_error_mgr struct, so coerce pointer + my_error_mgr *myerr = (my_error_mgr*) cinfo->err; + + // Always display the message. + (*cinfo->err->output_message) (cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); +} + +METHODDEF(void) +my_output_message(j_common_ptr cinfo) { + char buf[JMSG_LENGTH_MAX]; + cinfo->err->format_message(cinfo, buf); + dsyslog("skindesigner: libjpeg error: %s", buf); +} + cImageImporterJPG::cImageImporterJPG() { cinfo = NULL; } @@ -255,7 +279,8 @@ bool cImageImporterJPG::LoadImage(const char *path) { // Allocate space for our decompress struct cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct)); - // We set up the normal JPEG error routines, then override error_exit. + // We set up the normal JPEG error routines, then override error_exit + // and output_message. struct my_error_mgr jerr; cinfo->err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; @@ -309,8 +334,8 @@ void cImageImporterJPG::DrawToCairo(cairo_t *cr) { (void) jpeg_start_decompress(cinfo); // Allocate buffer. Directly allocate the space needed for ARGB - int width = cinfo->output_width; - int height = cinfo->output_height; + unsigned int width = cinfo->output_width; + unsigned int height = cinfo->output_height; bmp_buffer = (unsigned char*)malloc(width * height * 4); // Step 6: while (scan lines remain to be read) diff --git a/libcore/imageloader.h b/libcore/imageloader.h index a1a11de..a06f433 100644 --- a/libcore/imageloader.h +++ b/libcore/imageloader.h @@ -48,35 +48,10 @@ private: }; // Image importer for JPG - #if BITS_IN_JSAMPLE != 8 #error libjpeg has to be compiled with 8-bit samples! #endif -struct my_error_mgr { - struct jpeg_error_mgr pub; // "public" fields - jmp_buf setjmp_buffer; // for return to caller -}; - -METHODDEF(void) -my_error_exit(j_common_ptr cinfo) { - // cinfo->err really points to a my_error_mgr struct, so coerce pointer - my_error_mgr *myerr = (my_error_mgr*) cinfo->err; - - // Always display the message. - (*cinfo->err->output_message) (cinfo); - - // Return control to the setjmp point - longjmp(myerr->setjmp_buffer, 1); -} - -METHODDEF(void) -my_output_message(j_common_ptr cinfo) { - char buf[JMSG_LENGTH_MAX]; - cinfo->err->format_message(cinfo, buf); - dsyslog("skindesigner: libjpeg error: %s", buf); -} - class cImageImporterJPG : public cImageImporter { public: cImageImporterJPG(); @@ -89,8 +64,9 @@ private: FILE *infile; }; - - +// +// Image loader class +// class cImageLoader { private: cImageImporter *importer; From 60cf545efafbb1ad82056af11fc5ef3c22f90263 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Mon, 10 Nov 2014 16:38:09 +0100 Subject: [PATCH 16/20] Only log debug messages if "debugImageLoading" is set --- libcore/imageloader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libcore/imageloader.c b/libcore/imageloader.c index 4a641c0..b752a99 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -50,7 +50,7 @@ cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) { importer->DrawToCairo(cr); cairo_status_t status = cairo_status(cr); - if (status) + if (status && config.debugImageLoading) dsyslog("skindesigner: Cairo CreateImage Error %s", cairo_status_to_string(status)); unsigned char *data = cairo_image_surface_get_data(surface); @@ -246,7 +246,8 @@ METHODDEF(void) my_output_message(j_common_ptr cinfo) { char buf[JMSG_LENGTH_MAX]; cinfo->err->format_message(cinfo, buf); - dsyslog("skindesigner: libjpeg error: %s", buf); + if (config.debugImageLoading) + dsyslog("skindesigner: libjpeg error: %s", buf); } cImageImporterJPG::cImageImporterJPG() { From 7762e99dd097f40cfbeaba3ac58c3eab7b92de7d Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 11 Nov 2014 20:19:19 +0100 Subject: [PATCH 17/20] Fixed SIGABRT in case of empty text in "DrawMessage" --- views/displayreplayview.c | 1 + 1 file changed, 1 insertion(+) diff --git a/views/displayreplayview.c b/views/displayreplayview.c index 5777590..680eea6 100644 --- a/views/displayreplayview.c +++ b/views/displayreplayview.c @@ -352,6 +352,7 @@ void cDisplayReplayView::DrawJump(const char *jump) { void cDisplayReplayView::DrawMessage(eMessageType type, const char *text) { if (!text) { ClearViewElement(veMessage); + return; } map < string, string > stringTokens; From 234c8559905800413ba281c9cd8c6c1bc612f4c5 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Tue, 11 Nov 2014 20:50:00 +0100 Subject: [PATCH 18/20] Added support for SVG channellogos --- config.c | 1 - config.h | 1 - libcore/imagecache.c | 41 +++++++++++++++++------------------------ libcore/imageloader.c | 4 ++-- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/config.c b/config.c index 4ef6964..d60ccf6 100644 --- a/config.c +++ b/config.c @@ -7,7 +7,6 @@ cDesignerConfig::cDesignerConfig() { skinPathSet = false; logoPathSet = false; //Common - logoExtension = "png"; numLogosPerSizeInitial = 30; limitLogoCache = 1; numLogosMax = 200; diff --git a/config.h b/config.h index 7773208..85fba3b 100644 --- a/config.h +++ b/config.h @@ -43,7 +43,6 @@ public: void SetOSDFonts(void); bool OsdFontsChanged(void); cString GetSkinRessourcePath(void); - cString logoExtension; cString skinPath; cString logoPath; cString epgImagePath; diff --git a/libcore/imagecache.c b/libcore/imagecache.c index 62691e6..7eabbca 100644 --- a/libcore/imagecache.c +++ b/libcore/imagecache.c @@ -140,27 +140,18 @@ bool cImageCache::LogoExists(string channelID) { if (!channel) return false; string logoLower = StrToLowerCase(channel->Name()); - string logoExt = *config.logoExtension; - bool logoExists = FileExists(logoPath.c_str(), logoLower, logoExt); - if (logoExists) { - return true; - } - logoExists = FileExists(logoPath.c_str(), channelID, logoExt); - if (logoExists) { - return true; - } - return false; + + return (FileExists(logoPath.c_str(), logoLower, "svg") || + FileExists(logoPath.c_str(), logoLower, "png") || + FileExists(logoPath.c_str(), channelID, "svg") || + FileExists(logoPath.c_str(), channelID, "png")); } bool cImageCache::SeparatorLogoExists(string name) { string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str()); string nameLower = StrToLowerCase(name.c_str()); - string logoExt = *config.logoExtension; - bool logoExists = FileExists(separatorPath, nameLower, logoExt); - if (logoExists) { - return true; - } - return false; + + return FileExists(separatorPath, nameLower, "png"); } void cImageCache::CacheIcon(eImageType type, string name, int width, int height) { @@ -329,21 +320,23 @@ bool cImageCache::LoadLogo(const cChannel *channel) { return false; string channelID = StrToLowerCase(*(channel->GetChannelID().ToString())); string logoLower = StrToLowerCase(channel->Name()); - bool success = false; - if (FileExists(logoPath.c_str(), channelID.c_str(), *config.logoExtension)) - return LoadImage(logoPath.c_str(), channelID.c_str(), *config.logoExtension); - - if (FileExists(logoPath.c_str(), logoLower.c_str(), *config.logoExtension)) - return LoadImage(logoPath.c_str(), logoLower.c_str(), *config.logoExtension); + if (FileExists(logoPath.c_str(), channelID.c_str(), "svg")) + return LoadImage(logoPath.c_str(), channelID.c_str(), "svg"); + if (FileExists(logoPath.c_str(), channelID.c_str(), "png")) + return LoadImage(logoPath.c_str(), channelID.c_str(), "png"); + if (FileExists(logoPath.c_str(), logoLower.c_str(), "svg")) + return LoadImage(logoPath.c_str(), logoLower.c_str(), "svg"); + if (FileExists(logoPath.c_str(), logoLower.c_str(), "png")) + return LoadImage(logoPath.c_str(), logoLower.c_str(), "png"); return false; } bool cImageCache::LoadSeparatorLogo(string name) { - cString separatorPath = cString::sprintf("%sseparatorlogos/", logoPath.c_str()); + string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str()); string nameLower = StrToLowerCase(name.c_str()); - return LoadImage(*separatorPath, nameLower.c_str(), *config.logoExtension); + return LoadImage(separatorPath, nameLower.c_str(), "png"); } bool cImageCache::LoadSkinpart(string name) { diff --git a/libcore/imageloader.c b/libcore/imageloader.c index b752a99..1f4a31f 100644 --- a/libcore/imageloader.c +++ b/libcore/imageloader.c @@ -101,7 +101,6 @@ void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { else logoPath = config.logoPath; - cString logoExt = config.logoExtension; DIR *folder = NULL; struct dirent *file; folder = opendir(logoPath); @@ -109,7 +108,8 @@ void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) { return; while ( (file = readdir(folder)) ) { - if (endswith(file->d_name, *logoExt)) { + if (endswith(file->d_name, ".png") || + endswith(file->d_name, ".svg")) { std::stringstream filePath; filePath << *logoPath << file->d_name; if (LoadImage(filePath.str().c_str())) { From d7aa09fec3c9560edf218b5d0c3badd9d2db5972 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Wed, 12 Nov 2014 16:58:01 +0100 Subject: [PATCH 19/20] Init/Cleanup libxml only once at VDR start/end. Copied from xmltv2vdr plugin. --- libtemplate/xmlparser.c | 10 ++++++++-- libtemplate/xmlparser.h | 4 +++- skindesigner.c | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libtemplate/xmlparser.c b/libtemplate/xmlparser.c index 0e2c132..23912b7 100644 --- a/libtemplate/xmlparser.c +++ b/libtemplate/xmlparser.c @@ -12,7 +12,6 @@ cXmlParser::cXmlParser(void) { root = NULL; ctxt = NULL; - xmlInitParser(); initGenericErrorDefaultFunc(NULL); xmlSetStructuredErrorFunc(NULL, SkinDesignerXMLErrorHandler); ctxt = xmlNewParserCtxt(); @@ -21,7 +20,6 @@ cXmlParser::cXmlParser(void) { cXmlParser::~cXmlParser() { DeleteDocument(); xmlFreeParserCtxt(ctxt); - xmlCleanupParser(); } /********************************************************************* @@ -737,3 +735,11 @@ bool cXmlParser::DebugViewElement(xmlNodePtr node) { } return false; } + +void cXmlParser::InitLibXML() { + xmlInitParser(); +} + +void cXmlParser::CleanupLibXML() { + xmlCleanupParser(); +} diff --git a/libtemplate/xmlparser.h b/libtemplate/xmlparser.h index ecdcca3..cba1eb4 100644 --- a/libtemplate/xmlparser.h +++ b/libtemplate/xmlparser.h @@ -51,6 +51,8 @@ public: bool ParseView(void); bool ParseGlobals(void); void DeleteDocument(void); + static void InitLibXML(); + static void CleanupLibXML(); }; -#endif //__XMLPARSER_H \ No newline at end of file +#endif //__XMLPARSER_H diff --git a/skindesigner.c b/skindesigner.c index c2f499a..e6955f6 100644 --- a/skindesigner.c +++ b/skindesigner.c @@ -95,6 +95,7 @@ bool cPluginSkinDesigner::Initialize(void) { } bool cPluginSkinDesigner::Start(void) { + cXmlParser::InitLibXML(); bool trueColorAvailable = true; if (!cOsdProvider::SupportsTrueColor()) { esyslog("skindesigner: No TrueColor OSD found! Using default Skin LCARS!"); @@ -121,6 +122,7 @@ bool cPluginSkinDesigner::Start(void) { void cPluginSkinDesigner::Stop(void) { delete imgCache; delete fontManager; + cXmlParser::CleanupLibXML(); } void cPluginSkinDesigner::Housekeeping(void) { From 7a69d868c422218817f1c92a6666ee6c83825d23 Mon Sep 17 00:00:00 2001 From: Manuel Reimer Date: Wed, 12 Nov 2014 17:05:36 +0100 Subject: [PATCH 20/20] Added support for SVG separatorlogos --- libcore/imagecache.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libcore/imagecache.c b/libcore/imagecache.c index 7eabbca..369322e 100644 --- a/libcore/imagecache.c +++ b/libcore/imagecache.c @@ -151,7 +151,8 @@ bool cImageCache::SeparatorLogoExists(string name) { string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str()); string nameLower = StrToLowerCase(name.c_str()); - return FileExists(separatorPath, nameLower, "png"); + return (FileExists(separatorPath, nameLower, "svg") || + FileExists(separatorPath, nameLower, "png")); } void cImageCache::CacheIcon(eImageType type, string name, int width, int height) { @@ -336,7 +337,10 @@ bool cImageCache::LoadLogo(const cChannel *channel) { bool cImageCache::LoadSeparatorLogo(string name) { string separatorPath = *cString::sprintf("%sseparatorlogos/", logoPath.c_str()); string nameLower = StrToLowerCase(name.c_str()); - return LoadImage(separatorPath, nameLower.c_str(), "png"); + if (FileExists(separatorPath, nameLower.c_str(), "svg")) + return LoadImage(separatorPath, nameLower.c_str(), "svg"); + else + return LoadImage(separatorPath, nameLower.c_str(), "png"); } bool cImageCache::LoadSkinpart(string name) {