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: