Code

remove memory boundries on bitmap renderer, optimize memory usage
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
1 /** \file
2  * Rendering with Cairo.
3  */
4 /*
5  * Author:
6  *   Miklos Erdelyi <erdelyim@gmail.com>
7  *   Jon A. Cruz <jon@joncruz.org>
8  *   Abhishek Sharma
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(1),
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)
130     font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
133 CairoRenderContext::~CairoRenderContext(void)
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);
143 void CairoRenderContext::font_data_free(gpointer data)
145     cairo_font_face_t *font_face = (cairo_font_face_t *)data;
146     if (font_face) {
147         cairo_font_face_destroy(font_face);
148     }
151 CairoRenderer*
152 CairoRenderContext::getRenderer(void) const
154     return _renderer;
157 CairoRenderState*
158 CairoRenderContext::getCurrentState(void) const
160     return _state;
163 CairoRenderState*
164 CairoRenderContext::getParentState(void) const
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     }
174 void
175 CairoRenderContext::setStateForStyle(SPStyle const *style)
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;
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
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;
216 CairoRenderContext*
217 CairoRenderContext::cloneMe(void) const
219     g_assert( _is_valid );
221     return cloneMe(_width, _height);
224 bool
225 CairoRenderContext::setImageTarget(cairo_format_t format)
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;
247 bool
248 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
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;
330 bool
331 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
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;
413 void CairoRenderContext::setPSLevel(unsigned int level)
415     _ps_level = level;
418 void CairoRenderContext::setEPS(bool eps)
420     _eps = eps;
423 unsigned int CairoRenderContext::getPSLevel(void)
425     return _ps_level;
428 void CairoRenderContext::setPDFLevel(unsigned int level)
430     _pdf_level = level;
433 void CairoRenderContext::setTextToPath(bool texttopath)
435     _is_texttopath = texttopath;
438 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
440     _is_filtertobitmap = filtertobitmap;
443 bool CairoRenderContext::getFilterToBitmap(void)
445     return _is_filtertobitmap;
448 void CairoRenderContext::setBitmapResolution(int resolution)
450     _bitmapresolution = resolution;
453 int CairoRenderContext::getBitmapResolution(void)
455     return _bitmapresolution;
458 cairo_surface_t*
459 CairoRenderContext::getSurface(void)
461     g_assert( _is_valid );
463     return _surface;
466 bool
467 CairoRenderContext::saveAsPng(const char *file_name)
469     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
470     if (status)
471         return false;
472     else
473         return true;
476 void
477 CairoRenderContext::setRenderMode(CairoRenderMode mode)
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     }
490 CairoRenderContext::CairoRenderMode
491 CairoRenderContext::getRenderMode(void) const
493     return _render_mode;
496 void
497 CairoRenderContext::setClipMode(CairoClipMode mode)
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     }
510 CairoRenderContext::CairoClipMode
511 CairoRenderContext::getClipMode(void) const
513     return _clip_mode;
516 CairoRenderState*
517 CairoRenderContext::_createState(void)
519     CairoRenderState *state = (CairoRenderState*)g_try_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;
534 void
535 CairoRenderContext::pushLayer(void)
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     }
551 void
552 CairoRenderContext::popLayer(void)
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             // Mask should start black, but it is created white.
674             cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
675             cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
676             cairo_fill(mask_ctx->_cr);
678             // set rendering mode to normal
679             setRenderMode(RENDER_MODE_NORMAL);
681             // copy the correct CTM to mask context
682             /*
683             if (_state->parent_has_userspace)
684                 mask_ctx->setTransform(&getParentState()->transform);
685             else
686                 mask_ctx->setTransform(&_state->transform);
687             */
688             // This is probably not correct... but it seems to do the trick.
689             mask_ctx->setTransform(&_state->item_transform);
691             // render mask contents to mask_ctx
692             _renderer->applyMask(mask_ctx, mask);
694             TEST(mask_ctx->saveAsPng("mask.png"));
696             // composite with clip mask
697             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
698                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
699                 _renderer->destroyContext(clip_ctx);
700             }
702             cairo_surface_t *mask_image = mask_ctx->getSurface();
703             int width = cairo_image_surface_get_width(mask_image);
704             int height = cairo_image_surface_get_height(mask_image);
705             int stride = cairo_image_surface_get_stride(mask_image);
706             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
708             // In SVG, the rgb channels as well as the alpha channel is used in masking.
709             // In Cairo, only the alpha channel is used thus requiring this conversion.
710             // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
711             // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
712             // The incoming pixel values already include alpha, fill-opacity, etc.,
713             // however, opacity must still be applied.
714             TRACE(("premul w/ %f\n", opacity));
715             const float coeff_r = 0.2125 / 255.0;
716             const float coeff_g = 0.7154 / 255.0;
717             const float coeff_b = 0.0721 / 255.0;
718             for (int row = 0 ; row < height; row++) {
719                 unsigned char *row_data = pixels + (row * stride);
720                 for (int i = 0 ; i < width; i++) {
721                     guint32 *pixel = (guint32 *)row_data + i;
722                     float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
723                                        ((*pixel & 0x0000ff00) >>  8) * coeff_g +
724                                        ((*pixel & 0x000000ff)      ) * coeff_b );
725                     // lum_alpha can be slightly greater than 1 due to rounding errors...
726                     // but this should be OK since it doesn't matter what the lower
727                     // six hexadecimal numbers of *pixel are.
728                     *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
729                 }
730             }
732             cairo_pop_group_to_source(_cr);
733             if (_clip_mode == CLIP_MODE_PATH) {
734                 // we have to do the clipping after cairo_pop_group_to_source
735                 _renderer->applyClipPath(this, clip_path);
736             }
737             // apply the mask onto the layer
738             cairo_mask_surface(_cr, mask_image, 0, 0);
739             _renderer->destroyContext(mask_ctx);
740         }
741     } else {
742         // No clip path or mask
743         cairo_pop_group_to_source(_cr);
744         if (opacity == 1.0)
745             cairo_paint(_cr);
746         else
747             cairo_paint_with_alpha(_cr, opacity);
748     }
751 void
752 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
754     g_assert( _is_valid );
756     // here it should be checked whether the current clip winding changed
757     // so we could switch back to masked clipping
758     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
759         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
760     } else {
761         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
762     }
763     addPathVector(pv);
766 void
767 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
769     g_assert( _is_valid );
771     cairo_rectangle(_cr, x, y, width, height);
772     cairo_clip(_cr);
775 bool
776 CairoRenderContext::setupSurface(double width, double height)
778     // Is the surface already set up?
779     if (_is_valid)
780         return true;
782     if (_vector_based_target && _stream == NULL)
783         return false;
785     _width = width;
786     _height = height;
788     cairo_surface_t *surface = NULL;
789     cairo_matrix_t ctm;
790     cairo_matrix_init_identity (&ctm);
791     switch (_target) {
792         case CAIRO_SURFACE_TYPE_IMAGE:
793             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
794             break;
795 #ifdef CAIRO_HAS_PDF_SURFACE
796         case CAIRO_SURFACE_TYPE_PDF:
797             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
798 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
799             cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
800 #endif
801             break;
802 #endif
803 #ifdef CAIRO_HAS_PS_SURFACE
804         case CAIRO_SURFACE_TYPE_PS:
805             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
806             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
807                 return FALSE;
808             }
809 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
810             cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
811             cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
812 #endif
813             // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
814 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
815             if (override_bbox) {
816                 cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
817                 cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
818             }
819 #endif
820             break;
821 #endif
822         default:
823             return false;
824             break;
825     }
827     return _finishSurfaceSetup (surface, &ctm);
830 bool
831 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
833     if (_is_valid || !surface)
834         return false;
836     _vector_based_target = is_vector;
837     bool ret = _finishSurfaceSetup (surface, ctm);
838     if (ret)
839         cairo_surface_reference (surface);
840     return ret;
843 bool
844 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
846     if(surface == NULL) {
847         return false;
848     }
849     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
850         return false;
851     }
853     _cr = cairo_create(surface);
854     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
855         return false;
856     }
857     if (ctm)
858         cairo_set_matrix(_cr, ctm);
859     _surface = surface;
861     if (_vector_based_target) {
862         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
863     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
864         // set background color on non-alpha surfaces
865         // TODO: bgcolor should be derived from SPDocument
866         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
867         cairo_rectangle(_cr, 0, 0, _width, _height);
868         cairo_fill(_cr);
869     }
871     _is_valid = TRUE;
873     return true;
876 bool
877 CairoRenderContext::finish(void)
879     g_assert( _is_valid );
881     if (_vector_based_target)
882         cairo_show_page(_cr);
884     cairo_destroy(_cr);
885     cairo_surface_finish(_surface);
886     cairo_status_t status = cairo_surface_status(_surface);
887     cairo_surface_destroy(_surface);
888     _cr = NULL;
889     _surface = NULL;
891     if (_layout)
892         g_object_unref(_layout);
894     _is_valid = FALSE;
896     if (_vector_based_target && _stream) {
897         /* Flush stream to be sure. */
898         (void) fflush(_stream);
900         fclose(_stream);
901         _stream = NULL;
902     }
904     if (status == CAIRO_STATUS_SUCCESS)
905         return true;
906     else
907         return false;
910 void
911 CairoRenderContext::transform(Geom::Matrix const *transform)
913     g_assert( _is_valid );
915     cairo_matrix_t matrix;
916     _initCairoMatrix(&matrix, transform);
917     cairo_transform(_cr, &matrix);
919     // store new CTM
920     getTransform(&_state->transform);
923 void
924 CairoRenderContext::setTransform(Geom::Matrix const *transform)
926     g_assert( _is_valid );
928     cairo_matrix_t matrix;
929     _initCairoMatrix(&matrix, transform);
930     cairo_set_matrix(_cr, &matrix);
931     _state->transform = *transform;
934 void
935 CairoRenderContext::getTransform(Geom::Matrix *copy) const
937     g_assert( _is_valid );
939     cairo_matrix_t ctm;
940     cairo_get_matrix(_cr, &ctm);
941     (*copy)[0] = ctm.xx;
942     (*copy)[1] = ctm.yx;
943     (*copy)[2] = ctm.xy;
944     (*copy)[3] = ctm.yy;
945     (*copy)[4] = ctm.x0;
946     (*copy)[5] = ctm.y0;
949 void
950 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
952     g_assert( _is_valid );
954     CairoRenderState *parent_state = getParentState();
955     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
958 void
959 CairoRenderContext::pushState(void)
961     g_assert( _is_valid );
963     cairo_save(_cr);
965     CairoRenderState *new_state = _createState();
966     // copy current state's transform
967     new_state->transform = _state->transform;
968     _state_stack = g_slist_prepend(_state_stack, new_state);
969     _state = new_state;
972 void
973 CairoRenderContext::popState(void)
975     g_assert( _is_valid );
977     cairo_restore(_cr);
979     g_free(_state_stack->data);
980     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
981     _state = (CairoRenderState*)_state_stack->data;
983     g_assert( g_slist_length(_state_stack) > 0 );
986 static bool pattern_hasItemChildren(SPPattern *pat)
988     bool hasItems = false;
989     for ( SPObject *child = pat->firstChild() ; child && !hasItems; child = child->getNext() ) {
990         if (SP_IS_ITEM (child)) {
991             hasItems = true;
992         }
993     }
994     return hasItems;
997 cairo_pattern_t*
998 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
1000     g_assert( SP_IS_PATTERN(paintserver) );
1002     SPPattern *pat = SP_PATTERN (paintserver);
1004     Geom::Matrix ps2user, pcs2dev;
1005     ps2user = Geom::identity();
1006     pcs2dev = Geom::identity();
1008     double x = pattern_x(pat);
1009     double y = pattern_y(pat);
1010     double width = pattern_width(pat);
1011     double height = pattern_height(pat);
1012     double bbox_width_scaler;
1013     double bbox_height_scaler;
1015     TRACE(("%f x %f pattern\n", width, height));
1017     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1018         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1019         bbox_width_scaler = pbox->x1 - pbox->x0;
1020         bbox_height_scaler = pbox->y1 - pbox->y0;
1021         ps2user[4] = x * bbox_width_scaler + pbox->x0;
1022         ps2user[5] = y * bbox_height_scaler + pbox->y0;
1023     } else {
1024         bbox_width_scaler = 1.0;
1025         bbox_height_scaler = 1.0;
1026         ps2user[4] = x;
1027         ps2user[5] = y;
1028     }
1030     // apply pattern transformation
1031     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1032     ps2user *= pattern_transform;
1033     Geom::Point ori (ps2user[4], ps2user[5]);
1035     // create pattern contents coordinate system
1036     if (pat->viewBox_set) {
1037         NRRect *view_box = pattern_viewBox(pat);
1039         double x, y, w, h;
1040         double view_width, view_height;
1041         x = 0;
1042         y = 0;
1043         w = width * bbox_width_scaler;
1044         h = height * bbox_height_scaler;
1046         view_width = view_box->x1 - view_box->x0;
1047         view_height = view_box->y1 - view_box->y0;
1049         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1050         pcs2dev[0] = w / view_width;
1051         pcs2dev[3] = h / view_height;
1052         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1053         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1054     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1055         pcs2dev[0] = pbox->x1 - pbox->x0;
1056         pcs2dev[3] = pbox->y1 - pbox->y0;
1058     }
1060     // Calculate the size of the surface which has to be created
1061 #define SUBPIX_SCALE 100
1062     // Cairo requires an integer pattern surface width/height.
1063     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1064     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1065     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1066     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1067     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1068     // create new rendering context
1069     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1071     // adjust the size of the painted pattern to fit exactly the created surface
1072     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1073     double scale_width = surface_width / (bbox_width_scaler * width);
1074     double scale_height = surface_height / (bbox_height_scaler * height);
1075     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1076         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1077         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1078         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1079     }
1081     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1082     ps2user[4] = ori[Geom::X];
1083     ps2user[5] = ori[Geom::Y];
1085     pattern_ctx->setTransform(&pcs2dev);
1086     pattern_ctx->pushState();
1088     // create arena and group
1089     NRArena *arena = NRArena::create();
1090     unsigned dkey = SPItem::display_key_new(1);
1092     // show items and render them
1093     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1094         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1095             for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
1096                 if (SP_IS_ITEM (child)) {
1097                     SP_ITEM (child)->invoke_show (arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1098                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1099                 }
1100             }
1101             break; // do not go further up the chain if children are found
1102         }
1103     }
1105     pattern_ctx->popState();
1107     // setup a cairo_pattern_t
1108     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1109     TEST(pattern_ctx->saveAsPng("pattern.png"));
1110     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1111     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1113     // set pattern transformation
1114     cairo_matrix_t pattern_matrix;
1115     _initCairoMatrix(&pattern_matrix, &ps2user);
1116     cairo_matrix_invert(&pattern_matrix);
1117     cairo_pattern_set_matrix(result, &pattern_matrix);
1119     delete pattern_ctx;
1121     // hide all items
1122     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1123         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1124             for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
1125                 if (SP_IS_ITEM (child)) {
1126                     SP_ITEM (child)->invoke_hide (dkey);
1127                 }
1128             }
1129             break; // do not go further up the chain if children are found
1130         }
1131     }
1133     return result;
1136 cairo_pattern_t*
1137 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1138                                                  NRRect const *pbox, float alpha)
1140     cairo_pattern_t *pattern = NULL;
1141     bool apply_bbox2user = FALSE;
1143     if (SP_IS_LINEARGRADIENT (paintserver)) {
1145             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1147             SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
1149             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1150             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1151             if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1152                 // convert to userspace
1153                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1154                 p1 *= bbox2user;
1155                 p2 *= bbox2user;
1156             }
1158             // create linear gradient pattern
1159             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1161             // add stops
1162             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1163                 float rgb[3];
1164                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1165                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1166             }
1167     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1169         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1171         SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
1173         Geom::Point c (rg->cx.computed, rg->cy.computed);
1174         Geom::Point f (rg->fx.computed, rg->fy.computed);
1175         double r = rg->r.computed;
1176         if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1177             apply_bbox2user = true;
1179         // create radial gradient pattern
1180         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1182         // add stops
1183         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1184             float rgb[3];
1185             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1186             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1187         }
1188     } else if (SP_IS_PATTERN (paintserver)) {
1190         pattern = _createPatternPainter(paintserver, pbox);
1191     } else {
1192         return NULL;
1193     }
1195     if (pattern && SP_IS_GRADIENT (paintserver)) {
1196         SPGradient *g = SP_GRADIENT(paintserver);
1198         // set extend type
1199         SPGradientSpread spread = g->fetchSpread();
1200         switch (spread) {
1201             case SP_GRADIENT_SPREAD_REPEAT: {
1202                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1203                 break;
1204             }
1205             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1206                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1207                 break;
1208             }
1209             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1210                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1211                 break;
1212             }
1213             default: {
1214                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1215                 break;
1216             }
1217         }
1219         cairo_matrix_t pattern_matrix;
1220         if (g->gradientTransform_set) {
1221             // apply gradient transformation
1222             cairo_matrix_init(&pattern_matrix,
1223                 g->gradientTransform[0], g->gradientTransform[1],
1224                 g->gradientTransform[2], g->gradientTransform[3],
1225                 g->gradientTransform[4], g->gradientTransform[5]);
1226         } else {
1227             cairo_matrix_init_identity (&pattern_matrix);
1228         }
1230         if (apply_bbox2user) {
1231             // convert to userspace
1232             cairo_matrix_t bbox2user;
1233             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1234             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1235         }
1236         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1237         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1238     }
1240     return pattern;
1243 void
1244 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1246     g_return_if_fail( !style->fill.set
1247                       || style->fill.isColor()
1248                       || style->fill.isPaintserver() );
1250     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1251     if (_state->merge_opacity) {
1252         alpha *= _state->opacity;
1253         TRACE(("merged op=%f\n", alpha));
1254     }
1256     if (style->fill.isColor()) {
1257         float rgb[3];
1258         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1260         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1262     } else if (!style->fill.set) { // unset fill is black
1263         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1265     } else {
1266         g_assert( style->fill.isPaintserver()
1267                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1268                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1270         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1272         if (pattern) {
1273             cairo_set_source(_cr, pattern);
1274             cairo_pattern_destroy(pattern);
1275         }
1276     }
1279 void
1280 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1282     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1283     if (_state->merge_opacity)
1284         alpha *= _state->opacity;
1286     if (style->stroke.isColor()) {
1287         float rgb[3];
1288         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1290         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1291     } else {
1292         g_assert( style->stroke.isPaintserver()
1293                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1294                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1296         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1298         if (pattern) {
1299             cairo_set_source(_cr, pattern);
1300             cairo_pattern_destroy(pattern);
1301         }
1302     }
1304     if (style->stroke_dash.n_dash   &&
1305         style->stroke_dash.dash       )
1306     {
1307         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1308     } else {
1309         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1310     }
1312     cairo_set_line_width(_cr, style->stroke_width.computed);
1314     // set line join type
1315     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1316     switch (style->stroke_linejoin.computed) {
1317         case SP_STROKE_LINEJOIN_MITER:
1318             join = CAIRO_LINE_JOIN_MITER;
1319             break;
1320         case SP_STROKE_LINEJOIN_ROUND:
1321             join = CAIRO_LINE_JOIN_ROUND;
1322             break;
1323         case SP_STROKE_LINEJOIN_BEVEL:
1324             join = CAIRO_LINE_JOIN_BEVEL;
1325             break;
1326     }
1327     cairo_set_line_join(_cr, join);
1329     // set line cap type
1330     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1331     switch (style->stroke_linecap.computed) {
1332         case SP_STROKE_LINECAP_BUTT:
1333             cap = CAIRO_LINE_CAP_BUTT;
1334             break;
1335         case SP_STROKE_LINECAP_ROUND:
1336             cap = CAIRO_LINE_CAP_ROUND;
1337             break;
1338         case SP_STROKE_LINECAP_SQUARE:
1339             cap = CAIRO_LINE_CAP_SQUARE;
1340             break;
1341     }
1342     cairo_set_line_cap(_cr, cap);
1343     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1346 bool
1347 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1349     g_assert( _is_valid );
1351     if (_render_mode == RENDER_MODE_CLIP) {
1352         if (_clip_mode == CLIP_MODE_PATH) {
1353             addClipPath(pathv, &style->fill_rule);
1354         } else {
1355             setPathVector(pathv);
1356             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1357                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1358             } else {
1359                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1360             }
1361             cairo_fill(_cr);
1362             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1363         }
1364         return true;
1365     }
1367     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1368     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1369                     style->stroke_opacity.value == 0;
1371     if (no_fill && no_stroke)
1372         return true;
1374     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1375                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1377     if (!need_layer)
1378         cairo_save(_cr);
1379     else
1380         pushLayer();
1382     if (!no_fill) {
1383         _setFillStyle(style, pbox);
1384         setPathVector(pathv);
1386         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1387             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1388         } else {
1389             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1390         }
1392         if (no_stroke)
1393             cairo_fill(_cr);
1394         else
1395             cairo_fill_preserve(_cr);
1396     }
1398     if (!no_stroke) {
1399         _setStrokeStyle(style, pbox);
1400         if (no_fill)
1401             setPathVector(pathv);
1403         cairo_stroke(_cr);
1404     }
1406     if (need_layer)
1407         popLayer();
1408     else
1409         cairo_restore(_cr);
1411     return true;
1414 bool
1415 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1416                                 Geom::Matrix const *image_transform, SPStyle const *style)
1418     g_assert( _is_valid );
1420     if (_render_mode == RENDER_MODE_CLIP)
1421         return true;
1423     guchar* px_rgba = NULL;
1424     guint64 size = 4L * (guint64)w * (guint64)h;
1426     if(size < (guint64)G_MAXSIZE) {
1427         px_rgba = (guchar*)g_try_malloc(4 * w * h);
1428         if (!px_rgba) {
1429             g_warning ("Could not allocate %lu bytes for pixel buffer!", (long unsigned) size);
1430             return false;
1431         }
1432     } else {
1433         g_warning ("the requested memory exceeds the system limit");
1434         return false;
1435     }
1438     float opacity;
1439     if (_state->merge_opacity)
1440         opacity = _state->opacity;
1441     else
1442         opacity = 1.0;
1444     // make a copy of the original pixbuf with premultiplied alpha
1445     // if we pass the original pixbuf it will get messed up
1446     /// @todo optimize this code, it costs a lot of time
1447     for (unsigned i = 0; i < h; i++) {
1448         guchar const *src = px + i * rs;
1449         guint32 *dst = (guint32 *)(px_rgba + i * rs);
1450         for (unsigned j = 0; j < w; j++) {
1451             guchar r, g, b, alpha_dst;
1453             // calculate opacity-modified alpha
1454             alpha_dst = src[3];
1455             if ((opacity != 1.0) && _vector_based_target)
1456                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1458             // premul alpha (needed because this will be undone by cairo-pdf)
1459             r = src[0]*alpha_dst/255;
1460             g = src[1]*alpha_dst/255;
1461             b = src[2]*alpha_dst/255;
1463             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1465             dst++;  // pointer to 4byte variables
1466             src += 4;   // pointer to 1byte variables
1467         }
1468     }
1470     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1471     if (cairo_surface_status(image_surface)) {
1472         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1473         return false;
1474     }
1476     // setup automatic freeing of the image data when destroying the surface
1477     static cairo_user_data_key_t key;
1478     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1480     cairo_save(_cr);
1482     // scaling by width & height is not needed because it will be done by Cairo
1483     if (image_transform)
1484         transform(image_transform);
1486     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1488     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1489     if (_vector_based_target) {
1490         cairo_new_path(_cr);
1491         cairo_rectangle(_cr, 0, 0, w, h);
1492         cairo_clip(_cr);
1493     }
1495     if (_vector_based_target)
1496         cairo_paint(_cr);
1497     else
1498         cairo_paint_with_alpha(_cr, opacity);
1500     cairo_restore(_cr);
1502     cairo_surface_destroy(image_surface);
1504     return true;
1507 #define GLYPH_ARRAY_SIZE 64
1509 unsigned int
1510 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1512     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1513     cairo_glyph_t *glyphs = glyph_array;
1514     unsigned int num_glyphs = glyphtext.size();
1515     if (num_glyphs > GLYPH_ARRAY_SIZE) {
1516         glyphs = (cairo_glyph_t*)g_try_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1517         if(glyphs == NULL) {
1518             g_warning("CairorenderContext::_showGlyphs: can not allocate memory for %d glyphs.", num_glyphs);
1519             return 0;
1520         }
1521     }
1523     unsigned int num_invalid_glyphs = 0;
1524     unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
1525     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1526         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1527         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1528         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1529             TRACE(("INVALID GLYPH found\n"));
1530             g_message("Invalid glyph found, continuing...");
1531             num_invalid_glyphs++;
1532             continue;
1533         }
1534         glyphs[i].index = it_info->index;
1535         glyphs[i].x     = it_info->x;
1536         glyphs[i].y     = it_info->y;
1537         i++;
1538     }
1540     if (path) {
1541         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1542     } else {
1543         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1544     }
1546     if (num_glyphs > GLYPH_ARRAY_SIZE)
1547         g_free(glyphs);
1549     return num_glyphs - num_invalid_glyphs;
1552 bool
1553 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1554                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1556     // create a cairo_font_face from PangoFont
1557     double size = style->font_size.computed;
1558     gpointer fonthash = (gpointer)font;
1559     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1561     FcPattern *fc_pattern = NULL;
1563 #ifdef USE_PANGO_WIN32
1564 # ifdef CAIRO_HAS_WIN32_FONT
1565     LOGFONTA *lfa = pango_win32_font_logfont(font);
1566     LOGFONTW lfw;
1568     ZeroMemory(&lfw, sizeof(LOGFONTW));
1569     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1570     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1572     if(font_face == NULL) {
1573         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1574         g_hash_table_insert(font_table, fonthash, font_face);
1575     }
1576 # endif
1577 #else
1578 # ifdef CAIRO_HAS_FT_FONT
1579     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1580     fc_pattern = fc_font->font_pattern;
1581     if(font_face == NULL) {
1582         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1583         g_hash_table_insert(font_table, fonthash, font_face);
1584     }
1585 # endif
1586 #endif
1588     cairo_save(_cr);
1589     cairo_set_font_face(_cr, font_face);
1591     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1592         size = 12.0;
1594     // set the given font matrix
1595     cairo_matrix_t matrix;
1596     _initCairoMatrix(&matrix, font_matrix);
1597     cairo_set_font_matrix(_cr, &matrix);
1599     if (_render_mode == RENDER_MODE_CLIP) {
1600         if (_clip_mode == CLIP_MODE_MASK) {
1601             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1602                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1603             } else {
1604                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1605             }
1606             _showGlyphs(_cr, font, glyphtext, FALSE);
1607         } else {
1608             // just add the glyph paths to the current context
1609             _showGlyphs(_cr, font, glyphtext, TRUE);
1610         }
1611     } else {
1612         bool fill = false, stroke = false, have_path = false;
1613         if (style->fill.isColor() || style->fill.isPaintserver()) {
1614             fill = true;
1615         }
1617         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1618             stroke = true;
1619         }
1620         if (fill) {
1621             _setFillStyle(style, NULL);
1622             if (_is_texttopath) {
1623                 _showGlyphs(_cr, font, glyphtext, true);
1624                 have_path = true;
1625                 if (stroke) cairo_fill_preserve(_cr);
1626                 else cairo_fill(_cr);
1627             } else {
1628                 _showGlyphs(_cr, font, glyphtext, false);
1629             }
1630         }
1631         if (stroke) {
1632             _setStrokeStyle(style, NULL);
1633             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1634             cairo_stroke(_cr);
1635         }
1636     }
1638     cairo_restore(_cr);
1640 //    if (font_face)
1641 //        cairo_font_face_destroy(font_face);
1643     return true;
1646 /* Helper functions */
1648 void
1649 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1651     cairo_new_path(_cr);
1652     addPathVector(pv);
1655 void
1656 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1658     feed_pathvector_to_cairo(_cr, pv);
1661 void
1662 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1664     cairo_matrix_t matrix;
1666     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1667     cairo_transform(cr, &matrix);
1670 void
1671 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1673     matrix->xx = (*transform)[0];
1674     matrix->yx = (*transform)[1];
1675     matrix->xy = (*transform)[2];
1676     matrix->yy = (*transform)[3];
1677     matrix->x0 = (*transform)[4];
1678     matrix->y0 = (*transform)[5];
1681 void
1682 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1684     _concatTransform(cr, (*transform)[0], (*transform)[1],
1685                      (*transform)[2], (*transform)[3],
1686                      (*transform)[4], (*transform)[5]);
1689 static cairo_status_t
1690 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1692     size_t written;
1693     FILE *file = (FILE*)closure;
1695     written = fwrite (data, 1, length, file);
1697     if (written == length)
1698         return CAIRO_STATUS_SUCCESS;
1699     else
1700         return CAIRO_STATUS_WRITE_ERROR;
1703 #include "clear-n_.h"
1705 }  /* namespace Internal */
1706 }  /* namespace Extension */
1707 }  /* namespace Inkscape */
1709 #undef TRACE
1710 #undef TEST
1712 /* End of GNU GPL code */
1715 /*
1716   Local Variables:
1717   mode:c++
1718   c-file-style:"stroustrup"
1719   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1720   indent-tabs-mode:nil
1721   fill-column:99
1722   End:
1723 */
1724 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :