diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-07-14 02:32:10 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-07-14 02:32:10 +0000 |
| commit | bb8404b19557519bd828113fa93604b10e9e7fe3 (patch) | |
| tree | 01fd9c531829445801184b172fd7636cde1ab86e /src/display | |
| parent | Gaussian blur (diff) | |
| download | inkscape-bb8404b19557519bd828113fa93604b10e9e7fe3.tar.gz inkscape-bb8404b19557519bd828113fa93604b10e9e7fe3.zip | |
Merge redundant *-fns.h into respective filter headers.
Move gaussian blur to filters directory.
Blend filter effect.
(bzr r9508.1.16)
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/cairo-utils.cpp | 20 | ||||
| -rw-r--r-- | src/display/nr-arena-group.cpp | 2 | ||||
| -rw-r--r-- | src/display/nr-arena-image.cpp | 4 | ||||
| -rw-r--r-- | src/display/nr-arena-item.h | 31 | ||||
| -rw-r--r-- | src/display/nr-filter-blend.cpp | 253 | ||||
| -rw-r--r-- | src/display/nr-filter-blend.h | 2 | ||||
| -rw-r--r-- | src/display/nr-filter-gaussian.cpp | 429 | ||||
| -rw-r--r-- | src/display/nr-filter-gaussian.h | 3 | ||||
| -rw-r--r-- | src/display/nr-filter-primitive.h | 16 | ||||
| -rw-r--r-- | src/display/nr-filter-slot.cpp | 39 |
10 files changed, 341 insertions, 458 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index a063a62bb..36202f42e 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -337,11 +337,21 @@ 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); + 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; } diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp index 0fa5f332a..3bc78ea56 100644 --- a/src/display/nr-arena-group.cpp +++ b/src/display/nr-arena-group.cpp @@ -14,12 +14,10 @@ #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" diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp index 325cff65a..422b691b7 100644 --- a/src/display/nr-arena-image.cpp +++ b/src/display/nr-arena-image.cpp @@ -21,12 +21,8 @@ #include "display/cairo-utils.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; diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 468d352bc..447307535 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -1,6 +1,3 @@ -#ifndef __NR_ARENA_ITEM_H__ -#define __NR_ARENA_ITEM_H__ - /* * RGBA display list system for inkscape * @@ -13,6 +10,22 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#ifndef SEEN_DISPLAY_NR_ARENA_ITEM_H +#define SEEN_DISPLAY_NR_ARENA_ITEM_H + +#include <cairo.h> +#include <2geom/matrix.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" + +namespace Inkscape { +namespace Filters { +class Filter; +} } + #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)) @@ -52,15 +65,6 @@ #define NR_ARENA_ITEM_RENDER_NO_CACHE (1 << 0) -#include <2geom/matrix.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; @@ -181,8 +185,7 @@ NRArenaItem *nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child); #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__ */ +#endif /* !SEEN_DISPLAY_NR_ARENA_ITEM_H */ /* Local Variables: diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp index 4645d9bc0..4ce37ae3b 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -15,6 +15,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "display/cairo-utils.h" #include "display/nr-filter-blend.h" #include "display/nr-filter-pixops.h" #include "display/nr-filter-primitive.h" @@ -24,6 +29,7 @@ #include "libnr/nr-pixblock.h" #include "libnr/nr-blit.h" #include "libnr/nr-pixops.h" +#include "preferences.h" namespace Inkscape { namespace Filters { @@ -125,6 +131,247 @@ FilterPrimitive * FilterBlend::create() { FilterBlend::~FilterBlend() {} +#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; + +// cr = (1-qa)*cb + (1-qb)*ca + ca*cb +struct BlendMultiply { + void operator()(guint32 in1, guint32 in2, guint32 *out) + { + 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; + + ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) + *out = pxout; + } +}; + +// cr = cb + ca - ca * cb +struct BlendScreen { + void operator()(guint32 in1, guint32 in2, guint32 *out) + { + 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) + *out = pxout; + } +}; + +// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +struct BlendDarken { + void operator()(guint32 in1, guint32 in2, guint32 *out) + { + 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 = 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) + *out = pxout; + } +}; + +// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +struct BlendLighten { + void operator()(guint32 in1, guint32 in2, guint32 *out) + { + 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 = 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) + *out = pxout; + } +}; + +/* +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; +} +*/ + +template <typename Blend> +void surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_surface_t *out) +{ + cairo_surface_flush(in1); + cairo_surface_flush(in2); + + // WARNING: code below assumes that: + // 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 = 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; + // assumption: out surface is CAIRO_FORMAT_ARGB32 if at least one input is ARGB32 + + 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); + + #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 (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) { + Blend()(*in1_p, *in2_p, out_p); + ++in1_p; + ++in2_p; + ++out_p; + } + } + } else { + // bpp2 == 1 + #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; + 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; + Blend()(*in1_p, in2_px, out_p); + ++in1_p; + ++in2_p; + ++out_p; + } + } + } + } else { + if (bpp2 == 4) { + // bpp1 == 1 + #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; + 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; + Blend()(in1_px, *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(); + } + } + + cairo_surface_mark_dirty(out); +} + +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 = NULL; + if ((ct1 == CAIRO_CONTENT_ALPHA && ct2 == CAIRO_CONTENT_ALPHA) + || _blend_mode == BLEND_NORMAL) + { + out = ink_cairo_surface_copy(input2); + 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) { + case BLEND_MULTIPLY: + surface_blend<BlendMultiply>(input1, input2, out); + break; + case BLEND_SCREEN: + surface_blend<BlendScreen>(input1, input2, out); + break; + case BLEND_DARKEN: + surface_blend<BlendDarken>(input1, input2, out); + break; + case BLEND_LIGHTEN: + surface_blend<BlendLighten>(input1, input2, out); + break; + case BLEND_NORMAL: + default: + // this was handled before + g_assert_not_reached(); + break; + } + } + + slot.set(_output, out); + cairo_surface_destroy(out); +} + int FilterBlend::render(FilterSlot &slot, FilterUnits const & /*units*/) { NRPixBlock *in1 = slot.get(_input); NRPixBlock *in2 = slot.get(_input2); @@ -202,6 +449,12 @@ int FilterBlend::render(FilterSlot &slot, FilterUnits const & /*units*/) { return 0; } +bool FilterBlend::can_handle_affine(Geom::Matrix const &) +{ + // blend is a per-pixel primitive and is immutable under transformations + return true; +} + void FilterBlend::set_input(int slot) { _input = slot; } diff --git a/src/display/nr-filter-blend.h b/src/display/nr-filter-blend.h index ffdd62118..45da07f27 100644 --- a/src/display/nr-filter-blend.h +++ b/src/display/nr-filter-blend.h @@ -39,7 +39,9 @@ public: static FilterPrimitive *create(); virtual ~FilterBlend(); + virtual void render_cairo(FilterSlot &slot); virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual bool can_handle_affine(Geom::Matrix const &); virtual void set_input(int slot); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index 2e6bed070..1e59748c4 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -32,8 +32,6 @@ #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 <2geom/matrix.h> #include "util/fixed_point.h" #include "preferences.h" @@ -193,21 +191,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); @@ -287,8 +270,10 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, { #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 @@ -327,7 +312,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, dstimg -= dstr1; if ( PREMULTIPLIED_ALPHA ) { 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]); + PREMUL_ALPHA_LOOP 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]); } @@ -342,7 +327,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2, dstimg -= dstr1; if ( PREMULTIPLIED_ALPHA ) { 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]); + PREMUL_ALPHA_LOOP 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]); } @@ -457,93 +442,6 @@ 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) -{ - 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); - } - } - } -} - -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) -{ - 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; - } - } - } - } -} - static void gaussian_pass_IIR(Geom::Dim2 d, double deviation, cairo_surface_t *src, cairo_surface_t *dest, IIRValue **tmpdata, int num_threads) @@ -735,315 +633,6 @@ void FilterGaussian::render_cairo(FilterSlot &slot) } } -int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units) -{ - // 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; - } - - // 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::Matrix 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 - int const NTHREADS = std::max(1,std::min(8, Inkscape::Preferences::get()->getInt("/options/threading/numthreads", omp_get_num_procs()))); -#else - int const NTHREADS = 1; -#endif // HAVE_OPENMP - - // 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); - - // 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; - } - // 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); - 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; - } - } - } - - // 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); - }; - } - - // 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]; - } - - // 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]; - } - - // 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 - } - - // Upsampling, stores (the upsampled) out using slot.set(_output, ...) - if ( !resampling ) { - // No upsampling needed - out->empty = FALSE; - slot.set(_output, out); - } 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); - } - - // 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::Matrix const &trans) { int area_x = _effect_area_scr(_deviation_x * trans.expansionX()); @@ -1057,8 +646,14 @@ void FilterGaussian::area_enlarge(NRRectL &area, Geom::Matrix const &trans) area.y1 += area_max; } -FilterTraits FilterGaussian::get_input_traits() { - return TRAIT_PARALLER; +bool FilterGaussian::can_handle_affine(Geom::Matrix const &m) +{ + if (Geom::are_near(_deviation_x, _deviation_y)) { + // TODO after 2Geom sync, change this to m.preservesAngles() + return Geom::are_near(m[0], m[3]) && Geom::are_near(m[1], -m[2]); + } else { + return false; + } } void FilterGaussian::set_deviation(double deviation) diff --git a/src/display/nr-filter-gaussian.h b/src/display/nr-filter-gaussian.h index 7bcabdba9..01ce9efcb 100644 --- a/src/display/nr-filter-gaussian.h +++ b/src/display/nr-filter-gaussian.h @@ -39,9 +39,8 @@ public: 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(); + virtual bool can_handle_affine(Geom::Matrix const &m); /** * Set the standard deviation value for gaussian blur. Deviation along diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h index a7ae0125e..89927fdbd 100644 --- a/src/display/nr-filter-primitive.h +++ b/src/display/nr-filter-primitive.h @@ -44,7 +44,7 @@ public: virtual ~FilterPrimitive(); virtual void render_cairo(FilterSlot &slot); - virtual int render(FilterSlot &slot, FilterUnits const &units) = 0; + virtual int render(FilterSlot &slot, FilterUnits const &units) { return 0; } virtual void area_enlarge(NRRectL &area, Geom::Matrix const &m); /** @@ -109,6 +109,20 @@ public: */ virtual FilterTraits get_input_traits(); + /** @brief 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 will 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 false, the rendering is + * performed in display coordinate space and no intermediate surface is used. */ + virtual bool can_handle_affine(Geom::Matrix const &) { return false; } + protected: int _input; int _output; diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index d700cd433..5371499e4 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -20,7 +20,6 @@ #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" @@ -131,10 +130,9 @@ cairo_surface_t *FilterSlot::getcairo(int slot_nr) 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); + 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); @@ -158,8 +156,12 @@ cairo_surface_t *FilterSlot::getcairo(int slot_nr) if (s == _slots.end()) { // create empty surface - // TODO - return NULL; + cairo_surface_t *empty = cairo_surface_create_similar( + _source_graphic, cairo_surface_get_content(_source_graphic), + _slot_area.x1 - _slot_area.x0, _slot_area.y1 - _slot_area.y0); + _set_internal(slot_nr, empty); + cairo_surface_destroy(empty); + s = _slots.find(slot_nr); } return s->second; @@ -189,7 +191,23 @@ cairo_surface_t *FilterSlot::_get_transformed_source_graphic() cairo_surface_t *FilterSlot::_get_transformed_background() { - return NULL; + Geom::Matrix trans = _units.get_matrix_display2pb(); + + cairo_surface_t *bg = cairo_get_target(_background_ct); + cairo_surface_t *tbg = cairo_surface_create_similar( + bg, cairo_surface_get_content(bg), + _slot_area.x1 - _slot_area.x0, _slot_area.y1 - _slot_area.y0); + cairo_t *tbg_ct = cairo_create(tbg); + + cairo_translate(tbg_ct, -_slot_area.x0, -_slot_area.y0); + ink_cairo_transform(tbg_ct, trans); + cairo_translate(tbg_ct, _background_area->x0, _background_area->y0); + 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); + + return tbg; } cairo_surface_t *FilterSlot::get_result(int res) @@ -299,11 +317,6 @@ void FilterSlot::set(int slot_nr, cairo_surface_t *surface) 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); |
