summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-07-12 19:57:46 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2010-07-12 19:57:46 +0000
commit8217b2f74c9db38d7a64ce41eeb6c9659aae1ceb (patch)
treeaf4c9c0bf3e20bf79d801989512ed61d637dfe07 /src
parentGrid rendering (diff)
downloadinkscape-8217b2f74c9db38d7a64ce41eeb6c9659aae1ceb.tar.gz
inkscape-8217b2f74c9db38d7a64ce41eeb6c9659aae1ceb.zip
Gaussian blur
(bzr r9508.1.15)
Diffstat (limited to 'src')
-rw-r--r--src/display/cairo-utils.cpp72
-rw-r--r--src/display/cairo-utils.h7
-rw-r--r--src/display/nr-arena-item.cpp34
-rw-r--r--src/display/nr-filter-gaussian.cpp206
-rw-r--r--src/display/nr-filter-gaussian.h1
-rw-r--r--src/display/nr-filter-primitive.cpp7
-rw-r--r--src/display/nr-filter-primitive.h1
-rw-r--r--src/display/nr-filter-slot.cpp237
-rw-r--r--src/display/nr-filter-slot.h63
-rw-r--r--src/display/nr-filter.cpp72
-rw-r--r--src/display/nr-filter.h7
11 files changed, 574 insertions, 133 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index bb401dc87..a063a62bb 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -329,6 +329,78 @@ ink_cairo_set_source_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double
cairo_surface_destroy(pbs);
}
+/** @brief 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);
+
+ 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;
+}
+
+/** @brief 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 = cairo_surface_create_similar(s, cairo_surface_get_content(s),
+ ink_cairo_surface_get_width(s), ink_cairo_surface_get_height(s));
+ return ns;
+}
+
+/** @brief 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 = cairo_surface_create_similar(s, CAIRO_CONTENT_ALPHA,
+ ink_cairo_surface_get_width(s), ink_cairo_surface_get_height(s));
+
+ 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_unshare(cairo_surface_t *s)
+{
+ if (cairo_surface_get_reference_count(s) > 1) {
+ return ink_cairo_surface_copy(s);
+ } else {
+ cairo_surface_reference(s);
+ return s;
+ }
+}
+
+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);
+}
+
// taken from Cairo sources
static inline guint32 premul_alpha(guint32 color, guint32 alpha)
{
diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h
index 096dc6046..cfd33330b 100644
--- a/src/display/cairo-utils.h
+++ b/src/display/cairo-utils.h
@@ -84,6 +84,13 @@ void ink_cairo_transform(cairo_t *ct, Geom::Matrix const &m);
void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Matrix const &m);
void ink_cairo_set_source_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y);
+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_extract_alpha(cairo_surface_t *s);
+cairo_surface_t *ink_cairo_surface_unshare(cairo_surface_t *s);
+int ink_cairo_surface_get_width(cairo_surface_t *surface);
+int ink_cairo_surface_get_height(cairo_surface_t *surface);
+
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 *);
diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp
index f5731c0e3..e7cf08722 100644
--- a/src/display/nr-arena-item.cpp
+++ b/src/display/nr-arena-item.cpp
@@ -336,6 +336,7 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
return item->state | NR_ARENA_ITEM_STATE_RENDER;
// carea is the bounding box for intermediate rendering.
+ // NOTE: carea might be larger than area, because of filter effects.
NRRectL carea;
nr_rect_l_intersect (&carea, area, &item->drawbox);
if (nr_rect_l_test_empty(carea))
@@ -587,25 +588,39 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
this_ct = cairo_create(intermediate);
this_area = &carea;
cairo_surface_destroy(intermediate); // the surface will be held in memory by this_ct
+ } else {
+ cairo_reference(this_ct);
}
- Cairo::Context cct(this_ct);
+ // The pipeline needs to be different for filters.
+ // First we render the item into an intermediate surface. Then the filter rotates
+ // the surface to user coordinates (if necessary) and runs the rendering.
+ // Once that's done we retrieve the result, rotating it back to screen coords.
+ // Clipping and masking happens after the filter result is ready.
+ if (item->filter && filter) {
+ }
+
+ Cairo::Context cct(this_ct, true);
+ Cairo::Context base_ct(ct);
Cairo::RefPtr<Cairo::Pattern> mask;
- CairoSave clipsave(this_ct); // RAII for save / restore
+ CairoSave clipsave(ct); // RAII for save / restore
CairoGroup maskgroup(this_ct); // RAII for push_group / pop_group
CairoGroup drawgroup(this_ct);
CairoGroup maskopacitygroup(this_ct);
- if (item->clip && !(item->filter && filter)) {
+ // always clip the base context, not the one on the intermediate surface
+ // this is because filters must be done before clipping
+ if (item->clip) {
clipsave.save();
- state = nr_arena_item_invoke_clip(this_ct, item->clip, this_area);
+ state = nr_arena_item_invoke_clip(ct, item->clip, const_cast<NRRectL*>(area));
if (state & NR_ARENA_ITEM_STATE_INVALID) {
item->state |= NR_ARENA_ITEM_STATE_INVALID;
return item->state;
}
- cct.clip();
+ base_ct.clip();
}
+ // render mask on the intermediate context and store it
if (item->mask) {
maskgroup.push_with_content(CAIRO_CONTENT_ALPHA);
// handle opacity of a masked object by composing it with the mask
@@ -628,12 +643,20 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
/*if (mask) {
drawgroup.push();
}*/
+
+ // render the object (possibly to the intermediate surface)
state = NR_ARENA_ITEM_VIRTUAL (item, render) (this_ct, item, this_area, pb, flags);
if (state & NR_ARENA_ITEM_STATE_INVALID) {
/* Clean up and return error */
item->state |= NR_ARENA_ITEM_STATE_INVALID;
return item->state;
}
+
+ // apply filter
+ if (item->filter && filter) {
+ item->filter->render(item, ct, area, this_ct, &carea);
+ }
+
if (needs_intermediate_rendering) {
cairo_surface_t *intermediate = cairo_get_target(this_ct);
cairo_set_source_surface(ct, intermediate, carea.x0 - area->x0, carea.y0 - area->y0);
@@ -655,7 +678,6 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area
}
}
cairo_set_source_rgba(ct,0,0,0,0);
- cairo_destroy(this_ct);
}
return item->state | NR_ARENA_ITEM_STATE_RENDER;
diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp
index 9509eaef7..2e6bed070 100644
--- a/src/display/nr-filter-gaussian.cpp
+++ b/src/display/nr-filter-gaussian.cpp
@@ -27,6 +27,7 @@
#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"
@@ -284,6 +285,12 @@ 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;
+#else
+ static unsigned int const alpha_PC = 0;
+#endif
+
#if HAVE_OPENMP
#pragma omp parallel for num_threads(num_threads)
#else
@@ -319,8 +326,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]);
+ for(unsigned int c=0; c<alpha_PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c], std::numeric_limits<PT>::min(), dstimg[alpha_PC]);
} else {
for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]);
}
@@ -334,8 +341,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]);
+ for(unsigned int c=0; c<alpha_PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c], std::numeric_limits<PT>::min(), dstimg[alpha_PC]);
} else {
for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]);
}
@@ -537,6 +544,197 @@ upsample(PT *const dst, int const dstr1, int const dstr2, unsigned int const dn1
}
}
+static void
+gaussian_pass_IIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest,
+ IIRValue **tmpdata, int num_threads)
+{
+ // 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:
+ assert(false);
+ };
+}
+
+static void
+gaussian_pass_FIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest,
+ int num_threads)
+{
+ int scr_len = _effect_area_scr(deviation);
+ // Filter kernel for x direction
+ FIRValue kernel[scr_len+1];
+ _make_kernel(kernel, 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, 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, scr_len, num_threads);
+ break;
+ default:
+ assert(false);
+ };
+}
+
+void FilterGaussian::render_cairo(FilterSlot &slot)
+{
+ cairo_surface_t *in = slot.getcairo(_input);
+ if (!in) return;
+
+ // zero deviation = transparent black as output
+ if (_deviation_x <= 0 || _deviation_y <= 0) {
+ cairo_surface_t *blank = ink_cairo_surface_create_identical(in);
+ slot.set(_output, blank);
+ cairo_surface_destroy(blank);
+ return;
+ }
+
+ Geom::Matrix 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;
+ }
+
+#if HAVE_OPENMP
+ int threads = Inkscape::Preferences::get()->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256);
+#else
+ int threads = 1;
+#endif
+
+ 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 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[threads];
+ std::fill_n(tmpdata, threads, (IIRValue*)0);
+ if ( use_IIR_x || use_IIR_y ) {
+ for(int i = 0; i < threads; ++i) {
+ tmpdata[i] = new IIRValue[std::max(w_downsampled,h_downsampled)*bytes_per_pixel];
+ }
+ }
+
+ 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);
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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 {
+ slot.set(_output, downsampled);
+ cairo_surface_destroy(downsampled);
+ }
+}
+
int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
{
// TODO: Meaningful return values? (If they're checked at all.)
diff --git a/src/display/nr-filter-gaussian.h b/src/display/nr-filter-gaussian.h
index 763e42de2..7bcabdba9 100644
--- a/src/display/nr-filter-gaussian.h
+++ b/src/display/nr-filter-gaussian.h
@@ -38,6 +38,7 @@ public:
static FilterPrimitive *create();
virtual ~FilterGaussian();
+ virtual void render_cairo(FilterSlot &slot);
virtual int render(FilterSlot &slot, FilterUnits const &units);
virtual void area_enlarge(NRRectL &area, Geom::Matrix const &m);
virtual FilterTraits get_input_traits();
diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp
index b70ae57fe..31e314055 100644
--- a/src/display/nr-filter-primitive.cpp
+++ b/src/display/nr-filter-primitive.cpp
@@ -37,6 +37,13 @@ FilterPrimitive::~FilterPrimitive()
// Nothing to do here
}
+void FilterPrimitive::render_cairo(FilterSlot &slot)
+{
+ // passthrough
+ cairo_surface_t *in = slot.getcairo(_input);
+ slot.set(_output, in);
+}
+
void FilterPrimitive::area_enlarge(NRRectL &/*area*/, Geom::Matrix const &/*m*/)
{
// This doesn't need to do anything by default
diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h
index 74b41211b..a7ae0125e 100644
--- a/src/display/nr-filter-primitive.h
+++ b/src/display/nr-filter-primitive.h
@@ -43,6 +43,7 @@ public:
FilterPrimitive();
virtual ~FilterPrimitive();
+ virtual void render_cairo(FilterSlot &slot);
virtual int render(FilterSlot &slot, FilterUnits const &units) = 0;
virtual void area_enlarge(NRRectL &area, Geom::Matrix const &m);
diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp
index 7df9ab979..d700cd433 100644
--- a/src/display/nr-filter-slot.cpp
+++ b/src/display/nr-filter-slot.cpp
@@ -14,6 +14,8 @@
#include <assert.h>
#include <string.h>
+#include <2geom/transforms.h>
+#include "display/cairo-utils.h"
#include "display/nr-arena-item.h"
#include "display/nr-filter-types.h"
#include "display/nr-filter-gaussian.h"
@@ -64,95 +66,153 @@ inline static int _min2(const double a, const double b) {
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(NRArenaItem *item, cairo_t *bgct, NRRectL const *bgarea,
+ cairo_surface_t *graphic, NRRectL const *graphicarea, FilterUnits const &u)
+ : _item(item)
+ , _source_graphic(graphic)
+ , _background_ct(bgct)
+ , _source_graphic_area(graphicarea)
+ , _background_area(bgarea)
+ , _units(u)
+ , _last_out(NR_FILTER_SOURCEGRAPHIC)
+ , filterquality(FILTER_QUALITY_BEST)
+ , blurquality(BLUR_QUALITY_BEST)
{
- _slot_count = ((slots > 0) ? slots : 2);
- _slot = new NRPixBlock*[_slot_count];
- _slot_number = new int[_slot_count];
-
- for (int i = 0 ; i < _slot_count ; i++) {
- _slot[i] = NULL;
- _slot_number[i] = NR_FILTER_SLOT_NOT_SET;
- }
+ using Geom::X;
+ using Geom::Y;
+
+ // compute slot bbox
+ Geom::Rect bbox(
+ Geom::Point(_source_graphic_area->x0, _source_graphic_area->y0),
+ Geom::Point(_source_graphic_area->x1, _source_graphic_area->y1));
+
+ Geom::Matrix trans = _units.get_matrix_display2pb();
+
+ Geom::Rect bbox_trans = bbox * trans;
+ Geom::Point min = bbox_trans.min();
+ Geom::Point max = bbox_trans.max();
+ _slot_area.x0 = floor(min[X]);
+ _slot_area.y0 = floor(min[Y]);
+ _slot_area.x1 = ceil(max[X]);
+ _slot_area.y1 = ceil(max[Y]);
}
FilterSlot::~FilterSlot()
{
- for (int i = 0 ; i < _slot_count ; i++) {
- if (_slot[i]) {
- nr_pixblock_release(_slot[i]);
- delete _slot[i];
- }
+ for (SlotMap::iterator i = _slots.begin(); i != _slots.end(); ++i) {
+ cairo_surface_destroy(i->second);
}
- delete[] _slot;
- delete[] _slot_number;
}
-NRPixBlock *FilterSlot::get(int slot_nr)
+cairo_surface_t *FilterSlot::getcairo(int slot_nr)
{
- int index = _get_index(slot_nr);
- assert(index >= 0);
+ //int index = _get_index(slot_nr);
+ //assert(index >= 0);
+
+ if (slot_nr == NR_FILTER_SLOT_NOT_SET)
+ slot_nr = _last_out;
+
+ 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 (_slot[index] == 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);
- } 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: {
+ // TODO
+ //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 (_slot[index]) {
- _slot[index]->empty = false;
+ if (s == _slots.end()) {
+ // create empty surface
+ // TODO
+ return NULL;
}
+ return s->second;
- assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
- return _slot[index];
+ //assert(slot_nr == NR_FILTER_SLOT_NOT_SET ||_slot_number[index] == slot_nr);
+ //return _slot[index];
+}
+
+cairo_surface_t *FilterSlot::_get_transformed_source_graphic()
+{
+ Geom::Matrix trans = _units.get_matrix_display2pb();
+
+ cairo_surface_t *tsg = cairo_surface_create_similar(
+ _source_graphic, cairo_surface_get_content(_source_graphic),
+ _slot_area.x1 - _slot_area.x0, _slot_area.y1 - _slot_area.y0);
+ cairo_t *tsg_ct = cairo_create(tsg);
+
+ cairo_translate(tsg_ct, -_slot_area.x0, -_slot_area.y0);
+ ink_cairo_transform(tsg_ct, trans);
+ cairo_translate(tsg_ct, _source_graphic_area->x0, _source_graphic_area->y0);
+ 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;
+}
+
+cairo_surface_t *FilterSlot::_get_transformed_background()
+{
+ return NULL;
}
+cairo_surface_t *FilterSlot::get_result(int res)
+{
+ Geom::Matrix trans = _units.get_matrix_pb2display();
+
+ cairo_surface_t *r = cairo_surface_create_similar(_source_graphic,
+ cairo_surface_get_content(_source_graphic),
+ _source_graphic_area->x1 - _source_graphic_area->x0,
+ _source_graphic_area->y1 - _source_graphic_area->y0);
+ cairo_t *r_ct = cairo_create(r);
+
+ cairo_translate(r_ct, -_source_graphic_area->x0, -_source_graphic_area->y0);
+ ink_cairo_transform(r_ct, trans);
+ cairo_translate(r_ct, _slot_area.x0, _slot_area.y0);
+ 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;
+}
+/*
void FilterSlot::get_final(int slot_nr, NRPixBlock *result) {
NRPixBlock *final_usr = get(slot_nr);
Geom::Matrix trans = units.get_matrix_pb2display();
@@ -173,10 +233,33 @@ void FilterSlot::get_final(int slot_nr, NRPixBlock *result) {
} else {
nr_blit_pixblock_pixblock(result, final_usr);
}
+}*/
+
+void FilterSlot::_set_internal(int slot_nr, cairo_surface_t *surface)
+{
+ // 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;
}
-void FilterSlot::set(int slot_nr, NRPixBlock *pb)
+void FilterSlot::set(int slot_nr, cairo_surface_t *surface)
{
+ g_return_if_fail(surface != NULL);
+
+ if (slot_nr == NR_FILTER_SLOT_NOT_SET)
+ slot_nr = NR_FILTER_UNNAMED_SLOT;
+
+ _set_internal(slot_nr, surface);
+ _last_out = slot_nr;
+
+#if 0
/* 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
@@ -216,6 +299,11 @@ void FilterSlot::set(int slot_nr, NRPixBlock *pb)
trans[1] * x1 + trans[3] * y0 + trans[5],
trans[1] * x1 + trans[3] * y1 + trans[5]);
+ cairo_surface_t *trans_s = cairo_surface_create_similar(s,
+ CAIRO_CONTENT_COLOR, max_x - min_x, max_y - min_y);
+ cairo_t *ct = cairo_create(trans_s);
+
+
nr_pixblock_setup_fast(trans_pb, pb->mode,
min_x, min_y,
max_x, max_y, true);
@@ -270,25 +358,24 @@ void FilterSlot::set(int slot_nr, NRPixBlock *pb)
}
_slot[index] = pb;
_last_out = index;
+#endif
}
int FilterSlot::get_slot_count()
{
+ return _slots.size();
+ /*
int seek = _slot_count;
do {
seek--;
} while (!_slot[seek] && _slot_number[seek] == NR_FILTER_SLOT_NOT_SET);
- return seek + 1;
-}
-
-NRArenaItem const* FilterSlot::get_arenaitem()
-{
- return _arena_item;
+ return seek + 1;*/
}
int FilterSlot::_get_index(int slot_nr)
{
+#if 0
assert(slot_nr >= 0 ||
slot_nr == NR_FILTER_SLOT_NOT_SET ||
slot_nr == NR_FILTER_SOURCEGRAPHIC ||
@@ -340,10 +427,8 @@ int FilterSlot::_get_index(int slot_nr)
index = seek + 1;
}
return index;
-}
-
-void FilterSlot::set_units(FilterUnits const &units) {
- this->units = units;
+#endif
+ return 0;
}
void FilterSlot::set_quality(FilterQuality const q) {
diff --git a/src/display/nr-filter-slot.h b/src/display/nr-filter-slot.h
index 8d7a82d2d..92f66ddf8 100644
--- a/src/display/nr-filter-slot.h
+++ b/src/display/nr-filter-slot.h
@@ -14,6 +14,8 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <map>
+#include <cairo.h>
#include "libnr/nr-pixblock.h"
#include "display/nr-filter-types.h"
#include "display/nr-filter-units.h"
@@ -26,13 +28,11 @@ 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
+ * Parameter specifies the surface which should be used
* for background accesses from filters.
*/
- FilterSlot(int slots, NRArenaItem const *item);
+ FilterSlot(NRArenaItem *item, cairo_t *bgct, NRRectL const *bgarea,
+ cairo_surface_t *graphic, NRRectL const *graphicarea, FilterUnits const &u);
/** Destroys the FilterSlot object and all its contents */
virtual ~FilterSlot();
@@ -45,15 +45,8 @@ public:
* 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);
+ NRPixBlock *get(int slot) { return NULL; }
/** Sets or re-sets the pixblock associated with given slot.
* If there was a pixblock already assigned with this slot,
@@ -63,16 +56,19 @@ public:
* Pixblocks passed to this function should be reserved with
* c++ -style new-operator.
*/
- void set(int slot, NRPixBlock *pb);
+ void set(int slot, cairo_surface_t *s);
+
+ void set(int, NRPixBlock*){}
+
+ cairo_surface_t *get_result(int slot_nr);
+
+ NRRectL const *get_slot_area();
/** 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);
@@ -83,25 +79,36 @@ public:
/** Gets the gaussian filtering quality. Affects used interpolation methods */
int get_blurquality(void);
-private:
- NRPixBlock **_slot;
- int *_slot_number;
- int _slot_count;
+ FilterUnits const &get_units() const { return _units; }
+private:
+ typedef std::map<int, cairo_surface_t *> SlotMap;
+ SlotMap _slots;
+ NRArenaItem *_item;
+
+ //Geom::Rect _source_bbox; ///< bounding box of source graphic surface
+ //Geom::Rect _intermediate_bbox; ///< bounding box of intermediate surfaces
+
+ NRRectL _slot_area;
+ cairo_surface_t *_source_graphic;
+ cairo_t *_background_ct;
+ NRRectL const *_source_graphic_area;
+ NRRectL const *_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.cpp b/src/display/nr-filter.cpp
index 24079be4e..a5b5801b1 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -15,6 +15,7 @@
#include <cmath>
#include <cstring>
#include <string>
+#include <cairo.h>
#include "display/nr-filter.h"
#include "display/nr-filter-primitive.h"
@@ -129,20 +130,21 @@ Filter::~Filter()
}
-int Filter::render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct)
+int Filter::render(NRArenaItem const *item, cairo_t *bgct, NRRectL const *bgarea, cairo_t *graphic, NRRectL const *area)
{
if (!_primitive[0]) {
- // TODO: Should clear the input buffer instead of just returning
- return 1;
+ // when no primitives are defined, clear source graphic
+ cairo_set_source_rgba(graphic, 0,0,0,0);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(graphic);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_OVER);
+ return 1;
}
FilterQuality const filterquality = (FilterQuality)item->arena->filterquality;
int const blurquality = item->arena->blurquality;
Geom::Matrix trans = item->ctm;
- FilterSlot slot(_slot_count, item);
- slot.set_quality(filterquality);
- slot.set_blurquality(blurquality);
Geom::Rect item_bbox;
{
@@ -165,11 +167,17 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct)
units.set_item_bbox(item_bbox);
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;
+ if (!(resolution.first > 0 && resolution.second > 0)) {
+ // zero resolution - clear source graphic and return
+ cairo_set_source_rgba(graphic, 0,0,0,0);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(graphic);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_OVER);
+ return 1;
+ }
+
units.set_resolution(resolution.first, resolution.second);
if (_x_pixels > 0) {
units.set_automatic_resolution(false);
@@ -178,17 +186,44 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct)
units.set_automatic_resolution(true);
}
- units.set_paraller(false);
+ /*units.set_paraller(false);
for (int i = 0 ; i < _primitive_count ; i++) {
if (_primitive[i]->get_input_traits() & TRAIT_PARALLER) {
units.set_paraller(true);
break;
}
+ }*/
+ units.set_paraller(true);
+
+ FilterSlot slot(const_cast<NRArenaItem*>(item), bgct, bgarea, cairo_get_target(graphic), area, units);
+ slot.set_quality(filterquality);
+ slot.set_blurquality(blurquality);
+
+ for (int i = 0 ; i < _primitive_count ; i++) {
+ _primitive[i]->render_cairo(slot);
}
- slot.set_units(units);
+ cairo_surface_t *result = slot.get_result(_output_slot);
+ cairo_set_source_surface(graphic, result, 0, 0);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(graphic);
+ cairo_set_operator(graphic, CAIRO_OPERATOR_OVER);
+ cairo_surface_destroy(result);
+
+ //slot.set_units(units);
+
+ /*cairo_surface_t *in = cairo_surface_create_similar(
+ cairo_get_target(ct), CAIRO_CONTENT_COLOR_ALPHA,
+ area->x1 - area->x0, area->y1 - area->y0);
+ cairo_t *inct = cairo_create(in);
+ cairo_translate(inct, -area->x0, -area->y0);
+ cairo_set_source_surface(inct, cairo_get_target(ct), 0, 0);
+ cairo_paint(inct);
+ slot.set(NR_FILTER_SOURCEGRAPHIC, in);
+ cairo_destroy(inct);
+ cairo_surface_destroy(in);*/
- NRPixBlock *in = new NRPixBlock;
+ /*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) {
@@ -197,10 +232,10 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct)
}
nr_blit_pixblock_pixblock(in, pb);
in->empty = FALSE;
- slot.set(NR_FILTER_SOURCEGRAPHIC, in);
+ slot.set(NR_FILTER_SOURCEGRAPHIC, in);*/
// Check that we are rendering a non-empty area
- in = slot.get(NR_FILTER_SOURCEGRAPHIC);
+ /*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)",
@@ -209,17 +244,18 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct)
return 0;
}
in = NULL; // in is now handled by FilterSlot, we should not touch it
+ */
- for (int i = 0 ; i < _primitive_count ; i++) {
+ /*for (int i = 0 ; i < _primitive_count ; i++) {
_primitive[i]->render(slot, units);
- }
+ }*/
- slot.get_final(_output_slot, pb);
+ //slot.get_final(_output_slot, ct, area);
// 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();
+ //_slot_count = slot.get_slot_count();
return 0;
}
diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h
index 08d0254d1..cd805043c 100644
--- a/src/display/nr-filter.h
+++ b/src/display/nr-filter.h
@@ -30,7 +30,12 @@ namespace Filters {
class Filter : public Inkscape::GC::Managed<> {
public:
- int render(NRArenaItem const *item, NRPixBlock *pb, cairo_t *ct);
+ /** 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(NRArenaItem const *item, cairo_t *bgct, NRRectL const *bgarea, cairo_t *graphic, NRRectL const *area);
/**
* Creates a new filter primitive under this filter object.