~ubuntu-branches/debian/experimental/inkscape/experimental

« back to all changes in this revision

Viewing changes to src/extension/internal/cairo-render-context.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-09-09 23:29:02 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20080909232902-c50iujhk1w79u8e7
Tags: 0.46-2.1
* Non-maintainer upload.
* Add upstream patch fixing a crash in the open dialog
  in the zh_CN.utf8 locale. Closes: #487623.
  Thanks to Luca Bruno for the patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_CAIRO_RENDER_CONTEXT_C__
 
2
 
 
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
 */
 
14
 
 
15
#ifdef HAVE_CONFIG_H
 
16
# include "config.h"
 
17
#endif
 
18
 
 
19
#ifndef PANGO_ENABLE_BACKEND
 
20
#define PANGO_ENABLE_BACKEND
 
21
#endif
 
22
 
 
23
#ifndef PANGO_ENABLE_ENGINE
 
24
#define PANGO_ENABLE_ENGINE
 
25
#endif
 
26
 
 
27
 
 
28
#include <signal.h>
 
29
#include <errno.h>
 
30
 
 
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>
 
36
 
 
37
#include <glib/gmem.h>
 
38
 
 
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"
 
45
#include "sp-item.h"
 
46
#include "sp-item-group.h"
 
47
#include "style.h"
 
48
#include "sp-linear-gradient.h"
 
49
#include "sp-radial-gradient.h"
 
50
#include "sp-pattern.h"
 
51
#include "sp-mask.h"
 
52
#include "sp-clippath.h"
 
53
 
 
54
#include <unit-constants.h>
 
55
 
 
56
#include "cairo-render-context.h"
 
57
#include "cairo-renderer.h"
 
58
#include "extension/system.h"
 
59
 
 
60
#include "io/sys.h"
 
61
 
 
62
#include <cairo.h>
 
63
 
 
64
// include support for only the compiled-in surface types
 
65
#ifdef CAIRO_HAS_PDF_SURFACE
 
66
#include <cairo-pdf.h>
 
67
#endif
 
68
#ifdef CAIRO_HAS_PS_SURFACE
 
69
#include <cairo-ps.h>
 
70
#endif
 
71
 
 
72
 
 
73
#ifdef CAIRO_HAS_FT_FONT
 
74
#include <cairo-ft.h>
 
75
#endif
 
76
 
 
77
#include <pango/pangofc-fontmap.h>
 
78
 
 
79
//#define TRACE(_args) g_printf _args
 
80
#define TRACE(_args)
 
81
//#define TEST(_args) _args
 
82
#define TEST(_args)
 
83
 
 
84
// FIXME: expose these from sp-clippath/mask.cpp
 
85
struct SPClipPathView {
 
86
    SPClipPathView *next;
 
87
    unsigned int key;
 
88
    NRArenaItem *arenaitem;
 
89
    NRRect bbox;
 
90
};
 
91
 
 
92
struct SPMaskView {
 
93
    SPMaskView *next;
 
94
    unsigned int key;
 
95
    NRArenaItem *arenaitem;
 
96
    NRRect bbox;
 
97
};
 
98
 
 
99
namespace Inkscape {
 
100
namespace Extension {
 
101
namespace Internal {
 
102
 
 
103
static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
 
104
 
 
105
CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
 
106
    _dpi(72),
 
107
    _pdf_level(0),
 
108
    _ps_level(1),
 
109
    _is_texttopath(FALSE),
 
110
    _is_filtertobitmap(FALSE),
 
111
    _bitmapresolution(72),
 
112
    _stream(NULL),
 
113
    _is_valid(FALSE),
 
114
    _vector_based_target(FALSE),
 
115
    _cr(NULL),
 
116
    _surface(NULL),
 
117
    _target(CAIRO_SURFACE_TYPE_IMAGE),
 
118
    _target_format(CAIRO_FORMAT_ARGB32),
 
119
    _layout(NULL),
 
120
    _state(NULL),
 
121
    _renderer(parent),
 
122
    _render_mode(RENDER_MODE_NORMAL),
 
123
    _clip_mode(CLIP_MODE_MASK)
 
124
{}
 
125
 
 
126
CairoRenderContext::~CairoRenderContext(void)
 
127
{
 
128
    if (_cr) cairo_destroy(_cr);
 
129
    if (_surface) cairo_surface_destroy(_surface);
 
130
    if (_layout) g_object_unref(_layout);
 
131
}
 
132
 
 
133
CairoRenderer*
 
134
CairoRenderContext::getRenderer(void) const
 
135
{
 
136
    return _renderer;
 
137
}
 
138
 
 
139
CairoRenderState*
 
140
CairoRenderContext::getCurrentState(void) const
 
141
{
 
142
    return _state;
 
143
}
 
144
 
 
145
CairoRenderState*
 
146
CairoRenderContext::getParentState(void) const
 
147
{
 
148
    // if this is the root node just return it
 
149
    if (g_slist_length(_state_stack) == 1) {
 
150
        return _state;
 
151
    } else {
 
152
        return (CairoRenderState *)g_slist_nth_data(_state_stack, 1);
 
153
    }
 
154
}
 
155
 
 
156
void
 
157
CairoRenderContext::setStateForStyle(SPStyle const *style)
 
158
{
 
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;
 
163
 
 
164
    if (style->fill.isPaintserver() || style->stroke.isPaintserver())
 
165
        _state->merge_opacity = FALSE;
 
166
 
 
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;
 
172
}
 
173
 
 
174
/**
 
175
 * \brief Creates a new render context which will be compatible with the given context's Cairo surface
 
176
 *
 
177
 * \param width     width of the surface to be created
 
178
 * \param height    height of the surface to be created
 
179
 */
 
180
CairoRenderContext*
 
181
CairoRenderContext::cloneMe(double width, double height) const
 
182
{
 
183
    g_assert( _is_valid );
 
184
    g_assert( width > 0.0 && height > 0.0 );
 
185
 
 
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;
 
192
 
 
193
    return new_context;
 
194
}
 
195
 
 
196
CairoRenderContext*
 
197
CairoRenderContext::cloneMe(void) const
 
198
{
 
199
    g_assert( _is_valid );
 
200
 
 
201
    return cloneMe(_width, _height);
 
202
}
 
203
 
 
204
bool
 
205
CairoRenderContext::setImageTarget(cairo_format_t format)
 
206
{
 
207
    // format cannot be set on an already initialized surface
 
208
    if (_is_valid)
 
209
        return false;
 
210
 
 
211
    switch (format) {
 
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;
 
218
            return true;
 
219
            break;
 
220
        default:
 
221
            break;
 
222
    }
 
223
 
 
224
    return false;
 
225
}
 
226
 
 
227
bool
 
228
CairoRenderContext::setPdfTarget(gchar const *utf8_fn)
 
229
{
 
230
#ifndef CAIRO_HAS_PDF_SURFACE
 
231
    return false;
 
232
#else
 
233
    _target = CAIRO_SURFACE_TYPE_PDF;
 
234
    _vector_based_target = TRUE;
 
235
#endif
 
236
 
 
237
    FILE *osf = NULL;
 
238
    FILE *osp = NULL;
 
239
 
 
240
    gsize bytesRead = 0;
 
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;
 
246
 
 
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
 
250
    * return code.
 
251
    */
 
252
    if (fn != NULL) {
 
253
        if (*fn == '|') {
 
254
            fn += 1;
 
255
            while (isspace(*fn)) fn += 1;
 
256
#ifndef WIN32
 
257
            osp = popen(fn, "w");
 
258
#else
 
259
            osp = _popen(fn, "w");
 
260
#endif
 
261
            if (!osp) {
 
262
                fprintf(stderr, "inkscape: popen(%s): %s\n",
 
263
                        fn, strerror(errno));
 
264
                return false;
 
265
            }
 
266
            _stream = osp;
 
267
        } else if (*fn == '>') {
 
268
            fn += 1;
 
269
            while (isspace(*fn)) fn += 1;
 
270
            Inkscape::IO::dump_fopen_call(fn, "K");
 
271
            osf = Inkscape::IO::fopen_utf8name(fn, "w+");
 
272
            if (!osf) {
 
273
                fprintf(stderr, "inkscape: fopen(%s): %s\n",
 
274
                        fn, strerror(errno));
 
275
                return false;
 
276
            }
 
277
            _stream = osf;
 
278
        } else {
 
279
            /* put cwd stuff in here */
 
280
            gchar *qn = ( *fn
 
281
                    ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
 
282
                : g_strdup("lpr") );
 
283
#ifndef WIN32
 
284
            osp = popen(qn, "w");
 
285
#else
 
286
            osp = _popen(qn, "w");
 
287
#endif
 
288
            if (!osp) {
 
289
                fprintf(stderr, "inkscape: popen(%s): %s\n",
 
290
                        qn, strerror(errno));
 
291
                return false;
 
292
            }
 
293
            g_free(qn);
 
294
            _stream = osp;
 
295
        }
 
296
    }
 
297
 
 
298
    g_free(local_fn);
 
299
 
 
300
    if (_stream) {
 
301
        /* fixme: this is kinda icky */
 
302
#if !defined(_WIN32) && !defined(__WIN32__)
 
303
        (void) signal(SIGPIPE, SIG_IGN);
 
304
#endif
 
305
    }
 
306
 
 
307
    return true;
 
308
}
 
309
 
 
310
bool
 
311
CairoRenderContext::setPsTarget(gchar const *utf8_fn)
 
312
{
 
313
#ifndef CAIRO_HAS_PS_SURFACE
 
314
    return false;
 
315
#else
 
316
    _target = CAIRO_SURFACE_TYPE_PS;
 
317
    _vector_based_target = TRUE;
 
318
#endif
 
319
 
 
320
    FILE *osf = NULL;
 
321
    FILE *osp = NULL;
 
322
 
 
323
    gsize bytesRead = 0;
 
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;
 
329
 
 
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
 
333
    * return code.
 
334
    */
 
335
    if (fn != NULL) {
 
336
        if (*fn == '|') {
 
337
            fn += 1;
 
338
            while (isspace(*fn)) fn += 1;
 
339
#ifndef WIN32
 
340
            osp = popen(fn, "w");
 
341
#else
 
342
            osp = _popen(fn, "w");
 
343
#endif
 
344
            if (!osp) {
 
345
                fprintf(stderr, "inkscape: popen(%s): %s\n",
 
346
                        fn, strerror(errno));
 
347
                return false;
 
348
            }
 
349
            _stream = osp;
 
350
        } else if (*fn == '>') {
 
351
            fn += 1;
 
352
            while (isspace(*fn)) fn += 1;
 
353
            Inkscape::IO::dump_fopen_call(fn, "K");
 
354
            osf = Inkscape::IO::fopen_utf8name(fn, "w+");
 
355
            if (!osf) {
 
356
                fprintf(stderr, "inkscape: fopen(%s): %s\n",
 
357
                        fn, strerror(errno));
 
358
                return false;
 
359
            }
 
360
            _stream = osf;
 
361
        } else {
 
362
            /* put cwd stuff in here */
 
363
            gchar *qn = ( *fn
 
364
                    ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
 
365
                : g_strdup("lpr") );
 
366
#ifndef WIN32
 
367
            osp = popen(qn, "w");
 
368
#else
 
369
            osp = _popen(qn, "w");
 
370
#endif
 
371
            if (!osp) {
 
372
                fprintf(stderr, "inkscape: popen(%s): %s\n",
 
373
                        qn, strerror(errno));
 
374
                return false;
 
375
            }
 
376
            g_free(qn);
 
377
            _stream = osp;
 
378
        }
 
379
    }
 
380
 
 
381
    g_free(local_fn);
 
382
 
 
383
    if (_stream) {
 
384
        /* fixme: this is kinda icky */
 
385
#if !defined(_WIN32) && !defined(__WIN32__)
 
386
        (void) signal(SIGPIPE, SIG_IGN);
 
387
#endif
 
388
    }
 
389
 
 
390
    return true;
 
391
}
 
392
 
 
393
void CairoRenderContext::setPSLevel(unsigned int level)
 
394
{
 
395
    _ps_level = level;
 
396
}
 
397
 
 
398
unsigned int CairoRenderContext::getPSLevel(void)
 
399
{
 
400
    return _ps_level;
 
401
}
 
402
 
 
403
void CairoRenderContext::setPDFLevel(unsigned int level)
 
404
{
 
405
    _pdf_level = level;
 
406
}
 
407
 
 
408
void CairoRenderContext::setTextToPath(bool texttopath)
 
409
{
 
410
    _is_texttopath = texttopath;
 
411
}
 
412
 
 
413
void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
 
414
{
 
415
    _is_filtertobitmap = filtertobitmap;
 
416
}
 
417
 
 
418
bool CairoRenderContext::getFilterToBitmap(void)
 
419
{
 
420
    return _is_filtertobitmap;
 
421
}
 
422
 
 
423
void CairoRenderContext::setBitmapResolution(int resolution)
 
424
{
 
425
    _bitmapresolution = resolution;
 
426
}
 
427
 
 
428
int CairoRenderContext::getBitmapResolution(void)
 
429
{
 
430
    return _bitmapresolution;
 
431
}
 
432
 
 
433
cairo_surface_t*
 
434
CairoRenderContext::getSurface(void)
 
435
{
 
436
    g_assert( _is_valid );
 
437
 
 
438
    return _surface;
 
439
}
 
440
 
 
441
bool
 
442
CairoRenderContext::saveAsPng(const char *file_name)
 
443
{
 
444
    cairo_status_t status = cairo_surface_write_to_png(_surface, file_name);
 
445
    if (status)
 
446
        return false;
 
447
    else
 
448
        return true;
 
449
}
 
450
 
 
451
void
 
452
CairoRenderContext::setRenderMode(CairoRenderMode mode)
 
453
{
 
454
    switch (mode) {
 
455
        case RENDER_MODE_NORMAL:
 
456
        case RENDER_MODE_CLIP:
 
457
            _render_mode = mode;
 
458
            break;
 
459
        default:
 
460
            _render_mode = RENDER_MODE_NORMAL;
 
461
            break;
 
462
    }
 
463
}
 
464
 
 
465
CairoRenderContext::CairoRenderMode
 
466
CairoRenderContext::getRenderMode(void) const
 
467
{
 
468
    return _render_mode;
 
469
}
 
470
 
 
471
void
 
472
CairoRenderContext::setClipMode(CairoClipMode mode)
 
473
{
 
474
    switch (mode) {
 
475
        case CLIP_MODE_PATH:
 
476
        case CLIP_MODE_MASK:
 
477
            _clip_mode = mode;
 
478
            break;
 
479
        default:
 
480
            _clip_mode = CLIP_MODE_PATH;
 
481
            break;
 
482
    }
 
483
}
 
484
 
 
485
CairoRenderContext::CairoClipMode
 
486
CairoRenderContext::getClipMode(void) const
 
487
{
 
488
    return _clip_mode;
 
489
}
 
490
 
 
491
CairoRenderState*
 
492
CairoRenderContext::_createState(void)
 
493
{
 
494
    CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
 
495
    g_assert( state != NULL );
 
496
 
 
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;
 
504
    state->mask = NULL;
 
505
 
 
506
    return state;
 
507
}
 
508
 
 
509
void
 
510
CairoRenderContext::pushLayer(void)
 
511
{
 
512
    g_assert( _is_valid );
 
513
 
 
514
    TRACE(("--pushLayer\n"));
 
515
    cairo_push_group(_cr);
 
516
 
 
517
    // clear buffer
 
518
    if (!_vector_based_target) {
 
519
        cairo_save(_cr);
 
520
        cairo_set_operator(_cr, CAIRO_OPERATOR_CLEAR);
 
521
        cairo_paint(_cr);
 
522
        cairo_restore(_cr);
 
523
    }
 
524
}
 
525
 
 
526
void
 
527
CairoRenderContext::popLayer(void)
 
528
{
 
529
    g_assert( _is_valid );
 
530
 
 
531
    float opacity = _state->opacity;
 
532
    TRACE(("--popLayer w/ %f\n", opacity));
 
533
 
 
534
    // apply clipPath or mask if present
 
535
    SPClipPath *clip_path = _state->clip_path;
 
536
    SPMask *mask = _state->mask;
 
537
    if (clip_path || mask) {
 
538
 
 
539
        CairoRenderContext *clip_ctx = 0;
 
540
        cairo_surface_t *clip_mask = 0;
 
541
 
 
542
        if (clip_path) {
 
543
            if (_render_mode == RENDER_MODE_CLIP)
 
544
                mask = NULL;    // disable mask when performing nested clipping
 
545
 
 
546
            if (_vector_based_target) {
 
547
                setClipMode(CLIP_MODE_PATH);
 
548
                if (!mask) {
 
549
                    cairo_pop_group_to_source(_cr);
 
550
                    _renderer->applyClipPath(this, clip_path);
 
551
                    if (opacity == 1.0)
 
552
                        cairo_paint(_cr);
 
553
                    else
 
554
                        cairo_paint_with_alpha(_cr, opacity);
 
555
 
 
556
                } else {
 
557
                    // the clipPath will be applied before masking
 
558
                }
 
559
            } else {
 
560
 
 
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);
 
568
                    return;
 
569
                }
 
570
 
 
571
                // clear buffer
 
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);
 
576
 
 
577
                // if a mask won't be applied set opacity too
 
578
                if (!mask)
 
579
                    cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
 
580
                else
 
581
                    cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
 
582
 
 
583
                // copy over the correct CTM
 
584
                if (_state->parent_has_userspace)
 
585
                    clip_ctx->setTransform(&getParentState()->transform);
 
586
                else
 
587
                    clip_ctx->setTransform(&_state->transform);
 
588
 
 
589
                // apply the clip path
 
590
                clip_ctx->pushState();
 
591
                _renderer->applyClipPath(clip_ctx, clip_path);
 
592
                clip_ctx->popState();
 
593
 
 
594
                clip_mask = clip_ctx->getSurface();
 
595
                TEST(clip_ctx->saveAsPng("clip_mask.png"));
 
596
 
 
597
                if (!mask) {
 
598
                    cairo_pop_group_to_source(_cr);
 
599
                    cairo_mask_surface(_cr, clip_mask, 0, 0);
 
600
                    _renderer->destroyContext(clip_ctx);
 
601
                }
 
602
            }
 
603
        }
 
604
 
 
605
        if (mask) {
 
606
            // create rendering context for mask
 
607
            CairoRenderContext *mask_ctx = _renderer->createContext();
 
608
            mask_ctx->setupSurface(_width, _height);
 
609
 
 
610
            // set rendering mode to normal
 
611
            setRenderMode(RENDER_MODE_NORMAL);
 
612
 
 
613
            // copy the correct CTM to mask context
 
614
            if (_state->parent_has_userspace)
 
615
                mask_ctx->setTransform(&getParentState()->transform);
 
616
            else
 
617
                mask_ctx->setTransform(&_state->transform);
 
618
 
 
619
            // render mask contents to mask_ctx
 
620
            _renderer->applyMask(mask_ctx, mask);
 
621
 
 
622
            TEST(mask_ctx->saveAsPng("mask.png"));
 
623
 
 
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);
 
628
            }
 
629
 
 
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);
 
635
 
 
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) *
 
647
                                int_opacity);
 
648
                    }
 
649
                }
 
650
            }
 
651
 
 
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);
 
656
            }
 
657
            // apply the mask onto the layer
 
658
            cairo_mask_surface(_cr, mask_image, 0, 0);
 
659
            _renderer->destroyContext(mask_ctx);
 
660
        }
 
661
    } else {
 
662
        cairo_pop_group_to_source(_cr);
 
663
        if (opacity == 1.0)
 
664
            cairo_paint(_cr);
 
665
        else
 
666
            cairo_paint_with_alpha(_cr, opacity);
 
667
    }
 
668
}
 
669
 
 
670
void
 
671
CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
 
672
{
 
673
    g_assert( _is_valid );
 
674
 
 
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);
 
679
    } else {
 
680
        cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
 
681
    }
 
682
    addBpath(bp);
 
683
}
 
684
 
 
685
void
 
686
CairoRenderContext::addClippingRect(double x, double y, double width, double height)
 
687
{
 
688
    g_assert( _is_valid );
 
689
 
 
690
    cairo_rectangle(_cr, x, y, width, height);
 
691
    cairo_clip(_cr);
 
692
}
 
693
 
 
694
bool
 
695
CairoRenderContext::setupSurface(double width, double height)
 
696
{
 
697
    // Is the surface already set up?
 
698
    if (_is_valid)
 
699
        return true;
 
700
 
 
701
    if (_vector_based_target && _stream == NULL)
 
702
        return false;
 
703
 
 
704
    cairo_surface_t *surface = NULL;
 
705
    switch (_target) {
 
706
        case CAIRO_SURFACE_TYPE_IMAGE:
 
707
            surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
 
708
            break;
 
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);
 
712
            break;
 
713
#endif
 
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)) {
 
719
                return FALSE;
 
720
            }
 
721
            cairo_ps_surface_restrict_to_level (surface, (cairo_ps_level_t)_ps_level);
 
722
#endif
 
723
            break;
 
724
#endif
 
725
        default:
 
726
            return false;
 
727
            break;
 
728
    }
 
729
 
 
730
    return _finishSurfaceSetup (surface);
 
731
}
 
732
 
 
733
bool
 
734
CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector)
 
735
{
 
736
    if (_is_valid || !surface)
 
737
        return false;
 
738
 
 
739
    _vector_based_target = is_vector;
 
740
    bool ret = _finishSurfaceSetup (surface);
 
741
    if (ret)
 
742
        cairo_surface_reference (surface);
 
743
    return ret;
 
744
}
 
745
 
 
746
bool
 
747
CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface)
 
748
{
 
749
    if(surface == NULL) {
 
750
        return FALSE;
 
751
    }
 
752
    if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
 
753
        return FALSE;
 
754
    }
 
755
 
 
756
    _cr = cairo_create(surface);
 
757
    _surface = surface;
 
758
 
 
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);
 
766
        cairo_fill(_cr);
 
767
    }
 
768
 
 
769
    _is_valid = TRUE;
 
770
 
 
771
    return true;
 
772
}
 
773
 
 
774
bool
 
775
CairoRenderContext::finish(void)
 
776
{
 
777
    g_assert( _is_valid );
 
778
 
 
779
    if (_vector_based_target)
 
780
        cairo_show_page(_cr);
 
781
 
 
782
    cairo_destroy(_cr);
 
783
    cairo_surface_destroy(_surface);
 
784
    _cr = NULL;
 
785
    _surface = NULL;
 
786
 
 
787
    if (_layout)
 
788
        g_object_unref(_layout);
 
789
 
 
790
    _is_valid = FALSE;
 
791
 
 
792
    if (_vector_based_target && _stream) {
 
793
        /* Flush stream to be sure. */
 
794
        (void) fflush(_stream);
 
795
 
 
796
        fclose(_stream);
 
797
        _stream = NULL;
 
798
    }
 
799
 
 
800
    return true;
 
801
}
 
802
 
 
803
void
 
804
CairoRenderContext::transform(NRMatrix const *transform)
 
805
{
 
806
    g_assert( _is_valid );
 
807
 
 
808
    cairo_matrix_t matrix;
 
809
    _initCairoMatrix(&matrix, transform);
 
810
    cairo_transform(_cr, &matrix);
 
811
 
 
812
    // store new CTM
 
813
    getTransform(&_state->transform);
 
814
}
 
815
 
 
816
void
 
817
CairoRenderContext::setTransform(NRMatrix const *transform)
 
818
{
 
819
    g_assert( _is_valid );
 
820
 
 
821
    cairo_matrix_t matrix;
 
822
    _initCairoMatrix(&matrix, transform);
 
823
    cairo_set_matrix(_cr, &matrix);
 
824
    _state->transform = *transform;
 
825
}
 
826
 
 
827
void
 
828
CairoRenderContext::getTransform(NRMatrix *copy) const
 
829
{
 
830
    g_assert( _is_valid );
 
831
 
 
832
    cairo_matrix_t ctm;
 
833
    cairo_get_matrix(_cr, &ctm);
 
834
    (*copy)[0] = ctm.xx;
 
835
    (*copy)[1] = ctm.yx;
 
836
    (*copy)[2] = ctm.xy;
 
837
    (*copy)[3] = ctm.yy;
 
838
    (*copy)[4] = ctm.x0;
 
839
    (*copy)[5] = ctm.y0;
 
840
}
 
841
 
 
842
void
 
843
CairoRenderContext::getParentTransform(NRMatrix *copy) const
 
844
{
 
845
    g_assert( _is_valid );
 
846
 
 
847
    CairoRenderState *parent_state = getParentState();
 
848
    memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
 
849
}
 
850
 
 
851
void
 
852
CairoRenderContext::pushState(void)
 
853
{
 
854
    g_assert( _is_valid );
 
855
 
 
856
    cairo_save(_cr);
 
857
 
 
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);
 
862
    _state = new_state;
 
863
}
 
864
 
 
865
void
 
866
CairoRenderContext::popState(void)
 
867
{
 
868
    g_assert( _is_valid );
 
869
 
 
870
    cairo_restore(_cr);
 
871
 
 
872
    g_free(_state_stack->data);
 
873
    _state_stack = g_slist_remove_link(_state_stack, _state_stack);
 
874
    _state = (CairoRenderState*)_state_stack->data;
 
875
 
 
876
    g_assert( g_slist_length(_state_stack) > 0 );
 
877
}
 
878
 
 
879
static bool pattern_hasItemChildren (SPPattern *pat)
 
880
{
 
881
    for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
882
        if (SP_IS_ITEM (child)) {
 
883
            return true;
 
884
        }
 
885
    }
 
886
    return false;
 
887
}
 
888
 
 
889
cairo_pattern_t*
 
890
CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
 
891
{
 
892
    g_assert( SP_IS_PATTERN(paintserver) );
 
893
 
 
894
    SPPattern *pat = SP_PATTERN (paintserver);
 
895
 
 
896
    NRMatrix ps2user, pcs2dev;
 
897
    nr_matrix_set_identity(&ps2user);
 
898
    nr_matrix_set_identity(&pcs2dev);
 
899
 
 
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;
 
906
 
 
907
    TRACE(("%f x %f pattern\n", width, height));
 
908
 
 
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;
 
915
    } else {
 
916
        bbox_width_scaler = 1.0;
 
917
        bbox_height_scaler = 1.0;
 
918
        ps2user[4] = x;
 
919
        ps2user[5] = y;
 
920
    }
 
921
 
 
922
    // apply pattern transformation
 
923
    NRMatrix pattern_transform(pattern_patternTransform(pat));
 
924
    nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
 
925
 
 
926
    // create pattern contents coordinate system
 
927
    if (pat->viewBox_set) {
 
928
        NRRect *view_box = pattern_viewBox(pat);
 
929
 
 
930
        double x, y, w, h;
 
931
        double view_width, view_height;
 
932
        x = 0;
 
933
        y = 0;
 
934
        w = width * bbox_width_scaler;
 
935
        h = height * bbox_height_scaler;
 
936
 
 
937
        view_width = view_box->x1 - view_box->x0;
 
938
        view_height = view_box->y1 - view_box->y0;
 
939
 
 
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;
 
948
    }
 
949
 
 
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
 
952
    NRMatrix temp;
 
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]);
 
956
 
 
957
    if (_vector_based_target) {
 
958
        // eliminate PT_PER_PX mul from these
 
959
        width_scaler *= 1.25;
 
960
        height_scaler *= 1.25;
 
961
    }
 
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);
 
967
 
 
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));
 
974
        NRMatrix scale;
 
975
        nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
 
976
        nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
 
977
 
 
978
        nr_matrix_set_scale(&scale, scale_width, scale_height);
 
979
        nr_matrix_multiply(&ps2user, &ps2user, &scale);
 
980
    }
 
981
 
 
982
    pattern_ctx->setTransform(&pcs2dev);
 
983
    pattern_ctx->pushState();
 
984
 
 
985
    // create arena and group
 
986
    NRArena *arena = NRArena::create();
 
987
    unsigned dkey = sp_item_display_key_new(1);
 
988
 
 
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));
 
996
                }
 
997
            }
 
998
            break; // do not go further up the chain if children are found
 
999
        }
 
1000
    }
 
1001
 
 
1002
    pattern_ctx->popState();
 
1003
 
 
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);
 
1009
 
 
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);
 
1015
 
 
1016
    delete pattern_ctx;
 
1017
 
 
1018
    // hide all items
 
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);
 
1024
                }
 
1025
            }
 
1026
            break; // do not go further up the chain if children are found
 
1027
        }
 
1028
    }
 
1029
 
 
1030
    return result;
 
1031
}
 
1032
 
 
1033
cairo_pattern_t*
 
1034
CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver,
 
1035
                                                 NRRect const *pbox, float alpha)
 
1036
{
 
1037
    cairo_pattern_t *pattern = NULL;
 
1038
    bool apply_bbox2user = FALSE;
 
1039
 
 
1040
    if (SP_IS_LINEARGRADIENT (paintserver)) {
 
1041
 
 
1042
            SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
 
1043
 
 
1044
            sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
 
1045
 
 
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);
 
1051
                p1 *= bbox2user;
 
1052
                p2 *= bbox2user;
 
1053
            }
 
1054
 
 
1055
            // create linear gradient pattern
 
1056
            pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
 
1057
 
 
1058
            // add stops
 
1059
            for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
 
1060
                float rgb[3];
 
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);
 
1063
            }
 
1064
    } else if (SP_IS_RADIALGRADIENT (paintserver)) {
 
1065
 
 
1066
        SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
 
1067
 
 
1068
        sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
 
1069
 
 
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;
 
1075
 
 
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);
 
1078
 
 
1079
        // add stops
 
1080
        for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
 
1081
            float rgb[3];
 
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);
 
1084
        }
 
1085
    } else if (SP_IS_PATTERN (paintserver)) {
 
1086
 
 
1087
        pattern = _createPatternPainter(paintserver, pbox);
 
1088
    } else {
 
1089
        return NULL;
 
1090
    }
 
1091
 
 
1092
    if (pattern && SP_IS_GRADIENT (paintserver)) {
 
1093
        SPGradient *g = SP_GRADIENT(paintserver);
 
1094
 
 
1095
        // set extend type
 
1096
        SPGradientSpread spread = sp_gradient_get_spread(g);
 
1097
        switch (spread) {
 
1098
            case SP_GRADIENT_SPREAD_REPEAT: {
 
1099
                cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 
1100
                break;
 
1101
            }
 
1102
            case SP_GRADIENT_SPREAD_REFLECT: {      // not supported by cairo-pdf yet
 
1103
                cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT);
 
1104
                break;
 
1105
            }
 
1106
            case SP_GRADIENT_SPREAD_PAD: {    // not supported by cairo-pdf yet
 
1107
                cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
 
1108
                break;
 
1109
            }
 
1110
            default: {
 
1111
                cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE);
 
1112
                break;
 
1113
            }
 
1114
        }
 
1115
 
 
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]);
 
1123
        } else {
 
1124
            cairo_matrix_init_identity (&pattern_matrix);
 
1125
        }
 
1126
 
 
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);
 
1132
        }
 
1133
        cairo_matrix_invert(&pattern_matrix);   // because Cairo expects a userspace->patternspace matrix
 
1134
        cairo_pattern_set_matrix(pattern, &pattern_matrix);
 
1135
    }
 
1136
 
 
1137
    return pattern;
 
1138
}
 
1139
 
 
1140
void
 
1141
CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
 
1142
{
 
1143
    g_return_if_fail( style->fill.isColor()
 
1144
                      || style->fill.isPaintserver() );
 
1145
 
 
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));
 
1150
    }
 
1151
 
 
1152
    if (style->fill.isColor()) {
 
1153
        float rgb[3];
 
1154
        sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
 
1155
 
 
1156
        cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
 
1157
    } else {
 
1158
        g_assert( style->fill.isPaintserver()
 
1159
                  || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
 
1160
                  || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) );
 
1161
 
 
1162
        cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha);
 
1163
 
 
1164
        if (pattern) {
 
1165
            cairo_set_source(_cr, pattern);
 
1166
            cairo_pattern_destroy(pattern);
 
1167
        }
 
1168
    }
 
1169
}
 
1170
 
 
1171
void
 
1172
CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
 
1173
{
 
1174
    float alpha = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
 
1175
    if (_state->merge_opacity)
 
1176
        alpha *= _state->opacity;
 
1177
 
 
1178
    if (style->stroke.isColor()) {
 
1179
        float rgb[3];
 
1180
        sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
 
1181
 
 
1182
        cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
 
1183
    } else {
 
1184
        g_assert( style->fill.isPaintserver()
 
1185
                  || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
 
1186
                  || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
 
1187
 
 
1188
        cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
 
1189
 
 
1190
        if (pattern) {
 
1191
            cairo_set_source(_cr, pattern);
 
1192
            cairo_pattern_destroy(pattern);
 
1193
        }
 
1194
    }
 
1195
 
 
1196
    if (style->stroke_dash.n_dash   &&
 
1197
        style->stroke_dash.dash       )
 
1198
    {
 
1199
        cairo_set_dash(_cr, style->stroke_dash.dash, style->stroke_dash.n_dash, style->stroke_dash.offset);
 
1200
    } else {
 
1201
        cairo_set_dash(_cr, NULL, 0, 0.0);      // disable dashing
 
1202
    }
 
1203
 
 
1204
    cairo_set_line_width(_cr, style->stroke_width.computed);
 
1205
 
 
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;
 
1211
            break;
 
1212
        case SP_STROKE_LINEJOIN_ROUND:
 
1213
            join = CAIRO_LINE_JOIN_ROUND;
 
1214
            break;
 
1215
        case SP_STROKE_LINEJOIN_BEVEL:
 
1216
            join = CAIRO_LINE_JOIN_BEVEL;
 
1217
            break;
 
1218
    }
 
1219
    cairo_set_line_join(_cr, join);
 
1220
 
 
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;
 
1226
            break;
 
1227
        case SP_STROKE_LINECAP_ROUND:
 
1228
            cap = CAIRO_LINE_CAP_ROUND;
 
1229
            break;
 
1230
        case SP_STROKE_LINECAP_SQUARE:
 
1231
            cap = CAIRO_LINE_CAP_SQUARE;
 
1232
            break;
 
1233
    }
 
1234
    cairo_set_line_cap(_cr, cap);
 
1235
    cairo_set_miter_limit(_cr, MAX(1, style->stroke_miterlimit.value));
 
1236
}
 
1237
 
 
1238
bool
 
1239
CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
 
1240
{
 
1241
    g_assert( _is_valid );
 
1242
 
 
1243
    if (_render_mode == RENDER_MODE_CLIP) {
 
1244
        if (_clip_mode == CLIP_MODE_PATH) {
 
1245
            addClipPath(bpath->path, &style->fill_rule);
 
1246
        } else {
 
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);
 
1250
            } else {
 
1251
                cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
 
1252
            }
 
1253
            cairo_fill(_cr);
 
1254
            TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
 
1255
        }
 
1256
        return true;
 
1257
    }
 
1258
 
 
1259
    if (style->fill.isNone() && style->stroke.isNone())
 
1260
        return true;
 
1261
 
 
1262
    bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
 
1263
                        ( _state->opacity != 1.0 || _state->clip_path != NULL || _state->mask != NULL ) );
 
1264
 
 
1265
    if (!need_layer)
 
1266
        cairo_save(_cr);
 
1267
    else
 
1268
        pushLayer();
 
1269
 
 
1270
    if (!style->fill.isNone()) {
 
1271
        _setFillStyle(style, pbox);
 
1272
        setBpath(bpath->path);
 
1273
 
 
1274
        if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
 
1275
            cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
 
1276
        } else {
 
1277
            cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
 
1278
        }
 
1279
 
 
1280
        if (style->stroke.isNone())
 
1281
            cairo_fill(_cr);
 
1282
        else
 
1283
            cairo_fill_preserve(_cr);
 
1284
    }
 
1285
 
 
1286
    if (!style->stroke.isNone()) {
 
1287
        _setStrokeStyle(style, pbox);
 
1288
        if (style->fill.isNone())
 
1289
            setBpath(bpath->path);
 
1290
 
 
1291
        cairo_stroke(_cr);
 
1292
    }
 
1293
 
 
1294
    if (need_layer)
 
1295
        popLayer();
 
1296
    else
 
1297
        cairo_restore(_cr);
 
1298
 
 
1299
    return true;
 
1300
}
 
1301
 
 
1302
bool
 
1303
CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
 
1304
                                NRMatrix const *image_transform, SPStyle const *style)
 
1305
{
 
1306
    g_assert( _is_valid );
 
1307
 
 
1308
    if (_render_mode == RENDER_MODE_CLIP)
 
1309
        return true;
 
1310
 
 
1311
    guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
 
1312
    if (!px_rgba)
 
1313
        return false;
 
1314
 
 
1315
    float opacity;
 
1316
    if (_state->merge_opacity)
 
1317
        opacity = _state->opacity;
 
1318
    else
 
1319
        opacity = 1.0;
 
1320
 
 
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;
 
1328
 
 
1329
            // calculate opacity-modified alpha
 
1330
            alpha_dst = src[3];
 
1331
            if (opacity != 1.0 && _vector_based_target)
 
1332
                alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
 
1333
 
 
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;
 
1338
 
 
1339
            *dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
 
1340
        }
 
1341
    }
 
1342
 
 
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))));
 
1346
        return false;
 
1347
    }
 
1348
 
 
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);
 
1352
 
 
1353
    cairo_save(_cr);
 
1354
 
 
1355
    // scaling by width & height is not needed because it will be done by Cairo
 
1356
    if (image_transform)
 
1357
        transform(image_transform);
 
1358
 
 
1359
    cairo_set_source_surface(_cr, image_surface, 0.0, 0.0);
 
1360
 
 
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);
 
1365
        cairo_clip(_cr);
 
1366
    }
 
1367
 
 
1368
    if (_vector_based_target)
 
1369
        cairo_paint(_cr);
 
1370
    else
 
1371
        cairo_paint_with_alpha(_cr, opacity);
 
1372
 
 
1373
    cairo_restore(_cr);
 
1374
 
 
1375
    cairo_surface_destroy(image_surface);
 
1376
 
 
1377
    return true;
 
1378
}
 
1379
 
 
1380
#define GLYPH_ARRAY_SIZE 64
 
1381
 
 
1382
unsigned int
 
1383
CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
 
1384
{
 
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);
 
1390
 
 
1391
    unsigned int num_invalid_glyphs = 0;
 
1392
    unsigned int i = 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++;
 
1399
            continue;
 
1400
        }
 
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;
 
1404
        i++;
 
1405
    }
 
1406
 
 
1407
    if (is_stroke || _is_texttopath)
 
1408
        cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
 
1409
    else
 
1410
        cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
 
1411
 
 
1412
    if (num_glyphs > GLYPH_ARRAY_SIZE)
 
1413
        g_free(glyphs);
 
1414
 
 
1415
    return num_glyphs - num_invalid_glyphs;
 
1416
}
 
1417
 
 
1418
bool
 
1419
CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
 
1420
                                    std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
 
1421
{
 
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;
 
1426
 
 
1427
    cairo_save(_cr);
 
1428
 
 
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);
 
1432
 
 
1433
    if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
 
1434
        size = 12.0;
 
1435
 
 
1436
    // set the given font matrix
 
1437
    cairo_matrix_t matrix;
 
1438
    _initCairoMatrix(&matrix, font_matrix);
 
1439
    cairo_set_font_matrix(_cr, &matrix);
 
1440
 
 
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);
 
1445
            } else {
 
1446
                cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
 
1447
            }
 
1448
            _showGlyphs(_cr, font, glyphtext, FALSE);
 
1449
        } else {
 
1450
            // just add the glyph paths to the current context
 
1451
            _showGlyphs(_cr, font, glyphtext, TRUE);
 
1452
        }
 
1453
    } else {
 
1454
 
 
1455
        if (style->fill.isColor() || style->fill.isPaintserver()) {
 
1456
            // set fill style
 
1457
            _setFillStyle(style, NULL);
 
1458
 
 
1459
            _showGlyphs(_cr, font, glyphtext, FALSE);
 
1460
        }
 
1461
 
 
1462
        if (style->stroke.isColor() || style->stroke.isPaintserver()) {
 
1463
            // set stroke style
 
1464
            _setStrokeStyle(style, NULL);
 
1465
 
 
1466
            // paint stroke
 
1467
            _showGlyphs(_cr, font, glyphtext, TRUE);
 
1468
            cairo_stroke(_cr);
 
1469
        }
 
1470
    }
 
1471
 
 
1472
    cairo_restore(_cr);
 
1473
 
 
1474
    cairo_font_face_destroy(font_face);
 
1475
#else
 
1476
    (void)size;
 
1477
    (void)fc_pattern;
 
1478
 
 
1479
    cairo_restore(_cr);
 
1480
#endif
 
1481
 
 
1482
    return true;
 
1483
}
 
1484
 
 
1485
/* Helper functions */
 
1486
 
 
1487
void
 
1488
CairoRenderContext::addBpath(NArtBpath const *bp)
 
1489
{
 
1490
    bool closed = false;
 
1491
    while (bp->code != NR_END) {
 
1492
        switch (bp->code) {
 
1493
            case NR_MOVETO:
 
1494
                if (closed) {
 
1495
                    cairo_close_path(_cr);
 
1496
                }
 
1497
                closed = true;
 
1498
                cairo_move_to(_cr, bp->x3, bp->y3);
 
1499
                break;
 
1500
            case NR_MOVETO_OPEN:
 
1501
                if (closed) {
 
1502
                    cairo_close_path(_cr);
 
1503
                }
 
1504
                closed = false;
 
1505
                cairo_move_to(_cr, bp->x3, bp->y3);
 
1506
                break;
 
1507
            case NR_LINETO:
 
1508
                cairo_line_to(_cr, bp->x3, bp->y3);
 
1509
                break;
 
1510
            case NR_CURVETO:
 
1511
                cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
 
1512
                break;
 
1513
            default:
 
1514
                break;
 
1515
        }
 
1516
        bp += 1;
 
1517
    }
 
1518
    if (closed) {
 
1519
        cairo_close_path(_cr);
 
1520
    }
 
1521
}
 
1522
 
 
1523
void
 
1524
CairoRenderContext::setBpath(NArtBpath const *bp)
 
1525
{
 
1526
    cairo_new_path(_cr);
 
1527
    if (bp)
 
1528
        addBpath(bp);
 
1529
}
 
1530
 
 
1531
void
 
1532
CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
 
1533
{
 
1534
    cairo_matrix_t matrix;
 
1535
 
 
1536
    cairo_matrix_init(&matrix, xx, yx, xy, yy, x0, y0);
 
1537
    cairo_transform(cr, &matrix);
 
1538
}
 
1539
 
 
1540
void
 
1541
CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
 
1542
{
 
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];
 
1549
}
 
1550
 
 
1551
void
 
1552
CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
 
1553
{
 
1554
    _concatTransform(cr, (*transform)[0], (*transform)[1],
 
1555
                     (*transform)[2], (*transform)[3],
 
1556
                     (*transform)[4], (*transform)[5]);
 
1557
}
 
1558
 
 
1559
static cairo_status_t
 
1560
_write_callback(void *closure, const unsigned char *data, unsigned int length)
 
1561
{
 
1562
    size_t written;
 
1563
    FILE *file = (FILE*)closure;
 
1564
 
 
1565
    written = fwrite (data, 1, length, file);
 
1566
 
 
1567
    if (written == length)
 
1568
        return CAIRO_STATUS_SUCCESS;
 
1569
    else
 
1570
        return CAIRO_STATUS_WRITE_ERROR;
 
1571
}
 
1572
 
 
1573
#include "clear-n_.h"
 
1574
 
 
1575
}  /* namespace Internal */
 
1576
}  /* namespace Extension */
 
1577
}  /* namespace Inkscape */
 
1578
 
 
1579
#undef TRACE
 
1580
#undef TEST
 
1581
 
 
1582
/* End of GNU GPL code */
 
1583
 
 
1584
 
 
1585
/*
 
1586
  Local Variables:
 
1587
  mode:c++
 
1588
  c-file-style:"stroustrup"
 
1589
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1590
  indent-tabs-mode:nil
 
1591
  fill-column:99
 
1592
  End:
 
1593
*/
 
1594
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :