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

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

PDF plugin: Poppler library updated to version 0.10.0

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