diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-07-16 20:06:46 +0000 |
|---|---|---|
| committer | Krzysztof Kosiński <tweenk.pl@gmail.com> | 2010-07-16 20:06:46 +0000 |
| commit | d204ded3f6106fd25ffbc43f32d5ec5ac3dcb26d (patch) | |
| tree | 809b99a838070150fb5ecd69e1405b6e5e415fe6 /src | |
| parent | Offset filter (diff) | |
| download | inkscape-d204ded3f6106fd25ffbc43f32d5ec5ac3dcb26d.tar.gz inkscape-d204ded3f6106fd25ffbc43f32d5ec5ac3dcb26d.zip | |
Split out surface blending template into a separate file
(bzr r9508.1.19)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/Makefile_insert | 1 | ||||
| -rw-r--r-- | src/display/cairo-templates.h | 143 | ||||
| -rw-r--r-- | src/display/nr-filter-blend.cpp | 128 |
3 files changed, 157 insertions, 115 deletions
diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index da5ded824..621dc43d5 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -4,6 +4,7 @@ display/canvas-arena.$(OBJEXT): helper/sp-marshal.h display/sp-canvas.$(OBJEXT): helper/sp-marshal.h ink_common_sources += \ + display/cairo-templates.h \ display/cairo-utils.cpp \ display/cairo-utils.h \ display/canvas-arena.cpp \ diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h new file mode 100644 index 000000000..8bc8b1f49 --- /dev/null +++ b/src/display/cairo-templates.h @@ -0,0 +1,143 @@ +/** + * @file + * @brief Cairo software blending templates + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_TEMPLATES_H +#define SEEN_INKSCAPE_DISPLAY_CAIRO_TEMPLATES_H + +#ifdef HAVE_OPENMP +#include <omp.h> +#include "preferences.h" +#endif + +#include <cairo.h> +#include <glib.h> + +/** + * @brief Blend two surfaces using the supplied functor. + * This template blends two Cairo image surfaces using a blending functor that takes + * two 32-bit ARGB pixel values and returns a modified 32-bit pixel value. + * Differences in input surface formats are handled transparently. In future, this template + * will also handle software fallback for GL surfaces. */ +template <typename Blend> +void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_surface_t *out, Blend blend) +{ + cairo_surface_flush(in1); + cairo_surface_flush(in2); + + // 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 + // 3. Both surfaces are of the same size + // 4. Output surface is ARGB32 if at least one input is ARGB32 + + int w = cairo_image_surface_get_width(in2); + int h = cairo_image_surface_get_height(in2); + int stride1 = cairo_image_surface_get_stride(in1); + int stride2 = cairo_image_surface_get_stride(in2); + int strideout = cairo_image_surface_get_stride(out); + int bpp1 = cairo_image_surface_get_format(in1) == CAIRO_FORMAT_A8 ? 1 : 4; + int bpp2 = cairo_image_surface_get_format(in2) == CAIRO_FORMAT_A8 ? 1 : 4; + + 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) { + *out_p = blend(*in1_p, *in2_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; + *out_p = blend(*in1_p, in2_px); + ++in1_p; + ++in2_p; + ++out_p; + } + } + } + } else { + if (bpp2 == 4) { + // bpp1 == 1 + #if HAVE_OPENMP + #pragma omp parallel for num_threads(num_threads) + #endif + for (int i = 0; i < h; ++i) { + guint8 *in1_p = reinterpret_cast<guint8*>(in1_data) + i * stride1; + guint32 *in2_p = in2_data + i * stride2/4; + guint32 *out_p = out_data + i * strideout/4; + for (int j = 0; j < w; ++j) { + guint32 in1_px = *in1_p; + in1_px <<= 24; + *out_p = blend(in1_px, *in2_p); + ++in1_p; + ++in2_p; + ++out_p; + } + } + } else { + // bpp1 == 1 && bpp2 == 1 + // don't do anything - this should have been handled via Cairo blending + g_assert_not_reached(); + } + } + + cairo_surface_mark_dirty(out); +} + +// helper macros for pixel extraction +#define EXTRACT_ARGB32(px,a,r,g,b) \ + guint32 a, r, g, b; \ + a = (px & 0xff000000) >> 24; \ + r = (px & 0x00ff0000) >> 16; \ + g = (px & 0x0000ff00) >> 8; \ + b = (px & 0x000000ff); + +#define ASSEMBLE_ARGB32(px,a,r,g,b) \ + guint32 px = (a << 24) | (r << 16) | (g << 8) | b; + +#endif +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp index 8a1cfa6f4..9e911d199 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -19,6 +19,7 @@ #include "config.h" #endif +#include "display/cairo-templates.h" #include "display/cairo-utils.h" #include "display/nr-filter-blend.h" #include "display/nr-filter-primitive.h" @@ -55,19 +56,9 @@ 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) + guint32 operator()(guint32 in1, guint32 in2) { EXTRACT_ARGB32(in1, aa, ra, ga, ba) EXTRACT_ARGB32(in2, ab, rb, gb, bb) @@ -78,13 +69,13 @@ struct BlendMultiply { guint32 bo = (255-aa)*bb + (255-ab)*ba + ba*bb; bo = (bo + 127) / 255; ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - *out = pxout; + return pxout; } }; // cr = cb + ca - ca * cb struct BlendScreen { - void operator()(guint32 in1, guint32 in2, guint32 *out) + guint32 operator()(guint32 in1, guint32 in2) { EXTRACT_ARGB32(in1, aa, ra, ga, ba) EXTRACT_ARGB32(in2, ab, rb, gb, bb) @@ -95,13 +86,13 @@ struct BlendScreen { guint32 bo = 255*(bb + ba) - ba * bb; bo = (bo + 127) / 255; ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - *out = pxout; + return pxout; } }; // cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) struct BlendDarken { - void operator()(guint32 in1, guint32 in2, guint32 *out) + guint32 operator()(guint32 in1, guint32 in2) { EXTRACT_ARGB32(in1, aa, ra, ga, ba) EXTRACT_ARGB32(in2, ab, rb, gb, bb) @@ -112,13 +103,13 @@ struct BlendDarken { 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; + return pxout; } }; // cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) struct BlendLighten { - void operator()(guint32 in1, guint32 in2, guint32 *out) + guint32 operator()(guint32 in1, guint32 in2) { EXTRACT_ARGB32(in1, aa, ra, ga, ba) EXTRACT_ARGB32(in2, ab, rb, gb, bb) @@ -129,7 +120,7 @@ struct BlendLighten { 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; + return pxout; } }; @@ -150,99 +141,6 @@ static inline void blend_alpha(guint32 in1, guint32 in2, guint32 *out) } */ -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 - // 3. Both surfaces are of the same size - - 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); @@ -274,16 +172,16 @@ void FilterBlend::render_cairo(FilterSlot &slot) // 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); + ink_cairo_surface_blend(input1, input2, out, BlendMultiply()); break; case BLEND_SCREEN: - surface_blend<BlendScreen>(input1, input2, out); + ink_cairo_surface_blend(input1, input2, out, BlendScreen()); break; case BLEND_DARKEN: - surface_blend<BlendDarken>(input1, input2, out); + ink_cairo_surface_blend(input1, input2, out, BlendDarken()); break; case BLEND_LIGHTEN: - surface_blend<BlendLighten>(input1, input2, out); + ink_cairo_surface_blend(input1, input2, out, BlendLighten()); break; case BLEND_NORMAL: default: |
