beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / CairoOutputDev.cc
blobd0d6cb43f11e70a8438a063f5b850263e8dbd579
1 //========================================================================
2 //
3 // CairoOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 // Copyright 2004 Red Hat, Inc
7 //
8 //========================================================================
10 //========================================================================
12 // Modified under the Poppler project - http://poppler.freedesktop.org
14 // All changes made under the Poppler project to this file are licensed
15 // under GPL version 2 or later
17 // Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
19 // Copyright (C) 2005, 2009, 2012 Albert Astals Cid <aacid@kde.org>
20 // Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
21 // Copyright (C) 2006-2011, 2013, 2014 Carlos Garcia Campos <carlosgc@gnome.org>
22 // Copyright (C) 2008 Carl Worth <cworth@cworth.org>
23 // Copyright (C) 2008-2015 Adrian Johnson <ajohnson@redneon.com>
24 // Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
25 // Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
26 // Copyright (C) 2008, 2012 Hib Eris <hib@hiberis.nl>
27 // Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
28 // Copyright (C) 2011-2014 Thomas Freitag <Thomas.Freitag@alfa.de>
29 // Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com>
30 // Copyright (C) 2012, 2015 Jason Crain <jason@aquaticape.us>
31 // Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
33 // To see a description of the changes please see the Changelog file that
34 // came with your tarball or type make ChangeLog if you are building from git
36 //========================================================================
38 #include <config.h>
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
42 #endif
44 #include <string.h>
45 #include <math.h>
46 #include <assert.h>
47 #include <cairo.h>
49 #include "goo/gfile.h"
50 #include "goo/gtypes_p.h"
51 #include "GlobalParams.h"
52 #include "Error.h"
53 #include "Object.h"
54 #include "Gfx.h"
55 #include "GfxState.h"
56 #include "GfxFont.h"
57 #include "Page.h"
58 #include "Link.h"
59 #include "FontEncodingTables.h"
60 #include "PDFDocEncoding.h"
61 #include <fofi/FoFiTrueType.h>
62 #include <splash/SplashBitmap.h>
63 #include "CairoOutputDev.h"
64 #include "CairoFontEngine.h"
65 #include "CairoRescaleBox.h"
66 #include "UnicodeMap.h"
67 #include "JBIG2Stream.h"
68 //------------------------------------------------------------------------
70 // #define LOG_CAIRO
72 #ifdef LOG_CAIRO
73 #define LOG(x) (x)
74 #else
75 #define LOG(x)
76 #endif
78 static inline void printMatrix(cairo_matrix_t *matrix){
79 printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx,
80 matrix->xy, matrix->yy,
81 matrix->x0, matrix->y0);
85 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
86 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
89 //------------------------------------------------------------------------
90 // CairoImage
91 //------------------------------------------------------------------------
93 CairoImage::CairoImage (double x1, double y1, double x2, double y2) {
94 this->image = NULL;
95 this->x1 = x1;
96 this->y1 = y1;
97 this->x2 = x2;
98 this->y2 = y2;
101 CairoImage::~CairoImage () {
102 if (image)
103 cairo_surface_destroy (image);
106 void CairoImage::setImage (cairo_surface_t *image) {
107 if (this->image)
108 cairo_surface_destroy (this->image);
109 this->image = cairo_surface_reference (image);
112 //------------------------------------------------------------------------
113 // CairoOutputDev
114 //------------------------------------------------------------------------
116 // We cannot tie the lifetime of an FT_Library object to that of
117 // CairoOutputDev, since any FT_Faces created with it may end up with a
118 // reference by Cairo which can be held long after the CairoOutputDev is
119 // deleted. The simplest way to avoid problems is to never tear down the
120 // FT_Library instance; to avoid leaks, just use a single global instance
121 // initialized the first time it is needed.
122 FT_Library CairoOutputDev::ft_lib;
123 GBool CairoOutputDev::ft_lib_initialized = gFalse;
125 CairoOutputDev::CairoOutputDev() {
126 doc = NULL;
128 if (!ft_lib_initialized) {
129 FT_Init_FreeType(&ft_lib);
130 ft_lib_initialized = gTrue;
133 fontEngine = NULL;
134 fontEngine_owner = gFalse;
135 glyphs = NULL;
136 fill_pattern = NULL;
137 fill_color.r = fill_color.g = fill_color.b = 0;
138 stroke_pattern = NULL;
139 stroke_color.r = stroke_color.g = stroke_color.b = 0;
140 stroke_opacity = 1.0;
141 fill_opacity = 1.0;
142 textClipPath = NULL;
143 strokePathClip = NULL;
144 cairo = NULL;
145 currentFont = NULL;
146 prescaleImages = gTrue;
147 printing = gTrue;
148 use_show_text_glyphs = gFalse;
149 inUncoloredPattern = gFalse;
150 inType3Char = gFalse;
151 t3_glyph_has_bbox = gFalse;
152 text_matrix_valid = gTrue;
154 groupColorSpaceStack = NULL;
155 maskStack = NULL;
156 group = NULL;
157 mask = NULL;
158 shape = NULL;
159 cairo_shape = NULL;
160 knockoutCount = 0;
162 text = NULL;
163 actualText = NULL;
165 // the SA parameter supposedly defaults to false, but Acrobat
166 // apparently hardwires it to true
167 stroke_adjust = globalParams->getStrokeAdjust();
168 align_stroke_coords = gFalse;
169 adjusted_stroke_width = gFalse;
170 xref = NULL;
173 CairoOutputDev::~CairoOutputDev() {
174 if (fontEngine_owner && fontEngine) {
175 delete fontEngine;
178 if (cairo)
179 cairo_destroy (cairo);
180 cairo_pattern_destroy (stroke_pattern);
181 cairo_pattern_destroy (fill_pattern);
182 if (group)
183 cairo_pattern_destroy (group);
184 if (mask)
185 cairo_pattern_destroy (mask);
186 if (shape)
187 cairo_pattern_destroy (shape);
188 if (text)
189 text->decRefCnt();
190 if (actualText)
191 delete actualText;
194 void CairoOutputDev::setCairo(cairo_t *cairo)
196 if (this->cairo != NULL) {
197 cairo_status_t status = cairo_status (this->cairo);
198 if (status) {
199 error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status));
201 cairo_destroy (this->cairo);
202 assert(!cairo_shape);
204 if (cairo != NULL) {
205 this->cairo = cairo_reference (cairo);
206 /* save the initial matrix so that we can use it for type3 fonts. */
207 //XXX: is this sufficient? could we miss changes to the matrix somehow?
208 cairo_get_matrix(cairo, &orig_matrix);
209 } else {
210 this->cairo = NULL;
211 this->cairo_shape = NULL;
215 void CairoOutputDev::setTextPage(TextPage *text)
217 if (this->text)
218 this->text->decRefCnt();
219 if (actualText)
220 delete actualText;
221 if (text) {
222 this->text = text;
223 this->text->incRefCnt();
224 actualText = new ActualText(text);
225 } else {
226 this->text = NULL;
227 actualText = NULL;
231 void CairoOutputDev::startDoc(PDFDoc *docA,
232 CairoFontEngine *parentFontEngine) {
233 doc = docA;
234 if (parentFontEngine) {
235 fontEngine = parentFontEngine;
236 } else {
237 if (fontEngine) {
238 delete fontEngine;
240 fontEngine = new CairoFontEngine(ft_lib);
241 fontEngine_owner = gTrue;
243 xref = doc->getXRef();
246 void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) {
247 /* set up some per page defaults */
248 cairo_pattern_destroy(fill_pattern);
249 cairo_pattern_destroy(stroke_pattern);
251 fill_pattern = cairo_pattern_create_rgb(0., 0., 0.);
252 fill_color.r = fill_color.g = fill_color.b = 0;
253 stroke_pattern = cairo_pattern_reference(fill_pattern);
254 stroke_color.r = stroke_color.g = stroke_color.b = 0;
256 if (text)
257 text->startPage(state);
258 if (xrefA != NULL) {
259 xref = xrefA;
263 void CairoOutputDev::endPage() {
264 if (text) {
265 text->endPage();
266 text->coalesce(gTrue, 0, gFalse);
270 void CairoOutputDev::saveState(GfxState *state) {
271 LOG(printf ("save\n"));
272 cairo_save (cairo);
273 if (cairo_shape)
274 cairo_save (cairo_shape);
276 MaskStack *ms = new MaskStack;
277 ms->mask = cairo_pattern_reference(mask);
278 ms->mask_matrix = mask_matrix;
279 ms->next = maskStack;
280 maskStack = ms;
283 void CairoOutputDev::restoreState(GfxState *state) {
284 LOG(printf ("restore\n"));
285 cairo_restore (cairo);
286 if (cairo_shape)
287 cairo_restore (cairo_shape);
289 text_matrix_valid = gTrue;
291 /* These aren't restored by cairo_restore() since we keep them in
292 * the output device. */
293 updateFillColor(state);
294 updateStrokeColor(state);
295 updateFillOpacity(state);
296 updateStrokeOpacity(state);
297 updateBlendMode(state);
299 MaskStack* ms = maskStack;
300 if (ms) {
301 if (mask)
302 cairo_pattern_destroy(mask);
303 mask = ms->mask;
304 mask_matrix = ms->mask_matrix;
305 maskStack = ms->next;
306 delete ms;
310 void CairoOutputDev::updateAll(GfxState *state) {
311 updateLineDash(state);
312 updateLineJoin(state);
313 updateLineCap(state);
314 updateLineWidth(state);
315 updateFlatness(state);
316 updateMiterLimit(state);
317 updateFillColor(state);
318 updateStrokeColor(state);
319 updateFillOpacity(state);
320 updateStrokeOpacity(state);
321 updateBlendMode(state);
322 needFontUpdate = gTrue;
323 if (text)
324 text->updateFont(state);
327 void CairoOutputDev::setDefaultCTM(double *ctm) {
328 cairo_matrix_t matrix;
329 matrix.xx = ctm[0];
330 matrix.yx = ctm[1];
331 matrix.xy = ctm[2];
332 matrix.yy = ctm[3];
333 matrix.x0 = ctm[4];
334 matrix.y0 = ctm[5];
336 cairo_transform (cairo, &matrix);
337 if (cairo_shape)
338 cairo_transform (cairo_shape, &matrix);
340 OutputDev::setDefaultCTM(ctm);
343 void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
344 double m21, double m22,
345 double m31, double m32) {
346 cairo_matrix_t matrix, invert_matrix;
347 matrix.xx = m11;
348 matrix.yx = m12;
349 matrix.xy = m21;
350 matrix.yy = m22;
351 matrix.x0 = m31;
352 matrix.y0 = m32;
354 /* Make sure the matrix is invertible before setting it.
355 * cairo will blow up if we give it a matrix that's not
356 * invertible, so we need to check before passing it
357 * to cairo_transform. Ignoring it is likely to give better
358 * results than not rendering anything at all. See #14398
360 * Ideally, we could do the cairo_transform
361 * and then check if anything went wrong and fix it then
362 * instead of having to invert the matrix. */
363 invert_matrix = matrix;
364 if (cairo_matrix_invert(&invert_matrix)) {
365 error(errSyntaxWarning, -1, "matrix not invertible\n");
366 return;
369 cairo_transform (cairo, &matrix);
370 if (cairo_shape)
371 cairo_transform (cairo_shape, &matrix);
372 updateLineDash(state);
373 updateLineJoin(state);
374 updateLineCap(state);
375 updateLineWidth(state);
378 void CairoOutputDev::updateLineDash(GfxState *state) {
379 double *dashPattern;
380 int dashLength;
381 double dashStart;
383 state->getLineDash(&dashPattern, &dashLength, &dashStart);
384 cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
385 if (cairo_shape)
386 cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart);
389 void CairoOutputDev::updateFlatness(GfxState *state) {
390 // cairo_set_tolerance (cairo, state->getFlatness());
393 void CairoOutputDev::updateLineJoin(GfxState *state) {
394 switch (state->getLineJoin()) {
395 case 0:
396 cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
397 break;
398 case 1:
399 cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND);
400 break;
401 case 2:
402 cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL);
403 break;
405 if (cairo_shape)
406 cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo));
409 void CairoOutputDev::updateLineCap(GfxState *state) {
410 switch (state->getLineCap()) {
411 case 0:
412 cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
413 break;
414 case 1:
415 cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND);
416 break;
417 case 2:
418 cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE);
419 break;
421 if (cairo_shape)
422 cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo));
425 void CairoOutputDev::updateMiterLimit(GfxState *state) {
426 cairo_set_miter_limit (cairo, state->getMiterLimit());
427 if (cairo_shape)
428 cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
431 void CairoOutputDev::updateLineWidth(GfxState *state) {
432 LOG(printf ("line width: %f\n", state->getLineWidth()));
433 adjusted_stroke_width = gFalse;
434 double width = state->getLineWidth();
435 if (stroke_adjust && !printing) {
436 double x, y;
437 x = y = width;
439 /* find out line width in device units */
440 cairo_user_to_device_distance(cairo, &x, &y);
441 if (fabs(x) <= 1.0 && fabs(y) <= 1.0) {
442 /* adjust width to at least one device pixel */
443 x = y = 1.0;
444 cairo_device_to_user_distance(cairo, &x, &y);
445 width = MIN(fabs(x),fabs(y));
446 adjusted_stroke_width = gTrue;
448 } else if (width == 0.0) {
449 /* Cairo does not support 0 line width == 1 device pixel. Find out
450 * how big pixels (device unit) are in the x and y
451 * directions. Choose the smaller of the two as our line width.
453 double x = 1.0, y = 1.0;
454 if (printing) {
455 // assume printer pixel size is 1/600 inch
456 x = 72.0/600;
457 y = 72.0/600;
459 cairo_device_to_user_distance(cairo, &x, &y);
460 width = MIN(fabs(x),fabs(y));
462 cairo_set_line_width (cairo, width);
463 if (cairo_shape)
464 cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo));
467 void CairoOutputDev::updateFillColor(GfxState *state) {
468 GfxRGB color = fill_color;
470 if (inUncoloredPattern)
471 return;
473 state->getFillRGB(&fill_color);
474 if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
475 color.r != fill_color.r ||
476 color.g != fill_color.g ||
477 color.b != fill_color.b)
479 cairo_pattern_destroy(fill_pattern);
480 fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
481 colToDbl(fill_color.g),
482 colToDbl(fill_color.b),
483 fill_opacity);
485 LOG(printf ("fill color: %d %d %d\n",
486 fill_color.r, fill_color.g, fill_color.b));
490 void CairoOutputDev::updateStrokeColor(GfxState *state) {
491 GfxRGB color = stroke_color;
493 if (inUncoloredPattern)
494 return;
496 state->getStrokeRGB(&stroke_color);
497 if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID ||
498 color.r != stroke_color.r ||
499 color.g != stroke_color.g ||
500 color.b != stroke_color.b)
502 cairo_pattern_destroy(stroke_pattern);
503 stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
504 colToDbl(stroke_color.g),
505 colToDbl(stroke_color.b),
506 stroke_opacity);
508 LOG(printf ("stroke color: %d %d %d\n",
509 stroke_color.r, stroke_color.g, stroke_color.b));
513 void CairoOutputDev::updateFillOpacity(GfxState *state) {
514 double opacity = fill_opacity;
516 if (inUncoloredPattern)
517 return;
519 fill_opacity = state->getFillOpacity();
520 if (opacity != fill_opacity) {
521 cairo_pattern_destroy(fill_pattern);
522 fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
523 colToDbl(fill_color.g),
524 colToDbl(fill_color.b),
525 fill_opacity);
527 LOG(printf ("fill opacity: %f\n", fill_opacity));
531 void CairoOutputDev::updateStrokeOpacity(GfxState *state) {
532 double opacity = stroke_opacity;
534 if (inUncoloredPattern)
535 return;
537 stroke_opacity = state->getStrokeOpacity();
538 if (opacity != stroke_opacity) {
539 cairo_pattern_destroy(stroke_pattern);
540 stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
541 colToDbl(stroke_color.g),
542 colToDbl(stroke_color.b),
543 stroke_opacity);
545 LOG(printf ("stroke opacity: %f\n", stroke_opacity));
549 void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) {
550 if (inUncoloredPattern)
551 return;
553 state->getFillRGB(&fill_color);
555 cairo_pattern_add_color_stop_rgba(fill_pattern, offset,
556 colToDbl(fill_color.r),
557 colToDbl(fill_color.g),
558 colToDbl(fill_color.b),
559 fill_opacity);
560 LOG(printf ("fill color stop: %f (%d, %d, %d)\n",
561 offset, fill_color.r, fill_color.g, fill_color.b));
564 void CairoOutputDev::updateBlendMode(GfxState *state) {
565 switch (state->getBlendMode()) {
566 default:
567 case gfxBlendNormal:
568 cairo_set_operator (cairo, CAIRO_OPERATOR_OVER);
569 break;
570 case gfxBlendMultiply:
571 cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY);
572 break;
573 case gfxBlendScreen:
574 cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN);
575 break;
576 case gfxBlendOverlay:
577 cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY);
578 break;
579 case gfxBlendDarken:
580 cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN);
581 break;
582 case gfxBlendLighten:
583 cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN);
584 break;
585 case gfxBlendColorDodge:
586 cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE);
587 break;
588 case gfxBlendColorBurn:
589 cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN);
590 break;
591 case gfxBlendHardLight:
592 cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT);
593 break;
594 case gfxBlendSoftLight:
595 cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT);
596 break;
597 case gfxBlendDifference:
598 cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE);
599 break;
600 case gfxBlendExclusion:
601 cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION);
602 break;
603 case gfxBlendHue:
604 cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE);
605 break;
606 case gfxBlendSaturation:
607 cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION);
608 break;
609 case gfxBlendColor:
610 cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR);
611 break;
612 case gfxBlendLuminosity:
613 cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY);
614 break;
616 LOG(printf ("blend mode: %d\n", (int)state->getBlendMode()));
619 void CairoOutputDev::updateFont(GfxState *state) {
620 cairo_font_face_t *font_face;
621 cairo_matrix_t matrix, invert_matrix;
623 LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString()));
625 needFontUpdate = gFalse;
627 //FIXME: use cairo font engine?
628 if (text)
629 text->updateFont(state);
631 currentFont = fontEngine->getFont (state->getFont(), doc, printing, xref);
633 if (!currentFont)
634 return;
636 font_face = currentFont->getFontFace();
637 cairo_set_font_face (cairo, font_face);
639 use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() &&
640 cairo_surface_has_show_text_glyphs (cairo_get_target (cairo));
642 double fontSize = state->getFontSize();
643 double *m = state->getTextMat();
644 /* NOTE: adjusting by a constant is hack. The correct solution
645 * is probably to use user-fonts and compute the scale on a per
646 * glyph basis instead of for the entire font */
647 double w = currentFont->getSubstitutionCorrection(state->getFont());
648 matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
649 matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
650 matrix.xy = -m[2] * fontSize;
651 matrix.yy = -m[3] * fontSize;
652 matrix.x0 = 0;
653 matrix.y0 = 0;
655 LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy));
657 /* Make sure the font matrix is invertible before setting it. cairo
658 * will blow up if we give it a matrix that's not invertible, so we
659 * need to check before passing it to cairo_set_font_matrix. Ignoring it
660 * is likely to give better results than not rendering anything at
661 * all. See #18254.
663 invert_matrix = matrix;
664 if (cairo_matrix_invert(&invert_matrix)) {
665 error(errSyntaxWarning, -1, "font matrix not invertible");
666 text_matrix_valid = gFalse;
667 return;
670 cairo_set_font_matrix (cairo, &matrix);
671 text_matrix_valid = gTrue;
674 /* Tolerance in pixels for checking if strokes are horizontal or vertical
675 * lines in device space */
676 #define STROKE_COORD_TOLERANCE 0.5
678 /* Align stroke coordinate i if the point is the start or end of a
679 * horizontal or vertical line */
680 void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y)
682 double x1, y1, x2, y2;
683 GBool align = gFalse;
685 x1 = subpath->getX(i);
686 y1 = subpath->getY(i);
687 cairo_user_to_device (cairo, &x1, &y1);
689 // Does the current coord and prev coord form a horiz or vert line?
690 if (i > 0 && !subpath->getCurve(i - 1)) {
691 x2 = subpath->getX(i - 1);
692 y2 = subpath->getY(i - 1);
693 cairo_user_to_device (cairo, &x2, &y2);
694 if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
695 align = gTrue;
698 // Does the current coord and next coord form a horiz or vert line?
699 if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) {
700 x2 = subpath->getX(i + 1);
701 y2 = subpath->getY(i + 1);
702 cairo_user_to_device (cairo, &x2, &y2);
703 if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE)
704 align = gTrue;
707 *x = subpath->getX(i);
708 *y = subpath->getY(i);
709 if (align) {
710 /* see http://www.cairographics.org/FAQ/#sharp_lines */
711 cairo_user_to_device (cairo, x, y);
712 *x = floor(*x) + 0.5;
713 *y = floor(*y) + 0.5;
714 cairo_device_to_user (cairo, x, y);
718 #undef STROKE_COORD_TOLERANCE
720 void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) {
721 GfxSubpath *subpath;
722 int i, j;
723 double x, y;
724 cairo_new_path (cairo);
725 for (i = 0; i < path->getNumSubpaths(); ++i) {
726 subpath = path->getSubpath(i);
727 if (subpath->getNumPoints() > 0) {
728 if (align_stroke_coords) {
729 alignStrokeCoords(subpath, 0, &x, &y);
730 } else {
731 x = subpath->getX(0);
732 y = subpath->getY(0);
734 cairo_move_to (cairo, x, y);
735 j = 1;
736 while (j < subpath->getNumPoints()) {
737 if (subpath->getCurve(j)) {
738 if (align_stroke_coords) {
739 alignStrokeCoords(subpath, j + 2, &x, &y);
740 } else {
741 x = subpath->getX(j+2);
742 y = subpath->getY(j+2);
744 cairo_curve_to( cairo,
745 subpath->getX(j), subpath->getY(j),
746 subpath->getX(j+1), subpath->getY(j+1),
747 x, y);
749 j += 3;
750 } else {
751 if (align_stroke_coords) {
752 alignStrokeCoords(subpath, j, &x, &y);
753 } else {
754 x = subpath->getX(j);
755 y = subpath->getY(j);
757 cairo_line_to (cairo, x, y);
758 ++j;
761 if (subpath->isClosed()) {
762 LOG (printf ("close\n"));
763 cairo_close_path (cairo);
769 void CairoOutputDev::stroke(GfxState *state) {
770 if (inType3Char) {
771 GfxGray gray;
772 state->getFillGray(&gray);
773 if (colToDbl(gray) > 0.5)
774 return;
777 if (adjusted_stroke_width)
778 align_stroke_coords = gTrue;
779 doPath (cairo, state, state->getPath());
780 align_stroke_coords = gFalse;
781 cairo_set_source (cairo, stroke_pattern);
782 LOG(printf ("stroke\n"));
783 cairo_stroke (cairo);
784 if (cairo_shape) {
785 doPath (cairo_shape, state, state->getPath());
786 cairo_stroke (cairo_shape);
790 void CairoOutputDev::fill(GfxState *state) {
791 if (inType3Char) {
792 GfxGray gray;
793 state->getFillGray(&gray);
794 if (colToDbl(gray) > 0.5)
795 return;
798 doPath (cairo, state, state->getPath());
799 cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
800 cairo_set_source (cairo, fill_pattern);
801 LOG(printf ("fill\n"));
802 //XXX: how do we get the path
803 if (mask) {
804 cairo_save (cairo);
805 cairo_clip (cairo);
806 cairo_set_matrix (cairo, &mask_matrix);
807 cairo_mask (cairo, mask);
808 cairo_restore (cairo);
809 } else if (strokePathClip) {
810 fillToStrokePathClip(state);
811 } else {
812 cairo_fill (cairo);
814 if (cairo_shape) {
815 cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
816 doPath (cairo_shape, state, state->getPath());
817 cairo_fill (cairo_shape);
821 void CairoOutputDev::eoFill(GfxState *state) {
822 doPath (cairo, state, state->getPath());
823 cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
824 cairo_set_source (cairo, fill_pattern);
825 LOG(printf ("fill-eo\n"));
827 if (mask) {
828 cairo_save (cairo);
829 cairo_clip (cairo);
830 cairo_set_matrix (cairo, &mask_matrix);
831 cairo_mask (cairo, mask);
832 cairo_restore (cairo);
833 } else {
834 cairo_fill (cairo);
836 if (cairo_shape) {
837 cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
838 doPath (cairo_shape, state, state->getPath());
839 cairo_fill (cairo_shape);
844 GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, Object *str,
845 double *pmat, int paintType, int /*tilingType*/, Dict *resDict,
846 double *mat, double *bbox,
847 int x0, int y0, int x1, int y1,
848 double xStep, double yStep)
850 PDFRectangle box;
851 Gfx *gfx;
852 cairo_pattern_t *pattern;
853 cairo_surface_t *surface;
854 cairo_matrix_t matrix;
855 cairo_matrix_t pattern_matrix;
856 cairo_t *old_cairo;
857 double xMin, yMin, xMax, yMax;
858 double width, height;
859 int surface_width, surface_height;
860 StrokePathClip *strokePathTmp;
861 GBool adjusted_stroke_width_tmp;
863 width = bbox[2] - bbox[0];
864 height = bbox[3] - bbox[1];
866 if (xStep != width || yStep != height)
867 return gFalse;
868 /* TODO: implement the other cases here too */
870 // Find the width and height of the transformed pattern
871 cairo_get_matrix (cairo, &matrix);
872 cairo_matrix_init (&pattern_matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
873 cairo_matrix_multiply (&matrix, &matrix, &pattern_matrix);
875 double widthX = width, widthY = 0;
876 cairo_matrix_transform_distance (&matrix, &widthX, &widthY);
877 surface_width = ceil (sqrt (widthX * widthX + widthY * widthY));
879 double heightX = 0, heightY = height;
880 cairo_matrix_transform_distance (&matrix, &heightX, &heightY);
881 surface_height = ceil (sqrt (heightX * heightX + heightY * heightY));
883 surface = cairo_surface_create_similar (cairo_get_target (cairo),
884 CAIRO_CONTENT_COLOR_ALPHA,
885 surface_width, surface_height);
886 if (cairo_surface_status (surface))
887 return gFalse;
889 old_cairo = cairo;
890 cairo = cairo_create (surface);
891 cairo_surface_destroy (surface);
892 cairo_scale (cairo, surface_width / width, surface_height / height);
894 box.x1 = bbox[0]; box.y1 = bbox[1];
895 box.x2 = bbox[2]; box.y2 = bbox[3];
896 strokePathTmp = strokePathClip;
897 strokePathClip = NULL;
898 adjusted_stroke_width_tmp = adjusted_stroke_width;
899 gfx = new Gfx(doc, this, resDict, &box, NULL, NULL, NULL, gfxA->getXRef());
900 if (paintType == 2)
901 inUncoloredPattern = gTrue;
902 gfx->display(str);
903 if (paintType == 2)
904 inUncoloredPattern = gFalse;
905 delete gfx;
906 strokePathClip = strokePathTmp;
907 adjusted_stroke_width = adjusted_stroke_width_tmp;
909 pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo));
910 cairo_destroy (cairo);
911 cairo = old_cairo;
912 if (cairo_pattern_status (pattern))
913 return gFalse;
915 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
916 cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin);
918 cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height);
919 cairo_pattern_set_matrix (pattern, &matrix);
921 cairo_transform (cairo, &pattern_matrix);
922 cairo_set_source (cairo, pattern);
923 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
924 if (strokePathClip) {
925 fillToStrokePathClip(state);
926 } else {
927 cairo_fill (cairo);
930 cairo_pattern_destroy (pattern);
932 return gTrue;
935 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
936 GBool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
938 // Function shaded fills are subdivided to rectangles that are the
939 // following size in device space. Note when printing this size is
940 // in points.
941 const int subdivide_pixels = 10;
943 double x_begin, x_end, x1, x2;
944 double y_begin, y_end, y1, y2;
945 double x_step;
946 double y_step;
947 GfxColor color;
948 GfxRGB rgb;
949 double *matrix;
950 cairo_matrix_t mat;
952 matrix = shading->getMatrix();
953 mat.xx = matrix[0];
954 mat.yx = matrix[1];
955 mat.xy = matrix[2];
956 mat.yy = matrix[3];
957 mat.x0 = matrix[4];
958 mat.y0 = matrix[5];
959 if (cairo_matrix_invert(&mat)) {
960 error(errSyntaxWarning, -1, "matrix not invertible\n");
961 return gFalse;
964 // get cell size in pattern space
965 x_step = y_step = subdivide_pixels;
966 cairo_matrix_transform_distance (&mat, &x_step, &y_step);
968 cairo_pattern_destroy(fill_pattern);
969 fill_pattern = cairo_pattern_create_mesh ();
970 cairo_pattern_set_matrix(fill_pattern, &mat);
971 shading->getDomain(&x_begin, &y_begin, &x_end, &y_end);
973 for (x1 = x_begin; x1 < x_end; x1 += x_step) {
974 x2 = x1 + x_step;
975 if (x2 > x_end)
976 x2 = x_end;
978 for (y1 = y_begin; y1 < y_end; y1 += y_step) {
979 y2 = y1 + y_step;
980 if (y2 > y_end)
981 y2 = y_end;
983 cairo_mesh_pattern_begin_patch (fill_pattern);
984 cairo_mesh_pattern_move_to (fill_pattern, x1, y1);
985 cairo_mesh_pattern_line_to (fill_pattern, x2, y1);
986 cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
987 cairo_mesh_pattern_line_to (fill_pattern, x1, y2);
989 shading->getColor(x1, y1, &color);
990 shading->getColorSpace()->getRGB(&color, &rgb);
991 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 0,
992 colToDbl(rgb.r),
993 colToDbl(rgb.g),
994 colToDbl(rgb.b));
996 shading->getColor(x2, y1, &color);
997 shading->getColorSpace()->getRGB(&color, &rgb);
998 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 1,
999 colToDbl(rgb.r),
1000 colToDbl(rgb.g),
1001 colToDbl(rgb.b));
1003 shading->getColor(x2, y2, &color);
1004 shading->getColorSpace()->getRGB(&color, &rgb);
1005 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 2,
1006 colToDbl(rgb.r),
1007 colToDbl(rgb.g),
1008 colToDbl(rgb.b));
1010 shading->getColor(x1, y2, &color);
1011 shading->getColorSpace()->getRGB(&color, &rgb);
1012 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, 3,
1013 colToDbl(rgb.r),
1014 colToDbl(rgb.g),
1015 colToDbl(rgb.b));
1017 cairo_mesh_pattern_end_patch (fill_pattern);
1021 double xMin, yMin, xMax, yMax;
1022 // get the clip region bbox
1023 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1024 state->moveTo(xMin, yMin);
1025 state->lineTo(xMin, yMax);
1026 state->lineTo(xMax, yMax);
1027 state->lineTo(xMax, yMin);
1028 state->closePath();
1029 fill(state);
1030 state->clearPath();
1032 return gTrue;
1034 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1036 GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
1037 double x0, y0, x1, y1;
1038 double dx, dy;
1040 shading->getCoords(&x0, &y0, &x1, &y1);
1041 dx = x1 - x0;
1042 dy = y1 - y0;
1044 cairo_pattern_destroy(fill_pattern);
1045 fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy,
1046 x0 + tMax * dx, y0 + tMax * dy);
1047 if (!shading->getExtend0() && !shading->getExtend1())
1048 cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
1049 else
1050 cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
1052 LOG (printf ("axial-sh\n"));
1054 // TODO: use the actual stops in the shading in the case
1055 // of linear interpolation (Type 2 Exponential functions with N=1)
1056 return gFalse;
1059 GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading)
1061 return (shading->getExtend0() == shading->getExtend1());
1064 GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) {
1065 double x0, y0, r0, x1, y1, r1;
1066 double dx, dy, dr;
1067 cairo_matrix_t matrix;
1068 double scale;
1070 shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
1071 dx = x1 - x0;
1072 dy = y1 - y0;
1073 dr = r1 - r0;
1075 // Cairo/pixman do not work well with a very large or small scaled
1076 // matrix. See cairo bug #81657.
1078 // As a workaround, scale the pattern by the average of the vertical
1079 // and horizontal scaling of the current transformation matrix.
1080 cairo_get_matrix(cairo, &matrix);
1081 scale = (sqrt(matrix.xx * matrix.xx + matrix.yx * matrix.yx)
1082 + sqrt(matrix.xy * matrix.xy + matrix.yy * matrix.yy)) / 2;
1083 cairo_matrix_init_scale(&matrix, scale, scale);
1085 cairo_pattern_destroy(fill_pattern);
1086 fill_pattern = cairo_pattern_create_radial ((x0 + sMin * dx) * scale,
1087 (y0 + sMin * dy) * scale,
1088 (r0 + sMin * dr) * scale,
1089 (x0 + sMax * dx) * scale,
1090 (y0 + sMax * dy) * scale,
1091 (r0 + sMax * dr) * scale);
1092 cairo_pattern_set_matrix(fill_pattern, &matrix);
1093 if (shading->getExtend0() && shading->getExtend1())
1094 cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
1095 else
1096 cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
1098 LOG (printf ("radial-sh\n"));
1100 return gFalse;
1103 GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading)
1105 return (shading->getExtend0() == shading->getExtend1());
1108 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0)
1109 GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
1111 double x0, y0, x1, y1, x2, y2;
1112 GfxColor color[3];
1113 int i, j;
1114 GfxRGB rgb;
1116 cairo_pattern_destroy(fill_pattern);
1117 fill_pattern = cairo_pattern_create_mesh ();
1119 for (i = 0; i < shading->getNTriangles(); i++) {
1120 if (shading->isParameterized()) {
1121 double color0, color1, color2;
1122 shading->getTriangle(i, &x0, &y0, &color0,
1123 &x1, &y1, &color1,
1124 &x2, &y2, &color2);
1125 shading->getParameterizedColor(color0, &color[0]);
1126 shading->getParameterizedColor(color1, &color[1]);
1127 shading->getParameterizedColor(color2, &color[2]);
1128 } else {
1129 shading->getTriangle(i,
1130 &x0, &y0, &color[0],
1131 &x1, &y1, &color[1],
1132 &x2, &y2, &color[2]);
1136 cairo_mesh_pattern_begin_patch (fill_pattern);
1138 cairo_mesh_pattern_move_to (fill_pattern, x0, y0);
1139 cairo_mesh_pattern_line_to (fill_pattern, x1, y1);
1140 cairo_mesh_pattern_line_to (fill_pattern, x2, y2);
1142 for (j = 0; j < 3; j++) {
1143 shading->getColorSpace()->getRGB(&color[j], &rgb);
1144 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j,
1145 colToDbl(rgb.r),
1146 colToDbl(rgb.g),
1147 colToDbl(rgb.b));
1150 cairo_mesh_pattern_end_patch (fill_pattern);
1153 double xMin, yMin, xMax, yMax;
1154 // get the clip region bbox
1155 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1156 state->moveTo(xMin, yMin);
1157 state->lineTo(xMin, yMax);
1158 state->lineTo(xMax, yMax);
1159 state->lineTo(xMax, yMin);
1160 state->closePath();
1161 fill(state);
1162 state->clearPath();
1164 return gTrue;
1167 GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading)
1169 int i, j, k;
1171 cairo_pattern_destroy(fill_pattern);
1172 fill_pattern = cairo_pattern_create_mesh ();
1174 for (i = 0; i < shading->getNPatches(); i++) {
1175 GfxPatch *patch = shading->getPatch(i);
1176 GfxColor color;
1177 GfxRGB rgb;
1179 cairo_mesh_pattern_begin_patch (fill_pattern);
1181 cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]);
1182 cairo_mesh_pattern_curve_to (fill_pattern,
1183 patch->x[0][1], patch->y[0][1],
1184 patch->x[0][2], patch->y[0][2],
1185 patch->x[0][3], patch->y[0][3]);
1187 cairo_mesh_pattern_curve_to (fill_pattern,
1188 patch->x[1][3], patch->y[1][3],
1189 patch->x[2][3], patch->y[2][3],
1190 patch->x[3][3], patch->y[3][3]);
1192 cairo_mesh_pattern_curve_to (fill_pattern,
1193 patch->x[3][2], patch->y[3][2],
1194 patch->x[3][1], patch->y[3][1],
1195 patch->x[3][0], patch->y[3][0]);
1197 cairo_mesh_pattern_curve_to (fill_pattern,
1198 patch->x[2][0], patch->y[2][0],
1199 patch->x[1][0], patch->y[1][0],
1200 patch->x[0][0], patch->y[0][0]);
1202 cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]);
1203 cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]);
1204 cairo_mesh_pattern_set_control_point (fill_pattern, 2, patch->x[2][2], patch->y[2][2]);
1205 cairo_mesh_pattern_set_control_point (fill_pattern, 3, patch->x[2][1], patch->y[2][1]);
1207 for (j = 0; j < 4; j++) {
1208 int u, v;
1210 switch (j) {
1211 case 0:
1212 u = 0; v = 0;
1213 break;
1214 case 1:
1215 u = 0; v = 1;
1216 break;
1217 case 2:
1218 u = 1; v = 1;
1219 break;
1220 case 3:
1221 u = 1; v = 0;
1222 break;
1225 if (shading->isParameterized()) {
1226 shading->getParameterizedColor (patch->color[u][v].c[0], &color);
1227 } else {
1228 for (k = 0; k < shading->getColorSpace()->getNComps(); k++) {
1229 // simply cast to the desired type; that's all what is needed.
1230 color.c[k] = GfxColorComp (patch->color[u][v].c[k]);
1234 shading->getColorSpace()->getRGB(&color, &rgb);
1235 cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j,
1236 colToDbl(rgb.r),
1237 colToDbl(rgb.g),
1238 colToDbl(rgb.b));
1240 cairo_mesh_pattern_end_patch (fill_pattern);
1243 double xMin, yMin, xMax, yMax;
1244 // get the clip region bbox
1245 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1246 state->moveTo(xMin, yMin);
1247 state->lineTo(xMin, yMax);
1248 state->lineTo(xMax, yMax);
1249 state->lineTo(xMax, yMin);
1250 state->closePath();
1251 fill(state);
1252 state->clearPath();
1254 return gTrue;
1256 #endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */
1258 void CairoOutputDev::clip(GfxState *state) {
1259 doPath (cairo, state, state->getPath());
1260 cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
1261 cairo_clip (cairo);
1262 LOG (printf ("clip\n"));
1263 if (cairo_shape) {
1264 doPath (cairo_shape, state, state->getPath());
1265 cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
1266 cairo_clip (cairo_shape);
1270 void CairoOutputDev::eoClip(GfxState *state) {
1271 doPath (cairo, state, state->getPath());
1272 cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
1273 cairo_clip (cairo);
1274 LOG (printf ("clip-eo\n"));
1275 if (cairo_shape) {
1276 doPath (cairo_shape, state, state->getPath());
1277 cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
1278 cairo_clip (cairo_shape);
1283 void CairoOutputDev::clipToStrokePath(GfxState *state) {
1284 LOG(printf("clip-to-stroke-path\n"));
1285 strokePathClip = (StrokePathClip*)gmalloc (sizeof(*strokePathClip));
1286 strokePathClip->path = state->getPath()->copy();
1287 cairo_get_matrix (cairo, &strokePathClip->ctm);
1288 strokePathClip->line_width = cairo_get_line_width (cairo);
1289 strokePathClip->dash_count = cairo_get_dash_count (cairo);
1290 if (strokePathClip->dash_count) {
1291 strokePathClip->dashes = (double*) gmallocn (sizeof(double), strokePathClip->dash_count);
1292 cairo_get_dash (cairo, strokePathClip->dashes, &strokePathClip->dash_offset);
1293 } else {
1294 strokePathClip->dashes = NULL;
1296 strokePathClip->cap = cairo_get_line_cap (cairo);
1297 strokePathClip->join = cairo_get_line_join (cairo);
1298 strokePathClip->miter = cairo_get_miter_limit (cairo);
1301 void CairoOutputDev::fillToStrokePathClip(GfxState *state) {
1302 cairo_save (cairo);
1304 cairo_set_matrix (cairo, &strokePathClip->ctm);
1305 cairo_set_line_width (cairo, strokePathClip->line_width);
1306 strokePathClip->dash_count = cairo_get_dash_count (cairo);
1307 cairo_set_dash (cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset);
1308 cairo_set_line_cap (cairo, strokePathClip->cap);
1309 cairo_set_line_join (cairo, strokePathClip->join);
1310 cairo_set_miter_limit (cairo, strokePathClip->miter);
1311 doPath (cairo, state, strokePathClip->path);
1312 cairo_stroke (cairo);
1314 cairo_restore (cairo);
1316 delete strokePathClip->path;
1317 if (strokePathClip->dashes)
1318 gfree (strokePathClip->dashes);
1319 gfree (strokePathClip);
1320 strokePathClip = NULL;
1323 void CairoOutputDev::beginString(GfxState *state, GooString *s)
1325 int len = s->getLength();
1327 if (needFontUpdate)
1328 updateFont(state);
1330 if (!currentFont)
1331 return;
1333 glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t));
1334 glyphCount = 0;
1335 if (use_show_text_glyphs) {
1336 clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t));
1337 clusterCount = 0;
1338 utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more.
1339 utf8 = (char *) gmalloc (utf8Max);
1340 utf8Count = 0;
1344 void CairoOutputDev::drawChar(GfxState *state, double x, double y,
1345 double dx, double dy,
1346 double originX, double originY,
1347 CharCode code, int nBytes, Unicode *u, int uLen)
1349 if (currentFont) {
1350 glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen);
1351 glyphs[glyphCount].x = x - originX;
1352 glyphs[glyphCount].y = y - originY;
1353 glyphCount++;
1354 if (use_show_text_glyphs) {
1355 GooString enc("UTF-8");
1356 UnicodeMap *utf8Map = globalParams->getUnicodeMap(&enc);
1357 if (utf8Max - utf8Count < uLen*6) {
1358 // utf8 encoded characters can be up to 6 bytes
1359 if (utf8Max > uLen*6)
1360 utf8Max *= 2;
1361 else
1362 utf8Max += 2*uLen*6;
1363 utf8 = (char *) grealloc (utf8, utf8Max);
1365 clusters[clusterCount].num_bytes = 0;
1366 for (int i = 0; i < uLen; i++) {
1367 int size = utf8Map->mapUnicode(u[i], utf8 + utf8Count, utf8Max - utf8Count);
1368 utf8Count += size;
1369 clusters[clusterCount].num_bytes += size;
1371 clusters[clusterCount].num_glyphs = 1;
1372 clusterCount++;
1376 if (!text)
1377 return;
1378 actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen);
1381 void CairoOutputDev::endString(GfxState *state)
1383 int render;
1385 if (!currentFont)
1386 return;
1388 // endString can be called without a corresponding beginString. If this
1389 // happens glyphs will be null so don't draw anything, just return.
1390 // XXX: OutputDevs should probably not have to deal with this...
1391 if (!glyphs)
1392 return;
1394 // ignore empty strings and invisible text -- this is used by
1395 // Acrobat Capture
1396 render = state->getRender();
1397 if (render == 3 || glyphCount == 0 || !text_matrix_valid) {
1398 goto finish;
1401 if (!(render & 1)) {
1402 LOG (printf ("fill string\n"));
1403 cairo_set_source (cairo, fill_pattern);
1404 if (use_show_text_glyphs)
1405 cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0);
1406 else
1407 cairo_show_glyphs (cairo, glyphs, glyphCount);
1408 if (cairo_shape)
1409 cairo_show_glyphs (cairo_shape, glyphs, glyphCount);
1412 // stroke
1413 if ((render & 3) == 1 || (render & 3) == 2) {
1414 LOG (printf ("stroke string\n"));
1415 cairo_set_source (cairo, stroke_pattern);
1416 cairo_glyph_path (cairo, glyphs, glyphCount);
1417 cairo_stroke (cairo);
1418 if (cairo_shape) {
1419 cairo_glyph_path (cairo_shape, glyphs, glyphCount);
1420 cairo_stroke (cairo_shape);
1424 // clip
1425 if ((render & 4)) {
1426 LOG (printf ("clip string\n"));
1427 // append the glyph path to textClipPath.
1429 // set textClipPath as the currentPath
1430 if (textClipPath) {
1431 cairo_append_path (cairo, textClipPath);
1432 if (cairo_shape) {
1433 cairo_append_path (cairo_shape, textClipPath);
1435 cairo_path_destroy (textClipPath);
1438 // append the glyph path
1439 cairo_glyph_path (cairo, glyphs, glyphCount);
1441 // move the path back into textClipPath
1442 // and clear the current path
1443 textClipPath = cairo_copy_path (cairo);
1444 cairo_new_path (cairo);
1445 if (cairo_shape) {
1446 cairo_new_path (cairo_shape);
1450 finish:
1451 gfree (glyphs);
1452 glyphs = NULL;
1453 if (use_show_text_glyphs) {
1454 gfree (clusters);
1455 clusters = NULL;
1456 gfree (utf8);
1457 utf8 = NULL;
1462 GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y,
1463 double dx, double dy,
1464 CharCode code, Unicode *u, int uLen) {
1466 cairo_save (cairo);
1467 double *ctm;
1468 cairo_matrix_t matrix;
1470 ctm = state->getCTM();
1471 matrix.xx = ctm[0];
1472 matrix.yx = ctm[1];
1473 matrix.xy = ctm[2];
1474 matrix.yy = ctm[3];
1475 matrix.x0 = ctm[4];
1476 matrix.y0 = ctm[5];
1477 /* Restore the original matrix and then transform to matrix needed for the
1478 * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/
1479 cairo_set_matrix(cairo, &orig_matrix);
1480 cairo_transform(cairo, &matrix);
1481 if (cairo_shape) {
1482 cairo_save (cairo_shape);
1483 cairo_set_matrix(cairo_shape, &orig_matrix);
1484 cairo_transform(cairo_shape, &matrix);
1486 cairo_pattern_destroy(stroke_pattern);
1487 cairo_pattern_reference(fill_pattern);
1488 stroke_pattern = fill_pattern;
1489 return gFalse;
1492 void CairoOutputDev::endType3Char(GfxState *state) {
1493 cairo_restore (cairo);
1494 if (cairo_shape) {
1495 cairo_restore (cairo_shape);
1499 void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) {
1500 t3_glyph_wx = wx;
1501 t3_glyph_wy = wy;
1504 void CairoOutputDev::type3D1(GfxState *state, double wx, double wy,
1505 double llx, double lly, double urx, double ury) {
1506 t3_glyph_wx = wx;
1507 t3_glyph_wy = wy;
1508 t3_glyph_bbox[0] = llx;
1509 t3_glyph_bbox[1] = lly;
1510 t3_glyph_bbox[2] = urx;
1511 t3_glyph_bbox[3] = ury;
1512 t3_glyph_has_bbox = gTrue;
1515 void CairoOutputDev::beginTextObject(GfxState *state) {
1518 void CairoOutputDev::endTextObject(GfxState *state) {
1519 if (textClipPath) {
1520 // clip the accumulated text path
1521 cairo_append_path (cairo, textClipPath);
1522 cairo_clip (cairo);
1523 if (cairo_shape) {
1524 cairo_append_path (cairo_shape, textClipPath);
1525 cairo_clip (cairo_shape);
1527 cairo_path_destroy (textClipPath);
1528 textClipPath = NULL;
1532 void CairoOutputDev::beginActualText(GfxState *state, GooString *text)
1534 if (this->text)
1535 actualText->begin(state, text);
1538 void CairoOutputDev::endActualText(GfxState *state)
1540 if (text)
1541 actualText->end(state);
1544 static inline int splashRound(SplashCoord x) {
1545 return (int)floor(x + 0.5);
1548 static inline int splashCeil(SplashCoord x) {
1549 return (int)ceil(x);
1552 static inline int splashFloor(SplashCoord x) {
1553 return (int)floor(x);
1556 static
1557 cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content)
1559 cairo_pattern_t *pattern;
1560 cairo_surface_t *surface = NULL;
1562 cairo_push_group_with_content (cairo, content);
1563 pattern = cairo_pop_group (cairo);
1564 cairo_pattern_get_surface (pattern, &surface);
1565 cairo_surface_reference (surface);
1566 cairo_pattern_destroy (pattern);
1567 return surface;
1572 void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/,
1573 GfxColorSpace * blendingColorSpace,
1574 GBool /*isolated*/, GBool knockout,
1575 GBool forSoftMask) {
1576 /* push color space */
1577 ColorSpaceStack* css = new ColorSpaceStack;
1578 css->cs = blendingColorSpace;
1579 css->knockout = knockout;
1580 cairo_get_matrix(cairo, &css->group_matrix);
1581 css->next = groupColorSpaceStack;
1582 groupColorSpaceStack = css;
1584 LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no"));
1586 if (knockout) {
1587 knockoutCount++;
1588 if (!cairo_shape) {
1589 /* create a surface for tracking the shape */
1590 cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA);
1591 cairo_shape = cairo_create (cairo_shape_surface);
1592 cairo_surface_destroy (cairo_shape_surface);
1594 /* the color doesn't matter as long as it is opaque */
1595 cairo_set_source_rgb (cairo_shape, 0, 0, 0);
1596 cairo_matrix_t matrix;
1597 cairo_get_matrix (cairo, &matrix);
1598 //printMatrix(&matrix);
1599 cairo_set_matrix (cairo_shape, &matrix);
1602 if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1603 /* we need to track the shape */
1604 cairo_push_group (cairo_shape);
1606 if (0 && forSoftMask)
1607 cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
1608 else
1609 cairo_push_group (cairo);
1611 /* push_group has an implicit cairo_save() */
1612 if (knockout) {
1613 /*XXX: let's hope this matches the semantics needed */
1614 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
1615 } else {
1616 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
1620 void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) {
1621 if (group)
1622 cairo_pattern_destroy(group);
1623 group = cairo_pop_group (cairo);
1625 LOG(printf ("end transparency group\n"));
1627 if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1628 if (shape)
1629 cairo_pattern_destroy(shape);
1630 shape = cairo_pop_group (cairo_shape);
1634 void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
1635 LOG(printf ("paint transparency group\n"));
1637 cairo_save (cairo);
1638 cairo_set_matrix (cairo, &groupColorSpaceStack->group_matrix);
1640 if (shape) {
1641 /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask)
1642 * however our source has already been clipped to mask so we only need to
1643 * do ADD and OUT */
1645 /* clear the shape mask */
1646 cairo_set_source (cairo, shape);
1647 cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT);
1648 cairo_paint (cairo);
1649 cairo_set_operator (cairo, CAIRO_OPERATOR_ADD);
1651 cairo_set_source (cairo, group);
1653 if (!mask) {
1654 cairo_paint_with_alpha (cairo, fill_opacity);
1655 cairo_status_t status = cairo_status(cairo);
1656 if (status)
1657 printf("BAD status: %s\n", cairo_status_to_string(status));
1658 } else {
1659 if (fill_opacity < 1.0) {
1660 cairo_push_group(cairo);
1662 cairo_save(cairo);
1663 cairo_set_matrix(cairo, &mask_matrix);
1664 cairo_mask(cairo, mask);
1665 cairo_restore(cairo);
1666 if (fill_opacity < 1.0) {
1667 cairo_pop_group_to_source(cairo);
1668 cairo_paint_with_alpha (cairo, fill_opacity);
1670 cairo_pattern_destroy(mask);
1671 mask = NULL;
1674 if (shape) {
1675 if (cairo_shape) {
1676 cairo_set_source (cairo_shape, shape);
1677 cairo_paint (cairo_shape);
1678 cairo_set_source_rgb (cairo_shape, 0, 0, 0);
1680 cairo_pattern_destroy (shape);
1681 shape = NULL;
1684 popTransparencyGroup();
1685 cairo_restore(cairo);
1688 static int luminocity(uint32_t x)
1690 int r = (x >> 16) & 0xff;
1691 int g = (x >> 8) & 0xff;
1692 int b = (x >> 0) & 0xff;
1693 // an arbitrary integer approximation of .3*r + .59*g + .11*b
1694 int y = (r*19661+g*38666+b*7209 + 32829)>>16;
1695 return y;
1699 /* XXX: do we need to deal with shape here? */
1700 void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
1701 Function * transferFunc, GfxColor * backdropColor) {
1702 cairo_pattern_destroy(mask);
1704 LOG(printf ("set softMask\n"));
1706 if (!alpha || transferFunc) {
1707 /* We need to mask according to the luminocity of the group.
1708 * So we paint the group to an image surface convert it to a luminocity map
1709 * and then use that as the mask. */
1711 /* Get clip extents in device space */
1712 double x1, y1, x2, y2, x_min, y_min, x_max, y_max;
1713 cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
1714 cairo_user_to_device(cairo, &x1, &y1);
1715 cairo_user_to_device(cairo, &x2, &y2);
1716 x_min = MIN(x1, x2);
1717 y_min = MIN(y1, y2);
1718 x_max = MAX(x1, x2);
1719 y_max = MAX(y1, y2);
1720 cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
1721 cairo_user_to_device(cairo, &x1, &y2);
1722 cairo_user_to_device(cairo, &x2, &y1);
1723 x_min = MIN(x_min,MIN(x1, x2));
1724 y_min = MIN(y_min,MIN(y1, y2));
1725 x_max = MAX(x_max,MAX(x1, x2));
1726 y_max = MAX(y_max,MAX(y1, y2));
1728 int width = (int)(ceil(x_max) - floor(x_min));
1729 int height = (int)(ceil(y_max) - floor(y_min));
1731 /* Get group device offset */
1732 double x_offset, y_offset;
1733 if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1734 cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset);
1735 } else {
1736 cairo_surface_t *pats;
1737 cairo_pattern_get_surface(group, &pats);
1738 cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
1741 /* Adjust extents by group offset */
1742 x_min += x_offset;
1743 y_min += y_offset;
1745 cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1746 cairo_t *maskCtx = cairo_create(source);
1748 //XXX: hopefully this uses the correct color space */
1749 if (!alpha && groupColorSpaceStack->cs) {
1750 GfxRGB backdropColorRGB;
1751 groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB);
1752 /* paint the backdrop */
1753 cairo_set_source_rgb(maskCtx,
1754 colToDbl(backdropColorRGB.r),
1755 colToDbl(backdropColorRGB.g),
1756 colToDbl(backdropColorRGB.b));
1758 cairo_paint(maskCtx);
1760 /* Copy source ctm to mask ctm and translate origin so that the
1761 * mask appears it the same location on the source surface. */
1762 cairo_matrix_t mat, tmat;
1763 cairo_matrix_init_translate(&tmat, -x_min, -y_min);
1764 cairo_get_matrix(cairo, &mat);
1765 cairo_matrix_multiply(&mat, &mat, &tmat);
1766 cairo_set_matrix(maskCtx, &mat);
1768 /* make the device offset of the new mask match that of the group */
1769 cairo_surface_set_device_offset(source, x_offset, y_offset);
1771 /* paint the group */
1772 cairo_set_source(maskCtx, group);
1773 cairo_paint(maskCtx);
1775 /* XXX status = cairo_status(maskCtx); */
1776 cairo_destroy(maskCtx);
1778 /* convert to a luminocity map */
1779 uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source);
1780 /* get stride in units of 32 bits */
1781 int stride = cairo_image_surface_get_stride(source)/4;
1782 for (int y=0; y<height; y++) {
1783 for (int x=0; x<width; x++) {
1784 int lum = alpha ? fill_opacity : luminocity(source_data[y*stride + x]);
1785 if (transferFunc) {
1786 double lum_in, lum_out;
1787 lum_in = lum/256.0;
1788 transferFunc->transform(&lum_in, &lum_out);
1789 lum = (int)(lum_out * 255.0 + 0.5);
1791 source_data[y*stride + x] = lum << 24;
1794 cairo_surface_mark_dirty (source);
1796 /* setup the new mask pattern */
1797 mask = cairo_pattern_create_for_surface(source);
1798 cairo_get_matrix(cairo, &mask_matrix);
1800 if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1801 cairo_pattern_set_matrix(mask, &mat);
1802 } else {
1803 cairo_matrix_t patMatrix;
1804 cairo_pattern_get_matrix(group, &patMatrix);
1805 /* Apply x_min, y_min offset to it appears in the same location as source. */
1806 cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat);
1807 cairo_pattern_set_matrix(mask, &patMatrix);
1810 cairo_surface_destroy(source);
1811 } else if (alpha) {
1812 mask = cairo_pattern_reference(group);
1813 cairo_get_matrix(cairo, &mask_matrix);
1816 popTransparencyGroup();
1819 void CairoOutputDev::popTransparencyGroup() {
1820 /* pop color space */
1821 ColorSpaceStack *css = groupColorSpaceStack;
1822 if (css->knockout) {
1823 knockoutCount--;
1824 if (!knockoutCount) {
1825 /* we don't need to track the shape anymore because
1826 * we are not above any knockout groups */
1827 cairo_destroy(cairo_shape);
1828 cairo_shape = NULL;
1831 groupColorSpaceStack = css->next;
1832 delete css;
1836 void CairoOutputDev::clearSoftMask(GfxState * /*state*/) {
1837 if (mask)
1838 cairo_pattern_destroy(mask);
1839 mask = NULL;
1842 /* Taken from cairo/doc/tutorial/src/singular.c */
1843 static void
1844 get_singular_values (const cairo_matrix_t *matrix,
1845 double *major,
1846 double *minor)
1848 double xx = matrix->xx, xy = matrix->xy;
1849 double yx = matrix->yx, yy = matrix->yy;
1851 double a = xx*xx+yx*yx;
1852 double b = xy*xy+yy*yy;
1853 double k = xx*xy+yx*yy;
1855 double f = (a+b) * .5;
1856 double g = (a-b) * .5;
1857 double delta = sqrt (g*g + k*k);
1859 if (major)
1860 *major = sqrt (f + delta);
1861 if (minor)
1862 *minor = sqrt (f - delta);
1865 void CairoOutputDev::getScaledSize(const cairo_matrix_t *matrix,
1866 int orig_width,
1867 int orig_height,
1868 int *scaledWidth,
1869 int *scaledHeight)
1871 double xScale;
1872 double yScale;
1873 if (orig_width > orig_height)
1874 get_singular_values (matrix, &xScale, &yScale);
1875 else
1876 get_singular_values (matrix, &yScale, &xScale);
1878 int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
1879 if (xScale >= 0) {
1880 tx = splashRound(matrix->x0 - 0.01);
1881 tx2 = splashRound(matrix->x0 + xScale + 0.01) - 1;
1882 } else {
1883 tx = splashRound(matrix->x0 + 0.01) - 1;
1884 tx2 = splashRound(matrix->x0 + xScale - 0.01);
1886 *scaledWidth = abs(tx2 - tx) + 1;
1887 //scaledWidth = splashRound(fabs(xScale));
1888 if (*scaledWidth == 0) {
1889 // technically, this should draw nothing, but it generally seems
1890 // better to draw a one-pixel-wide stripe rather than throwing it
1891 // away
1892 *scaledWidth = 1;
1894 if (yScale >= 0) {
1895 ty = splashFloor(matrix->y0 + 0.01);
1896 ty2 = splashCeil(matrix->y0 + yScale - 0.01);
1897 } else {
1898 ty = splashCeil(matrix->y0 - 0.01);
1899 ty2 = splashFloor(matrix->y0 + yScale + 0.01);
1901 *scaledHeight = abs(ty2 - ty);
1902 if (*scaledHeight == 0) {
1903 *scaledHeight = 1;
1907 cairo_filter_t
1908 CairoOutputDev::getFilterForSurface(cairo_surface_t *image,
1909 GBool interpolate)
1911 if (interpolate)
1912 return CAIRO_FILTER_BILINEAR;
1914 int orig_width = cairo_image_surface_get_width (image);
1915 int orig_height = cairo_image_surface_get_height (image);
1916 if (orig_width == 0 || orig_height == 0)
1917 return CAIRO_FILTER_NEAREST;
1919 /* When printing, don't change the interpolation. */
1920 if (printing)
1921 return CAIRO_FILTER_NEAREST;
1923 cairo_matrix_t matrix;
1924 cairo_get_matrix(cairo, &matrix);
1925 int scaled_width, scaled_height;
1926 getScaledSize (&matrix, orig_width, orig_height, &scaled_width, &scaled_height);
1928 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
1929 if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4)
1930 return CAIRO_FILTER_NEAREST;
1932 return CAIRO_FILTER_BILINEAR;
1935 void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1936 int width, int height, GBool invert,
1937 GBool interpolate, GBool inlineImg) {
1939 /* FIXME: Doesn't the image mask support any colorspace? */
1940 cairo_set_source (cairo, fill_pattern);
1942 /* work around a cairo bug when scaling 1x1 surfaces */
1943 if (width == 1 && height == 1) {
1944 ImageStream *imgStr;
1945 Guchar pix;
1946 int invert_bit;
1948 imgStr = new ImageStream(str, width, 1, 1);
1949 imgStr->reset();
1950 imgStr->getPixel(&pix);
1951 imgStr->close();
1952 delete imgStr;
1954 invert_bit = invert ? 1 : 0;
1955 if (pix ^ invert_bit)
1956 return;
1958 cairo_save (cairo);
1959 cairo_rectangle (cairo, 0., 0., width, height);
1960 cairo_fill (cairo);
1961 cairo_restore (cairo);
1962 if (cairo_shape) {
1963 cairo_save (cairo_shape);
1964 cairo_rectangle (cairo_shape, 0., 0., width, height);
1965 cairo_fill (cairo_shape);
1966 cairo_restore (cairo_shape);
1968 return;
1971 /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
1973 cairo_matrix_t matrix;
1974 cairo_get_matrix (cairo, &matrix);
1975 //XXX: it is possible that we should only do sub pixel positioning if
1976 // we are rendering fonts */
1977 if (!printing && prescaleImages
1978 /* not rotated */
1979 && matrix.xy == 0 && matrix.yx == 0
1980 /* axes not flipped / not 180 deg rotated */
1981 && matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) {
1982 drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg);
1983 } else {
1984 drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg);
1989 void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
1990 int width, int height, GBool invert,
1991 GBool inlineImg, double *baseMatrix) {
1993 /* FIXME: Doesn't the image mask support any colorspace? */
1994 cairo_set_source (cairo, fill_pattern);
1996 /* work around a cairo bug when scaling 1x1 surfaces */
1997 if (width == 1 && height == 1) {
1998 ImageStream *imgStr;
1999 Guchar pix;
2000 int invert_bit;
2002 imgStr = new ImageStream(str, width, 1, 1);
2003 imgStr->reset();
2004 imgStr->getPixel(&pix);
2005 imgStr->close();
2006 delete imgStr;
2008 invert_bit = invert ? 1 : 0;
2009 if (!(pix ^ invert_bit)) {
2010 cairo_save (cairo);
2011 cairo_rectangle (cairo, 0., 0., width, height);
2012 cairo_fill (cairo);
2013 cairo_restore (cairo);
2014 if (cairo_shape) {
2015 cairo_save (cairo_shape);
2016 cairo_rectangle (cairo_shape, 0., 0., width, height);
2017 cairo_fill (cairo_shape);
2018 cairo_restore (cairo_shape);
2021 } else {
2022 cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
2024 /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
2026 cairo_matrix_t matrix;
2027 cairo_get_matrix (cairo, &matrix);
2028 //XXX: it is possible that we should only do sub pixel positioning if
2029 // we are rendering fonts */
2030 if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) {
2031 drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg);
2032 } else {
2033 drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg);
2036 if (state->getFillColorSpace()->getMode() == csPattern) {
2037 cairo_set_source_rgb (cairo, 1, 1, 1);
2038 cairo_set_matrix (cairo, &mask_matrix);
2039 cairo_mask (cairo, mask);
2042 if (mask)
2043 cairo_pattern_destroy (mask);
2044 mask = cairo_pop_group (cairo);
2047 saveState(state);
2048 double bbox[4] = {0,0,1,1}; // dummy
2049 beginTransparencyGroup(state, bbox, state->getFillColorSpace(),
2050 gTrue, gFalse, gFalse);
2053 void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {
2054 double bbox[4] = {0,0,1,1}; // dummy
2056 endTransparencyGroup(state);
2057 restoreState(state);
2058 paintTransparencyGroup(state, bbox);
2059 clearSoftMask(state);
2062 void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str,
2063 int width, int height, GBool invert,
2064 GBool interpolate, GBool inlineImg) {
2065 unsigned char *buffer;
2066 unsigned char *dest;
2067 cairo_surface_t *image;
2068 cairo_pattern_t *pattern;
2069 int x, y, i, bit;
2070 ImageStream *imgStr;
2071 Guchar *pix;
2072 cairo_matrix_t matrix;
2073 int invert_bit;
2074 int row_stride;
2075 cairo_filter_t filter;
2077 /* TODO: Do we want to cache these? */
2078 imgStr = new ImageStream(str, width, 1, 1);
2079 imgStr->reset();
2081 image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
2082 if (cairo_surface_status (image))
2083 goto cleanup;
2085 buffer = cairo_image_surface_get_data (image);
2086 row_stride = cairo_image_surface_get_stride (image);
2088 invert_bit = invert ? 1 : 0;
2090 for (y = 0; y < height; y++) {
2091 pix = imgStr->getLine();
2092 dest = buffer + y * row_stride;
2093 i = 0;
2094 bit = 0;
2095 for (x = 0; x < width; x++) {
2096 if (bit == 0)
2097 dest[i] = 0;
2098 if (!(pix[x] ^ invert_bit)) {
2099 #ifdef WORDS_BIGENDIAN
2100 dest[i] |= (1 << (7 - bit));
2101 #else
2102 dest[i] |= (1 << bit);
2103 #endif
2105 bit++;
2106 if (bit > 7) {
2107 bit = 0;
2108 i++;
2113 filter = getFilterForSurface (image, interpolate);
2115 cairo_surface_mark_dirty (image);
2116 pattern = cairo_pattern_create_for_surface (image);
2117 cairo_surface_destroy (image);
2118 if (cairo_pattern_status (pattern))
2119 goto cleanup;
2121 LOG (printf ("drawImageMask %dx%d\n", width, height));
2123 cairo_pattern_set_filter (pattern, filter);
2125 if (!printing)
2126 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2128 cairo_matrix_init_translate (&matrix, 0, height);
2129 cairo_matrix_scale (&matrix, width, -height);
2130 cairo_pattern_set_matrix (pattern, &matrix);
2131 if (cairo_pattern_status (pattern)) {
2132 cairo_pattern_destroy (pattern);
2133 goto cleanup;
2136 if (state->getFillColorSpace()->getMode() == csPattern) {
2137 mask = cairo_pattern_reference (pattern);
2138 cairo_get_matrix (cairo, &mask_matrix);
2139 } else if (!printing) {
2140 cairo_save (cairo);
2141 cairo_rectangle (cairo, 0., 0., 1., 1.);
2142 cairo_clip (cairo);
2143 cairo_mask (cairo, pattern);
2144 cairo_restore (cairo);
2145 } else {
2146 cairo_mask (cairo, pattern);
2149 if (cairo_shape) {
2150 cairo_save (cairo_shape);
2151 cairo_set_source (cairo_shape, pattern);
2152 if (!printing) {
2153 cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2154 cairo_fill (cairo_shape);
2155 } else {
2156 cairo_mask (cairo_shape, pattern);
2158 cairo_restore (cairo_shape);
2161 cairo_pattern_destroy (pattern);
2163 cleanup:
2164 imgStr->close();
2165 delete imgStr;
2169 void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str,
2170 int width, int height, GBool invert,
2171 GBool interpolate, GBool inlineImg) {
2172 unsigned char *buffer;
2173 cairo_surface_t *image;
2174 cairo_pattern_t *pattern;
2175 ImageStream *imgStr;
2176 Guchar *pix;
2177 cairo_matrix_t matrix;
2178 int invert_bit;
2179 int row_stride;
2181 /* cairo does a very poor job of scaling down images so we scale them ourselves */
2183 LOG (printf ("drawImageMaskPrescaled %dx%d\n", width, height));
2185 /* this scaling code is adopted from the splash image scaling code */
2186 cairo_get_matrix(cairo, &matrix);
2187 #if 0
2188 printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
2189 #endif
2190 /* this whole computation should be factored out */
2191 double xScale = matrix.xx;
2192 double yScale = matrix.yy;
2193 int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
2194 int scaledHeight;
2195 int scaledWidth;
2196 if (xScale >= 0) {
2197 tx = splashRound(matrix.x0 - 0.01);
2198 tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1;
2199 } else {
2200 tx = splashRound(matrix.x0 + 0.01) - 1;
2201 tx2 = splashRound(matrix.x0 + xScale - 0.01);
2203 scaledWidth = abs(tx2 - tx) + 1;
2204 //scaledWidth = splashRound(fabs(xScale));
2205 if (scaledWidth == 0) {
2206 // technically, this should draw nothing, but it generally seems
2207 // better to draw a one-pixel-wide stripe rather than throwing it
2208 // away
2209 scaledWidth = 1;
2211 if (yScale >= 0) {
2212 ty = splashFloor(matrix.y0 + 0.01);
2213 ty2 = splashCeil(matrix.y0 + yScale - 0.01);
2214 } else {
2215 ty = splashCeil(matrix.y0 - 0.01);
2216 ty2 = splashFloor(matrix.y0 + yScale + 0.01);
2218 scaledHeight = abs(ty2 - ty);
2219 if (scaledHeight == 0) {
2220 scaledHeight = 1;
2222 #if 0
2223 printf("xscale: %g, yscale: %g\n", xScale, yScale);
2224 printf("width: %d, height: %d\n", width, height);
2225 printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight);
2226 #endif
2228 /* compute the required padding */
2229 /* Padding is used to preserve the aspect ratio.
2230 We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */
2231 int head_pad = 0;
2232 int tail_pad = 0;
2233 int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height);
2235 /* compute the two pieces of padding */
2236 if (total_pad > 0) {
2237 //XXX: i'm not positive fabs() is correct
2238 float tail_error = fabs(matrix.y0 - ty);
2239 float head_error = fabs(ty2 - (matrix.y0 + yScale));
2240 float tail_fraction = tail_error/(tail_error + head_error);
2241 tail_pad = splashRound(total_pad*tail_fraction);
2242 head_pad = total_pad - tail_pad;
2243 } else {
2244 tail_pad = 0;
2245 head_pad = 0;
2247 int origHeight = height;
2248 height += tail_pad;
2249 height += head_pad;
2250 #if 0
2251 printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad);
2252 printf("origHeight: %d height: %d\n", origHeight, height);
2253 printf("ty: %d, ty2: %d\n", ty, ty2);
2254 #endif
2256 /* TODO: Do we want to cache these? */
2257 imgStr = new ImageStream(str, width, 1, 1);
2258 imgStr->reset();
2260 invert_bit = invert ? 1 : 0;
2262 image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight);
2263 if (cairo_surface_status (image)) {
2264 imgStr->close();
2265 delete imgStr;
2266 return;
2269 buffer = cairo_image_surface_get_data (image);
2270 row_stride = cairo_image_surface_get_stride (image);
2272 int yp = height / scaledHeight;
2273 int yq = height % scaledHeight;
2274 int xp = width / scaledWidth;
2275 int xq = width % scaledWidth;
2276 int yt = 0;
2277 int origHeight_c = origHeight;
2278 /* use MIN() because yp might be > origHeight because of padding */
2279 unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width);
2280 int lastYStep = 1;
2281 int total = 0;
2282 for (int y = 0; y < scaledHeight; y++) {
2283 // y scale Bresenham
2284 int yStep = yp;
2285 yt += yq;
2287 if (yt >= scaledHeight) {
2288 yt -= scaledHeight;
2289 ++yStep;
2292 // read row (s) from image ignoring the padding as appropriate
2294 int n = (yp > 0) ? yStep : lastYStep;
2295 total += n;
2296 if (n > 0) {
2297 unsigned char *p = pixBuf;
2298 int head_pad_count = head_pad;
2299 int origHeight_count = origHeight;
2300 int tail_pad_count = tail_pad;
2301 for (int i=0; i<n; i++) {
2302 // get row
2303 if (head_pad_count) {
2304 head_pad_count--;
2305 } else if (origHeight_count) {
2306 pix = imgStr->getLine();
2307 for (int j=0; j<width; j++) {
2308 if (pix[j] ^ invert_bit)
2309 p[j] = 0;
2310 else
2311 p[j] = 255;
2313 origHeight_count--;
2314 p += width;
2315 } else if (tail_pad_count) {
2316 tail_pad_count--;
2317 } else {
2318 printf("%d %d\n", n, total);
2319 assert(0 && "over run\n");
2325 lastYStep = yStep;
2326 int k1 = y;
2328 int xt = 0;
2329 int xSrc = 0;
2330 int x1 = k1;
2331 int n = yStep > 0 ? yStep : 1;
2332 int origN = n;
2334 /* compute the size of padding and pixels that will be used for this row */
2335 int head_pad_size = MIN(n, head_pad);
2336 n -= head_pad_size;
2337 head_pad -= MIN(head_pad_size, yStep);
2339 int pix_size = MIN(n, origHeight);
2340 n -= pix_size;
2341 origHeight -= MIN(pix_size, yStep);
2343 int tail_pad_size = MIN(n, tail_pad);
2344 n -= tail_pad_size;
2345 tail_pad -= MIN(tail_pad_size, yStep);
2346 if (n != 0) {
2347 printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size);
2348 assert(n == 0);
2351 for (int x = 0; x < scaledWidth; ++x) {
2352 int xStep = xp;
2353 xt += xq;
2354 if (xt >= scaledWidth) {
2355 xt -= scaledWidth;
2356 ++xStep;
2358 int m = xStep > 0 ? xStep : 1;
2359 float pixAcc0 = 0;
2360 /* could m * head_pad_size * tail_pad_size overflow? */
2361 if (invert_bit) {
2362 pixAcc0 += m * head_pad_size * tail_pad_size * 255;
2363 } else {
2364 pixAcc0 += m * head_pad_size * tail_pad_size * 0;
2366 /* Accumulate all of the source pixels for the destination pixel */
2367 for (int i = 0; i < pix_size; ++i) {
2368 for (int j = 0; j< m; ++j) {
2369 if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) {
2370 printf("%d > %d (%d %d %d %d) (%d %d %d)\n", xSrc + i*width + j, MIN(yp + 1, origHeight_c)*width, xSrc, i , width, j, yp, origHeight_c, width);
2371 printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size);
2372 assert(0 && "bad access\n");
2374 pixAcc0 += pixBuf[xSrc + i*width + j];
2377 buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m));
2378 xSrc += xStep;
2379 x1 += 1;
2383 free(pixBuf);
2385 cairo_surface_mark_dirty (image);
2386 pattern = cairo_pattern_create_for_surface (image);
2387 cairo_surface_destroy (image);
2388 if (cairo_pattern_status (pattern)) {
2389 imgStr->close();
2390 delete imgStr;
2391 return;
2394 /* we should actually be using CAIRO_FILTER_NEAREST here. However,
2395 * cairo doesn't yet do minifaction filtering causing scaled down
2396 * images with CAIRO_FILTER_NEAREST to look really bad */
2397 cairo_pattern_set_filter (pattern,
2398 interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST);
2399 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2401 if (state->getFillColorSpace()->getMode() == csPattern) {
2402 cairo_matrix_init_translate (&matrix, 0, scaledHeight);
2403 cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight);
2404 cairo_pattern_set_matrix (pattern, &matrix);
2405 if (cairo_pattern_status (pattern)) {
2406 cairo_pattern_destroy (pattern);
2407 imgStr->close();
2408 delete imgStr;
2409 return;
2412 mask = cairo_pattern_reference (pattern);
2413 cairo_get_matrix (cairo, &mask_matrix);
2414 } else {
2415 cairo_save (cairo);
2417 /* modify our current transformation so that the prescaled image
2418 * goes where it is supposed to */
2419 cairo_get_matrix(cairo, &matrix);
2420 cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy);
2421 // get integer co-ords
2422 cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0);
2423 if (yScale > 0)
2424 cairo_scale(cairo, 1, -1);
2426 cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight);
2427 cairo_clip (cairo);
2428 cairo_mask (cairo, pattern);
2430 //cairo_get_matrix(cairo, &matrix);
2431 //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
2432 cairo_restore(cairo);
2435 if (cairo_shape) {
2436 cairo_save (cairo_shape);
2438 /* modify our current transformation so that the prescaled image
2439 * goes where it is supposed to */
2440 cairo_get_matrix(cairo_shape, &matrix);
2441 cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy);
2442 // get integer co-ords
2443 cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0);
2444 if (yScale > 0)
2445 cairo_scale(cairo_shape, 1, -1);
2447 cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight);
2448 cairo_fill (cairo_shape);
2450 cairo_restore(cairo_shape);
2453 cairo_pattern_destroy (pattern);
2455 imgStr->close();
2456 delete imgStr;
2459 void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2460 Stream *str, int width, int height,
2461 GfxImageColorMap *colorMap,
2462 GBool interpolate,
2463 Stream *maskStr, int maskWidth,
2464 int maskHeight, GBool maskInvert,
2465 GBool maskInterpolate)
2467 ImageStream *maskImgStr, *imgStr;
2468 int row_stride;
2469 unsigned char *maskBuffer, *buffer;
2470 unsigned char *maskDest;
2471 unsigned int *dest;
2472 cairo_surface_t *maskImage, *image;
2473 cairo_pattern_t *maskPattern, *pattern;
2474 cairo_matrix_t matrix;
2475 cairo_matrix_t maskMatrix;
2476 Guchar *pix;
2477 int x, y;
2478 int invert_bit;
2479 cairo_filter_t filter;
2480 cairo_filter_t maskFilter;
2482 maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2483 maskImgStr->reset();
2485 maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
2486 if (cairo_surface_status (maskImage)) {
2487 maskImgStr->close();
2488 delete maskImgStr;
2489 return;
2492 maskBuffer = cairo_image_surface_get_data (maskImage);
2493 row_stride = cairo_image_surface_get_stride (maskImage);
2495 invert_bit = maskInvert ? 1 : 0;
2497 for (y = 0; y < maskHeight; y++) {
2498 pix = maskImgStr->getLine();
2499 maskDest = maskBuffer + y * row_stride;
2500 for (x = 0; x < maskWidth; x++) {
2501 if (pix[x] ^ invert_bit)
2502 *maskDest++ = 0;
2503 else
2504 *maskDest++ = 255;
2508 maskImgStr->close();
2509 delete maskImgStr;
2511 maskFilter = getFilterForSurface (maskImage, maskInterpolate);
2513 cairo_surface_mark_dirty (maskImage);
2514 maskPattern = cairo_pattern_create_for_surface (maskImage);
2515 cairo_surface_destroy (maskImage);
2516 if (cairo_pattern_status (maskPattern))
2517 return;
2519 #if 0
2520 /* ICCBased color space doesn't do any color correction
2521 * so check its underlying color space as well */
2522 int is_identity_transform;
2523 is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2524 (colorMap->getColorSpace()->getMode() == csICCBased &&
2525 ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2526 #endif
2528 /* TODO: Do we want to cache these? */
2529 imgStr = new ImageStream(str, width,
2530 colorMap->getNumPixelComps(),
2531 colorMap->getBits());
2532 imgStr->reset();
2534 image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2535 if (cairo_surface_status (image))
2536 goto cleanup;
2538 buffer = cairo_image_surface_get_data (image);
2539 row_stride = cairo_image_surface_get_stride (image);
2540 for (y = 0; y < height; y++) {
2541 dest = (unsigned int *) (buffer + y * row_stride);
2542 pix = imgStr->getLine();
2543 colorMap->getRGBLine (pix, dest, width);
2546 filter = getFilterForSurface (image, interpolate);
2548 cairo_surface_mark_dirty (image);
2549 pattern = cairo_pattern_create_for_surface (image);
2550 cairo_surface_destroy (image);
2551 if (cairo_pattern_status (pattern))
2552 goto cleanup;
2554 LOG (printf ("drawMaskedImage %dx%d\n", width, height));
2556 cairo_pattern_set_filter (pattern, filter);
2557 cairo_pattern_set_filter (maskPattern, maskFilter);
2559 if (!printing) {
2560 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2561 cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2564 cairo_matrix_init_translate (&matrix, 0, height);
2565 cairo_matrix_scale (&matrix, width, -height);
2566 cairo_pattern_set_matrix (pattern, &matrix);
2567 if (cairo_pattern_status (pattern)) {
2568 cairo_pattern_destroy (pattern);
2569 cairo_pattern_destroy (maskPattern);
2570 goto cleanup;
2573 cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2574 cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2575 cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2576 if (cairo_pattern_status (maskPattern)) {
2577 cairo_pattern_destroy (maskPattern);
2578 cairo_pattern_destroy (pattern);
2579 goto cleanup;
2582 if (!printing) {
2583 cairo_save (cairo);
2584 cairo_set_source (cairo, pattern);
2585 cairo_rectangle (cairo, 0., 0., 1., 1.);
2586 cairo_clip (cairo);
2587 cairo_mask (cairo, maskPattern);
2588 cairo_restore (cairo);
2589 } else {
2590 cairo_set_source (cairo, pattern);
2591 cairo_mask (cairo, maskPattern);
2594 if (cairo_shape) {
2595 cairo_save (cairo_shape);
2596 cairo_set_source (cairo_shape, pattern);
2597 if (!printing) {
2598 cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2599 cairo_fill (cairo_shape);
2600 } else {
2601 cairo_mask (cairo_shape, pattern);
2603 cairo_restore (cairo_shape);
2606 cairo_pattern_destroy (maskPattern);
2607 cairo_pattern_destroy (pattern);
2609 cleanup:
2610 imgStr->close();
2611 delete imgStr;
2615 //XXX: is this affect by AIS(alpha is shape)?
2616 void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2617 int width, int height,
2618 GfxImageColorMap *colorMap,
2619 GBool interpolate,
2620 Stream *maskStr,
2621 int maskWidth, int maskHeight,
2622 GfxImageColorMap *maskColorMap,
2623 GBool maskInterpolate)
2625 ImageStream *maskImgStr, *imgStr;
2626 int row_stride;
2627 unsigned char *maskBuffer, *buffer;
2628 unsigned char *maskDest;
2629 unsigned int *dest;
2630 cairo_surface_t *maskImage, *image;
2631 cairo_pattern_t *maskPattern, *pattern;
2632 cairo_matrix_t maskMatrix, matrix;
2633 Guchar *pix;
2634 int y;
2635 cairo_filter_t filter;
2636 cairo_filter_t maskFilter;
2638 maskImgStr = new ImageStream(maskStr, maskWidth,
2639 maskColorMap->getNumPixelComps(),
2640 maskColorMap->getBits());
2641 maskImgStr->reset();
2643 maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
2644 if (cairo_surface_status (maskImage)) {
2645 maskImgStr->close();
2646 delete maskImgStr;
2647 return;
2650 maskBuffer = cairo_image_surface_get_data (maskImage);
2651 row_stride = cairo_image_surface_get_stride (maskImage);
2652 for (y = 0; y < maskHeight; y++) {
2653 maskDest = (unsigned char *) (maskBuffer + y * row_stride);
2654 pix = maskImgStr->getLine();
2655 maskColorMap->getGrayLine (pix, maskDest, maskWidth);
2658 maskImgStr->close();
2659 delete maskImgStr;
2661 maskFilter = getFilterForSurface (maskImage, maskInterpolate);
2663 cairo_surface_mark_dirty (maskImage);
2664 maskPattern = cairo_pattern_create_for_surface (maskImage);
2665 cairo_surface_destroy (maskImage);
2666 if (cairo_pattern_status (maskPattern))
2667 return;
2669 #if 0
2670 /* ICCBased color space doesn't do any color correction
2671 * so check its underlying color space as well */
2672 int is_identity_transform;
2673 is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2674 (colorMap->getColorSpace()->getMode() == csICCBased &&
2675 ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2676 #endif
2678 /* TODO: Do we want to cache these? */
2679 imgStr = new ImageStream(str, width,
2680 colorMap->getNumPixelComps(),
2681 colorMap->getBits());
2682 imgStr->reset();
2684 image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2685 if (cairo_surface_status (image))
2686 goto cleanup;
2688 buffer = cairo_image_surface_get_data (image);
2689 row_stride = cairo_image_surface_get_stride (image);
2690 for (y = 0; y < height; y++) {
2691 dest = (unsigned int *) (buffer + y * row_stride);
2692 pix = imgStr->getLine();
2693 colorMap->getRGBLine (pix, dest, width);
2696 filter = getFilterForSurface (image, interpolate);
2698 cairo_surface_mark_dirty (image);
2700 setMimeData(state, str, ref, colorMap, image);
2702 pattern = cairo_pattern_create_for_surface (image);
2703 cairo_surface_destroy (image);
2704 if (cairo_pattern_status (pattern))
2705 goto cleanup;
2707 LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height));
2709 cairo_pattern_set_filter (pattern, filter);
2710 cairo_pattern_set_filter (maskPattern, maskFilter);
2712 if (!printing) {
2713 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2714 cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2717 cairo_matrix_init_translate (&matrix, 0, height);
2718 cairo_matrix_scale (&matrix, width, -height);
2719 cairo_pattern_set_matrix (pattern, &matrix);
2720 if (cairo_pattern_status (pattern)) {
2721 cairo_pattern_destroy (pattern);
2722 cairo_pattern_destroy (maskPattern);
2723 goto cleanup;
2726 cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2727 cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2728 cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2729 if (cairo_pattern_status (maskPattern)) {
2730 cairo_pattern_destroy (maskPattern);
2731 cairo_pattern_destroy (pattern);
2732 goto cleanup;
2735 if (fill_opacity != 1.0)
2736 cairo_push_group (cairo);
2737 else
2738 cairo_save (cairo);
2740 cairo_set_source (cairo, pattern);
2741 if (!printing) {
2742 cairo_rectangle (cairo, 0., 0., 1., 1.);
2743 cairo_clip (cairo);
2745 cairo_mask (cairo, maskPattern);
2747 if (fill_opacity != 1.0) {
2748 cairo_pop_group_to_source (cairo);
2749 cairo_save (cairo);
2750 if (!printing) {
2751 cairo_rectangle (cairo, 0., 0., 1., 1.);
2752 cairo_clip (cairo);
2754 cairo_paint_with_alpha (cairo, fill_opacity);
2756 cairo_restore (cairo);
2758 if (cairo_shape) {
2759 cairo_save (cairo_shape);
2760 cairo_set_source (cairo_shape, pattern);
2761 if (!printing) {
2762 cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2763 cairo_fill (cairo_shape);
2764 } else {
2765 cairo_mask (cairo_shape, pattern);
2767 cairo_restore (cairo_shape);
2770 cairo_pattern_destroy (maskPattern);
2771 cairo_pattern_destroy (pattern);
2773 cleanup:
2774 imgStr->close();
2775 delete imgStr;
2778 GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length)
2780 int len, i;
2781 char *strBuffer;
2783 len = 0;
2784 str->close();
2785 str->reset();
2786 while (str->getChar() != EOF) len++;
2787 if (len == 0)
2788 return gFalse;
2790 strBuffer = (char *)gmalloc (len);
2792 str->close();
2793 str->reset();
2794 for (i = 0; i < len; ++i)
2795 strBuffer[i] = str->getChar();
2797 *buffer = strBuffer;
2798 *length = len;
2800 return gTrue;
2803 static GBool colorMapHasIdentityDecodeMap(GfxImageColorMap *colorMap)
2805 for (int i = 0; i < colorMap->getNumPixelComps(); i++) {
2806 if (colorMap->getDecodeLow(i) != 0.0 || colorMap->getDecodeHigh(i) != 1.0)
2807 return gFalse;
2809 return gTrue;
2812 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
2813 static cairo_status_t setMimeIdFromRef(cairo_surface_t *surface,
2814 const char *mime_type,
2815 const char *mime_id_prefix,
2816 Ref ref)
2818 GooString *mime_id;
2819 char *idBuffer;
2820 cairo_status_t status;
2822 mime_id = new GooString;
2824 if (mime_id_prefix)
2825 mime_id->append(mime_id_prefix);
2827 mime_id->appendf("{0:d}-{1:d}", ref.gen, ref.num);
2829 idBuffer = copyString(mime_id->getCString());
2830 status = cairo_surface_set_mime_data (surface, mime_type,
2831 (const unsigned char *)idBuffer,
2832 mime_id->getLength(),
2833 gfree, idBuffer);
2834 delete mime_id;
2835 if (status)
2836 gfree (idBuffer);
2837 return status;
2839 #endif
2841 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2842 GBool CairoOutputDev::setMimeDataForJBIG2Globals(Stream *str,
2843 cairo_surface_t *image)
2845 JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str);
2846 Object* globalsStr = jb2Str->getGlobalsStream();
2847 char *globalsBuffer;
2848 int globalsLength;
2850 // nothing to do for JBIG2 stream without Globals
2851 if (!globalsStr->isStream())
2852 return gTrue;
2854 if (setMimeIdFromRef(image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, NULL,
2855 jb2Str->getGlobalsStreamRef()))
2856 return gFalse;
2858 if (!getStreamData(globalsStr->getStream(), &globalsBuffer, &globalsLength))
2859 return gFalse;
2861 if (cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL,
2862 (const unsigned char*)globalsBuffer,
2863 globalsLength,
2864 gfree, (void*)globalsBuffer))
2866 gfree (globalsBuffer);
2867 return gFalse;
2870 return gTrue;
2872 #endif
2874 void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref,
2875 GfxImageColorMap *colorMap, cairo_surface_t *image)
2877 char *strBuffer;
2878 int len;
2879 Object obj;
2880 GfxColorSpace *colorSpace;
2881 StreamKind strKind = str->getKind();
2882 const char *mime_type;
2884 if (!printing)
2885 return;
2887 switch (strKind) {
2888 case strDCT:
2889 mime_type = CAIRO_MIME_TYPE_JPEG;
2890 break;
2891 case strJPX:
2892 mime_type = CAIRO_MIME_TYPE_JP2;
2893 break;
2894 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2895 case strJBIG2:
2896 mime_type = CAIRO_MIME_TYPE_JBIG2;
2897 break;
2898 #endif
2899 default:
2900 return;
2903 str->getDict()->lookup("ColorSpace", &obj);
2904 colorSpace = GfxColorSpace::parse(NULL, &obj, this, state);
2905 obj.free();
2907 // colorspace in stream dict may be different from colorspace in jpx
2908 // data
2909 if (strKind == strJPX && colorSpace)
2910 return;
2912 // only embed mime data for gray, rgb, and cmyk colorspaces.
2913 if (colorSpace) {
2914 GfxColorSpaceMode mode = colorSpace->getMode();
2915 delete colorSpace;
2916 switch (mode) {
2917 case csDeviceGray:
2918 case csCalGray:
2919 case csDeviceRGB:
2920 case csCalRGB:
2921 case csDeviceCMYK:
2922 case csICCBased:
2923 break;
2925 case csLab:
2926 case csIndexed:
2927 case csSeparation:
2928 case csDeviceN:
2929 case csPattern:
2930 return;
2934 if (!colorMapHasIdentityDecodeMap(colorMap))
2935 return;
2937 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
2938 if (strKind == strJBIG2 && !setMimeDataForJBIG2Globals(str, image))
2939 return;
2940 #endif
2942 if (getStreamData (str->getNextStream(), &strBuffer, &len)) {
2943 cairo_status_t status = CAIRO_STATUS_SUCCESS;
2945 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2)
2946 if (ref && ref->isRef()) {
2947 status = setMimeIdFromRef(image, CAIRO_MIME_TYPE_UNIQUE_ID,
2948 "poppler-surface-", ref->getRef());
2950 #endif
2951 if (!status) {
2952 status = cairo_surface_set_mime_data (image, mime_type,
2953 (const unsigned char *)strBuffer, len,
2954 gfree, strBuffer);
2957 if (status)
2958 gfree (strBuffer);
2962 class RescaleDrawImage : public CairoRescaleBox {
2963 private:
2964 ImageStream *imgStr;
2965 GfxRGB *lookup;
2966 int width;
2967 GfxImageColorMap *colorMap;
2968 int *maskColors;
2969 int current_row;
2970 GBool imageError;
2972 public:
2973 cairo_surface_t *getSourceImage(Stream *str,
2974 int widthA, int height,
2975 int scaledWidth, int scaledHeight,
2976 GBool printing,
2977 GfxImageColorMap *colorMapA,
2978 int *maskColorsA) {
2979 cairo_surface_t *image = NULL;
2980 int i;
2982 lookup = NULL;
2983 colorMap = colorMapA;
2984 maskColors = maskColorsA;
2985 width = widthA;
2986 current_row = -1;
2987 imageError = gFalse;
2989 /* TODO: Do we want to cache these? */
2990 imgStr = new ImageStream(str, width,
2991 colorMap->getNumPixelComps(),
2992 colorMap->getBits());
2993 imgStr->reset();
2995 #if 0
2996 /* ICCBased color space doesn't do any color correction
2997 * so check its underlying color space as well */
2998 int is_identity_transform;
2999 is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
3000 (colorMap->getColorSpace()->getMode() == csICCBased &&
3001 ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
3002 #endif
3004 // special case for one-channel (monochrome/gray/separation) images:
3005 // build a lookup table here
3006 if (colorMap->getNumPixelComps() == 1) {
3007 int n;
3008 Guchar pix;
3010 n = 1 << colorMap->getBits();
3011 lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB));
3012 for (i = 0; i < n; ++i) {
3013 pix = (Guchar)i;
3015 colorMap->getRGB(&pix, &lookup[i]);
3019 if (printing || scaledWidth >= width || scaledHeight >= height) {
3020 // No downscaling. Create cairo image containing the source image data.
3021 unsigned char *buffer;
3022 int stride;
3024 image = cairo_image_surface_create (maskColors ?
3025 CAIRO_FORMAT_ARGB32 :
3026 CAIRO_FORMAT_RGB24,
3027 width, height);
3028 if (cairo_surface_status (image))
3029 goto cleanup;
3031 buffer = cairo_image_surface_get_data (image);
3032 stride = cairo_image_surface_get_stride (image);
3033 for (int y = 0; y < height; y++) {
3034 uint32_t *dest = (uint32_t *) (buffer + y * stride);
3035 getRow(y, dest);
3037 } else {
3038 // // Downscaling required. Create cairo image the size of the
3039 // rescaled image and // downscale the source image data into
3040 // the cairo image. downScaleImage() will call getRow() to read
3041 // source image data from the image stream. This avoids having
3042 // to create an image the size of the source image which may
3043 // exceed cairo's 32676x32767 image size limit (and also saves a
3044 // lot of memory).
3045 image = cairo_image_surface_create (maskColors ?
3046 CAIRO_FORMAT_ARGB32 :
3047 CAIRO_FORMAT_RGB24,
3048 scaledWidth, scaledHeight);
3049 if (cairo_surface_status (image))
3050 goto cleanup;
3052 downScaleImage(width, height,
3053 scaledWidth, scaledHeight,
3054 0, 0, scaledWidth, scaledHeight,
3055 image);
3057 cairo_surface_mark_dirty (image);
3059 cleanup:
3060 gfree(lookup);
3061 imgStr->close();
3062 delete imgStr;
3063 return image;
3066 void getRow(int row_num, uint32_t *row_data) {
3067 int i;
3068 Guchar *pix;
3070 if (row_num <= current_row)
3071 return;
3073 while (current_row < row_num) {
3074 pix = imgStr->getLine();
3075 current_row++;
3078 if (unlikely(pix == NULL)) {
3079 memset(row_data, 0, width*4);
3080 if (!imageError) {
3081 error(errInternal, -1, "Bad image stream");
3082 imageError = gTrue;
3084 } else if (lookup) {
3085 Guchar *p = pix;
3086 GfxRGB rgb;
3088 for (i = 0; i < width; i++) {
3089 rgb = lookup[*p];
3090 row_data[i] =
3091 ((int) colToByte(rgb.r) << 16) |
3092 ((int) colToByte(rgb.g) << 8) |
3093 ((int) colToByte(rgb.b) << 0);
3094 p++;
3096 } else {
3097 colorMap->getRGBLine (pix, row_data, width);
3100 if (maskColors) {
3101 for (int x = 0; x < width; x++) {
3102 bool is_opaque = false;
3103 for (int i = 0; i < colorMap->getNumPixelComps(); ++i) {
3104 if (pix[i] < maskColors[2*i] ||
3105 pix[i] > maskColors[2*i+1]) {
3106 is_opaque = true;
3107 break;
3110 if (is_opaque)
3111 *row_data |= 0xff000000;
3112 else
3113 *row_data = 0;
3114 row_data++;
3115 pix += colorMap->getNumPixelComps();
3122 void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3123 int widthA, int heightA,
3124 GfxImageColorMap *colorMap,
3125 GBool interpolate,
3126 int *maskColors, GBool inlineImg)
3128 cairo_surface_t *image;
3129 cairo_pattern_t *pattern, *maskPattern;
3130 cairo_matrix_t matrix;
3131 int width, height;
3132 int scaledWidth, scaledHeight;
3133 cairo_filter_t filter = CAIRO_FILTER_BILINEAR;
3134 RescaleDrawImage rescale;
3136 LOG (printf ("drawImage %dx%d\n", widthA, heightA));
3138 cairo_get_matrix(cairo, &matrix);
3139 getScaledSize (&matrix, widthA, heightA, &scaledWidth, &scaledHeight);
3140 image = rescale.getSourceImage(str, widthA, heightA, scaledWidth, scaledHeight, printing, colorMap, maskColors);
3141 if (!image)
3142 return;
3144 width = cairo_image_surface_get_width (image);
3145 height = cairo_image_surface_get_height (image);
3146 if (width == widthA && height == heightA)
3147 filter = getFilterForSurface (image, interpolate);
3149 if (!inlineImg) /* don't read stream twice if it is an inline image */
3150 setMimeData(state, str, ref, colorMap, image);
3152 pattern = cairo_pattern_create_for_surface (image);
3153 cairo_surface_destroy (image);
3154 if (cairo_pattern_status (pattern))
3155 return;
3157 cairo_pattern_set_filter (pattern, filter);
3159 if (!printing)
3160 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
3162 cairo_matrix_init_translate (&matrix, 0, height);
3163 cairo_matrix_scale (&matrix, width, -height);
3164 cairo_pattern_set_matrix (pattern, &matrix);
3165 if (cairo_pattern_status (pattern)) {
3166 cairo_pattern_destroy (pattern);
3167 return;
3170 if (!mask && fill_opacity != 1.0) {
3171 maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity);
3172 } else if (mask) {
3173 maskPattern = cairo_pattern_reference (mask);
3174 } else {
3175 maskPattern = NULL;
3178 cairo_save (cairo);
3179 cairo_set_source (cairo, pattern);
3180 if (!printing)
3181 cairo_rectangle (cairo, 0., 0., 1., 1.);
3182 if (maskPattern) {
3183 if (!printing)
3184 cairo_clip (cairo);
3185 if (mask)
3186 cairo_set_matrix (cairo, &mask_matrix);
3187 cairo_mask (cairo, maskPattern);
3188 } else {
3189 if (printing)
3190 cairo_paint (cairo);
3191 else
3192 cairo_fill (cairo);
3194 cairo_restore (cairo);
3196 cairo_pattern_destroy (maskPattern);
3198 if (cairo_shape) {
3199 cairo_save (cairo_shape);
3200 cairo_set_source (cairo_shape, pattern);
3201 if (printing) {
3202 cairo_paint (cairo_shape);
3203 } else {
3204 cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
3205 cairo_fill (cairo_shape);
3207 cairo_restore (cairo_shape);
3210 cairo_pattern_destroy (pattern);
3214 //------------------------------------------------------------------------
3215 // ImageOutputDev
3216 //------------------------------------------------------------------------
3218 CairoImageOutputDev::CairoImageOutputDev()
3220 images = NULL;
3221 numImages = 0;
3222 size = 0;
3223 imgDrawCbk = NULL;
3224 imgDrawCbkData = NULL;
3227 CairoImageOutputDev::~CairoImageOutputDev()
3229 int i;
3231 for (i = 0; i < numImages; i++)
3232 delete images[i];
3233 gfree (images);
3236 void CairoImageOutputDev::saveImage(CairoImage *image)
3238 if (numImages >= size) {
3239 size += 16;
3240 images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *));
3242 images[numImages++] = image;
3245 void CairoImageOutputDev::getBBox(GfxState *state, int width, int height,
3246 double *x1, double *y1, double *x2, double *y2)
3248 double *ctm = state->getCTM();
3249 cairo_matrix_t matrix;
3250 cairo_matrix_init(&matrix,
3251 ctm[0], ctm[1],
3252 -ctm[2], -ctm[3],
3253 ctm[2] + ctm[4], ctm[3] + ctm[5]);
3255 int scaledWidth, scaledHeight;
3256 getScaledSize (&matrix, width, height, &scaledWidth, &scaledHeight);
3258 if (matrix.xx >= 0) {
3259 *x1 = matrix.x0;
3260 } else {
3261 *x1 = matrix.x0 - scaledWidth;
3263 *x2 = *x1 + scaledWidth;
3265 if (matrix.yy >= 0) {
3266 *y1 = matrix.y0;
3267 } else {
3268 *y1 = matrix.y0 - scaledHeight;
3270 *y2 = *y1 + scaledHeight;
3273 void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
3274 int width, int height, GBool invert,
3275 GBool interpolate, GBool inlineImg)
3277 cairo_t *cr;
3278 cairo_surface_t *surface;
3279 double x1, y1, x2, y2;
3280 CairoImage *image;
3282 getBBox(state, width, height, &x1, &y1, &x2, &y2);
3284 image = new CairoImage (x1, y1, x2, y2);
3285 saveImage (image);
3287 if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3288 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3289 cr = cairo_create (surface);
3290 setCairo (cr);
3291 cairo_translate (cr, 0, height);
3292 cairo_scale (cr, width, -height);
3294 CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
3295 image->setImage (surface);
3297 setCairo (NULL);
3298 cairo_surface_destroy (surface);
3299 cairo_destroy (cr);
3303 void CairoImageOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str,
3304 int width, int height, GBool invert,
3305 GBool inlineImg, double *baseMatrix)
3307 cairo_t *cr;
3308 cairo_surface_t *surface;
3309 double x1, y1, x2, y2;
3310 CairoImage *image;
3312 getBBox(state, width, height, &x1, &y1, &x2, &y2);
3314 image = new CairoImage (x1, y1, x2, y2);
3315 saveImage (image);
3317 if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3318 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3319 cr = cairo_create (surface);
3320 setCairo (cr);
3321 cairo_translate (cr, 0, height);
3322 cairo_scale (cr, width, -height);
3324 CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg, gFalse);
3325 if (state->getFillColorSpace()->getMode() == csPattern) {
3326 cairo_mask (cairo, mask);
3328 image->setImage (surface);
3330 setCairo (NULL);
3331 cairo_surface_destroy (surface);
3332 cairo_destroy (cr);
3336 void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3337 int width, int height, GfxImageColorMap *colorMap,
3338 GBool interpolate, int *maskColors, GBool inlineImg)
3340 cairo_t *cr;
3341 cairo_surface_t *surface;
3342 double x1, y1, x2, y2;
3343 CairoImage *image;
3345 getBBox(state, width, height, &x1, &y1, &x2, &y2);
3347 image = new CairoImage (x1, y1, x2, y2);
3348 saveImage (image);
3350 if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3351 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3352 cr = cairo_create (surface);
3353 setCairo (cr);
3354 cairo_translate (cr, 0, height);
3355 cairo_scale (cr, width, -height);
3357 CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg);
3358 image->setImage (surface);
3360 setCairo (NULL);
3361 cairo_surface_destroy (surface);
3362 cairo_destroy (cr);
3366 void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
3367 int width, int height,
3368 GfxImageColorMap *colorMap,
3369 GBool interpolate,
3370 Stream *maskStr,
3371 int maskWidth, int maskHeight,
3372 GfxImageColorMap *maskColorMap,
3373 GBool maskInterpolate)
3375 cairo_t *cr;
3376 cairo_surface_t *surface;
3377 double x1, y1, x2, y2;
3378 CairoImage *image;
3380 getBBox(state, width, height, &x1, &y1, &x2, &y2);
3382 image = new CairoImage (x1, y1, x2, y2);
3383 saveImage (image);
3385 if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3386 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3387 cr = cairo_create (surface);
3388 setCairo (cr);
3389 cairo_translate (cr, 0, height);
3390 cairo_scale (cr, width, -height);
3392 CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3393 maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
3394 image->setImage (surface);
3396 setCairo (NULL);
3397 cairo_surface_destroy (surface);
3398 cairo_destroy (cr);
3402 void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
3403 int width, int height,
3404 GfxImageColorMap *colorMap,
3405 GBool interpolate,
3406 Stream *maskStr,
3407 int maskWidth, int maskHeight,
3408 GBool maskInvert, GBool maskInterpolate)
3410 cairo_t *cr;
3411 cairo_surface_t *surface;
3412 double x1, y1, x2, y2;
3413 CairoImage *image;
3415 getBBox(state, width, height, &x1, &y1, &x2, &y2);
3417 image = new CairoImage (x1, y1, x2, y2);
3418 saveImage (image);
3420 if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
3421 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
3422 cr = cairo_create (surface);
3423 setCairo (cr);
3424 cairo_translate (cr, 0, height);
3425 cairo_scale (cr, width, -height);
3427 CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3428 maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate);
3429 image->setImage (surface);
3431 setCairo (NULL);
3432 cairo_surface_destroy (surface);
3433 cairo_destroy (cr);