cf3c7243299f20fcb814acf9b089cec25058f92d
1 #define __SP_CAIRO_RENDER_CONTEXT_C__
3 /** \file
4 * Rendering with Cairo.
5 */
6 /*
7 * Author:
8 * Miklos Erdelyi <erdelyim@gmail.com>
9 *
10 * Copyright (C) 2006 Miklos Erdelyi
11 *
12 * Licensed under GNU GPL
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #ifndef PANGO_ENABLE_BACKEND
20 #define PANGO_ENABLE_BACKEND
21 #endif
23 #ifndef PANGO_ENABLE_ENGINE
24 #define PANGO_ENABLE_ENGINE
25 #endif
28 #include <signal.h>
29 #include <errno.h>
30 #include <2geom/pathvector.h>
32 #include <glib/gmem.h>
34 #include <glibmm/i18n.h>
35 #include "display/nr-arena.h"
36 #include "display/nr-arena-item.h"
37 #include "display/nr-arena-group.h"
38 #include "display/curve.h"
39 #include "display/canvas-bpath.h"
40 #include "display/inkscape-cairo.h"
41 #include "sp-item.h"
42 #include "sp-item-group.h"
43 #include "style.h"
44 #include "sp-linear-gradient.h"
45 #include "sp-radial-gradient.h"
46 #include "sp-pattern.h"
47 #include "sp-mask.h"
48 #include "sp-clippath.h"
49 #ifdef WIN32
50 #include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
51 #endif
53 #include <unit-constants.h>
55 #include "cairo-render-context.h"
56 #include "cairo-renderer.h"
57 #include "extension/system.h"
59 #include "io/sys.h"
61 #include <cairo.h>
63 // include support for only the compiled-in surface types
64 #ifdef CAIRO_HAS_PDF_SURFACE
65 #include <cairo-pdf.h>
66 #endif
67 #ifdef CAIRO_HAS_PS_SURFACE
68 #include <cairo-ps.h>
69 #endif
72 #ifdef CAIRO_HAS_FT_FONT
73 #include <cairo-ft.h>
74 #endif
75 #ifdef CAIRO_HAS_WIN32_FONT
76 #include <cairo-win32.h>
77 #include <pango/pangowin32.h>
78 #endif
80 #include <pango/pangofc-fontmap.h>
82 //#define TRACE(_args) g_printf _args
83 //#define TRACE(_args) g_message _args
84 #define TRACE(_args)
85 //#define TEST(_args) _args
86 #define TEST(_args)
88 // FIXME: expose these from sp-clippath/mask.cpp
89 struct SPClipPathView {
90 SPClipPathView *next;
91 unsigned int key;
92 NRArenaItem *arenaitem;
93 NRRect bbox;
94 };
96 struct SPMaskView {
97 SPMaskView *next;
98 unsigned int key;
99 NRArenaItem *arenaitem;
100 NRRect bbox;
101 };
103 namespace Inkscape {
104 namespace Extension {
105 namespace Internal {
107 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
109 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
110 _dpi(72),
111 _pdf_level(0),
112 _ps_level(1),
113 _eps(false),
114 _is_texttopath(FALSE),
115 _is_filtertobitmap(FALSE),
116 _bitmapresolution(72),
117 _stream(NULL),
118 _is_valid(FALSE),
119 _vector_based_target(FALSE),
120 _cr(NULL), // Cairo context
121 _surface(NULL),
122 _target(CAIRO_SURFACE_TYPE_IMAGE),
123 _target_format(CAIRO_FORMAT_ARGB32),
124 _layout(NULL),
125 _state(NULL),
126 _renderer(parent),
127 _render_mode(RENDER_MODE_NORMAL),
128 _clip_mode(CLIP_MODE_MASK)
129 {
130 font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
131 }
133 CairoRenderContext::~CairoRenderContext(void)
134 {
135 if(font_table != NULL) {
136 g_hash_table_remove_all(font_table);
137 }
139 if (_cr) cairo_destroy(_cr);
140 if (_surface) cairo_surface_destroy(_surface);
141 if (_layout) g_object_unref(_layout);
142 }
143 void CairoRenderContext::font_data_free(gpointer data)
144 {
145 cairo_font_face_t *font_face = (cairo_font_face_t *)data;
146 if (font_face) {
147 cairo_font_face_destroy(font_face);
148 }
149 }
151 CairoRenderer*
152 CairoRenderContext::getRenderer(void) const
153 {
154 return _renderer;
155 }
157 CairoRenderState*
158 CairoRenderContext::getCurrentState(void) const
159 {
160 return _state;
161 }
163 CairoRenderState*
164 CairoRenderContext::getParentState(void) const
165 {
166 // if this is the root node just return it
167 if (g_slist_length(_state_stack) == 1) {
168 return _state;
169 } else {
170 return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
171 }
172 }
174 void
175 CairoRenderContext::setStateForStyle(SPStyle const *style)
176 {
177 // only opacity & overflow is stored for now
178 _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
179 _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
180 _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
182 if (style->fill.isPaintserver() || style->stroke.isPaintserver())
183 _state->merge_opacity = FALSE;
185 // disable rendering of opacity if there's a stroke on the fill
186 if (_state->merge_opacity
187 && !style->fill.isNone()
188 && !style->stroke.isNone())
189 _state->merge_opacity = FALSE;
190 }
192 /**
193 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
194 *
195 * \param width width of the surface to be created
196 * \param height height of the surface to be created
197 */
198 CairoRenderContext*
199 CairoRenderContext::cloneMe(double width, double height) const
200 {
201 g_assert( _is_valid );
202 g_assert( width > 0.0 && height > 0.0 );
204 CairoRenderContext *new_context = _renderer->createContext();
205 cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
206 (int)ceil(width), (int)ceil(height));
207 new_context->_cr = cairo_create(surface);
208 new_context->_surface = surface;
209 new_context->_width = width;
210 new_context->_height = height;
211 new_context->_is_valid = TRUE;
213 return new_context;
214 }
216 CairoRenderContext*
217 CairoRenderContext::cloneMe(void) const
218 {
219 g_assert( _is_valid );
221 return cloneMe(_width, _height);
222 }
224 bool
225 CairoRenderContext::setImageTarget(cairo_format_t format)
226 {
227 // format cannot be set on an already initialized surface
228 if (_is_valid)
229 return false;
231 switch (format) {
232 case CAIRO_FORMAT_ARGB32:
233 case CAIRO_FORMAT_RGB24:
234 case CAIRO_FORMAT_A8:
235 case CAIRO_FORMAT_A1:
236 _target_format = format;
237 _target = CAIRO_SURFACE_TYPE_IMAGE;
238 return true;
239 break;
240 default:
241 break;
242 }
244 return false;
245 }
247 bool
248 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
249 {
250 #ifndef CAIRO_HAS_PDF_SURFACE
251 return false;
252 #else
253 _target = CAIRO_SURFACE_TYPE_PDF;
254 _vector_based_target = TRUE;
255 #endif
257 FILE *osf = NULL;
258 FILE *osp = NULL;
260 gsize bytesRead = 0;
261 gsize bytesWritten = 0;
262 GError *error = NULL;
263 gchar *local_fn = g_filename_from_utf8(utf8_fn,
264 -1, &bytesRead, &bytesWritten, &error);
265 gchar const *fn = local_fn;
267 /* TODO: Replace the below fprintf's with something that does the right thing whether in
268 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
269 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
270 * return code.
271 */
272 if (fn != NULL) {
273 if (*fn == '|') {
274 fn += 1;
275 while (isspace(*fn)) fn += 1;
276 #ifndef WIN32
277 osp = popen(fn, "w");
278 #else
279 osp = _popen(fn, "w");
280 #endif
281 if (!osp) {
282 fprintf(stderr, "inkscape: popen(%s): %s\n",
283 fn, strerror(errno));
284 return false;
285 }
286 _stream = osp;
287 } else if (*fn == '>') {
288 fn += 1;
289 while (isspace(*fn)) fn += 1;
290 Inkscape::IO::dump_fopen_call(fn, "K");
291 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
292 if (!osf) {
293 fprintf(stderr, "inkscape: fopen(%s): %s\n",
294 fn, strerror(errno));
295 return false;
296 }
297 _stream = osf;
298 } else {
299 /* put cwd stuff in here */
300 gchar *qn = ( *fn
301 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
302 : g_strdup("lpr") );
303 #ifndef WIN32
304 osp = popen(qn, "w");
305 #else
306 osp = _popen(qn, "w");
307 #endif
308 if (!osp) {
309 fprintf(stderr, "inkscape: popen(%s): %s\n",
310 qn, strerror(errno));
311 return false;
312 }
313 g_free(qn);
314 _stream = osp;
315 }
316 }
318 g_free(local_fn);
320 if (_stream) {
321 /* fixme: this is kinda icky */
322 #if !defined(_WIN32) && !defined(__WIN32__)
323 (void) signal(SIGPIPE, SIG_IGN);
324 #endif
325 }
327 return true;
328 }
330 bool
331 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
332 {
333 #ifndef CAIRO_HAS_PS_SURFACE
334 return false;
335 #else
336 _target = CAIRO_SURFACE_TYPE_PS;
337 _vector_based_target = TRUE;
338 #endif
340 FILE *osf = NULL;
341 FILE *osp = NULL;
343 gsize bytesRead = 0;
344 gsize bytesWritten = 0;
345 GError *error = NULL;
346 gchar *local_fn = g_filename_from_utf8(utf8_fn,
347 -1, &bytesRead, &bytesWritten, &error);
348 gchar const *fn = local_fn;
350 /* TODO: Replace the below fprintf's with something that does the right thing whether in
351 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
352 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
353 * return code.
354 */
355 if (fn != NULL) {
356 if (*fn == '|') {
357 fn += 1;
358 while (isspace(*fn)) fn += 1;
359 #ifndef WIN32
360 osp = popen(fn, "w");
361 #else
362 osp = _popen(fn, "w");
363 #endif
364 if (!osp) {
365 fprintf(stderr, "inkscape: popen(%s): %s\n",
366 fn, strerror(errno));
367 return false;
368 }
369 _stream = osp;
370 } else if (*fn == '>') {
371 fn += 1;
372 while (isspace(*fn)) fn += 1;
373 Inkscape::IO::dump_fopen_call(fn, "K");
374 osf = Inkscape::IO::fopen_utf8name(fn, "w+");
375 if (!osf) {
376 fprintf(stderr, "inkscape: fopen(%s): %s\n",
377 fn, strerror(errno));
378 return false;
379 }
380 _stream = osf;
381 } else {
382 /* put cwd stuff in here */
383 gchar *qn = ( *fn
384 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
385 : g_strdup("lpr") );
386 #ifndef WIN32
387 osp = popen(qn, "w");
388 #else
389 osp = _popen(qn, "w");
390 #endif
391 if (!osp) {
392 fprintf(stderr, "inkscape: popen(%s): %s\n",
393 qn, strerror(errno));
394 return false;
395 }
396 g_free(qn);
397 _stream = osp;
398 }
399 }
401 g_free(local_fn);
403 if (_stream) {
404 /* fixme: this is kinda icky */
405 #if !defined(_WIN32) && !defined(__WIN32__)
406 (void) signal(SIGPIPE, SIG_IGN);
407 #endif
408 }
410 return true;
411 }
413 void CairoRenderContext::setPSLevel(unsigned int level)
414 {
415 _ps_level = level;
416 }
418 void CairoRenderContext::setEPS(bool eps)
419 {
420 _eps = eps;
421 }
423 unsigned int CairoRenderContext::getPSLevel(void)
424 {
425 return _ps_level;
426 }
428 void CairoRenderContext::setPDFLevel(unsigned int level)
429 {
430 _pdf_level = level;
431 }
433 void CairoRenderContext::setTextToPath(bool texttopath)
434 {
435 _is_texttopath = texttopath;
436 }
438 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
439 {
440 _is_filtertobitmap = filtertobitmap;
441 }
443 bool CairoRenderContext::getFilterToBitmap(void)
444 {
445 return _is_filtertobitmap;
446 }
448 void CairoRenderContext::setBitmapResolution(int resolution)
449 {
450 _bitmapresolution = resolution;
451 }
453 int CairoRenderContext::getBitmapResolution(void)
454 {
455 return _bitmapresolution;
456 }
458 cairo_surface_t*
459 CairoRenderContext::getSurface(void)
460 {
461 g_assert( _is_valid );
463 return _surface;
464 }
466 bool
467 CairoRenderContext::saveAsPng(const char *file_name)
468 {
469 cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
470 if (status)
471 return false;
472 else
473 return true;
474 }
476 void
477 CairoRenderContext::setRenderMode(CairoRenderMode mode)
478 {
479 switch (mode) {
480 case RENDER_MODE_NORMAL:
481 case RENDER_MODE_CLIP:
482 _render_mode = mode;
483 break;
484 default:
485 _render_mode = RENDER_MODE_NORMAL;
486 break;
487 }
488 }
490 CairoRenderContext::CairoRenderMode
491 CairoRenderContext::getRenderMode(void) const
492 {
493 return _render_mode;
494 }
496 void
497 CairoRenderContext::setClipMode(CairoClipMode mode)
498 {
499 switch (mode) {
500 case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
501 case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
502 _clip_mode = mode;
503 break;
504 default:
505 _clip_mode = CLIP_MODE_PATH;
506 break;
507 }
508 }
510 CairoRenderContext::CairoClipMode
511 CairoRenderContext::getClipMode(void) const
512 {
513 return _clip_mode;
514 }
516 CairoRenderState*
517 CairoRenderContext::_createState(void)
518 {
519 CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
520 g_assert( state != NULL );
522 state->has_filtereffect = FALSE;
523 state->merge_opacity = TRUE;
524 state->opacity = 1.0;
525 state->need_layer = FALSE;
526 state->has_overflow = FALSE;
527 state->parent_has_userspace = FALSE;
528 state->clip_path = NULL;
529 state->mask = NULL;
531 return state;
532 }
534 void
535 CairoRenderContext::pushLayer(void)
536 {
537 g_assert( _is_valid );
539 TRACE(("--pushLayer\n"));
540 cairo_push_group(_cr);
542 // clear buffer
543 if (!_vector_based_target) {
544 cairo_save(_cr);
545 cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
546 cairo_paint(_cr);
547 cairo_restore(_cr);
548 }
549 }
551 void
552 CairoRenderContext::popLayer(void)
553 {
554 g_assert( _is_valid );
556 float opacity = _state->opacity;
557 TRACE(("--popLayer w/ opacity %f\n", opacity));
559 /*
560 At this point, the Cairo source is ready. A Cairo mask must be created if required.
561 Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
562 masks independently of the objects they effect while in SVG the clip paths and masks
563 are defined relative to the objects they are attached to.
564 Notes:
565 1. An SVG object may have both a clip path and a mask!
566 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
567 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
568 the page (document). This is also not handled properly.
569 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
570 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
571 alpha. This is handled here by doing a pixel by pixel conversion.
572 */
574 SPClipPath *clip_path = _state->clip_path;
575 SPMask *mask = _state->mask;
576 if (clip_path || mask) {
578 CairoRenderContext *clip_ctx = 0;
579 cairo_surface_t *clip_mask = 0;
581 // Apply any clip path first
582 if (clip_path) {
583 TRACE((" Applying clip\n"));
584 if (_render_mode == RENDER_MODE_CLIP)
585 mask = NULL; // disable mask when performing nested clipping
587 if (_vector_based_target) {
588 setClipMode(CLIP_MODE_PATH); // Vector
589 if (!mask) {
590 cairo_pop_group_to_source(_cr);
591 _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
592 if (opacity == 1.0)
593 cairo_paint(_cr);
594 else
595 cairo_paint_with_alpha(_cr, opacity);
597 } else {
598 // the clipPath will be applied before masking
599 }
600 } else {
602 // setup a new rendering context
603 clip_ctx = _renderer->createContext();
604 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
605 clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
606 // This code ties the clipping to the document coordinates. It doesn't allow
607 // for a clipped object intially drawn off the page and then translated onto
608 // the page.
609 if (!clip_ctx->setupSurface(_width, _height)) {
610 TRACE(("clip: setupSurface failed\n"));
611 _renderer->destroyContext(clip_ctx);
612 return;
613 }
615 // clear buffer
616 cairo_save(clip_ctx->_cr);
617 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
618 cairo_paint(clip_ctx->_cr);
619 cairo_restore(clip_ctx->_cr);
621 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
622 if (!mask)
623 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
624 else
625 cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
627 // copy over the correct CTM
628 // It must be stored in item_transform of current state after pushState.
629 Geom::Matrix item_transform;
630 if (_state->parent_has_userspace)
631 item_transform = getParentState()->transform * _state->item_transform;
632 else
633 item_transform = _state->item_transform;
635 // apply the clip path
636 clip_ctx->pushState();
637 clip_ctx->getCurrentState()->item_transform = item_transform;
638 _renderer->applyClipPath(clip_ctx, clip_path);
639 clip_ctx->popState();
641 clip_mask = clip_ctx->getSurface();
642 TEST(clip_ctx->saveAsPng("clip_mask.png"));
644 if (!mask) {
645 cairo_pop_group_to_source(_cr);
646 cairo_mask_surface(_cr, clip_mask, 0, 0);
647 _renderer->destroyContext(clip_ctx);
648 }
649 }
650 }
652 // Apply any mask second
653 if (mask) {
654 TRACE((" Applying mask\n"));
655 // create rendering context for mask
656 CairoRenderContext *mask_ctx = _renderer->createContext();
658 // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
659 // Cairo surface is expecting the mask to be 90 dpi.
660 float surface_width = _width;
661 float surface_height = _height;
662 if( _vector_based_target ) {
663 surface_width *= 1.25;
664 surface_height *= 1.25;
665 }
666 if (!mask_ctx->setupSurface( surface_width, surface_height )) {
667 TRACE(("mask: setupSurface failed\n"));
668 _renderer->destroyContext(mask_ctx);
669 return;
670 }
671 TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
673 // set rendering mode to normal
674 setRenderMode(RENDER_MODE_NORMAL);
676 // copy the correct CTM to mask context
677 /*
678 if (_state->parent_has_userspace)
679 mask_ctx->setTransform(&getParentState()->transform);
680 else
681 mask_ctx->setTransform(&_state->transform);
682 */
683 // This is probably not correct... but it seems to do the trick.
684 mask_ctx->setTransform(&_state->item_transform);
686 // render mask contents to mask_ctx
687 _renderer->applyMask(mask_ctx, mask);
689 TEST(mask_ctx->saveAsPng("mask.png"));
691 // composite with clip mask
692 if (clip_path && _clip_mode == CLIP_MODE_MASK) {
693 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
694 _renderer->destroyContext(clip_ctx);
695 }
697 cairo_surface_t *mask_image = mask_ctx->getSurface();
698 int width = cairo_image_surface_get_width(mask_image);
699 int height = cairo_image_surface_get_height(mask_image);
700 int stride = cairo_image_surface_get_stride(mask_image);
701 unsigned char *pixels = cairo_image_surface_get_data(mask_image);
703 // premultiply with opacity
704 // In SVG, the rgb channels as well as the alpha channel is used in masking.
705 // In Cairo, only the alpha channel is used thus requiring this conversion.
706 TRACE(("premul w/ %f\n", opacity));
707 guint8 int_opacity = (guint8)(255 * opacity);
708 for (int row = 0 ; row < height; row++) {
709 unsigned char *row_data = pixels + (row * stride);
710 for (int i = 0 ; i < width; i++) {
711 guint32 *pixel = (guint32 *)row_data + i;
712 *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
713 ((*pixel & 0x0000ff00) >> 8) * 46518 +
714 ((*pixel & 0x000000ff) ) * 4688) *
715 int_opacity);
716 }
717 }
719 cairo_pop_group_to_source(_cr);
720 if (_clip_mode == CLIP_MODE_PATH) {
721 // we have to do the clipping after cairo_pop_group_to_source
722 _renderer->applyClipPath(this, clip_path);
723 }
724 // apply the mask onto the layer
725 cairo_mask_surface(_cr, mask_image, 0, 0);
726 _renderer->destroyContext(mask_ctx);
727 }
728 } else {
729 // No clip path or mask
730 cairo_pop_group_to_source(_cr);
731 if (opacity == 1.0)
732 cairo_paint(_cr);
733 else
734 cairo_paint_with_alpha(_cr, opacity);
735 }
736 }
738 void
739 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
740 {
741 g_assert( _is_valid );
743 // here it should be checked whether the current clip winding changed
744 // so we could switch back to masked clipping
745 if (fill_rule->value == SP_WIND_RULE_EVENODD) {
746 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
747 } else {
748 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
749 }
750 addPathVector(pv);
751 }
753 void
754 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
755 {
756 g_assert( _is_valid );
758 cairo_rectangle(_cr, x, y, width, height);
759 cairo_clip(_cr);
760 }
762 bool
763 CairoRenderContext::setupSurface(double width, double height)
764 {
765 // Is the surface already set up?
766 if (_is_valid)
767 return true;
769 if (_vector_based_target && _stream == NULL)
770 return false;
772 _width = width;
773 _height = height;
775 cairo_surface_t *surface = NULL;
776 cairo_matrix_t ctm;
777 cairo_matrix_init_identity (&ctm);
778 switch (_target) {
779 case CAIRO_SURFACE_TYPE_IMAGE:
780 surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
781 break;
782 #ifdef CAIRO_HAS_PDF_SURFACE
783 case CAIRO_SURFACE_TYPE_PDF:
784 surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
785 break;
786 #endif
787 #ifdef CAIRO_HAS_PS_SURFACE
788 case CAIRO_SURFACE_TYPE_PS:
789 surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
790 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
791 return FALSE;
792 }
793 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
794 cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
795 cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
796 #endif
797 break;
798 #endif
799 default:
800 return false;
801 break;
802 }
804 return _finishSurfaceSetup (surface, &ctm);
805 }
807 bool
808 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
809 {
810 if (_is_valid || !surface)
811 return false;
813 _vector_based_target = is_vector;
814 bool ret = _finishSurfaceSetup (surface, ctm);
815 if (ret)
816 cairo_surface_reference (surface);
817 return ret;
818 }
820 bool
821 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
822 {
823 if(surface == NULL) {
824 return false;
825 }
826 if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
827 return false;
828 }
830 _cr = cairo_create(surface);
831 if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
832 return false;
833 }
834 if (ctm)
835 cairo_set_matrix(_cr, ctm);
836 _surface = surface;
838 if (_vector_based_target) {
839 cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
840 } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
841 // set background color on non-alpha surfaces
842 // TODO: bgcolor should be derived from SPDocument
843 cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
844 cairo_rectangle(_cr, 0, 0, _width, _height);
845 cairo_fill(_cr);
846 }
848 _is_valid = TRUE;
850 return true;
851 }
853 bool
854 CairoRenderContext::finish(void)
855 {
856 g_assert( _is_valid );
858 if (_vector_based_target)
859 cairo_show_page(_cr);
861 cairo_destroy(_cr);
862 cairo_surface_finish(_surface);
863 cairo_status_t status = cairo_surface_status(_surface);
864 cairo_surface_destroy(_surface);
865 _cr = NULL;
866 _surface = NULL;
868 if (_layout)
869 g_object_unref(_layout);
871 _is_valid = FALSE;
873 if (_vector_based_target && _stream) {
874 /* Flush stream to be sure. */
875 (void) fflush(_stream);
877 fclose(_stream);
878 _stream = NULL;
879 }
881 if (status == CAIRO_STATUS_SUCCESS)
882 return true;
883 else
884 return false;
885 }
887 void
888 CairoRenderContext::transform(Geom::Matrix const *transform)
889 {
890 g_assert( _is_valid );
892 cairo_matrix_t matrix;
893 _initCairoMatrix(&matrix, transform);
894 cairo_transform(_cr, &matrix);
896 // store new CTM
897 getTransform(&_state->transform);
898 }
900 void
901 CairoRenderContext::setTransform(Geom::Matrix const *transform)
902 {
903 g_assert( _is_valid );
905 cairo_matrix_t matrix;
906 _initCairoMatrix(&matrix, transform);
907 cairo_set_matrix(_cr, &matrix);
908 _state->transform = *transform;
909 }
911 void
912 CairoRenderContext::getTransform(Geom::Matrix *copy) const
913 {
914 g_assert( _is_valid );
916 cairo_matrix_t ctm;
917 cairo_get_matrix(_cr, &ctm);
918 (*copy)[0] = ctm.xx;
919 (*copy)[1] = ctm.yx;
920 (*copy)[2] = ctm.xy;
921 (*copy)[3] = ctm.yy;
922 (*copy)[4] = ctm.x0;
923 (*copy)[5] = ctm.y0;
924 }
926 void
927 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
928 {
929 g_assert( _is_valid );
931 CairoRenderState *parent_state = getParentState();
932 memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
933 }
935 void
936 CairoRenderContext::pushState(void)
937 {
938 g_assert( _is_valid );
940 cairo_save(_cr);
942 CairoRenderState *new_state = _createState();
943 // copy current state's transform
944 new_state->transform = _state->transform;
945 _state_stack = g_slist_prepend(_state_stack, new_state);
946 _state = new_state;
947 }
949 void
950 CairoRenderContext::popState(void)
951 {
952 g_assert( _is_valid );
954 cairo_restore(_cr);
956 g_free(_state_stack->data);
957 _state_stack = g_slist_remove_link(_state_stack, _state_stack);
958 _state = (CairoRenderState*)_state_stack->data;
960 g_assert( g_slist_length(_state_stack) > 0 );
961 }
963 static bool pattern_hasItemChildren (SPPattern *pat)
964 {
965 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
966 if (SP_IS_ITEM (child)) {
967 return true;
968 }
969 }
970 return false;
971 }
973 cairo_pattern_t*
974 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
975 {
976 g_assert( SP_IS_PATTERN(paintserver) );
978 SPPattern *pat = SP_PATTERN (paintserver);
980 Geom::Matrix ps2user, pcs2dev;
981 ps2user = Geom::identity();
982 pcs2dev = Geom::identity();
984 double x = pattern_x(pat);
985 double y = pattern_y(pat);
986 double width = pattern_width(pat);
987 double height = pattern_height(pat);
988 double bbox_width_scaler;
989 double bbox_height_scaler;
991 TRACE(("%f x %f pattern\n", width, height));
993 if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
994 //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
995 bbox_width_scaler = pbox->x1 - pbox->x0;
996 bbox_height_scaler = pbox->y1 - pbox->y0;
997 ps2user[4] = x * bbox_width_scaler + pbox->x0;
998 ps2user[5] = y * bbox_height_scaler + pbox->y0;
999 } else {
1000 bbox_width_scaler = 1.0;
1001 bbox_height_scaler = 1.0;
1002 ps2user[4] = x;
1003 ps2user[5] = y;
1004 }
1006 // apply pattern transformation
1007 Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1008 ps2user *= pattern_transform;
1009 Geom::Point ori (ps2user[4], ps2user[5]);
1011 // create pattern contents coordinate system
1012 if (pat->viewBox_set) {
1013 NRRect *view_box = pattern_viewBox(pat);
1015 double x, y, w, h;
1016 double view_width, view_height;
1017 x = 0;
1018 y = 0;
1019 w = width * bbox_width_scaler;
1020 h = height * bbox_height_scaler;
1022 view_width = view_box->x1 - view_box->x0;
1023 view_height = view_box->y1 - view_box->y0;
1025 //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1026 pcs2dev[0] = w / view_width;
1027 pcs2dev[3] = h / view_height;
1028 pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1029 pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1030 } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1031 pcs2dev[0] = pbox->x1 - pbox->x0;
1032 pcs2dev[3] = pbox->y1 - pbox->y0;
1034 }
1036 // Calculate the size of the surface which has to be created
1037 #define SUBPIX_SCALE 100
1038 // Cairo requires an integer pattern surface width/height.
1039 // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1040 // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1041 double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1042 double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1043 TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1044 // create new rendering context
1045 CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1047 // adjust the size of the painted pattern to fit exactly the created surface
1048 // this has to be done because of the rounding to obtain an integer pattern surface width/height
1049 double scale_width = surface_width / (bbox_width_scaler * width);
1050 double scale_height = surface_height / (bbox_height_scaler * height);
1051 if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1052 TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1053 pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1054 ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1055 }
1057 // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1058 ps2user[4] = ori[Geom::X];
1059 ps2user[5] = ori[Geom::Y];
1061 pattern_ctx->setTransform(&pcs2dev);
1062 pattern_ctx->pushState();
1064 // create arena and group
1065 NRArena *arena = NRArena::create();
1066 unsigned dkey = sp_item_display_key_new(1);
1068 // show items and render them
1069 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1070 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1071 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1072 if (SP_IS_ITEM (child)) {
1073 sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1074 _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1075 }
1076 }
1077 break; // do not go further up the chain if children are found
1078 }
1079 }
1081 pattern_ctx->popState();
1083 // setup a cairo_pattern_t
1084 cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1085 TEST(pattern_ctx->saveAsPng("pattern.png"));
1086 cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1087 cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1089 // set pattern transformation
1090 cairo_matrix_t pattern_matrix;
1091 _initCairoMatrix(&pattern_matrix, &ps2user);
1092 cairo_matrix_invert(&pattern_matrix);
1093 cairo_pattern_set_matrix(result, &pattern_matrix);
1095 delete pattern_ctx;
1097 // hide all items
1098 for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1099 if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1100 for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1101 if (SP_IS_ITEM (child)) {
1102 sp_item_invoke_hide (SP_ITEM (child), dkey);
1103 }
1104 }
1105 break; // do not go further up the chain if children are found
1106 }
1107 }
1109 return result;
1110 }
1112 cairo_pattern_t*
1113 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1114 NRRect const *pbox, float alpha)
1115 {
1116 cairo_pattern_t *pattern = NULL;
1117 bool apply_bbox2user = FALSE;
1119 if (SP_IS_LINEARGRADIENT (paintserver)) {
1121 SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1123 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1125 Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1126 Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1127 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1128 // convert to userspace
1129 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1130 p1 *= bbox2user;
1131 p2 *= bbox2user;
1132 }
1134 // create linear gradient pattern
1135 pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1137 // add stops
1138 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1139 float rgb[3];
1140 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1141 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1142 }
1143 } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1145 SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1147 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1149 Geom::Point c (rg->cx.computed, rg->cy.computed);
1150 Geom::Point f (rg->fx.computed, rg->fy.computed);
1151 double r = rg->r.computed;
1152 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1153 apply_bbox2user = true;
1155 // create radial gradient pattern
1156 pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1158 // add stops
1159 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1160 float rgb[3];
1161 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1162 cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1163 }
1164 } else if (SP_IS_PATTERN (paintserver)) {
1166 pattern = _createPatternPainter(paintserver, pbox);
1167 } else {
1168 return NULL;
1169 }
1171 if (pattern && SP_IS_GRADIENT (paintserver)) {
1172 SPGradient *g = SP_GRADIENT(paintserver);
1174 // set extend type
1175 SPGradientSpread spread = sp_gradient_get_spread(g);
1176 switch (spread) {
1177 case SP_GRADIENT_SPREAD_REPEAT: {
1178 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1179 break;
1180 }
1181 case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1182 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1183 break;
1184 }
1185 case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1186 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1187 break;
1188 }
1189 default: {
1190 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1191 break;
1192 }
1193 }
1195 cairo_matrix_t pattern_matrix;
1196 if (g->gradientTransform_set) {
1197 // apply gradient transformation
1198 cairo_matrix_init(&pattern_matrix,
1199 g->gradientTransform[0], g->gradientTransform[1],
1200 g->gradientTransform[2], g->gradientTransform[3],
1201 g->gradientTransform[4], g->gradientTransform[5]);
1202 } else {
1203 cairo_matrix_init_identity (&pattern_matrix);
1204 }
1206 if (apply_bbox2user) {
1207 // convert to userspace
1208 cairo_matrix_t bbox2user;
1209 cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1210 cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1211 }
1212 cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1213 cairo_pattern_set_matrix(pattern, &pattern_matrix);
1214 }
1216 return pattern;
1217 }
1219 void
1220 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1221 {
1222 g_return_if_fail( !style->fill.set
1223 || style->fill.isColor()
1224 || style->fill.isPaintserver() );
1226 float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1227 if (_state->merge_opacity) {
1228 alpha *= _state->opacity;
1229 TRACE(("merged op=%f\n", alpha));
1230 }
1232 if (style->fill.isColor()) {
1233 float rgb[3];
1234 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1236 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1238 } else if (!style->fill.set) { // unset fill is black
1239 cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1241 } else {
1242 g_assert( style->fill.isPaintserver()
1243 || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1244 || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1246 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1248 if (pattern) {
1249 cairo_set_source(_cr, pattern);
1250 cairo_pattern_destroy(pattern);
1251 }
1252 }
1253 }
1255 void
1256 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1257 {
1258 float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1259 if (_state->merge_opacity)
1260 alpha *= _state->opacity;
1262 if (style->stroke.isColor()) {
1263 float rgb[3];
1264 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1266 cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1267 } else {
1268 g_assert( style->fill.isPaintserver()
1269 || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1270 || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1272 cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1274 if (pattern) {
1275 cairo_set_source(_cr, pattern);
1276 cairo_pattern_destroy(pattern);
1277 }
1278 }
1280 if (style->stroke_dash.n_dash &&
1281 style->stroke_dash.dash )
1282 {
1283 cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1284 } else {
1285 cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1286 }
1288 cairo_set_line_width(_cr, style->stroke_width.computed);
1290 // set line join type
1291 cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1292 switch (style->stroke_linejoin.computed) {
1293 case SP_STROKE_LINEJOIN_MITER:
1294 join = CAIRO_LINE_JOIN_MITER;
1295 break;
1296 case SP_STROKE_LINEJOIN_ROUND:
1297 join = CAIRO_LINE_JOIN_ROUND;
1298 break;
1299 case SP_STROKE_LINEJOIN_BEVEL:
1300 join = CAIRO_LINE_JOIN_BEVEL;
1301 break;
1302 }
1303 cairo_set_line_join(_cr, join);
1305 // set line cap type
1306 cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1307 switch (style->stroke_linecap.computed) {
1308 case SP_STROKE_LINECAP_BUTT:
1309 cap = CAIRO_LINE_CAP_BUTT;
1310 break;
1311 case SP_STROKE_LINECAP_ROUND:
1312 cap = CAIRO_LINE_CAP_ROUND;
1313 break;
1314 case SP_STROKE_LINECAP_SQUARE:
1315 cap = CAIRO_LINE_CAP_SQUARE;
1316 break;
1317 }
1318 cairo_set_line_cap(_cr, cap);
1319 cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1320 }
1322 bool
1323 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1324 {
1325 g_assert( _is_valid );
1327 if (_render_mode == RENDER_MODE_CLIP) {
1328 if (_clip_mode == CLIP_MODE_PATH) {
1329 addClipPath(pathv, &style->fill_rule);
1330 } else {
1331 setPathVector(pathv);
1332 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1333 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1334 } else {
1335 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1336 }
1337 cairo_fill(_cr);
1338 TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1339 }
1340 return true;
1341 }
1343 bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1344 bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1345 style->stroke_opacity.value == 0;
1347 if (no_fill && no_stroke)
1348 return true;
1350 bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1351 ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1353 if (!need_layer)
1354 cairo_save(_cr);
1355 else
1356 pushLayer();
1358 if (!no_fill) {
1359 _setFillStyle(style, pbox);
1360 setPathVector(pathv);
1362 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1363 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1364 } else {
1365 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1366 }
1368 if (no_stroke)
1369 cairo_fill(_cr);
1370 else
1371 cairo_fill_preserve(_cr);
1372 }
1374 if (!no_stroke) {
1375 _setStrokeStyle(style, pbox);
1376 if (no_fill)
1377 setPathVector(pathv);
1379 cairo_stroke(_cr);
1380 }
1382 if (need_layer)
1383 popLayer();
1384 else
1385 cairo_restore(_cr);
1387 return true;
1388 }
1390 bool
1391 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1392 Geom::Matrix const *image_transform, SPStyle const *style)
1393 {
1394 g_assert( _is_valid );
1396 if (_render_mode == RENDER_MODE_CLIP)
1397 return true;
1399 guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1400 if (!px_rgba)
1401 return false;
1403 float opacity;
1404 if (_state->merge_opacity)
1405 opacity = _state->opacity;
1406 else
1407 opacity = 1.0;
1409 // make a copy of the original pixbuf with premultiplied alpha
1410 // if we pass the original pixbuf it will get messed up
1411 for (unsigned i = 0; i < h; i++) {
1412 for (unsigned j = 0; j < w; j++) {
1413 guchar const *src = px + i * rs + j * 4;
1414 guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1415 guchar r, g, b, alpha_dst;
1417 // calculate opacity-modified alpha
1418 alpha_dst = src[3];
1419 if (opacity != 1.0 && _vector_based_target)
1420 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1422 // premul alpha (needed because this will be undone by cairo-pdf)
1423 r = src[0]*alpha_dst/255;
1424 g = src[1]*alpha_dst/255;
1425 b = src[2]*alpha_dst/255;
1427 *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1428 }
1429 }
1431 cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1432 if (cairo_surface_status(image_surface)) {
1433 TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1434 return false;
1435 }
1437 // setup automatic freeing of the image data when destroying the surface
1438 static cairo_user_data_key_t key;
1439 cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1441 cairo_save(_cr);
1443 // scaling by width & height is not needed because it will be done by Cairo
1444 if (image_transform)
1445 transform(image_transform);
1447 cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1449 // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1450 if (_vector_based_target) {
1451 cairo_new_path(_cr);
1452 cairo_rectangle(_cr, 0, 0, w, h);
1453 cairo_clip(_cr);
1454 }
1456 if (_vector_based_target)
1457 cairo_paint(_cr);
1458 else
1459 cairo_paint_with_alpha(_cr, opacity);
1461 cairo_restore(_cr);
1463 cairo_surface_destroy(image_surface);
1465 return true;
1466 }
1468 #define GLYPH_ARRAY_SIZE 64
1470 unsigned int
1471 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1472 {
1473 cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1474 cairo_glyph_t *glyphs = glyph_array;
1475 unsigned int num_glyphs = glyphtext.size();
1476 if (num_glyphs > GLYPH_ARRAY_SIZE)
1477 glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1479 unsigned int num_invalid_glyphs = 0;
1480 unsigned int i = 0;
1481 for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1482 // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1483 // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1484 if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1485 TRACE(("INVALID GLYPH found\n"));
1486 num_invalid_glyphs++;
1487 continue;
1488 }
1489 glyphs[i - num_invalid_glyphs].index = it_info->index;
1490 glyphs[i - num_invalid_glyphs].x = it_info->x;
1491 glyphs[i - num_invalid_glyphs].y = it_info->y;
1492 i++;
1493 }
1495 if (path) {
1496 cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1497 } else {
1498 cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1499 }
1501 if (num_glyphs > GLYPH_ARRAY_SIZE)
1502 g_free(glyphs);
1504 return num_glyphs - num_invalid_glyphs;
1505 }
1507 bool
1508 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1509 std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1510 {
1511 // create a cairo_font_face from PangoFont
1512 double size = style->font_size.computed;
1513 gpointer fonthash = (gpointer)font;
1514 cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1516 FcPattern *fc_pattern = NULL;
1518 #ifdef USE_PANGO_WIN32
1519 # ifdef CAIRO_HAS_WIN32_FONT
1520 LOGFONTA *lfa = pango_win32_font_logfont(font);
1521 LOGFONTW lfw;
1523 ZeroMemory(&lfw, sizeof(LOGFONTW));
1524 memcpy(&lfw, lfa, sizeof(LOGFONTA));
1525 MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1527 if(font_face == NULL) {
1528 font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1529 g_hash_table_insert(font_table, fonthash, font_face);
1530 }
1531 # endif
1532 #else
1533 # ifdef CAIRO_HAS_FT_FONT
1534 PangoFcFont *fc_font = PANGO_FC_FONT(font);
1535 fc_pattern = fc_font->font_pattern;
1536 if(font_face == NULL) {
1537 font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1538 g_hash_table_insert(font_table, fonthash, font_face);
1539 }
1540 # endif
1541 #endif
1543 cairo_save(_cr);
1544 cairo_set_font_face(_cr, font_face);
1546 if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1547 size = 12.0;
1549 // set the given font matrix
1550 cairo_matrix_t matrix;
1551 _initCairoMatrix(&matrix, font_matrix);
1552 cairo_set_font_matrix(_cr, &matrix);
1554 if (_render_mode == RENDER_MODE_CLIP) {
1555 if (_clip_mode == CLIP_MODE_MASK) {
1556 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1557 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1558 } else {
1559 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1560 }
1561 _showGlyphs(_cr, font, glyphtext, FALSE);
1562 } else {
1563 // just add the glyph paths to the current context
1564 _showGlyphs(_cr, font, glyphtext, TRUE);
1565 }
1566 } else {
1567 bool fill = false, stroke = false, have_path = false;
1568 if (style->fill.isColor() || style->fill.isPaintserver()) {
1569 fill = true;
1570 }
1572 if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1573 stroke = true;
1574 }
1575 if (fill) {
1576 _setFillStyle(style, NULL);
1577 if (_is_texttopath) {
1578 _showGlyphs(_cr, font, glyphtext, true);
1579 have_path = true;
1580 if (stroke) cairo_fill_preserve(_cr);
1581 else cairo_fill(_cr);
1582 } else {
1583 _showGlyphs(_cr, font, glyphtext, false);
1584 }
1585 }
1586 if (stroke) {
1587 _setStrokeStyle(style, NULL);
1588 if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1589 cairo_stroke(_cr);
1590 }
1591 }
1593 cairo_restore(_cr);
1595 // if (font_face)
1596 // cairo_font_face_destroy(font_face);
1598 return true;
1599 }
1601 /* Helper functions */
1603 void
1604 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1605 {
1606 cairo_new_path(_cr);
1607 addPathVector(pv);
1608 }
1610 void
1611 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1612 {
1613 feed_pathvector_to_cairo(_cr, pv);
1614 }
1616 void
1617 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1618 {
1619 cairo_matrix_t matrix;
1621 cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1622 cairo_transform(cr, &matrix);
1623 }
1625 void
1626 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1627 {
1628 matrix->xx = (*transform)[0];
1629 matrix->yx = (*transform)[1];
1630 matrix->xy = (*transform)[2];
1631 matrix->yy = (*transform)[3];
1632 matrix->x0 = (*transform)[4];
1633 matrix->y0 = (*transform)[5];
1634 }
1636 void
1637 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1638 {
1639 _concatTransform(cr, (*transform)[0], (*transform)[1],
1640 (*transform)[2], (*transform)[3],
1641 (*transform)[4], (*transform)[5]);
1642 }
1644 static cairo_status_t
1645 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1646 {
1647 size_t written;
1648 FILE *file = (FILE*)closure;
1650 written = fwrite (data, 1, length, file);
1652 if (written == length)
1653 return CAIRO_STATUS_SUCCESS;
1654 else
1655 return CAIRO_STATUS_WRITE_ERROR;
1656 }
1658 #include "clear-n_.h"
1660 } /* namespace Internal */
1661 } /* namespace Extension */
1662 } /* namespace Inkscape */
1664 #undef TRACE
1665 #undef TEST
1667 /* End of GNU GPL code */
1670 /*
1671 Local Variables:
1672 mode:c++
1673 c-file-style:"stroustrup"
1674 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1675 indent-tabs-mode:nil
1676 fill-column:99
1677 End:
1678 */
1679 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :