| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898 |
- // 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 <string.h>
- #include <assert.h>
- // Thoughts for further optimisations:
- // All paths start with MoveTo. We could probably avoid most cases where
- // we store that. The next thing after a close must be a move.
- // Commands are MOVE, LINE, HORIZ, VERT, DEGEN, CURVE, CURVEV, CURVEY, QUAD, RECT.
- // We'd need to drop 2 to get us down to 3 bits.
- // Commands can be followed by CLOSE. Use 1 bit for close.
- // PDF 'RECT' implies close according to the spec, but I suspect
- // we can ignore this as filling closes implicitly.
- // We use a single bit in the path header to tell us whether we have
- // a trailing move. Trailing moves can always be stripped when path
- // construction completes.
- typedef enum
- {
- FZ_MOVETO = 'M',
- FZ_LINETO = 'L',
- FZ_DEGENLINETO = 'D',
- FZ_CURVETO = 'C',
- FZ_CURVETOV = 'V',
- FZ_CURVETOY = 'Y',
- FZ_HORIZTO = 'H',
- FZ_VERTTO = 'I',
- FZ_QUADTO = 'Q',
- FZ_RECTTO = 'R',
- FZ_MOVETOCLOSE = 'm',
- FZ_LINETOCLOSE = 'l',
- FZ_DEGENLINETOCLOSE = 'd',
- FZ_CURVETOCLOSE = 'c',
- FZ_CURVETOVCLOSE = 'v',
- FZ_CURVETOYCLOSE = 'y',
- FZ_HORIZTOCLOSE = 'h',
- FZ_VERTTOCLOSE = 'i',
- FZ_QUADTOCLOSE = 'q',
- } fz_path_item_kind;
- struct fz_path
- {
- int8_t refs;
- uint8_t packed;
- int cmd_len, cmd_cap;
- unsigned char *cmds;
- int coord_len, coord_cap;
- float *coords;
- fz_point current;
- fz_point begin;
- };
- typedef struct
- {
- int8_t refs;
- uint8_t packed;
- uint8_t coord_len;
- uint8_t cmd_len;
- } fz_packed_path;
- /*
- Paths are created UNPACKED. That means we have a fz_path
- structure with coords and cmds pointing to malloced blocks.
- After they have been completely constructed, callers may choose
- to 'pack' them into some target block of memory. If if coord_len
- and cmd_len are both < 256, then they are PACKED_FLAT into an
- fz_packed_path with the coords and cmds in the bytes afterwards,
- all inside the target block. If they cannot be accommodated in
- that way, then they are PACKED_OPEN, where an fz_path is put
- into the target block, and cmds and coords remain pointers to
- allocated blocks.
- */
- enum
- {
- FZ_PATH_UNPACKED = 0,
- FZ_PATH_PACKED_FLAT = 1,
- FZ_PATH_PACKED_OPEN = 2
- };
- #define LAST_CMD(path) ((path)->cmd_len > 0 ? (path)->cmds[(path)->cmd_len-1] : 0)
- fz_path *
- fz_new_path(fz_context *ctx)
- {
- fz_path *path;
- path = fz_malloc_struct(ctx, fz_path);
- path->refs = 1;
- path->packed = FZ_PATH_UNPACKED;
- path->current.x = 0;
- path->current.y = 0;
- path->begin.x = 0;
- path->begin.y = 0;
- return path;
- }
- /*
- Take an additional reference to
- a path.
- No modifications should be carried out on a path
- to which more than one reference is held, as
- this can cause race conditions.
- */
- fz_path *
- fz_keep_path(fz_context *ctx, const fz_path *pathc)
- {
- fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
- int trimmable = 0;
- if (path == NULL)
- return NULL;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- /* Technically, we should only access ->refs with the lock held,
- * so do that here. We can't actually do the trimming here, because
- * to do so would do memory accesses with the ALLOC lock held. */
- if (path->refs == 1 && path->packed == FZ_PATH_UNPACKED)
- trimmable = 1;
- fz_keep_imp8_locked(ctx, path, &path->refs);
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- /* This is thread safe, because we know that the only person
- * holding a reference to this thread is us. */
- if (trimmable)
- fz_trim_path(ctx, path);
- return path;
- }
- void
- fz_drop_path(fz_context *ctx, const fz_path *pathc)
- {
- fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
- if (fz_drop_imp8(ctx, path, &path->refs))
- {
- if (path->packed != FZ_PATH_PACKED_FLAT)
- {
- fz_free(ctx, path->cmds);
- fz_free(ctx, path->coords);
- }
- if (path->packed == FZ_PATH_UNPACKED)
- fz_free(ctx, path);
- }
- }
- int fz_packed_path_size(const fz_path *path)
- {
- switch (path->packed)
- {
- case FZ_PATH_UNPACKED:
- if (path->cmd_len > 255 || path->coord_len > 255)
- return sizeof(fz_path);
- return sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
- case FZ_PATH_PACKED_OPEN:
- return sizeof(fz_path);
- case FZ_PATH_PACKED_FLAT:
- {
- fz_packed_path *pack = (fz_packed_path *)path;
- return sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
- }
- default:
- assert("This never happens" == NULL);
- return 0;
- }
- }
- size_t
- fz_pack_path(fz_context *ctx, uint8_t *pack_, const fz_path *path)
- {
- uint8_t *ptr;
- size_t size;
- if (path->packed == FZ_PATH_PACKED_FLAT)
- {
- fz_packed_path *pack = (fz_packed_path *)path;
- fz_packed_path *out = (fz_packed_path *)pack_;
- size = sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
- if (out)
- {
- out->refs = 1;
- out->packed = FZ_PATH_PACKED_FLAT;
- out->coord_len = pack->coord_len;
- out->cmd_len = pack->cmd_len;
- memcpy(&out[1], &pack[1], size - sizeof(*out));
- }
- return size;
- }
- size = sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
- /* If the path can't be packed flat, then pack it open */
- if (path->cmd_len > 255 || path->coord_len > 255)
- {
- fz_path *pack = (fz_path *)pack_;
- if (pack != NULL)
- {
- pack->refs = 1;
- pack->packed = FZ_PATH_PACKED_OPEN;
- pack->current.x = 0;
- pack->current.y = 0;
- pack->begin.x = 0;
- pack->begin.y = 0;
- pack->coord_cap = path->coord_len;
- pack->coord_len = path->coord_len;
- pack->cmd_cap = path->cmd_len;
- pack->cmd_len = path->cmd_len;
- pack->coords = Memento_label(fz_malloc_array(ctx, path->coord_len, float), "path_packed_coords");
- fz_try(ctx)
- {
- pack->cmds = Memento_label(fz_malloc_array(ctx, path->cmd_len, uint8_t), "path_packed_cmds");
- }
- fz_catch(ctx)
- {
- fz_free(ctx, pack->coords);
- fz_rethrow(ctx);
- }
- memcpy(pack->coords, path->coords, sizeof(float) * path->coord_len);
- memcpy(pack->cmds, path->cmds, sizeof(uint8_t) * path->cmd_len);
- }
- return sizeof(fz_path);
- }
- else
- {
- fz_packed_path *pack = (fz_packed_path *)pack_;
- if (pack != NULL)
- {
- pack->refs = 1;
- pack->packed = FZ_PATH_PACKED_FLAT;
- pack->cmd_len = path->cmd_len;
- pack->coord_len = path->coord_len;
- ptr = (uint8_t *)&pack[1];
- memcpy(ptr, path->coords, sizeof(float) * path->coord_len);
- ptr += sizeof(float) * path->coord_len;
- memcpy(ptr, path->cmds, sizeof(uint8_t) * path->cmd_len);
- }
- return size;
- }
- }
- static void
- push_cmd(fz_context *ctx, fz_path *path, int cmd)
- {
- if (path->refs != 1)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot modify shared paths");
- if (path->cmd_len + 1 >= path->cmd_cap)
- {
- int new_cmd_cap = fz_maxi(16, path->cmd_cap * 2);
- path->cmds = fz_realloc_array(ctx, path->cmds, new_cmd_cap, unsigned char);
- path->cmd_cap = new_cmd_cap;
- }
- path->cmds[path->cmd_len++] = cmd;
- }
- static void
- push_coord(fz_context *ctx, fz_path *path, float x, float y)
- {
- if (path->coord_len + 2 >= path->coord_cap)
- {
- int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
- path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
- path->coord_cap = new_coord_cap;
- }
- path->coords[path->coord_len++] = x;
- path->coords[path->coord_len++] = y;
- path->current.x = x;
- path->current.y = y;
- }
- static void
- push_ord(fz_context *ctx, fz_path *path, float xy, int isx)
- {
- if (path->coord_len + 1 >= path->coord_cap)
- {
- int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
- path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
- path->coord_cap = new_coord_cap;
- }
- path->coords[path->coord_len++] = xy;
- if (isx)
- path->current.x = xy;
- else
- path->current.y = xy;
- }
- fz_point
- fz_currentpoint(fz_context *ctx, fz_path *path)
- {
- return path->current;
- }
- void
- fz_moveto(fz_context *ctx, fz_path *path, float x, float y)
- {
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
- {
- /* Collapse moveto followed by moveto. */
- path->coords[path->coord_len-2] = x;
- path->coords[path->coord_len-1] = y;
- path->current.x = x;
- path->current.y = y;
- path->begin = path->current;
- return;
- }
- push_cmd(ctx, path, FZ_MOVETO);
- push_coord(ctx, path, x, y);
- path->begin = path->current;
- }
- void
- fz_lineto(fz_context *ctx, fz_path *path, float x, float y)
- {
- float x0, y0;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- x0 = path->current.x;
- y0 = path->current.y;
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "lineto with no current point");
- return;
- }
- /* (Anything other than MoveTo) followed by (LineTo the same place) is a nop */
- if (LAST_CMD(path) != FZ_MOVETO && x0 == x && y0 == y)
- return;
- if (x0 == x)
- {
- if (y0 == y)
- {
- if (LAST_CMD(path) != FZ_MOVETO)
- return;
- push_cmd(ctx, path, FZ_DEGENLINETO);
- }
- else
- {
- push_cmd(ctx, path, FZ_VERTTO);
- push_ord(ctx, path, y, 0);
- }
- }
- else if (y0 == y)
- {
- push_cmd(ctx, path, FZ_HORIZTO);
- push_ord(ctx, path, x, 1);
- }
- else
- {
- push_cmd(ctx, path, FZ_LINETO);
- push_coord(ctx, path, x, y);
- }
- }
- void
- fz_curveto(fz_context *ctx, fz_path *path,
- float x1, float y1,
- float x2, float y2,
- float x3, float y3)
- {
- float x0, y0;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- x0 = path->current.x;
- y0 = path->current.y;
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "curveto with no current point");
- return;
- }
- /* Check for degenerate cases: */
- if (x0 == x1 && y0 == y1)
- {
- if (x2 == x3 && y2 == y3)
- {
- /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */
- if (x1 == x2 && y1 == y2 && LAST_CMD(path) != FZ_MOVETO)
- return;
- /* Otherwise a line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- else if (x1 == x2 && y1 == y2)
- {
- /* A line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- else
- fz_curvetov(ctx, path, x2, y2, x3, y3);
- return;
- }
- else if (x2 == x3 && y2 == y3)
- {
- if (x1 == x2 && y1 == y2)
- {
- /* A line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- else
- fz_curvetoy(ctx, path, x1, y1, x3, y3);
- return;
- }
- push_cmd(ctx, path, FZ_CURVETO);
- push_coord(ctx, path, x1, y1);
- push_coord(ctx, path, x2, y2);
- push_coord(ctx, path, x3, y3);
- }
- void
- fz_quadto(fz_context *ctx, fz_path *path,
- float x1, float y1,
- float x2, float y2)
- {
- float x0, y0;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- x0 = path->current.x;
- y0 = path->current.y;
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "quadto with no current point");
- return;
- }
- /* Check for degenerate cases: */
- if ((x0 == x1 && y0 == y1) || (x1 == x2 && y1 == y2))
- {
- if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
- return;
- /* A line will suffice */
- fz_lineto(ctx, path, x2, y2);
- return;
- }
- push_cmd(ctx, path, FZ_QUADTO);
- push_coord(ctx, path, x1, y1);
- push_coord(ctx, path, x2, y2);
- }
- void
- fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3)
- {
- float x0, y0;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- x0 = path->current.x;
- y0 = path->current.y;
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "curveto with no current point");
- return;
- }
- /* Check for degenerate cases: */
- if (x2 == x3 && y2 == y3)
- {
- /* If (x0,y0)==(x2,y2) and prev wasn't a moveto, then skip */
- if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
- return;
- /* Otherwise a line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- else if (x0 == x2 && y0 == y2)
- {
- /* A line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- push_cmd(ctx, path, FZ_CURVETOV);
- push_coord(ctx, path, x2, y2);
- push_coord(ctx, path, x3, y3);
- }
- void
- fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3)
- {
- float x0, y0;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- x0 = path->current.x;
- y0 = path->current.y;
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "curveto with no current point");
- return;
- }
- /* Check for degenerate cases: */
- if (x1 == x3 && y1 == y3)
- {
- /* If (x0,y0)==(x1,y1) and prev wasn't a moveto, then skip */
- if (x0 == x1 && y0 == y1 && LAST_CMD(path) != FZ_MOVETO)
- return;
- /* Otherwise a line will suffice */
- fz_lineto(ctx, path, x3, y3);
- }
- push_cmd(ctx, path, FZ_CURVETOY);
- push_coord(ctx, path, x1, y1);
- push_coord(ctx, path, x3, y3);
- }
- void
- fz_closepath(fz_context *ctx, fz_path *path)
- {
- uint8_t rep;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- if (path->cmd_len == 0)
- {
- fz_warn(ctx, "closepath with no current point");
- return;
- }
- switch(LAST_CMD(path))
- {
- case FZ_MOVETO:
- rep = FZ_MOVETOCLOSE;
- break;
- case FZ_LINETO:
- rep = FZ_LINETOCLOSE;
- break;
- case FZ_DEGENLINETO:
- rep = FZ_DEGENLINETOCLOSE;
- break;
- case FZ_CURVETO:
- rep = FZ_CURVETOCLOSE;
- break;
- case FZ_CURVETOV:
- rep = FZ_CURVETOVCLOSE;
- break;
- case FZ_CURVETOY:
- rep = FZ_CURVETOYCLOSE;
- break;
- case FZ_HORIZTO:
- rep = FZ_HORIZTOCLOSE;
- break;
- case FZ_VERTTO:
- rep = FZ_VERTTOCLOSE;
- break;
- case FZ_QUADTO:
- rep = FZ_QUADTOCLOSE;
- break;
- case FZ_RECTTO:
- /* RectTo implies close */
- return;
- case FZ_MOVETOCLOSE:
- case FZ_LINETOCLOSE:
- case FZ_DEGENLINETOCLOSE:
- case FZ_CURVETOCLOSE:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- case FZ_QUADTOCLOSE:
- /* CLOSE following a CLOSE is a NOP */
- return;
- default: /* default never happens */
- case 0:
- /* Closing an empty path is a NOP */
- return;
- }
- path->cmds[path->cmd_len-1] = rep;
- path->current = path->begin;
- }
- void
- fz_rectto(fz_context *ctx, fz_path *path, float x1, float y1, float x2, float y2)
- {
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot modify a packed path");
- if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
- {
- /* Collapse moveto followed by rectto. */
- path->coord_len -= 2;
- path->cmd_len--;
- }
- push_cmd(ctx, path, FZ_RECTTO);
- push_coord(ctx, path, x1, y1);
- push_coord(ctx, path, x2, y2);
- path->current = path->begin;
- }
- static inline void bound_expand(fz_rect *r, fz_point p)
- {
- if (p.x < r->x0) r->x0 = p.x;
- if (p.y < r->y0) r->y0 = p.y;
- if (p.x > r->x1) r->x1 = p.x;
- if (p.y > r->y1) r->y1 = p.y;
- }
- void fz_walk_path(fz_context *ctx, const fz_path *path, const fz_path_walker *proc, void *arg)
- {
- int i, k, cmd_len;
- float x=0, y=0, sx=0, sy=0;
- uint8_t *cmds;
- float *coords;
- switch (path->packed)
- {
- case FZ_PATH_UNPACKED:
- case FZ_PATH_PACKED_OPEN:
- cmd_len = path->cmd_len;
- coords = path->coords;
- cmds = path->cmds;
- break;
- case FZ_PATH_PACKED_FLAT:
- cmd_len = ((fz_packed_path *)path)->cmd_len;
- coords = (float *)&((fz_packed_path *)path)[1];
- cmds = (uint8_t *)&coords[((fz_packed_path *)path)->coord_len];
- break;
- default:
- assert("This never happens" == NULL);
- return;
- }
- if (cmd_len == 0)
- return;
- for (k=0, i = 0; i < cmd_len; i++)
- {
- uint8_t cmd = cmds[i];
- switch (cmd)
- {
- case FZ_CURVETO:
- case FZ_CURVETOCLOSE:
- proc->curveto(ctx, arg,
- coords[k],
- coords[k+1],
- coords[k+2],
- coords[k+3],
- x = coords[k+4],
- y = coords[k+5]);
- k += 6;
- if (cmd == FZ_CURVETOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_CURVETOV:
- case FZ_CURVETOVCLOSE:
- if (proc->curvetov)
- proc->curvetov(ctx, arg,
- coords[k],
- coords[k+1],
- x = coords[k+2],
- y = coords[k+3]);
- else
- {
- proc->curveto(ctx, arg,
- x,
- y,
- coords[k],
- coords[k+1],
- coords[k+2],
- coords[k+3]);
- x = coords[k+2];
- y = coords[k+3];
- }
- k += 4;
- if (cmd == FZ_CURVETOVCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_CURVETOY:
- case FZ_CURVETOYCLOSE:
- if (proc->curvetoy)
- proc->curvetoy(ctx, arg,
- coords[k],
- coords[k+1],
- x = coords[k+2],
- y = coords[k+3]);
- else
- proc->curveto(ctx, arg,
- coords[k],
- coords[k+1],
- coords[k+2],
- coords[k+3],
- x = coords[k+2],
- y = coords[k+3]);
- k += 4;
- if (cmd == FZ_CURVETOYCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_QUADTO:
- case FZ_QUADTOCLOSE:
- if (proc->quadto)
- proc->quadto(ctx, arg,
- coords[k],
- coords[k+1],
- x = coords[k+2],
- y = coords[k+3]);
- else
- {
- float c2x = coords[k] * 2;
- float c2y = coords[k+1] * 2;
- float c1x = (x + c2x) / 3;
- float c1y = (y + c2y) / 3;
- x = coords[k+2];
- y = coords[k+3];
- c2x = (c2x + x) / 3;
- c2y = (c2y + y) / 3;
- proc->curveto(ctx, arg,
- c1x,
- c1y,
- c2x,
- c2y,
- x,
- y);
- }
- k += 4;
- if (cmd == FZ_QUADTOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_MOVETO:
- case FZ_MOVETOCLOSE:
- proc->moveto(ctx, arg,
- x = coords[k],
- y = coords[k+1]);
- k += 2;
- sx = x;
- sy = y;
- if (cmd == FZ_MOVETOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_LINETO:
- case FZ_LINETOCLOSE:
- proc->lineto(ctx, arg,
- x = coords[k],
- y = coords[k+1]);
- k += 2;
- if (cmd == FZ_LINETOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_HORIZTO:
- case FZ_HORIZTOCLOSE:
- proc->lineto(ctx, arg,
- x = coords[k],
- y);
- k += 1;
- if (cmd == FZ_HORIZTOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_VERTTO:
- case FZ_VERTTOCLOSE:
- proc->lineto(ctx, arg,
- x,
- y = coords[k]);
- k += 1;
- if (cmd == FZ_VERTTOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_DEGENLINETO:
- case FZ_DEGENLINETOCLOSE:
- proc->lineto(ctx, arg,
- x,
- y);
- if (cmd == FZ_DEGENLINETOCLOSE)
- {
- if (proc->closepath)
- proc->closepath(ctx, arg);
- x = sx;
- y = sy;
- }
- break;
- case FZ_RECTTO:
- if (proc->rectto)
- {
- proc->rectto(ctx, arg,
- x = coords[k],
- y = coords[k+1],
- coords[k+2],
- coords[k+3]);
- }
- else
- {
- proc->moveto(ctx, arg,
- x = coords[k],
- y = coords[k+1]);
- proc->lineto(ctx, arg,
- coords[k+2],
- coords[k+1]);
- proc->lineto(ctx, arg,
- coords[k+2],
- coords[k+3]);
- proc->lineto(ctx, arg,
- coords[k],
- coords[k+3]);
- if (proc->closepath)
- proc->closepath(ctx, arg);
- }
- sx = x;
- sy = y;
- k += 4;
- break;
- }
- }
- }
- /*
- A couple of notes about the path bounding algorithm.
- Firstly, we don't expand the bounds immediately on a move, because
- a sequence of moves together will only actually use the last one,
- and trailing moves are ignored. This is achieved using 'trailing_move'.
- Secondly, we watch for paths that are entirely rectilinear (all segments
- move left/right/up/down only, with no curves). Such "only_right_angles"
- paths can be bounded with us ignoring any mitre limit. This is a really
- common case that can otherwise bloats simple boxes far more than is
- useful. This is particular annoying during table recognition!
- */
- typedef struct
- {
- fz_matrix ctm;
- fz_rect rect;
- fz_point move;
- int trailing_move;
- int first;
- int only_right_angles;
- fz_point prev;
- } bound_path_arg;
- static void
- bound_moveto(fz_context *ctx, void *arg_, float x, float y)
- {
- bound_path_arg *arg = (bound_path_arg *)arg_;
- arg->move = arg->prev = fz_transform_point_xy(x, y, arg->ctm);
- arg->trailing_move = 1;
- }
- static inline int
- eq0(float x)
- {
- return x >= -0.001 && x <= 0.001;
- }
- static void
- bound_lineto(fz_context *ctx, void *arg_, float x, float y)
- {
- bound_path_arg *arg = (bound_path_arg *)arg_;
- fz_point p = fz_transform_point_xy(x, y, arg->ctm);
- if (arg->first)
- {
- arg->rect.x0 = arg->rect.x1 = p.x;
- arg->rect.y0 = arg->rect.y1 = p.y;
- arg->first = 0;
- }
- else
- bound_expand(&arg->rect, p);
- if (arg->trailing_move)
- {
- arg->trailing_move = 0;
- bound_expand(&arg->rect, arg->move);
- }
- if (arg->only_right_angles && !eq0(arg->prev.x - p.x) && !eq0(arg->prev.y - p.y))
- arg->only_right_angles = 0;
- arg->prev = p;
- }
- static void
- bound_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
- {
- bound_path_arg *arg = (bound_path_arg *)arg_;
- fz_point p = fz_transform_point_xy(x1, y1, arg->ctm);
- if (arg->first)
- {
- arg->rect.x0 = arg->rect.x1 = p.x;
- arg->rect.y0 = arg->rect.y1 = p.y;
- arg->first = 0;
- }
- else
- bound_expand(&arg->rect, p);
- bound_expand(&arg->rect, fz_transform_point_xy(x2, y2, arg->ctm));
- bound_expand(&arg->rect, fz_transform_point_xy(x3, y3, arg->ctm));
- if (arg->trailing_move)
- {
- arg->trailing_move = 0;
- bound_expand(&arg->rect, arg->move);
- }
- arg->only_right_angles = 0;
- arg->prev = p;
- }
- static const fz_path_walker bound_path_walker =
- {
- bound_moveto,
- bound_lineto,
- bound_curveto,
- NULL
- };
- static fz_rect
- adjust_rect_for_stroke(fz_context *ctx, fz_rect r, const fz_stroke_state *stroke, fz_matrix ctm, int no_mitre)
- {
- float expand;
- if (!stroke)
- return r;
- expand = stroke->linewidth/2;
- if (expand == 0)
- expand = 0.5f;
- if (r.x1 == r.x0 || r.y1 == r.y0)
- {
- /* Mitring can't apply in this case. */
- }
- else if (!no_mitre && stroke->linejoin == FZ_LINEJOIN_MITER && stroke->miterlimit > 0.5f)
- {
- /* miter limit is expressed in terms of the linewidth, not half the line width. */
- expand *= stroke->miterlimit * 2;
- }
- else if (!no_mitre && stroke->linejoin == FZ_LINEJOIN_MITER_XPS && stroke->miterlimit > 1.0f)
- {
- /* for xps, miter limit is expressed in terms of half the linewidth. */
- expand *= stroke->miterlimit;
- }
- expand *= fz_matrix_max_expansion(ctm);
- r.x0 -= expand;
- r.y0 -= expand;
- r.x1 += expand;
- r.y1 += expand;
- return r;
- }
- fz_rect
- fz_bound_path(fz_context *ctx, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm)
- {
- bound_path_arg arg;
- arg.ctm = ctm;
- arg.rect = fz_empty_rect;
- arg.trailing_move = 0;
- arg.first = 1;
- arg.only_right_angles = 1;
- fz_walk_path(ctx, path, &bound_path_walker, &arg);
- if (!arg.first && stroke)
- {
- arg.rect = adjust_rect_for_stroke(ctx, arg.rect, stroke, ctm, arg.only_right_angles);
- }
- return arg.rect;
- }
- fz_rect
- fz_adjust_rect_for_stroke(fz_context *ctx, fz_rect r, const fz_stroke_state *stroke, fz_matrix ctm)
- {
- return adjust_rect_for_stroke(ctx, r, stroke, ctm, 0);
- }
- void
- fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix ctm)
- {
- int i, k, n;
- fz_point p, p1, p2, p3, q, s;
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot transform a packed path");
- if (ctm.b == 0 && ctm.c == 0)
- {
- /* Simple, in place transform */
- i = 0;
- k = 0;
- while (i < path->cmd_len)
- {
- uint8_t cmd = path->cmds[i];
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_LINETO:
- case FZ_MOVETOCLOSE:
- case FZ_LINETOCLOSE:
- n = 1;
- break;
- case FZ_DEGENLINETO:
- case FZ_DEGENLINETOCLOSE:
- n = 0;
- break;
- case FZ_CURVETO:
- case FZ_CURVETOCLOSE:
- n = 3;
- break;
- case FZ_RECTTO:
- s.x = path->coords[k];
- s.y = path->coords[k+1];
- n = 2;
- break;
- case FZ_CURVETOV:
- case FZ_CURVETOY:
- case FZ_QUADTO:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- n = 2;
- break;
- case FZ_HORIZTO:
- case FZ_HORIZTOCLOSE:
- q.x = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.x;
- n = 0;
- break;
- case FZ_VERTTO:
- case FZ_VERTTOCLOSE:
- q.y = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.y;
- n = 0;
- break;
- default:
- assert("Unknown path cmd" == NULL);
- }
- while (n > 0)
- {
- q.x = path->coords[k];
- q.y = path->coords[k+1];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.x;
- path->coords[k++] = p.y;
- n--;
- }
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_MOVETOCLOSE:
- s = q;
- break;
- case FZ_LINETOCLOSE:
- case FZ_DEGENLINETOCLOSE:
- case FZ_CURVETOCLOSE:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- case FZ_RECTTO:
- q = s;
- break;
- }
- i++;
- }
- }
- else if (ctm.a == 0 && ctm.d == 0)
- {
- /* In place transform with command rewriting */
- i = 0;
- k = 0;
- while (i < path->cmd_len)
- {
- uint8_t cmd = path->cmds[i];
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_LINETO:
- case FZ_MOVETOCLOSE:
- case FZ_LINETOCLOSE:
- n = 1;
- break;
- case FZ_DEGENLINETO:
- case FZ_DEGENLINETOCLOSE:
- n = 0;
- break;
- case FZ_CURVETO:
- case FZ_CURVETOCLOSE:
- n = 3;
- break;
- case FZ_RECTTO:
- s.x = path->coords[k];
- s.y = path->coords[k+1];
- n = 2;
- break;
- case FZ_CURVETOV:
- case FZ_CURVETOY:
- case FZ_QUADTO:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- n = 2;
- break;
- case FZ_HORIZTO:
- q.x = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.y;
- path->cmds[i] = FZ_VERTTO;
- n = 0;
- break;
- case FZ_HORIZTOCLOSE:
- q.x = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.y;
- path->cmds[i] = FZ_VERTTOCLOSE;
- n = 0;
- break;
- case FZ_VERTTO:
- q.y = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.x;
- path->cmds[i] = FZ_HORIZTO;
- n = 0;
- break;
- case FZ_VERTTOCLOSE:
- q.y = path->coords[k];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.x;
- path->cmds[i] = FZ_HORIZTOCLOSE;
- n = 0;
- break;
- default:
- assert("Unknown path cmd" == NULL);
- }
- while (n > 0)
- {
- q.x = path->coords[k];
- q.y = path->coords[k+1];
- p = fz_transform_point(q, ctm);
- path->coords[k++] = p.x;
- path->coords[k++] = p.y;
- n--;
- }
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_MOVETOCLOSE:
- s = q;
- break;
- case FZ_LINETOCLOSE:
- case FZ_DEGENLINETOCLOSE:
- case FZ_CURVETOCLOSE:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- case FZ_RECTTO:
- q = s;
- break;
- }
- i++;
- }
- }
- else
- {
- int extra_coord = 0;
- int extra_cmd = 0;
- int coord_read, coord_write, cmd_read, cmd_write;
- /* General case. Have to allow for rects/horiz/verts
- * becoming non-rects/horiz/verts. */
- for (i = 0; i < path->cmd_len; i++)
- {
- uint8_t cmd = path->cmds[i];
- switch (cmd)
- {
- case FZ_HORIZTO:
- case FZ_VERTTO:
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- extra_coord += 1;
- break;
- case FZ_RECTTO:
- extra_coord += 2;
- extra_cmd += 3;
- break;
- default:
- /* Do nothing */
- break;
- }
- }
- if (path->cmd_len + extra_cmd < path->cmd_cap)
- {
- path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len + extra_cmd, unsigned char);
- path->cmd_cap = path->cmd_len + extra_cmd;
- }
- if (path->coord_len + extra_coord < path->coord_cap)
- {
- path->coords = fz_realloc_array(ctx, path->coords, path->coord_len + extra_coord, float);
- path->coord_cap = path->coord_len + extra_coord;
- }
- memmove(path->cmds + extra_cmd, path->cmds, path->cmd_len * sizeof(unsigned char));
- path->cmd_len += extra_cmd;
- memmove(path->coords + extra_coord, path->coords, path->coord_len * sizeof(float));
- path->coord_len += extra_coord;
- for (cmd_write = 0, cmd_read = extra_cmd, coord_write = 0, coord_read = extra_coord; cmd_read < path->cmd_len; i += 2)
- {
- uint8_t cmd = path->cmds[cmd_write++] = path->cmds[cmd_read++];
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_LINETO:
- case FZ_MOVETOCLOSE:
- case FZ_LINETOCLOSE:
- n = 1;
- break;
- case FZ_DEGENLINETO:
- case FZ_DEGENLINETOCLOSE:
- n = 0;
- break;
- case FZ_CURVETO:
- case FZ_CURVETOCLOSE:
- n = 3;
- break;
- case FZ_CURVETOV:
- case FZ_CURVETOY:
- case FZ_QUADTO:
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- n = 2;
- break;
- case FZ_RECTTO:
- p.x = path->coords[coord_read++];
- p.y = path->coords[coord_read++];
- p2.x = path->coords[coord_read++];
- p2.y = path->coords[coord_read++];
- p1.x = p2.x;
- p1.y = p.y;
- p3.x = p.x;
- p3.y = p2.y;
- s = p;
- p = fz_transform_point(p, ctm);
- p1 = fz_transform_point(p1, ctm);
- p2 = fz_transform_point(p2, ctm);
- p3 = fz_transform_point(p3, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- path->coords[coord_write++] = p1.x;
- path->coords[coord_write++] = p1.y;
- path->coords[coord_write++] = p2.x;
- path->coords[coord_write++] = p2.y;
- path->coords[coord_write++] = p3.x;
- path->coords[coord_write++] = p3.y;
- path->cmds[cmd_write-1] = FZ_MOVETO;
- path->cmds[cmd_write++] = FZ_LINETO;
- path->cmds[cmd_write++] = FZ_LINETO;
- path->cmds[cmd_write++] = FZ_LINETOCLOSE;
- n = 0;
- break;
- case FZ_HORIZTO:
- q.x = path->coords[coord_read++];
- p = fz_transform_point(q, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- path->cmds[cmd_write-1] = FZ_LINETO;
- n = 0;
- break;
- case FZ_HORIZTOCLOSE:
- p.x = path->coords[coord_read++];
- p.y = q.y;
- p = fz_transform_point(p, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
- q = s;
- n = 0;
- break;
- case FZ_VERTTO:
- q.y = path->coords[coord_read++];
- p = fz_transform_point(q, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- path->cmds[cmd_write-1] = FZ_LINETO;
- n = 0;
- break;
- case FZ_VERTTOCLOSE:
- p.x = q.x;
- p.y = path->coords[coord_read++];
- p = fz_transform_point(p, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
- q = s;
- n = 0;
- break;
- default:
- assert("Unknown path cmd" == NULL);
- }
- while (n > 0)
- {
- q.x = path->coords[coord_read++];
- q.y = path->coords[coord_read++];
- p = fz_transform_point(q, ctm);
- path->coords[coord_write++] = p.x;
- path->coords[coord_write++] = p.y;
- n--;
- }
- switch (cmd)
- {
- case FZ_MOVETO:
- case FZ_MOVETOCLOSE:
- s = q;
- break;
- case FZ_LINETOCLOSE:
- case FZ_DEGENLINETOCLOSE:
- case FZ_CURVETOCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_CURVETOVCLOSE:
- case FZ_QUADTOCLOSE:
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- case FZ_RECTTO:
- q = s;
- break;
- }
- }
- }
- }
- void fz_trim_path(fz_context *ctx, fz_path *path)
- {
- if (path->packed)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't trim a packed path");
- if (path->cmd_cap > path->cmd_len)
- {
- path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len, unsigned char);
- path->cmd_cap = path->cmd_len;
- }
- if (path->coord_cap > path->coord_len)
- {
- path->coords = fz_realloc_array(ctx, path->coords, path->coord_len, float);
- path->coord_cap = path->coord_len;
- }
- }
- const fz_stroke_state fz_default_stroke_state = {
- -2, /* -2 is the magic number we use when we have stroke states stored on the stack */
- FZ_LINECAP_BUTT, FZ_LINECAP_BUTT, FZ_LINECAP_BUTT,
- FZ_LINEJOIN_MITER,
- 1, 10,
- 0, 0, { 0 }
- };
- fz_stroke_state *
- fz_keep_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
- {
- fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
- if (!stroke)
- return NULL;
- /* -2 is the magic number we use when we have stroke states stored on the stack */
- if (stroke->refs == -2)
- return fz_clone_stroke_state(ctx, stroke);
- return fz_keep_imp(ctx, stroke, &stroke->refs);
- }
- int
- fz_stroke_state_eq(fz_context *ctx, const fz_stroke_state *a, const fz_stroke_state *b)
- {
- int i;
- if (a == NULL && b == NULL) return 1;
- if (a == NULL && b != NULL) return 0;
- if (a != NULL && b == NULL) return 0;
- if (a->start_cap != b->start_cap) return 0;
- if (a->dash_cap != b->dash_cap) return 0;
- if (a->end_cap != b->end_cap) return 0;
- if (a->linejoin != b->linejoin) return 0;
- if (a->linewidth != b->linewidth) return 0;
- if (a->miterlimit != b->miterlimit) return 0;
- if (a->dash_phase != b->dash_phase) return 0;
- if (a->dash_len != b->dash_len) return 0;
- for (i = 0; i < a->dash_len; i++)
- if (a->dash_list[i] != b->dash_list[i]) return 0;
- return 1;
- }
- void
- fz_drop_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
- {
- fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
- if (fz_drop_imp(ctx, stroke, &stroke->refs))
- fz_free(ctx, stroke);
- }
- fz_stroke_state *
- fz_new_stroke_state_with_dash_len(fz_context *ctx, int len)
- {
- fz_stroke_state *state;
- if (len < 0)
- len = 0;
- state = fz_malloc_flexible(ctx, fz_stroke_state, dash_list, len);
- state->refs = 1;
- state->start_cap = FZ_LINECAP_BUTT;
- state->dash_cap = FZ_LINECAP_BUTT;
- state->end_cap = FZ_LINECAP_BUTT;
- state->linejoin = FZ_LINEJOIN_MITER;
- state->linewidth = 1;
- state->miterlimit = 10;
- state->dash_phase = 0;
- state->dash_len = 0;
- return state;
- }
- fz_stroke_state *
- fz_new_stroke_state(fz_context *ctx)
- {
- return fz_new_stroke_state_with_dash_len(ctx, 0);
- }
- fz_linecap
- fz_linecap_from_string(const char *str)
- {
- if (!strcmp(str, "Round"))
- return FZ_LINECAP_ROUND;
- if (!strcmp(str, "Square"))
- return FZ_LINECAP_SQUARE;
- if (!strcmp(str, "Triangle"))
- return FZ_LINECAP_TRIANGLE;
- return FZ_LINECAP_BUTT;
- }
- const char *
- fz_string_from_linecap(fz_linecap cap)
- {
- switch (cap) {
- default:
- case FZ_LINECAP_BUTT: return "Butt";
- case FZ_LINECAP_ROUND: return "Round";
- case FZ_LINECAP_SQUARE: return "Square";
- case FZ_LINECAP_TRIANGLE: return "Triangle";
- }
- }
- fz_linejoin
- fz_linejoin_from_string(const char *str)
- {
- if (!strcmp(str, "Round"))
- return FZ_LINEJOIN_ROUND;
- if (!strcmp(str, "Bevel"))
- return FZ_LINEJOIN_BEVEL;
- if (!strcmp(str, "MiterXPS"))
- return FZ_LINEJOIN_MITER_XPS;
- return FZ_LINEJOIN_MITER;
- }
- const char *
- fz_string_from_linejoin(fz_linejoin join)
- {
- switch (join) {
- default:
- case FZ_LINEJOIN_MITER: return "Miter";
- case FZ_LINEJOIN_ROUND: return "Round";
- case FZ_LINEJOIN_BEVEL: return "Bevel";
- case FZ_LINEJOIN_MITER_XPS: return "MiterXPS";
- }
- }
- fz_stroke_state *
- fz_clone_stroke_state(fz_context *ctx, const fz_stroke_state *stroke)
- {
- fz_stroke_state *clone = fz_new_stroke_state_with_dash_len(ctx, stroke->dash_len);
- size_t size = offsetof(fz_stroke_state, dash_list) + sizeof(float) * stroke->dash_len;
- memcpy(clone, stroke, size);
- clone->refs = 1;
- return clone;
- }
- fz_stroke_state *
- fz_unshare_stroke_state_with_dash_len(fz_context *ctx, fz_stroke_state *shared, int len)
- {
- int single;
- fz_stroke_state *unshared;
- fz_lock(ctx, FZ_LOCK_ALLOC);
- single = (shared->refs == 1);
- fz_unlock(ctx, FZ_LOCK_ALLOC);
- if (single && len == shared->dash_len)
- return shared;
- unshared = fz_new_stroke_state_with_dash_len(ctx, len);
- if (shared->dash_len >= len)
- memcpy(unshared, shared, offsetof(fz_stroke_state, dash_list) + sizeof(float) * len);
- else
- memcpy(unshared, shared, offsetof(fz_stroke_state, dash_list));
- unshared->refs = 1;
- unshared->dash_len = len;
- if (fz_drop_imp(ctx, shared, &shared->refs))
- fz_free(ctx, shared);
- return unshared;
- }
- fz_stroke_state *
- fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared)
- {
- return fz_unshare_stroke_state_with_dash_len(ctx, shared, shared->dash_len);
- }
- static void *
- clone_block(fz_context *ctx, void *block, size_t len)
- {
- void *target;
- if (len == 0 || block == NULL)
- return NULL;
- target = fz_malloc(ctx, len);
- memcpy(target, block, len);
- return target;
- }
- fz_path *
- fz_clone_path(fz_context *ctx, fz_path *path)
- {
- fz_path *new_path;
- assert(ctx != NULL);
- if (path == NULL)
- return NULL;
- new_path = fz_malloc_struct(ctx, fz_path);
- new_path->refs = 1;
- new_path->packed = FZ_PATH_UNPACKED;
- fz_try(ctx)
- {
- switch(path->packed)
- {
- case FZ_PATH_UNPACKED:
- case FZ_PATH_PACKED_OPEN:
- new_path->cmd_len = path->cmd_len;
- new_path->cmd_cap = path->cmd_cap;
- new_path->cmds = Memento_label(clone_block(ctx, path->cmds, path->cmd_cap), "path_cmds");
- new_path->coord_len = path->coord_len;
- new_path->coord_cap = path->coord_cap;
- new_path->coords = Memento_label(clone_block(ctx, path->coords, sizeof(float)*path->coord_cap), "path_coords");
- new_path->current = path->current;
- new_path->begin = path->begin;
- break;
- case FZ_PATH_PACKED_FLAT:
- {
- uint8_t *data;
- float *xy;
- int i;
- fz_packed_path *ppath = (fz_packed_path *)path;
- new_path->cmd_len = ppath->cmd_len;
- new_path->cmd_cap = ppath->cmd_len;
- new_path->coord_len = ppath->coord_len;
- new_path->coord_cap = ppath->coord_len;
- data = (uint8_t *)&ppath[1];
- new_path->coords = Memento_label(clone_block(ctx, data, sizeof(float)*path->coord_cap), "path_coords");
- data += sizeof(float) * path->coord_cap;
- new_path->cmds = Memento_label(clone_block(ctx, data, path->cmd_cap), "path_cmds");
- xy = new_path->coords;
- for (i = 0; i < new_path->cmd_len; i++)
- {
- switch (new_path->cmds[i])
- {
- case FZ_MOVETOCLOSE:
- case FZ_MOVETO:
- new_path->current.x = *xy++;
- new_path->current.y = *xy++;
- new_path->begin.x = new_path->current.x;
- new_path->begin.y = new_path->current.y;
- break;
- case FZ_CURVETO:
- xy += 2;
- /* fallthrough */
- case FZ_CURVETOV:
- case FZ_CURVETOY:
- case FZ_QUADTO:
- /* fallthrough */
- xy += 2;
- case FZ_LINETO:
- new_path->current.x = *xy++;
- new_path->current.y = *xy++;
- break;
- case FZ_DEGENLINETO:
- break;
- case FZ_HORIZTO:
- new_path->current.x = *xy++;
- break;
- case FZ_VERTTO:
- new_path->current.y = *xy++;
- break;
- case FZ_RECTTO:
- xy += 2;
- break;
- case FZ_CURVETOCLOSE:
- xy += 2;
- /* fallthrough */
- case FZ_CURVETOVCLOSE:
- case FZ_CURVETOYCLOSE:
- case FZ_QUADTOCLOSE:
- case FZ_LINETOCLOSE:
- xy++;
- /* fallthrough */
- case FZ_HORIZTOCLOSE:
- case FZ_VERTTOCLOSE:
- xy++;
- /* fallthrough */
- case FZ_DEGENLINETOCLOSE:
- new_path->current.x = new_path->begin.x;
- new_path->current.y = new_path->begin.y;
- break;
- }
- }
- }
- default:
- assert(!"Unknown packing method found in path");
- }
- }
- fz_catch(ctx)
- {
- fz_free(ctx, new_path->coords);
- fz_free(ctx, new_path->cmds);
- fz_free(ctx, new_path);
- fz_rethrow(ctx);
- }
- return new_path;
- }
- typedef struct
- {
- fz_matrix ctm;
- fz_point p[4];
- int count;
- int trailing_move;
- } rect_path_arg;
- static void
- rect_moveto(fz_context *ctx, void *arg_, float x, float y)
- {
- rect_path_arg *arg = (rect_path_arg *)arg_;
- fz_point p = fz_transform_point_xy(x, y, arg->ctm);
- /* If we've already decided that it's not a rectangle. Just exit. */
- if (arg->count < 0)
- return;
- /* We should never get multiple successive moves, by construction. */
- /* If we're starting out... */
- if (arg->count == 0)
- {
- arg->p[0] = p;
- arg->count = 1;
- return;
- }
- /* Otherwise, any move is fine, as long as it's not followed by another line... */
- arg->trailing_move = 1;
- }
- static void
- rect_lineto(fz_context *ctx, void *arg_, float x, float y)
- {
- rect_path_arg *arg = (rect_path_arg *)arg_;
- fz_point p = fz_transform_point_xy(x, y, arg->ctm);
- /* If we've already decided that it's not a rectangle. Just exit. */
- if (arg->count < 0)
- return;
- if (arg->trailing_move)
- {
- arg->count = -1;
- return;
- }
- /* Watch for pesky lines back to the same place. */
- if (arg->p[arg->count-1].x == p.x && arg->p[arg->count-1].y == p.y)
- return;
- if (arg->count < 4)
- {
- arg->p[arg->count++] = p;
- return;
- }
- /* Allow for lines back to the start. */
- if (arg->count == 4)
- {
- if (arg->p[0].x == p.x && arg->p[0].y == p.y)
- {
- arg->count++;
- return;
- }
- }
- arg->count = -1;
- }
- static void
- rect_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
- {
- rect_path_arg *arg = (rect_path_arg *)arg_;
- arg->count = -1;
- }
- static const fz_path_walker rect_path_walker =
- {
- rect_moveto,
- rect_lineto,
- rect_curveto,
- NULL
- };
- int
- fz_path_is_rect(fz_context *ctx, const fz_path *path, fz_matrix ctm)
- {
- return fz_path_is_rect_with_bounds(ctx, path, ctm, NULL);
- }
- int
- fz_path_is_rect_with_bounds(fz_context *ctx, const fz_path *path, fz_matrix ctm, fz_rect *bounds)
- {
- rect_path_arg arg;
- arg.ctm = ctm;
- arg.trailing_move = 0;
- arg.count = 0;
- fz_walk_path(ctx, path, &rect_path_walker, &arg);
- if (arg.count < 0)
- return 0;
- /* 3 entries are bad, unless the last one returns the first. */
- if (arg.count == 3 && (arg.p[0].x != arg.p[2].x || arg.p[0].y != arg.p[2].y))
- {
- return 0;
- }
- if (arg.count == 2 || arg.count == 3)
- {
- if (arg.p[0].x == arg.p[1].x || arg.p[0].y == arg.p[1].y)
- {
- if (bounds)
- {
- bounds->x0 = fz_min(arg.p[0].x, arg.p[1].x);
- bounds->x1 = fz_max(arg.p[0].x, arg.p[1].x);
- bounds->y0 = fz_min(arg.p[0].y, arg.p[1].y);
- bounds->y1 = fz_max(arg.p[0].y, arg.p[1].y);
- }
- return 1;
- }
- }
- /* All that's left are 4 entry ones */
- if (arg.count != 4)
- return 0;
- if (arg.p[0].x == arg.p[1].x)
- {
- /* p[0] p[3]
- * p[1] p[2]
- */
- if (arg.p[1].y == arg.p[2].y && arg.p[0].y == arg.p[3].y && arg.p[2].x == arg.p[3].x)
- {
- if (bounds)
- {
- bounds->x0 = fz_min(arg.p[0].x, arg.p[3].x);
- bounds->x1 = fz_max(arg.p[0].x, arg.p[3].x);
- bounds->y0 = fz_min(arg.p[0].y, arg.p[1].y);
- bounds->y1 = fz_max(arg.p[0].y, arg.p[1].y);
- }
- return 1;
- }
- }
- if (arg.p[0].y == arg.p[1].y)
- {
- /* p[0] p[1]
- * p[3] p[2]
- */
- if (arg.p[1].x == arg.p[2].x && arg.p[0].x == arg.p[3].x && arg.p[2].y == arg.p[3].y)
- {
- if (bounds)
- {
- bounds->x0 = fz_min(arg.p[0].x, arg.p[1].x);
- bounds->x1 = fz_max(arg.p[0].x, arg.p[1].x);
- bounds->y0 = fz_min(arg.p[0].y, arg.p[3].y);
- bounds->y1 = fz_max(arg.p[0].y, arg.p[3].y);
- }
- return 1;
- }
- }
- return 0;
- }
|