diff src/pixelarray_methods.c @ 997:46a786465642

Added weight support to PixelArray.extract() and PixelArray.replace().
author marcus
date Tue, 08 Apr 2008 18:57:29 +0000
parents 123fae0cfc2d
children 7355da1a6df6
line wrap: on
line diff
--- a/src/pixelarray_methods.c	Tue Apr 08 08:47:39 2008 +0000
+++ b/src/pixelarray_methods.c	Tue Apr 08 18:57:29 2008 +0000
@@ -23,11 +23,14 @@
  * It receives RGB values in the range 0-255 and returns a distance
  * value between 0.0 and 1.0.
  */
-#define COLOR_DIFF_RGB(r1,g1,b1,r2,g2,b2) \
-    (sqrt (.299 * (r1 - r2) * (r1 - r2) + \
-           .587 * (g1 - g2) * (g1 - g2) + \
-           .114 * (b1 - b2) * (b1 - b2)) / 255.0)
+#define COLOR_DIFF_RGB(wr,wg,wb,r1,g1,b1,r2,g2,b2) \
+    (sqrt (wr * (r1 - r2) * (r1 - r2) + \
+           wg * (g1 - g2) * (g1 - g2) + \
+           wb * (b1 - b2) * (b1 - b2)) / 255.0)
 
+#define WR_NTSC 0.299
+#define WG_NTSC 0.587
+#define WB_NTSC 0.114
 
 /**
  * Tries to retrieve a valid color for a Surface.
@@ -314,9 +317,103 @@
     return newsf;
 }
 
+static int
+_get_weights (PyObject *weights, float *wr, float *wg, float *wb)
+{
+    int success = 1;
+    float rgb[3] = { 0 };
+    
+    if (!weights)
+    {
+        *wr = WR_NTSC;
+        *wg = WG_NTSC;
+        *wb = WB_NTSC;
+        return 1;
+    }
+    
+    if (!PySequence_Check (weights))
+    {
+        PyErr_SetString (PyExc_TypeError, "weights must be a sequence");
+        success = 0;
+    }
+    else if (PySequence_Size (weights) < 3)
+    {
+        PyErr_SetString (PyExc_TypeError,
+            "weights must contain at least 3 values");
+        success = 0;
+    }
+    else
+    {
+        PyObject *item;
+        int i;
+        
+        for (i = 0; i < 3; i++)
+        {
+            item = PySequence_GetItem (weights, i);
+            if (PyNumber_Check (item))
+            {
+                PyObject *num = NULL;
+                if ((num = PyNumber_Float (item)) != NULL)
+                {
+                    rgb[i] = (float) PyFloat_AsDouble (num);
+                    Py_DECREF (num);
+                }
+                else if ((num = PyNumber_Int (item)) != NULL)
+                {
+                    rgb[i] = (float) PyInt_AsLong (num);
+                    if (rgb[i] == -1 && PyErr_Occurred ())
+                        success = 0;
+                    Py_DECREF (num);
+                }
+                else if ((num = PyNumber_Long (item)) != NULL)
+                {
+                    rgb[i] = (float) PyLong_AsLong (num);
+                    if (PyErr_Occurred () &&
+                        PyErr_ExceptionMatches (PyExc_OverflowError))
+                        success = 0;
+                    Py_DECREF (num);
+                }
+            }
+            else
+            {
+                PyErr_SetString (PyExc_TypeError, "invalid weights");
+                success = 0;
+            }
+            Py_XDECREF (item);
+            if (!success)
+                break;
+        }
+    }
+    
+    if (success)
+    {
+        float sum = 0;
+        
+        *wr = rgb[0];
+        *wg = rgb[1];
+        *wb = rgb[2];
+        if ((*wr < 0 || *wg < 0 || *wb < 0) ||
+            (*wr == 0 && *wg == 0 && *wb == 0))
+        {
+            PyErr_SetString (PyExc_ValueError,
+                "weights must be positive and greater than 0");
+            return 0;
+        }
+        /* Build the average weight values. */
+        sum = *wr + *wg + *wb;
+        *wr = *wr / sum;
+        *wg = *wg / sum;
+        *wb = *wb / sum;
+        
+        return success;
+    }
+    return 0;
+}
+
 static PyObject*
 _replace_color (PyPixelArray *array, PyObject *args, PyObject *kwds)
 {
+    PyObject *weights = NULL;
     PyObject *delcolor = NULL;
     PyObject *replcolor = NULL;
     Uint32 dcolor;
@@ -324,7 +421,7 @@
     Uint8 r1, g1, b1, r2, g2, b2, a2;
     SDL_Surface *surface;
     float distance = 0;
-    char *type;
+    float wr, wg, wb;
 
     Uint32 x = 0;
     Uint32 y = 0;
@@ -334,10 +431,10 @@
     Sint32 absystep;
     Uint8 *pixels;
 
-    static char *keys[] = { "color", "repcolor", "distance", NULL };
+    static char *keys[] = { "color", "repcolor", "distance", "weights", NULL };
     
-    if (!PyArg_ParseTupleAndKeywords (args, kwds, "OO|f", keys, &delcolor,
-            &replcolor, &distance))
+    if (!PyArg_ParseTupleAndKeywords (args, kwds, "OO|fO", keys, &delcolor,
+            &replcolor, &distance, &weights))
         return NULL;
 
     if (distance < 0 || distance > 1)
@@ -349,6 +446,9 @@
         !_get_color_from_object (replcolor, surface->format, &rcolor))
         return NULL;
 
+    if (!_get_weights (weights, &wr, &wg, &wb))
+        return NULL;
+
     surface = PySurface_AsSurface (array->surface);
     pixels = surface->pixels;
     absxstep = ABS (array->xstep);
@@ -373,7 +473,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS_1 (r2, g2, b2, a2, pixel, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = (Uint8) rcolor;
                 }
                 else if (*pixel == dcolor)
@@ -400,7 +501,8 @@
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, (Uint32) *pixel,
                         surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = (Uint16) rcolor;
                 }
                 else if (*pixel == dcolor)
@@ -430,7 +532,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, pxcolor, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                     {
                         *(px + (format->Rshift >> 3)) = (Uint8) (rcolor >> 16);
                         *(px + (format->Gshift >> 3)) = (Uint8) (rcolor >> 8);
@@ -448,7 +551,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, pxcolor, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                     {
                         *(px + 2 - (format->Rshift >> 3)) =
                             (Uint8) (rcolor >> 16);
@@ -485,7 +589,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, *pixel, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = rcolor;
                 }
                 else if (*pixel == dcolor)
@@ -505,6 +610,7 @@
 static PyObject*
 _extract_color (PyPixelArray *array, PyObject *args, PyObject *kwds)
 {
+    PyObject *weights = NULL;
     PyObject *sf = NULL;
     PyObject *excolor = NULL;
     PyPixelArray *newarray = NULL;
@@ -513,6 +619,7 @@
     Uint32 black;
     SDL_Surface *surface;
     float distance = 0;
+    float wr, wg, wb;
     Uint8 r1, g1, b1, r2, g2, b2, a2;
 
     Uint32 x = 0;
@@ -523,10 +630,10 @@
     Sint32 absystep;
     Uint8 *pixels;
 
-    static char *keys[] = { "color", "distance", NULL };
+    static char *keys[] = { "color", "distance", "weights", NULL };
 
-    if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|f", keys, &excolor,
-            &distance))
+    if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|fO", keys, &excolor,
+            &distance, &weights))
         return NULL;
 
     if (distance < 0 || distance > 1)
@@ -537,6 +644,9 @@
     if (!_get_color_from_object (excolor, surface->format, &color))
         return NULL;
 
+    if (!_get_weights (weights, &wr, &wg, &wb))
+        return NULL;
+
     /* Create the b/w mask surface. */
     sf = _make_surface (array);
     if (!sf)
@@ -571,7 +681,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS_1 (r2, g2, b2, a2, pixel, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = (Uint8) white;
                     else
                         *pixel = (Uint8) black;
@@ -600,7 +711,8 @@
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, (Uint32) *pixel,
                         surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = (Uint16) white;
                     else
                         *pixel = (Uint16) black;
@@ -633,7 +745,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, pxcolor, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <=  distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                     {
                         *(px + (format->Rshift >> 3)) = (Uint8) (white >> 16);
                         *(px + (format->Gshift >> 3)) = (Uint8) (white >> 8);
@@ -663,7 +776,7 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, pxcolor, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <=
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
                         distance)
                     {
                         *(px + 2 - (format->Rshift >> 3)) =
@@ -715,7 +828,8 @@
                 if (distance)
                 {
                     GET_PIXELVALS (r2, g2, b2, a2, *pixel, surface->format);
-                    if (COLOR_DIFF_RGB (r1, g1, b1, r2, g2, b2) <= distance)
+                    if (COLOR_DIFF_RGB (wr, wg, wb, r1, g1, b1, r2, g2, b2) <=
+                        distance)
                         *pixel = white;
                     else
                         *pixel = black;