WebSurf/desktop/knockout.c

777 lines
24 KiB
C
Raw Normal View History

/*
* 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);
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;
};
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;
/**
* 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)) {
if ((repeat_x && repeat_y) ||
/* horizontally repeating, full height */
(repeat_x && (y <= clip_y0_cur) &&
(y + height >= clip_y1_cur)) ||
/* vertically repeating, full width */
(repeat_y && (x <= clip_x0_cur) &&
(x + width >= clip_x1_cur)) ||
/* no repeat, full width & height */
((y <= clip_y0_cur) && (y + height >= clip_y1_cur) &&
(x <= clip_x0_cur) && (x + width >= clip_x1_cur)))
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;
}