summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorAndrew Higginson <at.higginson@gmail.com>2011-12-27 21:04:47 +0000
committerAndrew <at.higginson@gmail.com>2011-12-27 21:04:47 +0000
commit80960b623a99aae1402ab651b2974ef544ed3b03 (patch)
treeba49d42c2789e9e11f805e2d5263e10f9fedeef8 /src/display
parenttry to fix bug (diff)
parentGDL: Cherry-pick upstream patch 73852 (2011-03-23) - Add missing return value. (diff)
downloadinkscape-80960b623a99aae1402ab651b2974ef544ed3b03.tar.gz
inkscape-80960b623a99aae1402ab651b2974ef544ed3b03.zip
merged with trunk so I can build again...
(bzr r10092.1.36)
Diffstat (limited to 'src/display')
-rw-r--r--src/display/CMakeLists.txt182
-rw-r--r--src/display/Makefile_insert112
-rw-r--r--src/display/cairo-templates.h695
-rw-r--r--src/display/cairo-utils.cpp718
-rw-r--r--src/display/cairo-utils.h156
-rw-r--r--src/display/canvas-arena.cpp259
-rw-r--r--src/display/canvas-arena.h43
-rw-r--r--src/display/canvas-axonomgrid.cpp249
-rw-r--r--src/display/canvas-axonomgrid.h6
-rw-r--r--src/display/canvas-bpath.cpp65
-rw-r--r--src/display/canvas-bpath.h12
-rw-r--r--src/display/canvas-grid.cpp209
-rw-r--r--src/display/canvas-grid.h24
-rw-r--r--src/display/canvas-temporary-item-list.cpp2
-rw-r--r--src/display/canvas-temporary-item-list.h10
-rw-r--r--src/display/canvas-temporary-item.cpp4
-rw-r--r--src/display/canvas-temporary-item.h9
-rw-r--r--src/display/canvas-text.cpp138
-rw-r--r--src/display/canvas-text.h25
-rw-r--r--src/display/curve-test.h2
-rw-r--r--src/display/curve.cpp10
-rw-r--r--src/display/curve.h10
-rw-r--r--src/display/drawing-context.cpp155
-rw-r--r--src/display/drawing-context.h128
-rw-r--r--src/display/drawing-group.cpp166
-rw-r--r--src/display/drawing-group.h62
-rw-r--r--src/display/drawing-image.cpp263
-rw-r--r--src/display/drawing-image.h67
-rw-r--r--src/display/drawing-item.cpp912
-rw-r--r--src/display/drawing-item.h224
-rw-r--r--src/display/drawing-shape.cpp341
-rw-r--r--src/display/drawing-shape.h63
-rw-r--r--src/display/drawing-surface.cpp365
-rw-r--r--src/display/drawing-surface.h93
-rw-r--r--src/display/drawing-text.cpp251
-rw-r--r--src/display/drawing-text.h82
-rw-r--r--src/display/drawing.cpp205
-rw-r--r--src/display/drawing.h119
-rw-r--r--src/display/gnome-canvas-acetate.cpp42
-rw-r--r--src/display/gnome-canvas-acetate.h12
-rw-r--r--src/display/grayscale.cpp19
-rw-r--r--src/display/grayscale.h15
-rw-r--r--src/display/guideline.cpp239
-rw-r--r--src/display/guideline.h10
-rw-r--r--src/display/inkscape-cairo.cpp248
-rw-r--r--src/display/inkscape-cairo.h37
-rw-r--r--src/display/nr-3dutils.cpp127
-rw-r--r--src/display/nr-3dutils.h36
-rw-r--r--src/display/nr-arena-forward.h51
-rw-r--r--src/display/nr-arena-glyphs.cpp695
-rw-r--r--src/display/nr-arena-glyphs.h120
-rw-r--r--src/display/nr-arena-group.cpp301
-rw-r--r--src/display/nr-arena-group.h61
-rw-r--r--src/display/nr-arena-image.cpp392
-rw-r--r--src/display/nr-arena-image.h68
-rw-r--r--src/display/nr-arena-item.cpp949
-rw-r--r--src/display/nr-arena-item.h209
-rw-r--r--src/display/nr-arena-shape.cpp1519
-rw-r--r--src/display/nr-arena-shape.h230
-rw-r--r--src/display/nr-arena.cpp220
-rw-r--r--src/display/nr-arena.h70
-rw-r--r--src/display/nr-filter-blend.cpp290
-rw-r--r--src/display/nr-filter-blend.h7
-rw-r--r--src/display/nr-filter-colormatrix.cpp310
-rw-r--r--src/display/nr-filter-colormatrix.h15
-rw-r--r--src/display/nr-filter-component-transfer.cpp429
-rw-r--r--src/display/nr-filter-component-transfer.h11
-rw-r--r--src/display/nr-filter-composite.cpp192
-rw-r--r--src/display/nr-filter-composite.h4
-rw-r--r--src/display/nr-filter-convolve-matrix.cpp274
-rw-r--r--src/display/nr-filter-convolve-matrix.h11
-rw-r--r--src/display/nr-filter-diffuselighting.cpp261
-rw-r--r--src/display/nr-filter-diffuselighting.h24
-rw-r--r--src/display/nr-filter-displacement-map.cpp253
-rw-r--r--src/display/nr-filter-displacement-map.h11
-rw-r--r--src/display/nr-filter-flood.cpp81
-rw-r--r--src/display/nr-filter-flood.h13
-rw-r--r--src/display/nr-filter-gaussian.cpp613
-rw-r--r--src/display/nr-filter-gaussian.h13
-rw-r--r--src/display/nr-filter-getalpha.cpp56
-rw-r--r--src/display/nr-filter-getalpha.h35
-rw-r--r--src/display/nr-filter-image.cpp366
-rw-r--r--src/display/nr-filter-image.h16
-rw-r--r--src/display/nr-filter-merge.cpp117
-rw-r--r--src/display/nr-filter-merge.h9
-rw-r--r--src/display/nr-filter-morphology.cpp203
-rw-r--r--src/display/nr-filter-morphology.h11
-rw-r--r--src/display/nr-filter-offset.cpp61
-rw-r--r--src/display/nr-filter-offset.h7
-rw-r--r--src/display/nr-filter-pixops.h104
-rw-r--r--src/display/nr-filter-primitive.cpp17
-rw-r--r--src/display/nr-filter-primitive.h65
-rw-r--r--src/display/nr-filter-skeleton.cpp18
-rw-r--r--src/display/nr-filter-skeleton.h2
-rw-r--r--src/display/nr-filter-slot.cpp429
-rw-r--r--src/display/nr-filter-slot.h85
-rw-r--r--src/display/nr-filter-specularlighting.cpp285
-rw-r--r--src/display/nr-filter-specularlighting.h31
-rw-r--r--src/display/nr-filter-tile.cpp40
-rw-r--r--src/display/nr-filter-tile.h10
-rw-r--r--src/display/nr-filter-turbulence.cpp612
-rw-r--r--src/display/nr-filter-turbulence.h50
-rw-r--r--src/display/nr-filter-units.cpp22
-rw-r--r--src/display/nr-filter-units.h17
-rw-r--r--src/display/nr-filter-utils.h8
-rw-r--r--src/display/nr-filter.cpp269
-rw-r--r--src/display/nr-filter.h42
-rw-r--r--src/display/nr-light.cpp22
-rw-r--r--src/display/nr-light.h2
-rw-r--r--src/display/nr-plain-stuff-gdk.cpp46
-rw-r--r--src/display/nr-plain-stuff-gdk.h32
-rw-r--r--src/display/nr-plain-stuff.cpp94
-rw-r--r--src/display/nr-plain-stuff.h33
-rw-r--r--src/display/nr-style.cpp219
-rw-r--r--src/display/nr-style.h84
-rw-r--r--src/display/nr-svgfonts.cpp107
-rw-r--r--src/display/nr-svgfonts.h6
-rw-r--r--src/display/pixblock-scaler.cpp299
-rw-r--r--src/display/pixblock-scaler.h40
-rw-r--r--src/display/pixblock-transform.cpp279
-rw-r--r--src/display/pixblock-transform.h35
-rw-r--r--src/display/rendermode.h8
-rw-r--r--src/display/snap-indicator.cpp67
-rw-r--r--src/display/snap-indicator.h13
-rw-r--r--src/display/sodipodi-ctrl.cpp287
-rw-r--r--src/display/sodipodi-ctrl.h17
-rw-r--r--src/display/sodipodi-ctrlrect.cpp285
-rw-r--r--src/display/sodipodi-ctrlrect.h24
-rw-r--r--src/display/sp-canvas-group.h12
-rw-r--r--src/display/sp-canvas-item.h24
-rw-r--r--src/display/sp-canvas-util.cpp41
-rw-r--r--src/display/sp-canvas-util.h5
-rw-r--r--src/display/sp-canvas.cpp465
-rw-r--r--src/display/sp-canvas.h45
-rw-r--r--src/display/sp-ctrlline.cpp22
-rw-r--r--src/display/sp-ctrlline.h6
-rw-r--r--src/display/sp-ctrlpoint.cpp6
-rw-r--r--src/display/sp-ctrlpoint.h6
-rw-r--r--src/display/sp-ctrlquadr.cpp12
-rw-r--r--src/display/sp-ctrlquadr.h4
-rw-r--r--src/display/testnr.cpp24
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 &center, 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 &center, 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;
-}
-