summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Wilson <rjw@netsurf-browser.org>2006-07-01 18:16:05 +0000
committerRichard Wilson <rjw@netsurf-browser.org>2006-07-01 18:16:05 +0000
commitcf4294d3a85518a6502ce63a58c0b05d5baab949 (patch)
tree86af17af135871cee20434cad3fbfecca9a7f310
parentf22838ab626bed6948b18bd6eeb28c8bdf210057 (diff)
downloadnetsurf-cf4294d3a85518a6502ce63a58c0b05d5baab949.tar.gz
netsurf-cf4294d3a85518a6502ce63a58c0b05d5baab949.tar.bz2
Implement knockout rendering (controlled by 'knockout_rendering' option or Ctrl+F11, default is off). This attempts to minimise the amount of overlapping redraw performed, and thus can drasticly reduce the rendering time of many pages.
svn path=/trunk/netsurf/; revision=2682
-rw-r--r--desktop/knockout.c765
-rw-r--r--desktop/knockout.h19
-rw-r--r--desktop/options.c3
-rw-r--r--desktop/options.h1
-rw-r--r--makefile2
-rw-r--r--render/html_redraw.c16
-rw-r--r--riscos/window.c5
7 files changed, 808 insertions, 3 deletions
diff --git a/desktop/knockout.c b/desktop/knockout.c
new file mode 100644
index 000000000..89ef58bc3
--- /dev/null
+++ b/desktop/knockout.c
@@ -0,0 +1,765 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ */
+
+/** \file
+ * Knockout rendering (implementation).
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "netsurf/desktop/knockout.h"
+#include "netsurf/desktop/plotters.h"
+#include "netsurf/image/bitmap.h"
+#include "netsurf/utils/log.h"
+
+
+#define KNOCKOUT_BOXES 512 /* 28 bytes each */
+#define KNOCKOUT_ENTRIES 4096 /* 40 bytes each */
+#define KNOCKOUT_POLYGONS 512 /* 4 bytes each */
+
+struct knockout_box;
+struct knockout_entry;
+
+static void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *box);
+static bool knockout_plot_fill_recursive(struct knockout_box *box, colour c);
+static bool knockout_plot_bitmap_tile_recursive(struct knockout_box *box,
+ struct knockout_entry *entry);
+
+static bool knockout_plot_clg(colour c);
+static bool knockout_plot_rectangle(int x0, int y0, int width, int height,
+ int line_width, colour c, bool dotted, bool dashed);
+static bool knockout_plot_line(int x0, int y0, int x1, int y1, int width,
+ colour c, bool dotted, bool dashed);
+static bool knockout_plot_polygon(int *p, unsigned int n, colour fill);
+static bool knockout_plot_fill(int x0, int y0, int x1, int y1, colour c);
+static bool knockout_plot_clip(int clip_x0, int clip_y0,
+ int clip_x1, int clip_y1);
+static bool knockout_plot_text(int x, int y, struct css_style *style,
+ const char *text, size_t length, colour bg, colour c);
+static bool knockout_plot_disc(int x, int y, int radius, colour colour, bool filled);
+static bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2,
+ colour c);
+static bool knockout_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg);
+static bool knockout_plot_bitmap_tile(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bool repeat_x, bool repeat_y);
+static bool knockout_plot_group_start(const char *name);
+static bool knockout_plot_group_end(void);
+
+
+struct knockout_entry knockout_entries[KNOCKOUT_ENTRIES];
+struct knockout_box knockout_boxes[KNOCKOUT_BOXES];
+int knockout_polygons[KNOCKOUT_POLYGONS];
+int knockout_entry_cur = 0;
+int knockout_box_cur = 0;
+int knockout_polygon_cur = 0;
+struct knockout_box *knockout_list = NULL;
+
+struct plotter_table real_plot;
+
+int clip_x0_cur;
+int clip_y0_cur;
+int clip_x1_cur;
+int clip_y1_cur;
+
+
+const struct plotter_table knockout_plotters = {
+ knockout_plot_clg,
+ knockout_plot_rectangle,
+ knockout_plot_line,
+ knockout_plot_polygon,
+ knockout_plot_fill,
+ knockout_plot_clip,
+ knockout_plot_text,
+ knockout_plot_disc,
+ knockout_plot_arc,
+ knockout_plot_bitmap,
+ knockout_plot_bitmap_tile,
+ knockout_plot_group_start,
+ knockout_plot_group_end
+};
+
+
+typedef enum {
+ KNOCKOUT_PLOT_CLG, /* translated to _FILL */
+ KNOCKOUT_PLOT_RECTANGLE,
+ KNOCKOUT_PLOT_LINE,
+ KNOCKOUT_PLOT_POLYGON,
+ KNOCKOUT_PLOT_FILL, /* knockout, knocked out */
+ KNOCKOUT_PLOT_CLIP,
+ KNOCKOUT_PLOT_TEXT,
+ KNOCKOUT_PLOT_DISC,
+ KNOCKOUT_PLOT_ARC,
+ KNOCKOUT_PLOT_BITMAP, /* knockout */
+ KNOCKOUT_PLOT_BITMAP_TILE, /* knockout, knocked out */
+ KNOCKOUT_PLOT_GROUP_START,
+ KNOCKOUT_PLOT_GROUP_END,
+} knockout_type;
+
+
+struct knockout_box {
+ struct {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ } bbox;
+ bool deleted; /* box has been totally knocked out */
+ struct knockout_box *child;
+ struct knockout_box *next;
+};
+
+
+struct knockout_entry {
+ knockout_type type;
+ struct knockout_box *box; /* relating series of knockout clips */
+ union {
+ struct {
+ colour c;
+ } clg;
+ struct {
+ int x0;
+ int y0;
+ int width;
+ int height;
+ int line_width;
+ colour c;
+ bool dotted;
+ bool dashed;
+ } rectangle;
+ struct {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ int width;
+ colour c;
+ bool dotted;
+ bool dashed;
+ } line;
+ struct {
+ int *p;
+ unsigned int n;
+ colour fill;
+ } polygon;
+ struct {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ colour c;
+ } fill;
+ struct {
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ } clip;
+ struct {
+ int x;
+ int y;
+ struct css_style *style;
+ const char *text;
+ size_t length;
+ colour bg;
+ colour c;
+ } text;
+ struct {
+ int x;
+ int y;
+ int radius;
+ colour colour;
+ bool filled;
+ } disc;
+ struct {
+ int x;
+ int y;
+ int radius;
+ int angle1;
+ int angle2;
+ colour c;
+ } arc;
+ struct {
+ int x;
+ int y;
+ int width;
+ int height;
+ struct bitmap *bitmap;
+ colour bg;
+ } bitmap;
+ struct {
+ int x;
+ int y;
+ int width;
+ int height;
+ struct bitmap *bitmap;
+ colour bg;
+ bool repeat_x;
+ bool repeat_y;
+ } bitmap_tile;
+ struct {
+ const char *name;
+ } group_start;
+ } data;
+};
+
+
+/**
+ * Start a knockout plotting session
+ *
+ * \param plotter the plotter to use
+ * \return true on success, false otherwise
+ */
+bool knockout_plot_start(struct plotter_table *plotter)
+{
+ /* end any previous sessions */
+ if (knockout_entry_cur > 0)
+ knockout_plot_end();
+
+ /* take over the plotter */
+ real_plot = *plotter;
+ plot = knockout_plotters;
+ return true;
+}
+
+
+/**
+ * End a knockout plotting session
+ *
+ * \return true on success, false otherwise
+ */
+bool knockout_plot_end(void)
+{
+ int i;
+ bool success = true;
+ struct knockout_box *box;
+
+ /* release our plotter */
+ plot = real_plot;
+
+ for (i = 0; i < knockout_entry_cur; i++) {
+ switch (knockout_entries[i].type) {
+ case KNOCKOUT_PLOT_CLG:
+ success &= plot.clg(
+ knockout_entries[i].data.clg.c);
+ break;
+ case KNOCKOUT_PLOT_RECTANGLE:
+ success &= plot.rectangle(
+ knockout_entries[i].data.rectangle.x0,
+ knockout_entries[i].data.rectangle.y0,
+ knockout_entries[i].data.rectangle.width,
+ knockout_entries[i].data.rectangle.height,
+ knockout_entries[i].data.rectangle.line_width,
+ knockout_entries[i].data.rectangle.c,
+ knockout_entries[i].data.rectangle.dotted,
+ knockout_entries[i].data.rectangle.dashed);
+ break;
+ case KNOCKOUT_PLOT_LINE:
+ success &= plot.line(
+ knockout_entries[i].data.line.x0,
+ knockout_entries[i].data.line.y0,
+ knockout_entries[i].data.line.x1,
+ knockout_entries[i].data.line.y1,
+ knockout_entries[i].data.line.width,
+ knockout_entries[i].data.line.c,
+ knockout_entries[i].data.line.dotted,
+ knockout_entries[i].data.line.dashed);
+ break;
+ case KNOCKOUT_PLOT_POLYGON:
+ success &= plot.polygon(
+ knockout_entries[i].data.polygon.p,
+ knockout_entries[i].data.polygon.n,
+ knockout_entries[i].data.polygon.fill);
+ break;
+ case KNOCKOUT_PLOT_FILL:
+ box = knockout_entries[i].box->child;
+ if (box)
+ success &= knockout_plot_fill_recursive(box,
+ knockout_entries[i].data.fill.c);
+ else if (!knockout_entries[i].box->deleted)
+ success &= plot.fill(
+ knockout_entries[i].data.fill.x0,
+ knockout_entries[i].data.fill.y0,
+ knockout_entries[i].data.fill.x1,
+ knockout_entries[i].data.fill.y1,
+ knockout_entries[i].data.fill.c);
+ break;
+ case KNOCKOUT_PLOT_CLIP:
+ success &= plot.clip(
+ knockout_entries[i].data.clip.x0,
+ knockout_entries[i].data.clip.y0,
+ knockout_entries[i].data.clip.x1,
+ knockout_entries[i].data.clip.y1);
+ break;
+ case KNOCKOUT_PLOT_TEXT:
+ success &= plot.text(
+ knockout_entries[i].data.text.x,
+ knockout_entries[i].data.text.y,
+ knockout_entries[i].data.text.style,
+ knockout_entries[i].data.text.text,
+ knockout_entries[i].data.text.length,
+ knockout_entries[i].data.text.bg,
+ knockout_entries[i].data.text.c);
+ break;
+ case KNOCKOUT_PLOT_DISC:
+ success &= plot.disc(
+ knockout_entries[i].data.disc.x,
+ knockout_entries[i].data.disc.y,
+ knockout_entries[i].data.disc.radius,
+ knockout_entries[i].data.disc.colour,
+ knockout_entries[i].data.disc.filled);
+ break;
+ case KNOCKOUT_PLOT_ARC:
+ success &= plot.arc(
+ knockout_entries[i].data.arc.x,
+ knockout_entries[i].data.arc.y,
+ knockout_entries[i].data.arc.radius,
+ knockout_entries[i].data.arc.angle1,
+ knockout_entries[i].data.arc.angle2,
+ knockout_entries[i].data.arc.c);
+ break;
+ case KNOCKOUT_PLOT_BITMAP:
+ success &= plot.bitmap(
+ knockout_entries[i].data.bitmap.x,
+ knockout_entries[i].data.bitmap.y,
+ knockout_entries[i].data.bitmap.width,
+ knockout_entries[i].data.bitmap.height,
+ knockout_entries[i].data.bitmap.bitmap,
+ knockout_entries[i].data.bitmap.bg);
+ break;
+ case KNOCKOUT_PLOT_BITMAP_TILE:
+ box = knockout_entries[i].box->child;
+ if (box) {
+ success &= knockout_plot_bitmap_tile_recursive(box,
+ &knockout_entries[i]);
+ success &= plot.clip(knockout_entries[i].box->bbox.x0,
+ knockout_entries[i].box->bbox.y0,
+ knockout_entries[i].box->bbox.x1,
+ knockout_entries[i].box->bbox.y1);
+ } else if (!knockout_entries[i].box->deleted) {
+ success &= plot.bitmap_tile(
+ knockout_entries[i].data.
+ bitmap_tile.x,
+ knockout_entries[i].data.
+ bitmap_tile.y,
+ knockout_entries[i].data.
+ bitmap_tile.width,
+ knockout_entries[i].data.
+ bitmap_tile.height,
+ knockout_entries[i].data.
+ bitmap_tile.bitmap,
+ knockout_entries[i].data.
+ bitmap_tile.bg,
+ knockout_entries[i].data.
+ bitmap_tile.repeat_x,
+ knockout_entries[i].data.
+ bitmap_tile.repeat_y);
+ }
+ break;
+ case KNOCKOUT_PLOT_GROUP_START:
+ success &= plot.group_start(
+ knockout_entries[i].data.group_start.name);
+ break;
+ case KNOCKOUT_PLOT_GROUP_END:
+ success &= plot.group_end();
+ break;
+ }
+ }
+
+ knockout_entry_cur = 0;
+ knockout_box_cur = 0;
+ knockout_polygon_cur = 0;
+ knockout_list = NULL;
+ return success;
+}
+
+
+/**
+ * Knockout a section of previous rendering
+ *
+ * \param x0 the left edge of the removal box
+ * \param y0 the bottom edge of the removal box
+ * \param x1 the right edge of the removal box
+ * \param y1 the top edge of the removal box
+ * \param box the current box set to consider
+*/
+void knockout_calculate(int x0, int y0, int x1, int y1, struct knockout_box *box)
+{
+ struct knockout_box *parent;
+ int nx0, ny0, nx1, ny1;
+
+ for (parent = box; parent; parent = parent->next) {
+ if (parent->deleted)
+ continue;
+ /* reject non-overlapping boxes */
+ if ((parent->bbox.x0 >= x1) ||
+ (parent->bbox.x1 <= x0) ||
+ (parent->bbox.y0 >= y1) ||
+ (parent->bbox.y1 <= y0))
+ continue;
+ /* has the box been replaced by children? */
+ if (parent->child) {
+ knockout_calculate(x0, y0, x1, y1, parent->child);
+ } else {
+ nx0 = parent->bbox.x0;
+ ny0 = parent->bbox.y0;
+ nx1 = parent->bbox.x1;
+ ny1 = parent->bbox.y1;
+
+ /* check for a total knockout */
+ if ((x0 <= nx0) && (x1 >= nx1) && (y0 <= ny0) && (y1 >= ny1)) {
+ parent->deleted = true;
+ continue;
+ }
+
+ /* we need a maximum of 4 child boxes */
+ if (knockout_box_cur + 4 >= KNOCKOUT_ENTRIES) {
+ knockout_plot_start(&real_plot);
+ return;
+ }
+
+ /* clip top */
+ if (y1 < ny1) {
+ knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
+ knockout_boxes[knockout_box_cur].bbox.y0 = y1;
+ knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
+ knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = parent->child;
+ parent->child = &knockout_boxes[knockout_box_cur++];
+ ny1 = y1;
+ }
+ /* clip bottom */
+ if (y0 > ny0) {
+ knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
+ knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
+ knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
+ knockout_boxes[knockout_box_cur].bbox.y1 = y0;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = parent->child;
+ parent->child = &knockout_boxes[knockout_box_cur++];
+ ny0 = y0;
+ }
+ /* clip right */
+ if (x1 < nx1) {
+ knockout_boxes[knockout_box_cur].bbox.x0 = x1;
+ knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
+ knockout_boxes[knockout_box_cur].bbox.x1 = nx1;
+ knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = parent->child;
+ parent->child = &knockout_boxes[knockout_box_cur++];
+ nx1 = x1;
+ }
+ /* clip left */
+ if (x0 > nx0) {
+ knockout_boxes[knockout_box_cur].bbox.x0 = nx0;
+ knockout_boxes[knockout_box_cur].bbox.y0 = ny0;
+ knockout_boxes[knockout_box_cur].bbox.x1 = x0;
+ knockout_boxes[knockout_box_cur].bbox.y1 = ny1;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = parent->child;
+ parent->child = &knockout_boxes[knockout_box_cur++];
+ //nx0 = x0;
+ }
+ }
+ }
+}
+
+
+bool knockout_plot_fill_recursive(struct knockout_box *box, colour c)
+{
+ bool success = true;
+ struct knockout_box *parent;
+
+ for (parent = box; parent; parent = parent->next) {
+ if (parent->deleted)
+ continue;
+ if (parent->child)
+ knockout_plot_fill_recursive(parent->child, c);
+ else
+ success &= plot.fill(parent->bbox.x0,
+ parent->bbox.y0,
+ parent->bbox.x1,
+ parent->bbox.y1,
+ c);
+ }
+ return success;
+}
+
+
+bool knockout_plot_bitmap_tile_recursive(struct knockout_box *box,
+ struct knockout_entry *entry)
+{
+ bool success = true;
+ struct knockout_box *parent;
+
+ for (parent = box; parent; parent = parent->next) {
+ if (parent->deleted)
+ continue;
+ if (parent->child)
+ knockout_plot_bitmap_tile_recursive(parent->child, entry);
+ else {
+ success &= plot.clip(parent->bbox.x0,
+ parent->bbox.y0,
+ parent->bbox.x1,
+ parent->bbox.y1);
+ success &= plot.bitmap_tile(entry->data.bitmap_tile.x,
+ entry->data.bitmap_tile.y,
+ entry->data.bitmap_tile.width,
+ entry->data.bitmap_tile.height,
+ entry->data.bitmap_tile.bitmap,
+ entry->data.bitmap_tile.bg,
+ entry->data.bitmap_tile.repeat_x,
+ entry->data.bitmap_tile.repeat_y);
+ }
+ }
+ return success;
+}
+
+bool knockout_plot_clg(colour c)
+{
+ return knockout_plot_fill(clip_x0_cur, clip_y0_cur, clip_x1_cur, clip_y1_cur, c);
+}
+
+
+bool knockout_plot_rectangle(int x0, int y0, int width, int height,
+ int line_width, colour c, bool dotted, bool dashed)
+{
+ knockout_entries[knockout_entry_cur].data.rectangle.x0 = x0;
+ knockout_entries[knockout_entry_cur].data.rectangle.y0 = y0;
+ knockout_entries[knockout_entry_cur].data.rectangle.width = width;
+ knockout_entries[knockout_entry_cur].data.rectangle.height = height;
+ knockout_entries[knockout_entry_cur].data.rectangle.line_width = line_width;
+ knockout_entries[knockout_entry_cur].data.rectangle.c = c;
+ knockout_entries[knockout_entry_cur].data.rectangle.dotted = dotted;
+ knockout_entries[knockout_entry_cur].data.rectangle.dashed = dashed;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_RECTANGLE;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_line(int x0, int y0, int x1, int y1, int width,
+ colour c, bool dotted, bool dashed)
+{
+ knockout_entries[knockout_entry_cur].data.line.x0 = x0;
+ knockout_entries[knockout_entry_cur].data.line.y0 = y0;
+ knockout_entries[knockout_entry_cur].data.line.x1 = x1;
+ knockout_entries[knockout_entry_cur].data.line.y1 = y1;
+ knockout_entries[knockout_entry_cur].data.line.width = width;
+ knockout_entries[knockout_entry_cur].data.line.c = c;
+ knockout_entries[knockout_entry_cur].data.line.dotted = dotted;
+ knockout_entries[knockout_entry_cur].data.line.dashed = dashed;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_LINE;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_polygon(int *p, unsigned int n, colour fill)
+{
+ bool success = true;
+ int *dest;
+
+ /* ensure we have sufficient room even when flushed */
+ if (n * 2 >= KNOCKOUT_POLYGONS) {
+ knockout_plot_end();
+ success = plot.polygon(p, n, fill);
+ knockout_plot_start(&real_plot);
+ return success;
+ }
+
+ /* ensure we have enough room right now */
+ if (knockout_polygon_cur + n * 2 >= KNOCKOUT_POLYGONS)
+ knockout_plot_start(&real_plot);
+
+ /* copy our data */
+ dest = &(knockout_polygons[knockout_polygon_cur]);
+ memcpy(dest, p, n * 2 * sizeof(int));
+ knockout_polygon_cur += n * 2;
+ knockout_entries[knockout_entry_cur].data.polygon.p = dest;
+ knockout_entries[knockout_entry_cur].data.polygon.n = n;
+ knockout_entries[knockout_entry_cur].data.polygon.fill = fill;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_POLYGON;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_fill(int x0, int y0, int x1, int y1, colour c)
+{
+ /* fills both knock out and get knocked out */
+ knockout_calculate(x0, y0, x1, y1, knockout_list);
+ knockout_boxes[knockout_box_cur].bbox.x0 = x0;
+ knockout_boxes[knockout_box_cur].bbox.y0 = y0;
+ knockout_boxes[knockout_box_cur].bbox.x1 = x1;
+ knockout_boxes[knockout_box_cur].bbox.y1 = y1;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = knockout_list;
+ knockout_list = &knockout_boxes[knockout_box_cur];
+ knockout_entries[knockout_entry_cur].box = &knockout_boxes[knockout_box_cur];
+ knockout_entries[knockout_entry_cur].data.fill.x0 = x0;
+ knockout_entries[knockout_entry_cur].data.fill.y0 = y0;
+ knockout_entries[knockout_entry_cur].data.fill.x1 = x1;
+ knockout_entries[knockout_entry_cur].data.fill.y1 = y1;
+ knockout_entries[knockout_entry_cur].data.fill.c = c;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_FILL;
+ if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) ||
+ (++knockout_box_cur >= KNOCKOUT_BOXES))
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_clip(int clip_x0, int clip_y0,
+ int clip_x1, int clip_y1)
+{
+ if (clip_x1 < clip_x0 || clip_y0 > clip_y1) {
+ LOG(("bad clip rectangle %i %i %i %i",
+ clip_x0, clip_y0, clip_x1, clip_y1));
+ return false;
+ }
+
+ /* memorise clip for bitmap tiling */
+ clip_x0_cur = clip_x0;
+ clip_y0_cur = clip_y0;
+ clip_x1_cur = clip_x1;
+ clip_y1_cur = clip_y1;
+
+ knockout_entries[knockout_entry_cur].data.clip.x0 = clip_x0;
+ knockout_entries[knockout_entry_cur].data.clip.y0 = clip_y0;
+ knockout_entries[knockout_entry_cur].data.clip.x1 = clip_x1;
+ knockout_entries[knockout_entry_cur].data.clip.y1 = clip_y1;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_CLIP;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_text(int x, int y, struct css_style *style,
+ const char *text, size_t length, colour bg, colour c)
+{
+ knockout_entries[knockout_entry_cur].data.text.x = x;
+ knockout_entries[knockout_entry_cur].data.text.y = y;
+ knockout_entries[knockout_entry_cur].data.text.style = style;
+ knockout_entries[knockout_entry_cur].data.text.text = text;
+ knockout_entries[knockout_entry_cur].data.text.length = length;
+ knockout_entries[knockout_entry_cur].data.text.bg = bg;
+ knockout_entries[knockout_entry_cur].data.text.c = c;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_TEXT;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_disc(int x, int y, int radius, colour colour, bool filled)
+{
+ knockout_entries[knockout_entry_cur].data.disc.x = x;
+ knockout_entries[knockout_entry_cur].data.disc.y = y;
+ knockout_entries[knockout_entry_cur].data.disc.radius = radius;
+ knockout_entries[knockout_entry_cur].data.disc.colour = colour;
+ knockout_entries[knockout_entry_cur].data.disc.filled = filled;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_DISC;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+bool knockout_plot_arc(int x, int y, int radius, int angle1, int angle2, colour c)
+{
+ knockout_entries[knockout_entry_cur].data.arc.x = x;
+ knockout_entries[knockout_entry_cur].data.arc.y = y;
+ knockout_entries[knockout_entry_cur].data.arc.radius = radius;
+ knockout_entries[knockout_entry_cur].data.arc.angle1 = angle1;
+ knockout_entries[knockout_entry_cur].data.arc.angle2 = angle2;
+ knockout_entries[knockout_entry_cur].data.arc.c = c;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_ARC;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+bool knockout_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg)
+{
+ /* opaque bitmaps knockout, but don't get knocked out */
+ if (bitmap_get_opaque(bitmap))
+ knockout_calculate(x, y, x + width, y + height, knockout_list);
+ knockout_entries[knockout_entry_cur].data.bitmap.x = x;
+ knockout_entries[knockout_entry_cur].data.bitmap.x = x;
+ knockout_entries[knockout_entry_cur].data.bitmap.y = y;
+ knockout_entries[knockout_entry_cur].data.bitmap.width = width;
+ knockout_entries[knockout_entry_cur].data.bitmap.height = height;
+ knockout_entries[knockout_entry_cur].data.bitmap.bitmap = bitmap;
+ knockout_entries[knockout_entry_cur].data.bitmap.bg = bg;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+
+bool knockout_plot_bitmap_tile(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bool repeat_x, bool repeat_y)
+{
+ /* tiled bitmaps both knock out and get knocked out */
+ if (bitmap_get_opaque(bitmap))
+ knockout_calculate(clip_x0_cur, clip_y0_cur, clip_x1_cur, clip_y1_cur,
+ knockout_list);
+ knockout_boxes[knockout_box_cur].bbox.x0 = clip_x0_cur;
+ knockout_boxes[knockout_box_cur].bbox.y0 = clip_y0_cur;
+ knockout_boxes[knockout_box_cur].bbox.x1 = clip_x1_cur;
+ knockout_boxes[knockout_box_cur].bbox.y1 = clip_y1_cur;
+ knockout_boxes[knockout_box_cur].deleted = false;
+ knockout_boxes[knockout_box_cur].child = NULL;
+ knockout_boxes[knockout_box_cur].next = knockout_list;
+ knockout_list = &knockout_boxes[knockout_box_cur];
+ knockout_entries[knockout_entry_cur].box = &knockout_boxes[knockout_box_cur];
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.x = x;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.y = y;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.width = width;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.height = height;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.bitmap = bitmap;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.bg = bg;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.repeat_x = repeat_x;
+ knockout_entries[knockout_entry_cur].data.bitmap_tile.repeat_y = repeat_y;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_BITMAP_TILE;
+ if ((++knockout_entry_cur >= KNOCKOUT_ENTRIES) ||
+ (++knockout_box_cur >= KNOCKOUT_BOXES))
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+bool knockout_plot_group_start(const char *name)
+{
+ knockout_entries[knockout_entry_cur].data.group_start.name = name;
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_START;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
+
+bool knockout_plot_group_end(void)
+{
+ knockout_entries[knockout_entry_cur].type = KNOCKOUT_PLOT_GROUP_END;
+ if (++knockout_entry_cur >= KNOCKOUT_ENTRIES)
+ knockout_plot_start(&real_plot);
+ return true;
+}
diff --git a/desktop/knockout.h b/desktop/knockout.h
new file mode 100644
index 000000000..d564ee6f0
--- /dev/null
+++ b/desktop/knockout.h
@@ -0,0 +1,19 @@
+/*
+ * This file is part of NetSurf, http://netsurf.sourceforge.net/
+ * Licensed under the GNU General Public License,
+ * http://www.opensource.org/licenses/gpl-license
+ * Copyright 2006 Richard Wilson <info@tinct.net>
+ */
+
+/** \file
+ * Knockout rendering (interface).
+ */
+
+#include "netsurf/desktop/plotters.h"
+
+
+bool knockout_plot_start(struct plotter_table *plotter);
+bool knockout_plot_end(void);
+
+
+extern const struct plotter_table knockout_plotters;
diff --git a/desktop/options.c b/desktop/options.c
index 1787e55a5..d01caad61 100644
--- a/desktop/options.c
+++ b/desktop/options.c
@@ -101,6 +101,8 @@ int option_max_fetchers_per_host = 5;
* is this plus option_max_fetchers.
*/
int option_max_cached_fetch_handles = 6;
+/** Whether to use knockout rendering */
+bool option_knockout_rendering = false;
EXTRA_OPTION_DEFINE
@@ -142,6 +144,7 @@ struct {
OPTION_INTEGER, &option_max_fetchers_per_host },
{ "max_cached_fetch_handles",
OPTION_INTEGER, &option_max_cached_fetch_handles },
+ { "knockout_rendering", OPTION_BOOL, &option_knockout_rendering },
EXTRA_OPTION_TABLE
};
diff --git a/desktop/options.h b/desktop/options.h
index 35e1d62a9..08e808b99 100644
--- a/desktop/options.h
+++ b/desktop/options.h
@@ -55,6 +55,7 @@ extern char *option_ca_bundle;
extern char *option_cookie_file;
extern char *option_cookie_jar;
extern char *option_homepage_url;
+extern bool option_knockout_rendering;
/* Fetcher configuration. */
extern int option_max_fetchers;
diff --git a/makefile b/makefile
index c8aeb21e5..05619b9d9 100644
--- a/makefile
+++ b/makefile
@@ -24,7 +24,7 @@ OBJECTS_COMMON += box.o box_construct.o box_normalise.o form.o \
table.o textplain.o # render/
OBJECTS_COMMON += filename.o messages.o talloc.o url.o utf8.o \
utils.o # utils/
-OBJECTS_COMMON += options.o tree.o # desktop/
+OBJECTS_COMMON += knockout.o options.o tree.o # desktop/
OBJECTS_IMAGE = bmp.o bmpread.o gif.o gifread.o ico.o jpeg.o \
mng.o # image/
diff --git a/render/html_redraw.c b/render/html_redraw.c
index aeac805ab..3e70168c0 100644
--- a/render/html_redraw.c
+++ b/render/html_redraw.c
@@ -17,8 +17,10 @@
#include "netsurf/css/css.h"
#include "netsurf/desktop/gui.h"
#include "netsurf/desktop/plotters.h"
+#include "netsurf/desktop/knockout.h"
#include "netsurf/desktop/selection.h"
#include "netsurf/desktop/textinput.h"
+#include "netsurf/desktop/options.h"
#include "netsurf/render/box.h"
#include "netsurf/render/font.h"
#include "netsurf/render/form.h"
@@ -70,7 +72,6 @@ static bool html_redraw_scrollbars(struct box *box, float scale,
bool html_redraw_debug = false;
-
/**
* Draw a CONTENT_HTML using the current set of plotters (plot).
*
@@ -96,18 +97,29 @@ bool html_redraw(struct content *c, int x, int y,
float scale, unsigned long background_colour)
{
struct box *box;
+ bool result;
box = c->data.html.layout;
assert(box);
+ if (option_knockout_rendering)
+ knockout_plot_start(&plot);
+
/* clear to background colour */
+ plot.clip(clip_x0, clip_y0, clip_x1, clip_y1);
if (c->data.html.background_colour != TRANSPARENT)
background_colour = c->data.html.background_colour;
plot.clg(background_colour);
- return html_redraw_box(box, x, y,
+ result = html_redraw_box(box, x, y,
clip_x0, clip_y0, clip_x1, clip_y1,
scale, background_colour);
+
+ if (option_knockout_rendering)
+ knockout_plot_end();
+
+ return result;
+
}
diff --git a/riscos/window.c b/riscos/window.c
index 0864ecf8a..9b8e3ac10 100644
--- a/riscos/window.c
+++ b/riscos/window.c
@@ -2031,6 +2031,11 @@ bool ro_gui_window_keypress(struct gui_window *g, int key, bool toolbar)
return ro_gui_menu_handle_action(g->window,
BROWSER_SCALE_VIEW, false);
+ case wimp_KEY_CONTROL + wimp_KEY_F11: /* Toggle knockout rendering */
+ option_knockout_rendering = !option_knockout_rendering;
+ gui_window_redraw_window(g);
+ return true;
+
case wimp_KEY_SHIFT + wimp_KEY_F11: /* Toggle display of box outlines. */
html_redraw_debug = !html_redraw_debug;
gui_window_redraw_window(g);