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

Last change on this file since 290 was 290, checked in by rbri, 11 years ago

PDF plugin: Poppler library updated to version 0.12.4

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