static void fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; fz_bbox bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; fz_context *ctx = dev->ctx; if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); fz_sort_gel(dev->gel); state = push_stack(dev); model = state->dest->colorspace; bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); /* SumatraPDF: try to match rendering with and without display list */ else bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(fz_bound_path(ctx, path, stroke, ctm))); fz_try(ctx) { state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state->shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } if (!fz_is_empty_rect(bbox)) fz_scan_convert(dev->gel, 0, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke) begin\n"); #endif } fz_catch(ctx) { emergency_pop_stack(dev, state); } }
static void fz_draw_begin_tile(fz_device *devp, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_pixmap *dest = NULL; fz_pixmap *shape; fz_bbox bbox; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; /* area, view, xstep, ystep are in pattern space */ /* ctm maps from pattern space to device space */ if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_rect(fz_transform_rect(ctm, view)); /* We should never have a bbox that entirely covers our destination. * If we do, then the check for only 1 tile being visible above has * failed. Actually, this *can* fail due to the round_rect, at extreme * resolutions, so disable this assert. * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w || * bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h); */ dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(ctx, dest); shape = state[0].shape; if (shape) { fz_var(shape); fz_try(ctx) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].xstep = xstep; state[1].ystep = ystep; state[1].area = area; state[1].ctm = ctm; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Tile begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; }
char * fz_copy_selection(fz_context *ctx, fz_text_page *page, fz_bbox rect) { fz_buffer *buffer; fz_bbox hitbox; fz_text_block *block; fz_text_line *line; fz_text_span *span; int c, i, seen = 0; char *s; int x0 = rect.x0; int x1 = rect.x1; int y0 = rect.y0; int y1 = rect.y1; buffer = fz_new_buffer(ctx, 1024); for (block = page->blocks; block < page->blocks + page->len; block++) { for (line = block->lines; line < block->lines + block->len; line++) { for (span = line->spans; span < line->spans + line->len; span++) { if (seen) { fz_write_buffer_byte(ctx, buffer, '\n'); } seen = 0; for (i = 0; i < span->len; i++) { hitbox = fz_bbox_covering_rect(span->text[i].bbox); c = span->text[i].c; if (c < 32) c = '?'; if (hitbox.x1 >= x0 && hitbox.x0 <= x1 && hitbox.y1 >= y0 && hitbox.y0 <= y1) { fz_write_buffer_rune(ctx, buffer, c); seen = 1; } } seen = (seen && span + 1 == line->spans + line->len); } } } fz_write_buffer_byte(ctx, buffer, 0); s = (char*)buffer->data; fz_free(ctx, buffer); return s; }
static void fz_bbox_add_rect(fz_device *dev, fz_rect rect, int clip) { fz_bbox_data *data = dev->user; if (0 < data->top && data->top <= STACK_SIZE) rect = fz_intersect_rect(rect, data->stack[data->top-1]); if (!clip && data->top <= STACK_SIZE && !data->ignore) *data->result = fz_union_bbox(*data->result, fz_bbox_covering_rect(rect)); if (clip && ++data->top <= STACK_SIZE) data->stack[data->top-1] = rect; }
int fz_highlight_selection(fz_context *ctx, fz_text_page *page, fz_bbox rect, fz_bbox *hit_bbox, int hit_max) { fz_bbox linebox, charbox; fz_text_block *block; fz_text_line *line; fz_text_span *span; int i, hit_count; int x0 = rect.x0; int x1 = rect.x1; int y0 = rect.y0; int y1 = rect.y1; hit_count = 0; for (block = page->blocks; block < page->blocks + page->len; block++) { for (line = block->lines; line < block->lines + block->len; line++) { linebox = fz_empty_bbox; for (span = line->spans; span < line->spans + line->len; span++) { for (i = 0; i < span->len; i++) { charbox = fz_bbox_covering_rect(span->text[i].bbox); if (charbox.x1 >= x0 && charbox.x0 <= x1 && charbox.y1 >= y0 && charbox.y0 <= y1) { if (charbox.y0 != linebox.y0 || fz_absi(charbox.x0 - linebox.x1) > 5) { if (!fz_is_empty_bbox(linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; linebox = charbox; } else { linebox = fz_union_bbox(linebox, charbox); } } } } if (!fz_is_empty_bbox(linebox) && hit_count < hit_max) hit_bbox[hit_count++] = linebox; } } return hit_count; }
static void fz_draw_begin_mask(fz_device *devp, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv) { fz_draw_device *dev = devp->user; fz_pixmap *dest; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_pixmap *shape = state->shape; bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(dev->ctx, fz_device_gray, bbox); if (state->shape) { /* FIXME: If we ever want to support AIS true, then we * probably want to create a shape pixmap here, using: * shape = fz_new_pixmap_with_bbox(NULL, bbox); * then, in the end_mask code, we create the mask from this * rather than dest. */ shape = NULL; } if (luminosity) { float bc; if (!colorspace) colorspace = fz_device_gray; fz_convert_color(dev->ctx, fz_device_gray, &bc, colorspace, colorfv); fz_clear_pixmap_with_value(dev->ctx, dest, bc * 255); if (shape) fz_clear_pixmap_with_value(dev->ctx, shape, 255); } else { fz_clear_pixmap(dev->ctx, dest); if (shape) fz_clear_pixmap(dev->ctx, shape); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Mask begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].luminosity = luminosity; }
static void fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { state[1].scissor = bbox; state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif return; } state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state[1].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); #endif }
static void fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm, trunc_trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state; fz_colorspace *model; /* If accumulate == 0 then this text object is guaranteed complete */ /* If accumulate == 1 then this text object is the first (or only) in a sequence */ /* If accumulate == 2 then this text object is a continuation */ state = push_stack(dev); model = state->dest->colorspace; if (accumulate == 0) { /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); } else { /* be conservative about the size of the mask needed */ bbox = state->scissor; } if (accumulate == 0 || accumulate == 1) { mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } else shape = NULL; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].mask = mask; state[1].shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (text) begin\n"); #endif } else { mask = state->mask; dev->top--; } if (!fz_is_empty_rect(bbox) && mask) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trunc_trm = trm; trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_glyph(dev->ctx, text->font, gid, trunc_trm, model, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (state[1].shape) draw_glyph(NULL, state[1].shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, trm); if (path) { fz_pixmap *old_dest; float white = 1; state = &dev->stack[dev->top]; old_dest = state[0].dest; state[0].dest = state[0].mask; state[0].mask = NULL; fz_try(ctx) { fz_draw_fill_path(devp, path, 0, fz_identity, fz_device_gray, &white, 1); } fz_always(ctx) { state[0].mask = state[0].dest; state[0].dest = old_dest; fz_free_path(dev->ctx, path); } fz_catch(ctx) { fz_rethrow(ctx); } } else { fz_warn(dev->ctx, "cannot render glyph for clipping"); } } }
static void fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; fz_rect bounds; fz_bbox bbox, scissor; fz_pixmap *dest, *shape; float colorfv[FZ_MAX_COLORS]; unsigned char colorbv[FZ_MAX_COLORS + 1]; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; bounds = fz_bound_shade(dev->ctx, shade, ctm); scissor = state->scissor; bbox = fz_intersect_bbox(fz_bbox_covering_rect(bounds), scissor); if (fz_is_empty_rect(bbox)) return; if (!model) { fz_warn(dev->ctx, "cannot render shading directly to an alpha mask"); return; } if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(dev); dest = state->dest; shape = state->shape; if (alpha < 1) { dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); fz_clear_pixmap(dev->ctx, dest); if (shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } } if (shade->use_background) { unsigned char *s; int x, y, n, i; fz_convert_color(dev->ctx, model, colorfv, shade->colorspace, shade->background); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; colorbv[i] = 255; n = dest->n; for (y = scissor.y0; y < scissor.y1; y++) { s = dest->samples + (unsigned int)(((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n); for (x = scissor.x0; x < scissor.x1; x++) { for (i = 0; i < n; i++) *s++ = colorbv[i]; } } if (shape) { for (y = scissor.y0; y < scissor.y1; y++) { s = shape->samples + (unsigned int)((scissor.x0 - shape->x) + (y - shape->y) * shape->w); for (x = scissor.x0; x < scissor.x1; x++) { *s++ = 255; } } } } fz_paint_shade(dev->ctx, shade, ctm, dest, bbox); if (shape) fz_clear_pixmap_rect_with_value(dev->ctx, shape, 255, bbox); if (alpha < 1) { fz_paint_pixmap(state->dest, dest, alpha * 255); fz_drop_pixmap(dev->ctx, dest); if (shape) { fz_paint_pixmap(state->shape, shape, alpha * 255); fz_drop_pixmap(dev->ctx, shape); } } if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); }
static void fz_draw_clip_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } else shape = state->shape; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].mask = mask; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke text) begin\n"); #endif if (!fz_is_empty_rect(bbox)) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_stroked_glyph(dev->ctx, text->font, gid, trm, ctm, stroke, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (shape) draw_glyph(NULL, shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_warn(dev->ctx, "cannot draw glyph for clipping (unimplemented case)"); // TODO: outline/non-cached case } } } }
static void fz_draw_begin_group(fz_device *devp, fz_rect rect, int isolated, int knockout, int blendmode, float alpha) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *dest, *shape; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(ctx, model, bbox); #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED knockout = 0; isolated = 1; #endif if (isolated) { fz_clear_pixmap(dev->ctx, dest); } else { fz_copy_pixmap_rect(dev->ctx, dest, state[0].dest, bbox); } if (blendmode == 0 && alpha == 1.0 && isolated) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = state[0].shape; } else { fz_try(ctx) { shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].alpha = alpha; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Group begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); }
static void fz_draw_clip_image_mask(fz_device *devp, fz_image *image, fz_rect *rect, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask = NULL; fz_pixmap *dest = NULL; fz_pixmap *shape = NULL; fz_pixmap *scaled = NULL; fz_pixmap *pixmap; fz_pixmap *orig_pixmap; int dx, dy; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_pixmap_bbox(ctx, state->dest); clip = fz_intersect_bbox(clip, state->scissor); fz_var(mask); fz_var(dest); fz_var(shape); if (image->w == 0 || image->h == 0) { #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n"); #endif state[1].scissor = fz_empty_bbox; state[1].mask = NULL; return; } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (image mask) begin\n"); #endif bbox = fz_bbox_covering_rect(fz_transform_rect(ctm, fz_unit_rect)); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); pixmap = fz_image_to_pixmap(ctx, image, dx, dy); orig_pixmap = pixmap; fz_try(ctx) { mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } if (dx < pixmap->w && dy < pixmap->h) { int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(dev->ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) pixmap = scaled; } fz_paint_image(mask, bbox, state->shape, pixmap, ctm, 255); } fz_always(ctx) { fz_drop_pixmap(ctx, scaled); fz_drop_pixmap(ctx, orig_pixmap); } fz_catch(ctx) { fz_drop_pixmap(ctx, shape); fz_drop_pixmap(ctx, dest); fz_drop_pixmap(ctx, mask); fz_rethrow(ctx); } state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].mask = mask; }
void fz_run_display_list(fz_display_list *list, fz_device *dev, fz_matrix top_ctm, fz_bbox scissor, fz_cookie *cookie) { fz_display_node *node; fz_matrix ctm; fz_rect rect; fz_bbox bbox; int clipped = 0; int tiled = 0; int empty; int progress = 0; //LOGE("Count %d", count); if (cookie) { cookie->progress_max = list->last - list->first; cookie->progress = 0; } for (node = list->first; node; node = node->next) { /* Check the cookie for aborting */ if (cookie) { if (cookie->abort) break; cookie->progress = progress++; } /* 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_bbox_covering_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_STROKE_TEXT: case FZ_CMD_CLIP_IMAGE_MASK: case FZ_CMD_BEGIN_MASK: case FZ_CMD_BEGIN_GROUP: clipped++; continue; case FZ_CMD_CLIP_TEXT: /* Accumulated text has no extra pops */ if (node->flag != 2) 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; } } //free(node_array); }