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

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

PDF plugin: poppler library updated to version 0.8.3

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