Coverage Report

Created: 2023-06-07 06:20

/src/mupdf/source/fitz/path.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include <string.h>
26
#include <assert.h>
27
28
// Thoughts for further optimisations:
29
// All paths start with MoveTo. We could probably avoid most cases where
30
// we store that. The next thing after a close must be a move.
31
// Commands are MOVE, LINE, HORIZ, VERT, DEGEN, CURVE, CURVEV, CURVEY, QUAD, RECT.
32
// We'd need to drop 2 to get us down to 3 bits.
33
// Commands can be followed by CLOSE. Use 1 bit for close.
34
// PDF 'RECT' implies close according to the spec, but I suspect
35
// we can ignore this as filling closes implicitly.
36
// We use a single bit in the path header to tell us whether we have
37
// a trailing move. Trailing moves can always be stripped when path
38
// construction completes.
39
40
typedef enum
41
{
42
  FZ_MOVETO = 'M',
43
  FZ_LINETO = 'L',
44
  FZ_DEGENLINETO = 'D',
45
  FZ_CURVETO = 'C',
46
  FZ_CURVETOV = 'V',
47
  FZ_CURVETOY = 'Y',
48
  FZ_HORIZTO = 'H',
49
  FZ_VERTTO = 'I',
50
  FZ_QUADTO = 'Q',
51
  FZ_RECTTO = 'R',
52
  FZ_MOVETOCLOSE = 'm',
53
  FZ_LINETOCLOSE = 'l',
54
  FZ_DEGENLINETOCLOSE = 'd',
55
  FZ_CURVETOCLOSE = 'c',
56
  FZ_CURVETOVCLOSE = 'v',
57
  FZ_CURVETOYCLOSE = 'y',
58
  FZ_HORIZTOCLOSE = 'h',
59
  FZ_VERTTOCLOSE = 'i',
60
  FZ_QUADTOCLOSE = 'q',
61
} fz_path_item_kind;
62
63
struct fz_path
64
{
65
  int8_t refs;
66
  uint8_t packed;
67
  int cmd_len, cmd_cap;
68
  unsigned char *cmds;
69
  int coord_len, coord_cap;
70
  float *coords;
71
  fz_point current;
72
  fz_point begin;
73
};
74
75
typedef struct
76
{
77
  int8_t refs;
78
  uint8_t packed;
79
  uint8_t coord_len;
80
  uint8_t cmd_len;
81
} fz_packed_path;
82
83
/*
84
  Paths are created UNPACKED. That means we have a fz_path
85
  structure with coords and cmds pointing to malloced blocks.
86
87
  After they have been completely constructed, callers may choose
88
  to 'pack' them into some target block of memory. If if coord_len
89
  and cmd_len are both < 256, then they are PACKED_FLAT into an
90
  fz_packed_path with the coords and cmds in the bytes afterwards,
91
  all inside the target block. If they cannot be accomodated in
92
  that way, then they are PACKED_OPEN, where an fz_path is put
93
  into the target block, and cmds and coords remain pointers to
94
  allocated blocks.
95
*/
96
enum
97
{
98
  FZ_PATH_UNPACKED = 0,
99
  FZ_PATH_PACKED_FLAT = 1,
100
  FZ_PATH_PACKED_OPEN = 2
101
};
102
103
5.74M
#define LAST_CMD(path) ((path)->cmd_len > 0 ? (path)->cmds[(path)->cmd_len-1] : 0)
104
105
fz_path *
106
fz_new_path(fz_context *ctx)
107
1.23M
{
108
1.23M
  fz_path *path;
109
110
1.23M
  path = fz_malloc_struct(ctx, fz_path);
111
1.23M
  path->refs = 1;
112
1.23M
  path->packed = FZ_PATH_UNPACKED;
113
1.23M
  path->current.x = 0;
114
1.23M
  path->current.y = 0;
115
1.23M
  path->begin.x = 0;
116
1.23M
  path->begin.y = 0;
117
118
1.23M
  return path;
119
1.23M
}
120
121
/*
122
  Take an additional reference to
123
  a path.
124
125
  No modifications should be carried out on a path
126
  to which more than one reference is held, as
127
  this can cause race conditions.
128
*/
129
fz_path *
130
fz_keep_path(fz_context *ctx, const fz_path *pathc)
131
11.2k
{
132
11.2k
  fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
133
11.2k
  int trimmable = 0;
134
135
11.2k
  if (path == NULL)
136
0
    return NULL;
137
11.2k
  fz_lock(ctx, FZ_LOCK_ALLOC);
138
  /* Technically, we should only access ->refs with the lock held,
139
   * so do that here. We can't actually do the trimming here, because
140
   * to do so would do memory accesses with the ALLOC lock held. */
141
11.2k
  if (path->refs == 1 && path->packed == FZ_PATH_UNPACKED)
142
0
    trimmable = 1;
143
11.2k
  fz_keep_imp8_locked(ctx, path, &path->refs);
144
11.2k
  fz_unlock(ctx, FZ_LOCK_ALLOC);
145
146
  /* This is thread safe, because we know that the only person
147
   * holding a reference to this thread is us. */
148
11.2k
  if (trimmable)
149
0
    fz_trim_path(ctx, path);
150
151
11.2k
  return path;
152
11.2k
}
153
154
void
155
fz_drop_path(fz_context *ctx, const fz_path *pathc)
156
1.26M
{
157
1.26M
  fz_path *path = (fz_path *)pathc; /* Explicit cast away of const */
158
159
1.26M
  if (fz_drop_imp8(ctx, path, &path->refs))
160
1.24M
  {
161
1.24M
    if (path->packed != FZ_PATH_PACKED_FLAT)
162
1.23M
    {
163
1.23M
      fz_free(ctx, path->cmds);
164
1.23M
      fz_free(ctx, path->coords);
165
1.23M
    }
166
1.24M
    if (path->packed == FZ_PATH_UNPACKED)
167
1.23M
      fz_free(ctx, path);
168
1.24M
  }
169
1.26M
}
170
171
int fz_packed_path_size(const fz_path *path)
172
11.2k
{
173
11.2k
  switch (path->packed)
174
11.2k
  {
175
0
  case FZ_PATH_UNPACKED:
176
0
    if (path->cmd_len > 255 || path->coord_len > 255)
177
0
      return sizeof(fz_path);
178
0
    return sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
179
669
  case FZ_PATH_PACKED_OPEN:
180
669
    return sizeof(fz_path);
181
10.5k
  case FZ_PATH_PACKED_FLAT:
182
10.5k
  {
183
10.5k
    fz_packed_path *pack = (fz_packed_path *)path;
184
10.5k
    return sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
185
0
  }
186
0
  default:
187
0
    assert("This never happens" == NULL);
188
0
    return 0;
189
11.2k
  }
190
11.2k
}
191
192
size_t
193
fz_pack_path(fz_context *ctx, uint8_t *pack_, size_t max, const fz_path *path)
194
9.29k
{
195
9.29k
  uint8_t *ptr;
196
9.29k
  size_t size;
197
198
9.29k
  if (path->packed == FZ_PATH_PACKED_FLAT)
199
0
  {
200
0
    fz_packed_path *pack = (fz_packed_path *)path;
201
0
    fz_packed_path *out = (fz_packed_path *)pack_;
202
0
    size = sizeof(fz_packed_path) + sizeof(float) * pack->coord_len + sizeof(uint8_t) * pack->cmd_len;
203
204
0
    if (size > max)
205
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "Can't pack a path that small!");
206
207
0
    if (out)
208
0
    {
209
0
      out->refs = 1;
210
0
      out->packed = FZ_PATH_PACKED_FLAT;
211
0
      out->coord_len = pack->coord_len;
212
0
      out->cmd_len = pack->cmd_len;
213
0
      memcpy(&out[1], &pack[1], size - sizeof(*out));
214
0
    }
215
0
    return size;
216
0
  }
217
218
9.29k
  size = sizeof(fz_packed_path) + sizeof(float) * path->coord_len + sizeof(uint8_t) * path->cmd_len;
219
220
  /* If the path can't be packed flat, then pack it open */
221
9.29k
  if (path->cmd_len > 255 || path->coord_len > 255 || size > max)
222
610
  {
223
610
    fz_path *pack = (fz_path *)pack_;
224
225
610
    if (sizeof(fz_path) > max)
226
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "Can't pack a path that small!");
227
228
610
    if (pack != NULL)
229
305
    {
230
305
      pack->refs = 1;
231
305
      pack->packed = FZ_PATH_PACKED_OPEN;
232
305
      pack->current.x = 0;
233
305
      pack->current.y = 0;
234
305
      pack->begin.x = 0;
235
305
      pack->begin.y = 0;
236
305
      pack->coord_cap = path->coord_len;
237
305
      pack->coord_len = path->coord_len;
238
305
      pack->cmd_cap = path->cmd_len;
239
305
      pack->cmd_len = path->cmd_len;
240
305
      pack->coords = Memento_label(fz_malloc_array(ctx, path->coord_len, float), "path_packed_coords");
241
610
      fz_try(ctx)
242
610
      {
243
305
        pack->cmds = Memento_label(fz_malloc_array(ctx, path->cmd_len, uint8_t), "path_packed_cmds");
244
305
      }
245
610
      fz_catch(ctx)
246
0
      {
247
0
        fz_free(ctx, pack->coords);
248
0
        fz_rethrow(ctx);
249
0
      }
250
305
      memcpy(pack->coords, path->coords, sizeof(float) * path->coord_len);
251
305
      memcpy(pack->cmds, path->cmds, sizeof(uint8_t) * path->cmd_len);
252
305
    }
253
610
    return sizeof(fz_path);
254
610
  }
255
8.68k
  else
256
8.68k
  {
257
8.68k
    fz_packed_path *pack = (fz_packed_path *)pack_;
258
259
8.68k
    if (pack != NULL)
260
4.34k
    {
261
4.34k
      pack->refs = 1;
262
4.34k
      pack->packed = FZ_PATH_PACKED_FLAT;
263
4.34k
      pack->cmd_len = path->cmd_len;
264
4.34k
      pack->coord_len = path->coord_len;
265
4.34k
      ptr = (uint8_t *)&pack[1];
266
4.34k
      memcpy(ptr, path->coords, sizeof(float) * path->coord_len);
267
4.34k
      ptr += sizeof(float) * path->coord_len;
268
4.34k
      memcpy(ptr, path->cmds, sizeof(uint8_t) * path->cmd_len);
269
4.34k
    }
270
271
8.68k
    return size;
272
8.68k
  }
273
9.29k
}
274
275
static void
276
push_cmd(fz_context *ctx, fz_path *path, int cmd)
277
9.48M
{
278
9.48M
  if (path->refs != 1)
279
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "cannot modify shared paths");
280
281
9.48M
  if (path->cmd_len + 1 >= path->cmd_cap)
282
1.42M
  {
283
1.42M
    int new_cmd_cap = fz_maxi(16, path->cmd_cap * 2);
284
1.42M
    path->cmds = fz_realloc_array(ctx, path->cmds, new_cmd_cap, unsigned char);
285
1.42M
    path->cmd_cap = new_cmd_cap;
286
1.42M
  }
287
288
9.48M
  path->cmds[path->cmd_len++] = cmd;
289
9.48M
}
290
291
static void
292
push_coord(fz_context *ctx, fz_path *path, float x, float y)
293
15.3M
{
294
15.3M
  if (path->coord_len + 2 >= path->coord_cap)
295
1.66M
  {
296
1.66M
    int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
297
1.66M
    path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
298
1.66M
    path->coord_cap = new_coord_cap;
299
1.66M
  }
300
301
15.3M
  path->coords[path->coord_len++] = x;
302
15.3M
  path->coords[path->coord_len++] = y;
303
304
15.3M
  path->current.x = x;
305
15.3M
  path->current.y = y;
306
15.3M
}
307
308
static void
309
push_ord(fz_context *ctx, fz_path *path, float xy, int isx)
310
2.16M
{
311
2.16M
  if (path->coord_len + 1 >= path->coord_cap)
312
32.1k
  {
313
32.1k
    int new_coord_cap = fz_maxi(32, path->coord_cap * 2);
314
32.1k
    path->coords = fz_realloc_array(ctx, path->coords, new_coord_cap, float);
315
32.1k
    path->coord_cap = new_coord_cap;
316
32.1k
  }
317
318
2.16M
  path->coords[path->coord_len++] = xy;
319
320
2.16M
  if (isx)
321
992k
    path->current.x = xy;
322
1.17M
  else
323
1.17M
    path->current.y = xy;
324
2.16M
}
325
326
fz_point
327
fz_currentpoint(fz_context *ctx, fz_path *path)
328
0
{
329
0
  return path->current;
330
0
}
331
332
void
333
fz_moveto(fz_context *ctx, fz_path *path, float x, float y)
334
1.56M
{
335
1.56M
  if (path->packed)
336
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
337
338
1.56M
  if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
339
301k
  {
340
    /* Collapse moveto followed by moveto. */
341
301k
    path->coords[path->coord_len-2] = x;
342
301k
    path->coords[path->coord_len-1] = y;
343
301k
    path->current.x = x;
344
301k
    path->current.y = y;
345
301k
    path->begin = path->current;
346
301k
    return;
347
301k
  }
348
349
1.26M
  push_cmd(ctx, path, FZ_MOVETO);
350
1.26M
  push_coord(ctx, path, x, y);
351
352
1.26M
  path->begin = path->current;
353
1.26M
}
354
355
void
356
fz_lineto(fz_context *ctx, fz_path *path, float x, float y)
357
4.50M
{
358
4.50M
  float x0, y0;
359
360
4.50M
  if (path->packed)
361
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
362
363
4.50M
  x0 = path->current.x;
364
4.50M
  y0 = path->current.y;
365
366
4.50M
  if (path->cmd_len == 0)
367
42.5k
  {
368
42.5k
    fz_warn(ctx, "lineto with no current point");
369
42.5k
    return;
370
42.5k
  }
371
372
  /* (Anything other than MoveTo) followed by (LineTo the same place) is a nop */
373
4.46M
  if (LAST_CMD(path) != FZ_MOVETO && x0 == x && y0 == y)
374
541k
    return;
375
376
3.92M
  if (x0 == x)
377
1.18M
  {
378
1.18M
    if (y0 == y)
379
8.50k
    {
380
8.50k
      if (LAST_CMD(path) != FZ_MOVETO)
381
0
        return;
382
8.50k
      push_cmd(ctx, path, FZ_DEGENLINETO);
383
8.50k
    }
384
1.17M
    else
385
1.17M
    {
386
1.17M
      push_cmd(ctx, path, FZ_VERTTO);
387
1.17M
      push_ord(ctx, path, y, 0);
388
1.17M
    }
389
1.18M
  }
390
2.73M
  else if (y0 == y)
391
992k
  {
392
992k
    push_cmd(ctx, path, FZ_HORIZTO);
393
992k
    push_ord(ctx, path, x, 1);
394
992k
  }
395
1.74M
  else
396
1.74M
  {
397
1.74M
    push_cmd(ctx, path, FZ_LINETO);
398
1.74M
    push_coord(ctx, path, x, y);
399
1.74M
  }
400
3.92M
}
401
402
void
403
fz_curveto(fz_context *ctx, fz_path *path,
404
  float x1, float y1,
405
  float x2, float y2,
406
  float x3, float y3)
407
4.03M
{
408
4.03M
  float x0, y0;
409
410
4.03M
  if (path->packed)
411
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
412
413
4.03M
  x0 = path->current.x;
414
4.03M
  y0 = path->current.y;
415
416
4.03M
  if (path->cmd_len == 0)
417
53.2k
  {
418
53.2k
    fz_warn(ctx, "curveto with no current point");
419
53.2k
    return;
420
53.2k
  }
421
422
  /* Check for degenerate cases: */
423
3.97M
  if (x0 == x1 && y0 == y1)
424
83.8k
  {
425
83.8k
    if (x2 == x3 && y2 == y3)
426
11.8k
    {
427
      /* If (x1,y1)==(x2,y2) and prev wasn't a moveto, then skip */
428
11.8k
      if (x1 == x2 && y1 == y2 && LAST_CMD(path) != FZ_MOVETO)
429
9.40k
        return;
430
      /* Otherwise a line will suffice */
431
2.45k
      fz_lineto(ctx, path, x3, y3);
432
2.45k
    }
433
72.0k
    else if (x1 == x2 && y1 == y2)
434
959
    {
435
      /* A line will suffice */
436
959
      fz_lineto(ctx, path, x3, y3);
437
959
    }
438
71.0k
    else
439
71.0k
      fz_curvetov(ctx, path, x2, y2, x3, y3);
440
74.4k
    return;
441
83.8k
  }
442
3.89M
  else if (x2 == x3 && y2 == y3)
443
103k
  {
444
103k
    if (x1 == x2 && y1 == y2)
445
5.97k
    {
446
      /* A line will suffice */
447
5.97k
      fz_lineto(ctx, path, x3, y3);
448
5.97k
    }
449
97.3k
    else
450
97.3k
      fz_curvetoy(ctx, path, x1, y1, x3, y3);
451
103k
    return;
452
103k
  }
453
454
3.78M
  push_cmd(ctx, path, FZ_CURVETO);
455
3.78M
  push_coord(ctx, path, x1, y1);
456
3.78M
  push_coord(ctx, path, x2, y2);
457
3.78M
  push_coord(ctx, path, x3, y3);
458
3.78M
}
459
460
void
461
fz_quadto(fz_context *ctx, fz_path *path,
462
  float x1, float y1,
463
  float x2, float y2)
464
17.8k
{
465
17.8k
  float x0, y0;
466
467
17.8k
  if (path->packed)
468
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
469
470
17.8k
  x0 = path->current.x;
471
17.8k
  y0 = path->current.y;
472
473
17.8k
  if (path->cmd_len == 0)
474
0
  {
475
0
    fz_warn(ctx, "quadto with no current point");
476
0
    return;
477
0
  }
478
479
  /* Check for degenerate cases: */
480
17.8k
  if ((x0 == x1 && y0 == y1) || (x1 == x2 && y1 == y2))
481
2.11k
  {
482
2.11k
    if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
483
0
      return;
484
    /* A line will suffice */
485
2.11k
    fz_lineto(ctx, path, x2, y2);
486
2.11k
    return;
487
2.11k
  }
488
489
15.7k
  push_cmd(ctx, path, FZ_QUADTO);
490
15.7k
  push_coord(ctx, path, x1, y1);
491
15.7k
  push_coord(ctx, path, x2, y2);
492
15.7k
}
493
494
void
495
fz_curvetov(fz_context *ctx, fz_path *path, float x2, float y2, float x3, float y3)
496
73.3k
{
497
73.3k
  float x0, y0;
498
499
73.3k
  if (path->packed)
500
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
501
502
73.3k
  x0 = path->current.x;
503
73.3k
  y0 = path->current.y;
504
505
73.3k
  if (path->cmd_len == 0)
506
1.15k
  {
507
1.15k
    fz_warn(ctx, "curveto with no current point");
508
1.15k
    return;
509
1.15k
  }
510
511
  /* Check for degenerate cases: */
512
72.1k
  if (x2 == x3 && y2 == y3)
513
182
  {
514
    /* If (x0,y0)==(x2,y2) and prev wasn't a moveto, then skip */
515
182
    if (x0 == x2 && y0 == y2 && LAST_CMD(path) != FZ_MOVETO)
516
15
      return;
517
    /* Otherwise a line will suffice */
518
167
    fz_lineto(ctx, path, x3, y3);
519
167
  }
520
71.9k
  else if (x0 == x2 && y0 == y2)
521
0
  {
522
    /* A line will suffice */
523
0
    fz_lineto(ctx, path, x3, y3);
524
0
  }
525
526
72.1k
  push_cmd(ctx, path, FZ_CURVETOV);
527
72.1k
  push_coord(ctx, path, x2, y2);
528
72.1k
  push_coord(ctx, path, x3, y3);
529
72.1k
}
530
531
void
532
fz_curvetoy(fz_context *ctx, fz_path *path, float x1, float y1, float x3, float y3)
533
98.6k
{
534
98.6k
  float x0, y0;
535
536
98.6k
  if (path->packed)
537
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
538
539
98.6k
  x0 = path->current.x;
540
98.6k
  y0 = path->current.y;
541
542
98.6k
  if (path->cmd_len == 0)
543
206
  {
544
206
    fz_warn(ctx, "curveto with no current point");
545
206
    return;
546
206
  }
547
548
  /* Check for degenerate cases: */
549
98.4k
  if (x1 == x3 && y1 == y3)
550
32
  {
551
    /* If (x0,y0)==(x1,y1) and prev wasn't a moveto, then skip */
552
32
    if (x0 == x1 && y0 == y1 && LAST_CMD(path) != FZ_MOVETO)
553
15
      return;
554
    /* Otherwise a line will suffice */
555
17
    fz_lineto(ctx, path, x3, y3);
556
17
  }
557
558
98.3k
  push_cmd(ctx, path, FZ_CURVETOY);
559
98.3k
  push_coord(ctx, path, x1, y1);
560
98.3k
  push_coord(ctx, path, x3, y3);
561
98.3k
}
562
563
void
564
fz_closepath(fz_context *ctx, fz_path *path)
565
545k
{
566
545k
  uint8_t rep;
567
568
545k
  if (path->packed)
569
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
570
571
545k
  if (path->cmd_len == 0)
572
4.84k
  {
573
4.84k
    fz_warn(ctx, "closepath with no current point");
574
4.84k
    return;
575
4.84k
  }
576
577
540k
  switch(LAST_CMD(path))
578
540k
  {
579
152k
  case FZ_MOVETO:
580
152k
    rep = FZ_MOVETOCLOSE;
581
152k
    break;
582
78.2k
  case FZ_LINETO:
583
78.2k
    rep = FZ_LINETOCLOSE;
584
78.2k
    break;
585
231
  case FZ_DEGENLINETO:
586
231
    rep = FZ_DEGENLINETOCLOSE;
587
231
    break;
588
57.5k
  case FZ_CURVETO:
589
57.5k
    rep = FZ_CURVETOCLOSE;
590
57.5k
    break;
591
2.14k
  case FZ_CURVETOV:
592
2.14k
    rep = FZ_CURVETOVCLOSE;
593
2.14k
    break;
594
2.34k
  case FZ_CURVETOY:
595
2.34k
    rep = FZ_CURVETOYCLOSE;
596
2.34k
    break;
597
89.3k
  case FZ_HORIZTO:
598
89.3k
    rep = FZ_HORIZTOCLOSE;
599
89.3k
    break;
600
156k
  case FZ_VERTTO:
601
156k
    rep = FZ_VERTTOCLOSE;
602
156k
    break;
603
430
  case FZ_QUADTO:
604
430
    rep = FZ_QUADTOCLOSE;
605
430
    break;
606
1.23k
  case FZ_RECTTO:
607
    /* RectTo implies close */
608
1.23k
    return;
609
19
  case FZ_MOVETOCLOSE:
610
314
  case FZ_LINETOCLOSE:
611
335
  case FZ_DEGENLINETOCLOSE:
612
344
  case FZ_CURVETOCLOSE:
613
344
  case FZ_CURVETOVCLOSE:
614
344
  case FZ_CURVETOYCLOSE:
615
465
  case FZ_HORIZTOCLOSE:
616
476
  case FZ_VERTTOCLOSE:
617
476
  case FZ_QUADTOCLOSE:
618
    /* CLOSE following a CLOSE is a NOP */
619
476
    return;
620
0
  default: /* default never happens */
621
0
  case 0:
622
    /* Closing an empty path is a NOP */
623
0
    return;
624
540k
  }
625
626
538k
  path->cmds[path->cmd_len-1] = rep;
627
628
538k
  path->current = path->begin;
629
538k
}
630
631
void
632
fz_rectto(fz_context *ctx, fz_path *path, float x1, float y1, float x2, float y2)
633
317k
{
634
317k
  if (path->packed)
635
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot modify a packed path");
636
637
317k
  if (path->cmd_len > 0 && LAST_CMD(path) == FZ_MOVETO)
638
4.11k
  {
639
    /* Collapse moveto followed by rectto. */
640
4.11k
    path->coord_len -= 2;
641
4.11k
    path->cmd_len--;
642
4.11k
  }
643
644
317k
  push_cmd(ctx, path, FZ_RECTTO);
645
317k
  push_coord(ctx, path, x1, y1);
646
317k
  push_coord(ctx, path, x2, y2);
647
648
317k
  path->current = path->begin;
649
317k
}
650
651
static inline void bound_expand(fz_rect *r, fz_point p)
652
7.56M
{
653
7.56M
  if (p.x < r->x0) r->x0 = p.x;
654
7.56M
  if (p.y < r->y0) r->y0 = p.y;
655
7.56M
  if (p.x > r->x1) r->x1 = p.x;
656
7.56M
  if (p.y > r->y1) r->y1 = p.y;
657
7.56M
}
658
659
void fz_walk_path(fz_context *ctx, const fz_path *path, const fz_path_walker *proc, void *arg)
660
2.17M
{
661
2.17M
  int i, k, cmd_len;
662
2.17M
  float x=0, y=0, sx=0, sy=0;
663
2.17M
  uint8_t *cmds;
664
2.17M
  float *coords;
665
666
2.17M
  switch (path->packed)
667
2.17M
  {
668
2.17M
  case FZ_PATH_UNPACKED:
669
2.17M
  case FZ_PATH_PACKED_OPEN:
670
2.17M
    cmd_len = path->cmd_len;
671
2.17M
    coords = path->coords;
672
2.17M
    cmds = path->cmds;
673
2.17M
    break;
674
4.80k
  case FZ_PATH_PACKED_FLAT:
675
4.80k
    cmd_len = ((fz_packed_path *)path)->cmd_len;
676
4.80k
    coords = (float *)&((fz_packed_path *)path)[1];
677
4.80k
    cmds = (uint8_t *)&coords[((fz_packed_path *)path)->coord_len];
678
4.80k
    break;
679
0
  default:
680
0
    assert("This never happens" == NULL);
681
0
    return;
682
2.17M
  }
683
684
2.17M
  if (cmd_len == 0)
685
67.7k
    return;
686
687
15.8M
  for (k=0, i = 0; i < cmd_len; i++)
688
13.7M
  {
689
13.7M
    uint8_t cmd = cmds[i];
690
691
13.7M
    switch (cmd)
692
13.7M
    {
693
5.11M
    case FZ_CURVETO:
694
5.18M
    case FZ_CURVETOCLOSE:
695
5.18M
      proc->curveto(ctx, arg,
696
5.18M
          coords[k],
697
5.18M
          coords[k+1],
698
5.18M
          coords[k+2],
699
5.18M
          coords[k+3],
700
5.18M
          x = coords[k+4],
701
5.18M
          y = coords[k+5]);
702
5.18M
      k += 6;
703
5.18M
      if (cmd == FZ_CURVETOCLOSE)
704
64.6k
      {
705
64.6k
        if (proc->closepath)
706
57.8k
          proc->closepath(ctx, arg);
707
64.6k
        x = sx;
708
64.6k
        y = sy;
709
64.6k
      }
710
5.18M
      break;
711
75.0k
    case FZ_CURVETOV:
712
77.4k
    case FZ_CURVETOVCLOSE:
713
77.4k
      if (proc->curvetov)
714
0
        proc->curvetov(ctx, arg,
715
0
            coords[k],
716
0
            coords[k+1],
717
0
            x = coords[k+2],
718
0
            y = coords[k+3]);
719
77.4k
      else
720
77.4k
      {
721
77.4k
        proc->curveto(ctx, arg,
722
77.4k
            x,
723
77.4k
            y,
724
77.4k
            coords[k],
725
77.4k
            coords[k+1],
726
77.4k
            coords[k+2],
727
77.4k
            coords[k+3]);
728
77.4k
        x = coords[k+2];
729
77.4k
        y = coords[k+3];
730
77.4k
      }
731
77.4k
      k += 4;
732
77.4k
      if (cmd == FZ_CURVETOVCLOSE)
733
2.40k
      {
734
2.40k
        if (proc->closepath)
735
2.20k
          proc->closepath(ctx, arg);
736
2.40k
        x = sx;
737
2.40k
        y = sy;
738
2.40k
      }
739
77.4k
      break;
740
122k
    case FZ_CURVETOY:
741
125k
    case FZ_CURVETOYCLOSE:
742
125k
      if (proc->curvetoy)
743
0
        proc->curvetoy(ctx, arg,
744
0
            coords[k],
745
0
            coords[k+1],
746
0
            x = coords[k+2],
747
0
            y = coords[k+3]);
748
125k
      else
749
125k
        proc->curveto(ctx, arg,
750
125k
            coords[k],
751
125k
            coords[k+1],
752
125k
            coords[k+2],
753
125k
            coords[k+3],
754
125k
            x = coords[k+2],
755
125k
            y = coords[k+3]);
756
125k
      k += 4;
757
125k
      if (cmd == FZ_CURVETOYCLOSE)
758
3.24k
      {
759
3.24k
        if (proc->closepath)
760
2.64k
          proc->closepath(ctx, arg);
761
3.24k
        x = sx;
762
3.24k
        y = sy;
763
3.24k
      }
764
125k
      break;
765
15.3k
    case FZ_QUADTO:
766
15.7k
    case FZ_QUADTOCLOSE:
767
15.7k
      if (proc->quadto)
768
15.7k
        proc->quadto(ctx, arg,
769
15.7k
          coords[k],
770
15.7k
          coords[k+1],
771
15.7k
          x = coords[k+2],
772
15.7k
          y = coords[k+3]);
773
0
      else
774
0
      {
775
0
        float c2x = coords[k] * 2;
776
0
        float c2y = coords[k+1] * 2;
777
0
        float c1x = (x + c2x) / 3;
778
0
        float c1y = (y + c2y) / 3;
779
0
        x = coords[k+2];
780
0
        y = coords[k+3];
781
0
        c2x = (c2x + x) / 3;
782
0
        c2y = (c2y + y) / 3;
783
784
0
        proc->curveto(ctx, arg,
785
0
          c1x,
786
0
          c1y,
787
0
          c2x,
788
0
          c2y,
789
0
          x,
790
0
          y);
791
0
      }
792
15.7k
      k += 4;
793
15.7k
      if (cmd == FZ_QUADTOCLOSE)
794
430
      {
795
430
        if (proc->closepath)
796
430
          proc->closepath(ctx, arg);
797
430
        x = sx;
798
430
        y = sy;
799
430
      }
800
15.7k
      break;
801
1.91M
    case FZ_MOVETO:
802
2.06M
    case FZ_MOVETOCLOSE:
803
2.06M
      proc->moveto(ctx, arg,
804
2.06M
        x = coords[k],
805
2.06M
        y = coords[k+1]);
806
2.06M
      k += 2;
807
2.06M
      sx = x;
808
2.06M
      sy = y;
809
2.06M
      if (cmd == FZ_MOVETOCLOSE)
810
153k
      {
811
153k
        if (proc->closepath)
812
152k
          proc->closepath(ctx, arg);
813
153k
        x = sx;
814
153k
        y = sy;
815
153k
      }
816
2.06M
      break;
817
2.58M
    case FZ_LINETO:
818
2.69M
    case FZ_LINETOCLOSE:
819
2.69M
      proc->lineto(ctx, arg,
820
2.69M
        x = coords[k],
821
2.69M
        y = coords[k+1]);
822
2.69M
      k += 2;
823
2.69M
      if (cmd == FZ_LINETOCLOSE)
824
103k
      {
825
103k
        if (proc->closepath)
826
78.7k
          proc->closepath(ctx, arg);
827
103k
        x = sx;
828
103k
        y = sy;
829
103k
      }
830
2.69M
      break;
831
1.23M
    case FZ_HORIZTO:
832
1.41M
    case FZ_HORIZTOCLOSE:
833
1.41M
      proc->lineto(ctx, arg,
834
1.41M
        x = coords[k],
835
1.41M
        y);
836
1.41M
      k += 1;
837
1.41M
      if (cmd == FZ_HORIZTOCLOSE)
838
175k
      {
839
175k
        if (proc->closepath)
840
89.8k
          proc->closepath(ctx, arg);
841
175k
        x = sx;
842
175k
        y = sy;
843
175k
      }
844
1.41M
      break;
845
1.24M
    case FZ_VERTTO:
846
1.43M
    case FZ_VERTTOCLOSE:
847
1.43M
      proc->lineto(ctx, arg,
848
1.43M
        x,
849
1.43M
        y = coords[k]);
850
1.43M
      k += 1;
851
1.43M
      if (cmd == FZ_VERTTOCLOSE)
852
187k
      {
853
187k
        if (proc->closepath)
854
155k
          proc->closepath(ctx, arg);
855
187k
        x = sx;
856
187k
        y = sy;
857
187k
      }
858
1.43M
      break;
859
11.3k
    case FZ_DEGENLINETO:
860
12.1k
    case FZ_DEGENLINETOCLOSE:
861
12.1k
      proc->lineto(ctx, arg,
862
12.1k
        x,
863
12.1k
        y);
864
12.1k
      if (cmd == FZ_DEGENLINETOCLOSE)
865
776
      {
866
776
        if (proc->closepath)
867
433
          proc->closepath(ctx, arg);
868
776
        x = sx;
869
776
        y = sy;
870
776
      }
871
12.1k
      break;
872
750k
    case FZ_RECTTO:
873
750k
      if (proc->rectto)
874
209k
      {
875
209k
        proc->rectto(ctx, arg,
876
209k
            x = coords[k],
877
209k
            y = coords[k+1],
878
209k
            coords[k+2],
879
209k
            coords[k+3]);
880
209k
      }
881
540k
      else
882
540k
      {
883
540k
        proc->moveto(ctx, arg,
884
540k
          x = coords[k],
885
540k
          y = coords[k+1]);
886
540k
        proc->lineto(ctx, arg,
887
540k
          coords[k+2],
888
540k
          coords[k+1]);
889
540k
        proc->lineto(ctx, arg,
890
540k
          coords[k+2],
891
540k
          coords[k+3]);
892
540k
        proc->lineto(ctx, arg,
893
540k
          coords[k],
894
540k
          coords[k+3]);
895
540k
        if (proc->closepath)
896
110k
          proc->closepath(ctx, arg);
897
540k
      }
898
750k
      sx = x;
899
750k
      sy = y;
900
750k
      k += 4;
901
750k
      break;
902
13.7M
    }
903
13.7M
  }
904
2.10M
}
905
906
typedef struct
907
{
908
  fz_matrix ctm;
909
  fz_rect rect;
910
  fz_point move;
911
  int trailing_move;
912
  int first;
913
} bound_path_arg;
914
915
static void
916
bound_moveto(fz_context *ctx, void *arg_, float x, float y)
917
1.24M
{
918
1.24M
  bound_path_arg *arg = (bound_path_arg *)arg_;
919
1.24M
  arg->move = fz_transform_point_xy(x, y, arg->ctm);
920
1.24M
  arg->trailing_move = 1;
921
1.24M
}
922
923
static void
924
bound_lineto(fz_context *ctx, void *arg_, float x, float y)
925
2.91M
{
926
2.91M
  bound_path_arg *arg = (bound_path_arg *)arg_;
927
2.91M
  fz_point p = fz_transform_point_xy(x, y, arg->ctm);
928
2.91M
  if (arg->first)
929
777k
  {
930
777k
    arg->rect.x0 = arg->rect.x1 = p.x;
931
777k
    arg->rect.y0 = arg->rect.y1 = p.y;
932
777k
    arg->first = 0;
933
777k
  }
934
2.13M
  else
935
2.13M
    bound_expand(&arg->rect, p);
936
2.91M
  if (arg->trailing_move)
937
926k
  {
938
926k
    arg->trailing_move = 0;
939
926k
    bound_expand(&arg->rect, arg->move);
940
926k
  }
941
2.91M
}
942
943
static void
944
bound_curveto(fz_context *ctx, void *arg_, float x1, float y1, float x2, float y2, float x3, float y3)
945
1.46M
{
946
1.46M
  bound_path_arg *arg = (bound_path_arg *)arg_;
947
1.46M
  fz_point p = fz_transform_point_xy(x1, y1, arg->ctm);
948
1.46M
  if (arg->first)
949
169k
  {
950
169k
    arg->rect.x0 = arg->rect.x1 = p.x;
951
169k
    arg->rect.y0 = arg->rect.y1 = p.y;
952
169k
    arg->first = 0;
953
169k
  }
954
1.29M
  else
955
1.29M
    bound_expand(&arg->rect, p);
956
1.46M
  bound_expand(&arg->rect, fz_transform_point_xy(x2, y2, arg->ctm));
957
1.46M
  bound_expand(&arg->rect, fz_transform_point_xy(x3, y3, arg->ctm));
958
1.46M
  if (arg->trailing_move)
959
271k
  {
960
271k
    arg->trailing_move = 0;
961
271k
    bound_expand(&arg->rect, arg->move);
962
271k
  }
963
1.46M
}
964
965
static const fz_path_walker bound_path_walker =
966
{
967
  bound_moveto,
968
  bound_lineto,
969
  bound_curveto,
970
  NULL
971
};
972
973
fz_rect
974
fz_bound_path(fz_context *ctx, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm)
975
992k
{
976
992k
  bound_path_arg arg;
977
978
992k
  arg.ctm = ctm;
979
992k
  arg.rect = fz_empty_rect;
980
992k
  arg.trailing_move = 0;
981
992k
  arg.first = 1;
982
983
992k
  fz_walk_path(ctx, path, &bound_path_walker, &arg);
984
985
992k
  if (!arg.first && stroke)
986
353k
  {
987
353k
    arg.rect = fz_adjust_rect_for_stroke(ctx, arg.rect, stroke, ctm);
988
353k
  }
989
990
992k
  return arg.rect;
991
992k
}
992
993
fz_rect
994
fz_adjust_rect_for_stroke(fz_context *ctx, fz_rect r, const fz_stroke_state *stroke, fz_matrix ctm)
995
367k
{
996
367k
  float expand;
997
998
367k
  if (!stroke)
999
0
    return r;
1000
1001
367k
  expand = stroke->linewidth;
1002
367k
  if (expand == 0)
1003
1.81k
    expand = 1.0f;
1004
367k
  expand *= fz_matrix_max_expansion(ctm);
1005
367k
  if ((stroke->linejoin == FZ_LINEJOIN_MITER || stroke->linejoin == FZ_LINEJOIN_MITER_XPS) && stroke->miterlimit > 1)
1006
154k
    expand *= stroke->miterlimit;
1007
1008
367k
  r.x0 -= expand;
1009
367k
  r.y0 -= expand;
1010
367k
  r.x1 += expand;
1011
367k
  r.y1 += expand;
1012
367k
  return r;
1013
367k
}
1014
1015
void
1016
fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix ctm)
1017
0
{
1018
0
  int i, k, n;
1019
0
  fz_point p, p1, p2, p3, q, s;
1020
1021
0
  if (path->packed)
1022
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot transform a packed path");
1023
1024
0
  if (ctm.b == 0 && ctm.c == 0)
1025
0
  {
1026
    /* Simple, in place transform */
1027
0
    i = 0;
1028
0
    k = 0;
1029
0
    while (i < path->cmd_len)
1030
0
    {
1031
0
      uint8_t cmd = path->cmds[i];
1032
1033
0
      switch (cmd)
1034
0
      {
1035
0
      case FZ_MOVETO:
1036
0
      case FZ_LINETO:
1037
0
      case FZ_MOVETOCLOSE:
1038
0
      case FZ_LINETOCLOSE:
1039
0
        n = 1;
1040
0
        break;
1041
0
      case FZ_DEGENLINETO:
1042
0
      case FZ_DEGENLINETOCLOSE:
1043
0
        n = 0;
1044
0
        break;
1045
0
      case FZ_CURVETO:
1046
0
      case FZ_CURVETOCLOSE:
1047
0
        n = 3;
1048
0
        break;
1049
0
      case FZ_RECTTO:
1050
0
        s.x = path->coords[k];
1051
0
        s.y = path->coords[k+1];
1052
0
        n = 2;
1053
0
        break;
1054
0
      case FZ_CURVETOV:
1055
0
      case FZ_CURVETOY:
1056
0
      case FZ_QUADTO:
1057
0
      case FZ_CURVETOVCLOSE:
1058
0
      case FZ_CURVETOYCLOSE:
1059
0
      case FZ_QUADTOCLOSE:
1060
0
        n = 2;
1061
0
        break;
1062
0
      case FZ_HORIZTO:
1063
0
      case FZ_HORIZTOCLOSE:
1064
0
        q.x = path->coords[k];
1065
0
        p = fz_transform_point(q, ctm);
1066
0
        path->coords[k++] = p.x;
1067
0
        n = 0;
1068
0
        break;
1069
0
      case FZ_VERTTO:
1070
0
      case FZ_VERTTOCLOSE:
1071
0
        q.y = path->coords[k];
1072
0
        p = fz_transform_point(q, ctm);
1073
0
        path->coords[k++] = p.y;
1074
0
        n = 0;
1075
0
        break;
1076
0
      default:
1077
0
        assert("Unknown path cmd" == NULL);
1078
0
      }
1079
0
      while (n > 0)
1080
0
      {
1081
0
        q.x = path->coords[k];
1082
0
        q.y = path->coords[k+1];
1083
0
        p = fz_transform_point(q, ctm);
1084
0
        path->coords[k++] = p.x;
1085
0
        path->coords[k++] = p.y;
1086
0
        n--;
1087
0
      }
1088
0
      switch (cmd)
1089
0
      {
1090
0
      case FZ_MOVETO:
1091
0
      case FZ_MOVETOCLOSE:
1092
0
        s = q;
1093
0
        break;
1094
0
      case FZ_LINETOCLOSE:
1095
0
      case FZ_DEGENLINETOCLOSE:
1096
0
      case FZ_CURVETOCLOSE:
1097
0
      case FZ_CURVETOVCLOSE:
1098
0
      case FZ_CURVETOYCLOSE:
1099
0
      case FZ_QUADTOCLOSE:
1100
0
      case FZ_HORIZTOCLOSE:
1101
0
      case FZ_VERTTOCLOSE:
1102
0
      case FZ_RECTTO:
1103
0
        q = s;
1104
0
        break;
1105
0
      }
1106
0
      i++;
1107
0
    }
1108
0
  }
1109
0
  else if (ctm.a == 0 && ctm.d == 0)
1110
0
  {
1111
    /* In place transform with command rewriting */
1112
0
    i = 0;
1113
0
    k = 0;
1114
0
    while (i < path->cmd_len)
1115
0
    {
1116
0
      uint8_t cmd = path->cmds[i];
1117
1118
0
      switch (cmd)
1119
0
      {
1120
0
      case FZ_MOVETO:
1121
0
      case FZ_LINETO:
1122
0
      case FZ_MOVETOCLOSE:
1123
0
      case FZ_LINETOCLOSE:
1124
0
        n = 1;
1125
0
        break;
1126
0
      case FZ_DEGENLINETO:
1127
0
      case FZ_DEGENLINETOCLOSE:
1128
0
        n = 0;
1129
0
        break;
1130
0
      case FZ_CURVETO:
1131
0
      case FZ_CURVETOCLOSE:
1132
0
        n = 3;
1133
0
        break;
1134
0
      case FZ_RECTTO:
1135
0
        s.x = path->coords[k];
1136
0
        s.y = path->coords[k+1];
1137
0
        n = 2;
1138
0
        break;
1139
0
      case FZ_CURVETOV:
1140
0
      case FZ_CURVETOY:
1141
0
      case FZ_QUADTO:
1142
0
      case FZ_CURVETOVCLOSE:
1143
0
      case FZ_CURVETOYCLOSE:
1144
0
      case FZ_QUADTOCLOSE:
1145
0
        n = 2;
1146
0
        break;
1147
0
      case FZ_HORIZTO:
1148
0
        q.x = path->coords[k];
1149
0
        p = fz_transform_point(q, ctm);
1150
0
        path->coords[k++] = p.y;
1151
0
        path->cmds[i] = FZ_VERTTO;
1152
0
        n = 0;
1153
0
        break;
1154
0
      case FZ_HORIZTOCLOSE:
1155
0
        q.x = path->coords[k];
1156
0
        p = fz_transform_point(q, ctm);
1157
0
        path->coords[k++] = p.y;
1158
0
        path->cmds[i] = FZ_VERTTOCLOSE;
1159
0
        n = 0;
1160
0
        break;
1161
0
      case FZ_VERTTO:
1162
0
        q.y = path->coords[k];
1163
0
        p = fz_transform_point(q, ctm);
1164
0
        path->coords[k++] = p.x;
1165
0
        path->cmds[i] = FZ_HORIZTO;
1166
0
        n = 0;
1167
0
        break;
1168
0
      case FZ_VERTTOCLOSE:
1169
0
        q.y = path->coords[k];
1170
0
        p = fz_transform_point(q, ctm);
1171
0
        path->coords[k++] = p.x;
1172
0
        path->cmds[i] = FZ_HORIZTOCLOSE;
1173
0
        n = 0;
1174
0
        break;
1175
0
      default:
1176
0
        assert("Unknown path cmd" == NULL);
1177
0
      }
1178
0
      while (n > 0)
1179
0
      {
1180
0
        q.x = path->coords[k];
1181
0
        q.y = path->coords[k+1];
1182
0
        p = fz_transform_point(q, ctm);
1183
0
        path->coords[k++] = p.x;
1184
0
        path->coords[k++] = p.y;
1185
0
        n--;
1186
0
      }
1187
0
      switch (cmd)
1188
0
      {
1189
0
      case FZ_MOVETO:
1190
0
      case FZ_MOVETOCLOSE:
1191
0
        s = q;
1192
0
        break;
1193
0
      case FZ_LINETOCLOSE:
1194
0
      case FZ_DEGENLINETOCLOSE:
1195
0
      case FZ_CURVETOCLOSE:
1196
0
      case FZ_CURVETOVCLOSE:
1197
0
      case FZ_CURVETOYCLOSE:
1198
0
      case FZ_QUADTOCLOSE:
1199
0
      case FZ_HORIZTOCLOSE:
1200
0
      case FZ_VERTTOCLOSE:
1201
0
      case FZ_RECTTO:
1202
0
        q = s;
1203
0
        break;
1204
0
      }
1205
0
      i++;
1206
0
    }
1207
0
  }
1208
0
  else
1209
0
  {
1210
0
    int extra_coord = 0;
1211
0
    int extra_cmd = 0;
1212
0
    int coord_read, coord_write, cmd_read, cmd_write;
1213
1214
    /* General case. Have to allow for rects/horiz/verts
1215
     * becoming non-rects/horiz/verts. */
1216
0
    for (i = 0; i < path->cmd_len; i++)
1217
0
    {
1218
0
      uint8_t cmd = path->cmds[i];
1219
0
      switch (cmd)
1220
0
      {
1221
0
      case FZ_HORIZTO:
1222
0
      case FZ_VERTTO:
1223
0
      case FZ_HORIZTOCLOSE:
1224
0
      case FZ_VERTTOCLOSE:
1225
0
        extra_coord += 1;
1226
0
        break;
1227
0
      case FZ_RECTTO:
1228
0
        extra_coord += 2;
1229
0
        extra_cmd += 3;
1230
0
        break;
1231
0
      default:
1232
        /* Do nothing */
1233
0
        break;
1234
0
      }
1235
0
    }
1236
0
    if (path->cmd_len + extra_cmd < path->cmd_cap)
1237
0
    {
1238
0
      path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len + extra_cmd, unsigned char);
1239
0
      path->cmd_cap = path->cmd_len + extra_cmd;
1240
0
    }
1241
0
    if (path->coord_len + extra_coord < path->coord_cap)
1242
0
    {
1243
0
      path->coords = fz_realloc_array(ctx, path->coords, path->coord_len + extra_coord, float);
1244
0
      path->coord_cap = path->coord_len + extra_coord;
1245
0
    }
1246
0
    memmove(path->cmds + extra_cmd, path->cmds, path->cmd_len * sizeof(unsigned char));
1247
0
    path->cmd_len += extra_cmd;
1248
0
    memmove(path->coords + extra_coord, path->coords, path->coord_len * sizeof(float));
1249
0
    path->coord_len += extra_coord;
1250
1251
0
    for (cmd_write = 0, cmd_read = extra_cmd, coord_write = 0, coord_read = extra_coord; cmd_read < path->cmd_len; i += 2)
1252
0
    {
1253
0
      uint8_t cmd = path->cmds[cmd_write++] = path->cmds[cmd_read++];
1254
1255
0
      switch (cmd)
1256
0
      {
1257
0
      case FZ_MOVETO:
1258
0
      case FZ_LINETO:
1259
0
      case FZ_MOVETOCLOSE:
1260
0
      case FZ_LINETOCLOSE:
1261
0
        n = 1;
1262
0
        break;
1263
0
      case FZ_DEGENLINETO:
1264
0
      case FZ_DEGENLINETOCLOSE:
1265
0
        n = 0;
1266
0
        break;
1267
0
      case FZ_CURVETO:
1268
0
      case FZ_CURVETOCLOSE:
1269
0
        n = 3;
1270
0
        break;
1271
0
      case FZ_CURVETOV:
1272
0
      case FZ_CURVETOY:
1273
0
      case FZ_QUADTO:
1274
0
      case FZ_CURVETOVCLOSE:
1275
0
      case FZ_CURVETOYCLOSE:
1276
0
      case FZ_QUADTOCLOSE:
1277
0
        n = 2;
1278
0
        break;
1279
0
      case FZ_RECTTO:
1280
0
        p.x = path->coords[coord_read++];
1281
0
        p.y = path->coords[coord_read++];
1282
0
        p2.x = path->coords[coord_read++];
1283
0
        p2.y = path->coords[coord_read++];
1284
0
        p1.x = p2.x;
1285
0
        p1.y = p.y;
1286
0
        p3.x = p.x;
1287
0
        p3.y = p2.y;
1288
0
        s = p;
1289
0
        p = fz_transform_point(p, ctm);
1290
0
        p1 = fz_transform_point(p1, ctm);
1291
0
        p2 = fz_transform_point(p2, ctm);
1292
0
        p3 = fz_transform_point(p3, ctm);
1293
0
        path->coords[coord_write++] = p.x;
1294
0
        path->coords[coord_write++] = p.y;
1295
0
        path->coords[coord_write++] = p1.x;
1296
0
        path->coords[coord_write++] = p1.y;
1297
0
        path->coords[coord_write++] = p2.x;
1298
0
        path->coords[coord_write++] = p2.y;
1299
0
        path->coords[coord_write++] = p3.x;
1300
0
        path->coords[coord_write++] = p3.y;
1301
0
        path->cmds[cmd_write-1] = FZ_MOVETO;
1302
0
        path->cmds[cmd_write++] = FZ_LINETO;
1303
0
        path->cmds[cmd_write++] = FZ_LINETO;
1304
0
        path->cmds[cmd_write++] = FZ_LINETOCLOSE;
1305
0
        n = 0;
1306
0
        break;
1307
0
      case FZ_HORIZTO:
1308
0
        q.x = path->coords[coord_read++];
1309
0
        p = fz_transform_point(q, ctm);
1310
0
        path->coords[coord_write++] = p.x;
1311
0
        path->coords[coord_write++] = p.y;
1312
0
        path->cmds[cmd_write-1] = FZ_LINETO;
1313
0
        n = 0;
1314
0
        break;
1315
0
      case FZ_HORIZTOCLOSE:
1316
0
        p.x = path->coords[coord_read++];
1317
0
        p.y = q.y;
1318
0
        p = fz_transform_point(p, ctm);
1319
0
        path->coords[coord_write++] = p.x;
1320
0
        path->coords[coord_write++] = p.y;
1321
0
        path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
1322
0
        q = s;
1323
0
        n = 0;
1324
0
        break;
1325
0
      case FZ_VERTTO:
1326
0
        q.y = path->coords[coord_read++];
1327
0
        p = fz_transform_point(q, ctm);
1328
0
        path->coords[coord_write++] = p.x;
1329
0
        path->coords[coord_write++] = p.y;
1330
0
        path->cmds[cmd_write-1] = FZ_LINETO;
1331
0
        n = 0;
1332
0
        break;
1333
0
      case FZ_VERTTOCLOSE:
1334
0
        p.x = q.x;
1335
0
        p.y = path->coords[coord_read++];
1336
0
        p = fz_transform_point(p, ctm);
1337
0
        path->coords[coord_write++] = p.x;
1338
0
        path->coords[coord_write++] = p.y;
1339
0
        path->cmds[cmd_write-1] = FZ_LINETOCLOSE;
1340
0
        q = s;
1341
0
        n = 0;
1342
0
        break;
1343
0
      default:
1344
0
        assert("Unknown path cmd" == NULL);
1345
0
      }
1346
0
      while (n > 0)
1347
0
      {
1348
0
        q.x = path->coords[coord_read++];
1349
0
        q.y = path->coords[coord_read++];
1350
0
        p = fz_transform_point(q, ctm);
1351
0
        path->coords[coord_write++] = p.x;
1352
0
        path->coords[coord_write++] = p.y;
1353
0
        n--;
1354
0
      }
1355
0
      switch (cmd)
1356
0
      {
1357
0
      case FZ_MOVETO:
1358
0
      case FZ_MOVETOCLOSE:
1359
0
        s = q;
1360
0
        break;
1361
0
      case FZ_LINETOCLOSE:
1362
0
      case FZ_DEGENLINETOCLOSE:
1363
0
      case FZ_CURVETOCLOSE:
1364
0
      case FZ_CURVETOYCLOSE:
1365
0
      case FZ_CURVETOVCLOSE:
1366
0
      case FZ_QUADTOCLOSE:
1367
0
      case FZ_HORIZTOCLOSE:
1368
0
      case FZ_VERTTOCLOSE:
1369
0
      case FZ_RECTTO:
1370
0
        q = s;
1371
0
        break;
1372
0
      }
1373
0
    }
1374
0
  }
1375
0
}
1376
1377
void fz_trim_path(fz_context *ctx, fz_path *path)
1378
0
{
1379
0
  if (path->packed)
1380
0
    fz_throw(ctx, FZ_ERROR_GENERIC, "Can't trim a packed path");
1381
0
  if (path->cmd_cap > path->cmd_len)
1382
0
  {
1383
0
    path->cmds = fz_realloc_array(ctx, path->cmds, path->cmd_len, unsigned char);
1384
0
    path->cmd_cap = path->cmd_len;
1385
0
  }
1386
0
  if (path->coord_cap > path->coord_len)
1387
0
  {
1388
0
    path->coords = fz_realloc_array(ctx, path->coords, path->coord_len, float);
1389
0
    path->coord_cap = path->coord_len;
1390
0
  }
1391
0
}
1392
1393
const fz_stroke_state fz_default_stroke_state = {
1394
  -2, /* -2 is the magic number we use when we have stroke states stored on the stack */
1395
  FZ_LINECAP_BUTT, FZ_LINECAP_BUTT, FZ_LINECAP_BUTT,
1396
  FZ_LINEJOIN_MITER,
1397
  1, 10,
1398
  0, 0, { 0 }
1399
};
1400
1401
fz_stroke_state *
1402
fz_keep_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
1403
702k
{
1404
702k
  fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
1405
1406
702k
  if (!stroke)
1407
0
    return NULL;
1408
1409
  /* -2 is the magic number we use when we have stroke states stored on the stack */
1410
702k
  if (stroke->refs == -2)
1411
0
    return fz_clone_stroke_state(ctx, stroke);
1412
1413
702k
  return fz_keep_imp(ctx, stroke, &stroke->refs);
1414
702k
}
1415
1416
void
1417
fz_drop_stroke_state(fz_context *ctx, const fz_stroke_state *strokec)
1418
751k
{
1419
751k
  fz_stroke_state *stroke = (fz_stroke_state *)strokec; /* Explicit cast away of const */
1420
1421
751k
  if (fz_drop_imp(ctx, stroke, &stroke->refs))
1422
132k
    fz_free(ctx, stroke);
1423
751k
}
1424
1425
fz_stroke_state *
1426
fz_new_stroke_state_with_dash_len(fz_context *ctx, int len)
1427
36.5k
{
1428
36.5k
  fz_stroke_state *state;
1429
1430
36.5k
  len -= nelem(state->dash_list);
1431
36.5k
  if (len < 0)
1432
36.5k
    len = 0;
1433
1434
36.5k
  state = Memento_label(fz_malloc(ctx, sizeof(*state) + sizeof(state->dash_list[0]) * len), "fz_stroke_state");
1435
36.5k
  state->refs = 1;
1436
36.5k
  state->start_cap = FZ_LINECAP_BUTT;
1437
36.5k
  state->dash_cap = FZ_LINECAP_BUTT;
1438
36.5k
  state->end_cap = FZ_LINECAP_BUTT;
1439
36.5k
  state->linejoin = FZ_LINEJOIN_MITER;
1440
36.5k
  state->linewidth = 1;
1441
36.5k
  state->miterlimit = 10;
1442
36.5k
  state->dash_phase = 0;
1443
36.5k
  state->dash_len = 0;
1444
36.5k
  memset(state->dash_list, 0, sizeof(state->dash_list[0]) * (len + nelem(state->dash_list)));
1445
1446
36.5k
  return state;
1447
36.5k
}
1448
1449
fz_stroke_state *
1450
fz_new_stroke_state(fz_context *ctx)
1451
36.5k
{
1452
36.5k
  return fz_new_stroke_state_with_dash_len(ctx, 0);
1453
36.5k
}
1454
1455
fz_stroke_state *
1456
fz_clone_stroke_state(fz_context *ctx, fz_stroke_state *stroke)
1457
0
{
1458
0
  fz_stroke_state *clone = fz_new_stroke_state_with_dash_len(ctx, stroke->dash_len);
1459
0
  int extra = stroke->dash_len - nelem(stroke->dash_list);
1460
0
  int size = sizeof(*stroke) + sizeof(stroke->dash_list[0]) * extra;
1461
0
  memcpy(clone, stroke, size);
1462
0
  clone->refs = 1;
1463
0
  return clone;
1464
0
}
1465
1466
fz_stroke_state *
1467
fz_unshare_stroke_state_with_dash_len(fz_context *ctx, fz_stroke_state *shared, int len)
1468
467k
{
1469
467k
  int single, unsize, shsize, shlen;
1470
467k
  fz_stroke_state *unshared;
1471
1472
467k
  fz_lock(ctx, FZ_LOCK_ALLOC);
1473
467k
  single = (shared->refs == 1);
1474
467k
  fz_unlock(ctx, FZ_LOCK_ALLOC);
1475
1476
467k
  shlen = shared->dash_len - nelem(shared->dash_list);
1477
467k
  if (shlen < 0)
1478
467k
    shlen = 0;
1479
467k
  shsize = sizeof(*shared) + sizeof(shared->dash_list[0]) * shlen;
1480
467k
  len -= nelem(shared->dash_list);
1481
467k
  if (len < 0)
1482
467k
    len = 0;
1483
467k
  if (single && shlen >= len)
1484
371k
    return shared;
1485
1486
96.0k
  unsize = sizeof(*unshared) + sizeof(unshared->dash_list[0]) * len;
1487
96.0k
  unshared = Memento_label(fz_malloc(ctx, unsize), "fz_stroke_state");
1488
96.0k
  memcpy(unshared, shared, (shsize > unsize ? unsize : shsize));
1489
96.0k
  unshared->refs = 1;
1490
1491
96.0k
  if (fz_drop_imp(ctx, shared, &shared->refs))
1492
95
    fz_free(ctx, shared);
1493
96.0k
  return unshared;
1494
467k
}
1495
1496
fz_stroke_state *
1497
fz_unshare_stroke_state(fz_context *ctx, fz_stroke_state *shared)
1498
395k
{
1499
395k
  return fz_unshare_stroke_state_with_dash_len(ctx, shared, shared->dash_len);
1500
395k
}
1501
1502
static void *
1503
clone_block(fz_context *ctx, void *block, size_t len)
1504
0
{
1505
0
  void *target;
1506
1507
0
  if (len == 0 || block == NULL)
1508
0
    return NULL;
1509
1510
0
  target = fz_malloc(ctx, len);
1511
0
  memcpy(target, block, len);
1512
0
  return target;
1513
0
}
1514
1515
fz_path *
1516
fz_clone_path(fz_context *ctx, fz_path *path)
1517
0
{
1518
0
  fz_path *new_path;
1519
1520
0
  assert(ctx != NULL);
1521
1522
0
  if (path == NULL)
1523
0
    return NULL;
1524
1525
0
  new_path = fz_malloc_struct(ctx, fz_path);
1526
0
  new_path->refs = 1;
1527
0
  new_path->packed = FZ_PATH_UNPACKED;
1528
0
  fz_try(ctx)
1529
0
  {
1530
0
    switch(path->packed)
1531
0
    {
1532
0
    case FZ_PATH_UNPACKED:
1533
0
    case FZ_PATH_PACKED_OPEN:
1534
0
      new_path->cmd_len = path->cmd_len;
1535
0
      new_path->cmd_cap = path->cmd_cap;
1536
0
      new_path->cmds = Memento_label(clone_block(ctx, path->cmds, path->cmd_cap), "path_cmds");
1537
0
      new_path->coord_len = path->coord_len;
1538
0
      new_path->coord_cap = path->coord_cap;
1539
0
      new_path->coords = Memento_label(clone_block(ctx, path->coords, sizeof(float)*path->coord_cap), "path_coords");
1540
0
      new_path->current = path->current;
1541
0
      new_path->begin = path->begin;
1542
0
      break;
1543
0
    case FZ_PATH_PACKED_FLAT:
1544
0
      {
1545
0
        uint8_t *data;
1546
0
        float *xy;
1547
0
        int i;
1548
0
        fz_packed_path *ppath = (fz_packed_path *)path;
1549
1550
0
        new_path->cmd_len = ppath->cmd_len;
1551
0
        new_path->cmd_cap = ppath->cmd_len;
1552
0
        new_path->coord_len = ppath->coord_len;
1553
0
        new_path->coord_cap = ppath->coord_len;
1554
0
        data = (uint8_t *)&ppath[1];
1555
0
        new_path->coords = Memento_label(clone_block(ctx, data, sizeof(float)*path->coord_cap), "path_coords");
1556
0
        data += sizeof(float) * path->coord_cap;
1557
0
        new_path->cmds = Memento_label(clone_block(ctx, data, path->cmd_cap), "path_cmds");
1558
0
        xy = new_path->coords;
1559
0
        for (i = 0; i < new_path->cmd_len; i++)
1560
0
        {
1561
0
          switch (new_path->cmds[i])
1562
0
          {
1563
0
          case FZ_MOVETOCLOSE:
1564
0
          case FZ_MOVETO:
1565
0
            new_path->current.x = *xy++;
1566
0
            new_path->current.y = *xy++;
1567
0
            new_path->begin.x = new_path->current.x;
1568
0
            new_path->begin.y = new_path->current.y;
1569
0
            break;
1570
0
          case FZ_CURVETO:
1571
0
            xy += 2;
1572
            /* fallthrough */
1573
0
          case FZ_CURVETOV:
1574
0
          case FZ_CURVETOY:
1575
0
          case FZ_QUADTO:
1576
            /* fallthrough */
1577
0
            xy += 2;
1578
0
          case FZ_LINETO:
1579
0
            new_path->current.x = *xy++;
1580
0
            new_path->current.y = *xy++;
1581
0
            break;
1582
0
          case FZ_DEGENLINETO:
1583
0
            break;
1584
0
          case FZ_HORIZTO:
1585
0
            new_path->current.x = *xy++;
1586
0
            break;
1587
0
          case FZ_VERTTO:
1588
0
            new_path->current.y = *xy++;
1589
0
            break;
1590
0
          case FZ_RECTTO:
1591
0
            xy += 2;
1592
0
            break;
1593
0
          case FZ_CURVETOCLOSE:
1594
0
            xy += 2;
1595
            /* fallthrough */
1596
0
          case FZ_CURVETOVCLOSE:
1597
0
          case FZ_CURVETOYCLOSE:
1598
0
          case FZ_QUADTOCLOSE:
1599
0
          case FZ_LINETOCLOSE:
1600
0
            xy++;
1601
            /* fallthrough */
1602
0
          case FZ_HORIZTOCLOSE:
1603
0
          case FZ_VERTTOCLOSE:
1604
0
            xy++;
1605
            /* fallthrough */
1606
0
          case FZ_DEGENLINETOCLOSE:
1607
0
            new_path->current.x = new_path->begin.x;
1608
0
            new_path->current.y = new_path->begin.y;
1609
0
            break;
1610
0
          }
1611
0
        }
1612
0
      }
1613
0
    default:
1614
0
      fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown packing method found in path");
1615
0
    }
1616
0
  }
1617
0
  fz_catch(ctx)
1618
0
  {
1619
0
    fz_free(ctx, new_path->coords);
1620
0
    fz_free(ctx, new_path->cmds);
1621
0
    fz_free(ctx, new_path);
1622
0
    fz_rethrow(ctx);
1623
0
  }
1624
0
  return new_path;
1625
0
}