Code

Patch by Adib for 271695
[inkscape.git] / src / extension / internal / cairo-render-context.cpp
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)
84 //#define TEST(_args) _args
85 #define TEST(_args)
87 // FIXME: expose these from sp-clippath/mask.cpp
88 struct SPClipPathView {
89     SPClipPathView *next;
90     unsigned int key;
91     NRArenaItem *arenaitem;
92     NRRect bbox;
93 };
95 struct SPMaskView {
96     SPMaskView *next;
97     unsigned int key;
98     NRArenaItem *arenaitem;
99     NRRect bbox;
100 };
102 namespace Inkscape {
103 namespace Extension {
104 namespace Internal {
106 static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
108 CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109     _dpi(72),
110     _pdf_level(0),
111     _ps_level(1),
112     _eps(false),
113     _is_texttopath(FALSE),
114     _is_filtertobitmap(FALSE),
115     _bitmapresolution(72),
116     _stream(NULL),
117     _is_valid(FALSE),
118     _vector_based_target(FALSE),
119     _cr(NULL), // Cairo context
120     _surface(NULL),
121     _target(CAIRO_SURFACE_TYPE_IMAGE),
122     _target_format(CAIRO_FORMAT_ARGB32),
123     _layout(NULL),
124     _state(NULL),
125     _renderer(parent),
126     _render_mode(RENDER_MODE_NORMAL),
127     _clip_mode(CLIP_MODE_MASK)
129     font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
132 CairoRenderContext::~CairoRenderContext(void)
134     if(font_table != NULL) {
135         g_hash_table_remove_all(font_table);
136     }
138     if (_cr) cairo_destroy(_cr);
139     if (_surface) cairo_surface_destroy(_surface);
140     if (_layout) g_object_unref(_layout);
142 void CairoRenderContext::font_data_free(gpointer data)
144     cairo_font_face_t *font_face = (cairo_font_face_t *)data;
145     if (font_face) {
146         cairo_font_face_destroy(font_face);
147     }
150 CairoRenderer*
151 CairoRenderContext::getRenderer(void) const
153     return _renderer;
156 CairoRenderState*
157 CairoRenderContext::getCurrentState(void) const
159     return _state;
162 CairoRenderState*
163 CairoRenderContext::getParentState(void) const
165     // if this is the root node just return it
166     if (g_slist_length(_state_stack) == 1) {
167         return _state;
168     } else {
169         return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
170     }
173 void
174 CairoRenderContext::setStateForStyle(SPStyle const *style)
176     // only opacity & overflow is stored for now
177     _state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
178     _state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
179     _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
181     if (style->fill.isPaintserver() || style->stroke.isPaintserver())
182         _state->merge_opacity = FALSE;
184     // disable rendering of opacity if there's a stroke on the fill
185     if (_state->merge_opacity
186         && !style->fill.isNone()
187         && !style->stroke.isNone())
188         _state->merge_opacity = FALSE;
191 /**
192  * \brief Creates a new render context which will be compatible with the given context's Cairo surface
193  *
194  * \param width     width of the surface to be created
195  * \param height    height of the surface to be created
196  */
197 CairoRenderContext*
198 CairoRenderContext::cloneMe(double width, double height) const
200     g_assert( _is_valid );
201     g_assert( width > 0.0 && height > 0.0 );
203     CairoRenderContext *new_context = _renderer->createContext();
204     cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
205                                                             (int)ceil(width), (int)ceil(height));
206     new_context->_cr = cairo_create(surface);
207     new_context->_surface = surface;
208     new_context->_width = width;
209     new_context->_height = height;
210     new_context->_is_valid = TRUE;
212     return new_context;
215 CairoRenderContext*
216 CairoRenderContext::cloneMe(void) const
218     g_assert( _is_valid );
220     return cloneMe(_width, _height);
223 bool
224 CairoRenderContext::setImageTarget(cairo_format_t format)
226     // format cannot be set on an already initialized surface
227     if (_is_valid)
228         return false;
230     switch (format) {
231         case CAIRO_FORMAT_ARGB32:
232         case CAIRO_FORMAT_RGB24:
233         case CAIRO_FORMAT_A8:
234         case CAIRO_FORMAT_A1:
235             _target_format = format;
236             _target = CAIRO_SURFACE_TYPE_IMAGE;
237             return true;
238             break;
239         default:
240             break;
241     }
243     return false;
246 bool
247 CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
249 #ifndef CAIRO_HAS_PDF_SURFACE
250     return false;
251 #else
252     _target = CAIRO_SURFACE_TYPE_PDF;
253     _vector_based_target = TRUE;
254 #endif
256     FILE *osf = NULL;
257     FILE *osp = NULL;
259     gsize bytesRead = 0;
260     gsize bytesWritten = 0;
261     GError *error = NULL;
262     gchar *local_fn = g_filename_from_utf8(utf8_fn,
263                                            -1,  &bytesRead,  &bytesWritten, &error);
264     gchar const *fn = local_fn;
266     /* TODO: Replace the below fprintf's with something that does the right thing whether in
267     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
268     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
269     * return code.
270     */
271     if (fn != NULL) {
272         if (*fn == '|') {
273             fn += 1;
274             while (isspace(*fn)) fn += 1;
275 #ifndef WIN32
276             osp = popen(fn, "w");
277 #else
278             osp = _popen(fn, "w");
279 #endif
280             if (!osp) {
281                 fprintf(stderr, "inkscape: popen(%s): %s\n",
282                         fn, strerror(errno));
283                 return false;
284             }
285             _stream = osp;
286         } else if (*fn == '>') {
287             fn += 1;
288             while (isspace(*fn)) fn += 1;
289             Inkscape::IO::dump_fopen_call(fn, "K");
290             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
291             if (!osf) {
292                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
293                         fn, strerror(errno));
294                 return false;
295             }
296             _stream = osf;
297         } else {
298             /* put cwd stuff in here */
299             gchar *qn = ( *fn
300                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
301                 : g_strdup("lpr") );
302 #ifndef WIN32
303             osp = popen(qn, "w");
304 #else
305             osp = _popen(qn, "w");
306 #endif
307             if (!osp) {
308                 fprintf(stderr, "inkscape: popen(%s): %s\n",
309                         qn, strerror(errno));
310                 return false;
311             }
312             g_free(qn);
313             _stream = osp;
314         }
315     }
317     g_free(local_fn);
319     if (_stream) {
320         /* fixme: this is kinda icky */
321 #if !defined(_WIN32) && !defined(__WIN32__)
322         (void) signal(SIGPIPE, SIG_IGN);
323 #endif
324     }
326     return true;
329 bool
330 CairoRenderContext::setPsTarget(gchar const *utf8_fn)
332 #ifndef CAIRO_HAS_PS_SURFACE
333     return false;
334 #else
335     _target = CAIRO_SURFACE_TYPE_PS;
336     _vector_based_target = TRUE;
337 #endif
339     FILE *osf = NULL;
340     FILE *osp = NULL;
342     gsize bytesRead = 0;
343     gsize bytesWritten = 0;
344     GError *error = NULL;
345     gchar *local_fn = g_filename_from_utf8(utf8_fn,
346                                            -1,  &bytesRead,  &bytesWritten, &error);
347     gchar const *fn = local_fn;
349     /* TODO: Replace the below fprintf's with something that does the right thing whether in
350     * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
351     * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
352     * return code.
353     */
354     if (fn != NULL) {
355         if (*fn == '|') {
356             fn += 1;
357             while (isspace(*fn)) fn += 1;
358 #ifndef WIN32
359             osp = popen(fn, "w");
360 #else
361             osp = _popen(fn, "w");
362 #endif
363             if (!osp) {
364                 fprintf(stderr, "inkscape: popen(%s): %s\n",
365                         fn, strerror(errno));
366                 return false;
367             }
368             _stream = osp;
369         } else if (*fn == '>') {
370             fn += 1;
371             while (isspace(*fn)) fn += 1;
372             Inkscape::IO::dump_fopen_call(fn, "K");
373             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
374             if (!osf) {
375                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
376                         fn, strerror(errno));
377                 return false;
378             }
379             _stream = osf;
380         } else {
381             /* put cwd stuff in here */
382             gchar *qn = ( *fn
383                     ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
384                 : g_strdup("lpr") );
385 #ifndef WIN32
386             osp = popen(qn, "w");
387 #else
388             osp = _popen(qn, "w");
389 #endif
390             if (!osp) {
391                 fprintf(stderr, "inkscape: popen(%s): %s\n",
392                         qn, strerror(errno));
393                 return false;
394             }
395             g_free(qn);
396             _stream = osp;
397         }
398     }
400     g_free(local_fn);
402     if (_stream) {
403         /* fixme: this is kinda icky */
404 #if !defined(_WIN32) && !defined(__WIN32__)
405         (void) signal(SIGPIPE, SIG_IGN);
406 #endif
407     }
409     return true;
412 void CairoRenderContext::setPSLevel(unsigned int level)
414     _ps_level = level;
417 void CairoRenderContext::setEPS(bool eps)
419     _eps = eps;
422 unsigned int CairoRenderContext::getPSLevel(void)
424     return _ps_level;
427 void CairoRenderContext::setPDFLevel(unsigned int level)
429     _pdf_level = level;
432 void CairoRenderContext::setTextToPath(bool texttopath)
434     _is_texttopath = texttopath;
437 void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
439     _is_filtertobitmap = filtertobitmap;
442 bool CairoRenderContext::getFilterToBitmap(void)
444     return _is_filtertobitmap;
447 void CairoRenderContext::setBitmapResolution(int resolution)
449     _bitmapresolution = resolution;
452 int CairoRenderContext::getBitmapResolution(void)
454     return _bitmapresolution;
457 cairo_surface_t*
458 CairoRenderContext::getSurface(void)
460     g_assert( _is_valid );
462     return _surface;
465 bool
466 CairoRenderContext::saveAsPng(const char *file_name)
468     cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
469     if (status)
470         return false;
471     else
472         return true;
475 void
476 CairoRenderContext::setRenderMode(CairoRenderMode mode)
478     switch (mode) {
479         case RENDER_MODE_NORMAL:
480         case RENDER_MODE_CLIP:
481             _render_mode = mode;
482             break;
483         default:
484             _render_mode = RENDER_MODE_NORMAL;
485             break;
486     }
489 CairoRenderContext::CairoRenderMode
490 CairoRenderContext::getRenderMode(void) const
492     return _render_mode;
495 void
496 CairoRenderContext::setClipMode(CairoClipMode mode)
498     switch (mode) {
499         case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
500         case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
501             _clip_mode = mode;
502             break;
503         default:
504             _clip_mode = CLIP_MODE_PATH;
505             break;
506     }
509 CairoRenderContext::CairoClipMode
510 CairoRenderContext::getClipMode(void) const
512     return _clip_mode;
515 CairoRenderState*
516 CairoRenderContext::_createState(void)
518     CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
519     g_assert( state != NULL );
521     state->has_filtereffect = FALSE;
522     state->merge_opacity = TRUE;
523     state->opacity = 1.0;
524     state->need_layer = FALSE;
525     state->has_overflow = FALSE;
526     state->parent_has_userspace = FALSE;
527     state->clip_path = NULL;
528     state->mask = NULL;
530     return state;
533 void
534 CairoRenderContext::pushLayer(void)
536     g_assert( _is_valid );
538     TRACE(("--pushLayer\n"));
539     cairo_push_group(_cr);
541     // clear buffer
542     if (!_vector_based_target) {
543         cairo_save(_cr);
544         cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
545         cairo_paint(_cr);
546         cairo_restore(_cr);
547     }
550 void
551 CairoRenderContext::popLayer(void)
553     g_assert( _is_valid );
555     float opacity = _state->opacity;
556     TRACE(("--popLayer w/ opacity %f\n", opacity));
558     /*
559      At this point, the Cairo source is ready. A Cairo mask must be created if required.
560      Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
561      masks independently of the objects they effect while in SVG the clip paths and masks
562      are defined relative to the objects they are attached to.
563      Notes:
564      1. An SVG object may have both a clip path and a mask!
565      2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
566      3. An SVG clipped or masked object may be first drawn off the page and then translated onto
567         the page (document). This is also not handled properly.
568      4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
569      5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
570         alpha. This is handled here by doing a pixel by pixel conversion.
571     */
573     SPClipPath *clip_path = _state->clip_path;
574     SPMask *mask = _state->mask;
575     if (clip_path || mask) {
577         CairoRenderContext *clip_ctx = 0;
578         cairo_surface_t *clip_mask = 0;
580         // Apply any clip path first
581         if (clip_path) {
582             TRACE(("  Applying clip\n"));
583             if (_render_mode == RENDER_MODE_CLIP)
584                 mask = NULL;    // disable mask when performing nested clipping
586             if (_vector_based_target) {
587                 setClipMode(CLIP_MODE_PATH); // Vector
588                 if (!mask) {
589                     cairo_pop_group_to_source(_cr);
590                     _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
591                     if (opacity == 1.0)
592                         cairo_paint(_cr);
593                     else
594                         cairo_paint_with_alpha(_cr, opacity);
596                 } else {
597                     // the clipPath will be applied before masking
598                 }
599             } else {
601                 // setup a new rendering context
602                 clip_ctx = _renderer->createContext();
603                 clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
604                 clip_ctx->setClipMode(CLIP_MODE_MASK);  // Raster
605                 // This code ties the clipping to the document coordinates. It doesn't allow
606                 // for a clipped object intially drawn off the page and then translated onto
607                 // the page.
608                 if (!clip_ctx->setupSurface(_width, _height)) {
609                     TRACE(("clip: setupSurface failed\n"));
610                     _renderer->destroyContext(clip_ctx);
611                     return;
612                 }
614                 // clear buffer
615                 cairo_save(clip_ctx->_cr);
616                 cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
617                 cairo_paint(clip_ctx->_cr);
618                 cairo_restore(clip_ctx->_cr);
620                 // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
621                 if (!mask)
622                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
623                 else
624                     cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
626                 // copy over the correct CTM
627                 // It must be stored in item_transform of current state after pushState.
628                 Geom::Matrix item_transform;
629                 if (_state->parent_has_userspace)
630                     item_transform = getParentState()->transform * _state->item_transform;
631                 else
632                     item_transform = _state->item_transform;
634                 // apply the clip path
635                 clip_ctx->pushState();
636                 clip_ctx->getCurrentState()->item_transform = item_transform;
637                 _renderer->applyClipPath(clip_ctx, clip_path);
638                 clip_ctx->popState();
640                 clip_mask = clip_ctx->getSurface();
641                 TEST(clip_ctx->saveAsPng("clip_mask.png"));
643                 if (!mask) {
644                     cairo_pop_group_to_source(_cr);
645                     cairo_mask_surface(_cr, clip_mask, 0, 0);
646                     _renderer->destroyContext(clip_ctx);
647                 }
648             }
649         }
651         // Apply any mask second
652         if (mask) {
653             TRACE(("  Applying mask\n"));
654             // create rendering context for mask
655             CairoRenderContext *mask_ctx = _renderer->createContext();
657             // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
658             // Cairo surface is expecting the mask to be 90 dpi.
659             float surface_width = _width;
660             float surface_height = _height;
661             if( _vector_based_target ) {
662                 surface_width *= 1.25;
663                 surface_height *= 1.25;
664             }
665             mask_ctx->setupSurface( surface_width, surface_height );
666             TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
668             // set rendering mode to normal
669             setRenderMode(RENDER_MODE_NORMAL);
671             // copy the correct CTM to mask context
672             /*
673             if (_state->parent_has_userspace)
674                 mask_ctx->setTransform(&getParentState()->transform);
675             else
676                 mask_ctx->setTransform(&_state->transform);
677             */
678             // This is probably not correct... but it seems to do the trick.
679             mask_ctx->setTransform(&_state->item_transform);
681             // render mask contents to mask_ctx
682             _renderer->applyMask(mask_ctx, mask);
684             TEST(mask_ctx->saveAsPng("mask.png"));
686             // composite with clip mask
687             if (clip_path && _clip_mode == CLIP_MODE_MASK) {
688                 cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
689                 _renderer->destroyContext(clip_ctx);
690             }
692             cairo_surface_t *mask_image = mask_ctx->getSurface();
693             int width = cairo_image_surface_get_width(mask_image);
694             int height = cairo_image_surface_get_height(mask_image);
695             int stride = cairo_image_surface_get_stride(mask_image);
696             unsigned char *pixels = cairo_image_surface_get_data(mask_image);
698             // premultiply with opacity
699             // In SVG, the rgb channels as well as the alpha channel is used in masking.
700             // In Cairo, only the alpha channel is used thus requiring this conversion.
701             TRACE(("premul w/ %f\n", opacity));
702             guint8 int_opacity = (guint8)(255 * opacity);
703             for (int row = 0 ; row < height; row++) {
704                 unsigned char *row_data = pixels + (row * stride);
705                 for (int i = 0 ; i < width; i++) {
706                     guint32 *pixel = (guint32 *)row_data + i;
707                     *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
708                                ((*pixel & 0x0000ff00) >>  8) * 46518 +
709                                ((*pixel & 0x000000ff)      ) * 4688) *
710                               int_opacity);
711                 }
712             }
714             cairo_pop_group_to_source(_cr);
715             if (_clip_mode == CLIP_MODE_PATH) {
716                 // we have to do the clipping after cairo_pop_group_to_source
717                 _renderer->applyClipPath(this, clip_path);
718             }
719             // apply the mask onto the layer
720             cairo_mask_surface(_cr, mask_image, 0, 0);
721             _renderer->destroyContext(mask_ctx);
722         }
723     } else {
724         // No clip path or mask
725         cairo_pop_group_to_source(_cr);
726         if (opacity == 1.0)
727             cairo_paint(_cr);
728         else
729             cairo_paint_with_alpha(_cr, opacity);
730     }
733 void
734 CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
736     g_assert( _is_valid );
738     // here it should be checked whether the current clip winding changed
739     // so we could switch back to masked clipping
740     if (fill_rule->value == SP_WIND_RULE_EVENODD) {
741         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
742     } else {
743         cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
744     }
745     addPathVector(pv);
748 void
749 CairoRenderContext::addClippingRect(double x, double y, double width, double height)
751     g_assert( _is_valid );
753     cairo_rectangle(_cr, x, y, width, height);
754     cairo_clip(_cr);
757 bool
758 CairoRenderContext::setupSurface(double width, double height)
760     // Is the surface already set up?
761     if (_is_valid)
762         return true;
764     if (_vector_based_target && _stream == NULL)
765         return false;
767     _width = width;
768     _height = height;
770     cairo_surface_t *surface = NULL;
771     cairo_matrix_t ctm;
772     cairo_matrix_init_identity (&ctm);
773     switch (_target) {
774         case CAIRO_SURFACE_TYPE_IMAGE:
775             surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
776             break;
777 #ifdef CAIRO_HAS_PDF_SURFACE
778         case CAIRO_SURFACE_TYPE_PDF:
779             surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
780             break;
781 #endif
782 #ifdef CAIRO_HAS_PS_SURFACE
783         case CAIRO_SURFACE_TYPE_PS:
784             surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
785             if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
786                 return FALSE;
787             }
788 #if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
789             cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
790             cairo_ps_surface_set_eps (surface, (cairo_bool_t) _eps);
791 #endif
792             break;
793 #endif
794         default:
795             return false;
796             break;
797     }
799     return _finishSurfaceSetup (surface, &ctm);
802 bool
803 CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
805     if (_is_valid || !surface)
806         return false;
808     _vector_based_target = is_vector;
809     bool ret = _finishSurfaceSetup (surface, ctm);
810     if (ret)
811         cairo_surface_reference (surface);
812     return ret;
815 bool
816 CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
818     if(surface == NULL) {
819         return false;
820     }
821     if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
822         return false;
823     }
825     _cr = cairo_create(surface);
826     if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
827         return false;
828     }
829     if (ctm)
830         cairo_set_matrix(_cr, ctm);
831     _surface = surface;
833     if (_vector_based_target) {
834         cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
835     } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
836         // set background color on non-alpha surfaces
837         // TODO: bgcolor should be derived from SPDocument
838         cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
839         cairo_rectangle(_cr, 0, 0, _width, _height);
840         cairo_fill(_cr);
841     }
843     _is_valid = TRUE;
845     return true;
848 bool
849 CairoRenderContext::finish(void)
851     g_assert( _is_valid );
853     if (_vector_based_target)
854         cairo_show_page(_cr);
856     cairo_destroy(_cr);
857     cairo_surface_finish(_surface);
858     cairo_status_t status = cairo_surface_status(_surface);
859     cairo_surface_destroy(_surface);
860     _cr = NULL;
861     _surface = NULL;
863     if (_layout)
864         g_object_unref(_layout);
866     _is_valid = FALSE;
868     if (_vector_based_target && _stream) {
869         /* Flush stream to be sure. */
870         (void) fflush(_stream);
872         fclose(_stream);
873         _stream = NULL;
874     }
876     if (status == CAIRO_STATUS_SUCCESS)
877         return true;
878     else
879         return false;
882 void
883 CairoRenderContext::transform(Geom::Matrix const *transform)
885     g_assert( _is_valid );
887     cairo_matrix_t matrix;
888     _initCairoMatrix(&matrix, transform);
889     cairo_transform(_cr, &matrix);
891     // store new CTM
892     getTransform(&_state->transform);
895 void
896 CairoRenderContext::setTransform(Geom::Matrix const *transform)
898     g_assert( _is_valid );
900     cairo_matrix_t matrix;
901     _initCairoMatrix(&matrix, transform);
902     cairo_set_matrix(_cr, &matrix);
903     _state->transform = *transform;
906 void
907 CairoRenderContext::getTransform(Geom::Matrix *copy) const
909     g_assert( _is_valid );
911     cairo_matrix_t ctm;
912     cairo_get_matrix(_cr, &ctm);
913     (*copy)[0] = ctm.xx;
914     (*copy)[1] = ctm.yx;
915     (*copy)[2] = ctm.xy;
916     (*copy)[3] = ctm.yy;
917     (*copy)[4] = ctm.x0;
918     (*copy)[5] = ctm.y0;
921 void
922 CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
924     g_assert( _is_valid );
926     CairoRenderState *parent_state = getParentState();
927     memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
930 void
931 CairoRenderContext::pushState(void)
933     g_assert( _is_valid );
935     cairo_save(_cr);
937     CairoRenderState *new_state = _createState();
938     // copy current state's transform
939     new_state->transform = _state->transform;
940     _state_stack = g_slist_prepend(_state_stack, new_state);
941     _state = new_state;
944 void
945 CairoRenderContext::popState(void)
947     g_assert( _is_valid );
949     cairo_restore(_cr);
951     g_free(_state_stack->data);
952     _state_stack = g_slist_remove_link(_state_stack, _state_stack);
953     _state = (CairoRenderState*)_state_stack->data;
955     g_assert( g_slist_length(_state_stack) > 0 );
958 static bool pattern_hasItemChildren (SPPattern *pat)
960     for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
961         if (SP_IS_ITEM (child)) {
962             return true;
963         }
964     }
965     return false;
968 cairo_pattern_t*
969 CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
971     g_assert( SP_IS_PATTERN(paintserver) );
973     SPPattern *pat = SP_PATTERN (paintserver);
975     Geom::Matrix ps2user, pcs2dev;
976     ps2user = Geom::identity();
977     pcs2dev = Geom::identity();
979     double x = pattern_x(pat);
980     double y = pattern_y(pat);
981     double width = pattern_width(pat);
982     double height = pattern_height(pat);
983     double bbox_width_scaler;
984     double bbox_height_scaler;
986     TRACE(("%f x %f pattern\n", width, height));
988     if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
989         //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
990         bbox_width_scaler = pbox->x1 - pbox->x0;
991         bbox_height_scaler = pbox->y1 - pbox->y0;
992         ps2user[4] = x * bbox_width_scaler + pbox->x0;
993         ps2user[5] = y * bbox_height_scaler + pbox->y0;
994     } else {
995         bbox_width_scaler = 1.0;
996         bbox_height_scaler = 1.0;
997         ps2user[4] = x;
998         ps2user[5] = y;
999     }
1001     // apply pattern transformation
1002     Geom::Matrix pattern_transform(pattern_patternTransform(pat));
1003     ps2user *= pattern_transform;
1004     Geom::Point ori (ps2user[4], ps2user[5]);
1006     // create pattern contents coordinate system
1007     if (pat->viewBox_set) {
1008         NRRect *view_box = pattern_viewBox(pat);
1010         double x, y, w, h;
1011         double view_width, view_height;
1012         x = 0;
1013         y = 0;
1014         w = width * bbox_width_scaler;
1015         h = height * bbox_height_scaler;
1017         view_width = view_box->x1 - view_box->x0;
1018         view_height = view_box->y1 - view_box->y0;
1020         //calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
1021         pcs2dev[0] = w / view_width;
1022         pcs2dev[3] = h / view_height;
1023         pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
1024         pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
1025     } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
1026         pcs2dev[0] = pbox->x1 - pbox->x0;
1027         pcs2dev[3] = pbox->y1 - pbox->y0;
1029     }
1031     // Calculate the size of the surface which has to be created
1032 #define SUBPIX_SCALE 100
1033     // Cairo requires an integer pattern surface width/height.
1034     // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
1035     // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
1036     double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
1037     double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
1038     TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
1039     // create new rendering context
1040     CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
1042     // adjust the size of the painted pattern to fit exactly the created surface
1043     // this has to be done because of the rounding to obtain an integer pattern surface width/height
1044     double scale_width = surface_width / (bbox_width_scaler * width);
1045     double scale_height = surface_height / (bbox_height_scaler * height);
1046     if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
1047         TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
1048         pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
1049         ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
1050     }
1052     // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
1053     ps2user[4] = ori[Geom::X];
1054     ps2user[5] = ori[Geom::Y];
1056     pattern_ctx->setTransform(&pcs2dev);
1057     pattern_ctx->pushState();
1059     // create arena and group
1060     NRArena *arena = NRArena::create();
1061     unsigned dkey = sp_item_display_key_new(1);
1063     // show items and render them
1064     for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1065         if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1066             for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1067                 if (SP_IS_ITEM (child)) {
1068                     sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
1069                     _renderer->renderItem(pattern_ctx, SP_ITEM (child));
1070                 }
1071             }
1072             break; // do not go further up the chain if children are found
1073         }
1074     }
1076     pattern_ctx->popState();
1078     // setup a cairo_pattern_t
1079     cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1080     TEST(pattern_ctx->saveAsPng("pattern.png"));
1081     cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1082     cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1084     // set pattern transformation
1085     cairo_matrix_t pattern_matrix;
1086     _initCairoMatrix(&pattern_matrix, &ps2user);
1087     cairo_matrix_invert(&pattern_matrix);
1088     cairo_pattern_set_matrix(result, &pattern_matrix);
1090     delete pattern_ctx;
1092     // hide all items
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 = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1096                 if (SP_IS_ITEM (child)) {
1097                     sp_item_invoke_hide (SP_ITEM (child), dkey);
1098                 }
1099             }
1100             break; // do not go further up the chain if children are found
1101         }
1102     }
1104     return result;
1107 cairo_pattern_t*
1108 CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1109                                                  NRRect const *pbox, float alpha)
1111     cairo_pattern_t *pattern = NULL;
1112     bool apply_bbox2user = FALSE;
1114     if (SP_IS_LINEARGRADIENT (paintserver)) {
1116             SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1118             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1120             Geom::Point p1 (lg->x1.computed, lg->y1.computed);
1121             Geom::Point p2 (lg->x2.computed, lg->y2.computed);
1122             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1123                 // convert to userspace
1124                 Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1125                 p1 *= bbox2user;
1126                 p2 *= bbox2user;
1127             }
1129             // create linear gradient pattern
1130             pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
1132             // add stops
1133             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1134                 float rgb[3];
1135                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1136                 cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1137             }
1138     } else if (SP_IS_RADIALGRADIENT (paintserver)) {
1140         SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1142         sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1144         Geom::Point c (rg->cx.computed, rg->cy.computed);
1145         Geom::Point f (rg->fx.computed, rg->fy.computed);
1146         double r = rg->r.computed;
1147         if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1148             apply_bbox2user = true;
1150         // create radial gradient pattern
1151         pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
1153         // add stops
1154         for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1155             float rgb[3];
1156             sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1157             cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1158         }
1159     } else if (SP_IS_PATTERN (paintserver)) {
1161         pattern = _createPatternPainter(paintserver, pbox);
1162     } else {
1163         return NULL;
1164     }
1166     if (pattern && SP_IS_GRADIENT (paintserver)) {
1167         SPGradient *g = SP_GRADIENT(paintserver);
1169         // set extend type
1170         SPGradientSpread spread = sp_gradient_get_spread(g);
1171         switch (spread) {
1172             case SP_GRADIENT_SPREAD_REPEAT: {
1173                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1174                 break;
1175             }
1176             case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
1177                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1178                 break;
1179             }
1180             case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
1181                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1182                 break;
1183             }
1184             default: {
1185                 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1186                 break;
1187             }
1188         }
1190         cairo_matrix_t pattern_matrix;
1191         if (g->gradientTransform_set) {
1192             // apply gradient transformation
1193             cairo_matrix_init(&pattern_matrix,
1194                 g->gradientTransform[0], g->gradientTransform[1],
1195                 g->gradientTransform[2], g->gradientTransform[3],
1196                 g->gradientTransform[4], g->gradientTransform[5]);
1197         } else {
1198             cairo_matrix_init_identity (&pattern_matrix);
1199         }
1201         if (apply_bbox2user) {
1202             // convert to userspace
1203             cairo_matrix_t bbox2user;
1204             cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1205             cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1206         }
1207         cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
1208         cairo_pattern_set_matrix(pattern, &pattern_matrix);
1209     }
1211     return pattern;
1214 void
1215 CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1217     g_return_if_fail( !style->fill.set
1218                       || style->fill.isColor()
1219                       || style->fill.isPaintserver() );
1221     float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1222     if (_state->merge_opacity) {
1223         alpha *= _state->opacity;
1224         TRACE(("merged op=%f\n", alpha));
1225     }
1227     if (style->fill.isColor()) {
1228         float rgb[3];
1229         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1231         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1233     } else if (!style->fill.set) { // unset fill is black
1234         cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
1236     } else {
1237         g_assert( style->fill.isPaintserver()
1238                   || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1239                   || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1241         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1243         if (pattern) {
1244             cairo_set_source(_cr, pattern);
1245             cairo_pattern_destroy(pattern);
1246         }
1247     }
1250 void
1251 CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1253     float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1254     if (_state->merge_opacity)
1255         alpha *= _state->opacity;
1257     if (style->stroke.isColor()) {
1258         float rgb[3];
1259         sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1261         cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1262     } else {
1263         g_assert( style->fill.isPaintserver()
1264                   || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1265                   || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1267         cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1269         if (pattern) {
1270             cairo_set_source(_cr, pattern);
1271             cairo_pattern_destroy(pattern);
1272         }
1273     }
1275     if (style->stroke_dash.n_dash   &&
1276         style->stroke_dash.dash       )
1277     {
1278         cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1279     } else {
1280         cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
1281     }
1283     cairo_set_line_width(_cr, style->stroke_width.computed);
1285     // set line join type
1286     cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1287     switch (style->stroke_linejoin.computed) {
1288         case SP_STROKE_LINEJOIN_MITER:
1289             join = CAIRO_LINE_JOIN_MITER;
1290             break;
1291         case SP_STROKE_LINEJOIN_ROUND:
1292             join = CAIRO_LINE_JOIN_ROUND;
1293             break;
1294         case SP_STROKE_LINEJOIN_BEVEL:
1295             join = CAIRO_LINE_JOIN_BEVEL;
1296             break;
1297     }
1298     cairo_set_line_join(_cr, join);
1300     // set line cap type
1301     cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1302     switch (style->stroke_linecap.computed) {
1303         case SP_STROKE_LINECAP_BUTT:
1304             cap = CAIRO_LINE_CAP_BUTT;
1305             break;
1306         case SP_STROKE_LINECAP_ROUND:
1307             cap = CAIRO_LINE_CAP_ROUND;
1308             break;
1309         case SP_STROKE_LINECAP_SQUARE:
1310             cap = CAIRO_LINE_CAP_SQUARE;
1311             break;
1312     }
1313     cairo_set_line_cap(_cr, cap);
1314     cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1317 bool
1318 CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
1320     g_assert( _is_valid );
1322     if (_render_mode == RENDER_MODE_CLIP) {
1323         if (_clip_mode == CLIP_MODE_PATH) {
1324             addClipPath(pathv, &style->fill_rule);
1325         } else {
1326             setPathVector(pathv);
1327             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1328                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1329             } else {
1330                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1331             }
1332             cairo_fill(_cr);
1333             TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1334         }
1335         return true;
1336     }
1338     bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
1339     bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
1340                     style->stroke_opacity.value == 0;
1342     if (no_fill && no_stroke)
1343         return true;
1345     bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1346                         ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1348     if (!need_layer)
1349         cairo_save(_cr);
1350     else
1351         pushLayer();
1353     if (!no_fill) {
1354         _setFillStyle(style, pbox);
1355         setPathVector(pathv);
1357         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1358             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1359         } else {
1360             cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1361         }
1363         if (no_stroke)
1364             cairo_fill(_cr);
1365         else
1366             cairo_fill_preserve(_cr);
1367     }
1369     if (!no_stroke) {
1370         _setStrokeStyle(style, pbox);
1371         if (no_fill)
1372             setPathVector(pathv);
1374         cairo_stroke(_cr);
1375     }
1377     if (need_layer)
1378         popLayer();
1379     else
1380         cairo_restore(_cr);
1382     return true;
1385 bool
1386 CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1387                                 Geom::Matrix const *image_transform, SPStyle const *style)
1389     g_assert( _is_valid );
1391     if (_render_mode == RENDER_MODE_CLIP)
1392         return true;
1394     guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1395     if (!px_rgba)
1396         return false;
1398     float opacity;
1399     if (_state->merge_opacity)
1400         opacity = _state->opacity;
1401     else
1402         opacity = 1.0;
1404     // make a copy of the original pixbuf with premultiplied alpha
1405     // if we pass the original pixbuf it will get messed up
1406     for (unsigned i = 0; i < h; i++) {
1407         for (unsigned j = 0; j < w; j++) {
1408             guchar const *src = px + i * rs + j * 4;
1409             guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1410             guchar r, g, b, alpha_dst;
1412             // calculate opacity-modified alpha
1413             alpha_dst = src[3];
1414             if (opacity != 1.0 && _vector_based_target)
1415                 alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1417             // premul alpha (needed because this will be undone by cairo-pdf)
1418             r = src[0]*alpha_dst/255;
1419             g = src[1]*alpha_dst/255;
1420             b = src[2]*alpha_dst/255;
1422             *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1423         }
1424     }
1426     cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1427     if (cairo_surface_status(image_surface)) {
1428         TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1429         return false;
1430     }
1432     // setup automatic freeing of the image data when destroying the surface
1433     static cairo_user_data_key_t key;
1434     cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1436     cairo_save(_cr);
1438     // scaling by width & height is not needed because it will be done by Cairo
1439     if (image_transform)
1440         transform(image_transform);
1442     cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1444     // set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1445     if (_vector_based_target) {
1446         cairo_new_path(_cr);
1447         cairo_rectangle(_cr, 0, 0, w, h);
1448         cairo_clip(_cr);
1449     }
1451     if (_vector_based_target)
1452         cairo_paint(_cr);
1453     else
1454         cairo_paint_with_alpha(_cr, opacity);
1456     cairo_restore(_cr);
1458     cairo_surface_destroy(image_surface);
1460     return true;
1463 #define GLYPH_ARRAY_SIZE 64
1465 unsigned int
1466 CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
1468     cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1469     cairo_glyph_t *glyphs = glyph_array;
1470     unsigned int num_glyphs = glyphtext.size();
1471     if (num_glyphs > GLYPH_ARRAY_SIZE)
1472         glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1474     unsigned int num_invalid_glyphs = 0;
1475     unsigned int i = 0;
1476     for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1477         // skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1478         // or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1479         if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1480             TRACE(("INVALID GLYPH found\n"));
1481             num_invalid_glyphs++;
1482             continue;
1483         }
1484         glyphs[i - num_invalid_glyphs].index = it_info->index;
1485         glyphs[i - num_invalid_glyphs].x = it_info->x;
1486         glyphs[i - num_invalid_glyphs].y = it_info->y;
1487         i++;
1488     }
1490     if (path) {
1491         cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1492     } else {
1493         cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1494     }
1496     if (num_glyphs > GLYPH_ARRAY_SIZE)
1497         g_free(glyphs);
1499     return num_glyphs - num_invalid_glyphs;
1502 bool
1503 CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
1504                                     std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1506     // create a cairo_font_face from PangoFont
1507     double size = style->font_size.computed;
1508     gpointer fonthash = (gpointer)font;
1509     cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
1511     FcPattern *fc_pattern = NULL;
1513 #ifdef USE_PANGO_WIN32
1514 # ifdef CAIRO_HAS_WIN32_FONT
1515     LOGFONTA *lfa = pango_win32_font_logfont(font);
1516     LOGFONTW lfw;
1518     ZeroMemory(&lfw, sizeof(LOGFONTW));
1519     memcpy(&lfw, lfa, sizeof(LOGFONTA));
1520     MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
1522     if(font_face == NULL) {
1523         font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
1524         g_hash_table_insert(font_table, fonthash, font_face);
1525     }
1526 # endif
1527 #else
1528 # ifdef CAIRO_HAS_FT_FONT
1529     PangoFcFont *fc_font = PANGO_FC_FONT(font);
1530     fc_pattern = fc_font->font_pattern;
1531     if(font_face == NULL) {
1532         font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1533         g_hash_table_insert(font_table, fonthash, font_face);
1534     }
1535 # endif
1536 #endif
1538     cairo_save(_cr);
1539     cairo_set_font_face(_cr, font_face);
1541     if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1542         size = 12.0;
1544     // set the given font matrix
1545     cairo_matrix_t matrix;
1546     _initCairoMatrix(&matrix, font_matrix);
1547     cairo_set_font_matrix(_cr, &matrix);
1549     if (_render_mode == RENDER_MODE_CLIP) {
1550         if (_clip_mode == CLIP_MODE_MASK) {
1551             if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1552                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1553             } else {
1554                 cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1555             }
1556             _showGlyphs(_cr, font, glyphtext, FALSE);
1557         } else {
1558             // just add the glyph paths to the current context
1559             _showGlyphs(_cr, font, glyphtext, TRUE);
1560         }
1561     } else {
1562         bool fill = false, stroke = false, have_path = false;
1563         if (style->fill.isColor() || style->fill.isPaintserver()) {
1564             fill = true;
1565         }
1567         if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1568             stroke = true;
1569         }
1570         if (fill) {
1571             _setFillStyle(style, NULL);
1572             if (_is_texttopath) {
1573                 _showGlyphs(_cr, font, glyphtext, true);
1574                 have_path = true;
1575                 if (stroke) cairo_fill_preserve(_cr);
1576                 else cairo_fill(_cr);
1577             } else {
1578                 _showGlyphs(_cr, font, glyphtext, false);
1579             }
1580         }
1581         if (stroke) {
1582             _setStrokeStyle(style, NULL);
1583             if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
1584             cairo_stroke(_cr);
1585         }
1586     }
1588     cairo_restore(_cr);
1590 //    if (font_face)
1591 //        cairo_font_face_destroy(font_face);
1593     return true;
1596 /* Helper functions */
1598 void
1599 CairoRenderContext::setPathVector(Geom::PathVector const &pv)
1601     cairo_new_path(_cr);
1602     addPathVector(pv);
1605 void
1606 CairoRenderContext::addPathVector(Geom::PathVector const &pv)
1608     feed_pathvector_to_cairo(_cr, pv);
1611 void
1612 CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1614     cairo_matrix_t matrix;
1616     cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1617     cairo_transform(cr, &matrix);
1620 void
1621 CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
1623     matrix->xx = (*transform)[0];
1624     matrix->yx = (*transform)[1];
1625     matrix->xy = (*transform)[2];
1626     matrix->yy = (*transform)[3];
1627     matrix->x0 = (*transform)[4];
1628     matrix->y0 = (*transform)[5];
1631 void
1632 CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
1634     _concatTransform(cr, (*transform)[0], (*transform)[1],
1635                      (*transform)[2], (*transform)[3],
1636                      (*transform)[4], (*transform)[5]);
1639 static cairo_status_t
1640 _write_callback(void *closure, const unsigned char *data, unsigned int length)
1642     size_t written;
1643     FILE *file = (FILE*)closure;
1645     written = fwrite (data, 1, length, file);
1647     if (written == length)
1648         return CAIRO_STATUS_SUCCESS;
1649     else
1650         return CAIRO_STATUS_WRITE_ERROR;
1653 #include "clear-n_.h"
1655 }  /* namespace Internal */
1656 }  /* namespace Extension */
1657 }  /* namespace Inkscape */
1659 #undef TRACE
1660 #undef TEST
1662 /* End of GNU GPL code */
1665 /*
1666   Local Variables:
1667   mode:c++
1668   c-file-style:"stroustrup"
1669   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1670   indent-tabs-mode:nil
1671   fill-column:99
1672   End:
1673 */
1674 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :