cairo-render-context.cpp revision b17a9f02e386b2f328d61afc3c48d952b28dafe6
#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 "display/nr-arena.h"
#include "display/nr-arena-item.h"
#include "display/nr-arena-group.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-linear-gradient.h"
#include "sp-radial-gradient.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>
#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
#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 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),
_pdf_level(0),
_ps_level(1),
_eps(false),
_bitmapresolution(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;
}
{
}
{
}
unsigned int CairoRenderContext::getPSLevel(void)
{
return _ps_level;
}
{
_pdf_level = level;
}
{
}
{
}
bool CairoRenderContext::getFilterToBitmap(void)
{
return _is_filtertobitmap;
}
{
}
int CairoRenderContext::getBitmapResolution(void)
{
return _bitmapresolution;
}
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: // 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:
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)
{
/*
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.
*/
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)
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
// 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.
TRACE(("clip: setupSurface failed\n"));
return;
}
// clear buffer
// If a mask won't be applied set opacity too. (The clip is represented by a solid Cairo mask.)
if (!mask)
else
// copy over the correct CTM
// It must be stored in item_transform of current state after pushState.
if (_state->parent_has_userspace)
else
// apply the clip path
if (!mask) {
}
}
}
// Apply any mask second
if (mask) {
TRACE((" Applying mask\n"));
// create rendering context for mask
// 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;
}
// set rendering mode to 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.
// render mask contents to mask_ctx
// composite with clip mask
}
// premultiply with 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.
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 {
// No clip path or mask
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
{
// Is the surface already set up?
if (_is_valid)
return true;
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);
return FALSE;
}
#endif
break;
#endif
default:
return false;
break;
}
return _finishSurfaceSetup (surface);
}
bool
{
return false;
if (ret)
return ret;
}
bool
{
return false;
}
return false;
}
return false;
}
if (ctm)
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 && _stream) {
/* Flush stream to be sure. */
}
if (status == CAIRO_STATUS_SUCCESS)
return true;
else
return false;
}
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;
//Geom::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
#define SUBPIX_SCALE 100
// 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
// 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
CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle const *style, NRRect const *pbox)
{
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_PATH) {
} else {
} else {
}
}
return true;
}
return true;
if (!need_layer)
else
pushLayer();
if (!no_fill) {
} else {
}
if (no_stroke)
else
}
if (!no_stroke) {
if (no_fill)
}
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 (_is_texttopath) {
} else {
}
}
if (num_glyphs > GLYPH_ARRAY_SIZE)
return num_glyphs - num_invalid_glyphs;
}
bool
{
// create a cairo_font_face from PangoFont
#ifdef USE_PANGO_WIN32
# ifdef CAIRO_HAS_WIN32_FONT
MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, lfa->lfFaceName, LF_FACESIZE, lfw.lfFaceName, LF_FACESIZE);
# endif
#else
# ifdef CAIRO_HAS_FT_FONT
# endif
#endif
size = 12.0;
// set the given font matrix
if (_render_mode == RENDER_MODE_CLIP) {
if (_clip_mode == CLIP_MODE_MASK) {
} else {
}
} else {
// just add the glyph paths to the current context
}
} else {
// set fill style
}
// set stroke style
// paint stroke
}
}
if (font_face)
return true;
}
/* Helper functions */
void
{
}
void
{
}
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 :