summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-07-16 23:36:51 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2010-07-16 23:36:51 +0000
commitad8d4b3dc89eee0d50857f7cc48e9d54451aeb37 (patch)
treec2b4dc3bc1ad2919d7dcbc286e855197f67e994e /src
parentSplit out surface blending template into a separate file (diff)
downloadinkscape-ad8d4b3dc89eee0d50857f7cc48e9d54451aeb37.tar.gz
inkscape-ad8d4b3dc89eee0d50857f7cc48e9d54451aeb37.zip
Composite filter
(bzr r9508.1.20)
Diffstat (limited to 'src')
-rw-r--r--src/display/cairo-templates.h122
-rw-r--r--src/display/cairo-utils.cpp47
-rw-r--r--src/display/cairo-utils.h3
-rw-r--r--src/display/nr-filter-blend.cpp11
-rw-r--r--src/display/nr-filter-composite.cpp73
-rw-r--r--src/display/nr-filter-composite.h3
6 files changed, 218 insertions, 41 deletions
diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h
index 8bc8b1f49..79e461cf8 100644
--- a/src/display/cairo-templates.h
+++ b/src/display/cairo-templates.h
@@ -17,6 +17,7 @@
#include "preferences.h"
#endif
+#include <algorithm>
#include <cairo.h>
#include <glib.h>
@@ -32,7 +33,7 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s
cairo_surface_flush(in1);
cairo_surface_flush(in2);
- // WARNING: code below assumes that:
+ // 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
@@ -45,30 +46,50 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s
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 HAVE_OPENMP
- #pragma omp parallel for 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;
+ if (fast_path) {
+ #if HAVE_OPENMP
+ #pragma omp parallel for 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 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 {
@@ -84,9 +105,7 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s
guint32 in2_px = *in2_p;
in2_px <<= 24;
*out_p = blend(*in1_p, in2_px);
- ++in1_p;
- ++in2_p;
- ++out_p;
+ ++in1_p; ++in2_p; ++out_p;
}
}
}
@@ -104,21 +123,82 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s
guint32 in1_px = *in1_p;
in1_px <<= 24;
*out_p = blend(in1_px, *in2_p);
- ++in1_p;
- ++in2_p;
- ++out_p;
+ ++in1_p; ++in2_p; ++out_p;
}
}
} else {
// bpp1 == 1 && bpp2 == 1
- // don't do anything - this should have been handled via Cairo blending
- g_assert_not_reached();
+ if (fast_path) {
+ #if HAVE_OPENMP
+ #pragma omp parallel for 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 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);
}
+#if 0
+template <typename Filter>
+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 and pixel formats
+
+ 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 bpp = cairo_image_surface_get_format(in) == CAIRO_FORMAT_A8 ? 1 : 4;
+
+ 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
+
+ if (bpp == 4) {
+
+ } else {
+ #if HAVE_OPENMP
+ #pragma omp parallel for num_threads(num_threads)
+ #endif
+ }
+}
+#endif
+
// helper macros for pixel extraction
#define EXTRACT_ARGB32(px,a,r,g,b) \
guint32 a, r, g, b; \
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index ce56c21f5..c46caa7be 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -362,8 +362,7 @@ ink_cairo_surface_copy(cairo_surface_t *s)
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));
+ cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s));
return ns;
}
@@ -381,8 +380,7 @@ 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 *alpha = cairo_surface_create_similar(s, CAIRO_CONTENT_ALPHA,
- ink_cairo_surface_get_width(s), ink_cairo_surface_get_height(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);
@@ -394,13 +392,44 @@ ink_cairo_extract_alpha(cairo_surface_t *s)
}
cairo_surface_t *
-ink_cairo_surface_unshare(cairo_surface_t *s)
+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_reference_count(s) > 1) {
- return ink_cairo_surface_copy(s);
+ 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 {
- cairo_surface_reference(s);
- return s;
+ // 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);
}
}
diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h
index 3845d5ebb..1406636d0 100644
--- a/src/display/cairo-utils.h
+++ b/src/display/cairo-utils.h
@@ -88,7 +88,8 @@ 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_unshare(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);
diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp
index 9e911d199..d146dc46d 100644
--- a/src/display/nr-filter-blend.cpp
+++ b/src/display/nr-filter-blend.cpp
@@ -151,23 +151,18 @@ void FilterBlend::render_cairo(FilterSlot &slot)
// input2 is the "background" image
// out should be ARGB32 if any of the inputs is ARGB32
- cairo_surface_t *out = NULL;
+ cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2);
+
if ((ct1 == CAIRO_CONTENT_ALPHA && ct2 == CAIRO_CONTENT_ALPHA)
|| _blend_mode == BLEND_NORMAL)
{
- out = ink_cairo_surface_copy(input2);
+ 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
- // create surface identical to the ARGB32 surface
- if (ct1 == CAIRO_CONTENT_ALPHA) {
- out = ink_cairo_surface_create_identical(input2);
- } else {
- out = ink_cairo_surface_create_identical(input1);
- }
// TODO: convert to Cairo blending operators once we start using the 1.10 series
switch (_blend_mode) {
diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp
index 51652d743..e6abb7bae 100644
--- a/src/display/nr-filter-composite.cpp
+++ b/src/display/nr-filter-composite.cpp
@@ -12,7 +12,8 @@
#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"
@@ -99,6 +100,30 @@ FilterPrimitive * FilterComposite::create() {
FilterComposite::~FilterComposite()
{}
+struct BlendArithmetic {
+ BlendArithmetic(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)
+
+ guint32 ao = _k1*aa*ab + _k2*aa + _k3*ab + _k4; ao = (ao + 255*255) / (255*255);
+ guint32 ro = _k1*ra*rb + _k2*ra + _k3*rb + _k4; ro = (ro + 255*255) / (255*255);
+ guint32 go = _k1*ga*gb + _k2*ga + _k3*gb + _k4; go = (go + 255*255) / (255*255);
+ guint32 bo = _k1*ba*bb + _k2*ba + _k3*bb + _k4; bo = (bo + 255*255) / (255*255);
+
+ ASSEMBLE_ARGB32(pxout, ao, ro, go, bo)
+ return pxout;
+ }
+private:
+ guint32 _k1, _k2, _k3, _k4;
+};
+
+#if 0
int FilterComposite::render(FilterSlot &slot, FilterUnits const &/*units*/) {
NRPixBlock *in1 = slot.get(_input);
NRPixBlock *in2 = slot.get(_input2);
@@ -179,6 +204,52 @@ int FilterComposite::render(FilterSlot &slot, FilterUnits const &/*units*/) {
return 0;
}
+#endif
+
+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, BlendArithmetic(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:
+ cairo_set_operator(ct, CAIRO_OPERATOR_IN);
+ break;
+ case COMPOSITE_OUT:
+ cairo_set_operator(ct, CAIRO_OPERATOR_OUT);
+ break;
+ case COMPOSITE_ATOP:
+ cairo_set_operator(ct, CAIRO_OPERATOR_ATOP);
+ break;
+ case COMPOSITE_XOR:
+ cairo_set_operator(ct, CAIRO_OPERATOR_XOR);
+ break;
+ case COMPOSITE_OVER:
+ case COMPOSITE_DEFAULT:
+ default:
+ // OVER is the default operator
+ break;
+ }
+ cairo_paint(ct);
+ cairo_destroy(ct);
+ }
+
+ slot.set(_output, out);
+ cairo_surface_destroy(out);
+}
+
+bool FilterComposite::can_handle_affine(Geom::Matrix const &)
+{
+ return true;
+}
void FilterComposite::set_input(int input) {
_input = input;
diff --git a/src/display/nr-filter-composite.h b/src/display/nr-filter-composite.h
index b24666531..192e79c69 100644
--- a/src/display/nr-filter-composite.h
+++ b/src/display/nr-filter-composite.h
@@ -26,7 +26,8 @@ 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::Matrix const &);
virtual void set_input(int input);
virtual void set_input(int input, int slot);