diff options
| author | Andrew Higginson <at.higginson@gmail.com> | 2011-12-27 21:04:47 +0000 |
|---|---|---|
| committer | Andrew <at.higginson@gmail.com> | 2011-12-27 21:04:47 +0000 |
| commit | 80960b623a99aae1402ab651b2974ef544ed3b03 (patch) | |
| tree | ba49d42c2789e9e11f805e2d5263e10f9fedeef8 /src/display | |
| parent | try to fix bug (diff) | |
| parent | GDL: Cherry-pick upstream patch 73852 (2011-03-23) - Add missing return value. (diff) | |
| download | inkscape-80960b623a99aae1402ab651b2974ef544ed3b03.tar.gz inkscape-80960b623a99aae1402ab651b2974ef544ed3b03.zip | |
merged with trunk so I can build again...
(bzr r10092.1.36)
Diffstat (limited to 'src/display')
141 files changed, 9487 insertions, 11089 deletions
diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 9fa2304be..7b188b286 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -1,59 +1,125 @@ -SET(display_SRC -canvas-arena.cpp -canvas-axonomgrid.cpp -canvas-bpath.cpp -canvas-grid.cpp -canvas-temporary-item.cpp -canvas-temporary-item-list.cpp -curve.cpp -gnome-canvas-acetate.cpp -guideline.cpp -inkscape-cairo.cpp -nr-3dutils.cpp -nr-arena.cpp -nr-arena-glyphs.cpp -nr-arena-group.cpp -nr-arena-image.cpp -nr-arena-item.cpp -nr-arena-shape.cpp -nr-filter-blend.cpp -nr-filter-colormatrix.cpp -nr-filter-component-transfer.cpp -nr-filter-composite.cpp -nr-filter-convolve-matrix.cpp -nr-filter.cpp -nr-filter-diffuselighting.cpp -nr-filter-displacement-map.cpp -nr-filter-flood.cpp -nr-filter-gaussian.cpp -nr-filter-getalpha.cpp -nr-filter-image.cpp -nr-filter-merge.cpp -nr-filter-morphology.cpp -nr-filter-offset.cpp -nr-filter-primitive.cpp -nr-filter-skeleton.cpp -nr-filter-slot.cpp -nr-filter-specularlighting.cpp -nr-filter-tile.cpp -nr-filter-turbulence.cpp -nr-filter-units.cpp -nr-filter-utils.cpp -nr-light.cpp -nr-plain-stuff.cpp -nr-plain-stuff-gdk.cpp -nr-svgfonts.h -pixblock-scaler.cpp -pixblock-transform.cpp -snap-indicator.cpp -sodipodi-ctrl.cpp -sodipodi-ctrlrect.cpp -sp-canvas.cpp -sp-canvas-util.cpp -sp-ctrlline.cpp -sp-ctrlquadr.cpp -#testnr.cpp + +set(display_SRC + cairo-utils.cpp + canvas-arena.cpp + canvas-axonomgrid.cpp + canvas-bpath.cpp + canvas-grid.cpp + canvas-temporary-item-list.cpp + canvas-temporary-item.cpp + canvas-text.cpp + curve.cpp + drawing-context.cpp + drawing-group.cpp + drawing-image.cpp + drawing-item.cpp + drawing-shape.cpp + drawing-surface.cpp + drawing-text.cpp + drawing.cpp + gnome-canvas-acetate.cpp + grayscale.cpp + guideline.cpp + nr-3dutils.cpp + nr-filter-blend.cpp + nr-filter-colormatrix.cpp + nr-filter-component-transfer.cpp + nr-filter-composite.cpp + nr-filter-convolve-matrix.cpp + nr-filter-diffuselighting.cpp + nr-filter-displacement-map.cpp + nr-filter-flood.cpp + nr-filter-gaussian.cpp + nr-filter-image.cpp + nr-filter-merge.cpp + nr-filter-morphology.cpp + nr-filter-offset.cpp + nr-filter-primitive.cpp + # nr-filter-skeleton.cpp + nr-filter-slot.cpp + nr-filter-specularlighting.cpp + nr-filter-tile.cpp + nr-filter-turbulence.cpp + nr-filter-units.cpp + nr-filter-utils.cpp + nr-filter.cpp + nr-light.cpp + nr-style.cpp + nr-svgfonts.cpp + nr-svgfonts.h + snap-indicator.cpp + sodipodi-ctrl.cpp + sodipodi-ctrlrect.cpp + sp-canvas-util.cpp + sp-canvas.cpp + sp-ctrlline.cpp + sp-ctrlpoint.cpp + sp-ctrlquadr.cpp + + + # ------- + # Headers + cairo-templates.h + cairo-utils.h + canvas-arena.h + canvas-axonomgrid.h + canvas-bpath.h + canvas-grid.h + canvas-temporary-item-list.h + canvas-temporary-item.h + canvas-text.h + curve-test.h + curve.h + drawing-context.h + drawing-group.h + drawing-image.h + drawing-item.h + drawing-shape.h + drawing-surface.h + drawing-text.h + drawing.h + gnome-canvas-acetate.h + grayscale.h + guideline.h + nr-3dutils.h + nr-filter-blend.h + nr-filter-colormatrix.h + nr-filter-component-transfer.h + nr-filter-composite.h + nr-filter-convolve-matrix.h + nr-filter-diffuselighting.h + nr-filter-displacement-map.h + nr-filter-flood.h + nr-filter-gaussian.h + nr-filter-image.h + nr-filter-merge.h + nr-filter-morphology.h + nr-filter-offset.h + nr-filter-primitive.h + nr-filter-skeleton.h + nr-filter-slot.h + nr-filter-specularlighting.h + nr-filter-tile.h + nr-filter-turbulence.h + nr-filter-types.h + nr-filter-units.h + nr-filter-utils.h + nr-filter.h + nr-light-types.h + nr-light.h + nr-style.h + rendermode.h + snap-indicator.h + sodipodi-ctrl.h + sodipodi-ctrlrect.h + sp-canvas-group.h + sp-canvas-item.h + sp-canvas-util.h + sp-canvas.h + sp-ctrlline.h + sp-ctrlpoint.h + sp-ctrlquadr.h ) -ADD_LIBRARY(display STATIC ${display_SRC}) -TARGET_LINK_LIBRARIES(display -2geom ${INKSCAPE_LIBS})
\ No newline at end of file + +# add_inkscape_lib(display_LIB "${display_SRC}") +add_inkscape_source("${display_SRC}") diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index 370e28d6d..1c7a21dae 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -4,80 +4,61 @@ display/canvas-arena.$(OBJEXT): helper/sp-marshal.h display/sp-canvas.$(OBJEXT): helper/sp-marshal.h ink_common_sources += \ - display/nr-3dutils.h \ - display/nr-3dutils.cpp \ - display/nr-arena-forward.h \ - display/nr-arena.cpp \ - display/nr-arena.h \ - display/nr-arena-item.cpp \ - display/nr-arena-item.h \ - display/nr-arena-group.cpp \ - display/nr-arena-group.h \ - display/nr-arena-image.cpp \ - display/nr-arena-image.h \ - display/nr-arena-shape.cpp \ - display/nr-arena-shape.h \ - display/nr-arena-glyphs.cpp \ - display/nr-arena-glyphs.h \ + display/cairo-templates.h \ + display/cairo-utils.cpp \ + display/cairo-utils.h \ display/canvas-arena.cpp \ display/canvas-arena.h \ + display/canvas-axonomgrid.cpp \ + display/canvas-axonomgrid.h \ display/canvas-bpath.cpp \ display/canvas-bpath.h \ display/canvas-grid.cpp \ display/canvas-grid.h \ - display/canvas-axonomgrid.cpp \ - display/canvas-axonomgrid.h \ display/canvas-temporary-item.cpp \ display/canvas-temporary-item.h \ display/canvas-temporary-item-list.cpp \ display/canvas-temporary-item-list.h \ - display/canvas-text.h \ display/canvas-text.cpp \ + display/canvas-text.h \ display/curve.cpp \ display/curve.h \ + display/drawing.cpp \ + display/drawing.h \ + display/drawing-context.cpp \ + display/drawing-context.h \ + display/drawing-group.cpp \ + display/drawing-group.h \ + display/drawing-image.cpp \ + display/drawing-image.h \ + display/drawing-item.cpp \ + display/drawing-item.h \ + display/drawing-shape.cpp \ + display/drawing-shape.h \ + display/drawing-surface.cpp \ + display/drawing-surface.h \ + display/drawing-text.cpp \ + display/drawing-text.h \ display/gnome-canvas-acetate.cpp \ display/gnome-canvas-acetate.h \ display/grayscale.cpp \ display/grayscale.h \ display/guideline.cpp \ display/guideline.h \ - display/nr-plain-stuff-gdk.cpp \ - display/nr-plain-stuff-gdk.h \ - display/nr-plain-stuff.cpp \ - display/nr-plain-stuff.h \ - display/nr-svgfonts.cpp \ - display/nr-svgfonts.h \ - display/rendermode.h \ - display/snap-indicator.cpp \ - display/snap-indicator.h \ - display/sodipodi-ctrl.cpp \ - display/sodipodi-ctrl.h \ - display/sodipodi-ctrlrect.cpp \ - display/sodipodi-ctrlrect.h \ - display/sp-canvas-group.h \ - display/sp-canvas-item.h \ - display/sp-canvas-util.cpp \ - display/sp-canvas-util.h \ - display/sp-canvas.cpp \ - display/sp-canvas.h \ - display/sp-ctrlline.cpp \ - display/sp-ctrlline.h \ - display/sp-ctrlpoint.cpp \ - display/sp-ctrlpoint.h \ - display/sp-ctrlquadr.cpp \ - display/sp-ctrlquadr.h \ - display/nr-filter.cpp \ - display/nr-filter.h \ + display/nr-3dutils.cpp \ + display/nr-3dutils.h \ + display/nr-arena-forward.h \ display/nr-filter-blend.cpp \ display/nr-filter-blend.h \ display/nr-filter-colormatrix.cpp \ display/nr-filter-colormatrix.h \ display/nr-filter-component-transfer.cpp \ display/nr-filter-component-transfer.h \ - display/nr-filter-composite.h \ display/nr-filter-composite.cpp \ + display/nr-filter-composite.h \ display/nr-filter-convolve-matrix.cpp \ display/nr-filter-convolve-matrix.h \ + display/nr-filter.cpp \ display/nr-filter-diffuselighting.cpp \ display/nr-filter-diffuselighting.h \ display/nr-filter-displacement-map.cpp \ @@ -86,8 +67,7 @@ ink_common_sources += \ display/nr-filter-flood.h \ display/nr-filter-gaussian.cpp \ display/nr-filter-gaussian.h \ - display/nr-filter-getalpha.cpp \ - display/nr-filter-getalpha.h \ + display/nr-filter.h \ display/nr-filter-image.cpp \ display/nr-filter-image.h \ display/nr-filter-merge.cpp \ @@ -106,21 +86,35 @@ ink_common_sources += \ display/nr-filter-tile.h \ display/nr-filter-turbulence.cpp \ display/nr-filter-turbulence.h \ - display/nr-filter-pixops.h \ display/nr-filter-types.h \ - display/nr-filter-units.h \ display/nr-filter-units.cpp \ - display/nr-filter-utils.h \ + display/nr-filter-units.h \ display/nr-filter-utils.cpp \ - display/pixblock-scaler.cpp \ - display/pixblock-scaler.h \ - display/pixblock-transform.cpp \ - display/pixblock-transform.h \ - display/inkscape-cairo.cpp \ - display/inkscape-cairo.h \ - display/nr-light.h \ + display/nr-filter-utils.h \ display/nr-light.cpp \ - display/nr-light-types.h + display/nr-light.h \ + display/nr-light-types.h \ + display/nr-style.cpp \ + display/nr-style.h \ + display/nr-svgfonts.cpp \ + display/nr-svgfonts.h \ + display/rendermode.h \ + display/snap-indicator.cpp \ + display/snap-indicator.h \ + display/sodipodi-ctrl.cpp \ + display/sodipodi-ctrl.h \ + display/sodipodi-ctrlrect.cpp \ + display/sodipodi-ctrlrect.h \ + display/sp-canvas.cpp \ + display/sp-canvas.h \ + display/sp-canvas-util.cpp \ + display/sp-canvas-util.h \ + display/sp-ctrlline.cpp \ + display/sp-ctrlline.h \ + display/sp-ctrlpoint.cpp \ + display/sp-ctrlpoint.h \ + display/sp-ctrlquadr.cpp \ + display/sp-ctrlquadr.h # ###################### # ### CxxTest stuff #### diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h new file mode 100644 index 000000000..b48a22702 --- /dev/null +++ b/src/display/cairo-templates.h @@ -0,0 +1,695 @@ +/** + * @file + * Cairo software blending templates. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_TEMPLATES_H +#define SEEN_INKSCAPE_DISPLAY_CAIRO_TEMPLATES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_OPENMP +#include <omp.h> +#include "preferences.h" +// single-threaded operation if the number of pixels is below this threshold +static const int OPENMP_THRESHOLD = 2048; +#endif + +#include <algorithm> +#include <cairo.h> +#include <glib.h> +#include <math.h> +#include "display/nr-3dutils.h" +#include "display/cairo-utils.h" + +/** + * Blend two surfaces using the supplied functor. + * This template blends two Cairo image surfaces using a blending functor that takes + * two 32-bit ARGB pixel values and returns a modified 32-bit pixel value. + * Differences in input surface formats are handled transparently. In future, this template + * will also handle software fallback for GL surfaces. + */ +template <typename Blend> +void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_surface_t *out, Blend blend) +{ + cairo_surface_flush(in1); + cairo_surface_flush(in2); + + // ASSUMPTIONS + // 1. Cairo ARGB32 surface strides are always divisible by 4 + // 2. We can only receive CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_A8 surfaces + // 3. Both surfaces are of the same size + // 4. Output surface is ARGB32 if at least one input is ARGB32 + + int w = cairo_image_surface_get_width(in2); + int h = cairo_image_surface_get_height(in2); + int stride1 = cairo_image_surface_get_stride(in1); + int stride2 = cairo_image_surface_get_stride(in2); + int strideout = cairo_image_surface_get_stride(out); + int bpp1 = cairo_image_surface_get_format(in1) == CAIRO_FORMAT_A8 ? 1 : 4; + int bpp2 = cairo_image_surface_get_format(in2) == CAIRO_FORMAT_A8 ? 1 : 4; + int bppout = std::max(bpp1, bpp2); + + // Check whether we can loop over pixels without taking stride into account. + bool fast_path = true; + fast_path &= (stride1 == w * bpp1); + fast_path &= (stride2 == w * bpp2); + fast_path &= (strideout == w * bppout); + + int limit = w * h; + + guint32 *const in1_data = (guint32*) cairo_image_surface_get_data(in1); + guint32 *const in2_data = (guint32*) cairo_image_surface_get_data(in2); + guint32 *const out_data = (guint32*) cairo_image_surface_get_data(out); + + // NOTE + // OpenMP probably doesn't help much here. + // It would be better to render more than 1 tile at a time. + #if HAVE_OPENMP + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int num_threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); + #endif + + // The number of code paths here is evil. + if (bpp1 == 4) { + if (bpp2 == 4) { + if (fast_path) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + *(out_data + i) = blend(*(in1_data + i), *(in2_data + i)); + } + } else { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint32 *in1_p = in1_data + i * stride1/4; + guint32 *in2_p = in2_data + i * stride2/4; + guint32 *out_p = out_data + i * strideout/4; + for (int j = 0; j < w; ++j) { + *out_p = blend(*in1_p, *in2_p); + ++in1_p; ++in2_p; ++out_p; + } + } + } + } else { + // bpp2 == 1 + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint32 *in1_p = in1_data + i * stride1/4; + guint8 *in2_p = reinterpret_cast<guint8*>(in2_data) + i * stride2; + guint32 *out_p = out_data + i * strideout/4; + for (int j = 0; j < w; ++j) { + guint32 in2_px = *in2_p; + in2_px <<= 24; + *out_p = blend(*in1_p, in2_px); + ++in1_p; ++in2_p; ++out_p; + } + } + } + } else { + if (bpp2 == 4) { + // bpp1 == 1 + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint8 *in1_p = reinterpret_cast<guint8*>(in1_data) + i * stride1; + guint32 *in2_p = in2_data + i * stride2/4; + guint32 *out_p = out_data + i * strideout/4; + for (int j = 0; j < w; ++j) { + guint32 in1_px = *in1_p; + in1_px <<= 24; + *out_p = blend(in1_px, *in2_p); + ++in1_p; ++in2_p; ++out_p; + } + } + } else { + // bpp1 == 1 && bpp2 == 1 + if (fast_path) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + guint8 *in1_p = reinterpret_cast<guint8*>(in1_data) + i; + guint8 *in2_p = reinterpret_cast<guint8*>(in2_data) + i; + guint8 *out_p = reinterpret_cast<guint8*>(out_data) + i; + guint32 in1_px = *in1_p; in1_px <<= 24; + guint32 in2_px = *in2_p; in2_px <<= 24; + guint32 out_px = blend(in1_px, in2_px); + *out_p = out_px >> 24; + } + } else { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint8 *in1_p = reinterpret_cast<guint8*>(in1_data) + i * stride1; + guint8 *in2_p = reinterpret_cast<guint8*>(in2_data) + i * stride2; + guint8 *out_p = reinterpret_cast<guint8*>(out_data) + i * strideout; + for (int j = 0; j < w; ++j) { + guint32 in1_px = *in1_p; in1_px <<= 24; + guint32 in2_px = *in2_p; in2_px <<= 24; + guint32 out_px = blend(in1_px, in2_px); + *out_p = out_px >> 24; + ++in1_p; ++in2_p; ++out_p; + } + } + } + } + } + + cairo_surface_mark_dirty(out); +} + +template <typename Filter> +void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter filter) +{ + cairo_surface_flush(in); + + // ASSUMPTIONS + // 1. Cairo ARGB32 surface strides are always divisible by 4 + // 2. We can only receive CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_A8 surfaces + // 3. Surfaces have the same dimensions + // 4. Output surface is A8 if input is A8 + + int w = cairo_image_surface_get_width(in); + int h = cairo_image_surface_get_height(in); + int stridein = cairo_image_surface_get_stride(in); + int strideout = cairo_image_surface_get_stride(out); + int bppin = cairo_image_surface_get_format(in) == CAIRO_FORMAT_A8 ? 1 : 4; + int bppout = cairo_image_surface_get_format(out) == CAIRO_FORMAT_A8 ? 1 : 4; + int limit = w * h; + + // Check whether we can loop over pixels without taking stride into account. + bool fast_path = true; + fast_path &= (stridein == w * bppin); + fast_path &= (strideout == w * bppout); + + guint32 *const in_data = (guint32*) cairo_image_surface_get_data(in); + guint32 *const out_data = (guint32*) cairo_image_surface_get_data(out); + + #if HAVE_OPENMP + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int num_threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); + #endif + + // this is provided just in case, to avoid problems with strict aliasing rules + if (in == out) { + if (bppin == 4) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + *(in_data + i) = filter(*(in_data + i)); + } + } else { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + guint8 *in_p = reinterpret_cast<guint8*>(in_data) + i; + guint32 in_px = *in_p; in_px <<= 24; + guint32 out_px = filter(in_px); + *in_p = out_px >> 24; + } + } + cairo_surface_mark_dirty(out); + return; + } + + if (bppin == 4) { + if (bppout == 4) { + // bppin == 4, bppout == 4 + if (fast_path) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + *(out_data + i) = filter(*(in_data + i)); + } + } else { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint32 *in_p = in_data + i * stridein/4; + guint32 *out_p = out_data + i * strideout/4; + for (int j = 0; j < w; ++j) { + *out_p = filter(*in_p); + ++in_p; ++out_p; + } + } + } + } else { + // bppin == 4, bppout == 1 + // we use this path with COLORMATRIX_LUMINANCETOALPHA + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint32 *in_p = in_data + i * stridein/4; + guint8 *out_p = reinterpret_cast<guint8*>(out_data) + i * strideout; + for (int j = 0; j < w; ++j) { + guint32 out_px = filter(*in_p); + *out_p = out_px >> 24; + ++in_p; ++out_p; + } + } + } + } else { + // bppin == 1, bppout == 1 + // Note: there is no path for bppin == 1, bppout == 4 because it is useless + if (fast_path) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < limit; ++i) { + guint8 *in_p = reinterpret_cast<guint8*>(in_data) + i; + guint8 *out_p = reinterpret_cast<guint8*>(out_data) + i; + guint32 in_px = *in_p; in_px <<= 24; + guint32 out_px = filter(in_px); + *out_p = out_px >> 24; + } + } else { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint8 *in_p = reinterpret_cast<guint8*>(in_data) + i * stridein; + guint8 *out_p = reinterpret_cast<guint8*>(out_data) + i * strideout; + for (int j = 0; j < w; ++j) { + guint32 in_px = *in_p; in_px <<= 24; + guint32 out_px = filter(in_px); + *out_p = out_px >> 24; + ++in_p; ++out_p; + } + } + } + } + cairo_surface_mark_dirty(out); +} + + +/** + * Synthesize surface pixels based on their position. + * This template accepts a functor that gets called with the x and y coordinates of the pixels, + * given as integers. + * @param out Output surface + * @param out_area The region of the output surface that should be synthesized + * @param synth Synthesis functor + */ +template <typename Synth> +void ink_cairo_surface_synthesize(cairo_surface_t *out, cairo_rectangle_t const &out_area, Synth synth) +{ + // ASSUMPTIONS + // 1. Cairo ARGB32 surface strides are always divisible by 4 + // 2. We can only receive CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_A8 surfaces + + int w = out_area.width; + int h = out_area.height; + int strideout = cairo_image_surface_get_stride(out); + int bppout = cairo_image_surface_get_format(out) == CAIRO_FORMAT_A8 ? 1 : 4; + // NOTE: fast path is not used, because we would need 2 divisions to get pixel indices + + unsigned char *out_data = cairo_image_surface_get_data(out); + + #if HAVE_OPENMP + int limit = w * h; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int num_threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); + #endif + + if (bppout == 4) { + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = out_area.y; i < h; ++i) { + guint32 *out_p = reinterpret_cast<guint32*>(out_data + i * strideout); + for (int j = out_area.x; j < w; ++j) { + *out_p = synth(j, i); + ++out_p; + } + } + } else { + // bppout == 1 + #if HAVE_OPENMP + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(num_threads) + #endif + for (int i = out_area.y; i < h; ++i) { + guint8 *out_p = out_data + i * strideout; + for (int j = out_area.x; j < w; ++j) { + guint32 out_px = synth(j, i); + *out_p = out_px >> 24; + ++out_p; + } + } + } + cairo_surface_mark_dirty(out); +} + +template <typename Synth> +void ink_cairo_surface_synthesize(cairo_surface_t *out, Synth synth) +{ + int w = cairo_image_surface_get_width(out); + int h = cairo_image_surface_get_height(out); + + cairo_rectangle_t area; + area.x = 0; + area.y = 0; + area.width = w; + area.height = h; + + ink_cairo_surface_synthesize(out, area, synth); +} + +struct SurfaceSynth { + SurfaceSynth(cairo_surface_t *surface) + : _px(cairo_image_surface_get_data(surface)) + , _w(cairo_image_surface_get_width(surface)) + , _h(cairo_image_surface_get_height(surface)) + , _stride(cairo_image_surface_get_stride(surface)) + , _alpha(cairo_surface_get_content(surface) == CAIRO_CONTENT_ALPHA) + { + cairo_surface_flush(surface); + } + + guint32 pixelAt(int x, int y) const { + if (_alpha) { + unsigned char *px = _px + y*_stride + x; + return *px << 24; + } else { + unsigned char *px = _px + y*_stride + x*4; + return *reinterpret_cast<guint32*>(px); + } + } + guint32 alphaAt(int x, int y) const { + if (_alpha) { + unsigned char *px = _px + y*_stride + x; + return *px; + } else { + unsigned char *px = _px + y*_stride + x*4; + guint32 p = *reinterpret_cast<guint32*>(px); + return (p & 0xff000000) >> 24; + } + } + + // retrieve a pixel value with bilinear interpolation + guint32 pixelAt(double x, double y) const { + if (_alpha) { + return alphaAt(x, y) << 24; + } + + double xf = floor(x), yf = floor(y); + int xi = xf, yi = yf; + guint32 xif = round((x - xf) * 255), yif = round((y - yf) * 255); + guint32 p00, p01, p10, p11; + + unsigned char *pxi = _px + yi*_stride + xi*4; + guint32 *pxu = reinterpret_cast<guint32*>(pxi); + guint32 *pxl = reinterpret_cast<guint32*>(pxi + _stride); + p00 = *pxu; p10 = *(pxu + 1); + p01 = *pxl; p11 = *(pxl + 1); + + guint32 comp[4]; + + for (unsigned i = 0; i < 4; ++i) { + guint32 shift = i*8; + guint32 mask = 0xff << shift; + guint32 c00 = (p00 & mask) >> shift; + guint32 c10 = (p10 & mask) >> shift; + guint32 c01 = (p01 & mask) >> shift; + guint32 c11 = (p11 & mask) >> shift; + + guint32 iu = (255-xif) * c00 + xif * c10; + guint32 il = (255-xif) * c01 + xif * c11; + comp[i] = (255-yif) * iu + yif * il; + comp[i] = (comp[i] + (255*255/2)) / (255*255); + } + + guint32 result = comp[0] | (comp[1] << 8) | (comp[2] << 16) | (comp[3] << 24); + return result; + } + + // retrieve an alpha value with bilinear interpolation + guint32 alphaAt(double x, double y) const { + double xf = floor(x), yf = floor(y); + int xi = xf, yi = yf; + guint32 xif = round((x - xf) * 255), yif = round((y - yf) * 255); + guint32 p00, p01, p10, p11; + if (_alpha) { + unsigned char *pxu = _px + yi*_stride + xi; + unsigned char *pxl = pxu + _stride; + p00 = *pxu; p10 = *(pxu + 1); + p01 = *pxl; p11 = *(pxl + 1); + } else { + unsigned char *pxi = _px + yi*_stride + xi*4; + guint32 *pxu = reinterpret_cast<guint32*>(pxi); + guint32 *pxl = reinterpret_cast<guint32*>(pxi + _stride); + p00 = (*pxu & 0xff000000) >> 24; p10 = (*(pxu + 1) & 0xff000000) >> 24; + p01 = (*pxl & 0xff000000) >> 24; p11 = (*(pxl + 1) & 0xff000000) >> 24; + } + guint32 iu = (255-xif) * p00 + xif * p10; + guint32 il = (255-xif) * p01 + xif * p11; + guint32 result = (255-yif) * iu + yif * il; + result = (result + (255*255/2)) / (255*255); + return result; + } + + // compute surface normal at given coordinates using 3x3 Sobel gradient filter + NR::Fvector surfaceNormalAt(int x, int y, double scale) const { + // Below there are some multiplies by zero. They will be optimized out. + // Do not remove them, because they improve readability. + // NOTE: fetching using alphaAt is slightly lazy. + NR::Fvector normal; + double fx = -scale/255.0, fy = -scale/255.0; + normal[Z_3D] = 1.0; + if (G_UNLIKELY(x == 0)) { + // leftmost column + if (G_UNLIKELY(y == 0)) { + // upper left corner + fx *= (2.0/3.0); + fy *= (2.0/3.0); + double p00 = alphaAt(x,y), p10 = alphaAt(x+1, y), + p01 = alphaAt(x,y+1), p11 = alphaAt(x+1, y+1); + normal[X_3D] = + -2.0 * p00 +2.0 * p10 + -1.0 * p01 +1.0 * p11; + normal[Y_3D] = + -2.0 * p00 -1.0 * p10 + +2.0 * p01 +1.0 * p11; + } else if (G_UNLIKELY(y == (_h - 1))) { + // lower left corner + fx *= (2.0/3.0); + fy *= (2.0/3.0); + double p00 = alphaAt(x,y-1), p10 = alphaAt(x+1, y-1), + p01 = alphaAt(x,y ), p11 = alphaAt(x+1, y); + normal[X_3D] = + -1.0 * p00 +1.0 * p10 + -2.0 * p01 +2.0 * p11; + normal[Y_3D] = + -2.0 * p00 -1.0 * p10 + +2.0 * p01 +1.0 * p11; + } else { + // leftmost column + fx *= (1.0/2.0); + fy *= (1.0/3.0); + double p00 = alphaAt(x, y-1), p10 = alphaAt(x+1, y-1), + p01 = alphaAt(x, y ), p11 = alphaAt(x+1, y ), + p02 = alphaAt(x, y+1), p12 = alphaAt(x+1, y+1); + normal[X_3D] = + -1.0 * p00 +1.0 * p10 + -2.0 * p01 +2.0 * p11 + -1.0 * p02 +1.0 * p12; + normal[Y_3D] = + -2.0 * p00 -1.0 * p10 + +0.0 * p01 +0.0 * p11 // this will be optimized out + +2.0 * p02 +1.0 * p12; + } + } else if (G_UNLIKELY(x == (_w - 1))) { + // rightmost column + if (G_UNLIKELY(y == 0)) { + // top right corner + fx *= (2.0/3.0); + fy *= (2.0/3.0); + double p00 = alphaAt(x-1,y), p10 = alphaAt(x, y), + p01 = alphaAt(x-1,y+1), p11 = alphaAt(x, y+1); + normal[X_3D] = + -2.0 * p00 +2.0 * p10 + -1.0 * p01 +1.0 * p11; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 + +1.0 * p01 +2.0 * p11; + } else if (G_UNLIKELY(y == (_h - 1))) { + // bottom right corner + fx *= (2.0/3.0); + fy *= (2.0/3.0); + double p00 = alphaAt(x-1,y-1), p10 = alphaAt(x, y-1), + p01 = alphaAt(x-1,y ), p11 = alphaAt(x, y); + normal[X_3D] = + -1.0 * p00 +1.0 * p10 + -2.0 * p01 +2.0 * p11; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 + +1.0 * p01 +2.0 * p11; + } else { + // rightmost column + fx *= (1.0/2.0); + fy *= (1.0/3.0); + double p00 = alphaAt(x-1, y-1), p10 = alphaAt(x, y-1), + p01 = alphaAt(x-1, y ), p11 = alphaAt(x, y ), + p02 = alphaAt(x-1, y+1), p12 = alphaAt(x, y+1); + normal[X_3D] = + -1.0 * p00 +1.0 * p10 + -2.0 * p01 +2.0 * p11 + -1.0 * p02 +1.0 * p12; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 + +0.0 * p01 +0.0 * p11 + +1.0 * p02 +2.0 * p12; + } + } else { + // interior + if (G_UNLIKELY(y == 0)) { + // top row + fx *= (1.0/3.0); + fy *= (1.0/2.0); + double p00 = alphaAt(x-1, y ), p10 = alphaAt(x, y ), p20 = alphaAt(x+1, y ), + p01 = alphaAt(x-1, y+1), p11 = alphaAt(x, y+1), p21 = alphaAt(x+1, y+1); + normal[X_3D] = + -2.0 * p00 +0.0 * p10 +2.0 * p20 + -1.0 * p01 +0.0 * p11 +1.0 * p21; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 -1.0 * p20 + +1.0 * p01 +2.0 * p11 +1.0 * p21; + } else if (G_UNLIKELY(y == (_h - 1))) { + // bottom row + fx *= (1.0/3.0); + fy *= (1.0/2.0); + double p00 = alphaAt(x-1, y-1), p10 = alphaAt(x, y-1), p20 = alphaAt(x+1, y-1), + p01 = alphaAt(x-1, y ), p11 = alphaAt(x, y ), p21 = alphaAt(x+1, y ); + normal[X_3D] = + -1.0 * p00 +0.0 * p10 +1.0 * p20 + -2.0 * p01 +0.0 * p11 +2.0 * p21; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 -1.0 * p20 + +1.0 * p01 +2.0 * p11 +1.0 * p21; + } else { + // interior pixels + // note: p11 is actually unused, so we don't fetch its value + fx *= (1.0/4.0); + fy *= (1.0/4.0); + double p00 = alphaAt(x-1, y-1), p10 = alphaAt(x, y-1), p20 = alphaAt(x+1, y-1), + p01 = alphaAt(x-1, y ), p11 = 0.0, p21 = alphaAt(x+1, y ), + p02 = alphaAt(x-1, y+1), p12 = alphaAt(x, y+1), p22 = alphaAt(x+1, y+1); + normal[X_3D] = + -1.0 * p00 +0.0 * p10 +1.0 * p20 + -2.0 * p01 +0.0 * p11 +2.0 * p21 + -1.0 * p02 +0.0 * p12 +1.0 * p22; + normal[Y_3D] = + -1.0 * p00 -2.0 * p10 -1.0 * p20 + +0.0 * p01 +0.0 * p11 +0.0 * p21 + +1.0 * p02 +2.0 * p12 +1.0 * p22; + } + } + normal[X_3D] *= fx; + normal[Y_3D] *= fy; + NR::normalize_vector(normal); + return normal; + } + + unsigned char *_px; + int _w, _h, _stride; + bool _alpha; +}; + +/* +// simple pixel accessor for image surface that handles different edge wrapping modes +class PixelAccessor { +public: + typedef PixelAccessor self; + enum EdgeMode { + EDGE_PAD, + EDGE_WRAP, + EDGE_ZERO + }; + + PixelAccessor(cairo_surface_t *s, EdgeMode e) + : _surface(s) + , _px(cairo_image_surface_get_data(s)) + , _x(0), _y(0) + , _w(cairo_image_surface_get_width(s)) + , _h(cairo_image_surface_get_height(s)) + , _stride(cairo_image_surface_get_stride(s)) + , _edge_mode(e) + , _alpha(cairo_image_surface_get_format(s) == CAIRO_FORMAT_A8) + {} + + guint32 pixelAt(int x, int y) { + // This is a lot of ifs for a single pixel access. However, branch prediction + // should help us a lot, as the result of ifs is always the same for a single image. + int real_x = x, real_y = y; + switch (_edge_mode) { + case EDGE_PAD: + real_x = CLAMP(x, 0, _w-1); + real_y = CLAMP(y, 0, _h-1); + break; + case EDGE_WRAP: + real_x %= _w; + real_y %= _h; + break; + case EDGE_ZERO: + default: + if (x < 0 || x >= _w || y < 0 || y >= _h) + return 0; + break; + } + if (_alpha) { + return *(_px + real_y*_stride + real_x) << 24; + } else { + guint32 *px = reinterpret_cast<guint32*>(_px +real_y*_stride + real_x*4); + return *px; + } + } +private: + cairo_surface_t *_surface; + guint8 *_px; + int _x, _y, _w, _h, _stride; + EdgeMode _edge_mode; + bool _alpha; +};*/ + +// Some helpers for pixel manipulation +G_GNUC_CONST inline gint32 +pxclamp(gint32 v, gint32 low, gint32 high) { + // NOTE: it is possible to write a "branchless" clamping operation. + // However, it will be slower than this function, because the code below + // is compiled to conditional moves. + if (v < low) return low; + if (v > high) return high; + return v; +} + +#endif +/* + 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 : diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp new file mode 100644 index 000000000..2e2eb42dd --- /dev/null +++ b/src/display/cairo-utils.cpp @@ -0,0 +1,718 @@ +/* + * Helper functions to use cairo with inkscape + * + * Copyright (C) 2007 bulia byak + * Copyright (C) 2008 Johan Engelen + * + * Released under GNU GPL + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "display/cairo-utils.h" + +#include <stdexcept> +#include <2geom/pathvector.h> +#include <2geom/bezier-curve.h> +#include <2geom/hvlinesegment.h> +#include <2geom/affine.h> +#include <2geom/point.h> +#include <2geom/path.h> +#include <2geom/transforms.h> +#include <2geom/sbasis-to-bezier.h> +#include "color.h" +#include "helper/geom-curves.h" + +namespace Inkscape { + +CairoGroup::CairoGroup(cairo_t *_ct) : ct(_ct), pushed(false) {} +CairoGroup::~CairoGroup() { + if (pushed) { + cairo_pattern_t *p = cairo_pop_group(ct); + cairo_pattern_destroy(p); + } +} +void CairoGroup::push() { + cairo_push_group(ct); + pushed = true; +} +void CairoGroup::push_with_content(cairo_content_t content) { + cairo_push_group_with_content(ct, content); + pushed = true; +} +cairo_pattern_t *CairoGroup::pop() { + if (pushed) { + cairo_pattern_t *ret = cairo_pop_group(ct); + pushed = false; + return ret; + } else { + throw std::logic_error("Cairo group popped without pushing it first"); + } +} +Cairo::RefPtr<Cairo::Pattern> CairoGroup::popmm() { + if (pushed) { + cairo_pattern_t *ret = cairo_pop_group(ct); + Cairo::RefPtr<Cairo::Pattern> retmm(new Cairo::Pattern(ret, true)); + pushed = false; + return retmm; + } else { + throw std::logic_error("Cairo group popped without pushing it first"); + } +} +void CairoGroup::pop_to_source() { + if (pushed) { + cairo_pop_group_to_source(ct); + pushed = false; + } +} + +CairoContext::CairoContext(cairo_t *obj, bool ref) + : Cairo::Context(obj, ref) +{} + +void CairoContext::transform(Geom::Affine const &m) +{ + cairo_matrix_t cm; + cm.xx = m[0]; + cm.xy = m[2]; + cm.x0 = m[4]; + cm.yx = m[1]; + cm.yy = m[3]; + cm.y0 = m[5]; + cairo_transform(cobj(), &cm); +} + +void CairoContext::set_source_rgba32(guint32 color) +{ + double red = SP_RGBA32_R_F(color); + double gre = SP_RGBA32_G_F(color); + double blu = SP_RGBA32_B_F(color); + double alp = SP_RGBA32_A_F(color); + cairo_set_source_rgba(cobj(), red, gre, blu, alp); +} + +void CairoContext::append_path(Geom::PathVector const &pv) +{ + feed_pathvector_to_cairo(cobj(), pv); +} + +Cairo::RefPtr<CairoContext> CairoContext::create(Cairo::RefPtr<Cairo::Surface> const &target) +{ + cairo_t *ct = cairo_create(target->cobj()); + Cairo::RefPtr<CairoContext> ret(new CairoContext(ct, true)); + return ret; +} + +} // namespace Inkscape + +/* + * Can be called recursively. + * If optimize_stroke == false, the view Rect is not used. + */ +static void +feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & trans, Geom::Rect view, bool optimize_stroke) +{ + if( is_straight_curve(c) ) + { + Geom::Point end_tr = c.finalPoint() * trans; + if (!optimize_stroke) { + cairo_line_to(cr, end_tr[0], end_tr[1]); + } else { + Geom::Rect swept(c.initialPoint()*trans, end_tr); + if (swept.intersects(view)) { + cairo_line_to(cr, end_tr[0], end_tr[1]); + } else { + cairo_move_to(cr, end_tr[0], end_tr[1]); + } + } + } + else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const*>(&c)) { + std::vector<Geom::Point> points = quadratic_bezier->points(); + points[0] *= trans; + points[1] *= trans; + points[2] *= trans; + Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); + Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); + if (!optimize_stroke) { + cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); + } else { + Geom::Rect swept(points[0], points[2]); + swept.expandTo(points[1]); + if (swept.intersects(view)) { + cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); + } else { + cairo_move_to(cr, points[2][0], points[2][1]); + } + } + } + else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) { + std::vector<Geom::Point> points = cubic_bezier->points(); + //points[0] *= trans; // don't do this one here for fun: it is only needed for optimized strokes + points[1] *= trans; + points[2] *= trans; + points[3] *= trans; + if (!optimize_stroke) { + cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); + } else { + points[0] *= trans; // didn't transform this point yet + Geom::Rect swept(points[0], points[3]); + swept.expandTo(points[1]); + swept.expandTo(points[2]); + if (swept.intersects(view)) { + cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); + } else { + cairo_move_to(cr, points[3][0], points[3][1]); + } + } + } +// else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) { +// //TODO: get at the innards and spit them out to cairo +// } + else { + //this case handles sbasis as well as all other curve types + Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); + + //recurse to convert the new path resulting from the sbasis to svgd + for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { + feed_curve_to_cairo(cr, *iter, trans, view, optimize_stroke); + } + } +} + + +/** Feeds path-creating calls to the cairo context translating them from the Path */ +static void +feed_path_to_cairo (cairo_t *ct, Geom::Path const &path) +{ + if (path.empty()) + return; + + cairo_move_to(ct, path.initialPoint()[0], path.initialPoint()[1] ); + + for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { + feed_curve_to_cairo(ct, *cit, Geom::identity(), Geom::Rect(), false); // optimize_stroke is false, so the view rect is not used + } + + if (path.closed()) { + cairo_close_path(ct); + } +} + +/** Feeds path-creating calls to the cairo context translating them from the Path, with the given transform and shift */ +static void +feed_path_to_cairo (cairo_t *ct, Geom::Path const &path, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width) +{ + if (!area) + return; + if (path.empty()) + return; + + // Transform all coordinates to coords within "area" + Geom::Point shift = area->min(); + Geom::Rect view = *area; + view.expandBy (stroke_width); + view = view * (Geom::Affine)Geom::Translate(-shift); + // Pass transformation to feed_curve, so that we don't need to create a whole new path. + Geom::Affine transshift(trans * Geom::Translate(-shift)); + + Geom::Point initial = path.initialPoint() * transshift; + cairo_move_to(ct, initial[0], initial[1] ); + + for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { + feed_curve_to_cairo(ct, *cit, transshift, view, optimize_stroke); + } + + if (path.closed()) { + if (!optimize_stroke) { + cairo_close_path(ct); + } else { + cairo_line_to(ct, initial[0], initial[1]); + /* We cannot use cairo_close_path(ct) here because some parts of the path may have been + clipped and not drawn (maybe the before last segment was outside view area), which + would result in closing the "subpath" after the last interruption, not the entire path. + + However, according to cairo documentation: + The behavior of cairo_close_path() is distinct from simply calling cairo_line_to() with the equivalent coordinate + in the case of stroking. When a closed sub-path is stroked, there are no caps on the ends of the sub-path. Instead, + there is a line join connecting the final and initial segments of the sub-path. + + The correct fix will be possible when cairo introduces methods for moving without + ending/starting subpaths, which we will use for skipping invisible segments; then we + will be able to use cairo_close_path here. This issue also affects ps/eps/pdf export, + see bug 168129 + */ + } + } +} + +/** Feeds path-creating calls to the cairo context translating them from the PathVector, with the given transform and shift + * One must have done cairo_new_path(ct); before calling this function. */ +void +feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width) +{ + if (!area) + return; + if (pathv.empty()) + return; + + for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { + feed_path_to_cairo(ct, *it, trans, area, optimize_stroke, stroke_width); + } +} + +/** Feeds path-creating calls to the cairo context translating them from the PathVector + * One must have done cairo_new_path(ct); before calling this function. */ +void +feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) +{ + if (pathv.empty()) + return; + + for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { + feed_path_to_cairo(ct, *it); + } +} + +void +ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba) +{ + cairo_set_source_rgba(ct, SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_B_F(rgba), SP_RGBA32_A_F(rgba)); +} + +void +ink_cairo_set_source_color(cairo_t *ct, SPColor const &c, double opacity) +{ + cairo_set_source_rgba(ct, c.v.c[0], c.v.c[1], c.v.c[2], opacity); +} + +void ink_matrix_to_2geom(Geom::Affine &m, cairo_matrix_t const &cm) +{ + m[0] = cm.xx; + m[2] = cm.xy; + m[4] = cm.x0; + m[1] = cm.yx; + m[3] = cm.yy; + m[5] = cm.y0; +} + +void ink_matrix_to_cairo(cairo_matrix_t &cm, Geom::Affine const &m) +{ + cm.xx = m[0]; + cm.xy = m[2]; + cm.x0 = m[4]; + cm.yx = m[1]; + cm.yy = m[3]; + cm.y0 = m[5]; +} + +void +ink_cairo_transform(cairo_t *ct, Geom::Affine const &m) +{ + cairo_matrix_t cm; + ink_matrix_to_cairo(cm, m); + cairo_transform(ct, &cm); +} + +void +ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m) +{ + cairo_matrix_t cm; + ink_matrix_to_cairo(cm, m); + cairo_pattern_set_matrix(cp, &cm); +} + +void +ink_cairo_set_source_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) +{ + cairo_surface_t *pbs = ink_cairo_surface_create_for_argb32_pixbuf(pb); + cairo_set_source_surface(ct, pbs, x, y); + cairo_surface_destroy(pbs); +} + +cairo_surface_t * +ink_cairo_surface_create_for_argb32_pixbuf(GdkPixbuf *pb) +{ + guchar *data = gdk_pixbuf_get_pixels(pb); + int w = gdk_pixbuf_get_width(pb); + int h = gdk_pixbuf_get_height(pb); + int stride = gdk_pixbuf_get_rowstride(pb); + + cairo_surface_t *pbs = cairo_image_surface_create_for_data( + data, CAIRO_FORMAT_ARGB32, w, h, stride); + return pbs; +} + +/** + * Cleanup function for GdkPixbuf. + * This function should be passed as the GdkPixbufDestroyNotify parameter + * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by + * a Cairo surface. + */ +void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) +{ + cairo_surface_t *surface = reinterpret_cast<cairo_surface_t*>(data); + cairo_surface_destroy(surface); +} + +/** + * Create an exact copy of a surface. + * Creates a surface that has the same type, content type, dimensions and contents + * as the specified surface. + */ +cairo_surface_t * +ink_cairo_surface_copy(cairo_surface_t *s) +{ + cairo_surface_t *ns = ink_cairo_surface_create_identical(s); + + if (cairo_surface_get_type(s) == CAIRO_SURFACE_TYPE_IMAGE) { + // use memory copy instead of using a Cairo context + cairo_surface_flush(s); + int stride = cairo_image_surface_get_stride(s); + int h = cairo_image_surface_get_height(s); + memcpy(cairo_image_surface_get_data(ns), cairo_image_surface_get_data(s), stride * h); + cairo_surface_mark_dirty(ns); + } else { + // generic implementation + cairo_t *ct = cairo_create(ns); + cairo_set_source_surface(ct, s, 0, 0); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); + } + + return ns; +} + +/** + * Create a surface that differs only in pixel content. + * Creates a surface that has the same type, content type and dimensions + * as the specified surface. Pixel contents are not copied. + */ +cairo_surface_t * +ink_cairo_surface_create_identical(cairo_surface_t *s) +{ + cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s)); + return ns; +} + +cairo_surface_t * +ink_cairo_surface_create_same_size(cairo_surface_t *s, cairo_content_t c) +{ + cairo_surface_t *ns = cairo_surface_create_similar(s, c, + ink_cairo_surface_get_width(s), ink_cairo_surface_get_height(s)); + return ns; +} + +/** + * Extract the alpha channel into a new surface. + * Creates a surface with a content type of CAIRO_CONTENT_ALPHA that contains + * the alpha values of pixels from @a s. + */ +cairo_surface_t * +ink_cairo_extract_alpha(cairo_surface_t *s) +{ + cairo_surface_t *alpha = ink_cairo_surface_create_same_size(s, CAIRO_CONTENT_ALPHA); + + cairo_t *ct = cairo_create(alpha); + cairo_set_source_surface(ct, s, 0, 0); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); + + return alpha; +} + +cairo_surface_t * +ink_cairo_surface_create_output(cairo_surface_t *image, cairo_surface_t *bg) +{ + cairo_content_t imgt = cairo_surface_get_content(image); + cairo_content_t bgt = cairo_surface_get_content(bg); + cairo_surface_t *out = NULL; + + if (bgt == CAIRO_CONTENT_ALPHA && imgt == CAIRO_CONTENT_ALPHA) { + out = ink_cairo_surface_create_identical(bg); + } else { + out = ink_cairo_surface_create_same_size(bg, CAIRO_CONTENT_COLOR_ALPHA); + } + + return out; +} + +void +ink_cairo_surface_blit(cairo_surface_t *src, cairo_surface_t *dest) +{ + if (cairo_surface_get_type(src) == CAIRO_SURFACE_TYPE_IMAGE && + cairo_surface_get_type(dest) == CAIRO_SURFACE_TYPE_IMAGE && + cairo_image_surface_get_format(src) == cairo_image_surface_get_format(dest) && + cairo_image_surface_get_height(src) == cairo_image_surface_get_height(dest) && + cairo_image_surface_get_width(src) == cairo_image_surface_get_width(dest) && + cairo_image_surface_get_stride(src) == cairo_image_surface_get_stride(dest)) + { + // use memory copy instead of using a Cairo context + cairo_surface_flush(src); + int stride = cairo_image_surface_get_stride(src); + int h = cairo_image_surface_get_height(src); + memcpy(cairo_image_surface_get_data(dest), cairo_image_surface_get_data(src), stride * h); + cairo_surface_mark_dirty(dest); + } else { + // generic implementation + cairo_t *ct = cairo_create(dest); + cairo_set_source_surface(ct, src, 0, 0); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); + } +} + +int +ink_cairo_surface_get_width(cairo_surface_t *surface) +{ + // For now only image surface is handled. + // Later add others, e.g. cairo-gl + assert(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE); + return cairo_image_surface_get_width(surface); +} +int +ink_cairo_surface_get_height(cairo_surface_t *surface) +{ + assert(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE); + return cairo_image_surface_get_height(surface); +} + +static int ink_cairo_surface_average_color_internal(cairo_surface_t *surface, double &rf, double &gf, double &bf, double &af) +{ + rf = gf = bf = af = 0.0; + cairo_surface_flush(surface); + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + int stride = cairo_image_surface_get_stride(surface); + unsigned char *data = cairo_image_surface_get_data(surface); + + /* TODO convert this to OpenMP somehow */ + for (int y = 0; y < height; ++y, data += stride) { + for (int x = 0; x < width; ++x) { + guint32 px = *reinterpret_cast<guint32*>(data + 4*x); + EXTRACT_ARGB32(px, a,r,g,b) + rf += r / 255.0; + gf += g / 255.0; + bf += b / 255.0; + af += a / 255.0; + } + } + return width * height; +} + +guint32 ink_cairo_surface_average_color(cairo_surface_t *surface) +{ + double rf,gf,bf,af; + ink_cairo_surface_average_color_premul(surface, rf,gf,bf,af); + guint32 r = round(rf * 255); + guint32 g = round(gf * 255); + guint32 b = round(bf * 255); + guint32 a = round(af * 255); + ASSEMBLE_ARGB32(px, a,r,g,b); + return px; +} + +void ink_cairo_surface_average_color(cairo_surface_t *surface, double &r, double &g, double &b, double &a) +{ + int count = ink_cairo_surface_average_color_internal(surface, r,g,b,a); + + r /= a; + g /= a; + b /= a; + a /= count; + + r = CLAMP(r, 0.0, 1.0); + g = CLAMP(g, 0.0, 1.0); + b = CLAMP(b, 0.0, 1.0); + a = CLAMP(a, 0.0, 1.0); +} + +void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, double &g, double &b, double &a) +{ + int count = ink_cairo_surface_average_color_internal(surface, r,g,b,a); + + r /= count; + g /= count; + b /= count; + a /= count; + + r = CLAMP(r, 0.0, 1.0); + g = CLAMP(g, 0.0, 1.0); + b = CLAMP(b, 0.0, 1.0); + a = CLAMP(a, 0.0, 1.0); +} + +cairo_pattern_t * +ink_cairo_pattern_create_checkerboard() +{ + int const w = 6; + int const h = 6; + + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2*w, 2*h); + + cairo_t *ct = cairo_create(s); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgb(ct, 0.75, 0.75, 0.75); + cairo_paint(ct); + cairo_set_source_rgb(ct, 0.5, 0.5, 0.5); + cairo_rectangle(ct, 0, 0, w, h); + cairo_rectangle(ct, w, h, w, h); + cairo_fill(ct); + cairo_destroy(ct); + + cairo_pattern_t *p = cairo_pattern_create_for_surface(s); + cairo_pattern_set_extend(p, CAIRO_EXTEND_REPEAT); + cairo_pattern_set_filter(p, CAIRO_FILTER_NEAREST); + + cairo_surface_destroy(s); + return p; +} + +/* The following two functions use "from" instead of "to", because when you write: + val1 = argb32_from_pixbuf(val1); + the name of the format is closer to the value in that format. */ + +guint32 argb32_from_pixbuf(guint32 c) +{ + guint32 o = 0; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + guint32 a = (c & 0xff000000) >> 24; +#else + guint32 a = (c & 0x000000ff); +#endif + if (a != 0) { + // extract color components +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + guint32 r = (c & 0x000000ff); + guint32 g = (c & 0x0000ff00) >> 8; + guint32 b = (c & 0x00ff0000) >> 16; +#else + guint32 r = (c & 0xff000000) >> 24; + guint32 g = (c & 0x00ff0000) >> 16; + guint32 b = (c & 0x0000ff00) >> 8; +#endif + // premultiply + r = premul_alpha(r, a); + b = premul_alpha(b, a); + g = premul_alpha(g, a); + // combine into output + o = (a << 24) | (r << 16) | (g << 8) | (b); + } + return o; +} + +guint32 pixbuf_from_argb32(guint32 c) +{ + guint32 a = (c & 0xff000000) >> 24; + if (a == 0) return 0; + + // extract color components + guint32 r = (c & 0x00ff0000) >> 16; + guint32 g = (c & 0x0000ff00) >> 8; + guint32 b = (c & 0x000000ff); + // unpremultiply; adding a/2 gives correct rounding + // (taken from Cairo sources) + r = (r * 255 + a/2) / a; + b = (b * 255 + a/2) / a; + g = (g * 255 + a/2) / a; + // combine into output +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + guint32 o = (r) | (g << 8) | (b << 16) | (a << 24); +#else + guint32 o = (r << 24) | (g << 16) | (b << 8) | (a); +#endif + return o; +} + +/** + * Convert pixel data from GdkPixbuf format to ARGB. + * This will convert pixel data from GdkPixbuf format to Cairo's native pixel format. + * This involves premultiplying alpha and shuffling around the channels. + * Pixbuf data must have an alpha channel, otherwise the results are undefined + * (usually a segfault). + */ +void +convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int stride) +{ + for (int i = 0; i < h; ++i) { + guint32 *px = reinterpret_cast<guint32*>(data + i*stride); + for (int j = 0; j < w; ++j) { + *px = argb32_from_pixbuf(*px); + ++px; + } + } +} + +/** + * Convert pixel data from ARGB to GdkPixbuf format. + * This will convert pixel data from GdkPixbuf format to Cairo's native pixel format. + * This involves premultiplying alpha and shuffling around the channels. + */ +void +convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int stride) +{ + for (int i = 0; i < h; ++i) { + guint32 *px = reinterpret_cast<guint32*>(data + i*stride); + for (int j = 0; j < w; ++j) { + *px = pixbuf_from_argb32(*px); + ++px; + } + } +} + +/** + * Converts GdkPixbuf's data to premultiplied ARGB. + * This function will convert a GdkPixbuf in place into Cairo's native pixel format. + * Note that this is a hack intended to save memory. When the pixbuf is in Cairo's format, + * using it with GTK will result in corrupted drawings. + */ +void +convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) +{ + convert_pixels_pixbuf_to_argb32( + gdk_pixbuf_get_pixels(pb), + gdk_pixbuf_get_width(pb), + gdk_pixbuf_get_height(pb), + gdk_pixbuf_get_rowstride(pb)); +} + +/** + * Converts GdkPixbuf's data back to its native format. + * Once this is done, the pixbuf can be used with GTK again. + */ +void +convert_pixbuf_argb32_to_normal(GdkPixbuf *pb) +{ + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(pb), + gdk_pixbuf_get_width(pb), + gdk_pixbuf_get_height(pb), + gdk_pixbuf_get_rowstride(pb)); +} + +guint32 argb32_from_rgba(guint32 in) +{ + guint32 r, g, b, a; + a = (in & 0x000000ff); + r = premul_alpha((in & 0xff000000) >> 24, a); + g = premul_alpha((in & 0x00ff0000) >> 16, a); + b = premul_alpha((in & 0x0000ff00) >> 8, a); + ASSEMBLE_ARGB32(px, a, r, g, b) + return px; +} + +/* + 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 : diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h new file mode 100644 index 000000000..dc11231b9 --- /dev/null +++ b/src/display/cairo-utils.h @@ -0,0 +1,156 @@ +/** + * @file + * Cairo integration helpers. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H +#define SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H + +#include <glib.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <cairomm/cairomm.h> +#include <2geom/forward.h> + +struct SPColor; + +namespace Inkscape { + +/** + * RAII idiom for Cairo groups. + * Groups are temporary surfaces used when rendering e.g. masks and opacity. + * Use this class to ensure that each group push is matched with a pop. + */ +class CairoGroup { +public: + CairoGroup(cairo_t *_ct); + ~CairoGroup(); + void push(); + void push_with_content(cairo_content_t content); + cairo_pattern_t *pop(); + Cairo::RefPtr<Cairo::Pattern> popmm(); + void pop_to_source(); +private: + cairo_t *ct; + bool pushed; +}; + +/** RAII idiom for Cairo state saving. */ +class CairoSave { +public: + CairoSave(cairo_t *_ct, bool save=false) + : ct(_ct) + , saved(save) + { + if (save) { + cairo_save(ct); + } + } + void save() { + if (!saved) { + cairo_save(ct); + saved = true; + } + } + ~CairoSave() { + if (saved) + cairo_restore(ct); + } +private: + cairo_t *ct; + bool saved; +}; + +/** Cairo context with Inkscape-specific operations. */ +class CairoContext : public Cairo::Context { +public: + CairoContext(cairo_t *obj, bool ref = false); + + void transform(Geom::Affine const &m); + void set_source_rgba32(guint32 color); + void append_path(Geom::PathVector const &pv); + + static Cairo::RefPtr<CairoContext> create(Cairo::RefPtr<Cairo::Surface> const &target); +}; + +} // namespace Inkscape + +void ink_cairo_set_source_color(cairo_t *ct, SPColor const &color, double opacity); +void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba); +void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m); +void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m); +void ink_cairo_set_source_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y); + +void ink_matrix_to_2geom(Geom::Affine &, cairo_matrix_t const &); +void ink_matrix_to_cairo(cairo_matrix_t &, Geom::Affine const &); + +cairo_surface_t *ink_cairo_surface_copy(cairo_surface_t *s); +cairo_surface_t *ink_cairo_surface_create_identical(cairo_surface_t *s); +cairo_surface_t *ink_cairo_surface_create_same_size(cairo_surface_t *s, cairo_content_t c); +cairo_surface_t *ink_cairo_extract_alpha(cairo_surface_t *s); +cairo_surface_t *ink_cairo_surface_create_output(cairo_surface_t *image, cairo_surface_t *bg); +void ink_cairo_surface_blit(cairo_surface_t *src, cairo_surface_t *dest); +int ink_cairo_surface_get_width(cairo_surface_t *surface); +int ink_cairo_surface_get_height(cairo_surface_t *surface); +guint32 ink_cairo_surface_average_color(cairo_surface_t *surface); +void ink_cairo_surface_average_color(cairo_surface_t *surface, double &r, double &g, double &b, double &a); +void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, double &g, double &b, double &a); + +cairo_pattern_t *ink_cairo_pattern_create_checkerboard(); + +void convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int rs); +void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs); +void convert_pixbuf_normal_to_argb32(GdkPixbuf *); +void convert_pixbuf_argb32_to_normal(GdkPixbuf *); +cairo_surface_t *ink_cairo_surface_create_for_argb32_pixbuf(GdkPixbuf *pb); +void ink_cairo_pixbuf_cleanup(guchar *pixels, void *surface); + +G_GNUC_CONST guint32 argb32_from_pixbuf(guint32 in); +G_GNUC_CONST guint32 pixbuf_from_argb32(guint32 in); +/** Convert a pixel in 0xRRGGBBAA format to Cairo ARGB32 format. */ +G_GNUC_CONST guint32 argb32_from_rgba(guint32 in); + + +G_GNUC_CONST inline guint32 +premul_alpha(guint32 color, guint32 alpha) +{ + guint32 temp = alpha * color + 128; + return (temp + (temp >> 8)) >> 8; +} +G_GNUC_CONST inline guint32 +unpremul_alpha(guint32 color, guint32 alpha) +{ + // NOTE: you must check for alpha != 0 yourself. + return (255 * color + alpha/2) / alpha; +} + +// TODO: move those to 2Geom +void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width); +void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv); + +#define EXTRACT_ARGB32(px,a,r,g,b) \ + guint32 a, r, g, b; \ + a = ((px) & 0xff000000) >> 24; \ + r = ((px) & 0x00ff0000) >> 16; \ + g = ((px) & 0x0000ff00) >> 8; \ + b = ((px) & 0x000000ff); + +#define ASSEMBLE_ARGB32(px,a,r,g,b) \ + guint32 px = (a << 24) | (r << 16) | (g << 8) | b; + +#endif +/* + 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 : diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index f3e1b0f0c..9983e1c4d 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -10,15 +10,19 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <libnr/nr-blit.h> -#include <gtk/gtksignal.h> +#include <gtk/gtk.h> -#include <display/sp-canvas-util.h> +#include "display/sp-canvas-util.h" #include "helper/sp-marshal.h" -#include <display/nr-arena.h> -#include <display/nr-arena-group.h> -#include <display/canvas-arena.h> -#include <display/inkscape-cairo.h> +#include "display/canvas-arena.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-item.h" +#include "display/drawing-group.h" +#include "display/drawing-surface.h" +#include "preferences.h" + +using namespace Inkscape; enum { ARENA_EVENT, @@ -29,39 +33,58 @@ static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass); static void sp_canvas_arena_init(SPCanvasArena *group); static void sp_canvas_arena_destroy(GtkObject *object); +static void sp_canvas_arena_item_deleted(SPCanvasArena *arena, Inkscape::DrawingItem *item); static void sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf); static double sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); +static void sp_canvas_arena_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area); static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event); static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event); -static void sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data); -static void sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data); - -NRArenaEventVector carenaev = { - {NULL}, - sp_canvas_arena_request_update, - sp_canvas_arena_request_render -}; +static void sp_canvas_arena_request_update (SPCanvasArena *ca, DrawingItem *item); +static void sp_canvas_arena_request_render (SPCanvasArena *ca, Geom::IntRect const &area); static SPCanvasItemClass *parent_class; static guint signals[LAST_SIGNAL] = {0}; -GtkType +struct CachePrefObserver : public Inkscape::Preferences::Observer { + CachePrefObserver(SPCanvasArena *arena) + : Inkscape::Preferences::Observer("/options/renderingcache") + , _arena(arena) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + std::vector<Inkscape::Preferences::Entry> v = prefs->getAllEntries(observed_path); + for (unsigned i=0; i<v.size(); ++i) { + notify(v[i]); + } + prefs->addObserver(*this); + } + void notify(Preferences::Entry const &v) { + Glib::ustring name = v.getEntryName(); + if (name == "size") { + _arena->drawing.setCacheBudget((1 << 20) * v.getIntLimited(64, 0, 4096)); + } + } + SPCanvasArena *_arena; +}; + +GType sp_canvas_arena_get_type (void) { - static GtkType type = 0; + static GType type = 0; if (!type) { - GtkTypeInfo info = { - (gchar *)"SPCanvasArena", - sizeof (SPCanvasArena), + GTypeInfo info = { sizeof (SPCanvasArenaClass), - (GtkClassInitFunc) sp_canvas_arena_class_init, - (GtkObjectInitFunc) sp_canvas_arena_init, - NULL, NULL, NULL - }; - type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info); + NULL, NULL, + (GClassInitFunc) sp_canvas_arena_class_init, + NULL, NULL, + sizeof (SPCanvasArena), + 0, + (GInstanceInitFunc) sp_canvas_arena_init, + NULL + }; + type = g_type_register_static (SP_TYPE_CANVAS_ITEM, "SPCanvasArena", &info, (GTypeFlags)0); } return type; } @@ -75,12 +98,13 @@ sp_canvas_arena_class_init (SPCanvasArenaClass *klass) object_class = (GtkObjectClass *) klass; item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); - signals[ARENA_EVENT] = gtk_signal_new ("arena_event", - GTK_RUN_LAST, - GTK_CLASS_TYPE(object_class), + signals[ARENA_EVENT] = g_signal_new ("arena_event", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, ((glong)((guint8*)&(klass->arena_event) - (guint8*)klass)), + NULL, NULL, sp_marshal_INT__POINTER_POINTER, GTK_TYPE_INT, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); @@ -90,6 +114,7 @@ sp_canvas_arena_class_init (SPCanvasArenaClass *klass) item_class->render = sp_canvas_arena_render; item_class->point = sp_canvas_arena_point; item_class->event = sp_canvas_arena_event; + item_class->viewbox_changed = sp_canvas_arena_viewbox_changed; } static void @@ -97,14 +122,28 @@ sp_canvas_arena_init (SPCanvasArena *arena) { arena->sticky = FALSE; - arena->arena = NRArena::create(); - arena->arena->canvasarena = arena; - arena->root = NRArenaGroup::create(arena->arena); - nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE); + new (&arena->drawing) Inkscape::Drawing(arena); - arena->active = NULL; + Inkscape::DrawingGroup *root = new DrawingGroup(arena->drawing); + root->setPickChildren(true); + arena->drawing.setRoot(root); - nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena); + arena->observer = new CachePrefObserver(arena); + + arena->drawing.signal_request_update.connect( + sigc::bind<0>( + sigc::ptr_fun(&sp_canvas_arena_request_update), + arena)); + arena->drawing.signal_request_render.connect( + sigc::bind<0>( + sigc::ptr_fun(&sp_canvas_arena_request_render), + arena)); + arena->drawing.signal_item_deleted.connect( + sigc::bind<0>( + sigc::ptr_fun(&sp_canvas_arena_item_deleted), + arena)); + + arena->active = NULL; } static void @@ -112,22 +151,8 @@ sp_canvas_arena_destroy (GtkObject *object) { SPCanvasArena *arena = SP_CANVAS_ARENA (object); - if (arena->active) { - nr_object_unref ((NRObject *) arena->active); - arena->active = NULL; - } - - if (arena->root) { - nr_arena_item_unref (arena->root); - arena->root = NULL; - } - - if (arena->arena) { - nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena); - - nr_object_unref ((NRObject *) arena->arena); - arena->arena = NULL; - } + delete arena->observer; + arena->drawing.~Drawing(); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); @@ -141,21 +166,22 @@ sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned if (((SPCanvasItemClass *) parent_class)->update) (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags); - arena->gc.transform = affine; - - guint reset; - reset = (flags & SP_CANVAS_UPDATE_AFFINE)? NR_ARENA_ITEM_STATE_ALL : NR_ARENA_ITEM_STATE_NONE; + arena->ctx.ctm = affine; - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset); + unsigned reset = flags & SP_CANVAS_UPDATE_AFFINE ? DrawingItem::STATE_ALL : 0; + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_ALL, reset); - item->x1 = arena->root->bbox.x0 - 1; - item->y1 = arena->root->bbox.y0 - 1; - item->x2 = arena->root->bbox.x1 + 1; - item->y2 = arena->root->bbox.y1 + 1; + Geom::OptIntRect b = arena->drawing.root()->visualBounds(); + if (b) { + item->x1 = b->left() - 1; + item->y1 = b->top() - 1; + item->x2 = b->right() + 1; + item->y2 = b->bottom() + 1; + } if (arena->cursor) { /* Mess with enter/leave notifiers */ - NRArenaItem *new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky); + DrawingItem *new_arena = arena->drawing.pick(arena->c, arena->drawing.delta, arena->sticky); if (new_arena != arena->active) { GdkEventCrossing ec; ec.window = GTK_WIDGET (item->canvas)->window; @@ -169,10 +195,7 @@ sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned ec.type = GDK_LEAVE_NOTIFY; sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } - /* fixme: This is not optimal - better track ::destroy (Lauris) */ - if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = new_arena; - if (arena->active) nr_object_ref ((NRObject *) arena->active); if (arena->active) { ec.type = GDK_ENTER_NOTIFY; sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); @@ -182,45 +205,26 @@ sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned } static void +sp_canvas_arena_item_deleted(SPCanvasArena *arena, Inkscape::DrawingItem *item) +{ + if (arena->active == item) { + arena->active = NULL; + } +} + +static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf) { - gint bw, bh; - + // todo: handle NR_ARENA_ITEM_RENDER_NO_CACHE SPCanvasArena *arena = SP_CANVAS_ARENA (item); - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, - NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, - NR_ARENA_ITEM_STATE_NONE); - - sp_canvas_prepare_buffer(buf); - - bw = buf->rect.x1 - buf->rect.x0; - bh = buf->rect.y1 - buf->rect.y0; - if ((bw < 1) || (bh < 1)) return; - - NRRectL area; - NRPixBlock cb; - - area.x0 = buf->rect.x0; - area.y0 = buf->rect.y0; - area.x1 = buf->rect.x1; - area.y1 = buf->rect.y1; + Geom::OptIntRect r = buf->rect; + if (!r || r->hasZeroArea()) return; - nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, - buf->buf, - buf->buf_rowstride, - FALSE, FALSE); + Inkscape::DrawingContext ct(buf->ct, r->min()); - cb.visible_area = buf->visible_rect; - cairo_t *ct = nr_create_cairo_context (&area, &cb); - nr_arena_item_invoke_render (ct, arena->root, &area, &cb, 0); - - cairo_surface_t *cst = cairo_get_target(ct); - cairo_destroy (ct); - cairo_surface_finish (cst); - cairo_surface_destroy (cst); - - nr_pixblock_release (&cb); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx); + arena->drawing.render(ct, *r); } static double @@ -228,11 +232,8 @@ sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ { SPCanvasArena *arena = SP_CANVAS_ARENA (item); - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, - NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK, - NR_ARENA_ITEM_STATE_NONE); - - NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK); + DrawingItem *picked = arena->drawing.pick(p, arena->drawing.delta, arena->sticky); arena->picked = picked; @@ -244,10 +245,21 @@ sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ return 1e18; } +static void +sp_canvas_arena_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area) +{ + SPCanvasArena *arena = SP_CANVAS_ARENA(item); + // make the cache limit larger than screen to facilitate smooth scrolling + Geom::IntRect expanded = new_area; + Geom::IntPoint expansion(new_area.width()/2, new_area.height()/2); + expanded.expandBy(expansion); + arena->drawing.setCacheLimit(expanded); +} + static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) { - NRArenaItem *new_arena; + Inkscape::DrawingItem *new_arena; /* fixme: This sucks, we have to handle enter/leave notifiers */ SPCanvasArena *arena = SP_CANVAS_ARENA (item); @@ -259,7 +271,6 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) if (!arena->cursor) { if (arena->active) { //g_warning ("Cursor entered to arena with already active item"); - nr_object_unref ((NRObject *) arena->active); } arena->cursor = TRUE; @@ -267,9 +278,8 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) arena->c = Geom::Point(event->crossing.x, event->crossing.y); /* fixme: Not sure abut this, but seems the right thing (Lauris) */ - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE); - arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky); - if (arena->active) nr_object_ref ((NRObject *) arena->active); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK, 0); + arena->active = arena->drawing.pick(arena->c, arena->drawing.delta, arena->sticky); ret = sp_canvas_arena_send_event (arena, event); } break; @@ -277,7 +287,6 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) case GDK_LEAVE_NOTIFY: if (arena->cursor) { ret = sp_canvas_arena_send_event (arena, event); - if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = NULL; arena->cursor = FALSE; } @@ -288,8 +297,8 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) arena->c = Geom::Point(event->motion.x, event->motion.y); /* fixme: Not sure abut this, but seems the right thing (Lauris) */ - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE); - new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK); + new_arena = arena->drawing.pick(arena->c, arena->drawing.delta, arena->sticky); if (new_arena != arena->active) { GdkEventCrossing ec; ec.window = event->motion.window; @@ -303,9 +312,7 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) ec.type = GDK_LEAVE_NOTIFY; ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } - if (arena->active) nr_object_unref ((NRObject *) arena->active); arena->active = new_arena; - if (arena->active) nr_object_ref ((NRObject *) arena->active); if (arena->active) { ec.type = GDK_ENTER_NOTIFY; ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); @@ -329,21 +336,22 @@ sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event) gint ret = FALSE; /* Send event to arena */ - gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret); + g_signal_emit (G_OBJECT (arena), signals[ARENA_EVENT], 0, arena->active, event, &ret); return ret; } static void -sp_canvas_arena_request_update (NRArena */*arena*/, NRArenaItem */*item*/, void *data) +sp_canvas_arena_request_update (SPCanvasArena *ca, DrawingItem */*item*/) { - sp_canvas_item_request_update (SP_CANVAS_ITEM (data)); + sp_canvas_item_request_update (SP_CANVAS_ITEM (ca)); } static void -sp_canvas_arena_request_render (NRArena */*arena*/, NRRectL *area, void *data) +sp_canvas_arena_request_render (SPCanvasArena *ca, Geom::IntRect const &area) { - sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1); + SPCanvas *canvas = SP_CANVAS_ITEM (ca)->canvas; + sp_canvas_request_redraw (canvas, area.left(), area.top(), area.right(), area.bottom()); } void @@ -367,25 +375,16 @@ sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky) } void -sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb) +sp_canvas_arena_render_surface (SPCanvasArena *ca, cairo_surface_t *surface, Geom::IntRect const &r) { - NRRectL area; - g_return_if_fail (ca != NULL); g_return_if_fail (SP_IS_CANVAS_ARENA (ca)); - /* fixme: */ - pb->empty = FALSE; - - area.x0 = pb->area.x0; - area.y0 = pb->area.y0; - area.x1 = pb->area.x1; - area.y1 = pb->area.y1; - - nr_arena_item_invoke_render (NULL, ca->root, &area, pb, 0); + Inkscape::DrawingContext ct(surface, r.min()); + ca->drawing.update(Geom::IntRect::infinite(), ca->ctx); + ca->drawing.render(ct, r); } - /* Local Variables: mode:c++ diff --git a/src/display/canvas-arena.h b/src/display/canvas-arena.h index 7267583f0..26f19732d 100644 --- a/src/display/canvas-arena.h +++ b/src/display/canvas-arena.h @@ -13,19 +13,32 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "sp-canvas-item.h" -#include "nr-arena-item.h" +#include <cairo.h> +#include <2geom/rect.h> +#include "display/drawing.h" +#include "display/drawing-item.h" +#include "display/sp-canvas.h" +#include "display/sp-canvas-item.h" G_BEGIN_DECLS #define SP_TYPE_CANVAS_ARENA (sp_canvas_arena_get_type ()) -#define SP_CANVAS_ARENA(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CANVAS_ARENA, SPCanvasArena)) -#define SP_CANVAS_ARENA_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_CANVAS_ARENA, SPCanvasArenaClass)) -#define SP_IS_CANVAS_ARENA(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CANVAS_ARENA)) -#define SP_IS_CANVAS_ARENA_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_CANVAS_ARENA)) +#define SP_CANVAS_ARENA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVAS_ARENA, SPCanvasArena)) +#define SP_CANVAS_ARENA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CANVAS_ARENA, SPCanvasArenaClass)) +#define SP_IS_CANVAS_ARENA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CANVAS_ARENA)) +#define SP_IS_CANVAS_ARENA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CANVAS_ARENA)) typedef struct _SPCanvasArena SPCanvasArena; typedef struct _SPCanvasArenaClass SPCanvasArenaClass; +struct CachePrefObserver; + +namespace Inkscape { + +class Drawing; +class DrawingItem; + +} // namespace Inkscape + struct _SPCanvasArena { SPCanvasItem item; @@ -34,28 +47,28 @@ struct _SPCanvasArena { guint sticky : 1; Geom::Point c; // what is this? - NRArena *arena; - NRArenaItem *root; - NRGC gc; + Inkscape::Drawing drawing; + Inkscape::UpdateContext ctx; - NRArenaItem *active; + Inkscape::DrawingItem *active; /* fixme: */ - NRArenaItem *picked; - gdouble delta; + Inkscape::DrawingItem *picked; + CachePrefObserver *observer; + double delta; }; struct _SPCanvasArenaClass { SPCanvasItemClass parent_class; - gint (* arena_event) (SPCanvasArena *carena, NRArenaItem *item, GdkEvent *event); + gint (* arena_event) (SPCanvasArena *carena, Inkscape::DrawingItem *item, GdkEvent *event); }; -GtkType sp_canvas_arena_get_type (void); +GType sp_canvas_arena_get_type (void); void sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta); void sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky); -void sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb); +void sp_canvas_arena_render_surface (SPCanvasArena *ca, cairo_surface_t *surface, Geom::IntRect const &area); G_END_DECLS diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 845c4e077..6d453367f 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -1,5 +1,9 @@ /* - * Copyright (C) 2006-2008 Johan Engelen <johan@shouraizou.nl> + * Authors: + * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl> + * + * Copyright (C) 2006-2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information */ /* @@ -12,30 +16,27 @@ /* * TODO: * THIS FILE AND THE HEADER FILE NEED CLEANING UP. PLEASE DO NOT HESISTATE TO DO SO. - * For example: the line drawing code should not be here. There _must_ be a function somewhere else that can provide this functionality... */ -#include "sp-canvas-util.h" -#include "canvas-axonomgrid.h" -#include "util/mathfns.h" -#include "2geom/line.h" -#include <libnr/nr-pixops.h> +#include "display/canvas-axonomgrid.h" +#include "2geom/line.h" +#include "desktop.h" #include "canvas-grid.h" #include "desktop-handles.h" +#include "display/cairo-utils.h" +#include "display/canvas-grid.h" +#include "display/sp-canvas-util.h" +#include "document.h" #include "helper/units.h" -#include "svg/svg-color.h" -#include "xml/node-event-vector.h" -#include "sp-object.h" - -#include "sp-namedview.h" #include "inkscape.h" -#include "desktop.h" - -#include "document.h" #include "preferences.h" - -#define SAFE_SETPIXEL //undefine this when it is certain that setpixel is never called with invalid params +#include "sp-namedview.h" +#include "sp-object.h" +#include "svg/svg-color.h" +#include "util/mathfns.h" +#include "xml/node-event-vector.h" +#include "round.h" enum Dim3 { X=0, Y, Z }; @@ -45,103 +46,29 @@ enum Dim3 { X=0, Y, Z }; static double deg_to_rad(double deg) { return deg*M_PI/180.0;} - /** - \brief This function renders a pixel on a particular buffer. - - The topleft of the buffer equals - ( rect.x0 , rect.y0 ) in screen coordinates - ( 0 , 0 ) in setpixel coordinates - The bottomright of the buffer equals - ( rect.x1 , rect,y1 ) in screen coordinates - ( rect.x1 - rect.x0 , rect.y1 - rect.y0 ) in setpixel coordinates -*/ -static void -sp_caxonomgrid_setpixel (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) -{ -#ifdef SAFE_SETPIXEL - if ( (x >= buf->rect.x0) && (x < buf->rect.x1) && (y >= buf->rect.y0) && (y < buf->rect.y1) ) { -#endif - guint r, g, b, a; - r = NR_RGBA32_R (rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - guchar * p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); -#ifdef SAFE_SETPIXEL - } -#endif -} - -/** - \brief This function renders a line on a particular canvas buffer, - using Bresenham's line drawing function. - http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html - Coordinates are interpreted as SCREENcoordinates -*/ + * This function calls Cairo to render a line on a particular canvas buffer. + * Coordinates are interpreted as SCREENcoordinates + */ static void sp_caxonomgrid_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) { - int dy = y1 - y0; - int dx = x1 - x0; - int stepx, stepy; - - if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } - if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } - dy <<= 1; // dy is now 2*dy - dx <<= 1; // dx is now 2*dx - - sp_caxonomgrid_setpixel(buf, x0, y0, rgba); - if (dx > dy) { - int fraction = dy - (dx >> 1); // same as 2*dy - dx - while (x0 != x1) { - if (fraction >= 0) { - y0 += stepy; - fraction -= dx; // same as fraction -= 2*dx - } - x0 += stepx; - fraction += dy; // same as fraction -= 2*dy - sp_caxonomgrid_setpixel(buf, x0, y0, rgba); - } - } else { - int fraction = dx - (dy >> 1); - while (y0 != y1) { - if (fraction >= 0) { - x0 += stepx; - fraction -= dy; - } - y0 += stepy; - fraction += dx; - sp_caxonomgrid_setpixel(buf, x0, y0, rgba); - } - } - + cairo_move_to(buf->ct, 0.5 + x0, 0.5 + y0); + cairo_line_to(buf->ct, 0.5 + x1, 0.5 + y1); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); } static void sp_grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba) { - if ((x >= buf->rect.x0) && (x < buf->rect.x1)) { - guint r, g, b, a; - gint y0, y1, y; - guchar *p; - r = NR_RGBA32_R(rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - y0 = MAX (buf->rect.y0, ys); - y1 = MIN (buf->rect.y1, ye + 1); - p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - for (y = y0; y < y1; y++) { - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); - p += buf->buf_rowstride; - } - } + if ((x < buf->rect.left()) || (x >= buf->rect.right())) + return; + + cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys); + cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); } namespace Inkscape { @@ -262,6 +189,10 @@ static gboolean sp_nv_read_length(gchar const *str, guint base, gdouble *val, SP *unit = &sp_unit_get_by_id(SP_UNIT_M); } else if (!strncmp(u, "in", 2)) { *unit = &sp_unit_get_by_id(SP_UNIT_IN); + } else if (!strncmp(u, "ft", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_FT); + } else if (!strncmp(u, "pc", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_PC); } else { return FALSE; } @@ -416,13 +347,13 @@ _wr.setUpdating (true); Inkscape::UI::Widget::RegisteredSuffixedInteger *_rsi = Gtk::manage( new Inkscape::UI::Widget::RegisteredSuffixedInteger( _("_Major grid line every:"), "", _("lines"), "empspacing", _wr, repr, doc ) ); - _rsu_ox->setDigits(4); + _rsu_ox->setDigits(5); _rsu_ox->setIncrements(0.1, 1.0); - _rsu_oy->setDigits(4); + _rsu_oy->setDigits(5); _rsu_oy->setIncrements(0.1, 1.0); - _rsu_sy->setDigits(4); + _rsu_sy->setDigits(5); _rsu_sy->setIncrements(0.1, 1.0); _wr.setUpdating (false); @@ -545,7 +476,6 @@ CanvasAxonomGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) if (empspacing == 0) { scaled = true; } - } void @@ -561,34 +491,36 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) _empcolor = empcolor; } + cairo_save(buf->ct); + cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top()); + cairo_set_line_width(buf->ct, 1.0); + cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); + // gc = gridcoordinates (the coordinates calculated from the grids origin 'grid->ow'. - // sc = screencoordinates ( for example "buf->rect.x0" is in screencoordinates ) - // bc = buffer patch coordinates + // sc = screencoordinates ( for example "buf->rect.left()" is in screencoordinates ) + // bc = buffer patch coordinates (x=0 on left side of page, y=0 on bottom of page) // tl = topleft ; br = bottomright Geom::Point buf_tl_gc; Geom::Point buf_br_gc; - buf_tl_gc[Geom::X] = buf->rect.x0 - ow[Geom::X]; - buf_tl_gc[Geom::Y] = buf->rect.y0 - ow[Geom::Y]; - buf_br_gc[Geom::X] = buf->rect.x1 - ow[Geom::X]; - buf_br_gc[Geom::Y] = buf->rect.y1 - ow[Geom::Y]; - - gdouble x; - gdouble y; + buf_tl_gc[Geom::X] = buf->rect.left() - ow[Geom::X]; + buf_tl_gc[Geom::Y] = buf->rect.top() - ow[Geom::Y]; + buf_br_gc[Geom::X] = buf->rect.right() - ow[Geom::X]; + buf_br_gc[Geom::Y] = buf->rect.bottom() - ow[Geom::Y]; // render the three separate line groups representing the main-axes // x-axis always goes from topleft to bottomright. (0,0) - (1,1) gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle[X]) - buf_tl_gc[Geom::Y] ; - gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.y0; - gint const xlinestart = (gint) Inkscape::round( (xstart_y_sc - buf->rect.x0*tan_angle[X] -ow[Geom::Y]) / lyw ); + gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.top(); + gint const xlinestart = round( (xstart_y_sc - buf_tl_gc[Geom::X]*tan_angle[X] - ow[Geom::Y]) / lyw ); gint xlinenum = xlinestart; // lines starting on left side. - for (y = xstart_y_sc; y < buf->rect.y1; y += lyw, xlinenum++) { - gint const x0 = buf->rect.x0; - gint const y0 = (gint) Inkscape::round(y); - gint const x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - y) / tan_angle[X] ); - gint const y1 = buf->rect.y1; + for (gdouble y = xstart_y_sc; y < buf->rect.bottom(); y += lyw, xlinenum++) { + gint const x0 = buf->rect.left(); + gint const y0 = round(y); + gint const x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle[X] ); + gint const y1 = buf->rect.bottom(); if (!scaled && (xlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); @@ -597,13 +529,13 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) } } // lines starting from top side - gdouble const xstart_x_sc = buf->rect.x0 + (lxw_x - (xstart_y_sc - buf->rect.y0) / tan_angle[X]) ; + gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle[X]) ; xlinenum = xlinestart-1; - for (x = xstart_x_sc; x < buf->rect.x1; x += lxw_x, xlinenum--) { - gint const y0 = buf->rect.y0; - gint const y1 = buf->rect.y1; - gint const x0 = (gint) Inkscape::round(x); - gint const x1 = x0 + (gint) Inkscape::round( (y1 - y0) / tan_angle[X] ); + for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) { + gint const y0 = buf->rect.top(); + gint const y1 = buf->rect.bottom(); + gint const x0 = round(x); + gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] ); if (!scaled && (xlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); @@ -614,29 +546,30 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) // y-axis lines (vertical) gdouble const ystart_x_sc = floor (buf_tl_gc[Geom::X] / spacing_ylines) * spacing_ylines + ow[Geom::X]; - gint const ylinestart = (gint) Inkscape::round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); + gint const ylinestart = round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); gint ylinenum = ylinestart; - for (x = ystart_x_sc; x < buf->rect.x1; x += spacing_ylines, ylinenum++) { - gint const x0 = (gint) Inkscape::round(x); + for (gdouble x = ystart_x_sc; x < buf->rect.right(); x += spacing_ylines, ylinenum++) { + gint const x0 = round(x); if (!scaled && (ylinenum % empspacing) != 0) { - sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, color); + sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, color); } else { - sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, _empcolor); + sp_grid_vline (buf, x0, buf->rect.top(), buf->rect.bottom() - 1, _empcolor); } } // z-axis always goes from bottomleft to topright. (0,1) - (1,0) gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle[Z]) - buf_tl_gc[Geom::Y] ; - gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.y0; - gint const zlinestart = (gint) Inkscape::round( (zstart_y_sc + buf->rect.x0*tan_angle[Z] - ow[Geom::Y]) / lyw ); + gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.top(); + gint const zlinestart = round( (zstart_y_sc + buf_tl_gc[Geom::X]*tan_angle[Z] - ow[Geom::Y]) / lyw ); gint zlinenum = zlinestart; // lines starting from left side - for (y = zstart_y_sc; y < buf->rect.y1; y += lyw, zlinenum++) { - gint const x0 = buf->rect.x0; - gint const y0 = (gint) Inkscape::round(y); - gint const x1 = x0 + (gint) Inkscape::round( (y - buf->rect.y0 ) / tan_angle[Z] ); - gint const y1 = buf->rect.y0; + gdouble next_y = zstart_y_sc; + for (gdouble y = zstart_y_sc; y < buf->rect.bottom(); y += lyw, zlinenum++, next_y = y) { + gint const x0 = buf->rect.left(); + gint const y0 = round(y); + gint const x1 = x0 + round( (y - buf->rect.top() ) / tan_angle[Z] ); + gint const y1 = buf->rect.top(); if (!scaled && (zlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); @@ -645,12 +578,12 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) } } // draw lines from bottom-up - gdouble const zstart_x_sc = buf->rect.x0 + (y - buf->rect.y1) / tan_angle[Z] ; - for (x = zstart_x_sc; x < buf->rect.x1; x += lxw_z, zlinenum++) { - gint const y0 = buf->rect.y1; - gint const y1 = buf->rect.y0; - gint const x0 = (gint) Inkscape::round(x); - gint const x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - buf->rect.y0) / tan_angle[Z] ); + gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle[Z] ; + for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) { + gint const y0 = buf->rect.bottom(); + gint const y1 = buf->rect.top(); + gint const x0 = round(x); + gint const x1 = x0 + round(buf->rect.height() / tan_angle[Z] ); if (!scaled && (zlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); @@ -658,6 +591,8 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, _empcolor); } } + + cairo_restore(buf->ct); } CanvasAxonomGridSnapper::CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) @@ -752,7 +687,7 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const { inters = Geom::intersection(line_x, line_z); } - catch (Geom::InfiniteSolutions e) + catch (Geom::InfiniteSolutions &e) { // We're probably dealing with parallel lines; this is useless! return s; @@ -773,33 +708,33 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const if (use_left_half) { s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_max))); s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_min))); - s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_max, 0))); + s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_max, 0))); } if (use_right_half) { s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_min))); s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_max))); - s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_min, 0))); + s.push_back(std::make_pair(Geom::Point(1, 0), Geom::Point(x_min, 0))); } return s; } -void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +void CanvasAxonomGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const { SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); - sc.grid_lines.push_back(dummy); + isr.grid_lines.push_back(dummy); } -void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +void CanvasAxonomGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const { SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); - sc.points.push_back(dummy); + isr.points.push_back(dummy); } bool CanvasAxonomGridSnapper::ThisSnapperMightSnap() const { - return _snap_enabled && _snapmanager->snapprefs.getSnapToGrids() && _snapmanager->snapprefs.getSnapModeBBoxOrNodes(); + return _snap_enabled && _snapmanager->snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GRID); } diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index 282524c74..1f8bad51d 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -24,7 +24,7 @@ #include "canvas-grid.h" -struct SPDesktop; +class SPDesktop; struct SPNamedView; namespace Inkscape { @@ -78,8 +78,8 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; - void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; + void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; CanvasAxonomGrid *grid; }; diff --git a/src/display/canvas-bpath.cpp b/src/display/canvas-bpath.cpp index 725a1d0ff..14f120600 100644 --- a/src/display/canvas-bpath.cpp +++ b/src/display/canvas-bpath.cpp @@ -15,21 +15,16 @@ #endif #include <sstream> #include <string.h> -#include <desktop.h> +#include "desktop.h" #include "color.h" -#include "sp-canvas-util.h" -#include "inkscape-cairo.h" -#include "canvas-bpath.h" #include "display/sp-canvas-group.h" +#include "display/sp-canvas-util.h" +#include "display/canvas-bpath.h" #include "display/curve.h" -#include "display/inkscape-cairo.h" -#include "libnr/nr-pixops.h" +#include "display/cairo-utils.h" #include "helper/geom.h" - -void nr_pixblock_render_bpath_rgba (Shape* theS,uint32_t color,NRRectL &area,char* destBuf,int stride); - static void sp_canvas_bpath_class_init (SPCanvasBPathClass *klass); static void sp_canvas_bpath_init (SPCanvasBPath *path); static void sp_canvas_bpath_destroy (GtkObject *object); @@ -40,20 +35,22 @@ static double sp_canvas_bpath_point (SPCanvasItem *item, Geom::Point p, SPCanvas static SPCanvasItemClass *parent_class; -GtkType +GType sp_canvas_bpath_get_type (void) { - static GtkType type = 0; + static GType type = 0; if (!type) { - GtkTypeInfo info = { - (gchar *)"SPCanvasBPath", - sizeof (SPCanvasBPath), + GTypeInfo info = { sizeof (SPCanvasBPathClass), - (GtkClassInitFunc) sp_canvas_bpath_class_init, - (GtkObjectInitFunc) sp_canvas_bpath_init, - NULL, NULL, NULL - }; - type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info); + NULL, NULL, + (GClassInitFunc) sp_canvas_bpath_class_init, + NULL, NULL, + sizeof (SPCanvasBPath), + 0, + (GInstanceInitFunc) sp_canvas_bpath_init, + NULL + }; + type = g_type_register_static (SP_TYPE_CANVAS_ITEM, "SPCanvasBPath", &info, (GTypeFlags)0); } return type; } @@ -67,7 +64,7 @@ sp_canvas_bpath_class_init (SPCanvasBPathClass *klass) object_class = GTK_OBJECT_CLASS (klass); item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_canvas_bpath_destroy; @@ -139,9 +136,7 @@ sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf) { SPCanvasBPath *cbp = SP_CANVAS_BPATH (item); - sp_canvas_prepare_buffer(buf); - - Geom::Rect area (Geom::Point(buf->rect.x0, buf->rect.y0), Geom::Point(buf->rect.x1, buf->rect.y1)); + Geom::Rect area = buf->rect; if ( !cbp->curve || ((cbp->stroke_rgba & 0xff) == 0 && (cbp->fill_rgba & 0xff) == 0 ) || @@ -154,33 +149,29 @@ sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf) bool dofill = ((cbp->fill_rgba & 0xff) != 0); bool dostroke = ((cbp->stroke_rgba & 0xff) != 0); - cairo_set_tolerance(buf->ct, 1.25); // low quality, but good enough for canvas items + cairo_set_tolerance(buf->ct, 0.5); cairo_new_path(buf->ct); - if (!dofill) - feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, true, 1); - else - feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, false, 1); + feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, + /* optimized_stroke = */ !dofill, 1); if (dofill) { // RGB / BGR - cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cbp->fill_rgba), SP_RGBA32_G_F(cbp->fill_rgba), SP_RGBA32_R_F(cbp->fill_rgba), SP_RGBA32_A_F(cbp->fill_rgba)); + ink_cairo_set_source_rgba32(buf->ct, cbp->fill_rgba); cairo_set_fill_rule(buf->ct, cbp->fill_rule == SP_WIND_RULE_EVENODD? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); - if (dostroke) - cairo_fill_preserve(buf->ct); - else - cairo_fill(buf->ct); + cairo_fill_preserve(buf->ct); } if (dostroke) { - // RGB / BGR - cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cbp->stroke_rgba), SP_RGBA32_G_F(cbp->stroke_rgba), SP_RGBA32_R_F(cbp->stroke_rgba), SP_RGBA32_A_F(cbp->stroke_rgba)); + ink_cairo_set_source_rgba32(buf->ct, cbp->stroke_rgba); cairo_set_line_width(buf->ct, 1); if (cbp->dashes[0] != 0 && cbp->dashes[1] != 0) { cairo_set_dash (buf->ct, cbp->dashes, 2, 0); } cairo_stroke(buf->ct); + } else { + cairo_new_path(buf->ct); } } @@ -192,12 +183,12 @@ sp_canvas_bpath_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ if ( !cbp->curve || ((cbp->stroke_rgba & 0xff) == 0 && (cbp->fill_rgba & 0xff) == 0 ) || cbp->curve->get_segment_count() < 1) - return NR_HUGE; + return Geom::infinity(); double width = 0.5; Geom::Rect viewbox = item->canvas->getViewbox(); viewbox.expandBy (width); - double dist = NR_HUGE; + double dist = Geom::infinity(); pathv_matrix_point_bbox_wind_distance(cbp->curve->get_pathvector(), cbp->affine, p, NULL, NULL, &dist, 0.5, &viewbox); if (dist <= 1.0) { diff --git a/src/display/canvas-bpath.h b/src/display/canvas-bpath.h index 7f8b75dfe..f0520f012 100644 --- a/src/display/canvas-bpath.h +++ b/src/display/canvas-bpath.h @@ -15,7 +15,7 @@ * */ -#include <glib/gtypes.h> +#include <glib.h> #include "sp-canvas-item.h" @@ -25,10 +25,10 @@ struct SPCanvasGroup; struct SPCurve; #define SP_TYPE_CANVAS_BPATH (sp_canvas_bpath_get_type ()) -#define SP_CANVAS_BPATH(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CANVAS_BPATH, SPCanvasBPath)) -#define SP_CANVAS_BPATH_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_CANVAS_BPATH, SPCanvasBPathClass)) -#define SP_IS_CANVAS_BPATH(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CANVAS_BPATH)) -#define SP_IS_CANVAS_BPATH_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_CANVAS_BPATH)) +#define SP_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVAS_BPATH, SPCanvasBPath)) +#define SP_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CANVAS_BPATH, SPCanvasBPathClass)) +#define SP_IS_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CANVAS_BPATH)) +#define SP_IS_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CANVAS_BPATH)) #define bpath_liv @@ -90,7 +90,7 @@ struct SPCanvasBPathClass { SPCanvasItemClass parent_class; }; -GtkType sp_canvas_bpath_get_type (void); +GType sp_canvas_bpath_get_type (void); SPCanvasItem *sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve); diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index 79bfc31ad..734ce2043 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -1,5 +1,5 @@ /** @file - * @brief Cartesian grid implementation + * Cartesian grid implementation. */ /* Copyright (C) Johan Engelen 2006-2007 <johan@shouraizou.nl> * Copyright (C) Lauris Kaplinski 2000 @@ -12,25 +12,25 @@ * Don't be shy to correct things. */ -#include "sp-canvas-group.h" +#include "desktop.h" #include "sp-canvas-util.h" #include "util/mathfns.h" -#include "libnr/nr-pixops.h" #include "desktop-handles.h" +#include "display/cairo-utils.h" +#include "display/canvas-axonomgrid.h" +#include "display/canvas-grid.h" +#include "display/sp-canvas-util.h" +#include "display/sp-canvas-group.h" +#include "document.h" #include "helper/units.h" -#include "svg/svg-color.h" -#include "xml/node-event-vector.h" -#include "sp-object.h" - -#include "sp-namedview.h" #include "inkscape.h" -#include "desktop.h" - -#include "../document.h" #include "preferences.h" - -#include "canvas-grid.h" -#include "canvas-axonomgrid.h" +#include "sp-namedview.h" +#include "sp-object.h" +#include "svg/svg-color.h" +#include "svg/stringstream.h" +#include "util/mathfns.h" +#include "xml/node-event-vector.h" using Inkscape::DocumentUndo; @@ -57,22 +57,24 @@ static void grid_canvasitem_render (SPCanvasItem *item, SPCanvasBuf *buf); static SPCanvasItemClass * parent_class; -GtkType +GType grid_canvasitem_get_type (void) { - static GtkType grid_canvasitem_type = 0; + static GType grid_canvasitem_type = 0; if (!grid_canvasitem_type) { - GtkTypeInfo grid_canvasitem_info = { - (gchar *)"GridCanvasItem", - sizeof (GridCanvasItem), + GTypeInfo grid_canvasitem_info = { sizeof (GridCanvasItemClass), - (GtkClassInitFunc) grid_canvasitem_class_init, - (GtkObjectInitFunc) grid_canvasitem_init, - NULL, NULL, - (GtkClassInitFunc) NULL - }; - grid_canvasitem_type = gtk_type_unique (sp_canvas_item_get_type (), &grid_canvasitem_info); + NULL, NULL, + (GClassInitFunc) grid_canvasitem_class_init, + NULL, NULL, + sizeof (GridCanvasItem), + 0, + (GInstanceInitFunc) grid_canvasitem_init, + NULL + }; + + grid_canvasitem_type = g_type_register_static (sp_canvas_item_get_type (), "GridCanvasItem", &grid_canvasitem_info, (GTypeFlags)0); } return grid_canvasitem_type; } @@ -86,7 +88,7 @@ grid_canvasitem_class_init (GridCanvasItemClass *klass) object_class = (GtkObjectClass *) klass; item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ()); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = grid_canvasitem_destroy; @@ -376,6 +378,22 @@ bool CanvasGrid::isEnabled() return snapper->getEnabled(); } +void CanvasGrid::setOrigin(Geom::Point const &origin_px) +{ + Inkscape::SVGOStringStream os_x, os_y; + gdouble val; + + val = origin_px[Geom::X]; + val = sp_pixels_get_units (val, *gridunit); + os_x << val << sp_unit_get_abbreviation(gridunit); + val = origin_px[Geom::Y]; + val = sp_pixels_get_units (val, *gridunit); + os_y << val << sp_unit_get_abbreviation(gridunit); + repr->setAttribute("originx", os_x.str().c_str()); + repr->setAttribute("originy", os_y.str().c_str()); +} + + // ########################################################## // CanvasXYGrid @@ -426,8 +444,9 @@ CanvasXYGrid::CanvasXYGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, SPD { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/xy/units").data() ); - if (!gridunit) + if (!gridunit) { gridunit = &sp_unit_get_by_id(SP_UNIT_PX); + } origin[Geom::X] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_x", 0.0), *gridunit); origin[Geom::Y] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_y", 0.0), *gridunit); color = prefs->getInt("/options/grids/xy/color", 0x0000ff20); @@ -491,6 +510,10 @@ sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **uni *unit = &sp_unit_get_by_id(SP_UNIT_M); } else if (!strncmp(u, "in", 2)) { *unit = &sp_unit_get_by_id(SP_UNIT_IN); + } else if (!strncmp(u, "ft", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_FT); + } else if (!strncmp(u, "pc", 2)) { + *unit = &sp_unit_get_by_id(SP_UNIT_PC); } else { return FALSE; } @@ -696,16 +719,16 @@ CanvasXYGrid::newSpecificWidget() _wr.setUpdating (true); - _rsu_ox->setDigits(4); + _rsu_ox->setDigits(5); _rsu_ox->setIncrements(0.1, 1.0); - _rsu_oy->setDigits(4); + _rsu_oy->setDigits(5); _rsu_oy->setIncrements(0.1, 1.0); - _rsu_sx->setDigits(4); + _rsu_sx->setDigits(5); _rsu_sx->setIncrements(0.1, 1.0); - _rsu_sy->setDigits(4); + _rsu_sy->setDigits(5); _rsu_sy->setIncrements(0.1, 1.0); Inkscape::UI::Widget::RegisteredCheckButton * _rcb_dotted = Gtk::manage( @@ -831,74 +854,46 @@ CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) static void grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba) { - if ((y >= buf->rect.y0) && (y < buf->rect.y1)) { - guint r, g, b, a; - gint x0, x1, x; - guchar *p; - r = NR_RGBA32_R (rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - x0 = MAX (buf->rect.x0, xs); - x1 = MIN (buf->rect.x1, xe + 1); - p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4; - for (x = x0; x < x1; x++) { - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); - p += 4; - } - } + if ((y < buf->rect.top()) || (y >= buf->rect.bottom())) + return; + + cairo_move_to(buf->ct, 0.5 + xs, 0.5 + y); + cairo_line_to(buf->ct, 0.5 + xe, 0.5 + y); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); } static void grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba) { - if ((x >= buf->rect.x0) && (x < buf->rect.x1)) { - guint r, g, b, a; - gint y0, y1, y; - guchar *p; - r = NR_RGBA32_R(rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - y0 = MAX (buf->rect.y0, ys); - y1 = MIN (buf->rect.y1, ye + 1); - p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - for (y = y0; y < y1; y++) { - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); - p += buf->buf_rowstride; - } - } + if ((x < buf->rect.left()) || (x >= buf->rect.right())) + return; + + cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys); + cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_stroke(buf->ct); } static void grid_dot (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) { - if ( (y >= buf->rect.y0) && (y < buf->rect.y1) - && (x >= buf->rect.x0) && (x < buf->rect.x1) ) { - guint r, g, b, a; - guchar *p; - r = NR_RGBA32_R (rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); - } + if ( (y < buf->rect.top()) || (y >= buf->rect.bottom()) + || (x < buf->rect.left()) || (x >= buf->rect.right()) ) + return; + + cairo_rectangle(buf->ct, x, y, 1, 1); + ink_cairo_set_source_rgba32(buf->ct, rgba); + cairo_fill(buf->ct); } void CanvasXYGrid::Render (SPCanvasBuf *buf) { - gdouble const sxg = floor ((buf->rect.x0 - ow[Geom::X]) / sw[Geom::X]) * sw[Geom::X] + ow[Geom::X]; - gint const xlinestart = (gint) Inkscape::round((sxg - ow[Geom::X]) / sw[Geom::X]); - gdouble const syg = floor ((buf->rect.y0 - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y]; - gint const ylinestart = (gint) Inkscape::round((syg - ow[Geom::Y]) / sw[Geom::Y]); + gdouble const sxg = floor ((buf->rect.left() - ow[Geom::X]) / sw[Geom::X]) * sw[Geom::X] + ow[Geom::X]; + gint const xlinestart = round((sxg - ow[Geom::X]) / sw[Geom::X]); + gdouble const syg = floor ((buf->rect.top() - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y]; + gint const ylinestart = round((syg - ow[Geom::Y]) / sw[Geom::Y]); //set correct coloring, depending preference (when zoomed out, always major coloring or minor coloring) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -910,38 +905,43 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) _empcolor = empcolor; } + cairo_save(buf->ct); + cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top()); + cairo_set_line_width(buf->ct, 1.0); + cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); + if (!render_dotted) { gint ylinenum; gdouble y; - for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[Geom::Y], ylinenum++) { - gint const y0 = (gint) Inkscape::round(y); + for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { + gint const y0 = round(y); if (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) { - grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color); + grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, color); } else { - grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, _empcolor); + grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, _empcolor); } } gint xlinenum; gdouble x; - for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[Geom::X], xlinenum++) { - gint const ix = (gint) Inkscape::round(x); + for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) { + gint const ix = round(x); if (!scaled[Geom::X] && (xlinenum % empspacing) != 0) { - grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color); + grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), color); } else { - grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, _empcolor); + grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), _empcolor); } } } else { gint ylinenum; gdouble y; - for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[Geom::Y], ylinenum++) { - gint const iy = (gint) Inkscape::round(y); + for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { + gint const iy = round(y); gint xlinenum; gdouble x; - for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[Geom::X], xlinenum++) { - gint const ix = (gint) Inkscape::round(x); + for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) { + gint const ix = round(x); if ( (!scaled[Geom::X] && (xlinenum % empspacing) != 0) || (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) || ((scaled[Geom::X] || scaled[Geom::Y]) && no_emp_when_zoomed_out) ) @@ -961,6 +961,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) } } + cairo_restore(buf->ct); } CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) @@ -1012,29 +1013,31 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const Geom::Coord rounded; Geom::Point point_on_line; + Geom::Point cvec(0.,0.); + cvec[i] = 1.; rounded = Inkscape::Util::round_to_upper_multiple_plus(p[i], spacing, grid->origin[i]); point_on_line = i ? Geom::Point(0, rounded) : Geom::Point(rounded, 0); - s.push_back(std::make_pair(component_vectors[i], point_on_line)); + s.push_back(std::make_pair(cvec, point_on_line)); rounded = Inkscape::Util::round_to_lower_multiple_plus(p[i], spacing, grid->origin[i]); point_on_line = i ? Geom::Point(0, rounded) : Geom::Point(rounded, 0); - s.push_back(std::make_pair(component_vectors[i], point_on_line)); + s.push_back(std::make_pair(cvec, point_on_line)); } return s; } -void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const +void CanvasXYGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const { SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line); - sc.grid_lines.push_back(dummy); + isr.grid_lines.push_back(dummy); } -void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +void CanvasXYGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const { SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); - sc.points.push_back(dummy); + isr.points.push_back(dummy); } /** @@ -1042,7 +1045,7 @@ void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point c */ bool CanvasXYGridSnapper::ThisSnapperMightSnap() const { - return _snap_enabled && _snapmanager->snapprefs.getSnapToGrids() && _snapmanager->snapprefs.getSnapModeBBoxOrNodes(); + return _snap_enabled && _snapmanager->snapprefs.isTargetSnappable(Inkscape::SNAPTARGET_GRID); } } // namespace Inkscape diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index f386fe05e..2788316fd 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -1,5 +1,5 @@ /** @file - * @brief Cartesian grid item for the Inkscape canvas + * Cartesian grid item for the Inkscape canvas. */ /* Copyright (C) Johan Engelen 2006-2007 <johan@shouraizou.nl> * Copyright (C) Lauris Kaplinski 2000 @@ -24,10 +24,10 @@ #include "snapper.h" #include "line-snapper.h" -struct SPDesktop; +class SPDesktop; struct SPNamedView; struct SPCanvasBuf; -class SPDocument; +class SPDocument; namespace Inkscape { @@ -39,15 +39,14 @@ enum GridType { #define GRID_MAXTYPENR 1 #define INKSCAPE_TYPE_GRID_CANVASITEM (Inkscape::grid_canvasitem_get_type ()) -#define INKSCAPE_GRID_CANVASITEM(obj) (GTK_CHECK_CAST ((obj), INKSCAPE_TYPE_GRID_CANVASITEM, GridCanvasItem)) -#define INKSCAPE_GRID_CANVASITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), INKSCAPE_TYPE_GRID_CANVASITEM, GridCanvasItem)) -#define INKSCAPE_IS_GRID_CANVASITEM(obj) (GTK_CHECK_TYPE ((obj), INKSCAPE_TYPE_GRID_CANVASITEM)) -#define INKSCAPE_IS_GRID_CANVASITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), INKSCAPE_TYPE_GRID_CANVASITEM)) +#define INKSCAPE_GRID_CANVASITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INKSCAPE_TYPE_GRID_CANVASITEM, GridCanvasItem)) +#define INKSCAPE_GRID_CANVASITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INKSCAPE_TYPE_GRID_CANVASITEM, GridCanvasItem)) +#define INKSCAPE_IS_GRID_CANVASITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INKSCAPE_TYPE_GRID_CANVASITEM)) +#define INKSCAPE_IS_GRID_CANVASITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INKSCAPE_TYPE_GRID_CANVASITEM)) class CanvasGrid; -/** \brief All the variables that are tracked for a grid specific - canvas item. */ +/** All the variables that are tracked for a grid specific canvas item. */ struct GridCanvasItem : public SPCanvasItem{ CanvasGrid *grid; // the owning grid object }; @@ -57,7 +56,7 @@ struct GridCanvasItemClass { }; /* Standard Gtk function */ -GtkType grid_canvasitem_get_type (void); +GType grid_canvasitem_get_type (void); @@ -87,6 +86,7 @@ public: Gtk::Widget * newWidget(); + void setOrigin(Geom::Point const &origin_px); /**< writes new origin (specified in px units) to SVG */ Geom::Point origin; /**< Origin of the grid */ guint32 color; /**< Color for normal lines */ guint32 empcolor; /**< Color for emphasis lines */ @@ -167,8 +167,8 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; - void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; + void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; CanvasXYGrid *grid; }; diff --git a/src/display/canvas-temporary-item-list.cpp b/src/display/canvas-temporary-item-list.cpp index c324a5ddf..b0fec98b5 100644 --- a/src/display/canvas-temporary-item-list.cpp +++ b/src/display/canvas-temporary-item-list.cpp @@ -1,4 +1,4 @@ -/** \file +/* * Provides a class that can contain active TemporaryItem's on a desktop * Code inspired by message-stack.cpp * diff --git a/src/display/canvas-temporary-item-list.h b/src/display/canvas-temporary-item-list.h index 47556b9f1..d204c692f 100644 --- a/src/display/canvas-temporary-item-list.h +++ b/src/display/canvas-temporary-item-list.h @@ -1,9 +1,7 @@ #ifndef INKSCAPE_CANVAS_TEMPORARY_ITEM_LIST_H #define INKSCAPE_CANVAS_TEMPORARY_ITEM_LIST_H -/** \file - * Provides a class that can contain active TemporaryItem's on a desktop - * +/* * Authors: * Johan Engelen * @@ -12,16 +10,20 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "forward.h" #include <list> +#include <glib.h> struct SPCanvasItem; +class SPDesktop; namespace Inkscape { namespace Display { class TemporaryItem; +/** + * Provides a class that can contain active TemporaryItem's on a desktop. + */ class TemporaryItemList { public: TemporaryItemList(SPDesktop *desktop); diff --git a/src/display/canvas-temporary-item.cpp b/src/display/canvas-temporary-item.cpp index ccef4d0cb..abe5f2f1b 100644 --- a/src/display/canvas-temporary-item.cpp +++ b/src/display/canvas-temporary-item.cpp @@ -1,4 +1,4 @@ -/** \file +/* * Provides a class that can contain active TemporaryItem's on a desktop * When the object is deleted, it also deletes the canvasitem it contains! * This object should be created/managed by a TemporaryItemList. @@ -16,7 +16,7 @@ #include "display/canvas-temporary-item.h" -#include <gtk/gtkobject.h> +#include <gtk/gtk.h> namespace Inkscape { namespace Display { diff --git a/src/display/canvas-temporary-item.h b/src/display/canvas-temporary-item.h index b73907bad..09d243fa1 100644 --- a/src/display/canvas-temporary-item.h +++ b/src/display/canvas-temporary-item.h @@ -1,9 +1,7 @@ #ifndef INKSCAPE_CANVAS_TEMPORARY_ITEM_H #define INKSCAPE_CANVAS_TEMPORARY_ITEM_H -/** \file - * Provides a class to put a canvasitem temporarily on-canvas. - * +/* * Authors: * Johan Engelen * @@ -15,13 +13,16 @@ #include <stddef.h> #include <sigc++/sigc++.h> -#include <glib/gtypes.h> +#include <glib.h> struct SPCanvasItem; namespace Inkscape { namespace Display { +/** + * Provides a class to put a canvasitem temporarily on-canvas. + */ class TemporaryItem { public: TemporaryItem(SPCanvasItem *item, guint lifetime, bool destroy_on_deselect = false); diff --git a/src/display/canvas-text.cpp b/src/display/canvas-text.cpp index 70196b307..809bb4eeb 100644 --- a/src/display/canvas-text.cpp +++ b/src/display/canvas-text.cpp @@ -13,19 +13,18 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "sp-canvas-util.h" -#include "canvas-text.h" -#include "display/inkscape-cairo.h" -#include <sstream> -#include <string.h> -#include "desktop.h" - #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include <color.h> -#include <libnr/nr-pixops.h> +#include <sstream> +#include <string.h> + +#include "sp-canvas-util.h" +#include "canvas-text.h" +#include "display/cairo-utils.h" +#include "desktop.h" +#include "color.h" static void sp_canvastext_class_init (SPCanvasTextClass *klass); static void sp_canvastext_init (SPCanvasText *canvastext); @@ -36,21 +35,23 @@ static void sp_canvastext_render (SPCanvasItem *item, SPCanvasBuf *buf); static SPCanvasItemClass *parent_class_ct; -GtkType +GType sp_canvastext_get_type (void) { - static GtkType type = 0; + static GType type = 0; if (!type) { - GtkTypeInfo info = { - (gchar *)"SPCanvasText", - sizeof (SPCanvasText), + GTypeInfo info = { sizeof (SPCanvasTextClass), - (GtkClassInitFunc) sp_canvastext_class_init, - (GtkObjectInitFunc) sp_canvastext_init, - NULL, NULL, NULL - }; - type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info); + NULL, NULL, + (GClassInitFunc) sp_canvastext_class_init, + NULL, NULL, + sizeof (SPCanvasText), + 0, + (GInstanceInitFunc) sp_canvastext_init, + NULL + }; + type = g_type_register_static (SP_TYPE_CANVAS_ITEM, "SPCanvasText", &info, (GTypeFlags)0); } return type; } @@ -61,7 +62,7 @@ sp_canvastext_class_init (SPCanvasTextClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; - parent_class_ct = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class_ct = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_canvastext_destroy; @@ -72,8 +73,11 @@ sp_canvastext_class_init (SPCanvasTextClass *klass) static void sp_canvastext_init (SPCanvasText *canvastext) { + canvastext->anchor_position = TEXT_ANCHOR_CENTER; canvastext->rgba = 0x33337fff; canvastext->rgba_stroke = 0xffffffff; + canvastext->rgba_background = 0x0000007f; + canvastext->background = false; canvastext->s[Geom::X] = canvastext->s[Geom::Y] = 0.0; canvastext->affine = Geom::identity(); canvastext->fontsize = 10.0; @@ -98,9 +102,6 @@ sp_canvastext_destroy (GtkObject *object) (* GTK_OBJECT_CLASS (parent_class_ct)->destroy) (object); } -// FIXME: remove this as soon as we know how to correctly determine the text extent -static const double arbitrary_factor = 0.8; - // these are set in sp_canvastext_update() and then re-used in sp_canvastext_render(), which is called afterwards static double anchor_offset_x = 0; static double anchor_offset_y = 0; @@ -114,22 +115,37 @@ sp_canvastext_render (SPCanvasItem *item, SPCanvasBuf *buf) return; Geom::Point s = cl->s * cl->affine; - double offsetx = s[Geom::X] - buf->rect.x0; - double offsety = s[Geom::Y] - buf->rect.y0; + double offsetx = s[Geom::X] - buf->rect.left(); + double offsety = s[Geom::Y] - buf->rect.top(); offsetx -= anchor_offset_x; - offsety += anchor_offset_y; + offsety -= anchor_offset_y; - cairo_move_to(buf->ct, offsetx, offsety); cairo_set_font_size(buf->ct, cl->fontsize); + + if (cl->background){ + cairo_text_extents_t extents; + cairo_text_extents(buf->ct, cl->text, &extents); + + double border = extents.height*0.5; + cairo_rectangle(buf->ct, offsetx - extents.x_bearing - border, + offsety + extents.y_bearing - border, + extents.width + 2*border, + extents.height + 2*border); + + ink_cairo_set_source_rgba32(buf->ct, cl->rgba_background); + cairo_fill(buf->ct); + } + + cairo_move_to(buf->ct, offsetx, offsety); cairo_text_path(buf->ct, cl->text); - cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cl->rgba_stroke), SP_RGBA32_G_F(cl->rgba_stroke), SP_RGBA32_R_F(cl->rgba_stroke), SP_RGBA32_A_F(cl->rgba_stroke)); - cairo_set_line_width (buf->ct, 2.0); - cairo_stroke_preserve(buf->ct); - cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(cl->rgba), SP_RGBA32_G_F(cl->rgba), SP_RGBA32_R_F(cl->rgba), SP_RGBA32_A_F(cl->rgba)); + if (cl->outline){ + ink_cairo_set_source_rgba32(buf->ct, cl->rgba_stroke); + cairo_set_line_width (buf->ct, 2.0); + cairo_stroke_preserve(buf->ct); + } + ink_cairo_set_source_rgba32(buf->ct, cl->rgba); cairo_fill(buf->ct); - - cairo_new_path(buf->ct); } static void @@ -150,24 +166,52 @@ sp_canvastext_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned i // set up a temporary cairo_t to measure the text extents; it would be better to compute this in the render() // method but update() seems to be called before so we don't have the information available when we need it - /** - cairo_t tmp_buf; - cairo_text_extents_t bbox; - cairo_text_extents(&tmp_buf, cl->text, &bbox); - **/ - item->x1 = s[Geom::X] + 0; - item->y1 = s[Geom::Y] - cl->fontsize; - item->x2 = s[Geom::X] + cl->fontsize * strlen(cl->text); - item->y2 = s[Geom::Y] + cl->fontsize * 0.5; // for letters below the baseline + cairo_surface_t *tmp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + cairo_t* tmp_buf = cairo_create(tmp_surface); + + cairo_set_font_size(tmp_buf, cl->fontsize); + cairo_text_extents_t extents; + cairo_text_extents(tmp_buf, cl->text, &extents); + double border = extents.height; + + item->x1 = s[Geom::X] - extents.x_bearing - 2*border; + item->y1 = s[Geom::Y] + extents.y_bearing - 2*border; + item->x2 = s[Geom::X] + extents.width + 2*border; + item->y2 = s[Geom::Y] + extents.height + 2*border; // adjust update region according to anchor shift - // FIXME: use the correct text extent - anchor_offset_x = arbitrary_factor * cl->fontsize * strlen(cl->text) * (cl->anchor_x + 1.0) / 2.0; - anchor_offset_y = cl->fontsize * (cl->anchor_y + 1.0) / 2.0; + switch (cl->anchor_position){ + case TEXT_ANCHOR_LEFT: + anchor_offset_x = -2*border; + anchor_offset_y = -extents.height/2; + break; + case TEXT_ANCHOR_RIGHT: + anchor_offset_x = extents.width + 2*border; + anchor_offset_y = -extents.height/2; + break; + case TEXT_ANCHOR_BOTTOM: + anchor_offset_x = extents.width/2; + anchor_offset_y = 2*border; + break; + case TEXT_ANCHOR_TOP: + anchor_offset_x = extents.width/2; + anchor_offset_y = -extents.height - 2*border; + break; + case TEXT_ANCHOR_ZERO: + anchor_offset_x = 0; + anchor_offset_y = 0; + break; + case TEXT_ANCHOR_CENTER: + default: + anchor_offset_x = extents.width/2; + anchor_offset_y = -extents.height/2; + break; + } + item->x1 -= anchor_offset_x; item->x2 -= anchor_offset_x; - item->y1 += anchor_offset_y; - item->y2 += anchor_offset_y; + item->y1 -= anchor_offset_y; + item->y2 -= anchor_offset_y; sp_canvas_request_redraw (item->canvas, (int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2); } diff --git a/src/display/canvas-text.h b/src/display/canvas-text.h index d8bd86b7d..90c02717c 100644 --- a/src/display/canvas-text.h +++ b/src/display/canvas-text.h @@ -16,17 +16,31 @@ #include "sp-canvas-item.h" -struct SPItem; -struct SPDesktop; +class SPItem; +class SPDesktop; #define SP_TYPE_CANVASTEXT (sp_canvastext_get_type ()) -#define SP_CANVASTEXT(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CANVASTEXT, SPCanvasText)) -#define SP_IS_CANVASTEXT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CANVASTEXT)) +#define SP_CANVASTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVASTEXT, SPCanvasText)) +#define SP_IS_CANVASTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CANVASTEXT)) + +enum CanvasTextAnchorPositionEnum { + TEXT_ANCHOR_CENTER, + TEXT_ANCHOR_TOP, + TEXT_ANCHOR_BOTTOM, + TEXT_ANCHOR_LEFT, + TEXT_ANCHOR_RIGHT, + TEXT_ANCHOR_ZERO +}; struct SPCanvasText : public SPCanvasItem { SPItem *item; // the item to which this line belongs in some sense; may be NULL for some users guint32 rgba; guint32 rgba_stroke; + guint32 rgba_background; + bool outline; + bool background; + CanvasTextAnchorPositionEnum anchor_position; + SPDesktop *desktop; // the desktop to which this text is attached; needed for coordinate transforms (TODO: these should be eliminated) gchar* text; @@ -38,7 +52,7 @@ struct SPCanvasText : public SPCanvasItem { }; struct SPCanvasTextClass : public SPCanvasItemClass{}; -GtkType sp_canvastext_get_type (void); +GType sp_canvastext_get_type (void); SPCanvasItem *sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gchar const *text); @@ -53,6 +67,7 @@ void sp_canvastext_set_anchor (SPCanvasText *ct, double anchor_x, double anchor_ #endif // SEEN_SP_CANVASTEXT_H + /* Local Variables: mode:c++ diff --git a/src/display/curve-test.h b/src/display/curve-test.h index d89cb4c99..3d698ca07 100644 --- a/src/display/curve-test.h +++ b/src/display/curve-test.h @@ -21,7 +21,7 @@ public: path1.close(); // Closed path (ClosingSegment is zero length) path2.append(Geom::LineSegment(Geom::Point(2,0),Geom::Point(3,0))); - path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); + // TODO fix path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); path2.close(); // Open path path3.append(Geom::SVGEllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1))); diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 5c18324eb..1a788b59a 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -19,7 +19,7 @@ #include "display/curve.h" -#include <glib/gmessages.h> +#include <glib.h> #include <2geom/pathvector.h> #include <2geom/sbasis-geometric.h> #include <2geom/sbasis-to-bezier.h> @@ -322,7 +322,7 @@ SPCurve::is_closed() const return false; } else { bool closed = true; - for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) { + for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) { if ( ! it->closed() ) { closed = false; break; @@ -506,11 +506,11 @@ SPCurve::append(SPCurve const *curve2, _pathv.push_back( (*it) ); } - for (it++; it != curve2->_pathv.end(); it++) { + for (it++; it != curve2->_pathv.end(); ++it) { _pathv.push_back( (*it) ); } } else { - for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) { + for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); ++it) { _pathv.push_back( (*it) ); } } @@ -553,7 +553,7 @@ SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance) newfirstpath.setInitial(lastpath.finalPoint()); lastpath.append( newfirstpath ); - for (path_it++; path_it != c1->_pathv.end(); path_it++) { + for (++path_it; path_it != c1->_pathv.end(); ++path_it) { _pathv.push_back( (*path_it) ); } diff --git a/src/display/curve.h b/src/display/curve.h index ec828e674..4866655c4 100644 --- a/src/display/curve.h +++ b/src/display/curve.h @@ -1,9 +1,7 @@ #ifndef SEEN_DISPLAY_CURVE_H #define SEEN_DISPLAY_CURVE_H -/** \file - * Wrapper around a Geom::PathVector objects. - * +/* * Author: * Lauris Kaplinski <lauris@kaplinski.com> * @@ -15,13 +13,15 @@ * Released under GNU GPL */ -#include <glib/gtypes.h> -#include <glib/gslist.h> +#include <glib.h> #include <2geom/forward.h> #include <boost/optional.hpp> +/** + * Wrapper around a Geom::PathVector objects. + */ class SPCurve { public: /* Constructors */ diff --git a/src/display/drawing-context.cpp b/src/display/drawing-context.cpp new file mode 100644 index 000000000..de5beb0f6 --- /dev/null +++ b/src/display/drawing-context.cpp @@ -0,0 +1,155 @@ +/** + * @file + * Cairo drawing context with Inkscape extensions. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/cairo-utils.h" +#include "helper/geom.h" + +namespace Inkscape { + +using Geom::X; +using Geom::Y; + +/** + * @class DrawingContext::Save + * RAII idiom for saving the state of DrawingContext. + */ + +DrawingContext::Save::Save() + : _ct(NULL) +{} +DrawingContext::Save::Save(DrawingContext &ct) + : _ct(&ct) +{ + _ct->save(); +} +DrawingContext::Save::~Save() +{ + if (_ct) { + _ct->restore(); + } +} +void DrawingContext::Save::save(DrawingContext &ct) +{ + if (_ct) { + // TODO: it might be better to treat this occurence as a bug + _ct->restore(); + } + _ct = &ct; + _ct->save(); +} + +/** + * @class DrawingContext + * Minimal wrapper over Cairo. + * + * This is a wrapper over cairo_t, extended with operations that work + * with 2Geom geometrical primitives. Some of this is probably duplicated + * in cairo-render-context.cpp, which provides higher level operations + * for drawing entire SPObjects when exporting. + */ + +DrawingContext::DrawingContext(cairo_t *ct, Geom::Point const &origin) + : _ct(ct) + , _surface(new DrawingSurface(cairo_get_group_target(ct), origin)) + , _delete_surface(true) + , _restore_context(true) +{ + _surface->_has_context = true; + cairo_reference(_ct); + cairo_save(_ct); + cairo_translate(_ct, -origin[Geom::X], -origin[Geom::Y]); +} + +DrawingContext::DrawingContext(cairo_surface_t *surface, Geom::Point const &origin) + : _ct(NULL) + , _surface(new DrawingSurface(surface, origin)) + , _delete_surface(true) + , _restore_context(false) +{ + _surface->_has_context = true; + _ct = _surface->createRawContext(); +} + +DrawingContext::DrawingContext(DrawingSurface &s) + : _ct(s.createRawContext()) + , _surface(&s) + , _delete_surface(false) + , _restore_context(false) +{} + +DrawingContext::~DrawingContext() +{ + if (_restore_context) { + cairo_restore(_ct); + } + cairo_destroy(_ct); + _surface->_has_context = false; + if (_delete_surface) { + delete _surface; + } +} + +void DrawingContext::arc(Geom::Point const ¢er, double radius, Geom::AngleInterval const &angle) +{ + double from = angle.initialAngle(); + double to = angle.finalAngle(); + if (to > from) { + cairo_arc(_ct, center[X], center[Y], radius, from, to); + } else { + cairo_arc_negative(_ct, center[X], center[Y], radius, to, from); + } +} + +void DrawingContext::transform(Geom::Affine const &trans) { + ink_cairo_transform(_ct, trans); +} + +void DrawingContext::path(Geom::PathVector const &pv) { + feed_pathvector_to_cairo(_ct, pv); +} + +void DrawingContext::paint(double alpha) { + if (alpha == 1.0) cairo_paint(_ct); + else cairo_paint_with_alpha(_ct, alpha); +} +void DrawingContext::setSource(guint32 rgba) { + ink_cairo_set_source_rgba32(_ct, rgba); +} +void DrawingContext::setSource(DrawingSurface *s) { + Geom::Point origin = s->origin(); + cairo_set_source_surface(_ct, s->raw(), origin[X], origin[Y]); +} +void DrawingContext::setSourceCheckerboard() { + cairo_pattern_t *check = ink_cairo_pattern_create_checkerboard(); + cairo_set_source(_ct, check); + cairo_pattern_destroy(check); +} + +Geom::Rect DrawingContext::targetLogicalBounds() const +{ + Geom::Rect ret(_surface->area()); + return ret; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h new file mode 100644 index 000000000..fb6662202 --- /dev/null +++ b/src/display/drawing-context.h @@ -0,0 +1,128 @@ +/** + * @file + * Cairo drawing context with Inkscape extensions. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_CONTEXT_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_CONTEXT_H + +#include <boost/utility.hpp> +#include <glib.h> +#include <cairo.h> +#include <2geom/affine.h> +#include <2geom/angle.h> +#include <2geom/rect.h> +#include <2geom/transforms.h> + +namespace Inkscape { + +class DrawingSurface; + +class DrawingContext + : boost::noncopyable +{ +public: + class Save { + public: + Save(); + Save(DrawingContext &ct); + ~Save(); + void save(DrawingContext &ct); + private: + DrawingContext *_ct; + }; + + DrawingContext(cairo_t *ct, Geom::Point const &origin); + DrawingContext(cairo_surface_t *surface, Geom::Point const &origin); + DrawingContext(DrawingSurface &s); + ~DrawingContext(); + + void save() { cairo_save(_ct); } + void restore() { cairo_restore(_ct); } + void pushGroup() { cairo_push_group(_ct); } + void pushAlphaGroup() { cairo_push_group_with_content(_ct, CAIRO_CONTENT_ALPHA); } + void popGroupToSource() { cairo_pop_group_to_source(_ct); } + + void transform(Geom::Affine const &trans); + void translate(Geom::Point const &t) { cairo_translate(_ct, t[Geom::X], t[Geom::Y]); } // todo: take Translate + void translate(double dx, double dy) { cairo_translate(_ct, dx, dy); } + void scale(Geom::Scale const &s) { cairo_scale(_ct, s[Geom::X], s[Geom::Y]); } + void scale(double sx, double sy) { cairo_scale(_ct, sx, sy); } + + void moveTo(Geom::Point const &p) { cairo_move_to(_ct, p[Geom::X], p[Geom::Y]); } + void lineTo(Geom::Point const &p) { cairo_line_to(_ct, p[Geom::X], p[Geom::Y]); } + void curveTo(Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3) { + cairo_curve_to(_ct, p1[Geom::X], p1[Geom::Y], p2[Geom::X], p2[Geom::Y], p3[Geom::X], p3[Geom::Y]); + } + void arc(Geom::Point const ¢er, double radius, Geom::AngleInterval const &angle); + void rectangle(Geom::Rect const &r) { + cairo_rectangle(_ct, r.left(), r.top(), r.width(), r.height()); + } + void rectangle(Geom::IntRect const &r) { + cairo_rectangle(_ct, r.left(), r.top(), r.width(), r.height()); + } + void newPath() { cairo_new_path(_ct); } + void newSubpath() { cairo_new_sub_path(_ct); } + void path(Geom::PathVector const &pv); + + void paint(double alpha = 1.0); + void fill() { cairo_fill(_ct); } + void fillPreserve() { cairo_fill_preserve(_ct); } + void stroke() { cairo_stroke(_ct); } + void strokePreserve() { cairo_stroke_preserve(_ct); } + void clip() { cairo_clip(_ct); } + + void setLineWidth(double w) { cairo_set_line_width(_ct, w); } + void setLineCap(cairo_line_cap_t cap) { cairo_set_line_cap(_ct, cap); } + void setLineJoin(cairo_line_join_t join) { cairo_set_line_join(_ct, join); } + void setMiterLimit(double miter) { cairo_set_miter_limit(_ct, miter); } + void setFillRule(cairo_fill_rule_t rule) { cairo_set_fill_rule(_ct, rule); } + void setOperator(cairo_operator_t op) { cairo_set_operator(_ct, op); } + void setTolerance(double tol) { cairo_set_tolerance(_ct, tol); } + void setSource(cairo_pattern_t *source) { cairo_set_source(_ct, source); } + void setSource(cairo_surface_t *surface, double x, double y) { + cairo_set_source_surface(_ct, surface, x, y); + } + void setSource(double r, double g, double b, double a = 1.0) { + cairo_set_source_rgba(_ct, r, g, b, a); + } + void setSource(guint32 rgba); + void setSource(DrawingSurface *s); + void setSourceCheckerboard(); + + Geom::Rect targetLogicalBounds() const; + + cairo_t *raw() { return _ct; } + cairo_surface_t *rawTarget() { return cairo_get_group_target(_ct); } + +private: + DrawingContext(cairo_t *ct, DrawingSurface *surface, bool destroy); + + cairo_t *_ct; + DrawingSurface *_surface; + bool _delete_surface; + bool _restore_context; + + friend class DrawingSurface; +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp new file mode 100644 index 000000000..6d52b89fc --- /dev/null +++ b/src/display/drawing-group.cpp @@ -0,0 +1,166 @@ +/** + * @file + * Group belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/drawing.h" +#include "display/drawing-context.h" +#include "display/drawing-item.h" +#include "display/drawing-group.h" +#include "style.h" + +namespace Inkscape { + +DrawingGroup::DrawingGroup(Drawing &drawing) + : DrawingItem(drawing) + , _style(NULL) + , _child_transform(NULL) +{} + +DrawingGroup::~DrawingGroup() +{ + if (_style) + sp_style_unref(_style); +} + +/** + * Set whether the group returns children from pick calls. + * Previously this feature was called "transparent groups". + */ +void +DrawingGroup::setPickChildren(bool p) +{ + _pick_children = p; +} + +void +DrawingGroup::setStyle(SPStyle *style) +{ + _setStyleCommon(_style, style); +} + +/** + * Set additional transform for the group. + * This is applied after the normal transform and mainly useful for + * markers, clipping paths, etc. + */ +void +DrawingGroup::setChildTransform(Geom::Affine const &new_trans) +{ + Geom::Affine current; + if (_child_transform) { + current = *_child_transform; + } + + if (!Geom::are_near(current, new_trans, 1e-18)) { + // mark the area where the object was for redraw. + _markForRendering(); + if (new_trans.isIdentity()) { + delete _child_transform; // delete NULL; is safe + _child_transform = NULL; + } else { + _child_transform = new Geom::Affine(new_trans); + } + _markForUpdate(STATE_ALL, true); + } +} + +unsigned +DrawingGroup::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + unsigned beststate = STATE_ALL; + bool outline = _drawing.outline(); + + UpdateContext child_ctx(ctx); + if (_child_transform) { + child_ctx.ctm = *_child_transform * ctx.ctm; + } + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->update(area, child_ctx, flags, reset); + } + if (beststate & STATE_BBOX) { + _bbox = Geom::OptIntRect(); + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + if (i->visible()) { + _bbox.unionWith(outline ? i->geometricBounds() : i->visualBounds()); + } + } + } + return beststate; +} + +unsigned +DrawingGroup::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) +{ + if (stop_at == NULL) { + // normal rendering + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->render(ct, area, flags, stop_at); + } + } else { + // background rendering + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + if (&*i == stop_at) return RENDER_OK; // do not render the stop_at item at all + if (i->isAncestorOf(stop_at)) { + // render its ancestors without masks, opacity or filters + i->render(ct, area, flags | RENDER_FILTER_BACKGROUND, stop_at); + // stop further rendering + return RENDER_OK; + } else { + i->render(ct, area, flags, stop_at); + } + } + } + return RENDER_OK; +} + +void +DrawingGroup::_clipItem(DrawingContext &ct, Geom::IntRect const &area) +{ + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->clip(ct, area); + } +} + +DrawingItem * +DrawingGroup::_pickItem(Geom::Point const &p, double delta, unsigned flags) +{ + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + DrawingItem *picked = i->pick(p, delta, flags); + if (picked) { + return _pick_children ? picked : this; + } + } + return NULL; +} + +bool +DrawingGroup::_canClip() +{ + return true; +} + +bool is_drawing_group(DrawingItem *item) +{ + return dynamic_cast<DrawingGroup *>(item) != NULL; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-group.h b/src/display/drawing-group.h new file mode 100644 index 000000000..974b3c977 --- /dev/null +++ b/src/display/drawing-group.h @@ -0,0 +1,62 @@ +/** + * @file + * Group belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_GROUP_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_GROUP_H + +#include "display/drawing-item.h" + +class SPStyle; + +namespace Inkscape { + +class DrawingGroup + : public DrawingItem +{ +public: + DrawingGroup(Drawing &drawing); + ~DrawingGroup(); + + bool pickChildren() { return _pick_children; } + void setPickChildren(bool p); + + void setStyle(SPStyle *style); + void setChildTransform(Geom::Affine const &new_trans); + +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, + DrawingItem *stop_at); + virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area); + virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); + virtual bool _canClip(); + + SPStyle *_style; + Geom::Affine *_child_transform; +}; + +bool is_drawing_group(DrawingItem *item); + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp new file mode 100644 index 000000000..0c8ac9681 --- /dev/null +++ b/src/display/drawing-image.cpp @@ -0,0 +1,263 @@ +/** + * @file + * Bitmap image belonging to an SVG drawing. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/drawing.h" +#include "display/drawing-context.h" +#include "display/drawing-image.h" +#include "preferences.h" +#include "style.h" + +namespace Inkscape { + +DrawingImage::DrawingImage(Drawing &drawing) + : DrawingItem(drawing) + , _pixbuf(NULL) + , _surface(NULL) + , _style(NULL) +{} + +DrawingImage::~DrawingImage() +{ + if (_style) + sp_style_unref(_style); + if (_pixbuf) { + cairo_surface_destroy(_surface); + g_object_unref(_pixbuf); + } +} + +void +DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb) +{ + // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1 + if (pb != NULL) { + g_object_ref (pb); + } + if (_pixbuf != NULL) { + g_object_unref(_pixbuf); + cairo_surface_destroy(_surface); + } + _pixbuf = pb; + _surface = pb ? ink_cairo_surface_create_for_argb32_pixbuf(pb) : NULL; + + _markForUpdate(STATE_ALL, false); +} + +void +DrawingImage::setStyle(SPStyle *style) +{ + _setStyleCommon(_style, style); +} + +void +DrawingImage::setScale(double sx, double sy) +{ + _scale = Geom::Scale(sx, sy); + _markForUpdate(STATE_ALL, false); +} + +void +DrawingImage::setOrigin(Geom::Point const &o) +{ + _origin = o; + _markForUpdate(STATE_ALL, false); +} + +void +DrawingImage::setClipbox(Geom::Rect const &box) +{ + _clipbox = box; + _markForUpdate(STATE_ALL, false); +} + +Geom::Rect +DrawingImage::bounds() const +{ + if (!_pixbuf) return _clipbox; + + double pw = gdk_pixbuf_get_width(_pixbuf); + double ph = gdk_pixbuf_get_height(_pixbuf); + double vw = pw * _scale[Geom::X]; + double vh = ph * _scale[Geom::Y]; + Geom::Point wh(vw, vh); + Geom::Rect view(_origin, _origin+wh); + Geom::OptRect res = _clipbox & view; + Geom::Rect ret = res ? *res : _clipbox; + + return ret; +} + +unsigned +DrawingImage::_updateItem(Geom::IntRect const &, UpdateContext const &, unsigned, unsigned) +{ + _markForRendering(); + + // Calculate bbox + if (_pixbuf) { + Geom::Rect r = bounds() * _ctm; + _bbox = r.roundOutwards(); + } else { + _bbox = Geom::OptIntRect(); + } + + return STATE_ALL; +} + +unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) +{ + bool outline = _drawing.outline(); + + if (!outline) { + if (!_pixbuf) return RENDER_OK; + + Inkscape::DrawingContext::Save save(ct); + ct.transform(_ctm); + ct.newPath(); + ct.rectangle(_clipbox); + ct.clip(); + + ct.translate(_origin); + ct.scale(_scale); + ct.setSource(_surface, 0, 0); + + cairo_matrix_t tt; + Geom::Affine total; + cairo_get_matrix(ct.raw(), &tt); + ink_matrix_to_2geom(total, tt); + + if (total.expansionX() > 1.0 || total.expansionY() > 1.0) { + cairo_pattern_t *p = cairo_get_source(ct.raw()); + cairo_pattern_set_filter(p, CAIRO_FILTER_NEAREST); + } + //ct.paint(_opacity); + ct.paint(); + + } else { // outline; draw a rect instead + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff); + + { Inkscape::DrawingContext::Save save(ct); + ct.transform(_ctm); + ct.newPath(); + + Geom::Rect r = bounds(); + Geom::Point c00 = r.corner(0); + Geom::Point c01 = r.corner(3); + Geom::Point c11 = r.corner(2); + Geom::Point c10 = r.corner(1); + + ct.moveTo(c00); + // the box + ct.lineTo(c10); + ct.lineTo(c11); + ct.lineTo(c01); + ct.lineTo(c00); + // the diagonals + ct.lineTo(c11); + ct.moveTo(c10); + ct.lineTo(c01); + } + + ct.setLineWidth(0.5); + ct.setSource(rgba); + ct.stroke(); + } + return RENDER_OK; +} + +/** Calculates the closest distance from p to the segment a1-a2*/ +static double +distance_to_segment (Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2) +{ + // calculate sides of the triangle and their squares + double d1 = Geom::L2(p - a1); + double d1_2 = d1 * d1; + double d2 = Geom::L2(p - a2); + double d2_2 = d2 * d2; + double a = Geom::L2(a1 - a2); + double a_2 = a * a; + + // if one of the angles at the base is > 90, return the corresponding side + if (d1_2 + a_2 <= d2_2) return d1; + if (d2_2 + a_2 <= d1_2) return d2; + + // otherwise calculate the height to the base + double peri = (a + d1 + d2)/2; + return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a); +} + +DrawingItem * +DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) +{ + if (!_pixbuf) return NULL; + + bool outline = _drawing.outline(); + + if (outline) { + Geom::Rect r = bounds(); + + Geom::Point c00 = r.corner(0); + Geom::Point c01 = r.corner(3); + Geom::Point c11 = r.corner(2); + Geom::Point c10 = r.corner(1); + + // frame + if (distance_to_segment (p, c00, c10) < delta) return this; + if (distance_to_segment (p, c10, c11) < delta) return this; + if (distance_to_segment (p, c11, c01) < delta) return this; + if (distance_to_segment (p, c01, c00) < delta) return this; + + // diagonals + if (distance_to_segment (p, c00, c11) < delta) return this; + if (distance_to_segment (p, c10, c01) < delta) return this; + + return NULL; + + } else { + unsigned char *const pixels = gdk_pixbuf_get_pixels(_pixbuf); + int width = gdk_pixbuf_get_width(_pixbuf); + int height = gdk_pixbuf_get_height(_pixbuf); + int rowstride = gdk_pixbuf_get_rowstride(_pixbuf); + + Geom::Point tp = p * _ctm.inverse(); + Geom::Rect r = bounds(); + + if (!r.contains(tp)) + return NULL; + + double vw = width * _scale[Geom::X]; + double vh = height * _scale[Geom::Y]; + int ix = floor((tp[Geom::X] - _origin[Geom::X]) / vw * width); + int iy = floor((tp[Geom::Y] - _origin[Geom::Y]) / vh * height); + + if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height)) + return NULL; + + unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4; + // pick if the image is less than 99% transparent + float alpha = (pix_ptr[3] / 255.0f) * _opacity; + return alpha > 0.01 ? this : NULL; + } +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h new file mode 100644 index 000000000..306096d0e --- /dev/null +++ b/src/display/drawing-image.h @@ -0,0 +1,67 @@ +/** + * @file + * Bitmap image belonging to an SVG drawing. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_IMAGE_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_IMAGE_H + +#include <cairo.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <2geom/transforms.h> + +#include "display/drawing-item.h" + +namespace Inkscape { + +class DrawingImage + : public DrawingItem +{ +public: + DrawingImage(Drawing &drawing); + ~DrawingImage(); + + void setARGB32Pixbuf(GdkPixbuf *pb); + void setStyle(SPStyle *style); + void setScale(double sx, double sy); + void setOrigin(Geom::Point const &o); + void setClipbox(Geom::Rect const &box); + Geom::Rect bounds() const; + +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, + DrawingItem *stop_at); + virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); + + GdkPixbuf *_pixbuf; + cairo_surface_t *_surface; + SPStyle *_style; + + // TODO: the following three should probably be merged into a new Geom::Viewbox object + Geom::Rect _clipbox; ///< for preserveAspectRatio + Geom::Point _origin; + Geom::Scale _scale; +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp new file mode 100644 index 000000000..df44a3de0 --- /dev/null +++ b/src/display/drawing-item.cpp @@ -0,0 +1,912 @@ +/** + * @file + * Canvas item belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <climits> +#include "display/cairo-utils.h" +#include "display/cairo-templates.h" +#include "display/drawing.h" +#include "display/drawing-context.h" +#include "display/drawing-item.h" +#include "display/drawing-group.h" +#include "display/drawing-surface.h" +#include "nr-filter.h" +#include "preferences.h" +#include "style.h" + +namespace Inkscape { + +/** + * @class DrawingItem + * SVG drawing item for display. + * + * This was previously known as NRArenaItem. It represents the renderable + * portion of the SVG document. Typically this is created by the SP tree, + * in particular the show() virtual function. + * + * @section ObjectLifetime Object lifetime + * Deleting a DrawingItem will cause all of its children to be deleted as well. + * This can lead to nasty surprises if you hold references to things + * which are children of what is being deleted. Therefore, in the SP tree, + * you always need to delete the item views of children before deleting + * the view of the parent. Do not call delete on things returned from show() + * - this will cause dangling pointers inside the SPItem and lead to a crash. + * Use the corresponing hide() method. + * + * Outside of the SP tree, you should not use any references after the root node + * has been deleted. + */ + +DrawingItem::DrawingItem(Drawing &drawing) + : _drawing(drawing) + , _parent(NULL) + , _key(0) + , _opacity(1.0) + , _transform(NULL) + , _clip(NULL) + , _mask(NULL) + , _filter(NULL) + , _user_data(NULL) + , _cache(NULL) + , _state(0) + , _child_type(CHILD_ORPHAN) + , _background_new(0) + , _background_accumulate(0) + , _visible(true) + , _sensitive(true) + , _cached(0) + , _cached_persistent(0) + , _has_cache_iterator(0) + , _propagate(0) +// , _renders_opacity(0) + , _pick_children(0) +{} + +DrawingItem::~DrawingItem() +{ + _drawing.signal_item_deleted.emit(this); + //if (!_children.empty()) { + // g_warning("Removing item with children"); + //} + + // remove from the set of cached items and delete cache + setCached(false, true); + if (_has_cache_iterator) { + _drawing._candidate_items.erase(_cache_iterator); + } + // remove this item from parent's children list + // due to the effect of clearChildren(), this only happens for the top-level deleted item + if (_parent) { + _markForRendering(); + } + + switch (_child_type) { + case CHILD_NORMAL: { + ChildrenList::iterator ithis = _parent->_children.iterator_to(*this); + _parent->_children.erase(ithis); + } break; + case CHILD_CLIP: + // we cannot call setClip(NULL) or setMask(NULL), + // because that would be an endless loop + _parent->_clip = NULL; + break; + case CHILD_MASK: + _parent->_mask = NULL; + break; + case CHILD_ROOT: + _drawing._root = NULL; + break; + default: ; + } + + if (_parent) { + _parent->_markForUpdate(STATE_ALL, false); + } + clearChildren(); + delete _transform; + delete _clip; + delete _mask; + delete _filter; +} + +DrawingItem * +DrawingItem::parent() const +{ + // initially I wanted to return NULL if we are a clip or mask child, + // but the previous behavior was just to return the parent regardless of child type + return _parent; +} + +/// Returns true if item is among the descendants. Will return false if item == this. +bool +DrawingItem::isAncestorOf(DrawingItem *item) const +{ + for (DrawingItem *i = item->_parent; i; i = i->_parent) { + if (i == this) return true; + } + return false; +} + +void +DrawingItem::appendChild(DrawingItem *item) +{ + item->_parent = this; + assert(item->_child_type == CHILD_ORPHAN); + item->_child_type = CHILD_NORMAL; + _children.push_back(*item); + _markForUpdate(STATE_ALL, false); +} + +void +DrawingItem::prependChild(DrawingItem *item) +{ + item->_parent = this; + assert(item->_child_type == CHILD_ORPHAN); + item->_child_type = CHILD_NORMAL; + _children.push_front(*item); + _markForUpdate(STATE_ALL, false); +} + +/// Delete all regular children of this item (not mask or clip). +void +DrawingItem::clearChildren() +{ + _markForRendering(); + // prevent children from referencing the parent during deletion + // this way, children won't try to remove themselves from a list + // from which they have already been removed by clear_and_dispose + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->_parent = NULL; + i->_child_type = CHILD_ORPHAN; + } + _children.clear_and_dispose(DeleteDisposer()); + _markForUpdate(STATE_ALL, false); +} + +/// Set the incremental transform for this item +void +DrawingItem::setTransform(Geom::Affine const &new_trans) +{ + Geom::Affine current; + if (_transform) { + current = *_transform; + } + + if (!Geom::are_near(current, new_trans, 1e-18)) { + // mark the area where the object was for redraw. + _markForRendering(); + if (new_trans.isIdentity()) { + delete _transform; // delete NULL; is safe + _transform = NULL; + } else { + _transform = new Geom::Affine(new_trans); + } + _markForUpdate(STATE_ALL, true); + } +} + +void +DrawingItem::setOpacity(float opacity) +{ + _opacity = opacity; + _markForRendering(); +} + +void +DrawingItem::setVisible(bool v) +{ + _visible = v; + _markForRendering(); +} + +/// This is currently unused +void +DrawingItem::setSensitive(bool s) +{ + _sensitive = s; +} + +/** + * Enable / disable storing the rendering in memory. + * Calling setCached(false, true) will also remove the persistent status + */ +void +DrawingItem::setCached(bool cached, bool persistent) +{ + static const char *cache_env = getenv("_INKSCAPE_DISABLE_CACHE"); + if (cache_env) return; + + if (_cached_persistent && !persistent) + return; + + _cached = cached; + _cached_persistent = persistent ? cached : false; + if (cached) { + _drawing._cached_items.insert(this); + } else { + _drawing._cached_items.erase(this); + delete _cache; + _cache = NULL; + } +} + +void +DrawingItem::setClip(DrawingItem *item) +{ + _markForRendering(); + delete _clip; + _clip = item; + if (item) { + item->_parent = this; + assert(item->_child_type == CHILD_ORPHAN); + item->_child_type = CHILD_CLIP; + } + _markForUpdate(STATE_ALL, true); +} + +void +DrawingItem::setMask(DrawingItem *item) +{ + _markForRendering(); + delete _mask; + _mask = item; + if (item) { + item->_parent = this; + assert(item->_child_type == CHILD_ORPHAN); + item->_child_type = CHILD_MASK; + } + _markForUpdate(STATE_ALL, true); +} + +/// Move this item to the given place in the Z order of siblings. +/// Does nothing if the item has no parent. +void +DrawingItem::setZOrder(unsigned z) +{ + if (!_parent) return; + + ChildrenList::iterator it = _parent->_children.iterator_to(*this); + _parent->_children.erase(it); + + ChildrenList::iterator i = _parent->_children.begin(); + std::advance(i, std::min(z, unsigned(_parent->_children.size()))); + _parent->_children.insert(i, *this); + _markForRendering(); +} + +void +DrawingItem::setItemBounds(Geom::OptRect const &bounds) +{ + _item_bbox = bounds; +} + +/** + * Update derived data before operations. + * The purpose of this call is to recompute internal data which depends + * on the attributes of the object, but is not directly settable by the user. + * Precomputing this data speeds up later rendering, because some items + * can be omitted. + * + * Currently this method handles updating the visual and geometric bounding boxes + * in pixels, storing the total transformation from item space to the screen + * and cache invalidation. + * + * @param area Area to which the update should be restricted. Only takes effect + * if the bounding box is known. + * @param ctx A structure to store cascading state. + * @param flags Which internal data should be recomputed. This can be any combination + * of StateFlags. + * @param reset State fields that should be reset before processing them. This is + * a means to force a recomputation of internal data even if the item + * considers it up to date. Mainly for internal use, such as + * propagating bunding box recomputation to children when the item's + * transform changes. + */ +void +DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + bool render_filters = _drawing.renderFilters(); + bool outline = _drawing.outline(); + + // Set reset flags according to propagation status + reset |= _propagate_state; + _propagate_state = 0; + + _state &= ~reset; // reset state of this item + + if ((~_state & flags) == 0) return; // nothing to do + + // TODO this might be wrong + if (_state & STATE_BBOX) { + // we have up-to-date bbox + if (!area.intersects(outline ? _bbox : _drawbox)) return; + } + + // compute which elements need an update + unsigned to_update = _state ^ flags; + + // this needs to be called before we recurse into children + if (to_update & STATE_BACKGROUND) { + _background_accumulate = _background_new; + if (_child_type == CHILD_NORMAL && _parent->_background_accumulate) + _background_accumulate = true; + } + + UpdateContext child_ctx(ctx); + if (_transform) { + child_ctx.ctm = *_transform * ctx.ctm; + } + /* Remember the transformation matrix */ + Geom::Affine ctm_change = _ctm.inverse() * child_ctx.ctm; + _ctm = child_ctx.ctm; + + // update _bbox and call this function for children + _state = _updateItem(area, child_ctx, flags, reset); + + if (to_update & STATE_BBOX) { + // compute drawbox + if (_filter && render_filters) { + _drawbox = _filter->compute_drawbox(this, _item_bbox); + } else { + _drawbox = _bbox; + } + + // Clipping + if (_clip) { + _clip->update(area, child_ctx, flags, reset); + if (outline) { + _bbox.unionWith(_clip->_bbox); + } else { + _drawbox.intersectWith(_clip->_bbox); + } + } + // Masking + if (_mask) { + _mask->update(area, child_ctx, flags, reset); + if (outline) { + _bbox.unionWith(_mask->_bbox); + } else { + // for masking, we need full drawbox of mask + _drawbox.intersectWith(_mask->_drawbox); + } + } + } + + if (to_update & STATE_CACHE) { + // Update cache score for this item + if (_has_cache_iterator) { + // remove old score information + _drawing._candidate_items.erase(_cache_iterator); + _has_cache_iterator = false; + } + double score = _cacheScore(); + if (score >= _drawing._cache_score_threshold) { + CacheRecord cr; + cr.score = score; + // if _cacheRect() is empty, a negative score will be returned from _cacheScore(), + // so this will not execute (cache score threshold must be positive) + cr.cache_size = _cacheRect()->area() * 4; + cr.item = this; + _drawing._candidate_items.push_front(cr); + _cache_iterator = _drawing._candidate_items.begin(); + _has_cache_iterator = true; + } + + /* Update cache if enabled. + * General note: here we only tell the cache how it has to transform + * during the render phase. The transformation is deferred because + * after the update the item can have its caching turned off, + * e.g. because its filter was removed. This way we avoid tempoerarily + * using more memory than the cache budget */ + if (_cache) { + Geom::OptIntRect cl = _cacheRect(); + if (_visible && cl) { // never create cache for invisible items + // this takes care of invalidation on transform + _cache->scheduleTransform(*cl, ctm_change); + } else { + // Destroy cache for this item - outside of canvas or invisible. + // The opposite transition (invisible -> visible or object + // entering the canvas) is handled during the render phase + delete _cache; + _cache = NULL; + } + } + } + + if (to_update & STATE_RENDER) { + // now that we know drawbox, dirty the corresponding rect on canvas + // unless filtered, groups do not need to render by themselves, only their members + if (!is_drawing_group(this) || (_filter && render_filters)) { + _markForRendering(); + } + } +} + +struct MaskLuminanceToAlpha { + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b) + // the operation of unpremul -> luminance-to-alpha -> multiply by alpha + // is equivalent to luminance-to-alpha on premultiplied color values + // original computation in double: r*0.2125 + g*0.7154 + b*0.0721 + guint32 ao = r*109 + g*366 + b*37; // coeffs add up to 512 + return ((ao + 256) << 15) & 0xff000000; // equivalent to ((ao + 256) / 512) << 24 + } +}; + +/** + * Rasterize items. + * This method submits the drawing opeartions required to draw this item + * to the supplied DrawingContext, restricting drawing the the specified area. + * + * This method does some common tasks and calls the item-specific rendering + * function, _renderItem(), to render e.g. paths or bitmaps. + * + * @param flags Rendering options. This deals mainly with cache control. + */ +unsigned +DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) +{ + bool outline = _drawing.outline(); + bool render_filters = _drawing.renderFilters(); + + // stop_at is handled in DrawingGroup, but this check is required to handle the case + // where a filtered item with background-accessing filter has enable-background: new + if (this == stop_at) return RENDER_STOP; + + // If we are invisible, return immediately + if (!_visible) return RENDER_OK; + if (_ctm.isSingular(1e-18)) return RENDER_OK; + + // TODO convert outline rendering to a separate virtual function + if (outline) { + _renderOutline(ct, area, flags); + return RENDER_OK; + } + + // carea is the area to paint + Geom::OptIntRect carea = Geom::intersect(area, _drawbox); + if (!carea) return RENDER_OK; + + // render from cache if possible + if (_cached) { + if (_cache) { + _cache->prepare(); + _cache->paintFromCache(ct, carea); + if (!carea) return RENDER_OK; + } else { + // There is no cache. This could be because caching of this item + // was just turned on after the last update phase, or because + // we were previously outside of the canvas. + Geom::OptIntRect cl = _drawing.cacheLimit(); + cl.intersectWith(_drawbox); + if (cl) { + _cache = new DrawingCache(*cl); + } + } + } else { + // if our caching was turned off after the last update, it was already + // deleted in setCached() + } + + // determine whether this shape needs intermediate rendering. + bool needs_intermediate_rendering = false; + bool &nir = needs_intermediate_rendering; + bool needs_opacity = (_opacity < 0.995); + + // this item needs an intermediate rendering if: + nir |= (_clip != NULL); // 1. it has a clipping path + nir |= (_mask != NULL); // 2. it has a mask + nir |= (_filter != NULL && render_filters); // 3. it has a filter + nir |= needs_opacity; // 4. it is non-opaque + nir |= (_cache != NULL); // 5. it is cached + + /* How the rendering is done. + * + * Clipping, masking and opacity are done by rendering them to a surface + * and then compositing the object's rendering onto it with the IN operator. + * The object itself is rendered to a group. + * + * Opacity is done by rendering the clipping path with an alpha + * value corresponding to the opacity. If there is no clipping path, + * the entire intermediate surface is painted with alpha corresponding + * to the opacity value. + */ + + // Short-circuit the simple case. + // We also use this path for filter background rendering, because masking, clipping, + // filters and opacity do not apply when rendering the ancestors of the filtered + // element + if ((flags & RENDER_FILTER_BACKGROUND) || !needs_intermediate_rendering) { + return _renderItem(ct, *carea, flags & ~RENDER_FILTER_BACKGROUND, stop_at); + } + + // iarea is the bounding box for intermediate rendering + // Note 1: Pixels inside iarea but outside carea are invalid + // (incomplete filter dependence region). + // Note 2: We only need to render carea of clip and mask, but + // iarea of the object. + Geom::OptIntRect iarea = carea; + // expand carea to contain the dependent area of filters. + if (_filter && render_filters) { + _filter->area_enlarge(*iarea, this); + iarea.intersectWith(_drawbox); + } + + DrawingSurface intermediate(*iarea); + DrawingContext ict(intermediate); + unsigned render_result = RENDER_OK; + + // 1. Render clipping path with alpha = opacity. + ict.setSource(0,0,0,_opacity); + // Since clip can be combined with opacity, the result could be incorrect + // for overlapping clip children. To fix this we use the SOURCE operator + // instead of the default OVER. + ict.setOperator(CAIRO_OPERATOR_SOURCE); + if (_clip) { + _clip->clip(ict, *carea); // fixme: carea or area? + } else { + // if there is no clipping path, fill the entire surface with alpha = opacity. + ict.paint(); + } + ict.setOperator(CAIRO_OPERATOR_OVER); // reset back to default + + // 2. Render the mask if present and compose it with the clipping path + opacity. + if (_mask) { + ict.pushGroup(); + _mask->render(ict, *carea, flags); + + cairo_surface_t *mask_s = ict.rawTarget(); + // Convert mask's luminance to alpha + ink_cairo_surface_filter(mask_s, mask_s, MaskLuminanceToAlpha()); + ict.popGroupToSource(); + ict.setOperator(CAIRO_OPERATOR_IN); + ict.paint(); + ict.setOperator(CAIRO_OPERATOR_OVER); + } + + // 3. Render object itself + ict.pushGroup(); + render_result = _renderItem(ict, *iarea, flags, stop_at); + + // 4. Apply filter. + if (_filter && render_filters) { + bool rendered = false; + if (_filter->uses_background() && _background_accumulate) { + DrawingItem *bg_root = this; + for (; bg_root; bg_root = bg_root->_parent) { + if (bg_root->_background_new) break; + } + if (bg_root) { + DrawingSurface bg(*iarea); + DrawingContext bgct(bg); + bg_root->render(bgct, *iarea, flags | RENDER_FILTER_BACKGROUND, this); + _filter->render(this, ict, &bgct); + rendered = true; + } + } + if (!rendered) { + _filter->render(this, ict, NULL); + } + // Note that because the object was rendered to a group, + // the internals of the filter need to use cairo_get_group_target() + // instead of cairo_get_target(). + } + + // 5. Render object inside the composited mask + clip + ict.popGroupToSource(); + ict.setOperator(CAIRO_OPERATOR_IN); + ict.paint(); + + // 6. Paint the completed rendering onto the base context (or into cache) + if (_cached && _cache) { + DrawingContext cachect(*_cache); + cachect.rectangle(*carea); + cachect.setOperator(CAIRO_OPERATOR_SOURCE); + cachect.setSource(&intermediate); + cachect.fill(); + _cache->markClean(*carea); + } + ct.rectangle(*carea); + ct.setSource(&intermediate); + ct.fill(); + ct.setSource(0,0,0,0); + // the call above is to clear a ref on the intermediate surface held by ct + + return render_result; +} + +void +DrawingItem::_renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsigned flags) +{ + // intersect with bbox rather than drawbox, as we want to render things outside + // of the clipping path as well + Geom::OptIntRect carea = Geom::intersect(area, _bbox); + if (!carea) return; + + // just render everything: item, clip, mask + // First, render the object itself + _renderItem(ct, *carea, flags, NULL); + + // render clip and mask, if any + guint32 saved_rgba = _drawing.outlinecolor; // save current outline color + // render clippath as an object, using a different color + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (_clip) { + _drawing.outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips + _clip->render(ct, *carea, flags); + } + // render mask as an object, using a different color + if (_mask) { + _drawing.outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks + _mask->render(ct, *carea, flags); + } + _drawing.outlinecolor = saved_rgba; // restore outline color +} + +/** + * Rasterize the clipping path. + * This method submits drawing operations required to draw a basic filled shape + * of the item to the supplied drawing context. Rendering is limited to the + * given area. The rendering of the clipped object is composited into + * the result of this call using the IN operator. See the implementation + * of render() for details. + */ +void +DrawingItem::clip(Inkscape::DrawingContext &ct, Geom::IntRect const &area) +{ + // don't bother if the object does not implement clipping (e.g. DrawingImage) + if (!_canClip()) return; + if (!_visible) return; + if (!area.intersects(_bbox)) return; + + // The item used as the clipping path itself has a clipping path. + // Render this item's clipping path onto a temporary surface, then composite it + // with the item using the IN operator + if (_clip) { + ct.pushAlphaGroup(); + { Inkscape::DrawingContext::Save save(ct); + ct.setSource(0,0,0,1); + _clip->clip(ct, area); + } + ct.pushAlphaGroup(); + } + + // rasterize the clipping path + _clipItem(ct, area); + + if (_clip) { + ct.popGroupToSource(); + ct.setOperator(CAIRO_OPERATOR_IN); + ct.paint(); + ct.popGroupToSource(); + ct.setOperator(CAIRO_OPERATOR_SOURCE); + ct.paint(); + } +} + +/** + * Get the item under the specified point. + * Searches the tree for the first item in the Z-order which is closer than + * @a delta to the given point. The pick should be visual - for example + * an object with a thick stroke should pick on the entire area of the stroke. + * @param p Search point + * @param delta Maximum allowed distance from the point + * @param sticky Whether the pick should ignore visibility and sensitivity. + * When false, only visible and sensitive objects are considered. + * When true, invisible and insensitive objects can also be picked. + */ +DrawingItem * +DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) +{ + // Sometimes there's no BBOX in state, reason unknown (bug 992817) + // I made this not an assert to remove the warning + if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) + return NULL; + // ignore invisible and insensitive items unless sticky + if (!(flags & PICK_STICKY) && !(_visible && _sensitive)) + return NULL; + + bool outline = _drawing.outline(); + + if (!_drawing.outline()) { + // pick inside clipping path; if NULL, it means the object is clipped away there + if (_clip) { + DrawingItem *cpick = _clip->pick(p, delta, flags | PICK_AS_CLIP); + if (!cpick) return NULL; + } + // same for mask + if (_mask) { + DrawingItem *mpick = _mask->pick(p, delta, flags); + if (!mpick) return NULL; + } + } + + Geom::OptIntRect box = (outline || (flags & PICK_AS_CLIP)) ? _bbox : _drawbox; + if (!box) return NULL; + + Geom::Rect expanded = *box; + expanded.expandBy(delta); + + if (expanded.contains(p)) { + return _pickItem(p, delta, flags); + } + return NULL; +} + +/** + * Marks the current visual bounding box of the item for redrawing. + * This is called whenever the object changes its visible appearance. + * For some cases (such as setting opacity) this is enough, but for others + * _markForUpdate() also needs to be called. + */ +void +DrawingItem::_markForRendering() +{ + bool outline = _drawing.outline(); + Geom::OptIntRect dirty = outline ? _bbox : _drawbox; + if (!dirty) return; + + // dirty the caches of all parents + DrawingItem *bkg_root = NULL; + + for (DrawingItem *i = this; i; i = i->_parent) { + if (i != this && i->_filter) { + i->_filter->area_enlarge(*dirty, i); + } + if (i->_cache) { + i->_cache->markDirty(*dirty); + } + if (i->_background_accumulate) { + bkg_root = i; + } + } + + if (bkg_root) { + bkg_root->_invalidateFilterBackground(*dirty); + } + _drawing.signal_request_render.emit(*dirty); +} + +void +DrawingItem::_invalidateFilterBackground(Geom::IntRect const &area) +{ + if (!_drawbox.intersects(area)) return; + + if (_cache && _filter && _filter->uses_background()) { + _cache->markDirty(area); + } + + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->_invalidateFilterBackground(area); + } +} + +/** + * Marks the item as needing a recomputation of internal data. + * + * This mechanism avoids traversing the entire rendering tree (which could be vast) + * on every trivial state changed in any item. Only items marked as needing + * an update (having some bits in their _state unset) will be traversed + * during the update call. + * + * The _propagate variable is another optimization. We use it to specify that + * all children should also have the corresponding flags unset before checking + * whether they need to be traversed. This way there is one less traversal + * of the tree. Without this we would need to unset state bits in all children. + * With _propagate we do this during the update call, when we have to traverse + * the tree anyway. + */ +void +DrawingItem::_markForUpdate(unsigned flags, bool propagate) +{ + if (propagate) { + _propagate_state |= flags; + } + + if (_state & flags) { + _state &= ~flags; + if (_parent) { + _parent->_markForUpdate(flags, false); + } else { + _drawing.signal_request_update.emit(this); + } + } +} + +void +DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) +{ + if (style) sp_style_ref(style); + if (_style) sp_style_unref(_style); + _style = style; + + // if group has a filter + if (style->filter.set && style->getFilter()) { + if (!_filter) { + int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); + _filter = new Inkscape::Filters::Filter(primitives); + } + sp_filter_build_renderer(SP_FILTER(style->getFilter()), _filter); + } else { + // no filter set for this group + delete _filter; + _filter = NULL; + } + + if (style && style->enable_background.set) { + if (style->enable_background.value == SP_CSS_BACKGROUND_NEW && !_background_new) { + _background_new = true; + _markForUpdate(STATE_BACKGROUND, true); + } else if (style->enable_background.value == SP_CSS_BACKGROUND_ACCUMULATE && _background_new) { + _background_new = false; + _markForUpdate(STATE_BACKGROUND, true); + } + } + + _markForUpdate(STATE_ALL, false); +} + +/** + * Compute the caching score. + * + * Higher scores mean the item is more aggresively prioritized for automatic + * caching by Inkscape::Drawing. + */ +double +DrawingItem::_cacheScore() +{ + Geom::OptIntRect cache_rect = _cacheRect(); + if (!cache_rect) return -1.0; + + // a crude first approximation: + // the basic score is the number of pixels in the drawbox + double score = cache_rect->area(); + // this is multiplied by the filter complexity and its expansion + if (_filter &&_drawing.renderFilters()) { + score *= _filter->complexity(_ctm); + Geom::IntRect ref_area = Geom::IntRect::from_xywh(0, 0, 16, 16); + Geom::IntRect test_area = ref_area; + Geom::IntRect limit_area(0, INT_MIN, 16, INT_MAX); + _filter->area_enlarge(test_area, this); + // area_enlarge never shrinks the rect, so the result of intersection below + // must be non-empty + score *= double((test_area & limit_area)->area()) / ref_area.area(); + } + // if the object is clipped, add 1/2 of its bbox pixels + if (_clip && _clip->_bbox) { + score += _clip->_bbox->area() * 0.5; + } + // if masked, add mask score + if (_mask) { + score += _mask->_cacheScore(); + } + //g_message("caching score: %f", score); + return score; +} + +Geom::OptIntRect +DrawingItem::_cacheRect() +{ + Geom::OptIntRect r = _drawbox & _drawing.cacheLimit(); + return r; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h new file mode 100644 index 000000000..810a61d9d --- /dev/null +++ b/src/display/drawing-item.h @@ -0,0 +1,224 @@ +/** + * @file + * Canvas item belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +#include <list> +#include <exception> +#include <boost/operators.hpp> +#include <boost/utility.hpp> +#include <boost/intrusive/list.hpp> +#include <2geom/rect.h> +#include <2geom/affine.h> + +class SPStyle; + +namespace Inkscape { + +class Drawing; +class DrawingCache; +class DrawingContext; +class DrawingItem; + +namespace Filters { + +class Filter; + +} // namespace Filters + + + +struct UpdateContext { + Geom::Affine ctm; +}; + +struct CacheRecord + : boost::totally_ordered<CacheRecord> +{ + bool operator<(CacheRecord const &other) const { return score < other.score; } + bool operator==(CacheRecord const &other) const { return score == other.score; } + operator DrawingItem *() const { return item; } + double score; + size_t cache_size; + DrawingItem *item; +}; +typedef std::list<CacheRecord> CacheList; + +class InvalidItemException : public std::exception { + virtual const char *what() const throw() { + return "Invalid item in drawing"; + } +}; + +class DrawingItem + : boost::noncopyable +{ +public: + enum RenderFlags { + RENDER_DEFAULT = 0, + RENDER_CACHE_ONLY = 1, + RENDER_BYPASS_CACHE = 2, + RENDER_FILTER_BACKGROUND = 4 + }; + enum StateFlags { + STATE_NONE = 0, + STATE_BBOX = (1<<0), // bounding boxes are up-to-date + STATE_CACHE = (1<<1), // cache extents and clean area are up-to-date + STATE_PICK = (1<<2), // can process pick requests + STATE_RENDER = (1<<3), // can be rendered + STATE_BACKGROUND = (1<<4), // filter background data is up to date + STATE_ALL = (1<<5)-1 + }; + enum PickFlags { + PICK_NORMAL = 0, // normal pick + PICK_STICKY = (1<<0), // sticky pick - ignore visibility and sensitivity + PICK_AS_CLIP = (1<<2) // pick with no stroke and opaque fill regardless of item style + }; + + DrawingItem(Drawing &drawing); + virtual ~DrawingItem(); + + Geom::OptIntRect geometricBounds() const { return _bbox; } + Geom::OptIntRect visualBounds() const { return _drawbox; } + Geom::OptRect itemBounds() const { return _item_bbox; } + Geom::Affine ctm() const { return _ctm; } + Geom::Affine transform() const { return _transform ? *_transform : Geom::identity(); } + Drawing &drawing() const { return _drawing; } + DrawingItem *parent() const; + bool isAncestorOf(DrawingItem *item) const; + + void appendChild(DrawingItem *item); + void prependChild(DrawingItem *item); + void clearChildren(); + + bool visible() const { return _visible; } + void setVisible(bool v); + bool sensitive() const { return _sensitive; } + void setSensitive(bool v); + bool cached() const { return _cached; } + void setCached(bool c, bool persistent = false); + + void setOpacity(float opacity); + void setTransform(Geom::Affine const &trans); + void setClip(DrawingItem *item); + void setMask(DrawingItem *item); + void setZOrder(unsigned z); + void setItemBounds(Geom::OptRect const &bounds); + + void setKey(unsigned key) { _key = key; } + unsigned key() const { return _key; } + void setData(void *data) { _user_data = data; } + void *data() const { return _user_data; } + + void update(Geom::IntRect const &area = Geom::IntRect::infinite(), UpdateContext const &ctx = UpdateContext(), unsigned flags = STATE_ALL, unsigned reset = 0); + unsigned render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags = 0, DrawingItem *stop_at = NULL); + void clip(DrawingContext &ct, Geom::IntRect const &area); + DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags = 0); + +protected: + enum ChildType { + CHILD_ORPHAN = 0, // no parent - implies _parent == NULL + CHILD_NORMAL = 1, // contained in _children of parent + CHILD_CLIP = 2, // referenced by _clip member of parent + CHILD_MASK = 3, // referenced by _mask member of parent + CHILD_ROOT = 4, // root item of _drawing + CHILD_FILL_PATTERN = 5, // not yet implemented: referenced by fill pattern of parent + CHILD_STROKE_PATTERN = 6 // not yet implemented: referenced by stroke pattern of parent + }; + enum RenderResult { + RENDER_OK = 0, + RENDER_STOP = 1 + }; + void _renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsigned flags); + void _markForUpdate(unsigned state, bool propagate); + void _markForRendering(); + void _invalidateFilterBackground(Geom::IntRect const &area); + void _setStyleCommon(SPStyle *&_style, SPStyle *style); + double _cacheScore(); + Geom::OptIntRect _cacheRect(); + virtual unsigned _updateItem(Geom::IntRect const &/*area*/, UpdateContext const &/*ctx*/, + unsigned /*flags*/, unsigned /*reset*/) { return 0; } + virtual unsigned _renderItem(DrawingContext &/*ct*/, Geom::IntRect const &/*area*/, unsigned /*flags*/, + DrawingItem * /*stop_at*/) { return RENDER_OK; } + virtual void _clipItem(DrawingContext &/*ct*/, Geom::IntRect const &/*area*/) {} + virtual DrawingItem *_pickItem(Geom::Point const &/*p*/, double /*delta*/, unsigned /*flags*/) { return NULL; } + virtual bool _canClip() { return false; } + + // member variables start here + + Drawing &_drawing; + DrawingItem *_parent; + + typedef boost::intrusive::list_member_hook<> ListHook; + ListHook _child_hook; + + typedef boost::intrusive::list< + DrawingItem, + boost::intrusive::member_hook<DrawingItem, ListHook, &DrawingItem::_child_hook> + > ChildrenList; + ChildrenList _children; + + unsigned _key; ///< Some SPItems can have more than one DrawingItem; + /// this value is a hack used to distinguish between them + float _opacity; + + Geom::Affine *_transform; ///< Incremental transform from parent to this item's coords + Geom::Affine _ctm; ///< Total transform from item coords to display coords + Geom::OptIntRect _bbox; ///< Bounding box in display (pixel) coords including stroke + Geom::OptIntRect _drawbox; ///< Full visual bounding box - enlarged by filters, shrunk by clips and masks + Geom::OptRect _item_bbox; ///< Geometric bounding box in item coordinates + + DrawingItem *_clip; + DrawingItem *_mask; + Inkscape::Filters::Filter *_filter; + void *_user_data; ///< Used to associate DrawingItems with SPItems that created them + DrawingCache *_cache; + + CacheList::iterator _cache_iterator; + + unsigned _state : 8; + unsigned _propagate_state : 8; + unsigned _child_type : 3; // see ChildType enum + unsigned _background_new : 1; ///< Whether enable-background: new is set for this element + unsigned _background_accumulate : 1; ///< Whether this element accumulates background + /// (has any ancestor with enable-background: new) + unsigned _visible : 1; + unsigned _sensitive : 1; ///< Whether this item responds to events + unsigned _cached : 1; ///< Whether the rendering is stored for reuse + unsigned _cached_persistent : 1; ///< If set, will always be cached regardless of score + unsigned _has_cache_iterator : 1; ///< If set, _cache_iterator is valid + unsigned _propagate : 1; ///< Whether to call update for all children on next update + //unsigned _renders_opacity : 1; ///< Whether object needs temporary surface for opacity + unsigned _pick_children : 1; ///< For groups: if true, children are returned from pick(), + /// otherwise the group is returned + + friend class Drawing; +}; + +struct DeleteDisposer { + void operator()(DrawingItem *item) { delete item; } +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp new file mode 100644 index 000000000..4ca306092 --- /dev/null +++ b/src/display/drawing-shape.cpp @@ -0,0 +1,341 @@ +/** + * @file + * Shape (styled path) belonging to an SVG drawing. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> +#include <2geom/curves.h> +#include <2geom/pathvector.h> +#include <2geom/svg-path.h> +#include <2geom/svg-path-parser.h> + +#include "display/cairo-utils.h" +#include "display/canvas-arena.h" +#include "display/canvas-bpath.h" +#include "display/curve.h" +#include "display/drawing.h" +#include "display/drawing-context.h" +#include "display/drawing-group.h" +#include "display/drawing-shape.h" +#include "helper/geom-curves.h" +#include "helper/geom.h" +#include "preferences.h" +#include "style.h" +#include "svg/svg.h" + +namespace Inkscape { + +DrawingShape::DrawingShape(Drawing &drawing) + : DrawingItem(drawing) + , _curve(NULL) + , _style(NULL) + , _last_pick(NULL) + , _repick_after(0) +{} + +DrawingShape::~DrawingShape() +{ + if (_style) + sp_style_unref(_style); + if (_curve) + _curve->unref(); +} + +void +DrawingShape::setPath(SPCurve *curve) +{ + _markForRendering(); + + if (_curve) { + _curve->unref(); + _curve = NULL; + } + if (curve) { + _curve = curve; + curve->ref(); + } + + _markForUpdate(STATE_ALL, false); +} + +void +DrawingShape::setStyle(SPStyle *style) +{ + _setStyleCommon(_style, style); + _nrstyle.set(style); +} + +unsigned +DrawingShape::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + Geom::OptRect boundingbox; + + unsigned beststate = STATE_ALL; + + // update markers + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->update(area, ctx, flags, reset); + } + + if (!(flags & STATE_RENDER)) { + /* We do not have to create rendering structures */ + if (flags & STATE_BBOX) { + if (_curve) { + boundingbox = bounds_exact_transformed(_curve->get_pathvector(), ctx.ctm); + if (boundingbox) { + _bbox = boundingbox->roundOutwards(); + } else { + _bbox = Geom::OptIntRect(); + } + } + if (beststate & STATE_BBOX) { + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + _bbox.unionWith(i->geometricBounds()); + } + } + } + return (flags | _state); + } + + boundingbox = Geom::OptRect(); + bool outline = _drawing.outline(); + + // clear Cairo data to force update + _nrstyle.update(); + + if (_curve) { + boundingbox = bounds_exact_transformed(_curve->get_pathvector(), ctx.ctm); + + if (boundingbox && (_nrstyle.stroke.type != NRStyle::PAINT_NONE || outline)) { + float width, scale; + scale = ctx.ctm.descrim(); + width = std::max(0.125f, _nrstyle.stroke_width * scale); + if ( fabs(_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true + boundingbox->expandBy(width); + } + // those pesky miters, now + float miterMax = width * _nrstyle.miter_limit; + if ( miterMax > 0.01 ) { + // grunt mode. we should compute the various miters instead + // (one for each point on the curve) + boundingbox->expandBy(miterMax); + } + } + } + + _bbox = boundingbox ? boundingbox->roundOutwards() : Geom::OptIntRect(); + + if (!_curve || + !_style || + _curve->is_empty() || + (( _nrstyle.fill.type != NRStyle::PAINT_NONE ) && + ( _nrstyle.stroke.type != NRStyle::PAINT_NONE && !outline) )) + { + return STATE_ALL; + } + + if (beststate & STATE_BBOX) { + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + _bbox.unionWith(i->geometricBounds()); + } + } + + return STATE_ALL; +} + +unsigned +DrawingShape::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) +{ + if (!_curve || !_style) return RENDER_OK; + if (!area.intersects(_bbox)) return RENDER_OK; // skip if not within bounding box + + bool outline = _drawing.outline(); + + if (outline) { + guint32 rgba = _drawing.outlinecolor; + + { Inkscape::DrawingContext::Save save(ct); + ct.transform(_ctm); + ct.path(_curve->get_pathvector()); + } + { Inkscape::DrawingContext::Save save(ct); + ct.setSource(rgba); + ct.setLineWidth(0.5); + ct.setTolerance(1.25); + ct.stroke(); + } + } else { + bool has_stroke, has_fill; + // we assume the context has no path + Inkscape::DrawingContext::Save save(ct); + ct.transform(_ctm); + + // update fill and stroke paints. + // this cannot be done during nr_arena_shape_update, because we need a Cairo context + // to render svg:pattern + has_fill = _nrstyle.prepareFill(ct, _item_bbox); + has_stroke = _nrstyle.prepareStroke(ct, _item_bbox); + has_stroke &= (_nrstyle.stroke_width != 0); + + if (has_fill || has_stroke) { + // TODO: remove segments outside of bbox when no dashes present + ct.path(_curve->get_pathvector()); + if (has_fill) { + _nrstyle.applyFill(ct); + ct.fillPreserve(); + } + if (has_stroke) { + _nrstyle.applyStroke(ct); + ct.strokePreserve(); + } + ct.newPath(); // clear path + } // has fill or stroke pattern + } + + // marker rendering + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->render(ct, area, flags, stop_at); + } + return RENDER_OK; +} + +void DrawingShape::_clipItem(DrawingContext &ct, Geom::IntRect const & /*area*/) +{ + if (!_curve) return; + + Inkscape::DrawingContext::Save save(ct); + // handle clip-rule + if (_style) { + if (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) { + ct.setFillRule(CAIRO_FILL_RULE_EVEN_ODD); + } else { + ct.setFillRule(CAIRO_FILL_RULE_WINDING); + } + } + ct.transform(_ctm); + ct.path(_curve->get_pathvector()); + ct.fill(); +} + +DrawingItem * +DrawingShape::_pickItem(Geom::Point const &p, double delta, unsigned flags) +{ + if (_repick_after > 0) + --_repick_after; + + if (_repick_after > 0) // we are a slow, huge path + return _last_pick; // skip this pick, returning what was returned last time + + if (!_curve) return NULL; + if (!_style) return NULL; + + bool outline = _drawing.outline(); + bool pick_as_clip = flags & PICK_AS_CLIP; + + if (SP_SCALE24_TO_FLOAT(_style->opacity.value) == 0 && !outline && !pick_as_clip) + // fully transparent, no pick unless outline mode + return NULL; + + GTimeVal tstart, tfinish; + g_get_current_time (&tstart); + + double width; + if (pick_as_clip) { + width = 0; // no width should be applied to clip picking + // this overrides display mode and stroke style considerations + } else if (outline) { + width = 0.5; // in outline mode, everything is stroked with the same 0.5px line width + } else if (_nrstyle.stroke.type != NRStyle::PAINT_NONE && _nrstyle.stroke.opacity > 1e-3) { + // for normal picking calculate the distance corresponding top the stroke width + // FIXME BUG: this is incorrect for transformed strokes + float const scale = _ctm.descrim(); + width = std::max(0.125f, _nrstyle.stroke_width * scale) / 2; + } else { + width = 0; + } + + double dist = Geom::infinity(); + int wind = 0; + bool needfill = pick_as_clip || (_nrstyle.fill.type != NRStyle::PAINT_NONE && + _nrstyle.fill.opacity > 1e-3 && !outline); + bool wind_evenodd = pick_as_clip ? (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) : + (_style->fill_rule.computed == SP_WIND_RULE_EVENODD); + + // actual shape picking + if (_drawing.arena()) { + Geom::Rect viewbox = _drawing.arena()->item.canvas->getViewbox(); + viewbox.expandBy (width); + pathv_matrix_point_bbox_wind_distance(_curve->get_pathvector(), _ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, &viewbox); + } else { + pathv_matrix_point_bbox_wind_distance(_curve->get_pathvector(), _ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, NULL); + } + + g_get_current_time (&tfinish); + glong this_pick = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec); + //g_print ("pick time %lu\n", this_pick); + + if (this_pick > 10000) { // slow picking, remember to skip several new picks + _repick_after = this_pick / 5000; + } + + // covered by fill? + if (needfill) { + if (wind_evenodd) { + if (wind & 0x1) { + _last_pick = this; + return this; + } + } else { + if (wind != 0) { + _last_pick = this; + return this; + } + } + } + + // close to the edge, as defined by strokewidth and delta? + // this ignores dashing (as if the stroke is solid) and always works as if caps are round + if (needfill || width > 0) { // if either fill or stroke visible, + if ((dist - width) < delta) { + _last_pick = this; + return this; + } + } + + // if not picked on the shape itself, try its markers + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + DrawingItem *ret = i->pick(p, delta, flags & ~PICK_STICKY); + if (ret) { + _last_pick = this; + return this; + } + } + + _last_pick = NULL; + return NULL; +} + +bool +DrawingShape::_canClip() +{ + return true; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h new file mode 100644 index 000000000..ce9bed2eb --- /dev/null +++ b/src/display/drawing-shape.h @@ -0,0 +1,63 @@ +/** + * @file + * Group belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_SHAPE_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_SHAPE_H + +#include "display/drawing-item.h" +#include "display/nr-style.h" + +class SPStyle; +class SPCurve; + +namespace Inkscape { + +class DrawingShape + : public DrawingItem +{ +public: + DrawingShape(Drawing &drawing); + ~DrawingShape(); + + void setPath(SPCurve *curve); + void setStyle(SPStyle *style); + +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, + DrawingItem *stop_at); + virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area); + virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); + virtual bool _canClip(); + + SPCurve *_curve; + SPStyle *_style; + NRStyle _nrstyle; + + DrawingItem *_last_pick; + unsigned _repick_after; +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp new file mode 100644 index 000000000..bddccbd96 --- /dev/null +++ b/src/display/drawing-surface.cpp @@ -0,0 +1,365 @@ +/** + * @file + * Cairo surface that remembers its origin. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +//#include <iostream> +#include "display/drawing-surface.h" +#include "display/drawing-context.h" +#include "display/cairo-utils.h" + +namespace Inkscape { + +using Geom::X; +using Geom::Y; + + +/** + * @class DrawingSurface + * Drawing surface that remembers its origin. + * + * This is a very minimalistic wrapper over cairo_surface_t. The main + * extra functionality provided by this class is that it automates + * the mapping from "logical space" (coordinates in the rendering) + * and the "physical space" (surface pixels). For example, patterns + * have to be rendered on tiles which have possibly non-integer + * widths and heights. + * + * This class has delayed allocation functionality - it creates + * the Cairo surface it wraps on the first call to createRawContext() + * of when a DrawingContext is constructed. + */ + +/** + * Creates a surface with the given physical extents. + * When a drawing context is created for this surface, its pixels + * will cover the area under the given rectangle. + */ +DrawingSurface::DrawingSurface(Geom::IntRect const &area) + : _surface(NULL) + , _origin(area.min()) + , _scale(1, 1) + , _pixels(area.dimensions()) +{} + +/** + * Creates a surface with the given logical and physical extents. + * When a drawing context is created for this surface, its pixels + * will cover the area under the given rectangle. IT will contain + * the number of pixels specified by the second argument. + * @param logbox Logical extents of the surface + * @param pixdims Pixel dimensions of the surface. + */ +DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims) + : _surface(NULL) + , _origin(logbox.min()) + , _scale(pixdims[X] / logbox.width(), pixdims[Y] / logbox.height()) + , _pixels(pixdims) +{} + +/** + * Wrap a cairo_surface_t. + * This constructor will take an extra reference on @a surface, which will + * be released on destruction. + */ +DrawingSurface::DrawingSurface(cairo_surface_t *surface, Geom::Point const &origin) + : _surface(surface) + , _origin(origin) + , _scale(1, 1) +{ + cairo_surface_reference(surface); + _pixels[X] = cairo_image_surface_get_width(surface); + _pixels[Y] = cairo_image_surface_get_height(surface); +} + +DrawingSurface::~DrawingSurface() +{ + if (_surface) + cairo_surface_destroy(_surface); +} + +/// Get the logical extents of the surface. +Geom::Rect +DrawingSurface::area() const +{ + Geom::Rect r = Geom::Rect::from_xywh(_origin, dimensions()); + return r; +} + +/// Get the pixel dimensions of the surface +Geom::IntPoint +DrawingSurface::pixels() const +{ + return _pixels; +} + +/// Get the logical width and weight of the surface as a point. +Geom::Point +DrawingSurface::dimensions() const +{ + Geom::Point logical_dims(_pixels[X] / _scale[X], _pixels[Y] / _scale[Y]); + return logical_dims; +} + +Geom::Point +DrawingSurface::origin() const +{ + return _origin; +} + +Geom::Scale +DrawingSurface::scale() const +{ + return _scale; +} + +/// Get the transformation applied to the drawing context on construction. +Geom::Affine +DrawingSurface::drawingTransform() const +{ + Geom::Affine ret = _scale * Geom::Translate(-_origin); + return ret; +} + +cairo_surface_type_t +DrawingSurface::type() const +{ + // currently hardcoded + return CAIRO_SURFACE_TYPE_IMAGE; +} + +/// Drop contents of the surface and release the underlying Cairo object. +void +DrawingSurface::dropContents() +{ + if (_surface) { + cairo_surface_destroy(_surface); + _surface = NULL; + } +} + +/** + * Create a drawing context for this surface. + * It's better to use the surface constructor of DrawingContext. + */ +cairo_t * +DrawingSurface::createRawContext() +{ + // deferred allocation + if (!_surface) { + _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _pixels[X], _pixels[Y]); + } + cairo_t *ct = cairo_create(_surface); + if (_scale != Geom::Scale::identity()) { + cairo_scale(ct, _scale[X], _scale[Y]); + } + cairo_translate(ct, -_origin[X], -_origin[Y]); + return ct; +} + +Geom::IntRect +DrawingSurface::pixelArea() const +{ + Geom::IntRect ret = Geom::IntRect::from_xywh(_origin.round(), _pixels); + return ret; +} + +////////////////////////////////////////////////////////////////////////////// + +DrawingCache::DrawingCache(Geom::IntRect const &area) + : DrawingSurface(area) + , _clean_region(cairo_region_create()) + , _pending_area(area) +{} + +DrawingCache::~DrawingCache() +{ + cairo_region_destroy(_clean_region); +} + +void +DrawingCache::markDirty(Geom::IntRect const &area) +{ + cairo_rectangle_int_t dirty = _convertRect(area); + cairo_region_subtract_rectangle(_clean_region, &dirty); +} +void +DrawingCache::markClean(Geom::IntRect const &area) +{ + Geom::OptIntRect r = Geom::intersect(area, pixelArea()); + if (!r) return; + cairo_rectangle_int_t clean = _convertRect(*r); + cairo_region_union_rectangle(_clean_region, &clean); +} + +/// Call this during the update phase to schedule a transformation of the cache. +void +DrawingCache::scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans) +{ + _pending_area = new_area; + _pending_transform *= trans; +} + +/// Transforms the cache according to the transform specified during the update phase. +/// Call this during render phase, before painting. +void +DrawingCache::prepare() +{ + Geom::IntRect old_area = pixelArea(); + bool is_identity = _pending_transform.isIdentity(); + if (is_identity && _pending_area == old_area) return; // no change + + bool is_integer_translation = false; + if (!is_identity && _pending_transform.isTranslation()) { + Geom::IntPoint t = _pending_transform.translation().round(); + if (Geom::are_near(Geom::Point(t), _pending_transform.translation())) { + is_integer_translation = true; + cairo_region_translate(_clean_region, t[X], t[Y]); + if (old_area + t == _pending_area) { + // if the areas match, the only thing to do + // is to ensure that the clean area is not too large + cairo_rectangle_int_t limit = _convertRect(_pending_area); + cairo_region_intersect_rectangle(_clean_region, &limit); + _origin += t; + _pending_transform.setIdentity(); + return; + } + } + } + // otherwise, we need to transform the cache + Geom::IntPoint old_origin = old_area.min(); + cairo_surface_t *old_surface = _surface; + _surface = NULL; + _pixels = _pending_area.dimensions(); + _origin = _pending_area.min(); + + cairo_t *ct = createRawContext(); + if (!is_identity) { + ink_cairo_transform(ct, _pending_transform); + } + cairo_set_source_surface(ct, old_surface, old_origin[X], old_origin[Y]); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + + cairo_surface_destroy(old_surface); + cairo_destroy(ct); + + if (!is_identity && !is_integer_translation) { + // dirty everything + cairo_region_destroy(_clean_region); + _clean_region = cairo_region_create(); + } else { + cairo_rectangle_int_t limit = _convertRect(_pending_area); + cairo_region_intersect_rectangle(_clean_region, &limit); + } + //std::cout << _pending_transform << old_area << _pending_area << std::endl; + _pending_transform.setIdentity(); +} + +/** + * Paints the clean area from cache and modifies the @a area + * parameter to the bounds of the region that must be repainted. + */ +void +DrawingCache::paintFromCache(DrawingContext &ct, Geom::OptIntRect &area) +{ + if (!area) return; + + // We subtract the clean region from the area, then get the bounds + // of the resulting region. This is the area that needs to be repainted + // by the item. + // Then we subtract the area that needs to be repainted from the + // original area and paint the resulting region from cache. + cairo_rectangle_int_t area_c = _convertRect(*area); + cairo_region_t *dirty_region = cairo_region_create_rectangle(&area_c); + cairo_region_t *cache_region = cairo_region_copy(dirty_region); + cairo_region_subtract(dirty_region, _clean_region); + + if (cairo_region_is_empty(dirty_region)) { + area = Geom::OptIntRect(); + } else { + cairo_rectangle_int_t to_repaint; + cairo_region_get_extents(dirty_region, &to_repaint); + area = _convertRect(to_repaint); + cairo_region_subtract_rectangle(cache_region, &to_repaint); + } + cairo_region_destroy(dirty_region); + + if (!cairo_region_is_empty(cache_region)) { + int nr = cairo_region_num_rectangles(cache_region); + cairo_rectangle_int_t tmp; + for (int i = 0; i < nr; ++i) { + cairo_region_get_rectangle(cache_region, i, &tmp); + ct.rectangle(_convertRect(tmp)); + } + ct.setSource(this); + ct.fill(); + } + cairo_region_destroy(cache_region); +} + +// debugging utility +void +DrawingCache::_dumpCache(Geom::OptIntRect const &area) +{ + static int dumpnr = 0; + cairo_surface_t *surface = ink_cairo_surface_copy(_surface); + DrawingContext ct(surface, _origin); + if (!cairo_region_is_empty(_clean_region)) { + Inkscape::DrawingContext::Save save(ct); + int nr = cairo_region_num_rectangles(_clean_region); + cairo_rectangle_int_t tmp; + for (int i = 0; i < nr; ++i) { + cairo_region_get_rectangle(_clean_region, i, &tmp); + ct.rectangle(_convertRect(tmp)); + } + ct.setSource(0,1,0,0.1); + ct.fill(); + } + ct.rectangle(*area); + ct.setSource(1,0,0,0.1); + ct.fill(); + char *fn = g_strdup_printf("dump%d.png", dumpnr++); + cairo_surface_write_to_png(surface, fn); + cairo_surface_destroy(surface); + g_free(fn); +} + +cairo_rectangle_int_t +DrawingCache::_convertRect(Geom::IntRect const &area) +{ + cairo_rectangle_int_t ret; + ret.x = area.left(); + ret.y = area.top(); + ret.width = area.width(); + ret.height = area.height(); + return ret; +} + +Geom::IntRect +DrawingCache::_convertRect(cairo_rectangle_int_t const &r) +{ + Geom::IntRect ret = Geom::IntRect::from_xywh( + r.x, r.y, + r.width, r.height); + return ret; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-surface.h b/src/display/drawing-surface.h new file mode 100644 index 000000000..1ec848405 --- /dev/null +++ b/src/display/drawing-surface.h @@ -0,0 +1,93 @@ +/** + * @file + * Cairo surface that remembers its origin. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_SURFACE_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_SURFACE_H + +#include <boost/shared_ptr.hpp> +#include <cairo.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <2geom/affine.h> +#include <2geom/rect.h> +#include <2geom/transforms.h> + +namespace Inkscape { +class DrawingContext; + +class DrawingSurface +{ +public: + explicit DrawingSurface(Geom::IntRect const &area); + DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims); + DrawingSurface(cairo_surface_t *surface, Geom::Point const &origin); + virtual ~DrawingSurface(); + + Geom::Rect area() const; + Geom::IntPoint pixels() const; + Geom::Point dimensions() const; + Geom::Point origin() const; + Geom::Scale scale() const; + Geom::Affine drawingTransform() const; + cairo_surface_type_t type() const; + void dropContents(); + + cairo_surface_t *raw() { return _surface; } + cairo_t *createRawContext(); + +protected: + Geom::IntRect pixelArea() const; + + cairo_surface_t *_surface; + Geom::Point _origin; + Geom::Scale _scale; + Geom::IntPoint _pixels; + bool _has_context; + + friend class DrawingContext; +}; + +class DrawingCache + : public DrawingSurface +{ +public: + explicit DrawingCache(Geom::IntRect const &area); + ~DrawingCache(); + + void markDirty(Geom::IntRect const &area = Geom::IntRect::infinite()); + void markClean(Geom::IntRect const &area = Geom::IntRect::infinite()); + void scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans); + void prepare(); + void paintFromCache(DrawingContext &ct, Geom::OptIntRect &area); + +protected: + cairo_region_t *_clean_region; + Geom::IntRect _pending_area; + Geom::Affine _pending_transform; +private: + void _dumpCache(Geom::OptIntRect const &area); + static cairo_rectangle_int_t _convertRect(Geom::IntRect const &r); + static Geom::IntRect _convertRect(cairo_rectangle_int_t const &r); +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp new file mode 100644 index 000000000..94a9690fb --- /dev/null +++ b/src/display/drawing-text.cpp @@ -0,0 +1,251 @@ +/** + * @file + * Group belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/canvas-bpath.h" // for SPWindRule (WTF!) +#include "display/drawing.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing-text.h" +#include "helper/geom.h" +#include "libnrtype/font-instance.h" +#include "style.h" + +namespace Inkscape { + +DrawingGlyphs::DrawingGlyphs(Drawing &drawing) + : DrawingItem(drawing) + , _font(NULL) + , _glyph(0) +{} + +DrawingGlyphs::~DrawingGlyphs() +{ + if (_font) { + _font->Unref(); + _font = NULL; + } +} + +void +DrawingGlyphs::setGlyph(font_instance *font, int glyph, Geom::Affine const &trans) +{ + _markForRendering(); + + setTransform(trans); + + if (font) font->Ref(); + if (_font) _font->Unref(); + _font = font; + _glyph = glyph; + + _markForUpdate(STATE_ALL, false); +} + +unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext const &ctx, unsigned /*flags*/, unsigned /*reset*/) +{ + DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent); + if (!ggroup) throw InvalidItemException(); + + if (!_font || !ggroup->_style) return STATE_ALL; + if (ggroup->_nrstyle.fill.type == NRStyle::PAINT_NONE && + ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE) + { + return STATE_ALL; + } + + _pick_bbox = Geom::IntRect(); + _bbox = Geom::IntRect(); + + Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); + if (b && ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE) { + float width, scale; + scale = ctx.ctm.descrim(); + if (_transform) scale /= _transform->descrim(); // FIXME temporary hack + width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale); + if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true + b->expandBy(0.5 * width); + } + // save bbox without miters for picking + _pick_bbox = b->roundOutwards(); + + float miterMax = width * ggroup->_nrstyle.miter_limit; + if ( miterMax > 0.01 ) { + // grunt mode. we should compute the various miters instead + // (one for each point on the curve) + b->expandBy(miterMax); + } + _bbox = b->roundOutwards(); + } else if (b) { + _bbox = b->roundOutwards(); + _pick_bbox = *_bbox; + } + + return STATE_ALL; +} + +DrawingItem * +DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/) +{ + if (!_font || !_bbox) return NULL; + + // With text we take a simple approach: pick if the point is in a characher bbox + Geom::Rect expanded(_pick_bbox); + expanded.expandBy(delta); + if (expanded.contains(p)) return this; + return NULL; +} + + + +DrawingText::DrawingText(Drawing &drawing) + : DrawingGroup(drawing) +{} + +DrawingText::~DrawingText() +{} + +void +DrawingText::clear() +{ + _markForRendering(); + _children.clear_and_dispose(DeleteDisposer()); +} + +void +DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans) +{ + if (!font || !font->PathVector(glyph)) return; + + _markForRendering(); + DrawingGlyphs *ng = new DrawingGlyphs(_drawing); + ng->setGlyph(font, glyph, trans); + appendChild(ng); +} + +void +DrawingText::setStyle(SPStyle *style) +{ + _nrstyle.set(style); + DrawingGroup::setStyle(style); +} + +unsigned +DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + _nrstyle.update(); + return DrawingGroup::_updateItem(area, ctx, flags, reset); +} + +unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) +{ + if (_drawing.outline()) { + guint32 rgba = _drawing.outlinecolor; + Inkscape::DrawingContext::Save save(ct); + ct.setSource(rgba); + ct.setTolerance(1.25); // low quality, but good enough for outline mode + + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i); + if (!g) throw InvalidItemException(); + + Inkscape::DrawingContext::Save save(ct); + // skip glpyhs with singular transforms + if (g->_ctm.isSingular()) continue; + ct.transform(g->_ctm); + ct.path(*g->_font->PathVector(g->_glyph)); + ct.fill(); + } + return RENDER_OK; + } + + // NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding + bool has_stroke, has_fill; + + has_fill = _nrstyle.prepareFill(ct, _item_bbox); + has_stroke = _nrstyle.prepareStroke(ct, _item_bbox); + + if (has_fill || has_stroke) { + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i); + if (!g) throw InvalidItemException(); + + Inkscape::DrawingContext::Save save(ct); + if (g->_ctm.isSingular()) continue; + ct.transform(g->_ctm); + ct.path(*g->_font->PathVector(g->_glyph)); + } + + Inkscape::DrawingContext::Save save(ct); + ct.transform(_ctm); + if (has_fill) { + _nrstyle.applyFill(ct); + ct.fillPreserve(); + } + if (has_stroke) { + _nrstyle.applyStroke(ct); + ct.strokePreserve(); + } + ct.newPath(); // clear path + } + return RENDER_OK; +} + +void DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &/*area*/) +{ + Inkscape::DrawingContext::Save save(ct); + + // handle clip-rule + if (_style) { + if (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) { + ct.setFillRule(CAIRO_FILL_RULE_EVEN_ODD); + } else { + ct.setFillRule(CAIRO_FILL_RULE_WINDING); + } + } + + for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i); + if (!g) throw InvalidItemException(); + + Inkscape::DrawingContext::Save save(ct); + ct.transform(g->_ctm); + ct.path(*g->_font->PathVector(g->_glyph)); + } + ct.fill(); +} + +DrawingItem * +DrawingText::_pickItem(Geom::Point const &p, double delta, unsigned flags) +{ + DrawingItem *picked = DrawingGroup::_pickItem(p, delta, flags); + if (picked) return this; + return NULL; +} + +bool +DrawingText::_canClip() +{ + return true; +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h new file mode 100644 index 000000000..929d2bf2d --- /dev/null +++ b/src/display/drawing-text.h @@ -0,0 +1,82 @@ +/** + * @file + * Group belonging to an SVG drawing element. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_TEXT_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_TEXT_H + +#include "display/drawing-group.h" +#include "display/nr-style.h" + +class SPStyle; +class font_instance; + +namespace Inkscape { + +class DrawingGlyphs + : public DrawingItem +{ +public: + DrawingGlyphs(Drawing &drawing); + ~DrawingGlyphs(); + + void setGlyph(font_instance *font, int glyph, Geom::Affine const &trans); + +protected: + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); + + font_instance *_font; + int _glyph; + Geom::IntRect _pick_bbox; + + friend class DrawingText; +}; + +class DrawingText + : public DrawingGroup +{ +public: + DrawingText(Drawing &drawing); + ~DrawingText(); + + void clear(); + void addComponent(font_instance *font, int glyph, Geom::Affine const &trans); + void setStyle(SPStyle *style); + +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, + DrawingItem *stop_at); + virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area); + virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); + virtual bool _canClip(); + + NRStyle _nrstyle; + + friend class DrawingGlyphs; +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp new file mode 100644 index 000000000..77f24caf3 --- /dev/null +++ b/src/display/drawing.cpp @@ -0,0 +1,205 @@ +/** + * @file + * SVG drawing for display. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <algorithm> +#include "display/drawing.h" +#include "nr-filter-gaussian.h" +#include "nr-filter-types.h" + +namespace Inkscape { + +Drawing::Drawing(SPCanvasArena *arena) + : _root(NULL) + , outlinecolor(0x000000ff) + , delta(0) + , _exact(false) + , _rendermode(RENDERMODE_NORMAL) + , _colormode(COLORMODE_NORMAL) + , _blur_quality(BLUR_QUALITY_BEST) + , _filter_quality(Filters::FILTER_QUALITY_BEST) + , _cache_score_threshold(50000.0) + , _cache_budget(0) + , _canvasarena(arena) +{ + +} + +Drawing::~Drawing() +{ + delete _root; +} + +void +Drawing::setRoot(DrawingItem *item) +{ + delete _root; + _root = item; + if (item) { + assert(item->_child_type == DrawingItem::CHILD_ORPHAN); + item->_child_type = DrawingItem::CHILD_ROOT; + } +} + +RenderMode +Drawing::renderMode() const +{ + return _exact ? RENDERMODE_NORMAL : _rendermode; +} +ColorMode +Drawing::colorMode() const +{ + return (outline() || _exact) ? COLORMODE_NORMAL : _colormode; +} +bool +Drawing::outline() const +{ + return renderMode() == RENDERMODE_OUTLINE; +} +bool +Drawing::renderFilters() const +{ + return renderMode() == RENDERMODE_NORMAL; +} +int +Drawing::blurQuality() const +{ + if (renderMode() == RENDERMODE_NORMAL) { + return _exact ? BLUR_QUALITY_BEST : _blur_quality; + } else { + return BLUR_QUALITY_WORST; + } +} +int +Drawing::filterQuality() const +{ + if (renderMode() == RENDERMODE_NORMAL) { + return _exact ? Filters::FILTER_QUALITY_BEST : _filter_quality; + } else { + return Filters::FILTER_QUALITY_WORST; + } +} + +void +Drawing::setRenderMode(RenderMode mode) +{ + _rendermode = mode; +} +void +Drawing::setColorMode(ColorMode mode) +{ + _colormode = mode; +} +void +Drawing::setBlurQuality(int q) +{ + _blur_quality = q; +} +void +Drawing::setFilterQuality(int q) +{ + _filter_quality = q; +} +void +Drawing::setExact(bool e) +{ + _exact = e; +} + +Geom::OptIntRect const & +Drawing::cacheLimit() const +{ + return _cache_limit; +} +void +Drawing::setCacheLimit(Geom::OptIntRect const &r) +{ + _cache_limit = r; + for (std::set<DrawingItem *>::iterator i = _cached_items.begin(); + i != _cached_items.end(); ++i) + { + (*i)->_markForUpdate(DrawingItem::STATE_CACHE, false); + } +} +void +Drawing::setCacheBudget(size_t bytes) +{ + _cache_budget = bytes; + _pickItemsForCaching(); +} + +void +Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + if (_root) { + _root->update(area, ctx, flags, reset); + } + // process the updated cache scores + _pickItemsForCaching(); +} + +void +Drawing::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags) +{ + if (_root) { + _root->render(ct, area, flags); + } +} + +DrawingItem * +Drawing::pick(Geom::Point const &p, double delta, unsigned flags) +{ + if (_root) { + return _root->pick(p, delta, flags); + } + return NULL; +} + +void +Drawing::_pickItemsForCaching() +{ + // we cache the objects with the highest score until the budget is exhausted + _candidate_items.sort(std::greater<CacheRecord>()); + size_t used = 0; + CandidateList::iterator i; + for (i = _candidate_items.begin(); i != _candidate_items.end(); ++i) { + if (used + i->cache_size > _cache_budget) break; + used += i->cache_size; + } + + std::set<DrawingItem*> to_cache; + for (i = _candidate_items.begin(); i != _candidate_items.end(); ++i) { + i->item->setCached(true); + to_cache.insert(i->item); + } + // Everything which is now in _cached_items but not in to_cache must be uncached + // Note that calling setCached on an item modifies _cached_items + // TODO: find a way to avoid the set copy + std::set<DrawingItem*> to_uncache; + std::set_difference(_cached_items.begin(), _cached_items.end(), + to_cache.begin(), to_cache.end(), + std::inserter(to_uncache, to_uncache.end())); + for (std::set<DrawingItem*>::iterator j = to_uncache.begin(); j != to_uncache.end(); ++j) { + (*j)->setCached(false); + } +} + +} // end namespace Inkscape + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/drawing.h b/src/display/drawing.h new file mode 100644 index 000000000..8154f0783 --- /dev/null +++ b/src/display/drawing.h @@ -0,0 +1,119 @@ +/** + * @file + * SVG drawing for display. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2011 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_H + +#include <set> +#include <glib.h> +#include <boost/operators.hpp> +#include <boost/utility.hpp> +#include <sigc++/sigc++.h> +#include <2geom/rect.h> +#include "display/drawing-item.h" +#include "display/rendermode.h" + + +typedef struct _SPCanvasArena SPCanvasArena; + + +namespace Inkscape { + +class DrawingItem; + +class Drawing + : boost::noncopyable +{ +public: + struct OutlineColors { + guint32 paths; + guint32 clippaths; + guint32 masks; + guint32 images; + }; + + Drawing(SPCanvasArena *arena = NULL); + ~Drawing(); + + DrawingItem *root() { return _root; } + SPCanvasArena *arena() { return _canvasarena; } + void setRoot(DrawingItem *item); + + RenderMode renderMode() const; + ColorMode colorMode() const; + bool outline() const; + bool renderFilters() const; + int blurQuality() const; + int filterQuality() const; + void setRenderMode(RenderMode mode); + void setColorMode(ColorMode mode); + void setBlurQuality(int q); + void setFilterQuality(int q); + void setExact(bool e); + + Geom::OptIntRect const &cacheLimit() const; + void setCacheLimit(Geom::OptIntRect const &r); + void setCacheBudget(size_t bytes); + + OutlineColors const &colors() const { return _colors; } + + void update(Geom::IntRect const &area = Geom::IntRect::infinite(), UpdateContext const &ctx = UpdateContext(), unsigned flags = DrawingItem::STATE_ALL, unsigned reset = 0); + void render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags = 0); + DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags); + + sigc::signal<void, DrawingItem *> signal_request_update; + sigc::signal<void, Geom::IntRect const &> signal_request_render; + sigc::signal<void, DrawingItem *> signal_item_deleted; + +private: + void _pickItemsForCaching(); + + typedef std::list<CacheRecord> CandidateList; + + DrawingItem *_root; + std::set<DrawingItem *> _cached_items; // modified by DrawingItem::setCached() + CacheList _candidate_items; +public: + // TODO: remove these temporarily public members + guint32 outlinecolor; + double delta; +private: + bool _exact; // if true then rendering must be exact + RenderMode _rendermode; + ColorMode _colormode; + int _blur_quality; + int _filter_quality; + Geom::OptIntRect _cache_limit; + + double _cache_score_threshold; ///< do not consider objects for caching below this score + size_t _cache_budget; ///< maximum allowed size of cache + + OutlineColors _colors; + SPCanvasArena *_canvasarena; // may be NULL is this arena is not the screen + // but used for export etc. + + friend class DrawingItem; +}; + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DRAWING_H + +/* + 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/gnome-canvas-acetate.cpp b/src/display/gnome-canvas-acetate.cpp index b86892e32..d6ebfa175 100644 --- a/src/display/gnome-canvas-acetate.cpp +++ b/src/display/gnome-canvas-acetate.cpp @@ -25,26 +25,26 @@ static double sp_canvas_acetate_point (SPCanvasItem *item, Geom::Point p, SPCanv static SPCanvasItemClass *parent_class; -GtkType -sp_canvas_acetate_get_type (void) +GType sp_canvas_acetate_get_type (void) { - static GtkType acetate_type = 0; + static GType acetate_type = 0; if (!acetate_type) { - GtkTypeInfo acetate_info = { - (gchar *)"SPCanvasAcetate", - sizeof (SPCanvasAcetate), + GTypeInfo acetate_info = { sizeof (SPCanvasAcetateClass), - (GtkClassInitFunc) sp_canvas_acetate_class_init, - (GtkObjectInitFunc) sp_canvas_acetate_init, - NULL, NULL, NULL + NULL, NULL, + (GClassInitFunc) sp_canvas_acetate_class_init, + NULL, NULL, + sizeof (SPCanvasAcetate), + 0, + (GInstanceInitFunc) sp_canvas_acetate_init, + NULL }; - acetate_type = gtk_type_unique (sp_canvas_item_get_type (), &acetate_info); + acetate_type = g_type_register_static (sp_canvas_item_get_type (), "SPCanvasAcetate", &acetate_info, (GTypeFlags)0); } return acetate_type; } -static void -sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass) +static void sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass) { GtkObjectClass *object_class; SPCanvasItemClass *item_class; @@ -52,7 +52,7 @@ sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass) object_class = (GtkObjectClass *) klass; item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ()); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_canvas_acetate_destroy; @@ -60,28 +60,21 @@ sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass) item_class->point = sp_canvas_acetate_point; } -static void -sp_canvas_acetate_init (SPCanvasAcetate */*acetate*/) +static void sp_canvas_acetate_init (SPCanvasAcetate */*acetate*/) { /* Nothing here */ } -static void -sp_canvas_acetate_destroy (GtkObject *object) +static void sp_canvas_acetate_destroy (GtkObject *object) { - SPCanvasAcetate *acetate; - g_return_if_fail (object != NULL); g_return_if_fail (GNOME_IS_CANVAS_ACETATE (object)); - acetate = SP_CANVAS_ACETATE (object); - if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } -static void -sp_canvas_acetate_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, unsigned int /*flags*/ ) +static void sp_canvas_acetate_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, unsigned int /*flags*/ ) { item->x1 = -G_MAXINT; item->y1 = -G_MAXINT; @@ -89,8 +82,7 @@ sp_canvas_acetate_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, un item->y2 = G_MAXINT; } -static double -sp_canvas_acetate_point( SPCanvasItem *item, Geom::Point /*p*/, SPCanvasItem **actual_item ) +static double sp_canvas_acetate_point( SPCanvasItem *item, Geom::Point /*p*/, SPCanvasItem **actual_item ) { *actual_item = item; diff --git a/src/display/gnome-canvas-acetate.h b/src/display/gnome-canvas-acetate.h index 8c284291c..447c3a9c4 100644 --- a/src/display/gnome-canvas-acetate.h +++ b/src/display/gnome-canvas-acetate.h @@ -15,15 +15,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib/gtypes.h> +#include <glib.h> #include "display/sp-canvas-item.h" #define GNOME_TYPE_CANVAS_ACETATE (sp_canvas_acetate_get_type ()) -#define SP_CANVAS_ACETATE(obj) (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_ACETATE, SPCanvasAcetate)) -#define SP_CANVAS_ACETATE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ACETATE, SPCanvasAcetateClass)) -#define GNOME_IS_CANVAS_ACETATE(obj) (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_ACETATE)) -#define GNOME_IS_CANVAS_ACETATE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ACETATE)) +#define SP_CANVAS_ACETATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_ACETATE, SPCanvasAcetate)) +#define SP_CANVAS_ACETATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ACETATE, SPCanvasAcetateClass)) +#define GNOME_IS_CANVAS_ACETATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_ACETATE)) +#define GNOME_IS_CANVAS_ACETATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ACETATE)) struct SPCanvasAcetate { @@ -34,7 +34,7 @@ struct SPCanvasAcetateClass { SPCanvasItemClass parent_class; }; -GtkType sp_canvas_acetate_get_type (void); +GType sp_canvas_acetate_get_type (void); diff --git a/src/display/grayscale.cpp b/src/display/grayscale.cpp index 37f2b255c..f59cf6d23 100644 --- a/src/display/grayscale.cpp +++ b/src/display/grayscale.cpp @@ -21,14 +21,22 @@ namespace Grayscale { +/* Original values from Johan: +const float red_factor = 0.3; +const float green_factor = 0.59; +const float blue_factor = 0.11; +*/ + +// Values below are from the SVG specification +const float red_factor = 0.2125; +const float green_factor = 0.7154; +const float blue_factor = 0.0721; + guint32 process(guint32 rgba) { return process(SP_RGBA32_R_U(rgba), SP_RGBA32_G_U(rgba), SP_RGBA32_B_U(rgba), SP_RGBA32_A_U(rgba)); } guint32 process(guchar r, guchar g, guchar b, guchar a) { - float red_factor = 0.3; - float green_factor = 0.59; - float blue_factor = 0.11; /** To reduce banding in gradients, this calculation is tweaked a bit * by outputing blue+1 or red+1 or both. The luminance is calculated @@ -65,7 +73,8 @@ guchar luminance(guchar r, guchar g, guchar b) { return luminance & 0xff; } -/** @brief Use this method if there is no other way to find out if grayscale view or not +/** + * Use this method if there is no other way to find out if grayscale view or not. * * In some cases, the choice between normal or grayscale is so deep in the code hierarchy, * that it is not possible to determine whether grayscale is desired or not, without using @@ -74,7 +83,7 @@ guchar luminance(guchar r, guchar g, guchar b) { */ bool activeDesktopIsGrayscale() { if (SP_ACTIVE_DESKTOP) { - return (SP_ACTIVE_DESKTOP->getColorMode() == Inkscape::COLORRENDERMODE_GRAYSCALE); + return (SP_ACTIVE_DESKTOP->getColorMode() == Inkscape::COLORMODE_GRAYSCALE); } else { return false; } diff --git a/src/display/grayscale.h b/src/display/grayscale.h index 855c9e465..18162e1f3 100644 --- a/src/display/grayscale.h +++ b/src/display/grayscale.h @@ -1,9 +1,7 @@ #ifndef SEEN_DISPLAY_GRAYSCALE_H #define SEEN_DISPLAY_GRAYSCALE_H -/** \file - * Provide methods to calculate grayscale values (e.g. convert rgba value to grayscale rgba value) - * +/* * Author: * Johan Engelen <goejendaagh@zonnet.nl> * @@ -12,21 +10,20 @@ * Released under GNU GPL */ -#include <gdk/gdktypes.h> +#include <gdk/gdk.h> +/** + * Provide methods to calculate grayscale values (e.g. convert rgba value to grayscale rgba value). + */ namespace Grayscale { guint32 process(guint32 rgba); guint32 process(guchar r, guchar g, guchar b, guchar a); guchar luminance(guchar r, guchar g, guchar b); - const float red_factor = 0.3; - const float green_factor = 0.59; - const float blue_factor = 0.11; - bool activeDesktopIsGrayscale(); }; -#endif /* !SEEN_DISPLAY_GRAYSCALE_H */ +#endif // !SEEN_DISPLAY_GRAYSCALE_H /* Local Variables: diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index bebec2852..f2802c6fe 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -13,12 +13,16 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ - -#include <libnr/nr-pixops.h> +#include <2geom/coord.h> #include <2geom/transforms.h> #include "sp-canvas-util.h" #include "sp-ctrlpoint.h" #include "guideline.h" +#include "display/cairo-utils.h" + +#include "inkscape.h" // for inkscape_active_desktop() +#include "desktop.h" +#include "sp-namedview.h" static void sp_guideline_class_init(SPGuideLineClass *c); static void sp_guideline_init(SPGuideLine *guideline); @@ -78,6 +82,7 @@ static void sp_guideline_init(SPGuideLine *gl) gl->sensitive = 0; gl->origin = NULL; + gl->label = NULL; } static void sp_guideline_destroy(GtkObject *object) @@ -99,88 +104,84 @@ static void sp_guideline_destroy(GtkObject *object) static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) { - SPGuideLine const *gl = SP_GUIDELINE (item); - - sp_canvas_prepare_buffer(buf); - - unsigned int const r = NR_RGBA32_R (gl->rgba); - unsigned int const g = NR_RGBA32_G (gl->rgba); - unsigned int const b = NR_RGBA32_B (gl->rgba); - unsigned int const a = NR_RGBA32_A (gl->rgba); + //TODO: the routine that renders the label of a specific guideline sometimes + // ends up erasing the labels of the other guidelines. + // Maybe we should render all labels everytime. - if (gl->is_vertical()) { - int position = (int) Inkscape::round(gl->point_on_line[Geom::X]); - if (position < buf->rect.x0 || position >= buf->rect.x1) { - return; - } - - int p0 = buf->rect.y0; - int p1 = buf->rect.y1; - int step = buf->buf_rowstride; - unsigned char *d = buf->buf + 4 * (position - buf->rect.x0); - - for (int p = p0; p < p1; p++) { - d[0] = NR_COMPOSEN11_1111(r, a, d[0]); - d[1] = NR_COMPOSEN11_1111(g, a, d[1]); - d[2] = NR_COMPOSEN11_1111(b, a, d[2]); - d += step; - } - } else if (gl->is_horizontal()) { - int position = (int) Inkscape::round(gl->point_on_line[Geom::Y]); - if (position < buf->rect.y0 || position >= buf->rect.y1) { - return; - } + SPGuideLine const *gl = SP_GUIDELINE (item); - int p0 = buf->rect.x0; - int p1 = buf->rect.x1; - int step = 4; - unsigned char *d = buf->buf + (position - buf->rect.y0) * buf->buf_rowstride; + cairo_save(buf->ct); + cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top()); + ink_cairo_set_source_rgba32(buf->ct, gl->rgba); + cairo_set_line_width(buf->ct, 1); + cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); + cairo_set_font_size(buf->ct, 10); + + Geom::Point normal_dt = /*unit_vector*/(gl->normal_to_line * gl->affine.withoutTranslation()); // note that normal_dt does not have unit length + Geom::Point point_on_line_dt = gl->point_on_line * gl->affine; + + if (gl->label) { + int px = round(point_on_line_dt[Geom::X]); + int py = round(point_on_line_dt[Geom::Y]); + cairo_save(buf->ct); + cairo_translate(buf->ct, px, py); + cairo_rotate(buf->ct, atan2(normal_dt.cw())); + cairo_translate(buf->ct, 0, -5); + cairo_move_to(buf->ct, 0, 0); + cairo_show_text(buf->ct, gl->label); + cairo_restore(buf->ct); + } - for (int p = p0; p < p1; p++) { - d[0] = NR_COMPOSEN11_1111(r, a, d[0]); - d[1] = NR_COMPOSEN11_1111(g, a, d[1]); - d[2] = NR_COMPOSEN11_1111(b, a, d[2]); - d += step; - } + if ( Geom::are_near(normal_dt[Geom::Y], 0.) ) { // is vertical? + int position = round(point_on_line_dt[Geom::X]); + cairo_move_to(buf->ct, position + 0.5, buf->rect.top() + 0.5); + cairo_line_to(buf->ct, position + 0.5, buf->rect.bottom() - 0.5); + cairo_stroke(buf->ct); + } else if ( Geom::are_near(normal_dt[Geom::X], 0.) ) { // is horizontal? + int position = round(point_on_line_dt[Geom::Y]); + cairo_move_to(buf->ct, buf->rect.left() + 0.5, position + 0.5); + cairo_line_to(buf->ct, buf->rect.right() - 0.5, position + 0.5); + cairo_stroke(buf->ct); } else { - // render angled line, once intersection has been detected, draw from there. - Geom::Point parallel_to_line( gl->normal_to_line[Geom::Y], - /*should be minus, but inverted y axis*/ gl->normal_to_line[Geom::X]); + // render angled line. Once intersection has been detected, draw from there. + Geom::Point parallel_to_line( normal_dt.ccw() ); //try to intersect with left vertical of rect - double y_intersect_left = (buf->rect.x0 - gl->point_on_line[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + gl->point_on_line[Geom::Y]; - if ( (y_intersect_left >= buf->rect.y0) && (y_intersect_left <= buf->rect.y1) ) { + double y_intersect_left = (buf->rect.left() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; + if ( (y_intersect_left >= buf->rect.top()) && (y_intersect_left <= buf->rect.bottom()) ) { // intersects with left vertical! - double y_intersect_right = (buf->rect.x1 - gl->point_on_line[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + gl->point_on_line[Geom::Y]; - sp_guideline_drawline (buf, buf->rect.x0, static_cast<gint>(round(y_intersect_left)), buf->rect.x1, static_cast<gint>(round(y_intersect_right)), gl->rgba); - return; + double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; + sp_guideline_drawline (buf, buf->rect.left(), static_cast<gint>(round(y_intersect_left)), buf->rect.right(), static_cast<gint>(round(y_intersect_right)), gl->rgba); + goto end; } //try to intersect with right vertical of rect - double y_intersect_right = (buf->rect.x1 - gl->point_on_line[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + gl->point_on_line[Geom::Y]; - if ( (y_intersect_right >= buf->rect.y0) && (y_intersect_right <= buf->rect.y1) ) { + double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; + if ( (y_intersect_right >= buf->rect.top()) && (y_intersect_right <= buf->rect.bottom()) ) { // intersects with right vertical! - sp_guideline_drawline (buf, buf->rect.x1, static_cast<gint>(round(y_intersect_right)), buf->rect.x0, static_cast<gint>(round(y_intersect_left)), gl->rgba); - return; + sp_guideline_drawline (buf, buf->rect.right(), static_cast<gint>(round(y_intersect_right)), buf->rect.left(), static_cast<gint>(round(y_intersect_left)), gl->rgba); + goto end; } //try to intersect with top horizontal of rect - double x_intersect_top = (buf->rect.y0 - gl->point_on_line[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + gl->point_on_line[Geom::X]; - if ( (x_intersect_top >= buf->rect.x0) && (x_intersect_top <= buf->rect.x1) ) { + double x_intersect_top = (buf->rect.top() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; + if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { // intersects with top horizontal! - double x_intersect_bottom = (buf->rect.y1 - gl->point_on_line[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + gl->point_on_line[Geom::X]; - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_top)), buf->rect.y0, static_cast<gint>(round(x_intersect_bottom)), buf->rect.y1, gl->rgba); - return; + double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; + sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_top)), buf->rect.top(), static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), gl->rgba); + goto end; } //try to intersect with bottom horizontal of rect - double x_intersect_bottom = (buf->rect.y1 - gl->point_on_line[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + gl->point_on_line[Geom::X]; - if ( (x_intersect_top >= buf->rect.x0) && (x_intersect_top <= buf->rect.x1) ) { + double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; + if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { // intersects with bottom horizontal! - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_bottom)), buf->rect.y1, static_cast<gint>(round(x_intersect_top)), buf->rect.y0, gl->rgba); - return; + sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), static_cast<gint>(round(x_intersect_top)), buf->rect.top(), gl->rgba); + goto end; } } + end: + cairo_restore(buf->ct); } static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags) @@ -191,17 +192,18 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, ((SPCanvasItemClass *) parent_class)->update(item, affine, flags); } - gl->point_on_line[Geom::X] = affine[4]; - gl->point_on_line[Geom::Y] = affine[5]; + gl->affine = affine; - sp_ctrlpoint_set_coords(gl->origin, gl->point_on_line * affine.inverse()); + sp_ctrlpoint_set_coords(gl->origin, gl->point_on_line); sp_canvas_item_request_update(SP_CANVAS_ITEM (gl->origin)); + Geom::Point pol_transformed = gl->point_on_line*affine; if (gl->is_horizontal()) { - sp_canvas_update_bbox (item, -1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y]), 1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y] + 1)); + sp_canvas_update_bbox (item, -1000000, round(pol_transformed[Geom::Y] - 16), 1000000, round(pol_transformed[Geom::Y] + 1)); } else if (gl->is_vertical()) { - sp_canvas_update_bbox (item, (int) Inkscape::round(gl->point_on_line[Geom::X]), -1000000, (int) Inkscape::round(gl->point_on_line[Geom::X] + 1), 1000000); + sp_canvas_update_bbox (item, round(pol_transformed[Geom::X]), -1000000, round(pol_transformed[Geom::X] + 16), 1000000); } else { + //TODO: labels in angled guidelines are not showing up for some reason. sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); } } @@ -212,17 +214,17 @@ static double sp_guideline_point(SPCanvasItem *item, Geom::Point p, SPCanvasItem SPGuideLine *gl = SP_GUIDELINE (item); if (!gl->sensitive) { - return NR_HUGE; + return Geom::infinity(); } *actual_item = item; - Geom::Point vec(gl->normal_to_line[Geom::X], - gl->normal_to_line[Geom::Y]); - double distance = Geom::dot((p - gl->point_on_line), vec); + Geom::Point vec = gl->normal_to_line * gl->affine.withoutTranslation(); + double distance = Geom::dot((p - gl->point_on_line * gl->affine), unit_vector(vec)); return MAX(fabs(distance)-1, 0); } -SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, Geom::Point point_on_line, Geom::Point normal) +SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, char* label, Geom::Point point_on_line, Geom::Point normal) { SPCanvasItem *item = sp_canvas_item_new(parent, SP_TYPE_GUIDELINE, NULL); SPCanvasItem *origin = sp_canvas_item_new(parent, SP_TYPE_CTRLPOINT, NULL); @@ -232,6 +234,7 @@ SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, Geom::Point point_on_line, gl->origin = cp; normal.normalize(); + gl->label = label; gl->normal_to_line = normal; gl->angle = tan( -gl->normal_to_line[Geom::X] / gl->normal_to_line[Geom::Y]); sp_guideline_set_position(gl, point_on_line); @@ -241,10 +244,20 @@ SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, Geom::Point point_on_line, return item; } +void sp_guideline_set_label(SPGuideLine *gl, const char* label) +{ + if (gl->label) { + g_free(gl->label); + } + gl->label = g_strdup(label); + + sp_canvas_item_request_update(SP_CANVAS_ITEM (gl)); +} + void sp_guideline_set_position(SPGuideLine *gl, Geom::Point point_on_line) { - sp_canvas_item_affine_absolute(SP_CANVAS_ITEM (gl), Geom::Affine(Geom::Translate(point_on_line))); - sp_canvas_item_affine_absolute(SP_CANVAS_ITEM (gl->origin), Geom::Affine(Geom::Translate(point_on_line))); + gl->point_on_line = point_on_line; + sp_canvas_item_request_update(SP_CANVAS_ITEM (gl)); } void sp_guideline_set_normal(SPGuideLine *gl, Geom::Point normal_to_line) @@ -274,82 +287,12 @@ void sp_guideline_delete(SPGuideLine *gl) gtk_object_destroy(GTK_OBJECT(gl)); } -//########################################################## -// Line rendering -#define SAFE_SETPIXEL //undefine this when it is certain that setpixel is never called with invalid params - -/** - \brief This function renders a pixel on a particular buffer. - - The topleft of the buffer equals - ( rect.x0 , rect.y0 ) in screen coordinates - ( 0 , 0 ) in setpixel coordinates - The bottomright of the buffer equals - ( rect.x1 , rect,y1 ) in screen coordinates - ( rect.x1 - rect.x0 , rect.y1 - rect.y0 ) in setpixel coordinates -*/ -static void -sp_guideline_setpixel (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) -{ -#ifdef SAFE_SETPIXEL - if ( (x >= buf->rect.x0) && (x < buf->rect.x1) && (y >= buf->rect.y0) && (y < buf->rect.y1) ) { -#endif - guint r, g, b, a; - r = NR_RGBA32_R (rgba); - g = NR_RGBA32_G (rgba); - b = NR_RGBA32_B (rgba); - a = NR_RGBA32_A (rgba); - guchar * p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - p[0] = NR_COMPOSEN11_1111 (r, a, p[0]); - p[1] = NR_COMPOSEN11_1111 (g, a, p[1]); - p[2] = NR_COMPOSEN11_1111 (b, a, p[2]); -#ifdef SAFE_SETPIXEL - } -#endif -} - -/** - \brief This function renders a line on a particular canvas buffer, - using Bresenham's line drawing function. - http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html - Coordinates are interpreted as SCREENcoordinates -*/ static void -sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba) +sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 /*rgba*/) { - int dy = y1 - y0; - int dx = x1 - x0; - int stepx, stepy; - - if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } - if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } - dy <<= 1; // dy is now 2*dy - dx <<= 1; // dx is now 2*dx - - sp_guideline_setpixel(buf, x0, y0, rgba); - if (dx > dy) { - int fraction = dy - (dx >> 1); // same as 2*dy - dx - while (x0 != x1) { - if (fraction >= 0) { - y0 += stepy; - fraction -= dx; // same as fraction -= 2*dx - } - x0 += stepx; - fraction += dy; // same as fraction -= 2*dy - sp_guideline_setpixel(buf, x0, y0, rgba); - } - } else { - int fraction = dx - (dy >> 1); - while (y0 != y1) { - if (fraction >= 0) { - x0 += stepx; - fraction -= dy; - } - y0 += stepy; - fraction += dx; - sp_guideline_setpixel(buf, x0, y0, rgba); - } - } + cairo_move_to(buf->ct, x0 + 0.5, y0 + 0.5); + cairo_line_to(buf->ct, x1 + 0.5, y1 + 0.5); + cairo_stroke(buf->ct); } /* diff --git a/src/display/guideline.h b/src/display/guideline.h index 9654d04a1..164244c46 100644 --- a/src/display/guideline.h +++ b/src/display/guideline.h @@ -18,17 +18,20 @@ #include "sp-canvas-item.h" #define SP_TYPE_GUIDELINE (sp_guideline_get_type()) -#define SP_GUIDELINE(o) (GTK_CHECK_CAST((o), SP_TYPE_GUIDELINE, SPGuideLine)) -#define SP_IS_GUIDELINE(o) (GTK_CHECK_TYPE((o), SP_TYPE_GUIDELINE)) +#define SP_GUIDELINE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_GUIDELINE, SPGuideLine)) +#define SP_IS_GUIDELINE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_GUIDELINE)) class SPCtrlPoint; struct SPGuideLine { SPCanvasItem item; + Geom::Affine affine; + SPCtrlPoint *origin; // unlike 'item', this is only held locally guint32 rgba; + char* label; Geom::Point normal_to_line; Geom::Point point_on_line; double angle; @@ -45,8 +48,9 @@ struct SPGuideLineClass { GType sp_guideline_get_type(); -SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, Geom::Point point_on_line, Geom::Point normal); +SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, char* label, Geom::Point point_on_line, Geom::Point normal); +void sp_guideline_set_label(SPGuideLine *gl, const char* label); void sp_guideline_set_position(SPGuideLine *gl, Geom::Point point_on_line); void sp_guideline_set_normal(SPGuideLine *gl, Geom::Point normal_to_line); void sp_guideline_set_color(SPGuideLine *gl, unsigned int rgba); diff --git a/src/display/inkscape-cairo.cpp b/src/display/inkscape-cairo.cpp deleted file mode 100644 index 4fc492ba8..000000000 --- a/src/display/inkscape-cairo.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Helper functions to use cairo with inkscape - * - * Copyright (C) 2007 bulia byak - * Copyright (C) 2008 Johan Engelen - * - * Released under GNU GPL - * - */ - -#include <cairo.h> - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <libnr/nr-pixblock.h> -#include <libnr/nr-convert2geom.h> -#include "../style.h" -#include "nr-arena.h" -#include "sp-canvas.h" -#include <2geom/pathvector.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> -#include <2geom/affine.h> -#include <2geom/point.h> -#include <2geom/path.h> -#include <2geom/transforms.h> -#include <2geom/sbasis-to-bezier.h> -#include "helper/geom-curves.h" - -/** Creates a cairo context to render to the given pixblock on the given area */ -cairo_t * -nr_create_cairo_context_for_data (NRRectL *area, NRRectL *buf_area, unsigned char *px, unsigned int rowstride) -{ - if (!nr_rect_l_test_intersect_ptr(buf_area, area)) - return NULL; - - NRRectL clip; - nr_rect_l_intersect (&clip, buf_area, area); - unsigned char *dpx = px + (clip.y0 - buf_area->y0) * rowstride + 4 * (clip.x0 - buf_area->x0); - int width = area->x1 - area->x0; - int height = area->y1 - area->y0; - // even though cairo cannot draw in nonpremul mode, select ARGB32 for R8G8B8A8N as the closest; later eliminate R8G8B8A8N everywhere - cairo_surface_t* cst = cairo_image_surface_create_for_data - (dpx, - CAIRO_FORMAT_ARGB32, - width, - height, - rowstride); - cairo_t *ct = cairo_create (cst); - - return ct; -} - -/** Creates a cairo context to render to the given SPCanvasBuf on the given area */ -cairo_t * -nr_create_cairo_context_canvasbuf (NRRectL */*area*/, SPCanvasBuf *b) -{ - return nr_create_cairo_context_for_data (&(b->rect), &(b->rect), b->buf, b->buf_rowstride); -} - - -/** Creates a cairo context to render to the given NRPixBlock on the given area */ -cairo_t * -nr_create_cairo_context (NRRectL *area, NRPixBlock *pb) -{ - return nr_create_cairo_context_for_data (area, &(pb->area), NR_PIXBLOCK_PX (pb), pb->rs); -} - -/* - * Can be called recursively. - * If optimize_stroke == false, the view Rect is not used. - */ -static void -feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & trans, Geom::Rect view, bool optimize_stroke) -{ - if( is_straight_curve(c) ) - { - Geom::Point end_tr = c.finalPoint() * trans; - if (!optimize_stroke) { - cairo_line_to(cr, end_tr[0], end_tr[1]); - } else { - Geom::Rect swept(c.initialPoint()*trans, end_tr); - if (swept.intersects(view)) { - cairo_line_to(cr, end_tr[0], end_tr[1]); - } else { - cairo_move_to(cr, end_tr[0], end_tr[1]); - } - } - } - else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const*>(&c)) { - std::vector<Geom::Point> points = quadratic_bezier->points(); - points[0] *= trans; - points[1] *= trans; - points[2] *= trans; - Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); - Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); - if (!optimize_stroke) { - cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); - } else { - Geom::Rect swept(points[0], points[2]); - swept.expandTo(points[1]); - if (swept.intersects(view)) { - cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); - } else { - cairo_move_to(cr, points[2][0], points[2][1]); - } - } - } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) { - std::vector<Geom::Point> points = cubic_bezier->points(); - //points[0] *= trans; // don't do this one here for fun: it is only needed for optimized strokes - points[1] *= trans; - points[2] *= trans; - points[3] *= trans; - if (!optimize_stroke) { - cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); - } else { - points[0] *= trans; // didn't transform this point yet - Geom::Rect swept(points[0], points[3]); - swept.expandTo(points[1]); - swept.expandTo(points[2]); - if (swept.intersects(view)) { - cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); - } else { - cairo_move_to(cr, points[3][0], points[3][1]); - } - } - } -// else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) { -// //TODO: get at the innards and spit them out to cairo -// } - else { - //this case handles sbasis as well as all other curve types - Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); - - //recurse to convert the new path resulting from the sbasis to svgd - for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { - feed_curve_to_cairo(cr, *iter, trans, view, optimize_stroke); - } - } -} - - -/** Feeds path-creating calls to the cairo context translating them from the Path */ -static void -feed_path_to_cairo (cairo_t *ct, Geom::Path const &path) -{ - if (path.empty()) - return; - - cairo_move_to(ct, path.initialPoint()[0], path.initialPoint()[1] ); - - for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { - feed_curve_to_cairo(ct, *cit, Geom::identity(), Geom::Rect(), false); // optimize_stroke is false, so the view rect is not used - } - - if (path.closed()) { - cairo_close_path(ct); - } -} - -/** Feeds path-creating calls to the cairo context translating them from the Path, with the given transform and shift */ -static void -feed_path_to_cairo (cairo_t *ct, Geom::Path const &path, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width) -{ - if (!area) - return; - if (path.empty()) - return; - - // Transform all coordinates to coords within "area" - Geom::Point shift = area->min(); - Geom::Rect view = *area; - view.expandBy (stroke_width); - view = view * (Geom::Affine)Geom::Translate(-shift); - // Pass transformation to feed_curve, so that we don't need to create a whole new path. - Geom::Affine transshift(trans * Geom::Translate(-shift)); - - Geom::Point initial = path.initialPoint() * transshift; - cairo_move_to(ct, initial[0], initial[1] ); - - for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { - feed_curve_to_cairo(ct, *cit, transshift, view, optimize_stroke); - } - - if (path.closed()) { - if (!optimize_stroke) { - cairo_close_path(ct); - } else { - cairo_line_to(ct, initial[0], initial[1]); - /* We cannot use cairo_close_path(ct) here because some parts of the path may have been - clipped and not drawn (maybe the before last segment was outside view area), which - would result in closing the "subpath" after the last interruption, not the entire path. - - However, according to cairo documentation: - The behavior of cairo_close_path() is distinct from simply calling cairo_line_to() with the equivalent coordinate - in the case of stroking. When a closed sub-path is stroked, there are no caps on the ends of the sub-path. Instead, - there is a line join connecting the final and initial segments of the sub-path. - - The correct fix will be possible when cairo introduces methods for moving without - ending/starting subpaths, which we will use for skipping invisible segments; then we - will be able to use cairo_close_path here. This issue also affects ps/eps/pdf export, - see bug 168129 - */ - } - } -} - -/** Feeds path-creating calls to the cairo context translating them from the PathVector, with the given transform and shift - * One must have done cairo_new_path(ct); before calling this function. */ -void -feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width) -{ - if (!area) - return; - if (pathv.empty()) - return; - - for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { - feed_path_to_cairo(ct, *it, trans, area, optimize_stroke, stroke_width); - } -} - -/** Feeds path-creating calls to the cairo context translating them from the PathVector - * One must have done cairo_new_path(ct); before calling this function. */ -void -feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) -{ - if (pathv.empty()) - return; - - for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) { - feed_path_to_cairo(ct, *it); - } -} - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/inkscape-cairo.h b/src/display/inkscape-cairo.h deleted file mode 100644 index 4133661fe..000000000 --- a/src/display/inkscape-cairo.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef __INKSCAPE_CAIRO_H__ -#define __INKSCAPE_CAIRO_H__ - -/* - * Helper functions to use cairo with inkscape - * - * Copyright (C) 2007 bulia byak - * Copyright (C) 2008 Johan Engelen - * - * Released under GNU GPL - * - */ - -#include <2geom/forward.h> -#include <cairo/cairo.h> -#include <boost/optional.hpp> -#include "libnr/nr-rect.h" - -struct NRPixBlock; -class SPCanvasBuf; - -cairo_t *nr_create_cairo_context_canvasbuf (NRRectL *area, SPCanvasBuf *b); -cairo_t *nr_create_cairo_context (NRRectL *area, NRPixBlock *pb); -void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Affine trans, Geom::OptRect area, bool optimize_stroke, double stroke_width); -void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv); - -#endif -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-3dutils.cpp b/src/display/nr-3dutils.cpp index c5e7b4c1b..d2ac7d82b 100644 --- a/src/display/nr-3dutils.cpp +++ b/src/display/nr-3dutils.cpp @@ -9,136 +9,17 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib/gmessages.h> +#include <glib.h> -#include "libnr/nr-pixblock.h" #include "display/nr-3dutils.h" #include <cmath> +#include <2geom/point.h> +#include <2geom/affine.h> namespace NR { -#define BEGIN 0 // TOP or LEFT -#define MIDDLE 1 -#define END 2 // BOTTOM or RIGHT - -#define START(v) ((v)==BEGIN? 1 : 0) -#define FINISH(v) ((v)==END? 1 : 2) - -signed char K_X[3][3][3][3] = { - //K_X[TOP] - { - //K_X[TOP][LEFT] - { - { 0, 0, 0}, - { 0, -2, 2}, - { 0, -1, 1} - }, - { - { 0, 0, 0}, - {-2, 0, 2}, - {-1, 0, 1} - }, - { - { 0, 0, 0}, - {-2, 2, 0}, - {-1, 1, 0} - } - }, - //K_X[MIDDLE] - { - //K_X[MIDDLE][LEFT] - { - { 0, -1, 1}, - { 0, -2, 2}, - { 0, -1, 1} - }, - { - {-1, 0, 1}, - {-2, 0, 2}, - {-1, 0, 1} - }, - { - {-1, 1, 0}, - {-2, 2, 0}, - {-1, 1, 0} - } - }, - //K_X[BOTTOM] - { - //K_X[BOTTOM][LEFT] - { - { 0, -1, 1}, - { 0, -2, 2}, - { 0, 0, 0} - }, - { - {-1, 0, 1}, - {-2, 0, 2}, - { 0, 0, 0} - }, - { - {-1, 1, 0}, - {-2, 2, 0}, - { 0, 0, 0} - } - } -}; - -//K_Y is obtained by transposing K_X globally and each of its components - -gdouble FACTOR_X[3][3] = { - {2./3, 1./3, 2./3}, - {1./2, 1./4, 1./2}, - {2./3, 1./3, 2./3} -}; - -//FACTOR_Y is obtained by transposing FACTOR_X - -inline -int get_carac(int i, int len, int delta) { - if (i < delta) - return BEGIN; - else if (i > len - 1 - delta) - return END; - else - return MIDDLE; -} - -//assumes in is RGBA -//should be made more resistant -void compute_surface_normal(Fvector &N, gdouble ss, NRPixBlock *in, int i, int j, int dx, int dy) { - int w = in->area.x1 - in->area.x0; - int h = in->area.y1 - in->area.y0; - int k, l, alpha_idx, alpha_idx_y; - int x_carac, y_carac; - gdouble alpha; - gdouble accu_x; - gdouble accu_y; - unsigned char *data = NR_PIXBLOCK_PX (in); - g_assert(NR_PIXBLOCK_BPP(in) == 4); - x_carac = get_carac(j, w, dx); //LEFT, MIDDLE or RIGHT - y_carac = get_carac(i, h, dy); //TOP, MIDDLE or BOTTOM - alpha_idx = 4*(i*w + j); - accu_x = 0; - accu_y = 0; - for (k = START(y_carac); k <= FINISH(y_carac); k++) { - alpha_idx_y = alpha_idx + 4*(k-1)*dy*w; - for (l = START(x_carac); l <= FINISH(x_carac); l++) { - alpha = (data + alpha_idx_y + 4*dx*(l-1))[3]; - accu_x += K_X[y_carac][x_carac][k][l] * alpha; - accu_y += K_X[x_carac][y_carac][l][k] * alpha; - } - } - ss /= 255.0; // Correction for scale of pixel values - N[X_3D] = -ss * FACTOR_X[y_carac][x_carac] * accu_x / dx; - N[Y_3D] = -ss * FACTOR_X[x_carac][y_carac] * accu_y / dy; - N[Z_3D] = 1.0; - normalize_vector(N); - //std::cout << "(" << N[X_3D] << ", " << N[Y_3D] << ", " << N[Z_3D] << ")" << std::endl; -} - void convert_coord(gdouble &x, gdouble &y, gdouble &z, Geom::Affine const &trans) { - Point p = Point(x, y); + Geom::Point p = Geom::Point(x, y); p *= trans; x = p[Geom::X]; y = p[Geom::Y]; diff --git a/src/display/nr-3dutils.h b/src/display/nr-3dutils.h index 56bed6ba2..c278c81c6 100644 --- a/src/display/nr-3dutils.h +++ b/src/display/nr-3dutils.h @@ -14,11 +14,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gdk/gdktypes.h> +#include <gdk/gdk.h> #include <2geom/forward.h> -struct NRPixBlock; - namespace NR { #define X_3D 0 @@ -28,12 +26,24 @@ namespace NR { /** * a type of 3 gdouble components vectors */ -typedef gdouble Fvector[3]; +struct Fvector { + Fvector() { + v[0] = v[1] = v[2] = 0.0; + } + Fvector(double x, double y, double z) { + v[0] = x; + v[1] = y; + v[2] = z; + } + double v[3]; + double &operator[](unsigned i) { return v[i]; } + double operator[](unsigned i) const { return v[i]; } +}; /** * The eye vector */ -const static Fvector EYE_VECTOR = {0, 0, 1}; +const static Fvector EYE_VECTOR(0, 0, 1); /** * returns the euclidian norm of the vector v @@ -69,22 +79,6 @@ gdouble scalar_product(const Fvector &a, const Fvector &b); void normalized_sum(Fvector &r, const Fvector &a, const Fvector &b); /** - * Computes the unit suface normal vector of surface given by "in" at (i, j) - * and store it into N. "in" is a (NRPixBlock *) in mode RGBA but only the alpha - * channel is considered as a bump map. ss is the altitude when for the alpha - * value 255. dx and dy are the deltas used to compute in our discrete setting - * - * \param N a reference to a Fvector in which we store the unit surface normal - * \param ss the surface scale - * \param in a NRPixBlock * whose alpha channel codes the surface - * \param i the x coordinate of the point at which we compute the normal - * \param j the y coordinate of the point at which we compute the normal - * \param dx the delta used in the x coordinate - * \param dy the delta used in the y coordinate - */ -void compute_surface_normal(Fvector &N, gdouble ss, NRPixBlock *in, int i, int j, int dx, int dy); - -/** * Applies the transformation matrix to (x, y, z). This function assumes that * trans[0] = trans[3]. x and y are transformed according to trans, z is * multiplied by trans[0]. diff --git a/src/display/nr-arena-forward.h b/src/display/nr-arena-forward.h deleted file mode 100644 index 5a5cf228a..000000000 --- a/src/display/nr-arena-forward.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef __NR_ARENA_FORWARD_H__ -#define __NR_ARENA_FORWARD_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -struct NRArena; -struct NRArenaClass; - -struct NRArenaItem; -struct NRArenaItemClass; - -struct NRArenaGroup; -struct NRArenaGroupClass; - -struct NRArenaShape; -struct NRArenaShapeClass; - -struct NRArenaShapeGroup; -struct NRArenaShapeGroupClass; - -struct NRArenaImage; -struct NRArenaImageClass; - -struct NRArenaGlyphs; -struct NRArenaGlyphsClass; - -struct NRArenaGlyphsGroup; -struct NRArenaGlyphsGroupClass; - -#endif - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-glyphs.cpp b/src/display/nr-arena-glyphs.cpp deleted file mode 100644 index 42bca7d94..000000000 --- a/src/display/nr-arena-glyphs.cpp +++ /dev/null @@ -1,695 +0,0 @@ -#define __NR_ARENA_GLYPHS_C__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2002 Lauris Kaplinski - * - * Released under GNU GPL - * - */ - - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif -#include <libnr/nr-blit.h> -#include <libnr/nr-convert2geom.h> -#include <2geom/affine.h> -#include "../style.h" -#include "nr-arena.h" -#include "nr-arena-glyphs.h" -#include <cairo.h> -#include "inkscape-cairo.h" -#include "display/grayscale.h" - -#ifdef test_glyph_liv -#include "../display/canvas-bpath.h" -#include <libnrtype/font-instance.h> -#include <libnrtype/raster-glyph.h> -#include <libnrtype/RasterFont.h> - -// defined in nr-arena-shape.cpp -void nr_pixblock_render_shape_mask_or(NRPixBlock &m, Shape *theS); -#endif - -#ifdef ENABLE_SVG_FONTS -#include "nr-svgfonts.h" -#endif //#ifdef ENABLE_SVG_FONTS - -static void nr_arena_glyphs_class_init(NRArenaGlyphsClass *klass); -static void nr_arena_glyphs_init(NRArenaGlyphs *glyphs); -static void nr_arena_glyphs_finalize(NRObject *object); - -static guint nr_arena_glyphs_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); -static guint nr_arena_glyphs_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -static NRArenaItem *nr_arena_glyphs_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); - -static NRArenaItemClass *glyphs_parent_class; - -NRType -nr_arena_glyphs_get_type(void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type(NR_TYPE_ARENA_ITEM, - "NRArenaGlyphs", - sizeof(NRArenaGlyphsClass), - sizeof(NRArenaGlyphs), - (void (*)(NRObjectClass *)) nr_arena_glyphs_class_init, - (void (*)(NRObject *)) nr_arena_glyphs_init); - } - return type; -} - -static void -nr_arena_glyphs_class_init(NRArenaGlyphsClass *klass) -{ - NRObjectClass *object_class; - NRArenaItemClass *item_class; - - object_class = (NRObjectClass *) klass; - item_class = (NRArenaItemClass *) klass; - - glyphs_parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent; - - object_class->finalize = nr_arena_glyphs_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaGlyphs>; - - item_class->update = nr_arena_glyphs_update; - item_class->clip = nr_arena_glyphs_clip; - item_class->pick = nr_arena_glyphs_pick; -} - -static void -nr_arena_glyphs_init(NRArenaGlyphs *glyphs) -{ - glyphs->style = NULL; - glyphs->g_transform.setIdentity(); - glyphs->font = NULL; - glyphs->glyph = 0; - - glyphs->rfont = NULL; - glyphs->sfont = NULL; - glyphs->x = glyphs->y = 0.0; -} - -static void -nr_arena_glyphs_finalize(NRObject *object) -{ - NRArenaGlyphs *glyphs = static_cast<NRArenaGlyphs *>(object); - - if (glyphs->rfont) { - glyphs->rfont->Unref(); - glyphs->rfont=NULL; - } - if (glyphs->sfont) { - glyphs->sfont->Unref(); - glyphs->sfont=NULL; - } - - if (glyphs->font) { - glyphs->font->Unref(); - glyphs->font=NULL; - } - - if (glyphs->style) { - sp_style_unref(glyphs->style); - glyphs->style = NULL; - } - - ((NRObjectClass *) glyphs_parent_class)->finalize(object); -} - -static guint -nr_arena_glyphs_update(NRArenaItem *item, NRRectL */*area*/, NRGC *gc, guint /*state*/, guint /*reset*/) -{ - NRArenaGlyphs *glyphs; - raster_font *rfont; - - glyphs = NR_ARENA_GLYPHS(item); - - if (!glyphs->font || !glyphs->style) - return NR_ARENA_ITEM_STATE_ALL; - if ((glyphs->style->fill.isNone()) && (glyphs->style->stroke.isNone())) - return NR_ARENA_ITEM_STATE_ALL; - - NRRect bbox; - bbox.x0 = bbox.y0 = NR_HUGE; - bbox.x1 = bbox.y1 = -NR_HUGE; - - float const scale = gc->transform.descrim(); - - if (!glyphs->style->fill.isNone()) { - Geom::Affine t; - t = glyphs->g_transform * gc->transform; - glyphs->x = t[4]; - glyphs->y = t[5]; - t[4]=0; - t[5]=0; - rfont = glyphs->font->RasterFont(t, 0); - if (glyphs->rfont) glyphs->rfont->Unref(); - glyphs->rfont = rfont; - - if (glyphs->style->stroke.isNone() || fabs(glyphs->style->stroke_width.computed * scale) <= 0.01) { // Optimization: do fill bbox only if there's no stroke - NRRect narea; - if ( glyphs->rfont ) glyphs->rfont->BBox(glyphs->glyph, &narea); - bbox.x0 = narea.x0 + glyphs->x; - bbox.y0 = narea.y0 + glyphs->y; - bbox.x1 = narea.x1 + glyphs->x; - bbox.y1 = narea.y1 + glyphs->y; - } - } - - if (!glyphs->style->stroke.isNone()) { - /* Build state data */ - Geom::Affine t; - t = glyphs->g_transform * gc->transform; - glyphs->x = t[4]; - glyphs->y = t[5]; - t[4]=0; - t[5]=0; - - if ( fabs(glyphs->style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord - font_style nstyl; - nstyl.transform = t; - nstyl.stroke_width=MAX(0.125, glyphs->style->stroke_width.computed * scale); - if ( glyphs->style->stroke_linecap.computed == SP_STROKE_LINECAP_BUTT ) nstyl.stroke_cap=butt_straight; - if ( glyphs->style->stroke_linecap.computed == SP_STROKE_LINECAP_ROUND ) nstyl.stroke_cap=butt_round; - if ( glyphs->style->stroke_linecap.computed == SP_STROKE_LINECAP_SQUARE ) nstyl.stroke_cap=butt_square; - if ( glyphs->style->stroke_linejoin.computed == SP_STROKE_LINEJOIN_MITER ) nstyl.stroke_join=join_pointy; - if ( glyphs->style->stroke_linejoin.computed == SP_STROKE_LINEJOIN_ROUND ) nstyl.stroke_join=join_round; - if ( glyphs->style->stroke_linejoin.computed == SP_STROKE_LINEJOIN_BEVEL ) nstyl.stroke_join=join_straight; - nstyl.stroke_miter_limit = glyphs->style->stroke_miterlimit.value; - nstyl.nbDash=0; - nstyl.dash_offset = 0; - nstyl.dashes=NULL; - if ( glyphs->style->stroke_dash.n_dash > 0 ) { - nstyl.dash_offset = glyphs->style->stroke_dash.offset * scale; - nstyl.nbDash=glyphs->style->stroke_dash.n_dash; - nstyl.dashes=(double*)malloc(nstyl.nbDash*sizeof(double)); - for (int i = 0; i < nstyl.nbDash; i++) nstyl.dashes[i]= glyphs->style->stroke_dash.dash[i] * scale; - } - rfont = glyphs->font->RasterFont( nstyl); - if ( nstyl.dashes ) free(nstyl.dashes); - if (glyphs->sfont) glyphs->sfont->Unref(); - glyphs->sfont = rfont; - - NRRect narea; - if ( glyphs->sfont ) glyphs->sfont->BBox(glyphs->glyph, &narea); - narea.x0-=nstyl.stroke_width; - narea.y0-=nstyl.stroke_width; - narea.x1+=nstyl.stroke_width; - narea.y1+=nstyl.stroke_width; - bbox.x0 = narea.x0 + glyphs->x; - bbox.y0 = narea.y0 + glyphs->y; - bbox.x1 = narea.x1 + glyphs->x; - bbox.y1 = narea.y1 + glyphs->y; - } - } - if (nr_rect_d_test_empty(bbox)) return NR_ARENA_ITEM_STATE_ALL; - - item->bbox.x0 = static_cast<NR::ICoord>(floor(bbox.x0)); - item->bbox.y0 = static_cast<NR::ICoord>(floor(bbox.y0)); - item->bbox.x1 = static_cast<NR::ICoord>(ceil (bbox.x1)); - item->bbox.y1 = static_cast<NR::ICoord>(ceil (bbox.y1)); - - return NR_ARENA_ITEM_STATE_ALL; -} - -static guint -nr_arena_glyphs_clip(NRArenaItem *item, NRRectL */*area*/, NRPixBlock */*pb*/) -{ - NRArenaGlyphs *glyphs; - - glyphs = NR_ARENA_GLYPHS(item); - - if (!glyphs->font ) return item->state; - - /* TODO : render to greyscale pixblock provided for clipping */ - - return item->state; -} - -static NRArenaItem * -nr_arena_glyphs_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int /*sticky*/) -{ - NRArenaGlyphs *glyphs; - - glyphs = NR_ARENA_GLYPHS(item); - - if (!glyphs->font ) return NULL; - if (!glyphs->style) return NULL; - - double const x = p[Geom::X]; - double const y = p[Geom::Y]; - /* With text we take a simple approach: pick if the point is in a characher bbox */ - if ((x + delta >= item->bbox.x0) && (y + delta >= item->bbox.y0) && (x - delta <= item->bbox.x1) && (y - delta <= item->bbox.y1)) return item; - - return NULL; -} - -void -nr_arena_glyphs_set_path(NRArenaGlyphs *glyphs, SPCurve */*curve*/, unsigned int /*lieutenant*/, font_instance *font, gint glyph, Geom::Affine const *transform) -{ - nr_return_if_fail(glyphs != NULL); - nr_return_if_fail(NR_IS_ARENA_GLYPHS(glyphs)); - - nr_arena_item_request_render(NR_ARENA_ITEM(glyphs)); - - if (transform) { - glyphs->g_transform = *transform; - } else { - glyphs->g_transform.setIdentity(); - } - - if (font) font->Ref(); - if (glyphs->font) glyphs->font->Unref(); - glyphs->font=font; - glyphs->glyph = glyph; - - nr_arena_item_request_update(NR_ARENA_ITEM(glyphs), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void -nr_arena_glyphs_set_style(NRArenaGlyphs *glyphs, SPStyle *style) -{ - nr_return_if_fail(glyphs != NULL); - nr_return_if_fail(NR_IS_ARENA_GLYPHS(glyphs)); - - if (style) sp_style_ref(style); - if (glyphs->style) sp_style_unref(glyphs->style); - glyphs->style = style; - - nr_arena_item_request_update(NR_ARENA_ITEM(glyphs), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -static guint -nr_arena_glyphs_fill_mask(NRArenaGlyphs *glyphs, NRRectL *area, NRPixBlock *m) -{ - /* fixme: area == m->area, so merge these */ - - NRArenaItem *item = NR_ARENA_ITEM(glyphs); - - if (glyphs->rfont && nr_rect_l_test_intersect_ptr(area, &item->bbox)) { - raster_glyph *g = glyphs->rfont->GetGlyph(glyphs->glyph); - if ( g ) g->Blit(Geom::Point(glyphs->x, glyphs->y), *m); - } - - return item->state; -} - -static guint -nr_arena_glyphs_stroke_mask(NRArenaGlyphs *glyphs, NRRectL *area, NRPixBlock *m) -{ - NRArenaItem *item = NR_ARENA_ITEM(glyphs); - if (glyphs->sfont && nr_rect_l_test_intersect_ptr(area, &item->bbox)) { - raster_glyph *g=glyphs->sfont->GetGlyph(glyphs->glyph); - if ( g ) g->Blit(Geom::Point(glyphs->x, glyphs->y),*m); - } - - return item->state; -} - -static void nr_arena_glyphs_group_class_init(NRArenaGlyphsGroupClass *klass); -static void nr_arena_glyphs_group_init(NRArenaGlyphsGroup *group); -static void nr_arena_glyphs_group_finalize(NRObject *object); - -static guint nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); -static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static unsigned int nr_arena_glyphs_group_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -static NRArenaItem *nr_arena_glyphs_group_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int sticky); - -static NRArenaGroupClass *group_parent_class; - -NRType -nr_arena_glyphs_group_get_type(void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type(NR_TYPE_ARENA_GROUP, - "NRArenaGlyphsGroup", - sizeof(NRArenaGlyphsGroupClass), - sizeof(NRArenaGlyphsGroup), - (void (*)(NRObjectClass *)) nr_arena_glyphs_group_class_init, - (void (*)(NRObject *)) nr_arena_glyphs_group_init); - } - return type; -} - -static void -nr_arena_glyphs_group_class_init(NRArenaGlyphsGroupClass *klass) -{ - NRObjectClass *object_class; - NRArenaItemClass *item_class; - - object_class = (NRObjectClass *) klass; - item_class = (NRArenaItemClass *) klass; - - group_parent_class = (NRArenaGroupClass *) ((NRObjectClass *) klass)->parent; - - object_class->finalize = nr_arena_glyphs_group_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaGlyphsGroup>; - - item_class->update = nr_arena_glyphs_group_update; - item_class->render = nr_arena_glyphs_group_render; - item_class->clip = nr_arena_glyphs_group_clip; - item_class->pick = nr_arena_glyphs_group_pick; -} - -static void -nr_arena_glyphs_group_init(NRArenaGlyphsGroup *group) -{ - group->style = NULL; - group->paintbox.x0 = group->paintbox.y0 = 0.0F; - group->paintbox.x1 = group->paintbox.y1 = 1.0F; - - group->fill_painter = NULL; - group->stroke_painter = NULL; -} - -static void -nr_arena_glyphs_group_finalize(NRObject *object) -{ - NRArenaGlyphsGroup *group=static_cast<NRArenaGlyphsGroup *>(object); - - if (group->fill_painter) { - sp_painter_free(group->fill_painter); - group->fill_painter = NULL; - } - - if (group->stroke_painter) { - sp_painter_free(group->stroke_painter); - group->stroke_painter = NULL; - } - - if (group->style) { - sp_style_unref(group->style); - group->style = NULL; - } - - ((NRObjectClass *) group_parent_class)->finalize(object); -} - -static guint -nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset) -{ - NRArenaGlyphsGroup *group = NR_ARENA_GLYPHS_GROUP(item); - - if (group->fill_painter) { - sp_painter_free(group->fill_painter); - group->fill_painter = NULL; - } - - if (group->stroke_painter) { - sp_painter_free(group->stroke_painter); - group->stroke_painter = NULL; - } - - item->render_opacity = TRUE; - if (group->style->fill.isPaintserver()) { - group->fill_painter = sp_paint_server_painter_new(SP_STYLE_FILL_SERVER(group->style), - gc->transform, gc->parent->transform, - &group->paintbox); - item->render_opacity = FALSE; - } - - if (group->style->stroke.isPaintserver()) { - group->stroke_painter = sp_paint_server_painter_new(SP_STYLE_STROKE_SERVER(group->style), - gc->transform, gc->parent->transform, - &group->paintbox); - item->render_opacity = FALSE; - } - - if ( item->render_opacity == TRUE && !group->style->stroke.isNone() && !group->style->fill.isNone() ) { - item->render_opacity=FALSE; - } - - if (((NRArenaItemClass *) group_parent_class)->update) - return ((NRArenaItemClass *) group_parent_class)->update(item, area, gc, state, reset); - - return NR_ARENA_ITEM_STATE_ALL; -} - - -static unsigned int -nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int /*flags*/) -{ - NRArenaItem *child; - - NRArenaGroup *group = NR_ARENA_GROUP(item); - NRArenaGlyphsGroup *ggroup = NR_ARENA_GLYPHS_GROUP(item); - SPStyle const *style = ggroup->style; - - guint ret = item->state; - bool print_colors_preview = (item->arena->colorrendermode == Inkscape::COLORRENDERMODE_PRINT_COLORS_PREVIEW); - bool grayscale = (item->arena->colorrendermode == Inkscape::COLORRENDERMODE_GRAYSCALE); - - if (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { - - if (!ct) - return item->state; - - guint32 rgba = item->arena->outlinecolor; - // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat - // by setting color channels in the "wrong" order - cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); - cairo_set_tolerance(ct, 1.25); // low quality, but good enough for outline mode - - for (child = group->children; child != NULL; child = child->next) { - NRArenaGlyphs *g = NR_ARENA_GLYPHS(child); - - Geom::PathVector const * pathv = g->font->PathVector(g->glyph); - - cairo_new_path(ct); - Geom::Affine transform = g->g_transform * group->ctm; - feed_pathvector_to_cairo (ct, *pathv, transform, to_2geom((pb->area).upgrade()), false, 0); - cairo_fill(ct); - pb->empty = FALSE; - } - - return ret; - } - - - - /* Fill */ - if (!style->fill.isNone() || item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { - NRPixBlock m; - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - - /* Render children fill mask */ - for (child = group->children; child != NULL; child = child->next) { - ret = nr_arena_glyphs_fill_mask(NR_ARENA_GLYPHS(child), area, &m); - if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) { - nr_pixblock_release(&m); - return ret; - } - } - - /* Composite into buffer */ - if (style->fill.isPaintserver()) { - if (ggroup->fill_painter) { - nr_arena_render_paintserver_fill(pb, area, ggroup->fill_painter, SP_SCALE24_TO_FLOAT(style->fill_opacity.value), &m); - } - } else if (style->fill.isColor() || item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { - guint32 rgba; - if (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { - // In outline mode, render fill only, using outlinecolor - rgba = item->arena->outlinecolor; - } else if ( item->render_opacity ) { - rgba = style->fill.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->fill_opacity.value) * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = style->fill.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->fill_opacity.value) ); - } - - if (print_colors_preview) { - nr_arena_separate_color_plates(&rgba); - } else if (grayscale) { - rgba = Grayscale::process(rgba); - } - - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - } - - nr_pixblock_release(&m); - } - - /* Stroke */ - if (!style->stroke.isNone() && !(item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE)) { - NRPixBlock m; - guint32 rgba; - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - /* Render children stroke mask */ - for (child = group->children; child != NULL; child = child->next) { - ret = nr_arena_glyphs_stroke_mask(NR_ARENA_GLYPHS(child), area, &m); - if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) { - nr_pixblock_release(&m); - return ret; - } - } - /* Composite into buffer */ - if (style->stroke.isPaintserver()) { - if (ggroup->stroke_painter) { - nr_arena_render_paintserver_fill(pb, area, ggroup->stroke_painter, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value), &m); - } - } else if (style->stroke.isColor()) { - if ( item->render_opacity ) { - rgba = style->stroke.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->stroke_opacity.value) * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = style->stroke.value.color.toRGBA32( SP_SCALE24_TO_FLOAT(style->stroke_opacity.value) ); - } - - if (print_colors_preview) { - nr_arena_separate_color_plates(&rgba); - } else if (grayscale) { - rgba = Grayscale::process(rgba); - } - - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - } else { - // nothing - } - nr_pixblock_release(&m); - } - - return ret; -} - -static unsigned int -nr_arena_glyphs_group_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - NRArenaGroup *group = NR_ARENA_GROUP(item); - - guint ret = item->state; - - /* Render children fill mask */ - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - ret = nr_arena_glyphs_fill_mask(NR_ARENA_GLYPHS(child), area, pb); - if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) return ret; - } - - return ret; -} - -static NRArenaItem * -nr_arena_glyphs_group_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int sticky) -{ - NRArenaItem *picked = NULL; - - if (((NRArenaItemClass *) group_parent_class)->pick) - picked = ((NRArenaItemClass *) group_parent_class)->pick(item, p, delta, sticky); - - if (picked) picked = item; - - return picked; -} - -void -nr_arena_glyphs_group_clear(NRArenaGlyphsGroup *sg) -{ - NRArenaGroup *group = NR_ARENA_GROUP(sg); - - nr_arena_item_request_render(NR_ARENA_ITEM(group)); - - while (group->children) { - nr_arena_item_remove_child(NR_ARENA_ITEM(group), group->children); - } -} - -void -nr_arena_glyphs_group_add_component(NRArenaGlyphsGroup *sg, font_instance *font, int glyph, Geom::Affine const &transform) -{ - NRArenaGroup *group; - - group = NR_ARENA_GROUP(sg); - - Geom::PathVector const * pathv = ( font - ? font->PathVector(glyph) - : NULL ); - if ( pathv ) { - nr_arena_item_request_render(NR_ARENA_ITEM(group)); - - NRArenaItem *new_arena = NRArenaGlyphs::create(group->arena); - nr_arena_item_append_child(NR_ARENA_ITEM(group), new_arena); - nr_arena_item_unref(new_arena); - nr_arena_glyphs_set_path(NR_ARENA_GLYPHS(new_arena), NULL, FALSE, font, glyph, &transform); - nr_arena_glyphs_set_style(NR_ARENA_GLYPHS(new_arena), sg->style); - } -} - -void -nr_arena_glyphs_group_set_style(NRArenaGlyphsGroup *sg, SPStyle *style) -{ - nr_return_if_fail(sg != NULL); - nr_return_if_fail(NR_IS_ARENA_GLYPHS_GROUP(sg)); - - NRArenaGroup *group = NR_ARENA_GROUP(sg); - - if (style) sp_style_ref(style); - if (sg->style) sp_style_unref(sg->style); - sg->style = style; - - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - nr_return_if_fail(NR_IS_ARENA_GLYPHS(child)); - nr_arena_glyphs_set_style(NR_ARENA_GLYPHS(child), sg->style); - } - - nr_arena_item_request_update(NR_ARENA_ITEM(sg), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void -nr_arena_glyphs_group_set_paintbox(NRArenaGlyphsGroup *gg, NRRect const *pbox) -{ - nr_return_if_fail(gg != NULL); - nr_return_if_fail(NR_IS_ARENA_GLYPHS_GROUP(gg)); - nr_return_if_fail(pbox != NULL); - - if ((pbox->x0 < pbox->x1) && (pbox->y0 < pbox->y1)) { - gg->paintbox.x0 = pbox->x0; - gg->paintbox.y0 = pbox->y0; - gg->paintbox.x1 = pbox->x1; - gg->paintbox.y1 = pbox->y1; - } else { - /* fixme: We kill warning, although not sure what to do here (Lauris) */ - gg->paintbox.x0 = gg->paintbox.y0 = 0.0F; - gg->paintbox.x1 = gg->paintbox.y1 = 256.0F; - } - - nr_arena_item_request_update(NR_ARENA_ITEM(gg), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-glyphs.h b/src/display/nr-arena-glyphs.h deleted file mode 100644 index d04fdb4dc..000000000 --- a/src/display/nr-arena-glyphs.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef SEEN_NR_ARENA_GLYPHS_H -#define SEEN_NR_ARENA_GLYPHS_H - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2002 Lauris Kaplinski - * - * Released under GNU GPL - * - */ - -#define NR_TYPE_ARENA_GLYPHS (nr_arena_glyphs_get_type ()) -#define NR_ARENA_GLYPHS(obj) (NR_CHECK_INSTANCE_CAST ((obj), NR_TYPE_ARENA_GLYPHS, NRArenaGlyphs)) -#define NR_IS_ARENA_GLYPHS(obj) (NR_CHECK_INSTANCE_TYPE ((obj), NR_TYPE_ARENA_GLYPHS)) - -#include "libnrtype/nrtype-forward.h" - -#include "forward.h" -#include "sp-paint-server.h" -#include "display/nr-arena-item.h" - -#define test_glyph_liv - -struct SPCurve; -class Shape; - -NRType nr_arena_glyphs_get_type (void); - -struct NRArenaGlyphs : public NRArenaItem { - /* Glyphs data */ - SPStyle *style; - Geom::Affine g_transform; - font_instance *font; - gint glyph; - - raster_font *rfont; - raster_font *sfont; - float x, y; - -// Geom::Affine cached_tr; -// Shape *cached_shp; -// bool cached_shp_dirty; -// bool cached_style_dirty; - -// Shape *stroke_shp; - - static NRArenaGlyphs *create(NRArena *arena) { - NRArenaGlyphs *obj=reinterpret_cast<NRArenaGlyphs *>(nr_object_new(NR_TYPE_ARENA_GLYPHS)); - obj->init(arena); - return obj; - } -}; - -struct NRArenaGlyphsClass { - NRArenaItemClass parent_class; -}; - -void nr_arena_glyphs_set_path ( NRArenaGlyphs *glyphs, - SPCurve *curve, unsigned int lieutenant, - font_instance *font, int glyph, - Geom::Affine const *transform ); -void nr_arena_glyphs_set_style (NRArenaGlyphs *glyphs, SPStyle *style); - -/* Integrated group of component glyphss */ - -typedef struct NRArenaGlyphsGroup NRArenaGlyphsGroup; -typedef struct NRArenaGlyphsGroupClass NRArenaGlyphsGroupClass; - -#include "nr-arena-group.h" - -#define NR_TYPE_ARENA_GLYPHS_GROUP (nr_arena_glyphs_group_get_type ()) -#define NR_ARENA_GLYPHS_GROUP(obj) (NR_CHECK_INSTANCE_CAST ((obj), NR_TYPE_ARENA_GLYPHS_GROUP, NRArenaGlyphsGroup)) -#define NR_IS_ARENA_GLYPHS_GROUP(obj) (NR_CHECK_INSTANCE_TYPE ((obj), NR_TYPE_ARENA_GLYPHS_GROUP)) - -NRType nr_arena_glyphs_group_get_type (void); - -struct NRArenaGlyphsGroup : public NRArenaGroup { - //SPStyle *style; - NRRect paintbox; - /* State data */ - SPPainter *fill_painter; - SPPainter *stroke_painter; - - static NRArenaGlyphsGroup *create(NRArena *arena) { - NRArenaGlyphsGroup *obj=reinterpret_cast<NRArenaGlyphsGroup *>(nr_object_new(NR_TYPE_ARENA_GLYPHS_GROUP)); - obj->init(arena); - return obj; - } -}; - -struct NRArenaGlyphsGroupClass { - NRArenaGroupClass parent_class; -}; - -/* Utility functions */ - -void nr_arena_glyphs_group_clear (NRArenaGlyphsGroup *group); - -void nr_arena_glyphs_group_add_component (NRArenaGlyphsGroup *group, font_instance *font, int glyph, Geom::Affine const &transform); - -void nr_arena_glyphs_group_set_style (NRArenaGlyphsGroup *group, SPStyle *style); - -void nr_arena_glyphs_group_set_paintbox (NRArenaGlyphsGroup *group, const NRRect *pbox); - -#endif // SEEN_NR_ARENA_GLYPHS_H - -/* - 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 : diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp deleted file mode 100644 index 6c6043b2c..000000000 --- a/src/display/nr-arena-group.cpp +++ /dev/null @@ -1,301 +0,0 @@ -#define __NR_ARENA_GROUP_C__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "display/nr-arena-group.h" -#include "display/nr-filter.h" -#include "display/nr-filter-gaussian.h" -#include "display/nr-filter-types.h" -#include "style.h" -#include "sp-filter.h" -#include "sp-filter-reference.h" -#include "sp-gaussian-blur.h" -#include "filters/blend.h" -#include "display/nr-filter-blend.h" -#include "helper/geom.h" - -static void nr_arena_group_class_init (NRArenaGroupClass *klass); -static void nr_arena_group_init (NRArenaGroup *group); - -static NRArenaItem *nr_arena_group_children (NRArenaItem *item); -static NRArenaItem *nr_arena_group_last_child (NRArenaItem *item); -static void nr_arena_group_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); -static void nr_arena_group_remove_child (NRArenaItem *item, NRArenaItem *child); -static void nr_arena_group_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); - -static unsigned int nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); -static unsigned int nr_arena_group_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static unsigned int nr_arena_group_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -static NRArenaItem *nr_arena_group_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); - -static NRArenaItemClass *parent_class; - -NRType -nr_arena_group_get_type (void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type (NR_TYPE_ARENA_ITEM, - "NRArenaGroup", - sizeof (NRArenaGroupClass), - sizeof (NRArenaGroup), - (void (*) (NRObjectClass *)) nr_arena_group_class_init, - (void (*) (NRObject *)) nr_arena_group_init); - } - return type; -} - -static void -nr_arena_group_class_init (NRArenaGroupClass *klass) -{ - NRObjectClass *object_class; - NRArenaItemClass *item_class; - - object_class = (NRObjectClass *) klass; - item_class = (NRArenaItemClass *) klass; - - parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent; - - object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaGroup>; - - item_class->children = nr_arena_group_children; - item_class->last_child = nr_arena_group_last_child; - item_class->add_child = nr_arena_group_add_child; - item_class->set_child_position = nr_arena_group_set_child_position; - item_class->remove_child = nr_arena_group_remove_child; - item_class->update = nr_arena_group_update; - item_class->render = nr_arena_group_render; - item_class->clip = nr_arena_group_clip; - item_class->pick = nr_arena_group_pick; -} - -static void -nr_arena_group_init (NRArenaGroup *group) -{ - group->transparent = FALSE; - group->children = NULL; - group->last = NULL; - group->style = NULL; - group->child_transform.setIdentity(); -} - -static NRArenaItem * -nr_arena_group_children (NRArenaItem *item) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - return group->children; -} - -static NRArenaItem * -nr_arena_group_last_child (NRArenaItem *item) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - return group->last; -} - -static void -nr_arena_group_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - if (!ref) { - group->children = nr_arena_item_attach (item, child, NULL, group->children); - } else { - ref->next = nr_arena_item_attach (item, child, ref, ref->next); - } - - if (ref == group->last) group->last = child; - - nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -static void -nr_arena_group_remove_child (NRArenaItem *item, NRArenaItem *child) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - if (child == group->last) group->last = child->prev; - - if (child->prev) { - nr_arena_item_detach (item, child); - } else { - group->children = nr_arena_item_detach (item, child); - } - - nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -static void -nr_arena_group_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - if (child == group->last) group->last = child->prev; - - if (child->prev) { - nr_arena_item_detach (item, child); - } else { - group->children = nr_arena_item_detach (item, child); - } - - if (!ref) { - group->children = nr_arena_item_attach (item, child, NULL, group->children); - } else { - ref->next = nr_arena_item_attach (item, child, ref, ref->next); - } - - if (ref == group->last) group->last = child; - - nr_arena_item_request_render (child); -} - -static unsigned int -nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset) -{ - unsigned int newstate; - - NRArenaGroup *group = NR_ARENA_GROUP (item); - - unsigned int beststate = NR_ARENA_ITEM_STATE_ALL; - - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - NRGC cgc(gc); - cgc.transform = group->child_transform * gc->transform; - newstate = nr_arena_item_invoke_update (child, area, &cgc, state, reset); - beststate = beststate & newstate; - } - - if (beststate & NR_ARENA_ITEM_STATE_BBOX) { - nr_rect_l_set_empty (&item->bbox); - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - if (child->visible) - nr_rect_l_union (&item->bbox, &item->bbox, &child->drawbox); - } - } - - return beststate; -} - -void nr_arena_group_set_style (NRArenaGroup *group, SPStyle *style) -{ - g_return_if_fail(group != NULL); - g_return_if_fail(NR_IS_ARENA_GROUP(group)); - - if (style) sp_style_ref(style); - if (group->style) sp_style_unref(group->style); - group->style = style; - - //if group has a filter - if (style->filter.set && style->getFilter()) { - if (!group->filter) { - int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); - group->filter = new Inkscape::Filters::Filter(primitives); - } - sp_filter_build_renderer(SP_FILTER(style->getFilter()), group->filter); - } else { - //no filter set for this group - delete group->filter; - group->filter = NULL; - } - - if (style && style->enable_background.set - && style->enable_background.value == SP_CSS_BACKGROUND_NEW) { - group->background_new = true; - } -} - -static unsigned int -nr_arena_group_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - unsigned int ret = item->state; - - /* Just compose children into parent buffer */ - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - ret = nr_arena_item_invoke_render (ct, child, area, pb, flags); - if (ret & NR_ARENA_ITEM_STATE_INVALID) break; - } - - return ret; -} - -static unsigned int -nr_arena_group_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - unsigned int ret = item->state; - - /* Just compose children into parent buffer */ - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - ret = nr_arena_item_invoke_clip (child, area, pb); - if (ret & NR_ARENA_ITEM_STATE_INVALID) break; - } - - return ret; -} - -static NRArenaItem * -nr_arena_group_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky) -{ - NRArenaGroup *group = NR_ARENA_GROUP (item); - - for (NRArenaItem *child = group->last; child != NULL; child = child->prev) { - NRArenaItem *picked = nr_arena_item_invoke_pick (child, p, delta, sticky); - if (picked) - return (group->transparent) ? picked : item; - } - - return NULL; -} - -void -nr_arena_group_set_transparent (NRArenaGroup *group, unsigned int transparent) -{ - nr_return_if_fail (group != NULL); - nr_return_if_fail (NR_IS_ARENA_GROUP (group)); - - group->transparent = transparent; -} - -void nr_arena_group_set_child_transform(NRArenaGroup *group, Geom::Affine const &t) -{ - Geom::Affine nt(t); - nr_arena_group_set_child_transform(group, &nt); -} - -void nr_arena_group_set_child_transform(NRArenaGroup *group, Geom::Affine const *t) -{ - if (!t) t = &GEOM_MATRIX_IDENTITY; - - if (!Geom::matrix_equalp(*t, group->child_transform, NR_EPSILON)) { - nr_arena_item_request_render (NR_ARENA_ITEM (group)); - group->child_transform = *t; - nr_arena_item_request_update (NR_ARENA_ITEM (group), NR_ARENA_ITEM_STATE_ALL, TRUE); - } -} - - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-group.h b/src/display/nr-arena-group.h deleted file mode 100644 index 58394643c..000000000 --- a/src/display/nr-arena-group.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __NR_ARENA_GROUP_H__ -#define __NR_ARENA_GROUP_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@ximian.com> - * - * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. - * - * Released under GNU GPL - * - */ - -#define NR_TYPE_ARENA_GROUP (nr_arena_group_get_type ()) -#define NR_ARENA_GROUP(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ARENA_GROUP, NRArenaGroup)) -#define NR_IS_ARENA_GROUP(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA_GROUP)) - -#include "nr-arena-item.h" -#include "style.h" - -NRType nr_arena_group_get_type (void); - -struct NRArenaGroup : public NRArenaItem{ - unsigned int transparent : 1; - NRArenaItem *children; - NRArenaItem *last; - Geom::Affine child_transform; - SPStyle *style; - - static NRArenaGroup *create(NRArena *arena) { - NRArenaGroup *obj = reinterpret_cast<NRArenaGroup *>(nr_object_new(NR_TYPE_ARENA_GROUP)); - obj->init(arena); - return obj; - } -}; - -struct NRArenaGroupClass { - NRArenaItemClass parent_class; -}; - -void nr_arena_group_set_transparent(NRArenaGroup *group, unsigned int transparent); - -void nr_arena_group_set_child_transform(NRArenaGroup *group, Geom::Affine const &t); -void nr_arena_group_set_child_transform(NRArenaGroup *group, Geom::Affine const *t); -void nr_arena_group_set_style(NRArenaGroup *group, SPStyle *style); - -#endif - - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp deleted file mode 100644 index 6539ae4d2..000000000 --- a/src/display/nr-arena-image.cpp +++ /dev/null @@ -1,392 +0,0 @@ -#define __NR_ARENA_IMAGE_C__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <libnr/nr-compose-transform.h> -#include <2geom/transforms.h> -#include <libnr/nr-blit.h> -#include "../preferences.h" -#include "nr-arena-image.h" -#include "style.h" -#include "display/nr-arena.h" -#include "display/nr-filter.h" -#include "display/nr-filter-gaussian.h" -#include "sp-filter.h" -#include "sp-filter-reference.h" -#include "sp-gaussian-blur.h" -#include "filters/blend.h" -#include "display/nr-filter-blend.h" - -int nr_arena_image_x_sample = 1; -int nr_arena_image_y_sample = 1; - -/* - * NRArenaCanvasImage - * - */ - -static void nr_arena_image_class_init (NRArenaImageClass *klass); -static void nr_arena_image_init (NRArenaImage *image); -static void nr_arena_image_finalize (NRObject *object); - -static unsigned int nr_arena_image_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); -static unsigned int nr_arena_image_render (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static NRArenaItem *nr_arena_image_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); - -static NRArenaItemClass *parent_class; - -NRType -nr_arena_image_get_type (void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type (NR_TYPE_ARENA_ITEM, - "NRArenaImage", - sizeof (NRArenaImageClass), - sizeof (NRArenaImage), - (void (*) (NRObjectClass *)) nr_arena_image_class_init, - (void (*) (NRObject *)) nr_arena_image_init); - } - return type; -} - -static void -nr_arena_image_class_init (NRArenaImageClass *klass) -{ - NRObjectClass *object_class; - NRArenaItemClass *item_class; - - object_class = (NRObjectClass *) klass; - item_class = (NRArenaItemClass *) klass; - - parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent; - - object_class->finalize = nr_arena_image_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaImage>; - - item_class->update = nr_arena_image_update; - item_class->render = nr_arena_image_render; - item_class->pick = nr_arena_image_pick; -} - -static void -nr_arena_image_init (NRArenaImage *image) -{ - image->px = NULL; - - image->pxw = image->pxh = image->pxrs = 0; - image->x = image->y = 0.0; - image->width = 256.0; - image->height = 256.0; - - image->grid2px.setIdentity(); - - image->style = 0; - image->render_opacity = TRUE; -} - -static void -nr_arena_image_finalize (NRObject *object) -{ - NRArenaImage *image = NR_ARENA_IMAGE (object); - - image->px = NULL; - - ((NRObjectClass *) parent_class)->finalize (object); -} - -static unsigned int -nr_arena_image_update( NRArenaItem *item, NRRectL */*area*/, NRGC *gc, unsigned int /*state*/, unsigned int /*reset*/ ) -{ - Geom::Affine grid2px; - - // clear old bbox - nr_arena_item_request_render(item); - - NRArenaImage *image = NR_ARENA_IMAGE (item); - - /* Copy affine */ - grid2px = gc->transform.inverse(); - double hscale, vscale; // todo: replace with Geom::Scale - if (image->px) { - hscale = image->pxw / image->width; - vscale = image->pxh / image->height; - } else { - hscale = 1.0; - vscale = 1.0; - } - - image->grid2px[0] = grid2px[0] * hscale; - image->grid2px[2] = grid2px[2] * hscale; - image->grid2px[4] = grid2px[4] * hscale; - image->grid2px[1] = grid2px[1] * vscale; - image->grid2px[3] = grid2px[3] * vscale; - image->grid2px[5] = grid2px[5] * vscale; - - image->grid2px[4] -= image->x * hscale; - image->grid2px[5] -= image->y * vscale; - - /* Calculate bbox */ - if (image->px) { - NRRect bbox; - - bbox.x0 = image->x; - bbox.y0 = image->y; - bbox.x1 = image->x + image->width; - bbox.y1 = image->y + image->height; - - image->c00 = (Geom::Point(bbox.x0, bbox.y0) * gc->transform); - image->c01 = (Geom::Point(bbox.x0, bbox.y1) * gc->transform); - image->c10 = (Geom::Point(bbox.x1, bbox.y0) * gc->transform); - image->c11 = (Geom::Point(bbox.x1, bbox.y1) * gc->transform); - - nr_rect_d_matrix_transform (&bbox, &bbox, gc->transform); - - item->bbox.x0 = static_cast<NR::ICoord>(floor(bbox.x0)); // Floor gives the coordinate in which the point resides - item->bbox.y0 = static_cast<NR::ICoord>(floor(bbox.y0)); - item->bbox.x1 = static_cast<NR::ICoord>(ceil (bbox.x1)); // Ceil gives the first coordinate beyond the point - item->bbox.y1 = static_cast<NR::ICoord>(ceil (bbox.y1)); - } else { - item->bbox.x0 = (int) gc->transform[4]; - item->bbox.y0 = (int) gc->transform[5]; - item->bbox.x1 = item->bbox.x0 - 1; - item->bbox.y1 = item->bbox.y0 - 1; - } - - return NR_ARENA_ITEM_STATE_ALL; -} - -#define FBITS 12 -#define b2i (image->grid2px) - -static unsigned int -nr_arena_image_render( cairo_t *ct, NRArenaItem *item, NRRectL */*area*/, NRPixBlock *pb, unsigned int /*flags*/ ) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - nr_arena_image_x_sample = prefs->getInt("/options/bitmapoversample/value", 1); - nr_arena_image_y_sample = nr_arena_image_x_sample; - - bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - - NRArenaImage *image = NR_ARENA_IMAGE (item); - - Geom::Affine d2s; - - d2s[0] = b2i[0]; - d2s[1] = b2i[1]; - d2s[2] = b2i[2]; - d2s[3] = b2i[3]; - d2s[4] = b2i[0] * pb->area.x0 + b2i[2] * pb->area.y0 + b2i[4]; - d2s[5] = b2i[1] * pb->area.x0 + b2i[3] * pb->area.y0 + b2i[5]; - - if (!outline) { - - if (!image->px) return item->state; - - guint32 Falpha = item->opacity; - if (Falpha < 1) return item->state; - - unsigned char * dpx = NR_PIXBLOCK_PX (pb); - int const drs = pb->rs; - int const dw = pb->area.x1 - pb->area.x0; - int const dh = pb->area.y1 - pb->area.y0; - - unsigned char * spx = image->px; - int const srs = image->pxrs; - int const sw = image->pxw; - int const sh = image->pxh; - - if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) { - /* fixme: This is not implemented yet (Lauris) */ - /* nr_R8G8B8_R8G8B8_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); */ - } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) { - nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); - } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) { - nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (dpx, dw, dh, drs, spx, sw, sh, srs, d2s, Falpha, nr_arena_image_x_sample, nr_arena_image_y_sample); - } - - pb->empty = FALSE; - - } else { // outline; draw a rect instead - - if (!ct) - return item->state; - - guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff); - // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat - // by setting color channels in the "wrong" order - cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); - - cairo_set_line_width(ct, 0.5); - cairo_new_path(ct); - - Geom::Point shift(pb->area.x0, pb->area.y0); - Geom::Point c00 = image->c00 - shift; - Geom::Point c01 = image->c01 - shift; - Geom::Point c11 = image->c11 - shift; - Geom::Point c10 = image->c10 - shift; - - cairo_move_to (ct, c00[Geom::X], c00[Geom::Y]); - - // the box - cairo_line_to (ct, c10[Geom::X], c10[Geom::Y]); - cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]); - cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]); - cairo_line_to (ct, c00[Geom::X], c00[Geom::Y]); - // the diagonals - cairo_line_to (ct, c11[Geom::X], c11[Geom::Y]); - cairo_move_to (ct, c10[Geom::X], c10[Geom::Y]); - cairo_line_to (ct, c01[Geom::X], c01[Geom::Y]); - - cairo_stroke(ct); - - pb->empty = FALSE; - } - - return item->state; -} - -/** Calculates the closest distance from p to the segment a1-a2*/ -double -distance_to_segment (Geom::Point p, Geom::Point a1, Geom::Point a2) -{ - // calculate sides of the triangle and their squares - double d1 = Geom::L2(p - a1); - double d1_2 = d1 * d1; - double d2 = Geom::L2(p - a2); - double d2_2 = d2 * d2; - double a = Geom::L2(a1 - a2); - double a_2 = a * a; - - // if one of the angles at the base is > 90, return the corresponding side - if (d1_2 + a_2 <= d2_2) return d1; - if (d2_2 + a_2 <= d1_2) return d2; - - // otherwise calculate the height to the base - double peri = (a + d1 + d2)/2; - return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a); -} - -static NRArenaItem * -nr_arena_image_pick( NRArenaItem *item, Geom::Point p, double delta, unsigned int /*sticky*/ ) -{ - NRArenaImage *image = NR_ARENA_IMAGE (item); - - if (!image->px) return NULL; - - bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - - if (outline) { - - // frame - if (distance_to_segment (p, image->c00, image->c10) < delta) return item; - if (distance_to_segment (p, image->c10, image->c11) < delta) return item; - if (distance_to_segment (p, image->c11, image->c01) < delta) return item; - if (distance_to_segment (p, image->c01, image->c00) < delta) return item; - - // diagonals - if (distance_to_segment (p, image->c00, image->c11) < delta) return item; - if (distance_to_segment (p, image->c10, image->c01) < delta) return item; - - return NULL; - - } else { - - unsigned char *const pixels = image->px; - int const width = image->pxw; - int const height = image->pxh; - int const rowstride = image->pxrs; - Geom::Point tp = p * image->grid2px; - int const ix = (int)(tp[Geom::X]); - int const iy = (int)(tp[Geom::Y]); - - if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height)) - return NULL; - - unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4; - // is the alpha not transparent? - return (pix_ptr[3] > 0) ? item : NULL; - } -} - -/* Utility */ - -void -nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs) -{ - nr_return_if_fail (image != NULL); - nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); - - image->px = (unsigned char *) px; - image->pxw = pxw; - image->pxh = pxh; - image->pxrs = pxrs; - - nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void -nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height) -{ - nr_return_if_fail (image != NULL); - nr_return_if_fail (NR_IS_ARENA_IMAGE (image)); - - image->x = x; - image->y = y; - image->width = width; - image->height = height; - - nr_arena_item_request_update (NR_ARENA_ITEM (image), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style) -{ - g_return_if_fail(image != NULL); - g_return_if_fail(NR_IS_ARENA_IMAGE(image)); - - if (style) sp_style_ref(style); - if (image->style) sp_style_unref(image->style); - image->style = style; - - //if image has a filter - if (style->filter.set && style->getFilter()) { - if (!image->filter) { - int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); - image->filter = new Inkscape::Filters::Filter(primitives); - } - sp_filter_build_renderer(SP_FILTER(style->getFilter()), image->filter); - } else { - //no filter set for this image - delete image->filter; - image->filter = NULL; - } - - if (style && style->enable_background.set - && style->enable_background.value == SP_CSS_BACKGROUND_NEW) { - image->background_new = true; - } - - nr_arena_item_request_update(image, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-image.h b/src/display/nr-arena-image.h deleted file mode 100644 index c8d1525dc..000000000 --- a/src/display/nr-arena-image.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef __NR_ARENA_IMAGE_H__ -#define __NR_ARENA_IMAGE_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#define NR_TYPE_ARENA_IMAGE (nr_arena_image_get_type ()) -#define NR_ARENA_IMAGE(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ARENA_IMAGE, NRArenaImage)) -#define NR_IS_ARENA_IMAGE(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA_IMAGE)) - -#include "nr-arena-item.h" -#include "style.h" - -NRType nr_arena_image_get_type (void); - -struct NRArenaImage : public NRArenaItem { - unsigned char *px; - unsigned int pxw; - unsigned int pxh; - unsigned int pxrs; - - double x, y; - double width, height; - - Geom::Point c00, c01, c11, c10; // all 4 corners of the image, for outline mode rect - - /* From GRID to PIXELS */ - Geom::Affine grid2px; - - SPStyle *style; - - static NRArenaImage *create(NRArena *arena) { - NRArenaImage *obj=reinterpret_cast<NRArenaImage *>(nr_object_new(NR_TYPE_ARENA_IMAGE)); - obj->init(arena); - return obj; - } -}; - -struct NRArenaImageClass { - NRArenaItemClass parent_class; -}; - -void nr_arena_image_set_pixels (NRArenaImage *image, unsigned char const *px, unsigned int pxw, unsigned int pxh, unsigned int pxrs); -void nr_arena_image_set_geometry (NRArenaImage *image, double x, double y, double width, double height); -void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style); - - -#endif - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp deleted file mode 100644 index 6a5b7b5ba..000000000 --- a/src/display/nr-arena-item.cpp +++ /dev/null @@ -1,949 +0,0 @@ -#define __NR_ARENA_ITEM_C__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#define noNR_ARENA_ITEM_VERBOSE -#define noNR_ARENA_ITEM_DEBUG_CASCADE - -#include <cstring> -#include <string> - -#include <libnr/nr-blit.h> -#include <libnr/nr-pixops.h> -#include "nr-arena.h" -#include "nr-arena-item.h" -#include "gc-core.h" -#include "helper/geom.h" - -#include "nr-filter.h" -#include "nr-arena-group.h" -#include "preferences.h" - -namespace GC = Inkscape::GC; - -static void nr_arena_item_class_init (NRArenaItemClass *klass); -static void nr_arena_item_init (NRArenaItem *item); -static void nr_arena_item_private_finalize (NRObject *object); - -static NRObjectClass *parent_class; - -NRType -nr_arena_item_get_type (void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type (NR_TYPE_OBJECT, - "NRArenaItem", - sizeof (NRArenaItemClass), - sizeof (NRArenaItem), - (void (*)(NRObjectClass *)) - nr_arena_item_class_init, - (void (*)(NRObject *)) - nr_arena_item_init); - } - return type; -} - -static void -nr_arena_item_class_init (NRArenaItemClass *klass) -{ - NRObjectClass *object_class; - - object_class = (NRObjectClass *) klass; - - parent_class = ((NRObjectClass *) klass)->parent; - - object_class->finalize = nr_arena_item_private_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor < NRArenaItem >; -} - -static void -nr_arena_item_init (NRArenaItem *item) -{ - item->arena = NULL; - item->parent = NULL; - item->next = item->prev = NULL; - - item->key = 0; - - item->state = 0; - item->sensitive = TRUE; - item->visible = TRUE; - - memset (&item->bbox, 0, sizeof (item->bbox)); - memset (&item->drawbox, 0, sizeof (item->drawbox)); - item->transform = NULL; - item->opacity = 255; - item->render_opacity = FALSE; - - item->transform = NULL; - item->clip = NULL; - item->mask = NULL; - item->px = NULL; - item->data = NULL; - item->filter = NULL; - item->background_pb = NULL; - item->background_new = false; -} - -static void -nr_arena_item_private_finalize (NRObject *object) -{ - NRArenaItem *item = static_cast < NRArenaItem * >(object); - - item->px = NULL; - item->transform = NULL; - - ((NRObjectClass *) (parent_class))->finalize (object); -} - -NRArenaItem * -nr_arena_item_children (NRArenaItem *item) -{ - nr_return_val_if_fail (item != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL); - - if (NR_ARENA_ITEM_VIRTUAL (item, children)) - return NR_ARENA_ITEM_VIRTUAL (item, children) (item); - - return NULL; -} - -NRArenaItem * -nr_arena_item_last_child (NRArenaItem *item) -{ - nr_return_val_if_fail (item != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL); - - if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) { - return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item); - } else { - NRArenaItem *ref = nr_arena_item_children (item); - if (ref) - while (ref->next) - ref = ref->next; - return ref; - } -} - -void -nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, - NRArenaItem *ref) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (child != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (child)); - nr_return_if_fail (child->parent == NULL); - nr_return_if_fail (child->prev == NULL); - nr_return_if_fail (child->next == NULL); - nr_return_if_fail (child->arena == item->arena); - nr_return_if_fail (child != ref); - nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref)); - nr_return_if_fail (!ref || (ref->parent == item)); - - if (NR_ARENA_ITEM_VIRTUAL (item, add_child)) - NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref); -} - -void -nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (child != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (child)); - nr_return_if_fail (child->parent == item); - - if (NR_ARENA_ITEM_VIRTUAL (item, remove_child)) - NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child); -} - -void -nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, - NRArenaItem *ref) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (child != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (child)); - nr_return_if_fail (child->parent == item); - nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref)); - nr_return_if_fail (!ref || (ref->parent == item)); - - if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position)) - NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref); -} - -NRArenaItem * -nr_arena_item_ref (NRArenaItem *item) -{ - nr_object_ref ((NRObject *) item); - - return item; -} - -NRArenaItem * -nr_arena_item_unref (NRArenaItem *item) -{ - nr_object_unref ((NRObject *) item); - - return NULL; -} - -unsigned int -nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, - unsigned int state, unsigned int reset) -{ - NRGC childgc (gc); - bool filter = (item->arena->rendermode == Inkscape::RENDERMODE_NORMAL); - - nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), - NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), - NR_ARENA_ITEM_STATE_INVALID); - -#ifdef NR_ARENA_ITEM_DEBUG_CASCADE - printf ("Update %s:%p %x %x %x\n", - nr_type_name_from_instance ((GTypeInstance *) item), item, state, - item->state, reset); -#endif - - /* return if in error */ - if (item->state & NR_ARENA_ITEM_STATE_INVALID) - return item->state; - /* Set reset flags according to propagation status */ - if (item->propagate) { - reset |= ~item->state; - item->propagate = FALSE; - } - /* Reset our state */ - item->state &= ~reset; - /* Return if NOP */ - if (!(~item->state & state)) - return item->state; - /* Test whether to return immediately */ - if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) { - if (!nr_rect_l_test_intersect_ptr(area, &item->drawbox)) - return item->state; - } - - /* Reset image cache, if not to be kept */ - if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) { - item->px = NULL; - } - - /* Set up local gc */ - childgc = *gc; - if (item->transform) { - childgc.transform = (*item->transform) * childgc.transform; - } - /* Remember the transformation matrix */ - item->ctm = childgc.transform; - - /* Invoke the real method */ - // that will update bbox - item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset); - if (item->state & NR_ARENA_ITEM_STATE_INVALID) - return item->state; - - // get a copy of bbox - memcpy(&item->drawbox, &item->bbox, sizeof(item->bbox)); - - /* Enlarge the drawbox to contain filter effects */ - if (item->filter && filter) { - item->filter->bbox_enlarge (item->drawbox); - } - // fixme: to fix the display glitches, in outline mode bbox must be a combination of - // full item bbox and its clip and mask (after we have the API to get these) - - /* Clipping */ - if (item->clip) { - // FIXME: since here we only need bbox, consider passing - // ((state & !(NR_ARENA_ITEM_STATE_RENDER)) | NR_ARENA_ITEM_STATE_BBOX) - // instead of state, so it does not have to create rendering structures in nr_arena_shape_update - unsigned int newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset); - if (newstate & NR_ARENA_ITEM_STATE_INVALID) { - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - // for clipping, we need geometric bbox - nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->clip->bbox); - } - /* Masking */ - if (item->mask) { - unsigned int newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset); - if (newstate & NR_ARENA_ITEM_STATE_INVALID) { - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - // for masking, we need full drawbox of mask - nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->mask->drawbox); - } - - // now that we know drawbox, dirty the corresponding rect on canvas: - if (!NR_IS_ARENA_GROUP(item) || (item->filter && filter)) { - // unless filtered, groups do not need to render by themselves, only their members - nr_arena_item_request_render (item); - } - - return item->state; -} - -/** - * Render item to pixblock. - * - * \return Has NR_ARENA_ITEM_STATE_RENDER set on success. - */ - -unsigned int -nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area, - NRPixBlock *pb, unsigned int flags) -{ - bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - bool filter = (item->arena->rendermode != Inkscape::RENDERMODE_OUTLINE && - item->arena->rendermode != Inkscape::RENDERMODE_NO_FILTERS); - bool print_colors = (item->arena->colorrendermode == Inkscape::COLORRENDERMODE_PRINT_COLORS_PREVIEW); - - nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), - NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, - item->state); - -#ifdef NR_ARENA_ITEM_VERBOSE - g_message ("Invoke render %p on %p: %d %d - %d %d, %d %d - %d %d", item, pb, - area->x0, area->y0, - area->x1, area->y1, - item->drawbox.x0, item->drawbox.y0, - item->drawbox.x1, item->drawbox.y1); -#endif - - /* If we are invisible, just return successfully */ - if (!item->visible) - return item->state | NR_ARENA_ITEM_STATE_RENDER; - - NRRectL carea; - nr_rect_l_intersect (&carea, area, &item->drawbox); - if (nr_rect_l_test_empty(carea)) - return item->state | NR_ARENA_ITEM_STATE_RENDER; - if (item->filter && filter) { - item->filter->area_enlarge (carea, item); - nr_rect_l_intersect (&carea, &carea, &item->drawbox); - } - - if (outline) { - // No caching in outline mode for now; investigate if it really gives any advantage with cairo. - // Also no attempts to clip anything; just render everything: item, clip, mask - // First, render the object itself - unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, pb, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - - // render clip and mask, if any - guint32 saved_rgba = item->arena->outlinecolor; // save current outline color - // render clippath as an object, using a different color - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (item->clip) { - item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips - NR_ARENA_ITEM_VIRTUAL (item->clip, render) (ct, item->clip, &carea, pb, flags); - } - // render mask as an object, using a different color - if (item->mask) { - item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks - NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, pb, flags); - } - item->arena->outlinecolor = saved_rgba; // restore outline color - - return item->state | NR_ARENA_ITEM_STATE_RENDER; - } - - NRPixBlock cpb; - if (item->px) { - /* Has cache pixblock, render this and return */ - nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, - /* fixme: This probably cannot overflow, because we render only if visible */ - /* fixme: and pixel cache is there only for small items */ - /* fixme: But this still needs extra check (Lauris) */ - item->drawbox.x0, item->drawbox.y0, - item->drawbox.x1, item->drawbox.y1, - item->px, - 4 * (item->drawbox.x1 - item->drawbox.x0), FALSE, - FALSE); - nr_blit_pixblock_pixblock (pb, &cpb); - nr_pixblock_release (&cpb); - pb->empty = FALSE; - return item->state | NR_ARENA_ITEM_STATE_RENDER; - } - - NRPixBlock *dpb = pb; - - /* Setup cache if we can */ - if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) && - (carea.x0 <= item->drawbox.x0) && (carea.y0 <= item->drawbox.y0) && - (carea.x1 >= item->drawbox.x1) && (carea.y1 >= item->drawbox.y1) && - (((item->drawbox.x1 - item->drawbox.x0) * (item->drawbox.y1 - - item->drawbox.y0)) <= 4096)) { - // Item drawbox is fully in renderable area and size is acceptable - carea.x0 = item->drawbox.x0; - carea.y0 = item->drawbox.y0; - carea.x1 = item->drawbox.x1; - carea.y1 = item->drawbox.y1; - item->px = - new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * - (carea.y1 - carea.y0)]; - nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, - carea.y0, carea.x1, carea.y1, item->px, - 4 * (carea.x1 - carea.x0), TRUE, TRUE); - cpb.visible_area = pb->visible_area; - dpb = &cpb; - // Set nocache flag for downstream rendering - flags |= NR_ARENA_ITEM_RENDER_NO_CACHE; - } - - /* Determine, whether we need temporary buffer */ - if (item->clip || item->mask - || ((item->opacity != 255) && !item->render_opacity) - || (item->filter && filter) || item->background_new) { - - /* Setup and render item buffer */ - NRPixBlock ipb; - nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, - carea.x0, carea.y0, carea.x1, carea.y1, - TRUE); - - // if memory allocation failed, abort render - if (ipb.size != NR_PIXBLOCK_SIZE_TINY && ipb.data.px == NULL) { - nr_pixblock_release (&ipb); - return (item->state); - } - - /* If background access is used, save the pixblock address. - * This address is set to NULL at the end of this block */ - if (item->background_new) { - item->background_pb = &ipb; - } - - ipb.visible_area = pb->visible_area; - if (item->filter && filter) { - item->filter->area_enlarge (ipb.visible_area, item); - } - - unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, &ipb, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - nr_pixblock_release (&ipb); - if (dpb != pb) - nr_pixblock_release (dpb); - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - ipb.empty = FALSE; - - /* Run filtering, if a filter is set for this object */ - if (item->filter && filter) { - item->filter->render (item, &ipb); - } - - if (item->clip || item->mask) { - /* Setup mask pixblock */ - NRPixBlock mpb; - nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, - carea.y0, carea.x1, carea.y1, TRUE); - - if (mpb.data.px != NULL) { // if memory allocation was successful - - mpb.visible_area = pb->visible_area; - /* Do clip if needed */ - if (item->clip) { - state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - nr_pixblock_release (&mpb); - nr_pixblock_release (&ipb); - if (dpb != pb) - nr_pixblock_release (dpb); - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - mpb.empty = FALSE; - } - /* Do mask if needed */ - if (item->mask) { - NRPixBlock tpb; - /* Set up yet another temporary pixblock */ - nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, - carea.x0, carea.y0, carea.x1, - carea.y1, TRUE); - - if (tpb.data.px != NULL) { // if memory allocation was successful - - tpb.visible_area = pb->visible_area; - unsigned int state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, &tpb, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - nr_pixblock_release (&tpb); - nr_pixblock_release (&mpb); - nr_pixblock_release (&ipb); - if (dpb != pb) - nr_pixblock_release (dpb); - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - /* Composite with clip */ - if (item->clip) { - int x, y; - for (y = carea.y0; y < carea.y1; y++) { - unsigned char *s, *d; - s = NR_PIXBLOCK_PX (&tpb) + (y - - carea.y0) * tpb.rs; - d = NR_PIXBLOCK_PX (&mpb) + (y - - carea.y0) * mpb.rs; - for (x = carea.x0; x < carea.x1; x++) { - // See below for calculation - unsigned int m; - m = NR_PREMUL_112 ( (NR_PREMUL_112 (s[0], 54) + - NR_PREMUL_112 (s[1], 183) + - NR_PREMUL_112 (s[2], 19) ) >> 8, s[3]); - d[0] = FAST_DIV_ROUND < 255 * 255 > ( NR_PREMUL_123 (d[0], m)); - s += 4; - d += 1; - } - } - } else { - int x, y; - for (y = carea.y0; y < carea.y1; y++) { - unsigned char *s, *d; - s = NR_PIXBLOCK_PX (&tpb) + (y - - carea.y0) * tpb.rs; - d = NR_PIXBLOCK_PX (&mpb) + (y - - carea.y0) * mpb.rs; - for (x = carea.x0; x < carea.x1; x++) { - unsigned int m; - // m = NR_PREMUL_112 (s[0] + s[1] + s[2], s[3]); - // d[0] = FAST_DIV_ROUND < 3 * 255 > (m); - // Must use luminance to alpha from feColorMatrix. - // This is an approximation, optimized(?) for speed. - // 0.2125 * 256 = 54.4000 - // 0.7154 * 256 = 183.1424 - // 0.0721 * 256 = 18.4576 - m = NR_PREMUL_112 ( (NR_PREMUL_112 (s[0], 54) + - NR_PREMUL_112 (s[1], 183) + - NR_PREMUL_112 (s[2], 19) ) >> 8, s[3]); - d[0] = FAST_DIV_ROUND < 255 > (m); - s += 4; - d += 1; - } - } - mpb.empty = FALSE; - } - } - nr_pixblock_release (&tpb); - } - /* Multiply with opacity if needed */ - if ((item->opacity != 255) && !item->render_opacity - ) { - int x, y; - unsigned int a; - a = item->opacity; - for (y = carea.y0; y < carea.y1; y++) { - unsigned char *d; - d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs; - for (x = carea.x0; x < carea.x1; x++) { - d[0] = NR_PREMUL_111 (d[0], a); - d += 1; - } - } - } - /* Compose rendering pixblock int destination */ - nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb); - } - nr_pixblock_release (&mpb); - } else { - if (item->render_opacity) { // opacity was already rendered in, just copy to dpb here - nr_blit_pixblock_pixblock(dpb, &ipb); - } else { // copy while multiplying by opacity - nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity); - } - } - nr_pixblock_release (&ipb); - dpb->empty = FALSE; - /* This pointer wouldn't be valid outside this block, so clear it */ - item->background_pb = NULL; - } else { - /* Just render */ - unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, dpb, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - if (dpb != pb) - nr_pixblock_release (dpb); - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - dpb->empty = FALSE; - } - - if (dpb != pb) { - /* Have to blit from cache */ - nr_blit_pixblock_pixblock (pb, dpb); - nr_pixblock_release (dpb); - pb->empty = FALSE; - item->state |= NR_ARENA_ITEM_STATE_IMAGE; - } - - return item->state | NR_ARENA_ITEM_STATE_RENDER; -} - -unsigned int -nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), - NR_ARENA_ITEM_STATE_INVALID); - /* we originally short-circuited if the object state included - * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console); - * anyone know why we stopped doing so? - */ - nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= - (area->x1 - area->x0), - NR_ARENA_ITEM_STATE_INVALID); - nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= - (area->y1 - area->y0), - NR_ARENA_ITEM_STATE_INVALID); - -#ifdef NR_ARENA_ITEM_VERBOSE - printf ("Invoke clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n", - item, area->x0, area->y0, area->x1, area->y1, (&item->bbox)->x0, - (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->y1); -#endif - - if (item->visible && nr_rect_l_test_intersect_ptr(area, &item->bbox)) { - /* Need render that item */ - if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) { - return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))-> - clip (item, area, pb); - } - } - - return item->state; -} - -NRArenaItem * -nr_arena_item_invoke_pick (NRArenaItem *item, Geom::Point p, double delta, - unsigned int sticky) -{ - nr_return_val_if_fail (item != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL); - - // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning - if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) - || !(item->state & NR_ARENA_ITEM_STATE_PICK)) - return NULL; - - if (!sticky && !(item->visible && item->sensitive)) - return NULL; - - // TODO: rewrite using Geom::Rect - const double x = p[Geom::X]; - const double y = p[Geom::Y]; - - if (((x + delta) >= item->bbox.x0) && - ((x - delta) < item->bbox.x1) && - ((y + delta) >= item->bbox.y0) && ((y - delta) < item->bbox.y1)) { - if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick) - return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))-> - pick (item, p, delta, sticky); - } - - return NULL; -} - -void -nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, - unsigned int propagate) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID)); - - if (propagate && !item->propagate) - item->propagate = TRUE; - - if (item->state & reset) { - item->state &= ~reset; - if (item->parent) { - nr_arena_item_request_update (item->parent, reset, FALSE); - } else { - nr_arena_request_update (item->arena, item); - } - } -} - -void -nr_arena_item_request_render (NRArenaItem *item) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - nr_arena_request_render_rect (item->arena, &item->drawbox); -} - -/* Public */ - -NRArenaItem * -nr_arena_item_unparent (NRArenaItem *item) -{ - nr_return_val_if_fail (item != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL); - - nr_arena_item_request_render (item); - - if (item->parent) { - nr_arena_item_remove_child (item->parent, item); - } - - return NULL; -} - -void -nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child) -{ - nr_return_if_fail (parent != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (parent)); - nr_return_if_fail (child != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (child)); - nr_return_if_fail (parent->arena == child->arena); - nr_return_if_fail (child->parent == NULL); - nr_return_if_fail (child->prev == NULL); - nr_return_if_fail (child->next == NULL); - - nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent)); -} - -void -nr_arena_item_set_transform (NRArenaItem *item, Geom::Affine const &transform) -{ - Geom::Affine const t (transform); - nr_arena_item_set_transform (item, &t); -} - -void -nr_arena_item_set_transform (NRArenaItem *item, Geom::Affine const *transform) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - if (!transform && !item->transform) - return; - - const Geom::Affine *md = (item->transform) ? item->transform : &GEOM_MATRIX_IDENTITY; - const Geom::Affine *ms = (transform) ? transform : &GEOM_MATRIX_IDENTITY; - - if (!Geom::matrix_equalp(*md, *ms, NR_EPSILON)) { - nr_arena_item_request_render (item); - if (!transform || transform->isIdentity()) { - /* Set to identity affine */ - item->transform = NULL; - } else { - if (!item->transform) - item->transform = new (GC::ATOMIC) Geom::Affine (); - *item->transform = *transform; - } - nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE); - } -} - -void -nr_arena_item_set_opacity (NRArenaItem *item, double opacity) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - nr_arena_item_request_render (item); - - item->opacity = (unsigned int) (opacity * 255.9999); -} - -void -nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - /* fixme: mess with pick/repick... */ - - item->sensitive = sensitive; -} - -void -nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - item->visible = visible; - - nr_arena_item_request_render (item); -} - -void -nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip)); - - if (clip != item->clip) { - nr_arena_item_request_render (item); - if (item->clip) - item->clip = nr_arena_item_detach (item, item->clip); - if (clip) - item->clip = nr_arena_item_attach (item, clip, NULL, NULL); - nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE); - } -} - -void -nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask)); - - if (mask != item->mask) { - nr_arena_item_request_render (item); - if (item->mask) - item->mask = nr_arena_item_detach (item, item->mask); - if (mask) - item->mask = nr_arena_item_attach (item, mask, NULL, NULL); - nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE); - } -} - -void -nr_arena_item_set_order (NRArenaItem *item, int order) -{ - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - - if (!item->parent) - return; - - NRArenaItem *children = nr_arena_item_children (item->parent); - - NRArenaItem *ref = NULL; - int pos = 0; - for (NRArenaItem *child = children; child != NULL; child = child->next) { - if (pos >= order) - break; - if (child != item) { - ref = child; - pos += 1; - } - } - - nr_arena_item_set_child_position (item->parent, item, ref); -} - -void -nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect &bbox) -{ - nr_return_if_fail(item != NULL); - nr_return_if_fail(NR_IS_ARENA_ITEM(item)); - - item->item_bbox = bbox; -} - -/** Returns a background image for use with filter effects. */ -NRPixBlock * -nr_arena_item_get_background (NRArenaItem const *item) -{ - if (item->background_new) { - return item->background_pb; - } else if (item->parent) { - return nr_arena_item_get_background (item->parent); - } else { - return NULL; - } -} - -/* Helpers */ - -NRArenaItem * -nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, - NRArenaItem *prev, NRArenaItem *next) -{ - nr_return_val_if_fail (parent != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL); - nr_return_val_if_fail (child != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL); - nr_return_val_if_fail (child->parent == NULL, NULL); - nr_return_val_if_fail (child->prev == NULL, NULL); - nr_return_val_if_fail (child->next == NULL, NULL); - nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL); - nr_return_val_if_fail (!prev || (prev->parent == parent), NULL); - nr_return_val_if_fail (!prev || (prev->next == next), NULL); - nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL); - nr_return_val_if_fail (!next || (next->parent == parent), NULL); - nr_return_val_if_fail (!next || (next->prev == prev), NULL); - - child->parent = parent; - child->prev = prev; - child->next = next; - - if (prev) - prev->next = child; - if (next) - next->prev = child; - - return child; -} - -NRArenaItem * -nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child) -{ - nr_return_val_if_fail (parent != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL); - nr_return_val_if_fail (child != NULL, NULL); - nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL); - nr_return_val_if_fail (child->parent == parent, NULL); - - NRArenaItem *prev = child->prev; - NRArenaItem *next = child->next; - - child->parent = NULL; - child->prev = NULL; - child->next = NULL; - - if (prev) - prev->next = next; - if (next) - next->prev = prev; - - return next; -} - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h deleted file mode 100644 index ff9965b85..000000000 --- a/src/display/nr-arena-item.h +++ /dev/null @@ -1,209 +0,0 @@ -#ifndef __NR_ARENA_ITEM_H__ -#define __NR_ARENA_ITEM_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#define NR_TYPE_ARENA_ITEM (nr_arena_item_get_type ()) -#define NR_ARENA_ITEM(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ARENA_ITEM, NRArenaItem)) -#define NR_IS_ARENA_ITEM(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA_ITEM)) - -#define NR_ARENA_ITEM_VIRTUAL(i,m) (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (i))->m) - -/* - * NRArenaItem state flags - */ - -/* - * NR_ARENA_ITEM_STATE_INVALID - * - * If set or retuned indicates, that given object is in error. - * Calling method has to return immediately, with appropriate - * error flag. - */ - -#define NR_ARENA_ITEM_STATE_INVALID (1 << 0) - - -#define NR_ARENA_ITEM_STATE_BBOX (1 << 1) -#define NR_ARENA_ITEM_STATE_COVERAGE (1 << 2) -#define NR_ARENA_ITEM_STATE_DRAFT (1 << 3) -#define NR_ARENA_ITEM_STATE_RENDER (1 << 4) -#define NR_ARENA_ITEM_STATE_CLIP (1 << 5) -#define NR_ARENA_ITEM_STATE_MASK (1 << 6) -#define NR_ARENA_ITEM_STATE_PICK (1 << 7) -#define NR_ARENA_ITEM_STATE_IMAGE (1 << 8) - -#define NR_ARENA_ITEM_STATE_NONE 0x0000 -#define NR_ARENA_ITEM_STATE_ALL 0x01fe - -#define NR_ARENA_ITEM_STATE(i,s) (NR_ARENA_ITEM (i)->state & (s)) -#define NR_ARENA_ITEM_SET_STATE(i,s) (NR_ARENA_ITEM (i)->state |= (s)) -#define NR_ARENA_ITEM_UNSET_STATE(i,s) (NR_ARENA_ITEM (i)->state &= ~(s)) - -#define NR_ARENA_ITEM_RENDER_NO_CACHE (1 << 0) - -#include <2geom/affine.h> -#include <libnr/nr-rect-l.h> -#include <libnr/nr-pixblock.h> -#include <libnr/nr-object.h> -#include "gc-soft-ptr.h" -#include "nr-arena-forward.h" -#include "display/nr-filter.h" -#include <cairo.h> - -struct NRGC { - NRGC(NRGC const *p) : parent(p) {} - NRGC const *parent; - Geom::Affine transform; -}; - -struct NRArenaItem : public NRObject { - - NRArena *arena; - Inkscape::GC::soft_ptr<NRArenaItem> parent; - NRArenaItem *next; - Inkscape::GC::soft_ptr<NRArenaItem> prev; - - /* Item state */ - unsigned int state : 16; - unsigned int propagate : 1; - unsigned int sensitive : 1; - unsigned int visible : 1; - /* Whether items renders opacity itself */ - unsigned int render_opacity : 1; - /* Opacity itself */ - unsigned int opacity : 8; - - /* Key for secondary rendering */ - unsigned int key; - - /* BBox in grid coordinates */ - NRRectL bbox; - /* Redraw area in grid coordinates = bbox filter-enlarged and clipped/masked */ - NRRectL drawbox; - /* BBox in item coordinates - this should be a bounding box as - * specified in SVG standard. Required by filters. */ - Geom::OptRect item_bbox; - /* Our affine */ - Geom::Affine *transform; - /* Clip item */ - NRArenaItem *clip; - /* Mask item */ - NRArenaItem *mask; - /* Filter to be applied after rendering this object, NULL if none */ - Inkscape::Filters::Filter *filter; - /* Rendered buffer */ - unsigned char *px; - - /* Single data member */ - void *data; - - /* Current Transformation Matrix */ - Geom::Affine ctm; - - /* These hold background buffer state for filter rendering */ - NRPixBlock *background_pb; - bool background_new; - - void init(NRArena *arena) { - this->arena = arena; - } -}; - -struct NRArenaItemClass : public NRObjectClass { - NRArenaItem * (* children) (NRArenaItem *item); - NRArenaItem * (* last_child) (NRArenaItem *item); - void (* add_child) (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); - void (* remove_child) (NRArenaItem *item, NRArenaItem *child); - void (* set_child_position) (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); - - unsigned int (* update) (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); - unsigned int (* render) (cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); - unsigned int (* clip) (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); - NRArenaItem * (* pick) (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); -}; - -#define NR_ARENA_ITEM_ARENA(ai) (((NRArenaItem *) (ai))->arena) - -NRType nr_arena_item_get_type (void); - -NRArenaItem *nr_arena_item_ref (NRArenaItem *item); -NRArenaItem *nr_arena_item_unref (NRArenaItem *item); - -NRArenaItem *nr_arena_item_children (NRArenaItem *item); -NRArenaItem *nr_arena_item_last_child (NRArenaItem *item); -void nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); -void nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child); -void nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); - -/* - * Invoke update to given state, if item is inside area - * - * area == NULL is infinite - * gc is PARENT gc for invoke, CHILD gc in corresponding virtual method - * state - requested to state (bitwise or of requested flags) - * reset - reset to state (bitwise or of flags to reset) - */ - -unsigned int nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset); - -unsigned int nr_arena_item_invoke_render(cairo_t *ct, NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags); - -unsigned int nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -NRArenaItem *nr_arena_item_invoke_pick (NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); - -void nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate); -void nr_arena_item_request_render (NRArenaItem *item); - -/* Public */ - -NRArenaItem *nr_arena_item_unparent (NRArenaItem *item); - -void nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child); - -void nr_arena_item_set_transform(NRArenaItem *item, Geom::Affine const &transform); -void nr_arena_item_set_transform(NRArenaItem *item, Geom::Affine const *transform); -void nr_arena_item_set_opacity (NRArenaItem *item, double opacity); -void nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive); -void nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible); -void nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip); -void nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask); -void nr_arena_item_set_order (NRArenaItem *item, int order); -void nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect &bbox); - -NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item); - -/* Helpers */ - -NRArenaItem *nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next); -NRArenaItem *nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child); - -#define NR_ARENA_ITEM_SET_DATA(i,v) (((NRArenaItem *) (i))->data = (v)) -#define NR_ARENA_ITEM_GET_DATA(i) (((NRArenaItem *) (i))->data) - -#define NR_ARENA_ITEM_SET_KEY(i,k) (((NRArenaItem *) (i))->key = (k)) -#define NR_ARENA_ITEM_GET_KEY(i) (((NRArenaItem *) (i))->key) - - -#endif /* !__NR_ARENA_ITEM_H__ */ - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp deleted file mode 100644 index 33a218e99..000000000 --- a/src/display/nr-arena-shape.cpp +++ /dev/null @@ -1,1519 +0,0 @@ -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <glib.h> -#include <fenv.h> -#include <typeinfo> -#include <cairo.h> - -#include <2geom/svg-path.h> -#include <2geom/svg-path-parser.h> -#include <2geom/pathvector.h> -#include <2geom/curves.h> -#include "display/sp-canvas.h" -#include "display/canvas-arena.h" -#include "display/nr-arena.h" -#include "display/nr-arena-shape.h" -#include "display/curve.h" -#include "display/grayscale.h" -#include "libnr/nr-pixops.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-convert2geom.h" -#include "livarot/Path.h" -#include "livarot/float-line.h" -#include "livarot/int-line.h" -#include "style.h" -#include "inkscape-cairo.h" -#include "helper/geom.h" -#include "helper/geom-curves.h" -#include "sp-filter.h" -#include "sp-filter-reference.h" -#include "display/nr-filter.h" -#include "preferences.h" - -#include "svg/svg.h" - -//int showRuns=0; -void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS); - -static void nr_arena_shape_class_init(NRArenaShapeClass *klass); -static void nr_arena_shape_init(NRArenaShape *shape); -static void nr_arena_shape_finalize(NRObject *object); - -static NRArenaItem *nr_arena_shape_children(NRArenaItem *item); -static void nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); -static void nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child); -static void nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref); - -static guint nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); -static unsigned int nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); -static guint nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb); -static NRArenaItem *nr_arena_shape_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); - -static NRArenaItemClass *shape_parent_class; - -NRType -nr_arena_shape_get_type(void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type(NR_TYPE_ARENA_ITEM, - "NRArenaShape", - sizeof(NRArenaShapeClass), - sizeof(NRArenaShape), - (void (*)(NRObjectClass *)) nr_arena_shape_class_init, - (void (*)(NRObject *)) nr_arena_shape_init); - } - return type; -} - -static void -nr_arena_shape_class_init(NRArenaShapeClass *klass) -{ - NRObjectClass *object_class; - NRArenaItemClass *item_class; - - object_class = (NRObjectClass *) klass; - item_class = (NRArenaItemClass *) klass; - - shape_parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent; - - object_class->finalize = nr_arena_shape_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaShape>; - - item_class->children = nr_arena_shape_children; - item_class->add_child = nr_arena_shape_add_child; - item_class->set_child_position = nr_arena_shape_set_child_position; - item_class->remove_child = nr_arena_shape_remove_child; - item_class->update = nr_arena_shape_update; - item_class->render = nr_arena_shape_render; - item_class->clip = nr_arena_shape_clip; - item_class->pick = nr_arena_shape_pick; -} - -/** - * Initializes the arena shape, setting all parameters to null, 0, false, - * or other defaults - */ -static void -nr_arena_shape_init(NRArenaShape *shape) -{ - shape->curve = NULL; - shape->style = NULL; - shape->paintbox.x0 = shape->paintbox.y0 = 0.0F; - shape->paintbox.x1 = shape->paintbox.y1 = 256.0F; - - shape->ctm.setIdentity(); - shape->fill_painter = NULL; - shape->stroke_painter = NULL; - shape->cached_fill = NULL; - shape->cached_stroke = NULL; - shape->cached_fpartialy = false; - shape->cached_spartialy = false; - shape->fill_shp = NULL; - shape->stroke_shp = NULL; - - shape->delayed_shp = false; - - shape->approx_bbox.x0 = shape->approx_bbox.y0 = 0; - shape->approx_bbox.x1 = shape->approx_bbox.y1 = 0; - shape->cached_fctm.setIdentity(); - shape->cached_sctm.setIdentity(); - - shape->markers = NULL; - - shape->last_pick = NULL; - shape->repick_after = 0; -} - -static void -nr_arena_shape_finalize(NRObject *object) -{ - NRArenaShape *shape = (NRArenaShape *) object; - - if (shape->fill_shp) delete shape->fill_shp; - if (shape->stroke_shp) delete shape->stroke_shp; - if (shape->cached_fill) delete shape->cached_fill; - if (shape->cached_stroke) delete shape->cached_stroke; - if (shape->fill_painter) sp_painter_free(shape->fill_painter); - if (shape->stroke_painter) sp_painter_free(shape->stroke_painter); - - if (shape->style) sp_style_unref(shape->style); - if (shape->curve) shape->curve->unref(); - - ((NRObjectClass *) shape_parent_class)->finalize(object); -} - -/** - * Retrieves the markers from the item - */ -static NRArenaItem * -nr_arena_shape_children(NRArenaItem *item) -{ - NRArenaShape *shape = (NRArenaShape *) item; - - return shape->markers; -} - -/** - * Attaches child to item, and if ref is not NULL, sets it and ref->next as - * the prev and next items. If ref is NULL, then it sets the item's markers - * as the next items. - */ -static void -nr_arena_shape_add_child(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) -{ - NRArenaShape *shape = (NRArenaShape *) item; - - if (!ref) { - shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers); - } else { - ref->next = nr_arena_item_attach(item, child, ref, ref->next); - } - - nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -/** - * Removes child from the shape. If there are no prev items in - * the child, it sets items' markers to the next item in the child. - */ -static void -nr_arena_shape_remove_child(NRArenaItem *item, NRArenaItem *child) -{ - NRArenaShape *shape = (NRArenaShape *) item; - - if (child->prev) { - nr_arena_item_detach(item, child); - } else { - shape->markers = nr_arena_item_detach(item, child); - } - - nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -/** - * Detaches child from item, and if there are no previous items in child, it - * sets item's markers to the child. It then attaches the child back onto the item. - * If ref is null, it sets the markers to be the next item, otherwise it uses - * the next/prev items in ref. - */ -static void -nr_arena_shape_set_child_position(NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref) -{ - NRArenaShape *shape = (NRArenaShape *) item; - - if (child->prev) { - nr_arena_item_detach(item, child); - } else { - shape->markers = nr_arena_item_detach(item, child); - } - - if (!ref) { - shape->markers = nr_arena_item_attach(item, child, NULL, shape->markers); - } else { - ref->next = nr_arena_item_attach(item, child, ref, ref->next); - } - - nr_arena_item_request_render(child); -} - -void nr_arena_shape_update_stroke(NRArenaShape *shape, NRGC* gc, NRRectL *area); -void nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape = false); -void nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::OptRect &bbox); - -/** - * Updates the arena shape 'item' and all of its children, including the markers. - */ -static guint -nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset) -{ - Geom::OptRect boundingbox; - - NRArenaShape *shape = NR_ARENA_SHAPE(item); - - unsigned int beststate = NR_ARENA_ITEM_STATE_ALL; - - unsigned int newstate; - for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - newstate = nr_arena_item_invoke_update(child, area, gc, state, reset); - beststate = beststate & newstate; - } - - if (!(state & NR_ARENA_ITEM_STATE_RENDER)) { - /* We do not have to create rendering structures */ - shape->ctm = gc->transform; - if (state & NR_ARENA_ITEM_STATE_BBOX) { - if (shape->curve) { - boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); - if (boundingbox) { - item->bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); // Floor gives the coordinate in which the point resides - item->bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - item->bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); // Ceil gives the first coordinate beyond the point - item->bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - } else { - item->bbox = NR_RECT_L_EMPTY; - } - } - if (beststate & NR_ARENA_ITEM_STATE_BBOX) { - for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox); - } - } - } - return (state | item->state); - } - - shape->delayed_shp=true; - shape->ctm = gc->transform; - boundingbox = Geom::OptRect(); - - bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - - if (shape->curve) { - boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); - - if (boundingbox && (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE || outline)) { - float width, scale; - scale = gc->transform.descrim(); - width = MAX(0.125, shape->_stroke.width * scale); - if ( fabs(shape->_stroke.width * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord - boundingbox->expandBy(width); - } - // those pesky miters, now - float miterMax=width*shape->_stroke.mitre_limit; - if ( miterMax > 0.01 ) { - // grunt mode. we should compute the various miters instead (one for each point on the curve) - boundingbox->expandBy(miterMax); - } - } - } - - /// \todo just write item->bbox = boundingbox - if (boundingbox) { - shape->approx_bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); - shape->approx_bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - shape->approx_bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - shape->approx_bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - } else { - shape->approx_bbox = NR_RECT_L_EMPTY; - } - if ( area && nr_rect_l_test_intersect_ptr(area, &shape->approx_bbox) ) shape->delayed_shp=false; - - /* Release state data */ - if (shape->fill_shp) { - delete shape->fill_shp; - shape->fill_shp = NULL; - } - if (shape->stroke_shp) { - delete shape->stroke_shp; - shape->stroke_shp = NULL; - } - if (shape->fill_painter) { - sp_painter_free(shape->fill_painter); - shape->fill_painter = NULL; - } - if (shape->stroke_painter) { - sp_painter_free(shape->stroke_painter); - shape->stroke_painter = NULL; - } - - if (!shape->curve || - !shape->style || - shape->curve->is_empty() || - (( shape->_fill.paint.type() == NRArenaShape::Paint::NONE ) && - ( shape->_stroke.paint.type() == NRArenaShape::Paint::NONE && !outline) )) - { - item->bbox = shape->approx_bbox; - return NR_ARENA_ITEM_STATE_ALL; - } - - /* Build state data */ - if ( shape->delayed_shp ) { - item->bbox=shape->approx_bbox; - } else { - nr_arena_shape_update_stroke(shape, gc, area); - nr_arena_shape_update_fill(shape, gc, area); - - boundingbox = Geom::OptRect(); - nr_arena_shape_add_bboxes(shape, boundingbox); - - /// \todo just write shape->approx_bbox = boundingbox - if (boundingbox) { - shape->approx_bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); - shape->approx_bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - shape->approx_bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - shape->approx_bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - } else { - shape->approx_bbox = NR_RECT_L_EMPTY; - } - } - - if (!boundingbox) - return NR_ARENA_ITEM_STATE_ALL; - - /// \todo just write item->bbox = boundingbox - item->bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); - item->bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - item->bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - item->bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); - - item->render_opacity = TRUE; - if ( shape->_fill.paint.type() == NRArenaShape::Paint::SERVER ) { - if (gc && gc->parent) { - shape->fill_painter = sp_paint_server_painter_new(shape->_fill.paint.server(), - gc->transform, gc->parent->transform, - &shape->paintbox); - } - item->render_opacity = FALSE; - } - if ( shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER ) { - if (gc && gc->parent) { - shape->stroke_painter = sp_paint_server_painter_new(shape->_stroke.paint.server(), - gc->transform, gc->parent->transform, - &shape->paintbox); - } - item->render_opacity = FALSE; - } - if ( (shape->_fill.paint.type() != NRArenaShape::Paint::NONE && - shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) - || (shape->markers) - ) - { - // don't merge item opacity with paint opacity if there is a stroke on the fill, or markers on stroke - item->render_opacity = FALSE; - } - - if (beststate & NR_ARENA_ITEM_STATE_BBOX) { - for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - nr_rect_l_union(&item->bbox, &item->bbox, &child->bbox); - } - } - - return NR_ARENA_ITEM_STATE_ALL; -} - -int matrix_is_isometry(Geom::Affine p) { - Geom::Affine tp; - // transposition - tp[0]=p[0]; - tp[1]=p[2]; - tp[2]=p[1]; - tp[3]=p[3]; - for (int i = 4; i < 6; i++) // shut valgrind up :) - tp[i] = p[i] = 0; - Geom::Affine isom = tp*p; // A^T * A = adjunct? - // Is the adjunct nearly an identity function? - if (isom.isTranslation(0.01)) { - // the transformation is an isometry -> no need to recompute - // the uncrossed polygon - if ( p.det() < 0 ) - return -1; - else - return 1; - } - return 0; -} - -static bool is_inner_area(NRRectL const &outer, NRRectL const &inner) { - return (outer.x0 <= inner.x0 && outer.y0 <= inner.y0 && outer.x1 >= inner.x1 && outer.y1 >= inner.y1); -} - -/* returns true if the pathvector has a region that needs fill. - * is for optimizing purposes, so should be fast and can falsely return true. - * CANNOT falsely return false. */ -static bool has_inner_area(Geom::PathVector const & pv) { - // return false for the cases where there is surely no region to be filled - if (pv.empty()) - return false; - - if ( (pv.size() == 1) && (pv.front().size() <= 1) ) { - // vector has only one path with only one segment, see if that's a non-curve segment: that would mean no internal region - if ( is_straight_curve(pv.front().front()) ) - { - return false; - } - } - - return true; //too costly to see if it has region to be filled, so return true. -} - -/** force_shape is used for clipping paths, when we need the shape for clipping even if it's not filled */ -void -nr_arena_shape_update_fill(NRArenaShape *shape, NRGC *gc, NRRectL *area, bool force_shape) -{ - if ((shape->_fill.paint.type() != NRArenaShape::Paint::NONE || force_shape) && - has_inner_area(shape->curve->get_pathvector()) ) { - - Geom::Affine cached_to_new = Geom::identity(); - int isometry = 0; - if ( shape->cached_fill ) { - if (shape->cached_fctm == gc->transform) { - isometry = 2; // identity - } else { - cached_to_new = shape->cached_fctm.inverse() * gc->transform; - isometry = matrix_is_isometry(cached_to_new); - } - if (0 != isometry && !is_inner_area(shape->cached_farea, *area)) - isometry = 0; - } - if ( isometry == 0 ) { - if ( shape->cached_fill == NULL ) shape->cached_fill=new Shape; - shape->cached_fill->Reset(); - - Path* thePath=new Path; - Shape* theShape=new Shape; - { - Geom::Affine tempMat(gc->transform); - thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); - } - - if (is_inner_area(*area, NR_ARENA_ITEM(shape)->bbox)) { - thePath->Convert(1.0); - shape->cached_fpartialy = false; - } else { - thePath->Convert(area, 1.0); - shape->cached_fpartialy = true; - } - - thePath->Fill(theShape, 0); - - if ( shape->_fill.rule == NRArenaShape::EVEN_ODD ) { - shape->cached_fill->ConvertToShape(theShape, fill_oddEven); - // alternatively, this speeds up rendering of oddeven shapes but disables AA :( - //shape->cached_fill->Copy(theShape); - } else { - shape->cached_fill->ConvertToShape(theShape, fill_nonZero); - } - shape->cached_fctm=gc->transform; - shape->cached_farea = *area; - delete theShape; - delete thePath; - if ( shape->fill_shp == NULL ) - shape->fill_shp = new Shape; - - shape->fill_shp->Copy(shape->cached_fill); - - } else if ( 2 == isometry ) { - if ( shape->fill_shp == NULL ) { - shape->fill_shp = new Shape; - shape->fill_shp->Copy(shape->cached_fill); - } - } else { - - if ( shape->fill_shp == NULL ) - shape->fill_shp = new Shape; - - shape->fill_shp->Reset(shape->cached_fill->numberOfPoints(), - shape->cached_fill->numberOfEdges()); - for (int i = 0; i < shape->cached_fill->numberOfPoints(); i++) - shape->fill_shp->AddPoint(to_2geom(shape->cached_fill->getPoint(i).x) * cached_to_new); - if ( isometry == 1 ) { - for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++) - shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).st, - shape->cached_fill->getEdge(i).en); - } else if ( isometry == -1 ) { // need to flip poly. - for (int i = 0; i < shape->cached_fill->numberOfEdges(); i++) - shape->fill_shp->AddEdge(shape->cached_fill->getEdge(i).en, - shape->cached_fill->getEdge(i).st); - } - shape->fill_shp->ForceToPolygon(); - shape->fill_shp->needPointsSorting(); - shape->fill_shp->needEdgesSorting(); - } - shape->delayed_shp |= shape->cached_fpartialy; - } -} - -void -nr_arena_shape_update_stroke(NRArenaShape *shape,NRGC* gc, NRRectL *area) -{ - SPStyle* style = shape->style; - - float const scale = gc->transform.descrim(); - - bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - - if (outline) { - // cairo does not need the livarot path for rendering - return; - } - - // after switching normal stroke rendering to cairo too, optimize this: lower tolerance, disregard dashes - // (since it will only be used for picking, not for rendering) - - if (outline || - ((shape->_stroke.paint.type() != NRArenaShape::Paint::NONE) && - ( fabs(shape->_stroke.width * scale) > 0.01 ))) { // sinon c'est 0=oon veut pas de bord - - float style_width = MAX(0.125, shape->_stroke.width * scale); - float width; - if (outline) { - width = 0.5; // 1 pixel wide, independent of zoom - } else { - width = style_width; - } - - Geom::Affine cached_to_new = Geom::identity(); - - int isometry = 0; - if ( shape->cached_stroke ) { - if (shape->cached_sctm == gc->transform) { - isometry = 2; // identity - } else { - cached_to_new = shape->cached_sctm.inverse() * gc->transform; - isometry = matrix_is_isometry(cached_to_new); - } - if (0 != isometry && !is_inner_area(shape->cached_sarea, *area)) - isometry = 0; - if (0 != isometry && width != shape->cached_width) { - // if this happens without setting style, we have just switched to outline or back - isometry = 0; - } - } - - if ( isometry == 0 ) { - if ( shape->cached_stroke == NULL ) shape->cached_stroke=new Shape; - shape->cached_stroke->Reset(); - Path* thePath = new Path; - Shape* theShape = new Shape; - { - Geom::Affine tempMat( gc->transform ); - thePath->LoadPathVector(shape->curve->get_pathvector(), tempMat, true); - } - - // add some padding to the rendering area, so clipped path does not go into a render area - NRRectL padded_area = *area; - padded_area.x0 -= (NR::ICoord)width; - padded_area.x1 += (NR::ICoord)width; - padded_area.y0 -= (NR::ICoord)width; - padded_area.y1 += (NR::ICoord)width; - if ((style->stroke_dash.n_dash && !outline) || is_inner_area(padded_area, NR_ARENA_ITEM(shape)->bbox)) { - thePath->Convert((outline) ? 4.0 : 1.0); - shape->cached_spartialy = false; - } - else { - thePath->Convert(&padded_area, (outline) ? 4.0 : 1.0); - shape->cached_spartialy = true; - } - - if (style->stroke_dash.n_dash && !outline) { - thePath->DashPolylineFromStyle(style, scale, 1.0); - } - - ButtType butt=butt_straight; - switch (shape->_stroke.cap) { - case NRArenaShape::BUTT_CAP: - butt = butt_straight; - break; - case NRArenaShape::ROUND_CAP: - butt = butt_round; - break; - case NRArenaShape::SQUARE_CAP: - butt = butt_square; - break; - } - JoinType join=join_straight; - switch (shape->_stroke.join) { - case NRArenaShape::MITRE_JOIN: - join = join_pointy; - break; - case NRArenaShape::ROUND_JOIN: - join = join_round; - break; - case NRArenaShape::BEVEL_JOIN: - join = join_straight; - break; - } - - if (outline) { - butt = butt_straight; - join = join_straight; - } - - thePath->Stroke(theShape, false, 0.5*width, join, butt, - 0.5*width*shape->_stroke.mitre_limit); - - - if (outline) { - // speeds it up, but uses evenodd for the stroke shape (which does not matter for 1-pixel wide outline) - shape->cached_stroke->Copy(theShape); - } else { - shape->cached_stroke->ConvertToShape(theShape, fill_nonZero); - } - - shape->cached_width = width; - - shape->cached_sctm=gc->transform; - shape->cached_sarea = *area; - delete thePath; - delete theShape; - if ( shape->stroke_shp == NULL ) shape->stroke_shp=new Shape; - - shape->stroke_shp->Copy(shape->cached_stroke); - - } else if ( 2 == isometry ) { - if ( shape->stroke_shp == NULL ) { - shape->stroke_shp=new Shape; - shape->stroke_shp->Copy(shape->cached_stroke); - } - } else { - if ( shape->stroke_shp == NULL ) - shape->stroke_shp=new Shape; - shape->stroke_shp->Reset(shape->cached_stroke->numberOfPoints(), shape->cached_stroke->numberOfEdges()); - for (int i = 0; i < shape->cached_stroke->numberOfPoints(); i++) - shape->stroke_shp->AddPoint(to_2geom(shape->cached_stroke->getPoint(i).x) * cached_to_new); - if ( isometry == 1 ) { - for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++) - shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).st, - shape->cached_stroke->getEdge(i).en); - } else if ( isometry == -1 ) { - for (int i = 0; i < shape->cached_stroke->numberOfEdges(); i++) - shape->stroke_shp->AddEdge(shape->cached_stroke->getEdge(i).en, - shape->cached_stroke->getEdge(i).st); - } - shape->stroke_shp->ForceToPolygon(); - shape->stroke_shp->needPointsSorting(); - shape->stroke_shp->needEdgesSorting(); - } - shape->delayed_shp |= shape->cached_spartialy; - } -} - - -void -nr_arena_shape_add_bboxes(NRArenaShape* shape, Geom::OptRect &bbox) -{ - /* TODO: are these two if's mutually exclusive? ( i.e. "shape->stroke_shp <=> !shape->fill_shp" ) - * if so, then this can be written much more compact ! */ - - if ( shape->stroke_shp ) { - Shape *larger = shape->stroke_shp; - larger->CalcBBox(); - larger->leftX = floor(larger->leftX); - larger->rightX = ceil(larger->rightX); - larger->topY = floor(larger->topY); - larger->bottomY = ceil(larger->bottomY); - Geom::Rect stroke_bbox( Geom::Interval(larger->leftX, larger->rightX), - Geom::Interval(larger->topY, larger->bottomY) ); - bbox.unionWith(stroke_bbox); - } - - if ( shape->fill_shp ) { - Shape *larger = shape->fill_shp; - larger->CalcBBox(); - larger->leftX = floor(larger->leftX); - larger->rightX = ceil(larger->rightX); - larger->topY = floor(larger->topY); - larger->bottomY = ceil(larger->bottomY); - Geom::Rect fill_bbox( Geom::Interval(larger->leftX, larger->rightX), - Geom::Interval(larger->topY, larger->bottomY) ); - bbox.unionWith(fill_bbox); - } -} - -// cairo outline rendering: -static unsigned int -cairo_arena_shape_render_outline(cairo_t *ct, NRArenaItem *item, Geom::OptRect area) -{ - NRArenaShape *shape = NR_ARENA_SHAPE(item); - - if (!ct) - return item->state; - - guint32 rgba = NR_ARENA_ITEM(shape)->arena->outlinecolor; - // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat - // by setting color channels in the "wrong" order - cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); - - cairo_set_line_width(ct, 0.5); - cairo_set_tolerance(ct, 1.25); // low quality, but good enough for outline mode - cairo_new_path(ct); - - feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, area, true, 0); - - cairo_stroke(ct); - - return item->state; -} - -// cairo stroke rendering (flat color only so far!): -// works on canvas, but wrongs the colors in nonpremul buffers: icons and png export -// (need to switch them to premul before this can be enabled) -void -cairo_arena_shape_render_stroke(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - NRArenaShape *shape = NR_ARENA_SHAPE(item); - SPStyle const *style = shape->style; - - float const scale = shape->ctm.descrim(); - - if (fabs(shape->_stroke.width * scale) < 0.01) - return; - - cairo_t *ct = nr_create_cairo_context (area, pb); - - if (!ct) - return; - - guint32 rgba; - if ( item->render_opacity ) { - rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = shape->_stroke.paint.color().toRGBA32( shape->_stroke.opacity ); - } - - // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat - // by setting color channels in the "wrong" order - cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); - - float style_width = MAX(0.125, shape->_stroke.width * scale); - cairo_set_line_width(ct, style_width); - - switch (shape->_stroke.cap) { - case NRArenaShape::BUTT_CAP: - cairo_set_line_cap(ct, CAIRO_LINE_CAP_BUTT); - break; - case NRArenaShape::ROUND_CAP: - cairo_set_line_cap(ct, CAIRO_LINE_CAP_ROUND); - break; - case NRArenaShape::SQUARE_CAP: - cairo_set_line_cap(ct, CAIRO_LINE_CAP_SQUARE); - break; - } - switch (shape->_stroke.join) { - case NRArenaShape::MITRE_JOIN: - cairo_set_line_join(ct, CAIRO_LINE_JOIN_MITER); - break; - case NRArenaShape::ROUND_JOIN: - cairo_set_line_join(ct, CAIRO_LINE_JOIN_ROUND); - break; - case NRArenaShape::BEVEL_JOIN: - cairo_set_line_join(ct, CAIRO_LINE_JOIN_BEVEL); - break; - } - - cairo_set_miter_limit (ct, style->stroke_miterlimit.value); - - if (style->stroke_dash.n_dash) { - NRVpathDash dash; - dash.offset = style->stroke_dash.offset * scale; - dash.n_dash = style->stroke_dash.n_dash; - dash.dash = g_new(double, dash.n_dash); - for (int i = 0; i < dash.n_dash; i++) { - dash.dash[i] = style->stroke_dash.dash[i] * scale; - } - cairo_set_dash (ct, dash.dash, dash.n_dash, dash.offset); - g_free(dash.dash); - } - - cairo_set_tolerance(ct, 0.1); - cairo_new_path(ct); - - feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, to_2geom(area->upgrade()), true, style_width); - - cairo_stroke(ct); - - cairo_surface_t *cst = cairo_get_target(ct); - cairo_destroy (ct); - cairo_surface_finish (cst); - cairo_surface_destroy (cst); - - pb->empty = FALSE; -} - -/** - * Renders the item. Markers are just composed into the parent buffer. - */ -static unsigned int -nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags) -{ - NRArenaShape *shape = NR_ARENA_SHAPE(item); - - if (!shape->curve) return item->state; - if (!shape->style) return item->state; - - bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - bool print_colors_preview = (NR_ARENA_ITEM(shape)->arena->colorrendermode == Inkscape::COLORRENDERMODE_PRINT_COLORS_PREVIEW); - bool grayscale = (NR_ARENA_ITEM(shape)->arena->colorrendermode == Inkscape::COLORRENDERMODE_GRAYSCALE); - - if (outline) { // cairo outline rendering - - pb->empty = FALSE; - unsigned int ret = cairo_arena_shape_render_outline (ct, item, to_2geom((&pb->area)->upgrade())); - if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret; - - } else { - - if ( shape->delayed_shp ) { - if ( nr_rect_l_test_intersect_ptr(area, &item->bbox) ) { - NRGC tempGC(NULL); - tempGC.transform=shape->ctm; - shape->delayed_shp = false; - nr_arena_shape_update_stroke(shape,&tempGC,&pb->visible_area); - nr_arena_shape_update_fill(shape,&tempGC,&pb->visible_area); -/* NRRect bbox; - bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0.0; - nr_arena_shape_add_bboxes(shape,bbox); - item->bbox.x0 = (gint32)(bbox.x0 - 1.0F); - item->bbox.y0 = (gint32)(bbox.y0 - 1.0F); - item->bbox.x1 = (gint32)(bbox.x1 + 1.0F); - item->bbox.y1 = (gint32)(bbox.y1 + 1.0F); - shape->approx_bbox=item->bbox;*/ - } else { - return item->state; - } - } - - SPStyle const *style = shape->style; - - if (shape->fill_shp) { - NRPixBlock m; - guint32 rgba; - - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort render - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - nr_pixblock_render_shape_mask_or(m,shape->fill_shp); - m.empty = FALSE; - - if (shape->_fill.paint.type() == NRArenaShape::Paint::NONE) { - // do not render fill in any way - } else if (shape->_fill.paint.type() == NRArenaShape::Paint::COLOR) { - - const SPColor* fill_color = &shape->_fill.paint.color(); - if ( item->render_opacity ) { - rgba = fill_color->toRGBA32( shape->_fill.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = fill_color->toRGBA32( shape->_fill.opacity ); - } - - if (print_colors_preview) { - nr_arena_separate_color_plates(&rgba); - } else if (grayscale) { - rgba = Grayscale::process(rgba); - } - - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - } else if (shape->_fill.paint.type() == NRArenaShape::Paint::SERVER) { - if (shape->fill_painter) { - nr_arena_render_paintserver_fill(pb, area, shape->fill_painter, shape->_fill.opacity, &m); - } - } - - nr_pixblock_release(&m); - } - - if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::COLOR) { - - // cairo_arena_shape_render_stroke(item, area, pb); - - guint32 rgba; - NRPixBlock m; - - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort render - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); - m.empty = FALSE; - - const SPColor* stroke_color = &shape->_stroke.paint.color(); - if ( item->render_opacity ) { - rgba = stroke_color->toRGBA32( shape->_stroke.opacity * - SP_SCALE24_TO_FLOAT(style->opacity.value) ); - } else { - rgba = stroke_color->toRGBA32( shape->_stroke.opacity ); - } - - if (print_colors_preview) { - nr_arena_separate_color_plates(&rgba); - } else if (grayscale) { - rgba = Grayscale::process(rgba); - } - - nr_blit_pixblock_mask_rgba32(pb, &m, rgba); - pb->empty = FALSE; - - nr_pixblock_release(&m); - - } else if (shape->stroke_shp && shape->_stroke.paint.type() == NRArenaShape::Paint::SERVER) { - - NRPixBlock m; - - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort render - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - nr_pixblock_render_shape_mask_or(m, shape->stroke_shp); - m.empty = FALSE; - - if (shape->stroke_painter) { - nr_arena_render_paintserver_fill(pb, area, shape->stroke_painter, shape->_stroke.opacity, &m); - } - - nr_pixblock_release(&m); - } - - } // non-cairo non-outline branch - - /* Render markers into parent buffer */ - for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - unsigned int ret = nr_arena_item_invoke_render(ct, child, area, pb, flags); - if (ret & NR_ARENA_ITEM_STATE_INVALID) return ret; - } - - return item->state; -} - - -// cairo clipping: this basically works except for the stride-must-be-divisible-by-4 cairo bug; -// reenable this when the bug is fixed and remove the rest of this function -// TODO -#if defined(DEADCODE) && !defined(DEADCODE) -static guint -cairo_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - NRArenaShape *shape = NR_ARENA_SHAPE(item); - if (!shape->curve) return item->state; - - cairo_t *ct = nr_create_cairo_context (area, pb); - - if (!ct) - return item->state; - - cairo_set_source_rgba(ct, 0, 0, 0, 1); - - cairo_new_path(ct); - - feed_pathvector_to_cairo (ct, shape->curve->get_pathvector(), shape->ctm, (area)->upgrade(), false, 0); - - cairo_fill(ct); - - cairo_surface_t *cst = cairo_get_target(ct); - cairo_destroy (ct); - cairo_surface_finish (cst); - cairo_surface_destroy (cst); - - pb->empty = FALSE; - - return item->state; -} -#endif //defined(DEADCODE) && !defined(DEADCODE) - - -static guint -nr_arena_shape_clip(NRArenaItem *item, NRRectL *area, NRPixBlock *pb) -{ - //return cairo_arena_shape_clip(item, area, pb); - - NRArenaShape *shape = NR_ARENA_SHAPE(item); - if (!shape->curve) return item->state; - - if ( shape->delayed_shp || shape->fill_shp == NULL) { // we need a fill shape no matter what - if ( nr_rect_l_test_intersect_ptr(area, &item->bbox) ) { - NRGC tempGC(NULL); - tempGC.transform=shape->ctm; - shape->delayed_shp = false; - nr_arena_shape_update_fill(shape, &tempGC, &pb->visible_area, true); - } else { - return item->state; - } - } - - if ( shape->fill_shp ) { - NRPixBlock m; - - /* fixme: We can OR in one step (Lauris) */ - nr_pixblock_setup_fast(&m, NR_PIXBLOCK_MODE_A8, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort - if (m.size != NR_PIXBLOCK_SIZE_TINY && m.data.px == NULL) { - nr_pixblock_release (&m); - return (item->state); - } - - m.visible_area = pb->visible_area; - nr_pixblock_render_shape_mask_or(m,shape->fill_shp); - - for (int y = area->y0; y < area->y1; y++) { - unsigned char *s, *d; - s = NR_PIXBLOCK_PX(&m) + (y - area->y0) * m.rs; - d = NR_PIXBLOCK_PX(pb) + (y - area->y0) * pb->rs; - for (int x = area->x0; x < area->x1; x++) { - *d = NR_COMPOSEA_111(*s, *d); - d ++; - s ++; - } - } - nr_pixblock_release(&m); - pb->empty = FALSE; - } - - return item->state; -} - -static NRArenaItem * -nr_arena_shape_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int /*sticky*/) -{ - NRArenaShape *shape = NR_ARENA_SHAPE(item); - - if (shape->repick_after > 0) - shape->repick_after--; - - if (shape->repick_after > 0) // we are a slow, huge path. skip this pick, returning what was returned last time - return shape->last_pick; - - if (!shape->curve) return NULL; - if (!shape->style) return NULL; - - bool outline = (NR_ARENA_ITEM(shape)->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - - if (SP_SCALE24_TO_FLOAT(shape->style->opacity.value) == 0 && !outline) - // fully transparent, no pick unless outline mode - return NULL; - - GTimeVal tstart, tfinish; - g_get_current_time (&tstart); - - double width; - if (outline) { - width = 0.5; - } else if (shape->_stroke.paint.type() != NRArenaShape::Paint::NONE && shape->_stroke.opacity > 1e-3) { - float const scale = shape->ctm.descrim(); - width = MAX(0.125, shape->_stroke.width * scale) / 2; - } else { - width = 0; - } - - double dist = NR_HUGE; - int wind = 0; - bool needfill = (shape->_fill.paint.type() != NRArenaShape::Paint::NONE - && shape->_fill.opacity > 1e-3 && !outline); - - if (item->arena->canvasarena) { - Geom::Rect viewbox = item->arena->canvasarena->item.canvas->getViewbox(); - viewbox.expandBy (width); - pathv_matrix_point_bbox_wind_distance(shape->curve->get_pathvector(), shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, &viewbox); - } else { - pathv_matrix_point_bbox_wind_distance(shape->curve->get_pathvector(), shape->ctm, p, NULL, needfill? &wind : NULL, &dist, 0.5, NULL); - } - - g_get_current_time (&tfinish); - glong this_pick = (tfinish.tv_sec - tstart.tv_sec) * 1000000 + (tfinish.tv_usec - tstart.tv_usec); - //g_print ("pick time %lu\n", this_pick); - - if (this_pick > 10000) { // slow picking, remember to skip several new picks - shape->repick_after = this_pick / 5000; - } - - // covered by fill? - if (needfill) { - if (!shape->style->fill_rule.computed) { - if (wind != 0) { - shape->last_pick = item; - return item; - } - } else { - if (wind & 0x1) { - shape->last_pick = item; - return item; - } - } - } - - // close to the edge, as defined by strokewidth and delta? - // this ignores dashing (as if the stroke is solid) and always works as if caps are round - if (needfill || width > 0) { // if either fill or stroke visible, - if ((dist - width) < delta) { - shape->last_pick = item; - return item; - } - } - - // if not picked on the shape itself, try its markers - for (NRArenaItem *child = shape->markers; child != NULL; child = child->next) { - NRArenaItem *ret = nr_arena_item_invoke_pick(child, p, delta, 0); - if (ret) { - shape->last_pick = item; - return item; - } - } - - shape->last_pick = NULL; - return NULL; -} - -/** - * - * Requests a render of the shape, then if the shape is already a curve it - * unrefs the old curve; if the new curve is valid it creates a copy of the - * curve and adds it to the shape. Finally, it requests an update of the - * arena for the shape. - */ -void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve,bool justTrans) -{ - g_return_if_fail(shape != NULL); - g_return_if_fail(NR_IS_ARENA_SHAPE(shape)); - - if ( justTrans == false ) { - // dirty cached versions - if ( shape->cached_fill ) { - delete shape->cached_fill; - shape->cached_fill=NULL; - } - if ( shape->cached_stroke ) { - delete shape->cached_stroke; - shape->cached_stroke=NULL; - } - } - - nr_arena_item_request_render(NR_ARENA_ITEM(shape)); - - if (shape->curve) { - shape->curve->unref(); - shape->curve = NULL; - } - - if (curve) { - shape->curve = curve; - curve->ref(); - } - - nr_arena_item_request_update(NR_ARENA_ITEM(shape), NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void NRArenaShape::setFill(SPPaintServer *server) { - _fill.paint.set(server); - _invalidateCachedFill(); -} - -void NRArenaShape::setFill(SPColor const &color) { - _fill.paint.set(color); - _invalidateCachedFill(); -} - -void NRArenaShape::setFillOpacity(double opacity) { - _fill.opacity = opacity; - _invalidateCachedFill(); -} - -void NRArenaShape::setFillRule(NRArenaShape::FillRule rule) { - _fill.rule = rule; - _invalidateCachedFill(); -} - -void NRArenaShape::setStroke(SPPaintServer *server) { - _stroke.paint.set(server); - _invalidateCachedStroke(); -} - -void NRArenaShape::setStroke(SPColor const &color) { - _stroke.paint.set(color); - _invalidateCachedStroke(); -} - -void NRArenaShape::setStrokeOpacity(double opacity) { - _stroke.opacity = opacity; - _invalidateCachedStroke(); -} - -void NRArenaShape::setStrokeWidth(double width) { - _stroke.width = width; - _invalidateCachedStroke(); -} - -void NRArenaShape::setMitreLimit(double limit) { - _stroke.mitre_limit = limit; - _invalidateCachedStroke(); -} - -void NRArenaShape::setLineCap(NRArenaShape::CapType cap) { - _stroke.cap = cap; - _invalidateCachedStroke(); -} - -void NRArenaShape::setLineJoin(NRArenaShape::JoinType join) { - _stroke.join = join; - _invalidateCachedStroke(); -} - -/** nr_arena_shape_set_style - * - * Unrefs any existing style and ref's to the given one, then requests an update of the arena - */ -void -nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) -{ - g_return_if_fail(shape != NULL); - g_return_if_fail(NR_IS_ARENA_SHAPE(shape)); - - if (style) sp_style_ref(style); - if (shape->style) sp_style_unref(shape->style); - shape->style = style; - - if ( style->fill.isPaintserver() ) { - shape->setFill(style->getFillPaintServer()); - } else if ( style->fill.isColor() ) { - shape->setFill(style->fill.value.color); - } else if ( style->fill.isNone() ) { - shape->setFill(NULL); - } else { - g_assert_not_reached(); - } - shape->setFillOpacity(SP_SCALE24_TO_FLOAT(style->fill_opacity.value)); - switch (style->fill_rule.computed) { - case SP_WIND_RULE_EVENODD: { - shape->setFillRule(NRArenaShape::EVEN_ODD); - break; - } - case SP_WIND_RULE_NONZERO: { - shape->setFillRule(NRArenaShape::NONZERO); - break; - } - default: { - g_assert_not_reached(); - } - } - - if ( style->stroke.isPaintserver() ) { - shape->setStroke(style->getStrokePaintServer()); - } else if ( style->stroke.isColor() ) { - shape->setStroke(style->stroke.value.color); - } else if ( style->stroke.isNone() ) { - shape->setStroke(NULL); - } else { - g_assert_not_reached(); - } - shape->setStrokeWidth(style->stroke_width.computed); - shape->setStrokeOpacity(SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)); - switch (style->stroke_linecap.computed) { - case SP_STROKE_LINECAP_ROUND: { - shape->setLineCap(NRArenaShape::ROUND_CAP); - break; - } - case SP_STROKE_LINECAP_SQUARE: { - shape->setLineCap(NRArenaShape::SQUARE_CAP); - break; - } - case SP_STROKE_LINECAP_BUTT: { - shape->setLineCap(NRArenaShape::BUTT_CAP); - break; - } - default: { - g_assert_not_reached(); - } - } - switch (style->stroke_linejoin.computed) { - case SP_STROKE_LINEJOIN_ROUND: { - shape->setLineJoin(NRArenaShape::ROUND_JOIN); - break; - } - case SP_STROKE_LINEJOIN_BEVEL: { - shape->setLineJoin(NRArenaShape::BEVEL_JOIN); - break; - } - case SP_STROKE_LINEJOIN_MITER: { - shape->setLineJoin(NRArenaShape::MITRE_JOIN); - break; - } - default: { - g_assert_not_reached(); - } - } - shape->setMitreLimit(style->stroke_miterlimit.value); - - //if shape has a filter - if (style->filter.set && style->getFilter()) { - if (!shape->filter) { - int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); - shape->filter = new Inkscape::Filters::Filter(primitives); - } - sp_filter_build_renderer(SP_FILTER(style->getFilter()), shape->filter); - } else { - //no filter set for this shape - delete shape->filter; - shape->filter = NULL; - } - - nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void -nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox) -{ - g_return_if_fail(shape != NULL); - g_return_if_fail(NR_IS_ARENA_SHAPE(shape)); - g_return_if_fail(pbox != NULL); - - if ((pbox->x0 < pbox->x1) && (pbox->y0 < pbox->y1)) { - shape->paintbox = *pbox; - } else { - /* fixme: We kill warning, although not sure what to do here (Lauris) */ - shape->paintbox.x0 = shape->paintbox.y0 = 0.0F; - shape->paintbox.x1 = shape->paintbox.y1 = 256.0F; - } - - nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -void NRArenaShape::setPaintBox(Geom::Rect const &pbox) -{ - paintbox.x0 = pbox.min()[Geom::X]; - paintbox.y0 = pbox.min()[Geom::Y]; - paintbox.x1 = pbox.max()[Geom::X]; - paintbox.y1 = pbox.max()[Geom::Y]; - - nr_arena_item_request_update(this, NR_ARENA_ITEM_STATE_ALL, FALSE); -} - -static void -shape_run_A8_OR(raster_info &dest,void */*data*/,int st,float vst,int en,float ven) -{ - if ( st >= en ) return; - if ( vst < 0 ) vst=0; - if ( vst > 1 ) vst=1; - if ( ven < 0 ) ven=0; - if ( ven > 1 ) ven=1; - float sv=vst; - float dv=ven-vst; - int len=en-st; - unsigned char* d=(unsigned char*)dest.buffer; - d+=(st-dest.startPix); - if ( fabs(dv) < 0.001 ) { - if ( vst > 0.999 ) { - /* Simple copy */ - while (len > 0) { - d[0] = 255; - d += 1; - len -= 1; - } - } else { - sv*=256; - unsigned int c0_24=(int)sv; - c0_24&=0xFF; - while (len > 0) { - /* Draw */ - d[0] = NR_COMPOSEA_111(c0_24,d[0]); - d += 1; - len -= 1; - } - } - } else { - if ( en <= st+1 ) { - sv=0.5*(vst+ven); - sv*=256; - unsigned int c0_24=(int)sv; - c0_24&=0xFF; - /* Draw */ - d[0] = NR_COMPOSEA_111(c0_24,d[0]); - } else { - dv/=len; - sv+=0.5*dv; // correction trapezoidale - sv*=16777216; - dv*=16777216; - int c0_24 = static_cast<int>(CLAMP(sv, 0, 16777216)); - int s0_24 = static_cast<int>(dv); - while (len > 0) { - unsigned int ca; - /* Draw */ - ca = c0_24 >> 16; - if ( ca > 255 ) ca=255; - d[0] = NR_COMPOSEA_111(ca,d[0]); - d += 1; - c0_24 += s0_24; - c0_24 = CLAMP(c0_24, 0, 16777216); - len -= 1; - } - } - } -} - -void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS) -{ - theS->CalcBBox(); - float l = theS->leftX, r = theS->rightX, t = theS->topY, b = theS->bottomY; - int il,ir,it,ib; - il=(int)floor(l); - ir=(int)ceil(r); - it=(int)floor(t); - ib=(int)ceil(b); - - if ( il >= m.area.x1 || ir <= m.area.x0 || it >= m.area.y1 || ib <= m.area.y0 ) return; - if ( il < m.area.x0 ) il=m.area.x0; - if ( it < m.area.y0 ) it=m.area.y0; - if ( ir > m.area.x1 ) ir=m.area.x1; - if ( ib > m.area.y1 ) ib=m.area.y1; - - /* This is the FloatLigne version. See svn (prior to Apr 2006) for versions using BitLigne or direct BitLigne. */ - int curPt; - float curY; - theS->BeginQuickRaster(curY, curPt); - - FloatLigne *theI = new FloatLigne(); - IntLigne *theIL = new IntLigne(); - - theS->DirectQuickScan(curY, curPt, (float) it, true, 1.0); - - char *mdata = (char*)m.data.px; - if ( m.size == NR_PIXBLOCK_SIZE_TINY ) mdata=(char*)m.data.p; - uint32_t *ligStart = ((uint32_t*)(mdata + ((il - m.area.x0) + m.rs * (it - m.area.y0)))); - for (int y = it; y < ib; y++) { - theI->Reset(); - theS->QuickScan(curY, curPt, ((float)(y+1)), theI, 1.0); - theI->Flatten(); - theIL->Copy(theI); - - raster_info dest; - dest.startPix=il; - dest.endPix=ir; - dest.sth=il; - dest.stv=y; - dest.buffer=ligStart; - theIL->Raster(dest, NULL, shape_run_A8_OR); - ligStart=((uint32_t*)(((char*)ligStart)+m.rs)); - } - theS->EndQuickRaster(); - delete theI; - delete theIL; -} - - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena-shape.h b/src/display/nr-arena-shape.h deleted file mode 100644 index e00993901..000000000 --- a/src/display/nr-arena-shape.h +++ /dev/null @@ -1,230 +0,0 @@ -#ifndef __NR_ARENA_SHAPE_H__ -#define __NR_ARENA_SHAPE_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#define NR_TYPE_ARENA_SHAPE (nr_arena_shape_get_type ()) -#define NR_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_CAST ((obj), NR_TYPE_ARENA_SHAPE, NRArenaShape)) -#define NR_IS_ARENA_SHAPE(obj) (NR_CHECK_INSTANCE_TYPE ((obj), NR_TYPE_ARENA_SHAPE)) - -#include "display/canvas-bpath.h" -#include "forward.h" -#include "sp-paint-server.h" -#include "nr-arena-item.h" - -#include "../color.h" - -#include "../livarot/Shape.h" - -NRType nr_arena_shape_get_type (void); - -struct NRArenaShape : public NRArenaItem { - class Paint { - public: - enum Type { - NONE, - COLOR, - SERVER - }; - - Paint() : _type(NONE), _color(0), _server(NULL) {} - Paint(Paint const &p) { _assign(p); } - virtual ~Paint() { clear(); } - - Type type() const { return _type; } - SPPaintServer *server() const { return _server; } - SPColor const &color() const { return _color; } - - Paint &operator=(Paint const &p) { - set(p); - return *this; - } - - void set(Paint const &p) { - clear(); - _assign(p); - } - void set(SPColor const &color) { - clear(); - _type = COLOR; - _color = color; - } - void set(SPPaintServer *server) { - clear(); - if (server) { - _type = SERVER; - _server = server; - sp_object_ref(_server, NULL); - } - } - void clear() { - if ( _type == SERVER ) { - sp_object_unref(_server, NULL); - _server = NULL; - } - _type = NONE; - } - - private: - Type _type; - SPColor _color; - SPPaintServer *_server; - - void _assign(Paint const &p) { - _type = p._type; - _server = p._server; - _color = p._color; - if (_server) { - sp_object_ref(_server, NULL); - } - } - }; - - enum FillRule { - EVEN_ODD, - NONZERO - }; - - enum CapType { - ROUND_CAP, - SQUARE_CAP, - BUTT_CAP - }; - - enum JoinType { - ROUND_JOIN, - BEVEL_JOIN, - MITRE_JOIN - }; - - /* Shape data */ - SPCurve *curve; - SPStyle *style; - NRRect paintbox; - /* State data */ - Geom::Affine ctm; - - SPPainter *fill_painter; - SPPainter *stroke_painter; - // the 2 cached polygons, for rasterizations uses - Shape *fill_shp; - Shape *stroke_shp; - // the stroke width of stroke_shp, to detect when it changes (on normal/outline switching) and rebuild - float cached_width; - // delayed_shp=true means the *_shp polygons are not computed yet - // they'll be computed on demand in *_render(), *_pick() or *_clip() - // the goal is to not uncross polygons that are outside the viewing region - bool delayed_shp; - // approximate bounding box, for the case when the polygons have been delayed - NRRectL approx_bbox; - // cache for transformations: cached_fill and cached_stroke are - // polygons computed for the cached_fctm and cache_sctm respectively - // when the transformation changes interactively (tracked by the - // SP_OBJECT_USER_MODIFIED_FLAG_B), we check if it's an isometry wrt - // the cached ctm. if it's an isometry, just apply it to the cached - // polygon to get the *_shp polygon. Otherwise, recompute so this - // works fine for translation and rotation, but not scaling and - // skewing - Geom::Affine cached_fctm; - Geom::Affine cached_sctm; - NRRectL cached_farea; - NRRectL cached_sarea; - bool cached_fpartialy; - bool cached_spartialy; - - Shape *cached_fill; - Shape *cached_stroke; - /* Markers */ - NRArenaItem *markers; - - NRArenaItem *last_pick; - guint repick_after; - - static NRArenaShape *create(NRArena *arena) { - NRArenaShape *obj=reinterpret_cast<NRArenaShape *>(nr_object_new(NR_TYPE_ARENA_SHAPE)); - obj->init(arena); - obj->key = 0; - return obj; - } - - void setFill(SPPaintServer *server); - void setFill(SPColor const &color); - void setFillOpacity(double opacity); - void setFillRule(FillRule rule); - - void setStroke(SPPaintServer *server); - void setStroke(SPColor const &color); - void setStrokeOpacity(double opacity); - void setStrokeWidth(double width); - void setLineCap(CapType cap); - void setLineJoin(JoinType join); - void setMitreLimit(double limit); - - void setPaintBox(Geom::Rect const &pbox); - - void _invalidateCachedFill() { - if (cached_fill) { - delete cached_fill; - cached_fill = NULL; - } - } - void _invalidateCachedStroke() { - if (cached_stroke) { - delete cached_stroke; - cached_stroke = NULL; - } - } - - struct Style { - Style() : opacity(0.0) {} - Paint paint; - double opacity; - }; - struct FillStyle : public Style { - FillStyle() : rule(EVEN_ODD) {} - FillRule rule; - } _fill; - struct StrokeStyle : public Style { - StrokeStyle() - : cap(ROUND_CAP), join(ROUND_JOIN), - width(0.0), mitre_limit(0.0) - {} - - CapType cap; - JoinType join; - double width; - double mitre_limit; - } _stroke; -}; - -struct NRArenaShapeClass { - NRArenaItemClass parent_class; -}; - -void nr_arena_shape_set_path(NRArenaShape *shape, SPCurve *curve, bool justTrans); -void nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style); -void nr_arena_shape_set_paintbox(NRArenaShape *shape, NRRect const *pbox); - - -#endif /* !__NR_ARENA_SHAPE_H__ */ - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena.cpp b/src/display/nr-arena.cpp deleted file mode 100644 index 837bc0d86..000000000 --- a/src/display/nr-arena.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#define __NR_ARENA_C__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "nr-arena-item.h" -#include "nr-arena.h" -#include "nr-filter-gaussian.h" -#include "nr-filter-types.h" -#include <libnr/nr-blit.h> -#include "preferences.h" -#include "color.h" - -static void nr_arena_class_init (NRArenaClass *klass); -static void nr_arena_init (NRArena *arena); -static void nr_arena_finalize (NRObject *object); - -static NRActiveObjectClass *parent_class; - -NRType -nr_arena_get_type (void) -{ - static NRType type = 0; - if (!type) { - type = nr_object_register_type (NR_TYPE_ACTIVE_OBJECT, - "NRArena", - sizeof (NRArenaClass), - sizeof (NRArena), - (void (*) (NRObjectClass *)) nr_arena_class_init, - (void (*) (NRObject *)) nr_arena_init); - } - return type; -} - -static void -nr_arena_class_init (NRArenaClass *klass) -{ - NRObjectClass *object_class = (NRObjectClass *) klass; - - parent_class = (NRActiveObjectClass *) (((NRObjectClass *) klass)->parent); - - object_class->finalize = nr_arena_finalize; - object_class->cpp_ctor = NRObject::invoke_ctor<NRArena>; -} - -static void -nr_arena_init (NRArena *arena) -{ - arena->delta = 0; // to be set by desktop from prefs - arena->renderoffscreen = false; // use render values from preferences otherwise render exact - arena->rendermode = Inkscape::RENDERMODE_NORMAL; // default is normal render - arena->colorrendermode = Inkscape::COLORRENDERMODE_NORMAL; // default is normal color - arena->blurquality = BLUR_QUALITY_NORMAL; - arena->filterquality = Inkscape::Filters::FILTER_QUALITY_NORMAL; - arena->outlinecolor = 0xff; // black; to be set by desktop from bg color - arena->canvasarena = NULL; -} - -static void -nr_arena_finalize (NRObject *object) -{ - ((NRObjectClass *) (parent_class))->finalize (object); -} - -void -nr_arena_request_update (NRArena *arena, NRArenaItem *item) -{ - NRActiveObject *aobject = (NRActiveObject *) arena; - - nr_return_if_fail (arena != NULL); - nr_return_if_fail (NR_IS_ARENA (arena)); - nr_return_if_fail (item != NULL); - nr_return_if_fail (NR_IS_ARENA_ITEM (item)); - // setup render parameter - if (arena->renderoffscreen == false) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - arena->blurquality = prefs->getInt("/options/blurquality/value", 0); - arena->filterquality = prefs->getInt("/options/filterquality/value", 0); - } else { - arena->blurquality = BLUR_QUALITY_BEST; - arena->filterquality = Inkscape::Filters::FILTER_QUALITY_BEST; - arena->rendermode = Inkscape::RENDERMODE_NORMAL; - arena->colorrendermode = Inkscape::COLORRENDERMODE_NORMAL; - } - - if (aobject->callbacks) { - for (unsigned int i = 0; i < aobject->callbacks->length; i++) { - NRObjectListener *listener = aobject->callbacks->listeners + i; - NRArenaEventVector *avector = (NRArenaEventVector *) listener->vector; - if ((listener->size >= sizeof (NRArenaEventVector)) && avector->request_update) { - avector->request_update (arena, item, listener->data); - } - } - } -} - -void -nr_arena_request_render_rect (NRArena *arena, NRRectL *area) -{ - NRActiveObject *aobject = (NRActiveObject *) arena; - - nr_return_if_fail (arena != NULL); - nr_return_if_fail (NR_IS_ARENA (arena)); - nr_return_if_fail (area != NULL); - - // setup render parameter - if (arena->renderoffscreen == false) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - arena->blurquality = prefs->getInt("/options/blurquality/value", 0); - arena->filterquality = prefs->getInt("/options/filterquality/value", 0); - } else { - arena->blurquality = BLUR_QUALITY_BEST; - arena->filterquality = Inkscape::Filters::FILTER_QUALITY_BEST; - arena->rendermode = Inkscape::RENDERMODE_NORMAL; - arena->colorrendermode = Inkscape::COLORRENDERMODE_NORMAL; - } - if (aobject->callbacks && area && !nr_rect_l_test_empty_ptr(area)) { - for (unsigned int i = 0; i < aobject->callbacks->length; i++) { - NRObjectListener *listener = aobject->callbacks->listeners + i; - NRArenaEventVector *avector = (NRArenaEventVector *) listener->vector; - if ((listener->size >= sizeof (NRArenaEventVector)) && avector->request_render) { - avector->request_render (arena, area, listener->data); - } - } - } -} - -void -nr_arena_render_paintserver_fill (NRPixBlock *pb, NRRectL *area, SPPainter *painter, float opacity, NRPixBlock *mask) -{ - NRPixBlock cb, cb_opa; - nr_pixblock_setup_fast (&cb, NR_PIXBLOCK_MODE_R8G8B8A8N, area->x0, area->y0, area->x1, area->y1, TRUE); - nr_pixblock_setup_fast (&cb_opa, NR_PIXBLOCK_MODE_R8G8B8A8N, area->x0, area->y0, area->x1, area->y1, TRUE); - - // if memory allocation failed, abort - if ((cb.size != NR_PIXBLOCK_SIZE_TINY && cb.data.px == NULL) || (cb_opa.size != NR_PIXBLOCK_SIZE_TINY && cb_opa.data.px == NULL)) { - return; - } - - cb.visible_area = pb->visible_area; - cb_opa.visible_area = pb->visible_area; - - /* Need separate gradient buffer (lauris)*/ - // do the filling - painter->fill (painter, &cb); - cb.empty = FALSE; - - // do the fill-opacity and mask composite - if (opacity < 1.0) { - nr_blit_pixblock_pixblock_alpha (&cb_opa, &cb, (int) floor (255 * opacity)); - cb_opa.empty = FALSE; - nr_blit_pixblock_pixblock_mask (pb, &cb_opa, mask); - } else { - nr_blit_pixblock_pixblock_mask (pb, &cb, mask); - } - - pb->empty = FALSE; - - nr_pixblock_release (&cb); - nr_pixblock_release (&cb_opa); -} - -/** - set arena to offscreen mode - rendering will be exact - @param arena NRArena object -*/ -void -nr_arena_set_renderoffscreen (NRArena *arena) -{ - nr_return_if_fail (arena != NULL); - nr_return_if_fail (NR_IS_ARENA (arena)); - - // the real assignment to the quality indicators is in the update function - arena->renderoffscreen = true; - -} - -#define FLOAT_TO_UINT8(f) (int(f*255)) -#define RGBA_R(v) ((v) >> 24) -#define RGBA_G(v) (((v) >> 16) & 0xff) -#define RGBA_B(v) (((v) >> 8) & 0xff) -#define RGBA_A(v) ((v) & 0xff) - -void nr_arena_separate_color_plates(guint32* rgba){ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool render_cyan = prefs->getBool("/options/printcolorspreview/cyan", true); - bool render_magenta = prefs->getBool("/options/printcolorspreview/magenta", true); - bool render_yellow = prefs->getBool("/options/printcolorspreview/yellow", true); - bool render_black = prefs->getBool("/options/printcolorspreview/black", true); - - float rgb_v[3]; - float cmyk_v[4]; - sp_color_rgb_to_cmyk_floatv (cmyk_v, RGBA_R(*rgba)/256.0, RGBA_G(*rgba)/256.0, RGBA_B(*rgba)/256.0); - sp_color_cmyk_to_rgb_floatv (rgb_v, render_cyan ? cmyk_v[0] : 0, - render_magenta ? cmyk_v[1] : 0, - render_yellow ? cmyk_v[2] : 0, - render_black ? cmyk_v[3] : 0); - *rgba = (FLOAT_TO_UINT8(rgb_v[0])<<24) + (FLOAT_TO_UINT8(rgb_v[1])<<16) + (FLOAT_TO_UINT8(rgb_v[2])<<8) + 0xff; -} - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-arena.h b/src/display/nr-arena.h deleted file mode 100644 index bd6c3029d..000000000 --- a/src/display/nr-arena.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef __NR_ARENA_H__ -#define __NR_ARENA_H__ - -/* - * RGBA display list system for inkscape - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * Copyright (C) 2001 Ximian, Inc. - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <glib/gmacros.h> - -#include "display/rendermode.h" - -G_BEGIN_DECLS - -typedef struct _SPCanvasArena SPCanvasArena; - -G_END_DECLS - -#define NR_TYPE_ARENA (nr_arena_get_type ()) -#define NR_ARENA(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ARENA, NRArena)) -#define NR_IS_ARENA(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA)) - -#include <libnr/nr-forward.h> -#include <libnr/nr-object.h> -#include "nr-arena-forward.h" -#include "sp-paint-server.h" - -NRType nr_arena_get_type (void); - -struct NRArenaEventVector { - NRObjectEventVector parent; - void (* request_update) (NRArena *arena, NRArenaItem *item, void *data); - void (* request_render) (NRArena *arena, NRRectL *area, void *data); -}; - -struct NRArena : public NRActiveObject { - static NRArena *create() { - return reinterpret_cast<NRArena *>(nr_object_new(NR_TYPE_ARENA)); - } - - double delta; - bool renderoffscreen; // if true then rendering must be exact - Inkscape::RenderMode rendermode; - Inkscape::ColorRenderMode colorrendermode; - int blurquality; // will be updated during update from preferences - int filterquality; // will be updated during update from preferences - - guint32 outlinecolor; - SPCanvasArena *canvasarena; // may be NULL is this arena is not the screen but used for export etc. -}; - -struct NRArenaClass : public NRActiveObjectClass { -}; - -void nr_arena_request_update (NRArena *arena, NRArenaItem *item); -void nr_arena_request_render_rect (NRArena *arena, NRRectL *area); -void nr_arena_set_renderoffscreen (NRArena *arena); - -void nr_arena_render_paintserver_fill (NRPixBlock *pb, NRRectL *area, SPPainter *painter, float opacity, NRPixBlock *mask); - -void nr_arena_separate_color_plates(guint32* rgba); - -#endif diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp index a0f074dc9..267883b4b 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -15,15 +15,17 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter-blend.h" -#include "display/nr-filter-pixops.h" #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-types.h" -#include "display/nr-filter-units.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixops.h" +#include "preferences.h" namespace Inkscape { namespace Filters { @@ -42,77 +44,6 @@ namespace Filters { * cb = Color (RGB) at a given pixel for image B - premultiplied */ -/* - * These blending equations given in SVG standard are for color values - * in the range 0..1. As these values are stored as unsigned char values, - * they need some reworking. An unsigned char value can be thought as - * 0.8 fixed point representation of color value. This is how I've - * ended up with these equations here. - */ - -// Set alpha / opacity. This line is same for all the blending modes, -// so let's save some copy-pasting. -#define SET_ALPHA r[3] = NR_NORMALIZE_21((255 * 255) - (255 - a[3]) * (255 - b[3])) - -// cr = (1 - qa) * cb + ca -inline void -blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_COMPOSEPPP_1111(a[0],a[3],b[0]); - r[1] = NR_COMPOSEPPP_1111(a[1],a[3],b[1]); - r[2] = NR_COMPOSEPPP_1111(a[2],a[3],b[2]); - SET_ALPHA; -} - -// cr = (1-qa)*cb + (1-qb)*ca + ca*cb -inline void -blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0] + (255 - b[3]) * a[0] - + a[0] * b[0]); - r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1] + (255 - b[3]) * a[1] - + a[1] * b[1]); - r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2] + (255 - b[3]) * a[2] - + a[2] * b[2]); - SET_ALPHA; -} - -// cr = cb + ca - ca * cb -inline void -blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21((b[0] + a[0]) * 255 - a[0] * b[0]); - r[1] = NR_NORMALIZE_21((b[1] + a[1]) * 255 - a[1] * b[1]); - r[2] = NR_NORMALIZE_21((b[2] + a[2]) * 255 - a[2] * b[2]); - SET_ALPHA; -} - -// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) -inline void -blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[0],a[3],b[0]), - NR_COMPOSEPPP_1112(b[0],b[3],a[0]))); - r[1] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[1],a[3],b[1]), - NR_COMPOSEPPP_1112(b[1],b[3],a[1]))); - r[2] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[2],a[3],b[2]), - NR_COMPOSEPPP_1112(b[2],b[3],a[2]))); - SET_ALPHA; -} - -// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) -inline void -blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[0],a[3],b[0]), - NR_COMPOSEPPP_1112(b[0],b[3],a[0]))); - r[1] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[1],a[3],b[1]), - NR_COMPOSEPPP_1112(b[1],b[3],a[1]))); - r[2] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[2],a[3],b[2]), - NR_COMPOSEPPP_1112(b[2],b[3],a[2]))); - SET_ALPHA; -} - FilterBlend::FilterBlend() : _blend_mode(BLEND_NORMAL), _input2(NR_FILTER_SLOT_NOT_SET) @@ -125,81 +56,160 @@ FilterPrimitive * FilterBlend::create() { FilterBlend::~FilterBlend() {} -int FilterBlend::render(FilterSlot &slot, FilterUnits const & /*units*/) { - NRPixBlock *in1 = slot.get(_input); - NRPixBlock *in2 = slot.get(_input2); - NRPixBlock *original_in1 = in1; - NRPixBlock *original_in2 = in2; - NRPixBlock *out; - - // Bail out if either one of source images is missing - if (!in1 || !in2) { - g_warning("Missing source image for feBlend (in=%d in2=%d)", _input, _input2); - return 1; - } +// cr = (1-qa)*cb + (1-qb)*ca + ca*cb +struct BlendMultiply { + guint32 operator()(guint32 in1, guint32 in2) + { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) + + guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; + guint32 ro = (255-aa)*rb + (255-ab)*ra + ra*rb; ro = (ro + 127) / 255; + guint32 go = (255-aa)*gb + (255-ab)*ga + ga*gb; go = (go + 127) / 255; + guint32 bo = (255-aa)*bb + (255-ab)*ba + ba*bb; bo = (bo + 127) / 255; - out = new NRPixBlock; - NRRectL out_area; - nr_rect_l_union(&out_area, &in1->area, &in2->area); - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, - out_area.x0, out_area.y0, out_area.x1, out_area.y1, - true); - - // Blending modes are defined for premultiplied RGBA values, - // thus convert them to that format before blending - if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - in1 = new NRPixBlock; - nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in1->area.x0, original_in1->area.y0, - original_in1->area.x1, original_in1->area.y1, - false); - nr_blit_pixblock_pixblock(in1, original_in1); + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; } - if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - in2 = new NRPixBlock; - nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in2->area.x0, original_in2->area.y0, - original_in2->area.x1, original_in2->area.y1, - false); - nr_blit_pixblock_pixblock(in2, original_in2); +}; + +// cr = cb + ca - ca * cb +struct BlendScreen { + guint32 operator()(guint32 in1, guint32 in2) + { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) + + guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; + guint32 ro = 255*(rb + ra) - ra * rb; ro = (ro + 127) / 255; + guint32 go = 255*(gb + ga) - ga * gb; go = (go + 127) / 255; + guint32 bo = 255*(bb + ba) - ba * bb; bo = (bo + 127) / 255; + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; } +}; + +// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +struct BlendDarken { + guint32 operator()(guint32 in1, guint32 in2) + { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) - /* pixops_mix is defined in display/nr-filter-pixops.h - * It mixes the two input images with the function given as template - * and places the result in output image. - */ - switch (_blend_mode) { - case BLEND_MULTIPLY: - pixops_mix<blend_multiply>(*out, *in1, *in2); - break; - case BLEND_SCREEN: - pixops_mix<blend_screen>(*out, *in1, *in2); - break; - case BLEND_DARKEN: - pixops_mix<blend_darken>(*out, *in1, *in2); - break; - case BLEND_LIGHTEN: - pixops_mix<blend_lighten>(*out, *in1, *in2); - break; - case BLEND_NORMAL: - default: - pixops_mix<blend_normal>(*out, *in1, *in2); - break; + guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; + guint32 ro = std::min((255-aa)*rb + 255*ra, (255-ab)*ra + 255*rb); ro = (ro + 127) / 255; + guint32 go = std::min((255-aa)*gb + 255*ga, (255-ab)*ga + 255*gb); go = (go + 127) / 255; + guint32 bo = std::min((255-aa)*bb + 255*ba, (255-ab)*ba + 255*bb); bo = (bo + 127) / 255; + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; } +}; + +// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +struct BlendLighten { + guint32 operator()(guint32 in1, guint32 in2) + { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) - if (in1 != original_in1) { - nr_pixblock_release(in1); - delete in1; + guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; + guint32 ro = std::max((255-aa)*rb + 255*ra, (255-ab)*ra + 255*rb); ro = (ro + 127) / 255; + guint32 go = std::max((255-aa)*gb + 255*ga, (255-ab)*ga + 255*gb); go = (go + 127) / 255; + guint32 bo = std::max((255-aa)*bb + 255*ba, (255-ab)*ba + 255*bb); bo = (bo + 127) / 255; + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; } - if (in2 != original_in2) { - nr_pixblock_release(in2); - delete in2; +}; + +/* +struct BlendAlpha +static inline void blend_alpha(guint32 in1, guint32 in2, guint32 *out) +{ + EXTRACT_ARGB32(in1, a1, a2, a3, a4); + EXTRACT_ARGB32(in2, b1, b2, b3, b4); + + guint32 o1 = 255*255 - (255-a1)*(255-b1); o1 = (o1+127) / 255; + guint32 o2 = 255*255 - (255-a2)*(255-b2); o2 = (o2+127) / 255; + guint32 o3 = 255*255 - (255-a3)*(255-b3); o3 = (o3+127) / 255; + guint32 o4 = 255*255 - (255-a4)*(255-b4); o4 = (o4+127) / 255; + + ASSEMBLE_ARGB32(pxout, o1, o2, o3, o4); + *out = pxout; +} +*/ + +void FilterBlend::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input1 = slot.getcairo(_input); + cairo_surface_t *input2 = slot.getcairo(_input2); + + cairo_content_t ct1 = cairo_surface_get_content(input1); + cairo_content_t ct2 = cairo_surface_get_content(input2); + + // input2 is the "background" image + // out should be ARGB32 if any of the inputs is ARGB32 + cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2); + + if ((ct1 == CAIRO_CONTENT_ALPHA && ct2 == CAIRO_CONTENT_ALPHA) + || _blend_mode == BLEND_NORMAL) + { + ink_cairo_surface_blit(input2, out); + cairo_t *out_ct = cairo_create(out); + cairo_set_source_surface(out_ct, input1, 0, 0); + cairo_paint(out_ct); + cairo_destroy(out_ct); + } else { + // blend mode != normal and at least 1 surface is not pure alpha + + // TODO: convert to Cairo blending operators once we start using the 1.10 series + switch (_blend_mode) { + case BLEND_MULTIPLY: + ink_cairo_surface_blend(input1, input2, out, BlendMultiply()); + break; + case BLEND_SCREEN: + ink_cairo_surface_blend(input1, input2, out, BlendScreen()); + break; + case BLEND_DARKEN: + ink_cairo_surface_blend(input1, input2, out, BlendDarken()); + break; + case BLEND_LIGHTEN: + ink_cairo_surface_blend(input1, input2, out, BlendLighten()); + break; + case BLEND_NORMAL: + default: + // this was handled before + g_assert_not_reached(); + break; + } } - out->empty = FALSE; slot.set(_output, out); + cairo_surface_destroy(out); +} + +bool FilterBlend::can_handle_affine(Geom::Affine const &) +{ + // blend is a per-pixel primitive and is immutable under transformations + return true; +} + +double FilterBlend::complexity(Geom::Affine const &) +{ + return 1.1; +} - return 0; +bool FilterBlend::uses_background() +{ + if (_input == NR_FILTER_BACKGROUNDIMAGE || _input == NR_FILTER_BACKGROUNDALPHA || + _input2 == NR_FILTER_BACKGROUNDIMAGE || _input2 == NR_FILTER_BACKGROUNDALPHA) + { + return true; + } else { + return false; + } } void FilterBlend::set_input(int slot) { diff --git a/src/display/nr-filter-blend.h b/src/display/nr-filter-blend.h index 254653566..957d3cfc8 100644 --- a/src/display/nr-filter-blend.h +++ b/src/display/nr-filter-blend.h @@ -18,8 +18,6 @@ */ #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" namespace Inkscape { namespace Filters { @@ -39,7 +37,10 @@ public: static FilterPrimitive *create(); virtual ~FilterBlend(); - virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + virtual bool uses_background(); virtual void set_input(int slot); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-colormatrix.cpp b/src/display/nr-filter-colormatrix.cpp index 54d5dfd69..33718ed68 100644 --- a/src/display/nr-filter-colormatrix.cpp +++ b/src/display/nr-filter-colormatrix.cpp @@ -10,11 +10,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-colormatrix.h" -#include "display/nr-filter-units.h" -#include "display/nr-filter-utils.h" -#include "libnr/nr-blit.h" #include <math.h> +#include <algorithm> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" +#include "display/nr-filter-colormatrix.h" +#include "display/nr-filter-slot.h" +#include <2geom/math-utils.h> namespace Inkscape { namespace Filters { @@ -30,173 +32,165 @@ FilterPrimitive * FilterColorMatrix::create() { FilterColorMatrix::~FilterColorMatrix() {} -int FilterColorMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feColorMatrix (in=%d)", _input); - return 1; +struct ColorMatrixMatrix { + ColorMatrixMatrix(std::vector<double> const &values) { + unsigned limit = std::min(static_cast<size_t>(20), values.size()); + for (unsigned i = 0; i < limit; ++i) { + if (i % 5 == 4) { + _v[i] = round(values[i]*255*255); + } else { + _v[i] = round(values[i]*255); + } + } + for (unsigned i = limit; i < 20; ++i) { + _v[i] = 0; + } } - NRPixBlock *out = new NRPixBlock; - if ((type==COLORMATRIX_SATURATE || type==COLORMATRIX_HUEROTATE) && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N) { - // saturate and hueRotate do not touch the alpha channel and are linear (per-pixel) operations, so no premultiplied -> non-premultiplied operation is necessary - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - } else { - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b) + // we need to un-premultiply alpha values for this type of matrix + // TODO: unpremul can be ignored if there is an identity mapping on the alpha channel + if (a != 0) { + r = unpremul_alpha(r, a); + g = unpremul_alpha(g, a); + b = unpremul_alpha(b, a); + } + + gint32 ro = r*_v[0] + g*_v[1] + b*_v[2] + a*_v[3] + _v[4]; + gint32 go = r*_v[5] + g*_v[6] + b*_v[7] + a*_v[8] + _v[9]; + gint32 bo = r*_v[10] + g*_v[11] + b*_v[12] + a*_v[13] + _v[14]; + gint32 ao = r*_v[15] + g*_v[16] + b*_v[17] + a*_v[18] + _v[19]; + ro = (pxclamp(ro, 0, 255*255) + 127) / 255; + go = (pxclamp(go, 0, 255*255) + 127) / 255; + bo = (pxclamp(bo, 0, 255*255) + 127) / 255; + ao = (pxclamp(ao, 0, 255*255) + 127) / 255; + + ro = premul_alpha(ro, ao); + go = premul_alpha(go, ao); + bo = premul_alpha(bo, ao); + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; + } +private: + gint32 _v[20]; +}; + +struct ColorMatrixSaturate { + ColorMatrixSaturate(double v_in) { + // clamp parameter instead of clamping color values + double v = CLAMP(v_in, 0.0, 1.0); + _v[0] = 0.213+0.787*v; _v[1] = 0.715-0.715*v; _v[2] = 0.072-0.072*v; + _v[3] = 0.213-0.213*v; _v[4] = 0.715+0.285*v; _v[5] = 0.072-0.072*v; + _v[6] = 0.213-0.213*v; _v[7] = 0.715-0.715*v; _v[8] = 0.072+0.928*v; } - // this primitive is defined for non-premultiplied RGBA values, - // thus convert them to that format - // However, since not all operations care, the input is only transformed if necessary. - bool free_in_on_exit = false; - if (in->mode != out->mode) { - NRPixBlock *original_in = in; - in = new NRPixBlock; - nr_pixblock_setup_fast(in, out->mode, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - true); - nr_blit_pixblock_pixblock(in, original_in); - free_in_on_exit = true; + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b) + + // Note: this cannot be done in fixed point, because the loss of precision + // causes overflow for some values of v + guint32 ro = r*_v[0] + g*_v[1] + b*_v[2] + 0.5; + guint32 go = r*_v[3] + g*_v[4] + b*_v[5] + 0.5; + guint32 bo = r*_v[6] + g*_v[7] + b*_v[8] + 0.5; + + ASSEMBLE_ARGB32(pxout, a, ro, go, bo) + return pxout; + } +private: + double _v[9]; +}; + +struct ColorMatrixHueRotate { + ColorMatrixHueRotate(double v) { + double sinhue, coshue; + Geom::sincos(v * M_PI/180.0, sinhue, coshue); + + _v[0] = round((0.213 +0.787*coshue -0.213*sinhue)*255); + _v[1] = round((0.715 -0.715*coshue -0.715*sinhue)*255); + _v[2] = round((0.072 -0.072*coshue +0.928*sinhue)*255); + + _v[3] = round((0.213 -0.213*coshue +0.143*sinhue)*255); + _v[4] = round((0.715 +0.285*coshue +0.140*sinhue)*255); + _v[5] = round((0.072 -0.072*coshue -0.283*sinhue)*255); + + _v[6] = round((0.213 -0.213*coshue -0.787*sinhue)*255); + _v[7] = round((0.715 -0.715*coshue +0.715*sinhue)*255); + _v[8] = round((0.072 +0.928*coshue +0.072*sinhue)*255); + } + guint32 operator()(guint32 in) { + EXTRACT_ARGB32(in, a, r, g, b) + gint32 maxpx = a*255; + gint32 ro = r*_v[0] + g*_v[1] + b*_v[2]; + gint32 go = r*_v[3] + g*_v[4] + b*_v[5]; + gint32 bo = r*_v[6] + g*_v[7] + b*_v[8]; + ro = (pxclamp(ro, 0, maxpx) + 127) / 255; + go = (pxclamp(go, 0, maxpx) + 127) / 255; + bo = (pxclamp(bo, 0, maxpx) + 127) / 255; + + ASSEMBLE_ARGB32(pxout, a, ro, go, bo) + return pxout; + } +private: + gint32 _v[9]; +}; + +struct ColorMatrixLuminanceToAlpha { + guint32 operator()(guint32 in) { + // original computation in double: r*0.2125 + g*0.7154 + b*0.0721 + EXTRACT_ARGB32(in, a, r, g, b) + // unpremultiply color values + if (a != 0) { + r = unpremul_alpha(r, a); + g = unpremul_alpha(g, a); + b = unpremul_alpha(b, a); + } + guint32 ao = r*54 + g*182 + b*18; + return ((ao + 127) / 255) << 24; } +}; - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - unsigned char r,g,b,a; - int x,y,x0,y0,x1,y1,i; - x0=in->area.x0; - y0=in->area.y0; - x1=in->area.x1; - y1=in->area.y1; - - switch(type){ - case COLORMATRIX_MATRIX: - { - if (values.size()!=20) { - g_warning("ColorMatrix: values parameter error. Wrong size: %i.", static_cast<int>(values.size())); - return -1; - } - double a04 = 255*values[4]; - double a14 = 255*values[9]; - double a24 = 255*values[14]; - double a34 = 255*values[19]; - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - out_data[i] = CLAMP_D_TO_U8( r*values[0] + g*values[1] + b*values[2] + a*values[3] + a04 ); // CLAMP includes rounding! - out_data[i+1] = CLAMP_D_TO_U8( r*values[5] + g*values[6] + b*values[7] + a*values[8] + a14 ); - out_data[i+2] = CLAMP_D_TO_U8( r*values[10] + g*values[11] + b*values[12] + a*values[13] + a24 ); - out_data[i+3] = CLAMP_D_TO_U8( r*values[15] + g*values[16] + b*values[17] + a*values[18] + a34 ); - } - } - } - break; - case COLORMATRIX_SATURATE: - { - double v = std::max(0.0,std::min(1.0,value)); // The standard says it should be between 0 and 1, and clamping it here makes it unnecessary to clamp the color values. - double a00 = 0.213+0.787*v, a01 = 0.715-0.715*v, a02 = 0.072-0.072*v; - double a10 = 0.213-0.213*v, a11 = 0.715+0.285*v, a12 = 0.072-0.072*v; - double a20 = 0.213-0.213*v, a21 = 0.715-0.715*v, a22 = 0.072+0.928*v; - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - out_data[i] = static_cast<unsigned char>( r*a00 + g*a01 + b*a02 + .5 ); - out_data[i+1] = static_cast<unsigned char>( r*a10 + g*a11 + b*a12 + .5 ); - out_data[i+2] = static_cast<unsigned char>( r*a20 + g*a21 + b*a22 + .5 ); - out_data[i+3] = a; - } - } - } - break; - case COLORMATRIX_HUEROTATE: - { - double coshue = cos(value * M_PI/180.0); - double sinhue = sin(value * M_PI/180.0); - double a00 = 0.213 + coshue*( 0.787) + sinhue*(-0.213); - double a01 = 0.715 + coshue*(-0.715) + sinhue*(-0.715); - double a02 = 0.072 + coshue*(-0.072) + sinhue*( 0.928); - double a10 = 0.213 + coshue*(-0.213) + sinhue*( 0.143); - double a11 = 0.715 + coshue*( 0.285) + sinhue*( 0.140); - double a12 = 0.072 + coshue*(-0.072) + sinhue*(-0.283); - double a20 = 0.213 + coshue*(-0.213) + sinhue*(-0.787); - double a21 = 0.715 + coshue*(-0.715) + sinhue*( 0.715); - double a22 = 0.072 + coshue*( 0.928) + sinhue*( 0.072); - if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) { - // Although it does not change the alpha channel, it can give "out-of-bound" results, and in this case the bound is determined by the alpha channel - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - - out_data[i] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a00 + g*a01 + b*a02 + .5 ))); - out_data[i+1] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a10 + g*a11 + b*a12 + .5 ))); - out_data[i+2] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a20 + g*a21 + b*a22 + .5 ))); - out_data[i+3] = a; - } - } - } else { - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - - out_data[i] = CLAMP_D_TO_U8( r*a00 + g*a01 + b*a02); - out_data[i+1] = CLAMP_D_TO_U8( r*a10 + g*a11 + b*a12); - out_data[i+2] = CLAMP_D_TO_U8( r*a20 + g*a21 + b*a22); - out_data[i+3] = a; - } - } - } - } - break; - case COLORMATRIX_LUMINANCETOALPHA: - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - out_data[i] = 0; - out_data[i+1] = 0; - out_data[i+2] = 0; - out_data[i+3] = static_cast<unsigned char>( r*0.2125 + g*0.7154 + b*0.0721 + .5 ); - } - } - break; - case COLORMATRIX_ENDTYPE: - break; +void FilterColorMatrix::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = NULL; + if (type == COLORMATRIX_LUMINANCETOALPHA) { + out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_ALPHA); + } else { + out = ink_cairo_surface_create_identical(input); } - if (free_in_on_exit) { - nr_pixblock_release(in); - delete in; + switch (type) { + case COLORMATRIX_MATRIX: + ink_cairo_surface_filter(input, out, ColorMatrixMatrix(values)); + break; + case COLORMATRIX_SATURATE: + ink_cairo_surface_filter(input, out, ColorMatrixSaturate(value)); + break; + case COLORMATRIX_HUEROTATE: + ink_cairo_surface_filter(input, out, ColorMatrixHueRotate(value)); + break; + case COLORMATRIX_LUMINANCETOALPHA: + ink_cairo_surface_filter(input, out, ColorMatrixLuminanceToAlpha()); + break; + case COLORMATRIX_ENDTYPE: + default: + break; } - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); +} + +bool FilterColorMatrix::can_handle_affine(Geom::Affine const &) +{ + return true; } -void FilterColorMatrix::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) +double FilterColorMatrix::complexity(Geom::Affine const &) { + return 2.0; } void FilterColorMatrix::set_type(FilterColorMatrixType t){ @@ -207,7 +201,7 @@ void FilterColorMatrix::set_value(gdouble v){ value = v; } -void FilterColorMatrix::set_values(std::vector<gdouble> &v){ +void FilterColorMatrix::set_values(std::vector<gdouble> const &v){ values = v; } diff --git a/src/display/nr-filter-colormatrix.h b/src/display/nr-filter-colormatrix.h index 7a2395f5d..5f21a4210 100644 --- a/src/display/nr-filter-colormatrix.h +++ b/src/display/nr-filter-colormatrix.h @@ -12,14 +12,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <vector> +#include <2geom/forward.h> #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include<vector> namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterColorMatrixType { COLORMATRIX_MATRIX, COLORMATRIX_SATURATE, @@ -34,11 +35,13 @@ public: static FilterPrimitive *create(); virtual ~FilterColorMatrix(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + virtual void set_type(FilterColorMatrixType type); virtual void set_value(gdouble value); - virtual void set_values(std::vector<gdouble> &values); + virtual void set_values(std::vector<gdouble> const &values); private: std::vector<gdouble> values; gdouble value; diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp index 3fe8cbf4a..226a73cef 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -10,13 +10,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-component-transfer.h" -#include "display/nr-filter-units.h" -#include "display/nr-filter-utils.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixops.h" #include <math.h> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" +#include "display/nr-filter-component-transfer.h" +#include "display/nr-filter-slot.h" namespace Inkscape { namespace Filters { @@ -32,174 +30,283 @@ FilterPrimitive * FilterComponentTransfer::create() { FilterComponentTransfer::~FilterComponentTransfer() {} -int FilterComponentTransfer::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); +struct ComponentTransfer { + ComponentTransfer(guint32 color) + : _shift(color * 8) + , _mask(0xff << _shift) + {} +protected: + guint32 _shift; + guint32 _mask; +}; + +template <bool alpha> +struct ComponentTransferTable; + +template <> +struct ComponentTransferTable<false> : public ComponentTransfer { + ComponentTransferTable(guint32 color, std::vector<double> const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + component = (255 * component + alpha/2) / alpha; + guint32 k = (_v.size() - 1) * component; + guint32 dx = k % 255; k /= 255; + component = _v[k]*255 + (_v[k+1] - _v[k])*dx; + component = (component + 127) / 255; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); + } +private: + std::vector<guint32> _v; +}; + +template <> +struct ComponentTransferTable<true> { + ComponentTransferTable(std::vector<double> const &values) + : _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + guint32 k = (_v.size() - 1) * alpha; + guint32 dx = k % 255; k /= 255; + alpha = _v[k]*255 + (_v[k+1] - _v[k])*dx; + alpha = (alpha + 127) / 255; + return (in & 0x00ffffff) | (alpha << 24); + } +private: + std::vector<guint32> _v; +}; + +template <bool alpha> +struct ComponentTransferDiscrete; + +template <> +struct ComponentTransferDiscrete<false> : public ComponentTransfer { + ComponentTransferDiscrete(guint32 color, std::vector<double> const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + component = (255 * component + alpha/2) / alpha; + guint32 k = (_v.size() - 1) * component / 255; + component = _v[k]; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); + } +private: + std::vector<guint32> _v; +}; + +template <> +struct ComponentTransferDiscrete<true> { + ComponentTransferDiscrete(std::vector<double> const &values) + : _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + guint32 k = (_v.size() - 1) * alpha / 255; + alpha = _v[k]; + return (in & 0x00ffffff) | (alpha << 24); + } +private: + std::vector<guint32> _v; +}; + +template <bool alpha> +struct ComponentTransferLinear; + +template <> +struct ComponentTransferLinear<false> : public ComponentTransfer { + ComponentTransferLinear(guint32 color, double intercept, double slope) + : ComponentTransfer(color) + , _intercept(round(intercept*255*255)) + , _slope(round(slope*255)) + {} + guint32 operator()(guint32 in) { + gint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return 0; - if (!in) { - g_warning("Missing source image for feComponentTransfer (in=%d)", _input); - return 1; + // TODO: this can probably be reduced to something simpler + component = (255 * component + alpha/2) / alpha; + component = pxclamp(_slope * component + _intercept, 0, 255*255); + component = (component + 127) / 255; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); } +private: + gint32 _intercept; + gint32 _slope; +}; - int x0=in->area.x0; - int x1=in->area.x1; - int y0=in->area.y0; - int y1=in->area.y1; - - // this primitive is defined for RGBA values, - // thus convert them to that format before blending - bool free_in_on_exit = false; - if (in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - NRPixBlock *original_in = in; - in = new NRPixBlock; - nr_pixblock_setup_fast(in, NR_PIXBLOCK_MODE_R8G8B8A8N, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - false); - nr_blit_pixblock_pixblock(in, original_in); - free_in_on_exit = true; +template <> +struct ComponentTransferLinear<true> { + ComponentTransferLinear(double intercept, double slope) + : _intercept(round(intercept*255*255)) + , _slope(round(slope*255)) + {} + guint32 operator()(guint32 in) { + gint32 alpha = (in & 0xff000000) >> 24; + alpha = pxclamp(_slope * alpha + _intercept, 0, 255*255); + alpha = (alpha + 127) / 255; + return (in & 0x00ffffff) | (alpha << 24); } - bool premultiplied = in->mode == NR_PIXBLOCK_MODE_R8G8B8A8P; - - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, in->mode, x0, y0, x1, y1, true); - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - - (void)in_data; - (void)out_data; - - int size = 4 * (y1-y0) * (x1-x0); - int i; - - int color=4; - while(color-->0) { - int _vsize = tableValues[color].size(); - double _intercept = intercept[color]; - double _slope = slope[color]; - double _amplitude = amplitude[color]; - double _exponent = exponent[color]; - double _offset = offset[color]; - switch(type[color]){ - case COMPONENTTRANSFER_TYPE_IDENTITY: - for(i=color;i<size;i+=4){ - out_data[i]=in_data[i]; - } - break; - case COMPONENTTRANSFER_TYPE_TABLE: - if (_vsize<=1){ - if (_vsize==1) { - g_warning("A component transfer table has to have at least two values."); - } - for(i=color;i<size;i+=4){ - out_data[i]=in_data[i]; - } - } else { - if (!premultiplied || color==3) { - std::vector<gdouble> _tableValues(tableValues[color]); - // Scale by 255 and add .5 to avoid having to add it later for rounding purposes - // Note that this means that CLAMP_D_TO_U8 cannot be used here (as it includes rounding!) - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(255.,255*_tableValues[i])) + .5; - } - for(i=color;i<size;i+=4){ - int k = FAST_DIVIDE<255>((_vsize-1) * in_data[i]); - double dx = ((_vsize-1) * in_data[i])/255.0 - k; - out_data[i] = static_cast<unsigned char>(_tableValues[k] + dx * (_tableValues[k+1] - _tableValues[k])); - } - } else { - std::vector<gdouble> _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - int k = ((_vsize-1) * in_data[i]) / in_data[i+3-color]; - double dx = ((_vsize-1) * in_data[i]) / (double)in_data[i+3-color] - k; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * (_tableValues[k] + dx * (_tableValues[k+1] - _tableValues[k])), out_data[i+3-color]); // CLAMP includes rounding! - } - } - } - break; - case COMPONENTTRANSFER_TYPE_DISCRETE: - if (_vsize==0){ - for(i=color;i<size;i+=4){ - out_data[i] = in_data[i]; - } - } else { - if (!premultiplied || color==3) { - std::vector<unsigned char> _tableValues(_vsize); - // Convert to unsigned char - for(i=0;i<_vsize;i++) { - _tableValues[i] = CLAMP_D_TO_U8(255*tableValues[color][i]); - } - for(i=color;i<size;i+=4){ - int k = FAST_DIVIDE<255>((_vsize-1) * in_data[i]); - out_data[i] = _tableValues[k]; - } - } else { - std::vector<gdouble> _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - int k = ((_vsize-1) * in_data[i]) / in_data[i+3-color]; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * _tableValues[k], out_data[i+3-color]); - } - } - } - break; - case COMPONENTTRANSFER_TYPE_LINEAR: - if (!premultiplied || color==3) { - _intercept = 255*_intercept; - for(i=color;i<size;i+=4){ - out_data[i] = CLAMP_D_TO_U8(_slope * in_data[i] + _intercept); // CLAMP includes rounding! - } - } else { - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - double out = _slope * in_data[i] / in_data[i+3-color] + _intercept; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * out, out_data[i+3-color]); - } - } - break; - case COMPONENTTRANSFER_TYPE_GAMMA: - if (!premultiplied || color==3) { - _amplitude *= pow(255.0, -_exponent+1); // The input should be divided by 255, then exponentiated and then multiplied by 255 again, instead the amplitude is modified accordingly. - _offset = 255*_offset; - for(i=color;i<size;i+=4){ - out_data[i] = CLAMP_D_TO_U8(_amplitude * pow((double)in_data[i], _exponent) + _offset); - } - } else { - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - double out = _amplitude * pow((double)in_data[i] / in_data[i+3-color], _exponent) + _offset; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * out, out_data[i+3-color]); - } - } - break; - case COMPONENTTRANSFER_TYPE_ERROR: - //TODO: report an error here - g_warning("Component tranfer type \"error\"."); - break; - default: - g_warning("Invalid tranfer type %d.", type[color]); +private: + gint32 _intercept; + gint32 _slope; +}; + +template <bool alpha> +struct ComponentTransferGamma; + +template <> +struct ComponentTransferGamma<false> : public ComponentTransfer { + ComponentTransferGamma(guint32 color, double amplitude, double exponent, double offset) + : ComponentTransfer(color) + , _amplitude(amplitude) + , _exponent(exponent) + , _offset(offset) + {} + guint32 operator()(guint32 in) { + double component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return 0; + + double alphaf = alpha; + component /= alphaf; + component = _amplitude * pow(component, _exponent) + _offset; + guint32 cpx = pxclamp(component * alphaf, 0, 255); + return (in & ~_mask) | (cpx << _shift); + } +private: + double _amplitude; + double _exponent; + double _offset; +}; + +template <> +struct ComponentTransferGamma<true> { + ComponentTransferGamma(double amplitude, double exponent, double offset) + : _amplitude(amplitude) + , _exponent(exponent) + , _offset(offset) + {} + guint32 operator()(guint32 in) { + double alpha = (in & 0xff000000) >> 24; + alpha /= 255.0; + alpha = _amplitude * pow(alpha, _exponent) + _offset; + guint32 cpx = pxclamp(alpha * 255.0, 0, 255); + return (in & 0x00ffffff) | (cpx << 24); + } +private: + double _amplitude; + double _exponent; + double _offset; +}; + +void FilterComponentTransfer::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + //cairo_surface_t *outtemp = ink_cairo_surface_create_identical(out); + ink_cairo_surface_blit(input, out); + + // parameters: R = 0, G = 1, B = 2, A = 3 + // Cairo: R = 2, G = 1, B = 0, A = 3 + for (unsigned i = 0; i < 3; ++i) { + guint32 color = 2 - i; + switch (type[i]) { + case COMPONENTTRANSFER_TYPE_TABLE: + ink_cairo_surface_filter(out, out, + ComponentTransferTable<false>(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete<false>(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear<false>(color, intercept[i], slope[i])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma<false>(color, amplitude[i], exponent[i], offset[i])); + break; + case COMPONENTTRANSFER_TYPE_ERROR: + case COMPONENTTRANSFER_TYPE_IDENTITY: + default: + break; } + //ink_cairo_surface_blit(out, outtemp); } - if (free_in_on_exit) { - nr_pixblock_release(in); - delete in; + // fast paths for alpha channel + switch (type[3]) { + case COMPONENTTRANSFER_TYPE_TABLE: + ink_cairo_surface_filter(out, out, + ComponentTransferTable<true>(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete<true>(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear<true>(intercept[3], slope[3])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma<true>(amplitude[3], exponent[3], offset[3])); + break; + case COMPONENTTRANSFER_TYPE_ERROR: + case COMPONENTTRANSFER_TYPE_IDENTITY: + default: + break; } - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); + //cairo_surface_destroy(outtemp); +} + +bool FilterComponentTransfer::can_handle_affine(Geom::Affine const &) +{ + return true; } -void FilterComponentTransfer::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) +double FilterComponentTransfer::complexity(Geom::Affine const &) { + return 2.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-component-transfer.h b/src/display/nr-filter-component-transfer.h index 65b4d4079..558d097a8 100644 --- a/src/display/nr-filter-component-transfer.h +++ b/src/display/nr-filter-component-transfer.h @@ -12,14 +12,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" #include <vector> +#include "display/nr-filter-primitive.h" namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterComponentTransferType { COMPONENTTRANSFER_TYPE_IDENTITY, COMPONENTTRANSFER_TYPE_TABLE, @@ -35,8 +35,9 @@ public: static FilterPrimitive *create(); virtual ~FilterComponentTransfer(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); FilterComponentTransferType type[4]; std::vector<gdouble> tableValues[4]; diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp index 41175ee07..b25ecdf2c 100644 --- a/src/display/nr-filter-composite.cpp +++ b/src/display/nr-filter-composite.cpp @@ -11,78 +11,11 @@ #include <cmath> -#include "2geom/isnan.h" -#include "filters/composite.h" +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter-composite.h" -#include "display/nr-filter-pixops.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "display/nr-filter-utils.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-pixops.h" - -inline void -composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_COMPOSEPPP_1111(a[0],a[3],b[0]); - r[1] = NR_COMPOSEPPP_1111(a[1],a[3],b[1]); - r[2] = NR_COMPOSEPPP_1111(a[2],a[3],b[2]); - r[3] = NR_COMPOSEPPP_1111(a[3],a[3],b[3]); -} - -inline void -composite_in(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(a[0] * b[3]); - r[1] = NR_NORMALIZE_21(a[1] * b[3]); - r[2] = NR_NORMALIZE_21(a[2] * b[3]); - r[3] = NR_NORMALIZE_21(a[3] * b[3]); -} - -inline void -composite_out(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3])); - r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3])); - r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3])); - r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3])); -} - -inline void -composite_atop(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(a[0] * b[3] + b[0] * (255 - a[3])); - r[1] = NR_NORMALIZE_21(a[1] * b[3] + b[1] * (255 - a[3])); - r[2] = NR_NORMALIZE_21(a[2] * b[3] + b[2] * (255 - a[3])); - r[3] = b[3]; -} - -inline void -composite_xor(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]) + b[0] * (255 - a[3])); - r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]) + b[1] * (255 - a[3])); - r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]) + b[2] * (255 - a[3])); - r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]) + b[3] * (255 - a[3])); -} - -// BUGBUG / TODO -// This makes arithmetic compositing non re-entrant and non thread safe. -static int arith_k1, arith_k2, arith_k3, arith_k4; -inline void -composite_arithmetic(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - using Inkscape::Filters::clamp3; - r[0] = NR_NORMALIZE_31(clamp3(arith_k1 * a[0] * b[0] - + arith_k2 * a[0] + arith_k3 * b[0] + arith_k4)); - r[1] = NR_NORMALIZE_31(clamp3(arith_k1 * a[1] * b[1] - + arith_k2 * a[1] + arith_k3 * b[1] + arith_k4)); - r[2] = NR_NORMALIZE_31(clamp3(arith_k1 * a[2] * b[2] - + arith_k2 * a[2] + arith_k3 * b[2] + arith_k4)); - r[3] = NR_NORMALIZE_31(clamp3(arith_k1 * a[3] * b[3] - + arith_k2 * a[3] + arith_k3 * b[3] + arith_k4)); -} namespace Inkscape { namespace Filters { @@ -99,85 +32,77 @@ FilterPrimitive * FilterComposite::create() { FilterComposite::~FilterComposite() {} -int FilterComposite::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in1 = slot.get(_input); - NRPixBlock *in2 = slot.get(_input2); - NRPixBlock *original_in1 = in1; - NRPixBlock *original_in2 = in2; - NRPixBlock *out; - - // Bail out if either one of source images is missing - if (!in1 || !in2) { - g_warning("Missing source image for feComposite (in=%d in2=%d)", _input, _input2); - return 1; - } - - out = new NRPixBlock; - NRRectL out_area; - nr_rect_l_union(&out_area, &in1->area, &in2->area); - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, - out_area.x0, out_area.y0, out_area.x1, out_area.y1, - true); - - // Blending modes are defined for premultiplied RGBA values, - // thus convert them to that format before blending - if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - in1 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in1->area.x0, original_in1->area.y0, - original_in1->area.x1, original_in1->area.y1, - false); - nr_blit_pixblock_pixblock(in1, original_in1); - } - if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - in2 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in2->area.x0, original_in2->area.y0, - original_in2->area.x1, original_in2->area.y1, - false); - nr_blit_pixblock_pixblock(in2, original_in2); +struct ComposeArithmetic { + ComposeArithmetic(double k1, double k2, double k3, double k4) + : _k1(round(k1 * 255)) + , _k2(round(k2 * 255*255)) + , _k3(round(k3 * 255*255)) + , _k4(round(k4 * 255*255*255)) + {} + guint32 operator()(guint32 in1, guint32 in2) { + EXTRACT_ARGB32(in1, aa, ra, ga, ba) + EXTRACT_ARGB32(in2, ab, rb, gb, bb) + + gint32 ao = _k1*aa*ab + _k2*aa + _k3*ab + _k4; + gint32 ro = _k1*ra*rb + _k2*ra + _k3*rb + _k4; + gint32 go = _k1*ga*gb + _k2*ga + _k3*gb + _k4; + gint32 bo = _k1*ba*bb + _k2*ba + _k3*bb + _k4; + + ao = (pxclamp(ao, 0, 255*255*255) + (255*255/2)) / (255*255); + ro = (pxclamp(ro, 0, 255*255*255) + (255*255/2)) / (255*255); + go = (pxclamp(go, 0, 255*255*255) + (255*255/2)) / (255*255); + bo = (pxclamp(bo, 0, 255*255*255) + (255*255/2)) / (255*255); + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + return pxout; } +private: + gint32 _k1, _k2, _k3, _k4; +}; - /* pixops_mix is defined in display/nr-filter-pixops.h - * It mixes the two input images with the function given as template - * and places the result in output image. - */ - switch (op) { +void FilterComposite::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input1 = slot.getcairo(_input); + cairo_surface_t *input2 = slot.getcairo(_input2); + + cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2); + + if (op == COMPOSITE_ARITHMETIC) { + ink_cairo_surface_blend(input1, input2, out, ComposeArithmetic(k1, k2, k3, k4)); + } else { + ink_cairo_surface_blit(input2, out); + cairo_t *ct = cairo_create(out); + cairo_set_source_surface(ct, input1, 0, 0); + switch(op) { case COMPOSITE_IN: - pixops_mix<composite_in>(*out, *in1, *in2); + cairo_set_operator(ct, CAIRO_OPERATOR_IN); break; case COMPOSITE_OUT: - pixops_mix<composite_out>(*out, *in1, *in2); + cairo_set_operator(ct, CAIRO_OPERATOR_OUT); break; case COMPOSITE_ATOP: - pixops_mix<composite_atop>(*out, *in1, *in2); + cairo_set_operator(ct, CAIRO_OPERATOR_ATOP); break; case COMPOSITE_XOR: - pixops_mix<composite_xor>(*out, *in1, *in2); - break; - case COMPOSITE_ARITHMETIC: - arith_k1 = (int)round(k1 * 255); - arith_k2 = (int)round(k2 * 255 * 255); - arith_k3 = (int)round(k3 * 255 * 255); - arith_k4 = (int)round(k4 * 255 * 255 * 255); - pixops_mix<composite_arithmetic>(*out, *in1, *in2); + cairo_set_operator(ct, CAIRO_OPERATOR_XOR); break; - case COMPOSITE_DEFAULT: case COMPOSITE_OVER: + case COMPOSITE_DEFAULT: default: - pixops_mix<composite_over>(*out, *in1, *in2); + // OVER is the default operator break; + } + cairo_paint(ct); + cairo_destroy(ct); } - if (in1 != original_in1) { - nr_pixblock_free(in1); - } - if (in2 != original_in2) { - nr_pixblock_free(in2); - } - - out->empty = FALSE; slot.set(_output, out); + cairo_surface_destroy(out); +} - return 0; +bool FilterComposite::can_handle_affine(Geom::Affine const &) +{ + return true; } void FilterComposite::set_input(int input) { @@ -214,6 +139,11 @@ void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) this->k4 = k4; } +double FilterComposite::complexity(Geom::Affine const &) +{ + return 1.1; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-composite.h b/src/display/nr-filter-composite.h index 45114a92c..95579cc0e 100644 --- a/src/display/nr-filter-composite.h +++ b/src/display/nr-filter-composite.h @@ -26,7 +26,9 @@ public: static FilterPrimitive *create(); virtual ~FilterComposite(); - virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual void render_cairo(FilterSlot &); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); virtual void set_input(int input); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp index f5cc7175a..5469aff88 100644 --- a/src/display/nr-filter-convolve-matrix.cpp +++ b/src/display/nr-filter-convolve-matrix.cpp @@ -10,10 +10,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <vector> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter-convolve-matrix.h" +#include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" -#include <vector> namespace Inkscape { namespace Filters { @@ -28,186 +31,139 @@ FilterPrimitive * FilterConvolveMatrix::create() { FilterConvolveMatrix::~FilterConvolveMatrix() {} -template<bool PREMULTIPLIED, bool PRESERVE_ALPHA, bool X_LOWER, bool X_UPPER, bool Y_LOWER, bool Y_UPPER> -static inline void convolve2D_XY(unsigned int const x, unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) { - double result_R = 0; - double result_G = 0; - double result_B = 0; - double result_A = 0; - - unsigned int iBegin = Y_LOWER ? targetY-y : 0; // Note that to prevent signed/unsigned problems this requires that y<=targetY (which is true) - unsigned int iEnd = Y_UPPER ? height+targetY-y : orderY; // And this requires that y<=height+targetY (which is trivially true), in addition it should be true that height+targetY-y<=orderY (or equivalently y>=height+targetY-orderY, which is true) - unsigned int jBegin = X_LOWER ? targetX-x : 0; - unsigned int jEnd = X_UPPER ? width+targetX-x : orderX; - - for (unsigned int i=iBegin; i<iEnd; i++){ - for (unsigned int j=jBegin; j<jEnd; j++){ - unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) ); - unsigned int kernel_index = orderX-j-1 + orderX*(orderY-i-1); - double k = PREMULTIPLIED ? kernel[kernel_index] : in_data[index+3] * kernel[kernel_index]; - result_R += in_data[index+0] * k; - result_G += in_data[index+1] * k; - result_B += in_data[index+2] * k; - result_A += in_data[index+3] * kernel[kernel_index]; +enum PreserveAlphaMode { + PRESERVE_ALPHA, + NO_PRESERVE_ALPHA +}; + +template <PreserveAlphaMode preserve_alpha> +struct ConvolveMatrix : public SurfaceSynth { + ConvolveMatrix(cairo_surface_t *s, int targetX, int targetY, int orderX, int orderY, + double divisor, double bias, std::vector<double> const &kernel) + : SurfaceSynth(s) + , _kernel(kernel.size()) + , _targetX(targetX) + , _targetY(targetY) + , _orderX(orderX) + , _orderY(orderY) + , _bias(bias) + { + for (unsigned i = 0; i < _kernel.size(); ++i) { + _kernel[i] = kernel[i] / divisor; } - } - - unsigned int const out_index = 4*( x + width*y ); - if (PRESERVE_ALPHA) { - out_data[out_index+3] = in_data[out_index+3]; - } else if (PREMULTIPLIED) { - out_data[out_index+3] = CLAMP_D_TO_U8(result_A + 255*bias); - } else { - out_data[out_index+3] = CLAMP_D_TO_U8(result_A + bias); - } - if (PREMULTIPLIED) { - out_data[out_index+0] = CLAMP_D_TO_U8_ALPHA(result_R + out_data[out_index+3]*bias, out_data[out_index+3]); // CLAMP includes rounding! - out_data[out_index+1] = CLAMP_D_TO_U8_ALPHA(result_G + out_data[out_index+3]*bias, out_data[out_index+3]); - out_data[out_index+2] = CLAMP_D_TO_U8_ALPHA(result_B + out_data[out_index+3]*bias, out_data[out_index+3]); - } else if (out_data[out_index+3]==0) { - out_data[out_index+0] = 0; // TODO: Is there a more sensible value that can be used here? - out_data[out_index+1] = 0; - out_data[out_index+2] = 0; - } else { - out_data[out_index+0] = CLAMP_D_TO_U8(result_R / out_data[out_index+3] + bias); // CLAMP includes rounding! - out_data[out_index+1] = CLAMP_D_TO_U8(result_G / out_data[out_index+3] + bias); - out_data[out_index+2] = CLAMP_D_TO_U8(result_B / out_data[out_index+3] + bias); - } -} - -template<bool PREMULTIPLIED, bool PRESERVE_ALPHA, bool Y_LOWER, bool Y_UPPER> -static inline void convolve2D_Y(unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) { - // See convolve2D below for rationale. - - unsigned int const lowerEnd = std::min(targetX,width); - unsigned int const upperBegin = width - std::min<unsigned int>(width,orderX - 1u - targetX); - unsigned int const midXBegin = std::min(lowerEnd,upperBegin); - unsigned int const midXEnd = std::max(lowerEnd,upperBegin); - - for (unsigned int x=0; x<midXBegin; x++) { - convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,true,false,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } - if (lowerEnd==upperBegin) { - // Do nothing, empty mid section - } else if (lowerEnd<upperBegin) { - // In the middle no bounds have to be adjusted - for (unsigned int x=midXBegin; x<midXEnd; x++) { - convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,false,false,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + // the matrix is given rotated 180 degrees + // which corresponds to reverse element order + std::reverse(_kernel.begin(), _kernel.end()); + } + + guint32 operator()(int x, int y) const { + int startx = std::max(0, x - _targetX); + int starty = std::max(0, y - _targetY); + int endx = std::min(_w, startx + _orderX); + int endy = std::min(_h, starty + _orderY); + int limitx = endx - startx; + int limity = endy - starty; + double suma = 0.0, sumr = 0.0, sumg = 0.0, sumb = 0.0; + + for (int i = 0; i < limity; ++i) { + for (int j = 0; j < limitx; ++j) { + guint32 px = pixelAt(startx + j, starty + i); + double coeff = _kernel[i * _orderX + j]; + EXTRACT_ARGB32(px, a,r,g,b) + + sumr += r * coeff; + sumg += g * coeff; + sumb += b * coeff; + if (preserve_alpha == NO_PRESERVE_ALPHA) { + suma += a * coeff; + } + } } - } else { - // In the middle both bounds have to be adjusted - for (unsigned int x=midXBegin; x<midXEnd; x++) { - convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,true,true,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + if (preserve_alpha == PRESERVE_ALPHA) { + suma = alphaAt(x, y); + } else { + suma += _bias * 255; } - } - for (unsigned int x=midXEnd; x<width; x++) { - convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,false,true,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } -} -template<bool PREMULTIPLIED, bool PRESERVE_ALPHA> -static void convolve2D(unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const _bias) { - double const bias = PREMULTIPLIED ? _bias : 255*_bias; // If we're using non-premultiplied values the bias is always multiplied by 255. + guint32 ao = pxclamp(round(suma), 0, 255); + guint32 ro = pxclamp(round(sumr + ao * _bias), 0, ao); + guint32 go = pxclamp(round(sumg + ao * _bias), 0, ao); + guint32 bo = pxclamp(round(sumb + ao * _bias), 0, ao); + ASSEMBLE_ARGB32(pxout, ao,ro,go,bo); + return pxout; + } - // For the middle section it should hold that (for all i such that 0<=i<orderY): - // 0 <= y - targetY + i < height - // targetY <= y && y < height + targetY - orderY + 1 - // In other words, for y<targetY i's lower bound needs to be adjusted and for y>=height+targetY-orderY+1 i's upper bound needs to be adjusted. +private: + std::vector<double> _kernel; + int _targetX, _targetY, _orderX, _orderY; + double _bias; +}; - unsigned int const lowerEnd = std::min(targetY,height); - unsigned int const upperBegin = height - std::min<unsigned int>(height,orderY - 1u - targetY); - unsigned int const midYBegin = std::min(lowerEnd,upperBegin); - unsigned int const midYEnd = std::max(lowerEnd,upperBegin); +void FilterConvolveMatrix::render_cairo(FilterSlot &slot) +{ + static bool bias_warning = false; + static bool edge_warning = false; - for (unsigned int y=0; y<midYBegin; y++) { - convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,true,false>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } - if (lowerEnd==upperBegin) { - // Do nothing, empty mid section - } else if (lowerEnd<upperBegin) { - // In the middle no bounds have to be adjusted - for (unsigned int y=midYBegin; y<midYEnd; y++) { - convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,false,false>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } - } else { - // In the middle both bounds have to be adjusted - for (unsigned int y=midYBegin; y<midYEnd; y++) { - convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,true,true>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } - } - for (unsigned int y=midYEnd; y<height; y++) { - convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,false,true>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); - } -} + cairo_surface_t *input = slot.getcairo(_input); -int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feConvolveMatrix (in=%d)", _input); - return 1; - } if (orderX<=0 || orderY<=0) { g_warning("Empty kernel!"); - return 1; + return; } if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) { g_warning("Invalid target!"); - return 1; + return; } if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) { - g_warning("kernelMatrix does not have orderX*orderY elements!"); - return 1; - } - - if (bias!=0) { - g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!"); - // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense). - // It also says that bias should simply be added to the result for each color (without taking the alpha into account) - // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter". - // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard. + //g_warning("kernelMatrix does not have orderX*orderY elements!"); + return; + } + + cairo_surface_t *out = ink_cairo_surface_create_identical(input); + + if (bias!=0 && !bias_warning) { + g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix " + "is correct!"); + bias_warning = true; + // The SVG specification implies that feConvolveMatrix is defined for premultiplied + // colors (which makes sense). It also says that bias should simply be added to the result + // for each color (without taking the alpha into account). However, it also says that one + // purpose of bias is "to have .5 gray value be the zero response of the filter". + // It seems sensible to indeed support the latter behaviour instead of the former, + // but this does appear to go against the standard. // Note that Batik simply does not support bias!=0 } - if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE) { + if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE && !edge_warning) { g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!"); - // Note that to properly support edgeMode the interaction with area_enlarge should be well understood (and probably something needs to change) - // area_enlarge should NOT let Inkscape enlarge the area beyond the filter area, it should only enlarge the rendered area if a part of the object is rendered to make it overlapping (enough) with adjacent parts. + edge_warning = true; } - NRPixBlock *out = new NRPixBlock; - - nr_pixblock_setup_fast(out, in->mode, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); + //guint32 *in_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(input)); + //guint32 *out_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(out)); - unsigned int const width = in->area.x1 - in->area.x0; - unsigned int const height = in->area.y1 - in->area.y0; + //int width = cairo_image_surface_get_width(input); + //int height = cairo_image_surface_get_height(input); // Set up predivided kernel matrix - std::vector<double> kernel(kernelMatrix); + /*std::vector<double> kernel(kernelMatrix); for(size_t i=0; i<kernel.size(); i++) { kernel[i] /= divisor; // The code that creates this object makes sure that divisor != 0 - } + }*/ - if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) { - if (preserveAlpha) { - convolve2D<true,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); - } else { - convolve2D<true,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); - } + if (preserveAlpha) { + //convolve2D<true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, + // targetX, targetY, bias); + ink_cairo_surface_synthesize(out, ConvolveMatrix<PRESERVE_ALPHA>(input, + targetX, targetY, orderX, orderY, divisor, bias, kernelMatrix)); } else { - if (preserveAlpha) { - convolve2D<false,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); - } else { - convolve2D<false,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); - } + //convolve2D<false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, + // targetX, targetY, bias); + ink_cairo_surface_synthesize(out, ConvolveMatrix<NO_PRESERVE_ALPHA>(input, + targetX, targetY, orderX, orderY, divisor, bias, kernelMatrix)); } - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); } void FilterConvolveMatrix::set_targetX(int coord) { @@ -246,18 +202,20 @@ void FilterConvolveMatrix::set_preserveAlpha(bool pa){ preserveAlpha = pa; } -void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Affine const &/*trans*/) +void FilterConvolveMatrix::area_enlarge(Geom::IntRect &area, Geom::Affine const &/*trans*/) { //Seems to me that since this filter's operation is resolution dependent, // some spurious pixels may still appear at the borders when low zooming or rotating. Needs a better fix. - area.x0 -= targetX; - area.y0 -= targetY; - area.x1 += orderX - targetX - 1; // This makes sure the last row/column in the original image corresponds to the last row/column in the new image that can be convolved without adjusting the boundary conditions). - area.y1 += orderY - targetY - 1; + area.setMin(area.min() - Geom::IntPoint(targetX, targetY)); + // This makes sure the last row/column in the original image corresponds + // to the last row/column in the new image that can be convolved without + // adjusting the boundary conditions). + area.setMax(area.max() + Geom::IntPoint(orderX - targetX - 1, orderY - targetY -1)); } -FilterTraits FilterConvolveMatrix::get_input_traits() { - return TRAIT_PARALLER; +double FilterConvolveMatrix::complexity(Geom::Affine const &) +{ + return kernelMatrix.size(); } } /* namespace Filters */ diff --git a/src/display/nr-filter-convolve-matrix.h b/src/display/nr-filter-convolve-matrix.h index 50d97f1a0..4041ff96f 100644 --- a/src/display/nr-filter-convolve-matrix.h +++ b/src/display/nr-filter-convolve-matrix.h @@ -13,14 +13,13 @@ */ #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" #include <vector> namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterConvolveMatrixEdgeMode { CONVOLVEMATRIX_EDGEMODE_DUPLICATE, CONVOLVEMATRIX_EDGEMODE_WRAP, @@ -34,9 +33,9 @@ public: static FilterPrimitive *create(); virtual ~FilterConvolveMatrix(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); void set_targetY(int coord); void set_targetX(int coord); diff --git a/src/display/nr-filter-diffuselighting.cpp b/src/display/nr-filter-diffuselighting.cpp index 8e764f6d4..fcc986189 100644 --- a/src/display/nr-filter-diffuselighting.cpp +++ b/src/display/nr-filter-diffuselighting.cpp @@ -4,26 +4,23 @@ * Authors: * Niko Kiirala <niko@kiirala.com> * Jean-Rene Reinhard <jr@komite.net> + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> * - * Copyright (C) 2007 authors + * Copyright (C) 2007-2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib/gmessages.h> +#include <glib.h> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-3dutils.h" -#include "display/nr-arena-item.h" #include "display/nr-filter-diffuselighting.h" -#include "display/nr-filter-getalpha.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" #include "display/nr-light.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-rect-l.h" -#include "color.h" namespace Inkscape { namespace Filters { @@ -43,146 +40,136 @@ FilterPrimitive * FilterDiffuseLighting::create() { FilterDiffuseLighting::~FilterDiffuseLighting() {} -int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feDiffuseLighting (in=%d)", _input); - return 1; +struct DiffuseLight : public SurfaceSynth { + DiffuseLight(cairo_surface_t *bumpmap, double scale, double kd) + : SurfaceSynth(bumpmap) + , _scale(scale) + , _kd(kd) + {} + +protected: + guint32 diffuseLighting(int x, int y, NR::Fvector const &light, NR::Fvector const &light_components) { + NR::Fvector normal = surfaceNormalAt(x, y, _scale); + double k = _kd * NR::scalar_product(normal, light); + + guint32 r = CLAMP_D_TO_U8(k * light_components[LIGHT_RED]); + guint32 g = CLAMP_D_TO_U8(k * light_components[LIGHT_GREEN]); + guint32 b = CLAMP_D_TO_U8(k * light_components[LIGHT_BLUE]); + + ASSEMBLE_ARGB32(pxout, 255,r,g,b) + return pxout; + } + double _scale, _kd; +}; + +struct DiffuseDistantLight : public DiffuseLight { + DiffuseDistantLight(cairo_surface_t *bumpmap, SPFeDistantLight *light, guint32 color, + double scale, double diffuse_constant) + : DiffuseLight(bumpmap, scale, diffuse_constant) + { + DistantLight dl(light, color); + dl.light_vector(_lightv); + dl.light_components(_light_components); + } + + guint32 operator()(int x, int y) { + return diffuseLighting(x, y, _lightv, _light_components); + } +private: + NR::Fvector _lightv, _light_components; +}; + +struct DiffusePointLight : public DiffuseLight { + DiffusePointLight(cairo_surface_t *bumpmap, SPFePointLight *light, guint32 color, + Geom::Affine const &trans, double scale, double diffuse_constant, double x0, double y0) + : DiffuseLight(bumpmap, scale, diffuse_constant) + , _light(light, color, trans) + , _x0(x0) + , _y0(y0) + { + _light.light_components(_light_components); + } + + guint32 operator()(int x, int y) { + NR::Fvector light; + _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0); + return diffuseLighting(x, y, light, _light_components); + } +private: + PointLight _light; + NR::Fvector _light_components; + double _x0, _y0; +}; + +struct DiffuseSpotLight : public DiffuseLight { + DiffuseSpotLight(cairo_surface_t *bumpmap, SPFeSpotLight *light, guint32 color, + Geom::Affine const &trans, double scale, double diffuse_constant, double x0, double y0) + : DiffuseLight(bumpmap, scale, diffuse_constant) + , _light(light, color, trans) + , _x0(x0) + , _y0(y0) + {} + + guint32 operator()(int x, int y) { + NR::Fvector light, light_components; + _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0); + _light.light_components(light_components, light); + return diffuseLighting(x, y, light, light_components); } +private: + SpotLight _light; + double _x0, _y0; +}; + +void FilterDiffuseLighting::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + + Geom::Rect slot_area = slot.get_slot_area(); + Geom::Point p = slot_area.min(); + Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb(); + double x0 = p[Geom::X], y0 = p[Geom::Y]; + double scale = surfaceScale * trans.descrim(); - NRPixBlock *out = new NRPixBlock; - - int w = in->area.x1 - in->area.x0; - int h = in->area.y1 - in->area.y0; - int x0 = in->area.x0; - int y0 = in->area.y0; - int i, j; - //As long as FilterRes and kernel unit is not supported we hardcode the - //default value - int dx = 1; //TODO setup - int dy = 1; //TODO setup - //surface scale - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); - gdouble ss = surfaceScale * trans[0]; - gdouble kd = diffuseConstant; //diffuse lighting constant - - NR::Fvector L, N, LC; - gdouble inter; - - nr_pixblock_setup_fast(out, in->mode, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - unsigned char *data_i = NR_PIXBLOCK_PX (in); - unsigned char *data_o = NR_PIXBLOCK_PX (out); - //No light, nothing to do switch (light_type) { - case DISTANT_LIGHT: - //the light vector is constant - { - DistantLight *dl = new DistantLight(light.distant, lighting_color); - dl->light_vector(L); - dl->light_components(LC); - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - inter = kd * NR::scalar_product(N, L); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding! - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j++] = 255; - } - out->empty = FALSE; - delete dl; - } - break; - case POINT_LIGHT: - { - PointLight *pl = new PointLight(light.point, lighting_color, trans); - pl->light_components(LC); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - pl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - inter = kd * NR::scalar_product(N, L); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j++] = 255; - } - out->empty = FALSE; - delete pl; - } - break; - case SPOT_LIGHT: - { - SpotLight *sl = new SpotLight(light.spot, lighting_color, trans); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - sl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - sl->light_components(LC, L); - inter = kd * NR::scalar_product(N, L); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j++] = 255; - } - out->empty = FALSE; - delete sl; - } - break; - //else unknown light source, doing nothing - case NO_LIGHT: - default: - { - if (light_type != NO_LIGHT) - g_warning("unknown light source %d", light_type); - for (i = 0; i < w*h; i++) { - data_o[4*i+3] = 255; - } - out->empty = false; - } + case DISTANT_LIGHT: + ink_cairo_surface_synthesize(out, + DiffuseDistantLight(input, light.distant, lighting_color, scale, diffuseConstant)); + break; + case POINT_LIGHT: + ink_cairo_surface_synthesize(out, + DiffusePointLight(input, light.point, lighting_color, trans, scale, diffuseConstant, x0, y0)); + break; + case SPOT_LIGHT: + ink_cairo_surface_synthesize(out, + DiffuseSpotLight(input, light.spot, lighting_color, trans, scale, diffuseConstant, x0, y0)); + break; + default: { + cairo_t *ct = cairo_create(out); + cairo_set_source_rgba(ct, 0,0,0,1); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); + } break; } - - //finishing + slot.set(_output, out); - //nr_pixblock_release(in); - //delete in; - return 0; + cairo_surface_destroy(out); } -void FilterDiffuseLighting::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterDiffuseLighting::area_enlarge(Geom::IntRect &area, Geom::Affine const & /*trans*/) { // TODO: support kernelUnitLength - double scalex = std::fabs(trans[0]) + std::fabs(trans[1]); - double scaley = std::fabs(trans[2]) + std::fabs(trans[3]); - - //FIXME: no +2 should be there!... (noticable only for big scales at big zoom factor) - area.x0 -= (int)(scalex) + 2; - area.x1 += (int)(scalex) + 2; - area.y0 -= (int)(scaley) + 2; - area.y1 += (int)(scaley) + 2; + + // We expand the area by 1 in every direction to avoid artifacts on tile edges. + // However, it means that edge pixels will be incorrect. + area.expandBy(1); } -FilterTraits FilterDiffuseLighting::get_input_traits() { - return TRAIT_PARALLER; +double FilterDiffuseLighting::complexity(Geom::Affine const &) +{ + return 9.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-diffuselighting.h b/src/display/nr-filter-diffuselighting.h index 8dc7a1818..0da6cc218 100644 --- a/src/display/nr-filter-diffuselighting.h +++ b/src/display/nr-filter-diffuselighting.h @@ -13,21 +13,28 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gdk/gdktypes.h> +#include <gdk/gdk.h> #include "display/nr-light-types.h" #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "filters/distantlight.h" -#include "filters/pointlight.h" -#include "filters/spotlight.h" -#include "color.h" + +class SPFeDistantLight; +class SPFePointLight; +class SPFeSpotLight; namespace Inkscape { namespace Filters { class FilterDiffuseLighting : public FilterPrimitive { public: + FilterDiffuseLighting(); + static FilterPrimitive *create(); + virtual ~FilterDiffuseLighting(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + union { SPFeDistantLight *distant; SPFePointLight *point; @@ -37,13 +44,6 @@ public: gdouble diffuseConstant; gdouble surfaceScale; guint32 lighting_color; - - FilterDiffuseLighting(); - static FilterPrimitive *create(); - virtual ~FilterDiffuseLighting(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); private: }; diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp index d71a691ef..9b44c2302 100644 --- a/src/display/nr-filter-displacement-map.cpp +++ b/src/display/nr-filter-displacement-map.cpp @@ -9,11 +9,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter-displacement-map.h" #include "display/nr-filter-types.h" #include "display/nr-filter-units.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixops.h" namespace Inkscape { namespace Filters { @@ -28,190 +28,61 @@ FilterPrimitive * FilterDisplacementMap::create() { FilterDisplacementMap::~FilterDisplacementMap() {} -struct pixel_t { - unsigned char channels[4]; - inline unsigned char operator[](int c) const { return channels[c]; } - inline unsigned char& operator[](int c) { return channels[c]; } - static inline pixel_t blank() { - pixel_t p; - for(unsigned int i=0; i<4; i++) { - p[i] = 0; +struct Displace { + Displace(cairo_surface_t *texture, cairo_surface_t *map, + unsigned xch, unsigned ych, double scalex, double scaley) + : _texture(texture) + , _map(map) + , _xch(xch) + , _ych(ych) + , _scalex(scalex/255.0) + , _scaley(scaley/255.0) + {} + guint32 operator()(int x, int y) { + guint32 mappx = _map.pixelAt(x, y); + guint32 a = (mappx & 0xff000000) >> 24; + guint32 xpx = 0, ypx = 0; + double xtex = x, ytex = y; + + guint32 xshift = _xch * 8, yshift = _ych * 8; + xpx = (mappx & (0xff << xshift)) >> xshift; + ypx = (mappx & (0xff << yshift)) >> yshift; + if (a) { + if (_xch != 3) xpx = unpremul_alpha(xpx, a); + if (_ych != 3) ypx = unpremul_alpha(ypx, a); } - return p; - } -}; - -static inline pixel_t pixelValue(NRPixBlock const* pb, int x, int y) { - if ( x < pb->area.x0 || x >= pb->area.x1 || y < pb->area.y0 || y >= pb->area.y1 ) return pixel_t::blank(); // This assumes anything outside the defined range is (0,0,0,0) - pixel_t const* rowData = reinterpret_cast<pixel_t const*>(NR_PIXBLOCK_PX(pb) + (y-pb->area.y0)*pb->rs); - return rowData[x-pb->area.x0]; -} - -template<bool PREMULTIPLIED> -static pixel_t interpolatePixels(NRPixBlock const* pb, double x, double y) { - // NOTE: The values of x and y are shifted by -0.5 (the "true" values would be x+0.5 and y+0.5). - // This is done because otherwise the pixel values first have to be shifted by +0.5 and then by -0.5 again... - unsigned int const sfl = 8u; - unsigned int const sf = 1u<<sfl; - unsigned int const sf2h = 1u<<(2u*sfl-1); - int xi = (int)floor(x), yi = (int)floor(y); - unsigned int xf = static_cast<unsigned int>(round(sf * (x - xi))), - yf = static_cast<unsigned int>(round(sf * (y - yi))); - pixel_t p00 = pixelValue(pb, xi+0, yi+0); - pixel_t p01 = pixelValue(pb, xi+1, yi+0); - pixel_t p10 = pixelValue(pb, xi+0, yi+1); - pixel_t p11 = pixelValue(pb, xi+1, yi+1); - - /* It's a good idea to interpolate premultiplied colors: - * - * Consider two pixels, one being rgba(255,0,0,0), which is fully transparent, - * and the other being rgba(0,0,255,255), or blue (fully opaque). - * If these two colors are interpolated the expected result would be bluish pixels - * containing no red. - * - * However, if our final alpha value is zero, then the RGB values aren't really determinate. - * We might as well avoid premultiplication in this case, which still gives us a fully - * transparent result, but with interpolated RGB parts. */ - - pixel_t r; - if (PREMULTIPLIED) { - /* Premultiplied, so do simple interpolation. */ - for (unsigned i = 0; i != 4; ++i) { - // y0,y1 have range [0,a*sf] - unsigned const y0 = sf*p00[i] + xf*((unsigned int)p01[i]-(unsigned int)p00[i]); - unsigned const y1 = sf*p10[i] + xf*((unsigned int)p11[i]-(unsigned int)p10[i]); - - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,a*sf*sf] - r[i] = (ri + sf2h)>>(2*sfl); // range [0,a] - } - } else { - /* First calculate interpolated alpha value. */ - unsigned const y0 = sf*p00[3] + xf*((unsigned int)p01[3]-(unsigned int)p00[3]); // range [0,a*sf] - unsigned const y1 = sf*p10[3] + xf*((unsigned int)p11[3]-(unsigned int)p10[3]); - unsigned const ra = sf*y0 + yf*(y1-y0); // range [0,a*sf*sf] - - if (ra==0) { - /* Fully transparent, so do simple interpolation. */ - for (unsigned i = 0; i != 3; ++i) { - // y0,y1 have range [0,255*sf] - unsigned const y0 = sf*p00[i] + xf*((unsigned int)p01[i]-(unsigned int)p00[i]); - unsigned const y1 = sf*p10[i] + xf*((unsigned int)p11[i]-(unsigned int)p10[i]); + xtex += _scalex * (xpx - 127.5); + ytex += _scaley * (ypx - 127.5); - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,255*sf*sf] - r[i] = (ri + sf2h)>>(2*sfl); // range [0,255] - } - r[3] = 0; + if (xtex >= 0 && xtex < (_texture._w - 1) && + ytex >= 0 && ytex < (_texture._h - 1)) + { + return _texture.pixelAt(xtex, ytex); } else { - /* Do premultiplication ourselves. */ - for (unsigned i = 0; i != 3; ++i) { - // Premultiplied versions. Range [0,255*a]. - unsigned const c00 = p00[i]*p00[3]; - unsigned const c01 = p01[i]*p01[3]; - unsigned const c10 = p10[i]*p10[3]; - unsigned const c11 = p11[i]*p11[3]; - - // Interpolation. - unsigned const y0 = sf*c00 + xf*(c01-c00); // range [0,255*a*sf] - unsigned const y1 = sf*c10 + xf*(c11-c10); // range [0,255*a*sf] - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,255*a*sf*sf] - r[i] = (ri + ra/2) / ra; // range [0,255] - } - r[3] = (ra + sf2h)>>(2*sfl); // range [0,a] - } - } - - return r; -} - -template<bool MAP_PREMULTIPLIED, bool DATA_PREMULTIPLIED> -static void performDisplacement(NRPixBlock const* texture, NRPixBlock const* map, int Xchannel, int Ychannel, NRPixBlock* out, double scalex, double scaley) { - bool Xneedsdemul = MAP_PREMULTIPLIED && Xchannel<3; - bool Yneedsdemul = MAP_PREMULTIPLIED && Ychannel<3; - if (!Xneedsdemul) scalex /= 255.0; - if (!Yneedsdemul) scaley /= 255.0; - - for (int yout=out->area.y0; yout < out->area.y1; yout++){ - pixel_t const* mapRowData = reinterpret_cast<pixel_t const*>(NR_PIXBLOCK_PX(map) + (yout-map->area.y0)*map->rs); - pixel_t* outRowData = reinterpret_cast<pixel_t*>(NR_PIXBLOCK_PX(out) + (yout-out->area.y0)*out->rs); - for (int xout=out->area.x0; xout < out->area.x1; xout++){ - pixel_t const mapValue = mapRowData[xout-map->area.x0]; - - double xtex = xout + (Xneedsdemul ? // Although the value of the pixel corresponds to the MIDDLE of the pixel, no +0.5 is needed because we're interpolating pixels anyway (so to get the actual pixel locations 0.5 would have to be subtracted again). - (mapValue[3]==0?0:(scalex * (mapValue[Xchannel] - mapValue[3]*0.5) / mapValue[3])) : - (scalex * (mapValue[Xchannel] - 127.5))); - double ytex = yout + (Yneedsdemul ? - (mapValue[3]==0?0:(scaley * (mapValue[Ychannel] - mapValue[3]*0.5) / mapValue[3])) : - (scaley * (mapValue[Ychannel] - 127.5))); - - outRowData[xout-out->area.x0] = interpolatePixels<DATA_PREMULTIPLIED>(texture, xtex, ytex); + return 0; } } -} - -int FilterDisplacementMap::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *texture = slot.get(_input); - NRPixBlock *map = slot.get(_input2); - - // Bail out if either one of source images is missing - if (!map || !texture) { - g_warning("Missing source image for feDisplacementMap (map=%d texture=%d)", _input, _input2); - return 1; - } - - NR::IRect area = units.get_pixblock_filterarea_paraller(); - int x0 = std::max(map->area.x0,area.min()[NR::X]); - int y0 = std::max(map->area.y0,area.min()[NR::Y]); - int x1 = std::min(map->area.x1,area.max()[NR::X]); - int y1 = std::min(map->area.y1,area.max()[NR::Y]); - - //TODO: check whether we really need this check: - if (x1 <= x0 || y1 <= y0) return 0; //nothing to do! - - if (texture->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && texture->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - g_warning("Source images without an alpha channel are not supported by feDisplacementMap at the moment."); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, texture->mode, x0, y0, x1, y1, true); +private: + SurfaceSynth _texture; + SurfaceSynth _map; + unsigned _xch, _ych; + double _scalex, _scaley; +}; - // convert to a suitable format - bool free_map_on_exit = false; - if (map->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && map->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - NRPixBlock *original_map = map; - map = new NRPixBlock; - nr_pixblock_setup_fast(map, NR_PIXBLOCK_MODE_R8G8B8A8N, - original_map->area.x0, original_map->area.y0, - original_map->area.x1, original_map->area.y1, - false); - nr_blit_pixblock_pixblock(map, original_map); - free_map_on_exit = true; - } - bool map_premultiplied = (map->mode == NR_PIXBLOCK_MODE_R8G8B8A8P); - bool data_premultiplied = (out->mode == NR_PIXBLOCK_MODE_R8G8B8A8P); +void FilterDisplacementMap::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *texture = slot.getcairo(_input); + cairo_surface_t *map = slot.getcairo(_input2); + cairo_surface_t *out = ink_cairo_surface_create_identical(texture); - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); + Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb(); double scalex = scale * trans.expansionX(); double scaley = scale * trans.expansionY(); - if (map_premultiplied && data_premultiplied) { - performDisplacement<true,true>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else if (map_premultiplied && !data_premultiplied) { - performDisplacement<true,false>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else if (data_premultiplied) { - performDisplacement<false,true>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else { - performDisplacement<false,false>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } - - if (free_map_on_exit) { - nr_pixblock_release(map); - delete map; - } + ink_cairo_surface_synthesize(out, Displace(texture, map, Xchannel, Ychannel, scalex, scaley)); - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); } void FilterDisplacementMap::set_input(int slot) { @@ -233,11 +104,29 @@ void FilterDisplacementMap::set_channel_selector(int s, FilterDisplacementMapCha return; } - if (s == 0) Xchannel = channel; - if (s == 1) Ychannel = channel; + // channel numbering: + // a = 3, r = 2, g = 1, b = 0 + // this way we can get the component value using: + // component = (color & (ch*8)) >> (ch*8) + unsigned ch = 4; + switch (channel) { + case DISPLACEMENTMAP_CHANNEL_ALPHA: + ch = 3; break; + case DISPLACEMENTMAP_CHANNEL_RED: + ch = 2; break; + case DISPLACEMENTMAP_CHANNEL_GREEN: + ch = 1; break; + case DISPLACEMENTMAP_CHANNEL_BLUE: + ch = 0; break; + default: break; + } + if (ch == 4) return; + + if (s == 0) Xchannel = ch; + if (s == 1) Ychannel = ch; } -void FilterDisplacementMap::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterDisplacementMap::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) { //I assume scale is in user coordinates (?!?) //FIXME: trans should be multiplied by some primitiveunits2user, shouldn't it? @@ -246,14 +135,12 @@ void FilterDisplacementMap::area_enlarge(NRRectL &area, Geom::Affine const &tran double scaley = scale/2.*(std::fabs(trans[2])+std::fabs(trans[3])); //FIXME: no +2 should be there!... (noticable only for big scales at big zoom factor) - area.x0 -= (int)(scalex)+2; - area.x1 += (int)(scalex)+2; - area.y0 -= (int)(scaley)+2; - area.y1 += (int)(scaley)+2; + area.expandBy(scalex+2, scaley+2); } -FilterTraits FilterDisplacementMap::get_input_traits() { - return TRAIT_PARALLER; +double FilterDisplacementMap::complexity(Geom::Affine const &) +{ + return 3.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-displacement-map.h b/src/display/nr-filter-displacement-map.h index 1dad1873a..a01930045 100644 --- a/src/display/nr-filter-displacement-map.h +++ b/src/display/nr-filter-displacement-map.h @@ -16,7 +16,6 @@ #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" namespace Inkscape { namespace Filters { @@ -27,19 +26,19 @@ public: static FilterPrimitive *create(); virtual ~FilterDisplacementMap(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + virtual void set_input(int slot); virtual void set_input(int input, int slot); virtual void set_scale(double s); virtual void set_channel_selector(int s, FilterDisplacementMapChannelSelector channel); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); private: double scale; int _input2; - FilterDisplacementMapChannelSelector Xchannel; - FilterDisplacementMapChannelSelector Ychannel; + unsigned Xchannel, Ychannel; }; } /* namespace Filters */ diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp index ca073cfd8..7db14737b 100644 --- a/src/display/nr-filter-flood.cpp +++ b/src/display/nr-filter-flood.cpp @@ -3,8 +3,9 @@ * * Authors: * Felipe Corrêa da Silva Sanches <juca@members.fsf.org> + * Tavmjong Bah <tavmjong@free.fr> (use primitive filter region) * - * Copyright (C) 2007 authors + * Copyright (C) 2007, 2011 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -13,10 +14,12 @@ # include "config.h" #endif +#include "display/cairo-utils.h" #include "display/nr-filter-flood.h" -#include "display/nr-filter-utils.h" +#include "display/nr-filter-slot.h" #include "svg/svg-icc-color.h" #include "svg/svg-color.h" +#include "color.h" namespace Inkscape { namespace Filters { @@ -31,48 +34,40 @@ FilterPrimitive * FilterFlood::create() { FilterFlood::~FilterFlood() {} -int FilterFlood::render(FilterSlot &slot, FilterUnits const &/*units*/) { -//g_message("rendering feflood"); - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feFlood (in=%d)", _input); - return 1; +void FilterFlood::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + + double r = SP_RGBA32_R_F(color); + double g = SP_RGBA32_G_F(color); + double b = SP_RGBA32_B_F(color); + double a = opacity; + + #if ENABLE_LCMS + if (icc) { + guchar ru, gu, bu; + icc_color_to_sRGB(icc, &ru, &gu, &bu); + r = SP_COLOR_U_TO_F(ru); + g = SP_COLOR_U_TO_F(gu); + b = SP_COLOR_U_TO_F(bu); } + #endif - int i; - int in_w = in->area.x1 - in->area.x0; - int in_h = in->area.y1 - in->area.y0; - - NRPixBlock *out = new NRPixBlock; - - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - - unsigned char *out_data = NR_PIXBLOCK_PX(out); - unsigned char r,g,b,a; - - - r = CLAMP_D_TO_U8((color >> 24) % 256); - g = CLAMP_D_TO_U8((color >> 16) % 256); - b = CLAMP_D_TO_U8((color >> 8) % 256); - a = CLAMP_D_TO_U8(opacity*255); - -#if ENABLE_LCMS - icc_color_to_sRGB(icc, &r, &g, &b); -//g_message("result: r:%d g:%d b:%d", r, g, b); -#endif //ENABLE_LCMS + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + cairo_t *ct = cairo_create(out); + cairo_set_source_rgba(ct, r, g, b, a); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); - for(i=0; i < 4*in_h*in_w; i+=4){ - out_data[i]=r; - out_data[i+1]=g; - out_data[i+2]=b; - out_data[i+3]=a; - } - - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); +} + +bool FilterFlood::can_handle_affine(Geom::Affine const &) +{ + // flood is a per-pixel primitive and is immutable under transformations + return true; } void FilterFlood::set_color(guint32 c) { @@ -86,9 +81,11 @@ void FilterFlood::set_opacity(double o) { void FilterFlood::set_icc(SVGICCColor *icc_color) { icc = icc_color; } - -void FilterFlood::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) +double FilterFlood::complexity(Geom::Affine const &) { + // flood is actually less expensive than normal rendering, + // but when flood is processed, the object has already been rendered + return 1.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h index ebb7d4506..8568502ff 100644 --- a/src/display/nr-filter-flood.h +++ b/src/display/nr-filter-flood.h @@ -13,9 +13,8 @@ */ #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include "svg/svg-color.h" + +class SVGICCColor; namespace Inkscape { namespace Filters { @@ -25,12 +24,16 @@ public: FilterFlood(); static FilterPrimitive *create(); virtual ~FilterFlood(); + + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + virtual bool uses_background() { return false; } virtual void set_opacity(double o); virtual void set_color(guint32 c); virtual void set_icc(SVGICCColor *icc_color); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + private: double opacity; guint32 color; diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index 3f7bea35d..06c2d2718 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -1,5 +1,3 @@ -#define __NR_FILTER_GAUSSIAN_CPP__ - /* * Gaussian blur renderer * @@ -25,14 +23,12 @@ #include <omp.h> #endif //HAVE_OPENMP -#include "2geom/isnan.h" - +#include "display/cairo-utils.h" #include "display/nr-filter-primitive.h" #include "display/nr-filter-gaussian.h" #include "display/nr-filter-types.h" #include "display/nr-filter-units.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" +#include "display/nr-filter-slot.h" #include <2geom/affine.h> #include "util/fixed_point.h" #include "preferences.h" @@ -78,15 +74,46 @@ template<typename T> static inline T clip(T const& v, T const& a, T const& b) { } template<typename Tt, typename Ts> -static inline Tt round_cast(Ts const& v) { +static inline Tt round_cast(Ts v) { static Ts const rndoffset(.5); return static_cast<Tt>(v+rndoffset); } +/* +template<> +inline unsigned char round_cast(double v) { + // This (fast) rounding method is based on: + // http://stereopsis.com/sree/fpu2006.html +#if G_BYTE_ORDER==G_LITTLE_ENDIAN + double const dmr = 6755399441055744.0; + v = v + dmr; + return ((unsigned char*)&v)[0]; +#elif G_BYTE_ORDER==G_BIG_ENDIAN + double const dmr = 6755399441055744.0; + v = v + dmr; + return ((unsigned char*)&v)[7]; +#else + static double const rndoffset(.5); + return static_cast<unsigned char>(v+rndoffset); +#endif +}*/ + +template<typename Tt, typename Ts> +static inline Tt clip_round_cast(Ts const v) { + Ts const minval = std::numeric_limits<Tt>::min(); + Ts const maxval = std::numeric_limits<Tt>::max(); + Tt const minval_rounded = std::numeric_limits<Tt>::min(); + Ts const maxval_rounded = std::numeric_limits<Tt>::max(); + if ( v < minval ) return minval_rounded; + if ( v > maxval ) return maxval_rounded; + return round_cast<Tt>(v); +} template<typename Tt, typename Ts> -static inline Tt clip_round_cast(Ts const& v, Tt const minval=std::numeric_limits<Tt>::min(), Tt const maxval=std::numeric_limits<Tt>::max()) { - if ( v < minval ) return minval; - if ( v > maxval ) return maxval; +static inline Tt clip_round_cast_varmax(Ts const v, Ts const maxval, Tt const maxval_rounded) { + Ts const minval = std::numeric_limits<Tt>::min(); + Tt const minval_rounded = std::numeric_limits<Tt>::min(); + if ( v < minval ) return minval_rounded; + if ( v > maxval ) return maxval_rounded; return round_cast<Tt>(v); } @@ -111,7 +138,7 @@ FilterGaussian::~FilterGaussian() static int _effect_area_scr(double const deviation) { - return (int)std::ceil(deviation * 3.0); + return (int)std::ceil(std::fabs(deviation) * 3.0); } static void @@ -192,21 +219,6 @@ _effect_subsample_step_log2(double const deviation, int const quality) return stepsize_l2; } -/** - * Sanity check function for indexing pixblocks. - * Catches reading and writing outside the pixblock area. - * When enabled, decreases filter rendering speed massively. - */ -static inline void -_check_index(NRPixBlock const * const pb, int const location, int const line) -{ - if (false) { - int max_loc = pb->rs * (pb->area.y1 - pb->area.y0); - if (location < 0 || location >= max_loc) - g_warning("Location %d out of bounds (0 ... %d) at line %d", location, max_loc, line); - } -} - static void calcFilter(double const sigma, double b[N]) { assert(N==3); std::complex<double> const d1_org(1.40098, 1.00236); @@ -214,7 +226,6 @@ static void calcFilter(double const sigma, double b[N]) { double qbeg = 1; // Don't go lower than sigma==2 (we'd probably want a normal convolution in that case anyway) double qend = 2*sigma; double const sigmasqr = sqr(sigma); - double s; do { // Binary search for right q (a linear interpolation scheme is suggested, but this should work fine as well) double const q = (qbeg+qend)/2; // Compute scaled filter coefficients @@ -227,7 +238,6 @@ static void calcFilter(double const sigma, double b[N]) { } else { qend = q; } - s = sqrt(ssqr); } while(qend-qbeg>(sigma/(1<<30))); // Compute filter coefficients double const q = (qbeg+qend)/2; @@ -284,6 +294,14 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, int const n1, int const n2, IIRValue const b[N+1], double const M[N*N], IIRValue *const tmpdata[], int const num_threads) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + static unsigned int const alpha_PC = PC-1; + #define PREMUL_ALPHA_LOOP for(unsigned int c=0; c<PC-1; ++c) +#else + static unsigned int const alpha_PC = 0; + #define PREMUL_ALPHA_LOOP for(unsigned int c=1; c<PC; ++c) +#endif + #if HAVE_OPENMP #pragma omp parallel for num_threads(num_threads) #else @@ -319,8 +337,8 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, calcTriggsSdikaInitialization<PC>(M, u, iplus, iplus, b[0], v); dstimg -= dstr1; if ( PREMULTIPLIED_ALPHA ) { - dstimg[PC-1] = clip_round_cast<PT>(v[0][PC-1]); - for(unsigned int c=0; c<PC-1; c++) dstimg[c] = clip_round_cast<PT>(v[0][c], std::numeric_limits<PT>::min(), dstimg[PC-1]); + dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]); + PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], v[0][alpha_PC], dstimg[alpha_PC]); } else { for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]); } @@ -334,8 +352,8 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, } dstimg -= dstr1; if ( PREMULTIPLIED_ALPHA ) { - dstimg[PC-1] = clip_round_cast<PT>(v[0][PC-1]); - for(unsigned int c=0; c<PC-1; c++) dstimg[c] = clip_round_cast<PT>(v[0][c], std::numeric_limits<PT>::min(), dstimg[PC-1]); + dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]); + PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], v[0][alpha_PC], dstimg[alpha_PC]); } else { for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]); } @@ -450,418 +468,229 @@ filter2D_FIR(PT *const dst, int const dstr1, int const dstr2, } } -template<typename PT, unsigned int PC> static void -downsample(PT *const dst, int const dstr1, int const dstr2, int const dn1, int const dn2, - PT const *const src, int const sstr1, int const sstr2, int const sn1, int const sn2, - int const step1_l2, int const step2_l2) +gaussian_pass_IIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest, + IIRValue **tmpdata, int num_threads) { - unsigned int const divisor_l2 = step1_l2+step2_l2; // step1*step2=2^(step1_l2+step2_l2) - unsigned int const round_offset = (1<<divisor_l2)/2; - int const step1 = 1<<step1_l2; - int const step2 = 1<<step2_l2; - int const step1_2 = step1/2; - int const step2_2 = step2/2; - for(int dc2 = 0 ; dc2 < dn2 ; dc2++) { - int const sc2_begin = (dc2<<step2_l2)-step2_2; - int const sc2_end = sc2_begin+step2; - for(int dc1 = 0 ; dc1 < dn1 ; dc1++) { - int const sc1_begin = (dc1<<step1_l2)-step1_2; - int const sc1_end = sc1_begin+step1; - unsigned int sum[PC]; - std::fill_n(sum, PC, 0); - for(int sc2 = sc2_begin ; sc2 < sc2_end ; sc2++) { - for(int sc1 = sc1_begin ; sc1 < sc1_end ; sc1++) { - for(unsigned int ch = 0 ; ch < PC ; ch++) { - sum[ch] += src[clip(sc2,0,sn2-1)*sstr2+clip(sc1,0,sn1-1)*sstr1+ch]; - } - } - } - for(unsigned int ch = 0 ; ch < PC ; ch++) { - dst[dc2*dstr2+dc1*dstr1+ch] = static_cast<PT>((sum[ch]+round_offset)>>divisor_l2); - } - } + // Filter variables + IIRValue b[N+1]; // scaling coefficient + filter coefficients (can be 10.21 fixed point) + double bf[N]; // computed filter coefficients + double M[N*N]; // matrix used for initialization procedure (has to be double) + + // Compute filter + calcFilter(deviation, bf); + for(size_t i=0; i<N; i++) bf[i] = -bf[i]; + b[0] = 1; // b[0] == alpha (scaling coefficient) + for(size_t i=0; i<N; i++) { + b[i+1] = bf[i]; + b[0] -= b[i+1]; } + + // Compute initialization matrix + calcTriggsSdikaM(bf, M); + + int stride = cairo_image_surface_get_stride(src); + int w = cairo_image_surface_get_width(src); + int h = cairo_image_surface_get_height(src); + if (d != Geom::X) std::swap(w, h); + + // Filter + switch (cairo_image_surface_get_format(src)) { + case CAIRO_FORMAT_A8: ///< Grayscale + filter2D_IIR<unsigned char,1,false>( + cairo_image_surface_get_data(dest), d == Geom::X ? 1 : stride, d == Geom::X ? stride : 1, + cairo_image_surface_get_data(src), d == Geom::X ? 1 : stride, d == Geom::X ? stride : 1, + w, h, b, M, tmpdata, num_threads); + break; + case CAIRO_FORMAT_ARGB32: ///< Premultiplied 8 bit RGBA + filter2D_IIR<unsigned char,4,true>( + cairo_image_surface_get_data(dest), d == Geom::X ? 4 : stride, d == Geom::X ? stride : 4, + cairo_image_surface_get_data(src), d == Geom::X ? 4 : stride, d == Geom::X ? stride : 4, + w, h, b, M, tmpdata, num_threads); + break; + default: + g_warning("gaussian_pass_IIR: unsupported image format"); + }; } -template<typename PT, unsigned int PC> static void -upsample(PT *const dst, int const dstr1, int const dstr2, unsigned int const dn1, unsigned int const dn2, - PT const *const src, int const sstr1, int const sstr2, unsigned int const sn1, unsigned int const sn2, - unsigned int const step1_l2, unsigned int const step2_l2) +gaussian_pass_FIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest, + int num_threads) { - assert(((sn1-1)<<step1_l2)>=dn1 && ((sn2-1)<<step2_l2)>=dn2); // The last pixel of the source image should fall outside the destination image - unsigned int const divisor_l2 = step1_l2+step2_l2; // step1*step2=2^(step1_l2+step2_l2) - unsigned int const round_offset = (1<<divisor_l2)/2; - unsigned int const step1 = 1<<step1_l2; - unsigned int const step2 = 1<<step2_l2; - for ( unsigned int sc2 = 0 ; sc2 < sn2-1 ; sc2++ ) { - unsigned int const dc2_begin = (sc2 << step2_l2); - unsigned int const dc2_end = std::min(dn2, dc2_begin+step2); - for ( unsigned int sc1 = 0 ; sc1 < sn1-1 ; sc1++ ) { - unsigned int const dc1_begin = (sc1 << step1_l2); - unsigned int const dc1_end = std::min(dn1, dc1_begin+step1); - for ( unsigned int byte = 0 ; byte < PC ; byte++) { - - // get 4 values at the corners of the pixel from src - PT a00 = src[sstr2* sc2 + sstr1* sc1 + byte]; - PT a10 = src[sstr2* sc2 + sstr1*(sc1+1) + byte]; - PT a01 = src[sstr2*(sc2+1) + sstr1* sc1 + byte]; - PT a11 = src[sstr2*(sc2+1) + sstr1*(sc1+1) + byte]; - - // initialize values for linear interpolation - unsigned int a0 = a00*step2/*+a01*0*/; - unsigned int a1 = a10*step2/*+a11*0*/; - - // iterate over the rectangle to be interpolated - for ( unsigned int dc2 = dc2_begin ; dc2 < dc2_end ; dc2++ ) { - - // prepare linear interpolation for this row - unsigned int a = a0*step1/*+a1*0*/+round_offset; - - for ( unsigned int dc1 = dc1_begin ; dc1 < dc1_end ; dc1++ ) { - - // simple linear interpolation - dst[dstr2*dc2 + dstr1*dc1 + byte] = static_cast<PT>(a>>divisor_l2); - - // compute a = a0*(ix-1)+a1*(xi+1)+round_offset - a = a - a0 + a1; - } - - // compute a0 = a00*(iy-1)+a01*(yi+1) and similar for a1 - a0 = a0 - a00 + a01; - a1 = a1 - a10 + a11; - } - } - } - } + int scr_len = _effect_area_scr(deviation); + // Filter kernel for x direction + std::vector<FIRValue> kernel(scr_len + 1); + _make_kernel(&kernel[0], deviation); + + int stride = cairo_image_surface_get_stride(src); + int w = cairo_image_surface_get_width(src); + int h = cairo_image_surface_get_height(src); + if (d != Geom::X) std::swap(w, h); + + // Filter (x) + switch (cairo_image_surface_get_format(src)) { + case CAIRO_FORMAT_A8: ///< Grayscale + filter2D_FIR<unsigned char,1>( + cairo_image_surface_get_data(dest), d == Geom::X ? 1 : stride, d == Geom::X ? stride : 1, + cairo_image_surface_get_data(src), d == Geom::X ? 1 : stride, d == Geom::X ? stride : 1, + w, h, &kernel[0], scr_len, num_threads); + break; + case CAIRO_FORMAT_ARGB32: ///< Premultiplied 8 bit RGBA + filter2D_FIR<unsigned char,4>( + cairo_image_surface_get_data(dest), d == Geom::X ? 4 : stride, d == Geom::X ? stride : 4, + cairo_image_surface_get_data(src), d == Geom::X ? 4 : stride, d == Geom::X ? stride : 4, + w, h, &kernel[0], scr_len, num_threads); + break; + default: + g_warning("gaussian_pass_FIR: unsupported image format"); + }; } -int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units) +void FilterGaussian::render_cairo(FilterSlot &slot) { - // TODO: Meaningful return values? (If they're checked at all.) - - /* in holds the input pixblock */ - NRPixBlock *original_in = slot.get(_input); - - /* If to either direction, the standard deviation is zero or - * input image is not defined, - * a transparent black image should be returned. */ - if (_deviation_x <= 0 || _deviation_y <= 0 || original_in == NULL) { - NRPixBlock *src = original_in; - if (src == NULL) { - g_warning("Missing source image for feGaussianBlur (in=%d)", _input); - // A bit guessing here, but source graphic is likely to be of - // right size - src = slot.get(NR_FILTER_SOURCEGRAPHIC); - } - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, src->mode, src->area.x0, src->area.y0, - src->area.x1, src->area.y1, true); - if (out->size != NR_PIXBLOCK_SIZE_TINY && out->data.px != NULL) { - out->empty = false; - slot.set(_output, out); - } - return 0; + cairo_surface_t *in = slot.getcairo(_input); + if (!in) return; + + // zero deviation = no change in output + if (_deviation_x <= 0 && _deviation_y <= 0) { + cairo_surface_t *cp = ink_cairo_surface_copy(in); + slot.set(_output, cp); + cairo_surface_destroy(cp); + return; } - // Gaussian blur is defined to operate on non-premultiplied color values. - // So, convert the input first it uses non-premultiplied color values. - // And please note that this should not be done AFTER resampling, as resampling a non-premultiplied image - // does not simply yield a non-premultiplied version of the resampled premultiplied image!!! - NRPixBlock *in = original_in; - if (in->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) { - in = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - false); - if (!in) { - // ran out of memory - return 0; - } - nr_blit_pixblock_pixblock(in, original_in); + Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb(); + + int w_orig = ink_cairo_surface_get_width(in); + int h_orig = ink_cairo_surface_get_height(in); + double deviation_x_orig = _deviation_x * trans.expansionX(); + double deviation_y_orig = _deviation_y * trans.expansionY(); + cairo_format_t fmt = cairo_image_surface_get_format(in); + int bytes_per_pixel = 0; + switch (fmt) { + case CAIRO_FORMAT_A8: + bytes_per_pixel = 1; break; + case CAIRO_FORMAT_ARGB32: + default: + bytes_per_pixel = 4; break; } - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); - - // Some common constants - int const width_org = in->area.x1-in->area.x0, height_org = in->area.y1-in->area.y0; - double const deviation_x_org = _deviation_x * trans.expansionX(); - double const deviation_y_org = _deviation_y * trans.expansionY(); - int const PC = NR_PIXBLOCK_BPP(in); #if HAVE_OPENMP Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int const NTHREADS = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); + int threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); #else - int const NTHREADS = 1; -#endif // HAVE_OPENMP + int threads = 1; +#endif - // Subsampling constants - int const quality = slot.get_blurquality(); - int const x_step_l2 = _effect_subsample_step_log2(deviation_x_org, quality); - int const y_step_l2 = _effect_subsample_step_log2(deviation_y_org, quality); - int const x_step = 1<<x_step_l2; - int const y_step = 1<<y_step_l2; - bool const resampling = x_step > 1 || y_step > 1; - int const width = resampling ? static_cast<int>(ceil(static_cast<double>(width_org)/x_step))+1 : width_org; - int const height = resampling ? static_cast<int>(ceil(static_cast<double>(height_org)/y_step))+1 : height_org; - double const deviation_x = deviation_x_org / x_step; - double const deviation_y = deviation_y_org / y_step; - int const scr_len_x = _effect_area_scr(deviation_x); - int const scr_len_y = _effect_area_scr(deviation_y); + int quality = slot.get_blurquality(); + int x_step = 1 << _effect_subsample_step_log2(deviation_x_orig, quality); + int y_step = 1 << _effect_subsample_step_log2(deviation_y_orig, quality); + bool resampling = x_step > 1 || y_step > 1; + int w_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(w_orig)/x_step))+1 : w_orig; + int h_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(h_orig)/y_step))+1 : h_orig; + double deviation_x = deviation_x_orig / x_step; + double deviation_y = deviation_y_orig / y_step; + int scr_len_x = _effect_area_scr(deviation_x); + int scr_len_y = _effect_area_scr(deviation_y); // Decide which filter to use for X and Y // This threshold was determined by trial-and-error for one specific machine, // so there's a good chance that it's not optimal. // Whatever you do, don't go below 1 (and preferrably not even below 2), as // the IIR filter gets unstable there. - bool const use_IIR_x = deviation_x > 3; - bool const use_IIR_y = deviation_y > 3; - - // new buffer for the subsampled output - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, in->mode, in->area.x0/x_step, in->area.y0/y_step, - in->area.x0/x_step+width, in->area.y0/y_step+height, true); - if (out->size != NR_PIXBLOCK_SIZE_TINY && out->data.px == NULL) { - // alas, we've accomplished a lot, but ran out of memory - so abort - if (in != original_in) nr_pixblock_free(in); - delete out; - return 0; - } + bool use_IIR_x = deviation_x > 3; + bool use_IIR_y = deviation_y > 3; + // Temporary storage for IIR filter // NOTE: This can be eliminated, but it reduces the precision a bit - IIRValue * tmpdata[NTHREADS]; - std::fill_n(tmpdata, NTHREADS, (IIRValue*)0); + IIRValue * tmpdata[threads]; + std::fill_n(tmpdata, threads, (IIRValue*)0); if ( use_IIR_x || use_IIR_y ) { - for(int i=0; i<NTHREADS; i++) { - tmpdata[i] = new IIRValue[std::max(width,height)*PC]; - if (tmpdata[i] == NULL) { - if (in != original_in) nr_pixblock_free(in); - nr_pixblock_release(out); - while(i-->0) { - delete[] tmpdata[i]; - } - delete out; - return 0; - } + for(int i = 0; i < threads; ++i) { + tmpdata[i] = new IIRValue[std::max(w_downsampled,h_downsampled)*bytes_per_pixel]; } } - // Resampling (if necessary), goes from in -> out (setting ssin to out if used) - NRPixBlock *ssin = in; - if ( resampling ) { - ssin = out; - // Downsample - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - downsample<unsigned char,1>(NR_PIXBLOCK_PX(out), 1, out->rs, width, height, NR_PIXBLOCK_PX(in), 1, in->rs, width_org, height_org, x_step_l2, y_step_l2); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - downsample<unsigned char,3>(NR_PIXBLOCK_PX(out), 3, out->rs, width, height, NR_PIXBLOCK_PX(in), 3, in->rs, width_org, height_org, x_step_l2, y_step_l2); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // downsample<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, width, height, NR_PIXBLOCK_PX(in), 4, in->rs, width_org, height_org, x_step_l2, y_step_l2); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - downsample<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, width, height, NR_PIXBLOCK_PX(in), 4, in->rs, width_org, height_org, x_step_l2, y_step_l2); - break; - default: - assert(false); - }; + cairo_surface_t *downsampled = NULL; + if (resampling) { + downsampled = cairo_surface_create_similar(in, cairo_surface_get_content(in), + w_downsampled, h_downsampled); + cairo_t *ct = cairo_create(downsampled); + cairo_scale(ct, static_cast<double>(w_downsampled)/w_orig, static_cast<double>(h_downsampled)/h_orig); + cairo_set_source_surface(ct, in, 0, 0); + cairo_paint(ct); + cairo_destroy(ct); + } else { + downsampled = ink_cairo_surface_copy(in); } + cairo_surface_flush(downsampled); - // Horizontal filtering, goes from ssin -> out (ssin might be equal to out, but these algorithms can be used in-place) - if (use_IIR_x) { - // Filter variables - IIRValue b[N+1]; // scaling coefficient + filter coefficients (can be 10.21 fixed point) - double bf[N]; // computed filter coefficients - double M[N*N]; // matrix used for initialization procedure (has to be double) - - // Compute filter (x) - calcFilter(deviation_x, bf); - for(size_t i=0; i<N; i++) bf[i] = -bf[i]; - b[0] = 1; // b[0] == alpha (scaling coefficient) - for(size_t i=0; i<N; i++) { - b[i+1] = bf[i]; - b[0] -= b[i+1]; + if (scr_len_x > 0) { + if (use_IIR_x) { + gaussian_pass_IIR(Geom::X, deviation_x, downsampled, downsampled, tmpdata, threads); + } else { + gaussian_pass_FIR(Geom::X, deviation_x, downsampled, downsampled, threads); } - - // Compute initialization matrix (x) - calcTriggsSdikaM(bf, M); - - // Filter (x) - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, b, M, tmpdata, NTHREADS); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, b, M, tmpdata, NTHREADS); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata, NTHREADS); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata, NTHREADS); - break; - default: - assert(false); - }; - } else if ( scr_len_x > 0 ) { // !use_IIR_x - // Filter kernel for x direction - FIRValue kernel[scr_len_x+1]; - _make_kernel(kernel, deviation_x); - - // Filter (x) - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, kernel, scr_len_x, NTHREADS); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, kernel, scr_len_x, NTHREADS); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x, NTHREADS); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x, NTHREADS); - break; - default: - assert(false); - }; - } else if ( out != ssin ) { // out can be equal to ssin if resampling is used - nr_blit_pixblock_pixblock(out, ssin); } - // Vertical filtering, goes from out -> out - if (use_IIR_y) { - // Filter variables - IIRValue b[N+1]; // scaling coefficient + filter coefficients (can be 10.21 fixed point) - double bf[N]; // computed filter coefficients - double M[N*N]; // matrix used for initialization procedure (has to be double) - - // Compute filter (y) - calcFilter(deviation_y, bf); - for(size_t i=0; i<N; i++) bf[i] = -bf[i]; - b[0] = 1; // b[0] == alpha (scaling coefficient) - for(size_t i=0; i<N; i++) { - b[i+1] = bf[i]; - b[0] -= b[i+1]; + if (scr_len_y > 0) { + if (use_IIR_y) { + gaussian_pass_IIR(Geom::Y, deviation_y, downsampled, downsampled, tmpdata, threads); + } else { + gaussian_pass_FIR(Geom::Y, deviation_y, downsampled, downsampled, threads); } - - // Compute initialization matrix (y) - calcTriggsSdikaM(bf, M); - - // Filter (y) - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, b, M, tmpdata, NTHREADS); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, b, M, tmpdata, NTHREADS); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata, NTHREADS); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata, NTHREADS); - break; - default: - assert(false); - }; - } else if ( scr_len_y > 0 ) { // !use_IIR_y - // Filter kernel for y direction - FIRValue kernel[scr_len_y+1]; - _make_kernel(kernel, deviation_y); - - // Filter (y) - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, kernel, scr_len_y, NTHREADS); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, kernel, scr_len_y, NTHREADS); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y, NTHREADS); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y, NTHREADS); - break; - default: - assert(false); - }; } - for(int i=0; i<NTHREADS; i++) { - delete[] tmpdata[i]; // deleting a nullptr has no effect, so this is safe + // free the temporary data + if ( use_IIR_x || use_IIR_y ) { + for(int i = 0; i < threads; ++i) { + delete[] tmpdata[i]; + } } - // Upsampling, stores (the upsampled) out using slot.set(_output, ...) - if ( !resampling ) { - // No upsampling needed - out->empty = FALSE; - slot.set(_output, out); + cairo_surface_mark_dirty(downsampled); + if (resampling) { + cairo_surface_t *upsampled = cairo_surface_create_similar(downsampled, cairo_surface_get_content(downsampled), + w_orig, h_orig); + cairo_t *ct = cairo_create(upsampled); + cairo_scale(ct, static_cast<double>(w_orig)/w_downsampled, static_cast<double>(h_orig)/h_downsampled); + cairo_set_source_surface(ct, downsampled, 0, 0); + cairo_paint(ct); + cairo_destroy(ct); + + slot.set(_output, upsampled); + cairo_surface_destroy(upsampled); + cairo_surface_destroy(downsampled); } else { - // New buffer for the final output, same resolution as the in buffer - NRPixBlock *finalout = new NRPixBlock; - nr_pixblock_setup_fast(finalout, in->mode, in->area.x0, in->area.y0, - in->area.x1, in->area.y1, true); - if (finalout->size != NR_PIXBLOCK_SIZE_TINY && finalout->data.px == NULL) { - // alas, we've accomplished a lot, but ran out of memory - so abort - if (in != original_in) nr_pixblock_free(in); - nr_pixblock_release(out); - delete out; - return 0; - } - - // Upsample - switch(in->mode) { - case NR_PIXBLOCK_MODE_A8: ///< Grayscale - upsample<unsigned char,1>(NR_PIXBLOCK_PX(finalout), 1, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 1, out->rs, width, height, x_step_l2, y_step_l2); - break; - case NR_PIXBLOCK_MODE_R8G8B8: ///< 8 bit RGB - upsample<unsigned char,3>(NR_PIXBLOCK_PX(finalout), 3, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 3, out->rs, width, height, x_step_l2, y_step_l2); - break; - //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA - // upsample<unsigned char,4>(NR_PIXBLOCK_PX(finalout), 4, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 4, out->rs, width, height, x_step_l2, y_step_l2); - // break; - case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA - upsample<unsigned char,4>(NR_PIXBLOCK_PX(finalout), 4, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 4, out->rs, width, height, x_step_l2, y_step_l2); - break; - default: - assert(false); - }; - - // We don't need the out buffer anymore - nr_pixblock_release(out); - delete out; - - // The final out buffer gets returned - finalout->empty = FALSE; - slot.set(_output, finalout); + slot.set(_output, downsampled); + cairo_surface_destroy(downsampled); } - - // If we downsampled the input, clean up the downsampled data - if (in != original_in) nr_pixblock_free(in); - - return 0; } -void FilterGaussian::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterGaussian::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) { int area_x = _effect_area_scr(_deviation_x * trans.expansionX()); int area_y = _effect_area_scr(_deviation_y * trans.expansionY()); // maximum is used because rotations can mix up these directions // TODO: calculate a more tight-fitting rendering area int area_max = std::max(area_x, area_y); - area.x0 -= area_max; - area.x1 += area_max; - area.y0 -= area_max; - area.y1 += area_max; + area.expandBy(area_max); } -FilterTraits FilterGaussian::get_input_traits() { - return TRAIT_PARALLER; +bool FilterGaussian::can_handle_affine(Geom::Affine const &) +{ + // Previously we tried to be smart and return true for rotations. + // However, the transform passed here is NOT the total transform + // from filter user space to screen. + // TODO: fix this, or replace can_handle_affine() with isotropic(). + return false; +} + +double FilterGaussian::complexity(Geom::Affine const &trans) +{ + int area_x = _effect_area_scr(_deviation_x * trans.expansionX()); + int area_y = _effect_area_scr(_deviation_y * trans.expansionY()); + return 2.0 * area_x * area_y; } void FilterGaussian::set_deviation(double deviation) diff --git a/src/display/nr-filter-gaussian.h b/src/display/nr-filter-gaussian.h index 87d74ac63..1c35a0f1d 100644 --- a/src/display/nr-filter-gaussian.h +++ b/src/display/nr-filter-gaussian.h @@ -14,12 +14,8 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include "libnr/nr-pixblock.h" #include <2geom/forward.h> -#include "libnr/nr-rect-l.h" +#include "display/nr-filter-primitive.h" enum { BLUR_QUALITY_BEST = 2, @@ -38,9 +34,10 @@ public: static FilterPrimitive *create(); virtual ~FilterGaussian(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &m); - virtual FilterTraits get_input_traits(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &m); + virtual bool can_handle_affine(Geom::Affine const &m); + virtual double complexity(Geom::Affine const &ctm); /** * Set the standard deviation value for gaussian blur. Deviation along diff --git a/src/display/nr-filter-getalpha.cpp b/src/display/nr-filter-getalpha.cpp deleted file mode 100644 index 7b5e2a101..000000000 --- a/src/display/nr-filter-getalpha.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Functions for extracting alpha channel from NRPixBlocks. - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2007 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "display/nr-filter-getalpha.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" - -namespace Inkscape { -namespace Filters { - -NRPixBlock *filter_get_alpha(NRPixBlock *src) -{ - NRPixBlock *dst = new NRPixBlock; - nr_pixblock_setup_fast(dst, NR_PIXBLOCK_MODE_R8G8B8A8P, - src->area.x0, src->area.y0, - src->area.x1, src->area.y1, false); - if (!dst || (dst->size != NR_PIXBLOCK_SIZE_TINY && dst->data.px == NULL)) { - g_warning("Memory allocation failed in filter_get_alpha"); - delete dst; - return NULL; - } - nr_blit_pixblock_pixblock(dst, src); - - unsigned char *data = NR_PIXBLOCK_PX(dst); - int end = dst->rs * (dst->area.y1 - dst->area.y0); - for (int i = 0 ; i < end ; i += 4) { - data[i + 0] = 0; - data[i + 1] = 0; - data[i + 2] = 0; - } - dst->empty = false; - - return dst; -} - -} /* namespace Filters */ -} /* namespace Inkscape */ - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-filter-getalpha.h b/src/display/nr-filter-getalpha.h deleted file mode 100644 index e279494b1..000000000 --- a/src/display/nr-filter-getalpha.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __NR_FILTER_GETALPHA_H__ -#define __NR_FILTER_GETALPHA_H__ - -/* - * Functions for extracting alpha channel from NRPixBlocks. - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2007 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "libnr/nr-pixblock.h" - -namespace Inkscape { -namespace Filters { - -NRPixBlock *filter_get_alpha(NRPixBlock *src); - -} /* namespace Filters */ -} /* namespace Inkscape */ - -#endif /* __NR_FILTER_GETALPHA_H__ */ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index 9ecec69bf..8b2161425 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -12,25 +12,23 @@ */ #include "document.h" #include "sp-item.h" -#include "display/nr-arena.h" -#include "display/nr-arena-item.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing.h" +#include "display/drawing-item.h" #include "display/nr-filter.h" #include "display/nr-filter-image.h" #include "display/nr-filter-units.h" -#include "libnr/nr-compose-transform.h" -#include "libnr/nr-rect-l.h" -#include "preferences.h" -#include "svg/svg-length.h" -#include "enums.h" namespace Inkscape { namespace Filters { -FilterImage::FilterImage() : - SVGElem(0), - document(0), - feImageHref(0), - image_pixbuf(0) +FilterImage::FilterImage() + : SVGElem(0) + , document(0) + , feImageHref(0) + , image_surface(0) + , broken_ref(false) { } FilterPrimitive * FilterImage::create() { @@ -39,76 +37,82 @@ FilterPrimitive * FilterImage::create() { FilterImage::~FilterImage() { - if (feImageHref) g_free(feImageHref); + if (feImageHref) + g_free(feImageHref); } -int FilterImage::render(FilterSlot &slot, FilterUnits const &units) { - if (!feImageHref) return 0; +void FilterImage::render_cairo(FilterSlot &slot) +{ + if (!feImageHref) + return; + + //cairo_surface_t *input = slot.getcairo(_input); + + Geom::Affine m = slot.get_units().get_matrix_user2filterunits().inverse(); + Geom::Point bbox_00 = Geom::Point(0,0) * m; + Geom::Point bbox_w0 = Geom::Point(1,0) * m; + Geom::Point bbox_0h = Geom::Point(0,1) * m; + double bbox_width = Geom::distance(bbox_00, bbox_w0); + double bbox_height = Geom::distance(bbox_00, bbox_0h); + + + // feImage is suppose to use the same parameters as a normal SVG image. + // If a width or height is set to zero, the image is not suppose to be displayed. + // This does not seem to be what Firefox or Opera does, nor does the W3C displacement + // filter test expect this behavior. If the width and/or height are zero, we use + // the width and height of the object bounding box. + if( feImageWidth == 0 ) feImageWidth = bbox_width; + if( feImageHeight == 0 ) feImageHeight = bbox_height; + + if (from_element) { + if (!SVGElem) return; + + // TODO: do not recreate the rendering tree every time + // TODO: the entire thing is a hack, we should give filter primitives an "update" method + // like the one for DrawingItems + document->ensureUpToDate(); - NRPixBlock* pb = NULL; - bool free_pb_on_exit = false; + Drawing drawing; + Geom::OptRect optarea = SVGElem->visualBounds(); + if (!optarea) return; - if(from_element){ - if (!SVGElem) return 0; - - // prep the document - document->ensureUpToDate(); - NRArena* arena = NRArena::create(); unsigned const key = SPItem::display_key_new(1); - NRArenaItem* ai = SVGElem->invoke_show(arena, key, SP_ITEM_SHOW_DISPLAY); + DrawingItem *ai = SVGElem->invoke_show(drawing, key, SP_ITEM_SHOW_DISPLAY); if (!ai) { - g_warning("feImage renderer: error creating NRArenaItem for SVG Element"); - nr_object_unref((NRObject *) arena); - return 0; + g_warning("feImage renderer: error creating DrawingItem for SVG Element"); + return; } + drawing.setRoot(ai); - pb = new NRPixBlock; - free_pb_on_exit = true; - - Geom::OptRect area = SVGElem->getBounds(Geom::identity()); - - NRRectL rect; - rect.x0=area->min()[Geom::X]; - rect.x1=area->max()[Geom::X]; - rect.y0=area->min()[Geom::Y]; - rect.y1=area->max()[Geom::Y]; - - width = (int)(rect.x1-rect.x0); - height = (int)(rect.y1-rect.y0); - rowstride = 4*width; - has_alpha = true; - - if (image_pixbuf) g_free(image_pixbuf); - image_pixbuf = g_try_new(unsigned char, 4L * width * height); - if(image_pixbuf != NULL) - { - memset(image_pixbuf, 0x00, 4 * width * height); - - NRGC gc(NULL); - /* Update to renderable state */ - double sf = 1.0; - Geom::Affine t(Geom::Scale(sf, sf)); - nr_arena_item_set_transform(ai, &t); - gc.transform.setIdentity(); - nr_arena_item_invoke_update( ai, NULL, &gc, - NR_ARENA_ITEM_STATE_ALL, - NR_ARENA_ITEM_STATE_NONE ); - nr_pixblock_setup_extern(pb, NR_PIXBLOCK_MODE_R8G8B8A8N, - (int)rect.x0, (int)rect.y0, (int)rect.x1, (int)rect.y1, - image_pixbuf, 4 * width, FALSE, FALSE ); - - nr_arena_item_invoke_render(NULL, ai, &rect, pb, NR_ARENA_ITEM_RENDER_NO_CACHE); - } - else - { - g_warning("FilterImage::render: not enough memory to create pixel buffer. Need %ld.", 4L * width * height); - } + Geom::Rect area = *optarea; + Geom::Affine pu2pb = slot.get_units().get_matrix_primitiveunits2pb(); + + double scaleX = feImageWidth / area.width(); + double scaleY = feImageHeight / area.height(); + + Geom::Rect sa = slot.get_slot_area(); + cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + sa.width(), sa.height()); + Inkscape::DrawingContext ct(out, sa.min()); + ct.transform(pu2pb); // we are now in primitive units + ct.translate(feImageX, feImageY); + ct.scale(scaleX, scaleY); + + Geom::IntRect render_rect = area.roundOutwards(); + ct.translate(render_rect.min()); + + // Update to renderable state + drawing.update(render_rect); + drawing.render(ct, render_rect); SVGElem->invoke_hide(key); - nr_object_unref((NRObject *) arena); - } + slot.set(_output, out); + cairo_surface_destroy(out); + return; + } - if (!image_pixbuf){ + if (!image && !broken_ref) { + broken_ref = true; try { /* TODO: If feImageHref is absolute, then use that (preferably handling the * case that it's not a file URI). Otherwise, go up the tree looking @@ -127,190 +131,98 @@ int FilterImage::render(FilterSlot &slot, FilterUnits const &units) { if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { // Should display Broken Image png. g_warning("FilterImage::render: Can not find: %s", feImageHref ); + return; } image = Gdk::Pixbuf::create_from_file(fullname); if( fullname != feImageHref ) g_free( fullname ); } catch (const Glib::FileError & e) { - g_warning("caught Glib::FileError in FilterImage::render %i", e.code() ); - return 0; + g_warning("caught Glib::FileError in FilterImage::render: %s", e.what().data() ); + return; } catch (const Gdk::PixbufError & e) { - g_warning("Gdk::PixbufError in FilterImage::render: %i", e.code() ); - return 0; + g_warning("Gdk::PixbufError in FilterImage::render: %s", e.what().data() ); + return; } - if ( !image ) return 0; + if ( !image ) return; - // Native size of image - width = image->get_width(); - height = image->get_height(); - rowstride = image->get_rowstride(); - image_pixbuf = image->get_pixels(); - has_alpha = image->get_has_alpha(); - } + broken_ref = false; - // Viewport is filter primitive area (in user coordinates). - Geom::Rect vp = filter_primitive_area( units ); - double feImageX = vp.min()[Geom::X]; - double feImageY = vp.min()[Geom::Y]; - double feImageWidth = vp.width(); - double feImageHeight = vp.height(); - - // Now that we have the viewport, we must map image inside. - // Partially copied from sp-image.cpp. - - // Do nothing if preserveAspectRatio is "none". - if( aspect_align != SP_ASPECT_NONE ) { - - // Check aspect ratio of image vs. viewport - double feAspect = feImageHeight/feImageWidth; - double aspect = (double)height/(double)width; - bool ratio = (feAspect < aspect); - - double ax, ay; // Align side - switch( aspect_align ) { - case SP_ASPECT_XMIN_YMIN: - ax = 0.0; - ay = 0.0; - break; - case SP_ASPECT_XMID_YMIN: - ax = 0.5; - ay = 0.0; - break; - case SP_ASPECT_XMAX_YMIN: - ax = 1.0; - ay = 0.0; - break; - case SP_ASPECT_XMIN_YMID: - ax = 0.0; - ay = 0.5; - break; - case SP_ASPECT_XMID_YMID: - ax = 0.5; - ay = 0.5; - break; - case SP_ASPECT_XMAX_YMID: - ax = 1.0; - ay = 0.5; - break; - case SP_ASPECT_XMIN_YMAX: - ax = 0.0; - ay = 1.0; - break; - case SP_ASPECT_XMID_YMAX: - ax = 0.5; - ay = 1.0; - break; - case SP_ASPECT_XMAX_YMAX: - ax = 1.0; - ay = 1.0; - break; - default: - ax = 0.0; - ay = 0.0; - break; + bool has_alpha = image->get_has_alpha(); + if (!has_alpha) { + image = image->add_alpha(false, 0, 0, 0); } - if( aspect_clip == SP_ASPECT_SLICE ) { - // image clipped by viewbox - - if( ratio ) { - // clip top/bottom - feImageY -= ay * (feImageWidth * aspect - feImageHeight); - feImageHeight = feImageWidth * aspect; - } else { - // clip sides - feImageX -= ax * (feImageHeight / aspect - feImageWidth); - feImageWidth = feImageHeight / aspect; - } + // Native size of image + //width = image->get_width(); + //height = image->get_height(); + //rowstride = image->get_rowstride(); - } else { - // image fits into viewbox - - if( ratio ) { - // fit to height - feImageX += ax * (feImageWidth - feImageHeight / aspect ); - feImageWidth = feImageHeight / aspect; - } else { - // fit to width - feImageY += ay * (feImageHeight - feImageWidth * aspect); - feImageHeight = feImageWidth * aspect; - } - } - } + convert_pixbuf_normal_to_argb32(image->gobj()); - // Set up user coordinates to pix block transforms - double scaleX = width/feImageWidth; - double scaleY = height/feImageHeight; - Geom::Affine unit_trans = units.get_matrix_user2pb().inverse(); - - // Region being drawn on screen. Corresponds to Filter Region in current screen coordinates. - // Note, that the screen is refreshed in horizontal slices with y-axis inverted. - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feImage (in=%d)", _input); - return 1; - } - int x0 = in->area.x0, y0 = in->area.y0; - int x1 = in->area.x1, y1 = in->area.y1; - int w = x1 - x0; - - Geom::Affine d2s = Geom::Translate(x0, y0) * unit_trans * Geom::Translate(-feImageX,-feImageY) * Geom::Scale(scaleX, scaleY); - - // Set up pix block - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, true); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int nr_arena_image_x_sample = prefs->getInt("/options/bitmapoversample/value", 1); - int nr_arena_image_y_sample = nr_arena_image_x_sample; - - - if (has_alpha) { - nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM(out_data, x1-x0, y1-y0, 4*w, image_pixbuf, width, height, rowstride, d2s, 255, nr_arena_image_x_sample, nr_arena_image_y_sample); - } else { - for (int x=x0; x < x1; x++){ - for (int y=y0; y < y1; y++){ - //TODO: use interpolation - // Temporarily add 0.5 so we sample center of "cell" - double indexX = scaleX * (((x+0.5) * unit_trans[0] + unit_trans[4]) - feImageX); - double indexY = scaleY * (((y+0.5) * unit_trans[3] + unit_trans[5]) - feImageY); - - // coordx == 0 and coordy == 0 must be included, but we protect - // against negative numbers which round up to 0 with (int). - int coordx = ( indexX >= 0 ? int( indexX ) : -1 ); - int coordy = ( indexY >= 0 ? int( indexY ) : -1 ); - if (coordx >= 0 && coordx < width && coordy >= 0 && coordy < height){ - out_data[4*((x - x0)+w*(y - y0)) ] = (unsigned char) image_pixbuf[3*coordx + rowstride*coordy ]; //Red - out_data[4*((x - x0)+w*(y - y0)) + 1] = (unsigned char) image_pixbuf[3*coordx + rowstride*coordy + 1]; //Green - out_data[4*((x - x0)+w*(y - y0)) + 2] = (unsigned char) image_pixbuf[3*coordx + rowstride*coordy + 2]; //Blue - out_data[4*((x - x0)+w*(y - y0)) + 3] = 255; //Alpha - } - } - } - } - if (free_pb_on_exit) { - nr_pixblock_release(pb); - delete pb; + image_surface = cairo_image_surface_create_for_data(image->get_pixels(), + CAIRO_FORMAT_ARGB32, image->get_width(), image->get_height(), image->get_rowstride()); } - out->empty = FALSE; + Geom::Rect sa = slot.get_slot_area(); + cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + sa.width(), sa.height()); + + cairo_t *ct = cairo_create(out); + cairo_translate(ct, -sa.min()[Geom::X], -sa.min()[Geom::Y]); + // now ct is in pb coordinates + ink_cairo_transform(ct, slot.get_units().get_matrix_primitiveunits2pb()); + // now ct is in the coordinates of feImageX etc. + + // TODO: add preserveAspectRatio support here + + double scaleX = feImageWidth / image->get_width(); + double scaleY = feImageHeight / image->get_height(); + + cairo_translate(ct, feImageX, feImageY); + cairo_scale(ct, scaleX, scaleY); + cairo_set_source_surface(ct, image_surface, 0, 0); + cairo_paint(ct); + cairo_destroy(ct); + slot.set(_output, out); - return 0; +} + +bool FilterImage::can_handle_affine(Geom::Affine const &) +{ + return true; +} + +double FilterImage::complexity(Geom::Affine const &) +{ + // TODO: right now we cannot actually measure this in any meaningful way. + return 1.1; } void FilterImage::set_href(const gchar *href){ if (feImageHref) g_free (feImageHref); feImageHref = (href) ? g_strdup (href) : NULL; + + if (image_surface) { + cairo_surface_destroy(image_surface); + } + image.reset(); + broken_ref = false; } void FilterImage::set_document(SPDocument *doc){ document = doc; } +void FilterImage::set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height) { + feImageX=x.computed; + feImageY=y.computed; + feImageWidth=width.computed; + feImageHeight=height.computed; +} + void FilterImage::set_align( unsigned int align ) { aspect_align = align; } @@ -319,10 +231,6 @@ void FilterImage::set_clip( unsigned int clip ) { aspect_clip = clip; } -FilterTraits FilterImage::get_input_traits() { - return TRAIT_PARALLER; -} - } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h index ecbc9597d..5af0b3338 100644 --- a/src/display/nr-filter-image.h +++ b/src/display/nr-filter-image.h @@ -27,10 +27,13 @@ public: static FilterPrimitive *create(); virtual ~FilterImage(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual FilterTraits get_input_traits(); + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + void set_document( SPDocument *document ); void set_href(const gchar *href); + void set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height); void set_align( unsigned int align ); void set_clip( unsigned int clip ); bool from_element; @@ -39,12 +42,11 @@ public: private: SPDocument *document; gchar *feImageHref; - guint8* image_pixbuf; Glib::RefPtr<Gdk::Pixbuf> image; - int width, height, rowstride; - unsigned int aspect_align : 4; - unsigned int aspect_clip : 1; - bool has_alpha; + cairo_surface_t *image_surface; + float feImageX, feImageY, feImageWidth, feImageHeight; + unsigned int aspect_align, aspect_clip; + bool broken_ref; }; } /* namespace Filters */ diff --git a/src/display/nr-filter-merge.cpp b/src/display/nr-filter-merge.cpp index 971dcb80f..28ac19a19 100644 --- a/src/display/nr-filter-merge.cpp +++ b/src/display/nr-filter-merge.cpp @@ -9,28 +9,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <cmath> #include <vector> - -#include "2geom/isnan.h" -#include "filters/merge.h" +#include "display/cairo-utils.h" #include "display/nr-filter-merge.h" -#include "display/nr-filter-pixops.h" #include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-pixops.h" - -inline void -composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b) -{ - r[0] = a[0] + NR_NORMALIZE_21(b[0] * (255 - a[3])); - r[1] = a[1] + NR_NORMALIZE_21(b[1] * (255 - a[3])); - r[2] = a[2] + NR_NORMALIZE_21(b[2] * (255 - a[3])); - r[3] = a[3] + NR_NORMALIZE_21(b[3] * (255 - a[3])); -} namespace Inkscape { namespace Filters { @@ -46,70 +29,58 @@ FilterPrimitive * FilterMerge::create() { FilterMerge::~FilterMerge() {} -int FilterMerge::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in[_input_image.size()]; - NRPixBlock *original_in[_input_image.size()]; - - for (unsigned int i = 0 ; i < _input_image.size() ; i++) { - in[i] = slot.get(_input_image[i]); - original_in[i] = in[i]; - } - - NRPixBlock *out; - - // Bail out if one of source images is missing - for (unsigned int i = 0 ; i < _input_image.size() ; i++) { - bool missing = false; - if (!in[i]) { - g_warning("Missing source image for feMerge (number=%d slot=%d)", i, _input_image[i]); - missing = true; - } - if (missing) return 1; - } - - out = new NRPixBlock; - NRRectL out_area = in[0]->area; - for (unsigned int i = 1 ; i < _input_image.size() ; i++) { - nr_rect_l_union(&out_area, &out_area, &in[i]->area); - } - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, - out_area.x0, out_area.y0, out_area.x1, out_area.y1, - true); - - // Merge is defined for premultiplied RGBA values, thus convert them to - // that format before blending - for (unsigned int i = 0 ; i < _input_image.size() ; i++) { - if (in[i]->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - in[i] = new NRPixBlock; - nr_pixblock_setup_fast(in[i], NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in[i]->area.x0, - original_in[i]->area.y0, - original_in[i]->area.x1, - original_in[i]->area.y1, - false); - nr_blit_pixblock_pixblock(in[i], original_in[i]); +void FilterMerge::render_cairo(FilterSlot &slot) +{ + if (_input_image.size() == 0) return; + + // output is RGBA if at least one input is RGBA + bool rgba32 = false; + cairo_surface_t *out = NULL; + for (std::vector<int>::iterator i = _input_image.begin(); i != _input_image.end(); ++i) { + cairo_surface_t *in = slot.getcairo(*i); + if (cairo_surface_get_content(in) == CAIRO_CONTENT_COLOR_ALPHA) { + out = ink_cairo_surface_create_identical(in); + rgba32 = true; + break; } } - /* pixops_mix is defined in display/nr-filter-pixops.h - * It mixes the two input images with the function given as template - * and places the result in output image. - */ - for (unsigned int i = 0 ; i < _input_image.size() ; i++) { - pixops_mix<composite_over>(*out, *in[i], *out); + if (!rgba32) { + out = ink_cairo_surface_create_identical(slot.getcairo(_input_image[0])); } + cairo_t *out_ct = cairo_create(out); - for (unsigned int i = 0 ; i < _input_image.size() ; i++) { - if (in[i] != original_in[i]) { - nr_pixblock_release(in[i]); - delete in[i]; - } + for (std::vector<int>::iterator i = _input_image.begin(); i != _input_image.end(); ++i) { + cairo_surface_t *in = slot.getcairo(*i); + cairo_set_source_surface(out_ct, in, 0, 0); + cairo_paint(out_ct); } - out->empty = FALSE; + cairo_destroy(out_ct); slot.set(_output, out); + cairo_surface_destroy(out); +} + +bool FilterMerge::can_handle_affine(Geom::Affine const &) +{ + // Merge is a per-pixel primitive and is immutable under transformations + return true; +} - return 0; +double FilterMerge::complexity(Geom::Affine const &) +{ + return 1.02; +} + +bool FilterMerge::uses_background() +{ + for (int i = 0; i < _input_image.size(); ++i) { + int input = _input_image[i]; + if (input == NR_FILTER_BACKGROUNDIMAGE || input == NR_FILTER_BACKGROUNDALPHA) { + return true; + } + } + return false; } void FilterMerge::set_input(int slot) { diff --git a/src/display/nr-filter-merge.h b/src/display/nr-filter-merge.h index 715f20d83..238f9a3e7 100644 --- a/src/display/nr-filter-merge.h +++ b/src/display/nr-filter-merge.h @@ -13,11 +13,7 @@ */ #include <vector> - -#include "filters/merge.h" #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" namespace Inkscape { namespace Filters { @@ -28,7 +24,10 @@ public: static FilterPrimitive *create(); virtual ~FilterMerge(); - virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual void render_cairo(FilterSlot &); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + virtual bool uses_background(); virtual void set_input(int input); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp index ceb2a2b9d..b6aea1b06 100644 --- a/src/display/nr-filter-morphology.cpp +++ b/src/display/nr-filter-morphology.cpp @@ -10,9 +10,12 @@ */ #include <cmath> +#include <algorithm> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter-morphology.h" +#include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-blit.h" namespace Inkscape { namespace Filters { @@ -28,99 +31,135 @@ FilterPrimitive * FilterMorphology::create() { FilterMorphology::~FilterMorphology() {} -int FilterMorphology::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feMorphology (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - - // this primitive is defined for premultiplied RGBA values, - // thus convert them to that format - bool free_in_on_exit = false; - if (in->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - NRPixBlock *original_in = in; - in = new NRPixBlock; - nr_pixblock_setup_fast(in, NR_PIXBLOCK_MODE_R8G8B8A8P, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - true); - nr_blit_pixblock_pixblock(in, original_in); - free_in_on_exit = true; - } +enum MorphologyOp { + ERODE, + DILATE +}; + +namespace { + +/* This performs one "half" of the morphology operation by calculating + * the componentwise extreme in the specified axis with the given radius. + * Performing the operation one axis at a time gives us a MASSIVE performance boost + * at large morphology radii. Extreme of row extremes is equal to the extreme + * of components, so this doesn't change the result. */ +template <MorphologyOp OP, Geom::Dim2 axis> +struct Morphology : public SurfaceSynth { + Morphology(cairo_surface_t *in, double xradius) + : SurfaceSynth(in) + , _radius(round(xradius)) + {} + guint32 operator()(int x, int y) { + int start, end; + if (axis == Geom::X) { + start = std::max(0, x - _radius); + end = std::min(x + _radius + 1, _w); + } else { + start = std::max(0, y - _radius); + end = std::min(y + _radius + 1, _h); + } - Geom::Affine p2pb = units.get_matrix_primitiveunits2pb(); - int const xradius = (int)round(this->xradius * p2pb.expansionX()); - int const yradius = (int)round(this->yradius * p2pb.expansionY()); - - int x0=in->area.x0; - int y0=in->area.y0; - int x1=in->area.x1; - int y1=in->area.y1; - int w=x1-x0, h=y1-y0; - int x, y, i, j; - int rmax,gmax,bmax,amax; - int rmin,gmin,bmin,amin; - - nr_pixblock_setup_fast(out, in->mode, x0, y0, x1, y1, true); - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - - for(x = 0 ; x < w ; x++){ - for(y = 0 ; y < h ; y++){ - rmin = gmin = bmin = amin = 255; - rmax = gmax = bmax = amax = 0; - for(i = x - xradius ; i < x + xradius ; i++){ - if (i < 0 || i >= w) continue; - for(j = y - yradius ; j < y + yradius ; j++){ - if (j < 0 || j >= h) continue; - if(in_data[4*(i + w*j)]>rmax) rmax = in_data[4*(i + w*j)]; - if(in_data[4*(i + w*j)+1]>gmax) gmax = in_data[4*(i + w*j)+1]; - if(in_data[4*(i + w*j)+2]>bmax) bmax = in_data[4*(i + w*j)+2]; - if(in_data[4*(i + w*j)+3]>amax) amax = in_data[4*(i + w*j)+3]; - - if(in_data[4*(i + w*j)]<rmin) rmin = in_data[4*(i + w*j)]; - if(in_data[4*(i + w*j)+1]<gmin) gmin = in_data[4*(i + w*j)+1]; - if(in_data[4*(i + w*j)+2]<bmin) bmin = in_data[4*(i + w*j)+2]; - if(in_data[4*(i + w*j)+3]<amin) amin = in_data[4*(i + w*j)+3]; + guint32 ao = (OP == DILATE ? 0 : 255); + guint32 ro = (OP == DILATE ? 0 : 255); + guint32 go = (OP == DILATE ? 0 : 255); + guint32 bo = (OP == DILATE ? 0 : 255); + + if (_alpha) { + ao = (OP == DILATE ? 0 : 0xff000000); + for (int i = start; i < end; ++i) { + guint32 px = (axis == Geom::X ? pixelAt(i, y) : pixelAt(x, i)); + if (OP == DILATE) { + if (px > ao) ao = px; + } else { + if (px < ao) ao = px; } } - if (Operator==MORPHOLOGY_OPERATOR_DILATE){ - out_data[4*(x + w*y)]=rmax; - out_data[4*(x + w*y)+1]=gmax; - out_data[4*(x + w*y)+2]=bmax; - out_data[4*(x + w*y)+3]=amax; - } else { - out_data[4*(x + w*y)]=rmin; - out_data[4*(x + w*y)+1]=gmin; - out_data[4*(x + w*y)+2]=bmin; - out_data[4*(x + w*y)+3]=amin; + return ao; + } else { + for (int i = start; i < end; ++i) { + guint32 px = (axis == Geom::X ? pixelAt(i, y) : pixelAt(x, i)); + EXTRACT_ARGB32(px, a,r,g,b); + + // this will be compiled to conditional moves; + // the operator comparison will be evaluated at compile time. + // therefore there will be no branching in this loop + if (OP == DILATE) { + if (a > ao) ao = a; + if (r > ro) ro = r; + if (g > go) go = g; + if (b > bo) bo = b; + } else { + if (a < ao) ao = a; + if (r < ro) ro = r; + if (g < go) go = g; + if (b < bo) bo = b; + } + + // TODO: verify whether this check gives any speedup. + if (OP == ERODE && a == 0) break; } + + ASSEMBLE_ARGB32(pxout, ao,ro,go,bo) + return pxout; } } +private: + int _radius; +}; + +} // end anonymous namespace + +void FilterMorphology::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + + if (xradius == 0.0 || yradius == 0.0) { + // output is transparent black + cairo_surface_t *out = ink_cairo_surface_create_identical(input); + slot.set(_output, out); + cairo_surface_destroy(out); + return; + } - if (free_in_on_exit) { - nr_pixblock_release(in); - delete in; + Geom::Affine p2pb = slot.get_units().get_matrix_primitiveunits2pb(); + double xr = xradius * p2pb.expansionX(); + double yr = yradius * p2pb.expansionY(); + + cairo_surface_t *interm = ink_cairo_surface_create_identical(input); + + if (Operator == MORPHOLOGY_OPERATOR_DILATE) { + ink_cairo_surface_synthesize(interm, Morphology<DILATE, Geom::X>(input, xr)); + } else { + ink_cairo_surface_synthesize(interm, Morphology<ERODE, Geom::X>(input, xr)); + } + + cairo_surface_t *out = ink_cairo_surface_create_identical(input); + + if (Operator == MORPHOLOGY_OPERATOR_DILATE) { + ink_cairo_surface_synthesize(out, Morphology<DILATE, Geom::Y>(interm, yr)); + } else { + ink_cairo_surface_synthesize(out, Morphology<ERODE, Geom::Y>(interm, yr)); } - out->empty = FALSE; + cairo_surface_destroy(interm); + slot.set(_output, out); - return 0; + cairo_surface_destroy(out); } -void FilterMorphology::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterMorphology::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) { - int const enlarge_x = (int)std::ceil(this->xradius * (std::fabs(trans[0]) + std::fabs(trans[1]))); - int const enlarge_y = (int)std::ceil(this->yradius * (std::fabs(trans[2]) + std::fabs(trans[3]))); + int enlarge_x = ceil(xradius * trans.expansionX()); + int enlarge_y = ceil(yradius * trans.expansionY()); - area.x0 -= enlarge_x; - area.x1 += enlarge_x; - area.y0 -= enlarge_y; - area.y1 += enlarge_y; + area.expandBy(enlarge_x, enlarge_y); +} + +double FilterMorphology::complexity(Geom::Affine const &trans) +{ + int enlarge_x = ceil(xradius * trans.expansionX()); + int enlarge_y = ceil(yradius * trans.expansionY()); + return enlarge_x * enlarge_y; } void FilterMorphology::set_operator(FilterMorphologyOperator &o){ @@ -135,10 +174,6 @@ void FilterMorphology::set_yradius(double y){ yradius = y; } -FilterTraits FilterMorphology::get_input_traits() { - return TRAIT_PARALLER; -} - } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-morphology.h b/src/display/nr-filter-morphology.h index c8d8b2f50..0574ff4ad 100644 --- a/src/display/nr-filter-morphology.h +++ b/src/display/nr-filter-morphology.h @@ -13,12 +13,12 @@ */ #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterMorphologyOperator { MORPHOLOGY_OPERATOR_ERODE, MORPHOLOGY_OPERATOR_DILATE, @@ -31,9 +31,10 @@ public: static FilterPrimitive *create(); virtual ~FilterMorphology(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + void set_operator(FilterMorphologyOperator &o); void set_xradius(double x); void set_yradius(double y); diff --git a/src/display/nr-filter-offset.cpp b/src/display/nr-filter-offset.cpp index 7c924b7b7..833f6ecc9 100644 --- a/src/display/nr-filter-offset.cpp +++ b/src/display/nr-filter-offset.cpp @@ -9,12 +9,10 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "display/cairo-utils.h" #include "display/nr-filter-offset.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-rect-l.h" namespace Inkscape { namespace Filters { @@ -33,37 +31,29 @@ FilterPrimitive * FilterOffset::create() { FilterOffset::~FilterOffset() {} -int FilterOffset::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *in = slot.get(_input); - // Bail out if source image is missing - if (!in) { - g_warning("Missing source image for feOffset (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; +void FilterOffset::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *in = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_identical(in); + cairo_t *ct = cairo_create(out); - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); + Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb(); Geom::Point offset(dx, dy); offset *= trans; offset[X] -= trans[4]; offset[Y] -= trans[5]; - nr_pixblock_setup_fast(out, in->mode, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - nr_blit_pixblock_pixblock(out, in); + cairo_set_source_surface(ct, in, offset[X], offset[Y]); + cairo_paint(ct); + cairo_destroy(ct); - out->area.x0 += static_cast<NR::ICoord>(offset[X]); - out->area.y0 += static_cast<NR::ICoord>(offset[Y]); - out->area.x1 += static_cast<NR::ICoord>(offset[X]); - out->area.y1 += static_cast<NR::ICoord>(offset[Y]); - out->visible_area = out->area; - - out->empty = FALSE; slot.set(_output, out); + cairo_surface_destroy(out); +} - return 0; +bool FilterOffset::can_handle_affine(Geom::Affine const &) +{ + return true; } void FilterOffset::set_dx(double amount) { @@ -74,24 +64,35 @@ void FilterOffset::set_dy(double amount) { dy = amount; } -void FilterOffset::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterOffset::area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) { Geom::Point offset(dx, dy); offset *= trans; offset[X] -= trans[4]; offset[Y] -= trans[5]; + double x0, y0, x1, y1; + x0 = area.left(); + y0 = area.top(); + x1 = area.right(); + y1 = area.bottom(); if (offset[X] > 0) { - area.x0 -= static_cast<NR::ICoord>(offset[X]); + x0 -= ceil(offset[X]); } else { - area.x1 -= static_cast<NR::ICoord>(offset[X]); + x1 -= floor(offset[X]); } if (offset[Y] > 0) { - area.y0 -= static_cast<NR::ICoord>(offset[Y]); + y0 -= ceil(offset[Y]); } else { - area.y1 -= static_cast<NR::ICoord>(offset[Y]); + y1 -= floor(offset[Y]); } + area = Geom::IntRect(x0, y0, x1, y1); +} + +double FilterOffset::complexity(Geom::Affine const &) +{ + return 1.02; } } /* namespace Filters */ diff --git a/src/display/nr-filter-offset.h b/src/display/nr-filter-offset.h index a78450e17..1ecc1621e 100644 --- a/src/display/nr-filter-offset.h +++ b/src/display/nr-filter-offset.h @@ -15,7 +15,6 @@ #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" namespace Inkscape { namespace Filters { @@ -26,8 +25,10 @@ public: static FilterPrimitive *create(); virtual ~FilterOffset(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); void set_dx(double amount); void set_dy(double amount); diff --git a/src/display/nr-filter-pixops.h b/src/display/nr-filter-pixops.h deleted file mode 100644 index afd9e98bd..000000000 --- a/src/display/nr-filter-pixops.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __NR_FILTER_PIXOPS_H__ -#define __NR_FILTER_PIXOPS_H__ - -#include "libnr/nr-pixblock.h" -#include <algorithm> - -/* - * Per-pixel image manipulation functions. - * These can be used by all filter primitives, which combine two images on - * per-pixel basis. These are at least feBlend, feComposite and feMerge. - * - * Authors: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2007 authors - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -namespace Inkscape { -namespace Filters { - -/** - * Mixes the two input images using the function given as template. - * The result is placed in out. - * The mixing function should have the following type: - * void mix(unsigned char *result, unsigned char const *in1, - * unsigned char const *in2); - * Each of the parameters for mix-function is a pointer to four bytes of data, - * giving the RGBA values for that pixel. The mix function must only access - * the four bytes beginning at a pointer given as parameter. - */ -/* - * The implementation is in a header file because of the template. It has to - * be in the same compilation unit as the code using it. Otherwise, linking - * the program will not succeed. - */ -template <void(*blend)(unsigned char *cr, unsigned char const *ca, unsigned char const *cb)> -void pixops_mix(NRPixBlock &out, NRPixBlock &in1, NRPixBlock &in2) { - unsigned char *in1_data = NR_PIXBLOCK_PX(&in1); - unsigned char *in2_data = NR_PIXBLOCK_PX(&in2); - unsigned char *out_data = NR_PIXBLOCK_PX(&out); - unsigned char zero_rgba[4] = {0, 0, 0, 0}; - - // Possible scenarios (omitting cases where an interval is empty and those which are the same by interchanging 1 and 2): - // 01020, 01320, 01310 (no overlap, partial overlap, full overlap) - int out_y0 = out.area.y0; - int out_y1 = std::max(out.area.y1,out_y0); // Enforce sanity - int in1_y0 = std::min(std::max(in1.area.y0,out_y0),out_y1); - int in2_y0 = std::min(std::max(in2.area.y0,out_y0),out_y1); - int in1_y1 = std::min(std::max(in1.area.y1,in1_y0),out_y1); - int in2_y1 = std::min(std::max(in2.area.y1,in2_y0),out_y1); - int min_in_y0 = std::min(in1_y0,in2_y0); - int max_in_y0 = std::max(in1_y0,in2_y0); - int min_in_y1 = std::min(in1_y1,in2_y1); - int max_in_y1 = std::max(in1_y1,in2_y1); - int const yBound[6] = {out_y0, min_in_y0, std::min(max_in_y0,min_in_y1), std::max(max_in_y0,min_in_y1), max_in_y1, out_y1}; - bool const in1_zero_y[5] = {true, in1_y0>yBound[1], in1_y0>yBound[2] || in1_y1<=yBound[2], in1_y1<=yBound[3], true}; - bool const in2_zero_y[5] = {true, in2_y0>yBound[1], in2_y0>yBound[2] || in2_y1<=yBound[2], in2_y1<=yBound[3], true}; - - int out_x0 = out.area.x0; - int out_x1 = std::max(out.area.x1,out_x0); - int in1_x0 = std::min(std::max(in1.area.x0,out_x0),out_x1); - int in2_x0 = std::min(std::max(in2.area.x0,out_x0),out_x1); - int in1_x1 = std::min(std::max(in1.area.x1,in1_x0),out_x1); - int in2_x1 = std::min(std::max(in2.area.x1,in2_x0),out_x1); - int min_in_x0 = std::min(in1_x0,in2_x0); - int max_in_x0 = std::max(in1_x0,in2_x0); - int min_in_x1 = std::min(in1_x1,in2_x1); - int max_in_x1 = std::max(in1_x1,in2_x1); - int const xBound[6] = {out_x0, min_in_x0, std::min(max_in_x0,min_in_x1), std::max(max_in_x0,min_in_x1), max_in_x1, out_x1}; - bool const in1_zero_x[5] = {true, in1_x0>xBound[1], in1_x0>xBound[2] || in1_x1<=xBound[2], in1_x1<=xBound[3], true}; - bool const in2_zero_x[5] = {true, in2_x0>xBound[1], in2_x0>xBound[2] || in2_x1<=xBound[2], in2_x1<=xBound[3], true}; - - for (int yr = 0 ; yr < 5 ; yr++) { - for(int y = yBound[yr] ; y < yBound[yr+1] ; y++) { - int out_line = (y - out.area.y0) * out.rs; - int in1_line = (y - in1.area.y0) * in1.rs; - int in2_line = (y - in2.area.y0) * in2.rs; - for (int xr = 0 ; xr < 5 ; xr++) { - for(int x = xBound[xr] ; x < xBound[xr+1] ; x++) { - blend(out_data + out_line + 4 * (x - out.area.x0), - (in1_zero_x[xr]||in1_zero_y[yr]) ? zero_rgba : (in1_data + in1_line + 4 * (x - in1.area.x0)), - (in2_zero_x[xr]||in2_zero_y[yr]) ? zero_rgba : (in2_data + in2_line + 4 * (x - in2.area.x0))); - } - } - } - } -} - -} /* namespace Filters */ -} /* namespace Inkscape */ - -#endif // __NR_FILTER_PIXOPS_H_ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp index 1af86ec3e..c6bd8a74e 100644 --- a/src/display/nr-filter-primitive.cpp +++ b/src/display/nr-filter-primitive.cpp @@ -1,5 +1,3 @@ -#define __NR_FILTER_PRIMITIVE_CPP__ - /* * SVG filters rendering * @@ -13,8 +11,8 @@ */ #include "display/nr-filter-primitive.h" +#include "display/nr-filter-slot.h" #include "display/nr-filter-types.h" -#include "libnr/nr-pixblock.h" #include "svg/svg-length.h" namespace Inkscape { @@ -48,7 +46,14 @@ FilterPrimitive::~FilterPrimitive() // Nothing to do here } -void FilterPrimitive::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*m*/) +void FilterPrimitive::render_cairo(FilterSlot &slot) +{ + // passthrough + cairo_surface_t *in = slot.getcairo(_input); + slot.set(_output, in); +} + +void FilterPrimitive::area_enlarge(Geom::IntRect &/*area*/, Geom::Affine const &/*m*/) { // This doesn't need to do anything by default } @@ -156,10 +161,6 @@ Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) return area; } -FilterTraits FilterPrimitive::get_input_traits() { - return TRAIT_ANYTHING; -} - } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h index 0e8df3813..da2097156 100644 --- a/src/display/nr-filter-primitive.h +++ b/src/display/nr-filter-primitive.h @@ -1,6 +1,3 @@ -#ifndef __NR_FILTER_PRIMITIVE_H__ -#define __NR_FILTER_PRIMITIVE_H__ - /* * SVG filters rendering * @@ -11,40 +8,28 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ +#ifndef SEEN_NR_FILTER_PRIMITIVE_H +#define SEEN_NR_FILTER_PRIMITIVE_H -#include "display/nr-filter-slot.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-rect-l.h" +#include <2geom/forward.h> +#include <2geom/rect.h> +#include "display/nr-filter-types.h" #include "svg/svg-length.h" namespace Inkscape { namespace Filters { -/* - * Different filter effects need different types of inputs. This is what - * traits are used for: one can specify, what special restrictions - * there are for inputs. - * - * Example: gaussian blur requires that x- and y-axis of input image - * are paraller to blurred object's x- and y-axis, respectively. - * Otherwise blur wouldn't rotate with the object. - * - * Values here should be powers of two, so these can be used as bitfield. - * That is: any combination ef existing traits can be specified. (excluding - * TRAIT_ANYTHING, which is alias for no traits defined) - */ -enum FilterTraits { - TRAIT_ANYTHING = 0, - TRAIT_PARALLER = 1 -}; +class FilterSlot; +class FilterUnits; class FilterPrimitive { public: FilterPrimitive(); virtual ~FilterPrimitive(); - virtual int render(FilterSlot &slot, FilterUnits const &units) = 0; - virtual void area_enlarge(NRRectL &area, Geom::Affine const &m); + virtual void render_cairo(FilterSlot &slot); + virtual int render(FilterSlot & /*slot*/, FilterUnits const & /*units*/) { return 0; } + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &m); /** * Sets the input slot number 'slot' to be used as input in rendering @@ -78,6 +63,18 @@ public: */ virtual void set_output(int slot); + // returns cache score factor, reflecting the cost of rendering this filter + // this should return how many times slower this primitive is that normal rendering + virtual double complexity(Geom::Affine const &/*ctm*/) { return 1.0; } + + virtual bool uses_background() { + if (_input == NR_FILTER_BACKGROUNDIMAGE || _input == NR_FILTER_BACKGROUNDALPHA) { + return true; + } else { + return false; + } + } + /** * Sets the filter primitive subregion. Passing an unset length * (length._set == false) WILL change the parameter as it is @@ -101,12 +98,20 @@ public: Geom::Rect filter_primitive_area(FilterUnits const &units); /** - * Queries the filter, which traits it needs from its input buffers. - * At the time of writing this, only one trait was needed, having - * user coordinate system and input pixelblock coordinates paraller to - * each other. + *Indicate whether the filter primitive can handle the given affine. + * + * Results of some filter primitives depend on the coordinate system used when rendering. + * A gaussian blur with equal x and y deviation will remain unchanged by rotations. + * Per-pixel filters like color matrix and blend will not change regardless of + * the transformation. + * + * When any filter returns false, filter rendering is performed on an intermediate surface + * with edges parallel to the axes of the user coordinate system. This means + * the matrices from FilterUnits will contain at most a (possibly non-uniform) scale + * and a translation. When all primitives of the filter return true, the rendering is + * performed in display coordinate space and no intermediate surface is used. */ - virtual FilterTraits get_input_traits(); + virtual bool can_handle_affine(Geom::Affine const &) { return false; } protected: int _input; diff --git a/src/display/nr-filter-skeleton.cpp b/src/display/nr-filter-skeleton.cpp index c17bee1d4..0c455a818 100644 --- a/src/display/nr-filter-skeleton.cpp +++ b/src/display/nr-filter-skeleton.cpp @@ -22,9 +22,9 @@ */ #include "display/nr-filter-skeleton.h" +#include "display/cairo-utils.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-pixblock.h" namespace Inkscape { namespace Filters { @@ -39,17 +39,17 @@ FilterPrimitive * FilterSkeleton::create() { FilterSkeleton::~FilterSkeleton() {} -int FilterSkeleton::render(FilterSlot &slot, - FilterUnits const &/*units*/) { - //NRPixBlock *in = slot.get(_input); - NRPixBlock *out = new NRPixBlock(); +void FilterSkeleton::render_cairo(FilterSlot &slot) { + cairo_surface_t *in = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_identical(in); + cairo_t *ct = cairo_create(out); - /* Insert rendering code here */ +// cairo_set_source_surface(ct, in, offset[X], offset[Y]); +// cairo_paint(ct); +// cairo_destroy(ct); - out->empty = FALSE; slot.set(_output, out); - - return 0; + cairo_surface_destroy(out); } } /* namespace Filters */ diff --git a/src/display/nr-filter-skeleton.h b/src/display/nr-filter-skeleton.h index a03004be1..049c0df80 100644 --- a/src/display/nr-filter-skeleton.h +++ b/src/display/nr-filter-skeleton.h @@ -37,7 +37,7 @@ public: static FilterPrimitive *create(); virtual ~FilterSkeleton(); - virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual void render_cairo(FilterSlot &slot); private: diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index b019b194f..4f7a8849e 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -14,317 +14,221 @@ #include <assert.h> #include <string.h> -#include "display/nr-arena-item.h" +#include <2geom/transforms.h> +#include "display/cairo-utils.h" +#include "display/drawing-context.h" #include "display/nr-filter-types.h" #include "display/nr-filter-gaussian.h" #include "display/nr-filter-slot.h" -#include "display/nr-filter-getalpha.h" #include "display/nr-filter-units.h" -#include "display/pixblock-scaler.h" -#include "display/pixblock-transform.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" - -__attribute__ ((const)) -inline static int _max4(const double a, const double b, - const double c, const double d) { - double ret = a; - if (b > ret) ret = b; - if (c > ret) ret = c; - if (d > ret) ret = d; - return (int)round(ret); -} - -__attribute__ ((const)) -inline static int _min4(const double a, const double b, - const double c, const double d) { - double ret = a; - if (b < ret) ret = b; - if (c < ret) ret = c; - if (d < ret) ret = d; - return (int)round(ret); -} - -__attribute__ ((const)) -inline static int _max2(const double a, const double b) { - if (a > b) - return (int)round(a); - else - return (int)round(b); -} - -__attribute__ ((const)) -inline static int _min2(const double a, const double b) { - if (a > b) - return (int)round(b); - else - return (int)round(a); -} namespace Inkscape { namespace Filters { -FilterSlot::FilterSlot(int slots, NRArenaItem const *item) - : _last_out(-1), - filterquality(FILTER_QUALITY_BEST), - blurquality(BLUR_QUALITY_BEST), - _arena_item(item) +FilterSlot::FilterSlot(DrawingItem *item, DrawingContext *bgct, + DrawingContext &graphic, FilterUnits const &u) + : _item(item) + , _source_graphic(graphic.rawTarget()) + , _background_ct(bgct ? bgct->raw() : NULL) + , _source_graphic_area(graphic.targetLogicalBounds().roundOutwards()) // fixme + , _background_area(bgct ? bgct->targetLogicalBounds().roundOutwards() : Geom::IntRect()) // fixme + , _units(u) + , _last_out(NR_FILTER_SOURCEGRAPHIC) + , filterquality(FILTER_QUALITY_BEST) + , blurquality(BLUR_QUALITY_BEST) { - _slots.reserve((slots > 0) ? slots : 2); + using Geom::X; + using Geom::Y; + + // compute slot bbox + Geom::Affine trans = _units.get_matrix_display2pb(); + Geom::Rect bbox_trans = graphic.targetLogicalBounds() * trans; + Geom::Point min = bbox_trans.min(); + _slot_x = min[X]; + _slot_y = min[Y]; + + if (trans.isTranslation()) { + _slot_w = _source_graphic_area.width(); + _slot_h = _source_graphic_area.height(); + } else { + _slot_w = ceil(bbox_trans.width()); + _slot_h = ceil(bbox_trans.height()); + } } FilterSlot::~FilterSlot() { - for (unsigned int i = 0 ; i < _slots.size() ; i++) { - if (_slots[i].owned) { - nr_pixblock_release(_slots[i].pb); - delete _slots[i].pb; - } + for (SlotMap::iterator i = _slots.begin(); i != _slots.end(); ++i) { + cairo_surface_destroy(i->second); } } -FilterSlot::slot_entry_t::~slot_entry_t() +cairo_surface_t *FilterSlot::getcairo(int slot_nr) { - // It's a bad idea to destruct pixblocks here, as this will also be called upon resizing _slots -} + if (slot_nr == NR_FILTER_SLOT_NOT_SET) + slot_nr = _last_out; -NRPixBlock *FilterSlot::get(int slot_nr) -{ - int index = _get_index(slot_nr); - assert(index >= 0); + SlotMap::iterator s = _slots.find(slot_nr); /* If we didn't have the specified image, but we could create it * from the other information we have, let's do that */ - if (_slots[index].pb == NULL - && (slot_nr == NR_FILTER_SOURCEALPHA + if (s == _slots.end() + && (slot_nr == NR_FILTER_SOURCEGRAPHIC + || slot_nr == NR_FILTER_SOURCEALPHA || slot_nr == NR_FILTER_BACKGROUNDIMAGE || slot_nr == NR_FILTER_BACKGROUNDALPHA || slot_nr == NR_FILTER_FILLPAINT || slot_nr == NR_FILTER_STROKEPAINT)) { - /* If needed, fetch background */ - if (slot_nr == NR_FILTER_BACKGROUNDIMAGE) { - NRPixBlock *pb; - pb = nr_arena_item_get_background(_arena_item); - if (pb) { - pb->empty = false; - this->set(NR_FILTER_BACKGROUNDIMAGE, pb, false); - } else { - NRPixBlock *source = this->get(NR_FILTER_SOURCEGRAPHIC); - pb = new NRPixBlock(); - if (!pb) return NULL; // Allocation failed - nr_pixblock_setup_fast(pb, source->mode, - source->area.x0, source->area.y0, - source->area.x1, source->area.y1, true); - if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) { - // allocation failed - delete pb; - return NULL; - } - pb->empty = FALSE; - this->set(NR_FILTER_BACKGROUNDIMAGE, pb); - } - } else if (slot_nr == NR_FILTER_SOURCEALPHA) { - /* If only a alpha channel is needed, strip it from full image */ - NRPixBlock *src = get(NR_FILTER_SOURCEGRAPHIC); - NRPixBlock *sa = filter_get_alpha(src); - set(NR_FILTER_SOURCEALPHA, sa); - } else if (slot_nr == NR_FILTER_BACKGROUNDALPHA) { - NRPixBlock *src = get(NR_FILTER_BACKGROUNDIMAGE); - NRPixBlock *ba = filter_get_alpha(src); - set(NR_FILTER_BACKGROUNDALPHA, ba); - } else if (slot_nr == NR_FILTER_FILLPAINT) { - /* When a paint is needed, fetch it from arena item */ - // TODO - } else if (slot_nr == NR_FILTER_STROKEPAINT) { - // TODO + switch (slot_nr) { + case NR_FILTER_SOURCEGRAPHIC: { + cairo_surface_t *tr = _get_transformed_source_graphic(); + _set_internal(NR_FILTER_SOURCEGRAPHIC, tr); + cairo_surface_destroy(tr); + } break; + case NR_FILTER_BACKGROUNDIMAGE: { + cairo_surface_t *bg = _get_transformed_background(); + _set_internal(NR_FILTER_BACKGROUNDIMAGE, bg); + cairo_surface_destroy(bg); + } break; + case NR_FILTER_SOURCEALPHA: { + cairo_surface_t *src = getcairo(NR_FILTER_SOURCEGRAPHIC); + cairo_surface_t *alpha = ink_cairo_extract_alpha(src); + _set_internal(NR_FILTER_SOURCEALPHA, alpha); + cairo_surface_destroy(alpha); + } break; + case NR_FILTER_BACKGROUNDALPHA: { + cairo_surface_t *src = getcairo(NR_FILTER_BACKGROUNDIMAGE); + cairo_surface_t *ba = ink_cairo_extract_alpha(src); + _set_internal(NR_FILTER_BACKGROUNDALPHA, ba); + cairo_surface_destroy(ba); + } break; + case NR_FILTER_FILLPAINT: //TODO + case NR_FILTER_STROKEPAINT: //TODO + default: + break; } + s = _slots.find(slot_nr); } - if (_slots[index].pb) { - _slots[index].pb->empty = false; + if (s == _slots.end()) { + // create empty surface + cairo_surface_t *empty = cairo_surface_create_similar( + _source_graphic, cairo_surface_get_content(_source_graphic), + _slot_w, _slot_h); + _set_internal(slot_nr, empty); + cairo_surface_destroy(empty); + s = _slots.find(slot_nr); } - - assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slots[index].number == slot_nr); - return _slots[index].pb; + return s->second; } -void FilterSlot::get_final(int slot_nr, NRPixBlock *result) { - NRPixBlock *final_usr = get(slot_nr); - Geom::Affine trans = units.get_matrix_pb2display(); - - int size = (result->area.x1 - result->area.x0) - * (result->area.y1 - result->area.y0) - * NR_PIXBLOCK_BPP(result); - memset(NR_PIXBLOCK_PX(result), 0, size); +cairo_surface_t *FilterSlot::_get_transformed_source_graphic() +{ + Geom::Affine trans = _units.get_matrix_display2pb(); - if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) { - if (filterquality == FILTER_QUALITY_BEST) { - NR::transform_bicubic(result, final_usr, trans); - } else { - NR::transform_nearest(result, final_usr, trans); - } - } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) { - NR::scale_bicubic(result, final_usr, trans); - } else { - nr_blit_pixblock_pixblock(result, final_usr); + if (trans.isTranslation()) { + cairo_surface_reference(_source_graphic); + return _source_graphic; } + + cairo_surface_t *tsg = cairo_surface_create_similar( + _source_graphic, cairo_surface_get_content(_source_graphic), + _slot_w, _slot_h); + cairo_t *tsg_ct = cairo_create(tsg); + + cairo_translate(tsg_ct, -_slot_x, -_slot_y); + ink_cairo_transform(tsg_ct, trans); + cairo_translate(tsg_ct, _source_graphic_area.left(), _source_graphic_area.top()); + cairo_set_source_surface(tsg_ct, _source_graphic, 0, 0); + cairo_set_operator(tsg_ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(tsg_ct); + cairo_destroy(tsg_ct); + + return tsg; } -void FilterSlot::set(int slot_nr, NRPixBlock *pb, bool takeOwnership) +cairo_surface_t *FilterSlot::_get_transformed_background() { - /* Unnamed slot is for saving filter primitive results, when parameter - * 'result' is not set. Only the filter immediately after this one - * can access unnamed results, so we don't have to worry about overwriting - * previous results in filter chain. On the other hand, we may not - * overwrite any other image with this one, because they might be - * accessed later on. */ - int index = ((slot_nr != NR_FILTER_SLOT_NOT_SET) - ? _get_index(slot_nr) - : _get_index(NR_FILTER_UNNAMED_SLOT)); - assert(index >= 0); - // Unnamed slot is only for Inkscape::Filters::FilterSlot internal use. - assert(slot_nr != NR_FILTER_UNNAMED_SLOT); - assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slots[index].number == slot_nr); - - if (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_BACKGROUNDIMAGE) { - Geom::Affine trans = units.get_matrix_display2pb(); - if (fabs(trans[1]) > 1e-6 || fabs(trans[2]) > 1e-6) { - NRPixBlock *trans_pb = new NRPixBlock; - int x0 = pb->area.x0; - int y0 = pb->area.y0; - int x1 = pb->area.x1; - int y1 = pb->area.y1; - int min_x = _min4(trans[0] * x0 + trans[2] * y0 + trans[4], - trans[0] * x0 + trans[2] * y1 + trans[4], - trans[0] * x1 + trans[2] * y0 + trans[4], - trans[0] * x1 + trans[2] * y1 + trans[4]); - int max_x = _max4(trans[0] * x0 + trans[2] * y0 + trans[4], - trans[0] * x0 + trans[2] * y1 + trans[4], - trans[0] * x1 + trans[2] * y0 + trans[4], - trans[0] * x1 + trans[2] * y1 + trans[4]); - int min_y = _min4(trans[1] * x0 + trans[3] * y0 + trans[5], - trans[1] * x0 + trans[3] * y1 + trans[5], - trans[1] * x1 + trans[3] * y0 + trans[5], - trans[1] * x1 + trans[3] * y1 + trans[5]); - int max_y = _max4(trans[1] * x0 + trans[3] * y0 + trans[5], - trans[1] * x0 + trans[3] * y1 + trans[5], - trans[1] * x1 + trans[3] * y0 + trans[5], - trans[1] * x1 + trans[3] * y1 + trans[5]); - - nr_pixblock_setup_fast(trans_pb, pb->mode, - min_x, min_y, - max_x, max_y, true); - if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { - /* TODO: this gets hit occasionally. Worst case scenario: - * images are exported in horizontal stripes. One stripe - * is not too high, but can get thousands of pixels wide. - * Rotate this 45 degrees -> _huge_ image */ - g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (transform)"); - if (takeOwnership) { - nr_pixblock_release(pb); - delete pb; - } - return; - } - if (filterquality == FILTER_QUALITY_BEST) { - NR::transform_bicubic(trans_pb, pb, trans); - } else { - NR::transform_nearest(trans_pb, pb, trans); - } - if (takeOwnership) { - nr_pixblock_release(pb); - delete pb; - } - takeOwnership = true; - pb = trans_pb; - } else if (fabs(trans[0] - 1) > 1e-6 || fabs(trans[3] - 1) > 1e-6) { - NRPixBlock *trans_pb = new NRPixBlock; - - int x0 = pb->area.x0; - int y0 = pb->area.y0; - int x1 = pb->area.x1; - int y1 = pb->area.y1; - int min_x = _min2(trans[0] * x0 + trans[4], - trans[0] * x1 + trans[4]); - int max_x = _max2(trans[0] * x0 + trans[4], - trans[0] * x1 + trans[4]); - int min_y = _min2(trans[3] * y0 + trans[5], - trans[3] * y1 + trans[5]); - int max_y = _max2(trans[3] * y0 + trans[5], - trans[3] * y1 + trans[5]); - - nr_pixblock_setup_fast(trans_pb, pb->mode, - min_x, min_y, max_x, max_y, true); - if (trans_pb->size != NR_PIXBLOCK_SIZE_TINY && trans_pb->data.px == NULL) { - g_warning("Memory allocation failed in Inkscape::Filters::FilterSlot::set (scaling)"); - if (takeOwnership) { - nr_pixblock_release(pb); - delete pb; - } - return; - } - NR::scale_bicubic(trans_pb, pb, trans); - if (takeOwnership) { - nr_pixblock_release(pb); - delete pb; - } - takeOwnership = true; - pb = trans_pb; - } + Geom::Affine trans = _units.get_matrix_display2pb(); + + cairo_surface_t *tbg; + + if (_background_ct) { + cairo_surface_t *bg = cairo_get_group_target(_background_ct); + tbg = cairo_surface_create_similar( + bg, cairo_surface_get_content(bg), + _slot_w, _slot_h); + cairo_t *tbg_ct = cairo_create(tbg); + + cairo_translate(tbg_ct, -_slot_x, -_slot_y); + ink_cairo_transform(tbg_ct, trans); + cairo_translate(tbg_ct, _background_area.left(), _background_area.top()); + cairo_set_source_surface(tbg_ct, bg, 0, 0); + cairo_set_operator(tbg_ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(tbg_ct); + cairo_destroy(tbg_ct); + } else { + tbg = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _slot_w, _slot_h); } - if(_slots[index].owned) { - nr_pixblock_release(_slots[index].pb); - delete _slots[index].pb; - } - _slots[index].pb = pb; - _slots[index].owned = takeOwnership; - _last_out = index; + return tbg; } -int FilterSlot::get_slot_count() +cairo_surface_t *FilterSlot::get_result(int res) { - return _slots.size(); + Geom::Affine trans = _units.get_matrix_pb2display(); + if (trans.isIdentity()) { + cairo_surface_t *result = getcairo(res); + cairo_surface_reference(result); + return result; + } + + cairo_surface_t *r = cairo_surface_create_similar(_source_graphic, + cairo_surface_get_content(_source_graphic), + _source_graphic_area.width(), + _source_graphic_area.height()); + cairo_t *r_ct = cairo_create(r); + + cairo_translate(r_ct, -_source_graphic_area.left(), -_source_graphic_area.top()); + ink_cairo_transform(r_ct, trans); + cairo_translate(r_ct, _slot_x, _slot_y); + cairo_set_source_surface(r_ct, getcairo(res), 0, 0); + cairo_set_operator(r_ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(r_ct); + cairo_destroy(r_ct); + + return r; } -NRArenaItem const* FilterSlot::get_arenaitem() +void FilterSlot::_set_internal(int slot_nr, cairo_surface_t *surface) { - return _arena_item; + // destroy after referencing + // this way assigning a surface to a slot it already occupies will not cause errors + cairo_surface_reference(surface); + + SlotMap::iterator s = _slots.find(slot_nr); + if (s != _slots.end()) { + cairo_surface_destroy(s->second); + } + + _slots[slot_nr] = surface; } -int FilterSlot::_get_index(int slot_nr) +void FilterSlot::set(int slot_nr, cairo_surface_t *surface) { - assert(slot_nr >= 0 || - slot_nr == NR_FILTER_SLOT_NOT_SET || - slot_nr == NR_FILTER_SOURCEGRAPHIC || - slot_nr == NR_FILTER_SOURCEALPHA || - slot_nr == NR_FILTER_BACKGROUNDIMAGE || - slot_nr == NR_FILTER_BACKGROUNDALPHA || - slot_nr == NR_FILTER_FILLPAINT || - slot_nr == NR_FILTER_STROKEPAINT || - slot_nr == NR_FILTER_UNNAMED_SLOT); - - if (slot_nr == NR_FILTER_SLOT_NOT_SET) { - return _last_out; - } + g_return_if_fail(surface != NULL); - /* Search, if the slot already exists */ - for (int i = 0 ; i < (int)_slots.size() ; i++) { - if (_slots[i].number == slot_nr) { - return i; - } - } + if (slot_nr == NR_FILTER_SLOT_NOT_SET) + slot_nr = NR_FILTER_UNNAMED_SLOT; - /* If the slot doesn't already exist, create it */ - slot_entry_t entry; - entry.number = slot_nr; - _slots.push_back(entry); - return (int)_slots.size()-1; + _set_internal(slot_nr, surface); + _last_out = slot_nr; } -void FilterSlot::set_units(FilterUnits const &units) { - this->units = units; +int FilterSlot::get_slot_count() +{ + return _slots.size(); } void FilterSlot::set_quality(FilterQuality const q) { @@ -339,6 +243,13 @@ int FilterSlot::get_blurquality(void) { return blurquality; } +Geom::Rect FilterSlot::get_slot_area() const { + Geom::Point p(_slot_x, _slot_y); + Geom::Point dim(_slot_w, _slot_h); + Geom::Rect r(p, p+dim); + return r; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-slot.h b/src/display/nr-filter-slot.h index 871d80a8e..805027bfe 100644 --- a/src/display/nr-filter-slot.h +++ b/src/display/nr-filter-slot.h @@ -14,26 +14,22 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "libnr/nr-pixblock.h" +#include <map> +#include <cairo.h> #include "display/nr-filter-types.h" #include "display/nr-filter-units.h" -#include <vector> - -struct NRArenaItem; namespace Inkscape { +class DrawingContext; +class DrawingItem; + namespace Filters { class FilterSlot { public: - /** Creates a new FilterSlot object. - * First parameter specifies the amount of slots this SilterSlot - * should reserve beforehand. If a negative number is given, - * two slots will be reserved. - * Second parameter specifies the arena item, which should be used - * for background accesses from filters. - */ - FilterSlot(int slots, NRArenaItem const *item); + /** Creates a new FilterSlot object. */ + FilterSlot(DrawingItem *item, DrawingContext *bgct, + DrawingContext &graphic, FilterUnits const &u); /** Destroys the FilterSlot object and all its contents */ virtual ~FilterSlot(); @@ -43,37 +39,22 @@ public: * NR_FILTER_SOURCEGRAPHIC, NR_FILTER_SOURCEALPHA, * NR_FILTER_BACKGROUNDIMAGE, NR_FILTER_BACKGROUNDALPHA, * NR_FILTER_FILLPAINT, NR_FILTER_SOURCEPAINT. - * If the defined filter slot is not set before, this function - * returns NULL. Also, that filter slot is created in process. */ - NRPixBlock *get(int slot); - - /** Gets the final result from this filter. - * The result is fetched from the specified slot, see description of - * method get for valid values. The pixblock 'result' will be modified - * to contain the result image, ready to be used in the rest of rendering - * pipeline - */ - void get_final(int slot, NRPixBlock *result); + cairo_surface_t *getcairo(int slot); /** Sets or re-sets the pixblock associated with given slot. * If there was a pixblock already assigned with this slot, * that pixblock is destroyed. - * Pixblocks passed to this function should be considered - * managed by this FilterSlot object if takeOwnership==true. - * Pixblocks passed to this function should be reserved with - * c++ -style new-operator (if managed by FilterSlot). */ - void set(int slot, NRPixBlock *pb, bool takeOwnership=true); + void set(int slot, cairo_surface_t *s); + + cairo_surface_t *get_result(int slot_nr); /** Returns the number of slots in use. */ int get_slot_count(); - /** arenaitem getter method*/ - NRArenaItem const* get_arenaitem(); - /** Sets the unit system to be used for the internal images. */ - void set_units(FilterUnits const &units); + //void set_units(FilterUnits const &units); /** Sets the filtering quality. Affects used interpolation methods */ void set_quality(FilterQuality const q); @@ -84,30 +65,34 @@ public: /** Gets the gaussian filtering quality. Affects used interpolation methods */ int get_blurquality(void); -private: - struct slot_entry_t { - NRPixBlock* pb; - int number; - bool owned; - slot_entry_t() : pb(0), number(NR_FILTER_SLOT_NOT_SET), owned(false) {} - ~slot_entry_t(); - }; - std::vector<slot_entry_t> _slots; + FilterUnits const &get_units() const { return _units; } + Geom::Rect get_slot_area() const; +private: + typedef std::map<int, cairo_surface_t *> SlotMap; + SlotMap _slots; + DrawingItem *_item; + + //Geom::Rect _source_bbox; ///< bounding box of source graphic surface + //Geom::Rect _intermediate_bbox; ///< bounding box of intermediate surfaces + + int _slot_w, _slot_h; + double _slot_x, _slot_y; + cairo_surface_t *_source_graphic; + cairo_t *_background_ct; + Geom::IntRect _source_graphic_area; + Geom::IntRect _background_area; ///< needed to extract background + FilterUnits const &_units; int _last_out; - FilterQuality filterquality; - int blurquality; - NRArenaItem const *_arena_item; - - FilterUnits units; + cairo_surface_t *_get_transformed_source_graphic(); + cairo_surface_t *_get_transformed_background(); + cairo_surface_t *_get_fill_paint(); + cairo_surface_t *_get_stroke_paint(); - /** Returns the table index of given slot. If that slot does not exist, - * it is created. Table index can be used to read the correct - * pixblock from _slot */ - int _get_index(int slot); + void _set_internal(int slot, cairo_surface_t *s); }; } /* namespace Filters */ diff --git a/src/display/nr-filter-specularlighting.cpp b/src/display/nr-filter-specularlighting.cpp index 8ecee24e6..0242754eb 100644 --- a/src/display/nr-filter-specularlighting.cpp +++ b/src/display/nr-filter-specularlighting.cpp @@ -10,21 +10,17 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib/gmessages.h> +#include <glib.h> #include <cmath> +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-3dutils.h" -#include "display/nr-arena-item.h" #include "display/nr-filter-specularlighting.h" -#include "display/nr-filter-getalpha.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" #include "display/nr-light.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-rect-l.h" -#include "color.h" namespace Inkscape { namespace Filters { @@ -45,164 +41,147 @@ FilterPrimitive * FilterSpecularLighting::create() { FilterSpecularLighting::~FilterSpecularLighting() {} -//Investigating Phong Lighting model we should not take N.H but -//R.E which equals to 2*N.H^2 - 1 -//replace the second line by -//gdouble scal = scalar_product((N), (H)); scal = 2 * scal * scal - 1; -//to get the expected formula -#define COMPUTE_INTER(inter, H, N, ks, speculaExponent) \ -do {\ - gdouble scal = NR::scalar_product((N), (H)); \ - if (scal <= 0)\ - (inter) = 0;\ - else\ - (inter) = (ks) * std::pow(scal, (specularExponent));\ -}while(0) - -int FilterSpecularLighting::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feSpecularLighting (in=%d)", _input); - return 1; +struct SpecularLight : public SurfaceSynth { + SpecularLight(cairo_surface_t *bumpmap, double scale, double specular_constant, + double specular_exponent) + : SurfaceSynth(bumpmap) + , _scale(scale) + , _ks(specular_constant) + , _exp(specular_exponent) + {} +protected: + guint32 specularLighting(int x, int y, NR::Fvector const &halfway, NR::Fvector const &light_components) { + NR::Fvector normal = surfaceNormalAt(x, y, _scale); + double sp = NR::scalar_product(normal, halfway); + double k = sp <= 0.0 ? 0.0 : _ks * pow(sp, _exp); + + guint32 r = CLAMP_D_TO_U8(k * light_components[LIGHT_RED]); + guint32 g = CLAMP_D_TO_U8(k * light_components[LIGHT_GREEN]); + guint32 b = CLAMP_D_TO_U8(k * light_components[LIGHT_BLUE]); + guint32 a = std::max(std::max(r, g), b); + + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + + ASSEMBLE_ARGB32(pxout, a,r,g,b) + return pxout; } + double _scale, _ks, _exp; +}; + +struct SpecularDistantLight : public SpecularLight { + SpecularDistantLight(cairo_surface_t *bumpmap, SPFeDistantLight *light, guint32 color, + double scale, double specular_constant, double specular_exponent) + : SpecularLight(bumpmap, scale, specular_constant, specular_exponent) + { + DistantLight dl(light, color); + NR::Fvector lv; + dl.light_vector(lv); + dl.light_components(_light_components); + NR::normalized_sum(_halfway, lv, NR::EYE_VECTOR); + } + guint32 operator()(int x, int y) { + return specularLighting(x, y, _halfway, _light_components); + } +private: + NR::Fvector _halfway, _light_components; +}; + +struct SpecularPointLight : public SpecularLight { + SpecularPointLight(cairo_surface_t *bumpmap, SPFePointLight *light, guint32 color, + Geom::Affine const &trans, double scale, double specular_constant, + double specular_exponent, double x0, double y0) + : SpecularLight(bumpmap, scale, specular_constant, specular_exponent) + , _light(light, color, trans) + , _x0(x0) + , _y0(y0) + { + _light.light_components(_light_components); + } + + guint32 operator()(int x, int y) { + NR::Fvector light, halfway; + _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0); + NR::normalized_sum(halfway, light, NR::EYE_VECTOR); + return specularLighting(x, y, halfway, _light_components); + } +private: + PointLight _light; + NR::Fvector _light_components; + double _x0, _y0; +}; + +struct SpecularSpotLight : public SpecularLight { + SpecularSpotLight(cairo_surface_t *bumpmap, SPFeSpotLight *light, guint32 color, + Geom::Affine const &trans, double scale, double specular_constant, + double specular_exponent, double x0, double y0) + : SpecularLight(bumpmap, scale, specular_constant, specular_exponent) + , _light(light, color, trans) + , _x0(x0) + , _y0(y0) + {} + + guint32 operator()(int x, int y) { + NR::Fvector light, halfway, light_components; + _light.light_vector(light, _x0 + x, _y0 + y, _scale * alphaAt(x, y)/255.0); + _light.light_components(light_components, light); + NR::normalized_sum(halfway, light, NR::EYE_VECTOR); + return specularLighting(x, y, halfway, light_components); + } +private: + SpotLight _light; + double _x0, _y0; +}; + +void FilterSpecularLighting::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + + Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb(); + Geom::Point p = slot.get_slot_area().min(); + double x0 = p[Geom::X]; + double y0 = p[Geom::Y]; + double scale = surfaceScale * trans.descrim(); + double ks = specularConstant; + double se = specularExponent; - NRPixBlock *out = new NRPixBlock; - - //Fvector *L = NULL; //vector to the light - - int w = in->area.x1 - in->area.x0; - int h = in->area.y1 - in->area.y0; - int x0 = in->area.x0; - int y0 = in->area.y0; - int i, j; - //As long as FilterRes and kernel unit is not supported we hardcode the - //default value - int dx = 1; //TODO setup - int dy = 1; //TODO setup - //surface scale - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); - gdouble ss = surfaceScale * trans[0]; - gdouble ks = specularConstant; //diffuse lighting constant - NR::Fvector L, N, LC, H; - gdouble inter; - - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - unsigned char *data_i = NR_PIXBLOCK_PX (in); - unsigned char *data_o = NR_PIXBLOCK_PX (out); - //No light, nothing to do switch (light_type) { - case DISTANT_LIGHT: - //the light vector is constant - { - DistantLight *dl = new DistantLight(light.distant, lighting_color); - dl->light_vector(L); - dl->light_components(LC); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding! - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete dl; - } - break; - case POINT_LIGHT: - { - PointLight *pl = new PointLight(light.point, lighting_color, trans); - pl->light_components(LC); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - pl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete pl; - } - break; - case SPOT_LIGHT: - { - SpotLight *sl = new SpotLight(light.spot, lighting_color, trans); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - sl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - sl->light_components(LC, L); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete sl; - } - break; - //else unknown light source, doing nothing - case NO_LIGHT: - default: - { - if (light_type != NO_LIGHT) - g_warning("unknown light source %d", light_type); - out->empty = false; - } + case DISTANT_LIGHT: + ink_cairo_surface_synthesize(out, + SpecularDistantLight(input, light.distant, lighting_color, scale, ks, se)); + break; + case POINT_LIGHT: + ink_cairo_surface_synthesize(out, + SpecularPointLight(input, light.point, lighting_color, trans, scale, ks, se, x0, y0)); + break; + case SPOT_LIGHT: + ink_cairo_surface_synthesize(out, + SpecularSpotLight(input, light.spot, lighting_color, trans, scale, ks, se, x0, y0)); + break; + default: { + cairo_t *ct = cairo_create(out); + cairo_set_source_rgba(ct, 0,0,0,1); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_destroy(ct); + } break; } - //finishing slot.set(_output, out); - //nr_pixblock_release(in); - //delete in; - return 0; + cairo_surface_destroy(out); } -void FilterSpecularLighting::area_enlarge(NRRectL &area, Geom::Affine const &trans) +void FilterSpecularLighting::area_enlarge(Geom::IntRect &area, Geom::Affine const & /*trans*/) { // TODO: support kernelUnitLength - double scalex = std::fabs(trans[0]) + std::fabs(trans[1]); - double scaley = std::fabs(trans[2]) + std::fabs(trans[3]); - - //FIXME: no +2 should be there!... (noticable only for big scales at big zoom factor) - area.x0 -= (int)(scalex) + 2; - area.x1 += (int)(scalex) + 2; - area.y0 -= (int)(scaley) + 2; - area.y1 += (int)(scaley) + 2; + area.expandBy(1); } -FilterTraits FilterSpecularLighting::get_input_traits() { - return TRAIT_PARALLER; +double FilterSpecularLighting::complexity(Geom::Affine const &) +{ + return 9.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-specularlighting.h b/src/display/nr-filter-specularlighting.h index a5d29588a..33ea17a87 100644 --- a/src/display/nr-filter-specularlighting.h +++ b/src/display/nr-filter-specularlighting.h @@ -13,21 +13,29 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gdk/gdktypes.h> +#include <gdk/gdk.h> #include "display/nr-light-types.h" #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include "filters/distantlight.h" -#include "filters/pointlight.h" -#include "filters/spotlight.h" -#include "color.h" + +class SPFeDistantLight; +class SPFePointLight; +class SPFeSpotLight; namespace Inkscape { namespace Filters { - + +class FilterSlot; + class FilterSpecularLighting : public FilterPrimitive { public: + FilterSpecularLighting(); + static FilterPrimitive *create(); + virtual ~FilterSpecularLighting(); + + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + union { SPFeDistantLight *distant; SPFePointLight *point; @@ -38,13 +46,6 @@ public: gdouble specularConstant; gdouble specularExponent; guint32 lighting_color; - - FilterSpecularLighting(); - static FilterPrimitive *create(); - virtual ~FilterSpecularLighting(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); private: }; diff --git a/src/display/nr-filter-tile.cpp b/src/display/nr-filter-tile.cpp index b1ca5d191..93ca50210 100644 --- a/src/display/nr-filter-tile.cpp +++ b/src/display/nr-filter-tile.cpp @@ -9,7 +9,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <glib.h> #include "display/nr-filter-tile.h" +#include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" namespace Inkscape { @@ -17,7 +19,6 @@ namespace Filters { FilterTile::FilterTile() { - g_warning("FilterTile::render not implemented."); } FilterPrimitive * FilterTile::create() { @@ -27,38 +28,23 @@ FilterPrimitive * FilterTile::create() { FilterTile::~FilterTile() {} -int FilterTile::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feTile (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - - nr_pixblock_setup_fast(out, in->mode, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); +void FilterTile::render_cairo(FilterSlot &slot) +{ + static bool tile_warning = false; //IMPLEMENT ME! - g_warning("Renderer for feTile is not implemented."); - (void)in_data; - (void)out_data; + if (!tile_warning) { + g_warning("Renderer for feTile is not implemented."); + tile_warning = true; + } - out->empty = FALSE; - slot.set(_output, out); - return 0; + cairo_surface_t *in = slot.getcairo(_input); + slot.set(_output, in); } -void FilterTile::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) +double FilterTile::complexity(Geom::Affine const &) { -} - -FilterTraits FilterTile::get_input_traits() { - return TRAIT_PARALLER; + return 1.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-tile.h b/src/display/nr-filter-tile.h index 4a2a0dddc..dc5b99a42 100644 --- a/src/display/nr-filter-tile.h +++ b/src/display/nr-filter-tile.h @@ -13,22 +13,20 @@ */ #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" namespace Inkscape { namespace Filters { +class FilterSlot; + class FilterTile : public FilterPrimitive { public: FilterTile(); static FilterPrimitive *create(); virtual ~FilterTile(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); - virtual FilterTraits get_input_traits(); + virtual void render_cairo(FilterSlot &slot); + virtual double complexity(Geom::Affine const &ctm); }; } /* namespace Filters */ diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index a91de0460..bce532f21 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -17,30 +17,300 @@ * Released under GNU GPL version 2 (or later), read the file 'COPYING' for more information */ -#include "display/nr-arena-item.h" +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" #include "display/nr-filter.h" #include "display/nr-filter-turbulence.h" #include "display/nr-filter-units.h" #include "display/nr-filter-utils.h" -#include "libnr/nr-rect-l.h" -#include "libnr/nr-blit.h" #include <math.h> namespace Inkscape { namespace Filters{ +class TurbulenceGenerator { +public: + TurbulenceGenerator() : + _tile(), + _baseFreq(), + _latticeSelector(), + _gradient(), + _seed(0), + _octaves(0), + _stitchTiles(false), + _wrapx(0), + _wrapy(0), + _wrapw(0), + _wraph(0), + _inited(false), + _fractalnoise(false) + {} + + void init(long seed, Geom::Rect const &tile, Geom::Point const &freq, bool stitch, + bool fractalnoise, int octaves) + { + // setup random number generator + _setupSeed(seed); + + // set values + _tile = tile; + _baseFreq = freq; + _stitchTiles = stitch; + _fractalnoise = fractalnoise; + _octaves = octaves; + + int i; + for (int k = 0; k < 4; ++k) { + for (i = 0; i < BSize; ++i) { + _latticeSelector[i] = i; + + _gradient[i][k][0] = static_cast<double>(_random() % (BSize*2) - BSize) / BSize; + _gradient[i][k][1] = static_cast<double>(_random() % (BSize*2) - BSize) / BSize; + + // normalize gradient + double s = hypot(_gradient[i][k][0], _gradient[i][k][1]); + _gradient[i][k][0] /= s; + _gradient[i][k][1] /= s; + } + } + while (--i) { + // shuffle lattice selectors + int j = _random() % BSize; + std::swap(_latticeSelector[i], _latticeSelector[j]); + } + + // fill out the remaining part of the gradient + for (i = 0; i < BSize + 2; ++i) + { + _latticeSelector[BSize + i] = _latticeSelector[i]; + + for(int k = 0; k < 4; ++k) { + _gradient[BSize + i][k][0] = _gradient[i][k][0]; + _gradient[BSize + i][k][1] = _gradient[i][k][1]; + } + } + + // When stitching tiled turbulence, the frequencies must be adjusted + // so that the tile borders will be continuous. + if (_stitchTiles) { + if (_baseFreq[Geom::X] != 0.0) + { + double freq = _baseFreq[Geom::X]; + double lo = floor(_tile.width() * freq) / _tile.width(); + double hi = ceil(_tile.width() * freq) / _tile.width(); + _baseFreq[Geom::X] = freq / lo < hi / freq ? lo : hi; + } + if (_baseFreq[Geom::Y] != 0.0) + { + double freq = _baseFreq[Geom::Y]; + double lo = floor(_tile.height() * freq) / _tile.height(); + double hi = ceil(_tile.height() * freq) / _tile.height(); + _baseFreq[Geom::Y] = freq / lo < hi / freq ? lo : hi; + } + + _wrapw = _tile.width() * _baseFreq[Geom::X] + 0.5; + _wraph = _tile.height() * _baseFreq[Geom::Y] + 0.5; + _wrapx = _tile.left() * _baseFreq[Geom::X] + PerlinOffset + _wrapw; + _wrapy = _tile.top() * _baseFreq[Geom::Y] + PerlinOffset + _wraph; + } + _inited = true; + } + + G_GNUC_PURE + guint32 turbulencePixel(Geom::Point const &p) const { + int wrapx = _wrapx, wrapy = _wrapy, wrapw = _wrapw, wraph = _wraph; + + double pixel[4]; + double x = p[Geom::X] * _baseFreq[Geom::X]; + double y = p[Geom::Y] * _baseFreq[Geom::Y]; + double ratio = 1.0; + + for (int k = 0; k < 4; ++k) + pixel[k] = 0.0; + + for(int octave = 0; octave < _octaves; ++octave) + { + double tx = x + PerlinOffset; + double bx = floor(tx); + double rx0 = tx - bx, rx1 = rx0 - 1.0; + int bx0 = bx, bx1 = bx0 + 1; + + double ty = y + PerlinOffset; + double by = floor(ty); + double ry0 = ty - by, ry1 = ry0 - 1.0; + int by0 = by, by1 = by0 + 1; + + if (_stitchTiles) { + if (bx0 >= wrapx) bx0 -= wrapw; + if (bx1 >= wrapx) bx1 -= wrapw; + if (by0 >= wrapy) by0 -= wraph; + if (by1 >= wrapy) by1 -= wraph; + } + bx0 &= BMask; + bx1 &= BMask; + by0 &= BMask; + by1 &= BMask; + + int i = _latticeSelector[bx0]; + int j = _latticeSelector[bx1]; + int b00 = _latticeSelector[i + by0]; + int b01 = _latticeSelector[i + by1]; + int b10 = _latticeSelector[j + by0]; + int b11 = _latticeSelector[j + by1]; + + double sx = _scurve(rx0); + double sy = _scurve(ry0); + + double result[4]; + // channel numbering: R=0, G=1, B=2, A=3 + for (int k = 0; k < 4; ++k) { + double const *qxa = _gradient[b00][k]; + double const *qxb = _gradient[b10][k]; + double a = _lerp(sx, rx0 * qxa[0] + ry0 * qxa[1], + rx1 * qxb[0] + ry0 * qxb[1]); + double const *qya = _gradient[b01][k]; + double const *qyb = _gradient[b11][k]; + double b = _lerp(sx, rx0 * qya[0] + ry1 * qya[1], + rx1 * qyb[0] + ry1 * qyb[1]); + result[k] = _lerp(sy, a, b); + } + + if (_fractalnoise) { + for (int k = 0; k < 4; ++k) + pixel[k] += result[k] / ratio; + } else { + for (int k = 0; k < 4; ++k) + pixel[k] += fabs(result[k]) / ratio; + } + + x *= 2; + y *= 2; + ratio *= 2; + + if(_stitchTiles) + { + // Update stitch values. Subtracting PerlinOffset before the multiplication and + // adding it afterward simplifies to subtracting it once. + wrapw *= 2; + wraph *= 2; + wrapx = wrapx*2 - PerlinOffset; + wrapy = wrapy*2 - PerlinOffset; + } + } + + if (_fractalnoise) { + guint32 r = CLAMP_D_TO_U8((pixel[0]*255.0 + 255.0) / 2); + guint32 g = CLAMP_D_TO_U8((pixel[1]*255.0 + 255.0) / 2); + guint32 b = CLAMP_D_TO_U8((pixel[2]*255.0 + 255.0) / 2); + guint32 a = CLAMP_D_TO_U8((pixel[3]*255.0 + 255.0) / 2); + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + ASSEMBLE_ARGB32(pxout, a,r,g,b); + return pxout; + } else { + guint32 r = CLAMP_D_TO_U8(pixel[0]*255.0); + guint32 g = CLAMP_D_TO_U8(pixel[1]*255.0); + guint32 b = CLAMP_D_TO_U8(pixel[2]*255.0); + guint32 a = CLAMP_D_TO_U8(pixel[3]*255.0); + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + ASSEMBLE_ARGB32(pxout, a,r,g,b); + return pxout; + } + } + + //G_GNUC_PURE + /*guint32 turbulencePixel(Geom::Point const &p) const { + if (!_fractalnoise) { + guint32 r = CLAMP_D_TO_U8(turbulence(0, p)*255.0); + guint32 g = CLAMP_D_TO_U8(turbulence(1, p)*255.0); + guint32 b = CLAMP_D_TO_U8(turbulence(2, p)*255.0); + guint32 a = CLAMP_D_TO_U8(turbulence(3, p)*255.0); + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + ASSEMBLE_ARGB32(pxout, a,r,g,b); + return pxout; + } else { + guint32 r = CLAMP_D_TO_U8((turbulence(0, p)*255.0 + 255.0) / 2); + guint32 g = CLAMP_D_TO_U8((turbulence(1, p)*255.0 + 255.0) / 2); + guint32 b = CLAMP_D_TO_U8((turbulence(2, p)*255.0 + 255.0) / 2); + guint32 a = CLAMP_D_TO_U8((turbulence(3, p)*255.0 + 255.0) / 2); + r = premul_alpha(r, a); + g = premul_alpha(g, a); + b = premul_alpha(b, a); + ASSEMBLE_ARGB32(pxout, a,r,g,b); + return pxout; + } + }*/ + + bool ready() const { return _inited; } + void dirty() { _inited = false; } + +private: + void _setupSeed(long seed) { + _seed = seed; + if (_seed <= 0) _seed = -(_seed % (RAND_m - 1)) + 1; + if (_seed > RAND_m - 1) _seed = RAND_m - 1; + } + long _random() { + /* Produces results in the range [1, 2**31 - 2]. + * Algorithm is: r = (a * r) mod m + * where a = 16807 and m = 2**31 - 1 = 2147483647 + * See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 + * To test: the algorithm should produce the result 1043618065 + * as the 10,000th generated number if the original seed is 1. */ + _seed = RAND_a * (_seed % RAND_q) - RAND_r * (_seed / RAND_q); + if (_seed <= 0) _seed += RAND_m; + return _seed; + } + static inline double _scurve(double t) { + return t * t * (3.0 - 2.0*t); + } + static inline double _lerp(double t, double a, double b) { + return a + t * (b-a); + } + + // random number generator constants + static long const + RAND_m = 2147483647, // 2**31 - 1 + RAND_a = 16807, // 7**5; primitive root of m + RAND_q = 127773, // m / a + RAND_r = 2836; // m % a + + // other constants + static int const BSize = 0x100; + static int const BMask = 0xff; + static double const PerlinOffset = 4096.0; + + Geom::Rect _tile; + Geom::Point _baseFreq; + int _latticeSelector[2*BSize + 2]; + double _gradient[2*BSize + 2][4][2]; + long _seed; + int _octaves; + bool _stitchTiles; + int _wrapx; + int _wrapy; + int _wrapw; + int _wraph; + bool _inited; + bool _fractalnoise; +}; + FilterTurbulence::FilterTurbulence() -: XbaseFrequency(0), - YbaseFrequency(0), - numOctaves(1), - seed(0), - updated(false), - updated_area(NR::IPoint(), NR::IPoint()), - pix(NULL), - fTileWidth(10), //guessed - fTileHeight(10), //guessed - fTileX(1), //guessed - fTileY(1) //guessed + : gen(new TurbulenceGenerator()) + , XbaseFrequency(0) + , YbaseFrequency(0) + , numOctaves(1) + , seed(0) + , updated(false) + , fTileWidth(10) //guessed + , fTileHeight(10) //guessed + , fTileX(1) //guessed + , fTileY(1) //guessed { } @@ -50,305 +320,85 @@ FilterPrimitive * FilterTurbulence::create() { FilterTurbulence::~FilterTurbulence() { - if (pix) { - nr_pixblock_release(pix); - delete pix; - } + delete gen; } void FilterTurbulence::set_baseFrequency(int axis, double freq){ if (axis==0) XbaseFrequency=freq; if (axis==1) YbaseFrequency=freq; + gen->dirty(); } void FilterTurbulence::set_numOctaves(int num){ - numOctaves=num; + numOctaves = num; + gen->dirty(); } void FilterTurbulence::set_seed(double s){ - seed=s; + seed = s; + gen->dirty(); } void FilterTurbulence::set_stitchTiles(bool st){ - stitchTiles=st; + stitchTiles = st; + gen->dirty(); } void FilterTurbulence::set_type(FilterTurbulenceType t){ - type=t; -} - -void FilterTurbulence::set_updated(bool u){ - updated=u; + type = t; + gen->dirty(); } -void FilterTurbulence::render_area(NRPixBlock *pix, NR::IRect &full_area, FilterUnits const &units) { - const int bbox_x0 = full_area.min()[NR::X]; - const int bbox_y0 = full_area.min()[NR::Y]; - const int bbox_x1 = full_area.max()[NR::X]; - const int bbox_y1 = full_area.max()[NR::Y]; - - Geom::Affine unit_trans = units.get_matrix_primitiveunits2pb().inverse(); - - double point[2]; - - unsigned char *pb = NR_PIXBLOCK_PX(pix); - - if (type==TURBULENCE_TURBULENCE){ - for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){ - int out_line = (y - pix->area.y0) * pix->rs; - point[1] = y * unit_trans[3] + unit_trans[5]; - for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){ - int out_pos = out_line + 4 * (x - pix->area.x0); - point[0] = x * unit_trans[0] + unit_trans[4]; - pb[out_pos] = CLAMP_D_TO_U8( turbulence(0,point)*255 ); // CLAMP includes rounding! - pb[out_pos + 1] = CLAMP_D_TO_U8( turbulence(1,point)*255 ); - pb[out_pos + 2] = CLAMP_D_TO_U8( turbulence(2,point)*255 ); - pb[out_pos + 3] = CLAMP_D_TO_U8( turbulence(3,point)*255 ); - } - } - } else { - for (int y = std::max(bbox_y0, pix->area.y0); y < std::min(bbox_y1, pix->area.y1); y++){ - int out_line = (y - pix->area.y0) * pix->rs; - point[1] = y * unit_trans[3] + unit_trans[5]; - for (int x = std::max(bbox_x0, pix->area.x0); x < std::min(bbox_x1, pix->area.x1); x++){ - int out_pos = out_line + 4 * (x - pix->area.x0); - point[0] = x * unit_trans[0] + unit_trans[4]; - pb[out_pos] = CLAMP_D_TO_U8( ((turbulence(0,point)*255) +255)/2 ); - pb[out_pos + 1] = CLAMP_D_TO_U8( ((turbulence(1,point)*255)+255)/2 ); - pb[out_pos + 2] = CLAMP_D_TO_U8( ((turbulence(2,point)*255) +255)/2 ); - pb[out_pos + 3] = CLAMP_D_TO_U8( ((turbulence(3,point)*255) +255)/2 ); - } - } - } - - pix->empty = FALSE; +void FilterTurbulence::set_updated(bool /*u*/) +{ } -void FilterTurbulence::update_pixbuffer(NR::IRect &area, FilterUnits const &units) { - int bbox_x0 = area.min()[NR::X]; - int bbox_y0 = area.min()[NR::Y]; - int bbox_x1 = area.max()[NR::X]; - int bbox_y1 = area.max()[NR::Y]; - - TurbulenceInit((long)seed); - - if (!pix){ - pix = new NRPixBlock; - nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true); - } - else if (bbox_x0 != pix->area.x0 || bbox_y0 != pix->area.y0 || - bbox_x1 != pix->area.x1 || bbox_y1 != pix->area.y1) - { - /* TODO: release-setup cycle not actually needed, if pixblock - * width and height don't change */ - nr_pixblock_release(pix); - nr_pixblock_setup_fast(pix, NR_PIXBLOCK_MODE_R8G8B8A8N, bbox_x0, bbox_y0, bbox_x1, bbox_y1, true); +struct Turbulence { + Turbulence(TurbulenceGenerator const &gen, Geom::Affine const &trans, int x0, int y0) + : _gen(gen) + , _trans(trans) + , _x0(x0), _y0(y0) + {} + guint32 operator()(int x, int y) { + Geom::Point point(x + _x0, y + _y0); + point *= _trans; + return _gen.turbulencePixel(point); } +private: + TurbulenceGenerator const &_gen; + Geom::Affine _trans; + int _x0, _y0; +}; - /* This limits pre-rendered turbulence to two megapixels. This is - * arbitary limit and could be something other, too. - * If bigger area is needed, visible area is rendered on demand. */ - if (!pix || (pix->size != NR_PIXBLOCK_SIZE_TINY && pix->data.px == NULL) || - ((bbox_x1 - bbox_x0) * (bbox_y1 - bbox_y0) > 2*1024*1024)) { - pix_data = NULL; - return; +void FilterTurbulence::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + + if (!gen->ready()) { + Geom::Point ta(fTileX, fTileY); + Geom::Point tb(fTileX + fTileWidth, fTileY + fTileHeight); + gen->init(seed, Geom::Rect(ta, tb), + Geom::Point(XbaseFrequency, YbaseFrequency), stitchTiles, + type == TURBULENCE_FRACTALNOISE, numOctaves); } - render_area(pix, area, units); + Geom::Affine unit_trans = slot.get_units().get_matrix_primitiveunits2pb().inverse(); + Geom::Rect slot_area = slot.get_slot_area(); + double x0 = slot_area.min()[Geom::X]; + double y0 = slot_area.min()[Geom::Y]; - pix_data = NR_PIXBLOCK_PX(pix); - - updated=true; - updated_area = area; -} + ink_cairo_surface_synthesize(out, Turbulence(*gen, unit_trans, x0, y0)); -int FilterTurbulence::render(FilterSlot &slot, FilterUnits const &units) { - NR::IRect area = units.get_pixblock_filterarea_paraller(); - // TODO: could be faster - updated_area only has to be same size as area - if (!updated || updated_area != area) update_pixbuffer(area, units); + cairo_surface_mark_dirty(out); - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feTurbulence (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - int x0 = in->area.x0, y0 = in->area.y0; - int x1 = in->area.x1, y1 = in->area.y1; - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, x0, y0, x1, y1, true); - - if (pix_data) { - /* If pre-rendered output of whole filter area exists, just copy it. */ - nr_blit_pixblock_pixblock(out, pix); - } else { - /* No pre-rendered output, render the required area here. */ - render_area(out, area, units); - } - - out->empty = FALSE; slot.set(_output, out); - return 0; -} - -long FilterTurbulence::Turbulence_setup_seed(long lSeed) -{ - if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1; - if (lSeed > RAND_m - 1) lSeed = RAND_m - 1; - return lSeed; + cairo_surface_destroy(out); } -long FilterTurbulence::TurbulenceRandom(long lSeed) +double FilterTurbulence::complexity(Geom::Affine const &) { - long result; - result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q); - if (result <= 0) result += RAND_m; - return result; -} - -void FilterTurbulence::TurbulenceInit(long lSeed) -{ - double s; - int i, j, k; - lSeed = Turbulence_setup_seed(lSeed); - for(k = 0; k < 4; k++) - { - for(i = 0; i < BSize; i++) - { - uLatticeSelector[i] = i; - for (j = 0; j < 2; j++) - fGradient[k][i][j] = (double)(((lSeed = TurbulenceRandom(lSeed)) % (BSize + BSize)) - BSize) / BSize; - s = double(sqrt(fGradient[k][i][0] * fGradient[k][i][0] + fGradient[k][i][1] * fGradient[k][i][1])); - fGradient[k][i][0] /= s; - fGradient[k][i][1] /= s; - } - } - while(--i) - { - k = uLatticeSelector[i]; - uLatticeSelector[i] = uLatticeSelector[j = (lSeed = TurbulenceRandom(lSeed)) % BSize]; - uLatticeSelector[j] = k; - } - for(i = 0; i < BSize + 2; i++) - { - uLatticeSelector[BSize + i] = uLatticeSelector[i]; - for(k = 0; k < 4; k++) - for(j = 0; j < 2; j++) - fGradient[k][BSize + i][j] = fGradient[k][i][j]; - } -} - -double FilterTurbulence::TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo) -{ - int bx0, bx1, by0, by1, b00, b10, b01, b11; - double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; - int i, j; - t = vec[0] + PerlinN; - bx0 = (int)t; - bx1 = bx0+1; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - t = vec[1] + PerlinN; - by0 = (int)t; - by1 = by0+1; - ry0 = t - (int)t; - ry1 = ry0 - 1.0f; - // If stitching, adjust lattice points accordingly. - if(pStitchInfo != NULL) - { - if(bx0 >= pStitchInfo->nWrapX) - bx0 -= pStitchInfo->nWidth; - if(bx1 >= pStitchInfo->nWrapX) - bx1 -= pStitchInfo->nWidth; - if(by0 >= pStitchInfo->nWrapY) - by0 -= pStitchInfo->nHeight; - if(by1 >= pStitchInfo->nWrapY) - by1 -= pStitchInfo->nHeight; - } - bx0 &= BM; - bx1 &= BM; - by0 &= BM; - by1 &= BM; - i = uLatticeSelector[bx0]; - j = uLatticeSelector[bx1]; - b00 = uLatticeSelector[i + by0]; - b10 = uLatticeSelector[j + by0]; - b01 = uLatticeSelector[i + by1]; - b11 = uLatticeSelector[j + by1]; - sx = double(s_curve(rx0)); - sy = double(s_curve(ry0)); - q = fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1]; - q = fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1]; - a = turb_lerp(sx, u, v); - q = fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1]; - q = fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1]; - b = turb_lerp(sx, u, v); - return turb_lerp(sy, a, b); -} - -double FilterTurbulence::turbulence(int nColorChannel, double *point) -{ - StitchInfo stitch; - StitchInfo *pStitchInfo = NULL; // Not stitching when NULL. - // Adjust the base frequencies if necessary for stitching. - if(stitchTiles) - { - // When stitching tiled turbulence, the frequencies must be adjusted - // so that the tile borders will be continuous. - if(XbaseFrequency != 0.0) - { - double fLoFreq = double(floor(fTileWidth * XbaseFrequency)) / fTileWidth; - double fHiFreq = double(ceil(fTileWidth * XbaseFrequency)) / fTileWidth; - if(XbaseFrequency / fLoFreq < fHiFreq / XbaseFrequency) - XbaseFrequency = fLoFreq; - else - XbaseFrequency = fHiFreq; - } - if(YbaseFrequency != 0.0) - { - double fLoFreq = double(floor(fTileHeight * YbaseFrequency)) / fTileHeight; - double fHiFreq = double(ceil(fTileHeight * YbaseFrequency)) / fTileHeight; - if(YbaseFrequency / fLoFreq < fHiFreq / YbaseFrequency) - YbaseFrequency = fLoFreq; - else - YbaseFrequency = fHiFreq; - } - // Set up TurbulenceInitial stitch values. - pStitchInfo = &stitch; - stitch.nWidth = int(fTileWidth * XbaseFrequency + 0.5f); - stitch.nWrapX = int(fTileX * XbaseFrequency + PerlinN + stitch.nWidth); - stitch.nHeight = int(fTileHeight * YbaseFrequency + 0.5f); - stitch.nWrapY = int(fTileY * YbaseFrequency + PerlinN + stitch.nHeight); - } - double fSum = 0.0f; - double vec[2]; - vec[0] = point[0] * XbaseFrequency; - vec[1] = point[1] * YbaseFrequency; - double ratio = 1; - for(int nOctave = 0; nOctave < numOctaves; nOctave++) - { - if(type==TURBULENCE_FRACTALNOISE) - fSum += double(TurbulenceNoise2(nColorChannel, vec, pStitchInfo) / ratio); - else - fSum += double(fabs(TurbulenceNoise2(nColorChannel, vec, pStitchInfo)) / ratio); - vec[0] *= 2; - vec[1] *= 2; - ratio *= 2; - if(pStitchInfo != NULL) - { - // Update stitch values. Subtracting PerlinN before the multiplication and - // adding it afterward simplifies to subtracting it once. - stitch.nWidth *= 2; - stitch.nWrapX = 2 * stitch.nWrapX - PerlinN; - stitch.nHeight *= 2; - stitch.nWrapY = 2 * stitch.nWrapY - PerlinN; - } - } - return fSum; -} - -FilterTraits FilterTurbulence::get_input_traits() { - return TRAIT_PARALLER; + return 5.0; } } /* namespace Filters */ diff --git a/src/display/nr-filter-turbulence.h b/src/display/nr-filter-turbulence.h index ee870f758..360853364 100644 --- a/src/display/nr-filter-turbulence.h +++ b/src/display/nr-filter-turbulence.h @@ -21,10 +21,10 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <2geom/point.h> #include "display/nr-filter-primitive.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" namespace Inkscape { namespace Filters { @@ -35,32 +35,7 @@ enum FilterTurbulenceType { TURBULENCE_ENDTYPE }; -struct StitchInfo -{ - int nWidth; // How much to subtract to wrap for stitching. - int nHeight; - int nWrapX; // Minimum value to wrap. - int nWrapY; -}; - -/* Produces results in the range [1, 2**31 - 2]. -Algorithm is: r = (a * r) mod m -where a = 16807 and m = 2**31 - 1 = 2147483647 -See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 -To test: the algorithm should produce the result 1043618065 -as the 10,000th generated number if the original seed is 1. -*/ -#define RAND_m 2147483647 /* 2**31 - 1 */ -#define RAND_a 16807 /* 7**5; primitive root of m */ -#define RAND_q 127773 /* m / a */ -#define RAND_r 2836 /* m % a */ -#define BSize 0x100 -#define BM 0xff -#define PerlinN 0x1000 -#define NP 12 /* 2^PerlinN */ -#define NM 0xfff -#define s_curve(t) ( t * t * (3. - 2. * t) ) -#define turb_lerp(t, a, b) ( a + t * (b - a) ) +class TurbulenceGenerator; class FilterTurbulence : public FilterPrimitive { public: @@ -68,9 +43,9 @@ public: static FilterPrimitive *create(); virtual ~FilterTurbulence(); - virtual int render(FilterSlot &slot, FilterUnits const &units); - void update_pixbuffer(NR::IRect &area, FilterUnits const &units); - void render_area(NRPixBlock *pix, NR::IRect &full_area, FilterUnits const &units); + virtual void render_cairo(FilterSlot &slot); + virtual double complexity(Geom::Affine const &ctm); + virtual bool uses_background() { return false; } void set_baseFrequency(int axis, double freq); void set_numOctaves(int num); @@ -78,14 +53,11 @@ public: void set_stitchTiles(bool st); void set_type(FilterTurbulenceType t); void set_updated(bool u); - virtual FilterTraits get_input_traits(); private: - long Turbulence_setup_seed(long lSeed); - long TurbulenceRandom(long lSeed); - void TurbulenceInit(long lSeed); - double TurbulenceNoise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo); - double turbulence(int nColorChannel, double *point); + TurbulenceGenerator *gen; + + void turbulenceInit(long seed); double XbaseFrequency, YbaseFrequency; int numOctaves; @@ -93,18 +65,14 @@ private: bool stitchTiles; FilterTurbulenceType type; bool updated; - NR::IRect updated_area; - NRPixBlock *pix; unsigned char *pix_data; - int uLatticeSelector[BSize + BSize + 2]; - double fGradient[4][BSize + BSize + 2][2]; - double fTileWidth; double fTileHeight; double fTileX; double fTileY; + }; } /* namespace Filters */ diff --git a/src/display/nr-filter-units.cpp b/src/display/nr-filter-units.cpp index b1c475c41..369deeb00 100644 --- a/src/display/nr-filter-units.cpp +++ b/src/display/nr-filter-units.cpp @@ -12,7 +12,6 @@ #include <glib.h> #include "display/nr-filter-units.h" -#include "libnr/nr-rect-l.h" #include "sp-filter-units.h" #include <2geom/transforms.h> @@ -71,10 +70,10 @@ Geom::Affine FilterUnits::get_matrix_user2pb() const { Geom::Affine u2pb = ctm; if (paraller_axis || !automatic_resolution) { - u2pb[0] = resolution_x / (filter_area->max()[X] - filter_area->min()[X]); + u2pb[0] = resolution_x / filter_area->width(); u2pb[1] = 0; u2pb[2] = 0; - u2pb[3] = resolution_y / (filter_area->max()[Y] - filter_area->min()[Y]); + u2pb[3] = resolution_y / filter_area->height(); u2pb[4] = ctm[4]; u2pb[5] = ctm[5]; } @@ -158,22 +157,13 @@ Geom::Affine FilterUnits::get_matrix_user2primitiveunits() const { return get_matrix_user2units(primitiveUnits); } -NR::IRect FilterUnits::get_pixblock_filterarea_paraller() const { +Geom::IntRect FilterUnits::get_pixblock_filterarea_paraller() const { g_assert(filter_area); - int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN; Geom::Affine u2pb = get_matrix_user2pb(); - - for (int i = 0 ; i < 4 ; i++) { - Geom::Point p = filter_area->corner(i); - p *= u2pb; - if (p[X] < min_x) min_x = (int)std::floor(p[X]); - if (p[X] > max_x) max_x = (int)std::ceil(p[X]); - if (p[Y] < min_y) min_y = (int)std::floor(p[Y]); - if (p[Y] > max_y) max_y = (int)std::ceil(p[Y]); - } - NR::IRect ret(NR::IPoint(min_x, min_y), NR::IPoint(max_x, max_y)); - return ret; + Geom::Rect r = *filter_area * u2pb; + Geom::IntRect ir = r.roundOutwards(); + return ir; } FilterUnits& FilterUnits::operator=(FilterUnits const &other) { diff --git a/src/display/nr-filter-units.h b/src/display/nr-filter-units.h index b78e102cd..f918cf12e 100644 --- a/src/display/nr-filter-units.h +++ b/src/display/nr-filter-units.h @@ -13,15 +13,24 @@ */ #include "sp-filter-units.h" -#include "libnr/nr-matrix.h" -#include "libnr/nr-rect.h" -#include "libnr/nr-rect-l.h" #include <2geom/affine.h> #include <2geom/rect.h> namespace Inkscape { namespace Filters { +/* Notes: + * - "filter units" is a coordinate system where the filter region is contained + * between (0,0) and (1,1). Do not confuse this with the filterUnits property + * - "primitive units" is the coordinate system in which all lengths and distances + * in the filter definition should be interpreted. They are affected by the value + * of the primitiveUnits attribute + * - "pb" is the coordinate system in which filter rendering happens. + * It might be aligned with user or screen coordinates depending on + * the filter primitives used in the filter. + * - "display" are world coordinates of the canvas - pixel grid coordinates + * of the drawing area translated so that (0,0) corresponds to the document origin + */ class FilterUnits { public: FilterUnits(); @@ -123,7 +132,7 @@ public: * NOTE: use only in filters, that define TRAIT_PARALLER in * get_input_traits. The filter effects area may not be representable * by simple rectangle otherwise. */ - NR::IRect get_pixblock_filterarea_paraller() const; + Geom::IntRect get_pixblock_filterarea_paraller() const; FilterUnits& operator=(FilterUnits const &other); diff --git a/src/display/nr-filter-utils.h b/src/display/nr-filter-utils.h index 4d2b06bd5..7e073168f 100644 --- a/src/display/nr-filter-utils.h +++ b/src/display/nr-filter-utils.h @@ -1,9 +1,11 @@ #ifndef __NR_FILTER_UTILS_H__ #define __NR_FILTER_UTILS_H__ -/** \file - * filter utils. Definition of functions needed by several filters. - * +/** + * @file + * Definition of functions needed by several filters. + */ +/* * Authors: * Jean-Rene Reinhard <jr@komite.net> * diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 85815c31a..6e3eb91d5 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -1,5 +1,3 @@ -#define __NR_FILTER_CPP__ - /* * SVG filters rendering * @@ -15,6 +13,7 @@ #include <cmath> #include <cstring> #include <string> +#include <cairo.h> #include "display/nr-filter.h" #include "display/nr-filter-primitive.h" @@ -39,10 +38,9 @@ #include "display/nr-filter-tile.h" #include "display/nr-filter-turbulence.h" -#include "display/nr-arena.h" -#include "display/nr-arena-item.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" +#include "display/drawing.h" +#include "display/drawing-item.h" +#include "display/drawing-context.h" #include <2geom/affine.h> #include <2geom/rect.h> #include "svg/svg-length.h" @@ -60,43 +58,14 @@ namespace Filters { using Geom::X; using Geom::Y; -static Geom::OptRect get_item_bbox(NRArenaItem const *item) { - Geom::Rect item_bbox; - if (item->item_bbox) { - item_bbox = *(item->item_bbox); - } else { - // Bounding box might not exist, so create a dummy one. - Geom::Point zero(0, 0); - item_bbox = Geom::Rect(zero, zero); - } - if (item_bbox.min()[X] > item_bbox.max()[X] - || item_bbox.min()[Y] > item_bbox.max()[Y]) - { - // In case of negative-size bbox, return an empty OptRect - return Geom::OptRect(); - } - return Geom::OptRect(item_bbox); -} - Filter::Filter() { - _primitive_count = 0; - _primitive_table_size = 1; - _primitive = new FilterPrimitive*[1]; - _primitive[0] = NULL; - //_primitive_count = 1; - //_primitive[0] = new FilterGaussian; _common_init(); } Filter::Filter(int n) { - _primitive_count = 0; - _primitive_table_size = (n > 0) ? n : 1; // we guarantee there is at least 1(one) filter slot - _primitive = new FilterPrimitive*[_primitive_table_size]; - for ( int i = 0 ; i < _primitive_table_size ; i++ ) { - _primitive[i] = NULL; - } + if (n > 0) _primitive.reserve(n); _common_init(); } @@ -125,51 +94,44 @@ void Filter::_common_init() { Filter::~Filter() { clear_primitives(); - delete[] _primitive; } -int Filter::render(NRArenaItem const *item, NRPixBlock *pb) +int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgct) { - if (!_primitive[0]) { - // TODO: Should clear the input buffer instead of just returning - return 1; + if (_primitive.empty()) { + // when no primitives are defined, clear source graphic + graphic.setSource(0,0,0,0); + graphic.setOperator(CAIRO_OPERATOR_SOURCE); + graphic.paint(); + graphic.setOperator(CAIRO_OPERATOR_OVER); + return 1; } - FilterQuality const filterquality = (FilterQuality)item->arena->filterquality; - int const blurquality = item->arena->blurquality; - - Geom::Affine trans = item->ctm; - FilterSlot slot(_slot_count, item); - slot.set_quality(filterquality); - slot.set_blurquality(blurquality); + FilterQuality const filterquality = (FilterQuality)item->drawing().filterQuality(); + int const blurquality = item->drawing().blurQuality(); - Geom::Rect item_bbox; - { - Geom::OptRect maybe_bbox = get_item_bbox(item); - if (maybe_bbox.isEmpty()) { - // Code below needs a bounding box - return 1; - } - item_bbox = *maybe_bbox; - } + Geom::Affine trans = item->ctm(); - Geom::Rect filter_area = filter_effect_area(item_bbox); - if (item_bbox.hasZeroArea()) { - // It's no use to try and filter an empty object. - return 1; - } + Geom::OptRect filter_area = filter_effect_area(item->itemBounds()); + if (!filter_area) return 1; FilterUnits units(_filter_units, _primitive_units); units.set_ctm(trans); - units.set_item_bbox(item_bbox); - units.set_filter_area(filter_area); + units.set_item_bbox(item->itemBounds()); + units.set_filter_area(*filter_area); - // TODO: with filterRes of 0x0 should return an empty image std::pair<double,double> resolution - = _filter_resolution(filter_area, trans, filterquality); - if(!(resolution.first > 0 && resolution.second > 0)) - return 1; + = _filter_resolution(*filter_area, trans, filterquality); + if (!(resolution.first > 0 && resolution.second > 0)) { + // zero resolution - clear source graphic and return + graphic.setSource(0,0,0,0); + graphic.setOperator(CAIRO_OPERATOR_SOURCE); + graphic.paint(); + graphic.setOperator(CAIRO_OPERATOR_OVER); + return 1; + } + units.set_resolution(resolution.first, resolution.second); if (_x_pixels > 0) { units.set_automatic_resolution(false); @@ -179,47 +141,30 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) } units.set_paraller(false); - for (int i = 0 ; i < _primitive_count ; i++) { - if (_primitive[i]->get_input_traits() & TRAIT_PARALLER) { + Geom::Affine pbtrans = units.get_matrix_display2pb(); + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + if (!_primitive[i]->can_handle_affine(pbtrans)) { units.set_paraller(true); break; } } - slot.set_units(units); - - NRPixBlock *in = new NRPixBlock; - nr_pixblock_setup_fast(in, pb->mode, pb->area.x0, pb->area.y0, - pb->area.x1, pb->area.y1, true); - if (in->size != NR_PIXBLOCK_SIZE_TINY && in->data.px == NULL) { - g_warning("Inkscape::Filters::Filter::render: failed to reserve temporary buffer"); - return 0; - } - nr_blit_pixblock_pixblock(in, pb); - in->empty = FALSE; - slot.set(NR_FILTER_SOURCEGRAPHIC, in); - - // Check that we are rendering a non-empty area - in = slot.get(NR_FILTER_SOURCEGRAPHIC); - if (in->area.x1 - in->area.x0 <= 0 || in->area.y1 - in->area.y0 <= 0) { - if (in->area.x1 - in->area.x0 < 0 || in->area.y1 - in->area.y0 < 0) { - g_warning("Inkscape::Filters::Filter::render: negative area! (%d, %d) (%d, %d)", - in->area.x0, in->area.y0, in->area.x1, in->area.y1); - } - return 0; - } - in = NULL; // in is now handled by FilterSlot, we should not touch it + FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgct, graphic, units); + slot.set_quality(filterquality); + slot.set_blurquality(blurquality); - for (int i = 0 ; i < _primitive_count ; i++) { - _primitive[i]->render(slot, units); + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + _primitive[i]->render_cairo(slot); } - slot.get_final(_output_slot, pb); + Geom::Point origin = graphic.targetLogicalBounds().min(); + cairo_surface_t *result = slot.get_result(_output_slot); + graphic.setSource(result, origin[Geom::X], origin[Geom::Y]); + graphic.setOperator(CAIRO_OPERATOR_SOURCE); + graphic.paint(); + graphic.setOperator(CAIRO_OPERATOR_OVER); + cairo_surface_destroy(result); - // Take note of the amount of used image slots - // -> next time this filter is rendered, we can reserve enough slots - // immediately - _slot_count = slot.get_slot_count(); return 0; } @@ -231,10 +176,11 @@ void Filter::set_primitive_units(SPFilterUnits unit) { _primitive_units = unit; } -void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { - for (int i = 0 ; i < _primitive_count ; i++) { - if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm); +void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item) const { + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + if (_primitive[i]) _primitive[i]->area_enlarge(bbox, item->ctm()); } + /* TODO: something. See images at the bottom of filters.svg with medium-low filtering quality. @@ -248,7 +194,7 @@ void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { } Geom::Rect item_bbox; - Geom::OptRect maybe_bbox = get_item_bbox(item); + Geom::OptRect maybe_bbox = item->itemBounds(); if (maybe_bbox.isEmpty()) { // Code below needs a bounding box return; @@ -256,9 +202,9 @@ void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { item_bbox = *maybe_bbox; std::pair<double,double> res_low - = _filter_resolution(item_bbox, item->ctm, filterquality); + = _filter_resolution(item_bbox, item->ctm(), filterquality); //std::pair<double,double> res_full - // = _filter_resolution(item_bbox, item->ctm, FILTER_QUALITY_BEST); + // = _filter_resolution(item_bbox, item->ctm(), FILTER_QUALITY_BEST); double pixels_per_block = fmax(item_bbox.width() / res_low.first, item_bbox.height() / res_low.second); bbox.x0 -= (int)pixels_per_block; @@ -268,40 +214,36 @@ void Filter::area_enlarge(NRRectL &bbox, NRArenaItem const *item) const { */ } -void Filter::bbox_enlarge(NRRectL &bbox) { - // Modifying empty bounding boxes confuses rest of the renderer, so - // let's not do that. - if (bbox.x0 > bbox.x1 || bbox.y0 > bbox.y1) return; - - /* TODO: this is wrong. Should use bounding box in user coordinates - * and find its extents in display coordinates. */ - Geom::Point min(bbox.x0, bbox.y0); - Geom::Point max(bbox.x1, bbox.y1); - Geom::Rect tmp_bbox(min, max); +Geom::OptIntRect Filter::compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox) { - Geom::Rect enlarged = filter_effect_area(tmp_bbox); + Geom::OptRect enlarged = filter_effect_area(item_bbox); + if (enlarged) { + *enlarged *= item->ctm(); - bbox.x0 = (NR::ICoord)enlarged.min()[X]; - bbox.y0 = (NR::ICoord)enlarged.min()[Y]; - bbox.x1 = (NR::ICoord)enlarged.max()[X]; - bbox.y1 = (NR::ICoord)enlarged.max()[Y]; + Geom::OptIntRect ret(enlarged->roundOutwards()); + return ret; + } else { + return Geom::OptIntRect(); + } } -Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) +Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox) { Geom::Point minp, maxp; - double len_x = bbox.max()[X] - bbox.min()[X]; - double len_y = bbox.max()[Y] - bbox.min()[Y]; + double len_x = bbox ? bbox->width() : 0; + double len_y = bbox ? bbox->height() : 0; /* TODO: fetch somehow the object ex and em lengths */ _region_x.update(12, 6, len_x); _region_y.update(12, 6, len_y); _region_width.update(12, 6, len_x); _region_height.update(12, 6, len_y); if (_filter_units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) { + if (!bbox) return Geom::OptRect(); + if (_region_x.unit == SVGLength::PERCENT) { - minp[X] = bbox.min()[X] + _region_x.computed; + minp[X] = bbox->left() + _region_x.computed; } else { - minp[X] = bbox.min()[X] + _region_x.computed * len_x; + minp[X] = bbox->left() + _region_x.computed * len_x; } if (_region_width.unit == SVGLength::PERCENT) { maxp[X] = minp[X] + _region_width.computed; @@ -310,9 +252,9 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) } if (_region_y.unit == SVGLength::PERCENT) { - minp[Y] = bbox.min()[Y] + _region_y.computed; + minp[Y] = bbox->top() + _region_y.computed; } else { - minp[Y] = bbox.min()[Y] + _region_y.computed * len_y; + minp[Y] = bbox->top() + _region_y.computed * len_y; } if (_region_height.unit == SVGLength::PERCENT) { maxp[Y] = minp[Y] + _region_height.computed; @@ -328,10 +270,32 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) } else { g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units"); } - Geom::Rect area(minp, maxp); + Geom::OptRect area(minp, maxp); return area; } +double Filter::complexity(Geom::Affine const &ctm) +{ + double factor = 1.0; + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + if (_primitive[i]) { + double f = _primitive[i]->complexity(ctm); + factor += (f - 1.0); + } + } + return factor; +} + +bool Filter::uses_background() +{ + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + if (_primitive[i] && _primitive[i]->uses_background()) { + return true; + } + } + return false; +} + /* Constructor table holds pointers to static methods returning filter * primitives. This table is indexed with FilterPrimitiveType, so that * for example method in _constructor[NR_FILTER_GAUSSIANBLUR] @@ -367,27 +331,6 @@ void Filter::_create_constructor_table() created = true; } -/** Helper method for enlarging table of filter primitives. When new - * primitives are added, but we have no space for them, this function - * makes some more space. - */ -void Filter::_enlarge_primitive_table() { - FilterPrimitive **new_tbl = new FilterPrimitive*[_primitive_table_size * 2]; - for (int i = 0 ; i < _primitive_count ; i++) { - new_tbl[i] = _primitive[i]; - } - _primitive_table_size *= 2; - for (int i = _primitive_count ; i < _primitive_table_size ; i++) { - new_tbl[i] = NULL; - } - if(_primitive != NULL) { - delete[] _primitive; - } else { - g_warning("oh oh"); - } - _primitive = new_tbl; -} - int Filter::add_primitive(FilterPrimitiveType type) { _create_constructor_table(); @@ -398,14 +341,8 @@ int Filter::add_primitive(FilterPrimitiveType type) if (!_constructor[type]) return -1; FilterPrimitive *created = _constructor[type](); - // If there is no space for new filter primitive, enlarge the table - if (_primitive_count >= _primitive_table_size) { - _enlarge_primitive_table(); - } - - _primitive[_primitive_count] = created; - int handle = _primitive_count; - _primitive_count++; + int handle = _primitive.size(); + _primitive.push_back(created); return handle; } @@ -415,8 +352,7 @@ int Filter::replace_primitive(int target, FilterPrimitiveType type) // Check that target is valid primitive inside this filter if (target < 0) return -1; - if (target >= _primitive_count) return -1; - if (!_primitive[target]) return -1; + if (static_cast<unsigned>(target) >= _primitive.size()) return -1; // Check that we can create a new filter of specified type if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE) @@ -424,27 +360,22 @@ int Filter::replace_primitive(int target, FilterPrimitiveType type) if (!_constructor[type]) return -1; FilterPrimitive *created = _constructor[type](); - // If there is no space for new filter primitive, enlarge the table - if (_primitive_count >= _primitive_table_size) { - _enlarge_primitive_table(); - } - delete _primitive[target]; _primitive[target] = created; return target; } FilterPrimitive *Filter::get_primitive(int handle) { - if (handle < 0 || handle >= _primitive_count) return NULL; + if (handle < 0 || handle >= static_cast<int>(_primitive.size())) return NULL; return _primitive[handle]; } void Filter::clear_primitives() { - for (int i = 0 ; i < _primitive_count ; i++) { - if (_primitive[i]) delete _primitive[i]; + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + delete _primitive[i]; } - _primitive_count = 0; + _primitive.clear(); } void Filter::set_x(SVGLength const &length) diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h index ae5f96420..d53005c5d 100644 --- a/src/display/nr-filter.h +++ b/src/display/nr-filter.h @@ -13,23 +13,27 @@ */ //#include "display/nr-arena-item.h" +#include <cairo.h> #include "display/nr-filter-primitive.h" #include "display/nr-filter-types.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-matrix.h" -#include "libnr/nr-rect.h" #include "svg/svg-length.h" #include "sp-filter-units.h" #include "gc-managed.h" -struct NRArenaItem; - namespace Inkscape { +class DrawingContext; +class DrawingItem; + namespace Filters { -class Filter : public Inkscape::GC::Managed<> { +class Filter { public: - int render(NRArenaItem const *item, NRPixBlock *pb); + /** Given background state from @a bgct and an intermediate rendering from the surface + * backing @a graphic, modify the contents of the surface backing @a graphic to represent + * the results of filter rendering. @a bgarea and @a area specify bounding boxes + * of both surfaces in world coordinates; Cairo contexts are assumed to be in default state + * (0,0 = surface origin, no path, OVER operator) */ + int render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgct); /** * Creates a new filter primitive under this filter object. @@ -145,18 +149,25 @@ public: * to be rendered so that after filtering, the original area is * drawn correctly. */ - void area_enlarge(NRRectL &area, NRArenaItem const *item) const; + void area_enlarge(Geom::IntRect &area, Inkscape::DrawingItem const *item) const; /** - * Given an object bounding box, this function enlarges it so that - * it contains the filter effect area. + * Given an item bounding box (in user coords), this function enlarges it + * to contain the filter effects region and transforms it to screen + * coordinates */ - void bbox_enlarge(NRRectL &bbox); + Geom::OptIntRect compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox); /** * Returns the filter effects area in user coordinate system. * The given bounding box should be a bounding box as specified in * SVG standard and in user coordinate system. */ - Geom::Rect filter_effect_area(Geom::Rect const &bbox); + Geom::OptRect filter_effect_area(Geom::OptRect const &bbox); + + // returns cache score factor + double complexity(Geom::Affine const &ctm); + + // says whether the filter accesses any of the background images + bool uses_background(); /** Creates a new filter with space for one filter element */ Filter(); @@ -170,9 +181,7 @@ public: virtual ~Filter(); private: - int _primitive_count; - int _primitive_table_size; - + std::vector<FilterPrimitive*> _primitive; /** Amount of image slots used, when this filter was rendered last time */ int _slot_count; @@ -194,10 +203,7 @@ private: SPFilterUnits _filter_units; SPFilterUnits _primitive_units; - FilterPrimitive ** _primitive; - void _create_constructor_table(); - void _enlarge_primitive_table(); void _common_init(); int _resolution_limit(FilterQuality const quality) const; std::pair<double,double> _filter_resolution(Geom::Rect const &area, diff --git a/src/display/nr-light.cpp b/src/display/nr-light.cpp index 74d83da70..6331d1546 100644 --- a/src/display/nr-light.cpp +++ b/src/display/nr-light.cpp @@ -1,5 +1,3 @@ -#define __NR_LIGHT_CPP__ - /* * Light rendering helpers * @@ -13,12 +11,12 @@ #include <cmath> -#include "libnr/nr-pixops.h" #include "display/nr-light.h" #include "display/nr-3dutils.h" #include "filters/distantlight.h" #include "filters/pointlight.h" #include "filters/spotlight.h" +#include "color.h" namespace Inkscape { namespace Filters { @@ -38,9 +36,9 @@ void DistantLight::light_vector(NR::Fvector &v) { } void DistantLight::light_components(NR::Fvector &lc) { - lc[LIGHT_RED] = NR_RGBA32_R(color); - lc[LIGHT_GREEN] = NR_RGBA32_G(color); - lc[LIGHT_BLUE] = NR_RGBA32_B(color); + lc[LIGHT_RED] = SP_RGBA32_R_U(color); + lc[LIGHT_GREEN] = SP_RGBA32_G_U(color); + lc[LIGHT_BLUE] = SP_RGBA32_B_U(color); } PointLight::PointLight(SPFePointLight *light, guint32 lighting_color, const Geom::Affine &trans) { @@ -61,9 +59,9 @@ void PointLight::light_vector(NR::Fvector &v, gdouble x, gdouble y, gdouble z) { } void PointLight::light_components(NR::Fvector &lc) { - lc[LIGHT_RED] = NR_RGBA32_R(color); - lc[LIGHT_GREEN] = NR_RGBA32_G(color); - lc[LIGHT_BLUE] = NR_RGBA32_B(color); + lc[LIGHT_RED] = SP_RGBA32_R_U(color); + lc[LIGHT_GREEN] = SP_RGBA32_G_U(color); + lc[LIGHT_BLUE] = SP_RGBA32_B_U(color); } SpotLight::SpotLight(SPFeSpotLight *light, guint32 lighting_color, const Geom::Affine &trans) { @@ -101,9 +99,9 @@ void SpotLight::light_components(NR::Fvector &lc, const NR::Fvector &L) { spmod = 0; else spmod = std::pow(spmod, speExp); - lc[LIGHT_RED] = spmod * NR_RGBA32_R(color); - lc[LIGHT_GREEN] = spmod * NR_RGBA32_G(color); - lc[LIGHT_BLUE] = spmod * NR_RGBA32_B(color); + lc[LIGHT_RED] = spmod * SP_RGBA32_R_U(color); + lc[LIGHT_GREEN] = spmod * SP_RGBA32_G_U(color); + lc[LIGHT_BLUE] = spmod * SP_RGBA32_B_U(color); } } /* namespace Filters */ diff --git a/src/display/nr-light.h b/src/display/nr-light.h index 49130cc4e..022243bfc 100644 --- a/src/display/nr-light.h +++ b/src/display/nr-light.h @@ -8,7 +8,7 @@ * light color components (at a given point). */ -#include <gdk/gdktypes.h> +#include <gdk/gdk.h> #include "display/nr-3dutils.h" #include "display/nr-light-types.h" #include <2geom/forward.h> diff --git a/src/display/nr-plain-stuff-gdk.cpp b/src/display/nr-plain-stuff-gdk.cpp deleted file mode 100644 index d5b43f4ea..000000000 --- a/src/display/nr-plain-stuff-gdk.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#define __NR_PLAIN_STUFF_GDK_C__ - -/* - * Miscellaneous simple rendering utilities - * - * Author: - * Lauris Kaplinski <lauris@ximian.com> - * - * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. - * - * Released under GNU GPL - */ - -#include <libnr/nr-pixblock-pattern.h> -#include "nr-plain-stuff.h" -#include "nr-plain-stuff-gdk.h" - -void -nr_gdk_draw_rgba32_solid (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h, guint32 rgba) -{ - NRPixBlock pb; - - nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, w, h, FALSE); - - nr_render_rgba32_rgb (NR_PIXBLOCK_PX (&pb), w, h, pb.rs, x, y, rgba); - gdk_draw_rgb_image (drawable, gc, x, y, w, h, GDK_RGB_DITHER_MAX, NR_PIXBLOCK_PX (&pb), pb.rs); - - nr_pixblock_release (&pb); -} - -void -nr_gdk_draw_gray_garbage (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h) -{ - for (gint yy = y; yy < y + h; yy += 64) { - for (gint xx = x; xx < x + w; xx += 64) { - NRPixBlock pb; - gint ex = MIN (xx + 64, x + w); - gint ey = MIN (yy + 64, y + h); - nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8, xx, yy, ex, ey, FALSE); - nr_pixblock_render_gray_noise (&pb, NULL); - gdk_draw_rgb_image (drawable, gc, xx, yy, ex - xx, ey - yy, GDK_RGB_DITHER_NONE, NR_PIXBLOCK_PX (&pb), pb.rs); - nr_pixblock_release (&pb); - } - } -} - diff --git a/src/display/nr-plain-stuff-gdk.h b/src/display/nr-plain-stuff-gdk.h deleted file mode 100644 index bff54a81f..000000000 --- a/src/display/nr-plain-stuff-gdk.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __NR_PLAIN_STUFF_GDK_H__ -#define __NR_PLAIN_STUFF_GDK_H__ - -/* - * Miscellaneous simple rendering utilities - * - * Author: - * Lauris Kaplinski <lauris@ximian.com> - * - * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. - * - * Released under GNU GPL - */ - -#include <gdk/gdk.h> - -void nr_gdk_draw_rgba32_solid (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h, guint32 rgba); - -void nr_gdk_draw_gray_garbage (GdkDrawable *drawable, GdkGC *gc, gint x, gint y, gint w, gint h); - -#endif - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-plain-stuff.cpp b/src/display/nr-plain-stuff.cpp deleted file mode 100644 index 62a61102e..000000000 --- a/src/display/nr-plain-stuff.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#define __NR_PLAIN_STUFF_C__ - -/* - * Miscellaneous simple rendering utilities - * - * Author: - * Lauris Kaplinski <lauris@ximian.com> - * - * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. - * - * Released under GNU GPL - */ - -#include <glib/gmessages.h> -#include <libnr/nr-pixops.h> -#include "nr-plain-stuff.h" - -#define NR_DEFAULT_CHECKERSIZEP2 2 -#define NR_DEFAULT_CHECKERCOLOR0 0xbfbfbfff -#define NR_DEFAULT_CHECKERCOLOR1 0x808080ff - -void -nr_render_checkerboard_rgb (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff) -{ - g_return_if_fail (px != NULL); - - nr_render_checkerboard_rgb_custom (px, w, h, rs, xoff, yoff, NR_DEFAULT_CHECKERCOLOR0, NR_DEFAULT_CHECKERCOLOR1, NR_DEFAULT_CHECKERSIZEP2); -} - -void -nr_render_checkerboard_rgb_custom (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff, guint32 c0, guint32 c1, gint sizep2) -{ - gint x, y, m; - guint r0, g0, b0; - guint r1, g1, b1; - - g_return_if_fail (px != NULL); - g_return_if_fail (sizep2 >= 0); - g_return_if_fail (sizep2 <= 8); - - xoff &= 0x1ff; - yoff &= 0x1ff; - m = 0x1 << sizep2; - r0 = NR_RGBA32_R (c0); - g0 = NR_RGBA32_G (c0); - b0 = NR_RGBA32_B (c0); - r1 = NR_RGBA32_R (c1); - g1 = NR_RGBA32_G (c1); - b1 = NR_RGBA32_B (c1); - - for (y = 0; y < h; y++) { - guchar *p; - p = px; - for (x = 0; x < w; x++) { - if (((x + xoff) ^ (y + yoff)) & m) { - *p++ = r0; - *p++ = g0; - *p++ = b0; - } else { - *p++ = r1; - *p++ = g1; - *p++ = b1; - } - } - px += rs; - } -} - -void -nr_render_rgba32_rgb (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff, guint32 c) -{ - guint32 c0, c1; - gint a, r, g, b, cr, cg, cb; - - g_return_if_fail (px != NULL); - - r = NR_RGBA32_R (c); - g = NR_RGBA32_G (c); - b = NR_RGBA32_B (c); - a = NR_RGBA32_A (c); - - cr = NR_COMPOSEN11_1111 (r, a, NR_RGBA32_R (NR_DEFAULT_CHECKERCOLOR0)); - cg = NR_COMPOSEN11_1111 (g, a, NR_RGBA32_G (NR_DEFAULT_CHECKERCOLOR0)); - cb = NR_COMPOSEN11_1111 (b, a, NR_RGBA32_B (NR_DEFAULT_CHECKERCOLOR0)); - c0 = (cr << 24) | (cg << 16) | (cb << 8) | 0xff; - - cr = NR_COMPOSEN11_1111 (r, a, NR_RGBA32_R (NR_DEFAULT_CHECKERCOLOR1)); - cg = NR_COMPOSEN11_1111 (g, a, NR_RGBA32_G (NR_DEFAULT_CHECKERCOLOR1)); - cb = NR_COMPOSEN11_1111 (b, a, NR_RGBA32_B (NR_DEFAULT_CHECKERCOLOR1)); - c1 = (cr << 24) | (cg << 16) | (cb << 8) | 0xff; - - nr_render_checkerboard_rgb_custom (px, w, h, rs, xoff, yoff, c0, c1, NR_DEFAULT_CHECKERSIZEP2); -} - diff --git a/src/display/nr-plain-stuff.h b/src/display/nr-plain-stuff.h deleted file mode 100644 index 457442312..000000000 --- a/src/display/nr-plain-stuff.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef __NR_PLAIN_STUFF_H__ -#define __NR_PLAIN_STUFF_H__ - -/* - * Miscellaneous simple rendering utilities - * - * Author: - * Lauris Kaplinski <lauris@ximian.com> - * - * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc. - * - * Released under GNU GPL - */ - -#include <glib/gtypes.h> - -void nr_render_checkerboard_rgb (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff); -void nr_render_checkerboard_rgb_custom (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff, guint32 c0, guint32 c1, gint sizep2); - -void nr_render_rgba32_rgb (guchar *px, gint w, gint h, gint rs, gint xoff, gint yoff, guint32 c); - -#endif - -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp new file mode 100644 index 000000000..86102f9e8 --- /dev/null +++ b/src/display/nr-style.cpp @@ -0,0 +1,219 @@ +/** + * @file + * Style information for rendering. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/nr-style.h" +#include "style.h" +#include "sp-paint-server.h" +#include "display/canvas-bpath.h" // contains SPStrokeJoinType, SPStrokeCapType etc. (WTF!) +#include "display/drawing-context.h" + +void NRStyle::Paint::clear() +{ + if (server) { + sp_object_unref(server, NULL); + server = NULL; + } + type = PAINT_NONE; +} + +void NRStyle::Paint::set(SPColor const &c) +{ + clear(); + type = PAINT_COLOR; + color = c; +} + +void NRStyle::Paint::set(SPPaintServer *ps) +{ + clear(); + if (ps) { + type = PAINT_SERVER; + server = ps; + sp_object_ref(server, NULL); + } +} + +NRStyle::NRStyle() + : fill() + , stroke() + , stroke_width(0.0) + , miter_limit(0.0) + , n_dash(0) + , dash(NULL) + , dash_offset(0.0) + , fill_rule(CAIRO_FILL_RULE_EVEN_ODD) + , line_cap(CAIRO_LINE_CAP_BUTT) + , line_join(CAIRO_LINE_JOIN_MITER) + , fill_pattern(NULL) + , stroke_pattern(NULL) +{} + +NRStyle::~NRStyle() +{ + if (fill_pattern) cairo_pattern_destroy(fill_pattern); + if (stroke_pattern) cairo_pattern_destroy(stroke_pattern); + if (dash) delete dash; +} + +void NRStyle::set(SPStyle *style) +{ + if ( style->fill.isPaintserver() ) { + fill.set(style->getFillPaintServer()); + } else if ( style->fill.isColor() ) { + fill.set(style->fill.value.color); + } else if ( style->fill.isNone() ) { + fill.clear(); + } else { + g_assert_not_reached(); + } + fill.opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value); + + switch (style->fill_rule.computed) { + case SP_WIND_RULE_EVENODD: + fill_rule = CAIRO_FILL_RULE_EVEN_ODD; + break; + case SP_WIND_RULE_NONZERO: + fill_rule = CAIRO_FILL_RULE_WINDING; + break; + default: + g_assert_not_reached(); + } + + if ( style->stroke.isPaintserver() ) { + stroke.set(style->getStrokePaintServer()); + } else if ( style->stroke.isColor() ) { + stroke.set(style->stroke.value.color); + } else if ( style->stroke.isNone() ) { + stroke.clear(); + } else { + g_assert_not_reached(); + } + stroke.opacity = SP_SCALE24_TO_FLOAT(style->stroke_opacity.value); + stroke_width = style->stroke_width.computed; + switch (style->stroke_linecap.computed) { + case SP_STROKE_LINECAP_ROUND: + line_cap = CAIRO_LINE_CAP_ROUND; + break; + case SP_STROKE_LINECAP_SQUARE: + line_cap = CAIRO_LINE_CAP_SQUARE; + break; + case SP_STROKE_LINECAP_BUTT: + line_cap = CAIRO_LINE_CAP_BUTT; + break; + default: + g_assert_not_reached(); + } + switch (style->stroke_linejoin.computed) { + case SP_STROKE_LINEJOIN_ROUND: + line_join = CAIRO_LINE_JOIN_ROUND; + break; + case SP_STROKE_LINEJOIN_BEVEL: + line_join = CAIRO_LINE_JOIN_BEVEL; + break; + case SP_STROKE_LINEJOIN_MITER: + line_join = CAIRO_LINE_JOIN_MITER; + break; + default: + g_assert_not_reached(); + } + miter_limit = style->stroke_miterlimit.value; + + if (dash) delete [] dash; + + n_dash = style->stroke_dash.n_dash; + if (n_dash != 0) { + dash_offset = style->stroke_dash.offset; + dash = new double[n_dash]; + for (unsigned int i = 0; i < n_dash; ++i) { + dash[i] = style->stroke_dash.dash[i]; + } + } else { + dash_offset = 0.0; + dash = NULL; + } + + update(); +} + +bool NRStyle::prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox) +{ + // update fill pattern + if (!fill_pattern) { + switch (fill.type) { + case PAINT_SERVER: { + fill_pattern = sp_paint_server_create_pattern(fill.server, ct.raw(), paintbox, fill.opacity); + } break; + case PAINT_COLOR: { + SPColor const &c = fill.color; + fill_pattern = cairo_pattern_create_rgba( + c.v.c[0], c.v.c[1], c.v.c[2], fill.opacity); + } break; + default: break; + } + } + if (!fill_pattern) return false; + return true; +} + +void NRStyle::applyFill(Inkscape::DrawingContext &ct) +{ + ct.setSource(fill_pattern); + ct.setFillRule(fill_rule); +} + +bool NRStyle::prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox) +{ + if (!stroke_pattern) { + switch (stroke.type) { + case PAINT_SERVER: { + stroke_pattern = sp_paint_server_create_pattern(stroke.server, ct.raw(), paintbox, stroke.opacity); + } break; + case PAINT_COLOR: { + SPColor const &c = stroke.color; + stroke_pattern = cairo_pattern_create_rgba( + c.v.c[0], c.v.c[1], c.v.c[2], stroke.opacity); + } break; + default: break; + } + } + if (!stroke_pattern) return false; + return true; +} + +void NRStyle::applyStroke(Inkscape::DrawingContext &ct) +{ + ct.setSource(stroke_pattern); + ct.setLineWidth(stroke_width); + ct.setLineCap(line_cap); + ct.setLineJoin(line_join); + ct.setMiterLimit(miter_limit); + cairo_set_dash(ct.raw(), dash, n_dash, dash_offset); // fixme +} + +void NRStyle::update() +{ + // force pattern update + if (fill_pattern) cairo_pattern_destroy(fill_pattern); + if (stroke_pattern) cairo_pattern_destroy(stroke_pattern); + fill_pattern = NULL; + stroke_pattern = NULL; +} + +/* + 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 : diff --git a/src/display/nr-style.h b/src/display/nr-style.h new file mode 100644 index 000000000..ce154cec0 --- /dev/null +++ b/src/display/nr-style.h @@ -0,0 +1,84 @@ +/** + * @file + * Style information for rendering. + *//* + * Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_NR_ARENA_STYLE_H +#define SEEN_INKSCAPE_DISPLAY_NR_ARENA_STYLE_H + +#include <cairo.h> +#include <2geom/rect.h> +#include "color.h" + +class SPColor; +class SPPaintServer; +class SPStyle; +namespace Inkscape { +class DrawingContext; +} + +struct NRStyle { + NRStyle(); + ~NRStyle(); + + void set(SPStyle *); + bool prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox); + bool prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox); + void applyFill(Inkscape::DrawingContext &ct); + void applyStroke(Inkscape::DrawingContext &ct); + void update(); + + enum PaintType { + PAINT_NONE, + PAINT_COLOR, + PAINT_SERVER + }; + + struct Paint { + Paint() : type(PAINT_NONE), color(0), server(NULL), opacity(1.0) {} + ~Paint() { clear(); } + + PaintType type; + SPColor color; + SPPaintServer *server; + float opacity; + + void clear(); + void set(SPColor const &c); + void set(SPPaintServer *ps); + }; + + Paint fill; + Paint stroke; + float stroke_width; + float miter_limit; + unsigned int n_dash; + double *dash; + float dash_offset; + cairo_fill_rule_t fill_rule; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + + cairo_pattern_t *fill_pattern; + cairo_pattern_t *stroke_pattern; +}; + +#endif + + +/* + 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 : diff --git a/src/display/nr-svgfonts.cpp b/src/display/nr-svgfonts.cpp index 62d52b04b..8869274d4 100644 --- a/src/display/nr-svgfonts.cpp +++ b/src/display/nr-svgfonts.cpp @@ -14,12 +14,20 @@ #include <2geom/pathvector.h> #include <2geom/transforms.h> -#include "../style.h" #include <cairo.h> #include <vector> +#include "style.h" #include "svg/svg.h" -#include "inkscape-cairo.h" -#include "nr-svgfonts.h" +#include "display/cairo-utils.h" +#include "display/nr-svgfonts.h" +#include "display/nr-svgfonts.h" +#include "sp-path.h" +#include "sp-object-group.h" +#include "sp-use.h" +#include "sp-use-reference.h" +#include "display/curve.h" +#include "xml/repr.h" +#include "sp-font-face.h" //*************************// // UserFont Implementation // @@ -212,6 +220,47 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/, return CAIRO_STATUS_SUCCESS; } +void +SvgFont::render_glyph_path(cairo_t* cr, Geom::PathVector* pathv){ + if (!pathv->empty()){ + //This glyph has a path description on its d attribute, so we render it: + cairo_new_path(cr); + + //adjust scale of the glyph +// Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x); + Geom::Scale s(1.0/1000);//TODO: use here the units-per-em attribute? + + Geom::Rect area( Geom::Point(0,0), Geom::Point(1,1) ); //I need help here! (reaction: note that the 'area' parameter is an *optional* rect, so you can pass an empty Geom::OptRect() ) + + feed_pathvector_to_cairo (cr, *pathv, s, area, false, 0); + cairo_fill(cr); + } +} + +void +SvgFont::glyph_modified(SPObject* /* blah */, unsigned int /* bleh */){ + this->refresh(); + //TODO: update rendering on svgfonts preview widget (in the svg fonts dialog) +} + +Geom::PathVector +SvgFont::flip_coordinate_system(SPFont* spfont, Geom::PathVector pathv){ + double units_per_em = 1000; + SPObject* obj; + for (obj = ((SPObject*) spfont)->children; obj; obj=obj->next){ + if (SP_IS_FONTFACE(obj)){ + //XML Tree being directly used here while it shouldn't be. + sp_repr_get_double(obj->getRepr(), "units_per_em", &units_per_em); + } + } + + double baseline_offset = units_per_em - spfont->horiz_origin_y; + + //This matrix flips y-axis and places the origin at baseline + Geom::Affine m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(baseline_offset)); + return pathv*m; +} + cairo_status_t SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/, unsigned long glyph, @@ -234,37 +283,53 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/, node = (SPObject*) this->glyphs[glyph]; } + if (!SP_IS_GLYPH(node) && !SP_IS_MISSING_GLYPH(node)) { + return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return? + } + + SPFont* spfont = (SPFont*) node->parent; + if (!spfont) { + return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return? + } + //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node // or using the d attribute of a glyph node. // pathv stores the path description from the d attribute: Geom::PathVector pathv; if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) { pathv = sp_svg_read_pathv(((SPGlyph*)node)->d); + pathv = flip_coordinate_system(spfont, pathv); + this->render_glyph_path(cr, &pathv); } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) { pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d); - } else { - return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return? + pathv = flip_coordinate_system(spfont, pathv); + this->render_glyph_path(cr, &pathv); } - if (!pathv.empty()){ - //This glyph has a path description on its d attribute, so we render it: - cairo_new_path(cr); - //adjust scale of the glyph -// Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x); - Geom::Scale s(1.0/1000);//TODO: use here the units-per-em attribute? - //This matrix flips the glyph vertically - Geom::Affine m(Geom::Coord(1),Geom::Coord(0),Geom::Coord(0),Geom::Coord(-1),Geom::Coord(0),Geom::Coord(0)); - //then we offset it -// pathv += Geom::Point(Geom::Coord(0),Geom::Coord(-((SPFont*) node->parent)->horiz_adv_x)); - pathv += Geom::Point(Geom::Coord(0),Geom::Coord(-1000));//TODO: use here the units-per-em attribute? - - Geom::Rect area( Geom::Point(0,0), Geom::Point(1,1) ); //I need help here! (reaction: note that the 'area' parameter is an *optional* rect, so you can pass an empty Geom::OptRect() ) + if (node->hasChildren()){ + //render the SVG described on this glyph's child nodes. + for(node = node->children; node; node=node->next){ + if (SP_IS_PATH(node)){ + pathv = ((SPShape*)node)->curve->get_pathvector(); + pathv = flip_coordinate_system(spfont, pathv); + this->render_glyph_path(cr, &pathv); + } + if (SP_IS_OBJECTGROUP(node)){ + g_warning("TODO: svgfonts: render OBJECTGROUP"); + } + if (SP_IS_USE(node)){ + SPItem* item = SP_USE(node)->ref->getObject(); + if (SP_IS_PATH(item)){ + pathv = ((SPShape*)item)->curve->get_pathvector(); + pathv = flip_coordinate_system(spfont, pathv); + this->render_glyph_path(cr, &pathv); + } - feed_pathvector_to_cairo (cr, pathv, s*m, area, false, 0); - cairo_fill(cr); + glyph_modified_connection = ((SPObject*) item)->connectModified(sigc::mem_fun(*this, &SvgFont::glyph_modified)); + } + } } - //TODO: render the SVG described on this glyph's child nodes. return CAIRO_STATUS_SUCCESS; } diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h index ddf4ba327..3cfcbaa72 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -19,6 +19,7 @@ #include "../sp-missing-glyph.h" #include "../sp-font.h" #include "../sp-glyph-kerning.h" +#include <sigc++/sigc++.h> class SvgFont; struct SPFont; @@ -38,11 +39,16 @@ cairo_status_t scaled_font_init (cairo_scaled_font_t *scaled_font, cairo_font_ex cairo_status_t scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, const char *utf8, int utf8_len, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, cairo_text_cluster_flags_t *flags); cairo_status_t scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics); +Geom::PathVector flip_coordinate_system(SPFont* spfont, Geom::PathVector pathv); +void render_glyph_path(cairo_t* cr, Geom::PathVector* pathv); +void glyph_modified(SPObject *, unsigned int); + private: SPFont* font; UserFont* userfont; std::vector<SPGlyph*> glyphs; SPMissingGlyph* missingglyph; +sigc::connection glyph_modified_connection; bool drawing_expose_cb (Gtk::Widget *widget, GdkEventExpose *event, gpointer data); }; diff --git a/src/display/pixblock-scaler.cpp b/src/display/pixblock-scaler.cpp deleted file mode 100644 index 5af5230d8..000000000 --- a/src/display/pixblock-scaler.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#define __NR_PIXBLOCK_SCALER_CPP__ - -/* - * Functions for blitting pixblocks using scaling - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2006,2009 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <glib.h> -#include <cmath> -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif -using std::floor; - -#include "display/nr-filter-utils.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" -#include <2geom/forward.h> - -namespace NR { - -struct RGBA { - double r, g, b, a; -}; - -/** Calculates cubically interpolated value of the four given pixel values. - * The pixel values should be from four adjacent pixels in source image or - * four adjacent interpolated values. len should be the x- or y-coordinate - * (depending on interpolation direction) of the center of the target pixel - * in source image coordinates. - */ -__attribute__ ((const)) -inline static double sample(double const a, double const b, - double const c, double const d, - double const len) -{ - double lena = 1.5 + (len - round(len)); - double lenb = 0.5 + (len - round(len)); - double lenc = 0.5 - (len - round(len)); - double lend = 1.5 - (len - round(len)); - double const f = -0.5; // corresponds to cubic Hermite spline - double sum = 0; - sum += ((((f * lena) - 5.0 * f) * lena + 8.0 * f) * lena - 4 * f) * a; - sum += (((f + 2.0) * lenb - (f + 3.0)) * lenb * lenb + 1.0) * b; - sum += (((f + 2.0) * lenc - (f + 3.0)) * lenc * lenc + 1.0) * c; - sum += ((((f * lend) - 5.0 * f) * lend + 8.0 * f) * lend - 4 * f) * d; - - return sum; -} - -/** - * Sanity check function for indexing pixblocks. - * Catches reading and writing outside the pixblock area. - * When enabled, decreases filter rendering speed massively. - */ -inline static void _check_index(NRPixBlock const * const pb, int const location, int const line) -{ - if(false) { - int max_loc = pb->rs * (pb->area.y1 - pb->area.y0); - if (location < 0 || (location + 4) > max_loc) - g_warning("Location %d out of bounds (0 ... %d) at line %d", location, max_loc, line); - } -} - -static void scale_bicubic_rgba(NRPixBlock *to, NRPixBlock *from, - Geom::Affine const &trans) -{ - if (NR_PIXBLOCK_BPP(from) != 4 || NR_PIXBLOCK_BPP(to) != 4) { - g_warning("A non-32-bpp image passed to scale_bicubic_rgba: scaling aborted."); - return; - } - - bool free_from_on_exit = false; - if (from->mode != to->mode){ - NRPixBlock *o_from = from; - from = new NRPixBlock; - nr_pixblock_setup_fast(from, to->mode, o_from->area.x0, o_from->area.y0, o_from->area.x1, o_from->area.y1, false); - nr_blit_pixblock_pixblock(from, o_from); - free_from_on_exit = true; - } - - // Precalculate sizes of source and destination pixblocks - int from_width = from->area.x1 - from->area.x0; - int from_height = from->area.y1 - from->area.y0; - int to_width = to->area.x1 - to->area.x0; - int to_height = to->area.y1 - to->area.y0; - - // from_step: when advancing one pixel in destination image, - // how much we should advance in source image - double from_stepx = 1.0 / trans[0]; - double from_stepy = 1.0 / trans[3]; - double from_diffx = from_stepx * (-trans[4]); - double from_diffy = from_stepy * (-trans[5]); - from_diffx = (to->area.x0 * from_stepx + from_diffx) - from->area.x0; - from_diffy = (to->area.y0 * from_stepy + from_diffy) - from->area.y0; - - // Loop through every pixel of destination image, a line at a time - for (int to_y = 0 ; to_y < to_height ; to_y++) { - double from_y = (to_y + 0.5) * from_stepy + from_diffy; - // Pre-calculate beginning of the four horizontal lines, from - // which we should read - int from_line[4]; - for (int i = 0 ; i < 4 ; i++) { - int fy_line = (int)round(from_y) + i - 2; - if (fy_line >= 0) { - if (fy_line < from_height) { - from_line[i] = fy_line * from->rs; - } else { - from_line[i] = (from_height - 1) * from->rs; - } - } else { - from_line[i] = 0; - } - } - // Loop through this horizontal line in destination image - // For every pixel, calculate the color of pixel with - // bicubic interpolation and set the pixel value in destination image - for (int to_x = 0 ; to_x < to_width ; to_x++) { - double from_x = (to_x + 0.5) * from_stepx + from_diffx; - RGBA line[4]; - for (int i = 0 ; i < 4 ; i++) { - int k = (int)round(from_x) + i - 2; - if (k < 0) k = 0; - if (k >= from_width) k = from_width - 1; - k *= 4; - _check_index(from, from_line[0] + k, __LINE__); - _check_index(from, from_line[1] + k, __LINE__); - _check_index(from, from_line[2] + k, __LINE__); - _check_index(from, from_line[3] + k, __LINE__); - line[i].r = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k], - NR_PIXBLOCK_PX(from)[from_line[1] + k], - NR_PIXBLOCK_PX(from)[from_line[2] + k], - NR_PIXBLOCK_PX(from)[from_line[3] + k], - from_y); - line[i].g = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 1], - from_y); - line[i].b = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 2], - from_y); - line[i].a = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 3], - from_y); - } - RGBA result; - result.r = round(sample(line[0].r, line[1].r, line[2].r, line[3].r, - from_x)); - result.g = round(sample(line[0].g, line[1].g, line[2].g, line[3].g, - from_x)); - result.b = round(sample(line[0].b, line[1].b, line[2].b, line[3].b, - from_x)); - result.a = round(sample(line[0].a, line[1].a, line[2].a, line[3].a, - from_x)); - - _check_index(to, to_y * to->rs + to_x * 4, __LINE__); - - using Inkscape::Filters::clamp; - using Inkscape::Filters::clamp_alpha; - if (to->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) { - /* Clamp the colour channels to range from 0 to result.a to - * make sure, we don't exceed 100% per colour channel with - * images that have premultiplied alpha */ - - int const alpha = clamp((int)result.a); - - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] - = clamp_alpha((int)result.r, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] - = clamp_alpha((int)result.g, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] - = clamp_alpha((int)result.b, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] = alpha; - } else { - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] - = clamp((int)result.r); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] - = clamp((int)result.g); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] - = clamp((int)result.b); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] - = clamp((int)result.a); - } - } - } - if (free_from_on_exit) { - nr_pixblock_release(from); - delete from; - } - -} - -void scale_bicubic_alpha(NRPixBlock *to, NRPixBlock *from, - Geom::Affine const &trans) -{ - if (NR_PIXBLOCK_BPP(from) != 1 || NR_PIXBLOCK_BPP(to) != 1) { - g_warning("A non-8-bpp image passed to scale_bicubic_alpha: scaling aborted."); - return; - } - - // Precalculate sizes of source and destination pixblocks - int from_width = from->area.x1 - from->area.x0; - int from_height = from->area.y1 - from->area.y0; - int to_width = to->area.x1 - to->area.x0; - int to_height = to->area.y1 - to->area.y0; - - // from_step: when advancing one pixel in destination image, - // how much we should advance in source image - double from_stepx = 1.0 / trans[0]; - double from_stepy = 1.0 / trans[3]; - double from_diffx = from_stepx * (-trans[4]); - double from_diffy = from_stepy * (-trans[5]); - from_diffx = (to->area.x0 * from_stepx + from_diffx) - from->area.x0; - from_diffy = (to->area.y0 * from_stepy + from_diffy) - from->area.y0; - - // Loop through every pixel of destination image, a line at a time - for (int to_y = 0 ; to_y < to_height ; to_y++) { - double from_y = (to_y + 0.5) * from_stepy - from_diffy; - // Pre-calculate beginning of the four horizontal lines, from - // which we should read - int from_line[4]; - for (int i = 0 ; i < 4 ; i++) { - int fy_line = (int)round(from_y) + i - 2; - if (fy_line >= 0) { - if (fy_line < from_height) { - from_line[i] = fy_line * from->rs; - } else { - from_line[i] = (from_height - 1) * from->rs; - } - } else { - from_line[i] = 0; - } - } - // Loop through this horizontal line in destination image - // For every pixel, calculate the color of pixel with - // bicubic interpolation and set the pixel value in destination image - for (int to_x = 0 ; to_x < to_width ; to_x++) { - double from_x = (to_x + 0.5) * from_stepx - from_diffx; - double line[4]; - for (int i = 0 ; i < 4 ; i++) { - int k = (int)round(from_x) + i - 2; - if (k < 0) k = 0; - if (k >= from_width) k = from_width - 1; - _check_index(from, from_line[0] + k, __LINE__); - _check_index(from, from_line[1] + k, __LINE__); - _check_index(from, from_line[2] + k, __LINE__); - _check_index(from, from_line[3] + k, __LINE__); - line[i] = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k], - NR_PIXBLOCK_PX(from)[from_line[1] + k], - NR_PIXBLOCK_PX(from)[from_line[2] + k], - NR_PIXBLOCK_PX(from)[from_line[3] + k], - from_y); - } - int result; - result = (int)round(sample(line[0], line[1], line[2], line[3], - from_x)); - - _check_index(to, to_y * to->rs + to_x, __LINE__); - - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x] - = Inkscape::Filters::clamp(result); - } - } -} - -void scale_bicubic(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans) -{ - if (NR_PIXBLOCK_BPP(to) == 4 && NR_PIXBLOCK_BPP(from) == 4) { - scale_bicubic_rgba(to, from, trans); - } else if (NR_PIXBLOCK_BPP(to) == 1 && NR_PIXBLOCK_BPP(from) == 1) { - scale_bicubic_alpha(to, from, trans); - } else { - g_warning("NR::scale_bicubic: unsupported bitdepths for scaling: to %d, from %d", NR_PIXBLOCK_BPP(to), NR_PIXBLOCK_BPP(from)); - } -} - -} /* namespace NR */ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/pixblock-scaler.h b/src/display/pixblock-scaler.h deleted file mode 100644 index ff9613ab8..000000000 --- a/src/display/pixblock-scaler.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef __NR_PIXBLOCK_SCALER_H__ -#define __NR_PIXBLOCK_SCALER_H__ - -/* - * Functions for blitting pixblocks using scaling - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2006 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "libnr/nr-pixblock.h" -#include <2geom/forward.h> - -namespace NR { - -/** Blits the second pixblock to the first. - * Image in source pixblock is scaled to the size of destination pixblock - * using bicubic interpolation. - * Source pixblock is not modified in process. - * Only works for 32-bpp images. - */ -void scale_bicubic(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans); - -} /* namespace NR */ - -#endif // __NR_PIXBLOCK_SCALER_H__ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/pixblock-transform.cpp b/src/display/pixblock-transform.cpp deleted file mode 100644 index 5d5b8a9cf..000000000 --- a/src/display/pixblock-transform.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#define __NR_PIXBLOCK_SCALER_CPP__ - -/* - * Functions for blitting pixblocks using matrix transformation - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2006,2009 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <glib.h> -#include <cmath> -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif -using std::floor; - -#include "display/nr-filter-utils.h" - -#include "libnr/nr-blit.h" -#include "libnr/nr-pixblock.h" -#include <2geom/affine.h> - -namespace NR { - -struct RGBA { - double r, g, b, a; -}; -struct RGBAi { - int r, g, b, a; -}; - -/** - * Sanity check function for indexing pixblocks. - * Catches reading and writing outside the pixblock area. - * When enabled, decreases filter rendering speed massively. - */ -inline void _check_index(NRPixBlock const * const pb, int const location, int const line) -{ - if(false) { - int max_loc = pb->rs * (pb->area.y1 - pb->area.y0); - if (location < 0 || (location + 4) > max_loc) - g_warning("Location %d out of bounds (0 ... %d) at line %d", location, max_loc, line); - } -} - -void transform_nearest(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans) -{ - if (NR_PIXBLOCK_BPP(from) != 4 || NR_PIXBLOCK_BPP(to) != 4) { - g_warning("A non-32-bpp image passed to transform_nearest: scaling aborted."); - return; - } - - bool free_from_on_exit = false; - if (from->mode != to->mode){ - NRPixBlock *o_from = from; - from = new NRPixBlock; - nr_pixblock_setup_fast(from, to->mode, o_from->area.x0, o_from->area.y0, o_from->area.x1, o_from->area.y1, false); - nr_blit_pixblock_pixblock(from, o_from); - free_from_on_exit = true; - } - - // Precalculate sizes of source and destination pixblocks - int from_width = from->area.x1 - from->area.x0; - int from_height = from->area.y1 - from->area.y0; - int to_width = to->area.x1 - to->area.x0; - int to_height = to->area.y1 - to->area.y0; - - Geom::Affine itrans = trans.inverse(); - - // Loop through every pixel of destination image, a line at a time - for (int to_y = 0 ; to_y < to_height ; to_y++) { - for (int to_x = 0 ; to_x < to_width ; to_x++) { - RGBAi result = {0,0,0,0}; - - int from_x = (int)floor(itrans[0] * (to_x + 0.5 + to->area.x0) - + itrans[2] * (to_y + 0.5 + to->area.y0) - + itrans[4]); - from_x -= from->area.x0; - int from_y = (int)floor(itrans[1] * (to_x + 0.5 + to->area.x0) - + itrans[3] * (to_y + 0.5 + to->area.y0) - + itrans[5]); - from_y -= from->area.y0; - - if (from_x >= 0 && from_x < from_width - && from_y >= 0 && from_y < from_height) { - _check_index(from, from_y * from->rs + from_x * 4, __LINE__); - result.r = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4]; - result.g = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 1]; - result.b = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 2]; - result.a = NR_PIXBLOCK_PX(from)[from_y * from->rs + from_x * 4 + 3]; - } - - _check_index(to, to_y * to->rs + to_x * 4, __LINE__); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] = result.r; - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] = result.g; - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] = result.b; - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] = result.a; - } - } - if (free_from_on_exit) { - nr_pixblock_release(from); - delete from; - } -} - -/** Calculates cubically interpolated value of the four given pixel values. - * The pixel values should be from four adjacent pixels in source image or - * four adjacent interpolated values. len should be the x- or y-coordinate - * (depending on interpolation direction) of the center of the target pixel - * in source image coordinates. - */ -__attribute__ ((const)) -inline static double sample(double const a, double const b, - double const c, double const d, - double const len) -{ - double lena = 1.5 + (len - round(len)); - double lenb = 0.5 + (len - round(len)); - double lenc = 0.5 - (len - round(len)); - double lend = 1.5 - (len - round(len)); - double const f = -0.5; // corresponds to cubic Hermite spline - double sum = 0; - sum += ((((f * lena) - 5.0 * f) * lena + 8.0 * f) * lena - 4 * f) * a; - sum += (((f + 2.0) * lenb - (f + 3.0)) * lenb * lenb + 1.0) * b; - sum += (((f + 2.0) * lenc - (f + 3.0)) * lenc * lenc + 1.0) * c; - sum += ((((f * lend) - 5.0 * f) * lend + 8.0 * f) * lend - 4 * f) * d; - - return sum; -} - -void transform_bicubic(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans) -{ - if (NR_PIXBLOCK_BPP(from) != 4 || NR_PIXBLOCK_BPP(to) != 4) { - g_warning("A non-32-bpp image passed to transform_bicubic: scaling aborted."); - return; - } - - bool free_from_on_exit = false; - if (from->mode != to->mode){ - NRPixBlock *o_from = from; - from = new NRPixBlock; - nr_pixblock_setup_fast(from, to->mode, o_from->area.x0, o_from->area.y0, o_from->area.x1, o_from->area.y1, false); - nr_blit_pixblock_pixblock(from, o_from); - free_from_on_exit = true; - } - - if (from->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - // TODO: Fix this... (The problem is that for interpolation non-premultiplied colors should be premultiplied...) - g_warning("transform_bicubic does not properly support non-premultiplied images"); - } - - // Precalculate sizes of source and destination pixblocks - int from_width = from->area.x1 - from->area.x0; - int from_height = from->area.y1 - from->area.y0; - int to_width = to->area.x1 - to->area.x0; - int to_height = to->area.y1 - to->area.y0; - - Geom::Affine itrans = trans.inverse(); - - // Loop through every pixel of destination image, a line at a time - for (int to_y = 0 ; to_y < to_height ; to_y++) { - for (int to_x = 0 ; to_x < to_width ; to_x++) { - double from_x = itrans[0] * (to_x + 0.5 + to->area.x0) - + itrans[2] * (to_y + 0.5 + to->area.y0) - + itrans[4] - from->area.x0; - double from_y = itrans[1] * (to_x + 0.5 + to->area.x0) - + itrans[3] * (to_y + 0.5 + to->area.y0) - + itrans[5] - from->area.y0; - - if (from_x < 0 || from_x >= from_width || - from_y < 0 || from_y >= from_height) { - continue; - } - - RGBA line[4]; - - int from_line[4]; - for (int i = 0 ; i < 4 ; i++) { - int fy_line = (int)round(from_y) + i - 2; - if (fy_line >= 0) { - if (fy_line < from_height) { - from_line[i] = fy_line * from->rs; - } else { - from_line[i] = (from_height - 1) * from->rs; - } - } else { - from_line[i] = 0; - } - } - - for (int i = 0 ; i < 4 ; i++) { - int k = (int)round(from_x) + i - 2; - if (k < 0) k = 0; - if (k >= from_width) k = from_width - 1; - k *= 4; - _check_index(from, from_line[0] + k, __LINE__); - _check_index(from, from_line[1] + k, __LINE__); - _check_index(from, from_line[2] + k, __LINE__); - _check_index(from, from_line[3] + k, __LINE__); - line[i].r = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k], - NR_PIXBLOCK_PX(from)[from_line[1] + k], - NR_PIXBLOCK_PX(from)[from_line[2] + k], - NR_PIXBLOCK_PX(from)[from_line[3] + k], - from_y); - line[i].g = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 1], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 1], - from_y); - line[i].b = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 2], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 2], - from_y); - line[i].a = sample(NR_PIXBLOCK_PX(from)[from_line[0] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[1] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[2] + k + 3], - NR_PIXBLOCK_PX(from)[from_line[3] + k + 3], - from_y); - } - RGBA result; - result.r = round(sample(line[0].r, line[1].r, line[2].r, line[3].r, - from_x)); - result.g = round(sample(line[0].g, line[1].g, line[2].g, line[3].g, - from_x)); - result.b = round(sample(line[0].b, line[1].b, line[2].b, line[3].b, - from_x)); - result.a = round(sample(line[0].a, line[1].a, line[2].a, line[3].a, - from_x)); - - using Inkscape::Filters::clamp; - using Inkscape::Filters::clamp_alpha; - _check_index(to, to_y * to->rs + to_x * 4, __LINE__); - if (to->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) { - /* Make sure, none of the RGB channels exceeds 100% intensity - * in premultiplied output */ - int const alpha = clamp((int)result.a); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] = - clamp_alpha((int)result.r, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] = - clamp_alpha((int)result.g, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] = - clamp_alpha((int)result.b, alpha); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] = alpha; - } else { - /* Clamp the output to unsigned char range */ - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4] - = clamp((int)result.r); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 1] - = clamp((int)result.g); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 2] - = clamp((int)result.b); - NR_PIXBLOCK_PX(to)[to_y * to->rs + to_x * 4 + 3] - = clamp((int)result.a); - } - } - } - if (free_from_on_exit) { - nr_pixblock_release(from); - delete from; - } -} - -} /* namespace NR */ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/pixblock-transform.h b/src/display/pixblock-transform.h deleted file mode 100644 index b73435fdf..000000000 --- a/src/display/pixblock-transform.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __NR_PIXBLOCK_TRANSFORM_H__ -#define __NR_PIXBLOCK_TRANSFORM_H__ - -/* - * Functions for blitting pixblocks using matrix transfomation - * - * Author: - * Niko Kiirala <niko@kiirala.com> - * - * Copyright (C) 2006 Niko Kiirala - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "libnr/nr-pixblock.h" -#include <2geom/forward.h> - -namespace NR { - -void transform_nearest(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans); -void transform_bicubic(NRPixBlock *to, NRPixBlock *from, Geom::Affine const &trans); - -} /* namespace NR */ - -#endif // __NR_PIXBLOCK_TRANSFORM_H__ -/* - 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/rendermode.h b/src/display/rendermode.h index 8fc022bfb..cbd35de73 100644 --- a/src/display/rendermode.h +++ b/src/display/rendermode.h @@ -15,10 +15,10 @@ enum RenderMode { RENDERMODE_OUTLINE }; -enum ColorRenderMode { - COLORRENDERMODE_NORMAL, - COLORRENDERMODE_GRAYSCALE, - COLORRENDERMODE_PRINT_COLORS_PREVIEW +enum ColorMode { + COLORMODE_NORMAL, + COLORMODE_GRAYSCALE, + COLORMODE_PRINT_COLORS_PREVIEW }; } diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index c3198cd37..3b9bb57e1 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -1,4 +1,4 @@ -/** \file +/* * Provides a class that shows a temporary indicator on the canvas of where the snap was, and what kind of snap * * Authors: @@ -102,6 +102,15 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPTARGET_PATH_INTERSECTION: target_name = _("path intersection"); break; + case SNAPTARGET_PATH_GUIDE_INTERSECTION: + target_name = _("guide-path intersection"); + break; + case SNAPTARGET_PATH_CLIP: + target_name = _("clip-path"); + break; + case SNAPTARGET_PATH_MASK: + target_name = _("mask-path"); + break; case SNAPTARGET_BBOX_CORNER: target_name = _("bounding box corner"); break; @@ -120,9 +129,6 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPTARGET_ROTATION_CENTER: target_name = _("object rotation center"); break; - case SNAPTARGET_HANDLE: - target_name = _("handle"); - break; case SNAPTARGET_BBOX_EDGE_MIDPOINT: target_name = _("bounding box side midpoint"); break; @@ -132,18 +138,16 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPTARGET_PAGE_CORNER: target_name = _("page corner"); break; - case SNAPTARGET_CONVEX_HULL_CORNER: - target_name = _("convex hull corner"); - break; case SNAPTARGET_ELLIPSE_QUADRANT_POINT: target_name = _("quadrant point"); break; - case SNAPTARGET_CENTER: - target_name = _("center"); - break; - case SNAPTARGET_CORNER: + case SNAPTARGET_RECT_CORNER: + case SNAPTARGET_IMG_CORNER: target_name = _("corner"); break; + case SNAPTARGET_TEXT_ANCHOR: + target_name = _("text anchor"); + break; case SNAPTARGET_TEXT_BASELINE: target_name = _("text baseline"); break; @@ -206,14 +210,12 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap case SNAPSOURCE_ELLIPSE_QUADRANT_POINT: source_name = _("Quadrant point"); break; - case SNAPSOURCE_CENTER: - source_name = _("Center"); - break; - case SNAPSOURCE_CORNER: + case SNAPSOURCE_RECT_CORNER: + case SNAPSOURCE_IMG_CORNER: source_name = _("Corner"); break; - case SNAPSOURCE_TEXT_BASELINE: - source_name = _("Text baseline"); + case SNAPSOURCE_TEXT_ANCHOR: + source_name = _("Text anchor"); break; case SNAPSOURCE_GRID_PITCH: source_name = _("Multiple of grid spacing"); @@ -308,7 +310,7 @@ SnapIndicator::set_new_snapsource(Inkscape::SnapCandidatePoint const &p) { remove_snapsource(); - g_assert(_desktop != NULL); + g_assert(_desktop != NULL); // If this fails, then likely setup() has not been called on the snap manager (see snap.cpp -> setup()) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool value = prefs->getBool("/options/snapindicator/value", true); @@ -330,6 +332,25 @@ SnapIndicator::set_new_snapsource(Inkscape::SnapCandidatePoint const &p) } void +SnapIndicator::set_new_debugging_point(Geom::Point const &p) +{ + g_assert(_desktop != NULL); + SPCanvasItem * canvasitem = sp_canvas_item_new( sp_desktop_tempgroup (_desktop), + SP_TYPE_CTRL, + "anchor", GTK_ANCHOR_CENTER, + "size", 10.0, + "fill_color", 0x00ff00ff, + "stroked", FALSE, + "mode", SP_KNOT_MODE_XOR, + "shape", SP_KNOT_SHAPE_DIAMOND, + NULL ); + + SP_CTRL(canvasitem)->moveto(p); + _debugging_points.push_back(_desktop->add_temporary_canvasitem(canvasitem, 5000)); + +} + +void SnapIndicator::remove_snapsource() { if (_snapsource) { @@ -338,6 +359,16 @@ SnapIndicator::remove_snapsource() } } +void +SnapIndicator::remove_debugging_points() +{ + for (std::list<TemporaryItem *>::const_iterator i = _debugging_points.begin(); i != _debugging_points.end(); ++i) { + _desktop->remove_temporary_canvasitem(*i); + } + _debugging_points.clear(); +} + + } //namespace Display } /* namespace Inkscape */ diff --git a/src/display/snap-indicator.h b/src/display/snap-indicator.h index d60ff1481..30040d99c 100644 --- a/src/display/snap-indicator.h +++ b/src/display/snap-indicator.h @@ -1,9 +1,11 @@ #ifndef INKSCAPE_DISPLAY_SNAP_INDICATOR_H #define INKSCAPE_DISPLAY_SNAP_INDICATOR_H -/** \file +/** + * @file * Provides a class that shows a temporary indicator on the canvas of where the snap was, and what kind of snap - * + */ +/* * Authors: * Johan Engelen * Diederik van Lierop @@ -14,9 +16,10 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "forward.h" #include "snapped-point.h" +class SPDesktop; + namespace Inkscape { namespace Display { @@ -33,11 +36,15 @@ public: void set_new_snapsource(Inkscape::SnapCandidatePoint const &p); void remove_snapsource(); + void set_new_debugging_point(Geom::Point const &p); + void remove_debugging_points(); + protected: TemporaryItem *_snaptarget; TemporaryItem *_snaptarget_tooltip; TemporaryItem *_snaptarget_bbox; TemporaryItem *_snapsource; + std::list<TemporaryItem *> _debugging_points; bool _snaptarget_is_presnap; SPDesktop *_desktop; diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 202fc206e..a23cbd745 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -11,7 +11,7 @@ #include <2geom/transforms.h> #include "sp-canvas-util.h" #include "sodipodi-ctrl.h" -#include "libnr/nr-pixops.h" +#include "display/cairo-utils.h" enum { ARG_0, @@ -40,10 +40,10 @@ static double sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **a static SPCanvasItemClass *parent_class; -GtkType +GType sp_ctrl_get_type (void) { - static GtkType ctrl_type = 0; + static GType ctrl_type = 0; if (!ctrl_type) { static GTypeInfo const ctrl_info = { sizeof (SPCtrlClass), @@ -71,17 +71,17 @@ sp_ctrl_class_init (SPCtrlClass *klass) object_class = (GtkObjectClass *) klass; item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass *)gtk_type_class (sp_canvas_item_get_type ()); + parent_class = (SPCanvasItemClass *)g_type_class_peek_parent (klass); - gtk_object_add_arg_type ("SPCtrl::shape", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_SHAPE); - gtk_object_add_arg_type ("SPCtrl::mode", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MODE); - gtk_object_add_arg_type ("SPCtrl::anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_ANCHOR); - gtk_object_add_arg_type ("SPCtrl::size", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_SIZE); - gtk_object_add_arg_type ("SPCtrl::pixbuf", GTK_TYPE_POINTER, GTK_ARG_READWRITE, ARG_PIXBUF); - gtk_object_add_arg_type ("SPCtrl::filled", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_FILLED); - gtk_object_add_arg_type ("SPCtrl::fill_color", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_FILL_COLOR); - gtk_object_add_arg_type ("SPCtrl::stroked", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_STROKED); - gtk_object_add_arg_type ("SPCtrl::stroke_color", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_STROKE_COLOR); + gtk_object_add_arg_type ("SPCtrl::shape", G_TYPE_INT, G_PARAM_READWRITE, ARG_SHAPE); + gtk_object_add_arg_type ("SPCtrl::mode", G_TYPE_INT, G_PARAM_READWRITE, ARG_MODE); + gtk_object_add_arg_type ("SPCtrl::anchor", GTK_TYPE_ANCHOR_TYPE, G_PARAM_READWRITE, ARG_ANCHOR); + gtk_object_add_arg_type ("SPCtrl::size", G_TYPE_DOUBLE, G_PARAM_READWRITE, ARG_SIZE); + gtk_object_add_arg_type ("SPCtrl::pixbuf", G_TYPE_POINTER, G_PARAM_READWRITE, ARG_PIXBUF); + gtk_object_add_arg_type ("SPCtrl::filled", G_TYPE_BOOLEAN, G_PARAM_READWRITE, ARG_FILLED); + gtk_object_add_arg_type ("SPCtrl::fill_color", G_TYPE_INT, G_PARAM_READWRITE, ARG_FILL_COLOR); + gtk_object_add_arg_type ("SPCtrl::stroked", G_TYPE_BOOLEAN, G_PARAM_READWRITE, ARG_STROKED); + gtk_object_add_arg_type ("SPCtrl::stroke_color", G_TYPE_INT, G_PARAM_READWRITE, ARG_STROKE_COLOR); object_class->destroy = sp_ctrl_destroy; object_class->set_arg = sp_ctrl_set_arg; @@ -105,9 +105,14 @@ sp_ctrl_init (SPCtrl *ctrl) ctrl->stroked = 0; ctrl->fill_color = 0x000000ff; ctrl->stroke_color = 0x000000ff; - ctrl->_moved = false; - ctrl->box.x0 = ctrl->box.y0 = ctrl->box.x1 = ctrl->box.y1 = 0; + // This way we make sure that the first sp_ctrl_update() call finishes properly; + // in subsequent calls it will not update anything it the control hasn't moved + // Consider for example the case in which a snap indicator is drawn at (0, 0); + // If moveto() is called then it will not set _moved to true because we're initially already at (0, 0) + ctrl->_moved = true; // Is this flag ever going to be set back to false? I can't find where that is supposed to happen + + new (&ctrl->box) Geom::IntRect(0,0,0,0); ctrl->cache = NULL; ctrl->pixbuf = NULL; @@ -125,7 +130,7 @@ sp_ctrl_destroy (GtkObject *object) ctrl = SP_CTRL (object); if (ctrl->cache) { - g_free(ctrl->cache); + delete[] ctrl->cache; ctrl->cache = NULL; } @@ -175,11 +180,12 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) sp_canvas_item_request_update (item); break; - case ARG_FILL_COLOR: - ctrl->fill_color = GTK_VALUE_INT (*arg); + case ARG_FILL_COLOR: { + guint32 fill = GTK_VALUE_INT (*arg); + ctrl->fill_color = fill; ctrl->build = FALSE; sp_canvas_item_request_update (item); - break; + } break; case ARG_STROKED: ctrl->stroked = GTK_VALUE_BOOL (*arg); @@ -187,11 +193,12 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) sp_canvas_item_request_update (item); break; - case ARG_STROKE_COLOR: - ctrl->stroke_color = GTK_VALUE_INT (*arg); + case ARG_STROKE_COLOR: { + guint32 stroke = GTK_VALUE_INT (*arg); + ctrl->stroke_color = stroke; ctrl->build = FALSE; sp_canvas_item_request_update (item); - break; + } break; case ARG_PIXBUF: pixbuf = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg)); @@ -225,7 +232,7 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla if (!ctrl->_moved) return; if (ctrl->shown) { - sp_canvas_request_redraw (item->canvas, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1); + sp_canvas_request_redraw (item->canvas, ctrl->box.left(), ctrl->box.top(), ctrl->box.right() + 1, ctrl->box.bottom() + 1); } if (!ctrl->defined) return; @@ -271,12 +278,8 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla break; } - ctrl->box.x0 = x; - ctrl->box.y0 = y; - ctrl->box.x1 = ctrl->box.x0 + 2 * ctrl->span; - ctrl->box.y1 = ctrl->box.y0 + 2 * ctrl->span; - - sp_canvas_update_bbox (item, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1); + ctrl->box = Geom::IntRect::from_xywh(x, y, 2*ctrl->span, 2*ctrl->span); + sp_canvas_update_bbox (item, ctrl->box.left(), ctrl->box.top(), ctrl->box.right() + 1, ctrl->box.bottom() + 1); } static double @@ -286,63 +289,63 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) *actual_item = item; - double const x = p[Geom::X]; - double const y = p[Geom::Y]; - - if ((x >= ctrl->box.x0) && (x <= ctrl->box.x1) && (y >= ctrl->box.y0) && (y <= ctrl->box.y1)) return 0.0; - + if (ctrl->box.contains(p.floor())) return 0.0; return 1e18; } static void sp_ctrl_build_cache (SPCtrl *ctrl) { - guchar * p, *q; + guint32 *p, *q; gint size, x, y, z, s, a, side, c; - guint8 fr, fg, fb, fa, sr, sg, sb, sa; + guint32 stroke_color, fill_color; if (ctrl->filled) { - fr = (ctrl->fill_color >> 24) & 0xff; - fg = (ctrl->fill_color >> 16) & 0xff; - fb = (ctrl->fill_color >> 8) & 0xff; - fa = (ctrl->fill_color) & 0xff; + if (ctrl->mode == SP_CTRL_MODE_XOR) { + fill_color = ctrl->fill_color; + } else { + fill_color = argb32_from_rgba(ctrl->fill_color); + } } else { - fr = 0x00; fg = 0x00; fb = 0x00; fa = 0x00; + fill_color = 0; } if (ctrl->stroked) { - sr = (ctrl->stroke_color >> 24) & 0xff; - sg = (ctrl->stroke_color >> 16) & 0xff; - sb = (ctrl->stroke_color >> 8) & 0xff; - sa = (ctrl->stroke_color) & 0xff; + if (ctrl->mode == SP_CTRL_MODE_XOR) { + stroke_color = ctrl->stroke_color; + } else { + stroke_color = argb32_from_rgba(ctrl->stroke_color); + } } else { - sr = fr; sg = fg; sb = fb; sa = fa; + stroke_color = fill_color; } side = (ctrl->span * 2 +1); - c = ctrl->span ; - size = (side) * (side) * 4; + c = ctrl->span; + size = side * side; if (side < 2) return; - if (ctrl->cache) - g_free (ctrl->cache); - ctrl->cache = (guchar*)g_malloc (size); + if (ctrl->cache) delete[] ctrl->cache; + ctrl->cache = new guint32[size]; switch (ctrl->shape) { case SP_CTRL_SHAPE_SQUARE: p = ctrl->cache; + // top edge for (x=0; x < side; x++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } + // middle for (y = 2; y < side; y++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; // stroke at first and last pixel for (x=2; x < side; x++) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; + *p++ = fill_color; // fill in the middle } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } + // bottom edge for (x=0; x < side; x++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } ctrl->build = TRUE; break; @@ -352,19 +355,20 @@ sp_ctrl_build_cache (SPCtrl *ctrl) for (y = 0; y < side; y++) { z = abs (c - y); for (x = 0; x < z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; for (; x < side - z -1; x++) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; + *p++ = fill_color; } if (z != c) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; } for (; x < side; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } } + ctrl->build = TRUE; break; case SP_CTRL_SHAPE_CIRCLE: @@ -376,28 +380,28 @@ sp_ctrl_build_cache (SPCtrl *ctrl) z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a)); x = 0; while (x < c-z) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; - *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; + *p++ = 0; + *q-- = 0; x++; } do { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; - *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr; + *p++ = stroke_color; + *q-- = stroke_color; x++; } while (x < c-s); while (x < MIN(c+s+1, c+z)) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; - *q-- = fa; *q-- = fb; *q-- = fg; *q-- = fr; + *p++ = fill_color; + *q-- = fill_color; x++; } do { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; - *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr; + *p++ = stroke_color; + *q-- = stroke_color; x++; } while (x <= c+z); while (x < side) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; - *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; + *p++ = 0; + *q-- = 0; x++; } s = z; @@ -410,17 +414,17 @@ sp_ctrl_build_cache (SPCtrl *ctrl) for (y = 0; y < side; y++) { z = abs (c - y); for (x = 0; x < c-z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; for (; x < c + z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } if (z != 0) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; } for (; x < side; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } } ctrl->build = TRUE; @@ -433,28 +437,19 @@ sp_ctrl_build_cache (SPCtrl *ctrl) px = gdk_pixbuf_get_pixels (ctrl->pixbuf); rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); for (y = 0; y < side; y++){ - unsigned char *s, *d; + guint32 *d; + unsigned char *s; s = px + y * rs; - d = ctrl->cache + 4 * side * y; + d = ctrl->cache + side * y; for (x = 0; x < side; x++) { if (s[3] < 0x80) { - d[0] = 0x00; - d[1] = 0x00; - d[2] = 0x00; - d[3] = 0x00; + *d++ = 0; } else if (s[0] < 0x80) { - d[0] = sr; - d[1] = sg; - d[2] = sb; - d[3] = sa; + *d++ = stroke_color; } else { - d[0] = fr; - d[1] = fg; - d[2] = fb; - d[3] = fa; + *d++ = fill_color; } s += 4; - d += 4; } } } else { @@ -466,16 +461,13 @@ sp_ctrl_build_cache (SPCtrl *ctrl) case SP_CTRL_SHAPE_IMAGE: if (ctrl->pixbuf) { guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf); - guchar * pix; - q = gdk_pixbuf_get_pixels (ctrl->pixbuf); + guint32 *px; + guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf); p = ctrl->cache; for (y = 0; y < side; y++){ - pix = q + (y * r); + px = reinterpret_cast<guint32*>(data + y * r); for (x = 0; x < side; x++) { - *p++ = *pix++; - *p++ = *pix++; - *p++ = *pix++; - *p++ = *pix++; + *p++ = *px++; } } } else { @@ -487,65 +479,84 @@ sp_ctrl_build_cache (SPCtrl *ctrl) default: break; } - } -// composite background, foreground, alpha for xor mode -#define COMPOSE_X(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) ((b ^ ~f) + b/4 - (b>127? 63 : 0))) * ((guchar) a) ) ) -// composite background, foreground, alpha for color mode -#define COMPOSE_N(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) f) * ((guchar) a) ) ) +static inline guint32 compose_xor(guint32 bg, guint32 fg, guint32 a) +{ + guint32 c = bg * (255-a) + (((bg ^ ~fg) + (bg >> 2) - (bg > 127 ? 63 : 0)) & 255) * a; + return (c + 127) / 255; +} static void sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf) { - gint y0, y1, y, x0,x1,x; - guchar *p, *q, a; + //gint y0, y1, y, x0,x1,x; + //guchar *p, *q, a; SPCtrl *ctrl = SP_CTRL (item); if (!ctrl->defined) return; if ((!ctrl->filled) && (!ctrl->stroked)) return; - sp_canvas_prepare_buffer (buf); - // the control-image is rendered into ctrl->cache if (!ctrl->build) { sp_ctrl_build_cache (ctrl); } - // then we render from ctrl->cache - y0 = MAX (ctrl->box.y0, buf->rect.y0); - y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1); - x0 = MAX (ctrl->box.x0, buf->rect.x0); - x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1); - - bool colormode; - - for (y = y0; y <= y1; y++) { - p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4; - q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4; - for (x = x0; x <= x1; x++) { - a = *(q + 3); - // 00000000 is the only way to get invisible; all other colors with alpha 00 are treated as mode_color with alpha ff - colormode = false; - if (a == 0x00 && !(q[0] == 0x00 && q[1] == 0x00 && q[2] == 0x00)) { - a = 0xff; - colormode = true; - } - if (ctrl->mode == SP_CTRL_MODE_COLOR || colormode) { - p[0] = COMPOSE_N (p[0], q[0], a); - p[1] = COMPOSE_N (p[1], q[1], a); - p[2] = COMPOSE_N (p[2], q[2], a); - q += 4; - p += 4; - } else if (ctrl->mode == SP_CTRL_MODE_XOR) { - p[0] = COMPOSE_X (p[0], q[0], a); - p[1] = COMPOSE_X (p[1], q[1], a); - p[2] = COMPOSE_X (p[2], q[2], a); - q += 4; - p += 4; + int w, h; + w = h = (ctrl->span * 2 +1); + + // The code below works even when the target is not an image surface + if (ctrl->mode == SP_CTRL_MODE_XOR) { + // 1. Copy the affected part of output to a temporary surface + cairo_surface_t *work = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cairo_t *cr = cairo_create(work); + cairo_translate(cr, -ctrl->box.left(), -ctrl->box.top()); + cairo_set_source_surface(cr, cairo_get_target(buf->ct), buf->rect.left(), buf->rect.top()); + cairo_paint(cr); + cairo_destroy(cr); + + // 2. Composite the control on a temporary surface + cairo_surface_flush(work); + int strideb = cairo_image_surface_get_stride(work); + unsigned char *pxb = cairo_image_surface_get_data(work); + guint32 *p = ctrl->cache; + for (int i=0; i<h; ++i) { + guint32 *pb = reinterpret_cast<guint32*>(pxb + i*strideb); + for (int j=0; j<w; ++j) { + guint32 cc = *p++; + guint32 ac = cc & 0xff; + if (ac == 0 && cc != 0) { + *pb++ = argb32_from_rgba(cc | 0x000000ff); + } else { + EXTRACT_ARGB32(*pb, ab,rb,gb,bb) + guint32 ro = compose_xor(rb, (cc & 0xff000000) >> 24, ac); + guint32 go = compose_xor(gb, (cc & 0x00ff0000) >> 16, ac); + guint32 bo = compose_xor(bb, (cc & 0x0000ff00) >> 8, ac); + ASSEMBLE_ARGB32(px, ab,ro,go,bo) + *pb++ = px; + } } } + cairo_surface_mark_dirty(work); + + // 3. Replace the affected part of output with contents of temporary surface + cairo_save(buf->ct); + cairo_set_source_surface(buf->ct, work, + ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top()); + cairo_rectangle(buf->ct, ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top(), w, h); + cairo_clip(buf->ct); + cairo_set_operator(buf->ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(buf->ct); + cairo_restore(buf->ct); + cairo_surface_destroy(work); + } else { + cairo_surface_t *cache = cairo_image_surface_create_for_data( + reinterpret_cast<unsigned char*>(ctrl->cache), CAIRO_FORMAT_ARGB32, w, h, w*4); + cairo_set_source_surface(buf->ct, cache, + ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top()); + cairo_paint(buf->ct); + cairo_surface_destroy(cache); } ctrl->shown = TRUE; } diff --git a/src/display/sodipodi-ctrl.h b/src/display/sodipodi-ctrl.h index a708ad41b..2617e7db2 100644 --- a/src/display/sodipodi-ctrl.h +++ b/src/display/sodipodi-ctrl.h @@ -7,17 +7,16 @@ * */ -#include <gtk/gtkenums.h> +#include <gtk/gtk.h> #include <gdk-pixbuf/gdk-pixbuf.h> -#include <libnr/nr-rect-l.h> #include "sp-canvas-item.h" #define SP_TYPE_CTRL (sp_ctrl_get_type ()) -#define SP_CTRL(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CTRL, SPCtrl)) -#define SP_CTRL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_CTRL, SPCtrlClass)) -#define SP_IS_CTRL(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CTRL)) -#define SP_IS_CTRL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_CTRL)) +#define SP_CTRL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRL, SPCtrl)) +#define SP_CTRL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CTRL, SPCtrlClass)) +#define SP_IS_CTRL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRL)) +#define SP_IS_CTRL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CTRL)) typedef enum { SP_CTRL_SHAPE_SQUARE, @@ -48,8 +47,8 @@ struct SPCtrl : public SPCanvasItem { guint32 stroke_color; bool _moved; - NRRectL box; /* NB! x1 & y1 are included */ - guchar *cache; + Geom::IntRect box; /* NB! x1 & y1 are included */ + guint32 *cache; GdkPixbuf * pixbuf; void moveto(Geom::Point const p); @@ -62,7 +61,7 @@ struct SPCtrlClass : public SPCanvasItemClass{ /* Standard Gtk function */ -GtkType sp_ctrl_get_type (void); +GType sp_ctrl_get_type (void); #endif /* !INKSCAPE_CTRL_H */ diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp index 380039798..c0e08c00a 100644 --- a/src/display/sodipodi-ctrlrect.cpp +++ b/src/display/sodipodi-ctrlrect.cpp @@ -15,9 +15,9 @@ * */ -#include "sp-canvas-util.h" #include "sodipodi-ctrlrect.h" -#include "libnr/nr-pixops.h" +#include "sp-canvas-util.h" +#include "display/cairo-utils.h" /* * Currently we do not have point method, as it should always be painted @@ -64,7 +64,7 @@ static void sp_ctrlrect_class_init(SPCtrlRectClass *c) GtkObjectClass *object_class = (GtkObjectClass *) c; SPCanvasItemClass *item_class = (SPCanvasItemClass *) c; - parent_class = (SPCanvasItemClass*) gtk_type_class(sp_canvas_item_get_type()); + parent_class = (SPCanvasItemClass*) g_type_class_peek_parent(c); object_class->destroy = sp_ctrlrect_destroy; @@ -84,75 +84,6 @@ static void sp_ctrlrect_destroy(GtkObject *object) } } -/* FIXME: use definitions from somewhere else */ -#define RGBA_R(v) ((v) >> 24) -#define RGBA_G(v) (((v) >> 16) & 0xff) -#define RGBA_B(v) (((v) >> 8) & 0xff) -#define RGBA_A(v) ((v) & 0xff) - -static void sp_ctrlrect_hline(SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba, guint dashed) -{ - if (y >= buf->rect.y0 && y < buf->rect.y1) { - guint const r = RGBA_R(rgba); - guint const g = RGBA_G(rgba); - guint const b = RGBA_B(rgba); - guint const a = RGBA_A(rgba); - gint const x0 = MAX(buf->rect.x0, xs); - gint const x1 = MIN(buf->rect.x1, xe + 1); - guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4; - for (gint x = x0; x < x1; x++) { - if (!dashed || ((x / DASH_LENGTH) % 2)) { - p[0] = INK_COMPOSE(r, a, p[0]); - p[1] = INK_COMPOSE(g, a, p[1]); - p[2] = INK_COMPOSE(b, a, p[2]); - } - p += 4; - } - } -} - -static void sp_ctrlrect_vline(SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba, guint dashed) -{ - if (x >= buf->rect.x0 && x < buf->rect.x1) { - guint const r = RGBA_R(rgba); - guint const g = RGBA_G(rgba); - guint const b = RGBA_B(rgba); - guint const a = RGBA_A(rgba); - gint const y0 = MAX(buf->rect.y0, ys); - gint const y1 = MIN(buf->rect.y1, ye + 1); - guchar *p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4; - for (gint y = y0; y < y1; y++) { - if (!dashed || ((y / DASH_LENGTH) % 2)) { - p[0] = INK_COMPOSE(r, a, p[0]); - p[1] = INK_COMPOSE(g, a, p[1]); - p[2] = INK_COMPOSE(b, a, p[2]); - } - p += buf->buf_rowstride; - } - } -} - -/** Fills the pixels in [xs, xe)*[ys,ye) clipped to the tile with rgb * a. */ -static void sp_ctrlrect_area(SPCanvasBuf *buf, gint xs, gint ys, gint xe, gint ye, guint32 rgba) -{ - guint const r = RGBA_R(rgba); - guint const g = RGBA_G(rgba); - guint const b = RGBA_B(rgba); - guint const a = RGBA_A(rgba); - gint const x0 = MAX(buf->rect.x0, xs); - gint const x1 = MIN(buf->rect.x1, xe + 1); - gint const y0 = MAX(buf->rect.y0, ys); - gint const y1 = MIN(buf->rect.y1, ye + 1); - for (gint y = y0; y < y1; y++) { - guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4; - for (gint x = x0; x < x1; x++) { - p[0] = INK_COMPOSE(r, a, p[0]); - p[1] = INK_COMPOSE(g, a, p[1]); - p[2] = INK_COMPOSE(b, a, p[2]); - p += 4; - } - } -} static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf) { @@ -173,8 +104,7 @@ void CtrlRect::init() _dashed = false; _shadow = 0; - _area.x0 = _area.y0 = 0; - _area.x1 = _area.y1 = 0; + _area = Geom::OptIntRect(); _rect = Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)); @@ -188,179 +118,190 @@ void CtrlRect::init() void CtrlRect::render(SPCanvasBuf *buf) { - if ((_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) && - (_area.x0 < buf->rect.x1) && - (_area.y0 < buf->rect.y1) && - ((_area.x1 + _shadow_size) >= buf->rect.x0) && - ((_area.y1 + _shadow_size) >= buf->rect.y0)) { - sp_canvas_prepare_buffer(buf); + using Geom::X; + using Geom::Y; + + static double const dashes[2] = {4.0, 4.0}; + + if (!_area) { + return; + } + Geom::IntRect area = *_area; + Geom::IntRect area_w_shadow (area[X].min(), area[Y].min(), + area[X].max() + _shadow_size, area[Y].max() + _shadow_size); + if ( area_w_shadow.intersects(buf->rect) ) + { + cairo_save(buf->ct); + cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top()); + cairo_set_line_width(buf->ct, 1); + if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0); + cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(), + area[X].max() - area[X].min(), area[Y].max() - area[Y].min()); - /* Top */ - sp_ctrlrect_hline(buf, _area.y0, _area.x0, _area.x1, _border_color, _dashed); - /* Bottom */ - sp_ctrlrect_hline(buf, _area.y1, _area.x0, _area.x1, _border_color, _dashed); - /* Left */ - sp_ctrlrect_vline(buf, _area.x0, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed); - /* Right */ - sp_ctrlrect_vline(buf, _area.x1, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed); - if (_shadow_size > 0) { - /* Right shadow */ - sp_ctrlrect_area(buf, _area.x1 + 1, _area.y0 + _shadow_size, - _area.x1 + _shadow_size, _area.y1 + _shadow_size, _shadow_color); - /* Bottom shadow */ - sp_ctrlrect_area(buf, _area.x0 + _shadow_size, _area.y1 + 1, - _area.x1, _area.y1 + _shadow_size, _shadow_color); - } if (_has_fill) { - /* Fill */ - sp_ctrlrect_area(buf, _area.x0 + 1, _area.y0 + 1, - _area.x1 - 1, _area.y1 - 1, _fill_color); + ink_cairo_set_source_rgba32(buf->ct, _fill_color); + cairo_fill_preserve(buf->ct); + } + ink_cairo_set_source_rgba32(buf->ct, _border_color); + cairo_stroke(buf->ct); + + if (_shadow_size > 0) { + ink_cairo_set_source_rgba32(buf->ct, _shadow_color); + cairo_rectangle(buf->ct, 1 + area[X].max(), area[Y].min() + _shadow_size, + _shadow_size, area[Y].max() - area[Y].min() + 1); // right shadow + cairo_rectangle(buf->ct, area[X].min() + _shadow_size, 1 + area[Y].max(), + area[X].max() - area[X].min() - _shadow_size + 1, _shadow_size); + cairo_fill(buf->ct); } + cairo_restore(buf->ct); } } void CtrlRect::update(Geom::Affine const &affine, unsigned int flags) { + using Geom::X; + using Geom::Y; + if (((SPCanvasItemClass *) parent_class)->update) { ((SPCanvasItemClass *) parent_class)->update(this, affine, flags); } sp_canvas_item_reset_bounds(this); - NRRectL _area_old; - _area_old.x0 = _area.x0; - _area_old.x1 = _area.x1; - _area_old.y0 = _area.y0; - _area_old.y1 = _area.y1; - Geom::Rect bbox(_rect.min() * affine, _rect.max() * affine); - _area.x0 = (int) floor(bbox.min()[Geom::X] + 0.5); - _area.y0 = (int) floor(bbox.min()[Geom::Y] + 0.5); - _area.x1 = (int) floor(bbox.max()[Geom::X] + 0.5); - _area.y1 = (int) floor(bbox.max()[Geom::Y] + 0.5); + Geom::OptIntRect _area_old = _area; + Geom::IntRect area ( (int) floor(bbox.min()[Geom::X] + 0.5), + (int) floor(bbox.min()[Geom::Y] + 0.5), + (int) floor(bbox.max()[Geom::X] + 0.5), + (int) floor(bbox.max()[Geom::Y] + 0.5) ); + _area = area; + Geom::IntRect area_old(0,0,0,0); + if (_area_old) { // this weird construction is because the code below assumes _area_old to be 'valid' + area_old = *_area_old; + } gint _shadow_size_old = _shadow_size; _shadow_size = _shadow; // FIXME: we don't process a possible change in _has_fill if (_has_fill) { - if (_area_old.x0 != 0 || _area_old.x1 != 0 || _area_old.y0 != 0 || _area_old.y1 != 0) { + if (_area_old) { sp_canvas_request_redraw(canvas, - _area_old.x0 - 1, _area_old.y0 - 1, - _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1); + area_old[X].min() - 1, area_old[Y].min() - 1, + area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); } - if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) { + if (_area) { sp_canvas_request_redraw(canvas, - _area.x0 - 1, _area.y0 - 1, - _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1); + area[X].min() - 1, area[Y].min() - 1, + area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); } } else { // clear box, be smart about what part of the frame to redraw /* Top */ - if (_area.y0 != _area_old.y0) { // different level, redraw fully old and new - if (_area_old.x0 != _area_old.x1) + if (area[Y].min() != area_old[Y].min()) { // different level, redraw fully old and new + if (area_old[X].min() != area_old[X].max()) sp_canvas_request_redraw(canvas, - _area_old.x0 - 1, _area_old.y0 - 1, - _area_old.x1 + 1, _area_old.y0 + 1); + area_old[X].min() - 1, area_old[Y].min() - 1, + area_old[X].max() + 1, area_old[Y].min() + 1); - if (_area.x0 != _area.x1) + if (area[X].min() != area[X].max()) sp_canvas_request_redraw(canvas, - _area.x0 - 1, _area.y0 - 1, - _area.x1 + 1, _area.y0 + 1); + area[X].min() - 1, area[Y].min() - 1, + area[X].max() + 1, area[Y].min() + 1); } else { // same level, redraw only the ends - if (_area.x0 != _area_old.x0) { + if (area[X].min() != area_old[X].min()) { sp_canvas_request_redraw(canvas, - MIN(_area_old.x0,_area.x0) - 1, _area.y0 - 1, - MAX(_area_old.x0,_area.x0) + 1, _area.y0 + 1); + MIN(area_old[X].min(),area[X].min()) - 1, area[Y].min() - 1, + MAX(area_old[X].min(),area[X].min()) + 1, area[Y].min() + 1); } - if (_area.x1 != _area_old.x1) { + if (area[X].max() != area_old[X].max()) { sp_canvas_request_redraw(canvas, - MIN(_area_old.x1,_area.x1) - 1, _area.y0 - 1, - MAX(_area_old.x1,_area.x1) + 1, _area.y0 + 1); + MIN(area_old[X].max(),area[X].max()) - 1, area[Y].min() - 1, + MAX(area_old[X].max(),area[X].max()) + 1, area[Y].min() + 1); } } /* Left */ - if (_area.x0 != _area_old.x0) { // different level, redraw fully old and new - if (_area_old.y0 != _area_old.y1) + if (area[X].min() != area_old[X].min()) { // different level, redraw fully old and new + if (area_old[Y].min() != area_old[Y].max()) sp_canvas_request_redraw(canvas, - _area_old.x0 - 1, _area_old.y0 - 1, - _area_old.x0 + 1, _area_old.y1 + 1); + area_old[X].min() - 1, area_old[Y].min() - 1, + area_old[X].min() + 1, area_old[Y].max() + 1); - if (_area.y0 != _area.y1) + if (area[Y].min() != area[Y].max()) sp_canvas_request_redraw(canvas, - _area.x0 - 1, _area.y0 - 1, - _area.x0 + 1, _area.y1 + 1); + area[X].min() - 1, area[Y].min() - 1, + area[X].min() + 1, area[Y].max() + 1); } else { // same level, redraw only the ends - if (_area.y0 != _area_old.y0) { + if (area[Y].min() != area_old[Y].min()) { sp_canvas_request_redraw(canvas, - _area.x0 - 1, MIN(_area_old.y0,_area.y0) - 1, - _area.x0 + 1, MAX(_area_old.y0,_area.y0) + 1); + area[X].min() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1, + area[X].min() + 1, MAX(area_old[Y].min(),area[Y].min()) + 1); } - if (_area.y1 != _area_old.y1) { + if (area[Y].max() != area_old[Y].max()) { sp_canvas_request_redraw(canvas, - _area.x0 - 1, MIN(_area_old.y1,_area.y1) - 1, - _area.x0 + 1, MAX(_area_old.y1,_area.y1) + 1); + area[X].min() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1, + area[X].min() + 1, MAX(area_old[Y].max(),area[Y].max()) + 1); } } /* Right */ - if (_area.x1 != _area_old.x1 || _shadow_size_old != _shadow_size) { - if (_area_old.y0 != _area_old.y1) + if (area[X].max() != area_old[X].max() || _shadow_size_old != _shadow_size) { + if (area_old[Y].min() != area_old[Y].max()) sp_canvas_request_redraw(canvas, - _area_old.x1 - 1, _area_old.y0 - 1, - _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1); + area_old[X].max() - 1, area_old[Y].min() - 1, + area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); - if (_area.y0 != _area.y1) + if (area[Y].min() != area[Y].max()) sp_canvas_request_redraw(canvas, - _area.x1 - 1, _area.y0 - 1, - _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1); + area[X].max() - 1, area[Y].min() - 1, + area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); } else { // same level, redraw only the ends - if (_area.y0 != _area_old.y0) { + if (area[Y].min() != area_old[Y].min()) { sp_canvas_request_redraw(canvas, - _area.x1 - 1, MIN(_area_old.y0,_area.y0) - 1, - _area.x1 + _shadow_size + 1, MAX(_area_old.y0,_area.y0) + _shadow_size + 1); + area[X].max() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1, + area[X].max() + _shadow_size + 1, MAX(area_old[Y].min(),area[Y].min()) + _shadow_size + 1); } - if (_area.y1 != _area_old.y1) { + if (area[Y].max() != area_old[Y].max()) { sp_canvas_request_redraw(canvas, - _area.x1 - 1, MIN(_area_old.y1,_area.y1) - 1, - _area.x1 + _shadow_size + 1, MAX(_area_old.y1,_area.y1) + _shadow_size + 1); + area[X].max() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1, + area[X].max() + _shadow_size + 1, MAX(area_old[Y].max(),area[Y].max()) + _shadow_size + 1); } } /* Bottom */ - if (_area.y1 != _area_old.y1 || _shadow_size_old != _shadow_size) { - if (_area_old.x0 != _area_old.x1) + if (area[Y].max() != area_old[Y].max() || _shadow_size_old != _shadow_size) { + if (area_old[X].min() != area_old[X].max()) sp_canvas_request_redraw(canvas, - _area_old.x0 - 1, _area_old.y1 - 1, - _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1); + area_old[X].min() - 1, area_old[Y].max() - 1, + area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); - if (_area.x0 != _area.x1) + if (area[X].min() != area[X].max()) sp_canvas_request_redraw(canvas, - _area.x0 - 1, _area.y1 - 1, - _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1); + area[X].min() - 1, area[Y].max() - 1, + area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); } else { // same level, redraw only the ends - if (_area.x0 != _area_old.x0) { + if (area[X].min() != area_old[X].min()) { sp_canvas_request_redraw(canvas, - MIN(_area_old.x0,_area.x0) - 1, _area.y1 - 1, - MAX(_area_old.x0,_area.x0) + _shadow_size + 1, _area.y1 + _shadow_size + 1); + MIN(area_old[X].min(),area[X].min()) - 1, area[Y].max() - 1, + MAX(area_old[X].min(),area[X].min()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1); } - if (_area.x1 != _area_old.x1) { + if (area[X].max() != area_old[X].max()) { sp_canvas_request_redraw(canvas, - MIN(_area_old.x1,_area.x1) - 1, _area.y1 - 1, - MAX(_area_old.x1,_area.x1) + _shadow_size + 1, _area.y1 + _shadow_size + 1); + MIN(area_old[X].max(),area[X].max()) - 1, area[Y].max() - 1, + MAX(area_old[X].max(),area[X].max()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1); } } } // update SPCanvasItem box - if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) { - x1 = _area.x0 - 1; - y1 = _area.y0 - 1; - x2 = _area.x1 + _shadow_size + 1; - y2 = _area.y1 + _shadow_size + 1; + if (_area) { + x1 = area[X].min() - 1; + y1 = area[Y].min() - 1; + x2 = area[X].max() + _shadow_size + 1; + y2 = area[Y].max() + _shadow_size + 1; } } diff --git a/src/display/sodipodi-ctrlrect.h b/src/display/sodipodi-ctrlrect.h index 2ba73a4c9..65a40a850 100644 --- a/src/display/sodipodi-ctrlrect.h +++ b/src/display/sodipodi-ctrlrect.h @@ -2,9 +2,10 @@ #define SEEN_INKSCAPE_CTRLRECT_H /** - * \file sodipodi-ctrlrect.h - * \brief Simple non-transformed rectangle, usable for rubberband - * + * @file + * Simple non-transformed rectangle, usable for rubberband. + */ +/* * Authors: * Lauris Kaplinski <lauris@ximian.com> * Carl Hetherington <inkscape@carlh.net> @@ -16,17 +17,18 @@ * */ -#include <glib/gtypes.h> +#include <glib.h> #include "sp-canvas-item.h" -#include "libnr/nr-rect-l.h" +#include <2geom/rect.h> +#include <2geom/int-rect.h> struct SPCanvasBuf; #define SP_TYPE_CTRLRECT (sp_ctrlrect_get_type ()) -#define SP_CTRLRECT(obj) (GTK_CHECK_CAST((obj), SP_TYPE_CTRLRECT, CtrlRect)) -#define SP_CTRLRECT_CLASS(c) (GTK_CHECK_CLASS_CAST((c), SP_TYPE_CTRLRECT, SPCtrlRectClass)) -#define SP_IS_CTRLRECT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CTRLRECT)) -#define SP_IS_CTRLRECT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_CTRLRECT)) +#define SP_CTRLRECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CTRLRECT, CtrlRect)) +#define SP_CTRLRECT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST((c), SP_TYPE_CTRLRECT, SPCtrlRectClass)) +#define SP_IS_CTRLRECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRLRECT)) +#define SP_IS_CTRLRECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_CTRLRECT)) class CtrlRect : public SPCanvasItem { @@ -47,7 +49,7 @@ private: Geom::Rect _rect; bool _has_fill; bool _dashed; - NRRectL _area; + Geom::OptIntRect _area; gint _shadow_size; guint32 _border_color; guint32 _fill_color; @@ -57,7 +59,7 @@ private: struct SPCtrlRectClass : public SPCanvasItemClass {}; -GtkType sp_ctrlrect_get_type(); +GType sp_ctrlrect_get_type(); #endif // SEEN_RUBBERBAND_H diff --git a/src/display/sp-canvas-group.h b/src/display/sp-canvas-group.h index 10bf0fa6c..9aa99d563 100644 --- a/src/display/sp-canvas-group.h +++ b/src/display/sp-canvas-group.h @@ -1,9 +1,11 @@ #ifndef SEEN_SP_CANVAS_GROUP_H #define SEEN_SP_CANVAS_GROUP_H -/** \file - * SPCanvasGroup - * +/** + * @file + * SPCanvasGroup. + */ +/* * Authors: * Federico Mena <federico@nuclecu.unam.mx> * Raph Levien <raph@gimp.org> @@ -24,8 +26,8 @@ #include <glib-object.h> #define SP_TYPE_CANVAS_GROUP (sp_canvas_group_get_type()) -#define SP_CANVAS_GROUP(obj) (GTK_CHECK_CAST((obj), SP_TYPE_CANVAS_GROUP, SPCanvasGroup)) -#define SP_IS_CANVAS_GROUP(obj) (GTK_CHECK_TYPE((obj), SP_TYPE_CANVAS_GROUP)) +#define SP_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CANVAS_GROUP, SPCanvasGroup)) +#define SP_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_CANVAS_GROUP)) GType sp_canvas_group_get_type(); diff --git a/src/display/sp-canvas-item.h b/src/display/sp-canvas-item.h index cc0bdfc77..2c1dbdcf0 100644 --- a/src/display/sp-canvas-item.h +++ b/src/display/sp-canvas-item.h @@ -1,9 +1,11 @@ #ifndef SEEN_SP_CANVAS_ITEM_H #define SEEN_SP_CANVAS_ITEM_H -/** \file +/** + * @file * SPCanvasItem. - * + */ +/* * Authors: * Federico Mena <federico@nuclecu.unam.mx> * Raph Levien <raph@gimp.org> @@ -22,10 +24,9 @@ #endif #include <glib-object.h> -#include <gtk/gtkobject.h> -#include <gdk/gdkevents.h> - -#include "2geom/rect.h" +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <2geom/rect.h> G_BEGIN_DECLS @@ -36,9 +37,9 @@ struct SPCanvasGroup; typedef struct _SPCanvasItemClass SPCanvasItemClass; #define SP_TYPE_CANVAS_ITEM (sp_canvas_item_get_type()) -#define SP_CANVAS_ITEM(obj) (GTK_CHECK_CAST((obj), SP_TYPE_CANVAS_ITEM, SPCanvasItem)) -#define SP_IS_CANVAS_ITEM(obj) (GTK_CHECK_TYPE((obj), SP_TYPE_CANVAS_ITEM)) -#define SP_CANVAS_ITEM_GET_CLASS(o) (GTK_CHECK_GET_CLASS((o), SP_TYPE_CANVAS_ITEM, SPCanvasItemClass)) +#define SP_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CANVAS_ITEM, SPCanvasItem)) +#define SP_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_CANVAS_ITEM)) +#define SP_CANVAS_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), SP_TYPE_CANVAS_ITEM, SPCanvasItemClass)) GType sp_canvas_item_get_type(); @@ -65,14 +66,15 @@ struct _SPCanvasItemClass : public GtkObjectClass { double (* point) (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); int (* event) (SPCanvasItem *item, GdkEvent *event); + void (* viewbox_changed) (SPCanvasItem *item, Geom::IntRect const &new_area); }; -SPCanvasItem *sp_canvas_item_new(SPCanvasGroup *parent, GtkType type, const gchar *first_arg_name, ...); +SPCanvasItem *sp_canvas_item_new(SPCanvasGroup *parent, GType type, const gchar *first_arg_name, ...); G_END_DECLS -#define sp_canvas_item_set gtk_object_set +#define sp_canvas_item_set g_object_set void sp_canvas_item_affine_absolute(SPCanvasItem *item, Geom::Affine const &aff); diff --git a/src/display/sp-canvas-util.cpp b/src/display/sp-canvas-util.cpp index 7eac32e2d..78936009b 100644 --- a/src/display/sp-canvas-util.cpp +++ b/src/display/sp-canvas-util.cpp @@ -13,12 +13,10 @@ */ -#include <string.h> // for memset #include <2geom/affine.h> -#include "libnr/nr-pixops.h" -#include "sp-canvas-item.h" #include "sp-canvas-util.h" - +#include "sp-canvas-item.h" +#include "sp-canvas.h" void sp_canvas_update_bbox (SPCanvasItem *item, int x1, int y1, int x2, int y2) @@ -40,41 +38,8 @@ sp_canvas_item_reset_bounds (SPCanvasItem *item) item->y2 = 0.0; } -void -sp_canvas_prepare_buffer (SPCanvasBuf *buf) +void sp_canvas_prepare_buffer(SPCanvasBuf *) { - if (buf->is_empty) { - sp_canvas_clear_buffer(buf); - buf->is_empty = false; - } -} - -void -sp_canvas_clear_buffer (SPCanvasBuf *buf) -{ - unsigned char r, g, b; - - r = (buf->bg_color >> 16) & 0xff; - g = (buf->bg_color >> 8) & 0xff; - b = buf->bg_color & 0xff; - - if ((r != g) || (r != b)) { - int x, y; - for (y = buf->rect.y0; y < buf->rect.y1; y++) { - unsigned char *p; - p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride; - for (x = buf->rect.x0; x < buf->rect.x1; x++) { - *p++ = r; - *p++ = g; - *p++ = b; - } - } - } else { - int y; - for (y = buf->rect.y0; y < buf->rect.y1; y++) { - memset (buf->buf + (y - buf->rect.y0) * buf->buf_rowstride, r, 4 * (buf->rect.x1 - buf->rect.x0)); - } - } } Geom::Affine sp_canvas_item_i2p_affine (SPCanvasItem * item) diff --git a/src/display/sp-canvas-util.h b/src/display/sp-canvas-util.h index f0aa15cb9..41e085a5c 100644 --- a/src/display/sp-canvas-util.h +++ b/src/display/sp-canvas-util.h @@ -21,11 +21,6 @@ void sp_canvas_update_bbox (SPCanvasItem *item, int x1, int y1, int x2, int y2); void sp_canvas_item_reset_bounds (SPCanvasItem *item); void sp_canvas_prepare_buffer (SPCanvasBuf *buf); -/* fill buffer with background color */ - -void -sp_canvas_clear_buffer (SPCanvasBuf * buf); - /* get i2p (item to parent) affine transformation as general 6-element array */ Geom::Affine sp_canvas_item_i2p_affine (SPCanvasItem * item); diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 2d1e57092..add69401b 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1,4 +1,4 @@ -/** \file +/* * Port of GnomeCanvas for Inkscape needs * * Authors: @@ -15,44 +15,32 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +# include <config.h> #endif -#include <libnr/nr-pixblock.h> - -#include <gtk/gtkmain.h> -#include <gtk/gtksignal.h> -#include <gtk/gtkversion.h> - +#include <gtk/gtk.h> #include <gtkmm.h> #include "helper/sp-marshal.h" #include <helper/recthull.h> +#include <2geom/affine.h> #include "display/sp-canvas.h" #include "display/sp-canvas-group.h" -#include <2geom/affine.h> -#include "libnr/nr-convex-hull.h" #include "preferences.h" #include "inkscape.h" #include "sodipodi-ctrlrect.h" -#if ENABLE_LCMS -#include "color-profile-fns.h" -#endif // ENABLE_LCMS +#include "cms-system.h" #include "display/rendermode.h" -#include "libnr/nr-blit.h" -#include "display/inkscape-cairo.h" +#include "display/cairo-utils.h" #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" #include "sp-namedview.h" using Inkscape::Debug::GdkEventLatencyTracker; -// GTK_CHECK_VERSION returns false on failure -#define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0) - // gtk_check_version returns non-NULL on failure static bool const HAS_BROKEN_MOTION_HINTS = - true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS; + true || gtk_check_version(2, 12, 0) != NULL; // Define this to visualize the regions to be redrawn //#define DEBUG_REDRAW 1; @@ -178,15 +166,15 @@ sp_canvas_item_init (SPCanvasItem *item) * Constructs new SPCanvasItem on SPCanvasGroup. */ SPCanvasItem * -sp_canvas_item_new (SPCanvasGroup *parent, GtkType type, gchar const *first_arg_name, ...) +sp_canvas_item_new (SPCanvasGroup *parent, GType type, gchar const *first_arg_name, ...) { va_list args; g_return_val_if_fail (parent != NULL, NULL); g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL); - g_return_val_if_fail (gtk_type_is_a (type, sp_canvas_item_get_type ()), NULL); + g_return_val_if_fail (g_type_is_a (type, sp_canvas_item_get_type ()), NULL); - SPCanvasItem *item = SP_CANVAS_ITEM (gtk_type_new (type)); + SPCanvasItem *item = SP_CANVAS_ITEM (g_object_new (type, NULL)); va_start (args, first_arg_name); sp_canvas_item_construct (item, parent, first_arg_name, args); @@ -320,7 +308,7 @@ sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **a if (SP_CANVAS_ITEM_GET_CLASS (item)->point) return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item); - return NR_HUGE; + return Geom::infinity(); } /** @@ -542,7 +530,7 @@ sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, gu { g_return_val_if_fail (item != NULL, -1); g_return_val_if_fail (SP_IS_CANVAS_ITEM (item), -1); - g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), -1); + g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)), -1); if (item->canvas->grabbed_item) return -1; @@ -633,7 +621,7 @@ sp_canvas_item_grab_focus (SPCanvasItem *item) { g_return_if_fail (item != NULL); g_return_if_fail (SP_IS_CANVAS_ITEM (item)); - g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas))); + g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas))); SPCanvasItem *focused_item = item->canvas->focused_item; @@ -700,6 +688,7 @@ static void sp_canvas_group_destroy (GtkObject *object); static void sp_canvas_group_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf); +static void sp_canvas_group_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area); static SPCanvasItemClass *group_parent_class; @@ -736,13 +725,14 @@ sp_canvas_group_class_init (SPCanvasGroupClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; - group_parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ()); + group_parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_canvas_group_destroy; item_class->update = sp_canvas_group_update; item_class->render = sp_canvas_group_render; item_class->point = sp_canvas_group_point; + item_class->viewbox_changed = sp_canvas_group_viewbox_changed; } /** @@ -875,10 +865,10 @@ sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf) for (GList *list = group->items; list; list = list->next) { SPCanvasItem *child = (SPCanvasItem *)list->data; if (child->flags & SP_CANVAS_ITEM_VISIBLE) { - if ((child->x1 < buf->rect.x1) && - (child->y1 < buf->rect.y1) && - (child->x2 > buf->rect.x0) && - (child->y2 > buf->rect.y0)) { + if ((child->x1 < buf->rect.right()) && + (child->y1 < buf->rect.bottom()) && + (child->x2 > buf->rect.left()) && + (child->y2 > buf->rect.top())) { if (SP_CANVAS_ITEM_GET_CLASS (child)->render) SP_CANVAS_ITEM_GET_CLASS (child)->render (child, buf); } @@ -886,6 +876,20 @@ sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf) } } +static void +sp_canvas_group_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area) +{ + SPCanvasGroup *group = SP_CANVAS_GROUP (item); + + for (GList *list = group->items; list; list = list->next) { + SPCanvasItem *child = (SPCanvasItem *)list->data; + if (child->flags & SP_CANVAS_ITEM_VISIBLE) { + if (SP_CANVAS_ITEM_GET_CLASS (child)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (child)->viewbox_changed (child, new_area); + } + } +} + /** * Adds an item to a canvas group. */ @@ -893,7 +897,7 @@ static void group_add (SPCanvasGroup *group, SPCanvasItem *item) { gtk_object_ref (GTK_OBJECT (item)); - gtk_object_sink (GTK_OBJECT (item)); + g_object_ref_sink (item); if (!group->items) { group->items = g_list_append (group->items, item); @@ -958,8 +962,8 @@ static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event); static GtkWidgetClass *canvas_parent_class; static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int nb); -static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb); -static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val); +static void sp_canvas_dirty_rect(SPCanvas* canvas, Geom::IntRect const &area); +static void sp_canvas_mark_rect(SPCanvas* canvas, Geom::IntRect const &area, uint8_t val); static int do_update (SPCanvas *canvas); /** @@ -998,7 +1002,7 @@ sp_canvas_class_init (SPCanvasClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; - canvas_parent_class = (GtkWidgetClass *)gtk_type_class (GTK_TYPE_WIDGET); + canvas_parent_class = (GtkWidgetClass *)g_type_class_peek_parent (klass); object_class->destroy = sp_canvas_destroy; @@ -1025,20 +1029,20 @@ sp_canvas_class_init (SPCanvasClass *klass) static void sp_canvas_init (SPCanvas *canvas) { - GTK_WIDGET_UNSET_FLAGS (canvas, GTK_NO_WINDOW); - GTK_WIDGET_UNSET_FLAGS (canvas, GTK_DOUBLE_BUFFERED); - GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS); + gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE); + gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE); + gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE); canvas->pick_event.type = GDK_LEAVE_NOTIFY; canvas->pick_event.crossing.x = 0; canvas->pick_event.crossing.y = 0; /* Create the root item as a special case */ - canvas->root = SP_CANVAS_ITEM (gtk_type_new (sp_canvas_group_get_type ())); + canvas->root = SP_CANVAS_ITEM (g_object_new (sp_canvas_group_get_type (), NULL)); canvas->root->canvas = canvas; gtk_object_ref (GTK_OBJECT (canvas->root)); - gtk_object_sink (GTK_OBJECT (canvas->root)); + g_object_ref_sink (canvas->root); canvas->need_repick = TRUE; @@ -1056,7 +1060,7 @@ sp_canvas_init (SPCanvas *canvas) #if ENABLE_LCMS canvas->enable_cms_display_adj = false; - canvas->cms_key = new Glib::ustring(""); + new (&canvas->cms_key) Glib::ustring(""); #endif // ENABLE_LCMS canvas->is_scrolling = false; @@ -1069,7 +1073,7 @@ static void remove_idle (SPCanvas *canvas) { if (canvas->idle_id) { - gtk_idle_remove (canvas->idle_id); + g_source_remove (canvas->idle_id); canvas->idle_id = 0; } } @@ -1115,7 +1119,9 @@ sp_canvas_destroy (GtkObject *object) } shutdown_transients (canvas); - +#if ENABLE_LCMS + canvas->cms_key.~ustring(); +#endif if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object); } @@ -1134,7 +1140,7 @@ static void track_latency(GdkEvent const *event) { GtkWidget * sp_canvas_new_aa (void) { - SPCanvas *canvas = (SPCanvas *)gtk_type_new (sp_canvas_get_type ()); + SPCanvas *canvas = (SPCanvas *)g_object_new (sp_canvas_get_type (), NULL); return (GtkWidget *) canvas; } @@ -1145,8 +1151,6 @@ sp_canvas_new_aa (void) static void sp_canvas_realize (GtkWidget *widget) { - SPCanvas *canvas = SP_CANVAS (widget); - GdkWindowAttr attributes; attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; @@ -1181,9 +1185,7 @@ sp_canvas_realize (GtkWidget *widget) widget->style = gtk_style_attach (widget->style, widget->window); - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - - canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas)); + gtk_widget_set_realized (widget, TRUE); } /** @@ -1200,9 +1202,6 @@ sp_canvas_unrealize (GtkWidget *widget) shutdown_transients (canvas); - gdk_gc_destroy (canvas->pixmap_gc); - canvas->pixmap_gc = NULL; - if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget); } @@ -1227,8 +1226,16 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { SPCanvas *canvas = SP_CANVAS (widget); + Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + widget->allocation.width, widget->allocation.height); + Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + allocation->width, allocation->height); + /* Schedule redraw of new region */ sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height); + if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed (canvas->root, new_area); + if (allocation->width > widget->allocation.width) { sp_canvas_request_redraw (canvas, canvas->x0 + widget->allocation.width, @@ -1246,7 +1253,7 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) widget->allocation = *allocation; - if (GTK_WIDGET_REALIZED (widget)) { + if (gtk_widget_get_realized (widget)) { gdk_window_move_resize (widget->window, widget->allocation.x, widget->allocation.y, widget->allocation.width, widget->allocation.height); @@ -1365,7 +1372,7 @@ emit_event (SPCanvas *canvas, GdkEvent *event) while (item && !finished) { gtk_object_ref (GTK_OBJECT (item)); - gtk_signal_emit (GTK_OBJECT (item), item_signals[ITEM_EVENT], &ev, &finished); + g_signal_emit (G_OBJECT (item), item_signals[ITEM_EVENT], 0, &ev, &finished); SPCanvasItem *parent = item->parent; gtk_object_unref (GTK_OBJECT (item)); item = parent; @@ -1473,9 +1480,6 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event) && (canvas->current_item != NULL) && !canvas->left_grabbed_item) { GdkEvent new_event; - SPCanvasItem *item; - - item = canvas->current_item; new_event = canvas->pick_event; new_event.type = GDK_LEAVE_NOTIFY; @@ -1599,9 +1603,7 @@ sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event) static inline void request_motions(GdkWindow *w, GdkEventMotion *event) { gdk_window_get_pointer(w, NULL, NULL, NULL); -#if HAS_GDK_EVENT_REQUEST_MOTIONS gdk_event_request_motions(event); -#endif } /** @@ -1618,7 +1620,7 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) if (event->window != SP_CANVAS_WINDOW (canvas)) return FALSE; - if (canvas->pixmap_gc == NULL) // canvas being deleted + if (canvas->root == NULL) // canvas being deleted return FALSE; canvas->state = event->state; @@ -1631,143 +1633,99 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) return status; } -static void -sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw) +static void sp_canvas_paint_single_buffer(SPCanvas *canvas, Geom::IntRect const &paint_rect, Geom::IntRect const &canvas_rect, int /*sw*/) { GtkWidget *widget = GTK_WIDGET (canvas); - SPCanvasBuf buf; - if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) { - buf.buf = nr_pixelstore_256K_new (FALSE, 0); - } else { - buf.buf = nr_pixelstore_1M_new (FALSE, 0); - } - // Mark the region clean - sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0); - - buf.buf_rowstride = sw * 4; - buf.rect.x0 = x0; - buf.rect.y0 = y0; - buf.rect.x1 = x1; - buf.rect.y1 = y1; - buf.visible_rect.x0 = draw_x1; - buf.visible_rect.y0 = draw_y1; - buf.visible_rect.x1 = draw_x2; - buf.visible_rect.y1 = draw_y2; - GdkColor *color = &widget->style->bg[GTK_STATE_NORMAL]; - buf.bg_color = (((color->red & 0xff00) << 8) - | (color->green & 0xff00) - | (color->blue >> 8)); - buf.is_empty = true; + sp_canvas_mark_rect(canvas, paint_rect, 0); - buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf); + SPCanvasBuf buf; + buf.buf = NULL; + buf.buf_rowstride = 0; + buf.rect = paint_rect; + buf.visible_rect = canvas_rect; + buf.is_empty = true; + //buf.ct = gdk_cairo_create(widget->window); + + /* + cairo_t *xctt = gdk_cairo_create(widget->window); + cairo_translate(xctt, paint_rect.left() - canvas->x0, paint_rect.top() - canvas->y0); + cairo_set_source_rgb(xctt, 1,0,0); + cairo_rectangle(xctt, 0, 0, paint_rect.width(), paint_rect.height()); + cairo_fill(xctt); + cairo_destroy(xctt); + //*/ + + // create temporary surface + cairo_surface_t *imgs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, paint_rect.width(), paint_rect.height()); + buf.ct = cairo_create(imgs); + //cairo_translate(buf.ct, -x0, -y0); + + // fix coordinates, clip all drawing to the tile and clear the background + //cairo_translate(buf.ct, paint_rect.left() - canvas->x0, paint_rect.top() - canvas->y0); + //cairo_rectangle(buf.ct, 0, 0, paint_rect.width(), paint_rect.height()); + //cairo_set_line_width(buf.ct, 3); + //cairo_set_source_rgba(buf.ct, 1.0, 0.0, 0.0, 0.1); + //cairo_stroke_preserve(buf.ct); + //cairo_clip(buf.ct); + + gdk_cairo_set_source_color(buf.ct, &widget->style->bg[GTK_STATE_NORMAL]); + cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE); + //cairo_rectangle(buf.ct, 0, 0, paint_rect.width(), paint_rec.height()); + cairo_paint(buf.ct); + cairo_set_operator(buf.ct, CAIRO_OPERATOR_OVER); if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) { SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf); } -#if ENABLE_LCMS - cmsHTRANSFORM transf = 0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); - if ( fromDisplay ) { - transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" ); - } else { - transf = Inkscape::colorprofile_get_display_transform(); - } -#endif // ENABLE_LCMS + // output to X + cairo_destroy(buf.ct); - if (buf.is_empty) { #if ENABLE_LCMS - if ( transf && canvas->enable_cms_display_adj ) { - cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 ); + if (canvas->enable_cms_display_adj) { + cmsHTRANSFORM transf = 0; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); + if ( fromDisplay ) { + transf = Inkscape::CMSSystem::getDisplayPer( canvas->cms_key ); + } else { + transf = Inkscape::CMSSystem::getDisplayTransform(); } -#endif // ENABLE_LCMS - gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color); - gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas), - canvas->pixmap_gc, - TRUE, - x0 - canvas->x0, y0 - canvas->y0, - x1 - x0, y1 - y0); - } else { - -#if ENABLE_LCMS - if ( transf && canvas->enable_cms_display_adj ) { - for ( gint yy = 0; yy < (y1 - y0); yy++ ) { - guchar* p = buf.buf + (buf.buf_rowstride * yy); - cmsDoTransform( transf, p, p, (x1 - x0) ); + + if (transf) { + cairo_surface_flush(imgs); + unsigned char *px = cairo_image_surface_get_data(imgs); + int stride = cairo_image_surface_get_stride(imgs); + for (int i=0; i<paint_rect.height(); ++i) { + unsigned char *row = px + i*stride; + Inkscape::CMSSystem::doTransform(transf, row, row, paint_rect.width()); } + cairo_surface_mark_dirty(imgs); } -#endif // ENABLE_LCMS - -// Now we only need to output the prepared pixmap to the actual screen, and this define chooses one -// of the two ways to do it. The cairo way is direct and straightforward, but unfortunately -// noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason -// for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and -// use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo -// cannot handle at all. Still, this way is currently faster even despite the blit with squeeze. - -///#define CANVAS_OUTPUT_VIA_CAIRO - -#ifdef CANVAS_OUTPUT_VIA_CAIRO - - buf.cst = cairo_image_surface_create_for_data ( - buf.buf, - CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused - x1 - x0, y1 - y0, - buf.buf_rowstride - ); - cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas)); - cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0); - cairo_paint (window_ct); - cairo_destroy (window_ct); - cairo_surface_finish (buf.cst); - cairo_surface_destroy (buf.cst); - -#else - - NRPixBlock b3; - nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE); - - NRPixBlock b4; - nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, - buf.buf, - buf.buf_rowstride, - FALSE, FALSE); - - // this does the 32->24 squishing, using an assembler routine: - nr_blit_pixblock_pixblock (&b3, &b4); - - gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas), - canvas->pixmap_gc, - x0 - canvas->x0, y0 - canvas->y0, - x1 - x0, y1 - y0, - GDK_RGB_DITHER_MAX, - NR_PIXBLOCK_PX(&b3), - sw * 3, - x0 - canvas->x0, y0 - canvas->y0); - - nr_pixblock_release (&b3); - nr_pixblock_release (&b4); -#endif } +#endif // ENABLE_LCMS - cairo_surface_t *cst = cairo_get_target(buf.ct); - cairo_destroy (buf.ct); - cairo_surface_finish (cst); - cairo_surface_destroy (cst); + cairo_t *xct = gdk_cairo_create(widget->window); + cairo_translate(xct, paint_rect.left() - canvas->x0, paint_rect.top() - canvas->y0); + cairo_rectangle(xct, 0, 0, paint_rect.width(), paint_rect.height()); + cairo_clip(xct); + cairo_set_source_surface(xct, imgs, 0, 0); + cairo_set_operator(xct, CAIRO_OPERATOR_SOURCE); + cairo_paint(xct); + cairo_destroy(xct); + cairo_surface_destroy(imgs); - if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) { - nr_pixelstore_256K_free (buf.buf); - } else { - nr_pixelstore_1M_free (buf.buf); - } + //cairo_surface_t *cst = cairo_get_target(buf.ct); + //cairo_destroy (buf.ct); + //cairo_surface_finish (cst); + //cairo_surface_destroy (cst); } struct PaintRectSetup { SPCanvas* canvas; - NRRectL big_rect; + Geom::IntRect big_rect; GTimeVal start_time; int max_pixels; Geom::Point mouse_loc; @@ -1780,7 +1738,7 @@ struct PaintRectSetup { * @return true if the drawing completes */ static int -sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect) +sp_canvas_paint_rect_internal (PaintRectSetup const *setup, Geom::IntRect const &this_rect) { GTimeVal now; g_get_current_time (&now); @@ -1815,23 +1773,29 @@ sp_canvas_paint_rect_internal (PaintRectSetup const *setup, NRRectL this_rect) } // Find the optimal buffer dimensions - int bw = this_rect.x1 - this_rect.x0; - int bh = this_rect.y1 - this_rect.y0; + int bw = this_rect.width(); + int bh = this_rect.height(); if ((bw < 1) || (bh < 1)) return 0; if (bw * bh < setup->max_pixels) { // We are small enough + /*GdkRectangle r; + r.x = this_rect.x0 - setup->canvas->x0; + r.y = this_rect.y0 - setup->canvas->y0; + r.width = this_rect.x1 - this_rect.x0; + r.height = this_rect.y1 - this_rect.y0; + + GdkWindow *window = GTK_WIDGET(setup->canvas)->window; + gdk_window_begin_paint_rect(window, &r);*/ + sp_canvas_paint_single_buffer (setup->canvas, - this_rect.x0, this_rect.y0, - this_rect.x1, this_rect.y1, - setup->big_rect.x0, setup->big_rect.y0, - setup->big_rect.x1, setup->big_rect.y1, bw); + this_rect, setup->big_rect, bw); + //gdk_window_end_paint(window); return 1; } - NRRectL lo = this_rect; - NRRectL hi = this_rect; + Geom::IntRect lo, hi; /* This test determines the redraw strategy: @@ -1849,13 +1813,12 @@ faster. The default for now is the strips mode. */ if (bw < bh || bh < 2 * TILE_SIZE) { - // to correctly calculate the mean of two ints, we need to sum them into a larger int type - int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2; + int mid = this_rect[Geom::X].middle(); // Make sure that mid lies on a tile boundary mid = (mid / TILE_SIZE) * TILE_SIZE; - lo.x1 = mid; - hi.x0 = mid; + lo = Geom::IntRect(this_rect.left(), this_rect.top(), mid, this_rect.bottom()); + hi = Geom::IntRect(mid, this_rect.top(), this_rect.right(), this_rect.bottom()); if (setup->mouse_loc[Geom::X] < mid) { // Always paint towards the mouse first @@ -1866,13 +1829,12 @@ The default for now is the strips mode. && sp_canvas_paint_rect_internal(setup, lo); } } else { - // to correctly calculate the mean of two ints, we need to sum them into a larger int type - int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2; + int mid = this_rect[Geom::Y].middle(); // Make sure that mid lies on a tile boundary mid = (mid / TILE_SIZE) * TILE_SIZE; - lo.y1 = mid; - hi.y0 = mid; + lo = Geom::IntRect(this_rect.left(), this_rect.top(), this_rect.right(), mid); + hi = Geom::IntRect(this_rect.left(), mid, this_rect.right(), this_rect.bottom()); if (setup->mouse_loc[Geom::Y] < mid) { // Always paint towards the mouse first @@ -1896,32 +1858,19 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) { g_return_val_if_fail (!canvas->need_update, false); - NRRectL rect; - rect.x0 = xx0; - rect.x1 = xx1; - rect.y0 = yy0; - rect.y1 = yy1; - - // Clip rect-to-draw by the current visible area - rect.x0 = MAX (rect.x0, canvas->x0); - rect.y0 = MAX (rect.y0, canvas->y0); - rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width); - rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height); - -#ifdef DEBUG_REDRAW - // paint the area to redraw yellow - gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00); - gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas), - canvas->pixmap_gc, - TRUE, - rect.x0 - canvas->x0, rect.y0 - canvas->y0, - rect.x1 - rect.x0, rect.y1 - rect.y0); -#endif + Geom::IntRect canvas_rect = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + GTK_WIDGET (canvas)->allocation.width, GTK_WIDGET (canvas)->allocation.height); + Geom::IntRect paint_rect(xx0, yy0, xx1, yy1); + + Geom::OptIntRect area = paint_rect & canvas_rect; + if (!area || area->hasZeroArea()) return 0; + + paint_rect = *area; PaintRectSetup setup; setup.canvas = canvas; - setup.big_rect = rect; + setup.big_rect = paint_rect; // Save the mouse location gint x, y; @@ -1942,7 +1891,7 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) g_get_current_time(&(setup.start_time)); // Go - return sp_canvas_paint_rect_internal(&setup, rect); + return sp_canvas_paint_rect_internal(&setup, paint_rect); } /** @@ -1974,7 +1923,7 @@ sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event) { SPCanvas *canvas = SP_CANVAS (widget); - if (!GTK_WIDGET_DRAWABLE (widget) || + if (!gtk_widget_is_drawable (widget) || (event->window != SP_CANVAS_WINDOW (canvas))) return FALSE; @@ -1983,14 +1932,11 @@ sp_canvas_expose (GtkWidget *widget, GdkEventExpose *event) gdk_region_get_rectangles (event->region, &rects, &n_rects); for (int i = 0; i < n_rects; i++) { - NRRectL rect; - - rect.x0 = rects[i].x + canvas->x0; - rect.y0 = rects[i].y + canvas->y0; - rect.x1 = rect.x0 + rects[i].width; - rect.y1 = rect.y0 + rects[i].height; + Geom::IntRect r = Geom::IntRect::from_xywh( + rects[i].x + canvas->x0, rects[i].y + canvas->y0, + rects[i].width, rects[i].height); - sp_canvas_request_redraw (canvas, rect.x0, rect.y0, rect.x1, rect.y1); + sp_canvas_request_redraw (canvas, r.left(), r.top(), r.right(), r.bottom()); } if (n_rects > 0) @@ -2029,7 +1975,7 @@ sp_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event) static gint sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event) { - GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); + gtk_widget_grab_focus (widget); SPCanvas *canvas = SP_CANVAS (widget); @@ -2046,8 +1992,6 @@ sp_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event) static gint sp_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event) { - GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); - SPCanvas *canvas = SP_CANVAS (widget); if (canvas->focused_item) @@ -2117,7 +2061,7 @@ paint (SPCanvas *canvas) static int do_update (SPCanvas *canvas) { - if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display! + if (!canvas->root) // canvas may have already be destroyed by closing desktop during interrupted display! return TRUE; if (canvas->drawing_disabled) @@ -2130,7 +2074,7 @@ do_update (SPCanvas *canvas) } /* Paint if able to */ - if (GTK_WIDGET_DRAWABLE (canvas)) { + if (gtk_widget_is_drawable ( GTK_WIDGET (canvas))) { return paint (canvas); } @@ -2174,7 +2118,8 @@ add_idle (SPCanvas *canvas) if (canvas->idle_id != 0) return; - canvas->idle_id = gtk_idle_add_priority (sp_canvas_update_priority, idle_handler, canvas); + canvas->idle_id = g_idle_add_full (sp_canvas_update_priority, idle_handler, + canvas, NULL); } /** @@ -2203,25 +2148,29 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the int dy = iy - canvas->y0; // canvas w.r.t its previous position + Geom::IntRect old_area = canvas->getViewboxIntegers(); + Geom::IntRect new_area = old_area + Geom::IntPoint(dx, dy); + canvas->dx0 = cx; // here the 'd' stands for double, not delta! canvas->dy0 = cy; canvas->x0 = ix; canvas->y0 = iy; sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height); + if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed (canvas->root, new_area); if (!clear) { // scrolling without zoom; redraw only the newly exposed areas if ((dx != 0) || (dy != 0)) { canvas->is_scrolling = is_scrolling; - if (GTK_WIDGET_REALIZED (canvas)) { + if (gtk_widget_get_realized (GTK_WIDGET (canvas))) { gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy); } } } else { // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw } - } /** @@ -2256,30 +2205,21 @@ sp_canvas_request_update (SPCanvas *canvas) void sp_canvas_request_redraw (SPCanvas *canvas, int x0, int y0, int x1, int y1) { - NRRectL bbox; - NRRectL visible; - NRRectL clip; - g_return_if_fail (canvas != NULL); g_return_if_fail (SP_IS_CANVAS (canvas)); - if (!GTK_WIDGET_DRAWABLE (canvas)) return; + if (!gtk_widget_is_drawable ( GTK_WIDGET (canvas))) return; if ((x0 >= x1) || (y0 >= y1)) return; - bbox.x0 = x0; - bbox.y0 = y0; - bbox.x1 = x1; - bbox.y1 = y1; - - visible.x0 = canvas->x0; - visible.y0 = canvas->y0; - visible.x1 = visible.x0 + GTK_WIDGET (canvas)->allocation.width; - visible.y1 = visible.y0 + GTK_WIDGET (canvas)->allocation.height; - - nr_rect_l_intersect (&clip, &bbox, &visible); - - sp_canvas_dirty_rect(canvas, clip.x0, clip.y0, clip.x1, clip.y1); - add_idle (canvas); + Geom::IntRect bbox(x0, y0, x1, y1); + Geom::IntRect canvas_rect = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + GTK_WIDGET (canvas)->allocation.width, GTK_WIDGET (canvas)->allocation.height); + + Geom::OptIntRect clip = bbox & canvas_rect; + if (clip) { + sp_canvas_dirty_rect(canvas, *clip); + add_idle (canvas); + } } /** @@ -2354,13 +2294,15 @@ Geom::Rect SPCanvas::getViewbox() const } /** - * Return canvas window coordinates as IRect (a rectangle defined by integers). + * Return canvas window coordinates as integer rectangle. */ -NR::IRect SPCanvas::getViewboxIntegers() const +Geom::IntRect SPCanvas::getViewboxIntegers() const { GtkWidget const *w = GTK_WIDGET(this); - return NR::IRect(NR::IPoint(x0, y0), - NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height)); + Geom::IntRect ret; + ret.setMin(Geom::IntPoint(x0, y0)); + ret.setMax(Geom::IntPoint(x0 + w->allocation.width, y0 + w->allocation.height)); + return ret; } inline int sp_canvas_tile_floor(int x) @@ -2415,24 +2357,21 @@ static void sp_canvas_resize_tiles(SPCanvas* canvas, int nl, int nt, int nr, int /* * Helper that queues a canvas rectangle for redraw */ -static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb) { +static void sp_canvas_dirty_rect(SPCanvas* canvas, Geom::IntRect const &area) { canvas->need_redraw = TRUE; - sp_canvas_mark_rect(canvas, nl, nt, nr, nb, 1); + sp_canvas_mark_rect(canvas, area, 1); } /** * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise) */ -void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val) +void sp_canvas_mark_rect(SPCanvas* canvas, Geom::IntRect const &area, uint8_t val) { - if ( nl >= nr || nt >= nb ) { - return; - } - int tl=sp_canvas_tile_floor(nl); - int tt=sp_canvas_tile_floor(nt); - int tr=sp_canvas_tile_ceil(nr); - int tb=sp_canvas_tile_ceil(nb); + int tl=sp_canvas_tile_floor(area.left()); + int tt=sp_canvas_tile_floor(area.top()); + int tr=sp_canvas_tile_ceil(area.right()); + int tb=sp_canvas_tile_ceil(area.bottom()); if ( tl >= canvas->tRight || tr <= canvas->tLeft || tt >= canvas->tBottom || tb <= canvas->tTop ) return; if ( tl < canvas->tLeft ) tl=canvas->tLeft; if ( tr > canvas->tRight ) tr=canvas->tRight; diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 3a0b56585..14cbab6c3 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -1,9 +1,11 @@ #ifndef SEEN_SP_CANVAS_H #define SEEN_SP_CANVAS_H -/** \file +/** + * @file * SPCanvas, SPCanvasBuf. - * + */ +/* * Authors: * Federico Mena <federico@nuclecu.unam.mx> * Raph Levien <raph@gimp.org> @@ -27,24 +29,18 @@ # endif #endif -#include <glib/gtypes.h> -#include <gdk/gdkevents.h> -#include <gdk/gdkgc.h> -#include <gtk/gtkobject.h> -#include <gtk/gtkwidget.h> - +#include <glib.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> #include <glibmm/ustring.h> - #include <2geom/affine.h> -#include <libnr/nr-rect-l.h> - #include <2geom/rect.h> G_BEGIN_DECLS #define SP_TYPE_CANVAS sp_canvas_get_type() -#define SP_CANVAS(obj) (GTK_CHECK_CAST((obj), SP_TYPE_CANVAS, SPCanvas)) -#define SP_IS_CANVAS(obj) (GTK_CHECK_TYPE((obj), SP_TYPE_CANVAS)) +#define SP_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CANVAS, SPCanvas)) +#define SP_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_CANVAS)) GType sp_canvas_get_type(); @@ -58,18 +54,16 @@ enum { }; /** - * The canvas buf contains the actual pixels. + * Structure used when rendering canvas items. */ -struct SPCanvasBuf{ - guchar *buf; +struct SPCanvasBuf { + cairo_t *ct; + Geom::IntRect rect; + Geom::IntRect visible_rect; + + unsigned char *buf; int buf_rowstride; - NRRectL rect; - NRRectL visible_rect; - /// Background color, given as 0xrrggbb - guint32 bg_color; - // If empty, ignore contents of buffer and use a solid area of bg_color bool is_empty; - cairo_t *ct; }; G_END_DECLS @@ -116,9 +110,6 @@ struct SPCanvas { int close_enough; - /* GC for temporary draw pixmap */ - GdkGC *pixmap_gc; - unsigned int need_update : 1; unsigned int need_redraw : 1; unsigned int need_repick : 1; @@ -147,13 +138,13 @@ struct SPCanvas { #if ENABLE_LCMS bool enable_cms_display_adj; - Glib::ustring* cms_key; + Glib::ustring cms_key; #endif // ENABLE_LCMS bool is_scrolling; Geom::Rect getViewbox() const; - NR::IRect getViewboxIntegers() const; + Geom::IntRect getViewboxIntegers() const; }; GtkWidget *sp_canvas_new_aa(); diff --git a/src/display/sp-ctrlline.cpp b/src/display/sp-ctrlline.cpp index ef71c0a16..77f5c1d15 100644 --- a/src/display/sp-ctrlline.cpp +++ b/src/display/sp-ctrlline.cpp @@ -19,14 +19,14 @@ * */ -#include "sp-canvas-util.h" -#include "sp-ctrlline.h" - #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include <color.h> -#include "display/inkscape-cairo.h" + +#include "display/sp-ctrlline.h" +#include "display/sp-canvas-util.h" +#include "display/cairo-utils.h" +#include "color.h" static void sp_ctrlline_class_init (SPCtrlLineClass *klass); @@ -64,7 +64,7 @@ sp_ctrlline_class_init (SPCtrlLineClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_ctrlline_destroy; @@ -105,19 +105,15 @@ sp_ctrlline_render (SPCanvasItem *item, SPCanvasBuf *buf) if (cl->s == cl->e) return; - sp_canvas_prepare_buffer (buf); - - guint32 rgba = cl->rgba; - cairo_set_source_rgba(buf->ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); - + ink_cairo_set_source_rgba32(buf->ct, cl->rgba); cairo_set_line_width(buf->ct, 1); cairo_new_path(buf->ct); Geom::Point s = cl->s * cl->affine; Geom::Point e = cl->e * cl->affine; - cairo_move_to (buf->ct, s[Geom::X] - buf->rect.x0, s[Geom::Y] - buf->rect.y0); - cairo_line_to (buf->ct, e[Geom::X] - buf->rect.x0, e[Geom::Y] - buf->rect.y0); + cairo_move_to (buf->ct, s[Geom::X] - buf->rect.left(), s[Geom::Y] - buf->rect.top()); + cairo_line_to (buf->ct, e[Geom::X] - buf->rect.left(), e[Geom::Y] - buf->rect.top()); cairo_stroke(buf->ct); } diff --git a/src/display/sp-ctrlline.h b/src/display/sp-ctrlline.h index e69c478fb..4bfe50a77 100644 --- a/src/display/sp-ctrlline.h +++ b/src/display/sp-ctrlline.h @@ -16,11 +16,11 @@ #include "sp-canvas-item.h" -struct SPItem; +class SPItem; #define SP_TYPE_CTRLLINE (sp_ctrlline_get_type ()) -#define SP_CTRLLINE(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CTRLLINE, SPCtrlLine)) -#define SP_IS_CTRLLINE(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CTRLLINE)) +#define SP_CTRLLINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLLINE, SPCtrlLine)) +#define SP_IS_CTRLLINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRLLINE)) struct SPCtrlLine : public SPCanvasItem{ SPItem *item; // the item to which this line belongs in some sense; may be NULL for some users diff --git a/src/display/sp-ctrlpoint.cpp b/src/display/sp-ctrlpoint.cpp index 9f2ecb937..3a29b9b7c 100644 --- a/src/display/sp-ctrlpoint.cpp +++ b/src/display/sp-ctrlpoint.cpp @@ -18,7 +18,7 @@ # include "config.h" #endif #include <color.h> -#include "display/inkscape-cairo.h" +#include "display/cairo-utils.h" static void sp_ctrlpoint_class_init (SPCtrlPointClass *klass); @@ -56,7 +56,7 @@ sp_ctrlpoint_class_init (SPCtrlPointClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_ctrlpoint_destroy; @@ -105,7 +105,7 @@ sp_ctrlpoint_render (SPCanvasItem *item, SPCanvasBuf *buf) Geom::Point pt = cp->pt * cp->affine; - cairo_arc(buf->ct, pt[Geom::X] - buf->rect.x0, pt[Geom::Y] - buf->rect.y0, cp->radius, 0.0, 2 * M_PI); + cairo_arc(buf->ct, pt[Geom::X] - buf->rect.left(), pt[Geom::Y] - buf->rect.top(), cp->radius, 0.0, 2 * M_PI); cairo_stroke(buf->ct); } diff --git a/src/display/sp-ctrlpoint.h b/src/display/sp-ctrlpoint.h index b98d48f67..a7a5475b7 100644 --- a/src/display/sp-ctrlpoint.h +++ b/src/display/sp-ctrlpoint.h @@ -14,11 +14,11 @@ #include "sp-canvas-item.h" -struct SPItem; +class SPItem; #define SP_TYPE_CTRLPOINT (sp_ctrlpoint_get_type ()) -#define SP_CTRLPOINT(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CTRLPOINT, SPCtrlPoint)) -#define SP_IS_CTRLPOINT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CTRLPOINT)) +#define SP_CTRLPOINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLPOINT, SPCtrlPoint)) +#define SP_IS_CTRLPOINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRLPOINT)) struct SPCtrlPoint : public SPCanvasItem { SPItem *item; // the item to which this line belongs in some sense; may be NULL for some users diff --git a/src/display/sp-ctrlquadr.cpp b/src/display/sp-ctrlquadr.cpp index 1ed8d62b8..af761864c 100644 --- a/src/display/sp-ctrlquadr.cpp +++ b/src/display/sp-ctrlquadr.cpp @@ -12,10 +12,12 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#include "sp-canvas-util.h" + #include "sp-canvas-item.h" +#include "sp-canvas.h" +#include "sp-canvas-util.h" #include "sp-ctrlquadr.h" -#include "display/inkscape-cairo.h" +#include "display/cairo-utils.h" #include "color.h" struct SPCtrlQuadr : public SPCanvasItem{ @@ -61,7 +63,7 @@ sp_ctrlquadr_class_init (SPCtrlQuadrClass *klass) GtkObjectClass *object_class = (GtkObjectClass *) klass; SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; - parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM); + parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); object_class->destroy = sp_ctrlquadr_destroy; @@ -94,15 +96,13 @@ sp_ctrlquadr_render (SPCanvasItem *item, SPCanvasBuf *buf) { SPCtrlQuadr *cq = SP_CTRLQUADR (item); - //Geom::Rect area (Geom::Point(buf->rect.x0, buf->rect.y0), Geom::Point(buf->rect.x1, buf->rect.y1)); - if (!buf->ct) return; // RGB / BGR cairo_new_path(buf->ct); - Geom::Point min = Geom::Point(buf->rect.x0, buf->rect.y0); + Geom::Point min = buf->rect.min(); Geom::Point p1 = (cq->p1 * cq->affine) - min; Geom::Point p2 = (cq->p2 * cq->affine) - min; diff --git a/src/display/sp-ctrlquadr.h b/src/display/sp-ctrlquadr.h index f3c1ced45..9fdfd29b3 100644 --- a/src/display/sp-ctrlquadr.h +++ b/src/display/sp-ctrlquadr.h @@ -17,8 +17,8 @@ #define SP_TYPE_CTRLQUADR (sp_ctrlquadr_get_type ()) -#define SP_CTRLQUADR(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_CTRLQUADR, SPCtrlQuadr)) -#define SP_IS_CTRLQUADR(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_CTRLQUADR)) +#define SP_CTRLQUADR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLQUADR, SPCtrlQuadr)) +#define SP_IS_CTRLQUADR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRLQUADR)) struct SPCtrlQuadr; struct SPCtrlQuadrClass; diff --git a/src/display/testnr.cpp b/src/display/testnr.cpp deleted file mode 100644 index 3a3478d28..000000000 --- a/src/display/testnr.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <gtk/gtk.h> -#include "sp-arena.h" - -int -main (int argc, char ** argv) -{ - GtkWidget * w, * c; - - gtk_init (&argc, &argv); - - w = gtk_window_new (GTK_WINDOW_TOPLEVEL); - - c = sp_arena_new (); - gtk_widget_show (c); - - gtk_container_add (GTK_CONTAINER (w), c); - - gtk_widget_show (w); - - gtk_main (); - - return 0; -} - |
