Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * Content handler for image/svg using librsvg (implementation).
  21.  *
  22.  * SVG files are rendered to a NetSurf bitmap by creating a Cairo rendering
  23.  * surface (content_rsvg_data.cs) over the bitmap's data, creating a Cairo
  24.  * drawing context using that surface, and then passing that drawing context
  25.  * to librsvg which then uses Cairo calls to plot the graphic to the bitmap.
  26.  * We store this in content->bitmap, and then use the usual bitmap plotter
  27.  * function to render it for redraw requests.
  28.  */
  29.  
  30. #include <stdbool.h>
  31. #include <assert.h>
  32. #include <string.h>
  33. #include <sys/types.h>
  34.  
  35. #include <librsvg/rsvg.h>
  36. #include <librsvg/rsvg-cairo.h>
  37.  
  38. #include "content/content_protected.h"
  39. #include "desktop/plotters.h"
  40. #include "image/bitmap.h"
  41. #include "utils/log.h"
  42. #include "utils/utils.h"
  43. #include "utils/messages.h"
  44.  
  45. #include "image/rsvg.h"
  46.  
  47. typedef struct rsvg_content {
  48.         struct content base;
  49.  
  50.         RsvgHandle *rsvgh;      /**< Context handle for RSVG renderer */
  51.         cairo_surface_t *cs;    /**< The surface built inside a nsbitmap */
  52.         cairo_t *ct;            /**< Cairo drawing context */
  53.         struct bitmap *bitmap;  /**< Created NetSurf bitmap */
  54. } rsvg_content;
  55.  
  56. static nserror rsvg_create_svg_data(rsvg_content *c)
  57. {
  58.         union content_msg_data msg_data;
  59.  
  60.         c->rsvgh = NULL;
  61.         c->cs = NULL;
  62.         c->ct = NULL;
  63.         c->bitmap = NULL;
  64.  
  65.         if ((c->rsvgh = rsvg_handle_new()) == NULL) {
  66.                 LOG(("rsvg_handle_new() returned NULL."));
  67.                 msg_data.error = messages_get("NoMemory");
  68.                 content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  69.                 return NSERROR_NOMEM;
  70.         }
  71.  
  72.         return NSERROR_OK;
  73. }
  74.  
  75.  
  76. static nserror rsvg_create(const content_handler *handler,
  77.                 lwc_string *imime_type, const http_parameter *params,
  78.                 llcache_handle *llcache, const char *fallback_charset,
  79.                 bool quirks, struct content **c)
  80. {
  81.         rsvg_content *svg;
  82.         nserror error;
  83.  
  84.         svg = calloc(1, sizeof(rsvg_content));
  85.         if (svg == NULL)
  86.                 return NSERROR_NOMEM;
  87.  
  88.         error = content__init(&svg->base, handler, imime_type, params,
  89.                         llcache, fallback_charset, quirks);
  90.         if (error != NSERROR_OK) {
  91.                 free(svg);
  92.                 return error;
  93.         }
  94.  
  95.         error = rsvg_create_svg_data(svg);
  96.         if (error != NSERROR_OK) {
  97.                 free(svg);
  98.                 return error;
  99.         }
  100.  
  101.         *c = (struct content *) svg;
  102.  
  103.         return NSERROR_OK;
  104. }
  105.  
  106.  
  107. static bool rsvg_process_data(struct content *c, const char *data,
  108.                         unsigned int size)
  109. {
  110.         rsvg_content *d = (rsvg_content *) c;
  111.         union content_msg_data msg_data;
  112.         GError *err = NULL;
  113.  
  114.         if (rsvg_handle_write(d->rsvgh, (const guchar *)data, (gsize)size,
  115.                                 &err) == FALSE) {
  116.                 LOG(("rsvg_handle_write returned an error: %s", err->message));
  117.                 msg_data.error = err->message;
  118.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  119.                 return false;
  120.         }
  121.  
  122.         return true;
  123. }
  124.  
  125. /** Convert Cairo's ARGB output to NetSurf's favoured ABGR format.  It converts
  126.  * the data in-place.
  127.  *
  128.  * \param pixels        Pixel data, in the form of ARGB.  This will
  129.  *                      be overwritten with new data in the form of ABGR.
  130.  * \param width         Width of the bitmap
  131.  * \param height        Height of the bitmap
  132.  * \param rowstride     Number of bytes to skip after each row (this
  133.  *                      implementation requires this to be a multiple of 4.)
  134.  */
  135. static inline void rsvg_argb_to_abgr(uint8_t *pixels,
  136.                 int width, int height, size_t rowstride)
  137. {
  138.         uint8_t *p = pixels;
  139.  
  140.         for (int y = 0; y < height; y++) {
  141.                 for (int x = 0; x < width; x++) {
  142.                         /* Swap R and B */
  143.                         const uint8_t r = p[x+3];
  144.  
  145.                         p[x+3] = p[x];
  146.  
  147.                         p[x] = r;
  148.                 }
  149.  
  150.                 p += rowstride;
  151.         }
  152. }
  153.  
  154. static bool rsvg_convert(struct content *c)
  155. {
  156.         rsvg_content *d = (rsvg_content *) c;
  157.         union content_msg_data msg_data;
  158.         RsvgDimensionData rsvgsize;
  159.         GError *err = NULL;
  160.  
  161.         if (rsvg_handle_close(d->rsvgh, &err) == FALSE) {
  162.                 LOG(("rsvg_handle_close returned an error: %s", err->message));
  163.                 msg_data.error = err->message;
  164.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  165.                 return false;
  166.         }
  167.  
  168.         assert(err == NULL);
  169.  
  170.         /* we should now be able to query librsvg for the natural size of the
  171.          * graphic, so we can create our bitmap.
  172.          */
  173.  
  174.         rsvg_handle_get_dimensions(d->rsvgh, &rsvgsize);
  175.         c->width = rsvgsize.width;
  176.         c->height = rsvgsize.height;
  177.  
  178.         if ((d->bitmap = bitmap_create(c->width, c->height,
  179.                         BITMAP_NEW)) == NULL) {
  180.                 LOG(("Failed to create bitmap for rsvg render."));
  181.                 msg_data.error = messages_get("NoMemory");
  182.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  183.                 return false;
  184.         }
  185.  
  186.         if ((d->cs = cairo_image_surface_create_for_data(
  187.                         (unsigned char *)bitmap_get_buffer(d->bitmap),
  188.                         CAIRO_FORMAT_ARGB32,
  189.                         c->width, c->height,
  190.                         bitmap_get_rowstride(d->bitmap))) == NULL) {
  191.                 LOG(("Failed to create Cairo image surface for rsvg render."));
  192.                 msg_data.error = messages_get("NoMemory");
  193.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  194.                 return false;
  195.         }
  196.  
  197.         if ((d->ct = cairo_create(d->cs)) == NULL) {
  198.                 LOG(("Failed to create Cairo drawing context for rsvg render."));
  199.                 msg_data.error = messages_get("NoMemory");
  200.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  201.                 return false;
  202.         }
  203.  
  204.         rsvg_handle_render_cairo(d->rsvgh, d->ct);
  205.         rsvg_argb_to_abgr(bitmap_get_buffer(d->bitmap),
  206.                                 c->width, c->height,
  207.                                 bitmap_get_rowstride(d->bitmap));
  208.  
  209.         bitmap_modified(d->bitmap);
  210.         content_set_ready(c);
  211.         content_set_done(c);
  212.         /* Done: update status bar */
  213.         content_set_status(c, "");
  214.  
  215.         return true;
  216. }
  217.  
  218. static bool rsvg_redraw(struct content *c, struct content_redraw_data *data,
  219.                 const struct rect *clip, const struct redraw_context *ctx)
  220. {
  221.         rsvg_content *rsvgcontent = (rsvg_content *) c;
  222.         bitmap_flags_t flags = BITMAPF_NONE;
  223.  
  224.         assert(rsvgcontent->bitmap != NULL);
  225.  
  226.         if (data->repeat_x)
  227.                 flags |= BITMAPF_REPEAT_X;
  228.         if (data->repeat_y)
  229.                 flags |= BITMAPF_REPEAT_Y;
  230.  
  231.         return ctx->plot->bitmap(data->x, data->y, data->width, data->height,
  232.                         rsvgcontent->bitmap, data->background_colour, flags);
  233. }
  234.  
  235. static void rsvg_destroy(struct content *c)
  236. {
  237.         rsvg_content *d = (rsvg_content *) c;
  238.  
  239.         if (d->bitmap != NULL) bitmap_destroy(d->bitmap);
  240.         if (d->rsvgh != NULL) g_object_unref(d->rsvgh);
  241.         if (d->ct != NULL) cairo_destroy(d->ct);
  242.         if (d->cs != NULL) cairo_surface_destroy(d->cs);
  243.  
  244.         return;
  245. }
  246.  
  247. static nserror rsvg_clone(const struct content *old, struct content **newc)
  248. {
  249.         rsvg_content *svg;
  250.         nserror error;
  251.         const char *data;
  252.         unsigned long size;
  253.  
  254.         svg = calloc(1, sizeof(rsvg_content));
  255.         if (svg == NULL)
  256.                 return NSERROR_NOMEM;
  257.  
  258.         error = content__clone(old, &svg->base);
  259.         if (error != NSERROR_OK) {
  260.                 content_destroy(&svg->base);
  261.                 return error;
  262.         }
  263.  
  264.         /* Simply replay create/process/convert */
  265.         error = rsvg_create_svg_data(svg);
  266.         if (error != NSERROR_OK) {
  267.                 content_destroy(&svg->base);
  268.                 return error;
  269.         }
  270.  
  271.         data = content__get_source_data(&svg->base, &size);
  272.         if (size > 0) {
  273.                 if (rsvg_process_data(&svg->base, data, size) == false) {
  274.                         content_destroy(&svg->base);
  275.                         return NSERROR_NOMEM;
  276.                 }
  277.         }
  278.  
  279.         if (old->status == CONTENT_STATUS_READY ||
  280.                         old->status == CONTENT_STATUS_DONE) {
  281.                 if (rsvg_convert(&svg->base) == false) {
  282.                         content_destroy(&svg->base);
  283.                         return NSERROR_CLONE_FAILED;
  284.                 }
  285.         }
  286.  
  287.         *newc = (struct content *) svg;
  288.  
  289.         return NSERROR_OK;
  290. }
  291.  
  292. static void *rsvg_get_internal(const struct content *c, void *context)
  293. {
  294.         rsvg_content *d = (rsvg_content *) c;
  295.  
  296.         return d->bitmap;
  297. }
  298.  
  299. static content_type rsvg_content_type(void)
  300. {
  301.         return CONTENT_IMAGE;
  302. }
  303.  
  304. static const content_handler rsvg_content_handler = {
  305.         .create = rsvg_create,
  306.         .process_data = rsvg_process_data,
  307.         .data_complete = rsvg_convert,
  308.         .destroy = rsvg_destroy,
  309.         .redraw = rsvg_redraw,
  310.         .clone = rsvg_clone,
  311.         .get_internal = rsvg_get_internal,
  312.         .type = rsvg_content_type,
  313.         .no_share = false,
  314. };
  315.  
  316. static const char *rsvg_types[] = {
  317.         "image/svg",
  318.         "image/svg+xml"
  319. };
  320.  
  321. CONTENT_FACTORY_REGISTER_TYPES(nsrsvg, rsvg_types, rsvg_content_handler);
  322.  
  323.