vdr-plugin-skindesigner/libcore/imageloader.c

367 lines
9.8 KiB
C
Raw Normal View History

2014-09-27 09:25:14 +02:00
#include "../config.h"
#include "helpers.h"
#include "imageloader.h"
#include <string>
#include <dirent.h>
#include <iostream>
cImageLoader::cImageLoader() {
importer = NULL;
2014-09-27 09:25:14 +02:00
}
cImageLoader::~cImageLoader() {
delete(importer);
2014-09-27 09:25:14 +02:00
}
cImage *cImageLoader::CreateImage(int width, int height, bool preserveAspect) {
2014-11-04 16:55:45 +01:00
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_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);
2014-10-30 21:07:26 +01:00
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;
2014-10-30 21:07:26 +01:00
else if (endswith(fullpath, ".svg"))
importer = new cImageImporterSVG;
2014-11-02 13:41:21 +01:00
else if (endswith(fullpath, ".jpg"))
importer = new cImageImporterJPG;
else
return false;
return importer->LoadImage(fullpath);
2014-09-27 09:25:14 +02:00
}
// Just a different way to call LoadImage. Calls the above one.
2014-10-30 21:07:26 +01:00
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());
2014-09-27 09:25:14 +02:00
}
void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) {
cString logoPath;
cString logoPathSkin = cString::sprintf("%s%s/themes/%s/logos/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme);
2014-11-04 16:55:45 +01:00
if (FolderExists(*logoPathSkin))
logoPath = logoPathSkin;
2014-11-04 16:55:45 +01:00
else
logoPath = config.logoPath;
2014-11-04 16:55:45 +01:00
2014-09-27 09:25:14 +02:00
cString logoExt = config.logoExtension;
DIR *folder = NULL;
struct dirent *file;
folder = opendir(logoPath);
2014-11-04 16:55:45 +01:00
if (!folder)
2014-09-27 09:25:14 +02:00
return;
2014-11-04 16:55:45 +01:00
2014-09-27 09:25:14 +02:00
while (file = readdir(folder)) {
if (endswith(file->d_name, *logoExt)) {
std::stringstream filePath;
filePath << *logoPath << file->d_name;
if (LoadImage(filePath.str().c_str())) {
int logoWidth = 0;
int logoHeight = 0;
importer->GetImageSize(logoWidth, logoHeight);
2014-09-27 09:25:14 +02:00
if (logoWidth > 0 && logoHeight > 0) {
width = logoWidth;
height = logoHeight;
delete(importer);
importer = NULL;
2014-09-27 09:25:14 +02:00
return;
}
}
2014-09-27 09:25:14 +02:00
}
}
}
//
// 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) {
2014-10-30 21:07:26 +01:00
if (surface) {
cairo_set_source_surface(cr, surface, 0, 0);
2014-10-30 21:07:26 +01:00
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);
}
}
2014-10-30 21:07:26 +01:00
//
// 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;
}
2014-11-04 16:55:45 +01:00
// 90 dpi is the hardcoded default setting of the Inkscape SVG editor
2014-10-30 21:07:26 +01:00
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;
}
}
2014-11-02 13:41:21 +01:00
//
// 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;
}
2014-11-04 16:55:45 +01:00
// Allocate space for our decompress struct
cinfo = (j_decompress_ptr)malloc(sizeof(struct jpeg_decompress_struct));
2014-11-02 13:41:21 +01:00
// We set up the normal JPEG error routines, then override error_exit.
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;
2014-11-04 16:55:45 +01:00
// Re-establish error handling. We have to do this again as the saved
// calling environment of "LoadImage" is invalid if we reach here!
2014-11-02 13:41:21 +01:00
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;
2014-11-02 13:41:21 +01:00
// Step 5: Start decompressor
(void) jpeg_start_decompress(cinfo);
// 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);
2014-11-02 13:41:21 +01:00
// Step 6: while (scan lines remain to be read)
int jpg_stride = width * cinfo->output_components;
while (cinfo->output_scanline < height) {
2014-11-02 13:41:21 +01:00
unsigned char *buffer_array[1];
buffer_array[0] = bmp_buffer + (cinfo->output_scanline) * jpg_stride;
2014-11-02 13:41:21 +01:00
jpeg_read_scanlines(cinfo, buffer_array, 1);
}
2014-11-04 16:55:45 +01:00
// Step 7: Finish decompression.
2014-11-02 13:41:21 +01:00
(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.
2014-11-02 13:41:21 +01:00
fclose(infile);
jpeg_destroy_decompress(cinfo);
free(cinfo);
cinfo = NULL;
2014-11-02 13:41:21 +01:00
// --> 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
2014-11-04 16:55:45 +01:00
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);
}
2014-11-02 13:41:21 +01:00
// 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);
2014-11-02 13:41:21 +01:00
// Draw surface to Cairo
if (surface) {
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
cairo_surface_destroy(surface);
}
// Free our memory
2014-11-02 13:41:21 +01:00
free(bmp_buffer);
}
void cImageImporterJPG::GetImageSize(int &width, int &height) {
if (cinfo) {
width = cinfo->image_width;
height = cinfo->image_height;
}
}