summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2010-07-16 20:06:46 +0000
committerKrzysztof Kosiński <tweenk.pl@gmail.com>2010-07-16 20:06:46 +0000
commitd204ded3f6106fd25ffbc43f32d5ec5ac3dcb26d (patch)
tree809b99a838070150fb5ecd69e1405b6e5e415fe6 /src
parentOffset filter (diff)
downloadinkscape-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_insert1
-rw-r--r--src/display/cairo-templates.h143
-rw-r--r--src/display/nr-filter-blend.cpp128
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: