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

Last change on this file since 253 was 253, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.8.4

File size: 51.0 KB
Line 
1//========================================================================
2//
3// CairoOutputDev.cc
4//
5// Copyright 2003 Glyph & Cog, LLC
6// Copyright 2004 Red Hat, Inc
7//
8//========================================================================
9
10#include <config.h>
11
12#ifdef USE_GCC_PRAGMAS
13#pragma implementation
14#endif
15
16#include <string.h>
17#include <math.h>
18#include <assert.h>
19#include <cairo.h>
20
21#include "goo/gfile.h"
22#include "GlobalParams.h"
23#include "Error.h"
24#include "Object.h"
25#include "GfxState.h"
26#include "GfxFont.h"
27#include "Link.h"
28#include "CharCodeToUnicode.h"
29#include "FontEncodingTables.h"
30#include <fofi/FoFiTrueType.h>
31#include <splash/SplashBitmap.h>
32#include "CairoOutputDev.h"
33#include "CairoFontEngine.h"
34//------------------------------------------------------------------------
35
36// #define LOG_CAIRO
37
38#ifdef LOG_CAIRO
39#define LOG(x) (x)
40#else
41#define LOG(x)
42#endif
43
44static inline void printMatrix(cairo_matrix_t *matrix){
45        printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx,
46                        matrix->xy, matrix->yy,
47                        matrix->x0, matrix->y0);
48}
49
50//------------------------------------------------------------------------
51// CairoImage
52//------------------------------------------------------------------------
53
54CairoImage::CairoImage (double x1, double y1, double x2, double y2) {
55  this->image = NULL;
56  this->x1 = x1;
57  this->y1 = y1;
58  this->x2 = x2;
59  this->y2 = y2;
60}
61
62CairoImage::~CairoImage () {
63  if (image)
64    cairo_surface_destroy (image);
65}
66
67void CairoImage::setImage (cairo_surface_t *image) {
68  if (this->image)
69    cairo_surface_destroy (this->image);
70  this->image = cairo_surface_reference (image);
71}
72
73//------------------------------------------------------------------------
74// CairoOutputDev
75//------------------------------------------------------------------------
76
77// We cannot tie the lifetime of an FT_Library object to that of
78// CairoOutputDev, since any FT_Faces created with it may end up with a
79// reference by Cairo which can be held long after the CairoOutputDev is
80// deleted.  The simplest way to avoid problems is to never tear down the
81// FT_Library instance; to avoid leaks, just use a single global instance
82// initialized the first time it is needed.
83FT_Library CairoOutputDev::ft_lib;
84GBool CairoOutputDev::ft_lib_initialized = gFalse;
85
86CairoOutputDev::CairoOutputDev() {
87  xref = NULL;
88
89  if (!ft_lib_initialized) {
90    FT_Init_FreeType(&ft_lib);
91    ft_lib_initialized = gTrue;
92  }
93
94  fontEngine = NULL;
95  glyphs = NULL;
96  fill_pattern = NULL;
97  stroke_pattern = NULL;
98  stroke_opacity = 1.0;
99  fill_opacity = 1.0;
100  textClipPath = NULL;
101  cairo = NULL;
102  currentFont = NULL;
103  prescaleImages = gTrue;
104  printing = gTrue;
105
106  groupColorSpaceStack = NULL;
107  group = NULL;
108  mask = NULL;
109  shape = NULL;
110  cairo_shape = NULL;
111  knockoutCount = 0;
112}
113
114CairoOutputDev::~CairoOutputDev() {
115  if (fontEngine) {
116    delete fontEngine;
117  }
118
119  if (cairo)
120    cairo_destroy (cairo);
121  cairo_pattern_destroy (stroke_pattern);
122  cairo_pattern_destroy (fill_pattern);
123  if (group)
124    cairo_pattern_destroy (group);
125  if (shape)
126    cairo_pattern_destroy (shape);
127}
128
129void CairoOutputDev::setCairo(cairo_t *cairo)
130{
131  if (this->cairo != NULL) {
132    cairo_status_t status = cairo_status (this->cairo);
133    if (status) {
134      warning("cairo context error: %s\n", cairo_status_to_string(status));
135    }
136    cairo_destroy (this->cairo);
137    assert(!cairo_shape);
138  }
139  if (cairo != NULL) {
140    this->cairo = cairo_reference (cairo);
141        /* save the initial matrix so that we can use it for type3 fonts. */
142        //XXX: is this sufficient? could we miss changes to the matrix somehow?
143        cairo_get_matrix(cairo, &orig_matrix);
144  } else {
145    this->cairo = NULL;
146    this->cairo_shape = NULL;
147  }
148}
149
150void CairoOutputDev::startDoc(XRef *xrefA) {
151  xref = xrefA;
152  if (fontEngine) {
153    delete fontEngine;
154  }
155  fontEngine = new CairoFontEngine(ft_lib);
156}
157
158void CairoOutputDev::startPage(int pageNum, GfxState *state) {
159  /* set up some per page defaults */
160  cairo_pattern_destroy(fill_pattern);
161  fill_pattern = cairo_pattern_create_rgb(0., 0., 0.);
162
163  cairo_pattern_destroy(stroke_pattern);
164  stroke_pattern = cairo_pattern_create_rgb(0., 0., 0.);
165}
166
167void CairoOutputDev::drawLink(Link *link, Catalog *catalog) {
168}
169
170void CairoOutputDev::saveState(GfxState *state) {
171  LOG(printf ("save\n"));
172  cairo_save (cairo);
173  if (cairo_shape)
174      cairo_save (cairo_shape);
175}
176
177void CairoOutputDev::restoreState(GfxState *state) {
178  LOG(printf ("restore\n"));
179  cairo_restore (cairo);
180  if (cairo_shape)
181      cairo_restore (cairo_shape);
182
183  /* These aren't restored by cairo_restore() since we keep them in
184   * the output device. */
185  updateFillColor(state);
186  updateStrokeColor(state);
187  updateFillOpacity(state);
188  updateStrokeOpacity(state);
189}
190
191void CairoOutputDev::updateAll(GfxState *state) {
192  updateLineDash(state);
193  updateLineJoin(state);
194  updateLineCap(state);
195  updateLineWidth(state);
196  updateFlatness(state);
197  updateMiterLimit(state);
198  updateFillColor(state);
199  updateStrokeColor(state);
200  updateFillOpacity(state);
201  updateStrokeOpacity(state);
202  needFontUpdate = gTrue;
203}
204
205void CairoOutputDev::setDefaultCTM(double *ctm) {
206  cairo_matrix_t matrix;
207  matrix.xx = ctm[0];
208  matrix.yx = ctm[1];
209  matrix.xy = ctm[2];
210  matrix.yy = ctm[3];
211  matrix.x0 = ctm[4];
212  matrix.y0 = ctm[5];
213
214  cairo_transform (cairo, &matrix);
215  if (shape)
216      cairo_transform (cairo_shape, &matrix);
217
218  OutputDev::setDefaultCTM(ctm);
219}
220
221void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12,
222                                double m21, double m22,
223                                double m31, double m32) {
224  cairo_matrix_t matrix, invert_matrix;
225  matrix.xx = m11;
226  matrix.yx = m12;
227  matrix.xy = m21;
228  matrix.yy = m22;
229  matrix.x0 = m31;
230  matrix.y0 = m32;
231
232  /* Make sure the matrix is invertible before setting it.
233   * cairo will blow up if we give it a matrix that's not
234   * invertible, so we need to check before passing it
235   * to cairo_transform. Ignoring it is likely to give better
236   * results than not rendering anything at all. See #14398
237   *
238   * Ideally, we could do the cairo_transform
239   * and then check if anything went wrong and fix it then
240   * instead of having to invert the matrix. */
241  invert_matrix = matrix;
242  if (cairo_matrix_invert(&invert_matrix)) {
243    warning("matrix not invertible\n");
244    return;
245  }
246
247  cairo_transform (cairo, &matrix);
248  if (cairo_shape)
249    cairo_transform (cairo_shape, &matrix);
250  updateLineDash(state);
251  updateLineJoin(state);
252  updateLineCap(state);
253  updateLineWidth(state);
254}
255
256void CairoOutputDev::updateLineDash(GfxState *state) {
257  double *dashPattern;
258  int dashLength;
259  double dashStart;
260
261  state->getLineDash(&dashPattern, &dashLength, &dashStart);
262  cairo_set_dash (cairo, dashPattern, dashLength, dashStart);
263  if (cairo_shape)
264    cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart);
265}
266
267void CairoOutputDev::updateFlatness(GfxState *state) {
268  // cairo_set_tolerance (cairo, state->getFlatness());
269}
270
271void CairoOutputDev::updateLineJoin(GfxState *state) {
272  switch (state->getLineJoin()) {
273  case 0:
274    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER);
275    break;
276  case 1:
277    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND);
278    break;
279  case 2:
280    cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL);
281    break;
282  }
283  if (cairo_shape)
284    cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo));
285}
286
287void CairoOutputDev::updateLineCap(GfxState *state) {
288  switch (state->getLineCap()) {
289  case 0:
290    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT);
291    break;
292  case 1:
293    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND);
294    break;
295  case 2:
296    cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE);
297    break;
298  }
299  if (cairo_shape)
300    cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo));
301}
302
303void CairoOutputDev::updateMiterLimit(GfxState *state) {
304  cairo_set_miter_limit (cairo, state->getMiterLimit());
305  if (cairo_shape)
306    cairo_set_miter_limit (cairo_shape, state->getMiterLimit());
307}
308
309#define MIN(a,b) (((a) < (b)) ? (a) : (b))
310
311void CairoOutputDev::updateLineWidth(GfxState *state) {
312  LOG(printf ("line width: %f\n", state->getLineWidth()));
313  if (state->getLineWidth() == 0.0) {
314    /* find out how big pixels (device unit) are in the x and y directions
315     * choose the smaller of the two as our line width */
316    double x = 1.0, y = 1.0;
317    cairo_device_to_user_distance(cairo, &x, &y);
318    cairo_set_line_width (cairo, MIN(fabs(x),fabs(y)));
319  } else {
320    cairo_set_line_width (cairo, state->getLineWidth());
321  }
322  if (cairo_shape)
323    cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo));
324}
325
326void CairoOutputDev::updateFillColor(GfxState *state) {
327  state->getFillRGB(&fill_color);
328
329  cairo_pattern_destroy(fill_pattern);
330  fill_pattern = cairo_pattern_create_rgba(fill_color.r / 65535.0,
331                                           fill_color.g / 65535.0,
332                                           fill_color.b / 65535.0,
333                                           fill_opacity);
334
335  LOG(printf ("fill color: %d %d %d\n",
336              fill_color.r, fill_color.g, fill_color.b));
337}
338
339void CairoOutputDev::updateStrokeColor(GfxState *state) {
340  state->getStrokeRGB(&stroke_color);
341
342  cairo_pattern_destroy(stroke_pattern);
343  stroke_pattern = cairo_pattern_create_rgba(stroke_color.r / 65535.0,
344                                             stroke_color.g / 65535.0,
345                                             stroke_color.b / 65535.0,
346                                             stroke_opacity);
347 
348  LOG(printf ("stroke color: %d %d %d\n",
349              stroke_color.r, stroke_color.g, stroke_color.b));
350}
351
352void CairoOutputDev::updateFillOpacity(GfxState *state) {
353  fill_opacity = state->getFillOpacity();
354
355  cairo_pattern_destroy(fill_pattern);
356  fill_pattern = cairo_pattern_create_rgba(fill_color.r / 65535.0,
357                                           fill_color.g / 65535.0,
358                                           fill_color.b / 65535.0,
359                                           fill_opacity);
360
361  LOG(printf ("fill opacity: %f\n", fill_opacity));
362}
363
364void CairoOutputDev::updateStrokeOpacity(GfxState *state) {
365  stroke_opacity = state->getStrokeOpacity();
366
367  cairo_pattern_destroy(stroke_pattern);
368  stroke_pattern = cairo_pattern_create_rgba(stroke_color.r / 65535.0,
369                                             stroke_color.g / 65535.0,
370                                             stroke_color.b / 65535.0,
371                                             stroke_opacity);
372 
373  LOG(printf ("stroke opacity: %f\n", stroke_opacity));
374}
375
376void CairoOutputDev::updateFont(GfxState *state) {
377  cairo_font_face_t *font_face;
378  cairo_matrix_t matrix;
379
380  LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString()));
381
382  needFontUpdate = gFalse;
383
384  if (state->getFont()->getType() == fontType3) 
385    return;
386
387  currentFont = fontEngine->getFont (state->getFont(), xref);
388
389  if (!currentFont)
390    return;
391
392  LOG(printf ("font matrix: %f %f %f %f\n", m11, m12, m21, m22));
393 
394  font_face = currentFont->getFontFace();
395  cairo_set_font_face (cairo, font_face);
396 
397  double fontSize = state->getFontSize();
398  double *m = state->getTextMat();
399  /* NOTE: adjusting by a constant is hack. The correct solution
400   * is probably to use user-fonts and compute the scale on a per
401   * glyph basis instead of for the entire font */
402  double w = currentFont->getSubstitutionCorrection(state->getFont());
403  matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
404  matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
405  matrix.xy = -m[2] * fontSize;
406  matrix.yy = -m[3] * fontSize;
407  matrix.x0 = 0;
408  matrix.y0 = 0;
409  cairo_set_font_matrix (cairo, &matrix);
410}
411
412void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) {
413  GfxSubpath *subpath;
414  int i, j;
415  for (i = 0; i < path->getNumSubpaths(); ++i) {
416    subpath = path->getSubpath(i);
417    if (subpath->getNumPoints() > 0) {
418      cairo_move_to (cairo, subpath->getX(0), subpath->getY(0));
419         j = 1;
420      while (j < subpath->getNumPoints()) {
421        if (subpath->getCurve(j)) {
422          cairo_curve_to( cairo,
423                          subpath->getX(j), subpath->getY(j),
424                          subpath->getX(j+1), subpath->getY(j+1),
425                          subpath->getX(j+2), subpath->getY(j+2));
426
427          j += 3;
428        } else {
429          cairo_line_to (cairo, subpath->getX(j), subpath->getY(j));
430          ++j;
431        }
432      }
433      if (subpath->isClosed()) {
434        LOG (printf ("close\n"));
435        cairo_close_path (cairo);
436      }
437    }
438  }
439}
440
441void CairoOutputDev::stroke(GfxState *state) {
442  doPath (cairo, state, state->getPath());
443  cairo_set_source (cairo, stroke_pattern);
444  LOG(printf ("stroke\n"));
445  cairo_stroke (cairo);
446  if (cairo_shape) {
447    doPath (cairo_shape, state, state->getPath());
448    cairo_stroke (cairo_shape);
449  }
450}
451
452void CairoOutputDev::fill(GfxState *state) {
453  doPath (cairo, state, state->getPath());
454  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
455  cairo_set_source (cairo, fill_pattern);
456  LOG(printf ("fill\n"));
457  //XXX: how do we get the path
458  cairo_fill (cairo);
459  if (cairo_shape) {
460    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
461    doPath (cairo_shape, state, state->getPath());
462    cairo_fill (cairo_shape);
463  }
464}
465
466void CairoOutputDev::eoFill(GfxState *state) {
467  doPath (cairo, state, state->getPath());
468  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
469  cairo_set_source (cairo, fill_pattern);
470  LOG(printf ("fill-eo\n"));
471  cairo_fill (cairo);
472
473  if (cairo_shape) {
474    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
475    doPath (cairo_shape, state, state->getPath());
476    cairo_fill (cairo_shape);
477  }
478
479}
480
481void CairoOutputDev::clip(GfxState *state) {
482  doPath (cairo, state, state->getPath());
483  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING);
484  cairo_clip (cairo);
485  LOG (printf ("clip\n"));
486  if (cairo_shape) {
487    doPath (cairo_shape, state, state->getPath());
488    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING);
489    cairo_clip (cairo_shape);
490  }
491}
492
493void CairoOutputDev::eoClip(GfxState *state) {
494  doPath (cairo, state, state->getPath());
495  cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD);
496  cairo_clip (cairo);
497  LOG (printf ("clip-eo\n"));
498  if (cairo_shape) {
499    doPath (cairo_shape, state, state->getPath());
500    cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD);
501    cairo_clip (cairo_shape);
502  }
503
504}
505
506void CairoOutputDev::beginString(GfxState *state, GooString *s)
507{
508  int len = s->getLength();
509
510  if (needFontUpdate)
511    updateFont(state);
512
513  if (!currentFont)
514    return;
515
516  glyphs = (cairo_glyph_t *) gmalloc (len * sizeof (cairo_glyph_t));
517  glyphCount = 0;
518}
519
520void CairoOutputDev::drawChar(GfxState *state, double x, double y,
521                              double dx, double dy,
522                              double originX, double originY,
523                              CharCode code, int nBytes, Unicode *u, int uLen)
524{
525  if (!currentFont)
526    return;
527 
528  glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen);
529  glyphs[glyphCount].x = x - originX;
530  glyphs[glyphCount].y = y - originY;
531  glyphCount++;
532}
533
534void CairoOutputDev::endString(GfxState *state)
535{
536  int render;
537
538  if (!currentFont)
539    return;
540
541  // endString can be called without a corresponding beginString. If this
542  // happens glyphs will be null so don't draw anything, just return.
543  // XXX: OutputDevs should probably not have to deal with this...
544  if (!glyphs)
545    return;
546
547  // ignore empty strings and invisible text -- this is used by
548  // Acrobat Capture
549  render = state->getRender();
550  if (render == 3 || glyphCount == 0) {
551    gfree(glyphs);
552    glyphs = NULL;
553    return;
554  }
555 
556  if (!(render & 1)) {
557    LOG (printf ("fill string\n"));
558    cairo_set_source (cairo, fill_pattern);
559    cairo_show_glyphs (cairo, glyphs, glyphCount);
560    if (cairo_shape)
561      cairo_show_glyphs (cairo_shape, glyphs, glyphCount);
562  }
563 
564  // stroke
565  if ((render & 3) == 1 || (render & 3) == 2) {
566    LOG (printf ("stroke string\n"));
567    cairo_set_source (cairo, stroke_pattern);
568    cairo_glyph_path (cairo, glyphs, glyphCount);
569    cairo_stroke (cairo);
570    if (cairo_shape) {
571      cairo_glyph_path (cairo_shape, glyphs, glyphCount);
572      cairo_stroke (cairo_shape);
573    }
574  }
575
576  // clip
577  if (render & 4) {
578    LOG (printf ("clip string\n"));
579    // append the glyph path to textClipPath.
580
581    // set textClipPath as the currentPath
582    if (textClipPath) {
583      cairo_append_path (cairo, textClipPath);
584      if (cairo_shape) {
585        cairo_append_path (cairo_shape, textClipPath);
586      }
587      cairo_path_destroy (textClipPath);
588    }
589   
590    // append the glyph path
591    cairo_glyph_path (cairo, glyphs, glyphCount);
592   
593    // move the path back into textClipPath
594    // and clear the current path
595    textClipPath = cairo_copy_path (cairo);
596    cairo_new_path (cairo);
597    if (cairo_shape) {
598      cairo_new_path (cairo_shape);
599    }
600  }
601
602  gfree (glyphs);
603  glyphs = NULL;
604}
605
606
607GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y,
608                                      double dx, double dy,
609                                      CharCode code, Unicode *u, int uLen) {
610
611  cairo_save (cairo);
612  double *ctm;
613  cairo_matrix_t matrix;
614
615  ctm = state->getCTM();
616  matrix.xx = ctm[0];
617  matrix.yx = ctm[1];
618  matrix.xy = ctm[2];
619  matrix.yy = ctm[3];
620  matrix.x0 = ctm[4];
621  matrix.y0 = ctm[5];
622  /* Restore the original matrix and then transform to matrix needed for the
623   * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/
624  cairo_set_matrix(cairo, &orig_matrix);
625  cairo_transform(cairo, &matrix);
626  if (cairo_shape) {
627    cairo_save (cairo_shape);
628    cairo_set_matrix(cairo_shape, &orig_matrix);
629    cairo_transform(cairo_shape, &matrix);
630  }
631  return gFalse;
632}
633
634void CairoOutputDev::endType3Char(GfxState *state) {
635  cairo_restore (cairo);
636  if (cairo_shape) {
637    cairo_restore (cairo_shape);
638  }
639}
640
641void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) {
642}
643
644void CairoOutputDev::type3D1(GfxState *state, double wx, double wy,
645                             double llx, double lly, double urx, double ury) {
646}
647
648void CairoOutputDev::endTextObject(GfxState *state) {
649  if (textClipPath) {
650    // clip the accumulated text path
651    cairo_append_path (cairo, textClipPath);
652    cairo_clip (cairo);
653    if (cairo_shape) {
654      cairo_append_path (cairo_shape, textClipPath);
655      cairo_clip (cairo_shape);
656    }
657    cairo_path_destroy (textClipPath);
658    textClipPath = NULL;
659  }
660
661}
662
663static inline int splashRound(SplashCoord x) {
664  return (int)floor(x + 0.5);
665}
666
667static inline int splashCeil(SplashCoord x) {
668  return (int)ceil(x);
669}
670
671static inline int splashFloor(SplashCoord x) {
672  return (int)floor(x);
673}
674
675static
676cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content)
677{
678  double x1, y1, x2, y2;
679  int width, height;
680  cairo_clip_extents (cairo, &x1, &y1, &x2, &y2);
681  cairo_matrix_t matrix;
682  cairo_get_matrix (cairo, &matrix);
683  //cairo_matrix_transform_point(&matrix, &x1, &y1);
684  //cairo_matrix_transform_point(&matrix, &x2, &y2);*/
685  cairo_user_to_device(cairo, &x1, &y1);
686  cairo_user_to_device(cairo, &x2, &y2);
687  width = splashCeil(x2) - splashFloor(x1);
688  //XXX: negative matrix
689  ////height = splashCeil(y2) - splashFloor(y1);
690  height = splashFloor(y1) - splashCeil(y2);
691  cairo_surface_t *target = cairo_get_target (cairo);
692  cairo_surface_t *result;
693
694  result = cairo_surface_create_similar (target, content, width, height);
695  double x_offset, y_offset;
696    cairo_surface_get_device_offset(target, &x_offset, &y_offset);
697    cairo_surface_set_device_offset(result, x_offset, y_offset);
698
699 
700  return result;
701}
702
703
704
705void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/,
706                                      GfxColorSpace * blendingColorSpace,
707                                      GBool /*isolated*/, GBool knockout,
708                                      GBool forSoftMask) {
709  /* push color space */
710  ColorSpaceStack* css = new ColorSpaceStack;
711  css->cs = blendingColorSpace;
712  css->knockout = knockout;
713  css->next = groupColorSpaceStack;
714  groupColorSpaceStack = css;
715  if (knockout) {
716    knockoutCount++;
717    if (!cairo_shape) {
718      /* create a surface for tracking the shape */
719      cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA);
720      cairo_shape = cairo_create (cairo_shape_surface);
721      cairo_surface_destroy (cairo_shape_surface);
722
723      /* the color doesn't matter as long as it is opaque */
724      cairo_set_source_rgb (cairo_shape, 0, 0, 0);
725      cairo_matrix_t matrix;
726      cairo_get_matrix (cairo, &matrix);
727      //printMatrix(&matrix);
728      cairo_set_matrix (cairo_shape, &matrix);
729    } else {
730      cairo_reference (cairo_shape);
731    }
732  }
733  if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
734    /* we need to track the shape */
735    cairo_push_group (cairo_shape);
736  }
737  if (0 && forSoftMask)
738    cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA);
739  else
740    cairo_push_group (cairo);
741
742  /* push_group has an implicit cairo_save() */
743  if (knockout) {
744    /*XXX: let's hope this matches the semantics needed */
745    cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
746  } else {
747    cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
748  }
749}
750
751void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) {
752
753  if (group)
754    cairo_pattern_destroy(group);
755  group = cairo_pop_group (cairo);
756
757  if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) {
758    if (shape)
759      cairo_pattern_destroy(shape);
760    shape = cairo_pop_group (cairo_shape);
761  }
762}
763
764void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
765  cairo_set_source (cairo, group);
766
767  if (!mask) {
768    //XXX: deal with mask && shape case
769    if (shape) {
770      cairo_save (cairo);
771
772      /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask)
773       * however our source has already been clipped to mask so we only need to
774       * do ADD and OUT */
775
776      /* clear the shape mask */
777      cairo_set_source (cairo, shape);
778      cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT);
779      cairo_paint (cairo);
780
781      cairo_set_operator (cairo, CAIRO_OPERATOR_ADD);
782      cairo_set_source (cairo, group);
783      cairo_paint (cairo);
784
785      cairo_restore (cairo);
786
787      cairo_pattern_destroy (shape);
788      shape = NULL;
789    } else {
790      cairo_paint_with_alpha (cairo, fill_opacity);
791    }
792    cairo_status_t status = cairo_status(cairo);
793    if (status)
794      printf("BAD status: %s\n", cairo_status_to_string(status));
795  } else {
796    cairo_mask(cairo, mask);
797
798    cairo_pattern_destroy(mask);
799    mask = NULL;
800  }
801
802  popTransparencyGroup();
803}
804
805typedef unsigned int uint32_t;
806
807static uint32_t luminocity(uint32_t x)
808{
809  int r = (x >> 16) & 0xff;
810  int g = (x >>  8) & 0xff;
811  int b = (x >>  0) & 0xff;
812  // an arbitrary integer approximation of .3*r + .59*g + .11*b
813  int y = (r*19661+g*38666+b*7209 + 32829)>>16;
814  return y << 24;
815}
816
817
818/* XXX: do we need to deal with shape here? */
819void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha,
820                                 Function * transferFunc, GfxColor * backdropColor) {
821  if (alpha == false) {
822    /* We need to mask according to the luminocity of the group.
823     * So we paint the group to an image surface convert it to a luminocity map
824     * and then use that as the mask. */
825
826    double x1, y1, x2, y2;
827    cairo_clip_extents(cairo, &x1, &y1, &x2, &y2);
828    int width = (int)(ceil(x2) - floor(x1));
829    int height = (int)(ceil(y2) - floor(y1));
830
831    cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
832    cairo_t *maskCtx = cairo_create(source);
833
834    //XXX: hopefully this uses the correct color space */
835    GfxRGB backdropColorRGB;
836    groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB);
837    /* paint the backdrop */
838    cairo_set_source_rgb(maskCtx, backdropColorRGB.r / 65535.0,
839                         backdropColorRGB.g / 65535.0,
840                         backdropColorRGB.b / 65535.0);
841
842
843    cairo_matrix_t mat;
844    cairo_get_matrix(cairo, &mat);
845    cairo_set_matrix(maskCtx, &mat);
846
847    /* make the device offset of the new mask match that of the group */
848    double x_offset, y_offset;
849    cairo_surface_t *pats;
850    cairo_pattern_get_surface(group, &pats);
851    cairo_surface_get_device_offset(pats, &x_offset, &y_offset);
852    cairo_surface_set_device_offset(source, x_offset, y_offset);
853
854    /* paint the group */
855    cairo_set_source(maskCtx, group);
856    cairo_paint(maskCtx);
857
858    /* convert to a luminocity map */
859    uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source);
860    /* get stride in units of 32 bits */
861    int stride = cairo_image_surface_get_stride(source)/4;
862    for (int y=0; y<height; y++) {
863      for (int x=0; x<width; x++) {
864        source_data[y*stride + x] = luminocity(source_data[y*stride + x]);
865
866#if 0
867        here is how splash deals with the transferfunction we should deal with this
868          at some point
869        if (transferFunc) {
870          transferFunc->transform(&lum, &lum2);
871        } else {
872          lum2 = lum;
873        }
874        p[x] = (int)(lum2 * 255.0 + 0.5);
875#endif
876
877      }
878    }
879
880    /* setup the new mask pattern */
881    mask = cairo_pattern_create_for_surface(source);
882    cairo_matrix_t patMatrix;
883    cairo_pattern_get_matrix(group, &patMatrix);
884    cairo_pattern_set_matrix(mask, &patMatrix);
885
886    cairo_surface_destroy(source);
887    cairo_surface_destroy(pats);
888  } else {
889    cairo_pattern_reference(group);
890    mask = group;
891  }
892
893  popTransparencyGroup();
894}
895
896void CairoOutputDev::popTransparencyGroup() {
897  /* pop color space */
898  ColorSpaceStack *css = groupColorSpaceStack;
899  if (css->knockout) {
900    knockoutCount--;
901    if (!knockoutCount) {
902      /* we don't need to track the shape anymore because
903       * we are not above any knockout groups */
904      cairo_destroy(cairo_shape);
905      cairo_shape = NULL;
906    }
907  }
908  groupColorSpaceStack = css->next;
909  delete css;
910}
911
912
913void CairoOutputDev::clearSoftMask(GfxState * /*state*/) {
914  //XXX: should we be doing anything here?
915}
916
917void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
918                                    int width, int height, GBool invert,
919                                    GBool inlineImg) {
920
921  /* FIXME: Doesn't the image mask support any colorspace? */
922  cairo_set_source (cairo, fill_pattern);
923
924  /* work around a cairo bug when scaling 1x1 surfaces */
925  if (width == 1 && height == 1) {
926    cairo_save (cairo);
927    cairo_rectangle (cairo, 0., 0., width, height);
928    cairo_fill (cairo);
929    cairo_restore (cairo);
930    if (cairo_shape) {
931      cairo_save (cairo_shape);
932      cairo_rectangle (cairo_shape, 0., 0., width, height);
933      cairo_fill (cairo_shape);
934      cairo_restore (cairo_shape);
935    }
936    return;
937  }
938
939  /* shape is 1.0 for painted areas, 0.0 for unpainted ones */
940
941  cairo_matrix_t matrix;
942  cairo_get_matrix (cairo, &matrix);
943  //XXX: it is possible that we should only do sub pixel positioning if
944  // we are rendering fonts */
945  if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) {
946    drawImageMaskPrescaled(state, ref, str, width, height, invert, inlineImg);
947  } else {
948    drawImageMaskRegular(state, ref, str, width, height, invert, inlineImg);
949  }
950}
951
952void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str,
953                                    int width, int height, GBool invert,
954                                    GBool inlineImg) {
955  unsigned char *buffer;
956  unsigned char *dest;
957  cairo_surface_t *image;
958  cairo_pattern_t *pattern;
959  int x, y;
960  ImageStream *imgStr;
961  Guchar *pix;
962  cairo_matrix_t matrix;
963  int invert_bit;
964  int row_stride;
965
966  row_stride = (width + 3) & ~3;
967  buffer = (unsigned char *) malloc (height * row_stride);
968  if (buffer == NULL) {
969    error(-1, "Unable to allocate memory for image.");
970    return;
971  }
972
973  /* TODO: Do we want to cache these? */
974  imgStr = new ImageStream(str, width, 1, 1);
975  imgStr->reset();
976
977  invert_bit = invert ? 1 : 0;
978
979  for (y = 0; y < height; y++) {
980    pix = imgStr->getLine();
981    dest = buffer + y * row_stride;
982    for (x = 0; x < width; x++) {
983
984      if (pix[x] ^ invert_bit)
985        *dest++ = 0;
986      else
987        *dest++ = 255;
988    }
989  }
990
991  image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_A8,
992                                               width, height, row_stride);
993  if (image == NULL) {
994    delete imgStr;
995    return;
996  }
997  pattern = cairo_pattern_create_for_surface (image);
998  if (pattern == NULL) {
999    delete imgStr;
1000    return;
1001  }
1002
1003  cairo_matrix_init_translate (&matrix, 0, height);
1004  cairo_matrix_scale (&matrix, width, -height);
1005
1006  cairo_pattern_set_matrix (pattern, &matrix);
1007
1008  /* we should actually be using CAIRO_FILTER_NEAREST here. However,
1009   * cairo doesn't yet do minifaction filtering causing scaled down
1010   * images with CAIRO_FILTER_NEAREST to look really bad */
1011  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
1012
1013  cairo_mask (cairo, pattern);
1014
1015  if (cairo_shape) {
1016#if 0
1017    cairo_rectangle (cairo_shape, 0., 0., width, height);
1018    cairo_fill (cairo_shape);
1019#else
1020    cairo_save (cairo_shape);
1021    /* this should draw a rectangle the size of the image
1022     * we use this instead of rect,fill because of the lack
1023     * of EXTEND_PAD */
1024    /* NOTE: this will multiply the edges of the image twice */
1025    cairo_set_source (cairo_shape, pattern);
1026    cairo_mask (cairo_shape, pattern);
1027    cairo_restore (cairo_shape);
1028#endif
1029  }
1030
1031
1032  cairo_pattern_destroy (pattern);
1033  cairo_surface_destroy (image);
1034  free (buffer);
1035  delete imgStr;
1036}
1037
1038
1039void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str,
1040                                    int width, int height, GBool invert,
1041                                    GBool inlineImg) {
1042  unsigned char *buffer;
1043  cairo_surface_t *image;
1044  cairo_pattern_t *pattern;
1045  ImageStream *imgStr;
1046  Guchar *pix;
1047  cairo_matrix_t matrix;
1048  int invert_bit;
1049  int row_stride;
1050
1051  /* cairo does a very poor job of scaling down images so we scale them ourselves */
1052
1053  /* this scaling code is adopted from the splash image scaling code */
1054  cairo_get_matrix(cairo, &matrix);
1055#if 0
1056  printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
1057#endif
1058  /* this whole computation should be factored out */
1059  double xScale = matrix.xx;
1060  double yScale = matrix.yy;
1061  int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */
1062  int scaledHeight;
1063  int scaledWidth;
1064  if (xScale >= 0) {
1065    tx = splashRound(matrix.x0 - 0.01);
1066    tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1;
1067  } else {
1068    tx = splashRound(matrix.x0 + 0.01) - 1;
1069    tx2 = splashRound(matrix.x0 + xScale - 0.01);
1070  }
1071  scaledWidth = abs(tx2 - tx) + 1;
1072  //scaledWidth = splashRound(fabs(xScale));
1073  if (scaledWidth == 0) {
1074    // technically, this should draw nothing, but it generally seems
1075    // better to draw a one-pixel-wide stripe rather than throwing it
1076    // away
1077    scaledWidth = 1;
1078  }
1079  if (yScale >= 0) {
1080    ty = splashFloor(matrix.y0 + 0.01);
1081    ty2 = splashCeil(matrix.y0 + yScale - 0.01);
1082  } else {
1083    ty = splashCeil(matrix.y0 - 0.01);
1084    ty2 = splashFloor(matrix.y0 + yScale + 0.01);
1085  }
1086  scaledHeight = abs(ty2 - ty);
1087  if (scaledHeight == 0) {
1088    scaledHeight = 1;
1089  }
1090#if 0
1091  printf("xscale: %g, yscale: %g\n", xScale, yScale);
1092  printf("width: %d, height: %d\n", width, height);
1093  printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight);
1094#endif
1095
1096  /* compute the required padding */
1097  /* Padding is used to preserve the aspect ratio.
1098     We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */
1099  int head_pad = 0;
1100  int tail_pad = 0;
1101  int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height);
1102
1103  /* compute the two pieces of padding */
1104  if (total_pad > 0) {
1105    //XXX: i'm not positive fabs() is correct
1106    float tail_error = fabs(matrix.y0 - ty);
1107    float head_error = fabs(ty2 - (matrix.y0 + yScale));
1108    float tail_fraction = tail_error/(tail_error + head_error);
1109    tail_pad = splashRound(total_pad*tail_fraction);
1110    head_pad = total_pad - tail_pad;
1111  } else {
1112    tail_pad = 0;
1113    head_pad = 0;
1114  }
1115  int origHeight = height;
1116  height += tail_pad;
1117  height += head_pad;
1118#if 0
1119  printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad);
1120  printf("origHeight: %d height: %d\n", origHeight, height);
1121  printf("ty: %d, ty2: %d\n", ty, ty2);
1122#endif
1123
1124  /* TODO: Do we want to cache these? */
1125  imgStr = new ImageStream(str, width, 1, 1);
1126  imgStr->reset();
1127
1128  invert_bit = invert ? 1 : 0;
1129
1130  row_stride = (scaledWidth + 3) & ~3;
1131  buffer = (unsigned char *) malloc (scaledHeight * row_stride);
1132  if (buffer == NULL) {
1133    error(-1, "Unable to allocate memory for image.");
1134    return;
1135  }
1136
1137  int yp = height / scaledHeight;
1138  int yq = height % scaledHeight;
1139  int xp = width / scaledWidth;
1140  int xq = width % scaledWidth;
1141  int yt = 0;
1142  int origHeight_c = origHeight;
1143  /* use MIN() because yp might be > origHeight because of padding */
1144  unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width);
1145  int lastYStep = 1;
1146  int total = 0;
1147  for (int y = 0; y < scaledHeight; y++) {
1148    // y scale Bresenham
1149    int yStep = yp;
1150    yt += yq;
1151
1152    if (yt >= scaledHeight) {
1153      yt -= scaledHeight;
1154      ++yStep;
1155    }
1156
1157    // read row (s) from image ignoring the padding as appropriate
1158    {
1159      int n = (yp > 0) ? yStep : lastYStep;
1160      total += n;
1161      if (n > 0) {
1162        unsigned char *p = pixBuf;
1163        int head_pad_count = head_pad;
1164        int origHeight_count = origHeight;
1165        int tail_pad_count = tail_pad;
1166        for (int i=0; i<n; i++) {
1167          // get row
1168          if (head_pad_count) {
1169            head_pad_count--;
1170          } else if (origHeight_count) {
1171            pix = imgStr->getLine();
1172            for (int j=0; j<width; j++) {
1173              if (pix[j] ^ invert_bit)
1174                p[j] = 0;
1175              else
1176                p[j] = 255;
1177            }
1178            origHeight_count--;
1179            p += width;
1180          } else if (tail_pad_count) {
1181            tail_pad_count--;
1182          } else {
1183            printf("%d %d\n", n, total);
1184            assert(0 && "over run\n");
1185          }
1186        }
1187      }
1188    }
1189
1190    lastYStep = yStep;
1191    int k1 = y;
1192
1193    int xt = 0;
1194    int xSrc = 0;
1195    int x1 = k1;
1196    int n = yStep > 0 ? yStep : 1;
1197    int origN = n;
1198
1199    /* compute the size of padding and pixels that will be used for this row */
1200    int head_pad_size = MIN(n, head_pad);
1201    n -= head_pad_size;
1202    head_pad -= MIN(head_pad_size, yStep);
1203
1204    int pix_size = MIN(n, origHeight);
1205    n -= pix_size;
1206    origHeight -= MIN(pix_size, yStep);
1207
1208    int tail_pad_size = MIN(n, tail_pad);
1209    n -= tail_pad_size;
1210    tail_pad -= MIN(tail_pad_size, yStep);
1211    if (n != 0) {
1212      printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size);
1213      assert(n == 0);
1214    }
1215
1216    for (int x = 0; x < scaledWidth; ++x) {
1217      int xStep = xp;
1218      xt += xq;
1219      if (xt >= scaledWidth) {
1220        xt -= scaledWidth;
1221        ++xStep;
1222      }
1223      int m = xStep > 0 ? xStep : 1;
1224      float pixAcc0 = 0;
1225      /* could m * head_pad_size * tail_pad_size  overflow? */
1226      if (invert_bit) {
1227        pixAcc0 += m * head_pad_size * tail_pad_size * 255;
1228      } else {
1229        pixAcc0 += m * head_pad_size * tail_pad_size * 0;
1230      }
1231      /* Accumulate all of the source pixels for the destination pixel */
1232      for (int i = 0; i < pix_size; ++i) {
1233        for (int j = 0; j< m; ++j) {
1234          if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) {
1235            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);
1236            printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size);
1237            assert(0 && "bad access\n");
1238          }
1239          pixAcc0 += pixBuf[xSrc + i*width + j];
1240        }
1241      }
1242      buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m));
1243      xSrc += xStep;
1244      x1 += 1;
1245    }
1246
1247  }
1248  free(pixBuf);
1249
1250  //XXX: we should handle error's better than this
1251  image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_A8,
1252      scaledWidth, scaledHeight, row_stride);
1253  if (image == NULL) {
1254    delete imgStr;
1255    return;
1256  }
1257  pattern = cairo_pattern_create_for_surface (image);
1258  if (pattern == NULL) {
1259    delete imgStr;
1260    return;
1261  }
1262
1263  /* we should actually be using CAIRO_FILTER_NEAREST here. However,
1264   * cairo doesn't yet do minifaction filtering causing scaled down
1265   * images with CAIRO_FILTER_NEAREST to look really bad */
1266  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
1267
1268  cairo_save (cairo);
1269
1270  /* modify our current transformation so that the prescaled image
1271   * goes where it is supposed to */
1272  cairo_get_matrix(cairo, &matrix);
1273  cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy);
1274  // get integer co-ords
1275  cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0);
1276  if (yScale > 0)
1277    cairo_scale(cairo, 1, -1);
1278
1279  cairo_mask (cairo, pattern);
1280
1281  //cairo_get_matrix(cairo, &matrix);
1282  //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0);
1283  cairo_restore(cairo);
1284
1285  if (cairo_shape) {
1286    cairo_save (cairo_shape);
1287
1288    /* modify our current transformation so that the prescaled image
1289     * goes where it is supposed to */
1290    cairo_get_matrix(cairo_shape, &matrix);
1291    cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy);
1292    // get integer co-ords
1293    cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0);
1294    if (yScale > 0)
1295      cairo_scale(cairo_shape, 1, -1);
1296
1297    cairo_mask (cairo_shape, pattern);
1298
1299    cairo_restore(cairo_shape);
1300  }
1301
1302
1303  cairo_pattern_destroy (pattern);
1304  cairo_surface_destroy (image);
1305  free (buffer);
1306  delete imgStr;
1307}
1308
1309void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref,
1310                                Stream *str, int width, int height,
1311                                GfxImageColorMap *colorMap,
1312                                Stream *maskStr, int maskWidth,
1313                                int maskHeight, GBool maskInvert)
1314{
1315  ImageStream *maskImgStr;
1316  maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1);
1317  maskImgStr->reset();
1318
1319  int row_stride = (maskWidth + 3) & ~3;
1320  unsigned char *maskBuffer;
1321  maskBuffer = (unsigned char *)gmalloc (row_stride * maskHeight);
1322  unsigned char *maskDest;
1323  cairo_surface_t *maskImage;
1324  cairo_pattern_t *maskPattern;
1325  Guchar *pix;
1326  int x, y;
1327
1328  int invert_bit;
1329 
1330  invert_bit = maskInvert ? 1 : 0;
1331
1332  for (y = 0; y < maskHeight; y++) {
1333    pix = maskImgStr->getLine();
1334    maskDest = maskBuffer + y * row_stride;
1335    for (x = 0; x < maskWidth; x++) {
1336      if (pix[x] ^ invert_bit)
1337        *maskDest++ = 0;
1338      else
1339        *maskDest++ = 255;
1340    }
1341  }
1342
1343  maskImage = cairo_image_surface_create_for_data (maskBuffer, CAIRO_FORMAT_A8,
1344                                                 maskWidth, maskHeight, row_stride);
1345
1346  delete maskImgStr;
1347  maskStr->close();
1348
1349  unsigned char *buffer;
1350  unsigned int *dest;
1351  cairo_surface_t *image;
1352  cairo_pattern_t *pattern;
1353  ImageStream *imgStr;
1354  cairo_matrix_t matrix;
1355  int is_identity_transform;
1356
1357  buffer = (unsigned char *)gmalloc (width * height * 4);
1358
1359  /* TODO: Do we want to cache these? */
1360  imgStr = new ImageStream(str, width,
1361                           colorMap->getNumPixelComps(),
1362                           colorMap->getBits());
1363  imgStr->reset();
1364 
1365  /* ICCBased color space doesn't do any color correction
1366   * so check its underlying color space as well */
1367  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
1368                  colorMap->getColorSpace()->getMode() == csICCBased && 
1369                  ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB;
1370
1371  for (y = 0; y < height; y++) {
1372    dest = (unsigned int *) (buffer + y * 4 * width);
1373    pix = imgStr->getLine();
1374    colorMap->getRGBLine (pix, dest, width);
1375  }
1376
1377  image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_RGB24,
1378                                                 width, height, width * 4);
1379
1380  if (image == NULL) {
1381    delete imgStr;
1382    return;
1383  }
1384  pattern = cairo_pattern_create_for_surface (image);
1385  maskPattern = cairo_pattern_create_for_surface (maskImage);
1386  if (pattern == NULL) {
1387    delete imgStr;
1388    return;
1389  }
1390
1391  LOG (printf ("drawMaskedImage %dx%d\n", width, height));
1392
1393  cairo_matrix_init_translate (&matrix, 0, height);
1394  cairo_matrix_scale (&matrix, width, -height);
1395
1396  /* scale the mask to the size of the image unlike softMask */
1397  cairo_pattern_set_matrix (pattern, &matrix);
1398  cairo_pattern_set_matrix (maskPattern, &matrix);
1399
1400  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BILINEAR);
1401  cairo_set_source (cairo, pattern);
1402  cairo_mask (cairo, maskPattern);
1403
1404  if (cairo_shape) {
1405#if 0
1406    cairo_rectangle (cairo_shape, 0., 0., width, height);
1407    cairo_fill (cairo_shape);
1408#else
1409    cairo_save (cairo_shape);
1410    /* this should draw a rectangle the size of the image
1411     * we use this instead of rect,fill because of the lack
1412     * of EXTEND_PAD */
1413    /* NOTE: this will multiply the edges of the image twice */
1414    cairo_set_source (cairo_shape, pattern);
1415    cairo_mask (cairo_shape, pattern);
1416    cairo_restore (cairo_shape);
1417#endif
1418  }
1419
1420  cairo_pattern_destroy (maskPattern);
1421  cairo_surface_destroy (maskImage);
1422  cairo_pattern_destroy (pattern);
1423  cairo_surface_destroy (image);
1424  free (buffer);
1425  free (maskBuffer);
1426  delete imgStr;
1427}
1428
1429
1430//XXX: is this affect by AIS(alpha is shape)?
1431void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1432                                int width, int height,
1433                                GfxImageColorMap *colorMap,
1434                                Stream *maskStr,
1435                                int maskWidth, int maskHeight,
1436                                GfxImageColorMap *maskColorMap)
1437{
1438  ImageStream *maskImgStr;
1439  maskImgStr = new ImageStream(maskStr, maskWidth,
1440                                       maskColorMap->getNumPixelComps(),
1441                                       maskColorMap->getBits());
1442  maskImgStr->reset();
1443
1444  int row_stride = (maskWidth + 3) & ~3;
1445  unsigned char *maskBuffer;
1446  maskBuffer = (unsigned char *)gmalloc (row_stride * maskHeight);
1447  unsigned char *maskDest;
1448  cairo_surface_t *maskImage;
1449  cairo_pattern_t *maskPattern;
1450  Guchar *pix;
1451  int y;
1452  for (y = 0; y < maskHeight; y++) {
1453    maskDest = (unsigned char *) (maskBuffer + y * row_stride);
1454    pix = maskImgStr->getLine();
1455    maskColorMap->getGrayLine (pix, maskDest, maskWidth);
1456  }
1457
1458  maskImage = cairo_image_surface_create_for_data (maskBuffer, CAIRO_FORMAT_A8,
1459                                                 maskWidth, maskHeight, row_stride);
1460
1461  delete maskImgStr;
1462  maskStr->close();
1463
1464  unsigned char *buffer;
1465  unsigned int *dest;
1466  cairo_surface_t *image;
1467  cairo_pattern_t *pattern;
1468  ImageStream *imgStr;
1469  cairo_matrix_t matrix;
1470  cairo_matrix_t maskMatrix;
1471  int is_identity_transform;
1472
1473  buffer = (unsigned char *)gmalloc (width * height * 4);
1474
1475  /* TODO: Do we want to cache these? */
1476  imgStr = new ImageStream(str, width,
1477                           colorMap->getNumPixelComps(),
1478                           colorMap->getBits());
1479  imgStr->reset();
1480 
1481  /* ICCBased color space doesn't do any color correction
1482   * so check its underlying color space as well */
1483  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
1484                  colorMap->getColorSpace()->getMode() == csICCBased && 
1485                  ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB;
1486
1487  for (y = 0; y < height; y++) {
1488    dest = (unsigned int *) (buffer + y * 4 * width);
1489    pix = imgStr->getLine();
1490    colorMap->getRGBLine (pix, dest, width);
1491  }
1492
1493  image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_RGB24,
1494                                                 width, height, width * 4);
1495
1496  if (image == NULL) {
1497    delete imgStr;
1498    return;
1499  }
1500  pattern = cairo_pattern_create_for_surface (image);
1501  maskPattern = cairo_pattern_create_for_surface (maskImage);
1502  if (pattern == NULL) {
1503    delete imgStr;
1504    return;
1505  }
1506
1507  LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height));
1508
1509  cairo_matrix_init_translate (&matrix, 0, height);
1510  cairo_matrix_scale (&matrix, width, -height);
1511
1512  cairo_matrix_init_translate (&maskMatrix, 0, maskHeight);
1513  cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight);
1514
1515  cairo_pattern_set_matrix (pattern, &matrix);
1516  cairo_pattern_set_matrix (maskPattern, &maskMatrix);
1517
1518  //XXX: should set mask filter
1519  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BILINEAR);
1520  cairo_pattern_set_filter (maskPattern, CAIRO_FILTER_BILINEAR);
1521  cairo_set_source (cairo, pattern);
1522  cairo_mask (cairo, maskPattern);
1523
1524  if (cairo_shape) {
1525#if 0
1526    cairo_rectangle (cairo_shape, 0., 0., width, height);
1527    cairo_fill (cairo_shape);
1528#else
1529    cairo_save (cairo_shape);
1530    /* this should draw a rectangle the size of the image
1531     * we use this instead of rect,fill because of the lack
1532     * of EXTEND_PAD */
1533    /* NOTE: this will multiply the edges of the image twice */
1534    cairo_set_source (cairo_shape, pattern);
1535    cairo_mask (cairo_shape, pattern);
1536    cairo_restore (cairo_shape);
1537#endif
1538  }
1539
1540  cairo_pattern_destroy (maskPattern);
1541  cairo_surface_destroy (maskImage);
1542  cairo_pattern_destroy (pattern);
1543  cairo_surface_destroy (image);
1544  free (buffer);
1545  free (maskBuffer);
1546
1547  delete imgStr;
1548}
1549void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1550                                int width, int height,
1551                                GfxImageColorMap *colorMap,
1552                                int *maskColors, GBool inlineImg)
1553{
1554  unsigned char *buffer;
1555  unsigned int *dest;
1556  cairo_surface_t *image;
1557  cairo_pattern_t *pattern;
1558  int x, y;
1559  ImageStream *imgStr;
1560  Guchar *pix;
1561  int i;
1562  cairo_matrix_t matrix;
1563  int is_identity_transform;
1564 
1565  buffer = (unsigned char *)gmalloc (width * height * 4);
1566
1567  /* TODO: Do we want to cache these? */
1568  imgStr = new ImageStream(str, width,
1569                           colorMap->getNumPixelComps(),
1570                           colorMap->getBits());
1571  imgStr->reset();
1572 
1573  /* ICCBased color space doesn't do any color correction
1574   * so check its underlying color space as well */
1575  is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB ||
1576                  colorMap->getColorSpace()->getMode() == csICCBased && 
1577                  ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB;
1578
1579  if (maskColors) {
1580    for (y = 0; y < height; y++) {
1581      dest = (unsigned int *) (buffer + y * 4 * width);
1582      pix = imgStr->getLine();
1583      colorMap->getRGBLine (pix, dest, width);
1584
1585      for (x = 0; x < width; x++) {
1586        for (i = 0; i < colorMap->getNumPixelComps(); ++i) {
1587         
1588          if (pix[i] < maskColors[2*i] * 255||
1589              pix[i] > maskColors[2*i+1] * 255) {
1590            *dest = *dest | 0xff000000;
1591            break;
1592          }
1593        }
1594        pix += colorMap->getNumPixelComps();
1595        dest++;
1596      }
1597    }
1598
1599    image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_ARGB32,
1600                                                 width, height, width * 4);
1601  }
1602  else {
1603    for (y = 0; y < height; y++) {
1604      dest = (unsigned int *) (buffer + y * 4 * width);
1605      pix = imgStr->getLine();
1606      colorMap->getRGBLine (pix, dest, width);
1607    }
1608
1609    image = cairo_image_surface_create_for_data (buffer, CAIRO_FORMAT_RGB24,
1610                                                 width, height, width * 4);
1611  }
1612
1613  if (image == NULL) {
1614   delete imgStr;
1615   return;
1616  }
1617  pattern = cairo_pattern_create_for_surface (image);
1618  if (pattern == NULL) {
1619    delete imgStr;
1620    return;
1621  }
1622
1623  LOG (printf ("drawImageMask %dx%d\n", width, height));
1624 
1625  cairo_matrix_init_translate (&matrix, 0, height);
1626  cairo_matrix_scale (&matrix, width, -height);
1627
1628  cairo_pattern_set_matrix (pattern, &matrix);
1629
1630  cairo_pattern_set_filter (pattern, CAIRO_FILTER_BILINEAR);
1631  cairo_set_source (cairo, pattern);
1632  cairo_paint (cairo);
1633
1634  if (cairo_shape) {
1635#if 0
1636    cairo_rectangle (cairo_shape, 0., 0., width, height);
1637    cairo_fill (cairo_shape);
1638#else
1639    cairo_save (cairo_shape);
1640    /* this should draw a rectangle the size of the image
1641     * we use this instead of rect,fill because of the lack
1642     * of EXTEND_PAD */
1643    /* NOTE: this will multiply the edges of the image twice */
1644    cairo_set_source (cairo_shape, pattern);
1645    cairo_paint(cairo_shape);
1646    cairo_restore (cairo_shape);
1647#endif
1648  }
1649
1650  cairo_pattern_destroy (pattern);
1651  cairo_surface_destroy (image);
1652  free (buffer);
1653  delete imgStr;
1654}
1655
1656
1657//------------------------------------------------------------------------
1658// ImageOutputDev
1659//------------------------------------------------------------------------
1660
1661CairoImageOutputDev::CairoImageOutputDev()
1662{
1663  images = NULL;
1664  numImages = 0;
1665  size = 0;
1666  imgDrawCbk = NULL;
1667  imgDrawCbkData = NULL;
1668}
1669
1670CairoImageOutputDev::~CairoImageOutputDev()
1671{
1672  int i;
1673
1674  for (i = 0; i < numImages; i++)
1675    delete images[i];
1676  gfree (images);
1677}
1678
1679void CairoImageOutputDev::saveImage(CairoImage *image)
1680{ 
1681  if (numImages >= size) {
1682          size += 16;
1683          images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *));
1684  }
1685  images[numImages++] = image;
1686}       
1687
1688void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1689                                        int width, int height, GBool invert,
1690                                        GBool inlineImg)
1691{
1692  cairo_t *cr;
1693  cairo_surface_t *surface;
1694  double x1, y1, x2, y2;
1695  double *ctm;
1696  double mat[6];
1697  CairoImage *image;
1698
1699  ctm = state->getCTM();
1700 
1701  mat[0] = ctm[0];
1702  mat[1] = ctm[1];
1703  mat[2] = -ctm[2];
1704  mat[3] = -ctm[3];
1705  mat[4] = ctm[2] + ctm[4];
1706  mat[5] = ctm[3] + ctm[5];
1707  x1 = mat[4];
1708  y1 = mat[5];
1709  x2 = x1 + width;
1710  y2 = y1 + height;
1711
1712  image = new CairoImage (x1, y1, x2, y2);
1713  saveImage (image);
1714
1715  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
1716    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1717    cr = cairo_create (surface);
1718    setCairo (cr);
1719    cairo_translate (cr, 0, height);
1720    cairo_scale (cr, width, -height);
1721
1722    CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, inlineImg);
1723    image->setImage (surface);
1724
1725    setCairo (NULL);
1726    cairo_surface_destroy (surface);
1727    cairo_destroy (cr);
1728  }
1729}
1730
1731void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1732                                    int width, int height, GfxImageColorMap *colorMap,
1733                                    int *maskColors, GBool inlineImg)
1734{
1735  cairo_t *cr;
1736  cairo_surface_t *surface;
1737  double x1, y1, x2, y2;
1738  double *ctm;
1739  double mat[6];
1740  CairoImage *image;
1741
1742  ctm = state->getCTM();
1743 
1744  mat[0] = ctm[0];
1745  mat[1] = ctm[1];
1746  mat[2] = -ctm[2];
1747  mat[3] = -ctm[3];
1748  mat[4] = ctm[2] + ctm[4];
1749  mat[5] = ctm[3] + ctm[5];
1750  x1 = mat[4];
1751  y1 = mat[5];
1752  x2 = x1 + width;
1753  y2 = y1 + height;
1754
1755  image = new CairoImage (x1, y1, x2, y2);
1756  saveImage (image);
1757
1758  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
1759    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1760    cr = cairo_create (surface);
1761    setCairo (cr);
1762    cairo_translate (cr, 0, height);
1763    cairo_scale (cr, width, -height);
1764   
1765    CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, maskColors, inlineImg);
1766    image->setImage (surface);
1767   
1768    setCairo (NULL);
1769    cairo_surface_destroy (surface);
1770    cairo_destroy (cr);
1771  }
1772}
1773
1774void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1775                                              int width, int height,
1776                                              GfxImageColorMap *colorMap,
1777                                              Stream *maskStr,
1778                                              int maskWidth, int maskHeight,
1779                                              GfxImageColorMap *maskColorMap)
1780{
1781  cairo_t *cr;
1782  cairo_surface_t *surface;
1783  double x1, y1, x2, y2;
1784  double *ctm;
1785  double mat[6];
1786  CairoImage *image;
1787
1788  ctm = state->getCTM();
1789 
1790  mat[0] = ctm[0];
1791  mat[1] = ctm[1];
1792  mat[2] = -ctm[2];
1793  mat[3] = -ctm[3];
1794  mat[4] = ctm[2] + ctm[4];
1795  mat[5] = ctm[3] + ctm[5];
1796  x1 = mat[4];
1797  y1 = mat[5];
1798  x2 = x1 + width;
1799  y2 = y1 + height;
1800
1801  image = new CairoImage (x1, y1, x2, y2);
1802  saveImage (image);
1803
1804  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
1805    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1806    cr = cairo_create (surface);
1807    setCairo (cr);
1808    cairo_translate (cr, 0, height);
1809    cairo_scale (cr, width, -height);
1810   
1811    CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap,
1812                                        maskStr, maskWidth, maskHeight, maskColorMap);
1813    image->setImage (surface);
1814   
1815    setCairo (NULL);
1816    cairo_surface_destroy (surface);
1817    cairo_destroy (cr);
1818  }
1819}
1820
1821void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1822                                          int width, int height,
1823                                          GfxImageColorMap *colorMap,
1824                                          Stream *maskStr,
1825                                          int maskWidth, int maskHeight,
1826                                          GBool maskInvert)
1827{
1828  cairo_t *cr;
1829  cairo_surface_t *surface;
1830  double x1, y1, x2, y2;
1831  double *ctm;
1832  double mat[6];
1833  CairoImage *image;
1834
1835  ctm = state->getCTM();
1836 
1837  mat[0] = ctm[0];
1838  mat[1] = ctm[1];
1839  mat[2] = -ctm[2];
1840  mat[3] = -ctm[3];
1841  mat[4] = ctm[2] + ctm[4];
1842  mat[5] = ctm[3] + ctm[5];
1843  x1 = mat[4];
1844  y1 = mat[5];
1845  x2 = x1 + width;
1846  y2 = y1 + height;
1847
1848  image = new CairoImage (x1, y1, x2, y2);
1849  saveImage (image);
1850
1851  if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) {
1852    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1853    cr = cairo_create (surface);
1854    setCairo (cr);
1855    cairo_translate (cr, 0, height);
1856    cairo_scale (cr, width, -height);
1857   
1858    CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap,
1859                                    maskStr, maskWidth, maskHeight, maskInvert);
1860    image->setImage (surface);
1861   
1862    setCairo (NULL);
1863    cairo_surface_destroy (surface);
1864    cairo_destroy (cr);
1865  }
1866}
Note: See TracBrowser for help on using the repository browser.