diff options
author | Robin Watts <robin.watts@artifex.com> | 2017-04-26 12:03:07 +0100 |
---|---|---|
committer | Robin Watts <Robin.Watts@artifex.com> | 2017-06-13 16:03:59 +0100 |
commit | f1386c6778baded82a3b98215264c2613efc7fe7 (patch) | |
tree | 63c093d906d2a69f3d978577930484968eff7f3e /source/fitz | |
parent | 1bcfc08545f4bed3d1197af1756f7465af11cf96 (diff) | |
download | mupdf-f1386c6778baded82a3b98215264c2613efc7fe7.tar.xz |
Introduce fz_rasterizer encapsulation
This is intended to be a way to allow us to implement
compiletime/runtime selection of different scan converter
implementations.
Diffstat (limited to 'source/fitz')
-rw-r--r-- | source/fitz/draw-device.c | 88 | ||||
-rw-r--r-- | source/fitz/draw-edge.c | 480 | ||||
-rw-r--r-- | source/fitz/draw-imp.h | 325 | ||||
-rw-r--r-- | source/fitz/draw-path.c | 491 | ||||
-rw-r--r-- | source/fitz/draw-rasterize.c | 257 |
5 files changed, 1029 insertions, 612 deletions
diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c index 2ed90bb0..af8e8384 100644 --- a/source/fitz/draw-device.c +++ b/source/fitz/draw-device.c @@ -43,7 +43,7 @@ struct fz_draw_device_s { fz_device super; fz_matrix transform; - fz_gel *gel; + fz_rasterizer *rast; int flags; int top; fz_scale_cache *cache_x; @@ -285,7 +285,7 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); - fz_gel *gel = dev->gel; + fz_rasterizer *rast = dev->rast; float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; @@ -302,11 +302,8 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve if (flatness < 0.001f) flatness = 0.001f; - fz_flatten_fill_path(ctx, gel, path, &ctm, flatness, &state->scissor); - - fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor); - - if (fz_is_empty_irect(&bbox)) + fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest, &bbox), &state->scissor); + if (fz_flatten_fill_path(ctx, rast, path, &ctm, flatness, &bbox, &bbox)) return; if (state->blendmode & FZ_BLEND_KNOCKOUT) @@ -323,13 +320,14 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve i = 0; colorbv[i] = alpha * 255; - fz_scan_convert(ctx, gel, even_odd, &bbox, state->dest, colorbv); + fz_convert_rasterizer(ctx, rast, even_odd, state->dest, colorbv); if (state->shape) { - fz_flatten_fill_path(ctx, gel, path, &ctm, flatness, &state->scissor); + if (!rast->fns.reusable) + fz_flatten_fill_path(ctx, rast, path, &ctm, flatness, &bbox, NULL); colorbv[0] = alpha * 255; - fz_scan_convert(ctx, gel, even_odd, &bbox, state->shape, colorbv); + fz_convert_rasterizer(ctx, rast, even_odd, state->shape, colorbv); } if (state->blendmode & FZ_BLEND_KNOCKOUT) @@ -342,7 +340,7 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); - fz_gel *gel = dev->gel; + fz_rasterizer *rast = dev->rast; float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; @@ -366,11 +364,8 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const if (flatness < 0.001f) flatness = 0.001f; - fz_flatten_stroke_path(ctx, gel, path, stroke, &ctm, flatness, linewidth, &state->scissor); - - fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor); - - if (fz_is_empty_irect(&bbox)) + fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest, &bbox), &state->scissor); + if (fz_flatten_stroke_path(ctx, rast, path, stroke, &ctm, flatness, linewidth, &bbox, &bbox)) return; if (state->blendmode & FZ_BLEND_KNOCKOUT) @@ -394,13 +389,14 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_dump_blend(ctx, state->shape, "/"); printf("\n"); #endif - fz_scan_convert(ctx, gel, 0, &bbox, state->dest, colorbv); + fz_convert_rasterizer(ctx, rast, 0, state->dest, colorbv); if (state->shape) { - fz_flatten_stroke_path(ctx, gel, path, stroke, &ctm, flatness, linewidth, &state->scissor); + if (!rast->fns.reusable) + (void)fz_flatten_stroke_path(ctx, rast, path, stroke, &ctm, flatness, linewidth, &bbox, NULL); colorbv[0] = 255; - fz_scan_convert(ctx, gel, 0, &bbox, state->shape, colorbv); + fz_convert_rasterizer(ctx, rast, 0, state->shape, colorbv); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); @@ -419,33 +415,32 @@ fz_draw_clip_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); - fz_gel *gel = dev->gel; + fz_rasterizer *rast = dev->rast; float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; fz_irect bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; + fz_irect local_scissor; + fz_irect *scissor_ptr = &state->scissor; if (flatness < 0.001f) flatness = 0.001f; - fz_flatten_fill_path(ctx, gel, path, &ctm, flatness, &state->scissor); - state = push_stack(ctx, dev); STACK_PUSHED("clip path"); model = state->dest->colorspace; - fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor); if (scissor) { - fz_irect bbox2; fz_rect tscissor = *scissor; fz_transform_rect(&tscissor, &dev->transform); - fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, &tscissor)); + scissor_ptr = fz_intersect_irect(fz_irect_from_rect(&local_scissor, &tscissor), scissor_ptr); } + fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest, &bbox), scissor_ptr); - if (fz_is_empty_irect(&bbox) || fz_is_rect_gel(ctx, gel)) + if (fz_flatten_fill_path(ctx, rast, path, &ctm, flatness, &bbox, &bbox) || fz_is_rect_rasterizer(ctx, rast)) { state[1].scissor = bbox; state[1].mask = NULL; @@ -467,7 +462,7 @@ fz_draw_clip_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve fz_clear_pixmap(ctx, state[1].shape); } - fz_scan_convert(ctx, gel, even_odd, &bbox, state[1].mask, NULL); + fz_convert_rasterizer(ctx, rast, even_odd, state[1].mask, NULL); state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS @@ -485,7 +480,7 @@ fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); - fz_gel *gel = dev->gel; + fz_rasterizer *rast = dev->rast; float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; @@ -495,6 +490,8 @@ fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, fz_colorspace *model; float aa_level = 2.0f/(fz_graphics_aa_level(ctx)+2); float mlw = fz_graphics_min_line_width(ctx); + fz_irect local_scissor; + fz_irect *scissor_ptr = &state->scissor; if (mlw > aa_level) aa_level = mlw; @@ -503,19 +500,26 @@ fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, if (flatness < 0.001f) flatness = 0.001f; - fz_flatten_stroke_path(ctx, gel, path, stroke, &ctm, flatness, linewidth, &state->scissor); - state = push_stack(ctx, dev); STACK_PUSHED("clip stroke"); model = state->dest->colorspace; - fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor); if (scissor) { - fz_irect bbox2; fz_rect tscissor = *scissor; fz_transform_rect(&tscissor, &dev->transform); - fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, &tscissor)); + scissor_ptr = fz_intersect_irect(fz_irect_from_rect(&local_scissor, &tscissor), scissor_ptr); + } + fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest, &bbox), scissor_ptr); + + if (fz_flatten_stroke_path(ctx, rast, path, stroke, &ctm, flatness, linewidth, &bbox, &bbox)) + { + state[1].scissor = bbox; + state[1].mask = NULL; +#ifdef DUMP_GROUP_BLENDS + dump_spaces(dev->top-1, "Clip (stroke, empty) begin\n"); +#endif + return; } fz_try(ctx) @@ -537,8 +541,7 @@ fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, fz_clear_pixmap(ctx, state[1].shape); } - if (!fz_is_empty_irect(&bbox)) - fz_scan_convert(ctx, gel, 0, &bbox, state[1].mask, NULL); + fz_convert_rasterizer(ctx, rast, 0, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; @@ -2247,7 +2250,7 @@ static void fz_draw_drop_device(fz_context *ctx, fz_device *devp) { fz_draw_device *dev = (fz_draw_device*)devp; - fz_gel *gel = dev->gel; + fz_rasterizer *rast = dev->rast; /* pop and free the stacks */ if (dev->top > 0) @@ -2271,7 +2274,7 @@ fz_draw_drop_device(fz_context *ctx, fz_device *devp) fz_free(ctx, dev->stack); fz_drop_scale_cache(ctx, dev->cache_x); fz_drop_scale_cache(ctx, dev->cache_y); - fz_drop_gel(ctx, gel); + fz_drop_rasterizer(ctx, rast); } static void @@ -2333,7 +2336,7 @@ fz_new_draw_device(fz_context *ctx, const fz_matrix *transform, fz_pixmap *dest) fz_try(ctx) { - dev->gel = fz_new_gel(ctx); + dev->rast = fz_new_rasterizer(ctx); dev->cache_x = fz_new_scale_cache(ctx); dev->cache_y = fz_new_scale_cache(ctx); } @@ -2374,14 +2377,13 @@ fz_new_draw_device_type3(fz_context *ctx, const fz_matrix *transform, fz_pixmap fz_irect * fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth) { - fz_gel *gel = fz_new_gel(ctx); + fz_rasterizer *rast = fz_new_rasterizer(ctx); if (stroke) - fz_flatten_stroke_path(ctx, gel, path, stroke, ctm, flatness, linewidth, scissor); + (void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, scissor, bbox); else - fz_flatten_fill_path(ctx, gel, path, ctm, flatness, scissor); - fz_bound_gel(ctx, gel, bbox); - fz_drop_gel(ctx, gel); + (void)fz_flatten_fill_path(ctx, rast, path, ctm, flatness, scissor, bbox); + fz_drop_rasterizer(ctx, rast); return bbox; } diff --git a/source/fitz/draw-edge.c b/source/fitz/draw-edge.c index 68c98690..9b417345 100644 --- a/source/fitz/draw-edge.c +++ b/source/fitz/draw-edge.c @@ -1,241 +1,6 @@ #include "mupdf/fitz.h" #include "draw-imp.h" -#include <string.h> -#include <math.h> -#include <stdlib.h> -#include <limits.h> -#include <assert.h> - -#define BBOX_MIN -(1<<20) -#define BBOX_MAX (1<<20) - -/* divide and floor towards -inf */ -static inline int fz_idiv(int a, int b) -{ - return a < 0 ? (a - b + 1) / b : a / b; -} - -/* divide and ceil towards inf */ -static inline int fz_idiv_up(int a, int b) -{ - return a < 0 ? a / b : (a + b - 1) / b; -} - -/* If AA_BITS is defined, then we assume constant N bits of antialiasing. We - * will attempt to provide at least that number of bits of accuracy in the - * antialiasing (to a maximum of 8). If it is defined to be 0 then no - * antialiasing is done. If it is undefined to we will leave the antialiasing - * accuracy as a run time choice. - */ -struct fz_aa_context_s -{ - int hscale; - int vscale; - int scale; - int bits; - int text_bits; - float min_line_width; -}; - -void fz_new_aa_context(fz_context *ctx) -{ -#ifndef AA_BITS - ctx->aa = fz_malloc_struct(ctx, fz_aa_context); - ctx->aa->hscale = 17; - ctx->aa->vscale = 15; - ctx->aa->scale = 256; - ctx->aa->bits = 8; - ctx->aa->text_bits = 8; - -#define fz_aa_hscale (ctx->aa->hscale) -#define fz_aa_vscale (ctx->aa->vscale) -#define fz_aa_scale (ctx->aa->scale) -#define fz_aa_bits (ctx->aa->bits) -#define fz_aa_text_bits (ctx->aa->text_bits) -#define AA_SCALE(scale, x) ((x * scale) >> 8) - -#endif -} - -void fz_copy_aa_context(fz_context *dst, fz_context *src) -{ - if (dst && dst->aa && src && src->aa) - memcpy(dst->aa, src->aa, sizeof(*src->aa)); -} - -void fz_drop_aa_context(fz_context *ctx) -{ - if (!ctx) - return; -#ifndef AA_BITS - fz_free(ctx, ctx->aa); - ctx->aa = NULL; -#endif -} - -#ifdef AA_BITS - -#define fz_aa_scale 0 - -#if AA_BITS > 6 -#define AA_SCALE(s, x) (x) -#define fz_aa_hscale 17 -#define fz_aa_vscale 15 -#define fz_aa_bits 8 -#define fz_aa_text_bits 8 - -#elif AA_BITS > 4 -#define AA_SCALE(s, x) ((x * 255) >> 6) -#define fz_aa_hscale 8 -#define fz_aa_vscale 8 -#define fz_aa_bits 6 -#define fz_aa_text_bits 6 - -#elif AA_BITS > 2 -#define AA_SCALE(s, x) (x * 17) -#define fz_aa_hscale 5 -#define fz_aa_vscale 3 -#define fz_aa_bits 4 -#define fz_aa_text_bits 4 - -#elif AA_BITS > 0 -#define AA_SCALE(s, x) ((x * 255) >> 2) -#define fz_aa_hscale 2 -#define fz_aa_vscale 2 -#define fz_aa_bits 2 -#define fz_aa_text_bits 2 - -#else -#define AA_SCALE(s, x) (x * 255) -#define fz_aa_hscale 1 -#define fz_aa_vscale 1 -#define fz_aa_bits 0 -#define fz_aa_text_bits 0 - -#endif -#endif - -int -fz_aa_level(fz_context *ctx) -{ - return fz_aa_bits; -} - -int -fz_graphics_aa_level(fz_context *ctx) -{ - return fz_aa_bits; -} - -int -fz_text_aa_level(fz_context *ctx) -{ - return fz_aa_text_bits; -} - -#ifndef AA_BITS -static void -set_gfx_level(fz_context *ctx, int level) -{ - if (level > 6) - { - fz_aa_hscale = 17; - fz_aa_vscale = 15; - fz_aa_bits = 8; - } - else if (level > 4) - { - fz_aa_hscale = 8; - fz_aa_vscale = 8; - fz_aa_bits = 6; - } - else if (level > 2) - { - fz_aa_hscale = 5; - fz_aa_vscale = 3; - fz_aa_bits = 4; - } - else if (level > 0) - { - fz_aa_hscale = 2; - fz_aa_vscale = 2; - fz_aa_bits = 2; - } - else - { - fz_aa_hscale = 1; - fz_aa_vscale = 1; - fz_aa_bits = 0; - } - fz_aa_scale = 0xFF00 / (fz_aa_hscale * fz_aa_vscale); -} - -static void -set_txt_level(fz_context *ctx, int level) -{ - if (level > 6) - fz_aa_text_bits = 8; - else if (level > 4) - fz_aa_text_bits = 6; - else if (level > 2) - fz_aa_text_bits = 4; - else if (level > 0) - fz_aa_text_bits = 2; - else - fz_aa_text_bits = 0; -} -#endif /* AA_BITS */ - -void -fz_set_aa_level(fz_context *ctx, int level) -{ -#ifdef AA_BITS - fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); -#else - set_gfx_level(ctx, level); - set_txt_level(ctx, level); -#endif -} - -void -fz_set_text_aa_level(fz_context *ctx, int level) -{ -#ifdef AA_BITS - fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); -#else - set_txt_level(ctx, level); -#endif -} - -void -fz_set_graphics_aa_level(fz_context *ctx, int level) -{ -#ifdef AA_BITS - fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); -#else - set_gfx_level(ctx, level); -#endif -} - -void -fz_set_graphics_min_line_width(fz_context *ctx, float min_line_width) -{ - if (!ctx || !ctx->aa) - return; - - ctx->aa->min_line_width = min_line_width; -} - -float -fz_graphics_min_line_width(fz_context *ctx) -{ - if (!ctx || !ctx->aa) - return 0; - - return ctx->aa->min_line_width; -} - /* * Global Edge List -- list of straight path segments for scan conversion * @@ -244,88 +9,38 @@ fz_graphics_min_line_width(fz_context *ctx) * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) */ -typedef struct fz_edge_s fz_edge; - -struct fz_edge_s +typedef struct fz_edge_s { int x, e, h, y; int adj_up, adj_down; int xmove; int xdir, ydir; /* -1 or +1 */ -}; +} fz_edge; -struct fz_gel_s +typedef struct fz_gel_s { - fz_rect clip; - fz_irect bbox; + fz_rasterizer super; int cap, len; fz_edge *edges; int acap, alen; fz_edge **active; -}; - -fz_gel * -fz_new_gel(fz_context *ctx) -{ - fz_gel *gel; - - gel = fz_malloc_struct(ctx, fz_gel); - fz_try(ctx) - { - gel->edges = NULL; - gel->cap = 512; - gel->len = 0; - gel->edges = fz_malloc_array(ctx, gel->cap, sizeof(fz_edge)); - - gel->clip.x0 = gel->clip.y0 = BBOX_MIN; - gel->clip.x1 = gel->clip.y1 = BBOX_MAX; - - gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX; - gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN; - - gel->acap = 64; - gel->alen = 0; - gel->active = fz_malloc_array(ctx, gel->acap, sizeof(fz_edge*)); - } - fz_catch(ctx) - { - if (gel) - fz_free(ctx, gel->edges); - fz_free(ctx, gel); - fz_rethrow(ctx); - } - - return gel; -} +} fz_gel; -void -fz_reset_gel(fz_context *ctx, fz_gel *gel, const fz_irect *clip) +static int +fz_reset_gel(fz_context *ctx, fz_rasterizer *rast) { - const int hscale = fz_aa_hscale; - const int vscale = fz_aa_vscale; - - if (fz_is_infinite_irect(clip)) - { - gel->clip.x0 = gel->clip.y0 = BBOX_MIN; - gel->clip.x1 = gel->clip.y1 = BBOX_MAX; - } - else { - gel->clip.x0 = clip->x0 * hscale; - gel->clip.x1 = clip->x1 * hscale; - gel->clip.y0 = clip->y0 * vscale; - gel->clip.y1 = clip->y1 * vscale; - } - - gel->bbox.x0 = gel->bbox.y0 = BBOX_MAX; - gel->bbox.x1 = gel->bbox.y1 = BBOX_MIN; + fz_gel *gel = (fz_gel *)rast; gel->len = 0; gel->alen = 0; + + return 0; } -void -fz_drop_gel(fz_context *ctx, fz_gel *gel) +static void +fz_drop_gel(fz_context *ctx, fz_rasterizer *rast) { + fz_gel *gel = (fz_gel *)rast; if (gel == NULL) return; fz_free(ctx, gel->active); @@ -333,40 +48,6 @@ fz_drop_gel(fz_context *ctx, fz_gel *gel) fz_free(ctx, gel); } -fz_irect * -fz_bound_gel(fz_context *ctx, const fz_gel *gel, fz_irect *bbox) -{ - const int hscale = fz_aa_hscale; - const int vscale = fz_aa_vscale; - - if (gel->len == 0) - { - *bbox = fz_empty_irect; - } - else - { - bbox->x0 = fz_idiv(gel->bbox.x0, hscale); - bbox->y0 = fz_idiv(gel->bbox.y0, vscale); - bbox->x1 = fz_idiv_up(gel->bbox.x1, hscale); - bbox->y1 = fz_idiv_up(gel->bbox.y1, vscale); - } - return bbox; -} - -fz_rect * -fz_gel_scissor(fz_context *ctx, const fz_gel *gel, fz_rect *r) -{ - const int hscale = fz_aa_hscale; - const int vscale = fz_aa_vscale; - - r->x0 = gel->clip.x0 / hscale; - r->x1 = gel->clip.x1 / vscale; - r->y0 = gel->clip.y0 / hscale; - r->y1 = gel->clip.y1 / vscale; - - return r; -} - enum { INSIDE, OUTSIDE, LEAVE, ENTER }; #define clip_lerp_y(v,m,x0,y0,x1,y1,t) clip_lerp_x(v,m,y0,x0,y1,x1,t) @@ -397,8 +78,9 @@ clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out) } static void -fz_insert_gel_raw(fz_context *ctx, fz_gel *gel, int x0, int y0, int x1, int y1) +fz_insert_gel_raw(fz_context *ctx, fz_rasterizer *ras, int x0, int y0, int x1, int y1) { + fz_gel *gel = (fz_gel *)ras; fz_edge *edge; int dx, dy; int winding; @@ -416,13 +98,13 @@ fz_insert_gel_raw(fz_context *ctx, fz_gel *gel, int x0, int y0, int x1, int y1) else winding = 1; - if (x0 < gel->bbox.x0) gel->bbox.x0 = x0; - if (x0 > gel->bbox.x1) gel->bbox.x1 = x0; - if (x1 < gel->bbox.x0) gel->bbox.x0 = x1; - if (x1 > gel->bbox.x1) gel->bbox.x1 = x1; + if (x0 < gel->super.bbox.x0) gel->super.bbox.x0 = x0; + if (x0 > gel->super.bbox.x1) gel->super.bbox.x1 = x0; + if (x1 < gel->super.bbox.x0) gel->super.bbox.x0 = x1; + if (x1 > gel->super.bbox.x1) gel->super.bbox.x1 = x1; - if (y0 < gel->bbox.y0) gel->bbox.y0 = y0; - if (y1 > gel->bbox.y1) gel->bbox.y1 = y1; + if (y0 < gel->super.bbox.y0) gel->super.bbox.y0 = y0; + if (y1 > gel->super.bbox.y1) gel->super.bbox.y1 = y1; if (gel->len + 1 == gel->cap) { int new_cap = gel->cap * 2; @@ -462,8 +144,8 @@ fz_insert_gel_raw(fz_context *ctx, fz_gel *gel, int x0, int y0, int x1, int y1) } } -void -fz_insert_gel(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +static void +fz_insert_gel(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1, int rev) { int x0, y0, x1, y1; int d, v; @@ -484,51 +166,51 @@ fz_insert_gel(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1, flo x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale); y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale); - d = clip_lerp_y(gel->clip.y0, 0, x0, y0, x1, y1, &v); + d = clip_lerp_y(ras->clip.y0, 0, x0, y0, x1, y1, &v); if (d == OUTSIDE) return; - if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; } - if (d == ENTER) { y0 = gel->clip.y0; x0 = v; } + if (d == LEAVE) { y1 = ras->clip.y0; x1 = v; } + if (d == ENTER) { y0 = ras->clip.y0; x0 = v; } - d = clip_lerp_y(gel->clip.y1, 1, x0, y0, x1, y1, &v); + d = clip_lerp_y(ras->clip.y1, 1, x0, y0, x1, y1, &v); if (d == OUTSIDE) return; - if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; } - if (d == ENTER) { y0 = gel->clip.y1; x0 = v; } + if (d == LEAVE) { y1 = ras->clip.y1; x1 = v; } + if (d == ENTER) { y0 = ras->clip.y1; x0 = v; } - d = clip_lerp_x(gel->clip.x0, 0, x0, y0, x1, y1, &v); + d = clip_lerp_x(ras->clip.x0, 0, x0, y0, x1, y1, &v); if (d == OUTSIDE) { - x0 = x1 = gel->clip.x0; + x0 = x1 = ras->clip.x0; } if (d == LEAVE) { - fz_insert_gel_raw(ctx, gel, gel->clip.x0, v, gel->clip.x0, y1); - x1 = gel->clip.x0; + fz_insert_gel_raw(ctx, ras, ras->clip.x0, v, ras->clip.x0, y1); + x1 = ras->clip.x0; y1 = v; } if (d == ENTER) { - fz_insert_gel_raw(ctx, gel, gel->clip.x0, y0, gel->clip.x0, v); - x0 = gel->clip.x0; + fz_insert_gel_raw(ctx, ras, ras->clip.x0, y0, ras->clip.x0, v); + x0 = ras->clip.x0; y0 = v; } - d = clip_lerp_x(gel->clip.x1, 1, x0, y0, x1, y1, &v); + d = clip_lerp_x(ras->clip.x1, 1, x0, y0, x1, y1, &v); if (d == OUTSIDE) { - x0 = x1 = gel->clip.x1; + x0 = x1 = ras->clip.x1; } if (d == LEAVE) { - fz_insert_gel_raw(ctx, gel, gel->clip.x1, v, gel->clip.x1, y1); - x1 = gel->clip.x1; + fz_insert_gel_raw(ctx, ras, ras->clip.x1, v, ras->clip.x1, y1); + x1 = ras->clip.x1; y1 = v; } if (d == ENTER) { - fz_insert_gel_raw(ctx, gel, gel->clip.x1, y0, gel->clip.x1, v); - x0 = gel->clip.x1; + fz_insert_gel_raw(ctx, ras, ras->clip.x1, y0, ras->clip.x1, v); + x0 = ras->clip.x1; y0 = v; } - fz_insert_gel_raw(ctx, gel, x0, y0, x1, y1); + fz_insert_gel_raw(ctx, ras, x0, y0, x1, y1); } -void -fz_insert_gel_rect(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +static void +fz_insert_gel_rect(fz_context *ctx, fz_rasterizer *ras, float fx0, float fy0, float fx1, float fy1) { int x0, y0, x1, y1; const int hscale = fz_aa_hscale; @@ -555,10 +237,10 @@ fz_insert_gel_rect(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1 fy1 = floorf(fy1 * vscale); } - fx0 = fz_clamp(fx0, gel->clip.x0, gel->clip.x1); - fx1 = fz_clamp(fx1, gel->clip.x0, gel->clip.x1); - fy0 = fz_clamp(fy0, gel->clip.y0, gel->clip.y1); - fy1 = fz_clamp(fy1, gel->clip.y0, gel->clip.y1); + fx0 = fz_clamp(fx0, ras->clip.x0, ras->clip.x1); + fx1 = fz_clamp(fx1, ras->clip.x0, ras->clip.x1); + fy0 = fz_clamp(fy0, ras->clip.y0, ras->clip.y1); + fy1 = fz_clamp(fy1, ras->clip.y0, ras->clip.y1); /* Call fz_clamp so that clamping is done in the float domain, THEN * cast down to an int. Calling fz_clampi causes problems due to the @@ -569,8 +251,8 @@ fz_insert_gel_rect(fz_context *ctx, fz_gel *gel, float fx0, float fy0, float fx1 x1 = (int)fz_clamp(fx1, BBOX_MIN * hscale, BBOX_MAX * hscale); y1 = (int)fz_clamp(fy1, BBOX_MIN * vscale, BBOX_MAX * vscale); - fz_insert_gel_raw(ctx, gel, x1, y0, x1, y1); - fz_insert_gel_raw(ctx, gel, x0, y1, x0, y0); + fz_insert_gel_raw(ctx, ras, x1, y0, x1, y1); + fz_insert_gel_raw(ctx, ras, x0, y1, x0, y0); } static int @@ -631,9 +313,10 @@ sort_gel(fz_context *ctx, fz_gel *gel) #endif } -int -fz_is_rect_gel(fz_context *ctx, fz_gel *gel) +static int +fz_is_rect_gel(fz_context *ctx, fz_rasterizer *ras) { + fz_gel *gel = (fz_gel *)ras; /* a rectangular path is converted into two vertical edges of identical height */ if (gel->len == 2) { @@ -868,8 +551,8 @@ fz_scan_convert_aa(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *cli const int hscale = fz_aa_hscale; const int vscale = fz_aa_vscale; - int xmin = fz_idiv(gel->bbox.x0, hscale); - int xmax = fz_idiv_up(gel->bbox.x1, hscale); + int xmin = fz_idiv(gel->super.bbox.x0, hscale); + int xmax = fz_idiv_up(gel->super.bbox.x1, hscale); int xofs = xmin * hscale; @@ -1147,13 +830,10 @@ fz_scan_convert_sharp(fz_context *ctx, } } -void -fz_scan_convert(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color) +static void +fz_convert_gel(fz_context *ctx, fz_rasterizer *rast, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color) { - fz_irect local_clip; - - if (fz_is_empty_irect(fz_intersect_irect(fz_pixmap_bbox_no_ctx(dst, &local_clip), clip))) - return; + fz_gel *gel = (fz_gel *)rast; sort_gel(ctx, gel); @@ -1167,7 +847,7 @@ fz_scan_convert(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, assert(fn); if (fn == NULL) return; - fz_scan_convert_aa(ctx, gel, eofill, &local_clip, dst, color, fn); + fz_scan_convert_aa(ctx, gel, eofill, clip, dst, color, fn); } else { @@ -1175,6 +855,46 @@ fz_scan_convert(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, assert(fn); if (fn == NULL) return; - fz_scan_convert_sharp(ctx, gel, eofill, &local_clip, dst, color, (fz_solid_color_painter_t *)fn); + fz_scan_convert_sharp(ctx, gel, eofill, clip, dst, color, (fz_solid_color_painter_t *)fn); + } +} + +static const fz_rasterizer_fns gel_rasterizer = +{ + fz_drop_gel, + fz_reset_gel, + NULL, /* postindex */ + fz_insert_gel, + fz_insert_gel_rect, + NULL, /* gap */ + fz_convert_gel, + fz_is_rect_gel, + 0 /* Not reusable */ +}; + +fz_rasterizer * +fz_new_gel(fz_context *ctx) +{ + fz_gel *gel; + + gel = fz_new_derived_rasterizer(ctx, fz_gel, &gel_rasterizer); + fz_try(ctx) + { + gel->edges = NULL; + gel->cap = 512; + gel->len = 0; + gel->edges = fz_malloc_array(ctx, gel->cap, sizeof(fz_edge)); + + gel->acap = 64; + gel->alen = 0; + gel->active = fz_malloc_array(ctx, gel->acap, sizeof(fz_edge*)); + } + fz_catch(ctx) + { + fz_free(ctx, gel->edges); + fz_free(ctx, gel); + fz_rethrow(ctx); } + + return &gel->super; } diff --git a/source/fitz/draw-imp.h b/source/fitz/draw-imp.h index 52c10257..5ce1879e 100644 --- a/source/fitz/draw-imp.h +++ b/source/fitz/draw-imp.h @@ -1,26 +1,325 @@ #ifndef MUPDF_DRAW_IMP_H #define MUPDF_DRAW_IMP_H +#define BBOX_MIN -(1<<20) +#define BBOX_MAX (1<<20) + +/* divide and floor towards -inf */ +static inline int fz_idiv(int a, int b) +{ + return a < 0 ? (a - b + 1) / b : a / b; +} + +/* divide and ceil towards inf */ +static inline int fz_idiv_up(int a, int b) +{ + return a < 0 ? a / b : (a + b - 1) / b; +} + +#ifdef AA_BITS + +#define fz_aa_scale 0 + +#if AA_BITS > 6 +#define AA_SCALE(s, x) (x) +#define fz_aa_hscale 17 +#define fz_aa_vscale 15 +#define fz_aa_bits 8 +#define fz_aa_text_bits 8 + +#elif AA_BITS > 4 +#define AA_SCALE(s, x) ((x * 255) >> 6) +#define fz_aa_hscale 8 +#define fz_aa_vscale 8 +#define fz_aa_bits 6 +#define fz_aa_text_bits 6 + +#elif AA_BITS > 2 +#define AA_SCALE(s, x) (x * 17) +#define fz_aa_hscale 5 +#define fz_aa_vscale 3 +#define fz_aa_bits 4 +#define fz_aa_text_bits 4 + +#elif AA_BITS > 0 +#define AA_SCALE(s, x) ((x * 255) >> 2) +#define fz_aa_hscale 2 +#define fz_aa_vscale 2 +#define fz_aa_bits 2 +#define fz_aa_text_bits 2 + +#else +#define AA_SCALE(s, x) (x * 255) +#define fz_aa_hscale 1 +#define fz_aa_vscale 1 +#define fz_aa_bits 0 +#define fz_aa_text_bits 0 + +#endif +#else + +#define AA_SCALE(scale, x) ((x * scale) >> 8) +#define fz_aa_hscale (ctx->aa->hscale) +#define fz_aa_vscale (ctx->aa->vscale) +#define fz_aa_scale (ctx->aa->scale) +#define fz_aa_bits (ctx->aa->bits) +#define fz_aa_text_bits (ctx->aa->text_bits) + +#endif + +/* If AA_BITS is defined, then we assume constant N bits of antialiasing. We + * will attempt to provide at least that number of bits of accuracy in the + * antialiasing (to a maximum of 8). If it is defined to be 0 then no + * antialiasing is done. If it is undefined to we will leave the antialiasing + * accuracy as a run time choice. + */ +struct fz_aa_context_s +{ + int hscale; + int vscale; + int scale; + int bits; + int text_bits; + float min_line_width; +}; + /* * Scan converter */ -typedef struct fz_gel_s fz_gel; +typedef struct fz_rasterizer_s fz_rasterizer; + +typedef void (fz_rasterizer_drop_fn)(fz_context *ctx, fz_rasterizer *r); +typedef int (fz_rasterizer_reset_fn)(fz_context *ctx, fz_rasterizer *r); +typedef void (fz_rasterizer_postindex_fn)(fz_context *ctx, fz_rasterizer *r); +typedef void (fz_rasterizer_insert_fn)(fz_context *ctx, fz_rasterizer *r, float x0, float y0, float x1, float y1, int rev); +typedef void (fz_rasterizer_insert_rect_fn)(fz_context *ctx, fz_rasterizer *r, float fx0, float fy0, float fx1, float fy1); +typedef void (fz_rasterizer_gap_fn)(fz_context *ctx, fz_rasterizer *r); +typedef fz_irect *(fz_rasterizer_bound_fn)(fz_context *ctx, const fz_rasterizer *r, fz_irect *bbox); +typedef void (fz_rasterizer_fn)(fz_context *ctx, fz_rasterizer *r, int eofill, const fz_irect *clip, fz_pixmap *pix, unsigned char *colorbv); +typedef int (fz_rasterizer_is_rect_fn)(fz_context *ctx, fz_rasterizer *r); + +typedef struct +{ + fz_rasterizer_drop_fn *drop; + fz_rasterizer_reset_fn *reset; + fz_rasterizer_postindex_fn *postindex; + fz_rasterizer_insert_fn *insert; + fz_rasterizer_insert_rect_fn *rect; + fz_rasterizer_gap_fn *gap; + fz_rasterizer_fn *convert; + fz_rasterizer_is_rect_fn *is_rect; + int reusable; +} fz_rasterizer_fns; + +struct fz_rasterizer_s +{ + fz_rasterizer_fns fns; + fz_irect clip; /* Specified clip rectangle */ + fz_irect bbox; /* Measured bbox of path while stroking/filling */ +}; + +/* + When rasterizing a shape, we first create a rasterizer then + run through the edges of the shape, feeding them in. + + For a fill, this is easy as we just run along the path, feeding + edges as we go. + + For a stroke, this is trickier, as we feed in edges from + alternate sides of the stroke as we proceed along it. It is only + when we reach the end of a subpath that we know whether we need + an initial cap, or whether the list of edges match up. + + To identify whether a given edge fed in is forward or reverse, + we tag it with a 'rev' value. + + Consider the following simplified example: + + Consider a simple path A, B, C, D, close. + + +------->-------+ The outside edge of this shape is the + | A B | forward edge. This is fed into the rasterizer + | +---<---+ | in order, with rev=0. + | | | | + ^ v ^ v The inside edge of this shape is the reverse + | | | | edge. These edges are generated as we step + | +--->---+ | through the path in clockwise order, but + | D C | conceptually the path runs the other way. + +-------<-------+ These are fed into the rasterizer in clockwise + order, with rev=1. + + Consider another path, this time an open one: A,B,C,D + + +--->-------+ The outside edge of this shape is again the + * A B | forward edge. This is fed into the rasterizer + +---<---+ | in order, with rev=0. + | | + ^ v The inside edge of this shape is the reverse + | | edge. These edges are generated as we step + +--->---+ | through the path in clockwise order, but + ^ D C | conceptually the path runs the other way. + +---<-------+ These are fed into the rasterizer in clockwise + order, with rev=1. + + At the end of the path, we realise that this is an open path, and we + therefore have to put caps on. The cap at 'D' is easy, because it's + a simple continuation of the rev=0 edge list that joins to the end + of the rev=1 edge list. + + The cap at 'A' is trickier; it either needs to be (an) edge(s) prepended + to the rev=0 list or the rev=1 list. We signal this special case by + sending them with the special value rev=2. + + The "edge" rasterizer ignores these values. The "edgebuffer" rasterizer + needs to use them to ensure that edges are correctly joined together + to allow for any part of a pixel operation. +*/ + +/* + fz_new_rasterizer: Create a new rasterizer instance. + This encapsulates a scan converter. + + A single rasterizer instance can be used to scan convert many + things. +*/ +fz_rasterizer *fz_new_rasterizer(fz_context *ctx); + +/* + fz_drop_rasterizer: Dispose of a rasterizer once + finished with. +*/ +static inline void fz_drop_rasterizer(fz_context *ctx, fz_rasterizer *r) +{ + if (r) + r->fns.drop(ctx, r); +} + +/* + fz_reset_rasterizer: Reset a rasterizer, ready to scan convert + a new shape. + + clip: A pointer to a (device space) clipping rectangle. + + Returns 1 if a indexing pass is required, or 0 if not. + + After this, the edges should be 'inserted' into the rasterizer. +*/ +int fz_reset_rasterizer(fz_context *ctx, fz_rasterizer *r, const fz_irect *clip); + +/* + fz_insert_rasterizer: Insert an edge into a rasterizer. + + x0, y0: Initial point + + x1, y1: Final point + + rev: 'reverse' value, 0, 1 or 2. See above. +*/ +static inline void fz_insert_rasterizer(fz_context *ctx, fz_rasterizer *r, float x0, float y0, float x1, float y1, int rev) +{ + r->fns.insert(ctx, r, x0, y0, x1, y1, rev); +} + +/* + fz_insert_rasterizer: Insert a rectangle into a rasterizer. + + x0, y0: One corner of the rectangle. + + x1, y1: The opposite corner of the rectangle. + + The rectangle inserted is conceptually: + (x0,y0)->(x1,y0)->(x1,y1)->(x0,y1)->(x0,y0). + + This method is only used for axis aligned rectangles, + and enables rasterizers to perform special 'anti-dropout' + processing to ensure that horizontal artifacts aren't + lost. +*/ +static inline void fz_insert_rasterizer_rect(fz_context *ctx, fz_rasterizer *r, float x0, float y0, float x1, float y1) +{ + r->fns.rect(ctx, r, x0, y0, x1, y1); +} + +/* + fz_gap_rasterizer: Called to indicate that there is a gap + in the lists of edges fed into the rasterizer (i.e. when + a path hits a move). +*/ +static inline void fz_gap_rasterizer(fz_context *ctx, fz_rasterizer *r) +{ + if (r->fns.gap) + r->fns.gap(ctx, r); +} + +/* + fz_antidropout_rasterizer: Detect whether antidropout + behaviour is required with this rasterizer. + + Returns 1 if required, 0 otherwise. +*/ +static inline int fz_antidropout_rasterizer(fz_context *ctx, fz_rasterizer *r) +{ + return r->fns.rect != NULL; +} + +/* + fz_postindex_rasterizer: Called to signify the end of the + indexing phase. + + After this has been called, the edges should be inserted + again. +*/ +static inline void fz_postindex_rasterizer(fz_context *ctx, fz_rasterizer *r) +{ + if (r->fns.postindex) + r->fns.postindex(ctx, r); +} + +/* + fz_bound_rasterizer: Once a set of edges has been fed into a + rasterizer, the (device space) bounding box can be retrieved. +*/ +fz_irect *fz_bound_rasterizer(fz_context *ctx, const fz_rasterizer *rast, fz_irect *bbox); + +/* + fz_scissor_rasterizer: Retrieve the clipping box with which the + rasterizer was reset. +*/ +fz_rect *fz_scissor_rasterizer(fz_context *ctx, const fz_rasterizer *rast, fz_rect *r); + +/* + fz_convert_rasterizer: Convert the set of edges that have + been fed in, into pixels within the pixmap. + + eofill: Fill rule; True for even odd, false for non zero. + + pix: The pixmap to fill into. + + colorbv: The color components corresponding to the pixmap. +*/ +void fz_convert_rasterizer(fz_context *ctx, fz_rasterizer *r, int eofill, fz_pixmap *pix, unsigned char *colorbv); + +/* + fz_is_rect_rasterizer: Detect if the edges fed into a + rasterizer make up a simple rectangle. +*/ +static inline int fz_is_rect_rasterizer(fz_context *ctx, fz_rasterizer *r) +{ + return r->fns.is_rect(ctx, r); +} + +void *fz_new_rasterizer_of_size(fz_context *ctx, int size, const fz_rasterizer_fns *fns); + +#define fz_new_derived_rasterizer(C,M,F) \ + ((M*)Memento_label(fz_new_rasterizer_of_size(C, sizeof(M), F), #M)) + +fz_rasterizer *fz_new_gel(fz_context *ctx); -fz_gel *fz_new_gel(fz_context *ctx); -void fz_insert_gel(fz_context *ctx, fz_gel *gel, float x0, float y0, float x1, float y1); -void fz_insert_gel_rect(fz_context *ctx, fz_gel *gel, float x0, float y0, float x1, float y1); -void fz_reset_gel(fz_context *ctx, fz_gel *gel, const fz_irect *clip); -fz_irect *fz_bound_gel(fz_context *ctx, const fz_gel *gel, fz_irect *bbox); -void fz_drop_gel(fz_context *ctx, fz_gel *gel); -int fz_is_rect_gel(fz_context *ctx, fz_gel *gel); -fz_rect *fz_gel_scissor(fz_context *ctx, const fz_gel *gel, fz_rect *rect); -void fz_scan_convert(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *pix, unsigned char *colorbv); -void fz_flatten_fill_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const fz_matrix *ctm, float flatness, const fz_irect *irect); -void fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *irect); -void fz_flatten_dash_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *irect); +int fz_flatten_fill_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_matrix *ctm, float flatness, const fz_irect *irect, fz_irect *bounds); +int fz_flatten_stroke_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *irect, fz_irect *bounds); fz_irect *fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth); diff --git a/source/fitz/draw-path.c b/source/fitz/draw-path.c index fe140a56..2c3d8e37 100644 --- a/source/fitz/draw-path.c +++ b/source/fitz/draw-path.c @@ -7,18 +7,58 @@ #define MAX_DEPTH 8 +/* + When stroking/filling, we now label the edges as we emit them. + + For filling, we walk the outline of the shape in order, so everything + is labelled as '0'. + + For stroking, we walk up both sides of the stroke at once; the forward + side (0), and the reverse side (1). When we get to the top, either + both sides join back to where they started, or we cap them. + + The start cap is labelled 2, the end cap is labelled 0. + + These labels are ignored for edge based rasterization, but are required + for edgebuffer based rasterization. + + Consider the following simplified ascii art diagram of a stroke from + left to right with 3 sections. + + | 0 0 0 + | +----->-----+----->-----+----->-----+ + | | | + | ^ 2 A B C v 0 + | | | + | +-----<-----+-----<-----+-----<-----+ + | 1 1 1 + + Edge 0 is sent in order (the top edge of A then B then C, left to right + in the above diagram). Edge 1 is sent in reverse order (the bottom edge + of A then B then C, still left to right in the above diagram, even though + the sense of the line is right to left). + + Finally any caps required are sent, 0 and 2. + + It would be nicer if we could roll edge 2 into edge 1, but to do that + we'd need to know in advance if a stroke was closed or not, so we have + special case code in the edgebuffer based rasterizer to cope with this. +*/ + + + static void -line(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float x0, float y0, float x1, float y1) +line(fz_context *ctx, fz_rasterizer *rast, const fz_matrix *ctm, float x0, float y0, float x1, float y1) { float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e; float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f; float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e; float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f; - fz_insert_gel(ctx, gel, tx0, ty0, tx1, ty1); + fz_insert_rasterizer(ctx, rast, tx0, ty0, tx1, ty1, 0); } static void -bezier(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, +bezier(fz_context *ctx, fz_rasterizer *rast, const fz_matrix *ctm, float flatness, float xa, float ya, float xb, float yb, float xc, float yc, @@ -39,7 +79,7 @@ bezier(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, dmax = fz_max(dmax, fz_abs(yd - yc)); if (dmax < flatness || depth >= MAX_DEPTH) { - line(ctx, gel, ctm, xa, ya, xd, yd); + line(ctx, rast, ctm, xa, ya, xd, yd); return; } @@ -67,12 +107,12 @@ bezier(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, xabcd *= 0.125f; yabcd *= 0.125f; - bezier(ctx, gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); - bezier(ctx, gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); + bezier(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1); + bezier(ctx, rast, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1); } static void -quad(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, +quad(fz_context *ctx, fz_rasterizer *rast, const fz_matrix *ctm, float flatness, float xa, float ya, float xb, float yb, float xc, float yc, int depth) @@ -89,7 +129,7 @@ quad(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, dmax = fz_max(dmax, fz_abs(yc - yb)); if (dmax < flatness || depth >= MAX_DEPTH) { - line(ctx, gel, ctm, xa, ya, xc, yc); + line(ctx, rast, ctm, xa, ya, xc, yc); return; } @@ -106,13 +146,13 @@ quad(fz_context *ctx, fz_gel *gel, const fz_matrix *ctm, float flatness, xabc *= 0.25f; yabc *= 0.25f; - quad(ctx, gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, depth + 1); - quad(ctx, gel, ctm, flatness, xabc, yabc, xbc, ybc, xc, yc, depth + 1); + quad(ctx, rast, ctm, flatness, xa, ya, xab, yab, xabc, yabc, depth + 1); + quad(ctx, rast, ctm, flatness, xabc, yabc, xbc, ybc, xc, yc, depth + 1); } typedef struct { - fz_gel *gel; + fz_rasterizer *rast; const fz_matrix *ctm; float flatness; fz_point b; @@ -127,9 +167,11 @@ flatten_moveto(fz_context *ctx, void *arg_, float x, float y) /* implicit closepath before moveto */ if (arg->c.x != arg->b.x || arg->c.y != arg->b.y) - line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); + line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); arg->c.x = arg->b.x = x; arg->c.y = arg->b.y = y; + + fz_gap_rasterizer(ctx, arg->rast); } static void @@ -137,7 +179,7 @@ flatten_lineto(fz_context *ctx, void *arg_, float x, float y) { flatten_arg *arg = (flatten_arg *)arg_; - line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, x, y); + line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, x, y); arg->c.x = x; arg->c.y = y; } @@ -147,7 +189,7 @@ flatten_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float { flatten_arg *arg = (flatten_arg *)arg_; - bezier(ctx, arg->gel, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0); + bezier(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, x3, y3, 0); arg->c.x = x3; arg->c.y = y3; } @@ -157,7 +199,7 @@ flatten_quadto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float { flatten_arg *arg = (flatten_arg *)arg_; - quad(ctx, arg->gel, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, 0); + quad(ctx, arg->rast, arg->ctm, arg->flatness, arg->c.x, arg->c.y, x1, y1, x2, y2, 0); arg->c.x = x2; arg->c.y = y2; } @@ -167,7 +209,7 @@ flatten_close(fz_context *ctx, void *arg_) { flatten_arg *arg = (flatten_arg *)arg_; - line(ctx, arg->gel, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); + line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, arg->b.x, arg->b.y); arg->c.x = arg->b.x; arg->c.y = arg->b.y; } @@ -179,31 +221,35 @@ flatten_rectto(fz_context *ctx, void *arg_, float x0, float y0, float x1, float const fz_matrix *ctm = arg->ctm; flatten_moveto(ctx, arg_, x0, y0); - /* In the case where we have an axis aligned rectangle, do some - * horrid antidropout stuff. */ - if (ctm->b == 0 && ctm->c == 0) - { - float tx0 = ctm->a * x0 + ctm->e; - float ty0 = ctm->d * y0 + ctm->f; - float tx1 = ctm->a * x1 + ctm->e; - float ty1 = ctm->d * y1 + ctm->f; - fz_insert_gel_rect(ctx, arg->gel, tx0, ty0, tx1, ty1); - } - else if (ctm->a == 0 && ctm->d == 0) - { - float tx0 = ctm->c * y0 + ctm->e; - float ty0 = ctm->b * x0 + ctm->f; - float tx1 = ctm->c * y1 + ctm->e; - float ty1 = ctm->b * x1 + ctm->f; - fz_insert_gel_rect(ctx, arg->gel, tx0, ty1, tx1, ty0); - } - else + + if (fz_antidropout_rasterizer(ctx, arg->rast)) { - flatten_lineto(ctx, arg_, x1, y0); - flatten_lineto(ctx, arg_, x1, y1); - flatten_lineto(ctx, arg_, x0, y1); - flatten_close(ctx, arg_); + /* In the case where we have an axis aligned rectangle, do some + * horrid antidropout stuff. */ + if (ctm->b == 0 && ctm->c == 0) + { + float tx0 = ctm->a * x0 + ctm->e; + float ty0 = ctm->d * y0 + ctm->f; + float tx1 = ctm->a * x1 + ctm->e; + float ty1 = ctm->d * y1 + ctm->f; + fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty0, tx1, ty1); + return; + } + else if (ctm->a == 0 && ctm->d == 0) + { + float tx0 = ctm->c * y0 + ctm->e; + float ty0 = ctm->b * x0 + ctm->f; + float tx1 = ctm->c * y1 + ctm->e; + float ty1 = ctm->b * x1 + ctm->f; + fz_insert_rasterizer_rect(ctx, arg->rast, tx0, ty1, tx1, ty0); + return; + } } + + flatten_lineto(ctx, arg_, x1, y0); + flatten_lineto(ctx, arg_, x1, y1); + flatten_lineto(ctx, arg_, x0, y1); + flatten_close(ctx, arg_); } static const fz_path_walker flatten_proc = @@ -218,26 +264,50 @@ static const fz_path_walker flatten_proc = flatten_rectto }; -void -fz_flatten_fill_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const fz_matrix *ctm, float flatness, const fz_irect *scissor) +int +fz_flatten_fill_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_matrix *ctm, float flatness, const fz_irect *scissor, fz_irect *bbox) { flatten_arg arg; + fz_irect local_bbox; + + if (fz_reset_rasterizer(ctx, rast, scissor)) + { + arg.rast = rast; + arg.ctm = ctm; + arg.flatness = flatness; + arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0; - fz_reset_gel(ctx, gel, scissor); + fz_walk_path(ctx, path, &flatten_proc, &arg); + if (arg.c.x != arg.b.x || arg.c.y != arg.b.y) + line(ctx, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y); - arg.gel = gel; + fz_gap_rasterizer(ctx, rast); + + fz_postindex_rasterizer(ctx, rast); + } + + arg.rast = rast; arg.ctm = ctm; arg.flatness = flatness; arg.b.x = arg.b.y = arg.c.x = arg.c.y = 0; fz_walk_path(ctx, path, &flatten_proc, &arg); if (arg.c.x != arg.b.x || arg.c.y != arg.b.y) - line(ctx, gel, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y); + line(ctx, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y); + + fz_gap_rasterizer(ctx, rast); + + if (!bbox) + return 0; + + local_bbox = *scissor; + fz_bound_rasterizer(ctx, rast, bbox); + return fz_is_empty_irect(fz_intersect_irect(bbox, &local_bbox)); } typedef struct sctx { - fz_gel *gel; + fz_rasterizer *rast; const fz_matrix *ctm; float flatness; const fz_stroke_state *stroke; @@ -265,72 +335,79 @@ typedef struct sctx } sctx; static void -fz_add_line(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1) +fz_add_line(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1, int rev) { float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e; float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f; float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e; float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f; - fz_insert_gel(ctx, s->gel, tx0, ty0, tx1, ty1); + + fz_insert_rasterizer(ctx, s->rast, tx0, ty0, tx1, ty1, rev); } static void fz_add_horiz_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1) { - if (s->ctm->b == 0 && s->ctm->c == 0) - { - float tx0 = s->ctm->a * x0 + s->ctm->e; - float ty0 = s->ctm->d * y0 + s->ctm->f; - float tx1 = s->ctm->a * x1 + s->ctm->e; - float ty1 = s->ctm->d * y1 + s->ctm->f; - fz_insert_gel_rect(ctx, s->gel, tx1, ty1, tx0, ty0); - } - else if (s->ctm->a == 0 && s->ctm->d == 0) - { - float tx0 = s->ctm->c * y0 + s->ctm->e; - float ty0 = s->ctm->b * x0 + s->ctm->f; - float tx1 = s->ctm->c * y1 + s->ctm->e; - float ty1 = s->ctm->b * x1 + s->ctm->f; - fz_insert_gel_rect(ctx, s->gel, tx1, ty0, tx0, ty1); - } - else - { - fz_add_line(ctx, s, x0, y0, x1, y0); - fz_add_line(ctx, s, x1, y1, x0, y1); + if (fz_antidropout_rasterizer(ctx, s->rast)) { + if (s->ctm->b == 0 && s->ctm->c == 0) + { + float tx0 = s->ctm->a * x0 + s->ctm->e; + float ty0 = s->ctm->d * y0 + s->ctm->f; + float tx1 = s->ctm->a * x1 + s->ctm->e; + float ty1 = s->ctm->d * y1 + s->ctm->f; + fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty1, tx0, ty0); + return; + } + else if (s->ctm->a == 0 && s->ctm->d == 0) + { + float tx0 = s->ctm->c * y0 + s->ctm->e; + float ty0 = s->ctm->b * x0 + s->ctm->f; + float tx1 = s->ctm->c * y1 + s->ctm->e; + float ty1 = s->ctm->b * x1 + s->ctm->f; + fz_insert_rasterizer_rect(ctx, s->rast, tx1, ty0, tx0, ty1); + return; + } } + + fz_add_line(ctx, s, x0, y0, x1, y0, 0); + fz_add_line(ctx, s, x1, y1, x0, y1, 1); } static void fz_add_vert_rect(fz_context *ctx, sctx *s, float x0, float y0, float x1, float y1) { - if (s->ctm->b == 0 && s->ctm->c == 0) + if (fz_antidropout_rasterizer(ctx, s->rast)) { - float tx0 = s->ctm->a * x0 + s->ctm->e; - float ty0 = s->ctm->d * y0 + s->ctm->f; - float tx1 = s->ctm->a * x1 + s->ctm->e; - float ty1 = s->ctm->d * y1 + s->ctm->f; - fz_insert_gel_rect(ctx, s->gel, tx0, ty1, tx1, ty0); - } - else if (s->ctm->a == 0 && s->ctm->d == 0) - { - float tx0 = s->ctm->c * y0 + s->ctm->e; - float ty0 = s->ctm->b * x0 + s->ctm->f; - float tx1 = s->ctm->c * y1 + s->ctm->e; - float ty1 = s->ctm->b * x1 + s->ctm->f; - fz_insert_gel_rect(ctx, s->gel, tx0, ty0, tx1, ty1); - } - else - { - fz_add_line(ctx, s, x1, y0, x0, y0); - fz_add_line(ctx, s, x0, y1, x1, y1); + if (s->ctm->b == 0 && s->ctm->c == 0) + { + float tx0 = s->ctm->a * x0 + s->ctm->e; + float ty0 = s->ctm->d * y0 + s->ctm->f; + float tx1 = s->ctm->a * x1 + s->ctm->e; + float ty1 = s->ctm->d * y1 + s->ctm->f; + fz_insert_rasterizer_rect(ctx, s->rast, tx0, ty1, tx1, ty0); + return; + } + else if (s->ctm->a == 0 && s->ctm->d == 0) + { + float tx0 = s->ctm->c * y0 + s->ctm->e; + float ty0 = s->ctm->b * x0 + s->ctm->f; + float tx1 = s->ctm->c * y1 + s->ctm->e; + float ty1 = s->ctm->b * x1 + s->ctm->f; + fz_insert_rasterizer_rect(ctx, s->rast, tx0, ty0, tx1, ty1); + return; + } } + + fz_add_line(ctx, s, x1, y0, x0, y0, 0); + fz_add_line(ctx, s, x0, y1, x1, y1, 1); } static void fz_add_arc(fz_context *ctx, sctx *s, float xc, float yc, float x0, float y0, - float x1, float y1) + float x1, float y1, + int rev) { float th0, th1, r; float theta; @@ -355,42 +432,37 @@ fz_add_arc(fz_context *ctx, sctx *s, n = ceilf((th1 - th0) / theta); } - ox = x0; - oy = y0; - for (i = 1; i < n; i++) + if (rev) { - theta = th0 + (th1 - th0) * i / n; - nx = cosf(theta) * r; - ny = sinf(theta) * r; - fz_add_line(ctx, s, xc + ox, yc + oy, xc + nx, yc + ny); - ox = nx; - oy = ny; - } - - fz_add_line(ctx, s, xc + ox, yc + oy, xc + x1, yc + y1); -} - -static void -fz_add_line_stroke(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by) -{ - float dx = bx - ax; - float dy = by - ay; - float scale = s->linewidth / sqrtf(dx * dx + dy * dy); - float dlx = dy * scale; - float dly = -dx * scale; + ox = x1; + oy = y1; + for (i = n-1; i > 0; i--) + { + theta = th0 + (th1 - th0) * i / n; + nx = cosf(theta) * r; + ny = sinf(theta) * r; + fz_add_line(ctx, s, xc + nx, yc + ny, xc + ox, yc + oy, rev); + ox = nx; + oy = ny; + } - if (0 && dx == 0) - { - fz_add_vert_rect(ctx, s, ax - dlx, ay, bx + dlx, by); - } - else if (dy == 0) - { - fz_add_horiz_rect(ctx, s, ax, ay - dly, bx, by + dly); + fz_add_line(ctx, s, xc + x0, yc + y0, xc + ox, yc + oy, rev); } else { - fz_add_line(ctx, s, ax - dlx, ay - dly, bx - dlx, by - dly); - fz_add_line(ctx, s, bx + dlx, by + dly, ax + dlx, ay + dly); + ox = x0; + oy = y0; + for (i = 1; i < n; i++) + { + theta = th0 + (th1 - th0) * i / n; + nx = cosf(theta) * r; + ny = sinf(theta) * r; + fz_add_line(ctx, s, xc + ox, yc + oy, xc + nx, yc + ny, rev); + ox = nx; + oy = ny; + } + + fz_add_line(ctx, s, xc + ox, yc + oy, xc + x1, yc + y1, rev); } } @@ -409,6 +481,7 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b float scale; float cross; float len0, len1; + int rev = 0; dx0 = bx - ax; dy0 = by - ay; @@ -424,6 +497,7 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b tmp = dx1; dx1 = -dx0; dx0 = -tmp; tmp = dy1; dy1 = -dy0; dy0 = -tmp; cross = -cross; + rev = !rev; } len0 = dx0 * dx0 + dy0 * dy0; @@ -461,16 +535,6 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0) linejoin = FZ_LINEJOIN_BEVEL; - if (join_under) - { - fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0); - } - else - { - fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by); - fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0); - } - /* XPS miter joins are clipped at miterlength, rather than simply * being converted to bevelled joins. */ if (linejoin == FZ_LINEJOIN_MITER_XPS) @@ -479,47 +543,78 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b linejoin = FZ_LINEJOIN_BEVEL; else if (dmr2 * miterlimit * miterlimit >= linewidth * linewidth) linejoin = FZ_LINEJOIN_MITER; - else - { - float k, t0x, t0y, t1x, t1y; - scale = linewidth * linewidth / dmr2; - dmx *= scale; - dmy *= scale; - k = (scale - linewidth * miterlimit / sqrtf(dmr2)) / (scale - 1); - t0x = bx - dmx + k * (dmx - dlx0); - t0y = by - dmy + k * (dmy - dly0); - t1x = bx - dmx + k * (dmx - dlx1); - t1y = by - dmy + k * (dmy - dly1); - - fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y); - fz_add_line(ctx, s, t0x, t0y, t1x, t1y); - fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1); - } } else if (linejoin == FZ_LINEJOIN_MITER) if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) linejoin = FZ_LINEJOIN_BEVEL; + if (join_under) + { + fz_add_line(ctx, s, bx + dlx1, by + dly1, bx + dlx0, by + dly0, !rev); + } + else if (rev) + { + fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 0); + fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 0); + } + else + { + fz_add_line(ctx, s, bx, by, bx + dlx0, by + dly0, 1); + fz_add_line(ctx, s, bx + dlx1, by + dly1, bx, by, 1); + } + switch (linejoin) { case FZ_LINEJOIN_MITER_XPS: - break; + { + float k, t0x, t0y, t1x, t1y; + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + k = (scale - linewidth * miterlimit / sqrtf(dmr2)) / (scale - 1); + t0x = bx - dmx + k * (dmx - dlx0); + t0y = by - dmy + k * (dmy - dly0); + t1x = bx - dmx + k * (dmx - dlx1); + t1y = by - dmy + k * (dmy - dly1); + + if (rev) + { + fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1, 1); + fz_add_line(ctx, s, t0x, t0y, t1x, t1y, 1); + fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y, 1); + } + else + { + fz_add_line(ctx, s, bx - dlx0, by - dly0, t0x, t0y, 0); + fz_add_line(ctx, s, t0x, t0y, t1x, t1y, 0); + fz_add_line(ctx, s, t1x, t1y, bx - dlx1, by - dly1, 0); + } + break; + } case FZ_LINEJOIN_MITER: scale = linewidth * linewidth / dmr2; dmx *= scale; dmy *= scale; - fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy); - fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1); + if (rev) + { + fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 1); + fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 1); + } + else + { + fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dmx, by - dmy, 0); + fz_add_line(ctx, s, bx - dmx, by - dmy, bx - dlx1, by - dly1, 0); + } break; case FZ_LINEJOIN_BEVEL: - fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1); + fz_add_line(ctx, s, bx - dlx0, by - dly0, bx - dlx1, by - dly1, rev); break; case FZ_LINEJOIN_ROUND: - fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1); + fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1, rev); break; default: @@ -528,7 +623,7 @@ fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float b } static void -fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, fz_linecap linecap) +fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, fz_linecap linecap, int rev) { float flatness = s->flatness; float linewidth = s->linewidth; @@ -543,7 +638,7 @@ fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by switch (linecap) { case FZ_LINECAP_BUTT: - fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly); + fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly, rev); break; case FZ_LINECAP_ROUND: @@ -559,29 +654,29 @@ fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by float sth = sinf(theta); float nx = bx - dlx * cth - dly * sth; float ny = by - dly * cth + dlx * sth; - fz_add_line(ctx, s, ox, oy, nx, ny); + fz_add_line(ctx, s, ox, oy, nx, ny, rev); ox = nx; oy = ny; } - fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly); + fz_add_line(ctx, s, ox, oy, bx + dlx, by + dly, rev); break; } case FZ_LINECAP_SQUARE: fz_add_line(ctx, s, bx - dlx, by - dly, - bx - dlx - dly, by - dly + dlx); + bx - dlx - dly, by - dly + dlx, rev); fz_add_line(ctx, s, bx - dlx - dly, by - dly + dlx, - bx + dlx - dly, by + dly + dlx); + bx + dlx - dly, by + dly + dlx, rev); fz_add_line(ctx, s, bx + dlx - dly, by + dly + dlx, - bx + dlx, by + dly); + bx + dlx, by + dly, rev); break; case FZ_LINECAP_TRIANGLE: { float mx = -dly; float my = dlx; - fz_add_line(ctx, s, bx - dlx, by - dly, bx + mx, by + my); - fz_add_line(ctx, s, bx + mx, by + my, bx + dlx, by + dly); + fz_add_line(ctx, s, bx - dlx, by - dly, bx + mx, by + my, rev); + fz_add_line(ctx, s, bx + mx, by + my, bx + dlx, by + dly, rev); break; } @@ -609,12 +704,12 @@ fz_add_line_dot(fz_context *ctx, sctx *s, float ax, float ay) float sth = sinf(theta); float nx = ax - cth * linewidth; float ny = ay + sth * linewidth; - fz_add_line(ctx, s, ox, oy, nx, ny); + fz_add_line(ctx, s, ox, oy, nx, ny, 0); ox = nx; oy = ny; } - fz_add_line(ctx, s, ox, oy, ax - linewidth, ay); + fz_add_line(ctx, s, ox, oy, ax - linewidth, ay, 0); } static void @@ -622,13 +717,14 @@ fz_stroke_flush(fz_context *ctx, sctx *s, fz_linecap start_cap, fz_linecap end_c { if (s->sn == 2) { - fz_add_line_cap(ctx, s, s->beg[1].x, s->beg[1].y, s->beg[0].x, s->beg[0].y, start_cap); - fz_add_line_cap(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, end_cap); + fz_add_line_cap(ctx, s, s->beg[1].x, s->beg[1].y, s->beg[0].x, s->beg[0].y, start_cap, 2); + fz_add_line_cap(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, end_cap, 0); } else if (s->dot) { fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y); } + fz_gap_rasterizer(ctx, s->rast); } static void @@ -646,8 +742,11 @@ fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y) static void fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier) { - float dx = x - s->seg[s->sn-1].x; - float dy = y - s->seg[s->sn-1].y; + float ox = s->seg[s->sn-1].x; + float oy = s->seg[s->sn-1].y; + float dx = x - ox; + float dy = y - oy; + float scale, dlx, dly; if (dx * dx + dy * dy < FLT_EPSILON) { @@ -656,11 +755,32 @@ fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier) return; } - fz_add_line_stroke(ctx, s, s->seg[s->sn-1].x, s->seg[s->sn-1].y, x, y); + if (s->sn == 2) + fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, ox, oy, x, y, s->from_bezier & from_bezier); + + scale = s->linewidth / sqrtf(dx * dx + dy * dy); + dlx = dy * scale; + dly = -dx * scale; + +#if 1 + if (0 && dx == 0) + { + fz_add_vert_rect(ctx, s, ox - dlx, oy, x + dlx, y); + } + else if (dy == 0) + { + fz_add_horiz_rect(ctx, s, ox, oy - dly, x, y + dly); + } + else +#endif + { + + fz_add_line(ctx, s, ox - dlx, oy - dly, x - dlx, y - dly, 0); + fz_add_line(ctx, s, x + dlx, y + dly, ox + dlx, oy + dly, 1); + } if (s->sn == 2) { - fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->seg[1].x, s->seg[1].y, x, y, s->from_bezier & from_bezier); s->seg[0] = s->seg[1]; s->seg[1].x = x; s->seg[1].y = y; @@ -686,16 +806,17 @@ fz_stroke_closepath(fz_context *ctx, sctx *s) fz_add_line_join(ctx, s, s->seg[1].x, s->seg[1].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0); } else if (s->dot) - { fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y); - } s->seg[0] = s->beg[0]; s->sn = 1; s->dot = 0; s->from_bezier = 0; + + fz_gap_rasterizer(ctx, s->rast); } + static void fz_stroke_bezier(fz_context *ctx, struct sctx *s, float xa, float ya, @@ -1274,16 +1395,14 @@ static const fz_path_walker dash_proc = dash_quadto }; -void -fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *scissor) +static int +do_flatten_stroke(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *scissor, fz_irect *bbox) { struct sctx s; const fz_path_walker *proc = &stroke_proc; - fz_reset_gel(ctx, gel, scissor); - s.stroke = stroke; - s.gel = gel; + s.rast = rast; s.ctm = ctm; s.flatness = flatness; s.linejoin = stroke->linejoin; @@ -1310,11 +1429,11 @@ fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const for (i = 0; i < s.dash_len; i++) s.dash_total += list[i]; if (s.dash_total == 0) - return; + return 1; - fz_gel_scissor(ctx, gel, &s.rect); + fz_scissor_rasterizer(ctx, rast, &s.rect); if (fz_try_invert_matrix(&inv, ctm)) - return; + return 1; fz_transform_rect(&s.rect, &inv); s.rect.x0 -= linewidth; s.rect.x1 += linewidth; @@ -1333,4 +1452,24 @@ fz_flatten_stroke_path(fz_context *ctx, fz_gel *gel, const fz_path *path, const s.cur.x = s.cur.y = 0; fz_walk_path(ctx, path, proc, &s); fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap); + + if (!bbox) + return 0; + + return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast, bbox)); +} + +int +fz_flatten_stroke_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth, const fz_irect *scissor, fz_irect *bbox) +{ + + if (fz_reset_rasterizer(ctx, rast, scissor)) + { + if (do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth, scissor, bbox)) + return 1; + fz_postindex_rasterizer(ctx, rast); + bbox = NULL; + } + + return do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth, scissor, bbox); } diff --git a/source/fitz/draw-rasterize.c b/source/fitz/draw-rasterize.c new file mode 100644 index 00000000..5998ece4 --- /dev/null +++ b/source/fitz/draw-rasterize.c @@ -0,0 +1,257 @@ +#include "mupdf/fitz.h" +#include "draw-imp.h" + +void fz_new_aa_context(fz_context *ctx) +{ +#ifndef AA_BITS + ctx->aa = fz_malloc_struct(ctx, fz_aa_context); + ctx->aa->hscale = 17; + ctx->aa->vscale = 15; + ctx->aa->scale = 256; + ctx->aa->bits = 8; + ctx->aa->text_bits = 8; +#endif +} + +void fz_copy_aa_context(fz_context *dst, fz_context *src) +{ + if (dst && dst->aa && src && src->aa) + memcpy(dst->aa, src->aa, sizeof(*src->aa)); +} + +void fz_drop_aa_context(fz_context *ctx) +{ + if (!ctx) + return; +#ifndef AA_BITS + fz_free(ctx, ctx->aa); + ctx->aa = NULL; +#endif +} + +int +fz_aa_level(fz_context *ctx) +{ + return fz_aa_bits; +} + +int +fz_graphics_aa_level(fz_context *ctx) +{ + return fz_aa_bits; +} + +int +fz_text_aa_level(fz_context *ctx) +{ + return fz_aa_text_bits; +} + +#ifndef AA_BITS +static void +set_gfx_level(fz_context *ctx, int level) +{ + if (level == 9 || level == 10) + { + fz_aa_hscale = 1; + fz_aa_vscale = 1; + fz_aa_bits = level; + } + else if (level > 6) + { + fz_aa_hscale = 17; + fz_aa_vscale = 15; + fz_aa_bits = 8; + } + else if (level > 4) + { + fz_aa_hscale = 8; + fz_aa_vscale = 8; + fz_aa_bits = 6; + } + else if (level > 2) + { + fz_aa_hscale = 5; + fz_aa_vscale = 3; + fz_aa_bits = 4; + } + else if (level > 0) + { + fz_aa_hscale = 2; + fz_aa_vscale = 2; + fz_aa_bits = 2; + } + else + { + fz_aa_hscale = 1; + fz_aa_vscale = 1; + fz_aa_bits = 0; + } + fz_aa_scale = 0xFF00 / (fz_aa_hscale * fz_aa_vscale); +} + +static void +set_txt_level(fz_context *ctx, int level) +{ + if (level > 6) + fz_aa_text_bits = 8; + else if (level > 4) + fz_aa_text_bits = 6; + else if (level > 2) + fz_aa_text_bits = 4; + else if (level > 0) + fz_aa_text_bits = 2; + else + fz_aa_text_bits = 0; +} +#endif /* AA_BITS */ + +void +fz_set_aa_level(fz_context *ctx, int level) +{ +#ifdef AA_BITS + fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); +#else + set_gfx_level(ctx, level); + set_txt_level(ctx, level); +#endif +} + +void +fz_set_text_aa_level(fz_context *ctx, int level) +{ +#ifdef AA_BITS + fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); +#else + set_txt_level(ctx, level); +#endif +} + +void +fz_set_graphics_aa_level(fz_context *ctx, int level) +{ +#ifdef AA_BITS + fz_warn(ctx, "anti-aliasing was compiled with a fixed precision of %d bits", fz_aa_bits); +#else + set_gfx_level(ctx, level); +#endif +} + +void +fz_set_graphics_min_line_width(fz_context *ctx, float min_line_width) +{ + if (!ctx || !ctx->aa) + return; + + ctx->aa->min_line_width = min_line_width; +} + +float +fz_graphics_min_line_width(fz_context *ctx) +{ + if (!ctx || !ctx->aa) + return 0; + + return ctx->aa->min_line_width; +} + +fz_irect * +fz_bound_rasterizer(fz_context *ctx, const fz_rasterizer *rast, fz_irect *bbox) +{ + const int hscale = fz_aa_hscale; + const int vscale = fz_aa_vscale; + + if (rast->bbox.x1 < rast->bbox.x0 || rast->bbox.y1 < rast->bbox.y0) + { + *bbox = fz_empty_irect; + } + else + { + bbox->x0 = fz_idiv(rast->bbox.x0, hscale); + bbox->y0 = fz_idiv(rast->bbox.y0, vscale); + bbox->x1 = fz_idiv_up(rast->bbox.x1, hscale); + bbox->y1 = fz_idiv_up(rast->bbox.y1, vscale); + } + return bbox; +} + +fz_rect *fz_scissor_rasterizer(fz_context *ctx, const fz_rasterizer *rast, fz_rect *r) +{ + const int hscale = fz_aa_hscale; + const int vscale = fz_aa_vscale; + + r->x0 = ((float)rast->clip.x0) / hscale; + r->y0 = ((float)rast->clip.y0) / vscale; + r->x1 = ((float)rast->clip.x1) / hscale; + r->y1 = ((float)rast->clip.y1) / vscale; + + return r; +} + +static fz_irect *fz_clip_rasterizer(fz_context *ctx, const fz_rasterizer *rast, fz_irect *r) +{ + const int hscale = fz_aa_hscale; + const int vscale = fz_aa_vscale; + + r->x0 = fz_idiv(rast->clip.x0, hscale); + r->y0 = fz_idiv(rast->clip.y0, vscale); + r->x1 = fz_idiv_up(rast->clip.x1, hscale); + r->y1 = fz_idiv_up(rast->clip.y1, vscale); + + return r; +} + +int fz_reset_rasterizer(fz_context *ctx, fz_rasterizer *rast, const fz_irect *clip) +{ + const int hscale = fz_aa_hscale; + const int vscale = fz_aa_vscale; + + if (fz_is_infinite_irect(clip)) + { + rast->clip.x0 = rast->clip.y0 = BBOX_MIN; + rast->clip.x1 = rast->clip.y1 = BBOX_MAX; + } + else { + rast->clip.x0 = clip->x0 * hscale; + rast->clip.x1 = clip->x1 * hscale; + rast->clip.y0 = clip->y0 * vscale; + rast->clip.y1 = clip->y1 * vscale; + } + + rast->bbox.x0 = rast->bbox.y0 = BBOX_MAX; + rast->bbox.x1 = rast->bbox.y1 = BBOX_MIN; + if (rast->fns.reset) + return rast->fns.reset(ctx, rast); + return 0; +} + +void *fz_new_rasterizer_of_size(fz_context *ctx, int size, const fz_rasterizer_fns *fns) +{ + fz_rasterizer *rast = fz_calloc(ctx, 1, size); + + rast->fns = *fns; + rast->clip.x0 = rast->clip.y0 = BBOX_MIN; + rast->clip.x1 = rast->clip.y1 = BBOX_MAX; + + rast->bbox.x0 = rast->bbox.y0 = BBOX_MAX; + rast->bbox.x1 = rast->bbox.y1 = BBOX_MIN; + + return rast; +} + +fz_rasterizer *fz_new_rasterizer(fz_context *ctx) +{ + return fz_new_gel(ctx); +} + +void fz_convert_rasterizer(fz_context *ctx, fz_rasterizer *r, int eofill, fz_pixmap *pix, unsigned char *colorbv) +{ + fz_irect clip, scissor; + fz_irect pixmap_clip; + + if (fz_is_empty_irect(fz_intersect_irect(fz_bound_rasterizer(ctx, r, &clip), fz_pixmap_bbox_no_ctx(pix, &pixmap_clip)))) + return; + if (fz_is_empty_irect(fz_intersect_irect(&clip, fz_clip_rasterizer(ctx, r, &scissor)))) + return; + r->fns.convert(ctx, r, eofill, &clip, pix, colorbv); +} |