Code

Fix feConvolveMatrix to use premultiplied color values (and semi-properly use bias).
authorjaspervdg <jaspervdg@users.sourceforge.net>
Wed, 8 Apr 2009 12:03:17 +0000 (12:03 +0000)
committerjaspervdg <jaspervdg@users.sourceforge.net>
Wed, 8 Apr 2009 12:03:17 +0000 (12:03 +0000)
src/display/nr-filter-convolve-matrix.cpp

index fc279df4ce0e3f8c5166cc886aace3dd652819e3..47978edd6bb8a822f04076ce7f879b2129d85c8d 100644 (file)
@@ -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);
+                }
             }
         }
     }