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

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

PDF plugin: Poppler library updated to version 0.8.7

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