summaryrefslogtreecommitdiff
path: root/libcore/imageloader.c
diff options
context:
space:
mode:
authorlouis <louis.braun@gmx.de>2014-11-15 11:15:48 +0100
committerlouis <louis.braun@gmx.de>2014-11-15 11:15:48 +0100
commit0ed710a86884136a7bd772322f13f0b176a5dc22 (patch)
treecd4ae6f14b29988d35ce67acb9012c610348bc95 /libcore/imageloader.c
parentf225c38103013faf38512cc61362cf9bb9088ba6 (diff)
parent7a69d868c422218817f1c92a6666ee6c83825d23 (diff)
downloadvdr-plugin-skindesigner-0.0.5.tar.gz
vdr-plugin-skindesigner-0.0.5.tar.bz2
Version 0.0.5 - added SVG Support0.0.5
Diffstat (limited to 'libcore/imageloader.c')
-rw-r--r--libcore/imageloader.c392
1 files changed, 365 insertions, 27 deletions
diff --git a/libcore/imageloader.c b/libcore/imageloader.c
index 61e8076..139e2d0 100644
--- a/libcore/imageloader.c
+++ b/libcore/imageloader.c
@@ -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;
+
+ 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);
-bool cImageLoader::LoadImage(const char *path, int width, int height) {
- if (cImageMagickWrapper::LoadImage(path)) {
- buffer.sample(Geometry(width, height));
- return true;
+ 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;
}
}