diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-07-16 23:36:51 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-07-16 23:36:51 +0000 |
| commit | ad8d4b3dc89eee0d50857f7cc48e9d54451aeb37 (patch) | |
| tree | c2b4dc3bc1ad2919d7dcbc286e855197f67e994e /src | |
| parent | Split out surface blending template into a separate file (diff) | |
| download | inkscape-ad8d4b3dc89eee0d50857f7cc48e9d54451aeb37.tar.gz inkscape-ad8d4b3dc89eee0d50857f7cc48e9d54451aeb37.zip | |
Composite filter
(bzr r9508.1.20)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/cairo-templates.h | 122 | ||||
| -rw-r--r-- | src/display/cairo-utils.cpp | 47 | ||||
| -rw-r--r-- | src/display/cairo-utils.h | 3 | ||||
| -rw-r--r-- | src/display/nr-filter-blend.cpp | 11 | ||||
| -rw-r--r-- | src/display/nr-filter-composite.cpp | 73 | ||||
| -rw-r--r-- | src/display/nr-filter-composite.h | 3 |
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); |
