source: trunk/poppler/mypoppler/poppler/CairoOutputDev.cc @ 461

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

File size: 73.6 KB
Line 
1//========================================================================
2//
3// CairoOutputDev.cc
4//
5// Copyright 2003 Glyph & Cog, LLC
6// Copyright 2004 Red Hat, Inc
7//
8//========================================================================
9
10//========================================================================
11//
12// Modified under the Poppler project - http://poppler.freedesktop.org
13//
14// All changes made under the Poppler project to this file are licensed
15// under GPL version 2 or later
16//
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 Albert Astals Cid <aacid@kde.org>
20// Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
21// Copyright (C) 2006-2010 Carlos Garcia Campos <carlosgc@gnome.org>
22// Copyright (C) 2008 Carl Worth <cworth@cworth.org>
23// Copyright (C) 2008-2010 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 Hib Eris <hib@hiberis.nl>
27// Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu>
28//
29// To see a description of the changes please see the Changelog file that
30// came with your tarball or type make ChangeLog if you are building from git
31//
32//========================================================================
33
34#include <config.h>
35
36#ifdef USE_GCC_PRAGMAS
37#pragma implementation
38#endif
39
40#include <string.h>
41#include <math.h>
42#include <assert.h>
43#include <cairo.h>
44
45#include "goo/gfile.h"
46#include "GlobalParams.h"
47#include "Error.h"
48#include "Object.h"
49#include "Gfx.h"
50#include "GfxState.h"
51#include "GfxFont.h"
52#include "Page.h"
53#include "Link.h"
54#include "CharCodeToUnicode.h"
55#include "FontEncodingTables.h"
56#include "PDFDocEncoding.h"
57#include <fofi/FoFiTrueType.h>
58#include <splash/SplashBitmap.h>
59#include "CairoOutputDev.h"
60#include "CairoFontEngine.h"
61#include "CairoRescaleBox.h"
62//------------------------------------------------------------------------
63
64// #define LOG_CAIRO
65
66#ifdef LOG_CAIRO
67#define LOG(x) (x)
68#else
69#define LOG(x)
70#endif
71
72static inline void printMatrix(cairo_matrix_t *matrix){
73        printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx,
74                        matrix->xy, matrix->yy,
75                        matrix->x0, matrix->y0);
76}
77
78//------------------------------------------------------------------------
79// CairoImage
80//------------------------------------------------------------------------
81
82CairoImage::CairoImage (double x1, double y1, double x2, double y2) {
83  this->image = NULL;
84  this->x1 = x1;
85  this->y1 = y1;
86  this->x2 = x2;
87  this->y2 = y2;
88}
89
90CairoImage::~CairoImage () {
91  if (image)
92    cairo_surface_destroy (image);
93}
94
95void CairoImage::setImage (cairo_surface_t *image) {
96  if (this->image)
97    cairo_surface_destroy (this->image);
98  this->image = cairo_surface_reference (image);
99}
100
101//------------------------------------------------------------------------
102// CairoOutputDev
103//------------------------------------------------------------------------
104
105// We cannot tie the lifetime of an FT_Library object to that of
106// CairoOutputDev, since any FT_Faces created with it may end up with a
107// reference by Cairo which can be held long after the CairoOutputDev is
108// deleted.  The simplest way to avoid problems is to never tear down the
109// FT_Library instance; to avoid leaks, just use a single global instance
110// initialized the first time it is needed.
111FT_Library CairoOutputDev::ft_lib;
112GBool CairoOutputDev::ft_lib_initialized = gFalse;
113
114CairoOutputDev::CairoOutputDev() {
115  xref = NULL;
116  catalog = NULL;
117
118  if (!ft_lib_initialized) {
119    FT_Init_FreeType(&ft_lib);
120    ft_lib_initialized = gTrue;
121  }
122
123  fontEngine = NULL;
124  fontEngine_owner = gFalse;
125  glyphs = NULL;
126  fill_pattern = NULL;
127  fill_color.r = fill_color.g = fill_color.b = 0;
128  stroke_pattern = NULL;
129  stroke_color.r = stroke_color.g = stroke_color.b = 0;
130  stroke_opacity = 1.0;
131  fill_opacity = 1.0;
132  textClipPath = NULL;
133  haveCSPattern = gFalse;
134  cairo = NULL;
135  currentFont = NULL;
136  prescaleImages = gTrue;
137  printing = gTrue;
138  inType3Char = gFalse;
139  t3_glyph_has_bbox = gFalse;
140
141  groupColorSpaceStack = NULL;
142  maskStack = NULL;
143  group = NULL;
144  mask = NULL;
145  shape = NULL;
146  cairo_shape = NULL;
147  knockoutCount = 0;
148
149  text = NULL;
150  actualText = NULL;
151}
152
153CairoOutputDev::~CairoOutputDev() {
154  if (fontEngine_owner && fontEngine) {
155    delete fontEngine;
156  }
157
158  if (cairo)
159    cairo_destroy (cairo);
160  cairo_pattern_destroy (stroke_pattern);
161  cairo_pattern_destroy (fill_pattern);
162  if (group)
163    cairo_pattern_destroy (group);
164  if (mask)
165    cairo_pattern_destroy (mask);
166  if (shape)
167    cairo_pattern_destroy (shape);
168  if (text) 
169    text->decRefCnt();
170  if (actualText)
171    delete actualText; 
172}
173
174void CairoOutputDev::setCairo(cairo_t *cairo)
175{
176  if (this->cairo != NULL) {
177    cairo_status_t status = cairo_status (this->cairo);
178    if (status) {
179      warning("cairo context error: %s\n", cairo_status_to_string(status));
180    }
181    cairo_destroy (this->cairo);
182    assert(!cairo_shape);
183  }
184  if (cairo != NULL) {
185    this->cairo = cairo_reference (cairo);
186        /* save the initial matrix so that we can use it for type3 fonts. */
187        //XXX: is this sufficient? could we miss changes to the matrix somehow?
188        cairo_get_matrix(cairo, &orig_matrix);
189  } else {
190    this->cairo = NULL;
191    this->cairo_shape = NULL;
192  }
193}
194
195void CairoOutputDev::setTextPage(TextPage *text)
196{
197  if (this->text) 
198    this->text->decRefCnt();
199  if (actualText)
200    delete actualText;
201  if (text) {
202    this->text = text;
203    this->text->incRefCnt();
204    actualText = new ActualText(text);
205  } else {
206    this->text = NULL;
207    actualText = NULL;
208  }
209}
210
211void CairoOutputDev::startDoc(XRef *xrefA, Catalog *catalogA,
212                              CairoFontEngine *parentFontEngine) {
213  xref = xrefA;
214  catalog = catalogA;
215  if (parentFontEngine) {
216    fontEngine = parentFontEngine;
217  } else {
218    if (fontEngine) {
219      delete fontEngine;
220    }
221    fontEngine = new CairoFontEngine(ft_lib);
222    fontEngine_owner = gTrue;
223  }
224}
225
226void CairoOutputDev::startPage(int pageNum, GfxState *state) {
227  /* set up some per page defaults */
228  cairo_pattern_destroy(fill_pattern);
229  cairo_pattern_destroy(stroke_pattern);
230
231  fill_pattern = cairo_pattern_create_rgb(0., 0., 0.);
232  stroke_pattern = cairo_pattern_reference(fill_pattern);
233
234  if (text)
235    text->startPage(state);
236}
237
238void CairoOutputDev::endPage() {
239  if (text) {
240    text->endPage();
241    text->coalesce(gTrue, gFalse);
242  }
243}
244
245void CairoOutputDev::drawLink(Link *link, Catalog *catalog) {
246}
247
248void CairoOutputDev::saveState(GfxState *state) {
249  LOG(printf ("save\n"));
250  cairo_save (cairo);
251  if (cairo_shape)
252      cairo_save (cairo_shape);
253
254  MaskStack *ms = new MaskStack;
255  ms->mask = cairo_pattern_reference(mask);
256  ms->next = maskStack;
257  maskStack = ms;
258}
259
260void CairoOutputDev::restoreState(GfxState *state) {
261  LOG(printf ("restore\n"));
262  cairo_restore (cairo);
263  if (cairo_shape)
264      cairo_restore (cairo_shape);
265
266  /* These aren't restored by cairo_restore() since we keep them in
267   * the output device. */
268  updateFillColor(state);
269  updateStrokeColor(state);
270  updateFillOpacity(state);
271  updateStrokeOpacity(state);
272  updateBlendMode(state);
273
274  MaskStack* ms = maskStack;
275  if (ms) {
276    if (mask)
277      cairo_pattern_destroy(mask);
278    mask = ms->mask;
279    maskStack = ms->next;
280    delete ms;
281  }
282}
283
284void CairoOutputDev::updateAll(GfxState *state) {
285  updateLineDash(state);
286  updateLineJoin(state);
287  updateLineCap(state);
288  updateLineWidth(state);
289  updateFlatness(state);
290  updateMiterLimit(state);
291  updateFillColor(state);
292  updateStrokeColor(state);
293  updateFillOpacity(state);
294  updateStrokeOpacity(state);
295  updateBlendMode(state);
296  needFontUpdate = gTrue;
297  if (text)
298    text->updateFont(state);
299}
300
301void CairoOutputDev::setDefaultCTM(double *ctm) {
302  cairo_matrix_t matrix;
303  matrix.xx = ctm[0];
304  matrix.yx = ctm[1];
305  matrix.xy = ctm[2];
306  matrix.yy = ctm[3];
307  matrix.x0 = ctm[4];
308  matrix.y0 = ctm[5];
309
310  cairo_transform (cairo, &matrix);
311  if (cairo_shape)
312      cairo_transform (cairo_shape, &matrix);
313
314  OutputDev::setDefaultCTM(ctm);
315}
316
317void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
318                                double m21, double m22,
319                                double m31, double m32) {
320  cairo_matrix_t matrix, invert_matrix;
321  matrix.xx = m11;
322  matrix.yx = m12;
323  matrix.xy = m21;
324  matrix.yy = m22;
325  matrix.x0 = m31;
326  matrix.y0 = m32;
327
328  /* Make sure the matrix is invertible before setting it.
329   * cairo will blow up if we give it a matrix that's not
330   * invertible, so we need to check before passing it
331   * to cairo_transform. Ignoring it is likely to give better
332   * results than not rendering anything at all. See #14398
333   *
334   * Ideally, we could do the cairo_transform
335   * and then check if anything went wrong and fix it then
336   * instead of having to invert the matrix. */
337  invert_matrix = matrix;
338  if (cairo_matrix_invert(&invert_matrix)) {
339    warning("matrix not invertible\n");
340    return;
341  }
342
343  cairo_transform (cairo, &matrix);
344  if (cairo_shape)
345    cairo_transform (cairo_shape, &matrix);
346  updateLineDash(state);
347  updateLineJoin(state);
348  updateLineCap(state);
349  updateLineWidth(state);
350}
351
352void CairoOutputDev::updateLineDash(GfxState *state) {
353  double *dashPattern;
354  int dashLength;
355  double dashStart;
356
357  state->getLineDash(&dashPattern, &dashLength, &dashStart);
358  cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
359  if (cairo_shape)
360    cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart);
361}
362
363void CairoOutputDev::updateFlatness(GfxState *state) {
364  // cairo_set_tolerance (cairo, state->getFlatness());
365}
366
367void CairoOutputDev::updateLineJoin(GfxState *state) {
368  switch (state->getLineJoin()) {
369  case 0:
370    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
371    break;
372  case 1:
373    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND);
374    break;
375  case 2:
376    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL);
377    break;
378  }
379  if (cairo_shape)
380    cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo));
381}
382
383void CairoOutputDev::updateLineCap(GfxState *state) {
384  switch (state->getLineCap()) {
385  case 0:
386    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
387    break;
388  case 1:
389    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND);
390    break;
391  case 2:
392    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE);
393    break;
394  }
395  if (cairo_shape)
396    cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo));
397}
398
399void CairoOutputDev::updateMiterLimit(GfxState *state) {
400  cairo_set_miter_limit (cairo, state->getMiterLimit());
401  if (cairo_shape)
402    cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
403}
404
405#define MIN(a,b) (((a) < (b)) ? (a) : (b))
406
407void CairoOutputDev::updateLineWidth(GfxState *state) {
408  LOG(printf ("line width: %f\n", state->getLineWidth()));
409  if (state->getLineWidth() == 0.0) {
410    /* find out how big pixels (device unit) are in the x and y directions
411     * choose the smaller of the two as our line width */
412    double x = 1.0, y = 1.0;
413    cairo_device_to_user_distance(cairo, &x, &y);
414    cairo_set_line_width (cairo, MIN(fabs(x),fabs(y)));
415  } else {
416    cairo_set_line_width (cairo, state->getLineWidth());
417  }
418  if (cairo_shape)
419    cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo));
420}
421
422void CairoOutputDev::updateFillColor(GfxState *state) {
423  GfxRGB color = fill_color;
424
425  state->getFillRGB(&fill_color);
426  if (color.r != fill_color.r ||
427      color.g != fill_color.g ||
428      color.b != fill_color.b)
429  {
430    cairo_pattern_destroy(fill_pattern);
431    fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
432                                             colToDbl(fill_color.g),
433                                             colToDbl(fill_color.b),
434                                             fill_opacity);
435
436    LOG(printf ("fill color: %d %d %d\n",
437                fill_color.r, fill_color.g, fill_color.b));
438  }
439}
440
441void CairoOutputDev::updateStrokeColor(GfxState *state) {
442  GfxRGB color = stroke_color;
443
444  state->getStrokeRGB(&stroke_color);
445  if (color.r != stroke_color.r ||
446      color.g != stroke_color.g ||
447      color.b != stroke_color.b)
448  {
449    cairo_pattern_destroy(stroke_pattern);
450    stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
451                                               colToDbl(stroke_color.g),
452                                               colToDbl(stroke_color.b),
453                                               stroke_opacity);
454
455    LOG(printf ("stroke color: %d %d %d\n",
456                stroke_color.r, stroke_color.g, stroke_color.b));
457  }
458}
459
460void CairoOutputDev::updateFillOpacity(GfxState *state) {
461  double opacity = fill_opacity;
462
463  fill_opacity = state->getFillOpacity();
464  if (opacity != fill_opacity) {
465    cairo_pattern_destroy(fill_pattern);
466    fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r),
467                                             colToDbl(fill_color.g),
468                                             colToDbl(fill_color.b),
469                                             fill_opacity);
470
471    LOG(printf ("fill opacity: %f\n", fill_opacity));
472  }
473}
474
475void CairoOutputDev::updateStrokeOpacity(GfxState *state) {
476  double opacity = stroke_opacity;
477
478  stroke_opacity = state->getStrokeOpacity();
479  if (opacity != stroke_opacity) {
480    cairo_pattern_destroy(stroke_pattern);
481    stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r),
482                                               colToDbl(stroke_color.g),
483                                               colToDbl(stroke_color.b),
484                                               stroke_opacity);
485
486    LOG(printf ("stroke opacity: %f\n", stroke_opacity));
487  }
488}
489
490void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) {
491  state->getFillRGB(&fill_color);
492
493  cairo_pattern_add_color_stop_rgba(fill_pattern, offset,
494                                    colToDbl(fill_color.r),
495                                    colToDbl(fill_color.g),
496                                    colToDbl(fill_color.b),
497                                    fill_opacity);
498  LOG(printf ("fill color stop: %f (%d, %d, %d)\n",
499              offset, fill_color.r, fill_color.g, fill_color.b));
500}
501
502void CairoOutputDev::updateBlendMode(GfxState *state) {
503#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 4)
504  switch (state->getBlendMode()) {
505  default:
506  case gfxBlendNormal:
507    cairo_set_operator (cairo, CAIRO_OPERATOR_OVER);
508    break;
509  case gfxBlendMultiply:
510    cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY);
511    break;
512  case gfxBlendScreen:
513    cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN);
514    break;
515  case gfxBlendOverlay:
516    cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY);
517    break;
518  case gfxBlendDarken:
519    cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN);
520    break;
521  case gfxBlendLighten:
522    cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN);
523    break;
524  case gfxBlendColorDodge:
525    cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE);
526    break;
527  case gfxBlendColorBurn:
528    cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN);
529    break;
530  case gfxBlendHardLight:
531    cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT);
532    break;
533  case gfxBlendSoftLight:
534    cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT);
535    break;
536  case gfxBlendDifference:
537    cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE);
538    break;
539  case gfxBlendExclusion:
540    cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION);
541    break;
542  case gfxBlendHue:
543    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE);
544    break;
545  case gfxBlendSaturation:
546    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION);
547    break;
548  case gfxBlendColor:
549    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR);
550    break;
551  case gfxBlendLuminosity:
552    cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY);
553    break;
554  }
555  LOG(printf ("blend mode: %d\n", (int)state->getBlendMode()));
556#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 4) */
557}
558
559void CairoOutputDev::updateFont(GfxState *state) {
560  cairo_font_face_t *font_face;
561  cairo_matrix_t matrix, invert_matrix;
562
563  LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString()));
564
565  needFontUpdate = gFalse;
566
567  //FIXME: use cairo font engine?
568  if (text)
569    text->updateFont(state);
570 
571  currentFont = fontEngine->getFont (state->getFont(), xref, catalog, printing);
572
573  if (!currentFont)
574    return;
575
576  font_face = currentFont->getFontFace();
577  cairo_set_font_face (cairo, font_face);
578 
579  double fontSize = state->getFontSize();
580  double *m = state->getTextMat();
581  /* NOTE: adjusting by a constant is hack. The correct solution
582   * is probably to use user-fonts and compute the scale on a per
583   * glyph basis instead of for the entire font */
584  double w = currentFont->getSubstitutionCorrection(state->getFont());
585  matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
586  matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
587  matrix.xy = -m[2] * fontSize;
588  matrix.yy = -m[3] * fontSize;
589  matrix.x0 = 0;
590  matrix.y0 = 0;
591
592  LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy));
593
594 /* Make sure the font matrix is invertible before setting it.  cairo
595  * will blow up if we give it a matrix that's not invertible, so we
596  * need to check before passing it to cairo_set_font_matrix. Ignoring it
597  * is likely to give better results than not rendering anything at
598  * all. See #18254.
599  */
600  invert_matrix = matrix;
601  if (cairo_matrix_invert(&invert_matrix)) {
602    warning("font matrix not invertible\n");
603    return;
604  }
605
606  cairo_set_font_matrix (cairo, &matrix);
607}
608
609void CairoOutputDev::updateRender(GfxState *state) {
610  int rm;
611  rm = state->getRender();
612  if (rm == 7 && haveCSPattern) {
613    haveCSPattern = gFalse;
614    restoreState(state);
615  }
616}
617
618void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) {
619  GfxSubpath *subpath;
620  int i, j;
621  cairo_new_path (cairo);
622  for (i = 0; i < path->getNumSubpaths(); ++i) {
623    subpath = path->getSubpath(i);
624    if (subpath->getNumPoints() > 0) {
625      cairo_move_to (cairo, subpath->getX(0), subpath->getY(0));
626         j = 1;
627      while (j < subpath->getNumPoints()) {
628        if (subpath->getCurve(j)) {
629          cairo_curve_to( cairo,
630                          subpath->getX(j), subpath->getY(j),
631                          subpath->getX(j+1), subpath->getY(j+1),
632                          subpath->getX(j+2), subpath->getY(j+2));
633
634          j += 3;
635        } else {
636          cairo_line_to (cairo, subpath->getX(j), subpath->getY(j));
637          ++j;
638        }
639      }
640      if (subpath->isClosed()) {
641        LOG (printf ("close\n"));
642        cairo_close_path (cairo);
643      }
644    }
645  }
646}
647
648void CairoOutputDev::stroke(GfxState *state) {
649  if (inType3Char) {
650      GfxGray gray;
651      state->getFillGray(&gray);
652      if (colToDbl(gray) > 0.5)
653          return;
654  }
655
656  doPath (cairo, state, state->getPath());
657  cairo_set_source (cairo, stroke_pattern);
658  LOG(printf ("stroke\n"));
659  cairo_stroke (cairo);
660  if (cairo_shape) {
661    doPath (cairo_shape, state, state->getPath());
662    cairo_stroke (cairo_shape);
663  }
664}
665
666void CairoOutputDev::fill(GfxState *state) {
667  if (inType3Char) {
668      GfxGray gray;
669      state->getFillGray(&gray);
670      if (colToDbl(gray) > 0.5)
671          return;
672  }
673
674  doPath (cairo, state, state->getPath());
675  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
676  cairo_set_source (cairo, fill_pattern);
677  LOG(printf ("fill\n"));
678  //XXX: how do we get the path
679  if (mask) {
680    cairo_clip (cairo);
681    cairo_mask (cairo, mask);
682  } else {
683    cairo_fill (cairo);
684  }
685  if (cairo_shape) {
686    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
687    doPath (cairo_shape, state, state->getPath());
688    cairo_fill (cairo_shape);
689  }
690}
691
692void CairoOutputDev::eoFill(GfxState *state) {
693  doPath (cairo, state, state->getPath());
694  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
695  cairo_set_source (cairo, fill_pattern);
696  LOG(printf ("fill-eo\n"));
697  cairo_fill (cairo);
698
699  if (cairo_shape) {
700    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
701    doPath (cairo_shape, state, state->getPath());
702    cairo_fill (cairo_shape);
703  }
704
705}
706
707GBool CairoOutputDev::tilingPatternFill(GfxState *state, Object *str,
708                                        int paintType, Dict *resDict,
709                                        double *mat, double *bbox,
710                                        int x0, int y0, int x1, int y1,
711                                        double xStep, double yStep)
712{
713  PDFRectangle box;
714  Gfx *gfx;
715  cairo_pattern_t *pattern;
716  cairo_surface_t *surface;
717  cairo_matrix_t matrix;
718  cairo_t *old_cairo;
719  double xMin, yMin, xMax, yMax;
720  double width, height;
721  int surface_width, surface_height;
722
723  width = bbox[2] - bbox[0];
724  height = bbox[3] - bbox[1];
725
726  if (xStep != width || yStep != height)
727    return gFalse;
728  /* TODO: implement the other cases here too */
729
730  surface_width = (int) ceil (width);
731  surface_height = (int) ceil (height);
732
733  surface = cairo_surface_create_similar (cairo_get_target (cairo),
734                                          CAIRO_CONTENT_COLOR_ALPHA,
735                                          surface_width, surface_height);
736  if (cairo_surface_status (surface))
737    return gFalse;
738
739  old_cairo = cairo;
740  cairo = cairo_create (surface);
741  cairo_surface_destroy (surface);
742
743  box.x1 = bbox[0]; box.y1 = bbox[1];
744  box.x2 = bbox[2]; box.y2 = bbox[3];
745  gfx = new Gfx(xref, this, resDict, catalog, &box, NULL);
746  gfx->display(str);
747  delete gfx;
748
749  pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo));
750  cairo_destroy (cairo);
751  cairo = old_cairo;
752  if (cairo_pattern_status (pattern))
753    return gFalse;
754
755  state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
756  cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin);
757
758  cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height);
759  cairo_pattern_set_matrix (pattern, &matrix);
760
761  cairo_matrix_init (&matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
762  cairo_transform (cairo, &matrix);
763  cairo_set_source (cairo, pattern);
764  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
765  cairo_fill (cairo);
766
767  cairo_pattern_destroy (pattern);
768
769  return gTrue;
770}
771
772GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
773  double x0, y0, x1, y1;
774  double dx, dy;
775
776  shading->getCoords(&x0, &y0, &x1, &y1);
777  dx = x1 - x0;
778  dy = y1 - y0;
779
780  cairo_pattern_destroy(fill_pattern);
781  fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy,
782                                              x0 + tMax * dx, y0 + tMax * dy);
783  if (!shading->getExtend0() && !shading->getExtend1())
784    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
785  else
786    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
787
788  LOG (printf ("axial-sh\n"));
789
790  // TODO: use the actual stops in the shading in the case
791  // of linear interpolation (Type 2 Exponential functions with N=1)
792  return gFalse;
793}
794
795GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading)
796{
797  return (shading->getExtend0() == shading->getExtend1());
798}
799
800GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) {
801  double x0, y0, r0, x1, y1, r1;
802  double dx, dy, dr;
803
804  shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
805  dx = x1 - x0;
806  dy = y1 - y0;
807  dr = r1 - r0;
808  cairo_pattern_destroy(fill_pattern);
809  fill_pattern = cairo_pattern_create_radial (x0 + sMin * dx,
810                                              y0 + sMin * dy,
811                                              r0 + sMin * dr,
812                                              x0 + sMax * dx,
813                                              y0 + sMax * dy,
814                                              r0 + sMax * dr);
815  if (shading->getExtend0() && shading->getExtend1())
816    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD);
817  else
818    cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE);
819
820  LOG (printf ("radial-sh\n"));
821
822  return gFalse;
823}
824
825GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading)
826{
827  return (shading->getExtend0() == shading->getExtend1());
828}
829
830void CairoOutputDev::clip(GfxState *state) {
831  doPath (cairo, state, state->getPath());
832  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
833  cairo_clip (cairo);
834  LOG (printf ("clip\n"));
835  if (cairo_shape) {
836    doPath (cairo_shape, state, state->getPath());
837    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
838    cairo_clip (cairo_shape);
839  }
840}
841
842void CairoOutputDev::eoClip(GfxState *state) {
843  doPath (cairo, state, state->getPath());
844  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
845  cairo_clip (cairo);
846  LOG (printf ("clip-eo\n"));
847  if (cairo_shape) {
848    doPath (cairo_shape, state, state->getPath());
849    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
850    cairo_clip (cairo_shape);
851  }
852
853}
854
855void CairoOutputDev::clipToStrokePath(GfxState *state) {
856  LOG(printf("clip-to-stroke-path\n"));
857}
858
859void CairoOutputDev::beginString(GfxState *state, GooString *s)
860{
861  int len = s->getLength();
862
863  if (needFontUpdate)
864    updateFont(state);
865
866  if (!currentFont)
867    return;
868
869  glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t));
870  glyphCount = 0;
871}
872
873void CairoOutputDev::drawChar(GfxState *state, double x, double y,
874                              double dx, double dy,
875                              double originX, double originY,
876                              CharCode code, int nBytes, Unicode *u, int uLen)
877{
878  if (currentFont) {
879    glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen);
880    glyphs[glyphCount].x = x - originX;
881    glyphs[glyphCount].y = y - originY;
882    glyphCount++;
883  }
884
885  if (!text)
886    return;
887  actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen);
888}
889
890void CairoOutputDev::endString(GfxState *state)
891{
892  int render;
893
894  if (!currentFont)
895    return;
896
897  // endString can be called without a corresponding beginString. If this
898  // happens glyphs will be null so don't draw anything, just return.
899  // XXX: OutputDevs should probably not have to deal with this...
900  if (!glyphs)
901    return;
902
903  // ignore empty strings and invisible text -- this is used by
904  // Acrobat Capture
905  render = state->getRender();
906  if (render == 3 || glyphCount == 0) {
907    gfree(glyphs);
908    glyphs = NULL;
909    return;
910  }
911 
912  if (!(render & 1)) {
913    LOG (printf ("fill string\n"));
914    cairo_set_source (cairo, fill_pattern);
915    cairo_show_glyphs (cairo, glyphs, glyphCount);
916    if (cairo_shape)
917      cairo_show_glyphs (cairo_shape, glyphs, glyphCount);
918  }
919 
920  // stroke
921  if ((render & 3) == 1 || (render & 3) == 2) {
922    LOG (printf ("stroke string\n"));
923    cairo_set_source (cairo, stroke_pattern);
924    cairo_glyph_path (cairo, glyphs, glyphCount);
925    cairo_stroke (cairo);
926    if (cairo_shape) {
927      cairo_glyph_path (cairo_shape, glyphs, glyphCount);
928      cairo_stroke (cairo_shape);
929    }
930  }
931
932  // clip
933  if (render & 4) {
934    LOG (printf ("clip string\n"));
935    // append the glyph path to textClipPath.
936
937    // set textClipPath as the currentPath
938    if (textClipPath) {
939      cairo_append_path (cairo, textClipPath);
940      if (cairo_shape) {
941        cairo_append_path (cairo_shape, textClipPath);
942      }
943      cairo_path_destroy (textClipPath);
944    }
945   
946    // append the glyph path
947    cairo_glyph_path (cairo, glyphs, glyphCount);
948   
949    // move the path back into textClipPath
950    // and clear the current path
951    textClipPath = cairo_copy_path (cairo);
952    cairo_new_path (cairo);
953    if (cairo_shape) {
954      cairo_new_path (cairo_shape);
955    }
956  }
957
958  gfree (glyphs);
959  glyphs = NULL;
960}
961
962
963GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y,
964                                      double dx, double dy,
965                                      CharCode code, Unicode *u, int uLen) {
966
967  cairo_save (cairo);
968  double *ctm;
969  cairo_matrix_t matrix;
970
971  ctm = state->getCTM();
972  matrix.xx = ctm[0];
973  matrix.yx = ctm[1];
974  matrix.xy = ctm[2];
975  matrix.yy = ctm[3];
976  matrix.x0 = ctm[4];
977  matrix.y0 = ctm[5];
978  /* Restore the original matrix and then transform to matrix needed for the
979   * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/
980  cairo_set_matrix(cairo, &orig_matrix);
981  cairo_transform(cairo, &matrix);
982  if (cairo_shape) {
983    cairo_save (cairo_shape);
984    cairo_set_matrix(cairo_shape, &orig_matrix);
985    cairo_transform(cairo_shape, &matrix);
986  }
987  cairo_pattern_destroy(stroke_pattern);
988  cairo_pattern_reference(fill_pattern);
989  stroke_pattern = fill_pattern;
990  return gFalse;
991}
992
993void CairoOutputDev::endType3Char(GfxState *state) {
994  cairo_restore (cairo);
995  if (cairo_shape) {
996    cairo_restore (cairo_shape);
997  }
998}
999
1000void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) {
1001  t3_glyph_wx = wx;
1002  t3_glyph_wy = wy;
1003}
1004
1005void CairoOutputDev::type3D1(GfxState *state, double wx, double wy,
1006                             double llx, double lly, double urx, double ury) {
1007  t3_glyph_wx = wx;
1008  t3_glyph_wy = wy;
1009  t3_glyph_bbox[0] = llx;
1010  t3_glyph_bbox[1] = lly;
1011  t3_glyph_bbox[2] = urx;
1012  t3_glyph_bbox[3] = ury;
1013  t3_glyph_has_bbox = gTrue;
1014}
1015
1016void CairoOutputDev::beginTextObject(GfxState *state) {
1017  if (state->getFillColorSpace()->getMode() == csPattern) {
1018    haveCSPattern = gTrue;
1019    saveState(state);
1020    savedRender = state->getRender();
1021    state->setRender(7); // Set clip to text path
1022  }
1023}
1024
1025void CairoOutputDev::endTextObject(GfxState *state) {
1026  if (haveCSPattern) {
1027    state->setRender(savedRender);
1028    haveCSPattern = gFalse;
1029    if (state->getFillColorSpace()->getMode() != csPattern) {
1030      if (textClipPath) {
1031        cairo_new_path (cairo);
1032        cairo_append_path (cairo, textClipPath);
1033        cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
1034        cairo_set_source (cairo, fill_pattern);
1035        cairo_fill (cairo);
1036        if (cairo_shape) {
1037          cairo_new_path (cairo_shape);
1038          cairo_append_path (cairo_shape, textClipPath);
1039          cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
1040          cairo_fill (cairo_shape);
1041        }
1042        cairo_path_destroy (textClipPath);
1043        textClipPath = NULL;
1044      }
1045      restoreState(state);
1046      updateFillColor(state);
1047    }
1048  }
1049
1050  if (textClipPath) {
1051    // clip the accumulated text path
1052    cairo_append_path (cairo, textClipPath);
1053    cairo_clip (cairo);
1054    if (cairo_shape) {
1055      cairo_append_path (cairo_shape, textClipPath);
1056      cairo_clip (cairo_shape);
1057    }
1058    cairo_path_destroy (textClipPath);
1059    textClipPath = NULL;
1060  }
1061}
1062
1063void CairoOutputDev::beginMarkedContent(char *name, Dict *properties)
1064{
1065  if (text)
1066    actualText->beginMC(properties);
1067}
1068
1069void CairoOutputDev::endMarkedContent(GfxState *state)
1070{
1071  if (text)
1072    actualText->endMC(state);
1073}
1074
1075static inline int splashRound(SplashCoord x) {
1076  return (int)floor(x + 0.5);
1077}
1078
1079static inline int splashCeil(SplashCoord x) {
1080  return (int)ceil(x);
1081}
1082
1083static inline int splashFloor(SplashCoord x) {
1084  return (int)floor(x);
1085}
1086
1087static
1088cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content)
1089{
1090  double x1, y1, x2, y2;
1091  int width, height;
1092  cairo_clip_extents (cairo, &x1, &y1, &x2, &y2);
1093  cairo_matrix_t matrix;
1094  cairo_get_matrix (cairo, &matrix);
1095  //cairo_matrix_transform_point(&matrix, &x1, &y1);
1096  //cairo_matrix_transform_point(&matrix, &x2, &y2);*/
1097  cairo_user_to_device(cairo, &x1, &y1);
1098  cairo_user_to_device(cairo, &x2, &y2);
1099  width = splashCeil(x2) - splashFloor(x1);
1100  //XXX: negative matrix
1101  ////height = splashCeil(y2) - splashFloor(y1);
1102  height = splashFloor(y1) - splashCeil(y2);
1103  cairo_surface_t *target = cairo_get_target (cairo);
1104  cairo_surface_t *result;
1105
1106  result = cairo_surface_create_similar (target, content, width, height);
1107  double x_offset, y_offset;
1108    cairo_surface_get_device_offset(target, &x_offset, &y_offset);
1109    cairo_surface_set_device_offset(result, x_offset, y_offset);
1110
1111 
1112  return result;
1113}
1114
1115
1116
1117void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/,
1118                                      GfxColorSpace * blendingColorSpace,
1119                                      GBool /*isolated*/, GBool knockout,
1120                                      GBool forSoftMask) {
1121  /* push color space */
1122  ColorSpaceStack* css = new ColorSpaceStack;
1123  css->cs = blendingColorSpace;
1124  css->knockout = knockout;
1125  css->next = groupColorSpaceStack;
1126  groupColorSpaceStack = css;
1127
1128  LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no"));
1129
1130  if (knockout) {
1131    knockoutCount++;
1132    if (!cairo_shape) {
1133      /* create a surface for tracking the shape */
1134      cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA);
1135      cairo_shape = cairo_create (cairo_shape_surface);
1136      cairo_surface_destroy (cairo_shape_surface);
1137
1138      /* the color doesn't matter as long as it is opaque */
1139      cairo_set_source_rgb (cairo_shape, 0, 0, 0);
1140      cairo_matrix_t matrix;
1141      cairo_get_matrix (cairo, &matrix);
1142      //printMatrix(&matrix);
1143      cairo_set_matrix (cairo_shape, &matrix);
1144    } else {
1145      cairo_reference (cairo_shape);
1146    }
1147  }
1148  if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1149    /* we need to track the shape */
1150    cairo_push_group (cairo_shape);
1151  }
1152  if (0 && forSoftMask)
1153    cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
1154  else
1155    cairo_push_group (cairo);
1156
1157  /* push_group has an implicit cairo_save() */
1158  if (knockout) {
1159    /*XXX: let's hope this matches the semantics needed */
1160    cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
1161  } else {
1162    cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
1163  }
1164}
1165
1166void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) {
1167  if (group)
1168    cairo_pattern_destroy(group);
1169  group = cairo_pop_group (cairo);
1170
1171  LOG(printf ("end transparency group\n"));
1172
1173  if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
1174    if (shape)
1175      cairo_pattern_destroy(shape);
1176    shape = cairo_pop_group (cairo_shape);
1177  }
1178}
1179
1180void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
1181  cairo_set_source (cairo, group);
1182
1183  LOG(printf ("paint transparency group\n"));
1184
1185  if (!mask) {
1186    //XXX: deal with mask && shape case
1187    if (shape) {
1188      cairo_save (cairo);
1189
1190      /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask)
1191       * however our source has already been clipped to mask so we only need to
1192       * do ADD and OUT */
1193
1194      /* clear the shape mask */
1195      cairo_set_source (cairo, shape);
1196      cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT);
1197      cairo_paint (cairo);
1198
1199      cairo_set_operator (cairo, CAIRO_OPERATOR_ADD);
1200      cairo_set_source (cairo, group);
1201      cairo_paint (cairo);
1202
1203      cairo_restore (cairo);
1204
1205      cairo_pattern_destroy (shape);
1206      shape = NULL;
1207    } else {
1208      cairo_paint_with_alpha (cairo, fill_opacity);
1209    }
1210    cairo_status_t status = cairo_status(cairo);
1211    if (status)
1212      printf("BAD status: %s\n", cairo_status_to_string(status));
1213  } else {
1214    cairo_mask(cairo, mask);
1215
1216    cairo_pattern_destroy(mask);
1217    mask = NULL;
1218  }
1219
1220  popTransparencyGroup();
1221}
1222
1223typedef unsigned int uint32_t;
1224
1225static uint32_t luminocity(uint32_t x)
1226{
1227  int r = (x >> 16) & 0xff;
1228  int g = (x >>  8) & 0xff;
1229  int b = (x >>  0) & 0xff;
1230  // an arbitrary integer approximation of .3*r + .59*g + .11*b
1231  int y = (r*19661+g*38666+b*7209 + 32829)>>16;
1232  return y << 24;
1233}
1234
1235
1236/* XXX: do we need to deal with shape here? */
1237void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
1238                                 Function * transferFunc, GfxColor * backdropColor) {
1239  cairo_pattern_destroy(mask);
1240
1241  LOG(printf ("set softMask\n"));
1242
1243  if (alpha == false) {
1244    /* We need to mask according to the luminocity of the group.
1245     * So we paint the group to an image surface convert it to a luminocity map
1246     * and then use that as the mask. */
1247
1248    double x1, y1, x2, y2, tmp;
1249    cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
1250    cairo_user_to_device(cairo, &x1, &y1);
1251    cairo_user_to_device(cairo, &x2, &y2);
1252    if (x1 > x2) {
1253      tmp = x1;
1254      x1 = x2;
1255      x2 = tmp;
1256    }
1257
1258    if (y1 > y2) {
1259      tmp = y1;
1260      y1 = y2;
1261      y2 = tmp;
1262    }
1263
1264    int width = (int)(ceil(x2) - floor(x1));
1265    int height = (int)(ceil(y2) - floor(y1));
1266
1267    cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1268    cairo_t *maskCtx = cairo_create(source);
1269
1270    //XXX: hopefully this uses the correct color space */
1271    GfxRGB backdropColorRGB;
1272    groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB);
1273    /* paint the backdrop */
1274    cairo_set_source_rgb(maskCtx,
1275                         colToDbl(backdropColorRGB.r),
1276                         colToDbl(backdropColorRGB.g),
1277                         colToDbl(backdropColorRGB.b));
1278    cairo_paint(maskCtx);
1279
1280    cairo_matrix_t mat;
1281    cairo_get_matrix(cairo, &mat);
1282    cairo_set_matrix(maskCtx, &mat);
1283
1284    /* make the device offset of the new mask match that of the group */
1285    double x_offset, y_offset;
1286    if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1287      cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset);
1288    } else {
1289      cairo_surface_t *pats;
1290      cairo_pattern_get_surface(group, &pats);
1291      cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
1292    }
1293    cairo_surface_set_device_offset(source, x_offset, y_offset);
1294
1295    /* paint the group */
1296    cairo_set_source(maskCtx, group);
1297    cairo_paint(maskCtx);
1298
1299    /* XXX status = cairo_status(maskCtx); */
1300    cairo_destroy(maskCtx);
1301
1302    /* convert to a luminocity map */
1303    uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source);
1304    /* get stride in units of 32 bits */
1305    int stride = cairo_image_surface_get_stride(source)/4;
1306    for (int y=0; y<height; y++) {
1307      for (int x=0; x<width; x++) {
1308        source_data[y*stride + x] = luminocity(source_data[y*stride + x]);
1309
1310#if 0
1311        here is how splash deals with the transferfunction we should deal with this
1312          at some point
1313        if (transferFunc) {
1314          transferFunc->transform(&lum, &lum2);
1315        } else {
1316          lum2 = lum;
1317        }
1318        p[x] = (int)(lum2 * 255.0 + 0.5);
1319#endif
1320
1321      }
1322    }
1323    cairo_surface_mark_dirty (source);
1324
1325    /* setup the new mask pattern */
1326    mask = cairo_pattern_create_for_surface(source);
1327
1328    if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) {
1329      cairo_pattern_set_matrix(mask, &mat);
1330    } else {
1331      cairo_matrix_t patMatrix;
1332      cairo_pattern_get_matrix(group, &patMatrix);
1333      cairo_pattern_set_matrix(mask, &patMatrix);
1334    }
1335
1336    cairo_surface_destroy(source);
1337  } else {
1338    mask = cairo_pattern_reference(group);
1339  }
1340
1341  popTransparencyGroup();
1342}
1343
1344void CairoOutputDev::popTransparencyGroup() {
1345  /* pop color space */
1346  ColorSpaceStack *css = groupColorSpaceStack;
1347  if (css->knockout) {
1348    knockoutCount--;
1349    if (!knockoutCount) {
1350      /* we don't need to track the shape anymore because
1351       * we are not above any knockout groups */
1352      cairo_destroy(cairo_shape);
1353      cairo_shape = NULL;
1354    }
1355  }
1356  groupColorSpaceStack = css->next;
1357  delete css;
1358}
1359
1360
1361void CairoOutputDev::clearSoftMask(GfxState * /*state*/) {
1362  if (mask)
1363    cairo_pattern_destroy(mask);
1364  mask = NULL;
1365}
1366
1367void CairoOutputDev::endMaskClip(GfxState *state) {
1368  clearSoftMask(state);
1369}
1370
1371/* Taken from cairo/doc/tutorial/src/singular.c */
1372static void
1373get_singular_values (const cairo_matrix_t *matrix,
1374                     double               *major,
1375                     double               *minor)
1376{
1377        double xx = matrix->xx, xy = matrix->xy;
1378        double yx = matrix->yx, yy = matrix->yy;
1379
1380        double a = xx*xx+yx*yx;
1381        double b = xy*xy+yy*yy;
1382        double k = xx*xy+yx*yy;
1383
1384        double f = (a+b) * .5;
1385        double g = (a-b) * .5;
1386        double delta = sqrt (g*g + k*k);
1387
1388        if (major)
1389                *major = sqrt (f + delta);
1390        if (minor)
1391                *minor = sqrt (f - delta);
1392}
1393
1394void CairoOutputDev::getScaledSize(int  orig_width,
1395                                   int  orig_height,
1396                                   int *scaledWidth,
1397                                   int *scaledHeight) {
1398  cairo_matrix_t matrix;
1399  cairo_get_matrix(cairo, &matrix);
1400
1401  double xScale;
1402  double yScale;
1403  if (orig_width > orig_height)
1404    get_singular_values (&matrix, &xScale, &yScale);
1405  else
1406    get_singular_values (&matrix, &yScale, &xScale);
1407
1408  int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
1409  if (xScale >= 0) {
1410    tx = splashRound(matrix.x0 - 0.01);
1411    tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1;
1412  } else {
1413    tx = splashRound(matrix.x0 + 0.01) - 1;
1414    tx2 = splashRound(matrix.x0 + xScale - 0.01);
1415  }
1416  *scaledWidth = abs(tx2 - tx) + 1;
1417  //scaledWidth = splashRound(fabs(xScale));
1418  if (*scaledWidth == 0) {
1419    // technically, this should draw nothing, but it generally seems
1420    // better to draw a one-pixel-wide stripe rather than throwing it
1421    // away
1422    *scaledWidth = 1;
1423  }
1424  if (yScale >= 0) {
1425    ty = splashFloor(matrix.y0 + 0.01);
1426    ty2 = splashCeil(matrix.y0 + yScale - 0.01);
1427  } else {
1428    ty = splashCeil(matrix.y0 - 0.01);
1429    ty2 = splashFloor(matrix.y0 + yScale + 0.01);
1430  }
1431  *scaledHeight = abs(ty2 - ty);
1432  if (*scaledHeight == 0) {
1433    *scaledHeight = 1;
1434  }
1435}
1436
1437cairo_surface_t *CairoOutputDev::downscaleSurface(cairo_surface_t *orig_surface) {
1438  cairo_surface_t *dest_surface;
1439  unsigned char *dest_buffer;
1440  int dest_stride;
1441  unsigned char *orig_buffer;
1442  int orig_width, orig_height;
1443  int orig_stride;
1444  int scaledHeight;
1445  int scaledWidth;
1446  GBool res;
1447
1448  if (printing)
1449    return NULL;
1450
1451  orig_width = cairo_image_surface_get_width (orig_surface);
1452  orig_height = cairo_image_surface_get_height (orig_surface);
1453  getScaledSize (orig_width, orig_height, &scaledWidth, &scaledHeight);
1454  if (scaledWidth >= orig_width || scaledHeight >= orig_height)
1455    return NULL;
1456
1457  dest_surface = cairo_surface_create_similar (orig_surface,
1458                                               cairo_surface_get_content (orig_surface),
1459                                               scaledWidth, scaledHeight);
1460  dest_buffer = cairo_image_surface_get_data (dest_surface);
1461  dest_stride = cairo_image_surface_get_stride (dest_surface);
1462
1463  orig_buffer = cairo_image_surface_get_data (orig_surface);
1464  orig_stride = cairo_image_surface_get_stride (orig_surface);
1465
1466  res = downscale_box_filter((uint32_t *)orig_buffer,
1467                             orig_stride, orig_width, orig_height,
1468                             scaledWidth, scaledHeight, 0, 0,
1469                             scaledWidth, scaledHeight,
1470                             (uint32_t *)dest_buffer, dest_stride);
1471  if (!res) {
1472    cairo_surface_destroy (dest_surface);
1473    return NULL;
1474  }
1475
1476  return dest_surface;
1477
1478}
1479
1480cairo_filter_t
1481CairoOutputDev::getFilterForSurface(cairo_surface_t *image,
1482                                    GBool interpolate)
1483{
1484  if (interpolate)
1485    return CAIRO_FILTER_BILINEAR;
1486
1487  int orig_width = cairo_image_surface_get_width (image);
1488  int orig_height = cairo_image_surface_get_height (image);
1489  if (orig_width == 0 || orig_height == 0)
1490          return CAIRO_FILTER_NEAREST;
1491
1492  int scaled_width, scaled_height;
1493  getScaledSize (orig_width, orig_height, &scaled_width, &scaled_height);
1494
1495  /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
1496  if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4)
1497          return CAIRO_FILTER_NEAREST;
1498
1499  return CAIRO_FILTER_BILINEAR;
1500}
1501
1502void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1503                                   int width, int height, GBool invert,
1504                                   GBool interpolate, GBool inlineImg) {
1505
1506  /* FIXME: Doesn't the image mask support any colorspace? */
1507  cairo_set_source (cairo, fill_pattern);
1508
1509  /* work around a cairo bug when scaling 1x1 surfaces */
1510  if (width == 1 && height == 1) {
1511    ImageStream *imgStr;
1512    Guchar pix;
1513    int invert_bit;
1514
1515    imgStr = new ImageStream(str, width, 1, 1);
1516    imgStr->reset();
1517    imgStr->getPixel(&pix);
1518    imgStr->close();
1519    delete imgStr;
1520
1521    invert_bit = invert ? 1 : 0;
1522    if (pix ^ invert_bit)
1523      return;
1524
1525    cairo_save (cairo);
1526    cairo_rectangle (cairo, 0., 0., width, height);
1527    cairo_fill (cairo);
1528    cairo_restore (cairo);
1529    if (cairo_shape) {
1530      cairo_save (cairo_shape);
1531      cairo_rectangle (cairo_shape, 0., 0., width, height);
1532      cairo_fill (cairo_shape);
1533      cairo_restore (cairo_shape);
1534    }
1535    return;
1536  }
1537
1538  if (state->getFillColorSpace()->getMode() == csPattern)
1539    cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
1540
1541  /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
1542
1543  cairo_matrix_t matrix;
1544  cairo_get_matrix (cairo, &matrix);
1545  //XXX: it is possible that we should only do sub pixel positioning if
1546  // we are rendering fonts */
1547  if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) {
1548    drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg);
1549  } else {
1550    drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg);
1551  }
1552
1553  if (state->getFillColorSpace()->getMode() == csPattern) {
1554    if (mask)
1555      cairo_pattern_destroy (mask);
1556    mask = cairo_pop_group (cairo);
1557  }
1558}
1559
1560void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str,
1561                                          int width, int height, GBool invert,
1562                                          GBool interpolate, GBool inlineImg) {
1563  unsigned char *buffer;
1564  unsigned char *dest;
1565  cairo_surface_t *image;
1566  cairo_pattern_t *pattern;
1567  int x, y;
1568  ImageStream *imgStr;
1569  Guchar *pix;
1570  cairo_matrix_t matrix;
1571  int invert_bit;
1572  int row_stride;
1573  cairo_filter_t filter;
1574
1575  /* TODO: Do we want to cache these? */
1576  imgStr = new ImageStream(str, width, 1, 1);
1577  imgStr->reset();
1578
1579  image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
1580  if (cairo_surface_status (image))
1581    goto cleanup;
1582
1583  buffer = cairo_image_surface_get_data (image);
1584  row_stride = cairo_image_surface_get_stride (image);
1585
1586  invert_bit = invert ? 1 : 0;
1587
1588  for (y = 0; y < height; y++) {
1589    pix = imgStr->getLine();
1590    dest = buffer + y * row_stride;
1591    for (x = 0; x < width; x++) {
1592
1593      if (pix[x] ^ invert_bit)
1594        *dest++ = 0;
1595      else
1596        *dest++ = 255;
1597    }
1598  }
1599
1600  filter = getFilterForSurface (image, interpolate);
1601
1602  cairo_surface_mark_dirty (image);
1603  pattern = cairo_pattern_create_for_surface (image);
1604  cairo_surface_destroy (image);
1605  if (cairo_pattern_status (pattern))
1606    goto cleanup;
1607
1608  LOG (printf ("drawImageMask %dx%d\n", width, height));
1609
1610  cairo_pattern_set_filter (pattern, filter);
1611
1612  if (!printing)
1613    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
1614
1615  cairo_matrix_init_translate (&matrix, 0, height);
1616  cairo_matrix_scale (&matrix, width, -height);
1617  cairo_pattern_set_matrix (pattern, &matrix);
1618  if (cairo_pattern_status (pattern)) {
1619    cairo_pattern_destroy (pattern);
1620    goto cleanup;
1621  }
1622
1623  if (state->getFillColorSpace()->getMode() == csPattern) {
1624    mask = cairo_pattern_reference (pattern);
1625  } else if (!printing) {
1626    cairo_save (cairo);
1627    cairo_rectangle (cairo, 0., 0., 1., 1.);
1628    cairo_clip (cairo);
1629    cairo_mask (cairo, pattern);
1630    cairo_restore (cairo);
1631  } else {
1632    cairo_mask (cairo, pattern);
1633  }
1634
1635  if (cairo_shape) {
1636    cairo_save (cairo_shape);
1637    cairo_set_source (cairo_shape, pattern);
1638    if (!printing) {
1639      cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
1640      cairo_fill (cairo_shape);
1641    } else {
1642      cairo_mask (cairo_shape, pattern);
1643    }
1644    cairo_restore (cairo_shape);
1645  }
1646
1647  cairo_pattern_destroy (pattern);
1648
1649cleanup:
1650  imgStr->close();
1651  delete imgStr;
1652}
1653
1654
1655void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str,
1656                                            int width, int height, GBool invert,
1657                                            GBool interpolate, GBool inlineImg) {
1658  unsigned char *buffer;
1659  cairo_surface_t *image;
1660  cairo_pattern_t *pattern;
1661  ImageStream *imgStr;
1662  Guchar *pix;
1663  cairo_matrix_t matrix;
1664  int invert_bit;
1665  int row_stride;
1666
1667  /* cairo does a very poor job of scaling down images so we scale them ourselves */
1668
1669  /* this scaling code is adopted from the splash image scaling code */
1670  cairo_get_matrix(cairo, &matrix);
1671#if 0
1672  printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
1673#endif
1674  /* this whole computation should be factored out */
1675  double xScale = matrix.xx;
1676  double yScale = matrix.yy;
1677  int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
1678  int scaledHeight;
1679  int scaledWidth;
1680  if (xScale >= 0) {
1681    tx = splashRound(matrix.x0 - 0.01);
1682    tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1;
1683  } else {
1684    tx = splashRound(matrix.x0 + 0.01) - 1;
1685    tx2 = splashRound(matrix.x0 + xScale - 0.01);
1686  }
1687  scaledWidth = abs(tx2 - tx) + 1;
1688  //scaledWidth = splashRound(fabs(xScale));
1689  if (scaledWidth == 0) {
1690    // technically, this should draw nothing, but it generally seems
1691    // better to draw a one-pixel-wide stripe rather than throwing it
1692    // away
1693    scaledWidth = 1;
1694  }
1695  if (yScale >= 0) {
1696    ty = splashFloor(matrix.y0 + 0.01);
1697    ty2 = splashCeil(matrix.y0 + yScale - 0.01);
1698  } else {
1699    ty = splashCeil(matrix.y0 - 0.01);
1700    ty2 = splashFloor(matrix.y0 + yScale + 0.01);
1701  }
1702  scaledHeight = abs(ty2 - ty);
1703  if (scaledHeight == 0) {
1704    scaledHeight = 1;
1705  }
1706#if 0
1707  printf("xscale: %g, yscale: %g\n", xScale, yScale);
1708  printf("width: %d, height: %d\n", width, height);
1709  printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight);
1710#endif
1711
1712  /* compute the required padding */
1713  /* Padding is used to preserve the aspect ratio.
1714     We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */
1715  int head_pad = 0;
1716  int tail_pad = 0;
1717  int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height);
1718
1719  /* compute the two pieces of padding */
1720  if (total_pad > 0) {
1721    //XXX: i'm not positive fabs() is correct
1722    float tail_error = fabs(matrix.y0 - ty);
1723    float head_error = fabs(ty2 - (matrix.y0 + yScale));
1724    float tail_fraction = tail_error/(tail_error + head_error);
1725    tail_pad = splashRound(total_pad*tail_fraction);
1726    head_pad = total_pad - tail_pad;
1727  } else {
1728    tail_pad = 0;
1729    head_pad = 0;
1730  }
1731  int origHeight = height;
1732  height += tail_pad;
1733  height += head_pad;
1734#if 0
1735  printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad);
1736  printf("origHeight: %d height: %d\n", origHeight, height);
1737  printf("ty: %d, ty2: %d\n", ty, ty2);
1738#endif
1739
1740  /* TODO: Do we want to cache these? */
1741  imgStr = new ImageStream(str, width, 1, 1);
1742  imgStr->reset();
1743
1744  invert_bit = invert ? 1 : 0;
1745
1746  image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight);
1747  if (cairo_surface_status (image)) {
1748    imgStr->close();
1749    delete imgStr;
1750    return;
1751  }
1752
1753  buffer = cairo_image_surface_get_data (image);
1754  row_stride = cairo_image_surface_get_stride (image);
1755
1756  int yp = height / scaledHeight;
1757  int yq = height % scaledHeight;
1758  int xp = width / scaledWidth;
1759  int xq = width % scaledWidth;
1760  int yt = 0;
1761  int origHeight_c = origHeight;
1762  /* use MIN() because yp might be > origHeight because of padding */
1763  unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width);
1764  int lastYStep = 1;
1765  int total = 0;
1766  for (int y = 0; y < scaledHeight; y++) {
1767    // y scale Bresenham
1768    int yStep = yp;
1769    yt += yq;
1770
1771    if (yt >= scaledHeight) {
1772      yt -= scaledHeight;
1773      ++yStep;
1774    }
1775
1776    // read row (s) from image ignoring the padding as appropriate
1777    {
1778      int n = (yp > 0) ? yStep : lastYStep;
1779      total += n;
1780      if (n > 0) {
1781        unsigned char *p = pixBuf;
1782        int head_pad_count = head_pad;
1783        int origHeight_count = origHeight;
1784        int tail_pad_count = tail_pad;
1785        for (int i=0; i<n; i++) {
1786          // get row
1787          if (head_pad_count) {
1788            head_pad_count--;
1789          } else if (origHeight_count) {
1790            pix = imgStr->getLine();
1791            for (int j=0; j<width; j++) {
1792              if (pix[j] ^ invert_bit)
1793                p[j] = 0;
1794              else
1795                p[j] = 255;
1796            }
1797            origHeight_count--;
1798            p += width;
1799          } else if (tail_pad_count) {
1800            tail_pad_count--;
1801          } else {
1802            printf("%d %d\n", n, total);
1803            assert(0 && "over run\n");
1804          }
1805        }
1806      }
1807    }
1808
1809    lastYStep = yStep;
1810    int k1 = y;
1811
1812    int xt = 0;
1813    int xSrc = 0;
1814    int x1 = k1;
1815    int n = yStep > 0 ? yStep : 1;
1816    int origN = n;
1817
1818    /* compute the size of padding and pixels that will be used for this row */
1819    int head_pad_size = MIN(n, head_pad);
1820    n -= head_pad_size;
1821    head_pad -= MIN(head_pad_size, yStep);
1822
1823    int pix_size = MIN(n, origHeight);
1824    n -= pix_size;
1825    origHeight -= MIN(pix_size, yStep);
1826
1827    int tail_pad_size = MIN(n, tail_pad);
1828    n -= tail_pad_size;
1829    tail_pad -= MIN(tail_pad_size, yStep);
1830    if (n != 0) {
1831      printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size);
1832      assert(n == 0);
1833    }
1834
1835    for (int x = 0; x < scaledWidth; ++x) {
1836      int xStep = xp;
1837      xt += xq;
1838      if (xt >= scaledWidth) {
1839        xt -= scaledWidth;
1840        ++xStep;
1841      }
1842      int m = xStep > 0 ? xStep : 1;
1843      float pixAcc0 = 0;
1844      /* could m * head_pad_size * tail_pad_size  overflow? */
1845      if (invert_bit) {
1846        pixAcc0 += m * head_pad_size * tail_pad_size * 255;
1847      } else {
1848        pixAcc0 += m * head_pad_size * tail_pad_size * 0;
1849      }
1850      /* Accumulate all of the source pixels for the destination pixel */
1851      for (int i = 0; i < pix_size; ++i) {
1852        for (int j = 0; j< m; ++j) {
1853          if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) {
1854            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);
1855            printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size);
1856            assert(0 && "bad access\n");
1857          }
1858          pixAcc0 += pixBuf[xSrc + i*width + j];
1859        }
1860      }
1861      buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m));
1862      xSrc += xStep;
1863      x1 += 1;
1864    }
1865
1866  }
1867  free(pixBuf);
1868
1869  cairo_surface_mark_dirty (image);
1870  pattern = cairo_pattern_create_for_surface (image);
1871  cairo_surface_destroy (image);
1872  if (cairo_pattern_status (pattern)) {
1873    imgStr->close();
1874    delete imgStr;
1875    return;
1876  }
1877
1878  /* we should actually be using CAIRO_FILTER_NEAREST here. However,
1879   * cairo doesn't yet do minifaction filtering causing scaled down
1880   * images with CAIRO_FILTER_NEAREST to look really bad */
1881  cairo_pattern_set_filter (pattern,
1882                            interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST);
1883  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
1884
1885  if (state->getFillColorSpace()->getMode() == csPattern) {
1886    cairo_matrix_init_translate (&matrix, 0, scaledHeight);
1887    cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight);
1888    cairo_pattern_set_matrix (pattern, &matrix);
1889    if (cairo_pattern_status (pattern)) {
1890      cairo_pattern_destroy (pattern);
1891      imgStr->close();
1892      delete imgStr;
1893      return;
1894    }
1895
1896    mask = cairo_pattern_reference (pattern);
1897  } else {
1898    cairo_save (cairo);
1899
1900    /* modify our current transformation so that the prescaled image
1901     * goes where it is supposed to */
1902    cairo_get_matrix(cairo, &matrix);
1903    cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy);
1904    // get integer co-ords
1905    cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0);
1906    if (yScale > 0)
1907      cairo_scale(cairo, 1, -1);
1908
1909    cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight);
1910    cairo_clip (cairo);
1911    cairo_mask (cairo, pattern);
1912
1913    //cairo_get_matrix(cairo, &matrix);
1914    //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
1915    cairo_restore(cairo);
1916  }
1917
1918  if (cairo_shape) {
1919    cairo_save (cairo_shape);
1920
1921    /* modify our current transformation so that the prescaled image
1922     * goes where it is supposed to */
1923    cairo_get_matrix(cairo_shape, &matrix);
1924    cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy);
1925    // get integer co-ords
1926    cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0);
1927    if (yScale > 0)
1928      cairo_scale(cairo_shape, 1, -1);
1929
1930    cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight);
1931    cairo_fill (cairo_shape);
1932
1933    cairo_restore(cairo_shape);
1934  }
1935
1936  cairo_pattern_destroy (pattern);
1937
1938  imgStr->close();
1939  delete imgStr;
1940}
1941
1942void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref,
1943                                     Stream *str, int width, int height,
1944                                     GfxImageColorMap *colorMap,
1945                                     GBool interpolate,
1946                                     Stream *maskStr, int maskWidth,
1947                                     int maskHeight, GBool maskInvert,
1948                                     GBool maskInterpolate)
1949{
1950  ImageStream *maskImgStr, *imgStr;
1951  int row_stride;
1952  unsigned char *maskBuffer, *buffer;
1953  unsigned char *maskDest;
1954  unsigned int *dest;
1955  cairo_surface_t *maskImage, *image;
1956  cairo_pattern_t *maskPattern, *pattern;
1957  cairo_matrix_t matrix;
1958  cairo_matrix_t maskMatrix;
1959  Guchar *pix;
1960  int x, y;
1961  int invert_bit;
1962  cairo_filter_t filter;
1963  cairo_filter_t maskFilter;
1964
1965  maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1);
1966  maskImgStr->reset();
1967
1968  maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
1969  if (cairo_surface_status (maskImage)) {
1970    maskImgStr->close();
1971    delete maskImgStr;
1972    return;
1973  }
1974
1975  maskBuffer = cairo_image_surface_get_data (maskImage);
1976  row_stride = cairo_image_surface_get_stride (maskImage);
1977
1978  invert_bit = maskInvert ? 1 : 0;
1979
1980  for (y = 0; y < maskHeight; y++) {
1981    pix = maskImgStr->getLine();
1982    maskDest = maskBuffer + y * row_stride;
1983    for (x = 0; x < maskWidth; x++) {
1984      if (pix[x] ^ invert_bit)
1985        *maskDest++ = 0;
1986      else
1987        *maskDest++ = 255;
1988    }
1989  }
1990
1991  maskImgStr->close();
1992  delete maskImgStr;
1993
1994  maskFilter = getFilterForSurface (maskImage, maskInterpolate);
1995
1996  cairo_surface_mark_dirty (maskImage);
1997  maskPattern = cairo_pattern_create_for_surface (maskImage);
1998  cairo_surface_destroy (maskImage);
1999  if (cairo_pattern_status (maskPattern))
2000    return;
2001
2002#if 0
2003  /* ICCBased color space doesn't do any color correction
2004   * so check its underlying color space as well */
2005  int is_identity_transform;
2006  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2007                  (colorMap->getColorSpace()->getMode() == csICCBased &&
2008                   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2009#endif
2010
2011  /* TODO: Do we want to cache these? */
2012  imgStr = new ImageStream(str, width,
2013                           colorMap->getNumPixelComps(),
2014                           colorMap->getBits());
2015  imgStr->reset();
2016
2017  image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2018  if (cairo_surface_status (image))
2019    goto cleanup;
2020
2021  buffer = cairo_image_surface_get_data (image);
2022  row_stride = cairo_image_surface_get_stride (image);
2023  for (y = 0; y < height; y++) {
2024    dest = (unsigned int *) (buffer + y * row_stride);
2025    pix = imgStr->getLine();
2026    colorMap->getRGBLine (pix, dest, width);
2027  }
2028
2029  filter = getFilterForSurface (image, interpolate);
2030
2031  cairo_surface_mark_dirty (image);
2032  pattern = cairo_pattern_create_for_surface (image);
2033  cairo_surface_destroy (image);
2034  if (cairo_pattern_status (pattern))
2035    goto cleanup;
2036
2037  LOG (printf ("drawMaskedImage %dx%d\n", width, height));
2038
2039  cairo_pattern_set_filter (pattern, filter);
2040  cairo_pattern_set_filter (maskPattern, maskFilter);
2041
2042  if (!printing) {
2043    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2044    cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2045  }
2046
2047  cairo_matrix_init_translate (&matrix, 0, height);
2048  cairo_matrix_scale (&matrix, width, -height);
2049  cairo_pattern_set_matrix (pattern, &matrix);
2050  if (cairo_pattern_status (pattern)) {
2051    cairo_pattern_destroy (pattern);
2052    cairo_pattern_destroy (maskPattern);
2053    goto cleanup;
2054  }
2055
2056  cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2057  cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2058  cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2059  if (cairo_pattern_status (maskPattern)) {
2060    cairo_pattern_destroy (maskPattern);
2061    cairo_pattern_destroy (pattern);
2062    goto cleanup;
2063  }
2064
2065  if (!printing) {
2066    cairo_save (cairo);
2067    cairo_set_source (cairo, pattern);
2068    cairo_rectangle (cairo, 0., 0., 1., 1.);
2069    cairo_clip (cairo);
2070    cairo_mask (cairo, maskPattern);
2071    cairo_restore (cairo);
2072  } else {
2073    cairo_set_source (cairo, pattern);
2074    cairo_mask (cairo, maskPattern);
2075  }
2076
2077  if (cairo_shape) {
2078    cairo_save (cairo_shape);
2079    cairo_set_source (cairo_shape, pattern);
2080    if (!printing) {
2081      cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2082      cairo_fill (cairo_shape);
2083    } else {
2084      cairo_mask (cairo_shape, pattern);
2085    }
2086    cairo_restore (cairo_shape);
2087  }
2088
2089  cairo_pattern_destroy (maskPattern);
2090  cairo_pattern_destroy (pattern);
2091
2092cleanup:
2093  imgStr->close();
2094  delete imgStr;
2095}
2096
2097
2098//XXX: is this affect by AIS(alpha is shape)?
2099void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2100                                         int width, int height,
2101                                         GfxImageColorMap *colorMap,
2102                                         GBool interpolate,
2103                                         Stream *maskStr,
2104                                         int maskWidth, int maskHeight,
2105                                         GfxImageColorMap *maskColorMap,
2106                                         GBool maskInterpolate)
2107{
2108  ImageStream *maskImgStr, *imgStr;
2109  int row_stride;
2110  unsigned char *maskBuffer, *buffer;
2111  unsigned char *maskDest;
2112  unsigned int *dest;
2113  cairo_surface_t *maskImage, *image;
2114  cairo_pattern_t *maskPattern, *pattern;
2115  cairo_matrix_t maskMatrix, matrix;
2116  Guchar *pix;
2117  int y;
2118  cairo_filter_t filter;
2119  cairo_filter_t maskFilter;
2120
2121  maskImgStr = new ImageStream(maskStr, maskWidth,
2122                               maskColorMap->getNumPixelComps(),
2123                               maskColorMap->getBits());
2124  maskImgStr->reset();
2125
2126  maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight);
2127  if (cairo_surface_status (maskImage)) {
2128    maskImgStr->close();
2129    delete maskImgStr;
2130    return;
2131  }
2132
2133  maskBuffer = cairo_image_surface_get_data (maskImage);
2134  row_stride = cairo_image_surface_get_stride (maskImage);
2135  for (y = 0; y < maskHeight; y++) {
2136    maskDest = (unsigned char *) (maskBuffer + y * row_stride);
2137    pix = maskImgStr->getLine();
2138    maskColorMap->getGrayLine (pix, maskDest, maskWidth);
2139  }
2140
2141  maskImgStr->close();
2142  delete maskImgStr;
2143
2144  maskFilter = getFilterForSurface (maskImage, maskInterpolate);
2145
2146  cairo_surface_mark_dirty (maskImage);
2147  maskPattern = cairo_pattern_create_for_surface (maskImage);
2148  cairo_surface_destroy (maskImage);
2149  if (cairo_pattern_status (maskPattern))
2150    return;
2151
2152#if 0
2153  /* ICCBased color space doesn't do any color correction
2154   * so check its underlying color space as well */
2155  int is_identity_transform;
2156  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2157                  (colorMap->getColorSpace()->getMode() == csICCBased &&
2158                   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2159#endif
2160
2161  /* TODO: Do we want to cache these? */
2162  imgStr = new ImageStream(str, width,
2163                           colorMap->getNumPixelComps(),
2164                           colorMap->getBits());
2165  imgStr->reset();
2166
2167  image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
2168  if (cairo_surface_status (image))
2169    goto cleanup;
2170
2171  buffer = cairo_image_surface_get_data (image);
2172  row_stride = cairo_image_surface_get_stride (image);
2173  for (y = 0; y < height; y++) {
2174    dest = (unsigned int *) (buffer + y * row_stride);
2175    pix = imgStr->getLine();
2176    colorMap->getRGBLine (pix, dest, width);
2177  }
2178
2179  filter = getFilterForSurface (image, interpolate);
2180
2181  cairo_surface_mark_dirty (image);
2182  pattern = cairo_pattern_create_for_surface (image);
2183  cairo_surface_destroy (image);
2184  if (cairo_pattern_status (pattern))
2185    goto cleanup;
2186
2187  LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height));
2188
2189  cairo_pattern_set_filter (pattern, filter);
2190  cairo_pattern_set_filter (maskPattern, maskFilter);
2191
2192  if (!printing) {
2193    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2194    cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD);
2195  }
2196
2197  cairo_matrix_init_translate (&matrix, 0, height);
2198  cairo_matrix_scale (&matrix, width, -height);
2199  cairo_pattern_set_matrix (pattern, &matrix);
2200  if (cairo_pattern_status (pattern)) {
2201    cairo_pattern_destroy (pattern);
2202    cairo_pattern_destroy (maskPattern);
2203    goto cleanup;
2204  }
2205
2206  cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
2207  cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
2208  cairo_pattern_set_matrix (maskPattern, &maskMatrix);
2209  if (cairo_pattern_status (maskPattern)) {
2210    cairo_pattern_destroy (maskPattern);
2211    cairo_pattern_destroy (pattern);
2212    goto cleanup;
2213  }
2214
2215  if (fill_opacity != 1.0)
2216    cairo_push_group (cairo);
2217  else
2218    cairo_save (cairo);
2219
2220  cairo_set_source (cairo, pattern);
2221  if (!printing) {
2222    cairo_rectangle (cairo, 0., 0.,
2223                     MIN (width, maskWidth) / (double)width,
2224                     MIN (height, maskHeight) / (double)height);
2225    cairo_clip (cairo);
2226  }
2227  cairo_mask (cairo, maskPattern);
2228
2229  if (fill_opacity != 1.0) {
2230    cairo_pop_group_to_source (cairo);
2231    cairo_save (cairo);
2232    if (!printing) {
2233      cairo_rectangle (cairo, 0., 0.,
2234                       MIN (width, maskWidth) / (double)width,
2235                       MIN (height, maskHeight) / (double)height);
2236      cairo_clip (cairo);
2237    }
2238    cairo_paint_with_alpha (cairo, fill_opacity);
2239  }
2240  cairo_restore (cairo);
2241
2242  if (cairo_shape) {
2243    cairo_save (cairo_shape);
2244    cairo_set_source (cairo_shape, pattern);
2245    if (!printing) {
2246      cairo_rectangle (cairo_shape, 0., 0.,
2247                       MIN (width, maskWidth) / (double)width,
2248                       MIN (height, maskHeight) / (double)height);
2249      cairo_fill (cairo_shape);
2250    } else {
2251      cairo_mask (cairo_shape, pattern);
2252    }
2253    cairo_restore (cairo_shape);
2254  }
2255
2256  cairo_pattern_destroy (maskPattern);
2257  cairo_pattern_destroy (pattern);
2258
2259cleanup:
2260  imgStr->close();
2261  delete imgStr;
2262}
2263
2264GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length)
2265{
2266  int len, i;
2267  char *strBuffer;
2268
2269  len = 0;
2270  str->close();
2271  str->reset();
2272  while (str->getChar() != EOF) len++;
2273  if (len == 0)
2274    return gFalse;
2275
2276  strBuffer = (char *)gmalloc (len);
2277
2278  str->close();
2279  str->reset();
2280  for (i = 0; i < len; ++i)
2281    strBuffer[i] = str->getChar();
2282
2283  *buffer = strBuffer;
2284  *length = len;
2285
2286  return gTrue;
2287}
2288
2289void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2290                               int width, int height,
2291                               GfxImageColorMap *colorMap,
2292                               GBool interpolate,
2293                               int *maskColors, GBool inlineImg)
2294{
2295  cairo_surface_t *image;
2296  cairo_pattern_t *pattern, *maskPattern;
2297  ImageStream *imgStr;
2298  cairo_matrix_t matrix;
2299  unsigned char *buffer;
2300  int stride, i;
2301  GfxRGB *lookup = NULL;
2302  cairo_filter_t filter = CAIRO_FILTER_BILINEAR;
2303
2304  /* TODO: Do we want to cache these? */
2305  imgStr = new ImageStream(str, width,
2306                           colorMap->getNumPixelComps(),
2307                           colorMap->getBits());
2308  imgStr->reset();
2309
2310#if 0
2311  /* ICCBased color space doesn't do any color correction
2312   * so check its underlying color space as well */
2313  int is_identity_transform;
2314  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
2315                  (colorMap->getColorSpace()->getMode() == csICCBased &&
2316                   ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB);
2317#endif
2318
2319  image = cairo_image_surface_create (maskColors ?
2320                                      CAIRO_FORMAT_ARGB32 :
2321                                      CAIRO_FORMAT_RGB24,
2322                                      width, height);
2323  if (cairo_surface_status (image))
2324    goto cleanup;
2325
2326  // special case for one-channel (monochrome/gray/separation) images:
2327  // build a lookup table here
2328  if (colorMap->getNumPixelComps() == 1) {
2329    int n;
2330    Guchar pix;
2331
2332    n = 1 << colorMap->getBits();
2333    lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB));
2334    for (i = 0; i < n; ++i) {
2335      pix = (Guchar)i;
2336
2337      colorMap->getRGB(&pix, &lookup[i]);
2338    }
2339  }
2340
2341  buffer = cairo_image_surface_get_data (image);
2342  stride = cairo_image_surface_get_stride (image);
2343  for (int y = 0; y < height; y++) {
2344    uint32_t *dest = (uint32_t *) (buffer + y * stride);
2345    Guchar *pix = imgStr->getLine();
2346
2347    if (lookup) {
2348      Guchar *p = pix;
2349      GfxRGB rgb;
2350
2351      for (i = 0; i < width; i++) {
2352        rgb = lookup[*p];
2353        dest[i] =
2354                ((int) colToByte(rgb.r) << 16) |
2355                ((int) colToByte(rgb.g) << 8) |
2356                ((int) colToByte(rgb.b) << 0);
2357        p++;
2358      }
2359    } else {
2360      colorMap->getRGBLine (pix, dest, width);
2361    }
2362
2363    if (maskColors) {
2364      for (int x = 0; x < width; x++) {
2365        bool is_opaque = false;
2366        for (int i = 0; i < colorMap->getNumPixelComps(); ++i) {
2367          if (pix[i] < maskColors[2*i] ||
2368              pix[i] > maskColors[2*i+1]) {
2369            is_opaque = true;
2370            break;
2371          }
2372        }
2373        if (is_opaque)
2374          *dest |= 0xff000000;
2375        else
2376          *dest = 0;
2377        dest++;
2378        pix += colorMap->getNumPixelComps();
2379      }
2380    }
2381  }
2382  gfree(lookup);
2383
2384  LOG (printf ("drawImage %dx%d\n", width, height));
2385
2386  cairo_surface_t *scaled_surface;
2387
2388  scaled_surface = downscaleSurface (image);
2389  if (scaled_surface) {
2390    if (cairo_surface_status (scaled_surface))
2391      goto cleanup;
2392    cairo_surface_destroy (image);
2393    image = scaled_surface;
2394    width = cairo_image_surface_get_width (image);
2395    height = cairo_image_surface_get_height (image);
2396  } else {
2397    filter = getFilterForSurface (image, interpolate);
2398  }
2399
2400  cairo_surface_mark_dirty (image);
2401
2402#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 6)
2403  if (printing && (str->getKind() == strDCT || str->getKind() == strJPX)) {
2404    char *strBuffer;
2405    int len;
2406
2407    if (getStreamData (str->getNextStream(), &strBuffer, &len)) {
2408      cairo_status_t st;
2409      st = cairo_surface_set_mime_data (image,
2410                                        str->getKind() == strDCT ?
2411                                        CAIRO_MIME_TYPE_JPEG : CAIRO_MIME_TYPE_JP2,
2412                                        (const unsigned char *)strBuffer, len,
2413                                        gfree, strBuffer);
2414      if (st)
2415        gfree (strBuffer);
2416    }
2417  }
2418#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 9, 6) */
2419
2420  pattern = cairo_pattern_create_for_surface (image);
2421  cairo_surface_destroy (image);
2422  if (cairo_pattern_status (pattern))
2423    goto cleanup;
2424
2425  cairo_pattern_set_filter (pattern, filter);
2426
2427  if (!printing)
2428    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
2429
2430  cairo_matrix_init_translate (&matrix, 0, height);
2431  cairo_matrix_scale (&matrix, width, -height);
2432  cairo_pattern_set_matrix (pattern, &matrix);
2433  if (cairo_pattern_status (pattern)) {
2434    cairo_pattern_destroy (pattern);
2435    goto cleanup;
2436  }
2437
2438  if (!mask && fill_opacity != 1.0) {
2439    maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity);
2440  } else if (mask) {
2441    maskPattern = cairo_pattern_reference (mask);
2442  } else {
2443    maskPattern = NULL;
2444  }
2445
2446  cairo_save (cairo);
2447  cairo_set_source (cairo, pattern);
2448  if (printing)
2449    cairo_rectangle (cairo, 0., 0., width, height);
2450  else
2451    cairo_rectangle (cairo, 0., 0., 1., 1.);
2452  if (maskPattern) {
2453    cairo_clip (cairo);
2454    cairo_mask (cairo, maskPattern);
2455  } else {
2456    cairo_fill (cairo);
2457  }
2458  cairo_restore (cairo);
2459
2460  cairo_pattern_destroy (maskPattern);
2461
2462  if (cairo_shape) {
2463    cairo_save (cairo_shape);
2464    cairo_set_source (cairo_shape, pattern);
2465    if (printing)
2466      cairo_rectangle (cairo_shape, 0., 0., width, height);
2467    else
2468      cairo_rectangle (cairo_shape, 0., 0., 1., 1.);
2469    cairo_fill (cairo_shape);
2470    cairo_restore (cairo_shape);
2471  }
2472
2473  cairo_pattern_destroy (pattern);
2474
2475cleanup:
2476  imgStr->close();
2477  delete imgStr;
2478}
2479
2480
2481//------------------------------------------------------------------------
2482// ImageOutputDev
2483//------------------------------------------------------------------------
2484
2485CairoImageOutputDev::CairoImageOutputDev()
2486{
2487  images = NULL;
2488  numImages = 0;
2489  size = 0;
2490  imgDrawCbk = NULL;
2491  imgDrawCbkData = NULL;
2492}
2493
2494CairoImageOutputDev::~CairoImageOutputDev()
2495{
2496  int i;
2497
2498  for (i = 0; i < numImages; i++)
2499    delete images[i];
2500  gfree (images);
2501}
2502
2503void CairoImageOutputDev::saveImage(CairoImage *image)
2504{ 
2505  if (numImages >= size) {
2506          size += 16;
2507          images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *));
2508  }
2509  images[numImages++] = image;
2510}       
2511
2512void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2513                                        int width, int height, GBool invert,
2514                                        GBool interpolate, GBool inlineImg)
2515{
2516  cairo_t *cr;
2517  cairo_surface_t *surface;
2518  double x1, y1, x2, y2;
2519  double *ctm;
2520  double mat[6];
2521  CairoImage *image;
2522
2523  ctm = state->getCTM();
2524 
2525  mat[0] = ctm[0];
2526  mat[1] = ctm[1];
2527  mat[2] = -ctm[2];
2528  mat[3] = -ctm[3];
2529  mat[4] = ctm[2] + ctm[4];
2530  mat[5] = ctm[3] + ctm[5];
2531  x1 = mat[4];
2532  y1 = mat[5];
2533  x2 = x1 + width;
2534  y2 = y1 + height;
2535
2536  image = new CairoImage (x1, y1, x2, y2);
2537  saveImage (image);
2538
2539  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
2540    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
2541    cr = cairo_create (surface);
2542    setCairo (cr);
2543    cairo_translate (cr, 0, height);
2544    cairo_scale (cr, width, -height);
2545
2546    CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg);
2547    image->setImage (surface);
2548
2549    setCairo (NULL);
2550    cairo_surface_destroy (surface);
2551    cairo_destroy (cr);
2552  }
2553}
2554
2555void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2556                                    int width, int height, GfxImageColorMap *colorMap,
2557                                    GBool interpolate, int *maskColors, GBool inlineImg)
2558{
2559  cairo_t *cr;
2560  cairo_surface_t *surface;
2561  double x1, y1, x2, y2;
2562  double *ctm;
2563  double mat[6];
2564  CairoImage *image;
2565
2566  ctm = state->getCTM();
2567 
2568  mat[0] = ctm[0];
2569  mat[1] = ctm[1];
2570  mat[2] = -ctm[2];
2571  mat[3] = -ctm[3];
2572  mat[4] = ctm[2] + ctm[4];
2573  mat[5] = ctm[3] + ctm[5];
2574  x1 = mat[4];
2575  y1 = mat[5];
2576  x2 = x1 + width;
2577  y2 = y1 + height;
2578
2579  image = new CairoImage (x1, y1, x2, y2);
2580  saveImage (image);
2581
2582  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
2583    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
2584    cr = cairo_create (surface);
2585    setCairo (cr);
2586    cairo_translate (cr, 0, height);
2587    cairo_scale (cr, width, -height);
2588   
2589    CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg);
2590    image->setImage (surface);
2591   
2592    setCairo (NULL);
2593    cairo_surface_destroy (surface);
2594    cairo_destroy (cr);
2595  }
2596}
2597
2598void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2599                                              int width, int height,
2600                                              GfxImageColorMap *colorMap,
2601                                              GBool interpolate,
2602                                              Stream *maskStr,
2603                                              int maskWidth, int maskHeight,
2604                                              GfxImageColorMap *maskColorMap,
2605                                              GBool maskInterpolate)
2606{
2607  cairo_t *cr;
2608  cairo_surface_t *surface;
2609  double x1, y1, x2, y2;
2610  double *ctm;
2611  double mat[6];
2612  CairoImage *image;
2613
2614  ctm = state->getCTM();
2615 
2616  mat[0] = ctm[0];
2617  mat[1] = ctm[1];
2618  mat[2] = -ctm[2];
2619  mat[3] = -ctm[3];
2620  mat[4] = ctm[2] + ctm[4];
2621  mat[5] = ctm[3] + ctm[5];
2622  x1 = mat[4];
2623  y1 = mat[5];
2624  x2 = x1 + width;
2625  y2 = y1 + height;
2626
2627  image = new CairoImage (x1, y1, x2, y2);
2628  saveImage (image);
2629
2630  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
2631    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
2632    cr = cairo_create (surface);
2633    setCairo (cr);
2634    cairo_translate (cr, 0, height);
2635    cairo_scale (cr, width, -height);
2636   
2637    CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
2638                                        maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
2639    image->setImage (surface);
2640   
2641    setCairo (NULL);
2642    cairo_surface_destroy (surface);
2643    cairo_destroy (cr);
2644  }
2645}
2646
2647void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2648                                          int width, int height,
2649                                          GfxImageColorMap *colorMap,
2650                                          GBool interpolate,
2651                                          Stream *maskStr,
2652                                          int maskWidth, int maskHeight,
2653                                          GBool maskInvert, GBool maskInterpolate)
2654{
2655  cairo_t *cr;
2656  cairo_surface_t *surface;
2657  double x1, y1, x2, y2;
2658  double *ctm;
2659  double mat[6];
2660  CairoImage *image;
2661
2662  ctm = state->getCTM();
2663 
2664  mat[0] = ctm[0];
2665  mat[1] = ctm[1];
2666  mat[2] = -ctm[2];
2667  mat[3] = -ctm[3];
2668  mat[4] = ctm[2] + ctm[4];
2669  mat[5] = ctm[3] + ctm[5];
2670  x1 = mat[4];
2671  y1 = mat[5];
2672  x2 = x1 + width;
2673  y2 = y1 + height;
2674
2675  image = new CairoImage (x1, y1, x2, y2);
2676  saveImage (image);
2677
2678  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
2679    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
2680    cr = cairo_create (surface);
2681    setCairo (cr);
2682    cairo_translate (cr, 0, height);
2683    cairo_scale (cr, width, -height);
2684   
2685    CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate,
2686                                    maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate);
2687    image->setImage (surface);
2688   
2689    setCairo (NULL);
2690    cairo_surface_destroy (surface);
2691    cairo_destroy (cr);
2692  }
2693}
Note: See TracBrowser for help on using the repository browser.