cairo-render-context.cpp revision e9b6d4bb13a32c224543a70fd5702d558cc516c3
#define __SP_CAIRO_RENDER_CONTEXT_C__
/** \file
* Rendering with Cairo.
*/
/*
* Author:
* Miklos Erdelyi <erdelyim@gmail.com>
*
* Copyright (C) 2006 Miklos Erdelyi
*
* Licensed under GNU GPL
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifndef PANGO_ENABLE_BACKEND
#define PANGO_ENABLE_BACKEND
#endif
#ifndef PANGO_ENABLE_ENGINE
#define PANGO_ENABLE_ENGINE
#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 "display/nr-arena.h"
#include "display/nr-arena-item.h"
#include "display/nr-arena-group.h"
#include "display/canvas-bpath.h"
#include "sp-item.h"
#include "sp-item-group.h"
#include "style.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "sp-pattern.h"
#include "sp-mask.h"
#include "sp-clippath.h"
#include <unit-constants.h>
#include "cairo-render-context.h"
#include "cairo-renderer.h"
#include <cairo.h>
// include support for only the compiled-in surface types
#ifdef CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
#endif
#ifdef CAIRO_HAS_PS_SURFACE
#include <cairo-ps.h>
#endif
#ifndef PANGO_ENABLE_BACKEND
#include <cairo-ft.h>
#endif
#include <pango/pangofc-fontmap.h>
//#define TRACE(_args) g_printf _args
//#define TEST(_args) _args
// FIXME: expose these from sp-clippath/mask.cpp
struct SPClipPathView {
unsigned int key;
};
struct SPMaskView {
unsigned int key;
};
namespace Inkscape {
namespace Extension {
namespace Internal {
static cairo_status_t _write_callback(void *closure, const unsigned char *data, unsigned int length);
_dpi(72),
{}
CairoRenderContext::~CairoRenderContext(void)
{
}
CairoRenderContext::getRenderer(void) const
{
return _renderer;
}
CairoRenderContext::getCurrentState(void) const
{
return _state;
}
CairoRenderContext::getParentState(void) const
{
// if this is the root node just return it
return _state;
} else {
}
}
void
{
// only opacity & overflow is stored for now
// disable rendering of opacity if there's a stroke on the fill
if (_state->merge_opacity
}
/**
* \brief Creates a new render context which will be compatible with the given context's Cairo surface
*
* \param width width of the surface to be created
* \param height height of the surface to be created
*/
{
cairo_surface_t *surface = cairo_surface_create_similar(cairo_get_target(_cr), CAIRO_CONTENT_COLOR_ALPHA,
return new_context;
}
CairoRenderContext::cloneMe(void) const
{
}
bool
{
// format cannot be set on an already initialized surface
if (_is_valid)
return false;
switch (format) {
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_A8:
case CAIRO_FORMAT_A1:
return true;
break;
default:
break;
}
return false;
}
bool
{
#ifndef CAIRO_HAS_PDF_SURFACE
return false;
#else
#endif
gsize bytesWritten = 0;
/* TODO: Replace the below fprintf's with something that does the right thing whether in
* gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
* the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
* return code.
*/
if (*fn == '|') {
fn += 1;
#ifndef WIN32
#else
#endif
if (!osp) {
return false;
}
} else if (*fn == '>') {
fn += 1;
if (!osf) {
return false;
}
} else {
/* put cwd stuff in here */
: g_strdup("lpr") );
#ifndef WIN32
#else
#endif
if (!osp) {
return false;
}
}
}
if (_stream) {
/* fixme: this is kinda icky */
#endif
}
return true;
}
bool
{
#ifndef CAIRO_HAS_PS_SURFACE
return false;
#else
#endif
gsize bytesWritten = 0;
/* TODO: Replace the below fprintf's with something that does the right thing whether in
* gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of
* the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
* return code.
*/
if (*fn == '|') {
fn += 1;
#ifndef WIN32
#else
#endif
if (!osp) {
return false;
}
} else if (*fn == '>') {
fn += 1;
if (!osf) {
return false;
}
} else {
/* put cwd stuff in here */
: g_strdup("lpr") );
#ifndef WIN32
#else
#endif
if (!osp) {
return false;
}
}
}
if (_stream) {
/* fixme: this is kinda icky */
#endif
}
return true;
}
CairoRenderContext::getSurface(void)
{
return _surface;
}
bool
{
if (status)
return false;
else
return true;
}
void
{
switch (mode) {
case RENDER_MODE_NORMAL:
case RENDER_MODE_CLIP:
_render_mode = mode;
break;
default:
break;
}
}
CairoRenderContext::getRenderMode(void) const
{
return _render_mode;
}
void
{
switch (mode) {
case CLIP_MODE_PATH:
case CLIP_MODE_MASK:
_clip_mode = mode;
break;
default:
break;
}
}
CairoRenderContext::getClipMode(void) const
{
return _clip_mode;
}
CairoRenderContext::_createState(void)
{
return state;
}
void
CairoRenderContext::pushLayer(void)
{
TRACE(("--pushLayer\n"));
// clear buffer
if (!_vector_based_target) {
}
}
void
CairoRenderContext::popLayer(void)
{
// apply clipPath or mask if present
CairoRenderContext *clip_ctx = 0;
cairo_surface_t *clip_mask = 0;
if (clip_path) {
if (_render_mode == RENDER_MODE_CLIP)
if (_vector_based_target) {
if (!mask) {
if (opacity == 1.0)
else
} else {
// the clipPath will be applied before masking
}
} else {
// setup a new rendering context
TRACE(("setupSurface failed\n"));
return;
}
// clear buffer
// if a mask won't be applied set opacity too
if (!mask)
else
// copy over the correct CTM
if (_state->parent_has_userspace)
else
// apply the clip path
if (!mask) {
}
}
}
if (mask) {
// create rendering context for mask
// set rendering mode to normal
// copy the correct CTM to mask context
if (_state->parent_has_userspace)
else
// render mask contents to mask_ctx
// composite with clip mask
}
// premultiply with opacity
for (int i = 0 ; i < width; i++) {
}
}
}
if (_clip_mode == CLIP_MODE_PATH) {
// we have to do the clipping after cairo_pop_group_to_source
}
// apply the mask onto the layer
}
} else {
if (opacity == 1.0)
else
}
}
void
{
// here it should be checked whether the current clip winding changed
// so we could switch back to masked clipping
} else {
}
}
void
{
}
bool
{
return false;
switch (_target) {
case CAIRO_SURFACE_TYPE_IMAGE:
break;
#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);
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);
break;
#endif
default:
return false;
break;
}
return _finishSurfaceSetup (surface);
}
bool
{
return false;
if (ret)
return ret;
}
bool
{
if (_vector_based_target) {
// set background color on non-alpha surfaces
// TODO: bgcolor should be derived from SPDocument
}
return true;
}
bool
CairoRenderContext::finish(void)
{
if (_vector_based_target)
if (_layout)
if (_vector_based_target) {
/* Flush stream to be sure. */
}
return true;
}
void
{
// store new CTM
}
void
{
}
void
{
}
void
{
}
void
CairoRenderContext::pushState(void)
{
// copy current state's transform
}
void
CairoRenderContext::popState(void)
{
}
{
for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
if (SP_IS_ITEM (child)) {
return true;
}
}
return false;
}
CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver, NRRect const *pbox)
{
double bbox_width_scaler;
double bbox_height_scaler;
//NR::Matrix bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
} else {
bbox_width_scaler = 1.0;
bbox_height_scaler = 1.0;
ps2user[4] = x;
ps2user[5] = y;
}
// apply pattern transformation
// create pattern contents coordinate system
if (pat->viewBox_set) {
double x, y, w, h;
double view_width, view_height;
x = 0;
y = 0;
w = width * bbox_width_scaler;
h = height * bbox_height_scaler;
//calculatePreserveAspectRatio(pat->aspect_align, pat->aspect_clip, view_width, view_height, &x, &y, &w, &h);
pcs2dev[0] = w / view_width;
}
// 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
if (_vector_based_target) {
// eliminate PT_PER_PX mul from these
width_scaler *= 1.25;
height_scaler *= 1.25;
}
// create new rendering context
// adjust the size of the painted pattern to fit exactly the created surface
}
pattern_ctx->pushState();
// create arena and group
// show items and render them
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) ) {
if (SP_IS_ITEM (child)) {
}
}
break; // do not go further up the chain if children are found
}
}
pattern_ctx->popState();
// setup a cairo_pattern_t
// set pattern transformation
delete pattern_ctx;
// hide all items
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) ) {
if (SP_IS_ITEM (child)) {
}
}
break; // do not go further up the chain if children are found
}
}
return result;
}
{
bool apply_bbox2user = FALSE;
if (SP_IS_LINEARGRADIENT (paintserver)) {
// convert to userspace
}
// create linear gradient pattern
// add stops
float rgb[3];
cairo_pattern_add_color_stop_rgba(pattern, lg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], lg->vector.stops[i].opacity * alpha);
}
} else if (SP_IS_RADIALGRADIENT (paintserver)) {
apply_bbox2user = true;
// create radial gradient pattern
// add stops
float rgb[3];
cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha);
}
} else if (SP_IS_PATTERN (paintserver)) {
} else {
return NULL;
}
// set extend type
switch (spread) {
case SP_GRADIENT_SPREAD_REPEAT: {
break;
}
case SP_GRADIENT_SPREAD_REFLECT: { // not supported by cairo-pdf yet
break;
}
case SP_GRADIENT_SPREAD_PAD: { // not supported by cairo-pdf yet
break;
}
default: {
break;
}
}
if (g->gradientTransform_set) {
// apply gradient transformation
} else {
}
if (apply_bbox2user) {
// convert to userspace
}
}
return pattern;
}
void
{
if (_state->merge_opacity) {
}
float rgb[3];
} else {
if (pattern) {
}
}
}
void
{
if (_state->merge_opacity)
float rgb[3];
} else {
cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha);
if (pattern) {
}
}
{
} else {
}
// set line join type
case SP_STROKE_LINEJOIN_MITER:
break;
case SP_STROKE_LINEJOIN_ROUND:
break;
case SP_STROKE_LINEJOIN_BEVEL:
break;
}
// set line cap type
case SP_STROKE_LINECAP_BUTT:
break;
case SP_STROKE_LINECAP_ROUND:
break;
case SP_STROKE_LINECAP_SQUARE:
break;
}
}
bool
{
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_PATH) {
} else {
} else {
}
}
return true;
}
return true;
if (!need_layer)
else
pushLayer();
} else {
}
else
}
}
if (need_layer)
popLayer();
else
return true;
}
bool
{
if (_render_mode == RENDER_MODE_CLIP)
return true;
if (!px_rgba)
return false;
float opacity;
if (_state->merge_opacity)
else
opacity = 1.0;
// make a copy of the original pixbuf with premultiplied alpha
// if we pass the original pixbuf it will get messed up
for (unsigned i = 0; i < h; i++) {
for (unsigned j = 0; j < w; j++) {
// calculate opacity-modified alpha
// premul alpha (needed because this will be undone by cairo-pdf)
}
}
cairo_surface_t *image_surface = cairo_image_surface_create_for_data(px_rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
if (cairo_surface_status(image_surface)) {
TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface))));
return false;
}
// setup automatic freeing of the image data when destroying the surface
static cairo_user_data_key_t key;
// scaling by width & height is not needed because it will be done by Cairo
if (image_transform)
// set clip region so that the pattern will not be repeated (bug in Cairo-PDF)
if (_vector_based_target) {
cairo_rectangle(_cr, 0, 0, w, h);
}
if (_vector_based_target)
else
return true;
}
#define GLYPH_ARRAY_SIZE 64
unsigned int
CairoRenderContext::_showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke)
{
if (num_glyphs > GLYPH_ARRAY_SIZE)
unsigned int num_invalid_glyphs = 0;
unsigned int i = 0;
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
TRACE(("INVALID GLYPH found\n"));
continue;
}
i++;
}
if (is_stroke)
else
if (num_glyphs > GLYPH_ARRAY_SIZE)
return num_glyphs - num_invalid_glyphs;
}
bool
{
// create a cairo_font_face from PangoFont
#ifndef PANGO_ENABLE_BACKEND
size = 12.0;
// set the given font matrix
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_MASK) {
} else {
}
cairo_fill (_cr);
//cairo_fill(_cr);
} else {
// just add the glyph paths to the current context
}
} else {
// set fill style
}
// set stroke style
// paint stroke
}
}
#else
(void)size;
(void)fc_pattern;
#endif
return true;
}
/* Helper functions */
void
{
bool closed = false;
case NR_MOVETO:
if (closed) {
}
closed = true;
break;
case NR_MOVETO_OPEN:
if (closed) {
}
closed = false;
break;
case NR_LINETO:
break;
case NR_CURVETO:
break;
default:
break;
}
bp += 1;
}
if (closed) {
}
}
void
{
if (bp)
}
void
CairoRenderContext::_concatTransform(cairo_t *cr, double xx, double yx, double xy, double yy, double x0, double y0)
{
}
void
{
}
void
{
}
static cairo_status_t
{
return CAIRO_STATUS_SUCCESS;
else
return CAIRO_STATUS_WRITE_ERROR;
}
#include "clear-n_.h"
} /* namespace Internal */
} /* namespace Extension */
} /* namespace Inkscape */
/* End of GNU GPL code */
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :