Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; version 2 of the License.
  10.  *
  11.  * NetSurf is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20. /** \file
  21.  * Content for image/jpeg (implementation).
  22.  *
  23.  * This implementation uses the IJG JPEG library.
  24.  */
  25.  
  26. #include <assert.h>
  27. #include <setjmp.h>
  28. #include <string.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31.  
  32. #include "content/content_protected.h"
  33. #include "desktop/plotters.h"
  34. #include "image/image_cache.h"
  35.  
  36. #include "utils/log.h"
  37. #include "utils/messages.h"
  38. #include "utils/types.h"
  39. #include "utils/utils.h"
  40.  
  41. #define JPEG_INTERNAL_OPTIONS
  42. #include "jpeglib.h"
  43. #include "image/jpeg.h"
  44.  
  45. /** absolute minimum size of a jpeg below which it is not even worth
  46.  * trying to read header data
  47.  */
  48. #define MIN_JPEG_SIZE 20
  49.  
  50. #ifdef riscos
  51. /* We prefer the library to be configured with these options to save
  52.  * copying data during decoding. */
  53. #if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
  54. #warning JPEG library not optimally configured. Decoding will be slower.
  55. #endif
  56. /* but we don't care if we're not on RISC OS */
  57. #endif
  58.  
  59. static char nsjpeg_error_buffer[JMSG_LENGTH_MAX];
  60.  
  61. static unsigned char nsjpeg_eoi[] = { 0xff, JPEG_EOI };
  62.  
  63. /**
  64.  * Content create entry point.
  65.  */
  66. static nserror nsjpeg_create(const content_handler *handler,
  67.                 lwc_string *imime_type, const http_parameter *params,
  68.                 llcache_handle *llcache, const char *fallback_charset,
  69.                 bool quirks, struct content **c)
  70. {
  71.         struct content *jpeg;
  72.         nserror error;
  73.  
  74.         jpeg = calloc(1, sizeof(struct content));
  75.         if (jpeg == NULL)
  76.                 return NSERROR_NOMEM;
  77.  
  78.         error = content__init(jpeg, handler, imime_type, params,
  79.                               llcache, fallback_charset, quirks);
  80.         if (error != NSERROR_OK) {
  81.                 free(jpeg);
  82.                 return error;
  83.         }
  84.  
  85.         *c = jpeg;
  86.  
  87.         return NSERROR_OK;
  88. }
  89.  
  90. /**
  91.  * JPEG data source manager: initialize source.
  92.  */
  93. static void nsjpeg_init_source(j_decompress_ptr cinfo)
  94. {
  95. }
  96.  
  97.  
  98. /**
  99.  * JPEG data source manager: fill the input buffer.
  100.  *
  101.  * This can only occur if the JPEG data was truncated or corrupted. Insert a
  102.  * fake EOI marker to allow the decompressor to output as much as possible.
  103.  */
  104. static boolean nsjpeg_fill_input_buffer(j_decompress_ptr cinfo)
  105. {
  106.         cinfo->src->next_input_byte = nsjpeg_eoi;
  107.         cinfo->src->bytes_in_buffer = 2;
  108.         return TRUE;
  109. }
  110.  
  111.  
  112. /**
  113.  * JPEG data source manager: skip num_bytes worth of data.
  114.  */
  115.  
  116. static void nsjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
  117. {
  118.         if ((long) cinfo->src->bytes_in_buffer < num_bytes) {
  119.                 cinfo->src->next_input_byte = 0;
  120.                 cinfo->src->bytes_in_buffer = 0;
  121.         } else {
  122.                 cinfo->src->next_input_byte += num_bytes;
  123.                 cinfo->src->bytes_in_buffer -= num_bytes;
  124.         }
  125. }
  126.  
  127.  
  128. /**
  129.  * JPEG data source manager: terminate source.
  130.  */
  131. static void nsjpeg_term_source(j_decompress_ptr cinfo)
  132. {
  133. }
  134.  
  135.  
  136. /**
  137.  * Error output handler for JPEG library.
  138.  *
  139.  * This logs to NetSurf log instead of stderr.
  140.  * Warnings only - fatal errors are trapped by nsjpeg_error_exit
  141.  *                 and do not call the output handler.
  142.  */
  143. static void nsjpeg_error_log(j_common_ptr cinfo)
  144. {
  145.         cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
  146.         LOG(("%s", nsjpeg_error_buffer));
  147. }
  148.  
  149.  
  150. /**
  151.  * Fatal error handler for JPEG library.
  152.  *
  153.  * This prevents jpeglib calling exit() on a fatal error.
  154.  */
  155. static void nsjpeg_error_exit(j_common_ptr cinfo)
  156. {
  157.         jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data;
  158.  
  159.         cinfo->err->format_message(cinfo, nsjpeg_error_buffer);
  160.         LOG(("%s", nsjpeg_error_buffer));
  161.  
  162.         longjmp(*setjmp_buffer, 1);
  163. }
  164.  
  165. static struct bitmap *
  166. jpeg_cache_convert(struct content *c)
  167. {
  168.         uint8_t *source_data; /* Jpeg source data */
  169.         unsigned long source_size; /* length of Jpeg source data */
  170.         struct jpeg_decompress_struct cinfo;
  171.         struct jpeg_error_mgr jerr;
  172.         jmp_buf setjmp_buffer;
  173.         unsigned int height;
  174.         unsigned int width;
  175.         struct bitmap * volatile bitmap = NULL;
  176.         uint8_t * volatile pixels = NULL;
  177.         size_t rowstride;
  178.         struct jpeg_source_mgr source_mgr = {
  179.                 0,
  180.                 0,
  181.                 nsjpeg_init_source,
  182.                 nsjpeg_fill_input_buffer,
  183.                 nsjpeg_skip_input_data,
  184.                 jpeg_resync_to_restart,
  185.                 nsjpeg_term_source };
  186.  
  187.         /* obtain jpeg source data and perfom minimal sanity checks */
  188.         source_data = (uint8_t *)content__get_source_data(c, &source_size);
  189.  
  190.         if ((source_data == NULL) ||
  191.             (source_size < MIN_JPEG_SIZE)) {
  192.                 return NULL;
  193.         }
  194.  
  195.         /* setup a JPEG library error handler */
  196.         cinfo.err = jpeg_std_error(&jerr);
  197.         jerr.error_exit = nsjpeg_error_exit;
  198.         jerr.output_message = nsjpeg_error_log;
  199.  
  200.         /* handler for fatal errors during decompression */
  201.         if (setjmp(setjmp_buffer)) {
  202.                 jpeg_destroy_decompress(&cinfo);
  203.                 return bitmap;
  204.         }
  205.  
  206.         jpeg_create_decompress(&cinfo);
  207.         cinfo.client_data = &setjmp_buffer;
  208.  
  209.         /* setup data source */
  210.         source_mgr.next_input_byte = source_data;
  211.         source_mgr.bytes_in_buffer = source_size;
  212.         cinfo.src = &source_mgr;
  213.  
  214.         /* read JPEG header information */
  215.         jpeg_read_header(&cinfo, TRUE);
  216.  
  217.         /* set output processing parameters */
  218.         cinfo.out_color_space = JCS_RGB;
  219.         cinfo.dct_method = JDCT_ISLOW;
  220.  
  221.         /* commence the decompression, output parameters now valid */
  222.         jpeg_start_decompress(&cinfo);
  223.  
  224.         width = cinfo.output_width;
  225.         height = cinfo.output_height;
  226.  
  227.         /* create opaque bitmap (jpegs cannot be transparent) */
  228.         bitmap = bitmap_create(width, height, BITMAP_NEW | BITMAP_OPAQUE);
  229.         if (bitmap == NULL) {
  230.                 /* empty bitmap could not be created */
  231.                 jpeg_destroy_decompress(&cinfo);
  232.                 return NULL;
  233.         }
  234.  
  235.         pixels = bitmap_get_buffer(bitmap);
  236.         if (pixels == NULL) {
  237.                 /* bitmap with no buffer available */
  238.                 bitmap_destroy(bitmap);
  239.                 jpeg_destroy_decompress(&cinfo);
  240.                 return NULL;
  241.         }
  242.  
  243.         /* Convert scanlines from jpeg into bitmap */
  244.         rowstride = bitmap_get_rowstride(bitmap);
  245.         do {
  246.                 JSAMPROW scanlines[1];
  247.  
  248.                 scanlines[0] = (JSAMPROW) (pixels +
  249.                                            rowstride * cinfo.output_scanline);
  250.                 jpeg_read_scanlines(&cinfo, scanlines, 1);
  251.  
  252. #if RGB_RED != 0 || RGB_GREEN != 1 || RGB_BLUE != 2 || RGB_PIXELSIZE != 4
  253. {
  254.                 /* Missmatch between configured libjpeg pixel format and
  255.                  * NetSurf pixel format.  Convert to RGBA */
  256.                 int i;
  257.                 for (i = width - 1; 0 <= i; i--) {
  258.                         int r = scanlines[0][i * RGB_PIXELSIZE + RGB_RED];
  259.                         int g = scanlines[0][i * RGB_PIXELSIZE + RGB_GREEN];
  260.                         int b = scanlines[0][i * RGB_PIXELSIZE + RGB_BLUE];
  261.                         scanlines[0][i * 4 + 0] = r;
  262.                         scanlines[0][i * 4 + 1] = g;
  263.                         scanlines[0][i * 4 + 2] = b;
  264.                         scanlines[0][i * 4 + 3] = 0xff;
  265.                 }
  266. }
  267. #endif
  268.         } while (cinfo.output_scanline != cinfo.output_height);
  269.         bitmap_modified(bitmap);
  270.  
  271.         jpeg_finish_decompress(&cinfo);
  272.         jpeg_destroy_decompress(&cinfo);
  273.  
  274.         return bitmap;
  275. }
  276.  
  277. /**
  278.  * Convert a CONTENT_JPEG for display.
  279.  */
  280. static bool nsjpeg_convert(struct content *c)
  281. {
  282.         struct jpeg_decompress_struct cinfo;
  283.         struct jpeg_error_mgr jerr;
  284.         jmp_buf setjmp_buffer;
  285.         struct jpeg_source_mgr source_mgr = { 0, 0,
  286.                 nsjpeg_init_source, nsjpeg_fill_input_buffer,
  287.                 nsjpeg_skip_input_data, jpeg_resync_to_restart,
  288.                 nsjpeg_term_source };
  289.         union content_msg_data msg_data;
  290.         const char *data;
  291.         unsigned long size;
  292.         char *title;
  293.  
  294.         /* check image header is valid and get width/height */
  295.         data = content__get_source_data(c, &size);
  296.  
  297.         cinfo.err = jpeg_std_error(&jerr);
  298.         jerr.error_exit = nsjpeg_error_exit;
  299.         jerr.output_message = nsjpeg_error_log;
  300.  
  301.         if (setjmp(setjmp_buffer)) {
  302.                 jpeg_destroy_decompress(&cinfo);
  303.  
  304.                 msg_data.error = nsjpeg_error_buffer;
  305.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  306.                 return false;
  307.         }
  308.  
  309.         jpeg_create_decompress(&cinfo);
  310.         cinfo.client_data = &setjmp_buffer;
  311.         source_mgr.next_input_byte = (unsigned char *) data;
  312.         source_mgr.bytes_in_buffer = size;
  313.         cinfo.src = &source_mgr;
  314.         jpeg_read_header(&cinfo, TRUE);
  315.         cinfo.out_color_space = JCS_RGB;
  316.         cinfo.dct_method = JDCT_ISLOW;
  317.  
  318.         jpeg_calc_output_dimensions(&cinfo);
  319.  
  320.         c->width = cinfo.output_width;
  321.         c->height = cinfo.output_height;
  322.         c->size = c->width * c->height * 4;
  323.  
  324.         jpeg_destroy_decompress(&cinfo);
  325.  
  326.         image_cache_add(c, NULL, jpeg_cache_convert);
  327.  
  328.         /* set title text */
  329.         title = messages_get_buff("JPEGTitle",
  330.                         nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
  331.                         c->width, c->height);
  332.         if (title != NULL) {
  333.                 content__set_title(c, title);
  334.                 free(title);
  335.         }
  336.  
  337.         content_set_ready(c);
  338.         content_set_done(c);   
  339.         content_set_status(c, ""); /* Done: update status bar */
  340.  
  341.         return true;
  342. }
  343.  
  344.  
  345.  
  346. /**
  347.  * Clone content.
  348.  */
  349. static nserror nsjpeg_clone(const struct content *old, struct content **newc)
  350. {
  351.         struct content *jpeg_c;
  352.         nserror error;
  353.  
  354.         jpeg_c = calloc(1, sizeof(struct content));
  355.         if (jpeg_c == NULL)
  356.                 return NSERROR_NOMEM;
  357.  
  358.         error = content__clone(old, jpeg_c);
  359.         if (error != NSERROR_OK) {
  360.                 content_destroy(jpeg_c);
  361.                 return error;
  362.         }
  363.  
  364.         /* re-convert if the content is ready */
  365.         if ((old->status == CONTENT_STATUS_READY) ||
  366.             (old->status == CONTENT_STATUS_DONE)) {
  367.                 if (nsjpeg_convert(jpeg_c) == false) {
  368.                         content_destroy(jpeg_c);
  369.                         return NSERROR_CLONE_FAILED;
  370.                 }
  371.         }
  372.  
  373.         *newc = jpeg_c;
  374.  
  375.         return NSERROR_OK;
  376. }
  377.  
  378. static const content_handler nsjpeg_content_handler = {
  379.         .create = nsjpeg_create,
  380.         .data_complete = nsjpeg_convert,
  381.         .destroy = image_cache_destroy,
  382.         .redraw = image_cache_redraw,
  383.         .clone = nsjpeg_clone,
  384.         .get_internal = image_cache_get_internal,
  385.         .type = image_cache_content_type,
  386.         .no_share = false,
  387. };
  388.  
  389. static const char *nsjpeg_types[] = {
  390.         "image/jpeg",
  391.         "image/jpg",
  392.         "image/pjpeg"
  393. };
  394.  
  395. CONTENT_FACTORY_REGISTER_TYPES(nsjpeg, nsjpeg_types, nsjpeg_content_handler);
  396.