From ea2e0f3745ba3a9d2368f7368979113ee6fb9c0a Mon Sep 17 00:00:00 2001 From: jaspervdg Date: Wed, 8 Apr 2009 12:03:17 +0000 Subject: [PATCH] Fix feConvolveMatrix to use premultiplied color values (and semi-properly use bias). --- src/display/nr-filter-convolve-matrix.cpp | 84 ++++++++++++++++------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp index fc279df4c..47978edd6 100644 --- a/src/display/nr-filter-convolve-matrix.cpp +++ b/src/display/nr-filter-convolve-matrix.cpp @@ -27,14 +27,6 @@ FilterPrimitive * FilterConvolveMatrix::create() { FilterConvolveMatrix::~FilterConvolveMatrix() {} -static bool inside_area(int px, int py, int w, int h){ - if (px<0) return false; - if (py<0) return false; - if (px>w) return false; - if (py>h) return false; - return true; -} - int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { NRPixBlock *in = slot.get(_input); if (!in) { @@ -42,6 +34,15 @@ int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) return 1; } + if (bias!=0) { + g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!"); + // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense). + // It also says that bias should simply be added to the result for each color (without taking the alpha into account) + // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter". + // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard. + // Note that Batik simply does not support bias!=0 + } + NRPixBlock *out = new NRPixBlock; nr_pixblock_setup_fast(out, in->mode, @@ -59,15 +60,15 @@ int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) unsigned int index; unsigned int kernel_index; - for (x=targetX; x < width - (orderX - targetX); x++){ + if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) { for (y=targetY; y < height - (orderY - targetY); y++){ - result_R = 0; - result_G = 0; - result_B = 0; - result_A = 0; - for (i=0; i < orderY; i++){ - for (j=0; j < orderX; j++){ - if (inside_area(x - targetX + j, y - targetY + i, width, height)){ + for (x=targetX; x < width - (orderX - targetX); x++){ + result_R = 0; + result_G = 0; + result_B = 0; + result_A = 0; + for (i=0; i < orderY; i++){ + for (j=0; j < orderX; j++){ index = 4*( x - targetX + j + width*(y - targetY + i) ); kernel_index = orderX-j-1 + orderX*(orderY-i-1); result_R += ( (double) in_data[index++] * kernelMatrix[kernel_index] ); @@ -76,16 +77,49 @@ int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) result_A += ( (double) in_data[index] * kernelMatrix[kernel_index] ); } } + unsigned int out_index = 4*( x + width*y ); + if( preserveAlpha ) { + out_data[out_index+3] = in_data[out_index+3]; + } else { + out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias); + } + out_data[out_index+0] = CLAMP_D_TO_U8_ALPHA(result_R / divisor + out_data[out_index+3]*bias, out_data[out_index+3]); // CLAMP includes rounding! + out_data[out_index+1] = CLAMP_D_TO_U8_ALPHA(result_G / divisor + out_data[out_index+3]*bias, out_data[out_index+3]); + out_data[out_index+2] = CLAMP_D_TO_U8_ALPHA(result_B / divisor + out_data[out_index+3]*bias, out_data[out_index+3]); } - unsigned int out_index = 4*( x + width*y ); - out_data[out_index++] = CLAMP_D_TO_U8(result_R / divisor + bias); // CLAMP includes rounding! - out_data[out_index++] = CLAMP_D_TO_U8(result_G / divisor + bias); - out_data[out_index++] = CLAMP_D_TO_U8(result_B / divisor + bias); - - if( preserveAlpha ) { - out_data[out_index] = in_data[out_index]; - } else { - out_data[out_index] = CLAMP_D_TO_U8(result_A / divisor + bias); + } + } else { + for (y=targetY; y < height - (orderY - targetY); y++){ + for (x=targetX; x < width - (orderX - targetX); x++){ + result_R = 0; + result_G = 0; + result_B = 0; + result_A = 0; + for (i=0; i < orderY; i++){ + for (j=0; j < orderX; j++){ + index = 4*( x - targetX + j + width*(y - targetY + i) ); + kernel_index = orderX-j-1 + orderX*(orderY-i-1); + result_R += ( (double) in_data[index+0] * in_data[index+3] * kernelMatrix[kernel_index] ); + result_G += ( (double) in_data[index+1] * in_data[index+3] * kernelMatrix[kernel_index] ); + result_B += ( (double) in_data[index+2] * in_data[index+3] * kernelMatrix[kernel_index] ); + result_A += ( (double) in_data[index+3] * kernelMatrix[kernel_index] ); + } + } + unsigned int out_index = 4*( x + width*y ); + if( preserveAlpha ) { + out_data[out_index+3] = in_data[out_index+3]; + } else { + out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias); + } + if (out_data[out_index+3]==0) { + out_data[out_index+0] = 0; + out_data[out_index+1] = 0; + out_data[out_index+2] = 0; + } else { + out_data[out_index+0] = CLAMP_D_TO_U8(result_R / (divisor*out_data[out_index+3]) + 255*bias); // CLAMP includes rounding! + out_data[out_index+1] = CLAMP_D_TO_U8(result_G / (divisor*out_data[out_index+3]) + 255*bias); + out_data[out_index+2] = CLAMP_D_TO_U8(result_B / (divisor*out_data[out_index+3]) + 255*bias); + } } } } -- 2.30.2