Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2005 Richard Wilson <info@tinct.net>
  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 for image/mng, image/png, and image/jng (implementation).
  21.  */
  22.  
  23. #include <assert.h>
  24. #include <stdbool.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #include <sys/time.h>
  28. #include <time.h>
  29. #include <libmng.h>
  30. #include "content/content_protected.h"
  31. #include "desktop/options.h"
  32. #include "desktop/plotters.h"
  33. #include "image/bitmap.h"
  34. #include "image/mng.h"
  35. #include "utils/log.h"
  36. #include "utils/messages.h"
  37. #include "utils/schedule.h"
  38. #include "utils/utils.h"
  39.  
  40. /* This implementation does not currently support dynamic MNGs or any
  41.  * form of colour/gamma correction,
  42.  */
  43.  
  44. typedef struct nsmng_content
  45. {
  46.         struct content base;
  47.        
  48.         bool opaque_test_pending;
  49.         bool read_start;
  50.         bool read_resume;
  51.         int read_size;
  52.         bool waiting;
  53.         bool displayed;
  54.         void *handle;
  55.  
  56.         struct bitmap *bitmap;  /**< Created NetSurf bitmap */
  57. } nsmng_content;
  58.  
  59.  
  60. #ifndef MNG_INTERNAL_MEMMNGMT
  61.  
  62. /**
  63.  * Memory allocation callback for libmng.
  64.  */
  65.  
  66. static mng_ptr nsmng_alloc(mng_size_t n)
  67. {
  68.         return calloc(1, n);
  69. }
  70.  
  71.  
  72. /**
  73.  * Memory free callback for libmng.
  74.  */
  75.  
  76. static void nsmng_free(mng_ptr p, mng_size_t n)
  77. {
  78.         free(p);
  79. }
  80.  
  81. #endif
  82.  
  83. /**
  84.  * Broadcasts an error message and returns false
  85.  *
  86.  * \param c the content to broadcast for
  87.  * \return Appropriate error
  88.  */
  89. static nserror nsmng_broadcast_error(nsmng_content *c, mng_retcode code)
  90. {
  91.         union content_msg_data msg_data;
  92.         char error[100];
  93.  
  94.         assert(c != NULL);
  95.  
  96.         if (code == MNG_OUTOFMEMORY) {
  97.                 msg_data.error = messages_get("NoMemory");
  98.                 content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  99.                 return NSERROR_NOMEM;
  100.         }
  101.  
  102.         snprintf(error, sizeof error, messages_get("MNGError"), code);
  103.         msg_data.error = error;
  104.         content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  105.         return NSERROR_MNG_ERROR;
  106. }
  107.  
  108. /* CALLBACKS REQUIRED FOR libmng READING */
  109.  
  110. static mng_bool nsmng_openstream(mng_handle mng)
  111. {
  112.         assert(mng != NULL);
  113.         return MNG_TRUE;
  114. }
  115.  
  116. static mng_bool nsmng_readdata(mng_handle mng, mng_ptr buffer, mng_uint32 size,
  117.                 mng_uint32 *bytesread)
  118. {
  119.         nsmng_content *c;
  120.         const char *data;
  121.         unsigned long data_size;
  122.  
  123.         assert(mng != NULL);
  124.         assert(buffer != NULL);
  125.         assert(bytesread != NULL);
  126.  
  127.         /*      Get our content back
  128.         */
  129.         c = (nsmng_content *) mng_get_userdata(mng);
  130.         assert(c != NULL);
  131.  
  132.         /*      Copy any data we have (maximum of 'size')
  133.         */
  134.         data = content__get_source_data(&c->base, &data_size);
  135.  
  136.         *bytesread = ((data_size - c->read_size) < size) ?
  137.                         (data_size - c->read_size) : size;
  138.  
  139.         if ((*bytesread) > 0) {
  140.                 memcpy(buffer, data + c->read_size, *bytesread);
  141.                 c->read_size += *bytesread;
  142.         }
  143.  
  144.         /*      Return success
  145.         */
  146.         return MNG_TRUE;
  147. }
  148.  
  149. static mng_bool nsmng_closestream(mng_handle mng)
  150. {
  151.         assert(mng != NULL);
  152.         return MNG_TRUE;
  153. }
  154.  
  155. static mng_bool nsmng_processheader(mng_handle mng, mng_uint32 width,
  156.                 mng_uint32 height)
  157. {
  158.         nsmng_content *c;
  159.         union content_msg_data msg_data;
  160.         uint8_t *buffer;
  161.  
  162.         assert(mng != NULL);
  163.  
  164.         /*      This function is called when the header has been read and we
  165.                 know the dimensions of the canvas.
  166.         */
  167.         c = (nsmng_content *) mng_get_userdata(mng);
  168.         assert(c != NULL);
  169.  
  170.         c->bitmap = bitmap_create(width, height, BITMAP_NEW);
  171.         if (c->bitmap == NULL) {
  172.                 msg_data.error = messages_get("NoMemory");
  173.                 content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  174.                 LOG(("Insufficient memory to create canvas."));
  175.                 return MNG_FALSE;
  176.         }
  177.  
  178.         /* Get the buffer to ensure that it is allocated and the calls in
  179.          * nsmng_getcanvasline() succeed. */
  180.         buffer = bitmap_get_buffer(c->bitmap);
  181.         if (buffer == NULL) {
  182.                 msg_data.error = messages_get("NoMemory");
  183.                 content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  184.                 LOG(("Insufficient memory to create canvas."));
  185.                 return MNG_FALSE;
  186.         }
  187.  
  188.         /*      Initialise the content size
  189.         */
  190.         c->base.width = width;
  191.         c->base.height = height;
  192.  
  193.         /*      Set the canvas style
  194.         */
  195.         if (mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8) != MNG_NOERROR) {
  196.                 LOG(("Error setting canvas style."));
  197.         }
  198.  
  199.         /*      Return success
  200.         */
  201.         return MNG_TRUE;
  202. }
  203.  
  204.  
  205. /*      END OF CALLBACKS REQUIRED FOR READING
  206. */
  207.  
  208.  
  209. static bool nsmng_process_data(struct content *c, const char *data, unsigned int size)
  210. {
  211.         nsmng_content *mng = (nsmng_content *) c;
  212.         mng_retcode status;
  213.  
  214.         assert(c != NULL);
  215.         assert(data != NULL);
  216.  
  217.         /*      We only need to do any processing if we're starting/resuming reading.
  218.         */
  219.         if ((!mng->read_resume) && (!mng->read_start))
  220.                 return true;
  221.  
  222.         /*      Try to start processing, or process some more data
  223.         */
  224.         if (mng->read_start) {
  225.                 status = mng_read(mng->handle);
  226.                 mng->read_start = false;
  227.         } else {
  228.                 status = mng_read_resume(mng->handle);
  229.         }
  230.         mng->read_resume = (status == MNG_NEEDMOREDATA);
  231.         if ((status != MNG_NOERROR) && (status != MNG_NEEDMOREDATA)) {
  232.                 LOG(("Failed to start/continue reading (%i).", status));
  233.                 return nsmng_broadcast_error(mng, status) == NSERROR_OK;
  234.         }
  235.  
  236.         /*      Continue onwards
  237.         */
  238.         return true;
  239. }
  240.  
  241. /*      START OF CALLBACKS REQUIRED FOR DISPLAYING
  242. */
  243.  
  244.  
  245. static mng_ptr nsmng_getcanvasline(mng_handle mng, mng_uint32 line)
  246. {
  247.         nsmng_content *c;
  248.  
  249.         assert(mng != NULL);
  250.  
  251.         /*      Get our content back
  252.         */
  253.         c = (nsmng_content *) mng_get_userdata(mng);
  254.         assert(c != NULL);
  255.  
  256.         /*      Calculate the address
  257.         */
  258.         return bitmap_get_buffer(c->bitmap) +
  259.                         bitmap_get_rowstride(c->bitmap) * line;
  260. }
  261.  
  262. static mng_bool nsmng_refresh(mng_handle mng, mng_uint32 x, mng_uint32 y,
  263.                 mng_uint32 w, mng_uint32 h)
  264. {
  265.         union content_msg_data data;
  266.         nsmng_content *c;
  267.  
  268.         assert(mng != NULL);
  269.  
  270.         /*      Get our content back
  271.         */
  272.         c = (nsmng_content *) mng_get_userdata(mng);
  273.         assert(c != NULL);
  274.  
  275.         /*      Set the minimum redraw area
  276.         */
  277.         data.redraw.x = x;
  278.         data.redraw.y = y;
  279.         data.redraw.width = w;
  280.         data.redraw.height = h;
  281.  
  282.         /*      Set the redraw area to the whole canvas to ensure that if
  283.                 we can redraw something to trigger animation later then we do
  284.         */
  285. /*      data.redraw.x = 0;
  286.         data.redraw.y = 0;
  287.         data.redraw.width = c->width;
  288.         data.redraw.height = c->height;
  289. */
  290.         /*      Always redraw everything
  291.         */
  292.         data.redraw.full_redraw = true;
  293.  
  294.         /*      Set the object characteristics
  295.         */
  296.         data.redraw.object = &c->base;
  297.         data.redraw.object_x = 0;
  298.         data.redraw.object_y = 0;
  299.         data.redraw.object_width = c->base.width;
  300.         data.redraw.object_height = c->base.height;
  301.  
  302.         /* Only attempt to force the redraw if we've been requested to
  303.          * display the image in the first place (i.e. nsmng_redraw has
  304.          * been called). This avoids the situation of forcibly redrawing
  305.          * an image that shouldn't be shown (e.g. if the image is a fallback
  306.          * for an object that can't be rendered)
  307.          */
  308.         if (c->displayed)
  309.                 content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
  310.  
  311.         return MNG_TRUE;
  312. }
  313.  
  314. /**
  315.  * Animates to the next frame
  316.  */
  317. static void nsmng_animate(void *p)
  318. {
  319.         nsmng_content *c;
  320.  
  321.         assert(p != NULL);
  322.  
  323.         c = (nsmng_content *) p;
  324.  
  325.         /*      If we used the last animation we advance, if not we try again later
  326.         */
  327.         if (c->base.user_list->next == NULL) {
  328.                 c->waiting = true;
  329.         } else {
  330.                 c->waiting = false;
  331.                 mng_display_resume(c->handle);
  332.                 c->opaque_test_pending = true;
  333.                 if (c->bitmap)
  334.                         bitmap_modified(c->bitmap);
  335.         }
  336. }
  337.  
  338. static mng_bool nsmng_settimer(mng_handle mng, mng_uint32 msecs)
  339. {
  340.         nsmng_content *c;
  341.  
  342.         assert(mng != NULL);
  343.  
  344.         /*      Get our content back
  345.         */
  346.         c = (nsmng_content *) mng_get_userdata(mng);
  347.         assert(c != NULL);
  348.  
  349.         /*      Perform the scheduling
  350.         */
  351.         schedule(msecs / 10, nsmng_animate, c);
  352.         return MNG_TRUE;
  353. }
  354.  
  355. /**
  356.  * Get the wall-clock time in milliseconds since some fixed time.
  357.  */
  358.  
  359. static mng_uint32 nsmng_gettickcount(mng_handle mng)
  360. {
  361.         static bool start = true;
  362.         static time_t t0;
  363.         struct timeval tv;
  364. #if defined(__SVR4) && defined(__sun) || defined(__NetBSD__) || \
  365.         defined(__APPLE__)
  366.         /* Solaris, NetBSD, and OS X don't have this structure, and ignore the
  367.          * second parameter to gettimeofday()
  368.          */
  369.         int tz;
  370. #else
  371.         struct timezone tz;
  372. #endif
  373.         assert(mng != NULL);
  374.  
  375.         gettimeofday(&tv, &tz);
  376.         if (start) {
  377.                 t0 = tv.tv_sec;
  378.                 start = false;
  379.         }
  380.  
  381.         return (tv.tv_sec - t0) * 1000 + tv.tv_usec / 1000;
  382. }
  383.  
  384. /*      END OF CALLBACKS REQUIRED FOR DISPLAYING
  385. */
  386.  
  387. static mng_bool nsmng_errorproc(mng_handle mng, mng_int32 code,
  388.                 mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq,
  389.                 mng_int32 extra1, mng_int32 extra2, mng_pchar text)
  390. {
  391.         nsmng_content *c;
  392.         char chunk[5];
  393.  
  394.         assert(mng != NULL);
  395.  
  396.         c = (nsmng_content *) mng_get_userdata(mng);
  397.         assert(c != NULL);
  398.  
  399.         chunk[0] = (char)((chunktype >> 24) & 0xFF);
  400.         chunk[1] = (char)((chunktype >> 16) & 0xFF);
  401.         chunk[2] = (char)((chunktype >>  8) & 0xFF);
  402.         chunk[3] = (char)((chunktype      ) & 0xFF);
  403.         chunk[4] = '\0';
  404.  
  405.         LOG(("error playing '%s' chunk %s (%d):",
  406.                         content_get_url(&c->base), chunk, chunkseq));
  407.         LOG(("code %d severity %d extra1 %d extra2 %d text:'%s'", code,
  408.                                         severity, extra1, extra2, text));
  409.  
  410.         return (0);
  411. }
  412.  
  413. static nserror nsmng_create_mng_data(nsmng_content *c)
  414. {
  415.         mng_retcode code;
  416.         union content_msg_data msg_data;
  417.  
  418.         assert(c != NULL);
  419.  
  420.         /*      Initialise the library
  421.         */
  422. #ifdef MNG_INTERNAL_MEMMNGMT
  423.         c->handle = mng_initialize(c, MNG_NULL, MNG_NULL, MNG_NULL);
  424. #else
  425.         c->handle = mng_initialize(c, nsmng_alloc, nsmng_free, MNG_NULL);
  426. #endif
  427.         if (c->handle == MNG_NULL) {
  428.                 LOG(("Unable to initialise MNG library."));
  429.                 msg_data.error = messages_get("NoMemory");
  430.                 content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
  431.                 return NSERROR_NOMEM;
  432.         }
  433.  
  434.         /*      We need to decode in suspension mode
  435.         */
  436.         code = mng_set_suspensionmode(c->handle, MNG_TRUE);
  437.         if (code) {
  438.                 LOG(("Unable to set suspension mode."));
  439.                 return nsmng_broadcast_error(c, code);
  440.         }
  441.  
  442.         /*      We need to register our callbacks
  443.         */
  444.         code = mng_setcb_openstream(c->handle, nsmng_openstream);
  445.         if (code) {
  446.                 LOG(("Unable to set openstream callback."));
  447.                 return nsmng_broadcast_error(c, code);
  448.         }
  449.         code = mng_setcb_readdata(c->handle, nsmng_readdata);
  450.         if (code) {
  451.                 LOG(("Unable to set readdata callback."));
  452.                 return nsmng_broadcast_error(c, code);
  453.         }
  454.         code = mng_setcb_closestream(c->handle, nsmng_closestream);
  455.         if (code) {
  456.                 LOG(("Unable to set closestream callback."));
  457.                 return nsmng_broadcast_error(c, code);
  458.         }
  459.         code = mng_setcb_processheader(c->handle, nsmng_processheader);
  460.         if (code) {
  461.                 LOG(("Unable to set processheader callback."));
  462.                 return nsmng_broadcast_error(c, code);
  463.         }
  464.  
  465.         /*      Register our callbacks for displaying
  466.         */
  467.         code = mng_setcb_getcanvasline(c->handle, nsmng_getcanvasline);
  468.         if (code) {
  469.                 LOG(("Unable to set getcanvasline callback."));
  470.                 return nsmng_broadcast_error(c, code);
  471.         }
  472.         code = mng_setcb_refresh(c->handle, nsmng_refresh);
  473.         if (code) {
  474.                 LOG(("Unable to set refresh callback."));
  475.                 return nsmng_broadcast_error(c, code);
  476.         }
  477.         code = mng_setcb_gettickcount(c->handle, nsmng_gettickcount);
  478.         if (code) {
  479.                 LOG(("Unable to set gettickcount callback."));
  480.                 return nsmng_broadcast_error(c, code);
  481.         }
  482.         code = mng_setcb_settimer(c->handle, nsmng_settimer);
  483.         if (code) {
  484.                 LOG(("Unable to set settimer callback."));
  485.                 return nsmng_broadcast_error(c, code);
  486.         }
  487.  
  488.         /* register error handling function */
  489.         code = mng_setcb_errorproc(c->handle, nsmng_errorproc);
  490.         if (code) {
  491.                 LOG(("Unable to set errorproc"));
  492.                 return nsmng_broadcast_error(c, code);
  493.         }
  494.  
  495.         /*      Initialise the reading
  496.         */
  497.         c->read_start = true;
  498.         c->read_resume = false;
  499.         c->read_size = 0;
  500.         c->waiting = false;
  501.  
  502.         c->displayed = false;
  503.  
  504.         return NSERROR_OK;
  505. }
  506.  
  507. static nserror nsmng_create(const content_handler *handler,
  508.                 lwc_string *imime_type, const struct http_parameter *params,
  509.                 llcache_handle *llcache, const char *fallback_charset,
  510.                 bool quirks, struct content **c)
  511. {
  512.         nsmng_content *mng;
  513.         nserror error;
  514.  
  515.         mng = calloc(1, sizeof(nsmng_content));
  516.         if (mng == NULL)
  517.                 return NSERROR_NOMEM;
  518.  
  519.         error = content__init(&mng->base, handler, imime_type, params,
  520.                         llcache, fallback_charset, quirks);
  521.         if (error != NSERROR_OK) {
  522.                 free(mng);
  523.                 return error;
  524.         }
  525.  
  526.         error = nsmng_create_mng_data(mng);
  527.         if (error != NSERROR_OK) {
  528.                 free(mng);
  529.                 return error;
  530.         }
  531.  
  532.         *c = (struct content *) mng;
  533.  
  534.         return NSERROR_OK;
  535. }
  536.  
  537.  
  538.  
  539. static bool nsmng_convert(struct content *c)
  540. {
  541.         nsmng_content *mng = (nsmng_content *) c;
  542.         mng_retcode status;
  543.         unsigned long size;
  544.         char *title;
  545.  
  546.         assert(c != NULL);
  547.  
  548.         content__get_source_data(c, &size);
  549.  
  550.         /* by this point, the png should have been parsed
  551.          * and the bitmap created, so ensure that's the case
  552.          */
  553.         if (mng->bitmap == NULL) {
  554.                 return nsmng_broadcast_error(mng, -1) == NSERROR_OK;
  555.         }
  556.  
  557.  
  558.         /* set title text */
  559.         title = messages_get_buff("MNGTitle",
  560.                         nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
  561.                         c->width, c->height);
  562.         if (title != NULL) {
  563.                 content__set_title(c, title);
  564.                 free(title);
  565.         }
  566.  
  567.         c->size += c->width * c->height * 4;
  568.         content_set_ready(c);
  569.         content_set_done(c);
  570.         /* Done: update status bar */
  571.         content_set_status(c, "");
  572.  
  573.         /* jmb: I'm really not sure that this should be here.
  574.          * The *_convert functions are for converting a content into a
  575.          * displayable format. They should not, however, do anything which
  576.          * could cause the content to be displayed; the content may have
  577.          * hidden visibility or be a fallback for an object; this
  578.          * information is not available here (nor is there any need for it
  579.          * to be).
  580.          * The specific issue here is that mng_display calls the display
  581.          * callbacks, which include nsmng_refresh. nsmng_refresh forces
  582.          * a content to be redrawn regardless of whether it should be
  583.          * displayed or not.
  584.          */
  585.         /*      Start displaying
  586.         */
  587.         status = mng_display(mng->handle);
  588.         if ((status != MNG_NOERROR) && (status != MNG_NEEDTIMERWAIT)) {
  589.                 LOG(("Unable to start display (%i)", status));
  590.                 return nsmng_broadcast_error(mng, status) == NSERROR_OK;
  591.         }
  592.         bitmap_modified(mng->bitmap);
  593.  
  594.         /* Optimise the plotting of MNG */
  595.         mng->opaque_test_pending = false;
  596.  
  597.         return true;
  598. }
  599.  
  600. static bool nsjpng_convert(struct content *c)
  601. {
  602.         nsmng_content *mng = (nsmng_content *) c;
  603.         mng_retcode status;
  604.         unsigned long size;
  605.         char *title;
  606.         mng_handle handle;
  607.  
  608.         assert(c != NULL);
  609.  
  610.         content__get_source_data(c, &size);
  611.  
  612.         /* by this point, the png should have been parsed
  613.          * and the bitmap created, so ensure that's the case
  614.          */
  615.         if (mng->bitmap == NULL) {
  616.                 return nsmng_broadcast_error(mng, -1) == NSERROR_OK;
  617.         }
  618.  
  619.         /* set title text */
  620.         title = messages_get_buff("PNGTitle",
  621.                         nsurl_access_leaf(llcache_handle_get_url(c->llcache)),
  622.                         c->width, c->height);
  623.         if (title != NULL) {
  624.                 content__set_title(c, title);
  625.                 free(title);
  626.         }
  627.  
  628.         c->size += c->width * c->height * 4;
  629.         content_set_ready(c);
  630.         content_set_done(c);
  631.         /* Done: update status bar */
  632.         content_set_status(c, "");
  633.  
  634.         /* jmb: I'm really not sure that this should be here.
  635.          * The *_convert functions are for converting a content into a
  636.          * displayable format. They should not, however, do anything which
  637.          * could cause the content to be displayed; the content may have
  638.          * hidden visibility or be a fallback for an object; this
  639.          * information is not available here (nor is there any need for it
  640.          * to be).
  641.          * The specific issue here is that mng_display calls the display
  642.          * callbacks, which include nsmng_refresh. nsmng_refresh forces
  643.          * a content to be redrawn regardless of whether it should be
  644.          * displayed or not.
  645.          */
  646.         /*      Start displaying
  647.         */
  648.         status = mng_display(mng->handle);
  649.         if ((status != MNG_NOERROR) && (status != MNG_NEEDTIMERWAIT)) {
  650.                 LOG(("Unable to start display (%i)", status));
  651.                 return nsmng_broadcast_error(mng, status) == NSERROR_OK;
  652.         }
  653.         bitmap_modified(mng->bitmap);
  654.  
  655.         /*      Optimise the plotting of JNG/PNGs
  656.         */
  657.         mng->opaque_test_pending = true;
  658.         bitmap_set_opaque(mng->bitmap, false);
  659.  
  660.         /* free associated memory */
  661.  
  662.         handle = mng->handle;
  663.  
  664.         mng_cleanup(&handle);
  665.  
  666.         mng->handle = NULL;
  667.  
  668.         return true;
  669. }
  670.  
  671. static void nsmng_destroy(struct content *c)
  672. {
  673.         nsmng_content *mng = (nsmng_content *) c;
  674.  
  675.         assert (c != NULL);
  676.  
  677.         /*      Cleanup the MNG structure and release the canvas memory
  678.         */
  679.         schedule_remove(nsmng_animate, c);
  680.  
  681.         if (mng->handle != NULL) {
  682.                 mng_handle handle = mng->handle;
  683.  
  684.                 mng_cleanup(&handle);
  685.  
  686.                 mng->handle = NULL;
  687.         }
  688.  
  689.         if (mng->bitmap) {
  690.                 bitmap_destroy(mng->bitmap);
  691.         }
  692. }
  693.  
  694.  
  695. static bool nsmng_redraw(struct content *c, struct content_redraw_data *data,
  696.                 const struct rect *clip, const struct redraw_context *ctx)
  697. {
  698.         nsmng_content *mng = (nsmng_content *) c;
  699.         bool ret;
  700.         bitmap_flags_t flags = BITMAPF_NONE;
  701.  
  702.         /* mark image as having been requested to display */
  703.         mng->displayed = true;
  704.  
  705.         if ((mng->bitmap) &&
  706.             (mng->opaque_test_pending)) {
  707.                 bitmap_set_opaque(mng->bitmap, bitmap_test_opaque(mng->bitmap));
  708.                 mng->opaque_test_pending = false;
  709.         }
  710.  
  711.         if (data->repeat_x)
  712.                 flags |= BITMAPF_REPEAT_X;
  713.         if (data->repeat_y)
  714.                 flags |= BITMAPF_REPEAT_Y;
  715.  
  716.         ret = ctx->plot->bitmap(data->x, data->y, data->width, data->height,
  717.                         mng->bitmap, data->background_colour, flags);
  718.  
  719.         /* Check if we need to restart the animation */
  720.         if ((mng->waiting) &&
  721.             (nsoption_bool(animate_images))) {
  722.                 nsmng_animate(c);
  723.         }
  724.  
  725.         return ret;
  726. }
  727.  
  728.  
  729. static nserror nsmng_clone(const struct content *old, struct content **newc)
  730. {
  731.         nsmng_content *mng;
  732.         nserror error;
  733.         const char *data;
  734.         unsigned long size;
  735.  
  736.         mng = calloc(1, sizeof(nsmng_content));
  737.         if (mng == NULL)
  738.                 return NSERROR_NOMEM;
  739.  
  740.         error = content__clone(old, &mng->base);
  741.         if (error != NSERROR_OK) {
  742.                 content_destroy(&mng->base);
  743.                 return error;
  744.         }
  745.  
  746.         /* Simply replay create/process/convert */
  747.         error = nsmng_create_mng_data(mng);
  748.         if (error != NSERROR_OK) {
  749.                 content_destroy(&mng->base);
  750.                 return error;
  751.         }
  752.  
  753.         data = content__get_source_data(&mng->base, &size);
  754.         if (size > 0) {
  755.                 if (nsmng_process_data(&mng->base, data, size) == false) {
  756.                         content_destroy(&mng->base);
  757.                         return NSERROR_CLONE_FAILED;
  758.                 }
  759.         }
  760.  
  761.         if (old->status == CONTENT_STATUS_READY ||
  762.                         old->status == CONTENT_STATUS_DONE) {
  763.                 if (nsmng_convert(&mng->base) == false) {
  764.                         content_destroy(&mng->base);
  765.                         return NSERROR_CLONE_FAILED;
  766.                 }
  767.         }
  768.  
  769.         *newc = (struct content *) mng;
  770.  
  771.         return NSERROR_OK;
  772. }
  773.  
  774. static void *nsmng_get_internal(const struct content *c, void *context)
  775. {
  776.         nsmng_content *mng = (nsmng_content *)c;
  777.  
  778.         return mng->bitmap;
  779. }
  780.  
  781. static content_type nsmng_content_type(void)
  782. {
  783.         return CONTENT_IMAGE;
  784. }
  785.  
  786. /* register handler for mng types */
  787. static const content_handler nsmng_content_handler = {
  788.         .create = nsmng_create,
  789.         .process_data = nsmng_process_data,
  790.         .data_complete = nsmng_convert,
  791.         .destroy = nsmng_destroy,
  792.         .redraw = nsmng_redraw,
  793.         .clone = nsmng_clone,
  794.         .get_internal = nsmng_get_internal,
  795.         .type = nsmng_content_type,
  796.         .no_share = false,
  797. };
  798.  
  799. static const char *nsmng_types[] = {
  800.         /* MNG types*/
  801.         "image/mng",
  802.         "image/x-mng",
  803.         "video/mng",
  804.         "video/x-mng",
  805. };
  806.  
  807. CONTENT_FACTORY_REGISTER_TYPES(nsmng, nsmng_types, nsmng_content_handler);
  808.  
  809. /* register handler for jng and png types */
  810. static const content_handler nsjpng_content_handler = {
  811.         .create = nsmng_create,
  812.         .process_data = nsmng_process_data,
  813.         .data_complete = nsjpng_convert,
  814.         .destroy = nsmng_destroy,
  815.         .redraw = nsmng_redraw,
  816.         .clone = nsmng_clone,
  817.         .get_internal = nsmng_get_internal,
  818.         .type = nsmng_content_type,
  819.         .no_share = false,
  820. };
  821.  
  822.  
  823. static const char *nsjpng_types[] = {
  824.         /* JNG types*/
  825.         "image/jng",
  826.         "image/x-jng",
  827.         /* PNG types*/
  828.         "image/png",
  829.         "image/x-png"
  830. };
  831.  
  832. CONTENT_FACTORY_REGISTER_TYPES(nsjpng, nsjpng_types, nsjpng_content_handler);
  833.