diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp
index 61d40ea4eca2bea2d18e5210ca065e6a88866535..b68105b30908e275982102adc6ea90dc07208fd5 100644 (file)
-#define __SP_CAIRO_RENDER_CONTEXT_C__
-
/** \file
* Rendering with Cairo.
*/
/*
* Author:
* Miklos Erdelyi <erdelyim@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
*
* Copyright (C) 2006 Miklos Erdelyi
*
# include "config.h"
#endif
-#ifdef HAVE_CAIRO_PDF
-
#ifndef PANGO_ENABLE_BACKEND
#define PANGO_ENABLE_BACKEND
#endif
#include <signal.h>
#include <errno.h>
-
-#include <libnr/n-art-bpath.h>
-#include <libnr/nr-matrix-ops.h>
-#include <libnr/nr-matrix-fns.h>
-#include <libnr/nr-matrix-translate-ops.h>
-#include <libnr/nr-scale-matrix-ops.h>
+#include <2geom/pathvector.h>
#include <glib/gmem.h>
#include "display/nr-arena-group.h"
#include "display/curve.h"
#include "display/canvas-bpath.h"
+#include "display/inkscape-cairo.h"
#include "sp-item.h"
#include "sp-item-group.h"
#include "style.h"
#include "sp-pattern.h"
#include "sp-mask.h"
#include "sp-clippath.h"
+#ifdef WIN32
+#include "libnrtype/FontFactory.h" // USE_PANGO_WIN32
+#endif
#include <unit-constants.h>
#endif
-#ifndef PANGO_ENABLE_BACKEND
+#ifdef CAIRO_HAS_FT_FONT
#include <cairo-ft.h>
#endif
+#ifdef CAIRO_HAS_WIN32_FONT
+#include <cairo-win32.h>
+#include <pango/pangowin32.h>
+#endif
#include <pango/pangofc-fontmap.h>
//#define TRACE(_args) g_printf _args
+//#define TRACE(_args) g_message _args
#define TRACE(_args)
//#define TEST(_args) _args
#define TEST(_args)
@@ -106,10 +108,16 @@ static cairo_status_t _write_callback(void *closure, const unsigned char *data,
CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
_dpi(72),
+ _pdf_level(1),
+ _ps_level(1),
+ _eps(false),
+ _is_texttopath(FALSE),
+ _is_filtertobitmap(FALSE),
+ _bitmapresolution(72),
_stream(NULL),
_is_valid(FALSE),
_vector_based_target(FALSE),
- _cr(NULL),
+ _cr(NULL), // Cairo context
_surface(NULL),
_target(CAIRO_SURFACE_TYPE_IMAGE),
_target_format(CAIRO_FORMAT_ARGB32),
_renderer(parent),
_render_mode(RENDER_MODE_NORMAL),
_clip_mode(CLIP_MODE_MASK)
-{}
+{
+ font_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, font_data_free);
+}
CairoRenderContext::~CairoRenderContext(void)
{
+ if(font_table != NULL) {
+ g_hash_table_remove_all(font_table);
+ }
+
if (_cr) cairo_destroy(_cr);
if (_surface) cairo_surface_destroy(_surface);
if (_layout) g_object_unref(_layout);
}
+void CairoRenderContext::font_data_free(gpointer data)
+{
+ cairo_font_face_t *font_face = (cairo_font_face_t *)data;
+ if (font_face) {
+ cairo_font_face_destroy(font_face);
+ }
+}
CairoRenderer*
CairoRenderContext::getRenderer(void) const
// only opacity & overflow is stored for now
_state->opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
_state->has_overflow = (style->overflow.set && style->overflow.value != SP_CSS_OVERFLOW_VISIBLE);
+ _state->has_filtereffect = (style->filter.set != 0) ? TRUE : FALSE;
if (style->fill.isPaintserver() || style->stroke.isPaintserver())
_state->merge_opacity = FALSE;
(int)ceil(width), (int)ceil(height));
new_context->_cr = cairo_create(surface);
new_context->_surface = surface;
+ new_context->_width = width;
+ new_context->_height = height;
new_context->_is_valid = TRUE;
return new_context;
return true;
}
+void CairoRenderContext::setPSLevel(unsigned int level)
+{
+ _ps_level = level;
+}
+
+void CairoRenderContext::setEPS(bool eps)
+{
+ _eps = eps;
+}
+
+unsigned int CairoRenderContext::getPSLevel(void)
+{
+ return _ps_level;
+}
+
+void CairoRenderContext::setPDFLevel(unsigned int level)
+{
+ _pdf_level = level;
+}
+
+void CairoRenderContext::setTextToPath(bool texttopath)
+{
+ _is_texttopath = texttopath;
+}
+
+void CairoRenderContext::setFilterToBitmap(bool filtertobitmap)
+{
+ _is_filtertobitmap = filtertobitmap;
+}
+
+bool CairoRenderContext::getFilterToBitmap(void)
+{
+ return _is_filtertobitmap;
+}
+
+void CairoRenderContext::setBitmapResolution(int resolution)
+{
+ _bitmapresolution = resolution;
+}
+
+int CairoRenderContext::getBitmapResolution(void)
+{
+ return _bitmapresolution;
+}
+
cairo_surface_t*
CairoRenderContext::getSurface(void)
{
CairoRenderContext::setClipMode(CairoClipMode mode)
{
switch (mode) {
- case CLIP_MODE_PATH:
- case CLIP_MODE_MASK:
+ case CLIP_MODE_PATH: // Clip is rendered as a path for vector output
+ case CLIP_MODE_MASK: // Clip is rendered as a bitmap for raster output.
_clip_mode = mode;
break;
default:
CairoRenderState*
CairoRenderContext::_createState(void)
{
- CairoRenderState *state = (CairoRenderState*)g_malloc(sizeof(CairoRenderState));
+ CairoRenderState *state = (CairoRenderState*)g_try_malloc(sizeof(CairoRenderState));
g_assert( state != NULL );
+ state->has_filtereffect = FALSE;
state->merge_opacity = TRUE;
state->opacity = 1.0;
state->need_layer = FALSE;
g_assert( _is_valid );
float opacity = _state->opacity;
- TRACE(("--popLayer w/ %f\n", opacity));
+ TRACE(("--popLayer w/ opacity %f\n", opacity));
+
+ /*
+ At this point, the Cairo source is ready. A Cairo mask must be created if required.
+ Care must be taken of transformatons as Cairo, like PS and PDF, treats clip paths and
+ masks independently of the objects they effect while in SVG the clip paths and masks
+ are defined relative to the objects they are attached to.
+ Notes:
+ 1. An SVG object may have both a clip path and a mask!
+ 2. An SVG clip path can be composed of an object with a clip path. This is not handled properly.
+ 3. An SVG clipped or masked object may be first drawn off the page and then translated onto
+ the page (document). This is also not handled properly.
+ 4. The code converts all SVG masks to bitmaps. This shouldn't be necessary.
+ 5. Cairo expects a mask to use only the alpha channel. SVG masks combine the RGB luminance with
+ alpha. This is handled here by doing a pixel by pixel conversion.
+ */
- // apply clipPath or mask if present
SPClipPath *clip_path = _state->clip_path;
SPMask *mask = _state->mask;
if (clip_path || mask) {
CairoRenderContext *clip_ctx = 0;
cairo_surface_t *clip_mask = 0;
+ // Apply any clip path first
if (clip_path) {
+ TRACE((" Applying clip\n"));
if (_render_mode == RENDER_MODE_CLIP)
mask = NULL; // disable mask when performing nested clipping
if (_vector_based_target) {
- setClipMode(CLIP_MODE_PATH);
+ setClipMode(CLIP_MODE_PATH); // Vector
if (!mask) {
cairo_pop_group_to_source(_cr);
- _renderer->applyClipPath(this, clip_path);
+ _renderer->applyClipPath(this, clip_path); // Uses cairo_clip()
if (opacity == 1.0)
cairo_paint(_cr);
else
// setup a new rendering context
clip_ctx = _renderer->createContext();
clip_ctx->setImageTarget(CAIRO_FORMAT_A8);
- clip_ctx->setClipMode(CLIP_MODE_MASK);
+ clip_ctx->setClipMode(CLIP_MODE_MASK); // Raster
+ // This code ties the clipping to the document coordinates. It doesn't allow
+ // for a clipped object intially drawn off the page and then translated onto
+ // the page.
if (!clip_ctx->setupSurface(_width, _height)) {
- TRACE(("setupSurface failed\n"));
+ TRACE(("clip: setupSurface failed\n"));
_renderer->destroyContext(clip_ctx);
return;
}
cairo_paint(clip_ctx->_cr);
cairo_restore(clip_ctx->_cr);
- // if a mask won't be applied set opacity too
+ // If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
if (!mask)
cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, opacity);
else
cairo_set_source_rgba(clip_ctx->_cr, 1.0, 1.0, 1.0, 1.0);
// copy over the correct CTM
+ // It must be stored in item_transform of current state after pushState.
+ Geom::Matrix item_transform;
if (_state->parent_has_userspace)
- clip_ctx->setTransform(&getParentState()->transform);
+ item_transform = getParentState()->transform * _state->item_transform;
else
- clip_ctx->setTransform(&_state->transform);
+ item_transform = _state->item_transform;
// apply the clip path
clip_ctx->pushState();
+ clip_ctx->getCurrentState()->item_transform = item_transform;
_renderer->applyClipPath(clip_ctx, clip_path);
clip_ctx->popState();
}
}
+ // Apply any mask second
if (mask) {
+ TRACE((" Applying mask\n"));
// create rendering context for mask
CairoRenderContext *mask_ctx = _renderer->createContext();
- mask_ctx->setupSurface(_width, _height);
+
+ // Fix Me: This is a kludge. PDF and PS output is set to 72 dpi but the
+ // Cairo surface is expecting the mask to be 90 dpi.
+ float surface_width = _width;
+ float surface_height = _height;
+ if( _vector_based_target ) {
+ surface_width *= 1.25;
+ surface_height *= 1.25;
+ }
+ if (!mask_ctx->setupSurface( surface_width, surface_height )) {
+ TRACE(("mask: setupSurface failed\n"));
+ _renderer->destroyContext(mask_ctx);
+ return;
+ }
+ TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi ));
+
+ // Mask should start black, but it is created white.
+ cairo_set_source_rgba(mask_ctx->_cr, 0.0, 0.0, 0.0, 1.0);
+ cairo_rectangle(mask_ctx->_cr, 0, 0, surface_width, surface_height);
+ cairo_fill(mask_ctx->_cr);
// set rendering mode to normal
setRenderMode(RENDER_MODE_NORMAL);
// copy the correct CTM to mask context
+ /*
if (_state->parent_has_userspace)
mask_ctx->setTransform(&getParentState()->transform);
else
mask_ctx->setTransform(&_state->transform);
+ */
+ // This is probably not correct... but it seems to do the trick.
+ mask_ctx->setTransform(&_state->item_transform);
// render mask contents to mask_ctx
_renderer->applyMask(mask_ctx, mask);
int stride = cairo_image_surface_get_stride(mask_image);
unsigned char *pixels = cairo_image_surface_get_data(mask_image);
- // premultiply with opacity
- if (_state->opacity != 1.0) {
- TRACE(("premul w/ %f\n", opacity));
- guint8 int_opacity = (guint8)(255 * opacity);
- for (int row = 0 ; row < height; row++) {
- unsigned char *row_data = pixels + (row * stride);
- for (int i = 0 ; i < width; i++) {
- guint32 *pixel = (guint32 *)row_data + i;
- *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
- ((*pixel & 0x0000ff00) >> 8) * 46518 +
- ((*pixel & 0x000000ff) ) * 4688) *
- int_opacity);
- }
+ // In SVG, the rgb channels as well as the alpha channel is used in masking.
+ // In Cairo, only the alpha channel is used thus requiring this conversion.
+ // SVG specifies that RGB be converted to alpha using luminance-to-alpha.
+ // Notes: This calculation assumes linear RGB values. VERIFY COLOR SPACE!
+ // The incoming pixel values already include alpha, fill-opacity, etc.,
+ // however, opacity must still be applied.
+ TRACE(("premul w/ %f\n", opacity));
+ const float coeff_r = 0.2125 / 255.0;
+ const float coeff_g = 0.7154 / 255.0;
+ const float coeff_b = 0.0721 / 255.0;
+ for (int row = 0 ; row < height; row++) {
+ unsigned char *row_data = pixels + (row * stride);
+ for (int i = 0 ; i < width; i++) {
+ guint32 *pixel = (guint32 *)row_data + i;
+ float lum_alpha = (((*pixel & 0x00ff0000) >> 16) * coeff_r +
+ ((*pixel & 0x0000ff00) >> 8) * coeff_g +
+ ((*pixel & 0x000000ff) ) * coeff_b );
+ // lum_alpha can be slightly greater than 1 due to rounding errors...
+ // but this should be OK since it doesn't matter what the lower
+ // six hexadecimal numbers of *pixel are.
+ *pixel = (guint32)(0xff000000 * lum_alpha * opacity);
}
}
_renderer->destroyContext(mask_ctx);
}
} else {
+ // No clip path or mask
cairo_pop_group_to_source(_cr);
if (opacity == 1.0)
cairo_paint(_cr);
}
void
-CairoRenderContext::addClipPath(NArtBpath const *bp, SPIEnum const *fill_rule)
+CairoRenderContext::addClipPath(Geom::PathVector const &pv, SPIEnum const *fill_rule)
{
g_assert( _is_valid );
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
- addBpath(bp);
+ addPathVector(pv);
}
void
@@ -649,10 +775,19 @@ CairoRenderContext::addClippingRect(double x, double y, double width, double hei
bool
CairoRenderContext::setupSurface(double width, double height)
{
+ // Is the surface already set up?
+ if (_is_valid)
+ return true;
+
if (_vector_based_target && _stream == NULL)
return false;
+ _width = width;
+ _height = height;
+
cairo_surface_t *surface = NULL;
+ cairo_matrix_t ctm;
+ cairo_matrix_init_identity (&ctm);
switch (_target) {
case CAIRO_SURFACE_TYPE_IMAGE:
surface = cairo_image_surface_create(_target_format, (int)ceil(width), (int)ceil(height));
#ifdef CAIRO_HAS_PDF_SURFACE
case CAIRO_SURFACE_TYPE_PDF:
surface = cairo_pdf_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0))
+ cairo_pdf_surface_restrict_to_version(surface, (cairo_pdf_version_t)_pdf_level);
+#endif
break;
#endif
#ifdef CAIRO_HAS_PS_SURFACE
case CAIRO_SURFACE_TYPE_PS:
surface = cairo_ps_surface_create_for_stream(Inkscape::Extension::Internal::_write_callback, _stream, width, height);
+ if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
+ return FALSE;
+ }
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2))
+ cairo_ps_surface_restrict_to_level(surface, (cairo_ps_level_t)_ps_level);
+ cairo_ps_surface_set_eps(surface, (cairo_bool_t) _eps);
+#endif
+ // Cairo calculates the bounding box itself, however we want to override this. See Launchpad bug #380501
+#if (CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2))
+ if (override_bbox) {
+ cairo_ps_dsc_comment(surface, "%%BoundingBox: 100 100 200 200");
+ cairo_ps_dsc_comment(surface, "%%PageBoundingBox: 100 100 200 200");
+ }
+#endif
break;
#endif
default:
break;
}
+ return _finishSurfaceSetup (surface, &ctm);
+}
+
+bool
+CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, cairo_matrix_t *ctm)
+{
+ if (_is_valid || !surface)
+ return false;
+
+ _vector_based_target = is_vector;
+ bool ret = _finishSurfaceSetup (surface, ctm);
+ if (ret)
+ cairo_surface_reference (surface);
+ return ret;
+}
+
+bool
+CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm)
+{
+ if(surface == NULL) {
+ return false;
+ }
+ if(CAIRO_STATUS_SUCCESS != cairo_surface_status(surface)) {
+ return false;
+ }
+
_cr = cairo_create(surface);
+ if(CAIRO_STATUS_SUCCESS != cairo_status(_cr)) {
+ return false;
+ }
+ if (ctm)
+ cairo_set_matrix(_cr, ctm);
_surface = surface;
if (_vector_based_target) {
cairo_show_page(_cr);
cairo_destroy(_cr);
+ cairo_surface_finish(_surface);
+ cairo_status_t status = cairo_surface_status(_surface);
cairo_surface_destroy(_surface);
_cr = NULL;
_surface = NULL;
_is_valid = FALSE;
- if (_vector_based_target) {
+ if (_vector_based_target && _stream) {
/* Flush stream to be sure. */
(void) fflush(_stream);
_stream = NULL;
}
- return true;
+ if (status == CAIRO_STATUS_SUCCESS)
+ return true;
+ else
+ return false;
}
void
-CairoRenderContext::transform(NRMatrix const *transform)
+CairoRenderContext::transform(Geom::Matrix const *transform)
{
g_assert( _is_valid );
}
void
-CairoRenderContext::setTransform(NRMatrix const *transform)
+CairoRenderContext::setTransform(Geom::Matrix const *transform)
{
g_assert( _is_valid );
}
void
-CairoRenderContext::getTransform(NRMatrix *copy) const
+CairoRenderContext::getTransform(Geom::Matrix *copy) const
{
g_assert( _is_valid );
}
void
-CairoRenderContext::getParentTransform(NRMatrix *copy) const
+CairoRenderContext::getParentTransform(Geom::Matrix *copy) const
{
g_assert( _is_valid );
CairoRenderState *parent_state = getParentState();
- memcpy(copy, &parent_state->transform, sizeof(NRMatrix));
+ memcpy(copy, &parent_state->transform, sizeof(Geom::Matrix));
}
void
g_assert( g_slist_length(_state_stack) > 0 );
}
-static bool pattern_hasItemChildren (SPPattern *pat)
+static bool pattern_hasItemChildren(SPPattern *pat)
{
- for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ bool hasItems = false;
+ for ( SPObject *child = pat->firstChild() ; child && !hasItems; child = child->getNext() ) {
if (SP_IS_ITEM (child)) {
- return true;
+ hasItems = true;
}
}
- return false;
+ return hasItems;
}
cairo_pattern_t*
@@ -812,9 +1001,9 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
SPPattern *pat = SP_PATTERN (paintserver);
- NRMatrix ps2user, pcs2dev;
- nr_matrix_set_identity(&ps2user);
- nr_matrix_set_identity(&pcs2dev);
+ Geom::Matrix ps2user, pcs2dev;
+ ps2user = Geom::identity();
+ pcs2dev = Geom::identity();
double x = pattern_x(pat);
double y = pattern_y(pat);
@@ -826,7 +1015,7 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
TRACE(("%f x %f pattern\n", width, height));
if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
- //NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
+ //Geom::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
bbox_width_scaler = pbox->x1 - pbox->x0;
bbox_height_scaler = pbox->y1 - pbox->y0;
ps2user[4] = x * bbox_width_scaler + pbox->x0;
@@ -839,8 +1028,9 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
}
// apply pattern transformation
- NRMatrix pattern_transform(pattern_patternTransform(pat));
- nr_matrix_multiply(&ps2user, &ps2user, &pattern_transform);
+ Geom::Matrix pattern_transform(pattern_patternTransform(pat));
+ ps2user *= pattern_transform;
+ Geom::Point ori (ps2user[4], ps2user[5]);
// create pattern contents coordinate system
if (pat->viewBox_set) {
@@ -864,23 +1054,17 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
} else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
pcs2dev[0] = pbox->x1 - pbox->x0;
pcs2dev[3] = pbox->y1 - pbox->y0;
- }
-
- // calculate the size of the surface which has to be created
- // the scaling needs to be taken into account in the ctm after the pattern transformation
- NRMatrix temp;
- nr_matrix_multiply(&temp, &pattern_transform, &_state->transform);
- double width_scaler = sqrt(temp[0] * temp[0] + temp[2] * temp[2]);
- double height_scaler = sqrt(temp[1] * temp[1] + temp[3] * temp[3]);
- if (_vector_based_target) {
- // eliminate PT_PER_PX mul from these
- width_scaler *= 1.25;
- height_scaler *= 1.25;
}
- double surface_width = ceil(bbox_width_scaler * width_scaler * width);
- double surface_height = ceil(bbox_height_scaler * height_scaler * height);
- TRACE(("surface size: %f x %f\n", surface_width, surface_height));
+
+ // Calculate the size of the surface which has to be created
+#define SUBPIX_SCALE 100
+ // Cairo requires an integer pattern surface width/height.
+ // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel.
+ // Multiply by SUBPIX_SCALE to allow for less than a pixel precision
+ double surface_width = MAX(ceil(SUBPIX_SCALE * bbox_width_scaler * width - 0.5), 1);
+ double surface_height = MAX(ceil(SUBPIX_SCALE * bbox_height_scaler * height - 0.5), 1);
+ TRACE(("pattern surface size: %f x %f\n", surface_width, surface_height));
// create new rendering context
CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height);
@@ -890,27 +1074,27 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
double scale_height = surface_height / (bbox_height_scaler * height);
if (scale_width != 1.0 || scale_height != 1.0 || _vector_based_target) {
TRACE(("needed to scale with %f %f\n", scale_width, scale_height));
- NRMatrix scale;
- nr_matrix_set_scale(&scale, 1.0 / scale_width, 1.0 / scale_height);
- nr_matrix_multiply(&pcs2dev, &pcs2dev, &scale);
-
- nr_matrix_set_scale(&scale, scale_width, scale_height);
- nr_matrix_multiply(&ps2user, &ps2user, &scale);
+ pcs2dev *= Geom::Scale(SUBPIX_SCALE,SUBPIX_SCALE);
+ ps2user *= Geom::Scale(1.0/SUBPIX_SCALE,1.0/SUBPIX_SCALE);
}
+ // despite scaling up/down by subpixel scaler, the origin point of the pattern must be the same
+ ps2user[4] = ori[Geom::X];
+ ps2user[5] = ori[Geom::Y];
+
pattern_ctx->setTransform(&pcs2dev);
pattern_ctx->pushState();
// create arena and group
NRArena *arena = NRArena::create();
- unsigned dkey = sp_item_display_key_new(1);
+ unsigned dkey = SPItem::display_key_new(1);
// show items and render them
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
- for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
if (SP_IS_ITEM (child)) {
- sp_item_invoke_show (SP_ITEM (child), arena, dkey, SP_ITEM_REFERENCE_FLAGS);
+ SP_ITEM (child)->invoke_show (arena, dkey, SP_ITEM_REFERENCE_FLAGS);
_renderer->renderItem(pattern_ctx, SP_ITEM (child));
}
}
@@ -937,9 +1121,9 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver
// hide all items
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
- for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+ for ( SPObject *child = pat_i->firstChild() ; child; child = child->getNext() ) {
if (SP_IS_ITEM (child)) {
- sp_item_invoke_hide (SP_ITEM (child), dkey);
+ SP_ITEM (child)->invoke_hide (dkey);
}
}
break; // do not go further up the chain if children are found
@@ -960,19 +1144,19 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver);
- sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
+ SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built
- NR::Point p1 (lg->x1.computed, lg->y1.computed);
- NR::Point p2 (lg->x2.computed, lg->y2.computed);
- if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
+ Geom::Point p1 (lg->x1.computed, lg->y1.computed);
+ Geom::Point p2 (lg->x2.computed, lg->y2.computed);
+ if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
// convert to userspace
- NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
+ Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
p1 *= bbox2user;
p2 *= bbox2user;
}
// create linear gradient pattern
- pattern = cairo_pattern_create_linear(p1[NR::X], p1[NR::Y], p2[NR::X], p2[NR::Y]);
+ pattern = cairo_pattern_create_linear(p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y]);
// add stops
for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {
@@ -984,16 +1168,16 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver);
- sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
+ SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built
- NR::Point c (rg->cx.computed, rg->cy.computed);
- NR::Point f (rg->fx.computed, rg->fy.computed);
+ Geom::Point c (rg->cx.computed, rg->cy.computed);
+ Geom::Point f (rg->fx.computed, rg->fy.computed);
double r = rg->r.computed;
- if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
+ if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX)
apply_bbox2user = true;
// create radial gradient pattern
- pattern = cairo_pattern_create_radial(f[NR::X], f[NR::Y], 0, c[NR::X], c[NR::Y], r);
+ pattern = cairo_pattern_create_radial(f[Geom::X], f[Geom::Y], 0, c[Geom::X], c[Geom::Y], r);
// add stops
for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {
@@ -1012,7 +1196,7 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
SPGradient *g = SP_GRADIENT(paintserver);
// set extend type
- SPGradientSpread spread = sp_gradient_get_spread(g);
+ SPGradientSpread spread = g->fetchSpread();
switch (spread) {
case SP_GRADIENT_SPREAD_REPEAT: {
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
@@ -1059,7 +1243,8 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain
void
CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox)
{
- g_return_if_fail( style->fill.isColor()
+ g_return_if_fail( !style->fill.set
+ || style->fill.isColor()
|| style->fill.isPaintserver() );
float alpha = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
@@ -1073,6 +1258,10 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, NRRect const *pbox
sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
+
+ } else if (!style->fill.set) { // unset fill is black
+ cairo_set_source_rgba(_cr, 0, 0, 0, alpha);
+
} else {
g_assert( style->fill.isPaintserver()
|| SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha);
} else {
- g_assert( style->fill.isPaintserver()
+ g_assert( style->stroke.isPaintserver()
|| SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
|| SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) );
@@ -1155,27 +1344,31 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, NRRect const *pbox)
}
bool
-CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRect const *pbox)
+CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
{
g_assert( _is_valid );
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_PATH) {
- addClipPath(bpath->path, &style->fill_rule);
+ addClipPath(pathv, &style->fill_rule);
} else {
- setBpath(bpath->path);
+ setPathVector(pathv);
if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
cairo_fill(_cr);
- cairo_surface_write_to_png (_surface, "gtar2.png");
+ TEST(cairo_surface_write_to_png (_surface, "pathmask.png"));
}
return true;
}
- if (style->fill.isNone() && style->stroke.isNone())
+ bool no_fill = style->fill.isNone() || style->fill_opacity.value == 0;
+ bool no_stroke = style->stroke.isNone() || style->stroke_width.computed < 1e-9 ||
+ style->stroke_opacity.value == 0;
+
+ if (no_fill && no_stroke)
return true;
bool need_layer = ( !_state->merge_opacity && !_state->need_layer &&
@@ -1186,9 +1379,9 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
else
pushLayer();
- if (!style->fill.isNone()) {
+ if (!no_fill) {
_setFillStyle(style, pbox);
- setBpath(bpath->path);
+ setPathVector(pathv);
if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_EVEN_ODD);
@@ -1196,16 +1389,16 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
- if (style->stroke.isNone())
+ if (no_stroke)
cairo_fill(_cr);
else
cairo_fill_preserve(_cr);
}
- if (!style->stroke.isNone()) {
+ if (!no_stroke) {
_setStrokeStyle(style, pbox);
- if (style->fill.isNone())
- setBpath(bpath->path);
+ if (no_fill)
+ setPathVector(pathv);
cairo_stroke(_cr);
}
@@ -1220,16 +1413,27 @@ CairoRenderContext::renderPath(NRBPath const *bpath, SPStyle const *style, NRRec
bool
CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsigned int rs,
- NRMatrix const *image_transform, SPStyle const *style)
+ Geom::Matrix const *image_transform, SPStyle const *style)
{
g_assert( _is_valid );
if (_render_mode == RENDER_MODE_CLIP)
return true;
- guchar* px_rgba = (guchar*)g_malloc(4 * w * h);
- if (!px_rgba)
+ guchar* px_rgba = NULL;
+ guint64 size = 4L * (guint64)w * (guint64)h;
+
+ if(size < (guint64)G_MAXSIZE) {
+ px_rgba = (guchar*)g_try_malloc(4 * w * h);
+ if (!px_rgba) {
+ g_warning ("Could not allocate %lu bytes for pixel buffer!", (long unsigned) size);
+ return false;
+ }
+ } else {
+ g_warning ("the requested memory exceeds the system limit");
return false;
+ }
+
float opacity;
if (_state->merge_opacity)
@@ -1239,15 +1443,16 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi
// make a copy of the original pixbuf with premultiplied alpha
// if we pass the original pixbuf it will get messed up
+ /// @todo optimize this code, it costs a lot of time
for (unsigned i = 0; i < h; i++) {
+ guchar const *src = px + i * rs;
+ guint32 *dst = (guint32 *)(px_rgba + i * rs);
for (unsigned j = 0; j < w; j++) {
- guchar const *src = px + i * rs + j * 4;
- guint32 *dst = (guint32 *)(px_rgba + i * rs + j * 4);
guchar r, g, b, alpha_dst;
// calculate opacity-modified alpha
alpha_dst = src[3];
- if (opacity != 1.0 && _vector_based_target)
+ if ((opacity != 1.0) && _vector_based_target)
alpha_dst = (guchar)ceil((float)alpha_dst * opacity);
// premul alpha (needed because this will be undone by cairo-pdf)
@@ -1256,6 +1461,9 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi
b = src[2]*alpha_dst/255;
*dst = (((alpha_dst) << 24) | (((r)) << 16) | (((g)) << 8) | (b));
+
+ dst++; // pointer to 4byte variables
+ src += 4; // pointer to 1byte variables
}
}
@@ -1299,34 +1507,41 @@ CairoRenderContext::renderImage(guchar *px, unsigned int w, unsigned int h, unsi
#define GLYPH_ARRAY_SIZE 64
unsigned int
-CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
+CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool path)
{
cairo_glyph_t glyph_array[GLYPH_ARRAY_SIZE];
cairo_glyph_t *glyphs = glyph_array;
unsigned int num_glyphs = glyphtext.size();
- if (num_glyphs > GLYPH_ARRAY_SIZE)
- glyphs = (cairo_glyph_t*)g_malloc(sizeof(cairo_glyph_t) * num_glyphs);
+ if (num_glyphs > GLYPH_ARRAY_SIZE) {
+ glyphs = (cairo_glyph_t*)g_try_malloc(sizeof(cairo_glyph_t) * num_glyphs);
+ if(glyphs == NULL) {
+ g_warning("CairorenderContext::_showGlyphs: can not allocate memory for %d glyphs.", num_glyphs);
+ return 0;
+ }
+ }
unsigned int num_invalid_glyphs = 0;
- unsigned int i = 0;
+ unsigned int i = 0; // is a counter for indexing the glyphs array, only counts the valid glyphs
for (std::vector<CairoGlyphInfo>::const_iterator it_info = glyphtext.begin() ; it_info != glyphtext.end() ; it_info++) {
// skip glyphs which are PANGO_GLYPH_EMPTY (0x0FFFFFFF)
// or have the PANGO_GLYPH_UNKNOWN_FLAG (0x10000000) set
if (it_info->index == 0x0FFFFFFF || it_info->index & 0x10000000) {
TRACE(("INVALID GLYPH found\n"));
+ g_message("Invalid glyph found, continuing...");
num_invalid_glyphs++;
continue;
}
- glyphs[i - num_invalid_glyphs].index = it_info->index;
- glyphs[i - num_invalid_glyphs].x = it_info->x;
- glyphs[i - num_invalid_glyphs].y = it_info->y;
+ glyphs[i].index = it_info->index;
+ glyphs[i].x = it_info->x;
+ glyphs[i].y = it_info->y;
i++;
}
- if (is_stroke)
+ if (path) {
cairo_glyph_path(cr, glyphs, num_glyphs - num_invalid_glyphs);
- else
+ } else {
cairo_show_glyphs(cr, glyphs, num_glyphs - num_invalid_glyphs);
+ }
if (num_glyphs > GLYPH_ARRAY_SIZE)
g_free(glyphs);
@@ -1335,21 +1550,45 @@ CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoG
}
bool
-CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix,
+CairoRenderContext::renderGlyphtext(PangoFont *font, Geom::Matrix const *font_matrix,
std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style)
{
// create a cairo_font_face from PangoFont
double size = style->font_size.computed;
+ gpointer fonthash = (gpointer)font;
+ cairo_font_face_t *font_face = (cairo_font_face_t *)g_hash_table_lookup(font_table, fonthash);
+
+ FcPattern *fc_pattern = NULL;
+
+#ifdef USE_PANGO_WIN32
+# ifdef CAIRO_HAS_WIN32_FONT
+ LOGFONTA *lfa = pango_win32_font_logfont(font);
+ LOGFONTW lfw;
+
+ ZeroMemory(&lfw, sizeof(LOGFONTW));
+ memcpy(&lfw, lfa, sizeof(LOGFONTA));
+ MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
+
+ if(font_face == NULL) {
+ font_face = cairo_win32_font_face_create_for_logfontw(&lfw);
+ g_hash_table_insert(font_table, fonthash, font_face);
+ }
+# endif
+#else
+# ifdef CAIRO_HAS_FT_FONT
PangoFcFont *fc_font = PANGO_FC_FONT(font);
- FcPattern *fc_pattern = fc_font->font_pattern;
+ fc_pattern = fc_font->font_pattern;
+ if(font_face == NULL) {
+ font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
+ g_hash_table_insert(font_table, fonthash, font_face);
+ }
+# endif
+#endif
cairo_save(_cr);
-
-#ifndef PANGO_ENABLE_BACKEND
- cairo_font_face_t *font_face = cairo_ft_font_face_create_for_pattern(fc_pattern);
cairo_set_font_face(_cr, font_face);
- if (FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
+ if (fc_pattern && FcPatternGetDouble(fc_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
size = 12.0;
// set the given font matrix
@@ -1364,41 +1603,42 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix
} else {
cairo_set_fill_rule(_cr, CAIRO_FILL_RULE_WINDING);
}
- cairo_set_source_rgba (_cr, 1.0, 1.0, 1.0, 1.0);
- cairo_rectangle (_cr, 0, 0, 30, 40);
- cairo_fill (_cr);
_showGlyphs(_cr, font, glyphtext, FALSE);
- //cairo_fill(_cr);
} else {
// just add the glyph paths to the current context
_showGlyphs(_cr, font, glyphtext, TRUE);
}
} else {
+ bool fill = false, stroke = false, have_path = false;
+ if (style->fill.isColor() || style->fill.isPaintserver()) {
+ fill = true;
+ }
- if (style->fill.type == SP_PAINT_TYPE_COLOR || style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
- // set fill style
+ if (style->stroke.isColor() || style->stroke.isPaintserver()) {
+ stroke = true;
+ }
+ if (fill) {
_setFillStyle(style, NULL);
-
- _showGlyphs(_cr, font, glyphtext, FALSE);
+ if (_is_texttopath) {
+ _showGlyphs(_cr, font, glyphtext, true);
+ have_path = true;
+ if (stroke) cairo_fill_preserve(_cr);
+ else cairo_fill(_cr);
+ } else {
+ _showGlyphs(_cr, font, glyphtext, false);
+ }
}
-
- if (style->stroke.type == SP_PAINT_TYPE_COLOR || style->stroke.type == SP_PAINT_TYPE_PAINTSERVER) {
- // set stroke style
+ if (stroke) {
_setStrokeStyle(style, NULL);
-
- // paint stroke
- _showGlyphs(_cr, font, glyphtext, TRUE);
+ if (!have_path) _showGlyphs(_cr, font, glyphtext, true);
cairo_stroke(_cr);
}
}
cairo_restore(_cr);
- cairo_font_face_destroy(font_face);
-#else
- (void)size;
- (void)fc_pattern;
-#endif
+// if (font_face)
+// cairo_font_face_destroy(font_face);
return true;
}
@@ -1406,47 +1646,16 @@ CairoRenderContext::renderGlyphtext(PangoFont *font, NRMatrix const *font_matrix
/* Helper functions */
void
-CairoRenderContext::addBpath(NArtBpath const *bp)
+CairoRenderContext::setPathVector(Geom::PathVector const &pv)
{
- bool closed = false;
- while (bp->code != NR_END) {
- switch (bp->code) {
- case NR_MOVETO:
- if (closed) {
- cairo_close_path(_cr);
- }
- closed = true;
- cairo_move_to(_cr, bp->x3, bp->y3);
- break;
- case NR_MOVETO_OPEN:
- if (closed) {
- cairo_close_path(_cr);
- }
- closed = false;
- cairo_move_to(_cr, bp->x3, bp->y3);
- break;
- case NR_LINETO:
- cairo_line_to(_cr, bp->x3, bp->y3);
- break;
- case NR_CURVETO:
- cairo_curve_to(_cr, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
- break;
- default:
- break;
- }
- bp += 1;
- }
- if (closed) {
- cairo_close_path(_cr);
- }
+ cairo_new_path(_cr);
+ addPathVector(pv);
}
void
-CairoRenderContext::setBpath(NArtBpath const *bp)
+CairoRenderContext::addPathVector(Geom::PathVector const &pv)
{
- cairo_new_path(_cr);
- if (bp)
- addBpath(bp);
+ feed_pathvector_to_cairo(_cr, pv);
}
void
@@ -1459,7 +1668,7 @@ CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double x
}
void
-CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *transform)
+CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, Geom::Matrix const *transform)
{
matrix->xx = (*transform)[0];
matrix->yx = (*transform)[1];
@@ -1470,7 +1679,7 @@ CairoRenderContext::_initCairoMatrix(cairo_matrix_t *matrix, NRMatrix const *tra
}
void
-CairoRenderContext::_concatTransform(cairo_t *cr, NRMatrix const *transform)
+CairoRenderContext::_concatTransform(cairo_t *cr, Geom::Matrix const *transform)
{
_concatTransform(cr, (*transform)[0], (*transform)[1],
(*transform)[2], (*transform)[3],
@@ -1502,8 +1711,6 @@ _write_callback(void *closure, const unsigned char *data, unsigned int length)
/* End of GNU GPL code */
-#endif /* HAVE_CAIRO_PDF */
-
/*
Local Variables:
@@ -1514,4 +1721,4 @@ _write_callback(void *closure, const unsigned char *data, unsigned int length)
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :