Merge lp:~matttbe/ubuntu/raring/poppler/lp1072129 into lp:ubuntu/raring/poppler
- Raring (13.04)
- lp1072129
- Merge into raring
Proposed by
Matthieu Baerts
Status: | Merged |
---|---|
Merged at revision: | 120 |
Proposed branch: | lp:~matttbe/ubuntu/raring/poppler/lp1072129 |
Merge into: | lp:ubuntu/raring/poppler |
Diff against target: |
3231 lines (+3184/-4) 6 files modified
.pc/applied-patches (+1/-0) .pc/git_gouraud_shading_support.patch/poppler/CairoOutputDev.cc (+3121/-0) debian/changelog (+8/-0) debian/patches/git_gouraud_shading_support.patch (+38/-0) debian/patches/series (+1/-0) poppler/CairoOutputDev.cc (+15/-4) |
To merge this branch: | bzr merge lp:~matttbe/ubuntu/raring/poppler/lp1072129 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Sponsors | Pending | ||
Review via email: mp+134177@code.launchpad.net |
Commit message
Description of the change
Hello,
Due to a bug in poppler, Evince crash when loading some PDF files (LP: #1072129).
This new version simply adds the upstream patch in order to fix this bug.
For more details about this new version, please have a look at this bug #1072129.
Thank you for your help! :)
PS: You can easily test the new version by using these packages:
- Ubuntu Raring 13.04: https:/
- Ubuntu Quantal 12.10: https:/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.pc/applied-patches' |
2 | --- .pc/applied-patches 2012-08-14 13:11:05 +0000 |
3 | +++ .pc/applied-patches 2012-11-13 19:26:27 +0000 |
4 | @@ -1,2 +1,3 @@ |
5 | ltmain-as-needed.diff |
6 | qt4-visibility.diff |
7 | +git_gouraud_shading_support.patch |
8 | |
9 | === added directory '.pc/git_gouraud_shading_support.patch' |
10 | === added directory '.pc/git_gouraud_shading_support.patch/poppler' |
11 | === added file '.pc/git_gouraud_shading_support.patch/poppler/CairoOutputDev.cc' |
12 | --- .pc/git_gouraud_shading_support.patch/poppler/CairoOutputDev.cc 1970-01-01 00:00:00 +0000 |
13 | +++ .pc/git_gouraud_shading_support.patch/poppler/CairoOutputDev.cc 2012-11-13 19:26:27 +0000 |
14 | @@ -0,0 +1,3121 @@ |
15 | +//======================================================================== |
16 | +// |
17 | +// CairoOutputDev.cc |
18 | +// |
19 | +// Copyright 2003 Glyph & Cog, LLC |
20 | +// Copyright 2004 Red Hat, Inc |
21 | +// |
22 | +//======================================================================== |
23 | + |
24 | +//======================================================================== |
25 | +// |
26 | +// Modified under the Poppler project - http://poppler.freedesktop.org |
27 | +// |
28 | +// All changes made under the Poppler project to this file are licensed |
29 | +// under GPL version 2 or later |
30 | +// |
31 | +// Copyright (C) 2005-2008 Jeff Muizelaar <jeff@infidigm.net> |
32 | +// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> |
33 | +// Copyright (C) 2005, 2009, 2012 Albert Astals Cid <aacid@kde.org> |
34 | +// Copyright (C) 2005 Nickolay V. Shmyrev <nshmyrev@yandex.ru> |
35 | +// Copyright (C) 2006-2011 Carlos Garcia Campos <carlosgc@gnome.org> |
36 | +// Copyright (C) 2008 Carl Worth <cworth@cworth.org> |
37 | +// Copyright (C) 2008-2012 Adrian Johnson <ajohnson@redneon.com> |
38 | +// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu> |
39 | +// Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk> |
40 | +// Copyright (C) 2008 Hib Eris <hib@hiberis.nl> |
41 | +// Copyright (C) 2009, 2010 David Benjamin <davidben@mit.edu> |
42 | +// Copyright (C) 2011, 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
43 | +// Copyright (C) 2012 Patrick Pfeifer <p2000@mailinator.com> |
44 | +// |
45 | +// To see a description of the changes please see the Changelog file that |
46 | +// came with your tarball or type make ChangeLog if you are building from git |
47 | +// |
48 | +//======================================================================== |
49 | + |
50 | +#include <config.h> |
51 | + |
52 | +#ifdef USE_GCC_PRAGMAS |
53 | +#pragma implementation |
54 | +#endif |
55 | + |
56 | +#include <string.h> |
57 | +#include <math.h> |
58 | +#include <assert.h> |
59 | +#include <cairo.h> |
60 | + |
61 | +#include "goo/gfile.h" |
62 | +#include "goo/gtypes_p.h" |
63 | +#include "GlobalParams.h" |
64 | +#include "Error.h" |
65 | +#include "Object.h" |
66 | +#include "Gfx.h" |
67 | +#include "GfxState.h" |
68 | +#include "GfxFont.h" |
69 | +#include "Page.h" |
70 | +#include "Link.h" |
71 | +#include "FontEncodingTables.h" |
72 | +#include "PDFDocEncoding.h" |
73 | +#include <fofi/FoFiTrueType.h> |
74 | +#include <splash/SplashBitmap.h> |
75 | +#include "CairoOutputDev.h" |
76 | +#include "CairoFontEngine.h" |
77 | +#include "CairoRescaleBox.h" |
78 | +#include "UTF8.h" |
79 | +//------------------------------------------------------------------------ |
80 | + |
81 | +// #define LOG_CAIRO |
82 | + |
83 | +#ifdef LOG_CAIRO |
84 | +#define LOG(x) (x) |
85 | +#else |
86 | +#define LOG(x) |
87 | +#endif |
88 | + |
89 | +static inline void printMatrix(cairo_matrix_t *matrix){ |
90 | + printf("%f %f, %f %f (%f %f)\n", matrix->xx, matrix->yx, |
91 | + matrix->xy, matrix->yy, |
92 | + matrix->x0, matrix->y0); |
93 | +} |
94 | + |
95 | + |
96 | +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) |
97 | +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) |
98 | + |
99 | + |
100 | +//------------------------------------------------------------------------ |
101 | +// CairoImage |
102 | +//------------------------------------------------------------------------ |
103 | + |
104 | +CairoImage::CairoImage (double x1, double y1, double x2, double y2) { |
105 | + this->image = NULL; |
106 | + this->x1 = x1; |
107 | + this->y1 = y1; |
108 | + this->x2 = x2; |
109 | + this->y2 = y2; |
110 | +} |
111 | + |
112 | +CairoImage::~CairoImage () { |
113 | + if (image) |
114 | + cairo_surface_destroy (image); |
115 | +} |
116 | + |
117 | +void CairoImage::setImage (cairo_surface_t *image) { |
118 | + if (this->image) |
119 | + cairo_surface_destroy (this->image); |
120 | + this->image = cairo_surface_reference (image); |
121 | +} |
122 | + |
123 | +//------------------------------------------------------------------------ |
124 | +// CairoOutputDev |
125 | +//------------------------------------------------------------------------ |
126 | + |
127 | +// We cannot tie the lifetime of an FT_Library object to that of |
128 | +// CairoOutputDev, since any FT_Faces created with it may end up with a |
129 | +// reference by Cairo which can be held long after the CairoOutputDev is |
130 | +// deleted. The simplest way to avoid problems is to never tear down the |
131 | +// FT_Library instance; to avoid leaks, just use a single global instance |
132 | +// initialized the first time it is needed. |
133 | +FT_Library CairoOutputDev::ft_lib; |
134 | +GBool CairoOutputDev::ft_lib_initialized = gFalse; |
135 | + |
136 | +CairoOutputDev::CairoOutputDev() { |
137 | + doc = NULL; |
138 | + |
139 | + if (!ft_lib_initialized) { |
140 | + FT_Init_FreeType(&ft_lib); |
141 | + ft_lib_initialized = gTrue; |
142 | + } |
143 | + |
144 | + fontEngine = NULL; |
145 | + fontEngine_owner = gFalse; |
146 | + glyphs = NULL; |
147 | + fill_pattern = NULL; |
148 | + fill_color.r = fill_color.g = fill_color.b = 0; |
149 | + stroke_pattern = NULL; |
150 | + stroke_color.r = stroke_color.g = stroke_color.b = 0; |
151 | + stroke_opacity = 1.0; |
152 | + fill_opacity = 1.0; |
153 | + textClipPath = NULL; |
154 | + strokePathClip = NULL; |
155 | + cairo = NULL; |
156 | + currentFont = NULL; |
157 | + prescaleImages = gTrue; |
158 | + printing = gTrue; |
159 | + use_show_text_glyphs = gFalse; |
160 | + inType3Char = gFalse; |
161 | + t3_glyph_has_bbox = gFalse; |
162 | + |
163 | + groupColorSpaceStack = NULL; |
164 | + maskStack = NULL; |
165 | + group = NULL; |
166 | + mask = NULL; |
167 | + shape = NULL; |
168 | + cairo_shape = NULL; |
169 | + knockoutCount = 0; |
170 | + |
171 | + text = NULL; |
172 | + actualText = NULL; |
173 | + |
174 | + // the SA parameter supposedly defaults to false, but Acrobat |
175 | + // apparently hardwires it to true |
176 | + stroke_adjust = globalParams->getStrokeAdjust(); |
177 | + align_stroke_coords = gFalse; |
178 | + adjusted_stroke_width = gFalse; |
179 | +} |
180 | + |
181 | +CairoOutputDev::~CairoOutputDev() { |
182 | + if (fontEngine_owner && fontEngine) { |
183 | + delete fontEngine; |
184 | + } |
185 | + |
186 | + if (cairo) |
187 | + cairo_destroy (cairo); |
188 | + cairo_pattern_destroy (stroke_pattern); |
189 | + cairo_pattern_destroy (fill_pattern); |
190 | + if (group) |
191 | + cairo_pattern_destroy (group); |
192 | + if (mask) |
193 | + cairo_pattern_destroy (mask); |
194 | + if (shape) |
195 | + cairo_pattern_destroy (shape); |
196 | + if (text) |
197 | + text->decRefCnt(); |
198 | + if (actualText) |
199 | + delete actualText; |
200 | +} |
201 | + |
202 | +void CairoOutputDev::setCairo(cairo_t *cairo) |
203 | +{ |
204 | + if (this->cairo != NULL) { |
205 | + cairo_status_t status = cairo_status (this->cairo); |
206 | + if (status) { |
207 | + error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status)); |
208 | + } |
209 | + cairo_destroy (this->cairo); |
210 | + assert(!cairo_shape); |
211 | + } |
212 | + if (cairo != NULL) { |
213 | + this->cairo = cairo_reference (cairo); |
214 | + /* save the initial matrix so that we can use it for type3 fonts. */ |
215 | + //XXX: is this sufficient? could we miss changes to the matrix somehow? |
216 | + cairo_get_matrix(cairo, &orig_matrix); |
217 | + } else { |
218 | + this->cairo = NULL; |
219 | + this->cairo_shape = NULL; |
220 | + } |
221 | +} |
222 | + |
223 | +void CairoOutputDev::setTextPage(TextPage *text) |
224 | +{ |
225 | + if (this->text) |
226 | + this->text->decRefCnt(); |
227 | + if (actualText) |
228 | + delete actualText; |
229 | + if (text) { |
230 | + this->text = text; |
231 | + this->text->incRefCnt(); |
232 | + actualText = new ActualText(text); |
233 | + } else { |
234 | + this->text = NULL; |
235 | + actualText = NULL; |
236 | + } |
237 | +} |
238 | + |
239 | +void CairoOutputDev::startDoc(PDFDoc *docA, |
240 | + CairoFontEngine *parentFontEngine) { |
241 | + doc = docA; |
242 | + if (parentFontEngine) { |
243 | + fontEngine = parentFontEngine; |
244 | + } else { |
245 | + if (fontEngine) { |
246 | + delete fontEngine; |
247 | + } |
248 | + fontEngine = new CairoFontEngine(ft_lib); |
249 | + fontEngine_owner = gTrue; |
250 | + } |
251 | +} |
252 | + |
253 | +void CairoOutputDev::startPage(int pageNum, GfxState *state) { |
254 | + /* set up some per page defaults */ |
255 | + cairo_pattern_destroy(fill_pattern); |
256 | + cairo_pattern_destroy(stroke_pattern); |
257 | + |
258 | + fill_pattern = cairo_pattern_create_rgb(0., 0., 0.); |
259 | + stroke_pattern = cairo_pattern_reference(fill_pattern); |
260 | + |
261 | + if (text) |
262 | + text->startPage(state); |
263 | +} |
264 | + |
265 | +void CairoOutputDev::endPage() { |
266 | + if (text) { |
267 | + text->endPage(); |
268 | + text->coalesce(gTrue, 0, gFalse); |
269 | + } |
270 | +} |
271 | + |
272 | +void CairoOutputDev::saveState(GfxState *state) { |
273 | + LOG(printf ("save\n")); |
274 | + cairo_save (cairo); |
275 | + if (cairo_shape) |
276 | + cairo_save (cairo_shape); |
277 | + |
278 | + MaskStack *ms = new MaskStack; |
279 | + ms->mask = cairo_pattern_reference(mask); |
280 | + ms->mask_matrix = mask_matrix; |
281 | + ms->next = maskStack; |
282 | + maskStack = ms; |
283 | +} |
284 | + |
285 | +void CairoOutputDev::restoreState(GfxState *state) { |
286 | + LOG(printf ("restore\n")); |
287 | + cairo_restore (cairo); |
288 | + if (cairo_shape) |
289 | + cairo_restore (cairo_shape); |
290 | + |
291 | + /* These aren't restored by cairo_restore() since we keep them in |
292 | + * the output device. */ |
293 | + updateFillColor(state); |
294 | + updateStrokeColor(state); |
295 | + updateFillOpacity(state); |
296 | + updateStrokeOpacity(state); |
297 | + updateBlendMode(state); |
298 | + |
299 | + MaskStack* ms = maskStack; |
300 | + if (ms) { |
301 | + if (mask) |
302 | + cairo_pattern_destroy(mask); |
303 | + mask = ms->mask; |
304 | + mask_matrix = ms->mask_matrix; |
305 | + maskStack = ms->next; |
306 | + delete ms; |
307 | + } |
308 | +} |
309 | + |
310 | +void CairoOutputDev::updateAll(GfxState *state) { |
311 | + updateLineDash(state); |
312 | + updateLineJoin(state); |
313 | + updateLineCap(state); |
314 | + updateLineWidth(state); |
315 | + updateFlatness(state); |
316 | + updateMiterLimit(state); |
317 | + updateFillColor(state); |
318 | + updateStrokeColor(state); |
319 | + updateFillOpacity(state); |
320 | + updateStrokeOpacity(state); |
321 | + updateBlendMode(state); |
322 | + needFontUpdate = gTrue; |
323 | + if (text) |
324 | + text->updateFont(state); |
325 | +} |
326 | + |
327 | +void CairoOutputDev::setDefaultCTM(double *ctm) { |
328 | + cairo_matrix_t matrix; |
329 | + matrix.xx = ctm[0]; |
330 | + matrix.yx = ctm[1]; |
331 | + matrix.xy = ctm[2]; |
332 | + matrix.yy = ctm[3]; |
333 | + matrix.x0 = ctm[4]; |
334 | + matrix.y0 = ctm[5]; |
335 | + |
336 | + cairo_transform (cairo, &matrix); |
337 | + if (cairo_shape) |
338 | + cairo_transform (cairo_shape, &matrix); |
339 | + |
340 | + OutputDev::setDefaultCTM(ctm); |
341 | +} |
342 | + |
343 | +void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12, |
344 | + double m21, double m22, |
345 | + double m31, double m32) { |
346 | + cairo_matrix_t matrix, invert_matrix; |
347 | + matrix.xx = m11; |
348 | + matrix.yx = m12; |
349 | + matrix.xy = m21; |
350 | + matrix.yy = m22; |
351 | + matrix.x0 = m31; |
352 | + matrix.y0 = m32; |
353 | + |
354 | + /* Make sure the matrix is invertible before setting it. |
355 | + * cairo will blow up if we give it a matrix that's not |
356 | + * invertible, so we need to check before passing it |
357 | + * to cairo_transform. Ignoring it is likely to give better |
358 | + * results than not rendering anything at all. See #14398 |
359 | + * |
360 | + * Ideally, we could do the cairo_transform |
361 | + * and then check if anything went wrong and fix it then |
362 | + * instead of having to invert the matrix. */ |
363 | + invert_matrix = matrix; |
364 | + if (cairo_matrix_invert(&invert_matrix)) { |
365 | + error(errSyntaxWarning, -1, "matrix not invertible\n"); |
366 | + return; |
367 | + } |
368 | + |
369 | + cairo_transform (cairo, &matrix); |
370 | + if (cairo_shape) |
371 | + cairo_transform (cairo_shape, &matrix); |
372 | + updateLineDash(state); |
373 | + updateLineJoin(state); |
374 | + updateLineCap(state); |
375 | + updateLineWidth(state); |
376 | +} |
377 | + |
378 | +void CairoOutputDev::updateLineDash(GfxState *state) { |
379 | + double *dashPattern; |
380 | + int dashLength; |
381 | + double dashStart; |
382 | + |
383 | + state->getLineDash(&dashPattern, &dashLength, &dashStart); |
384 | + cairo_set_dash (cairo, dashPattern, dashLength, dashStart); |
385 | + if (cairo_shape) |
386 | + cairo_set_dash (cairo_shape, dashPattern, dashLength, dashStart); |
387 | +} |
388 | + |
389 | +void CairoOutputDev::updateFlatness(GfxState *state) { |
390 | + // cairo_set_tolerance (cairo, state->getFlatness()); |
391 | +} |
392 | + |
393 | +void CairoOutputDev::updateLineJoin(GfxState *state) { |
394 | + switch (state->getLineJoin()) { |
395 | + case 0: |
396 | + cairo_set_line_join (cairo, CAIRO_LINE_JOIN_MITER); |
397 | + break; |
398 | + case 1: |
399 | + cairo_set_line_join (cairo, CAIRO_LINE_JOIN_ROUND); |
400 | + break; |
401 | + case 2: |
402 | + cairo_set_line_join (cairo, CAIRO_LINE_JOIN_BEVEL); |
403 | + break; |
404 | + } |
405 | + if (cairo_shape) |
406 | + cairo_set_line_join (cairo_shape, cairo_get_line_join(cairo)); |
407 | +} |
408 | + |
409 | +void CairoOutputDev::updateLineCap(GfxState *state) { |
410 | + switch (state->getLineCap()) { |
411 | + case 0: |
412 | + cairo_set_line_cap (cairo, CAIRO_LINE_CAP_BUTT); |
413 | + break; |
414 | + case 1: |
415 | + cairo_set_line_cap (cairo, CAIRO_LINE_CAP_ROUND); |
416 | + break; |
417 | + case 2: |
418 | + cairo_set_line_cap (cairo, CAIRO_LINE_CAP_SQUARE); |
419 | + break; |
420 | + } |
421 | + if (cairo_shape) |
422 | + cairo_set_line_cap (cairo_shape, cairo_get_line_cap(cairo)); |
423 | +} |
424 | + |
425 | +void CairoOutputDev::updateMiterLimit(GfxState *state) { |
426 | + cairo_set_miter_limit (cairo, state->getMiterLimit()); |
427 | + if (cairo_shape) |
428 | + cairo_set_miter_limit (cairo_shape, state->getMiterLimit()); |
429 | +} |
430 | + |
431 | +void CairoOutputDev::updateLineWidth(GfxState *state) { |
432 | + LOG(printf ("line width: %f\n", state->getLineWidth())); |
433 | + adjusted_stroke_width = gFalse; |
434 | + double width = state->getLineWidth(); |
435 | + if (stroke_adjust && !printing) { |
436 | + double x, y; |
437 | + x = y = width; |
438 | + |
439 | + /* find out line width in device units */ |
440 | + cairo_user_to_device_distance(cairo, &x, &y); |
441 | + if (fabs(x) <= 1.0 && fabs(y) <= 1.0) { |
442 | + /* adjust width to at least one device pixel */ |
443 | + x = y = 1.0; |
444 | + cairo_device_to_user_distance(cairo, &x, &y); |
445 | + width = MIN(fabs(x),fabs(y)); |
446 | + adjusted_stroke_width = gTrue; |
447 | + } |
448 | + } else if (width == 0.0) { |
449 | + /* Cairo does not support 0 line width == 1 device pixel. Find out |
450 | + * how big pixels (device unit) are in the x and y |
451 | + * directions. Choose the smaller of the two as our line width. |
452 | + */ |
453 | + double x = 1.0, y = 1.0; |
454 | + if (printing) { |
455 | + // assume printer pixel size is 1/600 inch |
456 | + x = 72.0/600; |
457 | + y = 72.0/600; |
458 | + } |
459 | + cairo_device_to_user_distance(cairo, &x, &y); |
460 | + width = MIN(fabs(x),fabs(y)); |
461 | + } |
462 | + cairo_set_line_width (cairo, width); |
463 | + if (cairo_shape) |
464 | + cairo_set_line_width (cairo_shape, cairo_get_line_width (cairo)); |
465 | +} |
466 | + |
467 | +void CairoOutputDev::updateFillColor(GfxState *state) { |
468 | + GfxRGB color = fill_color; |
469 | + |
470 | + state->getFillRGB(&fill_color); |
471 | + if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || |
472 | + color.r != fill_color.r || |
473 | + color.g != fill_color.g || |
474 | + color.b != fill_color.b) |
475 | + { |
476 | + cairo_pattern_destroy(fill_pattern); |
477 | + fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), |
478 | + colToDbl(fill_color.g), |
479 | + colToDbl(fill_color.b), |
480 | + fill_opacity); |
481 | + |
482 | + LOG(printf ("fill color: %d %d %d\n", |
483 | + fill_color.r, fill_color.g, fill_color.b)); |
484 | + } |
485 | +} |
486 | + |
487 | +void CairoOutputDev::updateStrokeColor(GfxState *state) { |
488 | + GfxRGB color = stroke_color; |
489 | + |
490 | + state->getStrokeRGB(&stroke_color); |
491 | + if (cairo_pattern_get_type (fill_pattern) != CAIRO_PATTERN_TYPE_SOLID || |
492 | + color.r != stroke_color.r || |
493 | + color.g != stroke_color.g || |
494 | + color.b != stroke_color.b) |
495 | + { |
496 | + cairo_pattern_destroy(stroke_pattern); |
497 | + stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), |
498 | + colToDbl(stroke_color.g), |
499 | + colToDbl(stroke_color.b), |
500 | + stroke_opacity); |
501 | + |
502 | + LOG(printf ("stroke color: %d %d %d\n", |
503 | + stroke_color.r, stroke_color.g, stroke_color.b)); |
504 | + } |
505 | +} |
506 | + |
507 | +void CairoOutputDev::updateFillOpacity(GfxState *state) { |
508 | + double opacity = fill_opacity; |
509 | + |
510 | + fill_opacity = state->getFillOpacity(); |
511 | + if (opacity != fill_opacity) { |
512 | + cairo_pattern_destroy(fill_pattern); |
513 | + fill_pattern = cairo_pattern_create_rgba(colToDbl(fill_color.r), |
514 | + colToDbl(fill_color.g), |
515 | + colToDbl(fill_color.b), |
516 | + fill_opacity); |
517 | + |
518 | + LOG(printf ("fill opacity: %f\n", fill_opacity)); |
519 | + } |
520 | +} |
521 | + |
522 | +void CairoOutputDev::updateStrokeOpacity(GfxState *state) { |
523 | + double opacity = stroke_opacity; |
524 | + |
525 | + stroke_opacity = state->getStrokeOpacity(); |
526 | + if (opacity != stroke_opacity) { |
527 | + cairo_pattern_destroy(stroke_pattern); |
528 | + stroke_pattern = cairo_pattern_create_rgba(colToDbl(stroke_color.r), |
529 | + colToDbl(stroke_color.g), |
530 | + colToDbl(stroke_color.b), |
531 | + stroke_opacity); |
532 | + |
533 | + LOG(printf ("stroke opacity: %f\n", stroke_opacity)); |
534 | + } |
535 | +} |
536 | + |
537 | +void CairoOutputDev::updateFillColorStop(GfxState *state, double offset) { |
538 | + state->getFillRGB(&fill_color); |
539 | + |
540 | + cairo_pattern_add_color_stop_rgba(fill_pattern, offset, |
541 | + colToDbl(fill_color.r), |
542 | + colToDbl(fill_color.g), |
543 | + colToDbl(fill_color.b), |
544 | + fill_opacity); |
545 | + LOG(printf ("fill color stop: %f (%d, %d, %d)\n", |
546 | + offset, fill_color.r, fill_color.g, fill_color.b)); |
547 | +} |
548 | + |
549 | +void CairoOutputDev::updateBlendMode(GfxState *state) { |
550 | + switch (state->getBlendMode()) { |
551 | + default: |
552 | + case gfxBlendNormal: |
553 | + cairo_set_operator (cairo, CAIRO_OPERATOR_OVER); |
554 | + break; |
555 | + case gfxBlendMultiply: |
556 | + cairo_set_operator (cairo, CAIRO_OPERATOR_MULTIPLY); |
557 | + break; |
558 | + case gfxBlendScreen: |
559 | + cairo_set_operator (cairo, CAIRO_OPERATOR_SCREEN); |
560 | + break; |
561 | + case gfxBlendOverlay: |
562 | + cairo_set_operator (cairo, CAIRO_OPERATOR_OVERLAY); |
563 | + break; |
564 | + case gfxBlendDarken: |
565 | + cairo_set_operator (cairo, CAIRO_OPERATOR_DARKEN); |
566 | + break; |
567 | + case gfxBlendLighten: |
568 | + cairo_set_operator (cairo, CAIRO_OPERATOR_LIGHTEN); |
569 | + break; |
570 | + case gfxBlendColorDodge: |
571 | + cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_DODGE); |
572 | + break; |
573 | + case gfxBlendColorBurn: |
574 | + cairo_set_operator (cairo, CAIRO_OPERATOR_COLOR_BURN); |
575 | + break; |
576 | + case gfxBlendHardLight: |
577 | + cairo_set_operator (cairo, CAIRO_OPERATOR_HARD_LIGHT); |
578 | + break; |
579 | + case gfxBlendSoftLight: |
580 | + cairo_set_operator (cairo, CAIRO_OPERATOR_SOFT_LIGHT); |
581 | + break; |
582 | + case gfxBlendDifference: |
583 | + cairo_set_operator (cairo, CAIRO_OPERATOR_DIFFERENCE); |
584 | + break; |
585 | + case gfxBlendExclusion: |
586 | + cairo_set_operator (cairo, CAIRO_OPERATOR_EXCLUSION); |
587 | + break; |
588 | + case gfxBlendHue: |
589 | + cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_HUE); |
590 | + break; |
591 | + case gfxBlendSaturation: |
592 | + cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_SATURATION); |
593 | + break; |
594 | + case gfxBlendColor: |
595 | + cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_COLOR); |
596 | + break; |
597 | + case gfxBlendLuminosity: |
598 | + cairo_set_operator (cairo, CAIRO_OPERATOR_HSL_LUMINOSITY); |
599 | + break; |
600 | + } |
601 | + LOG(printf ("blend mode: %d\n", (int)state->getBlendMode())); |
602 | +} |
603 | + |
604 | +void CairoOutputDev::updateFont(GfxState *state) { |
605 | + cairo_font_face_t *font_face; |
606 | + cairo_matrix_t matrix, invert_matrix; |
607 | + |
608 | + LOG(printf ("updateFont() font=%s\n", state->getFont()->getName()->getCString())); |
609 | + |
610 | + needFontUpdate = gFalse; |
611 | + |
612 | + //FIXME: use cairo font engine? |
613 | + if (text) |
614 | + text->updateFont(state); |
615 | + |
616 | + currentFont = fontEngine->getFont (state->getFont(), doc, printing); |
617 | + |
618 | + if (!currentFont) |
619 | + return; |
620 | + |
621 | + font_face = currentFont->getFontFace(); |
622 | + cairo_set_font_face (cairo, font_face); |
623 | + |
624 | + use_show_text_glyphs = state->getFont()->hasToUnicodeCMap() && |
625 | + cairo_surface_has_show_text_glyphs (cairo_get_target (cairo)); |
626 | + |
627 | + double fontSize = state->getFontSize(); |
628 | + double *m = state->getTextMat(); |
629 | + /* NOTE: adjusting by a constant is hack. The correct solution |
630 | + * is probably to use user-fonts and compute the scale on a per |
631 | + * glyph basis instead of for the entire font */ |
632 | + double w = currentFont->getSubstitutionCorrection(state->getFont()); |
633 | + matrix.xx = m[0] * fontSize * state->getHorizScaling() * w; |
634 | + matrix.yx = m[1] * fontSize * state->getHorizScaling() * w; |
635 | + matrix.xy = -m[2] * fontSize; |
636 | + matrix.yy = -m[3] * fontSize; |
637 | + matrix.x0 = 0; |
638 | + matrix.y0 = 0; |
639 | + |
640 | + LOG(printf ("font matrix: %f %f %f %f\n", matrix.xx, matrix.yx, matrix.xy, matrix.yy)); |
641 | + |
642 | + /* Make sure the font matrix is invertible before setting it. cairo |
643 | + * will blow up if we give it a matrix that's not invertible, so we |
644 | + * need to check before passing it to cairo_set_font_matrix. Ignoring it |
645 | + * is likely to give better results than not rendering anything at |
646 | + * all. See #18254. |
647 | + */ |
648 | + invert_matrix = matrix; |
649 | + if (cairo_matrix_invert(&invert_matrix)) { |
650 | + error(errSyntaxWarning, -1, "font matrix not invertible\n"); |
651 | + return; |
652 | + } |
653 | + |
654 | + cairo_set_font_matrix (cairo, &matrix); |
655 | +} |
656 | + |
657 | +/* Tolerance in pixels for checking if strokes are horizontal or vertical |
658 | + * lines in device space */ |
659 | +#define STROKE_COORD_TOLERANCE 0.5 |
660 | + |
661 | +/* Align stroke coordinate i if the point is the start or end of a |
662 | + * horizontal or vertical line */ |
663 | +void CairoOutputDev::alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y) |
664 | +{ |
665 | + double x1, y1, x2, y2; |
666 | + GBool align = gFalse; |
667 | + |
668 | + x1 = subpath->getX(i); |
669 | + y1 = subpath->getY(i); |
670 | + cairo_user_to_device (cairo, &x1, &y1); |
671 | + |
672 | + // Does the current coord and prev coord form a horiz or vert line? |
673 | + if (i > 0 && !subpath->getCurve(i - 1)) { |
674 | + x2 = subpath->getX(i - 1); |
675 | + y2 = subpath->getY(i - 1); |
676 | + cairo_user_to_device (cairo, &x2, &y2); |
677 | + if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) |
678 | + align = gTrue; |
679 | + } |
680 | + |
681 | + // Does the current coord and next coord form a horiz or vert line? |
682 | + if (i < subpath->getNumPoints() - 1 && !subpath->getCurve(i + 1)) { |
683 | + x2 = subpath->getX(i + 1); |
684 | + y2 = subpath->getY(i + 1); |
685 | + cairo_user_to_device (cairo, &x2, &y2); |
686 | + if (fabs(x2 - x1) < STROKE_COORD_TOLERANCE || fabs(y2 - y1) < STROKE_COORD_TOLERANCE) |
687 | + align = gTrue; |
688 | + } |
689 | + |
690 | + *x = subpath->getX(i); |
691 | + *y = subpath->getY(i); |
692 | + if (align) { |
693 | + /* see http://www.cairographics.org/FAQ/#sharp_lines */ |
694 | + cairo_user_to_device (cairo, x, y); |
695 | + *x = floor(*x) + 0.5; |
696 | + *y = floor(*y) + 0.5; |
697 | + cairo_device_to_user (cairo, x, y); |
698 | + } |
699 | +} |
700 | + |
701 | +#undef STROKE_COORD_TOLERANCE |
702 | + |
703 | +void CairoOutputDev::doPath(cairo_t *cairo, GfxState *state, GfxPath *path) { |
704 | + GfxSubpath *subpath; |
705 | + int i, j; |
706 | + double x, y; |
707 | + cairo_new_path (cairo); |
708 | + for (i = 0; i < path->getNumSubpaths(); ++i) { |
709 | + subpath = path->getSubpath(i); |
710 | + if (subpath->getNumPoints() > 0) { |
711 | + if (align_stroke_coords) { |
712 | + alignStrokeCoords(subpath, 0, &x, &y); |
713 | + } else { |
714 | + x = subpath->getX(0); |
715 | + y = subpath->getY(0); |
716 | + } |
717 | + cairo_move_to (cairo, x, y); |
718 | + j = 1; |
719 | + while (j < subpath->getNumPoints()) { |
720 | + if (subpath->getCurve(j)) { |
721 | + if (align_stroke_coords) { |
722 | + alignStrokeCoords(subpath, j + 2, &x, &y); |
723 | + } else { |
724 | + x = subpath->getX(j+2); |
725 | + y = subpath->getY(j+2); |
726 | + } |
727 | + cairo_curve_to( cairo, |
728 | + subpath->getX(j), subpath->getY(j), |
729 | + subpath->getX(j+1), subpath->getY(j+1), |
730 | + x, y); |
731 | + |
732 | + j += 3; |
733 | + } else { |
734 | + if (align_stroke_coords) { |
735 | + alignStrokeCoords(subpath, j, &x, &y); |
736 | + } else { |
737 | + x = subpath->getX(j); |
738 | + y = subpath->getY(j); |
739 | + } |
740 | + cairo_line_to (cairo, x, y); |
741 | + ++j; |
742 | + } |
743 | + } |
744 | + if (subpath->isClosed()) { |
745 | + LOG (printf ("close\n")); |
746 | + cairo_close_path (cairo); |
747 | + } |
748 | + } |
749 | + } |
750 | +} |
751 | + |
752 | +void CairoOutputDev::stroke(GfxState *state) { |
753 | + if (inType3Char) { |
754 | + GfxGray gray; |
755 | + state->getFillGray(&gray); |
756 | + if (colToDbl(gray) > 0.5) |
757 | + return; |
758 | + } |
759 | + |
760 | + if (adjusted_stroke_width) |
761 | + align_stroke_coords = gTrue; |
762 | + doPath (cairo, state, state->getPath()); |
763 | + align_stroke_coords = gFalse; |
764 | + cairo_set_source (cairo, stroke_pattern); |
765 | + LOG(printf ("stroke\n")); |
766 | + cairo_stroke (cairo); |
767 | + if (cairo_shape) { |
768 | + doPath (cairo_shape, state, state->getPath()); |
769 | + cairo_stroke (cairo_shape); |
770 | + } |
771 | +} |
772 | + |
773 | +void CairoOutputDev::fill(GfxState *state) { |
774 | + if (inType3Char) { |
775 | + GfxGray gray; |
776 | + state->getFillGray(&gray); |
777 | + if (colToDbl(gray) > 0.5) |
778 | + return; |
779 | + } |
780 | + |
781 | + doPath (cairo, state, state->getPath()); |
782 | + cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); |
783 | + cairo_set_source (cairo, fill_pattern); |
784 | + LOG(printf ("fill\n")); |
785 | + //XXX: how do we get the path |
786 | + if (mask) { |
787 | + cairo_save (cairo); |
788 | + cairo_clip (cairo); |
789 | + cairo_set_matrix (cairo, &mask_matrix); |
790 | + cairo_mask (cairo, mask); |
791 | + cairo_restore (cairo); |
792 | + } else if (strokePathClip) { |
793 | + fillToStrokePathClip(state); |
794 | + } else { |
795 | + cairo_fill (cairo); |
796 | + } |
797 | + if (cairo_shape) { |
798 | + cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); |
799 | + doPath (cairo_shape, state, state->getPath()); |
800 | + cairo_fill (cairo_shape); |
801 | + } |
802 | +} |
803 | + |
804 | +void CairoOutputDev::eoFill(GfxState *state) { |
805 | + doPath (cairo, state, state->getPath()); |
806 | + cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); |
807 | + cairo_set_source (cairo, fill_pattern); |
808 | + LOG(printf ("fill-eo\n")); |
809 | + cairo_fill (cairo); |
810 | + |
811 | + if (cairo_shape) { |
812 | + cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); |
813 | + doPath (cairo_shape, state, state->getPath()); |
814 | + cairo_fill (cairo_shape); |
815 | + } |
816 | + |
817 | +} |
818 | + |
819 | +GBool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *cat, Object *str, |
820 | + double *pmat, int paintType, int /*tilingType*/, Dict *resDict, |
821 | + double *mat, double *bbox, |
822 | + int x0, int y0, int x1, int y1, |
823 | + double xStep, double yStep) |
824 | +{ |
825 | + PDFRectangle box; |
826 | + Gfx *gfx; |
827 | + cairo_pattern_t *pattern; |
828 | + cairo_surface_t *surface; |
829 | + cairo_matrix_t matrix; |
830 | + cairo_t *old_cairo; |
831 | + double xMin, yMin, xMax, yMax; |
832 | + double width, height; |
833 | + int surface_width, surface_height; |
834 | + StrokePathClip *strokePathTmp; |
835 | + |
836 | + width = bbox[2] - bbox[0]; |
837 | + height = bbox[3] - bbox[1]; |
838 | + |
839 | + if (xStep != width || yStep != height) |
840 | + return gFalse; |
841 | + /* TODO: implement the other cases here too */ |
842 | + |
843 | + surface_width = (int) ceil (width); |
844 | + surface_height = (int) ceil (height); |
845 | + |
846 | + surface = cairo_surface_create_similar (cairo_get_target (cairo), |
847 | + CAIRO_CONTENT_COLOR_ALPHA, |
848 | + surface_width, surface_height); |
849 | + if (cairo_surface_status (surface)) |
850 | + return gFalse; |
851 | + |
852 | + old_cairo = cairo; |
853 | + cairo = cairo_create (surface); |
854 | + cairo_surface_destroy (surface); |
855 | + |
856 | + box.x1 = bbox[0]; box.y1 = bbox[1]; |
857 | + box.x2 = bbox[2]; box.y2 = bbox[3]; |
858 | + strokePathTmp = strokePathClip; |
859 | + strokePathClip = NULL; |
860 | + gfx = new Gfx(doc, this, resDict, &box, NULL); |
861 | + gfx->display(str); |
862 | + delete gfx; |
863 | + strokePathClip = strokePathTmp; |
864 | + |
865 | + pattern = cairo_pattern_create_for_surface (cairo_get_target (cairo)); |
866 | + cairo_destroy (cairo); |
867 | + cairo = old_cairo; |
868 | + if (cairo_pattern_status (pattern)) |
869 | + return gFalse; |
870 | + |
871 | + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
872 | + cairo_rectangle (cairo, xMin, yMin, xMax - xMin, yMax - yMin); |
873 | + |
874 | + cairo_matrix_init_scale (&matrix, surface_width / width, surface_height / height); |
875 | + cairo_pattern_set_matrix (pattern, &matrix); |
876 | + |
877 | + cairo_matrix_init (&matrix, mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); |
878 | + cairo_transform (cairo, &matrix); |
879 | + cairo_set_source (cairo, pattern); |
880 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); |
881 | + if (strokePathClip) { |
882 | + fillToStrokePathClip(state); |
883 | + } else { |
884 | + cairo_fill (cairo); |
885 | + } |
886 | + |
887 | + cairo_pattern_destroy (pattern); |
888 | + |
889 | + return gTrue; |
890 | +} |
891 | + |
892 | +GBool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { |
893 | + double x0, y0, x1, y1; |
894 | + double dx, dy; |
895 | + |
896 | + shading->getCoords(&x0, &y0, &x1, &y1); |
897 | + dx = x1 - x0; |
898 | + dy = y1 - y0; |
899 | + |
900 | + cairo_pattern_destroy(fill_pattern); |
901 | + fill_pattern = cairo_pattern_create_linear (x0 + tMin * dx, y0 + tMin * dy, |
902 | + x0 + tMax * dx, y0 + tMax * dy); |
903 | + if (!shading->getExtend0() && !shading->getExtend1()) |
904 | + cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); |
905 | + else |
906 | + cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); |
907 | + |
908 | + LOG (printf ("axial-sh\n")); |
909 | + |
910 | + // TODO: use the actual stops in the shading in the case |
911 | + // of linear interpolation (Type 2 Exponential functions with N=1) |
912 | + return gFalse; |
913 | +} |
914 | + |
915 | +GBool CairoOutputDev::axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) |
916 | +{ |
917 | + return (shading->getExtend0() == shading->getExtend1()); |
918 | +} |
919 | + |
920 | +GBool CairoOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) { |
921 | + double x0, y0, r0, x1, y1, r1; |
922 | + double dx, dy, dr; |
923 | + |
924 | + shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); |
925 | + dx = x1 - x0; |
926 | + dy = y1 - y0; |
927 | + dr = r1 - r0; |
928 | + cairo_pattern_destroy(fill_pattern); |
929 | + fill_pattern = cairo_pattern_create_radial (x0 + sMin * dx, |
930 | + y0 + sMin * dy, |
931 | + r0 + sMin * dr, |
932 | + x0 + sMax * dx, |
933 | + y0 + sMax * dy, |
934 | + r0 + sMax * dr); |
935 | + if (shading->getExtend0() && shading->getExtend1()) |
936 | + cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_PAD); |
937 | + else |
938 | + cairo_pattern_set_extend (fill_pattern, CAIRO_EXTEND_NONE); |
939 | + |
940 | + LOG (printf ("radial-sh\n")); |
941 | + |
942 | + return gFalse; |
943 | +} |
944 | + |
945 | +GBool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) |
946 | +{ |
947 | + return (shading->getExtend0() == shading->getExtend1()); |
948 | +} |
949 | + |
950 | +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) |
951 | +GBool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) |
952 | +{ |
953 | + double x0, y0, x1, y1, x2, y2; |
954 | + GfxColor color[3]; |
955 | + int i, j; |
956 | + GfxRGB rgb; |
957 | + |
958 | + cairo_pattern_destroy(fill_pattern); |
959 | + fill_pattern = cairo_pattern_create_mesh (); |
960 | + |
961 | + for (i = 0; i < shading->getNTriangles(); i++) { |
962 | + shading->getTriangle(i, |
963 | + &x0, &y0, &color[0], |
964 | + &x1, &y1, &color[1], |
965 | + &x2, &y2, &color[2]); |
966 | + |
967 | + cairo_mesh_pattern_begin_patch (fill_pattern); |
968 | + |
969 | + cairo_mesh_pattern_move_to (fill_pattern, x0, y0); |
970 | + cairo_mesh_pattern_line_to (fill_pattern, x1, y1); |
971 | + cairo_mesh_pattern_line_to (fill_pattern, x2, y2); |
972 | + |
973 | + for (j = 0; j < 3; j++) { |
974 | + shading->getColorSpace()->getRGB(&color[j], &rgb); |
975 | + cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, |
976 | + colToDbl(rgb.r), |
977 | + colToDbl(rgb.g), |
978 | + colToDbl(rgb.b)); |
979 | + } |
980 | + |
981 | + cairo_mesh_pattern_end_patch (fill_pattern); |
982 | + } |
983 | + |
984 | + double xMin, yMin, xMax, yMax; |
985 | + // get the clip region bbox |
986 | + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
987 | + state->moveTo(xMin, yMin); |
988 | + state->lineTo(xMin, yMax); |
989 | + state->lineTo(xMax, yMax); |
990 | + state->lineTo(xMax, yMin); |
991 | + state->closePath(); |
992 | + fill(state); |
993 | + state->clearPath(); |
994 | + |
995 | + return gTrue; |
996 | +} |
997 | + |
998 | +GBool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) |
999 | +{ |
1000 | + int i, j, k; |
1001 | + |
1002 | + cairo_pattern_destroy(fill_pattern); |
1003 | + fill_pattern = cairo_pattern_create_mesh (); |
1004 | + |
1005 | + for (i = 0; i < shading->getNPatches(); i++) { |
1006 | + GfxPatch *patch = shading->getPatch(i); |
1007 | + GfxColor color; |
1008 | + GfxRGB rgb; |
1009 | + |
1010 | + cairo_mesh_pattern_begin_patch (fill_pattern); |
1011 | + |
1012 | + cairo_mesh_pattern_move_to (fill_pattern, patch->x[0][0], patch->y[0][0]); |
1013 | + cairo_mesh_pattern_curve_to (fill_pattern, |
1014 | + patch->x[0][1], patch->y[0][1], |
1015 | + patch->x[0][2], patch->y[0][2], |
1016 | + patch->x[0][3], patch->y[0][3]); |
1017 | + |
1018 | + cairo_mesh_pattern_curve_to (fill_pattern, |
1019 | + patch->x[1][3], patch->y[1][3], |
1020 | + patch->x[2][3], patch->y[2][3], |
1021 | + patch->x[3][3], patch->y[3][3]); |
1022 | + |
1023 | + cairo_mesh_pattern_curve_to (fill_pattern, |
1024 | + patch->x[3][2], patch->y[3][2], |
1025 | + patch->x[3][1], patch->y[3][1], |
1026 | + patch->x[3][0], patch->y[3][0]); |
1027 | + |
1028 | + cairo_mesh_pattern_curve_to (fill_pattern, |
1029 | + patch->x[2][0], patch->y[2][0], |
1030 | + patch->x[1][0], patch->y[1][0], |
1031 | + patch->x[0][0], patch->y[0][0]); |
1032 | + |
1033 | + cairo_mesh_pattern_set_control_point (fill_pattern, 0, patch->x[1][1], patch->y[1][1]); |
1034 | + cairo_mesh_pattern_set_control_point (fill_pattern, 1, patch->x[1][2], patch->y[1][2]); |
1035 | + cairo_mesh_pattern_set_control_point (fill_pattern, 2, patch->x[2][2], patch->y[2][2]); |
1036 | + cairo_mesh_pattern_set_control_point (fill_pattern, 3, patch->x[2][1], patch->y[2][1]); |
1037 | + |
1038 | + for (j = 0; j < 4; j++) { |
1039 | + int u, v; |
1040 | + |
1041 | + switch (j) { |
1042 | + case 0: |
1043 | + u = 0; v = 0; |
1044 | + break; |
1045 | + case 1: |
1046 | + u = 0; v = 1; |
1047 | + break; |
1048 | + case 2: |
1049 | + u = 1; v = 1; |
1050 | + break; |
1051 | + case 3: |
1052 | + u = 1; v = 0; |
1053 | + break; |
1054 | + } |
1055 | + |
1056 | + if (shading->isParameterized()) { |
1057 | + shading->getParameterizedColor (patch->color[u][v].c[0], &color); |
1058 | + } else { |
1059 | + for (k = 0; k < shading->getColorSpace()->getNComps(); k++) { |
1060 | + // simply cast to the desired type; that's all what is needed. |
1061 | + color.c[k] = GfxColorComp (patch->color[u][v].c[k]); |
1062 | + } |
1063 | + } |
1064 | + |
1065 | + shading->getColorSpace()->getRGB(&color, &rgb); |
1066 | + cairo_mesh_pattern_set_corner_color_rgb (fill_pattern, j, |
1067 | + colToDbl(rgb.r), |
1068 | + colToDbl(rgb.g), |
1069 | + colToDbl(rgb.b)); |
1070 | + } |
1071 | + cairo_mesh_pattern_end_patch (fill_pattern); |
1072 | + } |
1073 | + |
1074 | + double xMin, yMin, xMax, yMax; |
1075 | + // get the clip region bbox |
1076 | + state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); |
1077 | + state->moveTo(xMin, yMin); |
1078 | + state->lineTo(xMin, yMax); |
1079 | + state->lineTo(xMax, yMax); |
1080 | + state->lineTo(xMax, yMin); |
1081 | + state->closePath(); |
1082 | + fill(state); |
1083 | + state->clearPath(); |
1084 | + |
1085 | + return gTrue; |
1086 | +} |
1087 | +#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ |
1088 | + |
1089 | +void CairoOutputDev::clip(GfxState *state) { |
1090 | + doPath (cairo, state, state->getPath()); |
1091 | + cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_WINDING); |
1092 | + cairo_clip (cairo); |
1093 | + LOG (printf ("clip\n")); |
1094 | + if (cairo_shape) { |
1095 | + doPath (cairo_shape, state, state->getPath()); |
1096 | + cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_WINDING); |
1097 | + cairo_clip (cairo_shape); |
1098 | + } |
1099 | +} |
1100 | + |
1101 | +void CairoOutputDev::eoClip(GfxState *state) { |
1102 | + doPath (cairo, state, state->getPath()); |
1103 | + cairo_set_fill_rule (cairo, CAIRO_FILL_RULE_EVEN_ODD); |
1104 | + cairo_clip (cairo); |
1105 | + LOG (printf ("clip-eo\n")); |
1106 | + if (cairo_shape) { |
1107 | + doPath (cairo_shape, state, state->getPath()); |
1108 | + cairo_set_fill_rule (cairo_shape, CAIRO_FILL_RULE_EVEN_ODD); |
1109 | + cairo_clip (cairo_shape); |
1110 | + } |
1111 | + |
1112 | +} |
1113 | + |
1114 | +void CairoOutputDev::clipToStrokePath(GfxState *state) { |
1115 | + LOG(printf("clip-to-stroke-path\n")); |
1116 | + strokePathClip = (StrokePathClip*)gmalloc (sizeof(*strokePathClip)); |
1117 | + strokePathClip->path = state->getPath()->copy(); |
1118 | + cairo_get_matrix (cairo, &strokePathClip->ctm); |
1119 | + strokePathClip->line_width = cairo_get_line_width (cairo); |
1120 | + strokePathClip->dash_count = cairo_get_dash_count (cairo); |
1121 | + if (strokePathClip->dash_count) { |
1122 | + strokePathClip->dashes = (double*) gmallocn (sizeof(double), strokePathClip->dash_count); |
1123 | + cairo_get_dash (cairo, strokePathClip->dashes, &strokePathClip->dash_offset); |
1124 | + } else { |
1125 | + strokePathClip->dashes = NULL; |
1126 | + } |
1127 | + strokePathClip->cap = cairo_get_line_cap (cairo); |
1128 | + strokePathClip->join = cairo_get_line_join (cairo); |
1129 | + strokePathClip->miter = cairo_get_miter_limit (cairo); |
1130 | +} |
1131 | + |
1132 | +void CairoOutputDev::fillToStrokePathClip(GfxState *state) { |
1133 | + cairo_save (cairo); |
1134 | + |
1135 | + cairo_set_matrix (cairo, &strokePathClip->ctm); |
1136 | + cairo_set_line_width (cairo, strokePathClip->line_width); |
1137 | + strokePathClip->dash_count = cairo_get_dash_count (cairo); |
1138 | + cairo_set_dash (cairo, strokePathClip->dashes, strokePathClip->dash_count, strokePathClip->dash_offset); |
1139 | + cairo_set_line_cap (cairo, strokePathClip->cap); |
1140 | + cairo_set_line_join (cairo, strokePathClip->join); |
1141 | + cairo_set_miter_limit (cairo, strokePathClip->miter); |
1142 | + doPath (cairo, state, strokePathClip->path); |
1143 | + cairo_stroke (cairo); |
1144 | + |
1145 | + cairo_restore (cairo); |
1146 | + |
1147 | + delete strokePathClip->path; |
1148 | + if (strokePathClip->dashes) |
1149 | + gfree (strokePathClip->dashes); |
1150 | + gfree (strokePathClip); |
1151 | + strokePathClip = NULL; |
1152 | +} |
1153 | + |
1154 | +void CairoOutputDev::beginString(GfxState *state, GooString *s) |
1155 | +{ |
1156 | + int len = s->getLength(); |
1157 | + |
1158 | + if (needFontUpdate) |
1159 | + updateFont(state); |
1160 | + |
1161 | + if (!currentFont) |
1162 | + return; |
1163 | + |
1164 | + glyphs = (cairo_glyph_t *) gmallocn (len, sizeof (cairo_glyph_t)); |
1165 | + glyphCount = 0; |
1166 | + if (use_show_text_glyphs) { |
1167 | + clusters = (cairo_text_cluster_t *) gmallocn (len, sizeof (cairo_text_cluster_t)); |
1168 | + clusterCount = 0; |
1169 | + utf8Max = len*2; // start with twice the number of glyphs. we will realloc if we need more. |
1170 | + utf8 = (char *) gmalloc (utf8Max); |
1171 | + utf8Count = 0; |
1172 | + } |
1173 | +} |
1174 | + |
1175 | +void CairoOutputDev::drawChar(GfxState *state, double x, double y, |
1176 | + double dx, double dy, |
1177 | + double originX, double originY, |
1178 | + CharCode code, int nBytes, Unicode *u, int uLen) |
1179 | +{ |
1180 | + if (currentFont) { |
1181 | + glyphs[glyphCount].index = currentFont->getGlyph (code, u, uLen); |
1182 | + glyphs[glyphCount].x = x - originX; |
1183 | + glyphs[glyphCount].y = y - originY; |
1184 | + glyphCount++; |
1185 | + if (use_show_text_glyphs) { |
1186 | + if (utf8Max - utf8Count < uLen*6) { |
1187 | + // utf8 encoded characters can be up to 6 bytes |
1188 | + if (utf8Max > uLen*6) |
1189 | + utf8Max *= 2; |
1190 | + else |
1191 | + utf8Max += 2*uLen*6; |
1192 | + utf8 = (char *) grealloc (utf8, utf8Max); |
1193 | + } |
1194 | + clusters[clusterCount].num_bytes = 0; |
1195 | + for (int i = 0; i < uLen; i++) { |
1196 | + int size = mapUTF8 (u[i], utf8 + utf8Count, utf8Max - utf8Count); |
1197 | + utf8Count += size; |
1198 | + clusters[clusterCount].num_bytes += size; |
1199 | + } |
1200 | + clusters[clusterCount].num_glyphs = 1; |
1201 | + clusterCount++; |
1202 | + } |
1203 | + } |
1204 | + |
1205 | + if (!text) |
1206 | + return; |
1207 | + actualText->addChar (state, x, y, dx, dy, code, nBytes, u, uLen); |
1208 | +} |
1209 | + |
1210 | +void CairoOutputDev::endString(GfxState *state) |
1211 | +{ |
1212 | + int render; |
1213 | + |
1214 | + if (!currentFont) |
1215 | + return; |
1216 | + |
1217 | + // endString can be called without a corresponding beginString. If this |
1218 | + // happens glyphs will be null so don't draw anything, just return. |
1219 | + // XXX: OutputDevs should probably not have to deal with this... |
1220 | + if (!glyphs) |
1221 | + return; |
1222 | + |
1223 | + // ignore empty strings and invisible text -- this is used by |
1224 | + // Acrobat Capture |
1225 | + render = state->getRender(); |
1226 | + if (render == 3 || glyphCount == 0) { |
1227 | + gfree(glyphs); |
1228 | + glyphs = NULL; |
1229 | + return; |
1230 | + } |
1231 | + |
1232 | + if (!(render & 1)) { |
1233 | + LOG (printf ("fill string\n")); |
1234 | + cairo_set_source (cairo, fill_pattern); |
1235 | + if (use_show_text_glyphs) |
1236 | + cairo_show_text_glyphs (cairo, utf8, utf8Count, glyphs, glyphCount, clusters, clusterCount, (cairo_text_cluster_flags_t)0); |
1237 | + else |
1238 | + cairo_show_glyphs (cairo, glyphs, glyphCount); |
1239 | + if (cairo_shape) |
1240 | + cairo_show_glyphs (cairo_shape, glyphs, glyphCount); |
1241 | + } |
1242 | + |
1243 | + // stroke |
1244 | + if ((render & 3) == 1 || (render & 3) == 2) { |
1245 | + LOG (printf ("stroke string\n")); |
1246 | + cairo_set_source (cairo, stroke_pattern); |
1247 | + cairo_glyph_path (cairo, glyphs, glyphCount); |
1248 | + cairo_stroke (cairo); |
1249 | + if (cairo_shape) { |
1250 | + cairo_glyph_path (cairo_shape, glyphs, glyphCount); |
1251 | + cairo_stroke (cairo_shape); |
1252 | + } |
1253 | + } |
1254 | + |
1255 | + // clip |
1256 | + if ((render & 4)) { |
1257 | + LOG (printf ("clip string\n")); |
1258 | + // append the glyph path to textClipPath. |
1259 | + |
1260 | + // set textClipPath as the currentPath |
1261 | + if (textClipPath) { |
1262 | + cairo_append_path (cairo, textClipPath); |
1263 | + if (cairo_shape) { |
1264 | + cairo_append_path (cairo_shape, textClipPath); |
1265 | + } |
1266 | + cairo_path_destroy (textClipPath); |
1267 | + } |
1268 | + |
1269 | + // append the glyph path |
1270 | + cairo_glyph_path (cairo, glyphs, glyphCount); |
1271 | + |
1272 | + // move the path back into textClipPath |
1273 | + // and clear the current path |
1274 | + textClipPath = cairo_copy_path (cairo); |
1275 | + cairo_new_path (cairo); |
1276 | + if (cairo_shape) { |
1277 | + cairo_new_path (cairo_shape); |
1278 | + } |
1279 | + } |
1280 | + |
1281 | + gfree (glyphs); |
1282 | + glyphs = NULL; |
1283 | + if (use_show_text_glyphs) { |
1284 | + gfree (clusters); |
1285 | + clusters = NULL; |
1286 | + gfree (utf8); |
1287 | + utf8 = NULL; |
1288 | + } |
1289 | +} |
1290 | + |
1291 | + |
1292 | +GBool CairoOutputDev::beginType3Char(GfxState *state, double x, double y, |
1293 | + double dx, double dy, |
1294 | + CharCode code, Unicode *u, int uLen) { |
1295 | + |
1296 | + cairo_save (cairo); |
1297 | + double *ctm; |
1298 | + cairo_matrix_t matrix; |
1299 | + |
1300 | + ctm = state->getCTM(); |
1301 | + matrix.xx = ctm[0]; |
1302 | + matrix.yx = ctm[1]; |
1303 | + matrix.xy = ctm[2]; |
1304 | + matrix.yy = ctm[3]; |
1305 | + matrix.x0 = ctm[4]; |
1306 | + matrix.y0 = ctm[5]; |
1307 | + /* Restore the original matrix and then transform to matrix needed for the |
1308 | + * type3 font. This is ugly but seems to work. Perhaps there is a better way to do it?*/ |
1309 | + cairo_set_matrix(cairo, &orig_matrix); |
1310 | + cairo_transform(cairo, &matrix); |
1311 | + if (cairo_shape) { |
1312 | + cairo_save (cairo_shape); |
1313 | + cairo_set_matrix(cairo_shape, &orig_matrix); |
1314 | + cairo_transform(cairo_shape, &matrix); |
1315 | + } |
1316 | + cairo_pattern_destroy(stroke_pattern); |
1317 | + cairo_pattern_reference(fill_pattern); |
1318 | + stroke_pattern = fill_pattern; |
1319 | + return gFalse; |
1320 | +} |
1321 | + |
1322 | +void CairoOutputDev::endType3Char(GfxState *state) { |
1323 | + cairo_restore (cairo); |
1324 | + if (cairo_shape) { |
1325 | + cairo_restore (cairo_shape); |
1326 | + } |
1327 | +} |
1328 | + |
1329 | +void CairoOutputDev::type3D0(GfxState *state, double wx, double wy) { |
1330 | + t3_glyph_wx = wx; |
1331 | + t3_glyph_wy = wy; |
1332 | +} |
1333 | + |
1334 | +void CairoOutputDev::type3D1(GfxState *state, double wx, double wy, |
1335 | + double llx, double lly, double urx, double ury) { |
1336 | + t3_glyph_wx = wx; |
1337 | + t3_glyph_wy = wy; |
1338 | + t3_glyph_bbox[0] = llx; |
1339 | + t3_glyph_bbox[1] = lly; |
1340 | + t3_glyph_bbox[2] = urx; |
1341 | + t3_glyph_bbox[3] = ury; |
1342 | + t3_glyph_has_bbox = gTrue; |
1343 | +} |
1344 | + |
1345 | +void CairoOutputDev::beginTextObject(GfxState *state) { |
1346 | +} |
1347 | + |
1348 | +void CairoOutputDev::endTextObject(GfxState *state) { |
1349 | + if (textClipPath) { |
1350 | + // clip the accumulated text path |
1351 | + cairo_append_path (cairo, textClipPath); |
1352 | + cairo_clip (cairo); |
1353 | + if (cairo_shape) { |
1354 | + cairo_append_path (cairo_shape, textClipPath); |
1355 | + cairo_clip (cairo_shape); |
1356 | + } |
1357 | + cairo_path_destroy (textClipPath); |
1358 | + textClipPath = NULL; |
1359 | + } |
1360 | +} |
1361 | + |
1362 | +void CairoOutputDev::beginActualText(GfxState *state, GooString *text) |
1363 | +{ |
1364 | + if (this->text) |
1365 | + actualText->begin(state, text); |
1366 | +} |
1367 | + |
1368 | +void CairoOutputDev::endActualText(GfxState *state) |
1369 | +{ |
1370 | + if (text) |
1371 | + actualText->end(state); |
1372 | +} |
1373 | + |
1374 | +static inline int splashRound(SplashCoord x) { |
1375 | + return (int)floor(x + 0.5); |
1376 | +} |
1377 | + |
1378 | +static inline int splashCeil(SplashCoord x) { |
1379 | + return (int)ceil(x); |
1380 | +} |
1381 | + |
1382 | +static inline int splashFloor(SplashCoord x) { |
1383 | + return (int)floor(x); |
1384 | +} |
1385 | + |
1386 | +static |
1387 | +cairo_surface_t *cairo_surface_create_similar_clip (cairo_t *cairo, cairo_content_t content) |
1388 | +{ |
1389 | + double x1, y1, x2, y2; |
1390 | + int width, height; |
1391 | + cairo_clip_extents (cairo, &x1, &y1, &x2, &y2); |
1392 | + cairo_matrix_t matrix; |
1393 | + cairo_get_matrix (cairo, &matrix); |
1394 | + //cairo_matrix_transform_point(&matrix, &x1, &y1); |
1395 | + //cairo_matrix_transform_point(&matrix, &x2, &y2);*/ |
1396 | + cairo_user_to_device(cairo, &x1, &y1); |
1397 | + cairo_user_to_device(cairo, &x2, &y2); |
1398 | + width = splashCeil(x2) - splashFloor(x1); |
1399 | + //XXX: negative matrix |
1400 | + ////height = splashCeil(y2) - splashFloor(y1); |
1401 | + height = splashFloor(y1) - splashCeil(y2); |
1402 | + cairo_surface_t *target = cairo_get_target (cairo); |
1403 | + cairo_surface_t *result; |
1404 | + |
1405 | + result = cairo_surface_create_similar (target, content, width, height); |
1406 | + double x_offset, y_offset; |
1407 | + cairo_surface_get_device_offset(target, &x_offset, &y_offset); |
1408 | + cairo_surface_set_device_offset(result, x_offset, y_offset); |
1409 | + |
1410 | + |
1411 | + return result; |
1412 | +} |
1413 | + |
1414 | + |
1415 | + |
1416 | +void CairoOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/, |
1417 | + GfxColorSpace * blendingColorSpace, |
1418 | + GBool /*isolated*/, GBool knockout, |
1419 | + GBool forSoftMask) { |
1420 | + /* push color space */ |
1421 | + ColorSpaceStack* css = new ColorSpaceStack; |
1422 | + css->cs = blendingColorSpace; |
1423 | + css->knockout = knockout; |
1424 | + cairo_get_matrix(cairo, &css->group_matrix); |
1425 | + css->next = groupColorSpaceStack; |
1426 | + groupColorSpaceStack = css; |
1427 | + |
1428 | + LOG(printf ("begin transparency group. knockout: %s\n", knockout ? "yes":"no")); |
1429 | + |
1430 | + if (knockout) { |
1431 | + knockoutCount++; |
1432 | + if (!cairo_shape) { |
1433 | + /* create a surface for tracking the shape */ |
1434 | + cairo_surface_t *cairo_shape_surface = cairo_surface_create_similar_clip (cairo, CAIRO_CONTENT_ALPHA); |
1435 | + cairo_shape = cairo_create (cairo_shape_surface); |
1436 | + cairo_surface_destroy (cairo_shape_surface); |
1437 | + |
1438 | + /* the color doesn't matter as long as it is opaque */ |
1439 | + cairo_set_source_rgb (cairo_shape, 0, 0, 0); |
1440 | + cairo_matrix_t matrix; |
1441 | + cairo_get_matrix (cairo, &matrix); |
1442 | + //printMatrix(&matrix); |
1443 | + cairo_set_matrix (cairo_shape, &matrix); |
1444 | + } else { |
1445 | + cairo_reference (cairo_shape); |
1446 | + } |
1447 | + } |
1448 | + if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { |
1449 | + /* we need to track the shape */ |
1450 | + cairo_push_group (cairo_shape); |
1451 | + } |
1452 | + if (0 && forSoftMask) |
1453 | + cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); |
1454 | + else |
1455 | + cairo_push_group (cairo); |
1456 | + |
1457 | + /* push_group has an implicit cairo_save() */ |
1458 | + if (knockout) { |
1459 | + /*XXX: let's hope this matches the semantics needed */ |
1460 | + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
1461 | + } else { |
1462 | + cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); |
1463 | + } |
1464 | +} |
1465 | + |
1466 | +void CairoOutputDev::endTransparencyGroup(GfxState * /*state*/) { |
1467 | + if (group) |
1468 | + cairo_pattern_destroy(group); |
1469 | + group = cairo_pop_group (cairo); |
1470 | + |
1471 | + LOG(printf ("end transparency group\n")); |
1472 | + |
1473 | + if (groupColorSpaceStack->next && groupColorSpaceStack->next->knockout) { |
1474 | + if (shape) |
1475 | + cairo_pattern_destroy(shape); |
1476 | + shape = cairo_pop_group (cairo_shape); |
1477 | + } |
1478 | +} |
1479 | + |
1480 | +void CairoOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) { |
1481 | + LOG(printf ("paint transparency group\n")); |
1482 | + |
1483 | + cairo_save (cairo); |
1484 | + cairo_set_matrix (cairo, &groupColorSpaceStack->group_matrix); |
1485 | + cairo_set_source (cairo, group); |
1486 | + |
1487 | + if (!mask) { |
1488 | + //XXX: deal with mask && shape case |
1489 | + if (shape) { |
1490 | + cairo_save (cairo); |
1491 | + |
1492 | + /* OPERATOR_SOURCE w/ a mask is defined as (src IN mask) ADD (dest OUT mask) |
1493 | + * however our source has already been clipped to mask so we only need to |
1494 | + * do ADD and OUT */ |
1495 | + |
1496 | + /* clear the shape mask */ |
1497 | + cairo_set_source (cairo, shape); |
1498 | + cairo_set_operator (cairo, CAIRO_OPERATOR_DEST_OUT); |
1499 | + cairo_paint (cairo); |
1500 | + |
1501 | + cairo_set_operator (cairo, CAIRO_OPERATOR_ADD); |
1502 | + cairo_set_source (cairo, group); |
1503 | + cairo_paint (cairo); |
1504 | + |
1505 | + cairo_restore (cairo); |
1506 | + |
1507 | + cairo_pattern_destroy (shape); |
1508 | + shape = NULL; |
1509 | + } else { |
1510 | + cairo_paint_with_alpha (cairo, fill_opacity); |
1511 | + } |
1512 | + cairo_status_t status = cairo_status(cairo); |
1513 | + if (status) |
1514 | + printf("BAD status: %s\n", cairo_status_to_string(status)); |
1515 | + } else { |
1516 | + if (fill_opacity < 1.0) { |
1517 | + cairo_push_group(cairo); |
1518 | + } |
1519 | + cairo_save(cairo); |
1520 | + cairo_set_matrix(cairo, &mask_matrix); |
1521 | + cairo_mask(cairo, mask); |
1522 | + cairo_restore(cairo); |
1523 | + if (fill_opacity < 1.0) { |
1524 | + cairo_pop_group_to_source(cairo); |
1525 | + cairo_paint_with_alpha (cairo, fill_opacity); |
1526 | + } |
1527 | + cairo_pattern_destroy(mask); |
1528 | + mask = NULL; |
1529 | + } |
1530 | + |
1531 | + popTransparencyGroup(); |
1532 | + cairo_restore(cairo); |
1533 | +} |
1534 | + |
1535 | +static int luminocity(uint32_t x) |
1536 | +{ |
1537 | + int r = (x >> 16) & 0xff; |
1538 | + int g = (x >> 8) & 0xff; |
1539 | + int b = (x >> 0) & 0xff; |
1540 | + // an arbitrary integer approximation of .3*r + .59*g + .11*b |
1541 | + int y = (r*19661+g*38666+b*7209 + 32829)>>16; |
1542 | + return y; |
1543 | +} |
1544 | + |
1545 | + |
1546 | +/* XXX: do we need to deal with shape here? */ |
1547 | +void CairoOutputDev::setSoftMask(GfxState * state, double * bbox, GBool alpha, |
1548 | + Function * transferFunc, GfxColor * backdropColor) { |
1549 | + cairo_pattern_destroy(mask); |
1550 | + |
1551 | + LOG(printf ("set softMask\n")); |
1552 | + |
1553 | + if (alpha == false) { |
1554 | + /* We need to mask according to the luminocity of the group. |
1555 | + * So we paint the group to an image surface convert it to a luminocity map |
1556 | + * and then use that as the mask. */ |
1557 | + |
1558 | + /* Get clip extents in device space */ |
1559 | + double x1, y1, x2, y2, x_min, y_min, x_max, y_max; |
1560 | + cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); |
1561 | + cairo_user_to_device(cairo, &x1, &y1); |
1562 | + cairo_user_to_device(cairo, &x2, &y2); |
1563 | + x_min = MIN(x1, x2); |
1564 | + y_min = MIN(y1, y2); |
1565 | + x_max = MAX(x1, x2); |
1566 | + y_max = MAX(y1, y2); |
1567 | + cairo_clip_extents(cairo, &x1, &y1, &x2, &y2); |
1568 | + cairo_user_to_device(cairo, &x1, &y2); |
1569 | + cairo_user_to_device(cairo, &x2, &y1); |
1570 | + x_min = MIN(x_min,MIN(x1, x2)); |
1571 | + y_min = MIN(y_min,MIN(y1, y2)); |
1572 | + x_max = MAX(x_max,MAX(x1, x2)); |
1573 | + y_max = MAX(y_max,MAX(y1, y2)); |
1574 | + |
1575 | + int width = (int)(ceil(x_max) - floor(x_min)); |
1576 | + int height = (int)(ceil(y_max) - floor(y_min)); |
1577 | + |
1578 | + /* Get group device offset */ |
1579 | + double x_offset, y_offset; |
1580 | + if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { |
1581 | + cairo_surface_get_device_offset(cairo_get_group_target(cairo), &x_offset, &y_offset); |
1582 | + } else { |
1583 | + cairo_surface_t *pats; |
1584 | + cairo_pattern_get_surface(group, &pats); |
1585 | + cairo_surface_get_device_offset(pats, &x_offset, &y_offset); |
1586 | + } |
1587 | + |
1588 | + /* Adjust extents by group offset */ |
1589 | + x_min += x_offset; |
1590 | + y_min += y_offset; |
1591 | + |
1592 | + cairo_surface_t *source = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); |
1593 | + cairo_t *maskCtx = cairo_create(source); |
1594 | + |
1595 | + //XXX: hopefully this uses the correct color space */ |
1596 | + GfxRGB backdropColorRGB; |
1597 | + groupColorSpaceStack->cs->getRGB(backdropColor, &backdropColorRGB); |
1598 | + /* paint the backdrop */ |
1599 | + cairo_set_source_rgb(maskCtx, |
1600 | + colToDbl(backdropColorRGB.r), |
1601 | + colToDbl(backdropColorRGB.g), |
1602 | + colToDbl(backdropColorRGB.b)); |
1603 | + cairo_paint(maskCtx); |
1604 | + |
1605 | + /* Copy source ctm to mask ctm and translate origin so that the |
1606 | + * mask appears it the same location on the source surface. */ |
1607 | + cairo_matrix_t mat, tmat; |
1608 | + cairo_matrix_init_translate(&tmat, -x_min, -y_min); |
1609 | + cairo_get_matrix(cairo, &mat); |
1610 | + cairo_matrix_multiply(&mat, &mat, &tmat); |
1611 | + cairo_set_matrix(maskCtx, &mat); |
1612 | + |
1613 | + /* make the device offset of the new mask match that of the group */ |
1614 | + cairo_surface_set_device_offset(source, x_offset, y_offset); |
1615 | + |
1616 | + /* paint the group */ |
1617 | + cairo_set_source(maskCtx, group); |
1618 | + cairo_paint(maskCtx); |
1619 | + |
1620 | + /* XXX status = cairo_status(maskCtx); */ |
1621 | + cairo_destroy(maskCtx); |
1622 | + |
1623 | + /* convert to a luminocity map */ |
1624 | + uint32_t *source_data = (uint32_t*)cairo_image_surface_get_data(source); |
1625 | + /* get stride in units of 32 bits */ |
1626 | + int stride = cairo_image_surface_get_stride(source)/4; |
1627 | + for (int y=0; y<height; y++) { |
1628 | + for (int x=0; x<width; x++) { |
1629 | + int lum; |
1630 | + lum = luminocity(source_data[y*stride + x]); |
1631 | + if (transferFunc) { |
1632 | + double lum_in, lum_out; |
1633 | + lum_in = lum/256.0; |
1634 | + transferFunc->transform(&lum_in, &lum_out); |
1635 | + lum = (int)(lum_out * 255.0 + 0.5); |
1636 | + } |
1637 | + source_data[y*stride + x] = lum << 24; |
1638 | + } |
1639 | + } |
1640 | + cairo_surface_mark_dirty (source); |
1641 | + |
1642 | + /* setup the new mask pattern */ |
1643 | + mask = cairo_pattern_create_for_surface(source); |
1644 | + cairo_get_matrix(cairo, &mask_matrix); |
1645 | + |
1646 | + if (cairo_get_group_target(cairo) == cairo_get_target(cairo)) { |
1647 | + cairo_pattern_set_matrix(mask, &mat); |
1648 | + } else { |
1649 | + cairo_matrix_t patMatrix; |
1650 | + cairo_pattern_get_matrix(group, &patMatrix); |
1651 | + /* Apply x_min, y_min offset to it appears in the same location as source. */ |
1652 | + cairo_matrix_multiply(&patMatrix, &patMatrix, &tmat); |
1653 | + cairo_pattern_set_matrix(mask, &patMatrix); |
1654 | + } |
1655 | + |
1656 | + cairo_surface_destroy(source); |
1657 | + } else { |
1658 | + mask = cairo_pattern_reference(group); |
1659 | + cairo_get_matrix(cairo, &mask_matrix); |
1660 | + } |
1661 | + |
1662 | + popTransparencyGroup(); |
1663 | +} |
1664 | + |
1665 | +void CairoOutputDev::popTransparencyGroup() { |
1666 | + /* pop color space */ |
1667 | + ColorSpaceStack *css = groupColorSpaceStack; |
1668 | + if (css->knockout) { |
1669 | + knockoutCount--; |
1670 | + if (!knockoutCount) { |
1671 | + /* we don't need to track the shape anymore because |
1672 | + * we are not above any knockout groups */ |
1673 | + cairo_destroy(cairo_shape); |
1674 | + cairo_shape = NULL; |
1675 | + } |
1676 | + } |
1677 | + groupColorSpaceStack = css->next; |
1678 | + delete css; |
1679 | +} |
1680 | + |
1681 | + |
1682 | +void CairoOutputDev::clearSoftMask(GfxState * /*state*/) { |
1683 | + if (mask) |
1684 | + cairo_pattern_destroy(mask); |
1685 | + mask = NULL; |
1686 | +} |
1687 | + |
1688 | +/* Taken from cairo/doc/tutorial/src/singular.c */ |
1689 | +static void |
1690 | +get_singular_values (const cairo_matrix_t *matrix, |
1691 | + double *major, |
1692 | + double *minor) |
1693 | +{ |
1694 | + double xx = matrix->xx, xy = matrix->xy; |
1695 | + double yx = matrix->yx, yy = matrix->yy; |
1696 | + |
1697 | + double a = xx*xx+yx*yx; |
1698 | + double b = xy*xy+yy*yy; |
1699 | + double k = xx*xy+yx*yy; |
1700 | + |
1701 | + double f = (a+b) * .5; |
1702 | + double g = (a-b) * .5; |
1703 | + double delta = sqrt (g*g + k*k); |
1704 | + |
1705 | + if (major) |
1706 | + *major = sqrt (f + delta); |
1707 | + if (minor) |
1708 | + *minor = sqrt (f - delta); |
1709 | +} |
1710 | + |
1711 | +void CairoOutputDev::getScaledSize(int orig_width, |
1712 | + int orig_height, |
1713 | + int *scaledWidth, |
1714 | + int *scaledHeight) { |
1715 | + cairo_matrix_t matrix; |
1716 | + cairo_get_matrix(cairo, &matrix); |
1717 | + |
1718 | + double xScale; |
1719 | + double yScale; |
1720 | + if (orig_width > orig_height) |
1721 | + get_singular_values (&matrix, &xScale, &yScale); |
1722 | + else |
1723 | + get_singular_values (&matrix, &yScale, &xScale); |
1724 | + |
1725 | + int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ |
1726 | + if (xScale >= 0) { |
1727 | + tx = splashRound(matrix.x0 - 0.01); |
1728 | + tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; |
1729 | + } else { |
1730 | + tx = splashRound(matrix.x0 + 0.01) - 1; |
1731 | + tx2 = splashRound(matrix.x0 + xScale - 0.01); |
1732 | + } |
1733 | + *scaledWidth = abs(tx2 - tx) + 1; |
1734 | + //scaledWidth = splashRound(fabs(xScale)); |
1735 | + if (*scaledWidth == 0) { |
1736 | + // technically, this should draw nothing, but it generally seems |
1737 | + // better to draw a one-pixel-wide stripe rather than throwing it |
1738 | + // away |
1739 | + *scaledWidth = 1; |
1740 | + } |
1741 | + if (yScale >= 0) { |
1742 | + ty = splashFloor(matrix.y0 + 0.01); |
1743 | + ty2 = splashCeil(matrix.y0 + yScale - 0.01); |
1744 | + } else { |
1745 | + ty = splashCeil(matrix.y0 - 0.01); |
1746 | + ty2 = splashFloor(matrix.y0 + yScale + 0.01); |
1747 | + } |
1748 | + *scaledHeight = abs(ty2 - ty); |
1749 | + if (*scaledHeight == 0) { |
1750 | + *scaledHeight = 1; |
1751 | + } |
1752 | +} |
1753 | + |
1754 | +cairo_surface_t *CairoOutputDev::downscaleSurface(cairo_surface_t *orig_surface) { |
1755 | + cairo_surface_t *dest_surface; |
1756 | + unsigned char *dest_buffer; |
1757 | + int dest_stride; |
1758 | + unsigned char *orig_buffer; |
1759 | + int orig_width, orig_height; |
1760 | + int orig_stride; |
1761 | + int scaledHeight; |
1762 | + int scaledWidth; |
1763 | + GBool res; |
1764 | + |
1765 | + if (printing) |
1766 | + return NULL; |
1767 | + |
1768 | + orig_width = cairo_image_surface_get_width (orig_surface); |
1769 | + orig_height = cairo_image_surface_get_height (orig_surface); |
1770 | + getScaledSize (orig_width, orig_height, &scaledWidth, &scaledHeight); |
1771 | + if (scaledWidth >= orig_width || scaledHeight >= orig_height) |
1772 | + return NULL; |
1773 | + |
1774 | + dest_surface = cairo_surface_create_similar (orig_surface, |
1775 | + cairo_surface_get_content (orig_surface), |
1776 | + scaledWidth, scaledHeight); |
1777 | + dest_buffer = cairo_image_surface_get_data (dest_surface); |
1778 | + dest_stride = cairo_image_surface_get_stride (dest_surface); |
1779 | + |
1780 | + orig_buffer = cairo_image_surface_get_data (orig_surface); |
1781 | + orig_stride = cairo_image_surface_get_stride (orig_surface); |
1782 | + |
1783 | + res = downscale_box_filter((uint32_t *)orig_buffer, |
1784 | + orig_stride, orig_width, orig_height, |
1785 | + scaledWidth, scaledHeight, 0, 0, |
1786 | + scaledWidth, scaledHeight, |
1787 | + (uint32_t *)dest_buffer, dest_stride); |
1788 | + if (!res) { |
1789 | + cairo_surface_destroy (dest_surface); |
1790 | + return NULL; |
1791 | + } |
1792 | + |
1793 | + return dest_surface; |
1794 | + |
1795 | +} |
1796 | + |
1797 | +cairo_filter_t |
1798 | +CairoOutputDev::getFilterForSurface(cairo_surface_t *image, |
1799 | + GBool interpolate) |
1800 | +{ |
1801 | + if (interpolate) |
1802 | + return CAIRO_FILTER_BILINEAR; |
1803 | + |
1804 | + int orig_width = cairo_image_surface_get_width (image); |
1805 | + int orig_height = cairo_image_surface_get_height (image); |
1806 | + if (orig_width == 0 || orig_height == 0) |
1807 | + return CAIRO_FILTER_NEAREST; |
1808 | + |
1809 | + int scaled_width, scaled_height; |
1810 | + getScaledSize (orig_width, orig_height, &scaled_width, &scaled_height); |
1811 | + |
1812 | + /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */ |
1813 | + if (scaled_width / orig_width >= 4 || scaled_height / orig_height >= 4) |
1814 | + return CAIRO_FILTER_NEAREST; |
1815 | + |
1816 | + return CAIRO_FILTER_BILINEAR; |
1817 | +} |
1818 | + |
1819 | +void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, |
1820 | + int width, int height, GBool invert, |
1821 | + GBool interpolate, GBool inlineImg) { |
1822 | + |
1823 | + /* FIXME: Doesn't the image mask support any colorspace? */ |
1824 | + cairo_set_source (cairo, fill_pattern); |
1825 | + |
1826 | + /* work around a cairo bug when scaling 1x1 surfaces */ |
1827 | + if (width == 1 && height == 1) { |
1828 | + ImageStream *imgStr; |
1829 | + Guchar pix; |
1830 | + int invert_bit; |
1831 | + |
1832 | + imgStr = new ImageStream(str, width, 1, 1); |
1833 | + imgStr->reset(); |
1834 | + imgStr->getPixel(&pix); |
1835 | + imgStr->close(); |
1836 | + delete imgStr; |
1837 | + |
1838 | + invert_bit = invert ? 1 : 0; |
1839 | + if (pix ^ invert_bit) |
1840 | + return; |
1841 | + |
1842 | + cairo_save (cairo); |
1843 | + cairo_rectangle (cairo, 0., 0., width, height); |
1844 | + cairo_fill (cairo); |
1845 | + cairo_restore (cairo); |
1846 | + if (cairo_shape) { |
1847 | + cairo_save (cairo_shape); |
1848 | + cairo_rectangle (cairo_shape, 0., 0., width, height); |
1849 | + cairo_fill (cairo_shape); |
1850 | + cairo_restore (cairo_shape); |
1851 | + } |
1852 | + return; |
1853 | + } |
1854 | + |
1855 | + /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ |
1856 | + |
1857 | + cairo_matrix_t matrix; |
1858 | + cairo_get_matrix (cairo, &matrix); |
1859 | + //XXX: it is possible that we should only do sub pixel positioning if |
1860 | + // we are rendering fonts */ |
1861 | + if (!printing && prescaleImages |
1862 | + /* not rotated */ |
1863 | + && matrix.xy == 0 && matrix.yx == 0 |
1864 | + /* axes not flipped / not 180 deg rotated */ |
1865 | + && matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) { |
1866 | + drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg); |
1867 | + } else { |
1868 | + drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg); |
1869 | + } |
1870 | + |
1871 | +} |
1872 | + |
1873 | +void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, |
1874 | + int width, int height, GBool invert, |
1875 | + GBool inlineImg, double *baseMatrix) { |
1876 | + |
1877 | + /* FIXME: Doesn't the image mask support any colorspace? */ |
1878 | + cairo_set_source (cairo, fill_pattern); |
1879 | + |
1880 | + /* work around a cairo bug when scaling 1x1 surfaces */ |
1881 | + if (width == 1 && height == 1) { |
1882 | + ImageStream *imgStr; |
1883 | + Guchar pix; |
1884 | + int invert_bit; |
1885 | + |
1886 | + imgStr = new ImageStream(str, width, 1, 1); |
1887 | + imgStr->reset(); |
1888 | + imgStr->getPixel(&pix); |
1889 | + imgStr->close(); |
1890 | + delete imgStr; |
1891 | + |
1892 | + invert_bit = invert ? 1 : 0; |
1893 | + if (pix ^ invert_bit) |
1894 | + return; |
1895 | + |
1896 | + cairo_save (cairo); |
1897 | + cairo_rectangle (cairo, 0., 0., width, height); |
1898 | + cairo_fill (cairo); |
1899 | + cairo_restore (cairo); |
1900 | + if (cairo_shape) { |
1901 | + cairo_save (cairo_shape); |
1902 | + cairo_rectangle (cairo_shape, 0., 0., width, height); |
1903 | + cairo_fill (cairo_shape); |
1904 | + cairo_restore (cairo_shape); |
1905 | + } |
1906 | + return; |
1907 | + } |
1908 | + |
1909 | + cairo_push_group_with_content (cairo, CAIRO_CONTENT_ALPHA); |
1910 | + |
1911 | + /* shape is 1.0 for painted areas, 0.0 for unpainted ones */ |
1912 | + |
1913 | + cairo_matrix_t matrix; |
1914 | + cairo_get_matrix (cairo, &matrix); |
1915 | + //XXX: it is possible that we should only do sub pixel positioning if |
1916 | + // we are rendering fonts */ |
1917 | + if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) { |
1918 | + drawImageMaskPrescaled(state, ref, str, width, height, invert, gFalse, inlineImg); |
1919 | + } else { |
1920 | + drawImageMaskRegular(state, ref, str, width, height, invert, gFalse, inlineImg); |
1921 | + } |
1922 | + |
1923 | + if (state->getFillColorSpace()->getMode() == csPattern) { |
1924 | + cairo_set_source_rgb (cairo, 1, 1, 1); |
1925 | + cairo_set_matrix (cairo, &mask_matrix); |
1926 | + cairo_mask (cairo, mask); |
1927 | + } |
1928 | + |
1929 | + if (mask) |
1930 | + cairo_pattern_destroy (mask); |
1931 | + mask = cairo_pop_group (cairo); |
1932 | + |
1933 | + saveState(state); |
1934 | + double bbox[4] = {0,0,1,1}; // dummy |
1935 | + beginTransparencyGroup(state, bbox, state->getFillColorSpace(), |
1936 | + gTrue, gFalse, gFalse); |
1937 | +} |
1938 | + |
1939 | +void CairoOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { |
1940 | + double bbox[4] = {0,0,1,1}; // dummy |
1941 | + |
1942 | + endTransparencyGroup(state); |
1943 | + restoreState(state); |
1944 | + paintTransparencyGroup(state, bbox); |
1945 | + clearSoftMask(state); |
1946 | +} |
1947 | + |
1948 | +void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, |
1949 | + int width, int height, GBool invert, |
1950 | + GBool interpolate, GBool inlineImg) { |
1951 | + unsigned char *buffer; |
1952 | + unsigned char *dest; |
1953 | + cairo_surface_t *image; |
1954 | + cairo_pattern_t *pattern; |
1955 | + int x, y, i, bit; |
1956 | + ImageStream *imgStr; |
1957 | + Guchar *pix; |
1958 | + cairo_matrix_t matrix; |
1959 | + int invert_bit; |
1960 | + int row_stride; |
1961 | + cairo_filter_t filter; |
1962 | + |
1963 | + /* TODO: Do we want to cache these? */ |
1964 | + imgStr = new ImageStream(str, width, 1, 1); |
1965 | + imgStr->reset(); |
1966 | + |
1967 | + image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height); |
1968 | + if (cairo_surface_status (image)) |
1969 | + goto cleanup; |
1970 | + |
1971 | + buffer = cairo_image_surface_get_data (image); |
1972 | + row_stride = cairo_image_surface_get_stride (image); |
1973 | + |
1974 | + invert_bit = invert ? 1 : 0; |
1975 | + |
1976 | + for (y = 0; y < height; y++) { |
1977 | + pix = imgStr->getLine(); |
1978 | + dest = buffer + y * row_stride; |
1979 | + i = 0; |
1980 | + bit = 0; |
1981 | + for (x = 0; x < width; x++) { |
1982 | + if (bit == 0) |
1983 | + dest[i] = 0; |
1984 | + if (!(pix[x] ^ invert_bit)) { |
1985 | +#ifdef WORDS_BIGENDIAN |
1986 | + dest[i] |= (1 << (7 - bit)); |
1987 | +#else |
1988 | + dest[i] |= (1 << bit); |
1989 | +#endif |
1990 | + } |
1991 | + bit++; |
1992 | + if (bit > 7) { |
1993 | + bit = 0; |
1994 | + i++; |
1995 | + } |
1996 | + } |
1997 | + } |
1998 | + |
1999 | + filter = getFilterForSurface (image, interpolate); |
2000 | + |
2001 | + cairo_surface_mark_dirty (image); |
2002 | + pattern = cairo_pattern_create_for_surface (image); |
2003 | + cairo_surface_destroy (image); |
2004 | + if (cairo_pattern_status (pattern)) |
2005 | + goto cleanup; |
2006 | + |
2007 | + LOG (printf ("drawImageMask %dx%d\n", width, height)); |
2008 | + |
2009 | + cairo_pattern_set_filter (pattern, filter); |
2010 | + |
2011 | + if (!printing) |
2012 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
2013 | + |
2014 | + cairo_matrix_init_translate (&matrix, 0, height); |
2015 | + cairo_matrix_scale (&matrix, width, -height); |
2016 | + cairo_pattern_set_matrix (pattern, &matrix); |
2017 | + if (cairo_pattern_status (pattern)) { |
2018 | + cairo_pattern_destroy (pattern); |
2019 | + goto cleanup; |
2020 | + } |
2021 | + |
2022 | + if (state->getFillColorSpace()->getMode() == csPattern) { |
2023 | + mask = cairo_pattern_reference (pattern); |
2024 | + cairo_get_matrix (cairo, &mask_matrix); |
2025 | + } else if (!printing) { |
2026 | + cairo_save (cairo); |
2027 | + cairo_rectangle (cairo, 0., 0., 1., 1.); |
2028 | + cairo_clip (cairo); |
2029 | + cairo_mask (cairo, pattern); |
2030 | + cairo_restore (cairo); |
2031 | + } else { |
2032 | + cairo_mask (cairo, pattern); |
2033 | + } |
2034 | + |
2035 | + if (cairo_shape) { |
2036 | + cairo_save (cairo_shape); |
2037 | + cairo_set_source (cairo_shape, pattern); |
2038 | + if (!printing) { |
2039 | + cairo_rectangle (cairo_shape, 0., 0., 1., 1.); |
2040 | + cairo_fill (cairo_shape); |
2041 | + } else { |
2042 | + cairo_mask (cairo_shape, pattern); |
2043 | + } |
2044 | + cairo_restore (cairo_shape); |
2045 | + } |
2046 | + |
2047 | + cairo_pattern_destroy (pattern); |
2048 | + |
2049 | +cleanup: |
2050 | + imgStr->close(); |
2051 | + delete imgStr; |
2052 | +} |
2053 | + |
2054 | + |
2055 | +void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, |
2056 | + int width, int height, GBool invert, |
2057 | + GBool interpolate, GBool inlineImg) { |
2058 | + unsigned char *buffer; |
2059 | + cairo_surface_t *image; |
2060 | + cairo_pattern_t *pattern; |
2061 | + ImageStream *imgStr; |
2062 | + Guchar *pix; |
2063 | + cairo_matrix_t matrix; |
2064 | + int invert_bit; |
2065 | + int row_stride; |
2066 | + |
2067 | + /* cairo does a very poor job of scaling down images so we scale them ourselves */ |
2068 | + |
2069 | + LOG (printf ("drawImageMaskPrescaled %dx%d\n", width, height)); |
2070 | + |
2071 | + /* this scaling code is adopted from the splash image scaling code */ |
2072 | + cairo_get_matrix(cairo, &matrix); |
2073 | +#if 0 |
2074 | + printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); |
2075 | +#endif |
2076 | + /* this whole computation should be factored out */ |
2077 | + double xScale = matrix.xx; |
2078 | + double yScale = matrix.yy; |
2079 | + int tx, tx2, ty, ty2; /* the integer co-oridinates of the resulting image */ |
2080 | + int scaledHeight; |
2081 | + int scaledWidth; |
2082 | + if (xScale >= 0) { |
2083 | + tx = splashRound(matrix.x0 - 0.01); |
2084 | + tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; |
2085 | + } else { |
2086 | + tx = splashRound(matrix.x0 + 0.01) - 1; |
2087 | + tx2 = splashRound(matrix.x0 + xScale - 0.01); |
2088 | + } |
2089 | + scaledWidth = abs(tx2 - tx) + 1; |
2090 | + //scaledWidth = splashRound(fabs(xScale)); |
2091 | + if (scaledWidth == 0) { |
2092 | + // technically, this should draw nothing, but it generally seems |
2093 | + // better to draw a one-pixel-wide stripe rather than throwing it |
2094 | + // away |
2095 | + scaledWidth = 1; |
2096 | + } |
2097 | + if (yScale >= 0) { |
2098 | + ty = splashFloor(matrix.y0 + 0.01); |
2099 | + ty2 = splashCeil(matrix.y0 + yScale - 0.01); |
2100 | + } else { |
2101 | + ty = splashCeil(matrix.y0 - 0.01); |
2102 | + ty2 = splashFloor(matrix.y0 + yScale + 0.01); |
2103 | + } |
2104 | + scaledHeight = abs(ty2 - ty); |
2105 | + if (scaledHeight == 0) { |
2106 | + scaledHeight = 1; |
2107 | + } |
2108 | +#if 0 |
2109 | + printf("xscale: %g, yscale: %g\n", xScale, yScale); |
2110 | + printf("width: %d, height: %d\n", width, height); |
2111 | + printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight); |
2112 | +#endif |
2113 | + |
2114 | + /* compute the required padding */ |
2115 | + /* Padding is used to preserve the aspect ratio. |
2116 | + We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */ |
2117 | + int head_pad = 0; |
2118 | + int tail_pad = 0; |
2119 | + int total_pad = splashRound(height*(scaledHeight/fabs(yScale)) - height); |
2120 | + |
2121 | + /* compute the two pieces of padding */ |
2122 | + if (total_pad > 0) { |
2123 | + //XXX: i'm not positive fabs() is correct |
2124 | + float tail_error = fabs(matrix.y0 - ty); |
2125 | + float head_error = fabs(ty2 - (matrix.y0 + yScale)); |
2126 | + float tail_fraction = tail_error/(tail_error + head_error); |
2127 | + tail_pad = splashRound(total_pad*tail_fraction); |
2128 | + head_pad = total_pad - tail_pad; |
2129 | + } else { |
2130 | + tail_pad = 0; |
2131 | + head_pad = 0; |
2132 | + } |
2133 | + int origHeight = height; |
2134 | + height += tail_pad; |
2135 | + height += head_pad; |
2136 | +#if 0 |
2137 | + printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad); |
2138 | + printf("origHeight: %d height: %d\n", origHeight, height); |
2139 | + printf("ty: %d, ty2: %d\n", ty, ty2); |
2140 | +#endif |
2141 | + |
2142 | + /* TODO: Do we want to cache these? */ |
2143 | + imgStr = new ImageStream(str, width, 1, 1); |
2144 | + imgStr->reset(); |
2145 | + |
2146 | + invert_bit = invert ? 1 : 0; |
2147 | + |
2148 | + image = cairo_image_surface_create (CAIRO_FORMAT_A8, scaledWidth, scaledHeight); |
2149 | + if (cairo_surface_status (image)) { |
2150 | + imgStr->close(); |
2151 | + delete imgStr; |
2152 | + return; |
2153 | + } |
2154 | + |
2155 | + buffer = cairo_image_surface_get_data (image); |
2156 | + row_stride = cairo_image_surface_get_stride (image); |
2157 | + |
2158 | + int yp = height / scaledHeight; |
2159 | + int yq = height % scaledHeight; |
2160 | + int xp = width / scaledWidth; |
2161 | + int xq = width % scaledWidth; |
2162 | + int yt = 0; |
2163 | + int origHeight_c = origHeight; |
2164 | + /* use MIN() because yp might be > origHeight because of padding */ |
2165 | + unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp+1, origHeight)*width); |
2166 | + int lastYStep = 1; |
2167 | + int total = 0; |
2168 | + for (int y = 0; y < scaledHeight; y++) { |
2169 | + // y scale Bresenham |
2170 | + int yStep = yp; |
2171 | + yt += yq; |
2172 | + |
2173 | + if (yt >= scaledHeight) { |
2174 | + yt -= scaledHeight; |
2175 | + ++yStep; |
2176 | + } |
2177 | + |
2178 | + // read row (s) from image ignoring the padding as appropriate |
2179 | + { |
2180 | + int n = (yp > 0) ? yStep : lastYStep; |
2181 | + total += n; |
2182 | + if (n > 0) { |
2183 | + unsigned char *p = pixBuf; |
2184 | + int head_pad_count = head_pad; |
2185 | + int origHeight_count = origHeight; |
2186 | + int tail_pad_count = tail_pad; |
2187 | + for (int i=0; i<n; i++) { |
2188 | + // get row |
2189 | + if (head_pad_count) { |
2190 | + head_pad_count--; |
2191 | + } else if (origHeight_count) { |
2192 | + pix = imgStr->getLine(); |
2193 | + for (int j=0; j<width; j++) { |
2194 | + if (pix[j] ^ invert_bit) |
2195 | + p[j] = 0; |
2196 | + else |
2197 | + p[j] = 255; |
2198 | + } |
2199 | + origHeight_count--; |
2200 | + p += width; |
2201 | + } else if (tail_pad_count) { |
2202 | + tail_pad_count--; |
2203 | + } else { |
2204 | + printf("%d %d\n", n, total); |
2205 | + assert(0 && "over run\n"); |
2206 | + } |
2207 | + } |
2208 | + } |
2209 | + } |
2210 | + |
2211 | + lastYStep = yStep; |
2212 | + int k1 = y; |
2213 | + |
2214 | + int xt = 0; |
2215 | + int xSrc = 0; |
2216 | + int x1 = k1; |
2217 | + int n = yStep > 0 ? yStep : 1; |
2218 | + int origN = n; |
2219 | + |
2220 | + /* compute the size of padding and pixels that will be used for this row */ |
2221 | + int head_pad_size = MIN(n, head_pad); |
2222 | + n -= head_pad_size; |
2223 | + head_pad -= MIN(head_pad_size, yStep); |
2224 | + |
2225 | + int pix_size = MIN(n, origHeight); |
2226 | + n -= pix_size; |
2227 | + origHeight -= MIN(pix_size, yStep); |
2228 | + |
2229 | + int tail_pad_size = MIN(n, tail_pad); |
2230 | + n -= tail_pad_size; |
2231 | + tail_pad -= MIN(tail_pad_size, yStep); |
2232 | + if (n != 0) { |
2233 | + printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size); |
2234 | + assert(n == 0); |
2235 | + } |
2236 | + |
2237 | + for (int x = 0; x < scaledWidth; ++x) { |
2238 | + int xStep = xp; |
2239 | + xt += xq; |
2240 | + if (xt >= scaledWidth) { |
2241 | + xt -= scaledWidth; |
2242 | + ++xStep; |
2243 | + } |
2244 | + int m = xStep > 0 ? xStep : 1; |
2245 | + float pixAcc0 = 0; |
2246 | + /* could m * head_pad_size * tail_pad_size overflow? */ |
2247 | + if (invert_bit) { |
2248 | + pixAcc0 += m * head_pad_size * tail_pad_size * 255; |
2249 | + } else { |
2250 | + pixAcc0 += m * head_pad_size * tail_pad_size * 0; |
2251 | + } |
2252 | + /* Accumulate all of the source pixels for the destination pixel */ |
2253 | + for (int i = 0; i < pix_size; ++i) { |
2254 | + for (int j = 0; j< m; ++j) { |
2255 | + if (xSrc + i*width + j > MIN(yp + 1, origHeight_c)*width) { |
2256 | + 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); |
2257 | + printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size); |
2258 | + assert(0 && "bad access\n"); |
2259 | + } |
2260 | + pixAcc0 += pixBuf[xSrc + i*width + j]; |
2261 | + } |
2262 | + } |
2263 | + buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN*m)); |
2264 | + xSrc += xStep; |
2265 | + x1 += 1; |
2266 | + } |
2267 | + |
2268 | + } |
2269 | + free(pixBuf); |
2270 | + |
2271 | + cairo_surface_mark_dirty (image); |
2272 | + pattern = cairo_pattern_create_for_surface (image); |
2273 | + cairo_surface_destroy (image); |
2274 | + if (cairo_pattern_status (pattern)) { |
2275 | + imgStr->close(); |
2276 | + delete imgStr; |
2277 | + return; |
2278 | + } |
2279 | + |
2280 | + /* we should actually be using CAIRO_FILTER_NEAREST here. However, |
2281 | + * cairo doesn't yet do minifaction filtering causing scaled down |
2282 | + * images with CAIRO_FILTER_NEAREST to look really bad */ |
2283 | + cairo_pattern_set_filter (pattern, |
2284 | + interpolate ? CAIRO_FILTER_BEST : CAIRO_FILTER_FAST); |
2285 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
2286 | + |
2287 | + if (state->getFillColorSpace()->getMode() == csPattern) { |
2288 | + cairo_matrix_init_translate (&matrix, 0, scaledHeight); |
2289 | + cairo_matrix_scale (&matrix, scaledWidth, -scaledHeight); |
2290 | + cairo_pattern_set_matrix (pattern, &matrix); |
2291 | + if (cairo_pattern_status (pattern)) { |
2292 | + cairo_pattern_destroy (pattern); |
2293 | + imgStr->close(); |
2294 | + delete imgStr; |
2295 | + return; |
2296 | + } |
2297 | + |
2298 | + mask = cairo_pattern_reference (pattern); |
2299 | + cairo_get_matrix (cairo, &mask_matrix); |
2300 | + } else { |
2301 | + cairo_save (cairo); |
2302 | + |
2303 | + /* modify our current transformation so that the prescaled image |
2304 | + * goes where it is supposed to */ |
2305 | + cairo_get_matrix(cairo, &matrix); |
2306 | + cairo_scale(cairo, 1.0/matrix.xx, 1.0/matrix.yy); |
2307 | + // get integer co-ords |
2308 | + cairo_translate (cairo, tx - matrix.x0, ty2 - matrix.y0); |
2309 | + if (yScale > 0) |
2310 | + cairo_scale(cairo, 1, -1); |
2311 | + |
2312 | + cairo_rectangle (cairo, 0., 0., scaledWidth, scaledHeight); |
2313 | + cairo_clip (cairo); |
2314 | + cairo_mask (cairo, pattern); |
2315 | + |
2316 | + //cairo_get_matrix(cairo, &matrix); |
2317 | + //printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); |
2318 | + cairo_restore(cairo); |
2319 | + } |
2320 | + |
2321 | + if (cairo_shape) { |
2322 | + cairo_save (cairo_shape); |
2323 | + |
2324 | + /* modify our current transformation so that the prescaled image |
2325 | + * goes where it is supposed to */ |
2326 | + cairo_get_matrix(cairo_shape, &matrix); |
2327 | + cairo_scale(cairo_shape, 1.0/matrix.xx, 1.0/matrix.yy); |
2328 | + // get integer co-ords |
2329 | + cairo_translate (cairo_shape, tx - matrix.x0, ty2 - matrix.y0); |
2330 | + if (yScale > 0) |
2331 | + cairo_scale(cairo_shape, 1, -1); |
2332 | + |
2333 | + cairo_rectangle (cairo_shape, 0., 0., scaledWidth, scaledHeight); |
2334 | + cairo_fill (cairo_shape); |
2335 | + |
2336 | + cairo_restore(cairo_shape); |
2337 | + } |
2338 | + |
2339 | + cairo_pattern_destroy (pattern); |
2340 | + |
2341 | + imgStr->close(); |
2342 | + delete imgStr; |
2343 | +} |
2344 | + |
2345 | +void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, |
2346 | + Stream *str, int width, int height, |
2347 | + GfxImageColorMap *colorMap, |
2348 | + GBool interpolate, |
2349 | + Stream *maskStr, int maskWidth, |
2350 | + int maskHeight, GBool maskInvert, |
2351 | + GBool maskInterpolate) |
2352 | +{ |
2353 | + ImageStream *maskImgStr, *imgStr; |
2354 | + int row_stride; |
2355 | + unsigned char *maskBuffer, *buffer; |
2356 | + unsigned char *maskDest; |
2357 | + unsigned int *dest; |
2358 | + cairo_surface_t *maskImage, *image; |
2359 | + cairo_pattern_t *maskPattern, *pattern; |
2360 | + cairo_matrix_t matrix; |
2361 | + cairo_matrix_t maskMatrix; |
2362 | + Guchar *pix; |
2363 | + int x, y; |
2364 | + int invert_bit; |
2365 | + cairo_filter_t filter; |
2366 | + cairo_filter_t maskFilter; |
2367 | + |
2368 | + maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1); |
2369 | + maskImgStr->reset(); |
2370 | + |
2371 | + maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); |
2372 | + if (cairo_surface_status (maskImage)) { |
2373 | + maskImgStr->close(); |
2374 | + delete maskImgStr; |
2375 | + return; |
2376 | + } |
2377 | + |
2378 | + maskBuffer = cairo_image_surface_get_data (maskImage); |
2379 | + row_stride = cairo_image_surface_get_stride (maskImage); |
2380 | + |
2381 | + invert_bit = maskInvert ? 1 : 0; |
2382 | + |
2383 | + for (y = 0; y < maskHeight; y++) { |
2384 | + pix = maskImgStr->getLine(); |
2385 | + maskDest = maskBuffer + y * row_stride; |
2386 | + for (x = 0; x < maskWidth; x++) { |
2387 | + if (pix[x] ^ invert_bit) |
2388 | + *maskDest++ = 0; |
2389 | + else |
2390 | + *maskDest++ = 255; |
2391 | + } |
2392 | + } |
2393 | + |
2394 | + maskImgStr->close(); |
2395 | + delete maskImgStr; |
2396 | + |
2397 | + maskFilter = getFilterForSurface (maskImage, maskInterpolate); |
2398 | + |
2399 | + cairo_surface_mark_dirty (maskImage); |
2400 | + maskPattern = cairo_pattern_create_for_surface (maskImage); |
2401 | + cairo_surface_destroy (maskImage); |
2402 | + if (cairo_pattern_status (maskPattern)) |
2403 | + return; |
2404 | + |
2405 | +#if 0 |
2406 | + /* ICCBased color space doesn't do any color correction |
2407 | + * so check its underlying color space as well */ |
2408 | + int is_identity_transform; |
2409 | + is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || |
2410 | + (colorMap->getColorSpace()->getMode() == csICCBased && |
2411 | + ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); |
2412 | +#endif |
2413 | + |
2414 | + /* TODO: Do we want to cache these? */ |
2415 | + imgStr = new ImageStream(str, width, |
2416 | + colorMap->getNumPixelComps(), |
2417 | + colorMap->getBits()); |
2418 | + imgStr->reset(); |
2419 | + |
2420 | + image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); |
2421 | + if (cairo_surface_status (image)) |
2422 | + goto cleanup; |
2423 | + |
2424 | + buffer = cairo_image_surface_get_data (image); |
2425 | + row_stride = cairo_image_surface_get_stride (image); |
2426 | + for (y = 0; y < height; y++) { |
2427 | + dest = (unsigned int *) (buffer + y * row_stride); |
2428 | + pix = imgStr->getLine(); |
2429 | + colorMap->getRGBLine (pix, dest, width); |
2430 | + } |
2431 | + |
2432 | + filter = getFilterForSurface (image, interpolate); |
2433 | + |
2434 | + cairo_surface_mark_dirty (image); |
2435 | + pattern = cairo_pattern_create_for_surface (image); |
2436 | + cairo_surface_destroy (image); |
2437 | + if (cairo_pattern_status (pattern)) |
2438 | + goto cleanup; |
2439 | + |
2440 | + LOG (printf ("drawMaskedImage %dx%d\n", width, height)); |
2441 | + |
2442 | + cairo_pattern_set_filter (pattern, filter); |
2443 | + cairo_pattern_set_filter (maskPattern, maskFilter); |
2444 | + |
2445 | + if (!printing) { |
2446 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
2447 | + cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); |
2448 | + } |
2449 | + |
2450 | + cairo_matrix_init_translate (&matrix, 0, height); |
2451 | + cairo_matrix_scale (&matrix, width, -height); |
2452 | + cairo_pattern_set_matrix (pattern, &matrix); |
2453 | + if (cairo_pattern_status (pattern)) { |
2454 | + cairo_pattern_destroy (pattern); |
2455 | + cairo_pattern_destroy (maskPattern); |
2456 | + goto cleanup; |
2457 | + } |
2458 | + |
2459 | + cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); |
2460 | + cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); |
2461 | + cairo_pattern_set_matrix (maskPattern, &maskMatrix); |
2462 | + if (cairo_pattern_status (maskPattern)) { |
2463 | + cairo_pattern_destroy (maskPattern); |
2464 | + cairo_pattern_destroy (pattern); |
2465 | + goto cleanup; |
2466 | + } |
2467 | + |
2468 | + if (!printing) { |
2469 | + cairo_save (cairo); |
2470 | + cairo_set_source (cairo, pattern); |
2471 | + cairo_rectangle (cairo, 0., 0., 1., 1.); |
2472 | + cairo_clip (cairo); |
2473 | + cairo_mask (cairo, maskPattern); |
2474 | + cairo_restore (cairo); |
2475 | + } else { |
2476 | + cairo_set_source (cairo, pattern); |
2477 | + cairo_mask (cairo, maskPattern); |
2478 | + } |
2479 | + |
2480 | + if (cairo_shape) { |
2481 | + cairo_save (cairo_shape); |
2482 | + cairo_set_source (cairo_shape, pattern); |
2483 | + if (!printing) { |
2484 | + cairo_rectangle (cairo_shape, 0., 0., 1., 1.); |
2485 | + cairo_fill (cairo_shape); |
2486 | + } else { |
2487 | + cairo_mask (cairo_shape, pattern); |
2488 | + } |
2489 | + cairo_restore (cairo_shape); |
2490 | + } |
2491 | + |
2492 | + cairo_pattern_destroy (maskPattern); |
2493 | + cairo_pattern_destroy (pattern); |
2494 | + |
2495 | +cleanup: |
2496 | + imgStr->close(); |
2497 | + delete imgStr; |
2498 | +} |
2499 | + |
2500 | + |
2501 | +//XXX: is this affect by AIS(alpha is shape)? |
2502 | +void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, |
2503 | + int width, int height, |
2504 | + GfxImageColorMap *colorMap, |
2505 | + GBool interpolate, |
2506 | + Stream *maskStr, |
2507 | + int maskWidth, int maskHeight, |
2508 | + GfxImageColorMap *maskColorMap, |
2509 | + GBool maskInterpolate) |
2510 | +{ |
2511 | + ImageStream *maskImgStr, *imgStr; |
2512 | + int row_stride; |
2513 | + unsigned char *maskBuffer, *buffer; |
2514 | + unsigned char *maskDest; |
2515 | + unsigned int *dest; |
2516 | + cairo_surface_t *maskImage, *image; |
2517 | + cairo_pattern_t *maskPattern, *pattern; |
2518 | + cairo_matrix_t maskMatrix, matrix; |
2519 | + Guchar *pix; |
2520 | + int y; |
2521 | + cairo_filter_t filter; |
2522 | + cairo_filter_t maskFilter; |
2523 | + |
2524 | + maskImgStr = new ImageStream(maskStr, maskWidth, |
2525 | + maskColorMap->getNumPixelComps(), |
2526 | + maskColorMap->getBits()); |
2527 | + maskImgStr->reset(); |
2528 | + |
2529 | + maskImage = cairo_image_surface_create (CAIRO_FORMAT_A8, maskWidth, maskHeight); |
2530 | + if (cairo_surface_status (maskImage)) { |
2531 | + maskImgStr->close(); |
2532 | + delete maskImgStr; |
2533 | + return; |
2534 | + } |
2535 | + |
2536 | + maskBuffer = cairo_image_surface_get_data (maskImage); |
2537 | + row_stride = cairo_image_surface_get_stride (maskImage); |
2538 | + for (y = 0; y < maskHeight; y++) { |
2539 | + maskDest = (unsigned char *) (maskBuffer + y * row_stride); |
2540 | + pix = maskImgStr->getLine(); |
2541 | + maskColorMap->getGrayLine (pix, maskDest, maskWidth); |
2542 | + } |
2543 | + |
2544 | + maskImgStr->close(); |
2545 | + delete maskImgStr; |
2546 | + |
2547 | + maskFilter = getFilterForSurface (maskImage, maskInterpolate); |
2548 | + |
2549 | + cairo_surface_mark_dirty (maskImage); |
2550 | + maskPattern = cairo_pattern_create_for_surface (maskImage); |
2551 | + cairo_surface_destroy (maskImage); |
2552 | + if (cairo_pattern_status (maskPattern)) |
2553 | + return; |
2554 | + |
2555 | +#if 0 |
2556 | + /* ICCBased color space doesn't do any color correction |
2557 | + * so check its underlying color space as well */ |
2558 | + int is_identity_transform; |
2559 | + is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || |
2560 | + (colorMap->getColorSpace()->getMode() == csICCBased && |
2561 | + ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); |
2562 | +#endif |
2563 | + |
2564 | + /* TODO: Do we want to cache these? */ |
2565 | + imgStr = new ImageStream(str, width, |
2566 | + colorMap->getNumPixelComps(), |
2567 | + colorMap->getBits()); |
2568 | + imgStr->reset(); |
2569 | + |
2570 | + image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); |
2571 | + if (cairo_surface_status (image)) |
2572 | + goto cleanup; |
2573 | + |
2574 | + buffer = cairo_image_surface_get_data (image); |
2575 | + row_stride = cairo_image_surface_get_stride (image); |
2576 | + for (y = 0; y < height; y++) { |
2577 | + dest = (unsigned int *) (buffer + y * row_stride); |
2578 | + pix = imgStr->getLine(); |
2579 | + colorMap->getRGBLine (pix, dest, width); |
2580 | + } |
2581 | + |
2582 | + filter = getFilterForSurface (image, interpolate); |
2583 | + |
2584 | + cairo_surface_mark_dirty (image); |
2585 | + |
2586 | + setMimeData(str, ref, image); |
2587 | + |
2588 | + pattern = cairo_pattern_create_for_surface (image); |
2589 | + cairo_surface_destroy (image); |
2590 | + if (cairo_pattern_status (pattern)) |
2591 | + goto cleanup; |
2592 | + |
2593 | + LOG (printf ("drawSoftMaskedImage %dx%d\n", width, height)); |
2594 | + |
2595 | + cairo_pattern_set_filter (pattern, filter); |
2596 | + cairo_pattern_set_filter (maskPattern, maskFilter); |
2597 | + |
2598 | + if (!printing) { |
2599 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
2600 | + cairo_pattern_set_extend (maskPattern, CAIRO_EXTEND_PAD); |
2601 | + } |
2602 | + |
2603 | + cairo_matrix_init_translate (&matrix, 0, height); |
2604 | + cairo_matrix_scale (&matrix, width, -height); |
2605 | + cairo_pattern_set_matrix (pattern, &matrix); |
2606 | + if (cairo_pattern_status (pattern)) { |
2607 | + cairo_pattern_destroy (pattern); |
2608 | + cairo_pattern_destroy (maskPattern); |
2609 | + goto cleanup; |
2610 | + } |
2611 | + |
2612 | + cairo_matrix_init_translate (&maskMatrix, 0, maskHeight); |
2613 | + cairo_matrix_scale (&maskMatrix, maskWidth, -maskHeight); |
2614 | + cairo_pattern_set_matrix (maskPattern, &maskMatrix); |
2615 | + if (cairo_pattern_status (maskPattern)) { |
2616 | + cairo_pattern_destroy (maskPattern); |
2617 | + cairo_pattern_destroy (pattern); |
2618 | + goto cleanup; |
2619 | + } |
2620 | + |
2621 | + if (fill_opacity != 1.0) |
2622 | + cairo_push_group (cairo); |
2623 | + else |
2624 | + cairo_save (cairo); |
2625 | + |
2626 | + cairo_set_source (cairo, pattern); |
2627 | + if (!printing) { |
2628 | + cairo_rectangle (cairo, 0., 0., |
2629 | + MIN (width, maskWidth) / (double)width, |
2630 | + MIN (height, maskHeight) / (double)height); |
2631 | + cairo_clip (cairo); |
2632 | + } |
2633 | + cairo_mask (cairo, maskPattern); |
2634 | + |
2635 | + if (fill_opacity != 1.0) { |
2636 | + cairo_pop_group_to_source (cairo); |
2637 | + cairo_save (cairo); |
2638 | + if (!printing) { |
2639 | + cairo_rectangle (cairo, 0., 0., |
2640 | + MIN (width, maskWidth) / (double)width, |
2641 | + MIN (height, maskHeight) / (double)height); |
2642 | + cairo_clip (cairo); |
2643 | + } |
2644 | + cairo_paint_with_alpha (cairo, fill_opacity); |
2645 | + } |
2646 | + cairo_restore (cairo); |
2647 | + |
2648 | + if (cairo_shape) { |
2649 | + cairo_save (cairo_shape); |
2650 | + cairo_set_source (cairo_shape, pattern); |
2651 | + if (!printing) { |
2652 | + cairo_rectangle (cairo_shape, 0., 0., |
2653 | + MIN (width, maskWidth) / (double)width, |
2654 | + MIN (height, maskHeight) / (double)height); |
2655 | + cairo_fill (cairo_shape); |
2656 | + } else { |
2657 | + cairo_mask (cairo_shape, pattern); |
2658 | + } |
2659 | + cairo_restore (cairo_shape); |
2660 | + } |
2661 | + |
2662 | + cairo_pattern_destroy (maskPattern); |
2663 | + cairo_pattern_destroy (pattern); |
2664 | + |
2665 | +cleanup: |
2666 | + imgStr->close(); |
2667 | + delete imgStr; |
2668 | +} |
2669 | + |
2670 | +GBool CairoOutputDev::getStreamData (Stream *str, char **buffer, int *length) |
2671 | +{ |
2672 | + int len, i; |
2673 | + char *strBuffer; |
2674 | + |
2675 | + len = 0; |
2676 | + str->close(); |
2677 | + str->reset(); |
2678 | + while (str->getChar() != EOF) len++; |
2679 | + if (len == 0) |
2680 | + return gFalse; |
2681 | + |
2682 | + strBuffer = (char *)gmalloc (len); |
2683 | + |
2684 | + str->close(); |
2685 | + str->reset(); |
2686 | + for (i = 0; i < len; ++i) |
2687 | + strBuffer[i] = str->getChar(); |
2688 | + |
2689 | + *buffer = strBuffer; |
2690 | + *length = len; |
2691 | + |
2692 | + return gTrue; |
2693 | +} |
2694 | + |
2695 | +void CairoOutputDev::setMimeData(Stream *str, Object *ref, cairo_surface_t *image) |
2696 | +{ |
2697 | + char *strBuffer; |
2698 | + int len; |
2699 | + Object obj; |
2700 | + |
2701 | + if (!printing || !(str->getKind() == strDCT || str->getKind() == strJPX)) |
2702 | + return; |
2703 | + |
2704 | + // colorspace in stream dict may be different from colorspace in jpx |
2705 | + // data |
2706 | + if (str->getKind() == strJPX) { |
2707 | + GBool hasColorSpace = !str->getDict()->lookup("ColorSpace", &obj)->isNull(); |
2708 | + obj.free(); |
2709 | + if (hasColorSpace) |
2710 | + return; |
2711 | + } |
2712 | + |
2713 | + if (getStreamData (str->getNextStream(), &strBuffer, &len)) { |
2714 | + cairo_status_t st; |
2715 | + |
2716 | +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) |
2717 | + if (ref && ref->isRef()) { |
2718 | + Ref imgRef = ref->getRef(); |
2719 | + GooString *surfaceId = new GooString("poppler-surface-"); |
2720 | + surfaceId->appendf("{0:d}-{1:d}", imgRef.gen, imgRef.num); |
2721 | + char *idBuffer = copyString(surfaceId->getCString()); |
2722 | + st = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_UNIQUE_ID, |
2723 | + (const unsigned char *)idBuffer, |
2724 | + surfaceId->getLength(), |
2725 | + gfree, idBuffer); |
2726 | + if (st) |
2727 | + gfree(idBuffer); |
2728 | + delete surfaceId; |
2729 | + } |
2730 | +#endif |
2731 | + |
2732 | + st = cairo_surface_set_mime_data (image, |
2733 | + str->getKind() == strDCT ? |
2734 | + CAIRO_MIME_TYPE_JPEG : CAIRO_MIME_TYPE_JP2, |
2735 | + (const unsigned char *)strBuffer, len, |
2736 | + gfree, strBuffer); |
2737 | + if (st) |
2738 | + gfree (strBuffer); |
2739 | + } |
2740 | +} |
2741 | + |
2742 | +void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, |
2743 | + int width, int height, |
2744 | + GfxImageColorMap *colorMap, |
2745 | + GBool interpolate, |
2746 | + int *maskColors, GBool inlineImg) |
2747 | +{ |
2748 | + cairo_surface_t *image; |
2749 | + cairo_pattern_t *pattern, *maskPattern; |
2750 | + ImageStream *imgStr; |
2751 | + cairo_matrix_t matrix; |
2752 | + unsigned char *buffer; |
2753 | + int stride, i; |
2754 | + GfxRGB *lookup = NULL; |
2755 | + cairo_filter_t filter = CAIRO_FILTER_BILINEAR; |
2756 | + |
2757 | + /* TODO: Do we want to cache these? */ |
2758 | + imgStr = new ImageStream(str, width, |
2759 | + colorMap->getNumPixelComps(), |
2760 | + colorMap->getBits()); |
2761 | + imgStr->reset(); |
2762 | + |
2763 | +#if 0 |
2764 | + /* ICCBased color space doesn't do any color correction |
2765 | + * so check its underlying color space as well */ |
2766 | + int is_identity_transform; |
2767 | + is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || |
2768 | + (colorMap->getColorSpace()->getMode() == csICCBased && |
2769 | + ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); |
2770 | +#endif |
2771 | + |
2772 | + image = cairo_image_surface_create (maskColors ? |
2773 | + CAIRO_FORMAT_ARGB32 : |
2774 | + CAIRO_FORMAT_RGB24, |
2775 | + width, height); |
2776 | + if (cairo_surface_status (image)) |
2777 | + goto cleanup; |
2778 | + |
2779 | + // special case for one-channel (monochrome/gray/separation) images: |
2780 | + // build a lookup table here |
2781 | + if (colorMap->getNumPixelComps() == 1) { |
2782 | + int n; |
2783 | + Guchar pix; |
2784 | + |
2785 | + n = 1 << colorMap->getBits(); |
2786 | + lookup = (GfxRGB *)gmallocn(n, sizeof(GfxRGB)); |
2787 | + for (i = 0; i < n; ++i) { |
2788 | + pix = (Guchar)i; |
2789 | + |
2790 | + colorMap->getRGB(&pix, &lookup[i]); |
2791 | + } |
2792 | + } |
2793 | + |
2794 | + buffer = cairo_image_surface_get_data (image); |
2795 | + stride = cairo_image_surface_get_stride (image); |
2796 | + for (int y = 0; y < height; y++) { |
2797 | + uint32_t *dest = (uint32_t *) (buffer + y * stride); |
2798 | + Guchar *pix = imgStr->getLine(); |
2799 | + |
2800 | + if (lookup) { |
2801 | + Guchar *p = pix; |
2802 | + GfxRGB rgb; |
2803 | + |
2804 | + for (i = 0; i < width; i++) { |
2805 | + rgb = lookup[*p]; |
2806 | + dest[i] = |
2807 | + ((int) colToByte(rgb.r) << 16) | |
2808 | + ((int) colToByte(rgb.g) << 8) | |
2809 | + ((int) colToByte(rgb.b) << 0); |
2810 | + p++; |
2811 | + } |
2812 | + } else { |
2813 | + colorMap->getRGBLine (pix, dest, width); |
2814 | + } |
2815 | + |
2816 | + if (maskColors) { |
2817 | + for (int x = 0; x < width; x++) { |
2818 | + bool is_opaque = false; |
2819 | + for (int i = 0; i < colorMap->getNumPixelComps(); ++i) { |
2820 | + if (pix[i] < maskColors[2*i] || |
2821 | + pix[i] > maskColors[2*i+1]) { |
2822 | + is_opaque = true; |
2823 | + break; |
2824 | + } |
2825 | + } |
2826 | + if (is_opaque) |
2827 | + *dest |= 0xff000000; |
2828 | + else |
2829 | + *dest = 0; |
2830 | + dest++; |
2831 | + pix += colorMap->getNumPixelComps(); |
2832 | + } |
2833 | + } |
2834 | + } |
2835 | + gfree(lookup); |
2836 | + |
2837 | + LOG (printf ("drawImage %dx%d\n", width, height)); |
2838 | + |
2839 | + cairo_surface_t *scaled_surface; |
2840 | + |
2841 | + scaled_surface = downscaleSurface (image); |
2842 | + if (scaled_surface) { |
2843 | + if (cairo_surface_status (scaled_surface)) |
2844 | + goto cleanup; |
2845 | + cairo_surface_destroy (image); |
2846 | + image = scaled_surface; |
2847 | + width = cairo_image_surface_get_width (image); |
2848 | + height = cairo_image_surface_get_height (image); |
2849 | + } else { |
2850 | + filter = getFilterForSurface (image, interpolate); |
2851 | + } |
2852 | + |
2853 | + cairo_surface_mark_dirty (image); |
2854 | + |
2855 | + if (!inlineImg) /* don't read stream twice if it is an inline image */ |
2856 | + setMimeData(str, ref, image); |
2857 | + |
2858 | + pattern = cairo_pattern_create_for_surface (image); |
2859 | + cairo_surface_destroy (image); |
2860 | + if (cairo_pattern_status (pattern)) |
2861 | + goto cleanup; |
2862 | + |
2863 | + cairo_pattern_set_filter (pattern, filter); |
2864 | + |
2865 | + if (!printing) |
2866 | + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); |
2867 | + |
2868 | + cairo_matrix_init_translate (&matrix, 0, height); |
2869 | + cairo_matrix_scale (&matrix, width, -height); |
2870 | + cairo_pattern_set_matrix (pattern, &matrix); |
2871 | + if (cairo_pattern_status (pattern)) { |
2872 | + cairo_pattern_destroy (pattern); |
2873 | + goto cleanup; |
2874 | + } |
2875 | + |
2876 | + if (!mask && fill_opacity != 1.0) { |
2877 | + maskPattern = cairo_pattern_create_rgba (1., 1., 1., fill_opacity); |
2878 | + } else if (mask) { |
2879 | + maskPattern = cairo_pattern_reference (mask); |
2880 | + } else { |
2881 | + maskPattern = NULL; |
2882 | + } |
2883 | + |
2884 | + cairo_save (cairo); |
2885 | + cairo_set_source (cairo, pattern); |
2886 | + if (!printing) |
2887 | + cairo_rectangle (cairo, 0., 0., 1., 1.); |
2888 | + if (maskPattern) { |
2889 | + if (!printing) |
2890 | + cairo_clip (cairo); |
2891 | + cairo_set_matrix (cairo, &mask_matrix); |
2892 | + cairo_mask (cairo, maskPattern); |
2893 | + } else { |
2894 | + if (printing) |
2895 | + cairo_paint (cairo); |
2896 | + else |
2897 | + cairo_fill (cairo); |
2898 | + } |
2899 | + cairo_restore (cairo); |
2900 | + |
2901 | + cairo_pattern_destroy (maskPattern); |
2902 | + |
2903 | + if (cairo_shape) { |
2904 | + cairo_save (cairo_shape); |
2905 | + cairo_set_source (cairo_shape, pattern); |
2906 | + if (printing) { |
2907 | + cairo_paint (cairo_shape); |
2908 | + } else { |
2909 | + cairo_rectangle (cairo_shape, 0., 0., 1., 1.); |
2910 | + cairo_fill (cairo_shape); |
2911 | + } |
2912 | + cairo_restore (cairo_shape); |
2913 | + } |
2914 | + |
2915 | + cairo_pattern_destroy (pattern); |
2916 | + |
2917 | +cleanup: |
2918 | + imgStr->close(); |
2919 | + delete imgStr; |
2920 | +} |
2921 | + |
2922 | + |
2923 | +//------------------------------------------------------------------------ |
2924 | +// ImageOutputDev |
2925 | +//------------------------------------------------------------------------ |
2926 | + |
2927 | +CairoImageOutputDev::CairoImageOutputDev() |
2928 | +{ |
2929 | + images = NULL; |
2930 | + numImages = 0; |
2931 | + size = 0; |
2932 | + imgDrawCbk = NULL; |
2933 | + imgDrawCbkData = NULL; |
2934 | +} |
2935 | + |
2936 | +CairoImageOutputDev::~CairoImageOutputDev() |
2937 | +{ |
2938 | + int i; |
2939 | + |
2940 | + for (i = 0; i < numImages; i++) |
2941 | + delete images[i]; |
2942 | + gfree (images); |
2943 | +} |
2944 | + |
2945 | +void CairoImageOutputDev::saveImage(CairoImage *image) |
2946 | +{ |
2947 | + if (numImages >= size) { |
2948 | + size += 16; |
2949 | + images = (CairoImage **) greallocn (images, size, sizeof (CairoImage *)); |
2950 | + } |
2951 | + images[numImages++] = image; |
2952 | +} |
2953 | + |
2954 | +void CairoImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, |
2955 | + int width, int height, GBool invert, |
2956 | + GBool interpolate, GBool inlineImg) |
2957 | +{ |
2958 | + cairo_t *cr; |
2959 | + cairo_surface_t *surface; |
2960 | + double x1, y1, x2, y2; |
2961 | + double *ctm; |
2962 | + double mat[6]; |
2963 | + CairoImage *image; |
2964 | + |
2965 | + ctm = state->getCTM(); |
2966 | + |
2967 | + mat[0] = ctm[0]; |
2968 | + mat[1] = ctm[1]; |
2969 | + mat[2] = -ctm[2]; |
2970 | + mat[3] = -ctm[3]; |
2971 | + mat[4] = ctm[2] + ctm[4]; |
2972 | + mat[5] = ctm[3] + ctm[5]; |
2973 | + x1 = mat[4]; |
2974 | + y1 = mat[5]; |
2975 | + x2 = x1 + width; |
2976 | + y2 = y1 + height; |
2977 | + |
2978 | + image = new CairoImage (x1, y1, x2, y2); |
2979 | + saveImage (image); |
2980 | + |
2981 | + if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { |
2982 | + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
2983 | + cr = cairo_create (surface); |
2984 | + setCairo (cr); |
2985 | + cairo_translate (cr, 0, height); |
2986 | + cairo_scale (cr, width, -height); |
2987 | + |
2988 | + CairoOutputDev::drawImageMask(state, ref, str, width, height, invert, interpolate, inlineImg); |
2989 | + image->setImage (surface); |
2990 | + |
2991 | + setCairo (NULL); |
2992 | + cairo_surface_destroy (surface); |
2993 | + cairo_destroy (cr); |
2994 | + } |
2995 | +} |
2996 | + |
2997 | +void CairoImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, |
2998 | + int width, int height, GfxImageColorMap *colorMap, |
2999 | + GBool interpolate, int *maskColors, GBool inlineImg) |
3000 | +{ |
3001 | + cairo_t *cr; |
3002 | + cairo_surface_t *surface; |
3003 | + double x1, y1, x2, y2; |
3004 | + double *ctm; |
3005 | + double mat[6]; |
3006 | + CairoImage *image; |
3007 | + |
3008 | + ctm = state->getCTM(); |
3009 | + |
3010 | + mat[0] = ctm[0]; |
3011 | + mat[1] = ctm[1]; |
3012 | + mat[2] = -ctm[2]; |
3013 | + mat[3] = -ctm[3]; |
3014 | + mat[4] = ctm[2] + ctm[4]; |
3015 | + mat[5] = ctm[3] + ctm[5]; |
3016 | + x1 = mat[4]; |
3017 | + y1 = mat[5]; |
3018 | + x2 = x1 + width; |
3019 | + y2 = y1 + height; |
3020 | + |
3021 | + image = new CairoImage (x1, y1, x2, y2); |
3022 | + saveImage (image); |
3023 | + |
3024 | + if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { |
3025 | + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
3026 | + cr = cairo_create (surface); |
3027 | + setCairo (cr); |
3028 | + cairo_translate (cr, 0, height); |
3029 | + cairo_scale (cr, width, -height); |
3030 | + |
3031 | + CairoOutputDev::drawImage(state, ref, str, width, height, colorMap, interpolate, maskColors, inlineImg); |
3032 | + image->setImage (surface); |
3033 | + |
3034 | + setCairo (NULL); |
3035 | + cairo_surface_destroy (surface); |
3036 | + cairo_destroy (cr); |
3037 | + } |
3038 | +} |
3039 | + |
3040 | +void CairoImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, |
3041 | + int width, int height, |
3042 | + GfxImageColorMap *colorMap, |
3043 | + GBool interpolate, |
3044 | + Stream *maskStr, |
3045 | + int maskWidth, int maskHeight, |
3046 | + GfxImageColorMap *maskColorMap, |
3047 | + GBool maskInterpolate) |
3048 | +{ |
3049 | + cairo_t *cr; |
3050 | + cairo_surface_t *surface; |
3051 | + double x1, y1, x2, y2; |
3052 | + double *ctm; |
3053 | + double mat[6]; |
3054 | + CairoImage *image; |
3055 | + |
3056 | + ctm = state->getCTM(); |
3057 | + |
3058 | + mat[0] = ctm[0]; |
3059 | + mat[1] = ctm[1]; |
3060 | + mat[2] = -ctm[2]; |
3061 | + mat[3] = -ctm[3]; |
3062 | + mat[4] = ctm[2] + ctm[4]; |
3063 | + mat[5] = ctm[3] + ctm[5]; |
3064 | + x1 = mat[4]; |
3065 | + y1 = mat[5]; |
3066 | + x2 = x1 + width; |
3067 | + y2 = y1 + height; |
3068 | + |
3069 | + image = new CairoImage (x1, y1, x2, y2); |
3070 | + saveImage (image); |
3071 | + |
3072 | + if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { |
3073 | + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
3074 | + cr = cairo_create (surface); |
3075 | + setCairo (cr); |
3076 | + cairo_translate (cr, 0, height); |
3077 | + cairo_scale (cr, width, -height); |
3078 | + |
3079 | + CairoOutputDev::drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, |
3080 | + maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); |
3081 | + image->setImage (surface); |
3082 | + |
3083 | + setCairo (NULL); |
3084 | + cairo_surface_destroy (surface); |
3085 | + cairo_destroy (cr); |
3086 | + } |
3087 | +} |
3088 | + |
3089 | +void CairoImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, |
3090 | + int width, int height, |
3091 | + GfxImageColorMap *colorMap, |
3092 | + GBool interpolate, |
3093 | + Stream *maskStr, |
3094 | + int maskWidth, int maskHeight, |
3095 | + GBool maskInvert, GBool maskInterpolate) |
3096 | +{ |
3097 | + cairo_t *cr; |
3098 | + cairo_surface_t *surface; |
3099 | + double x1, y1, x2, y2; |
3100 | + double *ctm; |
3101 | + double mat[6]; |
3102 | + CairoImage *image; |
3103 | + |
3104 | + ctm = state->getCTM(); |
3105 | + |
3106 | + mat[0] = ctm[0]; |
3107 | + mat[1] = ctm[1]; |
3108 | + mat[2] = -ctm[2]; |
3109 | + mat[3] = -ctm[3]; |
3110 | + mat[4] = ctm[2] + ctm[4]; |
3111 | + mat[5] = ctm[3] + ctm[5]; |
3112 | + x1 = mat[4]; |
3113 | + y1 = mat[5]; |
3114 | + x2 = x1 + width; |
3115 | + y2 = y1 + height; |
3116 | + |
3117 | + image = new CairoImage (x1, y1, x2, y2); |
3118 | + saveImage (image); |
3119 | + |
3120 | + if (imgDrawCbk && imgDrawCbk (numImages - 1, imgDrawCbkData)) { |
3121 | + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
3122 | + cr = cairo_create (surface); |
3123 | + setCairo (cr); |
3124 | + cairo_translate (cr, 0, height); |
3125 | + cairo_scale (cr, width, -height); |
3126 | + |
3127 | + CairoOutputDev::drawMaskedImage(state, ref, str, width, height, colorMap, interpolate, |
3128 | + maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); |
3129 | + image->setImage (surface); |
3130 | + |
3131 | + setCairo (NULL); |
3132 | + cairo_surface_destroy (surface); |
3133 | + cairo_destroy (cr); |
3134 | + } |
3135 | +} |
3136 | |
3137 | === modified file 'debian/changelog' |
3138 | --- debian/changelog 2012-09-12 10:53:15 +0000 |
3139 | +++ debian/changelog 2012-11-13 19:26:27 +0000 |
3140 | @@ -1,3 +1,11 @@ |
3141 | +poppler (0.20.4-0ubuntu2) raring; urgency=low |
3142 | + |
3143 | + * debian/patches/git_gouraud_shading_support.patch: (LP: #1072129) |
3144 | + - Evince crashes after opening certain PDF file because parameterized |
3145 | + Gouraud shading is not supported. |
3146 | + |
3147 | + -- Matthieu Baerts (matttbe) <matttbe@ubuntu.com> Sat, 10 Nov 2012 16:51:12 +0100 |
3148 | + |
3149 | poppler (0.20.4-0ubuntu1) quantal; urgency=low |
3150 | |
3151 | * New upstream bugfix release |
3152 | |
3153 | === added file 'debian/patches/git_gouraud_shading_support.patch' |
3154 | --- debian/patches/git_gouraud_shading_support.patch 1970-01-01 00:00:00 +0000 |
3155 | +++ debian/patches/git_gouraud_shading_support.patch 2012-11-13 19:26:27 +0000 |
3156 | @@ -0,0 +1,38 @@ |
3157 | +From ae8fc0cbfc6123189e17b3cf1286e0540f181646 Mon Sep 17 00:00:00 2001 |
3158 | +From: Adrian Johnson <ajohnson@redneon.com> |
3159 | +Date: Tue, 30 Oct 2012 10:52:04 +0000 |
3160 | +Subject: cairo: support parameterized Gouraud shading |
3161 | +Bug: https://bugs.freedesktop.org/show_bug.cgi?id=56463 |
3162 | +Bug-Ubuntu: https://bugs.launchpad.net/poppler/+bug/1072129 |
3163 | + |
3164 | +--- |
3165 | +Index: poppler/poppler/CairoOutputDev.cc |
3166 | +=================================================================== |
3167 | +--- poppler.orig/poppler/CairoOutputDev.cc 2012-11-10 16:49:31.178020000 +0100 |
3168 | ++++ poppler/poppler/CairoOutputDev.cc 2012-11-10 16:50:44.513164938 +0100 |
3169 | +@@ -945,10 +945,21 @@ |
3170 | + fill_pattern = cairo_pattern_create_mesh (); |
3171 | + |
3172 | + for (i = 0; i < shading->getNTriangles(); i++) { |
3173 | +- shading->getTriangle(i, |
3174 | +- &x0, &y0, &color[0], |
3175 | +- &x1, &y1, &color[1], |
3176 | +- &x2, &y2, &color[2]); |
3177 | ++ if (shading->isParameterized()) { |
3178 | ++ double color0, color1, color2; |
3179 | ++ shading->getTriangle(i, &x0, &y0, &color0, |
3180 | ++ &x1, &y1, &color1, |
3181 | ++ &x2, &y2, &color2); |
3182 | ++ shading->getParameterizedColor(color0, &color[0]); |
3183 | ++ shading->getParameterizedColor(color1, &color[1]); |
3184 | ++ shading->getParameterizedColor(color2, &color[2]); |
3185 | ++ } else { |
3186 | ++ shading->getTriangle(i, |
3187 | ++ &x0, &y0, &color[0], |
3188 | ++ &x1, &y1, &color[1], |
3189 | ++ &x2, &y2, &color[2]); |
3190 | ++ |
3191 | ++ } |
3192 | + |
3193 | + cairo_mesh_pattern_begin_patch (fill_pattern); |
3194 | + |
3195 | |
3196 | === modified file 'debian/patches/series' |
3197 | --- debian/patches/series 2012-08-14 13:11:05 +0000 |
3198 | +++ debian/patches/series 2012-11-13 19:26:27 +0000 |
3199 | @@ -1,2 +1,3 @@ |
3200 | ltmain-as-needed.diff |
3201 | qt4-visibility.diff |
3202 | +git_gouraud_shading_support.patch |
3203 | |
3204 | === modified file 'poppler/CairoOutputDev.cc' |
3205 | --- poppler/CairoOutputDev.cc 2012-06-22 14:50:04 +0000 |
3206 | +++ poppler/CairoOutputDev.cc 2012-11-13 19:26:27 +0000 |
3207 | @@ -945,10 +945,21 @@ |
3208 | fill_pattern = cairo_pattern_create_mesh (); |
3209 | |
3210 | for (i = 0; i < shading->getNTriangles(); i++) { |
3211 | - shading->getTriangle(i, |
3212 | - &x0, &y0, &color[0], |
3213 | - &x1, &y1, &color[1], |
3214 | - &x2, &y2, &color[2]); |
3215 | + if (shading->isParameterized()) { |
3216 | + double color0, color1, color2; |
3217 | + shading->getTriangle(i, &x0, &y0, &color0, |
3218 | + &x1, &y1, &color1, |
3219 | + &x2, &y2, &color2); |
3220 | + shading->getParameterizedColor(color0, &color[0]); |
3221 | + shading->getParameterizedColor(color1, &color[1]); |
3222 | + shading->getParameterizedColor(color2, &color[2]); |
3223 | + } else { |
3224 | + shading->getTriangle(i, |
3225 | + &x0, &y0, &color[0], |
3226 | + &x1, &y1, &color[1], |
3227 | + &x2, &y2, &color[2]); |
3228 | + |
3229 | + } |
3230 | |
3231 | cairo_mesh_pattern_begin_patch (fill_pattern); |
3232 |