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

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

PDF plugin: Poppler library updated to version 0.10.2

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