kolibrios/contrib/media/updf/fitz/dev_list.c

661 lines
16 KiB
C
Raw Normal View History

#include "fitz.h"
typedef struct fz_display_node_s fz_display_node;
#define STACK_SIZE 96
typedef enum fz_display_command_e
{
FZ_CMD_FILL_PATH,
FZ_CMD_STROKE_PATH,
FZ_CMD_CLIP_PATH,
FZ_CMD_CLIP_STROKE_PATH,
FZ_CMD_FILL_TEXT,
FZ_CMD_STROKE_TEXT,
FZ_CMD_CLIP_TEXT,
FZ_CMD_CLIP_STROKE_TEXT,
FZ_CMD_IGNORE_TEXT,
FZ_CMD_FILL_SHADE,
FZ_CMD_FILL_IMAGE,
FZ_CMD_FILL_IMAGE_MASK,
FZ_CMD_CLIP_IMAGE_MASK,
FZ_CMD_POP_CLIP,
FZ_CMD_BEGIN_MASK,
FZ_CMD_END_MASK,
FZ_CMD_BEGIN_GROUP,
FZ_CMD_END_GROUP,
FZ_CMD_BEGIN_TILE,
FZ_CMD_END_TILE
} fz_display_command;
struct fz_display_node_s
{
fz_display_command cmd;
fz_display_node *next;
fz_rect rect;
union {
fz_path *path;
fz_text *text;
fz_shade *shade;
fz_pixmap *image;
int blendmode;
} item;
fz_stroke_state *stroke;
int flag; /* even_odd, accumulate, isolated/knockout... */
fz_matrix ctm;
fz_colorspace *colorspace;
float alpha;
float color[FZ_MAX_COLORS];
};
struct fz_display_list_s
{
fz_display_node *first;
fz_display_node *last;
int top;
struct {
fz_rect *update;
fz_rect rect;
} stack[STACK_SIZE];
int tiled;
};
enum { ISOLATED = 1, KNOCKOUT = 2 };
static fz_display_node *
fz_new_display_node(fz_display_command cmd, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
int i;
node = fz_malloc(sizeof(fz_display_node));
node->cmd = cmd;
node->next = NULL;
node->rect = fz_empty_rect;
node->item.path = NULL;
node->stroke = NULL;
node->flag = 0;
node->ctm = ctm;
if (colorspace)
{
node->colorspace = fz_keep_colorspace(colorspace);
if (color)
{
for (i = 0; i < node->colorspace->n; i++)
node->color[i] = color[i];
}
}
else
{
node->colorspace = NULL;
}
node->alpha = alpha;
return node;
}
static fz_stroke_state *
fz_clone_stroke_state(fz_stroke_state *stroke)
{
fz_stroke_state *newstroke = fz_malloc(sizeof(fz_stroke_state));
*newstroke = *stroke;
return newstroke;
}
static void
fz_append_display_node(fz_display_list *list, fz_display_node *node)
{
switch (node->cmd)
{
case FZ_CMD_CLIP_PATH:
case FZ_CMD_CLIP_STROKE_PATH:
case FZ_CMD_CLIP_IMAGE_MASK:
if (list->top < STACK_SIZE)
{
list->stack[list->top].update = &node->rect;
list->stack[list->top].rect = fz_empty_rect;
}
list->top++;
break;
case FZ_CMD_END_MASK:
case FZ_CMD_CLIP_TEXT:
case FZ_CMD_CLIP_STROKE_TEXT:
if (list->top < STACK_SIZE)
{
list->stack[list->top].update = NULL;
list->stack[list->top].rect = fz_empty_rect;
}
list->top++;
break;
case FZ_CMD_BEGIN_TILE:
list->tiled++;
if (list->top > 0 && list->top < STACK_SIZE)
{
list->stack[list->top-1].rect = fz_infinite_rect;
}
break;
case FZ_CMD_END_TILE:
list->tiled--;
break;
case FZ_CMD_END_GROUP:
break;
case FZ_CMD_POP_CLIP:
if (list->top > STACK_SIZE)
{
list->top--;
node->rect = fz_infinite_rect;
}
else if (list->top > 0)
{
fz_rect *update;
list->top--;
update = list->stack[list->top].update;
if (list->tiled == 0)
{
if (update != NULL)
{
*update = fz_intersect_rect(*update, list->stack[list->top].rect);
node->rect = *update;
}
else
node->rect = list->stack[list->top].rect;
}
else
node->rect = fz_infinite_rect;
}
/* fallthrough */
default:
if (list->top > 0 && list->tiled == 0 && list->top <= STACK_SIZE)
list->stack[list->top-1].rect = fz_union_rect(list->stack[list->top-1].rect, node->rect);
break;
}
if (!list->first)
{
list->first = node;
list->last = node;
}
else
{
list->last->next = node;
list->last = node;
}
}
static void
fz_free_display_node(fz_display_node *node)
{
switch (node->cmd)
{
case FZ_CMD_FILL_PATH:
case FZ_CMD_STROKE_PATH:
case FZ_CMD_CLIP_PATH:
case FZ_CMD_CLIP_STROKE_PATH:
fz_free_path(node->item.path);
break;
case FZ_CMD_FILL_TEXT:
case FZ_CMD_STROKE_TEXT:
case FZ_CMD_CLIP_TEXT:
case FZ_CMD_CLIP_STROKE_TEXT:
case FZ_CMD_IGNORE_TEXT:
fz_free_text(node->item.text);
break;
case FZ_CMD_FILL_SHADE:
fz_drop_shade(node->item.shade);
break;
case FZ_CMD_FILL_IMAGE:
case FZ_CMD_FILL_IMAGE_MASK:
case FZ_CMD_CLIP_IMAGE_MASK:
fz_drop_pixmap(node->item.image);
break;
case FZ_CMD_POP_CLIP:
case FZ_CMD_BEGIN_MASK:
case FZ_CMD_END_MASK:
case FZ_CMD_BEGIN_GROUP:
case FZ_CMD_END_GROUP:
case FZ_CMD_BEGIN_TILE:
case FZ_CMD_END_TILE:
break;
}
if (node->stroke)
fz_free(node->stroke);
if (node->colorspace)
fz_drop_colorspace(node->colorspace);
fz_free(node);
}
static void
fz_list_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_FILL_PATH, ctm, colorspace, color, alpha);
node->rect = fz_bound_path(path, NULL, ctm);
node->item.path = fz_clone_path(path);
node->flag = even_odd;
fz_append_display_node(user, node);
}
static void
fz_list_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_STROKE_PATH, ctm, colorspace, color, alpha);
node->rect = fz_bound_path(path, stroke, ctm);
node->item.path = fz_clone_path(path);
node->stroke = fz_clone_stroke_state(stroke);
fz_append_display_node(user, node);
}
static void
fz_list_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_CLIP_PATH, ctm, NULL, NULL, 0);
node->rect = fz_bound_path(path, NULL, ctm);
if (rect != NULL)
node->rect = fz_intersect_rect(node->rect, *rect);
node->item.path = fz_clone_path(path);
node->flag = even_odd;
fz_append_display_node(user, node);
}
static void
fz_list_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_CLIP_STROKE_PATH, ctm, NULL, NULL, 0);
node->rect = fz_bound_path(path, stroke, ctm);
if (rect != NULL)
node->rect = fz_intersect_rect(node->rect, *rect);
node->item.path = fz_clone_path(path);
node->stroke = fz_clone_stroke_state(stroke);
fz_append_display_node(user, node);
}
static void
fz_list_fill_text(void *user, fz_text *text, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_FILL_TEXT, ctm, colorspace, color, alpha);
node->rect = fz_bound_text(text, ctm);
node->item.text = fz_clone_text(text);
fz_append_display_node(user, node);
}
static void
fz_list_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_STROKE_TEXT, ctm, colorspace, color, alpha);
node->rect = fz_bound_text(text, ctm);
node->item.text = fz_clone_text(text);
node->stroke = fz_clone_stroke_state(stroke);
fz_append_display_node(user, node);
}
static void
fz_list_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_CLIP_TEXT, ctm, NULL, NULL, 0);
node->rect = fz_bound_text(text, ctm);
node->item.text = fz_clone_text(text);
node->flag = accumulate;
/* when accumulating, be conservative about culling */
if (accumulate)
node->rect = fz_infinite_rect;
fz_append_display_node(user, node);
}
static void
fz_list_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_CLIP_STROKE_TEXT, ctm, NULL, NULL, 0);
node->rect = fz_bound_text(text, ctm);
node->item.text = fz_clone_text(text);
node->stroke = fz_clone_stroke_state(stroke);
fz_append_display_node(user, node);
}
static void
fz_list_ignore_text(void *user, fz_text *text, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_IGNORE_TEXT, ctm, NULL, NULL, 0);
node->rect = fz_bound_text(text, ctm);
node->item.text = fz_clone_text(text);
fz_append_display_node(user, node);
}
static void
fz_list_pop_clip(void *user)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_POP_CLIP, fz_identity, NULL, NULL, 0);
fz_append_display_node(user, node);
}
static void
fz_list_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_FILL_SHADE, ctm, NULL, NULL, alpha);
node->rect = fz_bound_shade(shade, ctm);
node->item.shade = fz_keep_shade(shade);
fz_append_display_node(user, node);
}
static void
fz_list_fill_image(void *user, fz_pixmap *image, fz_matrix ctm, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_FILL_IMAGE, ctm, NULL, NULL, alpha);
node->rect = fz_transform_rect(ctm, fz_unit_rect);
node->item.image = fz_keep_pixmap(image);
fz_append_display_node(user, node);
}
static void
fz_list_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm,
fz_colorspace *colorspace, float *color, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_FILL_IMAGE_MASK, ctm, colorspace, color, alpha);
node->rect = fz_transform_rect(ctm, fz_unit_rect);
node->item.image = fz_keep_pixmap(image);
fz_append_display_node(user, node);
}
static void
fz_list_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_CLIP_IMAGE_MASK, ctm, NULL, NULL, 0);
node->rect = fz_transform_rect(ctm, fz_unit_rect);
if (rect != NULL)
node->rect = fz_intersect_rect(node->rect, *rect);
node->item.image = fz_keep_pixmap(image);
fz_append_display_node(user, node);
}
static void
fz_list_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *color)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_BEGIN_MASK, fz_identity, colorspace, color, 0);
node->rect = rect;
node->flag = luminosity;
fz_append_display_node(user, node);
}
static void
fz_list_end_mask(void *user)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_END_MASK, fz_identity, NULL, NULL, 0);
fz_append_display_node(user, node);
}
static void
fz_list_begin_group(void *user, fz_rect rect, int isolated, int knockout, int blendmode, float alpha)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_BEGIN_GROUP, fz_identity, NULL, NULL, alpha);
node->rect = rect;
node->item.blendmode = blendmode;
node->flag |= isolated ? ISOLATED : 0;
node->flag |= knockout ? KNOCKOUT : 0;
fz_append_display_node(user, node);
}
static void
fz_list_end_group(void *user)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_END_GROUP, fz_identity, NULL, NULL, 0);
fz_append_display_node(user, node);
}
static void
fz_list_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_BEGIN_TILE, ctm, NULL, NULL, 0);
node->rect = area;
node->color[0] = xstep;
node->color[1] = ystep;
node->color[2] = view.x0;
node->color[3] = view.y0;
node->color[4] = view.x1;
node->color[5] = view.y1;
fz_append_display_node(user, node);
}
static void
fz_list_end_tile(void *user)
{
fz_display_node *node;
node = fz_new_display_node(FZ_CMD_END_TILE, fz_identity, NULL, NULL, 0);
fz_append_display_node(user, node);
}
fz_device *
fz_new_list_device(fz_display_list *list)
{
fz_device *dev = fz_new_device(list);
dev->fill_path = fz_list_fill_path;
dev->stroke_path = fz_list_stroke_path;
dev->clip_path = fz_list_clip_path;
dev->clip_stroke_path = fz_list_clip_stroke_path;
dev->fill_text = fz_list_fill_text;
dev->stroke_text = fz_list_stroke_text;
dev->clip_text = fz_list_clip_text;
dev->clip_stroke_text = fz_list_clip_stroke_text;
dev->ignore_text = fz_list_ignore_text;
dev->fill_shade = fz_list_fill_shade;
dev->fill_image = fz_list_fill_image;
dev->fill_image_mask = fz_list_fill_image_mask;
dev->clip_image_mask = fz_list_clip_image_mask;
dev->pop_clip = fz_list_pop_clip;
dev->begin_mask = fz_list_begin_mask;
dev->end_mask = fz_list_end_mask;
dev->begin_group = fz_list_begin_group;
dev->end_group = fz_list_end_group;
dev->begin_tile = fz_list_begin_tile;
dev->end_tile = fz_list_end_tile;
return dev;
}
fz_display_list *
fz_new_display_list(void)
{
fz_display_list *list = fz_malloc(sizeof(fz_display_list));
list->first = NULL;
list->last = NULL;
list->top = 0;
list->tiled = 0;
return list;
}
void
fz_free_display_list(fz_display_list *list)
{
fz_display_node *node = list->first;
while (node)
{
fz_display_node *next = node->next;
fz_free_display_node(node);
node = next;
}
fz_free(list);
}
void
fz_execute_display_list(fz_display_list *list, fz_device *dev, fz_matrix top_ctm, fz_bbox scissor)
{
fz_display_node *node;
fz_matrix ctm;
fz_rect rect;
fz_bbox bbox;
int clipped = 0;
int tiled = 0;
int empty;
if (!fz_is_infinite_bbox(scissor))
{
/* add some fuzz at the edges, as especially glyph rects
* are sometimes not actually completely bounding the glyph */
scissor.x0 -= 20; scissor.y0 -= 20;
scissor.x1 += 20; scissor.y1 += 20;
}
for (node = list->first; node; node = node->next)
{
/* cull objects to draw using a quick visibility test */
if (tiled || node->cmd == FZ_CMD_BEGIN_TILE || node->cmd == FZ_CMD_END_TILE)
{
empty = 0;
}
else
{
bbox = fz_round_rect(fz_transform_rect(top_ctm, node->rect));
bbox = fz_intersect_bbox(bbox, scissor);
empty = fz_is_empty_bbox(bbox);
}
if (clipped || empty)
{
switch (node->cmd)
{
case FZ_CMD_CLIP_PATH:
case FZ_CMD_CLIP_STROKE_PATH:
case FZ_CMD_CLIP_TEXT:
case FZ_CMD_CLIP_STROKE_TEXT:
case FZ_CMD_CLIP_IMAGE_MASK:
case FZ_CMD_BEGIN_MASK:
case FZ_CMD_BEGIN_GROUP:
clipped++;
continue;
case FZ_CMD_POP_CLIP:
case FZ_CMD_END_GROUP:
if (!clipped)
goto visible;
clipped--;
continue;
case FZ_CMD_END_MASK:
if (!clipped)
goto visible;
continue;
default:
continue;
}
}
visible:
ctm = fz_concat(node->ctm, top_ctm);
switch (node->cmd)
{
case FZ_CMD_FILL_PATH:
fz_fill_path(dev, node->item.path, node->flag, ctm,
node->colorspace, node->color, node->alpha);
break;
case FZ_CMD_STROKE_PATH:
fz_stroke_path(dev, node->item.path, node->stroke, ctm,
node->colorspace, node->color, node->alpha);
break;
case FZ_CMD_CLIP_PATH:
{
fz_rect trect = fz_transform_rect(top_ctm, node->rect);
fz_clip_path(dev, node->item.path, &trect, node->flag, ctm);
break;
}
case FZ_CMD_CLIP_STROKE_PATH:
{
fz_rect trect = fz_transform_rect(top_ctm, node->rect);
fz_clip_stroke_path(dev, node->item.path, &trect, node->stroke, ctm);
break;
}
case FZ_CMD_FILL_TEXT:
fz_fill_text(dev, node->item.text, ctm,
node->colorspace, node->color, node->alpha);
break;
case FZ_CMD_STROKE_TEXT:
fz_stroke_text(dev, node->item.text, node->stroke, ctm,
node->colorspace, node->color, node->alpha);
break;
case FZ_CMD_CLIP_TEXT:
fz_clip_text(dev, node->item.text, ctm, node->flag);
break;
case FZ_CMD_CLIP_STROKE_TEXT:
fz_clip_stroke_text(dev, node->item.text, node->stroke, ctm);
break;
case FZ_CMD_IGNORE_TEXT:
fz_ignore_text(dev, node->item.text, ctm);
break;
case FZ_CMD_FILL_SHADE:
fz_fill_shade(dev, node->item.shade, ctm, node->alpha);
break;
case FZ_CMD_FILL_IMAGE:
fz_fill_image(dev, node->item.image, ctm, node->alpha);
break;
case FZ_CMD_FILL_IMAGE_MASK:
fz_fill_image_mask(dev, node->item.image, ctm,
node->colorspace, node->color, node->alpha);
break;
case FZ_CMD_CLIP_IMAGE_MASK:
{
fz_rect trect = fz_transform_rect(top_ctm, node->rect);
fz_clip_image_mask(dev, node->item.image, &trect, ctm);
break;
}
case FZ_CMD_POP_CLIP:
fz_pop_clip(dev);
break;
case FZ_CMD_BEGIN_MASK:
rect = fz_transform_rect(top_ctm, node->rect);
fz_begin_mask(dev, rect, node->flag, node->colorspace, node->color);
break;
case FZ_CMD_END_MASK:
fz_end_mask(dev);
break;
case FZ_CMD_BEGIN_GROUP:
rect = fz_transform_rect(top_ctm, node->rect);
fz_begin_group(dev, rect,
(node->flag & ISOLATED) != 0, (node->flag & KNOCKOUT) != 0,
node->item.blendmode, node->alpha);
break;
case FZ_CMD_END_GROUP:
fz_end_group(dev);
break;
case FZ_CMD_BEGIN_TILE:
tiled++;
rect.x0 = node->color[2];
rect.y0 = node->color[3];
rect.x1 = node->color[4];
rect.y1 = node->color[5];
fz_begin_tile(dev, node->rect, rect,
node->color[0], node->color[1], ctm);
break;
case FZ_CMD_END_TILE:
tiled--;
fz_end_tile(dev);
break;
}
}
}