diff options
Diffstat (limited to 'content/handlers/image/jpeg.c')
-rw-r--r-- | content/handlers/image/jpeg.c | 197 |
1 files changed, 153 insertions, 44 deletions
diff --git a/content/handlers/image/jpeg.c b/content/handlers/image/jpeg.c index 44b1c5271..93372f15a 100644 --- a/content/handlers/image/jpeg.c +++ b/content/handlers/image/jpeg.c @@ -17,8 +17,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Content for image/jpeg (implementation). +/** + * \file + * implementation of content handling for image/jpeg * * This implementation uses the IJG JPEG library. */ @@ -32,8 +33,11 @@ #include "utils/messages.h" #include "netsurf/bitmap.h" #include "content/llcache.h" +#include "content/content.h" #include "content/content_protected.h" +#include "content/content_factory.h" #include "desktop/gui_internal.h" +#include "desktop/bitmap.h" #include "image/image_cache.h" @@ -46,13 +50,8 @@ */ #define MIN_JPEG_SIZE 20 -#ifdef riscos -/* We prefer the library to be configured with these options to save - * copying data during decoding. */ -#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4 -#warning JPEG library not optimally configured. Decoding will be slower. -#endif -/* but we don't care if we're not on RISC OS */ +#ifndef LIBJPEG_TURBO_VERSION +#warning Using libjpeg (libjpeg-turbo is recommended) #endif static char nsjpeg_error_buffer[JMSG_LENGTH_MAX]; @@ -161,16 +160,107 @@ static void nsjpeg_error_exit(j_common_ptr cinfo) longjmp(*setjmp_buffer, 1); } +/** + * Convert scan lines from CMYK to core client bitmap layout. + */ +static inline void nsjpeg__decode_cmyk( + struct jpeg_decompress_struct *cinfo, + uint8_t * volatile pixels, + size_t rowstride) +{ + int width = cinfo->output_width * 4; + + do { + JSAMPROW scanlines[1] = { + [0] = (JSAMPROW) + (pixels + rowstride * cinfo->output_scanline), + }; + jpeg_read_scanlines(cinfo, scanlines, 1); + + for (int i = width - 4; 0 <= i; i -= 4) { + /* Trivial inverse CMYK -> RGBA */ + const int c = scanlines[0][i + 0]; + const int m = scanlines[0][i + 1]; + const int y = scanlines[0][i + 2]; + const int k = scanlines[0][i + 3]; + + const int ck = c * k; + const int mk = m * k; + const int yk = y * k; + +#define DIV255(x) ((x) + 1 + ((x) >> 8)) >> 8 + scanlines[0][i + bitmap_layout.r] = DIV255(ck); + scanlines[0][i + bitmap_layout.g] = DIV255(mk); + scanlines[0][i + bitmap_layout.b] = DIV255(yk); + scanlines[0][i + bitmap_layout.a] = 0xff; +#undef DIV255 + } + } while (cinfo->output_scanline != cinfo->output_height); +} + +/** + * Convert scan lines from CMYK to core client bitmap layout. + */ +static inline void nsjpeg__decode_rgb( + struct jpeg_decompress_struct *cinfo, + uint8_t * volatile pixels, + size_t rowstride) +{ +#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4 + int width = cinfo->output_width; +#endif + + do { + JSAMPROW scanlines[1] = { + [0] = (JSAMPROW) + (pixels + rowstride * cinfo->output_scanline), + }; + jpeg_read_scanlines(cinfo, scanlines, 1); + +#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4 + /* Missmatch between configured libjpeg pixel format and + * NetSurf pixel format. Convert to RGBA */ + for (int i = width - 1; 0 <= i; i--) { + int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED]; + int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN]; + int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE]; + scanlines[0][i * 4 + bitmap_layout.r] = r; + scanlines[0][i * 4 + bitmap_layout.g] = g; + scanlines[0][i * 4 + bitmap_layout.b] = b; + scanlines[0][i * 4 + bitmap_layout.a] = 0xff; + } +#endif + } while (cinfo->output_scanline != cinfo->output_height); +} + +/** + * Convert scan lines from CMYK to core client bitmap layout. + */ +static inline void nsjpeg__decode_client_fmt( + struct jpeg_decompress_struct *cinfo, + uint8_t * volatile pixels, + size_t rowstride) +{ + do { + JSAMPROW scanlines[1] = { + [0] = (JSAMPROW) + (pixels + rowstride * cinfo->output_scanline), + }; + jpeg_read_scanlines(cinfo, scanlines, 1); + } while (cinfo->output_scanline != cinfo->output_height); +} + +/** + * create a bitmap from jpeg content. + */ static struct bitmap * jpeg_cache_convert(struct content *c) { - uint8_t *source_data; /* Jpeg source data */ - unsigned long source_size; /* length of Jpeg source data */ + const uint8_t *source_data; /* Jpeg source data */ + size_t source_size; /* length of Jpeg source data */ struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; jmp_buf setjmp_buffer; - unsigned int height; - unsigned int width; struct bitmap * volatile bitmap = NULL; uint8_t * volatile pixels = NULL; size_t rowstride; @@ -184,7 +274,7 @@ jpeg_cache_convert(struct content *c) nsjpeg_term_source }; /* obtain jpeg source data and perfom minimal sanity checks */ - source_data = (uint8_t *)content__get_source_data(c, &source_size); + source_data = content__get_source_data(c, &source_size); if ((source_data == NULL) || (source_size < MIN_JPEG_SIZE)) { @@ -214,17 +304,43 @@ jpeg_cache_convert(struct content *c) jpeg_read_header(&cinfo, TRUE); /* set output processing parameters */ - cinfo.out_color_space = JCS_RGB; + if (cinfo.jpeg_color_space == JCS_CMYK || + cinfo.jpeg_color_space == JCS_YCCK) { + cinfo.out_color_space = JCS_CMYK; + } else { +#ifdef JCS_ALPHA_EXTENSIONS + switch (bitmap_fmt.layout) { + case BITMAP_LAYOUT_R8G8B8A8: + cinfo.out_color_space = JCS_EXT_RGBA; + break; + case BITMAP_LAYOUT_B8G8R8A8: + cinfo.out_color_space = JCS_EXT_BGRA; + break; + case BITMAP_LAYOUT_A8R8G8B8: + cinfo.out_color_space = JCS_EXT_ARGB; + break; + case BITMAP_LAYOUT_A8B8G8R8: + cinfo.out_color_space = JCS_EXT_ABGR; + break; + default: + NSLOG(netsurf, ERROR, "Unexpected bitmap format: %u", + bitmap_fmt.layout); + jpeg_destroy_decompress(&cinfo); + return NULL; + } +#else + cinfo.out_color_space = JCS_RGB; +#endif + } cinfo.dct_method = JDCT_ISLOW; /* commence the decompression, output parameters now valid */ jpeg_start_decompress(&cinfo); - width = cinfo.output_width; - height = cinfo.output_height; - /* create opaque bitmap (jpegs cannot be transparent) */ - bitmap = guit->bitmap->create(width, height, BITMAP_NEW | BITMAP_OPAQUE); + bitmap = guit->bitmap->create( + cinfo.output_width, + cinfo.output_height, BITMAP_OPAQUE); if (bitmap == NULL) { /* empty bitmap could not be created */ jpeg_destroy_decompress(&cinfo); @@ -241,30 +357,21 @@ jpeg_cache_convert(struct content *c) /* Convert scanlines from jpeg into bitmap */ rowstride = guit->bitmap->get_rowstride(bitmap); - do { - JSAMPROW scanlines[1]; - scanlines[0] = (JSAMPROW) (pixels + - rowstride * cinfo.output_scanline); - jpeg_read_scanlines(&cinfo, scanlines, 1); + switch (cinfo.out_color_space) { + case JCS_CMYK: + nsjpeg__decode_cmyk(&cinfo, pixels, rowstride); + break; + + case JCS_RGB: + nsjpeg__decode_rgb(&cinfo, pixels, rowstride); + break; + + default: + nsjpeg__decode_client_fmt(&cinfo, pixels, rowstride); + break; + } -#if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4 -{ - /* Missmatch between configured libjpeg pixel format and - * NetSurf pixel format. Convert to RGBA */ - int i; - for (i = width - 1; 0 <= i; i--) { - int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED]; - int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN]; - int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE]; - scanlines[0][i * 4 + 0] = r; - scanlines[0][i * 4 + 1] = g; - scanlines[0][i * 4 + 2] = b; - scanlines[0][i * 4 + 3] = 0xff; - } -} -#endif - } while (cinfo.output_scanline != cinfo.output_height); guit->bitmap->modified(bitmap); jpeg_finish_decompress(&cinfo); @@ -286,8 +393,8 @@ static bool nsjpeg_convert(struct content *c) nsjpeg_skip_input_data, jpeg_resync_to_restart, nsjpeg_term_source }; union content_msg_data msg_data; - const char *data; - unsigned long size; + const uint8_t *data; + size_t size; char *title; /* check image header is valid and get width/height */ @@ -300,7 +407,8 @@ static bool nsjpeg_convert(struct content *c) if (setjmp(setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); - msg_data.error = nsjpeg_error_buffer; + msg_data.errordata.errorcode = NSERROR_UNKNOWN; + msg_data.errordata.errormsg = nsjpeg_error_buffer; content_broadcast(c, CONTENT_MSG_ERROR, &msg_data); return false; } @@ -382,6 +490,7 @@ static const content_handler nsjpeg_content_handler = { .clone = nsjpeg_clone, .get_internal = image_cache_get_internal, .type = image_cache_content_type, + .is_opaque = image_cache_is_opaque, .no_share = false, }; |