Version 0.0.5 - added SVG Support

This commit is contained in:
louis
2014-11-15 11:15:48 +01:00
21 changed files with 804 additions and 593 deletions

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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++;
}
}

View File

@@ -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