Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/draw-edge.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/draw-edge.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,919 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "draw-imp.h" + +#include <assert.h> +#include <limits.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* + * Global Edge List -- list of straight path segments for scan conversion + * + * Stepping along the edges is with Bresenham's line algorithm. + * + * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) + */ + +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; + +typedef struct fz_gel_s +{ + fz_rasterizer super; + int cap, len; + fz_edge *edges; + int acap, alen; + fz_edge **active; + int bcap; + unsigned char *alphas; + int *deltas; +} fz_gel; + +static int +fz_reset_gel(fz_context *ctx, fz_rasterizer *rast) +{ + fz_gel *gel = (fz_gel *)rast; + + gel->len = 0; + gel->alen = 0; + + return 0; +} + +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); + fz_free(ctx, gel->edges); + fz_free(ctx, gel->alphas); + fz_free(ctx, gel->deltas); + fz_free(ctx, gel); +} + +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) + +static int +clip_lerp_x(int val, int m, int x0, int y0, int x1, int y1, int *out) +{ + int v0out = m ? x0 > val : x0 < val; + int v1out = m ? x1 > val : x1 < val; + + if (v0out + v1out == 0) + return INSIDE; + + if (v0out + v1out == 2) + return OUTSIDE; + + if (v1out) + { + *out = y0 + (int)(((float)(y1 - y0)) * (val - x0) / (x1 - x0)); + return LEAVE; + } + + else + { + *out = y1 + (int)(((float)(y0 - y1)) * (val - x1) / (x0 - x1)); + return ENTER; + } +} + +static void +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; + int width; + int tmp; + + if (y0 == y1) + return; + + if (y0 > y1) { + winding = -1; + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + } + else + winding = 1; + + 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->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; + gel->edges = fz_realloc_array(ctx, gel->edges, new_cap, fz_edge); + gel->cap = new_cap; + } + + edge = &gel->edges[gel->len++]; + + dy = y1 - y0; + dx = x1 - x0; + width = fz_absi(dx); + + edge->xdir = dx > 0 ? 1 : -1; + edge->ydir = winding; + edge->x = x0; + edge->y = y0; + edge->h = dy; + edge->adj_down = dy; + + /* initial error term going l->r and r->l */ + if (dx >= 0) + edge->e = 0; + else + edge->e = -dy + 1; + + /* y-major edge */ + if (dy >= width) { + edge->xmove = 0; + edge->adj_up = width; + } + + /* x-major edge */ + else { + edge->xmove = (width / dy) * edge->xdir; + edge->adj_up = width % dy; + } +} + +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; + const int hscale = fz_rasterizer_aa_hscale(ras); + const int vscale = fz_rasterizer_aa_vscale(ras); + + fx0 = floorf(fx0 * hscale); + fx1 = floorf(fx1 * hscale); + fy0 = floorf(fy0 * vscale); + fy1 = floorf(fy1 * vscale); + + /* 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 + * implicit cast down from float to int of the first argument + * over/underflowing and flipping sign at extreme values. */ + x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale); + y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale); + 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(ras->clip.y0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = ras->clip.y0; x1 = v; } + if (d == ENTER) { y0 = ras->clip.y0; x0 = v; } + + d = clip_lerp_y(ras->clip.y1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = ras->clip.y1; x1 = v; } + if (d == ENTER) { y0 = ras->clip.y1; x0 = v; } + + d = clip_lerp_x(ras->clip.x0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) { + x0 = x1 = ras->clip.x0; + } + if (d == LEAVE) { + 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, ras, ras->clip.x0, y0, ras->clip.x0, v); + x0 = ras->clip.x0; + y0 = v; + } + + d = clip_lerp_x(ras->clip.x1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) { + x0 = x1 = ras->clip.x1; + } + if (d == LEAVE) { + 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, ras, ras->clip.x1, y0, ras->clip.x1, v); + x0 = ras->clip.x1; + y0 = v; + } + + fz_insert_gel_raw(ctx, ras, x0, y0, x1, y1); +} + +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_rasterizer_aa_hscale(ras); + const int vscale = fz_rasterizer_aa_vscale(ras); + + fx0 = floorf(fx0 * hscale); + fx1 = floorf(fx1 * hscale); + if (fx1 == fx0) + fx1++; + fy0 = floorf(fy0 * vscale); + fy1 = floorf(fy1 * vscale); + if (fy1 == fy0) + fy1++; + + 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 + * implicit cast down from float to int of the first argument + * over/underflowing and flipping sign at extreme values. */ + x0 = (int)fz_clamp(fx0, BBOX_MIN * hscale, BBOX_MAX * hscale); + y0 = (int)fz_clamp(fy0, BBOX_MIN * vscale, BBOX_MAX * vscale); + 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, ras, x1, y0, x1, y1); + fz_insert_gel_raw(ctx, ras, x0, y1, x0, y0); +} + +static int +cmpedge(const void *va, const void *vb) +{ + const fz_edge *a = va; + const fz_edge *b = vb; + return a->y - b->y; +} + +static void +sort_gel(fz_context *ctx, fz_gel *gel) +{ + fz_edge *a = gel->edges; + int n = gel->len; + int h, i, k; + fz_edge t; + + /* quick sort for long lists */ + if (n > 10000) + { + qsort(a, n, sizeof *a, cmpedge); + return; + } + + /* shell sort for short lists */ + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + /* TODO: sort on y major, x minor */ + while (k >= 0 && a[k].y > t.y) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + h /= 3; + } +} + +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) + { + fz_edge *a = gel->edges + 0; + fz_edge *b = gel->edges + 1; + return a->y == b->y && a->h == b->h && + a->xmove == 0 && a->adj_up == 0 && + b->xmove == 0 && b->adj_up == 0; + } + return 0; +} + +/* + * Active Edge List -- keep track of active edges while sweeping + */ + +static void +sort_active(fz_edge **a, int n) +{ + int h, i, k; + fz_edge *t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + while (k >= 0 && a[k]->x > t->x) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +static int +insert_active(fz_context *ctx, fz_gel *gel, int y, int *e_) +{ + int h_min = INT_MAX; + int e = *e_; + + /* insert edges that start here */ + if (e < gel->len && gel->edges[e].y == y) + { + do { + if (gel->alen + 1 == gel->acap) { + int newcap = gel->acap + 64; + fz_edge **newactive = fz_realloc_array(ctx, gel->active, newcap, fz_edge*); + gel->active = newactive; + gel->acap = newcap; + } + gel->active[gel->alen++] = &gel->edges[e++]; + } while (e < gel->len && gel->edges[e].y == y); + *e_ = e; + } + + if (e < gel->len) + h_min = gel->edges[e].y - y; + + for (e=0; e < gel->alen; e++) + { + if (gel->active[e]->xmove != 0 || gel->active[e]->adj_up != 0) + { + h_min = 1; + break; + } + if (gel->active[e]->h < h_min) + { + h_min = gel->active[e]->h; + if (h_min == 1) + break; + } + } + + /* shell-sort the edges by increasing x */ + sort_active(gel->active, gel->alen); + + return h_min; +} + +static void +advance_active(fz_context *ctx, fz_gel *gel, int inc) +{ + fz_edge *edge; + int i = 0; + + while (i < gel->alen) + { + edge = gel->active[i]; + + edge->h -= inc; + + /* terminator! */ + if (edge->h == 0) { + gel->active[i] = gel->active[--gel->alen]; + } + + else { + edge->x += edge->xmove; + edge->e += edge->adj_up; + if (edge->e > 0) { + edge->x += edge->xdir; + edge->e -= edge->adj_down; + } + i ++; + } + } +} + +/* + * Anti-aliased scan conversion. + */ + +static inline void +add_span_aa(fz_context *ctx, fz_gel *gel, int *list, int x0, int x1, int xofs, int h) +{ + int x0pix, x0sub; + int x1pix, x1sub; + const int hscale = fz_rasterizer_aa_hscale(&gel->super); + + if (x0 == x1) + return; + + /* x between 0 and width of bbox */ + x0 -= xofs; + x1 -= xofs; + + /* The cast to unsigned below helps the compiler produce faster + * code on ARMs as the multiply by reciprocal trick it uses does not + * need to correct for signedness. */ + x0pix = ((unsigned int)x0) / hscale; + x0sub = ((unsigned int)x0) % hscale; + x1pix = ((unsigned int)x1) / hscale; + x1sub = ((unsigned int)x1) % hscale; + + if (x0pix == x1pix) + { + list[x0pix] += h*(x1sub - x0sub); + list[x0pix+1] += h*(x0sub - x1sub); + } + + else + { + list[x0pix] += h*(hscale - x0sub); + list[x0pix+1] += h*x0sub; + list[x1pix] += h*(x1sub - hscale); + list[x1pix+1] += h*-x1sub; + } +} + +static inline void +non_zero_winding_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h) +{ + int winding = 0; + int x = 0; + int i; + + for (i = 0; i < gel->alen; i++) + { + if (!winding && (winding + gel->active[i]->ydir)) + x = gel->active[i]->x; + if (winding && !(winding + gel->active[i]->ydir)) + add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h); + winding += gel->active[i]->ydir; + } +} + +static inline void +even_odd_aa(fz_context *ctx, fz_gel *gel, int *list, int xofs, int h) +{ + int even = 0; + int x = 0; + int i; + + for (i = 0; i < gel->alen; i++) + { + if (!even) + x = gel->active[i]->x; + else + add_span_aa(ctx, gel, list, x, gel->active[i]->x, xofs, h); + even = !even; + } +} + +static inline void +undelta_aa(fz_context *ctx, unsigned char * FZ_RESTRICT out, int * FZ_RESTRICT in, int n, int scale) +{ + int d = 0; + (void)scale; /* Avoid warnings in some builds */ + + while (n--) + { + d += *in++; + *out++ = AA_SCALE(scale, d); + } +} + +static inline void +blit_aa(fz_pixmap *dst, int x, int y, unsigned char *mp, int w, unsigned char *color, void *fn, fz_overprint *eop) +{ + unsigned char *dp; + dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x - dst->x) * (size_t)dst->n; + if (color) + (*(fz_span_color_painter_t *)fn)(dp, mp, dst->n, w, color, dst->alpha, eop); + else + (*(fz_span_painter_t *)fn)(dp, dst->alpha, mp, 1, 0, w, 255, eop); +} + +static void +fz_scan_convert_aa(fz_context *ctx, fz_gel *gel, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, void *painter, fz_overprint *eop) +{ + unsigned char *alphas; + int *deltas; + int y, e; + int yd, yc; + int height, h0, rh; + int bcap; + const int hscale = fz_rasterizer_aa_hscale(&gel->super); + const int vscale = fz_rasterizer_aa_vscale(&gel->super); + const int scale = fz_rasterizer_aa_scale(&gel->super); + + int xmin = fz_idiv(gel->super.bbox.x0, hscale); + int xmax = fz_idiv_up(gel->super.bbox.x1, hscale); + + int xofs = xmin * hscale; + + int skipx = clip->x0 - xmin; + int clipn = clip->x1 - clip->x0; + + if (gel->len == 0) + return; + + assert(xmin < xmax); + assert(clip->x0 >= xmin); + assert(clip->x1 <= xmax); + + bcap = xmax - xmin + 2; /* big enough for both alphas and deltas */ + if (bcap > gel->bcap) + { + gel->bcap = bcap; + fz_free(ctx, gel->alphas); + fz_free(ctx, gel->deltas); + gel->alphas = NULL; + gel->deltas = NULL; + gel->alphas = Memento_label(fz_malloc_array(ctx, bcap, unsigned char), "gel_alphas"); + gel->deltas = Memento_label(fz_malloc_array(ctx, bcap, int), "gel_deltas"); + } + alphas = gel->alphas; + deltas = gel->deltas; + + memset(deltas, 0, (xmax - xmin + 1) * sizeof(int)); + gel->alen = 0; + + /* The theory here is that we have a list of the edges (gel) of length + * gel->len. We have an initially empty list of 'active' edges (of + * length gel->alen). As we increase y, we move any edge that is + * active at this point into the active list. We know that any edge + * before index 'e' is either active, or has been retired. + * Once the length of the active list is 0, and e has reached gel->len + * we know we are finished. + * + * As we move through the list, we group fz_aa_vscale 'sub scanlines' + * into single scanlines, and we blit them. + */ + + e = 0; + y = gel->edges[0].y; + yd = fz_idiv(y, vscale); + + /* Quickly skip to the start of the clip region */ + while (yd < clip->y0 && (gel->alen > 0 || e < gel->len)) + { + /* rh = remaining height = number of subscanlines left to be + * inserted into the current scanline, which will be plotted + * at yd. */ + rh = (yd+1)*vscale - y; + + /* height = The number of subscanlines with identical edge + * positions (i.e. 1 if we have any non vertical edges). */ + height = insert_active(ctx, gel, y, &e); + h0 = height; + if (h0 >= rh) + { + /* We have enough subscanlines to skip to the next + * scanline. */ + h0 -= rh; + yd++; + } + /* Skip any whole scanlines we can */ + while (yd < clip->y0 && h0 >= vscale) + { + h0 -= vscale; + yd++; + } + /* If we haven't hit the start of the clip region, then we + * have less than a scanline left. */ + if (yd < clip->y0) + { + h0 = 0; + } + height -= h0; + advance_active(ctx, gel, height); + + y += height; + } + + /* Now do the active lines */ + while (gel->alen > 0 || e < gel->len) + { + yc = fz_idiv(y, vscale); /* yc = current scanline */ + /* rh = remaining height = number of subscanlines left to be + * inserted into the current scanline, which will be plotted + * at yd. */ + rh = (yc+1)*vscale - y; + if (yc != yd) + { + undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + } + yd = yc; + if (yd >= clip->y1) + break; + + /* height = The number of subscanlines with identical edge + * positions (i.e. 1 if we have any non vertical edges). */ + height = insert_active(ctx, gel, y, &e); + h0 = height; + if (h0 > rh) + { + if (rh < vscale) + { + /* We have to finish a scanline off, and we + * have more sub scanlines than will fit into + * it. */ + if (eofill) + even_odd_aa(ctx, gel, deltas, xofs, rh); + else + non_zero_winding_aa(ctx, gel, deltas, xofs, rh); + undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + yd++; + if (yd >= clip->y1) + break; + h0 -= rh; + } + if (h0 > vscale) + { + /* Calculate the deltas for any completely full + * scanlines. */ + h0 -= vscale; + if (eofill) + even_odd_aa(ctx, gel, deltas, xofs, vscale); + else + non_zero_winding_aa(ctx, gel, deltas, xofs, vscale); + undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); + do + { + /* Do any successive whole scanlines - no need + * to recalculate deltas here. */ + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); + yd++; + if (yd >= clip->y1) + goto clip_ended; + h0 -= vscale; + } + while (h0 > 0); + /* If we have exactly one full scanline left + * to go, then the deltas/alphas are set up + * already. */ + if (h0 == 0) + goto advance; + memset(deltas, 0, (skipx + clipn) * sizeof(int)); + h0 += vscale; + } + } + if (eofill) + even_odd_aa(ctx, gel, deltas, xofs, h0); + else + non_zero_winding_aa(ctx, gel, deltas, xofs, h0); +advance: + advance_active(ctx, gel, height); + + y += height; + } + + if (yd < clip->y1) + { + undelta_aa(ctx, alphas, deltas, skipx + clipn, scale); + blit_aa(dst, xmin + skipx, yd, alphas + skipx, clipn, color, painter, eop); + } +clip_ended: + ; +} + +/* + * Sharp (not anti-aliased) scan conversion + */ + +static inline void +blit_sharp(int x0, int x1, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) +{ + unsigned char *dp; + int da = dst->alpha; + x0 = fz_clampi(x0, dst->x, dst->x + dst->w); + x1 = fz_clampi(x1, dst->x, dst->x + dst->w); + if (x0 < x1) + { + dp = dst->samples + (y - dst->y) * (size_t)dst->stride + (x0 - dst->x) * (size_t)dst->n; + if (color) + (*fn)(dp, dst->n, x1 - x0, color, da, eop); + else + memset(dp, 255, x1-x0); + } +} + +static inline void +non_zero_winding_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) +{ + int winding = 0; + int x = 0; + int i; + for (i = 0; i < gel->alen; i++) + { + if (!winding && (winding + gel->active[i]->ydir)) + x = gel->active[i]->x; + if (winding && !(winding + gel->active[i]->ydir)) + blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop); + winding += gel->active[i]->ydir; + } +} + +static inline void +even_odd_sharp(fz_context *ctx, fz_gel *gel, int y, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) +{ + int even = 0; + int x = 0; + int i; + for (i = 0; i < gel->alen; i++) + { + if (!even) + x = gel->active[i]->x; + else + blit_sharp(x, gel->active[i]->x, y, clip, dst, color, fn, eop); + even = !even; + } +} + +static void +fz_scan_convert_sharp(fz_context *ctx, + fz_gel *gel, int eofill, const fz_irect *clip, + fz_pixmap *dst, unsigned char *color, fz_solid_color_painter_t *fn, fz_overprint *eop) +{ + int e = 0; + int y = gel->edges[0].y; + int height; + + gel->alen = 0; + + /* Skip any lines before the clip region */ + if (y < clip->y0) + { + while (gel->alen > 0 || e < gel->len) + { + height = insert_active(ctx, gel, y, &e); + y += height; + if (y >= clip->y0) + { + y = clip->y0; + break; + } + } + } + + /* Now process as lines within the clip region */ + while (gel->alen > 0 || e < gel->len) + { + height = insert_active(ctx, gel, y, &e); + + if (gel->alen == 0) + y += height; + else + { + int h; + if (height >= clip->y1 - y) + height = clip->y1 - y; + + h = height; + while (h--) + { + if (eofill) + even_odd_sharp(ctx, gel, y, clip, dst, color, fn, eop); + else + non_zero_winding_sharp(ctx, gel, y, clip, dst, color, fn, eop); + y++; + } + } + if (y >= clip->y1) + break; + + advance_active(ctx, gel, height); + } +} + +static void +fz_convert_gel(fz_context *ctx, fz_rasterizer *rast, int eofill, const fz_irect *clip, fz_pixmap *dst, unsigned char *color, fz_overprint *eop) +{ + fz_gel *gel = (fz_gel *)rast; + + sort_gel(ctx, gel); + + if (fz_aa_bits > 0) + { + void *fn; + if (color) + fn = (void *)fz_get_span_color_painter(dst->n, dst->alpha, color, eop); + else + fn = (void *)fz_get_span_painter(dst->alpha, 1, 0, 255, eop); + if (fn == NULL) + return; + fz_scan_convert_aa(ctx, gel, eofill, clip, dst, color, fn, eop); + } + else + { + fz_solid_color_painter_t *fn = fz_get_solid_color_painter(dst->n, color, dst->alpha, eop); + assert(fn); + if (fn == NULL) + return; + fz_scan_convert_sharp(ctx, gel, eofill, clip, dst, color, (fz_solid_color_painter_t *)fn, eop); + } +} + +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 = Memento_label(fz_malloc_array(ctx, gel->cap, fz_edge), "gel_edges"); + + gel->acap = 64; + gel->alen = 0; + gel->active = Memento_label(fz_malloc_array(ctx, gel->acap, fz_edge*), "gel_active"); + } + fz_catch(ctx) + { + fz_free(ctx, gel->edges); + fz_free(ctx, gel); + fz_rethrow(ctx); + } + + return &gel->super; +}
