mirror of
https://projects.vdr-developer.org/git/vdr-plugin-skindesigner.git
synced 2023-10-19 15:58:31 +00:00
Version 0.0.5 - added SVG Support
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -141,27 +140,19 @@ 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, "svg") ||
|
||||
FileExists(separatorPath, nameLower, "png"));
|
||||
}
|
||||
|
||||
void cImageCache::CacheIcon(eImageType type, string name, int width, int height) {
|
||||
@@ -312,18 +303,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,33 +321,33 @@ bool cImageCache::LoadLogo(const cChannel *channel) {
|
||||
return false;
|
||||
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(), "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());
|
||||
bool success = false;
|
||||
success = LoadImage(nameLower.c_str(), *separatorPath, *config.logoExtension);
|
||||
if (success)
|
||||
return true;
|
||||
return false;
|
||||
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) {
|
||||
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) {
|
||||
|
||||
@@ -5,14 +5,11 @@
|
||||
|
||||
#include <vdr/osd.h>
|
||||
#include <vdr/skins.h>
|
||||
#include <Magick++.h>
|
||||
#include <vector>
|
||||
#include "imagemagickwrapper.h"
|
||||
#include "imageloader.h"
|
||||
#include "../libtemplate/templatefunction.h"
|
||||
|
||||
using namespace Magick;
|
||||
|
||||
class cImageCache : public cImageMagickWrapper {
|
||||
class cImageCache : public cImageLoader {
|
||||
public:
|
||||
cImageCache();
|
||||
~cImageCache();
|
||||
|
||||
@@ -1,62 +1,400 @@
|
||||
#include "../config.h"
|
||||
#include "helpers.h"
|
||||
#include "imageloader.h"
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <dirent.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Magick;
|
||||
|
||||
cImageLoader::cImageLoader() : cImageMagickWrapper() {
|
||||
cImageLoader::cImageLoader() {
|
||||
importer = NULL;
|
||||
}
|
||||
|
||||
cImageLoader::~cImageLoader() {
|
||||
delete(importer);
|
||||
}
|
||||
|
||||
cImage cImageLoader::GetImage() {
|
||||
return CreateImageCopy();
|
||||
}
|
||||
cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) {
|
||||
if (!importer)
|
||||
return NULL;
|
||||
|
||||
bool cImageLoader::LoadImage(const char *path, int width, int height) {
|
||||
if (cImageMagickWrapper::LoadImage(path)) {
|
||||
buffer.sample(Geometry(width, height));
|
||||
return true;
|
||||
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) {
|
||||
double tx = 0;
|
||||
double ty = 0;
|
||||
if (sx < sy) {
|
||||
sy = sx;
|
||||
ty = (height - h * sy) / 2;
|
||||
}
|
||||
if (sy < sx) {
|
||||
sx = sy;
|
||||
tx = (width - w * sx) / 2;
|
||||
}
|
||||
cairo_translate(cr, tx, ty);
|
||||
}
|
||||
return false;
|
||||
cairo_scale(cr, sx, sy);
|
||||
|
||||
importer->DrawToCairo(cr);
|
||||
|
||||
cairo_status_t status = cairo_status(cr);
|
||||
if (status && config.debugImageLoading)
|
||||
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);
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
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 if (endswith(fullpath, ".svg"))
|
||||
importer = new cImageImporterSVG;
|
||||
else if (endswith(fullpath, ".jpg"))
|
||||
importer = new cImageImporterJPG;
|
||||
else
|
||||
return false;
|
||||
|
||||
return importer->LoadImage(fullpath);
|
||||
}
|
||||
|
||||
// Just a different way to call LoadImage. Calls the above one.
|
||||
bool cImageLoader::LoadImage(std::string Path, std::string FileName, 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) {
|
||||
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)) {
|
||||
|
||||
while ( (file = readdir(folder)) ) {
|
||||
if (endswith(file->d_name, ".png") ||
|
||||
endswith(file->d_name, ".svg")) {
|
||||
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);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
}
|
||||
|
||||
void cImageImporterPNG::GetImageSize(int &width, int &height) {
|
||||
if (surface) {
|
||||
width = cairo_image_surface_get_width(surface);
|
||||
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 && error) {
|
||||
dsyslog("skindesigner: RSVG Error: %s", error->message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 90 dpi is the hardcoded default setting of the Inkscape SVG editor
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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);
|
||||
if (config.debugImageLoading)
|
||||
dsyslog("skindesigner: libjpeg error: %s", buf);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
// and output_message.
|
||||
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;
|
||||
// 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. 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;
|
||||
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_RGB;
|
||||
|
||||
// Step 5: Start decompressor
|
||||
(void) jpeg_start_decompress(cinfo);
|
||||
|
||||
// Allocate buffer. Directly allocate the space needed for ARGB
|
||||
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)
|
||||
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) * jpg_stride;
|
||||
jpeg_read_scanlines(cinfo, buffer_array, 1);
|
||||
}
|
||||
|
||||
// Step 7: Finish decompression.
|
||||
(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
|
||||
unsigned char temp[3];
|
||||
for (int index = (width * height) - 1; index >= 0; index--) {
|
||||
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_RGB24,
|
||||
width,
|
||||
height,
|
||||
width * 4);
|
||||
|
||||
// Draw surface to Cairo
|
||||
if (surface) {
|
||||
cairo_set_source_surface(cr, surface, 0, 0);
|
||||
cairo_paint(cr);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
// Free our memory
|
||||
free(bmp_buffer);
|
||||
}
|
||||
|
||||
void cImageImporterJPG::GetImageSize(int &width, int &height) {
|
||||
if (cinfo) {
|
||||
width = cinfo->image_width;
|
||||
height = cinfo->image_height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,82 @@
|
||||
#ifndef __NOPACITY_IMAGELOADER_H
|
||||
#define __NOPACITY_IMAGELOADER_H
|
||||
|
||||
#define X_DISPLAY_MISSING
|
||||
|
||||
#include <cairo.h>
|
||||
#include <librsvg/rsvg.h>
|
||||
#ifndef LIBRSVG_VERSION // Workaround for librsvg < 2.36.2
|
||||
#include <librsvg/rsvg-cairo.h>
|
||||
#endif
|
||||
#include <jpeglib.h>
|
||||
#include <setjmp.h>
|
||||
#include <vdr/osd.h>
|
||||
#include <vdr/skins.h>
|
||||
#include <Magick++.h>
|
||||
#include "imagemagickwrapper.h"
|
||||
#include <vdr/tools.h>
|
||||
|
||||
using namespace Magick;
|
||||
//
|
||||
// Image importers
|
||||
//
|
||||
class cImageImporter {
|
||||
public:
|
||||
cImageImporter() {};
|
||||
virtual ~cImageImporter() {};
|
||||
virtual bool LoadImage(const char *path) { return false; };
|
||||
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();
|
||||
~cImageImporterSVG();
|
||||
bool LoadImage(const char *path);
|
||||
void DrawToCairo(cairo_t *cr);
|
||||
void GetImageSize(int &width, int &height);
|
||||
private:
|
||||
RsvgHandle *handle;
|
||||
};
|
||||
|
||||
// Image importer for JPG
|
||||
#if BITS_IN_JSAMPLE != 8
|
||||
#error libjpeg has to be compiled with 8-bit samples!
|
||||
#endif
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
//
|
||||
// Image loader class
|
||||
//
|
||||
class cImageLoader {
|
||||
private:
|
||||
cImageImporter *importer;
|
||||
public:
|
||||
cImageLoader();
|
||||
~cImageLoader();
|
||||
cImage GetImage();
|
||||
bool LoadImage(const char *path, int width, int height);
|
||||
virtual ~cImageLoader();
|
||||
cImage *CreateImage(int width, int height, bool preserveAspect = true);
|
||||
bool LoadImage(std::string Path, std::string FileName, std::string Extension);
|
||||
bool LoadImage(const char *fullpath);
|
||||
void DeterminateChannelLogoSize(int &width, int &height);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif //__NOPACITY_IMAGELOADER_H
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include "imagemagickwrapper.h"
|
||||
#include "../config.h"
|
||||
#include "imagescaler.h"
|
||||
|
||||
cImageMagickWrapper::cImageMagickWrapper() {
|
||||
InitializeMagick(NULL);
|
||||
}
|
||||
|
||||
cImageMagickWrapper::~cImageMagickWrapper() {
|
||||
}
|
||||
|
||||
cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAspect) {
|
||||
int w, h;
|
||||
w = buffer.columns();
|
||||
h = buffer.rows();
|
||||
if (width == 0)
|
||||
width = w;
|
||||
if (height == 0)
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
try {
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef __NOPACITY_IMAGEMAGICKWRAPPER_H
|
||||
#define __NOPACITY_IMAGEMAGICKWRAPPER_H
|
||||
|
||||
#define X_DISPLAY_MISSING
|
||||
|
||||
#include <Magick++.h>
|
||||
#include <vdr/osd.h>
|
||||
|
||||
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
|
||||
@@ -1,149 +0,0 @@
|
||||
|
||||
#include "imagescaler.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
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; i<m_dst_width; i++) {
|
||||
const ImageScaler::TmpPixel t( src[0]*h0 + src[1]*h1 + src[2]*h2 + src[3]*h3 );
|
||||
src += 4;
|
||||
dst[i] = shift_clamp(t[0]) | (shift_clamp(t[1])<<8) | (shift_clamp(t[2])<<16) | (shift_clamp(t[3])<<24);
|
||||
}
|
||||
|
||||
m_dst_y++;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
#ifndef _ImageScaler_h
|
||||
#define _ImageScaler_h
|
||||
|
||||
/*!
|
||||
* this class scales images consisting of 4 components (RGBA)
|
||||
* to an arbitrary size using a 4-tap filter
|
||||
*/
|
||||
class ImageScaler {
|
||||
public:
|
||||
|
||||
struct Filter {
|
||||
unsigned m_offset;
|
||||
short m_coeff[4];
|
||||
};
|
||||
|
||||
ImageScaler();
|
||||
~ImageScaler();
|
||||
|
||||
//! set destination image and source image size
|
||||
void SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height );
|
||||
|
||||
/*! process one pixel of source image; destination image is written while input is processed
|
||||
* SetImageParameters() must be called first
|
||||
*/
|
||||
void PutSourcePixel( unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3 ) {
|
||||
m_hbuf[ (m_src_x++) & 3 ].Set( c0, c1, c2, c3 );
|
||||
|
||||
TmpPixel *bp = m_buffer + 4 * m_dst_x + (m_src_y & 3);
|
||||
const Filter *fh;
|
||||
|
||||
while ( (fh=m_hor_filters+m_dst_x)->m_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
|
||||
|
||||
Reference in New Issue
Block a user