1
#define __SP_CAIRO_RENDER_CONTEXT_C__
4
* Rendering with Cairo.
8
* Miklos Erdelyi <erdelyim@gmail.com>
10
* Copyright (C) 2006 Miklos Erdelyi
12
* Licensed under GNU GPL
19
#ifndef PANGO_ENABLE_BACKEND
20
#define PANGO_ENABLE_BACKEND
23
#ifndef PANGO_ENABLE_ENGINE
24
#define PANGO_ENABLE_ENGINE
31
#include <libnr/n-art-bpath.h>
32
#include <libnr/nr-matrix-ops.h>
33
#include <libnr/nr-matrix-fns.h>
34
#include <libnr/nr-matrix-translate-ops.h>
35
#include <libnr/nr-scale-matrix-ops.h>
37
#include <glib/gmem.h>
39
#include <glibmm/i18n.h>
40
#include "display/nr-arena.h"
41
#include "display/nr-arena-item.h"
42
#include "display/nr-arena-group.h"
43
#include "display/curve.h"
44
#include "display/canvas-bpath.h"
46
#include "sp-item-group.h"
48
#include "sp-linear-gradient.h"
49
#include "sp-radial-gradient.h"
50
#include "sp-pattern.h"
52
#include "sp-clippath.h"
54
#include <unit-constants.h>
56
#include "cairo-render-context.h"
57
#include "cairo-renderer.h"
58
#include "extension/system.h"
64
// include support for only the compiled-in surface types
65
#ifdef CAIRO_HAS_PDF_SURFACE
66
#include <cairo-pdf.h>
68
#ifdef CAIRO_HAS_PS_SURFACE
73
#ifdef CAIRO_HAS_FT_FONT
77
#include <pango/pangofc-fontmap.h>
79
//#define TRACE(_args) g_printf _args
81
//#define TEST(_args) _args
84
// FIXME: expose these from sp-clippath/mask.cpp
85
struct SPClipPathView {
88
NRArenaItem *arenaitem;
95
NRArenaItem *arenaitem;
100
namespace Extension {
103
static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
105
CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
109
_is_texttopath(FALSE),
110
_is_filtertobitmap(FALSE),
111
_bitmapresolution(72),
114
_vector_based_target(FALSE),
117
_target(CAIRO_SURFACE_TYPE_IMAGE),
118
_target_format(CAIRO_FORMAT_ARGB32),
122
_render_mode(RENDER_MODE_NORMAL),
123
_clip_mode(CLIP_MODE_MASK)
126
CairoRenderContext::~CairoRenderContext(void)
128
if (_cr) cairo_destroy(_cr);
129
if (_surface) cairo_surface_destroy(_surface);
130
if (_layout) g_object_unref(_layout);
134
CairoRenderContext::getRenderer(void) const
140
CairoRenderContext::getCurrentState(void) const
146
CairoRenderContext::getParentState(void) const
148
// if this is the root node just return it
149
if (g_slist_length(_state_stack) == 1) {
152
return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
157
CairoRenderContext::setStateForStyle(SPStyle const *style)
159
// only opacity & overflow is stored for now
160
_state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
161
_state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
162
_state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
164
if (style->fill.isPaintserver() || style->stroke.isPaintserver())
165
_state->merge_opacity = FALSE;
167
// disable rendering of opacity if there's a stroke on the fill
168
if (_state->merge_opacity
169
&& !style->fill.isNone()
170
&& !style->stroke.isNone())
171
_state->merge_opacity = FALSE;
175
* \brief Creates a new render context which will be compatible with the given context's Cairo surface
177
* \param width width of the surface to be created
178
* \param height height of the surface to be created
181
CairoRenderContext::cloneMe(double width, double height) const
183
g_assert( _is_valid );
184
g_assert( width > 0.0 && height > 0.0 );
186
CairoRenderContext *new_context = _renderer->createContext();
187
cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
188
(int)ceil(width), (int)ceil(height));
189
new_context->_cr = cairo_create(surface);
190
new_context->_surface = surface;
191
new_context->_is_valid = TRUE;
197
CairoRenderContext::cloneMe(void) const
199
g_assert( _is_valid );
201
return cloneMe(_width, _height);
205
CairoRenderContext::setImageTarget(cairo_format_t format)
207
// format cannot be set on an already initialized surface
212
case CAIRO_FORMAT_ARGB32:
213
case CAIRO_FORMAT_RGB24:
214
case CAIRO_FORMAT_A8:
215
case CAIRO_FORMAT_A1:
216
_target_format = format;
217
_target = CAIRO_SURFACE_TYPE_IMAGE;
228
CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
230
#ifndef CAIRO_HAS_PDF_SURFACE
233
_target = CAIRO_SURFACE_TYPE_PDF;
234
_vector_based_target = TRUE;
241
gsize bytesWritten = 0;
242
GError *error = NULL;
243
gchar *local_fn = g_filename_from_utf8(utf8_fn,
244
-1, &bytesRead, &bytesWritten, &error);
245
gchar const *fn = local_fn;
247
/* TODO: Replace the below fprintf's with something that does the right thing whether in
248
* gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
249
* the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
255
while (isspace(*fn)) fn += 1;
257
osp = popen(fn, "w");
259
osp = _popen(fn, "w");
262
fprintf(stderr, "inkscape: popen(%s): %s\n",
263
fn, strerror(errno));
267
} else if (*fn == '>') {
269
while (isspace(*fn)) fn += 1;
270
Inkscape::IO::dump_fopen_call(fn, "K");
271
osf = Inkscape::IO::fopen_utf8name(fn, "w+");
273
fprintf(stderr, "inkscape: fopen(%s): %s\n",
274
fn, strerror(errno));
279
/* put cwd stuff in here */
281
? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
284
osp = popen(qn, "w");
286
osp = _popen(qn, "w");
289
fprintf(stderr, "inkscape: popen(%s): %s\n",
290
qn, strerror(errno));
301
/* fixme: this is kinda icky */
302
#if !defined(_WIN32) && !defined(__WIN32__)
303
(void) signal(SIGPIPE, SIG_IGN);
311
CairoRenderContext::setPsTarget(gchar const *utf8_fn)
313
#ifndef CAIRO_HAS_PS_SURFACE
316
_target = CAIRO_SURFACE_TYPE_PS;
317
_vector_based_target = TRUE;
324
gsize bytesWritten = 0;
325
GError *error = NULL;
326
gchar *local_fn = g_filename_from_utf8(utf8_fn,
327
-1, &bytesRead, &bytesWritten, &error);
328
gchar const *fn = local_fn;
330
/* TODO: Replace the below fprintf's with something that does the right thing whether in
331
* gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
332
* the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
338
while (isspace(*fn)) fn += 1;
340
osp = popen(fn, "w");
342
osp = _popen(fn, "w");
345
fprintf(stderr, "inkscape: popen(%s): %s\n",
346
fn, strerror(errno));
350
} else if (*fn == '>') {
352
while (isspace(*fn)) fn += 1;
353
Inkscape::IO::dump_fopen_call(fn, "K");
354
osf = Inkscape::IO::fopen_utf8name(fn, "w+");
356
fprintf(stderr, "inkscape: fopen(%s): %s\n",
357
fn, strerror(errno));
362
/* put cwd stuff in here */
364
? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */
367
osp = popen(qn, "w");
369
osp = _popen(qn, "w");
372
fprintf(stderr, "inkscape: popen(%s): %s\n",
373
qn, strerror(errno));
384
/* fixme: this is kinda icky */
385
#if !defined(_WIN32) && !defined(__WIN32__)
386
(void) signal(SIGPIPE, SIG_IGN);
393
void CairoRenderContext::setPSLevel(unsigned int level)
398
unsigned int CairoRenderContext::getPSLevel(void)
403
void CairoRenderContext::setPDFLevel(unsigned int level)
408
void CairoRenderContext::setTextToPath(bool texttopath)
410
_is_texttopath = texttopath;
413
void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
415
_is_filtertobitmap = filtertobitmap;
418
bool CairoRenderContext::getFilterToBitmap(void)
420
return _is_filtertobitmap;
423
void CairoRenderContext::setBitmapResolution(int resolution)
425
_bitmapresolution = resolution;
428
int CairoRenderContext::getBitmapResolution(void)
430
return _bitmapresolution;
434
CairoRenderContext::getSurface(void)
436
g_assert( _is_valid );
442
CairoRenderContext::saveAsPng(const char *file_name)
444
cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
452
CairoRenderContext::setRenderMode(CairoRenderMode mode)
455
case RENDER_MODE_NORMAL:
456
case RENDER_MODE_CLIP:
460
_render_mode = RENDER_MODE_NORMAL;
465
CairoRenderContext::CairoRenderMode
466
CairoRenderContext::getRenderMode(void) const
472
CairoRenderContext::setClipMode(CairoClipMode mode)
480
_clip_mode = CLIP_MODE_PATH;
485
CairoRenderContext::CairoClipMode
486
CairoRenderContext::getClipMode(void) const
492
CairoRenderContext::_createState(void)
494
CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
495
g_assert( state != NULL );
497
state->has_filtereffect = FALSE;
498
state->merge_opacity = TRUE;
499
state->opacity = 1.0;
500
state->need_layer = FALSE;
501
state->has_overflow = FALSE;
502
state->parent_has_userspace = FALSE;
503
state->clip_path = NULL;
510
CairoRenderContext::pushLayer(void)
512
g_assert( _is_valid );
514
TRACE(("--pushLayer\n"));
515
cairo_push_group(_cr);
518
if (!_vector_based_target) {
520
cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
527
CairoRenderContext::popLayer(void)
529
g_assert( _is_valid );
531
float opacity = _state->opacity;
532
TRACE(("--popLayer w/ %f\n", opacity));
534
// apply clipPath or mask if present
535
SPClipPath *clip_path = _state->clip_path;
536
SPMask *mask = _state->mask;
537
if (clip_path || mask) {
539
CairoRenderContext *clip_ctx = 0;
540
cairo_surface_t *clip_mask = 0;
543
if (_render_mode == RENDER_MODE_CLIP)
544
mask = NULL; // disable mask when performing nested clipping
546
if (_vector_based_target) {
547
setClipMode(CLIP_MODE_PATH);
549
cairo_pop_group_to_source(_cr);
550
_renderer->applyClipPath(this, clip_path);
554
cairo_paint_with_alpha(_cr, opacity);
557
// the clipPath will be applied before masking
561
// setup a new rendering context
562
clip_ctx = _renderer->createContext();
563
clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
564
clip_ctx->setClipMode(CLIP_MODE_MASK);
565
if (!clip_ctx->setupSurface(_width, _height)) {
566
TRACE(("setupSurface failed\n"));
567
_renderer->destroyContext(clip_ctx);
572
cairo_save(clip_ctx->_cr);
573
cairo_set_operator(clip_ctx->_cr, CAIRO_OPERATOR_CLEAR);
574
cairo_paint(clip_ctx->_cr);
575
cairo_restore(clip_ctx->_cr);
577
// if a mask won't be applied set opacity too
579
cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
581
cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
583
// copy over the correct CTM
584
if (_state->parent_has_userspace)
585
clip_ctx->setTransform(&getParentState()->transform);
587
clip_ctx->setTransform(&_state->transform);
589
// apply the clip path
590
clip_ctx->pushState();
591
_renderer->applyClipPath(clip_ctx, clip_path);
592
clip_ctx->popState();
594
clip_mask = clip_ctx->getSurface();
595
TEST(clip_ctx->saveAsPng("clip_mask.png"));
598
cairo_pop_group_to_source(_cr);
599
cairo_mask_surface(_cr, clip_mask, 0, 0);
600
_renderer->destroyContext(clip_ctx);
606
// create rendering context for mask
607
CairoRenderContext *mask_ctx = _renderer->createContext();
608
mask_ctx->setupSurface(_width, _height);
610
// set rendering mode to normal
611
setRenderMode(RENDER_MODE_NORMAL);
613
// copy the correct CTM to mask context
614
if (_state->parent_has_userspace)
615
mask_ctx->setTransform(&getParentState()->transform);
617
mask_ctx->setTransform(&_state->transform);
619
// render mask contents to mask_ctx
620
_renderer->applyMask(mask_ctx, mask);
622
TEST(mask_ctx->saveAsPng("mask.png"));
624
// composite with clip mask
625
if (clip_path && _clip_mode == CLIP_MODE_MASK) {
626
cairo_mask_surface(mask_ctx->_cr, clip_mask, 0, 0);
627
_renderer->destroyContext(clip_ctx);
630
cairo_surface_t *mask_image = mask_ctx->getSurface();
631
int width = cairo_image_surface_get_width(mask_image);
632
int height = cairo_image_surface_get_height(mask_image);
633
int stride = cairo_image_surface_get_stride(mask_image);
634
unsigned char *pixels = cairo_image_surface_get_data(mask_image);
636
// premultiply with opacity
637
if (_state->opacity != 1.0) {
638
TRACE(("premul w/ %f\n", opacity));
639
guint8 int_opacity = (guint8)(255 * opacity);
640
for (int row = 0 ; row < height; row++) {
641
unsigned char *row_data = pixels + (row * stride);
642
for (int i = 0 ; i < width; i++) {
643
guint32 *pixel = (guint32 *)row_data + i;
644
*pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
645
((*pixel & 0x0000ff00) >> 8) * 46518 +
646
((*pixel & 0x000000ff) ) * 4688) *
652
cairo_pop_group_to_source(_cr);
653
if (_clip_mode == CLIP_MODE_PATH) {
654
// we have to do the clipping after cairo_pop_group_to_source
655
_renderer->applyClipPath(this, clip_path);
657
// apply the mask onto the layer
658
cairo_mask_surface(_cr, mask_image, 0, 0);
659
_renderer->destroyContext(mask_ctx);
662
cairo_pop_group_to_source(_cr);
666
cairo_paint_with_alpha(_cr, opacity);
671
CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
673
g_assert( _is_valid );
675
// here it should be checked whether the current clip winding changed
676
// so we could switch back to masked clipping
677
if (fill_rule->value == SP_WIND_RULE_EVENODD) {
678
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
680
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
686
CairoRenderContext::addClippingRect(double x, double y, double width, double height)
688
g_assert( _is_valid );
690
cairo_rectangle(_cr, x, y, width, height);
695
CairoRenderContext::setupSurface(double width, double height)
697
// Is the surface already set up?
701
if (_vector_based_target && _stream == NULL)
704
cairo_surface_t *surface = NULL;
706
case CAIRO_SURFACE_TYPE_IMAGE:
707
surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
709
#ifdef CAIRO_HAS_PDF_SURFACE
710
case CAIRO_SURFACE_TYPE_PDF:
711
surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
714
#ifdef CAIRO_HAS_PS_SURFACE
715
case CAIRO_SURFACE_TYPE_PS:
716
surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
717
#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
718
if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
721
cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
730
return _finishSurfaceSetup (surface);
734
CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
736
if (_is_valid || !surface)
739
_vector_based_target = is_vector;
740
bool ret = _finishSurfaceSetup (surface);
742
cairo_surface_reference (surface);
747
CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
749
if(surface == NULL) {
752
if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
756
_cr = cairo_create(surface);
759
if (_vector_based_target) {
760
cairo_scale(_cr, PT_PER_PX, PT_PER_PX);
761
} else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
762
// set background color on non-alpha surfaces
763
// TODO: bgcolor should be derived from SPDocument
764
cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
765
cairo_rectangle(_cr, 0, 0, _width, _height);
775
CairoRenderContext::finish(void)
777
g_assert( _is_valid );
779
if (_vector_based_target)
780
cairo_show_page(_cr);
783
cairo_surface_destroy(_surface);
788
g_object_unref(_layout);
792
if (_vector_based_target && _stream) {
793
/* Flush stream to be sure. */
794
(void) fflush(_stream);
804
CairoRenderContext::transform(NRMatrix const *transform)
806
g_assert( _is_valid );
808
cairo_matrix_t matrix;
809
_initCairoMatrix(&matrix, transform);
810
cairo_transform(_cr, &matrix);
813
getTransform(&_state->transform);
817
CairoRenderContext::setTransform(NRMatrix const *transform)
819
g_assert( _is_valid );
821
cairo_matrix_t matrix;
822
_initCairoMatrix(&matrix, transform);
823
cairo_set_matrix(_cr, &matrix);
824
_state->transform = *transform;
828
CairoRenderContext::getTransform(NRMatrix *copy) const
830
g_assert( _is_valid );
833
cairo_get_matrix(_cr, &ctm);
843
CairoRenderContext::getParentTransform(NRMatrix *copy) const
845
g_assert( _is_valid );
847
CairoRenderState *parent_state = getParentState();
848
memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
852
CairoRenderContext::pushState(void)
854
g_assert( _is_valid );
858
CairoRenderState *new_state = _createState();
859
// copy current state's transform
860
new_state->transform = _state->transform;
861
_state_stack = g_slist_prepend(_state_stack, new_state);
866
CairoRenderContext::popState(void)
868
g_assert( _is_valid );
872
g_free(_state_stack->data);
873
_state_stack = g_slist_remove_link(_state_stack, _state_stack);
874
_state = (CairoRenderState*)_state_stack->data;
876
g_assert( g_slist_length(_state_stack) > 0 );
879
static bool pattern_hasItemChildren (SPPattern *pat)
881
for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
882
if (SP_IS_ITEM (child)) {
890
CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
892
g_assert( SP_IS_PATTERN(paintserver) );
894
SPPattern *pat = SP_PATTERN (paintserver);
896
NRMatrix ps2user, pcs2dev;
897
nr_matrix_set_identity(&ps2user);
898
nr_matrix_set_identity(&pcs2dev);
900
double x = pattern_x(pat);
901
double y = pattern_y(pat);
902
double width = pattern_width(pat);
903
double height = pattern_height(pat);
904
double bbox_width_scaler;
905
double bbox_height_scaler;
907
TRACE(("%f x %f pattern\n", width, height));
909
if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
910
//NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
911
bbox_width_scaler = pbox->x1 - pbox->x0;
912
bbox_height_scaler = pbox->y1 - pbox->y0;
913
ps2user[4] = x * bbox_width_scaler + pbox->x0;
914
ps2user[5] = y * bbox_height_scaler + pbox->y0;
916
bbox_width_scaler = 1.0;
917
bbox_height_scaler = 1.0;
922
// apply pattern transformation
923
NRMatrix pattern_transform(pattern_patternTransform(pat));
924
nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
926
// create pattern contents coordinate system
927
if (pat->viewBox_set) {
928
NRRect *view_box = pattern_viewBox(pat);
931
double view_width, view_height;
934
w = width * bbox_width_scaler;
935
h = height * bbox_height_scaler;
937
view_width = view_box->x1 - view_box->x0;
938
view_height = view_box->y1 - view_box->y0;
940
//calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
941
pcs2dev[0] = w / view_width;
942
pcs2dev[3] = h / view_height;
943
pcs2dev[4] = x - view_box->x0 * pcs2dev[0];
944
pcs2dev[5] = y - view_box->y0 * pcs2dev[3];
945
} else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
946
pcs2dev[0] = pbox->x1 - pbox->x0;
947
pcs2dev[3] = pbox->y1 - pbox->y0;
950
// calculate the size of the surface which has to be created
951
// the scaling needs to be taken into account in the ctm after the pattern transformation
953
nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
954
double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
955
double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
957
if (_vector_based_target) {
958
// eliminate PT_PER_PX mul from these
959
width_scaler *= 1.25;
960
height_scaler *= 1.25;
962
double surface_width = ceil(bbox_width_scaler * width_scaler * width);
963
double surface_height = ceil(bbox_height_scaler * height_scaler * height);
964
TRACE(("surface size: %f x %f\n", surface_width, surface_height));
965
// create new rendering context
966
CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
968
// adjust the size of the painted pattern to fit exactly the created surface
969
// this has to be done because of the rounding to obtain an integer pattern surface width/height
970
double scale_width = surface_width / (bbox_width_scaler * width);
971
double scale_height = surface_height / (bbox_height_scaler * height);
972
if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
973
TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
975
nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
976
nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
978
nr_matrix_set_scale(&scale, scale_width, scale_height);
979
nr_matrix_multiply(&ps2user, &ps2user, &scale);
982
pattern_ctx->setTransform(&pcs2dev);
983
pattern_ctx->pushState();
985
// create arena and group
986
NRArena *arena = NRArena::create();
987
unsigned dkey = sp_item_display_key_new(1);
989
// show items and render them
990
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
991
if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
992
for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
993
if (SP_IS_ITEM (child)) {
994
sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
995
_renderer->renderItem(pattern_ctx, SP_ITEM (child));
998
break; // do not go further up the chain if children are found
1002
pattern_ctx->popState();
1004
// setup a cairo_pattern_t
1005
cairo_surface_t *pattern_surface = pattern_ctx->getSurface();
1006
TEST(pattern_ctx->saveAsPng("pattern.png"));
1007
cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface);
1008
cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT);
1010
// set pattern transformation
1011
cairo_matrix_t pattern_matrix;
1012
_initCairoMatrix(&pattern_matrix, &ps2user);
1013
cairo_matrix_invert(&pattern_matrix);
1014
cairo_pattern_set_matrix(result, &pattern_matrix);
1019
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
1020
if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
1021
for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
1022
if (SP_IS_ITEM (child)) {
1023
sp_item_invoke_hide (SP_ITEM (child), dkey);
1026
break; // do not go further up the chain if children are found
1034
CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
1035
NRRect const *pbox, float alpha)
1037
cairo_pattern_t *pattern = NULL;
1038
bool apply_bbox2user = FALSE;
1040
if (SP_IS_LINEARGRADIENT (paintserver)) {
1042
SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
1044
sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
1046
NR::Point p1 (lg->x1.computed, lg->y1.computed);
1047
NR::Point p2 (lg->x2.computed, lg->y2.computed);
1048
if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1049
// convert to userspace
1050
NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1055
// create linear gradient pattern
1056
pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
1059
for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
1061
sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
1062
cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
1064
} else if (SP_IS_RADIALGRADIENT (paintserver)) {
1066
SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
1068
sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
1070
NR::Point c (rg->cx.computed, rg->cy.computed);
1071
NR::Point f (rg->fx.computed, rg->fy.computed);
1072
double r = rg->r.computed;
1073
if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
1074
apply_bbox2user = true;
1076
// create radial gradient pattern
1077
pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
1080
for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
1082
sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
1083
cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
1085
} else if (SP_IS_PATTERN (paintserver)) {
1087
pattern = _createPatternPainter(paintserver, pbox);
1092
if (pattern && SP_IS_GRADIENT (paintserver)) {
1093
SPGradient *g = SP_GRADIENT(paintserver);
1096
SPGradientSpread spread = sp_gradient_get_spread(g);
1098
case SP_GRADIENT_SPREAD_REPEAT: {
1099
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
1102
case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
1103
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
1106
case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
1107
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
1111
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
1116
cairo_matrix_t pattern_matrix;
1117
if (g->gradientTransform_set) {
1118
// apply gradient transformation
1119
cairo_matrix_init(&pattern_matrix,
1120
g->gradientTransform[0], g->gradientTransform[1],
1121
g->gradientTransform[2], g->gradientTransform[3],
1122
g->gradientTransform[4], g->gradientTransform[5]);
1124
cairo_matrix_init_identity (&pattern_matrix);
1127
if (apply_bbox2user) {
1128
// convert to userspace
1129
cairo_matrix_t bbox2user;
1130
cairo_matrix_init (&bbox2user, pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
1131
cairo_matrix_multiply (&pattern_matrix, &bbox2user, &pattern_matrix);
1133
cairo_matrix_invert(&pattern_matrix); // because Cairo expects a userspace->patternspace matrix
1134
cairo_pattern_set_matrix(pattern, &pattern_matrix);
1141
CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
1143
g_return_if_fail( style->fill.isColor()
1144
|| style->fill.isPaintserver() );
1146
float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
1147
if (_state->merge_opacity) {
1148
alpha *= _state->opacity;
1149
TRACE(("merged op=%f\n", alpha));
1152
if (style->fill.isColor()) {
1154
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
1156
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1158
g_assert( style->fill.isPaintserver()
1159
|| SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
1160
|| SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
1162
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
1165
cairo_set_source(_cr, pattern);
1166
cairo_pattern_destroy(pattern);
1172
CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
1174
float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
1175
if (_state->merge_opacity)
1176
alpha *= _state->opacity;
1178
if (style->stroke.isColor()) {
1180
sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
1182
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
1184
g_assert( style->fill.isPaintserver()
1185
|| SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
1186
|| SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
1188
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
1191
cairo_set_source(_cr, pattern);
1192
cairo_pattern_destroy(pattern);
1196
if (style->stroke_dash.n_dash &&
1197
style->stroke_dash.dash )
1199
cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
1201
cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing
1204
cairo_set_line_width(_cr, style->stroke_width.computed);
1206
// set line join type
1207
cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
1208
switch (style->stroke_linejoin.computed) {
1209
case SP_STROKE_LINEJOIN_MITER:
1210
join = CAIRO_LINE_JOIN_MITER;
1212
case SP_STROKE_LINEJOIN_ROUND:
1213
join = CAIRO_LINE_JOIN_ROUND;
1215
case SP_STROKE_LINEJOIN_BEVEL:
1216
join = CAIRO_LINE_JOIN_BEVEL;
1219
cairo_set_line_join(_cr, join);
1221
// set line cap type
1222
cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
1223
switch (style->stroke_linecap.computed) {
1224
case SP_STROKE_LINECAP_BUTT:
1225
cap = CAIRO_LINE_CAP_BUTT;
1227
case SP_STROKE_LINECAP_ROUND:
1228
cap = CAIRO_LINE_CAP_ROUND;
1230
case SP_STROKE_LINECAP_SQUARE:
1231
cap = CAIRO_LINE_CAP_SQUARE;
1234
cairo_set_line_cap(_cr, cap);
1235
cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
1239
CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
1241
g_assert( _is_valid );
1243
if (_render_mode == RENDER_MODE_CLIP) {
1244
if (_clip_mode == CLIP_MODE_PATH) {
1245
addClipPath(bpath->path, &style->fill_rule);
1247
setBpath(bpath->path);
1248
if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1249
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1251
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1254
TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
1259
if (style->fill.isNone() && style->stroke.isNone())
1262
bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
1263
( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
1270
if (!style->fill.isNone()) {
1271
_setFillStyle(style, pbox);
1272
setBpath(bpath->path);
1274
if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1275
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1277
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1280
if (style->stroke.isNone())
1283
cairo_fill_preserve(_cr);
1286
if (!style->stroke.isNone()) {
1287
_setStrokeStyle(style, pbox);
1288
if (style->fill.isNone())
1289
setBpath(bpath->path);
1303
CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
1304
NRMatrix const *image_transform, SPStyle const *style)
1306
g_assert( _is_valid );
1308
if (_render_mode == RENDER_MODE_CLIP)
1311
guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
1316
if (_state->merge_opacity)
1317
opacity = _state->opacity;
1321
// make a copy of the original pixbuf with premultiplied alpha
1322
// if we pass the original pixbuf it will get messed up
1323
for (unsigned i = 0; i < h; i++) {
1324
for (unsigned j = 0; j < w; j++) {
1325
guchar const *src = px + i * rs + j * 4;
1326
guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
1327
guchar r, g, b, alpha_dst;
1329
// calculate opacity-modified alpha
1331
if (opacity != 1.0 && _vector_based_target)
1332
alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
1334
// premul alpha (needed because this will be undone by cairo-pdf)
1335
r = src[0]*alpha_dst/255;
1336
g = src[1]*alpha_dst/255;
1337
b = src[2]*alpha_dst/255;
1339
*dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
1343
cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
1344
if (cairo_surface_status(image_surface)) {
1345
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
1349
// setup automatic freeing of the image data when destroying the surface
1350
static cairo_user_data_key_t key;
1351
cairo_surface_set_user_data(image_surface, &key, px_rgba, (cairo_destroy_func_t)g_free);
1355
// scaling by width & height is not needed because it will be done by Cairo
1356
if (image_transform)
1357
transform(image_transform);
1359
cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
1361
// set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
1362
if (_vector_based_target) {
1363
cairo_new_path(_cr);
1364
cairo_rectangle(_cr, 0, 0, w, h);
1368
if (_vector_based_target)
1371
cairo_paint_with_alpha(_cr, opacity);
1375
cairo_surface_destroy(image_surface);
1380
#define GLYPH_ARRAY_SIZE 64
1383
CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
1385
cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
1386
cairo_glyph_t *glyphs = glyph_array;
1387
unsigned int num_glyphs = glyphtext.size();
1388
if (num_glyphs > GLYPH_ARRAY_SIZE)
1389
glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
1391
unsigned int num_invalid_glyphs = 0;
1393
for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
1394
// skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
1395
// or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
1396
if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
1397
TRACE(("INVALID GLYPH found\n"));
1398
num_invalid_glyphs++;
1401
glyphs[i - num_invalid_glyphs].index = it_info->index;
1402
glyphs[i - num_invalid_glyphs].x = it_info->x;
1403
glyphs[i - num_invalid_glyphs].y = it_info->y;
1407
if (is_stroke || _is_texttopath)
1408
cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
1410
cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
1412
if (num_glyphs > GLYPH_ARRAY_SIZE)
1415
return num_glyphs - num_invalid_glyphs;
1419
CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
1420
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
1422
// create a cairo_font_face from PangoFont
1423
double size = style->font_size.computed;
1424
PangoFcFont *fc_font = PANGO_FC_FONT(font);
1425
FcPattern *fc_pattern = fc_font->font_pattern;
1429
#ifdef CAIRO_HAS_FT_FONT
1430
cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
1431
cairo_set_font_face(_cr, font_face);
1433
if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1436
// set the given font matrix
1437
cairo_matrix_t matrix;
1438
_initCairoMatrix(&matrix, font_matrix);
1439
cairo_set_font_matrix(_cr, &matrix);
1441
if (_render_mode == RENDER_MODE_CLIP) {
1442
if (_clip_mode == CLIP_MODE_MASK) {
1443
if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
1444
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
1446
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
1448
_showGlyphs(_cr, font, glyphtext, FALSE);
1450
// just add the glyph paths to the current context
1451
_showGlyphs(_cr, font, glyphtext, TRUE);
1455
if (style->fill.isColor() || style->fill.isPaintserver()) {
1457
_setFillStyle(style, NULL);
1459
_showGlyphs(_cr, font, glyphtext, FALSE);
1462
if (style->stroke.isColor() || style->stroke.isPaintserver()) {
1464
_setStrokeStyle(style, NULL);
1467
_showGlyphs(_cr, font, glyphtext, TRUE);
1474
cairo_font_face_destroy(font_face);
1485
/* Helper functions */
1488
CairoRenderContext::addBpath(NArtBpath const *bp)
1490
bool closed = false;
1491
while (bp->code != NR_END) {
1495
cairo_close_path(_cr);
1498
cairo_move_to(_cr, bp->x3, bp->y3);
1500
case NR_MOVETO_OPEN:
1502
cairo_close_path(_cr);
1505
cairo_move_to(_cr, bp->x3, bp->y3);
1508
cairo_line_to(_cr, bp->x3, bp->y3);
1511
cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1519
cairo_close_path(_cr);
1524
CairoRenderContext::setBpath(NArtBpath const *bp)
1526
cairo_new_path(_cr);
1532
CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
1534
cairo_matrix_t matrix;
1536
cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
1537
cairo_transform(cr, &matrix);
1541
CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
1543
matrix->xx = (*transform)[0];
1544
matrix->yx = (*transform)[1];
1545
matrix->xy = (*transform)[2];
1546
matrix->yy = (*transform)[3];
1547
matrix->x0 = (*transform)[4];
1548
matrix->y0 = (*transform)[5];
1552
CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
1554
_concatTransform(cr, (*transform)[0], (*transform)[1],
1555
(*transform)[2], (*transform)[3],
1556
(*transform)[4], (*transform)[5]);
1559
static cairo_status_t
1560
_write_callback(void *closure, const unsigned char *data, unsigned int length)
1563
FILE *file = (FILE*)closure;
1565
written = fwrite (data, 1, length, file);
1567
if (written == length)
1568
return CAIRO_STATUS_SUCCESS;
1570
return CAIRO_STATUS_WRITE_ERROR;
1573
#include "clear-n_.h"
1575
} /* namespace Internal */
1576
} /* namespace Extension */
1577
} /* namespace Inkscape */
1582
/* End of GNU GPL code */
1588
c-file-style:"stroustrup"
1589
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1590
indent-tabs-mode:nil
1594
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :