Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2006 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.  * Knockout rendering (implementation).
  21.  *
  22.  * Knockout rendering is an optimisation which is particularly for
  23.  * unaccelerated screen redraw. It tries to avoid plotting the same area more
  24.  * than once.
  25.  *
  26.  * If the object is to plot two overlapping rectangles (one large, one small),
  27.  * such as:
  28.  *
  29.  *   +-----------------+
  30.  *   |#################|
  31.  *   |####+-------+####|
  32.  *   |####|:::::::|####|
  33.  *   |####|:::::::|####|
  34.  *   |####|:::::::|####|
  35.  *   |####+-------+####|
  36.  *   |#################|
  37.  *   +-----------------+
  38.  *
  39.  * Without knockout rendering we plot the bottom rectangle and then the top one:
  40.  *
  41.  *   +-----------------+                 +-----------------+
  42.  *   |#################|                 |#################|
  43.  *   |#################|                 |####+-------+####|
  44.  *   |#################|                 |####|:::::::|####|
  45.  *   |#################|    and then,    |####|:::::::|####|
  46.  *   |#################|                 |####|:::::::|####|
  47.  *   |#################|                 |####+-------+####|
  48.  *   |#################|                 |#################|
  49.  *   +-----------------+                 +-----------------+
  50.  *
  51.  * With knockout rendering, the bottom rectangle is split up into smaller
  52.  * ones and each pixel is just plotted once:
  53.  *
  54.  *   +-----------------+
  55.  *   |#################|
  56.  *   +----+-------+----+
  57.  *   |####|:::::::|####|
  58.  *   |####|:::::::|####|
  59.  *   |####|:::::::|####|
  60.  *   +----+-------+----+
  61.  *   |#################|
  62.  *   +-----------------+
  63.  */
  64.  
  65. #include <assert.h>
  66. #include <string.h>
  67. #include "desktop/knockout.h"
  68. #include "desktop/plotters.h"
  69. #include "image/bitmap.h"
  70. #include "utils/log.h"
  71.  
  72. /* Define to enable knockout debug */
  73. #undef KNOCKOUT_DEBUG
  74.  
  75. #define KNOCKOUT_ENTRIES 3072   /* 40 bytes each */
  76. #define KNOCKOUT_BOXES 768      /* 28 bytes each */
  77. #define KNOCKOUT_POLYGONS 3072  /* 4 bytes each */
  78.  
  79. struct knockout_box;
  80. struct knockout_entry;
  81.  
  82.  
  83. static void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *box);
  84. static bool knockout_plot_fill_recursive(struct knockout_box *box, plot_style_t *plot_style);
  85. static bool knockout_plot_bitmap_recursive(struct knockout_box *box,
  86.                 struct knockout_entry *entry);
  87.  
  88. static bool knockout_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle);
  89. static bool knockout_plot_polygon(const int *p, unsigned int n, const plot_style_t *pstyle);
  90. static bool knockout_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *plot_style);
  91. static bool knockout_plot_clip(const struct rect *clip);
  92. static bool knockout_plot_text(int x, int y, const char *text, size_t length,
  93.                 const plot_font_style_t *fstyle);
  94. static bool knockout_plot_disc(int x, int y, int radius, const plot_style_t *pstyle);
  95. static bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *pstyle);
  96. static bool knockout_plot_bitmap(int x, int y, int width, int height,
  97.                 struct bitmap *bitmap, colour bg,
  98.                 bitmap_flags_t flags);
  99. static bool knockout_plot_flush(void);
  100. static bool knockout_plot_group_start(const char *name);
  101. static bool knockout_plot_group_end(void);
  102. static bool knockout_plot_path(const float *p, unsigned int n, colour fill,
  103.                 float width, colour c, const float transform[6]);
  104.  
  105.  
  106. const struct plotter_table knockout_plotters = {
  107.         .rectangle = knockout_plot_rectangle,
  108.         .line = knockout_plot_line,
  109.         .polygon = knockout_plot_polygon,
  110.         .clip = knockout_plot_clip,
  111.         .text = knockout_plot_text,
  112.         .disc = knockout_plot_disc,
  113.         .arc = knockout_plot_arc,
  114.         .bitmap = knockout_plot_bitmap,
  115.         .group_start = knockout_plot_group_start,
  116.         .group_end = knockout_plot_group_end,
  117.         .flush = knockout_plot_flush,
  118.         .path = knockout_plot_path,
  119.         .option_knockout = true,
  120. };
  121.  
  122.  
  123. typedef enum {
  124.         KNOCKOUT_PLOT_RECTANGLE,
  125.         KNOCKOUT_PLOT_LINE,
  126.         KNOCKOUT_PLOT_POLYGON,
  127.         KNOCKOUT_PLOT_FILL,             /* knockout, knocked out */
  128.         KNOCKOUT_PLOT_CLIP,
  129.         KNOCKOUT_PLOT_TEXT,
  130.         KNOCKOUT_PLOT_DISC,
  131.         KNOCKOUT_PLOT_ARC,
  132.         KNOCKOUT_PLOT_BITMAP,           /* knockout, knocked out */
  133.         KNOCKOUT_PLOT_GROUP_START,
  134.         KNOCKOUT_PLOT_GROUP_END,
  135. } knockout_type;
  136.  
  137.  
  138. struct knockout_box {
  139.         struct rect bbox;
  140.         bool deleted;                   /* box has been deleted, ignore */
  141.         struct knockout_box *child;
  142.         struct knockout_box *next;
  143. };
  144.  
  145.  
  146. struct knockout_entry {
  147.         knockout_type type;
  148.         struct knockout_box *box;       /* relating series of knockout clips */
  149.         union {
  150.                 struct {
  151.                         int x0;
  152.                         int y0;
  153.                         int x1;
  154.                         int y1;
  155.                         plot_style_t plot_style;
  156.                 } rectangle;
  157.                 struct {
  158.                         int x0;
  159.                         int y0;
  160.                         int x1;
  161.                         int y1;
  162.                         plot_style_t plot_style;
  163.                 } line;
  164.                 struct {
  165.                         int *p;
  166.                         unsigned int n;
  167.                         plot_style_t plot_style;
  168.                 } polygon;
  169.                 struct {
  170.                         int x0;
  171.                         int y0;
  172.                         int x1;
  173.                         int y1;
  174.                         plot_style_t plot_style;
  175.                 } fill;
  176.                 struct rect clip;
  177.                 struct {
  178.                         int x;
  179.                         int y;
  180.                         const char *text;
  181.                         size_t length;
  182.                         plot_font_style_t font_style;
  183.                 } text;
  184.                 struct {
  185.                         int x;
  186.                         int y;
  187.                         int radius;
  188.                         plot_style_t plot_style;
  189.                 } disc;
  190.                 struct {
  191.                         int x;
  192.                         int y;
  193.                         int radius;
  194.                         int angle1;
  195.                         int angle2;
  196.                         plot_style_t plot_style;
  197.                 } arc;
  198.                 struct {
  199.                         int x;
  200.                         int y;
  201.                         int width;
  202.                         int height;
  203.                         struct bitmap *bitmap;
  204.                         colour bg;
  205.                         bitmap_flags_t flags;
  206.                 } bitmap;
  207.                 struct {
  208.                         const char *name;
  209.                 } group_start;
  210.         } data;
  211. };
  212.  
  213.  
  214. static struct knockout_entry knockout_entries[KNOCKOUT_ENTRIES];
  215. static struct knockout_box knockout_boxes[KNOCKOUT_BOXES];
  216. static int knockout_polygons[KNOCKOUT_POLYGONS];
  217. static int knockout_entry_cur = 0;
  218. static int knockout_box_cur = 0;
  219. static int knockout_polygon_cur = 0;
  220. static struct knockout_box *knockout_list = NULL;
  221.  
  222. static struct plotter_table real_plot;
  223.  
  224. static struct rect clip_cur;
  225. static int nested_depth = 0;
  226.  
  227. /**
  228.  * Start a knockout plotting session
  229.  *
  230.  * \param ctx           the redraw context with real plotter table
  231.  * \param knk_ctx       updated to copy of ctx, with plotter table replaced
  232.  * \return  true on success, false otherwise
  233.  */
  234. bool knockout_plot_start(const struct redraw_context *ctx,
  235.                 struct redraw_context *knk_ctx)
  236. {
  237.         /* check if we're recursing */
  238.         if (nested_depth++ > 0) {
  239.                 /* we should already have the knockout renderer as default */
  240.                 assert(ctx->plot->rectangle == knockout_plotters.rectangle);
  241.                 *knk_ctx = *ctx;
  242.                 return true;
  243.         }
  244.  
  245.         /* end any previous sessions */
  246.         if (knockout_entry_cur > 0)
  247.                 knockout_plot_end();
  248.  
  249.         /* get copy of real plotter table */
  250.         real_plot = *(ctx->plot);
  251.  
  252.         /* set up knockout rendering context */
  253.         *knk_ctx = *ctx;
  254.         knk_ctx->plot = &knockout_plotters;
  255.         return true;
  256. }
  257.  
  258.  
  259. /**
  260.  * End a knockout plotting session
  261.  *
  262.  * \return  true on success, false otherwise
  263.  */
  264. bool knockout_plot_end(void)
  265. {
  266.         /* only output when we've finished any nesting */
  267.         if (--nested_depth == 0)
  268.                 return knockout_plot_flush();
  269.  
  270.         assert(nested_depth > 0);
  271.         return true;
  272. }
  273.  
  274.  
  275. /**
  276.  * Flush the current knockout session to empty the buffers
  277.  *
  278.  * \return  true on success, false otherwise
  279.  */
  280. bool knockout_plot_flush(void)
  281. {
  282.         int i;
  283.         bool success = true;
  284.         struct knockout_box *box;
  285.  
  286.         /* debugging information */
  287. #ifdef KNOCKOUT_DEBUG
  288.         LOG(("Entries are %i/%i, %i/%i, %i/%i",
  289.                         knockout_entry_cur, KNOCKOUT_ENTRIES,
  290.                         knockout_box_cur, KNOCKOUT_BOXES,
  291.                         knockout_polygon_cur, KNOCKOUT_POLYGONS));
  292. #endif
  293.  
  294.         for (i = 0; i < knockout_entry_cur; i++) {
  295.                 switch (knockout_entries[i].type) {
  296.                 case KNOCKOUT_PLOT_RECTANGLE:
  297.                         success &= real_plot.rectangle(
  298.                                 knockout_entries[i].data.rectangle.x0,
  299.                                 knockout_entries[i].data.rectangle.y0,
  300.                                 knockout_entries[i].data.rectangle.x1,
  301.                                 knockout_entries[i].data.rectangle.y1,
  302.                                 &knockout_entries[i].data.rectangle.plot_style);
  303.                         break;
  304.                 case KNOCKOUT_PLOT_LINE:
  305.                         success &= real_plot.line(
  306.                                         knockout_entries[i].data.line.x0,
  307.                                         knockout_entries[i].data.line.y0,
  308.                                         knockout_entries[i].data.line.x1,
  309.                                         knockout_entries[i].data.line.y1,
  310.                                         &knockout_entries[i].data.line.plot_style);
  311.                         break;
  312.                 case KNOCKOUT_PLOT_POLYGON:
  313.                         success &= real_plot.polygon(
  314.                                         knockout_entries[i].data.polygon.p,
  315.                                         knockout_entries[i].data.polygon.n,
  316.                                         &knockout_entries[i].data.polygon.plot_style);
  317.                         break;
  318.                 case KNOCKOUT_PLOT_FILL:
  319.                         box = knockout_entries[i].box->child;
  320.                         if (box)
  321.                                 success &= knockout_plot_fill_recursive(box,
  322.                                                 &knockout_entries[i].data.fill.plot_style);
  323.                         else if (!knockout_entries[i].box->deleted)
  324.                                 success &= real_plot.rectangle(
  325.                                                 knockout_entries[i].data.fill.x0,
  326.                                                 knockout_entries[i].data.fill.y0,
  327.                                                 knockout_entries[i].data.fill.x1,
  328.                                                 knockout_entries[i].data.fill.y1,
  329.                                                 &knockout_entries[i].data.fill.plot_style);
  330.                         break;
  331.                 case KNOCKOUT_PLOT_CLIP:
  332.                         success &= real_plot.clip(
  333.                                         &knockout_entries[i].data.clip);
  334.                         break;
  335.                 case KNOCKOUT_PLOT_TEXT:
  336.                         success &= real_plot.text(
  337.                                         knockout_entries[i].data.text.x,
  338.                                         knockout_entries[i].data.text.y,
  339.                                         knockout_entries[i].data.text.text,
  340.                                         knockout_entries[i].data.text.length,
  341.                                         &knockout_entries[i].data.text.font_style);
  342.                         break;
  343.                 case KNOCKOUT_PLOT_DISC:
  344.                         success &= real_plot.disc(
  345.                                         knockout_entries[i].data.disc.x,
  346.                                         knockout_entries[i].data.disc.y,
  347.                                         knockout_entries[i].data.disc.radius,
  348.                                         &knockout_entries[i].data.disc.plot_style);
  349.                         break;
  350.                 case KNOCKOUT_PLOT_ARC:
  351.                         success &= real_plot.arc(
  352.                                         knockout_entries[i].data.arc.x,
  353.                                         knockout_entries[i].data.arc.y,
  354.                                         knockout_entries[i].data.arc.radius,
  355.                                         knockout_entries[i].data.arc.angle1,
  356.                                         knockout_entries[i].data.arc.angle2,
  357.                                         &knockout_entries[i].data.arc.plot_style);
  358.                         break;
  359.                 case KNOCKOUT_PLOT_BITMAP:
  360.                         box = knockout_entries[i].box->child;
  361.                         if (box) {
  362.                                 success &= knockout_plot_bitmap_recursive(box,
  363.                                                 &knockout_entries[i]);
  364.                         } else if (!knockout_entries[i].box->deleted) {
  365.                                 success &= real_plot.bitmap(
  366.                                                 knockout_entries[i].data.
  367.                                                                 bitmap.x,
  368.                                                 knockout_entries[i].data.
  369.                                                                 bitmap.y,
  370.                                                 knockout_entries[i].data.
  371.                                                                 bitmap.width,
  372.                                                 knockout_entries[i].data.
  373.                                                                 bitmap.height,
  374.                                                 knockout_entries[i].data.
  375.                                                                 bitmap.bitmap,
  376.                                                 knockout_entries[i].data.
  377.                                                                 bitmap.bg,
  378.                                                 knockout_entries[i].data.
  379.                                                                 bitmap.flags);
  380.                         }
  381.                         break;
  382.                 case KNOCKOUT_PLOT_GROUP_START:
  383.                         success &= real_plot.group_start(
  384.                                         knockout_entries[i].data.group_start.name);
  385.                         break;
  386.                 case KNOCKOUT_PLOT_GROUP_END:
  387.                         success &= real_plot.group_end();
  388.                         break;
  389.                 }
  390.         }
  391.  
  392.         knockout_entry_cur = 0;
  393.         knockout_box_cur = 0;
  394.         knockout_polygon_cur = 0;
  395.         knockout_list = NULL;
  396.  
  397.         return success;
  398. }
  399.  
  400.  
  401. /**
  402.  * Knockout a section of previous rendering
  403.  *
  404.  * \param  x0   the left edge of the removal box
  405.  * \param  y0   the bottom edge of the removal box
  406.  * \param  x1   the right edge of the removal box
  407.  * \param  y1   the top edge of the removal box
  408.  * \param  box  the parent box set to consider, or NULL for top level
  409. */
  410. void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *owner)
  411. {
  412.         struct knockout_box *box;
  413.         struct knockout_box *parent;
  414.         struct knockout_box *prev = NULL;
  415.         int nx0, ny0, nx1, ny1;
  416.  
  417.         if (owner == NULL)
  418.                 box = knockout_list;
  419.         else
  420.                 box = owner->child;
  421.  
  422.         for (parent = box; parent; parent = parent->next) {
  423.                 /* permanently delink deleted nodes */
  424.                 if (parent->deleted) {
  425.                         if (prev) {
  426.                                 /* not the first valid element: just skip future */
  427.                                 prev->next = parent->next;
  428.                         } else {
  429.                                 if (owner) {
  430.                                         /* first valid element: update child reference */
  431.                                         owner->child = parent->next;
  432.                                         /* have we deleted all child nodes? */
  433.                                         if (!owner->child)
  434.                                                 owner->deleted = true;
  435.                                 } else {
  436.                                         /* we are the head of the list */
  437.                                         knockout_list = parent->next;
  438.                                 }
  439.                         }
  440.                         continue;
  441.                 } else {
  442.                         prev = parent;
  443.                 }
  444.  
  445.                 /* get the parent dimensions */
  446.                 nx0 = parent->bbox.x0;
  447.                 ny0 = parent->bbox.y0;
  448.                 nx1 = parent->bbox.x1;
  449.                 ny1 = parent->bbox.y1;
  450.  
  451.                 /* reject non-overlapping boxes */
  452.                 if ((nx0 >= x1) || (nx1 <= x0) || (ny0 >= y1) || (ny1 <= y0))
  453.                         continue;
  454.  
  455.                 /* check for a total knockout */
  456.                 if ((x0 <= nx0) && (x1 >= nx1) && (y0 <= ny0) && (y1 >= ny1)) {
  457.                         parent->deleted = true;
  458.                         continue;
  459.                 }
  460.  
  461.                 /* has the box been replaced by children? */
  462.                 if (parent->child) {
  463.                         knockout_calculate(x0, y0, x1, y1, parent);
  464.                 } else {
  465.                         /* we need a maximum of 4 child boxes */
  466.                         if (knockout_box_cur + 4 >= KNOCKOUT_BOXES) {
  467.                                 knockout_plot_flush();
  468.                                 return;
  469.                         }
  470.  
  471.                         /* clip top */
  472.                         if (y1 < ny1) {
  473.                                 knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
  474.                                 knockout_boxes[knockout_box_cur].bbox.y0 = y1;
  475.                                 knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
  476.                                 knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
  477.                                 knockout_boxes[knockout_box_cur].deleted = false;
  478.                                 knockout_boxes[knockout_box_cur].child = NULL;
  479.                                 knockout_boxes[knockout_box_cur].next = parent->child;
  480.                                 parent->child = &knockout_boxes[knockout_box_cur++];
  481.                                 ny1 = y1;
  482.                         }
  483.                         /* clip bottom */
  484.                         if (y0 > ny0) {
  485.                                 knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
  486.                                 knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
  487.                                 knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
  488.                                 knockout_boxes[knockout_box_cur].bbox.y1 = y0;
  489.                                 knockout_boxes[knockout_box_cur].deleted = false;
  490.                                 knockout_boxes[knockout_box_cur].child = NULL;
  491.                                 knockout_boxes[knockout_box_cur].next = parent->child;
  492.                                 parent->child = &knockout_boxes[knockout_box_cur++];
  493.                                 ny0 = y0;
  494.                         }
  495.                         /* clip right */
  496.                         if (x1 < nx1) {
  497.                                 knockout_boxes[knockout_box_cur].bbox.x0 = x1;
  498.                                 knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
  499.                                 knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
  500.                                 knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
  501.                                 knockout_boxes[knockout_box_cur].deleted = false;
  502.                                 knockout_boxes[knockout_box_cur].child = NULL;
  503.                                 knockout_boxes[knockout_box_cur].next = parent->child;
  504.                                 parent->child = &knockout_boxes[knockout_box_cur++];
  505.                                 /* nx1 isn't used again, but if it was it would
  506.                                  * need to be updated to x1 here. */
  507.                         }
  508.                         /* clip left */
  509.                         if (x0 > nx0) {
  510.                                 knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
  511.                                 knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
  512.                                 knockout_boxes[knockout_box_cur].bbox.x1 = x0;
  513.                                 knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
  514.                                 knockout_boxes[knockout_box_cur].deleted = false;
  515.                                 knockout_boxes[knockout_box_cur].child = NULL;
  516.                                 knockout_boxes[knockout_box_cur].next = parent->child;
  517.                                 parent->child = &knockout_boxes[knockout_box_cur++];
  518.                                 /* nx0 isn't used again, but if it was it would
  519.                                  * need to be updated to x0 here. */
  520.                         }
  521.                 }
  522.         }
  523. }
  524.  
  525.  
  526. bool knockout_plot_fill_recursive(struct knockout_box *box, plot_style_t *plot_style)
  527. {
  528.         bool success = true;
  529.         struct knockout_box *parent;
  530.  
  531.         for (parent = box; parent; parent = parent->next) {
  532.                 if (parent->deleted)
  533.                         continue;
  534.                 if (parent->child)
  535.                         knockout_plot_fill_recursive(parent->child, plot_style);
  536.                 else
  537.                         success &= real_plot.rectangle(parent->bbox.x0,
  538.                                                   parent->bbox.y0,
  539.                                                   parent->bbox.x1,
  540.                                                   parent->bbox.y1,
  541.                                                   plot_style);
  542.         }
  543.         return success;
  544. }
  545.  
  546.  
  547. bool knockout_plot_bitmap_recursive(struct knockout_box *box,
  548.                 struct knockout_entry *entry)
  549. {
  550.         bool success = true;
  551.         struct knockout_box *parent;
  552.  
  553.         for (parent = box; parent; parent = parent->next) {
  554.                 if (parent->deleted)
  555.                         continue;
  556.                 if (parent->child)
  557.                         knockout_plot_bitmap_recursive(parent->child, entry);
  558.                 else {
  559.                         success &= real_plot.clip(&parent->bbox);
  560.                         success &= real_plot.bitmap(entry->data.bitmap.x,
  561.                                         entry->data.bitmap.y,
  562.                                         entry->data.bitmap.width,
  563.                                         entry->data.bitmap.height,
  564.                                         entry->data.bitmap.bitmap,
  565.                                         entry->data.bitmap.bg,
  566.                                         entry->data.bitmap.flags);
  567.                 }
  568.         }
  569.         return success;
  570. }
  571.  
  572. bool knockout_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
  573. {
  574.         int kx0, ky0, kx1, ky1;
  575.  
  576.         if (pstyle->fill_type != PLOT_OP_TYPE_NONE) {
  577.                 /* filled draw */
  578.  
  579.                 /* get our bounds */
  580.                 kx0 = (x0 > clip_cur.x0) ? x0 : clip_cur.x0;
  581.                 ky0 = (y0 > clip_cur.y0) ? y0 : clip_cur.y0;
  582.                 kx1 = (x1 < clip_cur.x1) ? x1 : clip_cur.x1;
  583.                 ky1 = (y1 < clip_cur.y1) ? y1 : clip_cur.y1;
  584.                 if ((kx0 > clip_cur.x1) || (kx1 < clip_cur.x0) ||
  585.                     (ky0 > clip_cur.y1) || (ky1 < clip_cur.y0))
  586.                         return true;
  587.  
  588.                 /* fills both knock out and get knocked out */
  589.                 knockout_calculate(kx0, ky0, kx1, ky1, NULL);
  590.                 knockout_boxes[knockout_box_cur].bbox.x0 = x0;
  591.                 knockout_boxes[knockout_box_cur].bbox.y0 = y0;
  592.                 knockout_boxes[knockout_box_cur].bbox.x1 = x1;
  593.                 knockout_boxes[knockout_box_cur].bbox.y1 = y1;
  594.                 knockout_boxes[knockout_box_cur].deleted = false;
  595.                 knockout_boxes[knockout_box_cur].child = NULL;
  596.                 knockout_boxes[knockout_box_cur].next = knockout_list;
  597.                 knockout_list = &knockout_boxes[knockout_box_cur];
  598.                 knockout_entries[knockout_entry_cur].box = &knockout_boxes[knockout_box_cur];
  599.                 knockout_entries[knockout_entry_cur].data.fill.x0 = x0;
  600.                 knockout_entries[knockout_entry_cur].data.fill.y0 = y0;
  601.                 knockout_entries[knockout_entry_cur].data.fill.x1 = x1;
  602.                 knockout_entries[knockout_entry_cur].data.fill.y1 = y1;
  603.                 knockout_entries[knockout_entry_cur].data.fill.plot_style = *pstyle;
  604.                 knockout_entries[knockout_entry_cur].data.fill.plot_style.stroke_type = PLOT_OP_TYPE_NONE; /* ensure we only plot the fill */
  605.                 knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_FILL;
  606.                 if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) ||
  607.                     (++knockout_box_cur >= KNOCKOUT_BOXES))
  608.                         knockout_plot_flush();
  609.         }
  610.  
  611.         if (pstyle->stroke_type != PLOT_OP_TYPE_NONE) {
  612.                 /* draw outline */
  613.  
  614.                 knockout_entries[knockout_entry_cur].data.rectangle.x0 = x0;
  615.                 knockout_entries[knockout_entry_cur].data.rectangle.y0 = y0;
  616.                 knockout_entries[knockout_entry_cur].data.rectangle.x1 = x1;
  617.                 knockout_entries[knockout_entry_cur].data.rectangle.y1 = y1;
  618.                 knockout_entries[knockout_entry_cur].data.fill.plot_style = *pstyle;
  619.                 knockout_entries[knockout_entry_cur].data.fill.plot_style.fill_type = PLOT_OP_TYPE_NONE; /* ensure we only plot the outline */
  620.                 knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_RECTANGLE;
  621.                 if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  622.                         knockout_plot_flush();
  623.         }
  624.         return true;
  625. }
  626.  
  627. bool knockout_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
  628. {
  629.         knockout_entries[knockout_entry_cur].data.line.x0 = x0;
  630.         knockout_entries[knockout_entry_cur].data.line.y0 = y0;
  631.         knockout_entries[knockout_entry_cur].data.line.x1 = x1;
  632.         knockout_entries[knockout_entry_cur].data.line.y1 = y1;
  633.         knockout_entries[knockout_entry_cur].data.line.plot_style = *pstyle;
  634.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_LINE;
  635.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  636.                 knockout_plot_flush();
  637.         return true;
  638. }
  639.  
  640.  
  641. bool knockout_plot_polygon(const int *p, unsigned int n, const plot_style_t *pstyle)
  642. {
  643.         bool success = true;
  644.         int *dest;
  645.  
  646.         /* ensure we have sufficient room even when flushed */
  647.         if (n * 2 >= KNOCKOUT_POLYGONS) {
  648.                 knockout_plot_flush();
  649.                 success = real_plot.polygon(p, n, pstyle);
  650.                 return success;
  651.         }
  652.  
  653.         /* ensure we have enough room right now */
  654.         if (knockout_polygon_cur + n * 2 >= KNOCKOUT_POLYGONS)
  655.                 knockout_plot_flush();
  656.  
  657.         /* copy our data */
  658.         dest = &(knockout_polygons[knockout_polygon_cur]);
  659.         memcpy(dest, p, n * 2 * sizeof(int));
  660.         knockout_polygon_cur += n * 2;
  661.         knockout_entries[knockout_entry_cur].data.polygon.p = dest;
  662.         knockout_entries[knockout_entry_cur].data.polygon.n = n;
  663.         knockout_entries[knockout_entry_cur].data.polygon.plot_style = *pstyle;
  664.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_POLYGON;
  665.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  666.                 knockout_plot_flush();
  667.         return true;
  668. }
  669.  
  670.  
  671. bool knockout_plot_path(const float *p, unsigned int n, colour fill,
  672.                 float width, colour c, const float transform[6])
  673. {
  674.         knockout_plot_flush();
  675.         return real_plot.path(p, n, fill, width, c, transform);
  676. }
  677.  
  678.  
  679. bool knockout_plot_clip(const struct rect *clip)
  680. {
  681.         if (clip->x1 < clip->x0 || clip->y0 > clip->y1) {
  682. #ifdef KNOCKOUT_DEBUG
  683.                 LOG(("bad clip rectangle %i %i %i %i",
  684.                                 clip->x0, clip->y0, clip->x1, clip->y1));
  685. #endif
  686.                 return false;
  687.         }
  688.  
  689.         /* memorise clip for bitmap tiling */
  690.         clip_cur = *clip;
  691.  
  692.         knockout_entries[knockout_entry_cur].data.clip = *clip;
  693.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_CLIP;
  694.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  695.                 knockout_plot_flush();
  696.         return true;
  697. }
  698.  
  699.  
  700. bool knockout_plot_text(int x, int y, const char *text, size_t length,
  701.                 const plot_font_style_t *fstyle)
  702. {
  703.         knockout_entries[knockout_entry_cur].data.text.x = x;
  704.         knockout_entries[knockout_entry_cur].data.text.y = y;
  705.         knockout_entries[knockout_entry_cur].data.text.text = text;
  706.         knockout_entries[knockout_entry_cur].data.text.length = length;
  707.         knockout_entries[knockout_entry_cur].data.text.font_style = *fstyle;
  708.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_TEXT;
  709.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  710.                 knockout_plot_flush();
  711.         return true;
  712. }
  713.  
  714.  
  715. bool knockout_plot_disc(int x, int y, int radius, const plot_style_t *pstyle)
  716. {
  717.         knockout_entries[knockout_entry_cur].data.disc.x = x;
  718.         knockout_entries[knockout_entry_cur].data.disc.y = y;
  719.         knockout_entries[knockout_entry_cur].data.disc.radius = radius;
  720.         knockout_entries[knockout_entry_cur].data.disc.plot_style = *pstyle;
  721.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_DISC;
  722.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  723.                 knockout_plot_flush();
  724.         return true;
  725. }
  726.  
  727. bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *pstyle)
  728. {
  729.         knockout_entries[knockout_entry_cur].data.arc.x = x;
  730.         knockout_entries[knockout_entry_cur].data.arc.y = y;
  731.         knockout_entries[knockout_entry_cur].data.arc.radius = radius;
  732.         knockout_entries[knockout_entry_cur].data.arc.angle1 = angle1;
  733.         knockout_entries[knockout_entry_cur].data.arc.angle2 = angle2;
  734.         knockout_entries[knockout_entry_cur].data.arc.plot_style = *pstyle;
  735.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_ARC;
  736.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  737.                 knockout_plot_flush();
  738.         return true;
  739. }
  740.  
  741.  
  742.  
  743. bool knockout_plot_bitmap(int x, int y, int width, int height,
  744.                 struct bitmap *bitmap, colour bg,
  745.                 bitmap_flags_t flags)
  746. {
  747.         int kx0, ky0, kx1, ky1;
  748.  
  749.         /* get our bounds */
  750.         kx0 = clip_cur.x0;
  751.         ky0 = clip_cur.y0;
  752.         kx1 = clip_cur.x1;
  753.         ky1 = clip_cur.y1;
  754.         if (!(flags & BITMAPF_REPEAT_X)) {
  755.                 if (x > kx0)
  756.                         kx0 = x;
  757.                 if (x + width < kx1)
  758.                         kx1 = x + width;
  759.                 if ((kx0 > clip_cur.x1) || (kx1 < clip_cur.x0))
  760.                         return true;
  761.         }
  762.         if (!(flags & BITMAPF_REPEAT_Y)) {
  763.                 if (y > ky0)
  764.                         ky0 = y;
  765.                 if (y + height < ky1)
  766.                         ky1 = y + height;
  767.                 if ((ky0 > clip_cur.y1) || (ky1 < clip_cur.y0))
  768.                         return true;
  769.         }
  770.  
  771.         /* tiled bitmaps both knock out and get knocked out */
  772.         if (bitmap_get_opaque(bitmap))
  773.                 knockout_calculate(kx0, ky0, kx1, ky1, NULL);
  774.         knockout_boxes[knockout_box_cur].bbox.x0 = kx0;
  775.         knockout_boxes[knockout_box_cur].bbox.y0 = ky0;
  776.         knockout_boxes[knockout_box_cur].bbox.x1 = kx1;
  777.         knockout_boxes[knockout_box_cur].bbox.y1 = ky1;
  778.         knockout_boxes[knockout_box_cur].deleted = false;
  779.         knockout_boxes[knockout_box_cur].child = NULL;
  780.         knockout_boxes[knockout_box_cur].next = knockout_list;
  781.         knockout_list = &knockout_boxes[knockout_box_cur];
  782.         knockout_entries[knockout_entry_cur].box = &knockout_boxes[knockout_box_cur];
  783.         knockout_entries[knockout_entry_cur].data.bitmap.x = x;
  784.         knockout_entries[knockout_entry_cur].data.bitmap.y = y;
  785.         knockout_entries[knockout_entry_cur].data.bitmap.width = width;
  786.         knockout_entries[knockout_entry_cur].data.bitmap.height = height;
  787.         knockout_entries[knockout_entry_cur].data.bitmap.bitmap = bitmap;
  788.         knockout_entries[knockout_entry_cur].data.bitmap.bg = bg;
  789.         knockout_entries[knockout_entry_cur].data.bitmap.flags = flags;
  790.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP;
  791.         if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) ||
  792.                         (++knockout_box_cur >= KNOCKOUT_BOXES))
  793.                 knockout_plot_flush();
  794.         return knockout_plot_clip(&clip_cur);
  795. }
  796.  
  797. bool knockout_plot_group_start(const char *name)
  798. {
  799.         if (real_plot.group_start == NULL) {
  800.                 return true;
  801.         }
  802.  
  803.         knockout_entries[knockout_entry_cur].data.group_start.name = name;
  804.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_START;
  805.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  806.                 knockout_plot_flush();
  807.         return true;
  808. }
  809.  
  810. bool knockout_plot_group_end(void)
  811. {
  812.         if (real_plot.group_end == NULL) {
  813.                 return true;
  814.         }
  815.  
  816.         knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_END;
  817.         if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
  818.                 knockout_plot_flush();
  819.         return true;
  820. }
  821.