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 Richard Wilson <not_ginger_matt@hotmail.com>
  4.  * Copyright 2008 Daniel Silverstone <dsilvers@netsurf-browser.org>
  5.  *
  6.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  7.  *
  8.  * NetSurf is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; version 2 of the License.
  11.  *
  12.  * NetSurf is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19.  */
  20.  
  21. #include <assert.h>
  22. #include <stdbool.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25.  
  26. #include <png.h>
  27.  
  28. #include "desktop/plotters.h"
  29.  
  30. #include "content/content_protected.h"
  31.  
  32. #include "image/bitmap.h"
  33. #include "image/image_cache.h"
  34. #include "image/png.h"
  35.  
  36. #include "utils/log.h"
  37. #include "utils/messages.h"
  38. #include "utils/utils.h"
  39.  
  40. /* accommodate for old versions of libpng (beware security holes!) */
  41.  
  42. #ifndef png_jmpbuf
  43. #warning you have an antique libpng
  44. #define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
  45. #endif
  46.  
  47. #if PNG_LIBPNG_VER < 10209
  48. #define png_set_expand_gray_1_2_4_to_8(png) png_set_gray_1_2_4_to_8(png)
  49. #endif
  50.  
  51. typedef struct nspng_content {
  52.         struct content base; /**< base content type */
  53.  
  54.         bool no_process_data; /**< Do not continue to process data as it arrives */
  55.         png_structp png;
  56.         png_infop info;
  57.         int interlace;
  58.         struct bitmap *bitmap;  /**< Created NetSurf bitmap */
  59.         size_t rowstride, bpp; /**< Bitmap rowstride and bpp */
  60.         size_t rowbytes; /**< Number of bytes per row */
  61. } nspng_content;
  62.  
  63. static unsigned int interlace_start[8] = {0, 16, 0, 8, 0, 4, 0};
  64. static unsigned int interlace_step[8] = {28, 28, 12, 12, 4, 4, 0};
  65. static unsigned int interlace_row_start[8] = {0, 0, 4, 0, 2, 0, 1};
  66. static unsigned int interlace_row_step[8] = {8, 8, 8, 4, 4, 2, 2};
  67.  
  68. /** Callbak error numbers*/
  69. enum nspng_cberr {
  70.         CBERR_NONE = 0, /* no error */
  71.         CBERR_LIBPNG, /* error from png library */
  72.         CBERR_NOPRE, /* no pre-conversion performed */
  73. };
  74.  
  75. /**
  76.  * nspng_warning -- callback for libpng warnings
  77.  */
  78. static void nspng_warning(png_structp png_ptr, png_const_charp warning_message)
  79. {
  80.         LOG(("%s", warning_message));
  81. }
  82.  
  83. /**
  84.  * nspng_error -- callback for libpng errors
  85.  */
  86. static void nspng_error(png_structp png_ptr, png_const_charp error_message)
  87. {
  88.         LOG(("%s", error_message));
  89.         longjmp(png_jmpbuf(png_ptr), CBERR_LIBPNG);
  90. }
  91.  
  92. static void nspng_setup_transforms(png_structp png_ptr, png_infop info_ptr)
  93. {
  94.         int bit_depth, color_type, intent;
  95.         double gamma;
  96.  
  97.         bit_depth = png_get_bit_depth(png_ptr, info_ptr);
  98.         color_type = png_get_color_type(png_ptr, info_ptr);
  99.  
  100.         /* Set up our transformations */
  101.         if (color_type == PNG_COLOR_TYPE_PALETTE) {
  102.                 png_set_palette_to_rgb(png_ptr);
  103.         }
  104.  
  105.         if ((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < 8)) {
  106.                 png_set_expand_gray_1_2_4_to_8(png_ptr);
  107.         }
  108.  
  109.         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
  110.                 png_set_tRNS_to_alpha(png_ptr);
  111.         }
  112.  
  113.         if (bit_depth == 16) {
  114.                 png_set_strip_16(png_ptr);
  115.         }
  116.  
  117.         if (color_type == PNG_COLOR_TYPE_GRAY ||
  118.             color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
  119.                 png_set_gray_to_rgb(png_ptr);
  120.         }
  121.  
  122.         if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
  123.                 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
  124.         }
  125.  
  126.         /* gamma correction - we use 2.2 as our screen gamma
  127.          * this appears to be correct (at least in respect to !Browse)
  128.          * see http://www.w3.org/Graphics/PNG/all_seven.html for a test case
  129.          */
  130.         if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
  131.                 png_set_gamma(png_ptr, 2.2, 0.45455);
  132.         } else {
  133.                 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
  134.                         png_set_gamma(png_ptr, 2.2, gamma);
  135.                 } else {
  136.                         png_set_gamma(png_ptr, 2.2, 0.45455);
  137.                 }
  138.         }
  139.  
  140.         png_read_update_info(png_ptr, info_ptr);
  141. }
  142.  
  143. /**
  144.  * info_callback -- PNG header has been completely received, prepare to process
  145.  * image data
  146.  */
  147. static void info_callback(png_structp png_s, png_infop info)
  148. {
  149.         int interlace;
  150.         png_uint_32 width, height;
  151.         nspng_content *png_c = png_get_progressive_ptr(png_s);
  152.  
  153.         width = png_get_image_width(png_s, info);
  154.         height = png_get_image_height(png_s, info);
  155.         interlace = png_get_interlace_type(png_s, info);
  156.  
  157.         png_c->base.width = width;
  158.         png_c->base.height = height;
  159.         png_c->base.size += width * height * 4;
  160.  
  161.         /* see if progressive-conversion should continue */
  162.         if (image_cache_speculate((struct content *)png_c) == false) {
  163.                 longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
  164.         }
  165.  
  166.         /* Claim the required memory for the converted PNG */
  167.         png_c->bitmap = bitmap_create(width, height, BITMAP_NEW);
  168.         if (png_c->bitmap == NULL) {
  169.                 /* Failed to create bitmap skip pre-conversion */
  170.                 longjmp(png_jmpbuf(png_s), CBERR_NOPRE);
  171.         }
  172.  
  173.         png_c->rowstride = bitmap_get_rowstride(png_c->bitmap);
  174.         png_c->bpp = bitmap_get_bpp(png_c->bitmap);
  175.  
  176.         nspng_setup_transforms(png_s, info);
  177.  
  178.         png_c->rowbytes = png_get_rowbytes(png_s, info);
  179.         png_c->interlace = (interlace == PNG_INTERLACE_ADAM7);
  180.  
  181.         LOG(("size %li * %li, rowbytes %zu", (unsigned long)width,
  182.              (unsigned long)height, png_c->rowbytes));
  183. }
  184.  
  185. static void row_callback(png_structp png_s, png_bytep new_row,
  186.                          png_uint_32 row_num, int pass)
  187. {
  188.         nspng_content *png_c = png_get_progressive_ptr(png_s);
  189.         unsigned long rowbytes = png_c->rowbytes;
  190.         unsigned char *buffer, *row;
  191.  
  192.         /* Give up if there's no bitmap */
  193.         if (png_c->bitmap == NULL)
  194.                 return;
  195.  
  196.         /* Abort if we've not got any data */
  197.         if (new_row == NULL)
  198.                 return;
  199.  
  200.         /* Get bitmap buffer */
  201.         buffer = bitmap_get_buffer(png_c->bitmap);
  202.         if (buffer == NULL) {
  203.                 /* No buffer, bail out */
  204.                 longjmp(png_jmpbuf(png_s), 1);
  205.         }
  206.  
  207.         /* Calculate address of row start */
  208.         row = buffer + (png_c->rowstride * row_num);
  209.  
  210.         /* Handle interlaced sprites using the Adam7 algorithm */
  211.         if (png_c->interlace) {
  212.                 unsigned long dst_off;
  213.                 unsigned long src_off = 0;
  214.                 unsigned int start, step;
  215.  
  216.                 start = interlace_start[pass];
  217.                 step = interlace_step[pass];
  218.                 row_num = interlace_row_start[pass] +
  219.                         interlace_row_step[pass] * row_num;
  220.  
  221.                 /* Copy the data to our current row taking interlacing
  222.                  * into consideration */
  223.                 row = buffer + (png_c->rowstride * row_num);
  224.  
  225.                 for (dst_off = start; dst_off < rowbytes; dst_off += step) {
  226.                         row[dst_off++] = new_row[src_off++];
  227.                         row[dst_off++] = new_row[src_off++];
  228.                         row[dst_off++] = new_row[src_off++];
  229.                         row[dst_off++] = new_row[src_off++];
  230.                 }
  231.         } else {
  232.                 /* Do a fast memcpy of the row data */
  233.                 memcpy(row, new_row, rowbytes);
  234.         }
  235. }
  236.  
  237.  
  238. static void end_callback(png_structp png_s, png_infop info)
  239. {
  240. }
  241.  
  242. static nserror nspng_create_png_data(nspng_content *png_c)
  243. {
  244.         union content_msg_data msg_data;
  245.  
  246.         png_c->bitmap = NULL;
  247.  
  248.         png_c->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  249.         if (png_c->png == NULL) {
  250.                 msg_data.error = messages_get("NoMemory");
  251.                 content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
  252.                 warn_user("NoMemory", 0);
  253.                 return NSERROR_NOMEM;
  254.         }
  255.  
  256.         png_set_error_fn(png_c->png, NULL, nspng_error, nspng_warning);
  257.  
  258.         png_c->info = png_create_info_struct(png_c->png);
  259.         if (png_c->info == NULL) {
  260.                 png_destroy_read_struct(&png_c->png, &png_c->info, 0);
  261.  
  262.                 msg_data.error = messages_get("NoMemory");
  263.                 content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
  264.                 warn_user("NoMemory", 0);
  265.                 return NSERROR_NOMEM;
  266.         }
  267.  
  268.         if (setjmp(png_jmpbuf(png_c->png))) {
  269.                 png_destroy_read_struct(&png_c->png, &png_c->info, 0);
  270.                 LOG(("Failed to set callbacks"));
  271.                 png_c->png = NULL;
  272.                 png_c->info = NULL;
  273.  
  274.                 msg_data.error = messages_get("PNGError");
  275.                 content_broadcast(&png_c->base, CONTENT_MSG_ERROR, msg_data);
  276.                 return NSERROR_NOMEM;
  277.         }
  278.  
  279.         png_set_progressive_read_fn(png_c->png, png_c,
  280.                         info_callback, row_callback, end_callback);
  281.  
  282.         return NSERROR_OK;
  283. }
  284.  
  285. static nserror nspng_create(const content_handler *handler,
  286.                 lwc_string *imime_type, const http_parameter *params,
  287.                 llcache_handle *llcache, const char *fallback_charset,
  288.                 bool quirks, struct content **c)
  289. {
  290.         nspng_content *png_c;
  291.         nserror error;
  292.  
  293.         png_c = calloc(1, sizeof(nspng_content));
  294.         if (png_c == NULL)
  295.                 return NSERROR_NOMEM;
  296.  
  297.         error = content__init(&png_c->base,
  298.                               handler,
  299.                               imime_type,
  300.                               params,
  301.                               llcache,
  302.                               fallback_charset,
  303.                               quirks);
  304.         if (error != NSERROR_OK) {
  305.                 free(png_c);
  306.                 return error;
  307.         }
  308.  
  309.         error = nspng_create_png_data(png_c);
  310.         if (error != NSERROR_OK) {
  311.                 free(png_c);
  312.                 return error;
  313.         }
  314.  
  315.         *c = (struct content *)png_c;
  316.  
  317.         return NSERROR_OK;
  318. }
  319.  
  320.  
  321. static bool nspng_process_data(struct content *c, const char *data,
  322.                                unsigned int size)
  323. {
  324.         nspng_content *png_c = (nspng_content *)c;
  325.         union content_msg_data msg_data;
  326.         volatile bool ret = true;
  327.  
  328.         if (png_c->no_process_data) {
  329.                 return ret;
  330.         }
  331.  
  332.         switch (setjmp(png_jmpbuf(png_c->png))) {
  333.         case CBERR_NONE: /* direct return */   
  334.                 png_process_data(png_c->png, png_c->info, (uint8_t *)data, size);
  335.                 break;
  336.  
  337.         case CBERR_NOPRE: /* not going to progressive convert */
  338.                 png_c->no_process_data = true;
  339.                 break;
  340.  
  341.         default: /* fatal error from library processing png */
  342.                 if (png_c->bitmap != NULL) {
  343.                         /* A bitmap managed to get created so
  344.                          * operation is past header and possibly some
  345.                          * conversion happened before faliure.
  346.                          *
  347.                          * In this case keep the partial
  348.                          * conversion. This is usually seen if a png
  349.                          * has been truncated (often jsut lost its
  350.                          * last byte and hence end of image marker)
  351.                          */
  352.                         png_c->no_process_data = true;
  353.                 } else {
  354.                         /* not managed to progress past header, clean
  355.                          * up png conversion and signal the content
  356.                          * error
  357.                          */
  358.                         LOG(("Fatal PNG error during header, error content"));
  359.  
  360.                         png_destroy_read_struct(&png_c->png, &png_c->info, 0);
  361.                         png_c->png = NULL;
  362.                         png_c->info = NULL;
  363.  
  364.                         msg_data.error = messages_get("PNGError");
  365.                         content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  366.  
  367.                         ret = false;
  368.  
  369.                 }
  370.                 break;
  371.         }
  372.  
  373.         return ret;
  374. }
  375.  
  376. struct png_cache_read_data_s {
  377.         const char *data;
  378.         unsigned long size;
  379. };
  380.  
  381. /** PNG library read fucntion to read data from a memory array
  382.  */
  383. static void
  384. png_cache_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
  385. {
  386.         struct png_cache_read_data_s *png_cache_read_data;
  387.         png_cache_read_data = png_get_io_ptr(png_ptr);
  388.  
  389.         if (length > png_cache_read_data->size) {
  390.                 length = png_cache_read_data->size;
  391.         }
  392.  
  393.         if (length == 0) {
  394.                 png_error(png_ptr, "Read Error");
  395.         }
  396.  
  397.         memcpy(data, png_cache_read_data->data, length);
  398.  
  399.         png_cache_read_data->data += length;
  400.         png_cache_read_data->size -= length;
  401. }
  402.  
  403. /** calculate an array of row pointers into a bitmap data area
  404.  */
  405. static png_bytep *calc_row_pointers(struct bitmap *bitmap)
  406. {
  407.         int height = bitmap_get_height(bitmap);
  408.         unsigned char *buffer= bitmap_get_buffer(bitmap);
  409.         size_t rowstride = bitmap_get_rowstride(bitmap);
  410.         png_bytep *row_ptrs;
  411.         int hloop;
  412.  
  413.         row_ptrs = malloc(sizeof(png_bytep) * height);
  414.  
  415.         if (row_ptrs != NULL) {
  416.                 for (hloop = 0; hloop < height; hloop++) {
  417.                         row_ptrs[hloop] = buffer + (rowstride * hloop);
  418.                 }
  419.         }
  420.  
  421.         return row_ptrs;
  422. }
  423.  
  424. /** PNG content to bitmap conversion.
  425.  *
  426.  * This routine generates a bitmap object from a PNG image content
  427.  */
  428. static struct bitmap *
  429. png_cache_convert(struct content *c)
  430. {
  431.         png_structp png_ptr;
  432.         png_infop info_ptr;
  433.         png_infop end_info;
  434.         volatile struct bitmap *bitmap = NULL;
  435.         struct png_cache_read_data_s png_cache_read_data;
  436.         png_uint_32 width, height;
  437.         volatile png_bytep *row_pointers = NULL;
  438.  
  439.         png_cache_read_data.data =
  440.                 content__get_source_data(c, &png_cache_read_data.size);
  441.  
  442.         if ((png_cache_read_data.data == NULL) ||
  443.             (png_cache_read_data.size <= 8)) {
  444.                 return NULL;
  445.         }
  446.  
  447.         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
  448.                                          NULL,
  449.                                          nspng_error,
  450.                                          nspng_warning);
  451.         if (png_ptr == NULL) {
  452.                 return NULL;
  453.         }
  454.  
  455.         info_ptr = png_create_info_struct(png_ptr);
  456.         if (png_ptr == NULL) {
  457.                 png_destroy_read_struct(&png_ptr, NULL, NULL);
  458.                 return NULL;
  459.         }
  460.  
  461.         end_info = png_create_info_struct(png_ptr);
  462.         if (png_ptr == NULL) {
  463.                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
  464.                 return NULL;
  465.         }
  466.  
  467.         /* setup error exit path */
  468.         if (setjmp(png_jmpbuf(png_ptr))) {
  469.                 /* cleanup and bail */
  470.                 goto png_cache_convert_error;
  471.         }
  472.  
  473.         /* read from a buffer instead of stdio */
  474.         png_set_read_fn(png_ptr, &png_cache_read_data, png_cache_read_fn);
  475.  
  476.         /* ensure the png info structure is populated */
  477.         png_read_info(png_ptr, info_ptr);
  478.  
  479.         /* setup output transforms */
  480.         nspng_setup_transforms(png_ptr, info_ptr);
  481.  
  482.         width = png_get_image_width(png_ptr, info_ptr);
  483.         height = png_get_image_height(png_ptr,info_ptr);
  484.  
  485.         /* Claim the required memory for the converted PNG */
  486.         bitmap = bitmap_create(width, height, BITMAP_NEW);
  487.         if (bitmap == NULL) {
  488.                 /* cleanup and bail */
  489.                 goto png_cache_convert_error;
  490.         }
  491.  
  492.         row_pointers = calc_row_pointers((struct bitmap *) bitmap);
  493.  
  494.         if (row_pointers != NULL) {
  495.                 png_read_image(png_ptr, (png_bytep *) row_pointers);
  496.         }
  497.  
  498. png_cache_convert_error:
  499.  
  500.         /* cleanup png read */
  501.         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
  502.  
  503.         free((png_bytep *) row_pointers);
  504.  
  505.         if (bitmap != NULL)
  506.                 bitmap_modified((struct bitmap *)bitmap);
  507.  
  508.         return (struct bitmap *)bitmap;
  509. }
  510.  
  511. static bool nspng_convert(struct content *c)
  512. {
  513.         nspng_content *png_c = (nspng_content *) c;
  514.         char *title;
  515.  
  516.         assert(png_c->png != NULL);
  517.         assert(png_c->info != NULL);
  518.  
  519.         /* clean up png structures */
  520.         png_destroy_read_struct(&png_c->png, &png_c->info, 0);
  521.  
  522.         /* set title text */
  523.         title = messages_get_buff("PNGTitle",
  524.                         nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
  525.                         c->width, c->height);
  526.         if (title != NULL) {
  527.                 content__set_title(c, title);
  528.                 free(title);
  529.         }
  530.  
  531.         if (png_c->bitmap != NULL) {
  532.                 bitmap_set_opaque(png_c->bitmap, bitmap_test_opaque(png_c->bitmap));
  533.                 bitmap_modified(png_c->bitmap);
  534.         }
  535.  
  536.         image_cache_add(c, png_c->bitmap, png_cache_convert);
  537.  
  538.         content_set_ready(c);
  539.         content_set_done(c);
  540.         content_set_status(c, "");
  541.  
  542.         return true;
  543. }
  544.  
  545.  
  546. static nserror nspng_clone(const struct content *old_c, struct content **new_c)
  547. {
  548.         nspng_content *clone_png_c;
  549.         nserror error;
  550.         const char *data;
  551.         unsigned long size;
  552.  
  553.         clone_png_c = calloc(1, sizeof(nspng_content));
  554.         if (clone_png_c == NULL)
  555.                 return NSERROR_NOMEM;
  556.  
  557.         error = content__clone(old_c, &clone_png_c->base);
  558.         if (error != NSERROR_OK) {
  559.                 content_destroy(&clone_png_c->base);
  560.                 return error;
  561.         }
  562.  
  563.         /* Simply replay create/process/convert */
  564.         error = nspng_create_png_data(clone_png_c);
  565.         if (error != NSERROR_OK) {
  566.                 content_destroy(&clone_png_c->base);
  567.                 return error;
  568.         }
  569.  
  570.         data = content__get_source_data(&clone_png_c->base, &size);
  571.         if (size > 0) {
  572.                 if (nspng_process_data(&clone_png_c->base, data, size) == false) {
  573.                         content_destroy(&clone_png_c->base);
  574.                         return NSERROR_NOMEM;
  575.                 }
  576.         }
  577.  
  578.         if ((old_c->status == CONTENT_STATUS_READY) ||
  579.             (old_c->status == CONTENT_STATUS_DONE)) {
  580.                 if (nspng_convert(&clone_png_c->base) == false) {
  581.                         content_destroy(&clone_png_c->base);
  582.                         return NSERROR_CLONE_FAILED;
  583.                 }
  584.         }
  585.  
  586.         *new_c = (struct content *)clone_png_c;
  587.  
  588.         return NSERROR_OK;
  589. }
  590.  
  591. static const content_handler nspng_content_handler = {
  592.         .create = nspng_create,
  593.         .process_data = nspng_process_data,
  594.         .data_complete = nspng_convert,
  595.         .clone = nspng_clone,
  596.         .destroy = image_cache_destroy,
  597.         .redraw = image_cache_redraw,
  598.         .get_internal = image_cache_get_internal,
  599.         .type = image_cache_content_type,
  600.         .no_share = false,
  601. };
  602.  
  603. static const char *nspng_types[] = {
  604.         "image/png",
  605.         "image/x-png"
  606. };
  607.  
  608. CONTENT_FACTORY_REGISTER_TYPES(nspng, nspng_types, nspng_content_handler);
  609.