diff --git a/README.md b/README.md index 9b086e7..4724a38 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@ support](http://www.cairographics.org/manual/cairo-PNG-Support.html). The implementation is done on top of the Cairo API. It does not access Cairo-internal functions. -For compression and decompression [libjpeg](http://libjpeg.sourceforge.net/) is -used. +For compression and decompression a JPEG library is used. It compiles against +[libjpeg-turbo](https://libjpeg-turbo.org/) or the original +[libjpeg](http://www.ijg.org/). -The following prototypes are implemented. Their functionallity is equal to the +The following prototypes are implemented. Their functionality is equal to the PNG functions of Cairo with the advance that there are memory-buffer-based functions as well. @@ -36,3 +37,12 @@ gcc -Wall -c `pkg-config cairo libjpeg --cflags --libs` cairo_jpg.c Please have a look at the comments within the source files for further details. Don't hesitate to contact me at [bf@abenteuerland.at](mailto:bf@abenteuerland.at). +## Testing + +There is a ```main()``` function implemented which serves as demonstrational +purpose and for testing. To compile with the ```main()``` function run the +following statement: +```Shell +gcc -Wall -o cairo_jpg -DCAIRO_JPEG_MAIN `pkg-config cairo libjpeg --cflags --libs` cairo_jpg.c +``` + diff --git a/src/cairo_jpg.c b/src/cairo_jpg.c index 1ac349f..19bf790 100644 --- a/src/cairo_jpg.c +++ b/src/cairo_jpg.c @@ -15,8 +15,8 @@ * executable for testing of this code: * gcc -Wall -o cairo_jpg -DCAIRO_JPEG_MAIN `pkg-config cairo libjpeg --cflags --libs` cairo_jpg.c * - * @author Bernhard R. Fischer, 2048R/5C5FFD47 bf@abenteuerland.at - * @version 2016/01/01 r1922 + * @author Bernhard R. Fischer, 4096R/8E24F29D bf@abenteuerland.at + * @version 2018/02/15 * @license This code is free software. Do whatever you like to do with it. */ @@ -70,6 +70,45 @@ #endif +#ifndef LIBJPEG_TURBO_VERSION +/*! This function makes a covnersion for "odd" pixel sizes which typically is a + * conversion from a 3-byte to a 4-byte (or more) pixel size or vice versa. + * The conversion is done from the source buffer src to the destination buffer + * dst. The caller MUST ensure that src and dst have the correct memory size. + * This is dw * num for dst and sw * num for src. src and dst may point to the + * same memory address. + * @param dst Pointer to destination buffer. + * @param dw Pixel width (in bytes) of pixels in destination buffer, dw >= 3. + * @param src Pointer to source buffer. + * @param sw Pixel width (in bytes) of pixels in source buffer, sw >= 3. + * @param num Number of pixels to convert, num >= 1; + */ +static void pix_conv(unsigned char *dst, int dw, const unsigned char *src, int sw, int num) +{ + int si, di; + + // safety check + if (dw < 3 || sw < 3 || dst == NULL || src == NULL) + return; + + num--; + for (si = num * sw, di = num * dw; si >= 0; si -= sw, di -= dw) + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + dst[di + 2] = src[si ]; + dst[di + 1] = src[si + 1]; + dst[di + 0] = src[si + 2]; +#else + // FIXME: This is untested, it may be wrong. + dst[di - 3] = src[si - 3]; + dst[di - 2] = src[si - 2]; + dst[di - 1] = src[si - 1]; +#endif + } +} +#endif + + /*! This function creates a JPEG file in memory from a Cairo image surface. * @param sfc Pointer to a Cairo surface. It should be an image surface of * either CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_RGB24. Other formats are @@ -135,6 +174,7 @@ cairo_status_t cairo_image_surface_write_to_jpeg_mem(cairo_surface_t *sfc, unsig jpeg_mem_dest(&cinfo, data, len); cinfo.image_width = cairo_image_surface_get_width(sfc); cinfo.image_height = cairo_image_surface_get_height(sfc); +#ifdef LIBJPEG_TURBO_VERSION #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ //cinfo.in_color_space = JCS_EXT_BGRX; cinfo.in_color_space = cairo_image_surface_get_format(sfc) == CAIRO_FORMAT_ARGB32 ? JCS_EXT_BGRA : JCS_EXT_BGRX; @@ -143,6 +183,10 @@ cairo_status_t cairo_image_surface_write_to_jpeg_mem(cairo_surface_t *sfc, unsig cinfo.in_color_space = cairo_image_surface_get_format(sfc) == CAIRO_FORMAT_ARGB32 ? JCS_EXT_ARGB : JCS_EXT_XRGB; #endif cinfo.input_components = 4; +#else + cinfo.in_color_space = JCS_RGB; + cinfo.input_components = 3; +#endif jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); @@ -152,8 +196,15 @@ cairo_status_t cairo_image_surface_write_to_jpeg_mem(cairo_surface_t *sfc, unsig // loop over all lines and compress while (cinfo.next_scanline < cinfo.image_height) { +#ifdef LIBJPEG_TURBO_VERSION row_pointer[0] = cairo_image_surface_get_data(sfc) + (cinfo.next_scanline * cairo_image_surface_get_stride(sfc)); +#else + unsigned char row_buf[3 * cinfo.image_width]; + pix_conv(row_buf, 3, cairo_image_surface_get_data(sfc) + + (cinfo.next_scanline * cairo_image_surface_get_stride(sfc)), 4, cinfo.image_width); + row_pointer[0] = row_buf; +#endif (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } @@ -243,6 +294,7 @@ cairo_status_t cairo_image_surface_write_to_jpeg(cairo_surface_t *sfc, const cha } + /*! This function decompresses a JPEG image from a memory buffer and creates a * Cairo image surface. * @param data Pointer to JPEG data (i.e. the full contents of a JPEG file read @@ -264,11 +316,15 @@ cairo_surface_t *cairo_image_surface_create_from_jpeg_mem(void *data, size_t len jpeg_mem_src(&cinfo, data, len); (void) jpeg_read_header(&cinfo, TRUE); +#ifdef LIBJPEG_TURBO_VERSION #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ cinfo.out_color_space = JCS_EXT_BGRA; #else cinfo.out_color_space = JCS_EXT_ARGB; #endif +#else + cinfo.out_color_space = JCS_RGB; +#endif // start decompressor (void) jpeg_start_decompress(&cinfo); @@ -284,9 +340,13 @@ cairo_surface_t *cairo_image_surface_create_from_jpeg_mem(void *data, size_t len // loop over all scanlines and fill Cairo image surface while (cinfo.output_scanline < cinfo.output_height) { - row_pointer[0] = cairo_image_surface_get_data(sfc) + + unsigned char *row_address = cairo_image_surface_get_data(sfc) + (cinfo.output_scanline * cairo_image_surface_get_stride(sfc)); + row_pointer[0] = row_address; (void) jpeg_read_scanlines(&cinfo, row_pointer, 1); +#ifndef LIBJPEG_TURBO_VERSION + pix_conv(row_address, 4, row_address, 3, cinfo.output_width); +#endif } // finish and close everything