| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619 |
- // 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 <math.h>
- #include <float.h>
- #include <assert.h>
- #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_rasterizer *rast, 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_rasterizer(ctx, rast, tx0, ty0, tx1, ty1, 0);
- }
- static void
- bezier(fz_context *ctx, fz_rasterizer *rast, fz_matrix ctm, float flatness,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc,
- float xd, float yd, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xcd, ycd;
- float xabc, yabc;
- float xbcd, ybcd;
- float xabcd, yabcd;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xd - xc));
- dmax = fz_max(dmax, fz_abs(yd - yc));
- if (dmax < flatness || depth >= MAX_DEPTH)
- {
- line(ctx, rast, ctm, xa, ya, xd, yd);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xcd = xc + xd;
- ycd = yc + yd;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xbcd = xbc + xcd;
- ybcd = ybc + ycd;
- xabcd = xabc + xbcd;
- yabcd = yabc + ybcd;
- xab *= 0.5f; yab *= 0.5f;
- /* xbc *= 0.5f; ybc *= 0.5f; */
- xcd *= 0.5f; ycd *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- xbcd *= 0.25f; ybcd *= 0.25f;
- xabcd *= 0.125f; yabcd *= 0.125f;
- 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_rasterizer *rast, fz_matrix ctm, float flatness,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xabc, yabc;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xc - xb));
- dmax = fz_max(dmax, fz_abs(yc - yb));
- if (dmax < flatness || depth >= MAX_DEPTH)
- {
- line(ctx, rast, ctm, xa, ya, xc, yc);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xab *= 0.5f; yab *= 0.5f;
- xbc *= 0.5f; ybc *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- 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_rasterizer *rast;
- fz_matrix ctm;
- float flatness;
- fz_point b;
- fz_point c;
- }
- flatten_arg;
- static void
- flatten_moveto(fz_context *ctx, void *arg_, float x, float y)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- /* implicit closepath before moveto */
- if (arg->c.x != arg->b.x || arg->c.y != 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
- flatten_lineto(fz_context *ctx, void *arg_, float x, float y)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- line(ctx, arg->rast, arg->ctm, arg->c.x, arg->c.y, x, y);
- arg->c.x = x;
- arg->c.y = y;
- }
- static void
- flatten_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- 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;
- }
- static void
- flatten_quadto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- 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;
- }
- static void
- flatten_close(fz_context *ctx, void *arg_)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- 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;
- }
- static void
- flatten_rectto(fz_context *ctx, void *arg_, float x0, float y0, float x1, float y1)
- {
- flatten_arg *arg = (flatten_arg *)arg_;
- fz_matrix ctm = arg->ctm;
- flatten_moveto(ctx, arg_, x0, y0);
- if (fz_antidropout_rasterizer(ctx, arg->rast))
- {
- /* 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 =
- {
- flatten_moveto,
- flatten_lineto,
- flatten_curveto,
- flatten_close,
- flatten_quadto,
- NULL,
- NULL,
- flatten_rectto
- };
- static int
- do_flatten_fill(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, fz_matrix ctm, float flatness)
- {
- flatten_arg arg;
- 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, rast, ctm, arg.c.x, arg.c.y, arg.b.x, arg.b.y);
- fz_gap_rasterizer(ctx, rast);
- return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
- }
- int
- fz_flatten_fill_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, fz_matrix ctm, float flatness, fz_irect scissor, fz_irect *bbox)
- {
- int empty;
- fz_irect local_bbox;
- if (!bbox)
- bbox = &local_bbox;
- /* If we're given an empty scissor, sanitize it. This makes life easier
- * down the line. */
- if (fz_is_empty_irect(scissor))
- scissor.x1 = scissor.x0, scissor.y1 = scissor.y0;
- if (fz_reset_rasterizer(ctx, rast, scissor))
- {
- empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
- if (empty)
- return *bbox = fz_empty_irect, 1;
- fz_postindex_rasterizer(ctx, rast);
- }
- empty = do_flatten_fill(ctx, rast, path, ctm, flatness);
- if (empty)
- return *bbox = fz_empty_irect, 1;
- *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
- return fz_is_empty_irect(*bbox);
- }
- typedef struct sctx
- {
- fz_rasterizer *rast;
- fz_matrix ctm;
- float flatness;
- const fz_stroke_state *stroke;
- int linejoin;
- float linewidth;
- float miterlimit;
- fz_point beg[2];
- fz_point seg[2];
- int sn;
- int not_just_moves;
- int from_bezier;
- fz_point cur;
- fz_rect rect;
- const float *dash_list;
- float dash_phase;
- int dash_len;
- float dash_total;
- int toggle, cap;
- int offset;
- float phase;
- fz_point dash_cur;
- fz_point dash_beg;
- float dirn_x;
- float dirn_y;
- } sctx;
- static void
- 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_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 (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 (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, 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,
- int rev)
- {
- float th0, th1, r;
- float theta;
- float ox, oy, nx, ny;
- int n, i;
- r = fabsf(s->linewidth);
- theta = 2 * FZ_SQRT2 * sqrtf(s->flatness / r);
- th0 = atan2f(y0, x0);
- th1 = atan2f(y1, x1);
- if (r > 0)
- {
- if (th0 < th1)
- th0 += FZ_PI * 2;
- n = ceilf((th0 - th1) / theta);
- }
- else
- {
- if (th1 < th0)
- th1 += FZ_PI * 2;
- n = ceilf((th1 - th0) / theta);
- }
- if (rev)
- {
- 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;
- }
- fz_add_line(ctx, s, xc + x0, yc + y0, xc + ox, yc + oy, rev);
- }
- else
- {
- 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);
- }
- }
- /* FLT_TINY * FLT_TINY is approximately FLT_EPSILON */
- #define FLT_TINY 3.4e-4F
- static int find_normal_vectors(float dx, float dy, float linewidth, float *dlx, float *dly)
- {
- if (dx == 0)
- {
- if (dy < FLT_TINY && dy > - FLT_TINY)
- goto tiny;
- else if (dy > 0)
- *dlx = linewidth;
- else
- *dlx = -linewidth;
- *dly = 0;
- }
- else if (dy == 0)
- {
- if (dx < FLT_TINY && dx > - FLT_TINY)
- goto tiny;
- else if (dx > 0)
- *dly = -linewidth;
- else
- *dly = linewidth;
- *dlx = 0;
- }
- else
- {
- float sq = dx * dx + dy * dy;
- float scale;
- if (sq < FLT_EPSILON)
- goto tiny;
- scale = linewidth / sqrtf(sq);
- *dlx = dy * scale;
- *dly = -dx * scale;
- }
- return 0;
- tiny:
- *dlx = 0;
- *dly = 0;
- return 1;
- }
- static void
- fz_add_line_join(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, float cx, float cy, int join_under)
- {
- float miterlimit = s->miterlimit;
- float linewidth = s->linewidth;
- fz_linejoin linejoin = s->linejoin;
- float dx0, dy0;
- float dx1, dy1;
- float dlx0, dly0;
- float dlx1, dly1;
- float dmx, dmy;
- float dmr2;
- float scale;
- float cross;
- int rev = 0;
- dx0 = bx - ax;
- dy0 = by - ay;
- dx1 = cx - bx;
- dy1 = cy - by;
- cross = dx1 * dy0 - dx0 * dy1;
- /* Ensure that cross >= 0 */
- if (cross < 0)
- {
- float tmp;
- tmp = dx1; dx1 = -dx0; dx0 = -tmp;
- tmp = dy1; dy1 = -dy0; dy0 = -tmp;
- cross = -cross;
- rev = !rev;
- }
- if (find_normal_vectors(dx0, dy0, linewidth, &dlx0, &dly0))
- linejoin = FZ_LINEJOIN_BEVEL;
- if (find_normal_vectors(dx1, dy1, linewidth, &dlx1, &dly1))
- linejoin = FZ_LINEJOIN_BEVEL;
- dmx = (dlx0 + dlx1) * 0.5f;
- dmy = (dly0 + dly1) * 0.5f;
- dmr2 = dmx * dmx + dmy * dmy;
- if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
- linejoin = FZ_LINEJOIN_BEVEL;
- /* XPS miter joins are clipped at miterlength, rather than simply
- * being converted to bevelled joins. */
- if (linejoin == FZ_LINEJOIN_MITER_XPS)
- {
- if (cross == 0)
- linejoin = FZ_LINEJOIN_BEVEL;
- else if (dmr2 * miterlimit * miterlimit >= linewidth * linewidth)
- linejoin = FZ_LINEJOIN_MITER;
- }
- 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:
- {
- 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;
- 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, rev);
- break;
- case FZ_LINEJOIN_ROUND:
- fz_add_arc(ctx, s, bx, by, -dlx0, -dly0, -dlx1, -dly1, rev);
- break;
- default:
- assert("Invalid line join" == NULL);
- }
- }
- static void
- do_linecap(fz_context *ctx, sctx *s, float bx, float by, fz_linecap linecap, int rev, float dlx, float dly)
- {
- float flatness = s->flatness;
- float linewidth = s->linewidth;
- switch (linecap)
- {
- case FZ_LINECAP_BUTT:
- fz_add_line(ctx, s, bx - dlx, by - dly, bx + dlx, by + dly, rev);
- break;
- case FZ_LINECAP_ROUND:
- {
- int i;
- int n = ceilf(FZ_PI / (2.0f * FZ_SQRT2 * sqrtf(flatness / linewidth)));
- float ox = bx - dlx;
- float oy = by - dly;
- for (i = 1; i < n; i++)
- {
- float theta = FZ_PI * i / n;
- float cth = cosf(theta);
- 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, rev);
- ox = nx;
- oy = ny;
- }
- 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, rev);
- fz_add_line(ctx, s, 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, 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, rev);
- fz_add_line(ctx, s, bx + mx, by + my, bx + dlx, by + dly, rev);
- break;
- }
- default:
- assert("Invalid line cap" == NULL);
- }
- }
- static void
- fz_add_line_cap(fz_context *ctx, sctx *s, float ax, float ay, float bx, float by, fz_linecap linecap, int rev)
- {
- float linewidth = s->linewidth;
- float dx = bx - ax;
- float dy = by - ay;
- float scale = linewidth / sqrtf(dx * dx + dy * dy);
- float dlx = dy * scale;
- float dly = -dx * scale;
- do_linecap(ctx, s, bx, by, linecap, rev, dlx, dly);
- }
- static void
- fz_add_zero_len_cap(fz_context *ctx, sctx *s, float ax, float ay, fz_linecap linecap, int rev)
- {
- float linewidth = s->linewidth;
- float dx = rev ? -s->dirn_x : s->dirn_x;
- float dy = rev ? -s->dirn_y : s->dirn_y;
- float scale, dlx, dly;
- if (dx == 0 && dy == 0)
- return;
- scale = linewidth / sqrtf(dx * dx + dy * dy);
- dlx = dy * scale;
- dly = -dx * scale;
- do_linecap(ctx, s, ax, ay, linecap, rev, dlx, dly);
- }
- static void
- fz_add_line_dot(fz_context *ctx, sctx *s, float ax, float ay)
- {
- float flatness = s->flatness;
- float linewidth = s->linewidth;
- int n = ceilf(FZ_PI / (FZ_SQRT2 * sqrtf(flatness / linewidth)));
- float ox = ax - linewidth;
- float oy = ay;
- int i;
- if (n < 3)
- n = 3;
- for (i = 1; i < n; i++)
- {
- float theta = FZ_PI * 2 * i / n;
- float cth = cosf(theta);
- float sth = sinf(theta);
- float nx = ax - cth * linewidth;
- float ny = ay + sth * linewidth;
- fz_add_line(ctx, s, ox, oy, nx, ny, 0);
- ox = nx;
- oy = ny;
- }
- fz_add_line(ctx, s, ox, oy, ax - linewidth, ay, 0);
- }
- static void
- fz_stroke_flush(fz_context *ctx, sctx *s, fz_linecap start_cap, fz_linecap end_cap)
- {
- if (s->sn == 1)
- {
- 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->not_just_moves)
- {
- if (s->cap == FZ_LINECAP_ROUND)
- {
- fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y);
- }
- else
- {
- fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 2);
- fz_add_zero_len_cap(ctx, s, s->beg[0].x, s->beg[0].y, s->cap, 0);
- }
- }
- fz_gap_rasterizer(ctx, s->rast);
- }
- static void
- fz_stroke_moveto(fz_context *ctx, void *s_, float x, float y)
- {
- struct sctx *s = (struct sctx *)s_;
- s->seg[0].x = s->beg[0].x = x;
- s->seg[0].y = s->beg[0].y = y;
- s->sn = 0;
- s->not_just_moves = 0;
- s->from_bezier = 0;
- s->dirn_x = 0;
- s->dirn_y = 0;
- }
- static void
- fz_stroke_lineto_aux(fz_context *ctx, sctx *s, float x, float y, int from_bezier, float dirn_x, float dirn_y)
- {
- float ox = s->seg[s->sn].x;
- float oy = s->seg[s->sn].y;
- float dx = x - ox;
- float dy = y - oy;
- float dlx, dly;
- s->not_just_moves = 1;
- /* We store the direction (as used for the alignment of caps etc) based on the
- * direction we are passed in. */
- s->dirn_x = dirn_x;
- s->dirn_y = dirn_y;
- /* We calculate the normal vectors from the delta that we have just moved. */
- if (find_normal_vectors(dx, dy, s->linewidth, &dlx, &dly))
- {
- return;
- }
- if (s->sn == 1)
- fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, ox, oy, x, y, s->from_bezier & from_bezier);
- #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)
- {
- s->seg[0] = s->seg[1];
- s->seg[1].x = x;
- s->seg[1].y = y;
- }
- else
- {
- s->seg[1].x = s->beg[1].x = x;
- s->seg[1].y = s->beg[1].y = y;
- s->sn = 1;
- }
- s->from_bezier = from_bezier;
- }
- static void
- fz_stroke_lineto(fz_context *ctx, sctx *s, float x, float y, int from_bezier)
- {
- float ox = s->seg[s->sn].x;
- float oy = s->seg[s->sn].y;
- float dx = x - ox;
- float dy = y - oy;
- fz_stroke_lineto_aux(ctx, s, x, y, from_bezier, dx, dy);
- }
- static void
- fz_stroke_closepath(fz_context *ctx, sctx *s)
- {
- if (s->sn == 1)
- {
- fz_stroke_lineto(ctx, s, s->beg[0].x, s->beg[0].y, 0);
- /* fz_stroke_lineto will *normally* end up with s->seg[1] being the x,y coords passed in.
- * As such, the following line should draw a linejoin between the closing segment of this
- * subpath (seg[0]->seg[1]) == (seg[0]->beg[0]) and the first segment of this subpath
- * (beg[0]->beg[1]).
- * In cases where the line was already at an x,y infinitesimally close to s->beg[0],
- * fz_stroke_lineto may exit without doing any processing. This leaves seg[0]->seg[1]
- * pointing at the penultimate line segment. Thus this draws a linejoin between that
- * penultimate segment and the end segment. This is what we want. */
- fz_add_line_join(ctx, s, s->seg[0].x, s->seg[0].y, s->beg[0].x, s->beg[0].y, s->beg[1].x, s->beg[1].y, 0);
- }
- else if (s->not_just_moves && s->cap == FZ_LINECAP_ROUND)
- fz_add_line_dot(ctx, s, s->beg[0].x, s->beg[0].y);
- s->seg[0] = s->beg[0];
- s->sn = 0;
- s->not_just_moves = 0;
- s->from_bezier = 0;
- s->dirn_x = 0;
- s->dirn_y = 0;
- fz_gap_rasterizer(ctx, s->rast);
- }
- static void
- fz_stroke_bezier(fz_context *ctx, struct sctx *s,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc,
- float xd, float yd, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xcd, ycd;
- float xabc, yabc;
- float xbcd, ybcd;
- float xabcd, yabcd;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xd - xc));
- dmax = fz_max(dmax, fz_abs(yd - yc));
- if (dmax < s->flatness || depth >= MAX_DEPTH)
- {
- fz_stroke_lineto(ctx, s, xd, yd, 1);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xcd = xc + xd;
- ycd = yc + yd;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xbcd = xbc + xcd;
- ybcd = ybc + ycd;
- xabcd = xabc + xbcd;
- yabcd = yabc + ybcd;
- xab *= 0.5f; yab *= 0.5f;
- /* xbc *= 0.5f; ybc *= 0.5f; */
- xcd *= 0.5f; ycd *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- xbcd *= 0.25f; ybcd *= 0.25f;
- xabcd *= 0.125f; yabcd *= 0.125f;
- fz_stroke_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
- fz_stroke_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
- }
- static void
- fz_stroke_quad(fz_context *ctx, struct sctx *s,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xabc, yabc;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xc - xb));
- dmax = fz_max(dmax, fz_abs(yc - yb));
- if (dmax < s->flatness || depth >= MAX_DEPTH)
- {
- fz_stroke_lineto(ctx, s, xc, yc, 1);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xab *= 0.5f; yab *= 0.5f;
- xbc *= 0.5f; ybc *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- fz_stroke_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1);
- fz_stroke_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
- }
- static void
- stroke_moveto(fz_context *ctx, void *s_, float x, float y)
- {
- sctx *s = (sctx *)s_;
- fz_stroke_flush(ctx, s, s->stroke->start_cap, s->stroke->end_cap);
- fz_stroke_moveto(ctx, s, x, y);
- s->cur.x = x;
- s->cur.y = y;
- }
- static void
- stroke_lineto(fz_context *ctx, void *s_, float x, float y)
- {
- sctx *s = (sctx *)s_;
- fz_stroke_lineto(ctx, s, x, y, 0);
- s->cur.x = x;
- s->cur.y = y;
- }
- static void
- stroke_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3)
- {
- sctx *s = (sctx *)s_;
- fz_stroke_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
- s->cur.x = x3;
- s->cur.y = y3;
- }
- static void
- stroke_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2)
- {
- sctx *s = (sctx *)s_;
- fz_stroke_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0);
- s->cur.x = x2;
- s->cur.y = y2;
- }
- static void
- stroke_close(fz_context *ctx, void *s_)
- {
- sctx *s = (sctx *)s_;
- fz_stroke_closepath(ctx, s);
- }
- static const fz_path_walker stroke_proc =
- {
- stroke_moveto,
- stroke_lineto,
- stroke_curveto,
- stroke_close,
- stroke_quadto
- };
- static void
- fz_dash_moveto(fz_context *ctx, struct sctx *s, float x, float y)
- {
- s->toggle = 1;
- s->offset = 0;
- s->phase = s->dash_phase;
- while (s->phase > 0 && s->phase >= s->dash_list[s->offset])
- {
- s->toggle = !s->toggle;
- s->phase -= s->dash_list[s->offset];
- s->offset ++;
- if (s->offset == s->dash_len)
- s->offset = 0;
- }
- s->dash_cur.x = x;
- s->dash_cur.y = y;
- if (s->toggle)
- {
- fz_stroke_flush(ctx, s, s->cap, s->stroke->end_cap);
- s->cap = s->stroke->start_cap;
- fz_stroke_moveto(ctx, s, x, y);
- }
- }
- /*
- Performs: a += (b-a) * i/n
- allowing for FP inaccuracies that can cause a to "overrun" b.
- */
- static float advance(float a, float b, float i, float n)
- {
- float d = b - a;
- float target = a + d * i/n;
- if (d < 0 && target < b)
- target = b;
- else if (d > 0 && target > b)
- target = b;
- return target;
- }
- static void
- fz_dash_lineto(fz_context *ctx, struct sctx *s, float bx, float by, int from_bezier)
- {
- float dx, dy, d;
- float total, used, ratio, tail;
- float ax, ay;
- float mx, my;
- float old_bx = 0, old_by = 0;
- int n;
- int dash_cap = s->stroke->dash_cap;
- ax = s->dash_cur.x;
- ay = s->dash_cur.y;
- dx = bx - ax;
- dy = by - ay;
- used = 0;
- tail = 0;
- total = sqrtf(dx * dx + dy * dy);
- /* If a is off screen, bring it onto the screen. First
- * horizontally... */
- if ((d = s->rect.x0 - ax) > 0)
- {
- if (bx < s->rect.x0)
- {
- /* Entirely off screen */
- tail = total;
- old_bx = bx;
- old_by = by;
- goto adjust_for_tail;
- }
- ax = s->rect.x0; /* d > 0, dx > 0 */
- goto a_moved_horizontally;
- }
- else if (d < 0 && (d = (s->rect.x1 - ax)) < 0)
- {
- if (bx > s->rect.x1)
- {
- /* Entirely off screen */
- tail = total;
- old_bx = bx;
- old_by = by;
- goto adjust_for_tail;
- }
- ax = s->rect.x1; /* d < 0, dx < 0 */
- a_moved_horizontally: /* d and dx have the same sign */
- assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
- assert(dx != 0);
- ay = advance(ay, by, d, dx);
- used = total * d/dx;
- total -= used;
- dx = bx - ax;
- dy = by - ay;
- }
- /* Then vertically... */
- if ((d = s->rect.y0 - ay) > 0)
- {
- if (by < s->rect.y0)
- {
- /* Entirely off screen */
- tail = total;
- old_bx = bx;
- old_by = by;
- goto adjust_for_tail;
- }
- ay = s->rect.y0; /* d > 0, dy > 0 */
- goto a_moved_vertically;
- }
- else if (d < 0 && (d = (s->rect.y1 - ay)) < 0)
- {
- if (by > s->rect.y1)
- {
- /* Entirely off screen */
- tail = total;
- old_bx = bx;
- old_by = by;
- goto adjust_for_tail;
- }
- ay = s->rect.y1; /* d < 0, dy < 0 */
- a_moved_vertically: /* d and dy have the same sign */
- assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
- assert(dy != 0);
- ax = advance(ax, bx, d, dy);
- d = total * d/dy;
- total -= d;
- used += d;
- dx = bx - ax;
- dy = by - ay;
- }
- if (used != 0.0f)
- {
- /* Update the position in the dash array */
- if (s->toggle)
- {
- fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
- }
- else
- {
- fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
- s->cap = s->stroke->dash_cap;
- fz_stroke_moveto(ctx, s, ax, ay);
- }
- used += s->phase;
- n = used/s->dash_total;
- used -= n*s->dash_total;
- if (n & s->dash_len & 1)
- s->toggle = !s->toggle;
- while (used >= s->dash_list[s->offset])
- {
- used -= s->dash_list[s->offset];
- s->offset++;
- if (s->offset == s->dash_len)
- s->offset = 0;
- s->toggle = !s->toggle;
- }
- if (s->toggle)
- {
- fz_stroke_lineto(ctx, s, ax, ay, from_bezier);
- }
- else
- {
- fz_stroke_flush(ctx, s, s->cap, s->stroke->dash_cap);
- s->cap = s->stroke->dash_cap;
- fz_stroke_moveto(ctx, s, ax, ay);
- }
- s->phase = used;
- used = 0;
- }
- /* Now if bx is off screen, bring it back */
- if (dx == 0)
- {
- /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
- }
- else if ((d = bx - s->rect.x0) < 0)
- {
- old_bx = bx;
- old_by = by;
- bx = s->rect.x0; /* d < 0, dx < 0 */
- goto b_moved_horizontally;
- }
- else if (d > 0 && (d = (bx - s->rect.x1)) > 0)
- {
- old_bx = bx;
- old_by = by;
- bx = s->rect.x1; /* d > 0, dx > 0 */
- b_moved_horizontally: /* d and dx have the same sign */
- assert((d > 0 && dx > 0) || (d < 0 && dx < 0));
- assert(dx != 0);
- by = advance(by, ay, d, dx);
- tail = total * d/dx;
- total -= tail;
- dx = bx - ax;
- dy = by - ay;
- }
- /* Then vertically... */
- if (dy == 0)
- {
- /* Earlier stages can have moved a to be b, while leaving it completely off screen. */
- }
- else if ((d = by - s->rect.y0) < 0)
- {
- old_bx = bx;
- old_by = by;
- by = s->rect.y0; /* d < 0, dy < 0 */
- goto b_moved_vertically;
- }
- else if (d > 0 && (d = (by - s->rect.y1)) > 0)
- {
- float t;
- old_bx = bx;
- old_by = by;
- by = s->rect.y1; /* d > 0, dy > 0 */
- b_moved_vertically: /* d and dy have the same sign */
- assert((d > 0 && dy > 0) || (d < 0 && dy < 0));
- assert(dy != 0);
- bx = advance(bx, ax, d, dy);
- t = total * d/dy;
- tail += t;
- total -= t;
- dx = bx - ax;
- dy = by - ay;
- }
- while (total - used > s->dash_list[s->offset] - s->phase)
- {
- used += s->dash_list[s->offset] - s->phase;
- ratio = used / total;
- mx = ax + ratio * dx;
- my = ay + ratio * dy;
- if (s->toggle)
- {
- fz_stroke_lineto_aux(ctx, s, mx, my, from_bezier, dx, dy);
- }
- else
- {
- fz_stroke_flush(ctx, s, s->cap, dash_cap);
- s->cap = dash_cap;
- fz_stroke_moveto(ctx, s, mx, my);
- }
- s->toggle = !s->toggle;
- s->phase = 0;
- s->offset ++;
- if (s->offset == s->dash_len)
- s->offset = 0;
- }
- s->phase += total - used;
- if (tail == 0.0f)
- {
- s->dash_cur.x = bx;
- s->dash_cur.y = by;
- if (s->toggle)
- {
- fz_stroke_lineto_aux(ctx, s, bx, by, from_bezier, dx, dy);
- }
- }
- else
- {
- adjust_for_tail:
- s->dash_cur.x = old_bx;
- s->dash_cur.y = old_by;
- /* Update the position in the dash array */
- if (s->toggle)
- {
- fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
- }
- else
- {
- fz_stroke_flush(ctx, s, s->cap, dash_cap);
- s->cap = dash_cap;
- fz_stroke_moveto(ctx, s, old_bx, old_by);
- }
- tail += s->phase;
- n = tail/s->dash_total;
- tail -= n*s->dash_total;
- if (n & s->dash_len & 1)
- s->toggle = !s->toggle;
- while (tail > s->dash_list[s->offset])
- {
- tail -= s->dash_list[s->offset];
- s->offset++;
- if (s->offset == s->dash_len)
- s->offset = 0;
- s->toggle = !s->toggle;
- }
- if (s->toggle)
- {
- fz_stroke_lineto_aux(ctx, s, old_bx, old_by, from_bezier, dx, dy);
- }
- else
- {
- fz_stroke_flush(ctx, s, s->cap, dash_cap);
- s->cap = dash_cap;
- fz_stroke_moveto(ctx, s, old_bx, old_by);
- }
- s->phase = tail;
- }
- }
- static void
- fz_dash_bezier(fz_context *ctx, struct sctx *s,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc,
- float xd, float yd, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xcd, ycd;
- float xabc, yabc;
- float xbcd, ybcd;
- float xabcd, yabcd;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xd - xc));
- dmax = fz_max(dmax, fz_abs(yd - yc));
- if (dmax < s->flatness || depth >= MAX_DEPTH)
- {
- fz_dash_lineto(ctx, s, xd, yd, 1);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xcd = xc + xd;
- ycd = yc + yd;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xbcd = xbc + xcd;
- ybcd = ybc + ycd;
- xabcd = xabc + xbcd;
- yabcd = yabc + ybcd;
- xab *= 0.5f; yab *= 0.5f;
- /* xbc *= 0.5f; ybc *= 0.5f; */
- xcd *= 0.5f; ycd *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- xbcd *= 0.25f; ybcd *= 0.25f;
- xabcd *= 0.125f; yabcd *= 0.125f;
- fz_dash_bezier(ctx, s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd, depth + 1);
- fz_dash_bezier(ctx, s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd, depth + 1);
- }
- static void
- fz_dash_quad(fz_context *ctx, struct sctx *s,
- float xa, float ya,
- float xb, float yb,
- float xc, float yc, int depth)
- {
- float dmax;
- float xab, yab;
- float xbc, ybc;
- float xabc, yabc;
- /* termination check */
- dmax = fz_abs(xa - xb);
- dmax = fz_max(dmax, fz_abs(ya - yb));
- dmax = fz_max(dmax, fz_abs(xc - xb));
- dmax = fz_max(dmax, fz_abs(yc - yb));
- if (dmax < s->flatness || depth >= MAX_DEPTH)
- {
- fz_dash_lineto(ctx, s, xc, yc, 1);
- return;
- }
- xab = xa + xb;
- yab = ya + yb;
- xbc = xb + xc;
- ybc = yb + yc;
- xabc = xab + xbc;
- yabc = yab + ybc;
- xab *= 0.5f; yab *= 0.5f;
- xbc *= 0.5f; ybc *= 0.5f;
- xabc *= 0.25f; yabc *= 0.25f;
- fz_dash_quad(ctx, s, xa, ya, xab, yab, xabc, yabc, depth + 1);
- fz_dash_quad(ctx, s, xabc, yabc, xbc, ybc, xc, yc, depth + 1);
- }
- static void
- dash_moveto(fz_context *ctx, void *s_, float x, float y)
- {
- sctx *s = (sctx *)s_;
- fz_dash_moveto(ctx, s, x, y);
- s->dash_beg.x = s->cur.x = x;
- s->dash_beg.y = s->cur.y = y;
- }
- static void
- dash_lineto(fz_context *ctx, void *s_, float x, float y)
- {
- sctx *s = (sctx *)s_;
- fz_dash_lineto(ctx, s, x, y, 0);
- s->cur.x = x;
- s->cur.y = y;
- }
- static void
- dash_curveto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2, float x3, float y3)
- {
- sctx *s = (sctx *)s_;
- fz_dash_bezier(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, x3, y3, 0);
- s->cur.x = x3;
- s->cur.y = y3;
- }
- static void
- dash_quadto(fz_context *ctx, void *s_, float x1, float y1, float x2, float y2)
- {
- sctx *s = (sctx *)s_;
- fz_dash_quad(ctx, s, s->cur.x, s->cur.y, x1, y1, x2, y2, 0);
- s->cur.x = x2;
- s->cur.y = y2;
- }
- static void
- dash_close(fz_context *ctx, void *s_)
- {
- sctx *s = (sctx *)s_;
- fz_dash_lineto(ctx, s, s->dash_beg.x, s->dash_beg.y, 0);
- s->cur.x = s->dash_beg.x;
- s->cur.y = s->dash_beg.y;
- }
- static const fz_path_walker dash_proc =
- {
- dash_moveto,
- dash_lineto,
- dash_curveto,
- dash_close,
- dash_quadto
- };
- static int
- do_flatten_stroke(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
- {
- struct sctx s;
- const fz_path_walker *proc = &stroke_proc;
- s.stroke = stroke;
- s.rast = rast;
- s.ctm = ctm;
- s.flatness = flatness;
- s.linejoin = stroke->linejoin;
- s.linewidth = linewidth * 0.5f; /* hairlines use a different value from the path value */
- s.miterlimit = stroke->miterlimit;
- s.sn = 0;
- s.not_just_moves = 0;
- s.toggle = 0;
- s.offset = 0;
- s.phase = 0;
- s.dirn_x = 0;
- s.dirn_y = 0;
- s.cap = stroke->start_cap;
- s.dash_list = NULL;
- s.dash_len = stroke->dash_len;
- if (s.dash_len > 0)
- {
- int i;
- fz_matrix inv;
- float max_expand;
- const float *list = stroke->dash_list;
- s.dash_total = 0;
- for (i = 0; i < s.dash_len; i++)
- s.dash_total += list[i];
- if (s.dash_total == 0)
- return 1;
- s.rect = fz_scissor_rasterizer(ctx, rast);
- if (fz_try_invert_matrix(&inv, ctm))
- return 1;
- s.rect = fz_transform_rect(s.rect, inv);
- s.rect.x0 -= linewidth;
- s.rect.x1 += linewidth;
- s.rect.y0 -= linewidth;
- s.rect.y1 += linewidth;
- max_expand = fz_matrix_max_expansion(ctm);
- if (s.dash_total >= 0.01f && s.dash_total * max_expand >= 0.5f)
- {
- proc = &dash_proc;
- s.dash_phase = fmodf(stroke->dash_phase, s.dash_total);
- s.dash_list = list;
- }
- }
- s.cur.x = s.cur.y = 0;
- fz_walk_path(ctx, path, proc, &s);
- fz_stroke_flush(ctx, &s, s.cap, stroke->end_cap);
- return fz_is_empty_irect(fz_bound_rasterizer(ctx, rast));
- }
- int
- fz_flatten_stroke_path(fz_context *ctx, fz_rasterizer *rast, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth, fz_irect scissor, fz_irect *bbox)
- {
- int empty;
- fz_irect local_bbox;
- if (!bbox)
- bbox = &local_bbox;
- if (fz_reset_rasterizer(ctx, rast, scissor))
- {
- empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
- if (empty)
- return *bbox = fz_empty_irect, 1;
- fz_postindex_rasterizer(ctx, rast);
- }
- empty = do_flatten_stroke(ctx, rast, path, stroke, ctm, flatness, linewidth);
- if (empty)
- return *bbox = fz_empty_irect, 1;
- *bbox = fz_intersect_irect(scissor, fz_bound_rasterizer(ctx, rast));
- return fz_is_empty_irect(*bbox);
- }
|