From fd430e8b696a432a93c578930a0a7ab6f9127d5d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 16 Mar 2016 11:56:04 -0700 Subject: [PATCH] GUAC-236: Do not use features specific to libjpeg-turbo. Ensure compatibility with libjpeg. --- src/guacenc/jpeg.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/guacenc/jpeg.c b/src/guacenc/jpeg.c index dfed2d13..c1b9062b 100644 --- a/src/guacenc/jpeg.c +++ b/src/guacenc/jpeg.c @@ -32,6 +32,58 @@ #include +/** + * Translates libjpeg's 24-bit RGB format into Cairo's 32-bit ARGB32 / RGB24 + * format. The red, green, and blue components from the libjpeg pixel are + * copied verbatim, while the extra high byte used within Cairo is set to 0xFF. + * + * @param src + * A pointer to the first byte of the 24-bit RGB pixel within a libjpeg + * scanline buffer. + * + * @return + * A 32-bit Cairo ARGB32 / RGB24 pixel value equivalent to the libjpeg + * pixel at the given pointer. + */ +static uint32_t guacenc_jpeg_translate_rgb(const unsigned char* src) { + + /* Pull components from source */ + int r = *(src++); + int g = *(src++); + int b = *(src++); + + /* Translate to 32-bit integer compatible with Cairo */ + return 0xFF000000 | (r << 16) | (g << 8) | b; + +} + +/** + * Copies the data from a libjpeg scanline buffer into a row of image data + * within a Cairo surface, translating each pixel as necessary. + * + * @param dst + * The destination buffer into which the scanline should be copied. + * + * @param src + * The libjpeg scanline buffer that should be copied into the + * destination buffer. + * + * @param width + * The number of pixels available within both the scanline buffer and the + * destination buffer. + */ +static void guacenc_jpeg_copy_scanline(unsigned char* dst, + const unsigned char* src, int width) { + + uint32_t* current = (uint32_t*) dst; + + /* Copy all pixels from source to destination, translating for Cairo */ + for (; width > 0; width--, src += 3) { + *(current++) = guacenc_jpeg_translate_rgb(src); + } + +} + cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) { struct jpeg_decompress_struct cinfo; @@ -52,13 +104,16 @@ cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) { } /* Begin decompression */ - cinfo.out_color_space = JCS_EXT_BGRX; + cinfo.out_color_space = JCS_RGB; jpeg_start_decompress(&cinfo); /* Pull JPEG dimensions from decompressor */ int width = cinfo.output_width; int height = cinfo.output_height; + /* Allocate sufficient buffer space for one JPEG scanline */ + unsigned char* jpeg_scanline = malloc(width * 3); + /* Create blank Cairo surface (no transparency in JPEG) */ cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); @@ -69,11 +124,22 @@ cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) { /* Read JPEG into surface */ while (cinfo.output_scanline < height) { - unsigned char* buffers[1] = { row }; + + /* Read single scanline */ + unsigned char* buffers[1] = { jpeg_scanline }; jpeg_read_scanlines(&cinfo, buffers, 1); + + /* Copy scanline to Cairo surface */ + guacenc_jpeg_copy_scanline(row, jpeg_scanline, width); + + /* Advance to next row of Cairo surface */ row += stride; + } + /* Scanline buffer is no longer needed */ + free(jpeg_scanline); + /* End decompression */ jpeg_finish_decompress(&cinfo);