diff options
Diffstat (limited to 'xps/xpstile.c')
-rw-r--r-- | xps/xpstile.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/xps/xpstile.c b/xps/xpstile.c new file mode 100644 index 00000000..d7118b52 --- /dev/null +++ b/xps/xpstile.c @@ -0,0 +1,399 @@ +/* Copyright (C) 2006-2010 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* XPS interpreter - tiles for pattern rendering */ + +#include "ghostxps.h" + +/* + * Parse a tiling brush (visual and image brushes at this time) common + * properties. Use the callback to draw the individual tiles. + */ + +enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y }; + +struct tile_closure_s +{ + xps_context_t *ctx; + char *base_uri; + xps_resource_t *dict; + xps_item_t *tag; + gs_rect viewbox; + int tile_mode; + void *user; + int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*); +}; + +static int +xps_paint_tiling_brush_clipped(struct tile_closure_s *c) +{ + xps_context_t *ctx = c->ctx; + int code; + + gs_moveto(ctx->pgs, c->viewbox.p.x, c->viewbox.p.y); + gs_lineto(ctx->pgs, c->viewbox.p.x, c->viewbox.q.y); + gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.q.y); + gs_lineto(ctx->pgs, c->viewbox.q.x, c->viewbox.p.y); + gs_closepath(ctx->pgs); + gs_clip(ctx->pgs); + gs_newpath(ctx->pgs); + + code = c->func(c->ctx, c->base_uri, c->dict, c->tag, c->user); + if (code < 0) + return gs_rethrow(code, "cannot draw clipped tile"); + + return 0; +} + +static int +xps_paint_tiling_brush(const gs_client_color *pcc, gs_state *pgs) +{ + const gs_client_pattern *ppat = gs_getpattern(pcc); + struct tile_closure_s *c = ppat->client_data; + xps_context_t *ctx = c->ctx; + gs_state *saved_pgs; + int code; + + saved_pgs = ctx->pgs; + ctx->pgs = pgs; + + gs_gsave(ctx->pgs); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + + if (c->tile_mode == TILE_FLIP_X || c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, c->viewbox.q.x * 2, 0.0); + gs_scale(ctx->pgs, -1.0, 1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + if (c->tile_mode == TILE_FLIP_Y || c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, 0.0, c->viewbox.q.y * 2); + gs_scale(ctx->pgs, 1.0, -1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + if (c->tile_mode == TILE_FLIP_X_Y) + { + gs_gsave(ctx->pgs); + gs_translate(ctx->pgs, c->viewbox.q.x * 2, c->viewbox.q.y * 2); + gs_scale(ctx->pgs, -1.0, -1.0); + code = xps_paint_tiling_brush_clipped(c); + if (code) + goto cleanup; + gs_grestore(ctx->pgs); + } + + ctx->pgs = saved_pgs; + + return 0; + +cleanup: + gs_grestore(ctx->pgs); + ctx->pgs = saved_pgs; + return gs_rethrow(code, "cannot draw tile"); +} + +int +xps_high_level_pattern(xps_context_t *ctx) +{ + gs_matrix m; + gs_rect bbox; + gs_fixed_rect clip_box; + int code; + gx_device_color *pdc = gs_currentdevicecolor_inline(ctx->pgs); + const gs_client_pattern *ppat = gs_getpattern(&pdc->ccolor); + gs_pattern1_instance_t *pinst = + (gs_pattern1_instance_t *)gs_currentcolor(ctx->pgs)->pattern; + + code = gx_pattern_cache_add_dummy_entry((gs_imager_state *)ctx->pgs, + pinst, ctx->pgs->device->color_info.depth); + if (code < 0) + return code; + + code = gs_gsave(ctx->pgs); + if (code < 0) + return code; + + dev_proc(ctx->pgs->device, get_initial_matrix)(ctx->pgs->device, &m); + gs_setmatrix(ctx->pgs, &m); + code = gs_bbox_transform(&ppat->BBox, &ctm_only(ctx->pgs), &bbox); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + clip_box.p.x = float2fixed(bbox.p.x); + clip_box.p.y = float2fixed(bbox.p.y); + clip_box.q.x = float2fixed(bbox.q.x); + clip_box.q.y = float2fixed(bbox.q.y); + code = gx_clip_to_rectangle(ctx->pgs, &clip_box); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, pinst->id, pinst, + pattern_manage__start_accum); + if (code < 0) { + gs_grestore(ctx->pgs); + return code; + } + + code = xps_paint_tiling_brush(&pdc->ccolor, ctx->pgs); + if (code) { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "high level pattern brush function failed"); + } + + code = gs_grestore(ctx->pgs); + if (code < 0) + return code; + + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, gx_no_bitmap_id, NULL, + pattern_manage__finish_accum); + + return code; +} + +static int +xps_remap_pattern(const gs_client_color *pcc, gs_state *pgs) +{ + const gs_client_pattern *ppat = gs_getpattern(pcc); + struct tile_closure_s *c = ppat->client_data; + xps_context_t *ctx = c->ctx; + int code; + + /* pgs->device is the newly created pattern accumulator, but we want to test the device + * that is 'behind' that, the actual output device, so we use the one from + * the saved XPS graphics state. + */ + code = dev_proc(ctx->pgs->device, pattern_manage)(ctx->pgs->device, ppat->uid.id, ppat, + pattern_manage__can_accum); + + if (code == 1) { + /* Device handles high-level patterns, so return 'remap'. + * This closes the internal accumulator device, as we no longer need + * it, and the error trickles back up to the PDL client. The client + * must then take action to start the device's accumulator, draw the + * pattern, close the device's accumulator and generate a cache entry. + */ + return gs_error_Remap_Color; + } else { + code = xps_paint_tiling_brush(pcc, pgs); + if (code) + return gs_rethrow(code, "remap pattern brush function failed"); + return 0; + } +} + +int +xps_parse_tiling_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict, xps_item_t *root, + int (*func)(xps_context_t*, char*, xps_resource_t*, xps_item_t*, void*), void *user) +{ + xps_item_t *node; + int code; + + char *opacity_att; + char *transform_att; + char *viewbox_att; + char *viewport_att; + char *tile_mode_att; + char *viewbox_units_att; + char *viewport_units_att; + + xps_item_t *transform_tag = NULL; + + gs_matrix transform; + gs_rect viewbox; + gs_rect viewport; + float scalex, scaley; + int tile_mode; + + opacity_att = xps_att(root, "Opacity"); + transform_att = xps_att(root, "Transform"); + viewbox_att = xps_att(root, "Viewbox"); + viewport_att = xps_att(root, "Viewport"); + tile_mode_att = xps_att(root, "TileMode"); + viewbox_units_att = xps_att(root, "ViewboxUnits"); + viewport_units_att = xps_att(root, "ViewportUnits"); + + for (node = xps_down(root); node; node = xps_next(node)) + { + if (!strcmp(xps_tag(node), "ImageBrush.Transform")) + transform_tag = xps_down(node); + if (!strcmp(xps_tag(node), "VisualBrush.Transform")) + transform_tag = xps_down(node); + } + + xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL); + + gs_make_identity(&transform); + if (transform_att) + xps_parse_render_transform(ctx, transform_att, &transform); + if (transform_tag) + xps_parse_matrix_transform(ctx, transform_tag, &transform); + + viewbox.p.x = 0.0; viewbox.p.y = 0.0; + viewbox.q.x = 1.0; viewbox.q.y = 1.0; + if (viewbox_att) + xps_parse_rectangle(ctx, viewbox_att, &viewbox); + + viewport.p.x = 0.0; viewport.p.y = 0.0; + viewport.q.x = 1.0; viewport.q.y = 1.0; + if (viewport_att) + xps_parse_rectangle(ctx, viewport_att, &viewport); + + /* some sanity checks on the viewport/viewbox size */ + if (fabs(viewport.q.x - viewport.p.x) < 0.01) return 0; + if (fabs(viewport.q.y - viewport.p.y) < 0.01) return 0; + if (fabs(viewbox.q.x - viewbox.p.x) < 0.01) return 0; + if (fabs(viewbox.q.y - viewbox.p.y) < 0.01) return 0; + + scalex = (viewport.q.x - viewport.p.x) / (viewbox.q.x - viewbox.p.x); + scaley = (viewport.q.y - viewport.p.y) / (viewbox.q.y - viewbox.p.y); + + tile_mode = TILE_NONE; + if (tile_mode_att) + { + if (!strcmp(tile_mode_att, "None")) + tile_mode = TILE_NONE; + if (!strcmp(tile_mode_att, "Tile")) + tile_mode = TILE_TILE; + if (!strcmp(tile_mode_att, "FlipX")) + tile_mode = TILE_FLIP_X; + if (!strcmp(tile_mode_att, "FlipY")) + tile_mode = TILE_FLIP_Y; + if (!strcmp(tile_mode_att, "FlipXY")) + tile_mode = TILE_FLIP_X_Y; + } + + gs_gsave(ctx->pgs); + + code = xps_begin_opacity(ctx, base_uri, dict, opacity_att, NULL); + if (code) + { + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot create transparency group"); + } + + /* TODO(tor): check viewport and tiling to see if we can set it to TILE_NONE */ + + if (tile_mode != TILE_NONE) + { + struct tile_closure_s closure; + + gs_client_pattern gspat; + gs_client_color gscolor; + gs_color_space *cs; + + closure.ctx = ctx; + closure.base_uri = base_uri; + closure.dict = dict; + closure.tag = root; + closure.tile_mode = tile_mode; + closure.user = user; + closure.func = func; + + closure.viewbox.p.x = viewbox.p.x; + closure.viewbox.p.y = viewbox.p.y; + closure.viewbox.q.x = viewbox.q.x; + closure.viewbox.q.y = viewbox.q.y; + + gs_pattern1_init(&gspat); + uid_set_UniqueID(&gspat.uid, gs_next_ids(ctx->memory, 1)); + gspat.PaintType = 1; + gspat.TilingType = 1; + gspat.PaintProc = xps_remap_pattern; + gspat.client_data = &closure; + + gspat.XStep = viewbox.q.x - viewbox.p.x; + gspat.YStep = viewbox.q.y - viewbox.p.y; + gspat.BBox.p.x = viewbox.p.x; + gspat.BBox.p.y = viewbox.p.y; + gspat.BBox.q.x = viewbox.q.x; + gspat.BBox.q.y = viewbox.q.y; + + if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y) + { + gspat.BBox.q.x += gspat.XStep; + gspat.XStep *= 2; + } + + if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y) + { + gspat.BBox.q.y += gspat.YStep; + gspat.YStep *= 2; + } + + gs_matrix_translate(&transform, viewport.p.x, viewport.p.y, &transform); + gs_matrix_scale(&transform, scalex, scaley, &transform); + gs_matrix_translate(&transform, -viewbox.p.x, -viewbox.p.y, &transform); + + cs = ctx->srgb; + gs_setcolorspace(ctx->pgs, cs); + gs_makepattern(&gscolor, &gspat, &transform, ctx->pgs, NULL); + gs_setpattern(ctx->pgs, &gscolor); + + xps_fill(ctx); + + /* gs_makepattern increments the pattern count stored in the color + * structure. We will discard the color struct (its on the stack) + * so we need to decrement the reference before we throw away + * the structure. + */ + gs_pattern_reference(&gscolor, -1); + } + else + { + xps_clip(ctx); + + gs_concat(ctx->pgs, &transform); + + gs_translate(ctx->pgs, viewport.p.x, viewport.p.y); + gs_scale(ctx->pgs, scalex, scaley); + gs_translate(ctx->pgs, -viewbox.p.x, -viewbox.p.y); + + gs_moveto(ctx->pgs, viewbox.p.x, viewbox.p.y); + gs_lineto(ctx->pgs, viewbox.p.x, viewbox.q.y); + gs_lineto(ctx->pgs, viewbox.q.x, viewbox.q.y); + gs_lineto(ctx->pgs, viewbox.q.x, viewbox.p.y); + gs_closepath(ctx->pgs); + gs_clip(ctx->pgs); + gs_newpath(ctx->pgs); + + code = func(ctx, base_uri, dict, root, user); + if (code < 0) + { + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); + gs_grestore(ctx->pgs); + return gs_rethrow(code, "cannot draw tile"); + } + } + + xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL); + + gs_grestore(ctx->pgs); + + return 0; +} |