diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-07-17 23:31:07 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-07-17 23:31:07 +0000 |
| commit | 73150a4a03282c19b4b04bd2e3b5ff02fb15952e (patch) | |
| tree | 034937d8a365f03d65ce995c5792f0627185c828 /src | |
| parent | Color matrix filter. Fix arithmetic operator in feComposite (diff) | |
| download | inkscape-73150a4a03282c19b4b04bd2e3b5ff02fb15952e.tar.gz inkscape-73150a4a03282c19b4b04bd2e3b5ff02fb15952e.zip | |
Component transfer filter
(bzr r9508.1.24)
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/cairo-templates.h | 1 | ||||
| -rw-r--r-- | src/display/cairo-utils.cpp | 7 | ||||
| -rw-r--r-- | src/display/cairo-utils.h | 6 | ||||
| -rw-r--r-- | src/display/nr-filter-colormatrix.cpp | 176 | ||||
| -rw-r--r-- | src/display/nr-filter-colormatrix.h | 7 | ||||
| -rw-r--r-- | src/display/nr-filter-component-transfer.cpp | 426 | ||||
| -rw-r--r-- | src/display/nr-filter-component-transfer.h | 9 |
7 files changed, 283 insertions, 349 deletions
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<int>(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<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - out_data[i] = CLAMP_D_TO_U8( r*values[0] + g*values[1] + b*values[2] + a*values[3] + a04 ); // CLAMP includes rounding! - out_data[i+1] = CLAMP_D_TO_U8( r*values[5] + g*values[6] + b*values[7] + a*values[8] + a14 ); - out_data[i+2] = CLAMP_D_TO_U8( r*values[10] + g*values[11] + b*values[12] + a*values[13] + a24 ); - out_data[i+3] = CLAMP_D_TO_U8( r*values[15] + g*values[16] + b*values[17] + a*values[18] + a34 ); - } - } - } - break; - case COLORMATRIX_SATURATE: - { - double v = std::max(0.0,std::min(1.0,value)); // The standard says it should be between 0 and 1, and clamping it here makes it unnecessary to clamp the color values. - double a00 = 0.213+0.787*v, a01 = 0.715-0.715*v, a02 = 0.072-0.072*v; - double a10 = 0.213-0.213*v, a11 = 0.715+0.285*v, a12 = 0.072-0.072*v; - double a20 = 0.213-0.213*v, a21 = 0.715-0.715*v, a22 = 0.072+0.928*v; - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - out_data[i] = static_cast<unsigned char>( r*a00 + g*a01 + b*a02 + .5 ); - out_data[i+1] = static_cast<unsigned char>( r*a10 + g*a11 + b*a12 + .5 ); - out_data[i+2] = static_cast<unsigned char>( 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<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - - out_data[i] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a00 + g*a01 + b*a02 + .5 ))); - out_data[i+1] = static_cast<unsigned char>(std::max(0.0,std::min((double)a, r*a10 + g*a11 + b*a12 + .5 ))); - out_data[i+2] = static_cast<unsigned char>(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<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - a = in_data[i+3]; - - out_data[i] = CLAMP_D_TO_U8( r*a00 + g*a01 + b*a02); - out_data[i+1] = CLAMP_D_TO_U8( r*a10 + g*a11 + b*a12); - out_data[i+2] = CLAMP_D_TO_U8( r*a20 + g*a21 + b*a22); - out_data[i+3] = a; - } - } - } - } - break; - case COLORMATRIX_LUMINANCETOALPHA: - for (x=x0;x<x1;x++){ - for (y=y0;y<y1;y++){ - i = ((x-x0) + (x1-x0)*(y-y0))*4; - r = in_data[i]; - g = in_data[i+1]; - b = in_data[i+2]; - out_data[i] = 0; - out_data[i+1] = 0; - out_data[i+2] = 0; - out_data[i+3] = static_cast<unsigned char>( 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<double> 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 <vector> +#include <2geom/forward.h> #include "display/nr-filter-primitive.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" -#include<vector> 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 <math.h> +#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 <bool alpha> +struct ComponentTransferTable; + +template <> +struct ComponentTransferTable<false> : public ComponentTransfer { + ComponentTransferTable(guint32 color, std::vector<double> 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<guint32> _v; +}; + +template <> +struct ComponentTransferTable<true> { + ComponentTransferTable(std::vector<double> 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<guint32> _v; +}; + +template <bool alpha> +struct ComponentTransferDiscrete; + +template <> +struct ComponentTransferDiscrete<false> : public ComponentTransfer { + ComponentTransferDiscrete(guint32 color, std::vector<double> 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<guint32> _v; +}; + +template <> +struct ComponentTransferDiscrete<true> { + ComponentTransferDiscrete(std::vector<double> 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<guint32> _v; +}; + +template <bool alpha> +struct ComponentTransferLinear; + +template <> +struct ComponentTransferLinear<false> : 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<true> { + 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<size;i+=4){ - out_data[i]=in_data[i]; - } - break; - case COMPONENTTRANSFER_TYPE_TABLE: - if (_vsize<=1){ - if (_vsize==1) { - g_warning("A component transfer table has to have at least two values."); - } - for(i=color;i<size;i+=4){ - out_data[i]=in_data[i]; - } - } else { - if (!premultiplied || color==3) { - std::vector<gdouble> _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<size;i+=4){ - int k = FAST_DIVIDE<255>((_vsize-1) * in_data[i]); - double dx = ((_vsize-1) * in_data[i])/255.0 - k; - out_data[i] = static_cast<unsigned char>(_tableValues[k] + dx * (_tableValues[k+1] - _tableValues[k])); - } - } else { - std::vector<gdouble> _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - int k = ((_vsize-1) * in_data[i]) / in_data[i+3-color]; - double dx = ((_vsize-1) * in_data[i]) / (double)in_data[i+3-color] - k; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * (_tableValues[k] + dx * (_tableValues[k+1] - _tableValues[k])), out_data[i+3-color]); // CLAMP includes rounding! - } - } - } - break; - case COMPONENTTRANSFER_TYPE_DISCRETE: - if (_vsize==0){ - for(i=color;i<size;i+=4){ - out_data[i] = in_data[i]; - } - } else { - if (!premultiplied || color==3) { - std::vector<unsigned char> _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<size;i+=4){ - int k = FAST_DIVIDE<255>((_vsize-1) * in_data[i]); - out_data[i] = _tableValues[k]; - } - } else { - std::vector<gdouble> _tableValues(tableValues[color]); - for(i=0;i<_vsize;i++) { - _tableValues[i] = std::max(0.,std::min(1.,_tableValues[i])); - } - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - int k = ((_vsize-1) * in_data[i]) / in_data[i+3-color]; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * _tableValues[k], out_data[i+3-color]); - } - } - } - break; - case COMPONENTTRANSFER_TYPE_LINEAR: - if (!premultiplied || color==3) { - _intercept = 255*_intercept; - for(i=color;i<size;i+=4){ - out_data[i] = CLAMP_D_TO_U8(_slope * in_data[i] + _intercept); // CLAMP includes rounding! - } - } else { - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - double out = _slope * in_data[i] / in_data[i+3-color] + _intercept; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * out, out_data[i+3-color]); - } - } - break; - case COMPONENTTRANSFER_TYPE_GAMMA: - if (!premultiplied || color==3) { - _amplitude *= pow(255.0, -_exponent+1); // The input should be divided by 255, then exponentiated and then multiplied by 255 again, instead the amplitude is modified accordingly. - _offset = 255*_offset; - for(i=color;i<size;i+=4){ - out_data[i] = CLAMP_D_TO_U8(_amplitude * pow((double)in_data[i], _exponent) + _offset); - } - } else { - for(i=color;i<size;i+=4){ - if (in_data[i+3-color]==0) continue; - double out = _amplitude * pow((double)in_data[i] / in_data[i+3-color], _exponent) + _offset; - out_data[i] = CLAMP_D_TO_U8_ALPHA(out_data[i+3-color] * out, out_data[i+3-color]); - } - } - break; - case COMPONENTTRANSFER_TYPE_ERROR: - //TODO: report an error here - g_warning("Component tranfer type \"error\"."); - break; - default: - g_warning("Invalid tranfer type %d.", type[color]); +private: + gint32 _intercept; + gint32 _slope; +}; + +template <bool alpha> +struct ComponentTransferGamma; + +template <> +struct ComponentTransferGamma<false> : 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<true> { + 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<false>(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete<false>(color, tableValues[i])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear<false>(color, intercept[i], slope[i])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma<false>(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<true>(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_DISCRETE: + ink_cairo_surface_filter(out, out, + ComponentTransferDiscrete<true>(tableValues[3])); + break; + case COMPONENTTRANSFER_TYPE_LINEAR: + ink_cairo_surface_filter(out, out, + ComponentTransferLinear<true>(intercept[3], slope[3])); + break; + case COMPONENTTRANSFER_TYPE_GAMMA: + ink_cairo_surface_filter(out, out, + ComponentTransferGamma<true>(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 <vector> +#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]; |
