Subversion Repositories Kolibri OS

Rev

Rev 4680 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
 
3
#define QUANT(x,a) (((int)((x) * (a))) / (a))
4
#define HSUBPIX 5.0
5
#define VSUBPIX 5.0
6
 
7
#define STACK_SIZE 96
8
 
9
/* Enable the following to attempt to support knockout and/or isolated
10
 * blending groups. This code is known to give incorrect results currently
11
 * so disabled by default. See bug 692377. */
12
#undef ATTEMPT_KNOCKOUT_AND_ISOLATED
13
 
14
/* Enable the following to help debug group blending. */
15
#undef DUMP_GROUP_BLENDS
16
 
17
/* Note #1: At various points in this code (notably when clipping with non
18
 * rectangular masks), we create a new (empty) destination pixmap. We then
19
 * render this pixmap, then plot it back into the original destination
20
 * through a mask. This works well for normal blending, but falls down for
21
 * non-zero blending modes; effectively we are forcing ourselves to use an
22
 * isolated group.
23
 *
24
 * The fix for this would be to copy the contents from the underlying dest
25
 * into the newly created dest. This would enable us to use a non
26
 * FZ_BLEND_ISOLATED blendmode. Unfortunately, tt would break tiling, as
27
 * we could no longer render once and blend back multiple times.
28
 */
29
 
30
 
31
typedef struct fz_draw_device_s fz_draw_device;
32
 
33
enum {
34
	FZ_DRAWDEV_FLAGS_TYPE3 = 1,
35
};
36
 
37
struct fz_draw_device_s
38
{
39
	fz_glyph_cache *cache;
40
	fz_gel *gel;
41
 
42
	fz_pixmap *dest;
43
	fz_pixmap *shape;
44
	fz_bbox scissor;
45
 
46
	int flags;
47
	int top;
48
	int blendmode;
49
	struct {
50
		fz_bbox scissor;
51
		fz_pixmap *dest;
52
		fz_pixmap *mask;
53
		fz_pixmap *shape;
54
		int blendmode;
55
		int luminosity;
56
		float alpha;
57
		fz_matrix ctm;
58
		float xstep, ystep;
59
		fz_rect area;
60
	} stack[STACK_SIZE];
61
};
62
 
63
#ifdef DUMP_GROUP_BLENDS
64
static int group_dump_count = 0;
65
 
66
static void fz_dump_blend(fz_pixmap *pix, const char *s)
67
{
68
	char name[80];
69
 
70
	if (pix == NULL)
71
		return;
72
 
73
	sprintf(name, "dump%02d.png", group_dump_count);
74
	if (s)
75
		printf("%s%02d", s, group_dump_count);
76
	group_dump_count++;
77
 
78
	fz_write_png(pix, name, (pix->n > 1));
79
}
80
 
81
static void dump_spaces(int x, const char *s)
82
{
83
	int i;
84
	for (i = 0; i < x; i++)
85
		printf(" ");
86
	printf("%s", s);
87
}
88
 
89
#endif
90
 
91
static void fz_knockout_begin(void *user)
92
{
93
	fz_draw_device *dev = user;
94
	fz_bbox bbox;
95
	fz_pixmap *dest, *shape;
96
	int isolated = dev->blendmode & FZ_BLEND_ISOLATED;
97
 
98
	if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0)
99
		return;
100
 
101
	if (dev->top == STACK_SIZE)
102
	{
103
		fz_warn("assert: too many buffers on stack");
104
		return;
105
	}
106
 
107
	bbox = fz_bound_pixmap(dev->dest);
108
	bbox = fz_intersect_bbox(bbox, dev->scissor);
109
	dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
110
 
111
	if (isolated)
112
	{
113
		fz_clear_pixmap(dest);
114
	}
115
	else
116
	{
117
		fz_pixmap *prev;
118
		int i  = dev->top;
119
		do
120
			prev = dev->stack[--i].dest;
121
		while (prev == NULL);
122
		fz_copy_pixmap_rect(dest, prev, bbox);
123
	}
124
 
125
	if (dev->blendmode == 0 && isolated)
126
	{
127
		/* We can render direct to any existing shape plane. If there
128
		 * isn't one, we don't need to make one. */
129
		shape = dev->shape;
130
	}
131
	else
132
	{
133
		shape = fz_new_pixmap_with_rect(NULL, bbox);
134
		fz_clear_pixmap(shape);
135
	}
136
	dev->stack[dev->top].blendmode = dev->blendmode;
137
	dev->stack[dev->top].scissor = dev->scissor;
138
	dev->stack[dev->top].dest = dev->dest;
139
	dev->stack[dev->top].shape = dev->shape;
140
#ifdef DUMP_GROUP_BLENDS
141
	dump_spaces(dev->top, "Knockout begin\n");
142
#endif
143
	dev->top++;
144
 
145
	dev->scissor = bbox;
146
	dev->dest = dest;
147
	dev->shape = shape;
148
	dev->blendmode &= ~FZ_BLEND_MODEMASK;
149
}
150
 
151
static void fz_knockout_end(void *user)
152
{
153
	fz_draw_device *dev = user;
154
	fz_pixmap *group = dev->dest;
155
	fz_pixmap *shape = dev->shape;
156
	int blendmode;
157
	int isolated;
158
 
159
	if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0)
160
		return;
161
 
162
	if (dev->top == STACK_SIZE)
163
	{
164
		fz_warn("assert: too many buffers on stack");
165
		return;
166
	}
167
 
168
	if (dev->top > 0)
169
	{
170
		dev->top--;
171
		blendmode = dev->blendmode & FZ_BLEND_MODEMASK;
172
		isolated = dev->blendmode & FZ_BLEND_ISOLATED;
173
		dev->blendmode = dev->stack[dev->top].blendmode;
174
		dev->shape = dev->stack[dev->top].shape;
175
		dev->dest = dev->stack[dev->top].dest;
176
		dev->scissor = dev->stack[dev->top].scissor;
177
 
178
#ifdef DUMP_GROUP_BLENDS
179
		dump_spaces(dev->top, "");
180
		fz_dump_blend(group, "Blending ");
181
		if (shape)
182
			fz_dump_blend(shape, "/");
183
		fz_dump_blend(dev->dest, " onto ");
184
		if (dev->shape)
185
			fz_dump_blend(dev->shape, "/");
186
		if (blendmode != 0)
187
			printf(" (blend %d)", blendmode);
188
		if (isolated != 0)
189
			printf(" (isolated)");
190
		printf(" (knockout)");
191
#endif
192
		if ((blendmode == 0) && (shape == NULL))
193
			fz_paint_pixmap(dev->dest, group, 255);
194
		else
195
			fz_blend_pixmap(dev->dest, group, 255, blendmode, isolated, shape);
196
 
197
		fz_drop_pixmap(group);
198
		if (shape != dev->shape)
199
		{
200
			if (dev->shape)
201
			{
202
				fz_paint_pixmap(dev->shape, shape, 255);
203
			}
204
			fz_drop_pixmap(shape);
205
		}
206
#ifdef DUMP_GROUP_BLENDS
207
		fz_dump_blend(dev->dest, " to get ");
208
		if (dev->shape)
209
			fz_dump_blend(dev->shape, "/");
210
		printf("\n");
211
#endif
212
	}
213
}
214
 
215
static void
216
fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
217
	fz_colorspace *colorspace, float *color, float alpha)
218
{
219
	fz_draw_device *dev = user;
220
	fz_colorspace *model = dev->dest->colorspace;
221
	float expansion = fz_matrix_expansion(ctm);
222
	float flatness = 0.3f / expansion;
223
	unsigned char colorbv[FZ_MAX_COLORS + 1];
224
	float colorfv[FZ_MAX_COLORS];
225
	fz_bbox bbox;
226
	int i;
227
 
228
	fz_reset_gel(dev->gel, dev->scissor);
229
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
230
	fz_sort_gel(dev->gel);
231
 
232
	bbox = fz_bound_gel(dev->gel);
233
	bbox = fz_intersect_bbox(bbox, dev->scissor);
234
 
235
	if (fz_is_empty_rect(bbox))
236
		return;
237
 
238
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
239
		fz_knockout_begin(dev);
240
 
241
	fz_convert_color(colorspace, color, model, colorfv);
242
	for (i = 0; i < model->n; i++)
243
		colorbv[i] = colorfv[i] * 255;
244
	colorbv[i] = alpha * 255;
245
 
246
	fz_scan_convert(dev->gel, even_odd, bbox, dev->dest, colorbv);
247
	if (dev->shape)
248
	{
249
		fz_reset_gel(dev->gel, dev->scissor);
250
		fz_flatten_fill_path(dev->gel, path, ctm, flatness);
251
		fz_sort_gel(dev->gel);
252
 
253
		colorbv[0] = alpha * 255;
254
		fz_scan_convert(dev->gel, even_odd, bbox, dev->shape, colorbv);
255
	}
256
 
257
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
258
		fz_knockout_end(dev);
259
}
260
 
261
static void
262
fz_draw_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm,
263
	fz_colorspace *colorspace, float *color, float alpha)
264
{
265
	fz_draw_device *dev = user;
266
	fz_colorspace *model = dev->dest->colorspace;
267
	float expansion = fz_matrix_expansion(ctm);
268
	float flatness = 0.3f / expansion;
269
	float linewidth = stroke->linewidth;
270
	unsigned char colorbv[FZ_MAX_COLORS + 1];
271
	float colorfv[FZ_MAX_COLORS];
272
	fz_bbox bbox;
273
	int i;
274
 
275
	if (linewidth * expansion < 0.1f)
276
		linewidth = 1 / expansion;
277
 
278
	fz_reset_gel(dev->gel, dev->scissor);
279
	if (stroke->dash_len > 0)
280
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
281
	else
282
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
283
	fz_sort_gel(dev->gel);
284
 
285
	bbox = fz_bound_gel(dev->gel);
286
	bbox = fz_intersect_bbox(bbox, dev->scissor);
287
 
288
	if (fz_is_empty_rect(bbox))
289
		return;
290
 
291
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
292
		fz_knockout_begin(dev);
293
 
294
	fz_convert_color(colorspace, color, model, colorfv);
295
	for (i = 0; i < model->n; i++)
296
		colorbv[i] = colorfv[i] * 255;
297
	colorbv[i] = alpha * 255;
298
 
299
	fz_scan_convert(dev->gel, 0, bbox, dev->dest, colorbv);
300
	if (dev->shape)
301
	{
302
		fz_reset_gel(dev->gel, dev->scissor);
303
		if (stroke->dash_len > 0)
304
			fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
305
		else
306
			fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
307
		fz_sort_gel(dev->gel);
308
 
309
		colorbv[0] = 255;
310
		fz_scan_convert(dev->gel, 0, bbox, dev->shape, colorbv);
311
	}
312
 
313
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
314
		fz_knockout_end(dev);
315
}
316
 
317
static void
318
fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm)
319
{
320
	fz_draw_device *dev = user;
321
	fz_colorspace *model = dev->dest->colorspace;
322
	float expansion = fz_matrix_expansion(ctm);
323
	float flatness = 0.3f / expansion;
324
	fz_pixmap *mask, *dest, *shape;
325
	fz_bbox bbox;
326
 
327
	if (dev->top == STACK_SIZE)
328
	{
329
		fz_warn("assert: too many buffers on stack");
330
		return;
331
	}
332
 
333
	fz_reset_gel(dev->gel, dev->scissor);
334
	fz_flatten_fill_path(dev->gel, path, ctm, flatness);
335
	fz_sort_gel(dev->gel);
336
 
337
	bbox = fz_bound_gel(dev->gel);
338
	bbox = fz_intersect_bbox(bbox, dev->scissor);
339
	if (rect)
340
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
341
 
342
	if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel))
343
	{
344
		dev->stack[dev->top].scissor = dev->scissor;
345
		dev->stack[dev->top].mask = NULL;
346
		dev->stack[dev->top].dest = NULL;
347
		dev->stack[dev->top].shape = dev->shape;
348
		dev->stack[dev->top].blendmode = dev->blendmode;
349
		dev->scissor = bbox;
350
#ifdef DUMP_GROUP_BLENDS
351
		dump_spaces(dev->top, "Clip (rectangular) begin\n");
352
#endif
353
		dev->top++;
354
		return;
355
	}
356
 
357
	mask = fz_new_pixmap_with_rect(NULL, bbox);
358
	fz_clear_pixmap(mask);
359
	dest = fz_new_pixmap_with_rect(model, bbox);
360
	/* FIXME: See note #1 */
361
	fz_clear_pixmap(dest);
362
	if (dev->shape)
363
	{
364
		shape = fz_new_pixmap_with_rect(NULL, bbox);
365
		fz_clear_pixmap(shape);
366
	}
367
	else
368
		shape = NULL;
369
 
370
	fz_scan_convert(dev->gel, even_odd, bbox, mask, NULL);
371
 
372
	dev->stack[dev->top].scissor = dev->scissor;
373
	dev->stack[dev->top].mask = mask;
374
	dev->stack[dev->top].dest = dev->dest;
375
	dev->stack[dev->top].shape = dev->shape;
376
	/* FIXME: See note #1 */
377
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
378
	dev->scissor = bbox;
379
	dev->dest = dest;
380
	dev->shape = shape;
381
#ifdef DUMP_GROUP_BLENDS
382
	dump_spaces(dev->top, "Clip (non-rectangular) begin\n");
383
#endif
384
	dev->top++;
385
}
386
 
387
static void
388
fz_draw_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm)
389
{
390
	fz_draw_device *dev = user;
391
	fz_colorspace *model = dev->dest->colorspace;
392
	float expansion = fz_matrix_expansion(ctm);
393
	float flatness = 0.3f / expansion;
394
	float linewidth = stroke->linewidth;
395
	fz_pixmap *mask, *dest, *shape;
396
	fz_bbox bbox;
397
 
398
	if (dev->top == STACK_SIZE)
399
	{
400
		fz_warn("assert: too many buffers on stack");
401
		return;
402
	}
403
 
404
	if (linewidth * expansion < 0.1f)
405
		linewidth = 1 / expansion;
406
 
407
	fz_reset_gel(dev->gel, dev->scissor);
408
	if (stroke->dash_len > 0)
409
		fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth);
410
	else
411
		fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth);
412
	fz_sort_gel(dev->gel);
413
 
414
	bbox = fz_bound_gel(dev->gel);
415
	bbox = fz_intersect_bbox(bbox, dev->scissor);
416
	if (rect)
417
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
418
 
419
	mask = fz_new_pixmap_with_rect(NULL, bbox);
420
	fz_clear_pixmap(mask);
421
	dest = fz_new_pixmap_with_rect(model, bbox);
422
	/* FIXME: See note #1 */
423
	fz_clear_pixmap(dest);
424
	if (dev->shape)
425
	{
426
		shape = fz_new_pixmap_with_rect(NULL, bbox);
427
		fz_clear_pixmap(shape);
428
	}
429
	else
430
		shape = NULL;
431
 
432
	if (!fz_is_empty_rect(bbox))
433
		fz_scan_convert(dev->gel, 0, bbox, mask, NULL);
434
 
435
	dev->stack[dev->top].scissor = dev->scissor;
436
	dev->stack[dev->top].mask = mask;
437
	dev->stack[dev->top].dest = dev->dest;
438
	dev->stack[dev->top].shape = dev->shape;
439
	/* FIXME: See note #1 */
440
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
441
	dev->scissor = bbox;
442
	dev->dest = dest;
443
	dev->shape = shape;
444
#ifdef DUMP_GROUP_BLENDS
445
	dump_spaces(dev->top, "Clip (stroke) begin\n");
446
#endif
447
	dev->top++;
448
}
449
 
450
static void
451
draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_pixmap *msk,
452
	int xorig, int yorig, fz_bbox scissor)
453
{
454
	unsigned char *dp, *mp;
455
	fz_bbox bbox;
456
	int x, y, w, h;
457
 
458
	bbox = fz_bound_pixmap(msk);
459
	bbox.x0 += xorig;
460
	bbox.y0 += yorig;
461
	bbox.x1 += xorig;
462
	bbox.y1 += yorig;
463
 
464
	bbox = fz_intersect_bbox(bbox, scissor); /* scissor < dst */
465
	x = bbox.x0;
466
	y = bbox.y0;
467
	w = bbox.x1 - bbox.x0;
468
	h = bbox.y1 - bbox.y0;
469
 
470
	mp = msk->samples + ((y - msk->y - yorig) * msk->w + (x - msk->x - xorig));
471
	dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
472
 
473
	assert(msk->n == 1);
474
 
475
	while (h--)
476
	{
477
		if (dst->colorspace)
478
			fz_paint_span_with_color(dp, mp, dst->n, w, colorbv);
479
		else
480
			fz_paint_span(dp, mp, 1, w, 255);
481
		dp += dst->w * dst->n;
482
		mp += msk->w;
483
	}
484
}
485
 
486
static void
487
fz_draw_fill_text(void *user, fz_text *text, fz_matrix ctm,
488
	fz_colorspace *colorspace, float *color, float alpha)
489
{
490
	fz_draw_device *dev = user;
491
	fz_colorspace *model = dev->dest->colorspace;
492
	unsigned char colorbv[FZ_MAX_COLORS + 1];
493
	unsigned char shapebv;
494
	float colorfv[FZ_MAX_COLORS];
495
	fz_matrix tm, trm;
496
	fz_pixmap *glyph;
497
	int i, x, y, gid;
498
 
499
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
500
		fz_knockout_begin(dev);
501
 
502
	fz_convert_color(colorspace, color, model, colorfv);
503
	for (i = 0; i < model->n; i++)
504
		colorbv[i] = colorfv[i] * 255;
505
	colorbv[i] = alpha * 255;
506
	shapebv = 255;
507
 
508
	tm = text->trm;
509
 
510
	for (i = 0; i < text->len; i++)
511
	{
512
		gid = text->items[i].gid;
513
		if (gid < 0)
514
			continue;
515
 
516
		tm.e = text->items[i].x;
517
		tm.f = text->items[i].y;
518
		trm = fz_concat(tm, ctm);
519
		x = floorf(trm.e);
520
		y = floorf(trm.f);
521
		trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
522
		trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
523
 
524
		glyph = fz_render_glyph(dev->cache, text->font, gid, trm, model);
525
		if (glyph)
526
		{
527
			if (glyph->n == 1)
528
			{
529
				draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
530
				if (dev->shape)
531
					draw_glyph(&shapebv, dev->shape, glyph, x, y, dev->scissor);
532
			}
533
			else
534
			{
535
				fz_matrix ctm = {glyph->w, 0.0, 0.0, -glyph->h, x + glyph->x, y + glyph->y + glyph->h};
536
				fz_paint_image(dev->dest, dev->scissor, dev->shape, glyph, ctm, alpha * 255);
537
			}
538
			fz_drop_pixmap(glyph);
539
		}
540
	}
541
 
542
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
543
		fz_knockout_end(dev);
544
}
545
 
546
static void
547
fz_draw_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm,
548
	fz_colorspace *colorspace, float *color, float alpha)
549
{
550
	fz_draw_device *dev = user;
551
	fz_colorspace *model = dev->dest->colorspace;
552
	unsigned char colorbv[FZ_MAX_COLORS + 1];
553
	float colorfv[FZ_MAX_COLORS];
554
	fz_matrix tm, trm;
555
	fz_pixmap *glyph;
556
	int i, x, y, gid;
557
 
558
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
559
		fz_knockout_begin(dev);
560
 
561
	fz_convert_color(colorspace, color, model, colorfv);
562
	for (i = 0; i < model->n; i++)
563
		colorbv[i] = colorfv[i] * 255;
564
	colorbv[i] = alpha * 255;
565
 
566
	tm = text->trm;
567
 
568
	for (i = 0; i < text->len; i++)
569
	{
570
		gid = text->items[i].gid;
571
		if (gid < 0)
572
			continue;
573
 
574
		tm.e = text->items[i].x;
575
		tm.f = text->items[i].y;
576
		trm = fz_concat(tm, ctm);
577
		x = floorf(trm.e);
578
		y = floorf(trm.f);
579
		trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
580
		trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
581
 
582
		glyph = fz_render_stroked_glyph(dev->cache, text->font, gid, trm, ctm, stroke);
583
		if (glyph)
584
		{
585
			draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor);
586
			if (dev->shape)
587
				draw_glyph(colorbv, dev->shape, glyph, x, y, dev->scissor);
588
			fz_drop_pixmap(glyph);
589
		}
590
	}
591
 
592
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
593
		fz_knockout_end(dev);
594
}
595
 
596
static void
597
fz_draw_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
598
{
599
	fz_draw_device *dev = user;
600
	fz_colorspace *model = dev->dest->colorspace;
601
	fz_bbox bbox;
602
	fz_pixmap *mask, *dest, *shape;
603
	fz_matrix tm, trm;
604
	fz_pixmap *glyph;
605
	int i, x, y, gid;
606
 
607
	/* If accumulate == 0 then this text object is guaranteed complete */
608
	/* If accumulate == 1 then this text object is the first (or only) in a sequence */
609
	/* If accumulate == 2 then this text object is a continuation */
610
 
611
	if (dev->top == STACK_SIZE)
612
	{
613
		fz_warn("assert: too many buffers on stack");
614
		return;
615
	}
616
 
617
	if (accumulate == 0)
618
	{
619
		/* make the mask the exact size needed */
620
		bbox = fz_round_rect(fz_bound_text(text, ctm));
621
		bbox = fz_intersect_bbox(bbox, dev->scissor);
622
	}
623
	else
624
	{
625
		/* be conservative about the size of the mask needed */
626
		bbox = dev->scissor;
627
	}
628
 
629
	if (accumulate == 0 || accumulate == 1)
630
	{
631
		mask = fz_new_pixmap_with_rect(NULL, bbox);
632
		fz_clear_pixmap(mask);
633
		dest = fz_new_pixmap_with_rect(model, bbox);
634
		/* FIXME: See note #1 */
635
		fz_clear_pixmap(dest);
636
		if (dev->shape)
637
		{
638
			shape = fz_new_pixmap_with_rect(NULL, bbox);
639
			fz_clear_pixmap(shape);
640
		}
641
		else
642
			shape = NULL;
643
 
644
		dev->stack[dev->top].scissor = dev->scissor;
645
		dev->stack[dev->top].mask = mask;
646
		dev->stack[dev->top].dest = dev->dest;
647
		dev->stack[dev->top].shape = dev->shape;
648
		/* FIXME: See note #1 */
649
		dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
650
		dev->scissor = bbox;
651
		dev->dest = dest;
652
		dev->shape = shape;
653
#ifdef DUMP_GROUP_BLENDS
654
		dump_spaces(dev->top, "Clip (text) begin\n");
655
#endif
656
		dev->top++;
657
	}
658
	else
659
	{
660
		mask = dev->stack[dev->top-1].mask;
661
	}
662
 
663
	if (!fz_is_empty_rect(bbox))
664
	{
665
		tm = text->trm;
666
 
667
		for (i = 0; i < text->len; i++)
668
		{
669
			gid = text->items[i].gid;
670
			if (gid < 0)
671
				continue;
672
 
673
			tm.e = text->items[i].x;
674
			tm.f = text->items[i].y;
675
			trm = fz_concat(tm, ctm);
676
			x = floorf(trm.e);
677
			y = floorf(trm.f);
678
			trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
679
			trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
680
 
681
			glyph = fz_render_glyph(dev->cache, text->font, gid, trm, model);
682
			if (glyph)
683
			{
684
				draw_glyph(NULL, mask, glyph, x, y, bbox);
685
				if (dev->shape)
686
					draw_glyph(NULL, dev->shape, glyph, x, y, bbox);
687
				fz_drop_pixmap(glyph);
688
			}
689
		}
690
	}
691
}
692
 
693
static void
694
fz_draw_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm)
695
{
696
	fz_draw_device *dev = user;
697
	fz_colorspace *model = dev->dest->colorspace;
698
	fz_bbox bbox;
699
	fz_pixmap *mask, *dest, *shape;
700
	fz_matrix tm, trm;
701
	fz_pixmap *glyph;
702
	int i, x, y, gid;
703
 
704
	if (dev->top == STACK_SIZE)
705
	{
706
		fz_warn("assert: too many buffers on stack");
707
		return;
708
	}
709
 
710
	/* make the mask the exact size needed */
711
	bbox = fz_round_rect(fz_bound_text(text, ctm));
712
	bbox = fz_intersect_bbox(bbox, dev->scissor);
713
 
714
	mask = fz_new_pixmap_with_rect(NULL, bbox);
715
	fz_clear_pixmap(mask);
716
	dest = fz_new_pixmap_with_rect(model, bbox);
717
	/* FIXME: See note #1 */
718
	fz_clear_pixmap(dest);
719
	if (dev->shape)
720
	{
721
		shape = fz_new_pixmap_with_rect(NULL, bbox);
722
		fz_clear_pixmap(shape);
723
	}
724
	else
725
		shape = dev->shape;
726
 
727
	dev->stack[dev->top].scissor = dev->scissor;
728
	dev->stack[dev->top].mask = mask;
729
	dev->stack[dev->top].dest = dev->dest;
730
	dev->stack[dev->top].shape = dev->shape;
731
	/* FIXME: See note #1 */
732
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
733
	dev->scissor = bbox;
734
	dev->dest = dest;
735
	dev->shape = shape;
736
#ifdef DUMP_GROUP_BLENDS
737
	dump_spaces(dev->top, "Clip (stroke text) begin\n");
738
#endif
739
	dev->top++;
740
 
741
	if (!fz_is_empty_rect(bbox))
742
	{
743
		tm = text->trm;
744
 
745
		for (i = 0; i < text->len; i++)
746
		{
747
			gid = text->items[i].gid;
748
			if (gid < 0)
749
				continue;
750
 
751
			tm.e = text->items[i].x;
752
			tm.f = text->items[i].y;
753
			trm = fz_concat(tm, ctm);
754
			x = floorf(trm.e);
755
			y = floorf(trm.f);
756
			trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX);
757
			trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX);
758
 
759
			glyph = fz_render_stroked_glyph(dev->cache, text->font, gid, trm, ctm, stroke);
760
			if (glyph)
761
			{
762
				draw_glyph(NULL, mask, glyph, x, y, bbox);
763
				if (dev->shape)
764
					draw_glyph(NULL, dev->shape, glyph, x, y, bbox);
765
				fz_drop_pixmap(glyph);
766
			}
767
		}
768
	}
769
}
770
 
771
static void
772
fz_draw_ignore_text(void *user, fz_text *text, fz_matrix ctm)
773
{
774
}
775
 
776
static void
777
fz_draw_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha)
778
{
779
	fz_draw_device *dev = user;
780
	fz_colorspace *model = dev->dest->colorspace;
781
	fz_pixmap *dest = dev->dest;
782
	fz_rect bounds;
783
	fz_bbox bbox, scissor;
784
	float colorfv[FZ_MAX_COLORS];
785
	unsigned char colorbv[FZ_MAX_COLORS + 1];
786
 
787
	bounds = fz_bound_shade(shade, ctm);
788
	bbox = fz_intersect_bbox(fz_round_rect(bounds), dev->scissor);
789
	scissor = dev->scissor;
790
 
791
	// TODO: proper clip by shade->bbox
792
 
793
	if (fz_is_empty_rect(bbox))
794
		return;
795
 
796
	if (!model)
797
	{
798
		fz_warn("cannot render shading directly to an alpha mask");
799
		return;
800
	}
801
 
802
	if (alpha < 1)
803
	{
804
		dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
805
		fz_clear_pixmap(dest);
806
	}
807
 
808
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
809
		fz_knockout_begin(dev);
810
 
811
	if (shade->use_background)
812
	{
813
		unsigned char *s;
814
		int x, y, n, i;
815
		fz_convert_color(shade->colorspace, shade->background, model, colorfv);
816
		for (i = 0; i < model->n; i++)
817
			colorbv[i] = colorfv[i] * 255;
818
		colorbv[i] = 255;
819
 
820
		n = dest->n;
821
		for (y = scissor.y0; y < scissor.y1; y++)
822
		{
823
			s = dest->samples + ((scissor.x0 - dest->x) + (y - dest->y) * dest->w) * dest->n;
824
			for (x = scissor.x0; x < scissor.x1; x++)
825
			{
826
				for (i = 0; i < n; i++)
827
					*s++ = colorbv[i];
828
			}
829
		}
830
		if (dev->shape)
831
		{
832
			for (y = scissor.y0; y < scissor.y1; y++)
833
			{
834
				s = dev->shape->samples + (scissor.x0 - dev->shape->x) + (y - dev->shape->y) * dev->shape->w;
835
				for (x = scissor.x0; x < scissor.x1; x++)
836
				{
837
					*s++ = 255;
838
				}
839
			}
840
		}
841
	}
842
 
843
	fz_paint_shade(shade, ctm, dest, bbox);
844
	if (dev->shape)
845
		fz_clear_pixmap_rect_with_color(dev->shape, 255, bbox);
846
 
847
	if (alpha < 1)
848
	{
849
		fz_paint_pixmap(dev->dest, dest, alpha * 255);
850
		fz_drop_pixmap(dest);
851
	}
852
 
853
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
854
		fz_knockout_end(dev);
855
}
856
 
857
static fz_pixmap *
858
fz_transform_pixmap(fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit)
859
{
860
	fz_pixmap *scaled;
861
 
862
	if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
863
	{
864
		/* Unrotated or X-flip or Y-flip or XY-flip */
865
		scaled = fz_scale_pixmap_gridfit(image, ctm->e, ctm->f, ctm->a, ctm->d, gridfit);
866
		if (scaled == NULL)
867
			return NULL;
868
		ctm->a = scaled->w;
869
		ctm->d = scaled->h;
870
		ctm->e = scaled->x;
871
		ctm->f = scaled->y;
872
		return scaled;
873
	}
874
 
875
	if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
876
	{
877
		/* Other orthogonal flip/rotation cases */
878
		scaled = fz_scale_pixmap_gridfit(image, ctm->f, ctm->e, ctm->b, ctm->c, gridfit);
879
		if (scaled == NULL)
880
			return NULL;
881
		ctm->b = scaled->w;
882
		ctm->c = scaled->h;
883
		ctm->f = scaled->x;
884
		ctm->e = scaled->y;
885
		return scaled;
886
	}
887
 
888
	/* Downscale, non rectilinear case */
889
	if (dx > 0 && dy > 0)
890
	{
891
		scaled = fz_scale_pixmap(image, 0, 0, (float)dx, (float)dy);
892
		return scaled;
893
	}
894
 
895
	return NULL;
896
}
897
 
898
static void
899
fz_draw_fill_image(void *user, fz_pixmap *image, fz_matrix ctm, float alpha)
900
{
901
	fz_draw_device *dev = user;
902
	fz_colorspace *model = dev->dest->colorspace;
903
	fz_pixmap *converted = NULL;
904
	fz_pixmap *scaled = NULL;
905
	int after;
906
	int dx, dy;
907
 
908
	if (!model)
909
	{
910
		fz_warn("cannot render image directly to an alpha mask");
911
		return;
912
	}
913
 
914
	if (image->w == 0 || image->h == 0)
915
		return;
916
 
917
	/* convert images with more components (cmyk->rgb) before scaling */
918
	/* convert images with fewer components (gray->rgb after scaling */
919
	/* convert images with expensive colorspace transforms after scaling */
920
 
921
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
922
		fz_knockout_begin(dev);
923
 
924
	after = 0;
925
	if (image->colorspace == fz_device_gray)
926
		after = 1;
927
 
928
	if (image->colorspace != model && !after)
929
	{
930
		converted = fz_new_pixmap_with_rect(model, fz_bound_pixmap(image));
931
		fz_convert_pixmap(image, converted);
932
		image = converted;
933
	}
934
 
935
	dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
936
	dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
937
	if (dx < image->w && dy < image->h)
938
	{
939
		int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
940
		scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
941
		if (scaled == NULL)
942
		{
943
			if (dx < 1)
944
				dx = 1;
945
			if (dy < 1)
946
				dy = 1;
947
			scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
948
		}
949
		if (scaled != NULL)
950
			image = scaled;
951
	}
952
 
953
	if (image->colorspace != model)
954
	{
955
		if ((image->colorspace == fz_device_gray && model == fz_device_rgb) ||
956
			(image->colorspace == fz_device_gray && model == fz_device_bgr))
957
		{
958
			/* We have special case rendering code for gray -> rgb/bgr */
959
		}
960
		else
961
		{
962
			converted = fz_new_pixmap_with_rect(model, fz_bound_pixmap(image));
963
			fz_convert_pixmap(image, converted);
964
			image = converted;
965
		}
966
	}
967
 
968
	fz_paint_image(dev->dest, dev->scissor, dev->shape, image, ctm, alpha * 255);
969
 
970
	if (scaled)
971
		fz_drop_pixmap(scaled);
972
	if (converted)
973
		fz_drop_pixmap(converted);
974
 
975
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
976
		fz_knockout_end(dev);
977
}
978
 
979
static void
980
fz_draw_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm,
981
	fz_colorspace *colorspace, float *color, float alpha)
982
{
983
	fz_draw_device *dev = user;
984
	fz_colorspace *model = dev->dest->colorspace;
985
	unsigned char colorbv[FZ_MAX_COLORS + 1];
986
	float colorfv[FZ_MAX_COLORS];
987
	fz_pixmap *scaled = NULL;
988
	int dx, dy;
989
	int i;
990
 
991
	if (image->w == 0 || image->h == 0)
992
		return;
993
 
994
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
995
		fz_knockout_begin(dev);
996
 
997
	dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
998
	dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
999
	if (dx < image->w && dy < image->h)
1000
	{
1001
		int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1002
		scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
1003
		if (scaled == NULL)
1004
		{
1005
			if (dx < 1)
1006
				dx = 1;
1007
			if (dy < 1)
1008
				dy = 1;
1009
			scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
1010
		}
1011
		if (scaled != NULL)
1012
			image = scaled;
1013
	}
1014
 
1015
	fz_convert_color(colorspace, color, model, colorfv);
1016
	for (i = 0; i < model->n; i++)
1017
		colorbv[i] = colorfv[i] * 255;
1018
	colorbv[i] = alpha * 255;
1019
 
1020
	fz_paint_image_with_color(dev->dest, dev->scissor, dev->shape, image, ctm, colorbv);
1021
 
1022
	if (scaled)
1023
		fz_drop_pixmap(scaled);
1024
 
1025
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
1026
		fz_knockout_begin(dev);
1027
}
1028
 
1029
static void
1030
fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix ctm)
1031
{
1032
	fz_draw_device *dev = user;
1033
	fz_colorspace *model = dev->dest->colorspace;
1034
	fz_bbox bbox;
1035
	fz_pixmap *mask, *dest, *shape;
1036
	fz_pixmap *scaled = NULL;
1037
	int dx, dy;
1038
 
1039
	if (dev->top == STACK_SIZE)
1040
	{
1041
		fz_warn("assert: too many buffers on stack");
1042
		return;
1043
	}
1044
 
1045
#ifdef DUMP_GROUP_BLENDS
1046
	dump_spaces(dev->top, "Clip (image mask) begin\n");
1047
#endif
1048
 
1049
	if (image->w == 0 || image->h == 0)
1050
	{
1051
		dev->stack[dev->top].scissor = dev->scissor;
1052
		dev->stack[dev->top].mask = NULL;
1053
		dev->stack[dev->top].dest = NULL;
1054
		dev->stack[dev->top].blendmode = dev->blendmode;
1055
		dev->scissor = fz_empty_bbox;
1056
		dev->top++;
1057
		return;
1058
	}
1059
 
1060
	bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect));
1061
	bbox = fz_intersect_bbox(bbox, dev->scissor);
1062
	if (rect)
1063
		bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect));
1064
 
1065
	mask = fz_new_pixmap_with_rect(NULL, bbox);
1066
	fz_clear_pixmap(mask);
1067
	dest = fz_new_pixmap_with_rect(model, bbox);
1068
	/* FIXME: See note #1 */
1069
	fz_clear_pixmap(dest);
1070
	if (dev->shape)
1071
	{
1072
		shape = fz_new_pixmap_with_rect(NULL, bbox);
1073
		fz_clear_pixmap(shape);
1074
	}
1075
	else
1076
		shape = NULL;
1077
 
1078
	dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b);
1079
	dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d);
1080
	if (dx < image->w && dy < image->h)
1081
	{
1082
		int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1083
		scaled = fz_transform_pixmap(image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit);
1084
		if (scaled == NULL)
1085
		{
1086
			if (dx < 1)
1087
				dx = 1;
1088
			if (dy < 1)
1089
				dy = 1;
1090
			scaled = fz_scale_pixmap(image, image->x, image->y, dx, dy);
1091
		}
1092
		if (scaled != NULL)
1093
			image = scaled;
1094
	}
1095
 
1096
	fz_paint_image(mask, bbox, dev->shape, image, ctm, 255);
1097
 
1098
	if (scaled)
1099
		fz_drop_pixmap(scaled);
1100
 
1101
	dev->stack[dev->top].scissor = dev->scissor;
1102
	dev->stack[dev->top].mask = mask;
1103
	dev->stack[dev->top].dest = dev->dest;
1104
	dev->stack[dev->top].shape = dev->shape;
1105
	/* FIXME: See note #1 */
1106
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
1107
	dev->scissor = bbox;
1108
	dev->dest = dest;
1109
	dev->shape = shape;
1110
	dev->top++;
1111
}
1112
 
1113
static void
1114
fz_draw_pop_clip(void *user)
1115
{
1116
	fz_draw_device *dev = user;
1117
	fz_pixmap *mask, *dest, *shape;
1118
	if (dev->top > 0)
1119
	{
1120
		dev->top--;
1121
		dev->scissor = dev->stack[dev->top].scissor;
1122
		mask = dev->stack[dev->top].mask;
1123
		dest = dev->stack[dev->top].dest;
1124
		shape = dev->stack[dev->top].shape;
1125
		dev->blendmode = dev->stack[dev->top].blendmode;
1126
 
1127
		/* We can get here with mask == NULL if the clipping actually
1128
		 * resolved to a rectangle earlier. In this case, we will
1129
		 * have a dest, and the shape will be unchanged.
1130
		 */
1131
		if (mask)
1132
		{
1133
			assert(dest);
1134
 
1135
#ifdef DUMP_GROUP_BLENDS
1136
			dump_spaces(dev->top, "");
1137
			fz_dump_blend(dev->dest, "Clipping ");
1138
			if (dev->shape)
1139
				fz_dump_blend(dev->shape, "/");
1140
			fz_dump_blend(dest, " onto ");
1141
			if (shape)
1142
				fz_dump_blend(shape, "/");
1143
			fz_dump_blend(mask, " with ");
1144
#endif
1145
			fz_paint_pixmap_with_mask(dest, dev->dest, mask);
1146
			if (shape != NULL)
1147
			{
1148
				assert(shape != dev->shape);
1149
				fz_paint_pixmap_with_mask(shape, dev->shape, mask);
1150
				fz_drop_pixmap(dev->shape);
1151
				dev->shape = shape;
1152
			}
1153
			fz_drop_pixmap(mask);
1154
			fz_drop_pixmap(dev->dest);
1155
			dev->dest = dest;
1156
#ifdef DUMP_GROUP_BLENDS
1157
			fz_dump_blend(dev->dest, " to get ");
1158
			if (dev->shape)
1159
				fz_dump_blend(dev->shape, "/");
1160
			printf("\n");
1161
#endif
1162
		}
1163
		else
1164
		{
1165
#ifdef DUMP_GROUP_BLENDS
1166
			dump_spaces(dev->top, "Clip End\n");
1167
#endif
1168
			assert(dest == NULL);
1169
			assert(shape == dev->shape);
1170
		}
1171
	}
1172
}
1173
 
1174
static void
1175
fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv)
1176
{
1177
	fz_draw_device *dev = user;
1178
	fz_pixmap *dest;
1179
	fz_pixmap *shape = dev->shape;
1180
	fz_bbox bbox;
1181
 
1182
	if (dev->top == STACK_SIZE)
1183
	{
1184
		fz_warn("assert: too many buffers on stack");
1185
		return;
1186
	}
1187
 
1188
	bbox = fz_round_rect(rect);
1189
	bbox = fz_intersect_bbox(bbox, dev->scissor);
1190
	dest = fz_new_pixmap_with_rect(fz_device_gray, bbox);
1191
	if (dev->shape)
1192
	{
1193
		/* FIXME: If we ever want to support AIS true, then we
1194
		 * probably want to create a shape pixmap here, using:
1195
		 *     shape = fz_new_pixmap_with_rect(NULL, bbox);
1196
		 * then, in the end_mask code, we create the mask from this
1197
		 * rather than dest.
1198
		 */
1199
		shape = NULL;
1200
	}
1201
 
1202
	if (luminosity)
1203
	{
1204
		float bc;
1205
		if (!colorspace)
1206
			colorspace = fz_device_gray;
1207
		fz_convert_color(colorspace, colorfv, fz_device_gray, &bc);
1208
		fz_clear_pixmap_with_color(dest, bc * 255);
1209
		if (shape)
1210
			fz_clear_pixmap_with_color(shape, 255);
1211
	}
1212
	else
1213
	{
1214
		fz_clear_pixmap(dest);
1215
		if (shape)
1216
			fz_clear_pixmap(shape);
1217
	}
1218
 
1219
	dev->stack[dev->top].scissor = dev->scissor;
1220
	dev->stack[dev->top].dest = dev->dest;
1221
	dev->stack[dev->top].luminosity = luminosity;
1222
	dev->stack[dev->top].shape = dev->shape;
1223
	dev->stack[dev->top].blendmode = dev->blendmode;
1224
#ifdef DUMP_GROUP_BLENDS
1225
	dump_spaces(dev->top, "Mask begin\n");
1226
#endif
1227
	dev->top++;
1228
 
1229
	dev->scissor = bbox;
1230
	dev->dest = dest;
1231
	dev->shape = shape;
1232
}
1233
 
1234
static void
1235
fz_draw_end_mask(void *user)
1236
{
1237
	fz_draw_device *dev = user;
1238
	fz_pixmap *mask = dev->dest;
1239
	fz_pixmap *maskshape = dev->shape;
1240
	fz_pixmap *temp, *dest;
1241
	fz_bbox bbox;
1242
	int luminosity;
1243
 
1244
	if (dev->top == STACK_SIZE)
1245
	{
1246
		fz_warn("assert: too many buffers on stack");
1247
		return;
1248
	}
1249
 
1250
	if (dev->top > 0)
1251
	{
1252
		/* pop soft mask buffer */
1253
		dev->top--;
1254
		luminosity = dev->stack[dev->top].luminosity;
1255
		dev->scissor = dev->stack[dev->top].scissor;
1256
		dev->dest = dev->stack[dev->top].dest;
1257
		dev->shape = dev->stack[dev->top].shape;
1258
 
1259
		/* convert to alpha mask */
1260
		temp = fz_alpha_from_gray(mask, luminosity);
1261
		fz_drop_pixmap(mask);
1262
		fz_drop_pixmap(maskshape);
1263
 
1264
		/* create new dest scratch buffer */
1265
		bbox = fz_bound_pixmap(temp);
1266
		dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox);
1267
		/* FIXME: See note #1 */
1268
		fz_clear_pixmap(dest);
1269
 
1270
		/* push soft mask as clip mask */
1271
		dev->stack[dev->top].scissor = dev->scissor;
1272
		dev->stack[dev->top].mask = temp;
1273
		dev->stack[dev->top].dest = dev->dest;
1274
		/* FIXME: See note #1 */
1275
		dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
1276
		/* If we have a shape, then it'll need to be masked with the
1277
		 * clip mask when we pop. So create a new shape now. */
1278
		if (dev->shape)
1279
		{
1280
			dev->stack[dev->top].shape = dev->shape;
1281
			dev->shape = fz_new_pixmap_with_rect(NULL, bbox);
1282
			fz_clear_pixmap(dev->shape);
1283
		}
1284
		dev->scissor = bbox;
1285
		dev->dest = dest;
1286
#ifdef DUMP_GROUP_BLENDS
1287
		dump_spaces(dev->top, "Mask -> Clip\n");
1288
#endif
1289
		dev->top++;
1290
	}
1291
}
1292
 
1293
static void
1294
fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int blendmode, float alpha)
1295
{
1296
	fz_draw_device *dev = user;
1297
	fz_colorspace *model = dev->dest->colorspace;
1298
	fz_bbox bbox;
1299
	fz_pixmap *dest, *shape;
1300
 
1301
	if (dev->top == STACK_SIZE)
1302
	{
1303
		fz_warn("assert: too many buffers on stack");
1304
		return;
1305
	}
1306
 
1307
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
1308
		fz_knockout_begin(dev);
1309
 
1310
	bbox = fz_round_rect(rect);
1311
	bbox = fz_intersect_bbox(bbox, dev->scissor);
1312
	dest = fz_new_pixmap_with_rect(model, bbox);
1313
 
1314
#ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED
1315
	knockout = 0;
1316
	isolated = 1;
1317
#endif
1318
 
1319
	if (isolated)
1320
	{
1321
		fz_clear_pixmap(dest);
1322
	}
1323
	else
1324
	{
1325
		fz_copy_pixmap_rect(dest, dev->dest, bbox);
1326
	}
1327
 
1328
	if (blendmode == 0 && alpha == 1.0 && isolated)
1329
	{
1330
		/* We can render direct to any existing shape plane. If there
1331
		 * isn't one, we don't need to make one. */
1332
		shape = dev->shape;
1333
	}
1334
	else
1335
	{
1336
		shape = fz_new_pixmap_with_rect(NULL, bbox);
1337
		fz_clear_pixmap(shape);
1338
	}
1339
 
1340
	dev->stack[dev->top].alpha = alpha;
1341
	dev->stack[dev->top].blendmode = dev->blendmode;
1342
	dev->stack[dev->top].scissor = dev->scissor;
1343
	dev->stack[dev->top].dest = dev->dest;
1344
	dev->stack[dev->top].shape = dev->shape;
1345
#ifdef DUMP_GROUP_BLENDS
1346
	dump_spaces(dev->top, "Group Begin\n");
1347
#endif
1348
	dev->top++;
1349
 
1350
	dev->scissor = bbox;
1351
	dev->dest = dest;
1352
	dev->shape = shape;
1353
	dev->blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0);
1354
}
1355
 
1356
static void
1357
fz_draw_end_group(void *user)
1358
{
1359
	fz_draw_device *dev = user;
1360
	fz_pixmap *group = dev->dest;
1361
	fz_pixmap *shape = dev->shape;
1362
	int blendmode;
1363
	int isolated;
1364
	float alpha;
1365
 
1366
	if (dev->top > 0)
1367
	{
1368
		dev->top--;
1369
		alpha = dev->stack[dev->top].alpha;
1370
		blendmode = dev->blendmode & FZ_BLEND_MODEMASK;
1371
		isolated = dev->blendmode & FZ_BLEND_ISOLATED;
1372
		dev->blendmode = dev->stack[dev->top].blendmode;
1373
		dev->shape = dev->stack[dev->top].shape;
1374
		dev->dest = dev->stack[dev->top].dest;
1375
		dev->scissor = dev->stack[dev->top].scissor;
1376
 
1377
#ifdef DUMP_GROUP_BLENDS
1378
		dump_spaces(dev->top, "");
1379
		fz_dump_blend(group, "Blending ");
1380
		if (shape)
1381
			fz_dump_blend(shape, "/");
1382
		fz_dump_blend(dev->dest, " onto ");
1383
		if (dev->shape)
1384
			fz_dump_blend(dev->shape, "/");
1385
		if (alpha != 1.0f)
1386
			printf(" (alpha %g)", alpha);
1387
		if (blendmode != 0)
1388
			printf(" (blend %d)", blendmode);
1389
		if (isolated != 0)
1390
			printf(" (isolated)");
1391
		if (blendmode & FZ_BLEND_KNOCKOUT)
1392
			printf(" (knockout)");
1393
#endif
1394
		if ((blendmode == 0) && (shape == NULL))
1395
			fz_paint_pixmap(dev->dest, group, alpha * 255);
1396
		else
1397
			fz_blend_pixmap(dev->dest, group, alpha * 255, blendmode, isolated, shape);
1398
 
1399
		fz_drop_pixmap(group);
1400
		if (shape != dev->shape)
1401
		{
1402
			if (dev->shape)
1403
			{
1404
				fz_paint_pixmap(dev->shape, shape, alpha * 255);
1405
			}
1406
			fz_drop_pixmap(shape);
1407
		}
1408
#ifdef DUMP_GROUP_BLENDS
1409
		fz_dump_blend(dev->dest, " to get ");
1410
		if (dev->shape)
1411
			fz_dump_blend(dev->shape, "/");
1412
		printf("\n");
1413
#endif
1414
	}
1415
 
1416
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
1417
		fz_knockout_end(dev);
1418
}
1419
 
1420
static void
1421
fz_draw_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm)
1422
{
1423
	fz_draw_device *dev = user;
1424
	fz_colorspace *model = dev->dest->colorspace;
1425
	fz_pixmap *dest;
1426
	fz_bbox bbox;
1427
 
1428
	/* area, view, xstep, ystep are in pattern space */
1429
	/* ctm maps from pattern space to device space */
1430
 
1431
	if (dev->top == STACK_SIZE)
1432
	{
1433
		fz_warn("assert: too many buffers on stack");
1434
		return;
1435
	}
1436
 
1437
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
1438
		fz_knockout_begin(dev);
1439
 
1440
	bbox = fz_round_rect(fz_transform_rect(ctm, view));
1441
	dest = fz_new_pixmap_with_rect(model, bbox);
1442
	/* FIXME: See note #1 */
1443
	fz_clear_pixmap(dest);
1444
 
1445
	dev->stack[dev->top].scissor = dev->scissor;
1446
	dev->stack[dev->top].dest = dev->dest;
1447
	dev->stack[dev->top].shape = dev->shape;
1448
	/* FIXME: See note #1 */
1449
	dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
1450
	dev->stack[dev->top].xstep = xstep;
1451
	dev->stack[dev->top].ystep = ystep;
1452
	dev->stack[dev->top].area = area;
1453
	dev->stack[dev->top].ctm = ctm;
1454
#ifdef DUMP_GROUP_BLENDS
1455
	dump_spaces(dev->top, "Tile begin\n");
1456
#endif
1457
	dev->top++;
1458
 
1459
	dev->scissor = bbox;
1460
	dev->dest = dest;
1461
}
1462
 
1463
static void
1464
fz_draw_end_tile(void *user)
1465
{
1466
	fz_draw_device *dev = user;
1467
	fz_pixmap *tile = dev->dest;
1468
	float xstep, ystep;
1469
	fz_matrix ctm, ttm;
1470
	fz_rect area;
1471
	int x0, y0, x1, y1, x, y;
1472
 
1473
	if (dev->top > 0)
1474
	{
1475
		dev->top--;
1476
#ifdef DUMP_GROUP_BLENDS
1477
		dump_spaces(dev->top, "Tile end\n");
1478
#endif
1479
		xstep = dev->stack[dev->top].xstep;
1480
		ystep = dev->stack[dev->top].ystep;
1481
		area = dev->stack[dev->top].area;
1482
		ctm = dev->stack[dev->top].ctm;
1483
		dev->scissor = dev->stack[dev->top].scissor;
1484
		dev->dest = dev->stack[dev->top].dest;
1485
		dev->blendmode = dev->stack[dev->top].blendmode;
1486
 
1487
 
1488
		x0 = floorf(area.x0 / xstep);
1489
		y0 = floorf(area.y0 / ystep);
1490
		x1 = ceilf(area.x1 / xstep);
1491
		y1 = ceilf(area.y1 / ystep);
1492
 
1493
		ctm.e = tile->x;
1494
		ctm.f = tile->y;
1495
 
1496
		for (y = y0; y < y1; y++)
1497
		{
1498
			for (x = x0; x < x1; x++)
1499
			{
1500
				ttm = fz_concat(fz_translate(x * xstep, y * ystep), ctm);
1501
				tile->x = ttm.e;
1502
				tile->y = ttm.f;
1503
				fz_paint_pixmap_with_rect(dev->dest, tile, 255, dev->scissor);
1504
			}
1505
		}
1506
 
1507
		fz_drop_pixmap(tile);
1508
	}
1509
 
1510
	if (dev->blendmode & FZ_BLEND_KNOCKOUT)
1511
		fz_knockout_begin(dev);
1512
}
1513
 
1514
static void
1515
fz_draw_free_user(void *user)
1516
{
1517
	fz_draw_device *dev = user;
1518
	/* TODO: pop and free the stacks */
1519
	if (dev->top > 0)
1520
		fz_warn("items left on stack in draw device: %d", dev->top);
1521
	fz_free_gel(dev->gel);
1522
	fz_free(dev);
1523
}
1524
 
1525
fz_device *
1526
fz_new_draw_device(fz_glyph_cache *cache, fz_pixmap *dest)
1527
{
1528
	fz_device *dev;
1529
	fz_draw_device *ddev = fz_malloc(sizeof(fz_draw_device));
1530
	ddev->cache = cache;
1531
	ddev->gel = fz_new_gel();
1532
	ddev->dest = dest;
1533
	ddev->shape = NULL;
1534
	ddev->top = 0;
1535
	ddev->blendmode = 0;
1536
	ddev->flags = 0;
1537
 
1538
	ddev->scissor.x0 = dest->x;
1539
	ddev->scissor.y0 = dest->y;
1540
	ddev->scissor.x1 = dest->x + dest->w;
1541
	ddev->scissor.y1 = dest->y + dest->h;
1542
 
1543
	dev = fz_new_device(ddev);
1544
	dev->free_user = fz_draw_free_user;
1545
 
1546
	dev->fill_path = fz_draw_fill_path;
1547
	dev->stroke_path = fz_draw_stroke_path;
1548
	dev->clip_path = fz_draw_clip_path;
1549
	dev->clip_stroke_path = fz_draw_clip_stroke_path;
1550
 
1551
	dev->fill_text = fz_draw_fill_text;
1552
	dev->stroke_text = fz_draw_stroke_text;
1553
	dev->clip_text = fz_draw_clip_text;
1554
	dev->clip_stroke_text = fz_draw_clip_stroke_text;
1555
	dev->ignore_text = fz_draw_ignore_text;
1556
 
1557
	dev->fill_image_mask = fz_draw_fill_image_mask;
1558
	dev->clip_image_mask = fz_draw_clip_image_mask;
1559
	dev->fill_image = fz_draw_fill_image;
1560
	dev->fill_shade = fz_draw_fill_shade;
1561
 
1562
	dev->pop_clip = fz_draw_pop_clip;
1563
 
1564
	dev->begin_mask = fz_draw_begin_mask;
1565
	dev->end_mask = fz_draw_end_mask;
1566
	dev->begin_group = fz_draw_begin_group;
1567
	dev->end_group = fz_draw_end_group;
1568
 
1569
	dev->begin_tile = fz_draw_begin_tile;
1570
	dev->end_tile = fz_draw_end_tile;
1571
 
1572
	return dev;
1573
}
1574
 
1575
fz_device *
1576
fz_new_draw_device_type3(fz_glyph_cache *cache, fz_pixmap *dest)
1577
{
1578
	fz_device *dev = fz_new_draw_device(cache, dest);
1579
	fz_draw_device *ddev = dev->user;
1580
	ddev->flags |= FZ_DRAWDEV_FLAGS_TYPE3;
1581
	return dev;
1582
}