Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "mupdf/fitz.h"
2
#include "draw-imp.h"
3
4
#define STACK_SIZE 96
5
6
/* Enable the following to attempt to support knockout and/or isolated
7
* blending groups. */
8
#define ATTEMPT_KNOCKOUT_AND_ISOLATED
9
10
/* Enable the following to help debug group blending. */
11
#undef DUMP_GROUP_BLENDS
12
13
/* Enable the following to help debug graphics stack pushes/pops */
14
#undef DUMP_STACK_CHANGES
15
16
typedef struct fz_draw_device_s fz_draw_device;
17
18
enum {
19
FZ_DRAWDEV_FLAGS_TYPE3 = 1,
20
};
21
22
typedef struct fz_draw_state_s fz_draw_state;
23
24
struct fz_draw_state_s {
25
fz_irect scissor;
26
fz_pixmap *dest;
27
fz_pixmap *mask;
28
fz_pixmap *shape;
29
int blendmode;
30
int luminosity;
31
int id;
32
float alpha;
33
fz_matrix ctm;
34
float xstep, ystep;
35
fz_irect area;
36
};
37
38
struct fz_draw_device_s
39
{
40
fz_device super;
41
fz_gel *gel;
42
int flags;
43
int top;
44
fz_scale_cache *cache_x;
45
fz_scale_cache *cache_y;
46
fz_draw_state *stack;
47
int stack_cap;
48
fz_draw_state init_stack[STACK_SIZE];
49
};
50
51
#ifdef DUMP_GROUP_BLENDS
52
static int group_dump_count = 0;
53
54
static void fz_dump_blend(fz_context *ctx, fz_pixmap *pix, const char *s)
55
{
56
char name[80];
57
58
if (!pix)
59
return;
60
61
sprintf(name, "dump%02d.png", group_dump_count);
62
if (s)
63
printf("%s%02d", s, group_dump_count);
64
group_dump_count++;
65
66
fz_write_png(ctx, pix, name, (pix->n > 1));
67
}
68
69
static void dump_spaces(int x, const char *s)
70
{
71
int i;
72
for (i = 0; i < x; i++)
73
printf(" ");
74
printf("%s", s);
75
}
76
77
#endif
78
79
#ifdef DUMP_STACK_CHANGES
80
#define STACK_PUSHED(A) stack_change(ctx, dev, ">" ## A)
81
#define STACK_POPPED(A) stack_change(ctx, dev, "<" ## A)
82
#define STACK_CONVERT(A) stack_change(ctx, dev, A)
83
84
static void stack_change(fz_context *ctx, fz_draw_device *dev, char *s)
85
{
86
int depth = dev->top;
87
int n;
88
89
if (*s != '<')
90
depth--;
91
n = depth;
92
while (n--)
93
fputc(' ', stderr);
94
fprintf(stderr, "%s (%d)\n", s, depth);
95
}
96
#else
97
#define STACK_PUSHED(A) do {} while (0)
98
#define STACK_POPPED(A) do {} while (0)
99
#define STACK_CONVERT(A) do {} while (0)
100
#endif
101
102
static void fz_grow_stack(fz_context *ctx, fz_draw_device *dev)
103
{
104
int max = dev->stack_cap * 2;
105
fz_draw_state *stack;
106
107
if (dev->stack == &dev->init_stack[0])
108
{
109
stack = Memento_label(fz_malloc_array(ctx, max, sizeof *stack), "draw device stack");
110
memcpy(stack, dev->stack, sizeof(*stack) * dev->stack_cap);
111
}
112
else
113
{
114
stack = fz_resize_array(ctx, dev->stack, max, sizeof(*stack));
115
}
116
dev->stack = stack;
117
dev->stack_cap = max;
118
}
119
120
/* 'Push' the stack. Returns a pointer to the current state, with state[1]
121
* already having been initialised to contain the same thing. Simply
122
* change any contents of state[1] that you want to and continue. */
123
static fz_draw_state *
124
push_stack(fz_context *ctx, fz_draw_device *dev)
125
{
126
fz_draw_state *state;
127
128
if (dev->top == dev->stack_cap-1)
129
fz_grow_stack(ctx, dev);
130
state = &dev->stack[dev->top];
131
dev->top++;
132
memcpy(&state[1], state, sizeof(*state));
133
return state;
134
}
135
136
static void emergency_pop_stack(fz_context *ctx, fz_draw_device *dev, fz_draw_state *state)
137
{
138
if (state[1].mask != state[0].mask)
139
fz_drop_pixmap(ctx, state[1].mask);
140
if (state[1].dest != state[0].dest)
141
fz_drop_pixmap(ctx, state[1].dest);
142
if (state[1].shape != state[0].shape)
143
fz_drop_pixmap(ctx, state[1].shape);
144
dev->top--;
145
STACK_POPPED("emergency");
146
fz_rethrow(ctx);
147
}
148
149
static fz_draw_state *
150
fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
151
{
152
fz_irect bbox;
153
fz_pixmap *dest, *shape;
154
fz_draw_state *state = &dev->stack[dev->top];
155
int isolated = state->blendmode & FZ_BLEND_ISOLATED;
156
157
if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0)
158
return state;
159
160
state = push_stack(ctx, dev);
161
STACK_PUSHED("knockout");
162
163
fz_pixmap_bbox(ctx, state->dest, &bbox);
164
fz_intersect_irect(&bbox, &state->scissor);
165
dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, &bbox);
166
167
if (isolated)
168
{
169
fz_clear_pixmap(ctx, dest);
170
}
171
else
172
{
173
/* Find the last but one destination to copy */
174
int i = dev->top-1; /* i = the one on entry (i.e. the last one) */
175
fz_pixmap *prev = state->dest;
176
while (i > 0)
177
{
178
prev = dev->stack[--i].dest;
179
if (prev != state->dest)
180
break;
181
}
182
if (prev)
183
fz_copy_pixmap_rect(ctx, dest, prev, &bbox);
184
else
185
fz_clear_pixmap(ctx, dest);
186
}
187
188
if ((state->blendmode & FZ_BLEND_MODEMASK) == 0 && isolated)
189
{
190
/* We can render direct to any existing shape plane. If there
191
* isn't one, we don't need to make one. */
192
shape = state->shape;
193
}
194
else
195
{
196
shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
197
fz_clear_pixmap(ctx, shape);
198
}
199
#ifdef DUMP_GROUP_BLENDS
200
dump_spaces(dev->top-1, "Knockout begin\n");
201
#endif
202
state[1].scissor = bbox;
203
state[1].dest = dest;
204
state[1].shape = shape;
205
state[1].blendmode &= ~FZ_BLEND_MODEMASK;
206
207
return &state[1];
208
}
209
210
static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
211
{
212
fz_draw_state *state;
213
int blendmode;
214
int isolated;
215
216
if (dev->top == 0)
217
{
218
fz_warn(ctx, "unexpected knockout end");
219
return;
220
}
221
state = &dev->stack[--dev->top];
222
STACK_POPPED("knockout");
223
if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0)
224
return;
225
226
blendmode = state->blendmode & FZ_BLEND_MODEMASK;
227
isolated = state->blendmode & FZ_BLEND_ISOLATED;
228
229
#ifdef DUMP_GROUP_BLENDS
230
dump_spaces(dev->top, "");
231
fz_dump_blend(ctx, state[1].dest, "Knockout end: blending ");
232
if (state[1].shape)
233
fz_dump_blend(ctx, state[1].shape, "/");
234
fz_dump_blend(ctx, state[0].dest, " onto ");
235
if (state[0].shape)
236
fz_dump_blend(ctx, state[0].shape, "/");
237
if (blendmode != 0)
238
printf(" (blend %d)", blendmode);
239
if (isolated != 0)
240
printf(" (isolated)");
241
printf(" (knockout)");
242
#endif
243
if ((blendmode == 0) && (state[0].shape == state[1].shape))
244
fz_paint_pixmap(state[0].dest, state[1].dest, 255);
245
else
246
fz_blend_pixmap(state[0].dest, state[1].dest, 255, blendmode, isolated, state[1].shape);
247
248
/* The following test should not be required, but just occasionally
249
* errors can cause the stack to get out of sync, and this saves our
250
* bacon. */
251
if (state[0].dest != state[1].dest)
252
fz_drop_pixmap(ctx, state[1].dest);
253
if (state[0].shape != state[1].shape)
254
{
255
if (state[0].shape)
256
fz_paint_pixmap(state[0].shape, state[1].shape, 255);
257
fz_drop_pixmap(ctx, state[1].shape);
258
}
259
#ifdef DUMP_GROUP_BLENDS
260
fz_dump_blend(ctx, state[0].dest, " to get ");
261
if (state[0].shape)
262
fz_dump_blend(ctx, state[0].shape, "/");
263
printf("\n");
264
#endif
265
}
266
267
static void
268
fz_draw_fill_path(fz_context *ctx, fz_device *devp, fz_path *path, int even_odd, const fz_matrix *ctm,
269
fz_colorspace *colorspace, float *color, float alpha)
270
{
271
fz_draw_device *dev = (fz_draw_device*)devp;
272
fz_gel *gel = dev->gel;
273
274
float expansion = fz_matrix_expansion(ctm);
275
float flatness = 0.3f / expansion;
276
unsigned char colorbv[FZ_MAX_COLORS + 1];
277
float colorfv[FZ_MAX_COLORS];
278
fz_irect bbox;
279
int i;
280
fz_draw_state *state = &dev->stack[dev->top];
281
fz_colorspace *model = state->dest->colorspace;
282
283
if (model == NULL)
284
model = fz_device_gray(ctx);
285
286
if (flatness < 0.001f)
287
flatness = 0.001f;
288
289
fz_reset_gel(ctx, gel, &state->scissor);
290
fz_flatten_fill_path(ctx, gel, path, ctm, flatness);
291
fz_sort_gel(ctx, gel);
292
293
fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor);
294
295
if (fz_is_empty_irect(&bbox))
296
return;
297
298
if (state->blendmode & FZ_BLEND_KNOCKOUT)
299
state = fz_knockout_begin(ctx, dev);
300
301
fz_convert_color(ctx, model, colorfv, colorspace, color);
302
for (i = 0; i < model->n; i++)
303
colorbv[i] = colorfv[i] * 255;
304
colorbv[i] = alpha * 255;
305
306
fz_scan_convert(ctx, gel, even_odd, &bbox, state->dest, colorbv);
307
if (state->shape)
308
{
309
fz_reset_gel(ctx, gel, &state->scissor);
310
fz_flatten_fill_path(ctx, gel, path, ctm, flatness);
311
fz_sort_gel(ctx, gel);
312
313
colorbv[0] = alpha * 255;
314
fz_scan_convert(ctx, gel, even_odd, &bbox, state->shape, colorbv);
315
}
316
317
if (state->blendmode & FZ_BLEND_KNOCKOUT)
318
fz_knockout_end(ctx, dev);
319
}
320
321
static void
322
fz_draw_stroke_path(fz_context *ctx, fz_device *devp, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm,
323
fz_colorspace *colorspace, float *color, float alpha)
324
{
325
fz_draw_device *dev = (fz_draw_device*)devp;
326
fz_gel *gel = dev->gel;
327
328
float expansion = fz_matrix_expansion(ctm);
329
float flatness = 0.3f / expansion;
330
float linewidth = stroke->linewidth;
331
unsigned char colorbv[FZ_MAX_COLORS + 1];
332
float colorfv[FZ_MAX_COLORS];
333
fz_irect bbox;
334
int i;
335
fz_draw_state *state = &dev->stack[dev->top];
336
fz_colorspace *model = state->dest->colorspace;
337
338
if (model == NULL)
339
model = fz_device_gray(ctx);
340
341
if (linewidth * expansion < 0.1f)
342
linewidth = 1 / expansion;
343
if (flatness < 0.001f)
344
flatness = 0.001f;
345
346
fz_reset_gel(ctx, gel, &state->scissor);
347
if (stroke->dash_len > 0)
348
fz_flatten_dash_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
349
else
350
fz_flatten_stroke_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
351
fz_sort_gel(ctx, gel);
352
353
fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor);
354
355
if (fz_is_empty_irect(&bbox))
356
return;
357
358
if (state->blendmode & FZ_BLEND_KNOCKOUT)
359
state = fz_knockout_begin(ctx, dev);
360
361
fz_convert_color(ctx, model, colorfv, colorspace, color);
362
for (i = 0; i < model->n; i++)
363
colorbv[i] = colorfv[i] * 255;
364
colorbv[i] = alpha * 255;
365
366
fz_scan_convert(ctx, gel, 0, &bbox, state->dest, colorbv);
367
if (state->shape)
368
{
369
fz_reset_gel(ctx, gel, &state->scissor);
370
if (stroke->dash_len > 0)
371
fz_flatten_dash_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
372
else
373
fz_flatten_stroke_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
374
fz_sort_gel(ctx, gel);
375
376
colorbv[0] = 255;
377
fz_scan_convert(ctx, gel, 0, &bbox, state->shape, colorbv);
378
}
379
380
if (state->blendmode & FZ_BLEND_KNOCKOUT)
381
fz_knockout_end(ctx, dev);
382
}
383
384
static void
385
fz_draw_clip_path(fz_context *ctx, fz_device *devp, fz_path *path, const fz_rect *rect, int even_odd, const fz_matrix *ctm)
386
{
387
fz_draw_device *dev = (fz_draw_device*)devp;
388
fz_gel *gel = dev->gel;
389
390
float expansion = fz_matrix_expansion(ctm);
391
float flatness = 0.3f / expansion;
392
fz_irect bbox;
393
fz_draw_state *state = &dev->stack[dev->top];
394
fz_colorspace *model;
395
396
if (flatness < 0.001f)
397
flatness = 0.001f;
398
399
fz_reset_gel(ctx, gel, &state->scissor);
400
fz_flatten_fill_path(ctx, gel, path, ctm, flatness);
401
fz_sort_gel(ctx, gel);
402
403
state = push_stack(ctx, dev);
404
STACK_PUSHED("clip path");
405
model = state->dest->colorspace;
406
407
fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor);
408
if (rect)
409
{
410
fz_irect bbox2;
411
fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect));
412
}
413
414
if (fz_is_empty_irect(&bbox) || fz_is_rect_gel(ctx, gel))
415
{
416
state[1].scissor = bbox;
417
state[1].mask = NULL;
418
#ifdef DUMP_GROUP_BLENDS
419
dump_spaces(dev->top-1, "Clip (rectangular) begin\n");
420
#endif
421
return;
422
}
423
424
fz_try(ctx)
425
{
426
state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
427
fz_clear_pixmap(ctx, state[1].mask);
428
state[1].dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
429
fz_clear_pixmap(ctx, state[1].dest);
430
if (state[1].shape)
431
{
432
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
433
fz_clear_pixmap(ctx, state[1].shape);
434
}
435
436
fz_scan_convert(ctx, gel, even_odd, &bbox, state[1].mask, NULL);
437
438
state[1].blendmode |= FZ_BLEND_ISOLATED;
439
state[1].scissor = bbox;
440
#ifdef DUMP_GROUP_BLENDS
441
dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n");
442
#endif
443
}
444
fz_catch(ctx)
445
{
446
emergency_pop_stack(ctx, dev, state);
447
}
448
}
449
450
static void
451
fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, fz_path *path, const fz_rect *rect, fz_stroke_state *stroke, const fz_matrix *ctm)
452
{
453
fz_draw_device *dev = (fz_draw_device*)devp;
454
fz_gel *gel = dev->gel;
455
456
float expansion = fz_matrix_expansion(ctm);
457
float flatness = 0.3f / expansion;
458
float linewidth = stroke->linewidth;
459
fz_irect bbox;
460
fz_draw_state *state = &dev->stack[dev->top];
461
fz_colorspace *model;
462
463
if (linewidth * expansion < 0.1f)
464
linewidth = 1 / expansion;
465
if (flatness < 0.001f)
466
flatness = 0.001f;
467
468
fz_reset_gel(ctx, gel, &state->scissor);
469
if (stroke->dash_len > 0)
470
fz_flatten_dash_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
471
else
472
fz_flatten_stroke_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
473
fz_sort_gel(ctx, gel);
474
475
state = push_stack(ctx, dev);
476
STACK_PUSHED("clip stroke");
477
model = state->dest->colorspace;
478
479
fz_intersect_irect(fz_bound_gel(ctx, gel, &bbox), &state->scissor);
480
if (rect)
481
{
482
fz_irect bbox2;
483
fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect));
484
}
485
486
fz_try(ctx)
487
{
488
state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
489
fz_clear_pixmap(ctx, state[1].mask);
490
state[1].dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
491
fz_clear_pixmap(ctx, state[1].dest);
492
if (state->shape)
493
{
494
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
495
fz_clear_pixmap(ctx, state[1].shape);
496
}
497
498
if (!fz_is_empty_irect(&bbox))
499
fz_scan_convert(ctx, gel, 0, &bbox, state[1].mask, NULL);
500
501
state[1].blendmode |= FZ_BLEND_ISOLATED;
502
state[1].scissor = bbox;
503
#ifdef DUMP_GROUP_BLENDS
504
dump_spaces(dev->top-1, "Clip (stroke) begin\n");
505
#endif
506
}
507
fz_catch(ctx)
508
{
509
emergency_pop_stack(ctx, dev, state);
510
}
511
}
512
513
static void
514
draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_glyph *glyph,
515
int xorig, int yorig, const fz_irect *scissor)
516
{
517
unsigned char *dp;
518
fz_irect bbox, bbox2;
519
int x, y, w, h;
520
int skip_x, skip_y;
521
fz_pixmap *msk;
522
523
fz_glyph_bbox_no_ctx(glyph, &bbox);
524
fz_translate_irect(&bbox, xorig, yorig);
525
fz_intersect_irect(&bbox, scissor); /* scissor < dst */
526
527
if (fz_is_empty_irect(fz_intersect_irect(&bbox, fz_pixmap_bbox_no_ctx(dst, &bbox2))))
528
return;
529
530
x = bbox.x0;
531
y = bbox.y0;
532
w = bbox.x1 - bbox.x0;
533
h = bbox.y1 - bbox.y0;
534
535
skip_x = x - glyph->x - xorig;
536
skip_y = y - glyph->y - yorig;
537
538
dp = dst->samples + (unsigned int)(((y - dst->y) * dst->w + (x - dst->x)) * dst->n);
539
540
msk = glyph->pixmap;
541
if (msk == NULL)
542
{
543
fz_paint_glyph(colorbv, dst, dp, glyph, w, h, skip_x, skip_y);
544
}
545
else
546
{
547
unsigned char *mp = msk->samples + skip_y * msk->w + skip_x;
548
while (h--)
549
{
550
if (dst->colorspace)
551
fz_paint_span_with_color(dp, mp, dst->n, w, colorbv);
552
else
553
fz_paint_span(dp, mp, 1, w, 255);
554
dp += dst->w * dst->n;
555
mp += msk->w;
556
}
557
}
558
}
559
560
static void
561
fz_draw_fill_text(fz_context *ctx, fz_device *devp, fz_text *text, const fz_matrix *ctm,
562
fz_colorspace *colorspace, float *color, float alpha)
563
{
564
fz_draw_device *dev = (fz_draw_device*)devp;
565
566
unsigned char colorbv[FZ_MAX_COLORS + 1];
567
unsigned char shapebv;
568
float colorfv[FZ_MAX_COLORS];
569
fz_matrix tm, trm;
570
fz_glyph *glyph;
571
int i, gid;
572
fz_draw_state *state = &dev->stack[dev->top];
573
fz_colorspace *model = state->dest->colorspace;
574
575
if (state->blendmode & FZ_BLEND_KNOCKOUT)
576
state = fz_knockout_begin(ctx, dev);
577
578
fz_convert_color(ctx, model, colorfv, colorspace, color);
579
for (i = 0; i < model->n; i++)
580
colorbv[i] = colorfv[i] * 255;
581
colorbv[i] = alpha * 255;
582
shapebv = 255;
583
584
tm = text->trm;
585
586
for (i = 0; i < text->len; i++)
587
{
588
gid = text->items[i].gid;
589
if (gid < 0)
590
continue;
591
592
tm.e = text->items[i].x;
593
tm.f = text->items[i].y;
594
fz_concat(&trm, &tm, ctm);
595
596
glyph = fz_render_glyph(ctx, text->font, gid, &trm, model, &state->scissor);
597
if (glyph)
598
{
599
fz_pixmap *pixmap = glyph->pixmap;
600
int x = floorf(trm.e);
601
int y = floorf(trm.f);
602
if (pixmap == NULL || pixmap->n == 1)
603
{
604
draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor);
605
if (state->shape)
606
draw_glyph(&shapebv, state->shape, glyph, x, y, &state->scissor);
607
}
608
else
609
{
610
fz_matrix mat;
611
mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
612
mat.e = x + pixmap->x; mat.f = y + pixmap->y;
613
fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES));
614
}
615
fz_drop_glyph(ctx, glyph);
616
}
617
else
618
{
619
fz_path *path = fz_outline_glyph(ctx, text->font, gid, &tm);
620
if (path)
621
{
622
fz_draw_fill_path(ctx, devp, path, 0, ctm, colorspace, color, alpha);
623
fz_drop_path(ctx, path);
624
}
625
else
626
{
627
fz_warn(ctx, "cannot render glyph");
628
}
629
}
630
}
631
632
if (state->blendmode & FZ_BLEND_KNOCKOUT)
633
fz_knockout_end(ctx, dev);
634
}
635
636
static void
637
fz_draw_stroke_text(fz_context *ctx, fz_device *devp, fz_text *text, fz_stroke_state *stroke,
638
const fz_matrix *ctm, fz_colorspace *colorspace,
639
float *color, float alpha)
640
{
641
fz_draw_device *dev = (fz_draw_device*)devp;
642
643
unsigned char colorbv[FZ_MAX_COLORS + 1];
644
float colorfv[FZ_MAX_COLORS];
645
fz_matrix tm, trm;
646
fz_glyph *glyph;
647
int i, gid;
648
fz_draw_state *state = &dev->stack[dev->top];
649
fz_colorspace *model = state->dest->colorspace;
650
651
if (state->blendmode & FZ_BLEND_KNOCKOUT)
652
state = fz_knockout_begin(ctx, dev);
653
654
fz_convert_color(ctx, model, colorfv, colorspace, color);
655
for (i = 0; i < model->n; i++)
656
colorbv[i] = colorfv[i] * 255;
657
colorbv[i] = alpha * 255;
658
659
tm = text->trm;
660
661
for (i = 0; i < text->len; i++)
662
{
663
gid = text->items[i].gid;
664
if (gid < 0)
665
continue;
666
667
tm.e = text->items[i].x;
668
tm.f = text->items[i].y;
669
fz_concat(&trm, &tm, ctm);
670
671
glyph = fz_render_stroked_glyph(ctx, text->font, gid, &trm, ctm, stroke, &state->scissor);
672
if (glyph)
673
{
674
int x = (int)trm.e;
675
int y = (int)trm.f;
676
draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor);
677
if (state->shape)
678
draw_glyph(colorbv, state->shape, glyph, x, y, &state->scissor);
679
fz_drop_glyph(ctx, glyph);
680
}
681
else
682
{
683
fz_path *path = fz_outline_glyph(ctx, text->font, gid, &tm);
684
if (path)
685
{
686
fz_draw_stroke_path(ctx, devp, path, stroke, ctm, colorspace, color, alpha);
687
fz_drop_path(ctx, path);
688
}
689
else
690
{
691
fz_warn(ctx, "cannot render glyph");
692
}
693
}
694
}
695
696
if (state->blendmode & FZ_BLEND_KNOCKOUT)
697
fz_knockout_end(ctx, dev);
698
}
699
700
static void
701
fz_draw_clip_text(fz_context *ctx, fz_device *devp, fz_text *text, const fz_matrix *ctm, int accumulate)
702
{
703
fz_draw_device *dev = (fz_draw_device*)devp;
704
fz_irect bbox;
705
fz_pixmap *mask, *dest, *shape;
706
fz_matrix tm, trm;
707
fz_glyph *glyph;
708
int i, gid;
709
fz_draw_state *state;
710
fz_colorspace *model;
711
712
/* If accumulate == 0 then this text object is guaranteed complete */
713
/* If accumulate == 1 then this text object is the first (or only) in a sequence */
714
/* If accumulate == 2 then this text object is a continuation */
715
716
state = push_stack(ctx, dev);
717
STACK_PUSHED("clip text");
718
model = state->dest->colorspace;
719
720
if (accumulate == 0)
721
{
722
/* make the mask the exact size needed */
723
fz_rect rect;
724
725
fz_irect_from_rect(&bbox, fz_bound_text(ctx, text, NULL, ctm, &rect));
726
fz_intersect_irect(&bbox, &state->scissor);
727
}
728
else
729
{
730
/* be conservative about the size of the mask needed */
731
bbox = state->scissor;
732
}
733
734
fz_try(ctx)
735
{
736
if (accumulate == 0 || accumulate == 1)
737
{
738
mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
739
fz_clear_pixmap(ctx, mask);
740
dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
741
fz_clear_pixmap(ctx, dest);
742
if (state->shape)
743
{
744
shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
745
fz_clear_pixmap(ctx, shape);
746
}
747
else
748
shape = NULL;
749
750
state[1].blendmode |= FZ_BLEND_ISOLATED;
751
state[1].scissor = bbox;
752
state[1].dest = dest;
753
state[1].mask = mask;
754
state[1].shape = shape;
755
#ifdef DUMP_GROUP_BLENDS
756
dump_spaces(dev->top-1, "Clip (text) begin\n");
757
#endif
758
}
759
else
760
{
761
mask = state->mask;
762
dev->top--;
763
STACK_POPPED("clip text");
764
}
765
766
if (!fz_is_empty_irect(&bbox) && mask)
767
{
768
tm = text->trm;
769
770
for (i = 0; i < text->len; i++)
771
{
772
gid = text->items[i].gid;
773
if (gid < 0)
774
continue;
775
776
tm.e = text->items[i].x;
777
tm.f = text->items[i].y;
778
fz_concat(&trm, &tm, ctm);
779
780
glyph = fz_render_glyph(ctx, text->font, gid, &trm, model, &state->scissor);
781
if (glyph)
782
{
783
int x = (int)trm.e;
784
int y = (int)trm.f;
785
draw_glyph(NULL, mask, glyph, x, y, &bbox);
786
if (state[1].shape)
787
draw_glyph(NULL, state[1].shape, glyph, x, y, &bbox);
788
fz_drop_glyph(ctx, glyph);
789
}
790
else
791
{
792
fz_path *path = fz_outline_glyph(ctx, text->font, gid, &tm);
793
if (path)
794
{
795
fz_pixmap *old_dest;
796
float white = 1;
797
798
old_dest = state[1].dest;
799
state[1].dest = state[1].mask;
800
state[1].mask = NULL;
801
fz_try(ctx)
802
{
803
fz_draw_fill_path(ctx, devp, path, 0, ctm, fz_device_gray(ctx), &white, 1);
804
}
805
fz_always(ctx)
806
{
807
state[1].mask = state[1].dest;
808
state[1].dest = old_dest;
809
fz_drop_path(ctx, path);
810
}
811
fz_catch(ctx)
812
{
813
fz_rethrow(ctx);
814
}
815
}
816
else
817
{
818
fz_warn(ctx, "cannot render glyph for clipping");
819
}
820
}
821
}
822
}
823
}
824
fz_catch(ctx)
825
{
826
if (accumulate == 0 || accumulate == 1)
827
emergency_pop_stack(ctx, dev, state);
828
fz_rethrow(ctx);
829
}
830
}
831
832
static void
833
fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm)
834
{
835
fz_draw_device *dev = (fz_draw_device*)devp;
836
fz_irect bbox;
837
fz_pixmap *mask, *dest, *shape;
838
fz_matrix tm, trm;
839
fz_glyph *glyph;
840
int i, gid;
841
fz_draw_state *state = push_stack(ctx, dev);
842
fz_colorspace *model = state->dest->colorspace;
843
fz_rect rect;
844
845
STACK_PUSHED("clip stroke text");
846
/* make the mask the exact size needed */
847
fz_irect_from_rect(&bbox, fz_bound_text(ctx, text, stroke, ctm, &rect));
848
fz_intersect_irect(&bbox, &state->scissor);
849
850
fz_try(ctx)
851
{
852
state[1].mask = mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
853
fz_clear_pixmap(ctx, mask);
854
state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
855
fz_clear_pixmap(ctx, dest);
856
if (state->shape)
857
{
858
state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
859
fz_clear_pixmap(ctx, shape);
860
}
861
else
862
shape = state->shape;
863
864
state[1].blendmode |= FZ_BLEND_ISOLATED;
865
state[1].scissor = bbox;
866
#ifdef DUMP_GROUP_BLENDS
867
dump_spaces(dev->top-1, "Clip (stroke text) begin\n");
868
#endif
869
870
if (!fz_is_empty_irect(&bbox))
871
{
872
tm = text->trm;
873
874
for (i = 0; i < text->len; i++)
875
{
876
gid = text->items[i].gid;
877
if (gid < 0)
878
continue;
879
880
tm.e = text->items[i].x;
881
tm.f = text->items[i].y;
882
fz_concat(&trm, &tm, ctm);
883
884
glyph = fz_render_stroked_glyph(ctx, text->font, gid, &trm, ctm, stroke, &state->scissor);
885
if (glyph)
886
{
887
int x = (int)trm.e;
888
int y = (int)trm.f;
889
draw_glyph(NULL, mask, glyph, x, y, &bbox);
890
if (shape)
891
draw_glyph(NULL, shape, glyph, x, y, &bbox);
892
fz_drop_glyph(ctx, glyph);
893
}
894
else
895
{
896
fz_path *path = fz_outline_glyph(ctx, text->font, gid, &tm);
897
if (path)
898
{
899
fz_pixmap *old_dest;
900
float white = 1;
901
902
state = &dev->stack[dev->top];
903
old_dest = state[0].dest;
904
state[0].dest = state[0].mask;
905
state[0].mask = NULL;
906
fz_try(ctx)
907
{
908
fz_draw_stroke_path(ctx, devp, path, stroke, ctm, fz_device_gray(ctx), &white, 1);
909
}
910
fz_always(ctx)
911
{
912
state[0].mask = state[0].dest;
913
state[0].dest = old_dest;
914
fz_drop_path(ctx, path);
915
}
916
fz_catch(ctx)
917
{
918
fz_rethrow(ctx);
919
}
920
}
921
else
922
{
923
fz_warn(ctx, "cannot render glyph for stroked clipping");
924
}
925
}
926
}
927
}
928
}
929
fz_catch(ctx)
930
{
931
emergency_pop_stack(ctx, dev, state);
932
}
933
}
934
935
static void
936
fz_draw_ignore_text(fz_context *ctx, fz_device *dev, fz_text *text, const fz_matrix *ctm)
937
{
938
}
939
940
static void
941
fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_matrix *ctm, float alpha)
942
{
943
fz_draw_device *dev = (fz_draw_device*)devp;
944
fz_rect bounds;
945
fz_irect bbox, scissor;
946
fz_pixmap *dest, *shape;
947
float colorfv[FZ_MAX_COLORS];
948
unsigned char colorbv[FZ_MAX_COLORS + 1];
949
fz_draw_state *state = &dev->stack[dev->top];
950
fz_colorspace *model = state->dest->colorspace;
951
952
fz_bound_shade(ctx, shade, ctm, &bounds);
953
scissor = state->scissor;
954
fz_intersect_irect(fz_irect_from_rect(&bbox, &bounds), &scissor);
955
956
if (fz_is_empty_irect(&bbox))
957
return;
958
959
if (!model)
960
{
961
fz_warn(ctx, "cannot render shading directly to an alpha mask");
962
return;
963
}
964
965
if (state->blendmode & FZ_BLEND_KNOCKOUT)
966
state = fz_knockout_begin(ctx, dev);
967
968
dest = state->dest;
969
shape = state->shape;
970
971
if (alpha < 1)
972
{
973
dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, &bbox);
974
fz_clear_pixmap(ctx, dest);
975
if (shape)
976
{
977
shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
978
fz_clear_pixmap(ctx, shape);
979
}
980
}
981
982
if (shade->use_background)
983
{
984
unsigned char *s;
985
int x, y, n, i;
986
fz_convert_color(ctx, model, colorfv, shade->colorspace, shade->background);
987
for (i = 0; i < model->n; i++)
988
colorbv[i] = colorfv[i] * 255;
989
colorbv[i] = 255;
990
991
n = dest->n;
992
for (y = scissor.y0; y < scissor.y1; y++)
993
{
994
s = dest->samples + (unsigned int)(((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n);
995
for (x = scissor.x0; x < scissor.x1; x++)
996
{
997
for (i = 0; i < n; i++)
998
*s++ = colorbv[i];
999
}
1000
}
1001
if (shape)
1002
{
1003
for (y = scissor.y0; y < scissor.y1; y++)
1004
{
1005
s = shape->samples + (unsigned int)((scissor.x0 - shape->x) + (y - shape->y) * shape->w);
1006
for (x = scissor.x0; x < scissor.x1; x++)
1007
{
1008
*s++ = 255;
1009
}
1010
}
1011
}
1012
}
1013
1014
fz_paint_shade(ctx, shade, ctm, dest, &bbox);
1015
if (shape)
1016
fz_clear_pixmap_rect_with_value(ctx, shape, 255, &bbox);
1017
1018
if (alpha < 1)
1019
{
1020
fz_paint_pixmap(state->dest, dest, alpha * 255);
1021
fz_drop_pixmap(ctx, dest);
1022
if (shape)
1023
{
1024
fz_paint_pixmap(state->shape, shape, alpha * 255);
1025
fz_drop_pixmap(ctx, shape);
1026
}
1027
}
1028
1029
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1030
fz_knockout_end(ctx, dev);
1031
}
1032
1033
static fz_pixmap *
1034
fz_transform_pixmap(fz_context *ctx, fz_draw_device *dev, fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit, const fz_irect *clip)
1035
{
1036
fz_pixmap *scaled;
1037
1038
if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
1039
{
1040
/* Unrotated or X-flip or Y-flip or XY-flip */
1041
fz_matrix m = *ctm;
1042
if (gridfit)
1043
fz_gridfit_matrix(&m);
1044
scaled = fz_scale_pixmap_cached(ctx, image, m.e, m.f, m.a, m.d, clip, dev->cache_x, dev->cache_y);
1045
if (!scaled)
1046
return NULL;
1047
ctm->a = scaled->w;
1048
ctm->d = scaled->h;
1049
ctm->e = scaled->x;
1050
ctm->f = scaled->y;
1051
return scaled;
1052
}
1053
1054
if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
1055
{
1056
/* Other orthogonal flip/rotation cases */
1057
fz_matrix m = *ctm;
1058
fz_irect rclip;
1059
if (gridfit)
1060
fz_gridfit_matrix(&m);
1061
if (clip)
1062
{
1063
rclip.x0 = clip->y0;
1064
rclip.y0 = clip->x0;
1065
rclip.x1 = clip->y1;
1066
rclip.y1 = clip->x1;
1067
}
1068
scaled = fz_scale_pixmap_cached(ctx, image, m.f, m.e, m.b, m.c, (clip ? &rclip : NULL), dev->cache_x, dev->cache_y);
1069
if (!scaled)
1070
return NULL;
1071
ctm->b = scaled->w;
1072
ctm->c = scaled->h;
1073
ctm->f = scaled->x;
1074
ctm->e = scaled->y;
1075
return scaled;
1076
}
1077
1078
/* Downscale, non rectilinear case */
1079
if (dx > 0 && dy > 0)
1080
{
1081
scaled = fz_scale_pixmap_cached(ctx, image, 0, 0, (float)dx, (float)dy, NULL, dev->cache_x, dev->cache_y);
1082
return scaled;
1083
}
1084
1085
return NULL;
1086
}
1087
1088
static void
1089
fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_matrix *ctm, float alpha)
1090
{
1091
fz_draw_device *dev = (fz_draw_device*)devp;
1092
fz_pixmap *converted = NULL;
1093
fz_pixmap *scaled = NULL;
1094
fz_pixmap *pixmap;
1095
fz_pixmap *orig_pixmap;
1096
int after;
1097
int dx, dy;
1098
fz_draw_state *state = &dev->stack[dev->top];
1099
fz_colorspace *model = state->dest->colorspace;
1100
fz_irect clip;
1101
fz_matrix local_ctm = *ctm;
1102
1103
fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest, &clip), &state->scissor);
1104
1105
fz_var(scaled);
1106
1107
if (!model)
1108
{
1109
fz_warn(ctx, "cannot render image directly to an alpha mask");
1110
return;
1111
}
1112
1113
if (image->w == 0 || image->h == 0)
1114
return;
1115
1116
dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
1117
dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
1118
1119
pixmap = fz_new_pixmap_from_image(ctx, image, dx, dy);
1120
orig_pixmap = pixmap;
1121
1122
/* convert images with more components (cmyk->rgb) before scaling */
1123
/* convert images with fewer components (gray->rgb after scaling */
1124
/* convert images with expensive colorspace transforms after scaling */
1125
1126
fz_try(ctx)
1127
{
1128
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1129
state = fz_knockout_begin(ctx, dev);
1130
1131
after = 0;
1132
if (pixmap->colorspace == fz_device_gray(ctx))
1133
after = 1;
1134
1135
if (pixmap->colorspace != model && !after)
1136
{
1137
fz_irect bbox;
1138
fz_pixmap_bbox(ctx, pixmap, &bbox);
1139
converted = fz_new_pixmap_with_bbox(ctx, model, &bbox);
1140
fz_convert_pixmap(ctx, converted, pixmap);
1141
pixmap = converted;
1142
}
1143
1144
if (dx < pixmap->w && dy < pixmap->h && !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES))
1145
{
1146
int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1147
scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1148
if (!scaled)
1149
{
1150
if (dx < 1)
1151
dx = 1;
1152
if (dy < 1)
1153
dy = 1;
1154
scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1155
}
1156
if (scaled)
1157
pixmap = scaled;
1158
}
1159
1160
if (pixmap->colorspace != model)
1161
{
1162
if ((pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) ||
1163
(pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_bgr(ctx)))
1164
{
1165
/* We have special case rendering code for gray -> rgb/bgr */
1166
}
1167
else
1168
{
1169
fz_irect bbox;
1170
fz_pixmap_bbox(ctx, pixmap, &bbox);
1171
converted = fz_new_pixmap_with_bbox(ctx, model, &bbox);
1172
fz_convert_pixmap(ctx, converted, pixmap);
1173
pixmap = converted;
1174
}
1175
}
1176
1177
fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES));
1178
1179
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1180
fz_knockout_end(ctx, dev);
1181
}
1182
fz_always(ctx)
1183
{
1184
fz_drop_pixmap(ctx, scaled);
1185
fz_drop_pixmap(ctx, converted);
1186
fz_drop_pixmap(ctx, orig_pixmap);
1187
}
1188
fz_catch(ctx)
1189
{
1190
fz_rethrow(ctx);
1191
}
1192
}
1193
1194
static void
1195
fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const fz_matrix *ctm,
1196
fz_colorspace *colorspace, float *color, float alpha)
1197
{
1198
fz_draw_device *dev = (fz_draw_device*)devp;
1199
unsigned char colorbv[FZ_MAX_COLORS + 1];
1200
float colorfv[FZ_MAX_COLORS];
1201
fz_pixmap *scaled = NULL;
1202
fz_pixmap *pixmap;
1203
fz_pixmap *orig_pixmap;
1204
int dx, dy;
1205
int i;
1206
fz_draw_state *state = &dev->stack[dev->top];
1207
fz_colorspace *model = state->dest->colorspace;
1208
fz_irect clip;
1209
fz_matrix local_ctm = *ctm;
1210
1211
fz_pixmap_bbox(ctx, state->dest, &clip);
1212
fz_intersect_irect(&clip, &state->scissor);
1213
1214
if (image->w == 0 || image->h == 0)
1215
return;
1216
1217
dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
1218
dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
1219
pixmap = fz_new_pixmap_from_image(ctx, image, dx, dy);
1220
orig_pixmap = pixmap;
1221
1222
fz_try(ctx)
1223
{
1224
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1225
state = fz_knockout_begin(ctx, dev);
1226
1227
if (dx < pixmap->w && dy < pixmap->h)
1228
{
1229
int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1230
scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1231
if (!scaled)
1232
{
1233
if (dx < 1)
1234
dx = 1;
1235
if (dy < 1)
1236
dy = 1;
1237
scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1238
}
1239
if (scaled)
1240
pixmap = scaled;
1241
}
1242
1243
fz_convert_color(ctx, model, colorfv, colorspace, color);
1244
for (i = 0; i < model->n; i++)
1245
colorbv[i] = colorfv[i] * 255;
1246
colorbv[i] = alpha * 255;
1247
1248
fz_paint_image_with_color(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES));
1249
1250
if (scaled)
1251
fz_drop_pixmap(ctx, scaled);
1252
1253
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1254
fz_knockout_end(ctx, dev);
1255
}
1256
fz_always(ctx)
1257
{
1258
fz_drop_pixmap(ctx, orig_pixmap);
1259
}
1260
fz_catch(ctx)
1261
{
1262
fz_rethrow(ctx);
1263
}
1264
}
1265
1266
static void
1267
fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const fz_rect *rect, const fz_matrix *ctm)
1268
{
1269
fz_draw_device *dev = (fz_draw_device*)devp;
1270
fz_irect bbox;
1271
fz_pixmap *mask = NULL;
1272
fz_pixmap *dest = NULL;
1273
fz_pixmap *shape = NULL;
1274
fz_pixmap *scaled = NULL;
1275
fz_pixmap *pixmap = NULL;
1276
fz_pixmap *orig_pixmap = NULL;
1277
int dx, dy;
1278
fz_draw_state *state = push_stack(ctx, dev);
1279
fz_colorspace *model = state->dest->colorspace;
1280
fz_irect clip;
1281
fz_matrix local_ctm = *ctm;
1282
fz_rect urect;
1283
1284
STACK_PUSHED("clip image mask");
1285
fz_pixmap_bbox(ctx, state->dest, &clip);
1286
fz_intersect_irect(&clip, &state->scissor);
1287
1288
fz_var(mask);
1289
fz_var(dest);
1290
fz_var(shape);
1291
fz_var(pixmap);
1292
fz_var(orig_pixmap);
1293
1294
if (image->w == 0 || image->h == 0)
1295
{
1296
#ifdef DUMP_GROUP_BLENDS
1297
dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n");
1298
#endif
1299
state[1].scissor = fz_empty_irect;
1300
state[1].mask = NULL;
1301
return;
1302
}
1303
1304
#ifdef DUMP_GROUP_BLENDS
1305
dump_spaces(dev->top-1, "Clip (image mask) begin\n");
1306
#endif
1307
1308
urect = fz_unit_rect;
1309
fz_irect_from_rect(&bbox, fz_transform_rect(&urect, &local_ctm));
1310
fz_intersect_irect(&bbox, &state->scissor);
1311
if (rect)
1312
{
1313
fz_irect bbox2;
1314
fz_intersect_irect(&bbox, fz_irect_from_rect(&bbox2, rect));
1315
}
1316
1317
dx = sqrtf(local_ctm.a * local_ctm.a + local_ctm.b * local_ctm.b);
1318
dy = sqrtf(local_ctm.c * local_ctm.c + local_ctm.d * local_ctm.d);
1319
1320
fz_try(ctx)
1321
{
1322
pixmap = fz_new_pixmap_from_image(ctx, image, dx, dy);
1323
orig_pixmap = pixmap;
1324
1325
state[1].mask = mask = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
1326
fz_clear_pixmap(ctx, mask);
1327
1328
state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
1329
fz_clear_pixmap(ctx, dest);
1330
if (state->shape)
1331
{
1332
state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
1333
fz_clear_pixmap(ctx, shape);
1334
}
1335
1336
state[1].blendmode |= FZ_BLEND_ISOLATED;
1337
state[1].scissor = bbox;
1338
1339
if (dx < pixmap->w && dy < pixmap->h)
1340
{
1341
int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1342
scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1343
if (!scaled)
1344
{
1345
if (dx < 1)
1346
dx = 1;
1347
if (dy < 1)
1348
dy = 1;
1349
scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1350
}
1351
if (scaled)
1352
pixmap = scaled;
1353
}
1354
fz_paint_image(mask, &bbox, state->shape, pixmap, &local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES));
1355
}
1356
fz_always(ctx)
1357
{
1358
fz_drop_pixmap(ctx, scaled);
1359
fz_drop_pixmap(ctx, orig_pixmap);
1360
}
1361
fz_catch(ctx)
1362
{
1363
emergency_pop_stack(ctx, dev, state);
1364
}
1365
}
1366
1367
static void
1368
fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
1369
{
1370
fz_draw_device *dev = (fz_draw_device*)devp;
1371
fz_draw_state *state;
1372
1373
if (dev->top == 0)
1374
{
1375
fz_warn(ctx, "Unexpected pop clip");
1376
return;
1377
}
1378
state = &dev->stack[--dev->top];
1379
STACK_POPPED("clip");
1380
1381
/* We can get here with state[1].mask == NULL if the clipping actually
1382
* resolved to a rectangle earlier.
1383
*/
1384
if (state[1].mask)
1385
{
1386
#ifdef DUMP_GROUP_BLENDS
1387
dump_spaces(dev->top, "");
1388
fz_dump_blend(ctx, state[1].dest, "Clipping ");
1389
if (state[1].shape)
1390
fz_dump_blend(ctx, state[1].shape, "/");
1391
fz_dump_blend(ctx, state[0].dest, " onto ");
1392
if (state[0].shape)
1393
fz_dump_blend(ctx, state[0].shape, "/");
1394
fz_dump_blend(ctx, state[1].mask, " with ");
1395
#endif
1396
fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask);
1397
if (state[0].shape != state[1].shape)
1398
{
1399
fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask);
1400
fz_drop_pixmap(ctx, state[1].shape);
1401
}
1402
/* The following tests should not be required, but just occasionally
1403
* errors can cause the stack to get out of sync, and this might save
1404
* our bacon. */
1405
if (state[0].mask != state[1].mask)
1406
fz_drop_pixmap(ctx, state[1].mask);
1407
if (state[0].dest != state[1].dest)
1408
fz_drop_pixmap(ctx, state[1].dest);
1409
#ifdef DUMP_GROUP_BLENDS
1410
fz_dump_blend(ctx, state[0].dest, " to get ");
1411
if (state[0].shape)
1412
fz_dump_blend(ctx, state[0].shape, "/");
1413
printf("\n");
1414
#endif
1415
}
1416
else
1417
{
1418
#ifdef DUMP_GROUP_BLENDS
1419
dump_spaces(dev->top, "Clip end\n");
1420
#endif
1421
}
1422
}
1423
1424
static void
1425
fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, float *colorfv)
1426
{
1427
fz_draw_device *dev = (fz_draw_device*)devp;
1428
fz_pixmap *dest;
1429
fz_irect bbox;
1430
fz_draw_state *state = push_stack(ctx, dev);
1431
fz_pixmap *shape = state->shape;
1432
1433
STACK_PUSHED("mask");
1434
fz_intersect_irect(fz_irect_from_rect(&bbox, rect), &state->scissor);
1435
1436
fz_try(ctx)
1437
{
1438
state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), &bbox);
1439
if (state->shape)
1440
{
1441
/* FIXME: If we ever want to support AIS true, then
1442
* we probably want to create a shape pixmap here,
1443
* using: shape = fz_new_pixmap_with_bbox(NULL, bbox);
1444
* then, in the end_mask code, we create the mask
1445
* from this rather than dest.
1446
*/
1447
state[1].shape = shape = NULL;
1448
}
1449
1450
if (luminosity)
1451
{
1452
float bc;
1453
if (!colorspace)
1454
colorspace = fz_device_gray(ctx);
1455
fz_convert_color(ctx, fz_device_gray(ctx), &bc, colorspace, colorfv);
1456
fz_clear_pixmap_with_value(ctx, dest, bc * 255);
1457
if (shape)
1458
fz_clear_pixmap_with_value(ctx, shape, 255);
1459
}
1460
else
1461
{
1462
fz_clear_pixmap(ctx, dest);
1463
if (shape)
1464
fz_clear_pixmap(ctx, shape);
1465
}
1466
1467
#ifdef DUMP_GROUP_BLENDS
1468
dump_spaces(dev->top-1, "Mask begin\n");
1469
#endif
1470
state[1].scissor = bbox;
1471
state[1].luminosity = luminosity;
1472
}
1473
fz_catch(ctx)
1474
{
1475
emergency_pop_stack(ctx, dev, state);
1476
}
1477
}
1478
1479
static void
1480
fz_draw_end_mask(fz_context *ctx, fz_device *devp)
1481
{
1482
fz_draw_device *dev = (fz_draw_device*)devp;
1483
fz_pixmap *temp, *dest;
1484
fz_irect bbox;
1485
int luminosity;
1486
fz_draw_state *state;
1487
1488
if (dev->top == 0)
1489
{
1490
fz_warn(ctx, "Unexpected draw_end_mask");
1491
return;
1492
}
1493
state = &dev->stack[dev->top-1];
1494
STACK_CONVERT("(mask)");
1495
/* pop soft mask buffer */
1496
luminosity = state[1].luminosity;
1497
1498
#ifdef DUMP_GROUP_BLENDS
1499
dump_spaces(dev->top-1, "Mask -> Clip\n");
1500
#endif
1501
fz_try(ctx)
1502
{
1503
/* convert to alpha mask */
1504
temp = fz_alpha_from_gray(ctx, state[1].dest, luminosity);
1505
if (state[1].mask != state[0].mask)
1506
fz_drop_pixmap(ctx, state[1].mask);
1507
state[1].mask = temp;
1508
if (state[1].dest != state[0].dest)
1509
fz_drop_pixmap(ctx, state[1].dest);
1510
state[1].dest = NULL;
1511
if (state[1].shape != state[0].shape)
1512
fz_drop_pixmap(ctx, state[1].shape);
1513
state[1].shape = NULL;
1514
1515
/* create new dest scratch buffer */
1516
fz_pixmap_bbox(ctx, temp, &bbox);
1517
dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, &bbox);
1518
fz_clear_pixmap(ctx, dest);
1519
1520
/* push soft mask as clip mask */
1521
state[1].dest = dest;
1522
state[1].blendmode |= FZ_BLEND_ISOLATED;
1523
/* If we have a shape, then it'll need to be masked with the
1524
* clip mask when we pop. So create a new shape now. */
1525
if (state[0].shape)
1526
{
1527
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
1528
fz_clear_pixmap(ctx, state[1].shape);
1529
}
1530
state[1].scissor = bbox;
1531
}
1532
fz_catch(ctx)
1533
{
1534
emergency_pop_stack(ctx, dev, state);
1535
}
1536
}
1537
1538
static void
1539
fz_draw_begin_group(fz_context *ctx, fz_device *devp, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha)
1540
{
1541
fz_draw_device *dev = (fz_draw_device*)devp;
1542
fz_irect bbox;
1543
fz_pixmap *dest;
1544
fz_draw_state *state = &dev->stack[dev->top];
1545
fz_colorspace *model = state->dest->colorspace;
1546
1547
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1548
fz_knockout_begin(ctx, dev);
1549
1550
state = push_stack(ctx, dev);
1551
STACK_PUSHED("group");
1552
fz_intersect_irect(fz_irect_from_rect(&bbox, rect), &state->scissor);
1553
1554
fz_try(ctx)
1555
{
1556
state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
1557
1558
#ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED
1559
knockout = 0;
1560
isolated = 1;
1561
#endif
1562
1563
if (isolated)
1564
{
1565
fz_clear_pixmap(ctx, dest);
1566
}
1567
else
1568
{
1569
fz_copy_pixmap_rect(ctx, dest, state[0].dest, &bbox);
1570
}
1571
1572
if (blendmode == 0 && alpha == 1.0 && isolated)
1573
{
1574
/* We can render direct to any existing shape plane.
1575
* If there isn't one, we don't need to make one. */
1576
state[1].shape = state[0].shape;
1577
}
1578
else
1579
{
1580
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
1581
fz_clear_pixmap(ctx, state[1].shape);
1582
}
1583
1584
state[1].alpha = alpha;
1585
#ifdef DUMP_GROUP_BLENDS
1586
dump_spaces(dev->top-1, "Group begin\n");
1587
#endif
1588
1589
state[1].scissor = bbox;
1590
state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0);
1591
}
1592
fz_catch(ctx)
1593
{
1594
emergency_pop_stack(ctx, dev, state);
1595
}
1596
}
1597
1598
static void
1599
fz_draw_end_group(fz_context *ctx, fz_device *devp)
1600
{
1601
fz_draw_device *dev = (fz_draw_device*)devp;
1602
int blendmode;
1603
int isolated;
1604
float alpha;
1605
fz_draw_state *state;
1606
1607
if (dev->top == 0)
1608
{
1609
fz_warn(ctx, "Unexpected end_group");
1610
return;
1611
}
1612
1613
state = &dev->stack[--dev->top];
1614
STACK_POPPED("group");
1615
alpha = state[1].alpha;
1616
blendmode = state[1].blendmode & FZ_BLEND_MODEMASK;
1617
isolated = state[1].blendmode & FZ_BLEND_ISOLATED;
1618
#ifdef DUMP_GROUP_BLENDS
1619
dump_spaces(dev->top, "");
1620
fz_dump_blend(ctx, state[1].dest, "Group end: blending ");
1621
if (state[1].shape)
1622
fz_dump_blend(ctx, state[1].shape, "/");
1623
fz_dump_blend(ctx, state[0].dest, " onto ");
1624
if (state[0].shape)
1625
fz_dump_blend(ctx, state[0].shape, "/");
1626
if (alpha != 1.0f)
1627
printf(" (alpha %g)", alpha);
1628
if (blendmode != 0)
1629
printf(" (blend %d)", blendmode);
1630
if (isolated != 0)
1631
printf(" (isolated)");
1632
if (state[1].blendmode & FZ_BLEND_KNOCKOUT)
1633
printf(" (knockout)");
1634
#endif
1635
if ((blendmode == 0) && (state[0].shape == state[1].shape))
1636
fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255);
1637
else
1638
fz_blend_pixmap(state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].shape);
1639
1640
/* The following test should not be required, but just occasionally
1641
* errors can cause the stack to get out of sync, and this might save
1642
* our bacon. */
1643
if (state[0].dest != state[1].dest)
1644
fz_drop_pixmap(ctx, state[1].dest);
1645
if (state[0].shape != state[1].shape)
1646
{
1647
if (state[0].shape)
1648
fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255);
1649
fz_drop_pixmap(ctx, state[1].shape);
1650
}
1651
#ifdef DUMP_GROUP_BLENDS
1652
fz_dump_blend(ctx, state[0].dest, " to get ");
1653
if (state[0].shape)
1654
fz_dump_blend(ctx, state[0].shape, "/");
1655
printf("\n");
1656
#endif
1657
1658
if (state[0].blendmode & FZ_BLEND_KNOCKOUT)
1659
fz_knockout_end(ctx, dev);
1660
}
1661
1662
typedef struct
1663
{
1664
int refs;
1665
float ctm[4];
1666
int id;
1667
} tile_key;
1668
1669
typedef struct
1670
{
1671
fz_storable storable;
1672
fz_pixmap *dest;
1673
fz_pixmap *shape;
1674
} tile_record;
1675
1676
static int
1677
fz_make_hash_tile_key(fz_context *ctx, fz_store_hash *hash, void *key_)
1678
{
1679
tile_key *key = (tile_key *)key_;
1680
1681
hash->u.im.id = key->id;
1682
hash->u.im.m[0] = key->ctm[0];
1683
hash->u.im.m[1] = key->ctm[1];
1684
hash->u.im.m[2] = key->ctm[2];
1685
hash->u.im.m[3] = key->ctm[3];
1686
return 1;
1687
}
1688
1689
static void *
1690
fz_keep_tile_key(fz_context *ctx, void *key_)
1691
{
1692
tile_key *key = (tile_key *)key_;
1693
return fz_keep_imp(ctx, key, &key->refs);
1694
}
1695
1696
static void
1697
fz_drop_tile_key(fz_context *ctx, void *key_)
1698
{
1699
tile_key *key = (tile_key *)key_;
1700
if (fz_drop_imp(ctx, key, &key->refs))
1701
fz_free(ctx, key);
1702
}
1703
1704
static int
1705
fz_cmp_tile_key(fz_context *ctx, void *k0_, void *k1_)
1706
{
1707
tile_key *k0 = (tile_key *)k0_;
1708
tile_key *k1 = (tile_key *)k1_;
1709
return k0->id == k1->id && k0->ctm[0] == k1->ctm[0] && k0->ctm[1] == k1->ctm[1] && k0->ctm[2] == k1->ctm[2] && k0->ctm[3] == k1->ctm[3];
1710
}
1711
1712
#ifndef NDEBUG
1713
static void
1714
fz_debug_tile(fz_context *ctx, FILE *out, void *key_)
1715
{
1716
tile_key *key = (tile_key *)key_;
1717
fprintf(out, "(tile id=%x, ctm=%g %g %g %g) ", key->id, key->ctm[0], key->ctm[1], key->ctm[2], key->ctm[3]);
1718
}
1719
#endif
1720
1721
static fz_store_type fz_tile_store_type =
1722
{
1723
fz_make_hash_tile_key,
1724
fz_keep_tile_key,
1725
fz_drop_tile_key,
1726
fz_cmp_tile_key,
1727
#ifndef NDEBUG
1728
fz_debug_tile
1729
#endif
1730
};
1731
1732
static void
1733
fz_drop_tile_record_imp(fz_context *ctx, fz_storable *storable)
1734
{
1735
tile_record *tr = (tile_record *)storable;
1736
fz_drop_pixmap(ctx, tr->dest);
1737
fz_drop_pixmap(ctx, tr->shape);
1738
fz_free(ctx, tr);
1739
}
1740
1741
static void
1742
fz_drop_tile_record(fz_context *ctx, tile_record *tile)
1743
{
1744
fz_drop_storable(ctx, &tile->storable);
1745
}
1746
1747
static tile_record *
1748
fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape)
1749
{
1750
tile_record *tile = fz_malloc_struct(ctx, tile_record);
1751
FZ_INIT_STORABLE(tile, 1, fz_drop_tile_record_imp);
1752
tile->dest = fz_keep_pixmap(ctx, dest);
1753
tile->shape = fz_keep_pixmap(ctx, shape);
1754
return tile;
1755
}
1756
1757
unsigned int
1758
fz_tile_size(fz_context *ctx, tile_record *tile)
1759
{
1760
if (!tile)
1761
return 0;
1762
return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape);
1763
}
1764
1765
static int
1766
fz_draw_begin_tile(fz_context *ctx, fz_device *devp, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id)
1767
{
1768
fz_draw_device *dev = (fz_draw_device*)devp;
1769
fz_pixmap *dest = NULL;
1770
fz_pixmap *shape;
1771
fz_irect bbox;
1772
fz_draw_state *state = &dev->stack[dev->top];
1773
fz_colorspace *model = state->dest->colorspace;
1774
fz_rect local_view = *view;
1775
1776
/* area, view, xstep, ystep are in pattern space */
1777
/* ctm maps from pattern space to device space */
1778
1779
if (state->blendmode & FZ_BLEND_KNOCKOUT)
1780
fz_knockout_begin(ctx, dev);
1781
1782
state = push_stack(ctx, dev);
1783
STACK_PUSHED("tile");
1784
fz_irect_from_rect(&bbox, fz_transform_rect(&local_view, ctm));
1785
/* We should never have a bbox that entirely covers our destination.
1786
* If we do, then the check for only 1 tile being visible above has
1787
* failed. Actually, this *can* fail due to the round_rect, at extreme
1788
* resolutions, so disable this assert.
1789
* assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w ||
1790
* bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h);
1791
*/
1792
1793
/* Check to see if we have one cached */
1794
if (id)
1795
{
1796
tile_key tk;
1797
tile_record *tile;
1798
tk.ctm[0] = ctm->a;
1799
tk.ctm[1] = ctm->b;
1800
tk.ctm[2] = ctm->c;
1801
tk.ctm[3] = ctm->d;
1802
tk.id = id;
1803
1804
tile = fz_find_item(ctx, fz_drop_tile_record_imp, &tk, &fz_tile_store_type);
1805
if (tile)
1806
{
1807
state[1].dest = fz_keep_pixmap(ctx, tile->dest);
1808
state[1].shape = fz_keep_pixmap(ctx, tile->shape);
1809
state[1].blendmode |= FZ_BLEND_ISOLATED;
1810
state[1].xstep = xstep;
1811
state[1].ystep = ystep;
1812
state[1].id = id;
1813
fz_irect_from_rect(&state[1].area, area);
1814
state[1].ctm = *ctm;
1815
#ifdef DUMP_GROUP_BLENDS
1816
dump_spaces(dev->top-1, "Tile begin (cached)\n");
1817
#endif
1818
1819
state[1].scissor = bbox;
1820
fz_drop_tile_record(ctx, tile);
1821
return 1;
1822
}
1823
}
1824
1825
fz_try(ctx)
1826
{
1827
state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, &bbox);
1828
fz_clear_pixmap(ctx, dest);
1829
shape = state[0].shape;
1830
if (shape)
1831
{
1832
state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox);
1833
fz_clear_pixmap(ctx, shape);
1834
}
1835
state[1].blendmode |= FZ_BLEND_ISOLATED;
1836
state[1].xstep = xstep;
1837
state[1].ystep = ystep;
1838
state[1].id = id;
1839
fz_irect_from_rect(&state[1].area, area);
1840
state[1].ctm = *ctm;
1841
#ifdef DUMP_GROUP_BLENDS
1842
dump_spaces(dev->top-1, "Tile begin\n");
1843
#endif
1844
1845
state[1].scissor = bbox;
1846
}
1847
fz_catch(ctx)
1848
{
1849
emergency_pop_stack(ctx, dev, state);
1850
}
1851
1852
return 0;
1853
}
1854
1855
static void
1856
fz_draw_end_tile(fz_context *ctx, fz_device *devp)
1857
{
1858
fz_draw_device *dev = (fz_draw_device*)devp;
1859
float xstep, ystep;
1860
fz_matrix ttm, ctm, shapectm;
1861
fz_irect area, scissor;
1862
fz_rect scissor_tmp;
1863
int x0, y0, x1, y1, x, y;
1864
fz_draw_state *state;
1865
tile_record *tile;
1866
tile_key *key;
1867
1868
if (dev->top == 0)
1869
{
1870
fz_warn(ctx, "Unexpected end_tile");
1871
return;
1872
}
1873
1874
state = &dev->stack[--dev->top];
1875
STACK_PUSHED("tile");
1876
xstep = state[1].xstep;
1877
ystep = state[1].ystep;
1878
area = state[1].area;
1879
ctm = state[1].ctm;
1880
1881
/* Fudge the scissor bbox a little to allow for inaccuracies in the
1882
* matrix inversion. */
1883
fz_rect_from_irect(&scissor_tmp, &state[0].scissor);
1884
fz_transform_rect(fz_expand_rect(&scissor_tmp, 1), fz_invert_matrix(&ttm, &ctm));
1885
fz_intersect_irect(&area, fz_irect_from_rect(&scissor, &scissor_tmp));
1886
1887
/* FIXME: area is a bbox, so FP not appropriate here */
1888
/* In PDF files xstep/ystep can be smaller than view (the area of a
1889
* single tile) (see fts_15_1506.pdf for an example). This means that
1890
* we have to bias the left hand/bottom edge calculations by the
1891
* difference between the step and the width/height of the tile. */
1892
/* scissor, xstep and area are all in pattern space. */
1893
x0 = xstep - scissor.x1 + scissor.x0;
1894
if (x0 > 0)
1895
x0 = 0;
1896
y0 = ystep - scissor.y1 + scissor.y0;
1897
if (y0 > 0)
1898
y0 = 0;
1899
x0 = floorf((area.x0 + x0) / xstep);
1900
y0 = floorf((area.y0 + y0) / ystep);
1901
x1 = ceilf(area.x1 / xstep);
1902
y1 = ceilf(area.y1 / ystep);
1903
1904
ctm.e = state[1].dest->x;
1905
ctm.f = state[1].dest->y;
1906
if (state[1].shape)
1907
{
1908
shapectm = ctm;
1909
shapectm.e = state[1].shape->x;
1910
shapectm.f = state[1].shape->y;
1911
}
1912
1913
#ifdef DUMP_GROUP_BLENDS
1914
dump_spaces(dev->top, "");
1915
fz_dump_blend(ctx, state[1].dest, "Tiling ");
1916
if (state[1].shape)
1917
fz_dump_blend(ctx, state[1].shape, "/");
1918
fz_dump_blend(ctx, state[0].dest, " onto ");
1919
if (state[0].shape)
1920
fz_dump_blend(ctx, state[0].shape, "/");
1921
#endif
1922
1923
for (y = y0; y < y1; y++)
1924
{
1925
for (x = x0; x < x1; x++)
1926
{
1927
ttm = ctm;
1928
fz_pre_translate(&ttm, x * xstep, y * ystep);
1929
state[1].dest->x = ttm.e;
1930
state[1].dest->y = ttm.f;
1931
if (state[1].dest->x > 0 && state[1].dest->x + state[1].dest->w < 0)
1932
continue;
1933
if (state[1].dest->y > 0 && state[1].dest->y + state[1].dest->h < 0)
1934
continue;
1935
fz_paint_pixmap_with_bbox(state[0].dest, state[1].dest, 255, state[0].scissor);
1936
if (state[1].shape)
1937
{
1938
ttm = shapectm;
1939
fz_pre_translate(&ttm, x * xstep, y * ystep);
1940
state[1].shape->x = ttm.e;
1941
state[1].shape->y = ttm.f;
1942
fz_paint_pixmap_with_bbox(state[0].shape, state[1].shape, 255, state[0].scissor);
1943
}
1944
}
1945
}
1946
1947
state[1].dest->x = ctm.e;
1948
state[1].dest->y = ctm.f;
1949
if (state[1].shape)
1950
{
1951
state[1].shape->x = shapectm.e;
1952
state[1].shape->y = shapectm.f;
1953
}
1954
1955
/* Now we try to cache the tiles. Any failure here will just result
1956
* in us not caching. */
1957
tile = NULL;
1958
key = NULL;
1959
fz_var(tile);
1960
fz_var(key);
1961
fz_try(ctx)
1962
{
1963
tile_record *existing_tile;
1964
1965
tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape);
1966
1967
key = fz_malloc_struct(ctx, tile_key);
1968
key->refs = 1;
1969
key->id = state[1].id;
1970
key->ctm[0] = ctm.a;
1971
key->ctm[1] = ctm.b;
1972
key->ctm[2] = ctm.c;
1973
key->ctm[3] = ctm.d;
1974
existing_tile = fz_store_item(ctx, key, tile, fz_tile_size(ctx, tile), &fz_tile_store_type);
1975
if (existing_tile)
1976
{
1977
/* We already have a tile. This will either have been
1978
* produced by a racing thread, or there is already
1979
* an entry for this one in the store. */
1980
fz_drop_tile_record(ctx, tile);
1981
tile = existing_tile;
1982
}
1983
}
1984
fz_always(ctx)
1985
{
1986
fz_drop_tile_key(ctx, key);
1987
fz_drop_tile_record(ctx, tile);
1988
}
1989
fz_catch(ctx)
1990
{
1991
/* Do nothing */
1992
}
1993
1994
/* The following tests should not be required, but just occasionally
1995
* errors can cause the stack to get out of sync, and this might save
1996
* our bacon. */
1997
if (state[0].dest != state[1].dest)
1998
fz_drop_pixmap(ctx, state[1].dest);
1999
if (state[0].shape != state[1].shape)
2000
fz_drop_pixmap(ctx, state[1].shape);
2001
#ifdef DUMP_GROUP_BLENDS
2002
fz_dump_blend(ctx, state[0].dest, " to get ");
2003
if (state[0].shape)
2004
fz_dump_blend(ctx, state[0].shape, "/");
2005
printf("\n");
2006
#endif
2007
2008
if (state->blendmode & FZ_BLEND_KNOCKOUT)
2009
fz_knockout_end(ctx, dev);
2010
}
2011
2012
static void
2013
fz_draw_drop_imp(fz_context *ctx, fz_device *devp)
2014
{
2015
fz_draw_device *dev = (fz_draw_device*)devp;
2016
fz_gel *gel = dev->gel;
2017
2018
/* pop and free the stacks */
2019
if (dev->top > 0)
2020
fz_warn(ctx, "items left on stack in draw device: %d", dev->top+1);
2021
2022
while(dev->top-- > 0)
2023
{
2024
fz_draw_state *state = &dev->stack[dev->top];
2025
if (state[1].mask != state[0].mask)
2026
fz_drop_pixmap(ctx, state[1].mask);
2027
if (state[1].dest != state[0].dest)
2028
fz_drop_pixmap(ctx, state[1].dest);
2029
if (state[1].shape != state[0].shape)
2030
fz_drop_pixmap(ctx, state[1].shape);
2031
}
2032
/* We never free the dest/mask/shape at level 0, as:
2033
* 1) dest is passed in and ownership remains with the caller.
2034
* 2) shape and mask are NULL at level 0.
2035
*/
2036
if (dev->stack != &dev->init_stack[0])
2037
fz_free(ctx, dev->stack);
2038
fz_drop_scale_cache(ctx, dev->cache_x);
2039
fz_drop_scale_cache(ctx, dev->cache_y);
2040
fz_drop_gel(ctx, gel);
2041
}
2042
2043
fz_device *
2044
fz_new_draw_device(fz_context *ctx, fz_pixmap *dest)
2045
{
2046
fz_draw_device *dev = fz_new_device(ctx, sizeof *dev);
2047
2048
dev->super.drop_imp = fz_draw_drop_imp;
2049
2050
dev->super.fill_path = fz_draw_fill_path;
2051
dev->super.stroke_path = fz_draw_stroke_path;
2052
dev->super.clip_path = fz_draw_clip_path;
2053
dev->super.clip_stroke_path = fz_draw_clip_stroke_path;
2054
2055
dev->super.fill_text = fz_draw_fill_text;
2056
dev->super.stroke_text = fz_draw_stroke_text;
2057
dev->super.clip_text = fz_draw_clip_text;
2058
dev->super.clip_stroke_text = fz_draw_clip_stroke_text;
2059
dev->super.ignore_text = fz_draw_ignore_text;
2060
2061
dev->super.fill_image_mask = fz_draw_fill_image_mask;
2062
dev->super.clip_image_mask = fz_draw_clip_image_mask;
2063
dev->super.fill_image = fz_draw_fill_image;
2064
dev->super.fill_shade = fz_draw_fill_shade;
2065
2066
dev->super.pop_clip = fz_draw_pop_clip;
2067
2068
dev->super.begin_mask = fz_draw_begin_mask;
2069
dev->super.end_mask = fz_draw_end_mask;
2070
dev->super.begin_group = fz_draw_begin_group;
2071
dev->super.end_group = fz_draw_end_group;
2072
2073
dev->super.begin_tile = fz_draw_begin_tile;
2074
dev->super.end_tile = fz_draw_end_tile;
2075
2076
dev->flags = 0;
2077
dev->top = 0;
2078
dev->stack = &dev->init_stack[0];
2079
dev->stack_cap = STACK_SIZE;
2080
dev->stack[0].dest = dest;
2081
dev->stack[0].shape = NULL;
2082
dev->stack[0].mask = NULL;
2083
dev->stack[0].blendmode = 0;
2084
dev->stack[0].scissor.x0 = dest->x;
2085
dev->stack[0].scissor.y0 = dest->y;
2086
dev->stack[0].scissor.x1 = dest->x + dest->w;
2087
dev->stack[0].scissor.y1 = dest->y + dest->h;
2088
2089
fz_try(ctx)
2090
{
2091
dev->gel = fz_new_gel(ctx);
2092
dev->cache_x = fz_new_scale_cache(ctx);
2093
dev->cache_y = fz_new_scale_cache(ctx);
2094
}
2095
fz_catch(ctx)
2096
{
2097
fz_drop_device(ctx, (fz_device*)dev);
2098
fz_rethrow(ctx);
2099
}
2100
2101
return (fz_device*)dev;
2102
}
2103
2104
fz_device *
2105
fz_new_draw_device_with_bbox(fz_context *ctx, fz_pixmap *dest, const fz_irect *clip)
2106
{
2107
fz_draw_device *dev = (fz_draw_device*)fz_new_draw_device(ctx, dest);
2108
2109
if (clip->x0 > dev->stack[0].scissor.x0)
2110
dev->stack[0].scissor.x0 = clip->x0;
2111
if (clip->x1 < dev->stack[0].scissor.x1)
2112
dev->stack[0].scissor.x1 = clip->x1;
2113
if (clip->y0 > dev->stack[0].scissor.y0)
2114
dev->stack[0].scissor.y0 = clip->y0;
2115
if (clip->y1 < dev->stack[0].scissor.y1)
2116
dev->stack[0].scissor.y1 = clip->y1;
2117
2118
return (fz_device*)dev;
2119
}
2120
2121
fz_device *
2122
fz_new_draw_device_type3(fz_context *ctx, fz_pixmap *dest)
2123
{
2124
fz_draw_device *dev = (fz_draw_device*)fz_new_draw_device(ctx, dest);
2125
dev->flags |= FZ_DRAWDEV_FLAGS_TYPE3;
2126
return (fz_device*)dev;
2127
}
2128
2129
fz_irect *
2130
fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, float flatness, float linewidth)
2131
{
2132
fz_gel *gel = fz_new_gel(ctx);
2133
2134
fz_reset_gel(ctx, gel, scissor);
2135
if (stroke)
2136
{
2137
if (stroke->dash_len > 0)
2138
fz_flatten_dash_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
2139
else
2140
fz_flatten_stroke_path(ctx, gel, path, stroke, ctm, flatness, linewidth);
2141
}
2142
else
2143
fz_flatten_fill_path(ctx, gel, path, ctm, flatness);
2144
fz_bound_gel(ctx, gel, bbox);
2145
fz_drop_gel(ctx, gel);
2146
2147
return bbox;
2148
}
2149
2150