1 #include "mupdf/fitz.h"
2 
3 #include "context-imp.h"
4 #include "color-imp.h"
5 #include "draw-imp.h"
6 #include "glyph-imp.h"
7 #include "pixmap-imp.h"
8 
9 #include <string.h>
10 #include <assert.h>
11 #include <math.h>
12 #include <float.h>
13 
14 #define STACK_SIZE 96
15 
16 /* Enable the following to attempt to support knockout and/or isolated
17  * blending groups. */
18 #define ATTEMPT_KNOCKOUT_AND_ISOLATED
19 
20 /* Enable the following to help debug group blending. */
21 #undef DUMP_GROUP_BLENDS
22 
23 /* Enable the following to help debug graphics stack pushes/pops */
24 #undef DUMP_STACK_CHANGES
25 
26 enum {
27 	FZ_DRAWDEV_FLAGS_TYPE3 = 1,
28 };
29 
30 typedef struct {
31 	fz_irect scissor;
32 	fz_pixmap *dest;
33 	fz_pixmap *mask;
34 	fz_pixmap *shape;
35 	fz_pixmap *group_alpha;
36 	int blendmode;
37 	int id, encache;
38 	float alpha;
39 	fz_matrix ctm;
40 	float xstep, ystep;
41 	fz_irect area;
42 } fz_draw_state;
43 
44 typedef struct fz_draw_device
45 {
46 	fz_device super;
47 	fz_matrix transform;
48 	fz_rasterizer *rast;
49 	fz_default_colorspaces *default_cs;
50 	fz_colorspace *proof_cs;
51 	int flags;
52 	int resolve_spots;
53 	int top;
54 	fz_scale_cache *cache_x;
55 	fz_scale_cache *cache_y;
56 	fz_draw_state *stack;
57 	int stack_cap;
58 	fz_draw_state init_stack[STACK_SIZE];
59 } fz_draw_device;
60 
61 #ifdef DUMP_GROUP_BLENDS
62 
63 #include <stdio.h>
64 
65 static int group_dump_count = 0;
66 
fz_dump_blend(fz_context * ctx,const char * s,fz_pixmap * pix)67 static void fz_dump_blend(fz_context *ctx, const char *s, fz_pixmap *pix)
68 {
69 	char name[80];
70 	int psd = 0;
71 
72 	if (!pix)
73 		return;
74 
75 	if (pix->s || fz_colorspace_is_subtractive(ctx, pix->colorspace))
76 		psd = 1;
77 
78 	fz_snprintf(name, sizeof(name), "dump%02d.%s", group_dump_count, psd ? "psd" : "png");
79 	printf("%s%02d%s(%p)", s ? s : "", group_dump_count++, psd ? "(PSD)" : "", pix);
80 	if (psd)
81 		fz_save_pixmap_as_psd(ctx, pix, name);
82 	else
83 		fz_save_pixmap_as_png(ctx, pix, name);
84 }
85 
dump_spaces(int x,const char * s)86 static void dump_spaces(int x, const char *s)
87 {
88 	int i;
89 	for (i = 0; i < x; i++)
90 		printf(" ");
91 	printf("%s", s);
92 }
93 
94 #endif
95 
96 #ifdef DUMP_STACK_CHANGES
97 #define STACK_PUSHED(A) stack_change(ctx, dev, '>', A)
98 #define STACK_POPPED(A) stack_change(ctx, dev, '<', A)
99 #define STACK_CONVERT(A) stack_change(ctx, dev, '=', A)
stack_change(fz_context * ctx,fz_draw_device * dev,int c,const char * s)100 static void stack_change(fz_context *ctx, fz_draw_device *dev, int c, const char *s)
101 {
102 	int n, depth = dev->top;
103 	if (c != '<')
104 		depth--;
105 	n = depth;
106 	while (n-- > 0)
107 		fputc('\t', stderr);
108 	fprintf(stderr, "%c%s (%d)\n", c, s, depth);
109 }
110 #else
111 #define STACK_PUSHED(A) do {} while (0)
112 #define STACK_POPPED(A) do {} while (0)
113 #define STACK_CONVERT(A) do {} while (0)
114 #endif
115 
116 /* Logic below assumes that default cs is set to color context cs if there
117  * was not a default in the document for that particular cs
118  */
fz_default_colorspace(fz_context * ctx,fz_default_colorspaces * default_cs,fz_colorspace * cs)119 static fz_colorspace *fz_default_colorspace(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
120 {
121 	if (cs == NULL)
122 		return NULL;
123 	if (default_cs == NULL)
124 		return cs;
125 
126 	switch (fz_colorspace_type(ctx, cs))
127 	{
128 	case FZ_COLORSPACE_GRAY:
129 		if (cs == fz_device_gray(ctx))
130 			return fz_default_gray(ctx, default_cs);
131 		break;
132 	case FZ_COLORSPACE_RGB:
133 		if (cs == fz_device_rgb(ctx))
134 			return fz_default_rgb(ctx, default_cs);
135 		break;
136 	case FZ_COLORSPACE_CMYK:
137 		if (cs == fz_device_cmyk(ctx))
138 			return fz_default_cmyk(ctx, default_cs);
139 		break;
140 	default:
141 		break;
142 	}
143 	return cs;
144 }
145 
grow_stack(fz_context * ctx,fz_draw_device * dev)146 static void grow_stack(fz_context *ctx, fz_draw_device *dev)
147 {
148 	int max = dev->stack_cap * 2;
149 	fz_draw_state *stack;
150 	if (dev->stack == &dev->init_stack[0])
151 	{
152 		stack = fz_malloc_array(ctx, max, fz_draw_state);
153 		memcpy(stack, dev->stack, sizeof(*stack) * dev->stack_cap);
154 	}
155 	else
156 	{
157 		stack = fz_realloc_array(ctx, dev->stack, max, fz_draw_state);
158 	}
159 	dev->stack = Memento_label(stack, "draw_stack");
160 	dev->stack_cap = max;
161 }
162 
163 /* 'Push' the stack. Returns a pointer to the current state, with state[1]
164  * already having been initialised to contain the same thing. Simply
165  * change any contents of state[1] that you want to and continue. */
push_stack(fz_context * ctx,fz_draw_device * dev,const char * message)166 static fz_draw_state *push_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
167 {
168 	fz_draw_state *state;
169 	if (dev->top == dev->stack_cap-1)
170 		grow_stack(ctx, dev);
171 	state = &dev->stack[dev->top];
172 	dev->top++;
173 	memcpy(&state[1], state, sizeof(*state));
174 	STACK_PUSHED(message);
175 	return state;
176 }
177 
pop_stack(fz_context * ctx,fz_draw_device * dev,const char * message)178 static fz_draw_state *pop_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
179 {
180 	fz_draw_state *state = &dev->stack[--dev->top];
181 	STACK_POPPED(message);
182 	return state;
183 }
184 
convert_stack(fz_context * ctx,fz_draw_device * dev,const char * message)185 static fz_draw_state *convert_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
186 {
187 	fz_draw_state *state = &dev->stack[dev->top-1];
188 	STACK_CONVERT(message);
189 	return state;
190 }
191 
192 static fz_draw_state *
fz_knockout_begin(fz_context * ctx,fz_draw_device * dev)193 fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
194 {
195 	fz_irect bbox, ga_bbox;
196 	fz_draw_state *state = &dev->stack[dev->top];
197 	int isolated = state->blendmode & FZ_BLEND_ISOLATED;
198 
199 	if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0)
200 		return state;
201 
202 	state = push_stack(ctx, dev, "knockout");
203 
204 	bbox = fz_pixmap_bbox(ctx, state->dest);
205 	bbox = fz_intersect_irect(bbox, state->scissor);
206 	state[1].dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
207 	if (state[0].group_alpha)
208 	{
209 		ga_bbox = fz_pixmap_bbox(ctx, state->group_alpha);
210 		ga_bbox = fz_intersect_irect(ga_bbox, state->scissor);
211 		state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, state->group_alpha->colorspace, ga_bbox, state->group_alpha->seps, state->group_alpha->alpha);
212 	}
213 
214 	if (isolated)
215 	{
216 		fz_clear_pixmap(ctx, state[1].dest);
217 		if (state[1].group_alpha)
218 			fz_clear_pixmap(ctx, state[1].group_alpha);
219 	}
220 	else
221 	{
222 		/* Find the last but one destination to copy */
223 		int i = dev->top-1; /* i = the one on entry (i.e. the last one) */
224 		fz_draw_state *prev = state;
225 		while (i > 0)
226 		{
227 			prev = &dev->stack[--i];
228 			if (prev->dest != state->dest)
229 				break;
230 		}
231 		if (prev->dest)
232 		{
233 			fz_copy_pixmap_rect(ctx, state[1].dest, prev->dest, bbox, dev->default_cs);
234 			if (state[1].group_alpha)
235 			{
236 				if (prev->group_alpha)
237 					fz_copy_pixmap_rect(ctx, state[1].group_alpha, prev->group_alpha, ga_bbox, dev->default_cs);
238 				else
239 					fz_clear_pixmap(ctx, state[1].group_alpha);
240 			}
241 		}
242 		else
243 		{
244 			fz_clear_pixmap(ctx, state[1].dest);
245 			if (state[1].group_alpha)
246 				fz_clear_pixmap(ctx, state[1].group_alpha);
247 		}
248 	}
249 
250 	/* Knockout groups (and only knockout groups) rely on shape */
251 	state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
252 	fz_clear_pixmap(ctx, state[1].shape);
253 
254 #ifdef DUMP_GROUP_BLENDS
255 	dump_spaces(dev->top-1, "");
256 	fz_dump_blend(ctx, "Knockout begin: background is ", state[1].dest);
257 	if (state[1].shape)
258 		fz_dump_blend(ctx, "/S=", state[1].shape);
259 	if (state[1].group_alpha)
260 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
261 	printf("\n");
262 #endif
263 
264 	state[1].scissor = bbox;
265 	state[1].blendmode &= ~(FZ_BLEND_MODEMASK | FZ_BLEND_ISOLATED);
266 
267 	return &state[1];
268 }
269 
fz_knockout_end(fz_context * ctx,fz_draw_device * dev)270 static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
271 {
272 	fz_draw_state *state;
273 
274 	if (dev->top == 0)
275 		fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected knockout end");
276 
277 	state = pop_stack(ctx, dev, "knockout");
278 	if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0)
279 		return;
280 
281 	assert((state[1].blendmode & FZ_BLEND_ISOLATED) == 0);
282 	assert((state[1].blendmode & FZ_BLEND_MODEMASK) == 0);
283 	assert(state[1].shape);
284 
285 #ifdef DUMP_GROUP_BLENDS
286 	dump_spaces(dev->top, "");
287 	fz_dump_blend(ctx, "Knockout end: blending ", state[1].dest);
288 	if (state[1].shape)
289 		fz_dump_blend(ctx, "/S=", state[1].shape);
290 	if (state[1].group_alpha)
291 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
292 	fz_dump_blend(ctx, " onto ", state[0].dest);
293 	if (state[0].shape)
294 		fz_dump_blend(ctx, "/S=", state[0].shape);
295 	if (state[0].group_alpha)
296 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
297 	if ((state->blendmode & FZ_BLEND_MODEMASK) != 0)
298 		printf(" (blend %d)", state->blendmode & FZ_BLEND_MODEMASK);
299 	if ((state->blendmode & FZ_BLEND_ISOLATED) != 0)
300 		printf(" (isolated)");
301 	printf(" (knockout)");
302 #endif
303 
304 	fz_blend_pixmap_knockout(ctx, state[0].dest, state[1].dest, state[1].shape);
305 	fz_drop_pixmap(ctx, state[1].dest);
306 	state[1].dest = NULL;
307 
308 	if (state[1].group_alpha && state[0].group_alpha != state[1].group_alpha)
309 	{
310 		if (state[0].group_alpha)
311 			fz_blend_pixmap_knockout(ctx, state[0].group_alpha, state[1].group_alpha, state[1].shape);
312 		fz_drop_pixmap(ctx, state[1].group_alpha);
313 		state[1].group_alpha = NULL;
314 	}
315 
316 	if (state[0].shape != state[1].shape)
317 	{
318 		if (state[0].shape)
319 			fz_paint_pixmap(state[0].shape, state[1].shape, 255);
320 		fz_drop_pixmap(ctx, state[1].shape);
321 		state[1].shape = NULL;
322 	}
323 
324 #ifdef DUMP_GROUP_BLENDS
325 	fz_dump_blend(ctx, " to get ", state[0].dest);
326 	if (state[0].shape)
327 		fz_dump_blend(ctx, "/S=", state[0].shape);
328 	if (state[0].group_alpha)
329 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
330 	printf("\n");
331 #endif
332 }
333 
334 static int
colors_supported(fz_context * ctx,fz_colorspace * cs,fz_pixmap * dest)335 colors_supported(fz_context *ctx, fz_colorspace *cs, fz_pixmap *dest)
336 {
337 	/* Even if we support separations in the destination, if the color space has CMY or K as one of
338 	 * its colorants and we are in RGB or Gray we will want to do the tint transform */
339 	if (!fz_colorspace_is_subtractive(ctx, dest->colorspace) && fz_colorspace_device_n_has_cmyk(ctx, cs))
340 		return 0;
341 
342 	/* If we have separations then we should support it */
343 	if (dest->seps)
344 		return 1;
345 
346 	/* If our destination is CMYK and the source color space is only C, M, Y or K we support it
347 	 * even if we have no seps */
348 	if (fz_colorspace_is_subtractive(ctx, dest->colorspace))
349 	{
350 		int i, n;
351 		if (fz_colorspace_device_n_has_only_cmyk(ctx, cs))
352 			return 1;
353 
354 		n = fz_colorspace_n(ctx, cs);
355 		for (i = 0; i < n; i++)
356 		{
357 			const char *name = fz_colorspace_colorant(ctx, cs, i);
358 
359 			if (!name)
360 				return 0;
361 			if (!strcmp(name, "All"))
362 				continue;
363 			if (!strcmp(name, "Cyan"))
364 				continue;
365 			if (!strcmp(name, "Magenta"))
366 				continue;
367 			if (!strcmp(name, "Yellow"))
368 				continue;
369 			if (!strcmp(name, "Black"))
370 				continue;
371 			if (!strcmp(name, "None"))
372 				continue;
373 			return 0;
374 		}
375 		return 1;
376 	}
377 
378 	return 0;
379 }
380 
381 static fz_overprint *
set_op_from_spaces(fz_context * ctx,fz_overprint * op,const fz_pixmap * dest,fz_colorspace * src,int opm)382 set_op_from_spaces(fz_context *ctx, fz_overprint *op, const fz_pixmap *dest, fz_colorspace *src, int opm)
383 {
384 	int dn, sn, i, j, dc;
385 
386 	if (!op)
387 		return NULL;
388 
389 	if (!fz_colorspace_is_subtractive(ctx, src) || !fz_colorspace_is_subtractive(ctx, dest->colorspace))
390 		return NULL;
391 
392 	sn = fz_colorspace_n(ctx, src);
393 	dn = dest->n - dest->alpha;
394 	dc = dn - dest->s;
395 
396 	/* If a source colorant is not mentioned in the destination
397 	 * colorants (either process or spots), then it will be mapped
398 	 * to process colorants. In this case, the process colorants
399 	 * can never be protected.
400 	 */
401 	for (j = 0; j < sn; j++)
402 	{
403 		/* Run through the colorants looking for one that isn't mentioned.
404 		 * i.e. continue if we we find one, break if not. */
405 		const char *sname = fz_colorspace_colorant(ctx, src, j);
406 		if (!sname)
407 			break;
408 		if (!strcmp(sname, "All") || !strcmp(sname, "None"))
409 			continue;
410 		for (i = 0; i < dc; i++)
411 		{
412 			const char *name = fz_colorspace_colorant(ctx, dest->colorspace, i);
413 			if (!name)
414 				continue;
415 			if (!strcmp(name, sname))
416 				break;
417 		}
418 		if (i != dc)
419 			continue;
420 		for (; i < dn; i++)
421 		{
422 			const char *name = fz_separation_name(ctx, dest->seps, i - dc);
423 			if (!name)
424 				continue;
425 			if (!strcmp(name, sname))
426 				break;
427 		}
428 		if (i == dn)
429 		{
430 			/* This source colorant wasn't mentioned */
431 			break;
432 		}
433 	}
434 	if (j == sn)
435 	{
436 		/* We did not find any source colorants that weren't mentioned, so
437 		 * process colorants might not be touched... */
438 		for (i = 0; i < dc; i++)
439 		{
440 			const char *name = fz_colorspace_colorant(ctx, dest->colorspace, i);
441 
442 			for (j = 0; j < sn; j++)
443 			{
444 				const char *sname = fz_colorspace_colorant(ctx, src, j);
445 				if (!name || !sname)
446 					continue;
447 				if (!strcmp(name, sname))
448 					break;
449 				if (!strcmp(sname, "All"))
450 					break;
451 			}
452 			if (j == sn)
453 				fz_set_overprint(op, i);
454 		}
455 	}
456 	for (i = dc; i < dn; i++)
457 	{
458 		const char *name = fz_separation_name(ctx, dest->seps, i - dc);
459 
460 		for (j = 0; j < sn; j++)
461 		{
462 			const char *sname = fz_colorspace_colorant(ctx, src, j);
463 			if (!name || !sname)
464 				continue;
465 			if (!strcmp(name, sname))
466 				break;
467 			if (!strcmp(sname, "All"))
468 				break;
469 		}
470 		if (j == sn)
471 			fz_set_overprint(op, i);
472 	}
473 
474 	return op;
475 }
476 
477 static fz_overprint *
resolve_color(fz_context * ctx,fz_overprint * op,const float * color,fz_colorspace * colorspace,float alpha,fz_color_params color_params,unsigned char * colorbv,fz_pixmap * dest)478 resolve_color(fz_context *ctx,
479 	fz_overprint *op,
480 	const float *color,
481 	fz_colorspace *colorspace,
482 	float alpha,
483 	fz_color_params color_params,
484 	unsigned char *colorbv,
485 	fz_pixmap *dest)
486 {
487 	float colorfv[FZ_MAX_COLORS];
488 	int i;
489 	int n = dest->n - dest->alpha;
490 	fz_colorspace *model = dest->colorspace;
491 	int devn, devgray;
492 	int effective_opm;
493 
494 	if (colorspace == NULL && model != NULL)
495 		fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color");
496 
497 	effective_opm = color_params.opm;
498 	devn = fz_colorspace_is_device_n(ctx, colorspace);
499 	devgray = fz_colorspace_is_device_gray(ctx, colorspace);
500 
501 	/* We can only overprint when enabled, and when we are in a subtractive colorspace */
502 	if (color_params.op == 0 || !fz_colorspace_is_subtractive(ctx, dest->colorspace))
503 		op = NULL;
504 
505 	else if (devgray)
506 	{
507 		/* Device Gray is additive, but seems to still be
508 		 * counted for overprint (see
509 		 * Ghent_V3.0/030_Gray_K_black_OP_x1a.pdf 030.pdf). */
510 	}
511 
512 	/* If we are in a CMYK space (i.e. not a devn one, given we know we are subtractive at this point),
513 	 * then we only adhere to overprint mode if it's the same space as the destination. */
514 	/* FIXME: Possibly we need a better equivalency test here. */
515 	else if (!devn && colorspace != dest->colorspace)
516 	{
517 		effective_opm = 0;
518 	}
519 
520 	if (n == 0)
521 		i = 0;
522 	else if (devn && colors_supported(ctx, colorspace, dest))
523 	{
524 		fz_convert_separation_colors(ctx, colorspace, color, dest->seps, dest->colorspace, colorfv, color_params);
525 		for (i = 0; i < n; i++)
526 			colorbv[i] = colorfv[i] * 255;
527 		op = set_op_from_spaces(ctx, op, dest, colorspace, effective_opm);
528 	}
529 	else
530 	{
531 		int c = n - dest->s;
532 		fz_convert_color(ctx, colorspace, color, dest->colorspace, colorfv, NULL, color_params);
533 		for (i = 0; i < c; i++)
534 			colorbv[i] = colorfv[i] * 255;
535 		for (; i < n; i++)
536 		{
537 			colorfv[i] = 0;
538 			colorbv[i] = 0;
539 		}
540 	}
541 	colorbv[i] = alpha * 255;
542 
543 	/* op && !devn => overprinting in cmyk or devicegray. */
544 	if (op && !devn)
545 	{
546 		/* We are overprinting, so protect all spots. */
547 		for (i = 4; i < n; i++)
548 			fz_set_overprint(op, i);
549 		/* If OPM, then protect all components for which the color values are zero.
550 		 * (but only if we're in devicecmyk). */
551 		if (effective_opm == 1 && colorspace != fz_device_gray(ctx))
552 			for (i = 0; i < n; i++)
553 				if (colorfv[i] == 0)
554 					fz_set_overprint(op, i);
555 	}
556 
557 	return op;
558 }
559 
560 static fz_draw_state *
push_group_for_separations(fz_context * ctx,fz_draw_device * dev,fz_color_params color_params,fz_default_colorspaces * default_cs)561 push_group_for_separations(fz_context *ctx, fz_draw_device *dev, fz_color_params color_params, fz_default_colorspaces *default_cs)
562 {
563 	fz_separations *clone = fz_clone_separations_for_overprint(ctx, dev->stack[0].dest->seps);
564 	fz_colorspace *oi = fz_default_output_intent(ctx, default_cs);
565 	fz_colorspace *dcs = fz_device_cmyk(ctx);
566 
567 	/* Pick sep target CMYK based upon proof and output intent settings.  Priority
568 	* is oi, proof, devicecmyk. */
569 	if (dev->proof_cs)
570 	{
571 		dcs = dev->proof_cs;
572 	}
573 
574 	if (oi)
575 	{
576 		dcs = oi;
577 	}
578 
579 	/* Not needed if dest has the seps, and we are not using a proof or the target is the same as the proof and we don't have an oi or the target is the same as the oi */
580 	if ((clone == dev->stack[0].dest->seps) && (dev->proof_cs == NULL || dev->proof_cs == dev->stack[0].dest->colorspace) && (oi == NULL || oi == dev->stack[0].dest->colorspace))
581 	{
582 		fz_drop_separations(ctx, clone);
583 		dev->resolve_spots = 0;
584 		return &dev->stack[0];
585 	}
586 
587 	/* Make a new pixmap to render to. */
588 	fz_try(ctx)
589 	{
590 		push_stack(ctx, dev, "separations");
591 		dev->stack[1].dest = fz_clone_pixmap_area_with_different_seps(ctx, dev->stack[0].dest, &dev->stack[0].scissor, dcs, clone, color_params, default_cs);
592 	}
593 	fz_always(ctx)
594 		fz_drop_separations(ctx, clone);
595 	fz_catch(ctx)
596 		fz_rethrow(ctx);
597 
598 	return &dev->stack[1];
599 }
600 
601 static void
fz_draw_fill_path(fz_context * ctx,fz_device * devp,const fz_path * path,int even_odd,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)602 fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int even_odd, fz_matrix in_ctm,
603 	fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
604 {
605 	fz_draw_device *dev = (fz_draw_device*)devp;
606 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
607 	fz_rasterizer *rast = dev->rast;
608 	fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
609 	float expansion = fz_matrix_expansion(ctm);
610 	float flatness;
611 	unsigned char colorbv[FZ_MAX_COLORS + 1];
612 	fz_irect bbox;
613 	fz_draw_state *state = &dev->stack[dev->top];
614 	fz_overprint op = { { 0 } };
615 	fz_overprint *eop;
616 
617 	if (dev->top == 0 && dev->resolve_spots)
618 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
619 
620 	if (expansion < FLT_EPSILON)
621 		expansion = 1;
622 	flatness = 0.3f / expansion;
623 	if (flatness < 0.001f)
624 		flatness = 0.001f;
625 
626 	bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
627 	if (fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, &bbox))
628 		return;
629 
630 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
631 		state = fz_knockout_begin(ctx, dev);
632 
633 	eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
634 
635 	fz_convert_rasterizer(ctx, rast, even_odd, state->dest, colorbv, eop);
636 	if (state->shape)
637 	{
638 		if (!rast->fns.reusable)
639 			fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, NULL);
640 
641 		colorbv[0] = 255;
642 		fz_convert_rasterizer(ctx, rast, even_odd, state->shape, colorbv, 0);
643 	}
644 	if (state->group_alpha)
645 	{
646 		if (!rast->fns.reusable)
647 			fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, NULL);
648 
649 		colorbv[0] = alpha * 255;
650 		fz_convert_rasterizer(ctx, rast, even_odd, state->group_alpha, colorbv, 0);
651 	}
652 
653 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
654 		fz_knockout_end(ctx, dev);
655 }
656 
657 static void
fz_draw_stroke_path(fz_context * ctx,fz_device * devp,const fz_path * path,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)658 fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_stroke_state *stroke, fz_matrix in_ctm,
659 	fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
660 {
661 	fz_draw_device *dev = (fz_draw_device*)devp;
662 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
663 	fz_rasterizer *rast = dev->rast;
664 	fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
665 	float expansion = fz_matrix_expansion(ctm);
666 	float flatness;
667 	float linewidth = stroke->linewidth;
668 	unsigned char colorbv[FZ_MAX_COLORS + 1];
669 	fz_irect bbox;
670 	float aa_level = 2.0f/(fz_rasterizer_graphics_aa_level(rast)+2);
671 	fz_draw_state *state = &dev->stack[dev->top];
672 	float mlw = fz_rasterizer_graphics_min_line_width(rast);
673 	fz_overprint op = { { 0 } };
674 	fz_overprint *eop;
675 
676 	if (dev->top == 0 && dev->resolve_spots)
677 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
678 
679 	if (mlw > aa_level)
680 		aa_level = mlw;
681 	if (expansion < FLT_EPSILON)
682 		expansion = 1;
683 	if (linewidth * expansion < aa_level)
684 		linewidth = aa_level / expansion;
685 	flatness = 0.3f / expansion;
686 	if (flatness < 0.001f)
687 		flatness = 0.001f;
688 
689 	bbox = fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest), state->scissor);
690 	if (fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, &bbox))
691 		return;
692 
693 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
694 		state = fz_knockout_begin(ctx, dev);
695 
696 	eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
697 
698 #ifdef DUMP_GROUP_BLENDS
699 	dump_spaces(dev->top, "");
700 	fz_dump_blend(ctx, "Before stroke ", state->dest);
701 	if (state->shape)
702 		fz_dump_blend(ctx, "/S=", state->shape);
703 	if (state->group_alpha)
704 		fz_dump_blend(ctx, "/GA=", state->group_alpha);
705 	printf("\n");
706 #endif
707 	fz_convert_rasterizer(ctx, rast, 0, state->dest, colorbv, eop);
708 	if (state->shape)
709 	{
710 		if (!rast->fns.reusable)
711 			(void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, NULL);
712 
713 		colorbv[0] = 255;
714 		fz_convert_rasterizer(ctx, rast, 0, state->shape, colorbv, 0);
715 	}
716 	if (state->group_alpha)
717 	{
718 		if (!rast->fns.reusable)
719 			(void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, NULL);
720 
721 		colorbv[0] = 255 * alpha;
722 		fz_convert_rasterizer(ctx, rast, 0, state->group_alpha, colorbv, 0);
723 	}
724 
725 #ifdef DUMP_GROUP_BLENDS
726 	dump_spaces(dev->top, "");
727 	fz_dump_blend(ctx, "After stroke ", state->dest);
728 	if (state->shape)
729 		fz_dump_blend(ctx, "/S=", state->shape);
730 	if (state->group_alpha)
731 		fz_dump_blend(ctx, "/GA=", state->group_alpha);
732 	printf("\n");
733 #endif
734 
735 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
736 		fz_knockout_end(ctx, dev);
737 }
738 
739 static void
fz_draw_clip_path(fz_context * ctx,fz_device * devp,const fz_path * path,int even_odd,fz_matrix in_ctm,fz_rect scissor)740 fz_draw_clip_path(fz_context *ctx, fz_device *devp, const fz_path *path, int even_odd, fz_matrix in_ctm, fz_rect scissor)
741 {
742 	fz_draw_device *dev = (fz_draw_device*)devp;
743 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
744 	fz_rasterizer *rast = dev->rast;
745 
746 	float expansion = fz_matrix_expansion(ctm);
747 	float flatness;
748 	fz_irect bbox;
749 	fz_draw_state *state = &dev->stack[dev->top];
750 	fz_colorspace *model;
751 
752 	if (dev->top == 0 && dev->resolve_spots)
753 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
754 
755 	if (expansion < FLT_EPSILON)
756 		expansion = 1;
757 	flatness = 0.3f / expansion;
758 	if (flatness < 0.001f)
759 		flatness = 0.001f;
760 
761 	state = push_stack(ctx, dev, "clip path");
762 
763 	model = state->dest->colorspace;
764 
765 	if (!fz_is_infinite_rect(scissor))
766 	{
767 		bbox = fz_irect_from_rect(fz_transform_rect(scissor, dev->transform));
768 		bbox = fz_intersect_irect(bbox, fz_pixmap_bbox(ctx, state->dest));
769 		bbox = fz_intersect_irect(bbox, state->scissor);
770 	}
771 	else
772 	{
773 		bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
774 	}
775 
776 	if (fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, &bbox) || fz_is_rect_rasterizer(ctx, rast))
777 	{
778 		state[1].scissor = bbox;
779 		state[1].mask = NULL;
780 #ifdef DUMP_GROUP_BLENDS
781 		dump_spaces(dev->top-1, "Clip (rectangular) begin\n");
782 #endif
783 		return;
784 	}
785 
786 	state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
787 	fz_clear_pixmap(ctx, state[1].mask);
788 	state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
789 	fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
790 	if (state[1].shape)
791 	{
792 		state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
793 		fz_clear_pixmap(ctx, state[1].shape);
794 	}
795 	if (state[1].group_alpha)
796 	{
797 		state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
798 		fz_clear_pixmap(ctx, state[1].group_alpha);
799 	}
800 
801 	fz_convert_rasterizer(ctx, rast, even_odd, state[1].mask, NULL, 0);
802 
803 	state[1].scissor = bbox;
804 
805 #ifdef DUMP_GROUP_BLENDS
806 	dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n");
807 #endif
808 }
809 
810 static void
fz_draw_clip_stroke_path(fz_context * ctx,fz_device * devp,const fz_path * path,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_rect scissor)811 fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_stroke_state *stroke, fz_matrix in_ctm, fz_rect scissor)
812 {
813 	fz_draw_device *dev = (fz_draw_device*)devp;
814 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
815 	fz_rasterizer *rast = dev->rast;
816 
817 	float expansion = fz_matrix_expansion(ctm);
818 	float flatness;
819 	float linewidth = stroke->linewidth;
820 	fz_irect bbox;
821 	fz_draw_state *state = &dev->stack[dev->top];
822 	fz_colorspace *model;
823 	float aa_level = 2.0f/(fz_rasterizer_graphics_aa_level(rast)+2);
824 	float mlw = fz_rasterizer_graphics_min_line_width(rast);
825 
826 	if (dev->top == 0 && dev->resolve_spots)
827 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
828 
829 	if (mlw > aa_level)
830 		aa_level = mlw;
831 	if (expansion < FLT_EPSILON)
832 		expansion = 1;
833 	if (linewidth * expansion < aa_level)
834 		linewidth = aa_level / expansion;
835 	flatness = 0.3f / expansion;
836 	if (flatness < 0.001f)
837 		flatness = 0.001f;
838 
839 	state = push_stack(ctx, dev, "clip stroke");
840 
841 	model = state->dest->colorspace;
842 
843 	if (!fz_is_infinite_rect(scissor))
844 	{
845 		bbox = fz_irect_from_rect(fz_transform_rect(scissor, dev->transform));
846 		bbox = fz_intersect_irect(bbox, fz_pixmap_bbox(ctx, state->dest));
847 		bbox = fz_intersect_irect(bbox, state->scissor);
848 	}
849 	else
850 	{
851 		bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
852 	}
853 
854 	if (fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, &bbox))
855 	{
856 		state[1].scissor = bbox;
857 		state[1].mask = NULL;
858 #ifdef DUMP_GROUP_BLENDS
859 		dump_spaces(dev->top-1, "Clip (stroke, empty) begin\n");
860 #endif
861 		return;
862 	}
863 
864 	state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
865 	fz_clear_pixmap(ctx, state[1].mask);
866 	/* When there is no alpha in the current destination (state[0].dest->alpha == 0)
867 	 * we have a choice. We can either create the new destination WITH alpha, or
868 	 * we can copy the old pixmap contents in. We opt for the latter here, but
869 	 * may want to revisit this decision in the future. */
870 	state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
871 	if (state[0].dest->alpha)
872 		fz_clear_pixmap(ctx, state[1].dest);
873 	else
874 		fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
875 	if (state->shape)
876 	{
877 		state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
878 		fz_clear_pixmap(ctx, state[1].shape);
879 	}
880 	if (state->group_alpha)
881 	{
882 		state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
883 		fz_clear_pixmap(ctx, state[1].group_alpha);
884 	}
885 
886 	fz_convert_rasterizer(ctx, rast, 0, state[1].mask, NULL, 0);
887 
888 	state[1].blendmode |= FZ_BLEND_ISOLATED;
889 	state[1].scissor = bbox;
890 
891 #ifdef DUMP_GROUP_BLENDS
892 	dump_spaces(dev->top-1, "Clip (stroke) begin\n");
893 #endif
894 }
895 
896 static void
draw_glyph(unsigned char * colorbv,fz_pixmap * dst,fz_glyph * glyph,int xorig,int yorig,const fz_irect * scissor,fz_overprint * eop)897 draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_glyph *glyph,
898 	int xorig, int yorig, const fz_irect *scissor, fz_overprint *eop)
899 {
900 	unsigned char *dp;
901 	fz_irect bbox;
902 	int x, y, w, h;
903 	int skip_x, skip_y;
904 	fz_pixmap *msk;
905 
906 	bbox = fz_glyph_bbox_no_ctx(glyph);
907 	bbox = fz_translate_irect(bbox, xorig, yorig);
908 	bbox = fz_intersect_irect(bbox, *scissor); /* scissor < dst */
909 	bbox = fz_intersect_irect(bbox, fz_pixmap_bbox_no_ctx(dst));
910 
911 	if (fz_is_empty_irect(bbox))
912 		return;
913 
914 	x = bbox.x0;
915 	y = bbox.y0;
916 	w = bbox.x1 - bbox.x0;
917 	h = bbox.y1 - bbox.y0;
918 
919 	skip_x = x - glyph->x - xorig;
920 	skip_y = y - glyph->y - yorig;
921 
922 	msk = glyph->pixmap;
923 	dp = dst->samples + (unsigned int)((y - dst->y) * dst->stride + (x - dst->x) * dst->n);
924 	if (msk == NULL)
925 	{
926 		fz_paint_glyph(colorbv, dst, dp, glyph, w, h, skip_x, skip_y, eop);
927 	}
928 	else
929 	{
930 		unsigned char *mp = msk->samples + skip_y * msk->stride + skip_x;
931 		int da = dst->alpha;
932 
933 		if (dst->colorspace)
934 		{
935 			fz_span_color_painter_t *fn;
936 
937 			fn = fz_get_span_color_painter(dst->n, da, colorbv, eop);
938 			assert(fn);
939 			if (fn == NULL)
940 				return;
941 			while (h--)
942 			{
943 				(*fn)(dp, mp, dst->n, w, colorbv, da, eop);
944 				dp += dst->stride;
945 				mp += msk->stride;
946 			}
947 		}
948 		else
949 		{
950 			fz_span_painter_t *fn;
951 
952 			fn = fz_get_span_painter(da, 1, 0, 255, eop);
953 			assert(fn);
954 			if (fn == NULL)
955 				return;
956 			while (h--)
957 			{
958 				(*fn)(dp, da, mp, 1, 0, w, 255, eop);
959 				dp += dst->stride;
960 				mp += msk->stride;
961 			}
962 		}
963 	}
964 }
965 
966 static void
fz_draw_fill_text(fz_context * ctx,fz_device * devp,const fz_text * text,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)967 fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, fz_matrix in_ctm,
968 	fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
969 {
970 	fz_draw_device *dev = (fz_draw_device*)devp;
971 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
972 	fz_draw_state *state = &dev->stack[dev->top];
973 	fz_colorspace *model = state->dest->colorspace;
974 	unsigned char colorbv[FZ_MAX_COLORS + 1];
975 	unsigned char shapebv, shapebva;
976 	fz_text_span *span;
977 	int i;
978 	fz_colorspace *colorspace = NULL;
979 	fz_rasterizer *rast = dev->rast;
980 	fz_overprint op = { { 0 } };
981 	fz_overprint *eop;
982 
983 	if (dev->top == 0 && dev->resolve_spots)
984 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
985 
986 	if (colorspace_in)
987 		colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
988 
989 	if (colorspace == NULL && model != NULL)
990 		fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color");
991 
992 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
993 		state = fz_knockout_begin(ctx, dev);
994 
995 	eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
996 	shapebv = 255;
997 	shapebva = 255 * alpha;
998 
999 	for (span = text->head; span; span = span->next)
1000 	{
1001 		fz_matrix tm, trm;
1002 		fz_glyph *glyph;
1003 		int gid;
1004 
1005 		tm = span->trm;
1006 
1007 		for (i = 0; i < span->len; i++)
1008 		{
1009 			gid = span->items[i].gid;
1010 			if (gid < 0)
1011 				continue;
1012 
1013 			tm.e = span->items[i].x;
1014 			tm.f = span->items[i].y;
1015 			trm = fz_concat(tm, ctm);
1016 
1017 			glyph = fz_render_glyph(ctx, span->font, gid, &trm, model, &state->scissor, state->dest->alpha, fz_rasterizer_text_aa_level(rast));
1018 			if (glyph)
1019 			{
1020 				fz_pixmap *pixmap = glyph->pixmap;
1021 				int x = floorf(trm.e);
1022 				int y = floorf(trm.f);
1023 				if (pixmap == NULL || pixmap->n == 1)
1024 				{
1025 					draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
1026 					if (state->shape)
1027 						draw_glyph(&shapebv, state->shape, glyph, x, y, &state->scissor, 0);
1028 					if (state->group_alpha)
1029 						draw_glyph(&shapebva, state->group_alpha, glyph, x, y, &state->scissor, 0);
1030 				}
1031 				else
1032 				{
1033 					fz_matrix mat;
1034 					mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
1035 					mat.e = x + pixmap->x; mat.f = y + pixmap->y;
1036 					fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1037 				}
1038 				fz_drop_glyph(ctx, glyph);
1039 			}
1040 			else
1041 			{
1042 				fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1043 				if (path)
1044 				{
1045 					fz_draw_fill_path(ctx, devp, path, 0, in_ctm, colorspace, color, alpha, color_params);
1046 					fz_drop_path(ctx, path);
1047 				}
1048 				else
1049 				{
1050 					fz_warn(ctx, "cannot render glyph");
1051 				}
1052 			}
1053 		}
1054 	}
1055 
1056 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
1057 		fz_knockout_end(ctx, dev);
1058 }
1059 
1060 static void
fz_draw_stroke_text(fz_context * ctx,fz_device * devp,const fz_text * text,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)1061 fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_stroke_state *stroke,
1062 	fz_matrix in_ctm, fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
1063 {
1064 	fz_draw_device *dev = (fz_draw_device*)devp;
1065 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1066 	fz_draw_state *state = &dev->stack[dev->top];
1067 	unsigned char colorbv[FZ_MAX_COLORS + 1];
1068 	unsigned char solid = 255;
1069 	unsigned char alpha_byte = alpha * 255;
1070 	fz_text_span *span;
1071 	int i;
1072 	fz_colorspace *colorspace = NULL;
1073 	int aa = fz_rasterizer_text_aa_level(dev->rast);
1074 	fz_overprint op = { { 0 } };
1075 	fz_overprint *eop;
1076 
1077 	if (dev->top == 0 && dev->resolve_spots)
1078 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1079 
1080 	if (colorspace_in)
1081 		colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
1082 
1083 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
1084 		state = fz_knockout_begin(ctx, dev);
1085 
1086 	eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
1087 
1088 	for (span = text->head; span; span = span->next)
1089 	{
1090 		fz_matrix tm, trm;
1091 		fz_glyph *glyph;
1092 		int gid;
1093 
1094 		tm = span->trm;
1095 
1096 		for (i = 0; i < span->len; i++)
1097 		{
1098 			gid = span->items[i].gid;
1099 			if (gid < 0)
1100 				continue;
1101 
1102 			tm.e = span->items[i].x;
1103 			tm.f = span->items[i].y;
1104 			trm = fz_concat(tm, ctm);
1105 
1106 			glyph = fz_render_stroked_glyph(ctx, span->font, gid, &trm, ctm, stroke, &state->scissor, aa);
1107 			if (glyph)
1108 			{
1109 				fz_pixmap *pixmap = glyph->pixmap;
1110 				int x = (int)trm.e;
1111 				int y = (int)trm.f;
1112 				if (pixmap == NULL || pixmap->n == 1)
1113 				{
1114 					draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
1115 					if (state->shape)
1116 						draw_glyph(&solid, state->shape, glyph, x, y, &state->scissor, 0);
1117 					if (state->group_alpha)
1118 						draw_glyph(&alpha_byte, state->group_alpha, glyph, x, y, &state->scissor, 0);
1119 				}
1120 				else
1121 				{
1122 					fz_matrix mat;
1123 					mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
1124 					mat.e = x + pixmap->x; mat.f = y + pixmap->y;
1125 					fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1126 				}
1127 				fz_drop_glyph(ctx, glyph);
1128 			}
1129 			else
1130 			{
1131 				fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1132 				if (path)
1133 				{
1134 					fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, colorspace, color, alpha, color_params);
1135 					fz_drop_path(ctx, path);
1136 				}
1137 				else
1138 				{
1139 					fz_warn(ctx, "cannot render glyph");
1140 				}
1141 			}
1142 		}
1143 	}
1144 
1145 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
1146 		fz_knockout_end(ctx, dev);
1147 }
1148 
1149 static void
fz_draw_clip_text(fz_context * ctx,fz_device * devp,const fz_text * text,fz_matrix in_ctm,fz_rect scissor)1150 fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, fz_matrix in_ctm, fz_rect scissor)
1151 {
1152 	fz_draw_device *dev = (fz_draw_device*)devp;
1153 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1154 	fz_irect bbox;
1155 	fz_matrix tm, trm;
1156 	fz_glyph *glyph;
1157 	int i, gid;
1158 	fz_draw_state *state;
1159 	fz_colorspace *model;
1160 	fz_text_span *span;
1161 	fz_rasterizer *rast = dev->rast;
1162 
1163 	if (dev->top == 0 && dev->resolve_spots)
1164 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1165 
1166 	state = push_stack(ctx, dev, "clip text");
1167 
1168 	model = state->dest->colorspace;
1169 
1170 	/* make the mask the exact size needed */
1171 	bbox = fz_irect_from_rect(fz_bound_text(ctx, text, NULL, ctm));
1172 	bbox = fz_intersect_irect(bbox, state->scissor);
1173 	if (!fz_is_infinite_rect(scissor))
1174 	{
1175 		fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1176 		bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1177 	}
1178 
1179 	state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1180 	fz_clear_pixmap(ctx, state[1].mask);
1181 	/* When there is no alpha in the current destination (state[0].dest->alpha == 0)
1182 	 * we have a choice. We can either create the new destination WITH alpha, or
1183 	 * we can copy the old pixmap contents in. We opt for the latter here, but
1184 	 * may want to revisit this decision in the future. */
1185 	state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1186 	if (state[0].dest->alpha)
1187 		fz_clear_pixmap(ctx, state[1].dest);
1188 	else
1189 		fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1190 	if (state->shape)
1191 	{
1192 		state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1193 		fz_clear_pixmap(ctx, state[1].shape);
1194 	}
1195 	else
1196 		state[1].shape = NULL;
1197 	if (state->group_alpha)
1198 	{
1199 		state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1200 		fz_clear_pixmap(ctx, state[1].group_alpha);
1201 	}
1202 	else
1203 		state[1].group_alpha = NULL;
1204 
1205 	state[1].blendmode |= FZ_BLEND_ISOLATED;
1206 	state[1].scissor = bbox;
1207 
1208 #ifdef DUMP_GROUP_BLENDS
1209 	dump_spaces(dev->top-1, "Clip (text) begin\n");
1210 #endif
1211 
1212 	if (!fz_is_empty_irect(bbox) && state[1].mask)
1213 	{
1214 		for (span = text->head; span; span = span->next)
1215 		{
1216 			tm = span->trm;
1217 
1218 			for (i = 0; i < span->len; i++)
1219 			{
1220 				gid = span->items[i].gid;
1221 				if (gid < 0)
1222 					continue;
1223 
1224 				tm.e = span->items[i].x;
1225 				tm.f = span->items[i].y;
1226 				trm = fz_concat(tm, ctm);
1227 
1228 				glyph = fz_render_glyph(ctx, span->font, gid, &trm, model, &state->scissor, state[1].dest->alpha, fz_rasterizer_text_aa_level(rast));
1229 				if (glyph)
1230 				{
1231 					int x = (int)trm.e;
1232 					int y = (int)trm.f;
1233 					draw_glyph(NULL, state[1].mask, glyph, x, y, &bbox, 0);
1234 					if (state[1].shape)
1235 						draw_glyph(NULL, state[1].shape, glyph, x, y, &bbox, 0);
1236 					if (state[1].group_alpha)
1237 						draw_glyph(NULL, state[1].group_alpha, glyph, x, y, &bbox, 0);
1238 					fz_drop_glyph(ctx, glyph);
1239 				}
1240 				else
1241 				{
1242 					fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1243 					if (path)
1244 					{
1245 						fz_pixmap *old_dest;
1246 						float white = 1;
1247 
1248 						old_dest = state[1].dest;
1249 						state[1].dest = state[1].mask;
1250 						state[1].mask = NULL;
1251 						fz_try(ctx)
1252 						{
1253 							fz_draw_fill_path(ctx, devp, path, 0, in_ctm, fz_device_gray(ctx), &white, 1, fz_default_color_params);
1254 						}
1255 						fz_always(ctx)
1256 						{
1257 							state[1].mask = state[1].dest;
1258 							state[1].dest = old_dest;
1259 							fz_drop_path(ctx, path);
1260 						}
1261 						fz_catch(ctx)
1262 						{
1263 							fz_rethrow(ctx);
1264 						}
1265 					}
1266 					else
1267 					{
1268 						fz_warn(ctx, "cannot render glyph for clipping");
1269 					}
1270 				}
1271 			}
1272 		}
1273 	}
1274 }
1275 
1276 static void
fz_draw_clip_stroke_text(fz_context * ctx,fz_device * devp,const fz_text * text,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_rect scissor)1277 fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_stroke_state *stroke, fz_matrix in_ctm, fz_rect scissor)
1278 {
1279 	fz_draw_device *dev = (fz_draw_device*)devp;
1280 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1281 	fz_irect bbox;
1282 	fz_pixmap *mask, *dest, *shape, *group_alpha;
1283 	fz_matrix tm, trm;
1284 	fz_glyph *glyph;
1285 	int i, gid;
1286 	fz_draw_state *state = push_stack(ctx, dev, "clip stroke text");
1287 	fz_colorspace *model = state->dest->colorspace;
1288 	fz_text_span *span;
1289 	int aa = fz_rasterizer_text_aa_level(dev->rast);
1290 
1291 	if (dev->top == 0 && dev->resolve_spots)
1292 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1293 
1294 	/* make the mask the exact size needed */
1295 	bbox = fz_irect_from_rect(fz_bound_text(ctx, text, stroke, ctm));
1296 	bbox = fz_intersect_irect(bbox, state->scissor);
1297 	if (!fz_is_infinite_rect(scissor))
1298 	{
1299 		fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1300 		bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1301 	}
1302 
1303 	state[1].mask = mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1304 	fz_clear_pixmap(ctx, mask);
1305 	/* When there is no alpha in the current destination (state[0].dest->alpha == 0)
1306 	 * we have a choice. We can either create the new destination WITH alpha, or
1307 	 * we can copy the old pixmap contents in. We opt for the latter here, but
1308 	 * may want to revisit this decision in the future. */
1309 	state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1310 	if (state[0].dest->alpha)
1311 		fz_clear_pixmap(ctx, state[1].dest);
1312 	else
1313 		fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1314 	if (state->shape)
1315 	{
1316 		state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1317 		fz_clear_pixmap(ctx, shape);
1318 	}
1319 	else
1320 		shape = state->shape;
1321 	if (state->group_alpha)
1322 	{
1323 		state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1324 		fz_clear_pixmap(ctx, group_alpha);
1325 	}
1326 	else
1327 		group_alpha = NULL;
1328 
1329 	state[1].blendmode |= FZ_BLEND_ISOLATED;
1330 	state[1].scissor = bbox;
1331 
1332 #ifdef DUMP_GROUP_BLENDS
1333 	dump_spaces(dev->top-1, "Clip (stroke text) begin\n");
1334 #endif
1335 
1336 	if (!fz_is_empty_irect(bbox))
1337 	{
1338 		for (span = text->head; span; span = span->next)
1339 		{
1340 			tm = span->trm;
1341 
1342 			for (i = 0; i < span->len; i++)
1343 			{
1344 				gid = span->items[i].gid;
1345 				if (gid < 0)
1346 					continue;
1347 
1348 				tm.e = span->items[i].x;
1349 				tm.f = span->items[i].y;
1350 				trm = fz_concat(tm, ctm);
1351 
1352 				glyph = fz_render_stroked_glyph(ctx, span->font, gid, &trm, ctm, stroke, &state->scissor, aa);
1353 				if (glyph)
1354 				{
1355 					int x = (int)trm.e;
1356 					int y = (int)trm.f;
1357 					draw_glyph(NULL, mask, glyph, x, y, &bbox, 0);
1358 					if (shape)
1359 						draw_glyph(NULL, shape, glyph, x, y, &bbox, 0);
1360 					if (group_alpha)
1361 						draw_glyph(NULL, group_alpha, glyph, x, y, &bbox, 0);
1362 					fz_drop_glyph(ctx, glyph);
1363 				}
1364 				else
1365 				{
1366 					fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1367 					if (path)
1368 					{
1369 						fz_pixmap *old_dest;
1370 						float white = 1;
1371 
1372 						state = &dev->stack[dev->top];
1373 						old_dest = state[0].dest;
1374 						state[0].dest = state[0].mask;
1375 						state[0].mask = NULL;
1376 						fz_try(ctx)
1377 						{
1378 							fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, fz_device_gray(ctx), &white, 1, fz_default_color_params);
1379 						}
1380 						fz_always(ctx)
1381 						{
1382 							state[0].mask = state[0].dest;
1383 							state[0].dest = old_dest;
1384 							fz_drop_path(ctx, path);
1385 						}
1386 						fz_catch(ctx)
1387 						{
1388 							fz_rethrow(ctx);
1389 						}
1390 					}
1391 					else
1392 					{
1393 						fz_warn(ctx, "cannot render glyph for stroked clipping");
1394 					}
1395 				}
1396 			}
1397 		}
1398 	}
1399 }
1400 
1401 static void
fz_draw_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)1402 fz_draw_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
1403 {
1404 }
1405 
1406 static void
fz_draw_fill_shade(fz_context * ctx,fz_device * devp,fz_shade * shade,fz_matrix in_ctm,float alpha,fz_color_params color_params)1407 fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, fz_matrix in_ctm, float alpha, fz_color_params color_params)
1408 {
1409 	fz_draw_device *dev = (fz_draw_device*)devp;
1410 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1411 	fz_irect bbox, scissor;
1412 	fz_pixmap *dest, *shape, *group_alpha;
1413 	unsigned char colorbv[FZ_MAX_COLORS + 1];
1414 	unsigned char alpha_byte = 255 * alpha;
1415 	fz_draw_state *state = &dev->stack[dev->top];
1416 	fz_overprint op = { { 0 } };
1417 	fz_overprint *eop;
1418 	fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, shade->colorspace);
1419 
1420 	if (dev->top == 0 && dev->resolve_spots)
1421 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1422 
1423 	scissor = state->scissor;
1424 	bbox = fz_irect_from_rect(fz_bound_shade(ctx, shade, ctm));
1425 	bbox = fz_intersect_irect(bbox, scissor);
1426 
1427 	if (fz_is_empty_irect(bbox))
1428 		return;
1429 
1430 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
1431 		state = fz_knockout_begin(ctx, dev);
1432 
1433 	fz_var(dest);
1434 	fz_var(shape);
1435 	fz_var(group_alpha);
1436 
1437 	dest = state->dest;
1438 	shape = state->shape;
1439 	group_alpha = state->group_alpha;
1440 
1441 	fz_try(ctx)
1442 	{
1443 		if (alpha < 1)
1444 		{
1445 			dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
1446 			if (state->dest->alpha)
1447 				fz_clear_pixmap(ctx, dest);
1448 			else
1449 				fz_copy_pixmap_rect(ctx, dest, state[0].dest, bbox, dev->default_cs);
1450 			if (shape)
1451 			{
1452 				shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1453 				fz_clear_pixmap(ctx, shape);
1454 			}
1455 			if (group_alpha)
1456 			{
1457 				group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1458 				fz_clear_pixmap(ctx, group_alpha);
1459 			}
1460 		}
1461 
1462 		if (shade->use_background)
1463 		{
1464 			unsigned char *s;
1465 			int x, y, n, i;
1466 
1467 			/* Disable OPM */
1468 			color_params.opm = 0;
1469 
1470 			eop = resolve_color(ctx, &op, shade->background, colorspace, alpha, color_params, colorbv, state->dest);
1471 
1472 			n = dest->n;
1473 			if (fz_overprint_required(eop))
1474 			{
1475 				for (y = scissor.y0; y < scissor.y1; y++)
1476 				{
1477 					s = dest->samples + (unsigned int)((y - dest->y) * dest->stride + (scissor.x0 - dest->x) * n);
1478 					for (x = scissor.x0; x < scissor.x1; x++)
1479 					{
1480 						for (i = 0; i < n; i++)
1481 							if (fz_overprint_component(eop, i))
1482 								*s++ = colorbv[i];
1483 					}
1484 				}
1485 			}
1486 			else
1487 			{
1488 				for (y = scissor.y0; y < scissor.y1; y++)
1489 				{
1490 					s = dest->samples + (unsigned int)((y - dest->y) * dest->stride + (scissor.x0 - dest->x) * n);
1491 					for (x = scissor.x0; x < scissor.x1; x++)
1492 					{
1493 						for (i = 0; i < n; i++)
1494 							*s++ = colorbv[i];
1495 					}
1496 				}
1497 			}
1498 			if (shape)
1499 			{
1500 				for (y = scissor.y0; y < scissor.y1; y++)
1501 				{
1502 					s = shape->samples + (unsigned int)((y - shape->y) * shape->stride + (scissor.x0 - shape->x));
1503 					for (x = scissor.x0; x < scissor.x1; x++)
1504 					{
1505 						*s++ = 255;
1506 					}
1507 				}
1508 			}
1509 			if (group_alpha)
1510 			{
1511 				for (y = scissor.y0; y < scissor.y1; y++)
1512 				{
1513 					s = group_alpha->samples + (unsigned int)((y - group_alpha->y) * group_alpha->stride + (scissor.x0 - group_alpha->x));
1514 					for (x = scissor.x0; x < scissor.x1; x++)
1515 					{
1516 						*s++ = alpha_byte;
1517 					}
1518 				}
1519 			}
1520 		}
1521 
1522 		if (color_params.op)
1523 			eop = set_op_from_spaces(ctx, &op, dest, colorspace, 0);
1524 		else
1525 			eop = NULL;
1526 
1527 		fz_paint_shade(ctx, shade, colorspace, ctm, dest, color_params, bbox, eop);
1528 		if (shape)
1529 			fz_clear_pixmap_rect_with_value(ctx, shape, 255, bbox);
1530 		if (group_alpha)
1531 			fz_clear_pixmap_rect_with_value(ctx, group_alpha, 255, bbox);
1532 
1533 #ifdef DUMP_GROUP_BLENDS
1534 		dump_spaces(dev->top, "");
1535 		fz_dump_blend(ctx, "Shade ", dest);
1536 		if (shape)
1537 			fz_dump_blend(ctx, "/S=", shape);
1538 		if (group_alpha)
1539 			fz_dump_blend(ctx, "/GA=", group_alpha);
1540 		printf("\n");
1541 #endif
1542 
1543 		if (alpha < 1)
1544 		{
1545 			/* FIXME: eop */
1546 			fz_paint_pixmap(state->dest, dest, alpha * 255);
1547 			fz_drop_pixmap(ctx, dest);
1548 			dest = NULL;
1549 
1550 			if (shape)
1551 			{
1552 				fz_paint_pixmap(state->shape, shape, 255);
1553 				fz_drop_pixmap(ctx, shape);
1554 				shape = NULL;
1555 			}
1556 
1557 			if (group_alpha)
1558 			{
1559 				fz_paint_pixmap(state->group_alpha, group_alpha, alpha * 255);
1560 				fz_drop_pixmap(ctx, group_alpha);
1561 				group_alpha = NULL;
1562 			}
1563 		}
1564 
1565 		if (state->blendmode & FZ_BLEND_KNOCKOUT)
1566 			fz_knockout_end(ctx, dev);
1567 	}
1568 	fz_catch(ctx)
1569 	{
1570 		if (dest != state[0].dest) fz_drop_pixmap(ctx, dest);
1571 		if (shape != state[0].shape) fz_drop_pixmap(ctx, shape);
1572 		if (group_alpha != state[0].group_alpha) fz_drop_pixmap(ctx, group_alpha);
1573 		fz_rethrow(ctx);
1574 	}
1575 }
1576 
1577 static fz_pixmap *
fz_transform_pixmap(fz_context * ctx,fz_draw_device * dev,const fz_pixmap * image,fz_matrix * ctm,int x,int y,int dx,int dy,int gridfit,const fz_irect * clip)1578 fz_transform_pixmap(fz_context *ctx, fz_draw_device *dev, const fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit, const fz_irect *clip)
1579 {
1580 	fz_pixmap *scaled;
1581 
1582 	if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
1583 	{
1584 		/* Unrotated or X-flip or Y-flip or XY-flip */
1585 		fz_matrix m = *ctm;
1586 		if (gridfit)
1587 		{
1588 			m = fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, m);
1589 		}
1590 		scaled = fz_scale_pixmap_cached(ctx, image, m.e, m.f, m.a, m.d, clip, dev->cache_x, dev->cache_y);
1591 		if (!scaled)
1592 			return NULL;
1593 		ctm->a = scaled->w;
1594 		ctm->d = scaled->h;
1595 		ctm->e = scaled->x;
1596 		ctm->f = scaled->y;
1597 		return scaled;
1598 	}
1599 
1600 	if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
1601 	{
1602 		/* Other orthogonal flip/rotation cases */
1603 		fz_matrix m = *ctm;
1604 		fz_irect rclip;
1605 		if (gridfit)
1606 			m = fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, m);
1607 		if (clip)
1608 		{
1609 			rclip.x0 = clip->y0;
1610 			rclip.y0 = clip->x0;
1611 			rclip.x1 = clip->y1;
1612 			rclip.y1 = clip->x1;
1613 		}
1614 		scaled = fz_scale_pixmap_cached(ctx, image, m.f, m.e, m.b, m.c, (clip ? &rclip : NULL), dev->cache_x, dev->cache_y);
1615 		if (!scaled)
1616 			return NULL;
1617 		ctm->b = scaled->w;
1618 		ctm->c = scaled->h;
1619 		ctm->f = scaled->x;
1620 		ctm->e = scaled->y;
1621 		return scaled;
1622 	}
1623 
1624 	/* Downscale, non rectilinear case */
1625 	if (dx > 0 && dy > 0)
1626 	{
1627 		scaled = fz_scale_pixmap_cached(ctx, image, 0, 0, dx, dy, NULL, dev->cache_x, dev->cache_y);
1628 		return scaled;
1629 	}
1630 
1631 	return NULL;
1632 }
1633 
1634 int
fz_default_image_scale(void * arg,int dst_w,int dst_h,int src_w,int src_h)1635 fz_default_image_scale(void *arg, int dst_w, int dst_h, int src_w, int src_h)
1636 {
1637 	(void)arg;
1638 	return dst_w < src_w && dst_h < src_h;
1639 }
1640 
1641 static fz_pixmap *
convert_pixmap_for_painting(fz_context * ctx,fz_pixmap * pixmap,fz_colorspace * model,fz_colorspace * src_cs,fz_pixmap * dest,fz_color_params color_params,fz_draw_device * dev,fz_overprint ** eop)1642 convert_pixmap_for_painting(fz_context *ctx, fz_pixmap *pixmap, fz_colorspace *model, fz_colorspace *src_cs, fz_pixmap *dest, fz_color_params color_params, fz_draw_device *dev, fz_overprint **eop)
1643 {
1644 	fz_pixmap *converted;
1645 
1646 	if (fz_colorspace_is_device_n(ctx, src_cs) && dest->seps)
1647 	{
1648 		converted = fz_clone_pixmap_area_with_different_seps(ctx, pixmap, NULL, model, dest->seps, color_params, dev->default_cs);
1649 		*eop = set_op_from_spaces(ctx, *eop, dest, src_cs, 0);
1650 	}
1651 	else
1652 	{
1653 		converted = fz_convert_pixmap(ctx, pixmap, model, NULL, dev->default_cs, color_params, 1);
1654 		if (*eop)
1655 		{
1656 			if (fz_colorspace_type(ctx, model) != FZ_COLORSPACE_CMYK)
1657 			{
1658 				/* Can only overprint to CMYK based spaces */
1659 				*eop = NULL;
1660 			}
1661 			else if (!fz_colorspace_is_device_n(ctx, pixmap->colorspace))
1662 			{
1663 				int i;
1664 				int n = dest->n - dest->alpha;
1665 				for (i = 4; i < n; i++)
1666 					fz_set_overprint(*eop, i);
1667 			}
1668 			else
1669 			{
1670 				*eop = set_op_from_spaces(ctx, *eop, dest, src_cs, 0);
1671 			}
1672 		}
1673 	}
1674 	fz_drop_pixmap(ctx, pixmap);
1675 
1676 	return converted;
1677 }
1678 
1679 static void
fz_draw_fill_image(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,float alpha,fz_color_params color_params)1680 fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm, float alpha, fz_color_params color_params)
1681 {
1682 	fz_draw_device *dev = (fz_draw_device*)devp;
1683 	fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1684 	fz_pixmap *pixmap;
1685 	int after;
1686 	int dx, dy;
1687 	fz_draw_state *state = &dev->stack[dev->top];
1688 	fz_colorspace *model;
1689 	fz_irect clip;
1690 	fz_matrix inverse;
1691 	fz_irect src_area;
1692 	fz_colorspace *src_cs;
1693 	fz_overprint op = { { 0 } };
1694 	fz_overprint *eop = &op;
1695 
1696 	if (alpha == 0)
1697 		return;
1698 
1699 	if (dev->top == 0 && dev->resolve_spots)
1700 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1701 	model = state->dest->colorspace;
1702 
1703 	clip = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
1704 
1705 	if (image->w == 0 || image->h == 0)
1706 		return;
1707 
1708 	if (color_params.op == 0)
1709 		eop = NULL;
1710 
1711 	/* ctm maps the image (expressed as the unit square) onto the
1712 	 * destination device. Reverse that to get a mapping from
1713 	 * the destination device to the source pixels. */
1714 	if (fz_try_invert_matrix(&inverse, local_ctm))
1715 	{
1716 		/* Not invertible. Could just bail? Use the whole image
1717 		 * for now. */
1718 		src_area.x0 = 0;
1719 		src_area.x1 = image->w;
1720 		src_area.y0 = 0;
1721 		src_area.y1 = image->h;
1722 	}
1723 	else
1724 	{
1725 		float exp;
1726 		fz_rect rect;
1727 		fz_irect sane;
1728 		/* We want to scale from image coords, not from unit square */
1729 		inverse = fz_post_scale(inverse, image->w, image->h);
1730 		/* Are we scaling up or down? exp < 1 means scaling down. */
1731 		exp = fz_matrix_max_expansion(inverse);
1732 		rect = fz_rect_from_irect(clip);
1733 		rect = fz_transform_rect(rect, inverse);
1734 		/* Allow for support requirements for scalers. */
1735 		rect = fz_expand_rect(rect, fz_max(exp, 1) * 4);
1736 		src_area = fz_irect_from_rect(rect);
1737 		sane.x0 = 0;
1738 		sane.y0 = 0;
1739 		sane.x1 = image->w;
1740 		sane.y1 = image->h;
1741 		src_area = fz_intersect_irect(src_area, sane);
1742 		if (fz_is_empty_irect(src_area))
1743 			return;
1744 	}
1745 
1746 	pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
1747 	src_cs = fz_default_colorspace(ctx, dev->default_cs, pixmap->colorspace);
1748 
1749 	/* convert images with more components (cmyk->rgb) before scaling */
1750 	/* convert images with fewer components (gray->rgb) after scaling */
1751 	/* convert images with expensive colorspace transforms after scaling */
1752 
1753 	fz_var(pixmap);
1754 
1755 	fz_try(ctx)
1756 	{
1757 		int conversion_required = (src_cs != model || state->dest->seps);
1758 
1759 		if (state->blendmode & FZ_BLEND_KNOCKOUT)
1760 			state = fz_knockout_begin(ctx, dev);
1761 
1762 		switch (fz_colorspace_type(ctx, src_cs))
1763 		{
1764 		case FZ_COLORSPACE_GRAY:
1765 			after = 1;
1766 			break;
1767 		case FZ_COLORSPACE_INDEXED:
1768 			after = 0;
1769 			break;
1770 		default:
1771 			if (fz_colorspace_n(ctx, src_cs) <= fz_colorspace_n(ctx, model))
1772 				after = 1;
1773 			else
1774 				after = 0;
1775 			break;
1776 		}
1777 
1778 		if (conversion_required && !after)
1779 			pixmap = convert_pixmap_for_painting(ctx, pixmap, model, src_cs, state->dest, color_params, dev, &eop);
1780 
1781 		if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
1782 		{
1783 			int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1784 			fz_pixmap *scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1785 			if (!scaled)
1786 			{
1787 				if (dx < 1)
1788 					dx = 1;
1789 				if (dy < 1)
1790 					dy = 1;
1791 				scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1792 			}
1793 			if (scaled)
1794 			{
1795 				fz_drop_pixmap(ctx, pixmap);
1796 				pixmap = scaled;
1797 			}
1798 		}
1799 
1800 		if (conversion_required && after)
1801 		{
1802 #if FZ_PLOTTERS_RGB
1803 			if (state->dest->seps == NULL &&
1804 				((src_cs == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) ||
1805 				(src_cs == fz_device_gray(ctx) && model == fz_device_bgr(ctx))))
1806 			{
1807 				/* We have special case rendering code for gray -> rgb/bgr */
1808 			}
1809 			else
1810 #endif
1811 				pixmap = convert_pixmap_for_painting(ctx, pixmap, model, src_cs, state->dest, color_params, dev, &eop);
1812 		}
1813 
1814 		fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1815 
1816 		if (state->blendmode & FZ_BLEND_KNOCKOUT)
1817 			fz_knockout_end(ctx, dev);
1818 	}
1819 	fz_always(ctx)
1820 		fz_drop_pixmap(ctx, pixmap);
1821 	fz_catch(ctx)
1822 		fz_rethrow(ctx);
1823 }
1824 
1825 static void
fz_draw_fill_image_mask(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)1826 fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm,
1827 	fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
1828 {
1829 	fz_draw_device *dev = (fz_draw_device*)devp;
1830 	fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1831 	unsigned char colorbv[FZ_MAX_COLORS + 1];
1832 	fz_pixmap *scaled = NULL;
1833 	fz_pixmap *pixmap;
1834 	int dx, dy;
1835 	fz_draw_state *state = &dev->stack[dev->top];
1836 	fz_irect clip;
1837 	fz_matrix inverse;
1838 	fz_irect src_area;
1839 	fz_colorspace *colorspace = NULL;
1840 	fz_overprint op = { { 0 } };
1841 	fz_overprint *eop;
1842 
1843 	if (alpha == 0)
1844 		return;
1845 
1846 	if (dev->top == 0 && dev->resolve_spots)
1847 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1848 
1849 	if (colorspace_in)
1850 		colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
1851 
1852 	clip = fz_pixmap_bbox(ctx, state->dest);
1853 	clip = fz_intersect_irect(clip, state->scissor);
1854 
1855 	if (image->w == 0 || image->h == 0)
1856 		return;
1857 
1858 	/* ctm maps the image (expressed as the unit square) onto the
1859 	 * destination device. Reverse that to get a mapping from
1860 	 * the destination device to the source pixels. */
1861 	if (fz_try_invert_matrix(&inverse, local_ctm))
1862 	{
1863 		/* Not invertible. Could just bail? Use the whole image
1864 		 * for now. */
1865 		src_area.x0 = 0;
1866 		src_area.x1 = image->w;
1867 		src_area.y0 = 0;
1868 		src_area.y1 = image->h;
1869 	}
1870 	else
1871 	{
1872 		float exp;
1873 		fz_rect rect;
1874 		fz_irect sane;
1875 		/* We want to scale from image coords, not from unit square */
1876 		inverse = fz_post_scale(inverse, image->w, image->h);
1877 		/* Are we scaling up or down? exp < 1 means scaling down. */
1878 		exp = fz_matrix_max_expansion(inverse);
1879 		rect = fz_rect_from_irect(clip);
1880 		rect = fz_transform_rect(rect, inverse);
1881 		/* Allow for support requirements for scalers. */
1882 		rect = fz_expand_rect(rect, fz_max(exp, 1) * 4);
1883 		src_area = fz_irect_from_rect(rect);
1884 		sane.x0 = 0;
1885 		sane.y0 = 0;
1886 		sane.x1 = image->w;
1887 		sane.y1 = image->h;
1888 		src_area = fz_intersect_irect(src_area, sane);
1889 		if (fz_is_empty_irect(src_area))
1890 			return;
1891 	}
1892 
1893 	pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
1894 
1895 	fz_var(pixmap);
1896 
1897 	fz_try(ctx)
1898 	{
1899 		if (state->blendmode & FZ_BLEND_KNOCKOUT)
1900 			state = fz_knockout_begin(ctx, dev);
1901 
1902 		if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
1903 		{
1904 			int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1905 			scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1906 			if (!scaled)
1907 			{
1908 				if (dx < 1)
1909 					dx = 1;
1910 				if (dy < 1)
1911 					dy = 1;
1912 				scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1913 			}
1914 			if (scaled)
1915 			{
1916 				fz_drop_pixmap(ctx, pixmap);
1917 				pixmap = scaled;
1918 			}
1919 		}
1920 
1921 		eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
1922 
1923 		fz_paint_image_with_color(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1924 
1925 		if (state->blendmode & FZ_BLEND_KNOCKOUT)
1926 			fz_knockout_end(ctx, dev);
1927 	}
1928 	fz_always(ctx)
1929 		fz_drop_pixmap(ctx, pixmap);
1930 	fz_catch(ctx)
1931 		fz_rethrow(ctx);
1932 }
1933 
1934 static void
fz_draw_clip_image_mask(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,fz_rect scissor)1935 fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm, fz_rect scissor)
1936 {
1937 	fz_draw_device *dev = (fz_draw_device*)devp;
1938 	fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1939 	fz_irect bbox;
1940 	fz_pixmap *scaled = NULL;
1941 	fz_pixmap *pixmap = NULL;
1942 	int dx, dy;
1943 	fz_draw_state *state = push_stack(ctx, dev, "clip image mask");
1944 	fz_colorspace *model = state->dest->colorspace;
1945 	fz_irect clip;
1946 
1947 	fz_var(pixmap);
1948 
1949 	if (dev->top == 0 && dev->resolve_spots)
1950 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1951 
1952 	clip = fz_pixmap_bbox(ctx, state->dest);
1953 	clip = fz_intersect_irect(clip, state->scissor);
1954 
1955 	if (image->w == 0 || image->h == 0)
1956 	{
1957 #ifdef DUMP_GROUP_BLENDS
1958 		dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n");
1959 #endif
1960 		state[1].scissor = fz_empty_irect;
1961 		state[1].mask = NULL;
1962 		return;
1963 	}
1964 
1965 #ifdef DUMP_GROUP_BLENDS
1966 	dump_spaces(dev->top-1, "Clip (image mask) begin\n");
1967 #endif
1968 
1969 	bbox = fz_irect_from_rect(fz_transform_rect(fz_unit_rect, local_ctm));
1970 	bbox = fz_intersect_irect(bbox, state->scissor);
1971 	if (!fz_is_infinite_rect(scissor))
1972 	{
1973 		fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1974 		bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1975 	}
1976 
1977 	fz_try(ctx)
1978 	{
1979 		pixmap = fz_get_pixmap_from_image(ctx, image, NULL, &local_ctm, &dx, &dy);
1980 
1981 		state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1982 		fz_clear_pixmap(ctx, state[1].mask);
1983 
1984 		state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1985 		fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1986 		if (state[0].shape)
1987 		{
1988 			state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1989 			fz_clear_pixmap(ctx, state[1].shape);
1990 		}
1991 		if (state[0].group_alpha)
1992 		{
1993 			state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1994 			fz_clear_pixmap(ctx, state[1].group_alpha);
1995 		}
1996 
1997 		state[1].blendmode |= FZ_BLEND_ISOLATED;
1998 		state[1].scissor = bbox;
1999 
2000 		if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
2001 		{
2002 			int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
2003 			scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
2004 			if (!scaled)
2005 			{
2006 				if (dx < 1)
2007 					dx = 1;
2008 				if (dy < 1)
2009 					dy = 1;
2010 				scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
2011 			}
2012 			if (scaled)
2013 			{
2014 				fz_drop_pixmap(ctx, pixmap);
2015 				pixmap = scaled;
2016 			}
2017 		}
2018 
2019 #ifdef DUMP_GROUP_BLENDS
2020 		dump_spaces(dev->top, "");
2021 		fz_dump_blend(ctx, "Creating imagemask: plotting ", pixmap);
2022 		fz_dump_blend(ctx, " onto ", state[1].mask);
2023 		if (state[1].shape)
2024 			fz_dump_blend(ctx, "/S=", state[1].shape);
2025 		if (state[1].group_alpha)
2026 			fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2027 #endif
2028 
2029 		fz_paint_image(ctx, state[1].mask, &bbox, state[1].shape, state[1].group_alpha, pixmap, local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
2030 
2031 #ifdef DUMP_GROUP_BLENDS
2032 		fz_dump_blend(ctx, " to get ", state[1].mask);
2033 		if (state[1].shape)
2034 			fz_dump_blend(ctx, "/S=", state[1].shape);
2035 		if (state[1].group_alpha)
2036 			fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2037 		printf("\n");
2038 #endif
2039 	}
2040 	fz_always(ctx)
2041 		fz_drop_pixmap(ctx, pixmap);
2042 	fz_catch(ctx)
2043 		fz_rethrow(ctx);
2044 }
2045 
2046 static void
fz_draw_pop_clip(fz_context * ctx,fz_device * devp)2047 fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
2048 {
2049 	fz_draw_device *dev = (fz_draw_device*)devp;
2050 	fz_draw_state *state;
2051 
2052 	if (dev->top == 0)
2053 		fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected pop clip");
2054 
2055 	state = pop_stack(ctx, dev, "clip");
2056 
2057 	/* We can get here with state[1].mask == NULL if the clipping actually
2058 	 * resolved to a rectangle earlier.
2059 	 */
2060 	if (state[1].mask)
2061 	{
2062 #ifdef DUMP_GROUP_BLENDS
2063 		dump_spaces(dev->top, "");
2064 		fz_dump_blend(ctx, "Clipping ", state[1].dest);
2065 		if (state[1].shape)
2066 			fz_dump_blend(ctx, "/S=", state[1].shape);
2067 		if (state[1].group_alpha)
2068 			fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2069 		fz_dump_blend(ctx, " onto ", state[0].dest);
2070 		if (state[0].shape)
2071 			fz_dump_blend(ctx, "/S=", state[0].shape);
2072 		if (state[0].group_alpha)
2073 			fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2074 		fz_dump_blend(ctx, " with ", state[1].mask);
2075 #endif
2076 
2077 		fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask);
2078 		if (state[0].shape != state[1].shape)
2079 		{
2080 			fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask);
2081 			fz_drop_pixmap(ctx, state[1].shape);
2082 			state[1].shape = NULL;
2083 		}
2084 		if (state[0].group_alpha != state[1].group_alpha)
2085 		{
2086 			fz_paint_pixmap_with_mask(state[0].group_alpha, state[1].group_alpha, state[1].mask);
2087 			fz_drop_pixmap(ctx, state[1].group_alpha);
2088 			state[1].group_alpha = NULL;
2089 		}
2090 		fz_drop_pixmap(ctx, state[1].mask);
2091 		state[1].mask = NULL;
2092 		fz_drop_pixmap(ctx, state[1].dest);
2093 		state[1].dest = NULL;
2094 
2095 #ifdef DUMP_GROUP_BLENDS
2096 		fz_dump_blend(ctx, " to get ", state[0].dest);
2097 		if (state[0].shape)
2098 			fz_dump_blend(ctx, "/S=", state[0].shape);
2099 		if (state[0].group_alpha)
2100 			fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2101 		printf("\n");
2102 #endif
2103 	}
2104 	else
2105 	{
2106 #ifdef DUMP_GROUP_BLENDS
2107 		dump_spaces(dev->top, "Clip end\n");
2108 #endif
2109 	}
2110 }
2111 
2112 static void
fz_draw_begin_mask(fz_context * ctx,fz_device * devp,fz_rect area,int luminosity,fz_colorspace * colorspace_in,const float * colorfv,fz_color_params color_params)2113 fz_draw_begin_mask(fz_context *ctx, fz_device *devp, fz_rect area, int luminosity, fz_colorspace *colorspace_in, const float *colorfv, fz_color_params color_params)
2114 {
2115 	fz_draw_device *dev = (fz_draw_device*)devp;
2116 	fz_pixmap *dest;
2117 	fz_irect bbox;
2118 	fz_draw_state *state = push_stack(ctx, dev, "mask");
2119 	fz_pixmap *shape = state->shape;
2120 	fz_pixmap *group_alpha = state->group_alpha;
2121 	fz_rect trect;
2122 	fz_colorspace *colorspace = NULL;
2123 
2124 	if (dev->top == 0 && dev->resolve_spots)
2125 		state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
2126 
2127 	if (colorspace_in)
2128 		colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
2129 
2130 	trect = fz_transform_rect(area, dev->transform);
2131 	bbox = fz_intersect_irect(fz_irect_from_rect(trect), state->scissor);
2132 
2133 	/* Reset the blendmode for the mask rendering. In particular,
2134 	 * don't carry forward knockout or isolated. */
2135 	state[1].blendmode = 0;
2136 
2137 	/* If luminosity, then we generate a mask from the greyscale value of the shapes.
2138 	 * If !luminosity, then we generate a mask from the alpha value of the shapes.
2139 	 */
2140 	if (luminosity)
2141 		state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0);
2142 	else
2143 		state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2144 	if (state->shape)
2145 	{
2146 		/* FIXME: If we ever want to support AIS true, then
2147 		 * we probably want to create a shape pixmap here,
2148 		 * using: shape = fz_new_pixmap_with_bbox(NULL, bbox);
2149 		 * then, in the end_mask code, we create the mask
2150 		 * from this rather than dest.
2151 		 */
2152 		state[1].shape = shape = NULL;
2153 	}
2154 	if (state->group_alpha)
2155 	{
2156 		state[1].group_alpha = group_alpha = NULL;
2157 	}
2158 
2159 	if (luminosity)
2160 	{
2161 		float bc;
2162 		if (!colorspace)
2163 			colorspace = fz_device_gray(ctx);
2164 		fz_convert_color(ctx, colorspace, colorfv, fz_device_gray(ctx), &bc, NULL, color_params);
2165 		fz_clear_pixmap_with_value(ctx, dest, bc * 255);
2166 		if (shape)
2167 			fz_clear_pixmap_with_value(ctx, shape, 255);
2168 		if (group_alpha)
2169 			fz_clear_pixmap_with_value(ctx, group_alpha, 255);
2170 	}
2171 	else
2172 	{
2173 		fz_clear_pixmap(ctx, dest);
2174 		if (shape)
2175 			fz_clear_pixmap(ctx, shape);
2176 		if (group_alpha)
2177 			fz_clear_pixmap(ctx, group_alpha);
2178 	}
2179 
2180 #ifdef DUMP_GROUP_BLENDS
2181 	dump_spaces(dev->top-1, "Mask begin\n");
2182 #endif
2183 	state[1].scissor = bbox;
2184 }
2185 
2186 static void
fz_draw_end_mask(fz_context * ctx,fz_device * devp)2187 fz_draw_end_mask(fz_context *ctx, fz_device *devp)
2188 {
2189 	fz_draw_device *dev = (fz_draw_device*)devp;
2190 	fz_pixmap *temp, *dest;
2191 	fz_irect bbox;
2192 	fz_draw_state *state;
2193 
2194 	if (dev->top == 0)
2195 		fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end mask");
2196 
2197 	state = convert_stack(ctx, dev, "mask");
2198 
2199 #ifdef DUMP_GROUP_BLENDS
2200 	dump_spaces(dev->top-1, "Mask -> Clip: ");
2201 	fz_dump_blend(ctx, "Mask ", state[1].dest);
2202 	if (state[1].shape)
2203 		fz_dump_blend(ctx, "/S=", state[1].shape);
2204 	if (state[1].group_alpha)
2205 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2206 #endif
2207 	{
2208 		/* convert to alpha mask */
2209 		temp = fz_alpha_from_gray(ctx, state[1].dest);
2210 		if (state[1].mask != state[0].mask)
2211 			fz_drop_pixmap(ctx, state[1].mask);
2212 		state[1].mask = temp;
2213 		if (state[1].dest != state[0].dest)
2214 			fz_drop_pixmap(ctx, state[1].dest);
2215 		state[1].dest = NULL;
2216 		if (state[1].shape != state[0].shape)
2217 			fz_drop_pixmap(ctx, state[1].shape);
2218 		state[1].shape = NULL;
2219 		if (state[1].group_alpha != state[0].group_alpha)
2220 			fz_drop_pixmap(ctx, state[1].group_alpha);
2221 		state[1].group_alpha = NULL;
2222 
2223 #ifdef DUMP_GROUP_BLENDS
2224 		fz_dump_blend(ctx, "-> Clip ", temp);
2225 		printf("\n");
2226 #endif
2227 
2228 		/* create new dest scratch buffer */
2229 		bbox = fz_pixmap_bbox(ctx, temp);
2230 		dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
2231 		fz_copy_pixmap_rect(ctx, dest, state->dest, bbox, dev->default_cs);
2232 
2233 		/* push soft mask as clip mask */
2234 		state[1].dest = dest;
2235 		state[1].blendmode |= FZ_BLEND_ISOLATED;
2236 		/* If we have a shape, then it'll need to be masked with the
2237 		 * clip mask when we pop. So create a new shape now. */
2238 		if (state[0].shape)
2239 		{
2240 			state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2241 			fz_clear_pixmap(ctx, state[1].shape);
2242 		}
2243 		if (state[0].group_alpha)
2244 		{
2245 			state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2246 			fz_clear_pixmap(ctx, state[1].group_alpha);
2247 		}
2248 		state[1].scissor = bbox;
2249 	}
2250 }
2251 
2252 static void
fz_draw_begin_group(fz_context * ctx,fz_device * devp,fz_rect area,fz_colorspace * cs,int isolated,int knockout,int blendmode,float alpha)2253 fz_draw_begin_group(fz_context *ctx, fz_device *devp, fz_rect area, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
2254 {
2255 	fz_draw_device *dev = (fz_draw_device*)devp;
2256 	fz_irect bbox;
2257 	fz_pixmap *dest;
2258 	fz_draw_state *state = &dev->stack[dev->top];
2259 	fz_colorspace *model = state->dest->colorspace;
2260 	fz_rect trect;
2261 
2262 	if (dev->top == 0 && dev->resolve_spots)
2263 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
2264 
2265 	if (cs != NULL)
2266 		model = fz_default_colorspace(ctx, dev->default_cs, cs);
2267 
2268 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
2269 		fz_knockout_begin(ctx, dev);
2270 
2271 	state = push_stack(ctx, dev, "group");
2272 
2273 	trect = fz_transform_rect(area, dev->transform);
2274 	bbox = fz_intersect_irect(fz_irect_from_rect(trect), state->scissor);
2275 
2276 #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED
2277 	knockout = 0;
2278 	isolated = 1;
2279 #endif
2280 
2281 	state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha || isolated);
2282 
2283 	if (isolated)
2284 	{
2285 		fz_clear_pixmap(ctx, dest);
2286 		state[1].group_alpha = NULL;
2287 	}
2288 	else
2289 	{
2290 		fz_copy_pixmap_rect(ctx, dest, state[0].dest, bbox, dev->default_cs);
2291 		state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2292 		fz_clear_pixmap(ctx, state[1].group_alpha);
2293 	}
2294 
2295 	/* shape is inherited from the previous group */
2296 	state[1].alpha = alpha;
2297 
2298 #ifdef DUMP_GROUP_BLENDS
2299 	dump_spaces(dev->top-1, "");
2300 	{
2301 		char text[240];
2302 		char atext[80];
2303 		char btext[80];
2304 		if (alpha != 1)
2305 			sprintf(atext, " (alpha %g)", alpha);
2306 		else
2307 			atext[0] = 0;
2308 		if (blendmode != 0)
2309 			sprintf(btext, " (blend %d)", blendmode);
2310 		else
2311 			btext[0] = 0;
2312 		sprintf(text, "Group begin%s%s%s%s: background is ", isolated ? " (isolated)" : "", knockout ? " (knockout)" : "", atext, btext);
2313 		fz_dump_blend(ctx, text, state[1].dest);
2314 	}
2315 	if (state[1].shape)
2316 		fz_dump_blend(ctx, "/S=", state[1].shape);
2317 	if (state[1].group_alpha)
2318 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2319 	printf("\n");
2320 #endif
2321 
2322 	state[1].scissor = bbox;
2323 	state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0);
2324 }
2325 
2326 static void
fz_draw_end_group(fz_context * ctx,fz_device * devp)2327 fz_draw_end_group(fz_context *ctx, fz_device *devp)
2328 {
2329 	fz_draw_device *dev = (fz_draw_device*)devp;
2330 	int blendmode;
2331 	int isolated;
2332 	float alpha;
2333 	fz_draw_state *state;
2334 
2335 	if (dev->top == 0)
2336 		fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end group");
2337 
2338 	state = pop_stack(ctx, dev, "group");
2339 
2340 	alpha = state[1].alpha;
2341 	blendmode = state[1].blendmode & FZ_BLEND_MODEMASK;
2342 	isolated = state[1].blendmode & FZ_BLEND_ISOLATED;
2343 
2344 #ifdef DUMP_GROUP_BLENDS
2345 	dump_spaces(dev->top, "");
2346 	fz_dump_blend(ctx, "Group end: blending ", state[1].dest);
2347 	if (state[1].shape)
2348 		fz_dump_blend(ctx, "/S=", state[1].shape);
2349 	if (state[1].group_alpha)
2350 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2351 	fz_dump_blend(ctx, " onto ", state[0].dest);
2352 	if (state[0].shape)
2353 		fz_dump_blend(ctx, "/S=", state[0].shape);
2354 	if (state[0].group_alpha)
2355 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2356 	if (alpha != 1.0f)
2357 		printf(" (alpha %g)", alpha);
2358 	if (blendmode != 0)
2359 		printf(" (blend %d)", blendmode);
2360 	if (isolated != 0)
2361 		printf(" (isolated)");
2362 	if (state[1].blendmode & FZ_BLEND_KNOCKOUT)
2363 		printf(" (knockout)");
2364 #endif
2365 
2366 	if (state[0].dest->colorspace != state[1].dest->colorspace)
2367 	{
2368 		fz_pixmap *converted = fz_convert_pixmap(ctx, state[1].dest, state[0].dest->colorspace, NULL, dev->default_cs, fz_default_color_params, 1);
2369 		fz_drop_pixmap(ctx, state[1].dest);
2370 		state[1].dest = converted;
2371 	}
2372 
2373 	if ((blendmode == 0) && (state[0].shape == state[1].shape) && (state[0].group_alpha == state[1].group_alpha))
2374 		fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255);
2375 	else
2376 		fz_blend_pixmap(ctx, state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].group_alpha);
2377 
2378 	if (state[0].shape != state[1].shape)
2379 	{
2380 		/* The 'D' on page 7 of Altona_Technical_v20_x4.pdf goes wrong if this
2381 		 * isn't alpha * 255, as the blend back fails to take account of alpha. */
2382 		if (state[0].shape)
2383 		{
2384 			if (state[1].shape)
2385 				fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255);
2386 			else
2387 				fz_paint_pixmap_alpha(state[0].shape, state[1].dest, alpha * 255);
2388 		}
2389 	}
2390 	assert(state[0].group_alpha == NULL || state[0].group_alpha != state[1].group_alpha);
2391 	if (state[0].group_alpha && state[0].group_alpha != state[1].group_alpha)
2392 	{
2393 		/* The 'D' on page 7 of Altona_Technical_v20_x4.pdf uses an isolated group,
2394 		 * and goes wrong if this is 255 * alpha, as an alpha effectively gets
2395 		 * applied twice. CATX5233 page 7 uses a non-isolated group, and goes wrong
2396 		 * if alpha isn't applied here. */
2397 		if (state[1].group_alpha)
2398 			fz_paint_pixmap(state[0].group_alpha, state[1].group_alpha, isolated ? 255 : alpha * 255);
2399 		else
2400 			fz_paint_pixmap_alpha(state[0].group_alpha, state[1].dest, isolated ? 255 : alpha * 255);
2401 	}
2402 
2403 	assert(state[0].dest != state[1].dest);
2404 
2405 #ifdef DUMP_GROUP_BLENDS
2406 	fz_dump_blend(ctx, " to get ", state[0].dest);
2407 	if (state[0].shape)
2408 		fz_dump_blend(ctx, "/S=", state[0].shape);
2409 	if (state[0].group_alpha)
2410 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2411 	printf("\n");
2412 #endif
2413 
2414 	if (state[0].shape != state[1].shape)
2415 	{
2416 		fz_drop_pixmap(ctx, state[1].shape);
2417 		state[1].shape = NULL;
2418 	}
2419 	fz_drop_pixmap(ctx, state[1].group_alpha);
2420 	state[1].group_alpha = NULL;
2421 	fz_drop_pixmap(ctx, state[1].dest);
2422 	state[1].dest = NULL;
2423 
2424 	if (state[0].blendmode & FZ_BLEND_KNOCKOUT)
2425 		fz_knockout_end(ctx, dev);
2426 }
2427 
2428 typedef struct
2429 {
2430 	int refs;
2431 	float ctm[4];
2432 	int id;
2433 	char has_shape;
2434 	char has_group_alpha;
2435 	fz_colorspace *cs;
2436 } tile_key;
2437 
2438 typedef struct
2439 {
2440 	fz_storable storable;
2441 	fz_pixmap *dest;
2442 	fz_pixmap *shape;
2443 	fz_pixmap *group_alpha;
2444 } tile_record;
2445 
2446 static int
fz_make_hash_tile_key(fz_context * ctx,fz_store_hash * hash,void * key_)2447 fz_make_hash_tile_key(fz_context *ctx, fz_store_hash *hash, void *key_)
2448 {
2449 	tile_key *key = key_;
2450 
2451 	hash->u.im.id = key->id;
2452 	hash->u.im.has_shape = key->has_shape;
2453 	hash->u.im.has_group_alpha = key->has_group_alpha;
2454 	hash->u.im.m[0] = key->ctm[0];
2455 	hash->u.im.m[1] = key->ctm[1];
2456 	hash->u.im.m[2] = key->ctm[2];
2457 	hash->u.im.m[3] = key->ctm[3];
2458 	hash->u.im.ptr = key->cs;
2459 	return 1;
2460 }
2461 
2462 static void *
fz_keep_tile_key(fz_context * ctx,void * key_)2463 fz_keep_tile_key(fz_context *ctx, void *key_)
2464 {
2465 	tile_key *key = key_;
2466 	return fz_keep_imp(ctx, key, &key->refs);
2467 }
2468 
2469 static void
fz_drop_tile_key(fz_context * ctx,void * key_)2470 fz_drop_tile_key(fz_context *ctx, void *key_)
2471 {
2472 	tile_key *key = key_;
2473 	if (fz_drop_imp(ctx, key, &key->refs))
2474 	{
2475 		fz_drop_colorspace_store_key(ctx, key->cs);
2476 		fz_free(ctx, key);
2477 	}
2478 }
2479 
2480 static int
fz_cmp_tile_key(fz_context * ctx,void * k0_,void * k1_)2481 fz_cmp_tile_key(fz_context *ctx, void *k0_, void *k1_)
2482 {
2483 	tile_key *k0 = k0_;
2484 	tile_key *k1 = k1_;
2485 	return k0->id == k1->id &&
2486 		k0->has_shape == k1->has_shape &&
2487 		k0->has_group_alpha == k1->has_group_alpha &&
2488 		k0->ctm[0] == k1->ctm[0] &&
2489 		k0->ctm[1] == k1->ctm[1] &&
2490 		k0->ctm[2] == k1->ctm[2] &&
2491 		k0->ctm[3] == k1->ctm[3] &&
2492 		k0->cs == k1->cs;
2493 }
2494 
2495 static void
fz_format_tile_key(fz_context * ctx,char * s,size_t n,void * key_)2496 fz_format_tile_key(fz_context *ctx, char *s, size_t n, void *key_)
2497 {
2498 	tile_key *key = (tile_key *)key_;
2499 	fz_snprintf(s, n, "(tile id=%x, ctm=%g %g %g %g, cs=%x, shape=%d, ga=%d)",
2500 			key->id, key->ctm[0], key->ctm[1], key->ctm[2], key->ctm[3], key->cs,
2501 			key->has_shape, key->has_group_alpha);
2502 }
2503 
2504 static const fz_store_type fz_tile_store_type =
2505 {
2506 	"struct tile_record",
2507 	fz_make_hash_tile_key,
2508 	fz_keep_tile_key,
2509 	fz_drop_tile_key,
2510 	fz_cmp_tile_key,
2511 	fz_format_tile_key,
2512 	NULL
2513 };
2514 
2515 static void
fz_drop_tile_record_imp(fz_context * ctx,fz_storable * storable)2516 fz_drop_tile_record_imp(fz_context *ctx, fz_storable *storable)
2517 {
2518 	tile_record *tr = (tile_record *)storable;
2519 	fz_drop_pixmap(ctx, tr->dest);
2520 	fz_drop_pixmap(ctx, tr->shape);
2521 	fz_drop_pixmap(ctx, tr->group_alpha);
2522 	fz_free(ctx, tr);
2523 }
2524 
2525 static void
fz_drop_tile_record(fz_context * ctx,tile_record * tile)2526 fz_drop_tile_record(fz_context *ctx, tile_record *tile)
2527 {
2528 	fz_drop_storable(ctx, &tile->storable);
2529 }
2530 
2531 static tile_record *
fz_new_tile_record(fz_context * ctx,fz_pixmap * dest,fz_pixmap * shape,fz_pixmap * group_alpha)2532 fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape, fz_pixmap *group_alpha)
2533 {
2534 	tile_record *tile = fz_malloc_struct(ctx, tile_record);
2535 	FZ_INIT_STORABLE(tile, 1, fz_drop_tile_record_imp);
2536 	tile->dest = fz_keep_pixmap(ctx, dest);
2537 	tile->shape = fz_keep_pixmap(ctx, shape);
2538 	tile->group_alpha = fz_keep_pixmap(ctx, group_alpha);
2539 	return tile;
2540 }
2541 
2542 size_t
fz_tile_size(fz_context * ctx,tile_record * tile)2543 fz_tile_size(fz_context *ctx, tile_record *tile)
2544 {
2545 	if (!tile)
2546 		return 0;
2547 	return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape) + fz_pixmap_size(ctx, tile->group_alpha);
2548 }
2549 
2550 static int
fz_draw_begin_tile(fz_context * ctx,fz_device * devp,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix in_ctm,int id)2551 fz_draw_begin_tile(fz_context *ctx, fz_device *devp, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix in_ctm, int id)
2552 {
2553 	fz_draw_device *dev = (fz_draw_device*)devp;
2554 	fz_matrix ctm = fz_concat(in_ctm, dev->transform);
2555 	fz_pixmap *dest = NULL;
2556 	fz_pixmap *shape, *group_alpha;
2557 	fz_irect bbox;
2558 	fz_draw_state *state = &dev->stack[dev->top];
2559 	fz_colorspace *model = state->dest->colorspace;
2560 	fz_rect local_view;
2561 
2562 	if (dev->top == 0 && dev->resolve_spots)
2563 		state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
2564 
2565 	/* area, view, xstep, ystep are in pattern space */
2566 	/* ctm maps from pattern space to device space */
2567 
2568 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
2569 		fz_knockout_begin(ctx, dev);
2570 
2571 	state = push_stack(ctx, dev, "tile");
2572 
2573 	local_view = fz_transform_rect(view, ctm);
2574 	bbox = fz_irect_from_rect(local_view);
2575 	/* We should never have a bbox that entirely covers our destination.
2576 	 * If we do, then the check for only 1 tile being visible above has
2577 	 * failed. Actually, this *can* fail due to the round_rect, at extreme
2578 	 * resolutions, so disable this assert.
2579 	 * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w ||
2580 	 *	bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h);
2581 	 */
2582 
2583 	/* Check to see if we have one cached */
2584 	if (id)
2585 	{
2586 		tile_key tk;
2587 		tile_record *tile;
2588 		tk.ctm[0] = ctm.a;
2589 		tk.ctm[1] = ctm.b;
2590 		tk.ctm[2] = ctm.c;
2591 		tk.ctm[3] = ctm.d;
2592 		tk.id = id;
2593 		tk.cs = state[1].dest->colorspace;
2594 		tk.has_shape = (state[1].shape != NULL);
2595 		tk.has_group_alpha = (state[1].group_alpha != NULL);
2596 
2597 		tile = fz_find_item(ctx, fz_drop_tile_record_imp, &tk, &fz_tile_store_type);
2598 		if (tile)
2599 		{
2600 			state[1].dest = fz_keep_pixmap(ctx, tile->dest);
2601 			state[1].shape = fz_keep_pixmap(ctx, tile->shape);
2602 			state[1].group_alpha = fz_keep_pixmap(ctx, tile->group_alpha);
2603 			state[1].blendmode |= FZ_BLEND_ISOLATED;
2604 			state[1].xstep = xstep;
2605 			state[1].ystep = ystep;
2606 			state[1].id = id;
2607 			state[1].encache = 0;
2608 			state[1].area = fz_irect_from_rect(area);
2609 			state[1].ctm = ctm;
2610 			state[1].scissor = bbox;
2611 
2612 #ifdef DUMP_GROUP_BLENDS
2613 			dump_spaces(dev->top-1, "Tile begin (cached)\n");
2614 #endif
2615 
2616 			fz_drop_tile_record(ctx, tile);
2617 			return 1;
2618 		}
2619 	}
2620 
2621 	/* Patterns can be transparent, so we need to have an alpha here. */
2622 	state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, 1);
2623 	fz_clear_pixmap(ctx, dest);
2624 	shape = state[0].shape;
2625 	if (shape)
2626 	{
2627 		state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2628 		fz_clear_pixmap(ctx, shape);
2629 	}
2630 	group_alpha = state[0].group_alpha;
2631 	if (group_alpha)
2632 	{
2633 		state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2634 		fz_clear_pixmap(ctx, group_alpha);
2635 	}
2636 
2637 	state[1].blendmode |= FZ_BLEND_ISOLATED;
2638 	state[1].xstep = xstep;
2639 	state[1].ystep = ystep;
2640 	state[1].id = id;
2641 	state[1].encache = 1;
2642 	state[1].area = fz_irect_from_rect(area);
2643 	state[1].ctm = ctm;
2644 	state[1].scissor = bbox;
2645 
2646 #ifdef DUMP_GROUP_BLENDS
2647 	dump_spaces(dev->top-1, "Tile begin\n");
2648 #endif
2649 
2650 	return 0;
2651 }
2652 
2653 static void
fz_draw_end_tile(fz_context * ctx,fz_device * devp)2654 fz_draw_end_tile(fz_context *ctx, fz_device *devp)
2655 {
2656 	fz_draw_device *dev = (fz_draw_device*)devp;
2657 	float xstep, ystep;
2658 	fz_matrix ttm, ctm, shapectm, gactm;
2659 	fz_irect area, scissor, tile_bbox;
2660 	fz_rect scissor_tmp, tile_tmp;
2661 	int x0, y0, x1, y1, x, y, extra_x, extra_y;
2662 	fz_draw_state *state;
2663 	fz_pixmap *dest = NULL;
2664 	fz_pixmap *shape = NULL;
2665 	fz_pixmap *group_alpha = NULL;
2666 
2667 	if (dev->top == 0)
2668 		fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end tile");
2669 
2670 	state = pop_stack(ctx, dev, "tile");
2671 
2672 	xstep = state[1].xstep;
2673 	ystep = state[1].ystep;
2674 	area = state[1].area;
2675 	ctm = state[1].ctm;
2676 
2677 	/* Fudge the scissor bbox a little to allow for inaccuracies in the
2678 	 * matrix inversion. */
2679 	ttm = fz_invert_matrix(ctm);
2680 	scissor_tmp = fz_rect_from_irect(state[0].scissor);
2681 	scissor_tmp = fz_expand_rect(scissor_tmp, 1);
2682 	scissor_tmp = fz_transform_rect(scissor_tmp, ttm);
2683 	scissor = fz_irect_from_rect(scissor_tmp);
2684 	area = fz_intersect_irect(area, scissor);
2685 
2686 	tile_bbox.x0 = state[1].dest->x;
2687 	tile_bbox.y0 = state[1].dest->y;
2688 	tile_bbox.x1 = state[1].dest->w + tile_bbox.x0;
2689 	tile_bbox.y1 = state[1].dest->h + tile_bbox.y0;
2690 	tile_tmp = fz_rect_from_irect(tile_bbox);
2691 	tile_tmp = fz_expand_rect(tile_tmp, 1);
2692 	tile_tmp = fz_transform_rect(tile_tmp, ttm);
2693 
2694 	/* FIXME: area is a bbox, so FP not appropriate here */
2695 	/* In PDF files xstep/ystep can be smaller than view (the area of a
2696 	 * single tile) (see fts_15_1506.pdf for an example). This means that
2697 	 * we have to bias the left hand/bottom edge calculations by the
2698 	 * difference between the step and the width/height of the tile. */
2699 	/* scissor, xstep and area are all in pattern space. */
2700 	extra_x = tile_tmp.x1 - tile_tmp.x0 - xstep;
2701 	if (extra_x < 0)
2702 		extra_x = 0;
2703 	extra_y = tile_tmp.y1 - tile_tmp.y0 - ystep;
2704 	if (extra_y < 0)
2705 		extra_y = 0;
2706 	x0 = floorf((area.x0 - tile_tmp.x0 - extra_x) / xstep);
2707 	y0 = floorf((area.y0 - tile_tmp.y0 - extra_y) / ystep);
2708 	x1 = ceilf((area.x1 - tile_tmp.x0 + extra_x) / xstep);
2709 	y1 = ceilf((area.y1 - tile_tmp.y0 + extra_y) / ystep);
2710 
2711 	ctm.e = state[1].dest->x;
2712 	ctm.f = state[1].dest->y;
2713 	if (state[1].shape)
2714 	{
2715 		shapectm = ctm;
2716 		shapectm.e = state[1].shape->x;
2717 		shapectm.f = state[1].shape->y;
2718 	}
2719 	if (state[1].group_alpha)
2720 	{
2721 		gactm = ctm;
2722 		gactm.e = state[1].group_alpha->x;
2723 		gactm.f = state[1].group_alpha->y;
2724 	}
2725 
2726 #ifdef DUMP_GROUP_BLENDS
2727 	dump_spaces(dev->top, "");
2728 	fz_dump_blend(ctx, "Tiling ", state[1].dest);
2729 	if (state[1].shape)
2730 		fz_dump_blend(ctx, "/S=", state[1].shape);
2731 	if (state[1].group_alpha)
2732 		fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2733 	fz_dump_blend(ctx, " onto ", state[0].dest);
2734 	if (state[0].shape)
2735 		fz_dump_blend(ctx, "/S=", state[0].shape);
2736 	if (state[0].group_alpha)
2737 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2738 #endif
2739 	fz_var(dest);
2740 	fz_var(shape);
2741 	fz_var(group_alpha);
2742 
2743 	fz_try(ctx)
2744 	{
2745 		dest = fz_new_pixmap_from_pixmap(ctx, state[1].dest, NULL);
2746 
2747 		shape = fz_new_pixmap_from_pixmap(ctx, state[1].shape, NULL);
2748 		group_alpha = fz_new_pixmap_from_pixmap(ctx, state[1].group_alpha, NULL);
2749 
2750 		for (y = y0; y < y1; y++)
2751 		{
2752 			for (x = x0; x < x1; x++)
2753 			{
2754 				ttm = fz_pre_translate(ctm, x * xstep, y * ystep);
2755 				dest->x = ttm.e;
2756 				dest->y = ttm.f;
2757 				/* Check for overflow due to float -> int conversions */
2758 				if (dest->x > 0 && dest->x + dest->w < 0)
2759 					continue;
2760 				if (dest->y > 0 && dest->y + dest->h < 0)
2761 					continue;
2762 				fz_paint_pixmap_with_bbox(state[0].dest, dest, 255, state[0].scissor);
2763 				if (shape)
2764 				{
2765 					ttm = fz_pre_translate(shapectm, x * xstep, y * ystep);
2766 					shape->x = ttm.e;
2767 					shape->y = ttm.f;
2768 					fz_paint_pixmap_with_bbox(state[0].shape, shape, 255, state[0].scissor);
2769 				}
2770 				if (group_alpha)
2771 				{
2772 					ttm = fz_pre_translate(gactm, x * xstep, y * ystep);
2773 					group_alpha->x = ttm.e;
2774 					group_alpha->y = ttm.f;
2775 					fz_paint_pixmap_with_bbox(state[0].group_alpha, group_alpha, 255, state[0].scissor);
2776 				}
2777 			}
2778 		}
2779 
2780 		/* Now we try to cache the tiles. Any failure here will just result in us not caching. */
2781 		if (state[1].encache && state[1].id != 0)
2782 		{
2783 			tile_record *tile = NULL;
2784 			tile_key *key = NULL;
2785 			fz_var(tile);
2786 			fz_var(key);
2787 			fz_try(ctx)
2788 			{
2789 				tile_record *existing_tile;
2790 
2791 				tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape, state[1].group_alpha);
2792 
2793 				key = fz_malloc_struct(ctx, tile_key);
2794 				key->refs = 1;
2795 				key->id = state[1].id;
2796 				key->ctm[0] = ctm.a;
2797 				key->ctm[1] = ctm.b;
2798 				key->ctm[2] = ctm.c;
2799 				key->ctm[3] = ctm.d;
2800 				key->cs = fz_keep_colorspace_store_key(ctx, state[1].dest->colorspace);
2801 				key->has_shape = (state[1].shape != NULL);
2802 				key->has_group_alpha = (state[1].group_alpha != NULL);
2803 				existing_tile = fz_store_item(ctx, key, tile, fz_tile_size(ctx, tile), &fz_tile_store_type);
2804 				if (existing_tile)
2805 				{
2806 					/* We already have a tile. This will either have been
2807 					 * produced by a racing thread, or there is already
2808 					 * an entry for this one in the store. */
2809 					fz_drop_tile_record(ctx, tile);
2810 					tile = existing_tile;
2811 				}
2812 			}
2813 			fz_always(ctx)
2814 			{
2815 				fz_drop_tile_key(ctx, key);
2816 				fz_drop_tile_record(ctx, tile);
2817 			}
2818 			fz_catch(ctx)
2819 			{
2820 				/* Do nothing */
2821 			}
2822 		}
2823 	}
2824 	fz_always(ctx)
2825 	{
2826 		fz_drop_pixmap(ctx, dest);
2827 		fz_drop_pixmap(ctx, shape);
2828 		fz_drop_pixmap(ctx, group_alpha);
2829 		fz_drop_pixmap(ctx, state[1].dest);
2830 		state[1].dest = NULL;
2831 		fz_drop_pixmap(ctx, state[1].shape);
2832 		state[1].shape = NULL;
2833 		fz_drop_pixmap(ctx, state[1].group_alpha);
2834 		state[1].group_alpha = NULL;
2835 	}
2836 	fz_catch(ctx)
2837 		fz_rethrow(ctx);
2838 
2839 #ifdef DUMP_GROUP_BLENDS
2840 	fz_dump_blend(ctx, " to get ", state[0].dest);
2841 	if (state[0].shape)
2842 		fz_dump_blend(ctx, "/S=", state[0].shape);
2843 	if (state[0].group_alpha)
2844 		fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2845 	printf("\n");
2846 #endif
2847 
2848 	if (state->blendmode & FZ_BLEND_KNOCKOUT)
2849 		fz_knockout_end(ctx, dev);
2850 }
2851 
2852 static void
fz_draw_render_flags(fz_context * ctx,fz_device * devp,int set,int clear)2853 fz_draw_render_flags(fz_context *ctx, fz_device *devp, int set, int clear)
2854 {
2855 	fz_draw_device *dev = (fz_draw_device*)devp;
2856 	dev->flags = (dev->flags | set ) & ~clear;
2857 }
2858 
2859 static void
fz_draw_set_default_colorspaces(fz_context * ctx,fz_device * devp,fz_default_colorspaces * default_cs)2860 fz_draw_set_default_colorspaces(fz_context *ctx, fz_device *devp, fz_default_colorspaces *default_cs)
2861 {
2862 	fz_draw_device *dev = (fz_draw_device*)devp;
2863 	fz_drop_default_colorspaces(ctx, dev->default_cs);
2864 	dev->default_cs = fz_keep_default_colorspaces(ctx, default_cs);
2865 }
2866 
2867 static void
fz_draw_close_device(fz_context * ctx,fz_device * devp)2868 fz_draw_close_device(fz_context *ctx, fz_device *devp)
2869 {
2870 	fz_draw_device *dev = (fz_draw_device*)devp;
2871 
2872 	/* pop and free the stacks */
2873 	if (dev->top > dev->resolve_spots)
2874 		fz_throw(ctx, FZ_ERROR_GENERIC, "items left on stack in draw device: %d", dev->top);
2875 
2876 	if (dev->resolve_spots && dev->top)
2877 	{
2878 		fz_draw_state *state = &dev->stack[--dev->top];
2879 		fz_try(ctx)
2880 		{
2881 			fz_copy_pixmap_area_converting_seps(ctx, state[1].dest, state[0].dest, dev->proof_cs, fz_default_color_params, dev->default_cs);
2882 			assert(state[1].mask == NULL);
2883 			assert(state[1].shape == NULL);
2884 			assert(state[1].group_alpha == NULL);
2885 		}
2886 		fz_always(ctx)
2887 		{
2888 			fz_drop_pixmap(ctx, state[1].dest);
2889 			state[1].dest = NULL;
2890 		}
2891 		fz_catch(ctx)
2892 			fz_rethrow(ctx);
2893 	}
2894 }
2895 
2896 static void
fz_draw_drop_device(fz_context * ctx,fz_device * devp)2897 fz_draw_drop_device(fz_context *ctx, fz_device *devp)
2898 {
2899 	fz_draw_device *dev = (fz_draw_device*)devp;
2900 	fz_rasterizer *rast = dev->rast;
2901 
2902 	fz_drop_default_colorspaces(ctx, dev->default_cs);
2903 	fz_drop_colorspace(ctx, dev->proof_cs);
2904 
2905 	/* pop and free the stacks */
2906 	for (; dev->top > 0; dev->top--)
2907 	{
2908 		fz_draw_state *state = &dev->stack[dev->top - 1];
2909 		if (state[1].mask != state[0].mask)
2910 			fz_drop_pixmap(ctx, state[1].mask);
2911 		if (state[1].dest != state[0].dest)
2912 			fz_drop_pixmap(ctx, state[1].dest);
2913 		if (state[1].shape != state[0].shape)
2914 			fz_drop_pixmap(ctx, state[1].shape);
2915 		if (state[1].group_alpha != state[0].group_alpha)
2916 			fz_drop_pixmap(ctx, state[1].group_alpha);
2917 	}
2918 
2919 	/* We never free the dest/mask/shape at level 0, as:
2920 	 * 1) dest is passed in and ownership remains with the caller.
2921 	 * 2) shape and mask are NULL at level 0.
2922 	 */
2923 
2924 	if (dev->stack != &dev->init_stack[0])
2925 		fz_free(ctx, dev->stack);
2926 	fz_drop_scale_cache(ctx, dev->cache_x);
2927 	fz_drop_scale_cache(ctx, dev->cache_y);
2928 	fz_drop_rasterizer(ctx, rast);
2929 }
2930 
2931 static fz_device *
new_draw_device(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_aa_context * aa,const fz_irect * clip,fz_colorspace * proof_cs)2932 new_draw_device(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_aa_context *aa, const fz_irect *clip, fz_colorspace *proof_cs)
2933 {
2934 	fz_draw_device *dev = fz_new_derived_device(ctx, fz_draw_device);
2935 
2936 	dev->super.drop_device = fz_draw_drop_device;
2937 	dev->super.close_device = fz_draw_close_device;
2938 
2939 	dev->super.fill_path = fz_draw_fill_path;
2940 	dev->super.stroke_path = fz_draw_stroke_path;
2941 	dev->super.clip_path = fz_draw_clip_path;
2942 	dev->super.clip_stroke_path = fz_draw_clip_stroke_path;
2943 
2944 	dev->super.fill_text = fz_draw_fill_text;
2945 	dev->super.stroke_text = fz_draw_stroke_text;
2946 	dev->super.clip_text = fz_draw_clip_text;
2947 	dev->super.clip_stroke_text = fz_draw_clip_stroke_text;
2948 	dev->super.ignore_text = fz_draw_ignore_text;
2949 
2950 	dev->super.fill_image_mask = fz_draw_fill_image_mask;
2951 	dev->super.clip_image_mask = fz_draw_clip_image_mask;
2952 	dev->super.fill_image = fz_draw_fill_image;
2953 	dev->super.fill_shade = fz_draw_fill_shade;
2954 
2955 	dev->super.pop_clip = fz_draw_pop_clip;
2956 
2957 	dev->super.begin_mask = fz_draw_begin_mask;
2958 	dev->super.end_mask = fz_draw_end_mask;
2959 	dev->super.begin_group = fz_draw_begin_group;
2960 	dev->super.end_group = fz_draw_end_group;
2961 
2962 	dev->super.begin_tile = fz_draw_begin_tile;
2963 	dev->super.end_tile = fz_draw_end_tile;
2964 
2965 	dev->super.render_flags = fz_draw_render_flags;
2966 	dev->super.set_default_colorspaces = fz_draw_set_default_colorspaces;
2967 
2968 	dev->proof_cs = fz_keep_colorspace(ctx, proof_cs);
2969 	dev->transform = transform;
2970 	dev->flags = 0;
2971 	dev->resolve_spots = 0;
2972 	dev->top = 0;
2973 	dev->stack = &dev->init_stack[0];
2974 	dev->stack_cap = STACK_SIZE;
2975 	dev->stack[0].dest = dest;
2976 	dev->stack[0].shape = NULL;
2977 	dev->stack[0].group_alpha = NULL;
2978 	dev->stack[0].mask = NULL;
2979 	dev->stack[0].blendmode = 0;
2980 	dev->stack[0].scissor.x0 = dest->x;
2981 	dev->stack[0].scissor.y0 = dest->y;
2982 	dev->stack[0].scissor.x1 = dest->x + dest->w;
2983 	dev->stack[0].scissor.y1 = dest->y + dest->h;
2984 
2985 	if (clip)
2986 	{
2987 		if (clip->x0 > dev->stack[0].scissor.x0)
2988 			dev->stack[0].scissor.x0 = clip->x0;
2989 		if (clip->x1 < dev->stack[0].scissor.x1)
2990 			dev->stack[0].scissor.x1 = clip->x1;
2991 		if (clip->y0 > dev->stack[0].scissor.y0)
2992 			dev->stack[0].scissor.y0 = clip->y0;
2993 		if (clip->y1 < dev->stack[0].scissor.y1)
2994 			dev->stack[0].scissor.y1 = clip->y1;
2995 	}
2996 
2997 	/* If we have no separations structure at all, then we want a
2998 	 * simple composite rendering (with no overprint simulation).
2999 	 * If we do have a separations structure, so: 1) Any
3000 	 * 'disabled' separations are ignored. 2) Any 'composite'
3001 	 * separations means we will need to do an overprint
3002 	 * simulation.
3003 	 *
3004 	 * The supplied pixmaps 's' will match the number of
3005 	 * 'spots' separations. If we have any 'composite'
3006 	 * separations therefore, we'll need to make a new pixmap
3007 	 * with a new (completely 'spots') separations structure,
3008 	 * render to that, and then map down at the end.
3009 	 *
3010 	 * Unfortunately we can't produce this until we know what
3011 	 * the default_colorspaces etc are, so set a flag for us
3012 	 * to trigger on later.
3013 	 */
3014 	if (dest->seps || dev->proof_cs != NULL)
3015 #if FZ_ENABLE_SPOT_RENDERING
3016 		dev->resolve_spots = 1;
3017 #else
3018 		fz_throw(ctx, FZ_ERROR_GENERIC, "Spot rendering (and overprint/overprint simulation) not available in this build");
3019 #endif
3020 
3021 	fz_try(ctx)
3022 	{
3023 		dev->rast = fz_new_rasterizer(ctx, aa);
3024 		dev->cache_x = fz_new_scale_cache(ctx);
3025 		dev->cache_y = fz_new_scale_cache(ctx);
3026 	}
3027 	fz_catch(ctx)
3028 	{
3029 		fz_drop_device(ctx, (fz_device*)dev);
3030 		fz_rethrow(ctx);
3031 	}
3032 
3033 	return (fz_device*)dev;
3034 }
3035 
3036 fz_device *
fz_new_draw_device(fz_context * ctx,fz_matrix transform,fz_pixmap * dest)3037 fz_new_draw_device(fz_context *ctx, fz_matrix transform, fz_pixmap *dest)
3038 {
3039 	return new_draw_device(ctx, transform, dest, NULL, NULL, NULL);
3040 }
3041 
3042 fz_device *
fz_new_draw_device_with_bbox(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_irect * clip)3043 fz_new_draw_device_with_bbox(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_irect *clip)
3044 {
3045 	return new_draw_device(ctx, transform, dest, NULL, clip, NULL);
3046 }
3047 
3048 fz_device *
fz_new_draw_device_with_proof(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,fz_colorspace * cs)3049 fz_new_draw_device_with_proof(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, fz_colorspace *cs)
3050 {
3051 	return new_draw_device(ctx, transform, dest, NULL, NULL, cs);
3052 }
3053 
3054 fz_device *
fz_new_draw_device_with_bbox_proof(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_irect * clip,fz_colorspace * cs)3055 fz_new_draw_device_with_bbox_proof(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_irect *clip, fz_colorspace *cs)
3056 {
3057 	return new_draw_device(ctx, transform, dest, NULL, clip, cs);
3058 }
3059 
3060 fz_device *
fz_new_draw_device_type3(fz_context * ctx,fz_matrix transform,fz_pixmap * dest)3061 fz_new_draw_device_type3(fz_context *ctx, fz_matrix transform, fz_pixmap *dest)
3062 {
3063 	fz_draw_device *dev = (fz_draw_device*)fz_new_draw_device(ctx, transform, dest);
3064 	dev->flags |= FZ_DRAWDEV_FLAGS_TYPE3;
3065 	return (fz_device*)dev;
3066 }
3067 
3068 fz_irect *
fz_bound_path_accurate(fz_context * ctx,fz_irect * bbox,const fz_irect * scissor,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,float flatness,float linewidth)3069 fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
3070 {
3071 	fz_rasterizer *rast = fz_new_rasterizer(ctx, NULL);
3072 
3073 	fz_try(ctx)
3074 	{
3075 		if (stroke)
3076 			(void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, scissor, bbox);
3077 		else
3078 			(void)fz_flatten_fill_path(ctx, rast, path, ctm, flatness, scissor, bbox);
3079 	}
3080 	fz_always(ctx)
3081 		fz_drop_rasterizer(ctx, rast);
3082 	fz_catch(ctx)
3083 		fz_rethrow(ctx);
3084 
3085 	return bbox;
3086 }
3087 
3088 const char *fz_draw_options_usage =
3089 	"Raster output options:\n"
3090 	"\trotate=N: rotate rendered pages N degrees counterclockwise\n"
3091 	"\tresolution=N: set both X and Y resolution in pixels per inch\n"
3092 	"\tx-resolution=N: X resolution of rendered pages in pixels per inch\n"
3093 	"\ty-resolution=N: Y resolution of rendered pages in pixels per inch\n"
3094 	"\twidth=N: render pages to fit N pixels wide (ignore resolution option)\n"
3095 	"\theight=N: render pages to fit N pixels tall (ignore resolution option)\n"
3096 	"\tcolorspace=(gray|rgb|cmyk): render using specified colorspace\n"
3097 	"\talpha: render pages with alpha channel and transparent background\n"
3098 	"\tgraphics=(aaN|cop|app): set the rasterizer to use\n"
3099 	"\ttext=(aaN|cop|app): set the rasterizer to use for text\n"
3100 	"\t\taaN=antialias with N bits (0 to 8)\n"
3101 	"\t\tcop=center of pixel\n"
3102 	"\t\tapp=any part of pixel\n"
3103 	"\n";
3104 
parse_aa_opts(const char * val)3105 static int parse_aa_opts(const char *val)
3106 {
3107 	if (fz_option_eq(val, "cop"))
3108 		return 9;
3109 	if (fz_option_eq(val, "app"))
3110 		return 10;
3111 	if (val[0] == 'a' && val[1] == 'a' && val[2] >= '0' && val[2] <= '9')
3112 		return  fz_clampi(fz_atoi(&val[2]), 0, 8);
3113 	return 8;
3114 }
3115 
3116 fz_draw_options *
fz_parse_draw_options(fz_context * ctx,fz_draw_options * opts,const char * args)3117 fz_parse_draw_options(fz_context *ctx, fz_draw_options *opts, const char *args)
3118 {
3119 	const char *val;
3120 
3121 	memset(opts, 0, sizeof *opts);
3122 
3123 	opts->x_resolution = 96;
3124 	opts->y_resolution = 96;
3125 	opts->rotate = 0;
3126 	opts->width = 0;
3127 	opts->height = 0;
3128 	opts->colorspace = fz_device_rgb(ctx);
3129 	opts->alpha = 0;
3130 	opts->graphics = fz_aa_level(ctx);
3131 	opts->text = fz_text_aa_level(ctx);
3132 
3133 	if (fz_has_option(ctx, args, "rotate", &val))
3134 		opts->rotate = fz_atoi(val);
3135 	if (fz_has_option(ctx, args, "resolution", &val))
3136 		opts->x_resolution = opts->y_resolution = fz_atoi(val);
3137 	if (fz_has_option(ctx, args, "x-resolution", &val))
3138 		opts->x_resolution = fz_atoi(val);
3139 	if (fz_has_option(ctx, args, "y-resolution", &val))
3140 		opts->y_resolution = fz_atoi(val);
3141 	if (fz_has_option(ctx, args, "width", &val))
3142 		opts->width = fz_atoi(val);
3143 	if (fz_has_option(ctx, args, "height", &val))
3144 		opts->height = fz_atoi(val);
3145 	if (fz_has_option(ctx, args, "colorspace", &val))
3146 	{
3147 		if (fz_option_eq(val, "gray") || fz_option_eq(val, "grey") || fz_option_eq(val, "mono"))
3148 			opts->colorspace = fz_device_gray(ctx);
3149 		else if (fz_option_eq(val, "rgb"))
3150 			opts->colorspace = fz_device_rgb(ctx);
3151 		else if (fz_option_eq(val, "cmyk"))
3152 			opts->colorspace = fz_device_cmyk(ctx);
3153 		else
3154 			fz_throw(ctx, FZ_ERROR_GENERIC, "unknown colorspace in options");
3155 	}
3156 	if (fz_has_option(ctx, args, "alpha", &val))
3157 		opts->alpha = fz_option_eq(val, "yes");
3158 	if (fz_has_option(ctx, args, "graphics", &val))
3159 		opts->text = opts->graphics = parse_aa_opts(val);
3160 	if (fz_has_option(ctx, args, "text", &val))
3161 		opts->text = parse_aa_opts(val);
3162 
3163 	/* Sanity check values */
3164 	if (opts->x_resolution <= 0) opts->x_resolution = 96;
3165 	if (opts->y_resolution <= 0) opts->y_resolution = 96;
3166 	if (opts->width < 0) opts->width = 0;
3167 	if (opts->height < 0) opts->height = 0;
3168 
3169 	return opts;
3170 }
3171 
3172 fz_device *
fz_new_draw_device_with_options(fz_context * ctx,const fz_draw_options * opts,fz_rect mediabox,fz_pixmap ** pixmap)3173 fz_new_draw_device_with_options(fz_context *ctx, const fz_draw_options *opts, fz_rect mediabox, fz_pixmap **pixmap)
3174 {
3175 	fz_aa_context aa = ctx->aa;
3176 	float x_zoom = opts->x_resolution / 72.0f;
3177 	float y_zoom = opts->y_resolution / 72.0f;
3178 	float page_w = mediabox.x1 - mediabox.x0;
3179 	float page_h = mediabox.y1 - mediabox.y0;
3180 	float w = opts->width;
3181 	float h = opts->height;
3182 	float x_scale, y_scale;
3183 	fz_matrix transform;
3184 	fz_irect bbox;
3185 	fz_device *dev;
3186 
3187 	fz_set_rasterizer_graphics_aa_level(ctx, &aa, opts->graphics);
3188 	fz_set_rasterizer_text_aa_level(ctx, &aa, opts->text);
3189 
3190 	if (w > 0)
3191 	{
3192 		x_scale = w / page_w;
3193 		if (h > 0)
3194 			y_scale = h / page_h;
3195 		else
3196 			y_scale = floorf(page_h * x_scale + 0.5f) / page_h;
3197 	}
3198 	else if (h > 0)
3199 	{
3200 		y_scale = h / page_h;
3201 		x_scale = floorf(page_w * y_scale + 0.5f) / page_w;
3202 	}
3203 	else
3204 	{
3205 		x_scale = floorf(page_w * x_zoom + 0.5f) / page_w;
3206 		y_scale = floorf(page_h * y_zoom + 0.5f) / page_h;
3207 	}
3208 
3209 	transform = fz_pre_rotate(fz_scale(x_scale, y_scale), opts->rotate);
3210 	bbox = fz_irect_from_rect(fz_transform_rect(mediabox, transform));
3211 
3212 	*pixmap = fz_new_pixmap_with_bbox(ctx, opts->colorspace, bbox, NULL, opts->alpha);
3213 	fz_try(ctx)
3214 	{
3215 		fz_set_pixmap_resolution(ctx, *pixmap, opts->x_resolution, opts->y_resolution);
3216 		if (opts->alpha)
3217 			fz_clear_pixmap(ctx, *pixmap);
3218 		else
3219 			fz_clear_pixmap_with_value(ctx, *pixmap, 255);
3220 
3221 		dev = new_draw_device(ctx, transform, *pixmap, &aa, NULL, NULL);
3222 	}
3223 	fz_catch(ctx)
3224 	{
3225 		fz_drop_pixmap(ctx, *pixmap);
3226 		*pixmap = NULL;
3227 		fz_rethrow(ctx);
3228 	}
3229 	return dev;
3230 }
3231