summaryrefslogtreecommitdiff
path: root/source/fitz
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-04-26 12:03:07 +0100
committerRobin Watts <Robin.Watts@artifex.com>2017-06-13 16:03:59 +0100
commitf1386c6778baded82a3b98215264c2613efc7fe7 (patch)
tree63c093d906d2a69f3d978577930484968eff7f3e /source/fitz
parent1bcfc08545f4bed3d1197af1756f7465af11cf96 (diff)
downloadmupdf-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.c88
-rw-r--r--source/fitz/draw-edge.c480
-rw-r--r--source/fitz/draw-imp.h325
-rw-r--r--source/fitz/draw-path.c491
-rw-r--r--source/fitz/draw-rasterize.c257
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);
+}