From 69ace948e6245fbb2ca7b4fa450087f7563201aa Mon Sep 17 00:00:00 2001 From: jaspervdg Date: Wed, 13 May 2009 13:10:16 +0000 Subject: [PATCH] Fix for bug #372412. As well as proper handling of borders (assuming edgeMode="none") for feConvolveMatrix. --- src/display/nr-filter-convolve-matrix.cpp | 204 +++++++++++++++------- 1 file changed, 145 insertions(+), 59 deletions(-) diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp index 7db13a5b2..0fd46202e 100644 --- a/src/display/nr-filter-convolve-matrix.cpp +++ b/src/display/nr-filter-convolve-matrix.cpp @@ -3,8 +3,9 @@ * * Authors: * Felipe Corrêa da Silva Sanches + * Jasper van de Gronde * - * Copyright (C) 2007 authors + * Copyright (C) 2007,2009 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -27,12 +28,136 @@ FilterPrimitive * FilterConvolveMatrix::create() { FilterConvolveMatrix::~FilterConvolveMatrix() {} +template +static inline void convolve2D_XY(unsigned int const x, unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) { + double result_R = 0; + double result_G = 0; + double result_B = 0; + double result_A = 0; + + unsigned int iBegin = Y_LOWER ? targetY-y : 0; // Note that to prevent signed/unsigned problems this requires that y<=targetY (which is true) + unsigned int iEnd = Y_UPPER ? height+targetY-y : orderY; // And this requires that y<=height+targetY (which is trivially true), in addition it should be true that height+targetY-y<=orderY (or equivalently y>=height+targetY-orderY, which is true) + unsigned int jBegin = X_LOWER ? targetX-x : 0; + unsigned int jEnd = X_UPPER ? width+targetX-x : orderX; + + for (unsigned int i=iBegin; i +static inline void convolve2D_Y(unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) { + // See convolve2D below for rationale. + + unsigned int const lowerEnd = std::min(targetX,width); + unsigned int const upperBegin = width - std::min(width,orderX - 1u - targetX); + unsigned int const midXBegin = std::min(lowerEnd,upperBegin); + unsigned int const midXEnd = std::max(lowerEnd,upperBegin); + + for (unsigned int x=0; x(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + if (lowerEnd==upperBegin) { + // Do nothing, empty mid section + } else if (lowerEnd(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + } else { + // In the middle both bounds have to be adjusted + for (unsigned int x=midXBegin; x(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + } + for (unsigned int x=midXEnd; x(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } +} + +template +static void convolve2D(unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const _bias) { + double const bias = PREMULTIPLIED ? _bias : 255*_bias; // If we're using non-premultiplied values the bias is always multiplied by 255. + + // For the middle section it should hold that (for all i such that 0<=i=height+targetY-orderY+1 i's upper bound needs to be adjusted. + + unsigned int const lowerEnd = std::min(targetY,height); + unsigned int const upperBegin = height - std::min(height,orderY - 1u - targetY); + unsigned int const midYBegin = std::min(lowerEnd,upperBegin); + unsigned int const midYEnd = std::max(lowerEnd,upperBegin); + + for (unsigned int y=0; y(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + if (lowerEnd==upperBegin) { + // Do nothing, empty mid section + } else if (lowerEnd(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + } else { + // In the middle both bounds have to be adjusted + for (unsigned int y=midYBegin; y(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } + } + for (unsigned int y=midYEnd; y(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias); + } +} + int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) { NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feConvolveMatrix (in=%d)", _input); return 1; } + if (orderX<=0 || orderY<=0) { + g_warning("Empty kernel!"); + return 1; + } + if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) { + g_warning("Invalid target!"); + return 1; + } + if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) { + g_warning("kernelMatrix does not have orderX*orderY elements!"); + return 1; + } if (bias!=0) { g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!"); @@ -42,6 +167,11 @@ int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) // 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 } + if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE) { + g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!"); + // Note that to properly support edgeMode the interaction with area_enlarge should be well understood (and probably something needs to change) + // area_enlarge should NOT let Inkscape enlarge the area beyond the filter area, it should only enlarge the rendered area if a part of the object is rendered to make it overlapping (enough) with adjacent parts. + } NRPixBlock *out = new NRPixBlock; @@ -55,67 +185,23 @@ int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) unsigned int const width = in->area.x1 - in->area.x0; unsigned int const height = in->area.y1 - in->area.y0; + // Set up predivided kernel matrix + std::vector kernel(kernelMatrix); + for(size_t i=0; imode==NR_PIXBLOCK_MODE_R8G8B8A8P) { - for (unsigned int y=targetY; y < height - (orderY - targetY); y++){ - for (unsigned int x=targetX; x < width - (orderX - targetX); x++){ - double result_R = 0; - double result_G = 0; - double result_B = 0; - double result_A = 0; - for (unsigned int i=0; i < orderY; i++){ - for (int j=0; j < orderX; j++){ - unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) ); - unsigned int kernel_index = orderX-j-1 + orderX*(orderY-i-1); - result_R += ( (double) in_data[index+0] * kernelMatrix[kernel_index] ); - result_G += ( (double) in_data[index+1] * kernelMatrix[kernel_index] ); - result_B += ( (double) in_data[index+2] * 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); - } - 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]); - } + if (preserveAlpha) { + convolve2D(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); + } else { + convolve2D(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } } else { - for (unsigned int y=targetY; y < height - (orderY - targetY); y++){ - for (unsigned int x=targetX; x < width - (orderX - targetX); x++){ - double result_R = 0; - double result_G = 0; - double result_B = 0; - double result_A = 0; - for (unsigned int i=0; i < orderY; i++){ - for (unsigned int j=0; j < orderX; j++){ - unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) ); - unsigned int 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); - } - } + if (preserveAlpha) { + convolve2D(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); + } else { + convolve2D(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias); } } -- 2.30.2