From 73150a4a03282c19b4b04bd2e3b5ff02fb15952e Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 18 Jul 2010 01:31:07 +0200 Subject: Component transfer filter (bzr r9508.1.24) --- src/display/cairo-templates.h | 1 + src/display/cairo-utils.cpp | 7 - src/display/cairo-utils.h | 6 + src/display/nr-filter-colormatrix.cpp | 176 +---------- src/display/nr-filter-colormatrix.h | 7 +- src/display/nr-filter-component-transfer.cpp | 426 +++++++++++++++++---------- src/display/nr-filter-component-transfer.h | 9 +- 7 files changed, 283 insertions(+), 349 deletions(-) (limited to 'src') diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h index c64ad78c1..efbd9c094 100644 --- a/src/display/cairo-templates.h +++ b/src/display/cairo-templates.h @@ -265,6 +265,7 @@ void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter } } } + cairo_surface_mark_dirty(out); } // Some helpers for pixel manipulation diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index c46caa7be..a05d28170 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -448,13 +448,6 @@ ink_cairo_surface_get_height(cairo_surface_t *surface) return cairo_image_surface_get_height(surface); } -// taken from Cairo sources -static inline guint32 premul_alpha(guint32 color, guint32 alpha) -{ - guint32 temp = alpha * color + 128; - return (temp + (temp >> 8)) >> 8; -} - /** * @brief Convert pixel data from GdkPixbuf format to ARGB. * This will convert pixel data from GdkPixbuf format to Cairo's native pixel format. diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 1406636d0..02bfe0f73 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -98,6 +98,12 @@ void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs); void convert_pixbuf_normal_to_argb32(GdkPixbuf *); void convert_pixbuf_argb32_to_normal(GdkPixbuf *); +inline guint32 premul_alpha(guint32 color, guint32 alpha) +{ + guint32 temp = alpha * color + 128; + return (temp + (temp >> 8)) >> 8; +} + // TODO: move those to 2Geom void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv, Geom::Matrix trans, Geom::OptRect area, bool optimize_stroke, double stroke_width); void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv); diff --git a/src/display/nr-filter-colormatrix.cpp b/src/display/nr-filter-colormatrix.cpp index d0926ebcc..94459e3aa 100644 --- a/src/display/nr-filter-colormatrix.cpp +++ b/src/display/nr-filter-colormatrix.cpp @@ -15,8 +15,7 @@ #include "display/cairo-templates.h" #include "display/cairo-utils.h" #include "display/nr-filter-colormatrix.h" -#include "display/nr-filter-units.h" -#include "display/nr-filter-utils.h" +#include "display/nr-filter-slot.h" namespace Inkscape { namespace Filters { @@ -32,173 +31,6 @@ FilterPrimitive * FilterColorMatrix::create() { FilterColorMatrix::~FilterColorMatrix() {} -#if 0 -int FilterColorMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feColorMatrix (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - if ((type==COLORMATRIX_SATURATE || type==COLORMATRIX_HUEROTATE) && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N) { - // saturate and hueRotate do not touch the alpha channel and are linear (per-pixel) operations, so no premultiplied -> non-premultiplied operation is necessary - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - } else { - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - } - - // this primitive is defined for non-premultiplied RGBA values, - // thus convert them to that format - // However, since not all operations care, the input is only transformed if necessary. - bool free_in_on_exit = false; - if (in->mode != out->mode) { - NRPixBlock *original_in = in; - in = new NRPixBlock; - nr_pixblock_setup_fast(in, out->mode, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - true); - nr_blit_pixblock_pixblock(in, original_in); - free_in_on_exit = true; - } - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - unsigned char r,g,b,a; - int x,y,x0,y0,x1,y1,i; - x0=in->area.x0; - y0=in->area.y0; - x1=in->area.x1; - y1=in->area.y1; - - switch(type){ - case COLORMATRIX_MATRIX: - { - if (values.size()!=20) { - g_warning("ColorMatrix: values parameter error. Wrong size: %i.", static_cast(values.size())); - return -1; - } - double a04 = 255*values[4]; - double a14 = 255*values[9]; - double a24 = 255*values[14]; - double a34 = 255*values[19]; - for (x=x0;x( r*a00 + g*a01 + b*a02 + .5 ); - out_data[i+1] = static_cast( r*a10 + g*a11 + b*a12 + .5 ); - out_data[i+2] = static_cast( r*a20 + g*a21 + b*a22 + .5 ); - out_data[i+3] = a; - } - } - } - break; - case COLORMATRIX_HUEROTATE: - { - double coshue = cos(value * M_PI/180.0); - double sinhue = sin(value * M_PI/180.0); - double a00 = 0.213 + coshue*( 0.787) + sinhue*(-0.213); - double a01 = 0.715 + coshue*(-0.715) + sinhue*(-0.715); - double a02 = 0.072 + coshue*(-0.072) + sinhue*( 0.928); - double a10 = 0.213 + coshue*(-0.213) + sinhue*( 0.143); - double a11 = 0.715 + coshue*( 0.285) + sinhue*( 0.140); - double a12 = 0.072 + coshue*(-0.072) + sinhue*(-0.283); - double a20 = 0.213 + coshue*(-0.213) + sinhue*(-0.787); - double a21 = 0.715 + coshue*(-0.715) + sinhue*( 0.715); - double a22 = 0.072 + coshue*( 0.928) + sinhue*( 0.072); - if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) { - // Although it does not change the alpha channel, it can give "out-of-bound" results, and in this case the bound is determined by the alpha channel - for (x=x0;x(std::max(0.0,std::min((double)a, r*a00 + g*a01 + b*a02 + .5 ))); - out_data[i+1] = static_cast(std::max(0.0,std::min((double)a, r*a10 + g*a11 + b*a12 + .5 ))); - out_data[i+2] = static_cast(std::max(0.0,std::min((double)a, r*a20 + g*a21 + b*a22 + .5 ))); - out_data[i+3] = a; - } - } - } else { - for (x=x0;x( r*0.2125 + g*0.7154 + b*0.0721 + .5 ); - } - } - break; - case COLORMATRIX_ENDTYPE: - break; - } - - if (free_in_on_exit) { - nr_pixblock_release(in); - delete in; - } - - out->empty = FALSE; - slot.set(_output, out); - return 0; -} -#endif - struct ColorMatrixMatrix { ColorMatrixMatrix(std::vector const &values) { unsigned limit = std::min(20ul, values.size()); @@ -214,12 +46,6 @@ struct ColorMatrixMatrix { } } - static inline guint32 premul_alpha(guint32 color, guint32 alpha) - { - guint32 temp = alpha * color + 128; - return (temp + (temp >> 8)) >> 8; - } - guint32 operator()(guint32 in) { EXTRACT_ARGB32(in, a, r, g, b) // we need to un-premultiply alpha values for this type of matrix diff --git a/src/display/nr-filter-colormatrix.h b/src/display/nr-filter-colormatrix.h index e3beb943d..c95c84568 100644 --- a/src/display/nr-filter-colormatrix.h +++ b/src/display/nr-filter-colormatrix.h @@ -12,14 +12,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include +#include <2geom/forward.h> #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterColorMatrixType { COLORMATRIX_MATRIX, COLORMATRIX_SATURATE, diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp index ab9990360..05795d670 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -10,13 +10,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-component-transfer.h" -#include "display/nr-filter-units.h" -#include "display/nr-filter-utils.h" -#include "libnr/nr-pixblock.h" -#include "libnr/nr-blit.h" -#include "libnr/nr-pixops.h" #include +#include "display/cairo-templates.h" +#include "display/cairo-utils.h" +#include "display/nr-filter-component-transfer.h" +#include "display/nr-filter-slot.h" namespace Inkscape { namespace Filters { @@ -32,170 +30,278 @@ FilterPrimitive * FilterComponentTransfer::create() { FilterComponentTransfer::~FilterComponentTransfer() {} -int FilterComponentTransfer::render(FilterSlot &slot, FilterUnits const &/*units*/) { - NRPixBlock *in = slot.get(_input); +struct ComponentTransfer { + ComponentTransfer(guint32 color) + : _shift(color * 8) + , _mask(0xff << _shift) + {} +protected: + guint32 _shift; + guint32 _mask; +}; + +template +struct ComponentTransferTable; + +template <> +struct ComponentTransferTable : public ComponentTransfer { + ComponentTransferTable(guint32 color, std::vector const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + component = (255 * component + alpha/2) / alpha; + guint32 k = (_v.size() - 1) * component; + guint32 dx = k % 255; k /= 255; + component = _v[k]*255 + (_v[k+1] - _v[k])*dx; + component = (component + 127) / 255; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); + } +private: + std::vector _v; +}; + +template <> +struct ComponentTransferTable { + ComponentTransferTable(std::vector const &values) + : _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + guint32 k = (_v.size() - 1) * alpha; + guint32 dx = k % 255; k /= 255; + alpha = _v[k]*255 + (_v[k+1] - _v[k])*dx; + alpha = (alpha + 127) / 255; + return (in & 0x00ffffff) | (alpha << 24); + } +private: + std::vector _v; +}; + +template +struct ComponentTransferDiscrete; + +template <> +struct ComponentTransferDiscrete : public ComponentTransfer { + ComponentTransferDiscrete(guint32 color, std::vector const &values) + : ComponentTransfer(color) + , _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + component = (255 * component + alpha/2) / alpha; + guint32 k = (_v.size() - 1) * component / 255; + component = _v[k]; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); + } +private: + std::vector _v; +}; + +template <> +struct ComponentTransferDiscrete { + ComponentTransferDiscrete(std::vector const &values) + : _v(values.size()) + { + for (unsigned i = 0; i< values.size(); ++i) { + _v[i] = round(CLAMP(values[i], 0.0, 1.0) * 255); + } + } + guint32 operator()(guint32 in) { + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return in; + + guint32 k = (_v.size() - 1) * alpha / 255; + alpha = _v[k]; + return (in & 0x00ffffff) | (alpha << 24); + } +private: + std::vector _v; +}; + +template +struct ComponentTransferLinear; + +template <> +struct ComponentTransferLinear : public ComponentTransfer { + ComponentTransferLinear(guint32 color, double intercept, double slope) + : ComponentTransfer(color) + , _intercept(round(intercept*255*255)) + , _slope(round(slope*255)) + {} + guint32 operator()(guint32 in) { + gint32 component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return 0; - if (!in) { - g_warning("Missing source image for feComponentTransfer (in=%d)", _input); - return 1; + // TODO: this can probably be reduced to something simpler + component = (255 * component + alpha/2) / alpha; + component = pxclamp(_slope * component + _intercept, 0, 255*255); + component = (component + 127) / 255; + component = premul_alpha(component, alpha); + return (in & ~_mask) | (component << _shift); } +private: + gint32 _intercept; + gint32 _slope; +}; - int x0=in->area.x0; - int x1=in->area.x1; - int y0=in->area.y0; - int y1=in->area.y1; - - // this primitive is defined for RGBA values, - // thus convert them to that format before blending - bool free_in_on_exit = false; - if (in->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && in->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - NRPixBlock *original_in = in; - in = new NRPixBlock; - nr_pixblock_setup_fast(in, NR_PIXBLOCK_MODE_R8G8B8A8N, - original_in->area.x0, original_in->area.y0, - original_in->area.x1, original_in->area.y1, - false); - nr_blit_pixblock_pixblock(in, original_in); - free_in_on_exit = true; +template <> +struct ComponentTransferLinear { + ComponentTransferLinear(double intercept, double slope) + : _intercept(round(intercept*255*255)) + , _slope(round(slope*255)) + {} + guint32 operator()(guint32 in) { + gint32 alpha = (in & 0xff000000) >> 24; + alpha = pxclamp(_slope * alpha + _intercept, 0, 255*255); + alpha = (alpha + 127) / 255; + return (in & 0x00ffffff) | (alpha << 24); } - bool premultiplied = in->mode == NR_PIXBLOCK_MODE_R8G8B8A8P; - - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, in->mode, x0, y0, x1, y1, true); - - unsigned char *in_data = NR_PIXBLOCK_PX(in); - unsigned char *out_data = NR_PIXBLOCK_PX(out); - - (void)in_data; - (void)out_data; - - int size = 4 * (y1-y0) * (x1-x0); - int i; - - int color=4; - while(color-->0) { - int _vsize = tableValues[color].size(); - double _intercept = intercept[color]; - double _slope = slope[color]; - double _amplitude = amplitude[color]; - double _exponent = exponent[color]; - double _offset = offset[color]; - switch(type[color]){ - case COMPONENTTRANSFER_TYPE_IDENTITY: - for(i=color;i _tableValues(tableValues[color]); - // Scale by 255 and add .5 to avoid having to add it later for rounding purposes - // Note that this means that CLAMP_D_TO_U8 cannot be used here (as it includes rounding!) - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(255.,255*_tableValues[i])) + .5; - } - for(i=color;i((_vsize-1) * in_data[i]); - double dx = ((_vsize-1) * in_data[i])/255.0 - k; - out_data[i] = static_cast(_tableValues[k] + dx * (_tableValues[k+1] - _tableValues[k])); - } - } else { - std::vector _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i _tableValues(_vsize); - // Convert to unsigned char - for(i=0;i<_vsize;i++) { - _tableValues[i] = CLAMP_D_TO_U8(255*tableValues[color][i]); - } - for(i=color;i((_vsize-1) * in_data[i]); - out_data[i] = _tableValues[k]; - } - } else { - std::vector _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i +struct ComponentTransferGamma; + +template <> +struct ComponentTransferGamma : public ComponentTransfer { + ComponentTransferGamma(guint32 color, double amplitude, double exponent, double offset) + : ComponentTransfer(color) + , _amplitude(amplitude) + , _exponent(exponent) + , _offset(offset) + {} + guint32 operator()(guint32 in) { + double component = (in & _mask) >> _shift; + guint32 alpha = (in & 0xff000000) >> 24; + if (alpha == 0) return 0; + + double alphaf = alpha; + component /= alphaf; + component = _amplitude * pow(component, _exponent) + _offset; + guint32 cpx = pxclamp(component * alphaf, 0, 255); + return (in & ~_mask) | (cpx << _shift); + } +private: + double _amplitude; + double _exponent; + double _offset; +}; + +template <> +struct ComponentTransferGamma { + ComponentTransferGamma(double amplitude, double exponent, double offset) + : _amplitude(amplitude) + , _exponent(exponent) + , _offset(offset) + {} + guint32 operator()(guint32 in) { + double alpha = (in & 0xff000000) >> 24; + alpha /= 255.0; + alpha = _amplitude * pow(alpha, _exponent) + _offset; + guint32 cpx = pxclamp(alpha * 255.0, 0, 255); + return (in & 0x00ffffff) | (cpx << 24); + } +private: + double _amplitude; + double _exponent; + double _offset; +}; + +void FilterComponentTransfer::render_cairo(FilterSlot &slot) +{ + cairo_surface_t *input = slot.getcairo(_input); + cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA); + //cairo_surface_t *outtemp = ink_cairo_surface_create_identical(out); + ink_cairo_surface_blit(input, out); + + // parameters: R = 0, G = 1, B = 2, A = 3 + // Cairo: R = 2, G = 1, B = 0, A = 3 + for (unsigned i = 0; i < 3; ++i) { + guint32 color = 2 - i; + switch (type[i]) { + case COMPONENTTRANSFER_TYPE_TABLE: + ink_cairo_surface_filter(out, out, + ComponentTransferTable(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear(color, intercept[i], slope[i])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma(color, amplitude[i], exponent[i], offset[i])); + break; + case COMPONENTTRANSFER_TYPE_ERROR: + case COMPONENTTRANSFER_TYPE_IDENTITY: + default: + break; } + //ink_cairo_surface_blit(out, outtemp); } - if (free_in_on_exit) { - nr_pixblock_release(in); - delete in; + // fast paths for alpha channel + switch (type[3]) { + case COMPONENTTRANSFER_TYPE_TABLE: + ink_cairo_surface_filter(out, out, + ComponentTransferTable(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear(intercept[3], slope[3])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma(amplitude[3], exponent[3], offset[3])); + break; + case COMPONENTTRANSFER_TYPE_ERROR: + case COMPONENTTRANSFER_TYPE_IDENTITY: + default: + break; } - out->empty = FALSE; slot.set(_output, out); - return 0; + cairo_surface_destroy(out); + //cairo_surface_destroy(outtemp); +} + +bool FilterComponentTransfer::can_handle_affine(Geom::Matrix const &) +{ + return true; } void FilterComponentTransfer::area_enlarge(NRRectL &/*area*/, Geom::Matrix const &/*trans*/) diff --git a/src/display/nr-filter-component-transfer.h b/src/display/nr-filter-component-transfer.h index eb76bd543..1e69deb15 100644 --- a/src/display/nr-filter-component-transfer.h +++ b/src/display/nr-filter-component-transfer.h @@ -12,14 +12,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" #include +#include "display/nr-filter-primitive.h" namespace Inkscape { namespace Filters { +class FilterSlot; + enum FilterComponentTransferType { COMPONENTTRANSFER_TYPE_IDENTITY, COMPONENTTRANSFER_TYPE_TABLE, @@ -35,7 +35,8 @@ public: static FilterPrimitive *create(); virtual ~FilterComponentTransfer(); - virtual int render(FilterSlot &slot, FilterUnits const &units); + virtual void render_cairo(FilterSlot &slot); + virtual bool can_handle_affine(Geom::Matrix const &); virtual void area_enlarge(NRRectL &area, Geom::Matrix const &trans); FilterComponentTransferType type[4]; -- cgit v1.2.3