summaryrefslogtreecommitdiffstats
path: root/plug-ins/pygimp/pygimp-colors.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/pygimp/pygimp-colors.c2407
1 files changed, 2407 insertions, 0 deletions
diff --git a/plug-ins/pygimp/pygimp-colors.c b/plug-ins/pygimp/pygimp-colors.c
new file mode 100644
index 0000000..fe37b5d
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-colors.c
@@ -0,0 +1,2407 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005-2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#define NO_IMPORT_PYGOBJECT
+
+#include "pygimp.h"
+#include "pygimpcolor.h"
+
+#include <libgimpmath/gimpmath.h>
+
+static PyObject *
+rgb_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *r = NULL, *g = NULL, *b = NULL, *a = NULL;
+ GimpRGB tmprgb, *rgb;
+ static char *kwlist[] = { "r", "g", "b", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &r, &g, &b, &a))
+ return NULL;
+
+ if (!r && !g && !b && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide r,g,b or a arguments");
+ return NULL;
+ }
+
+ if ((r && (!g || !b)) ||
+ (g && (!r || !b)) ||
+ (b && (!r || !g))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 r,g,b arguments");
+ return NULL;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+ tmprgb = *rgb;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmprgb.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ tmprgb.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (r) {
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+ }
+
+ if (a)
+ SET_MEMBER(a);
+
+#undef SET_MEMBER
+
+ *rgb = tmprgb;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpRGB *rgb;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ if (PyInt_Check(py_a))
+ rgb->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ rgb->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be an int or a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_add(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "color", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:add", kwlist,
+ &PyGimpRGB_Type, &color, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_add(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+ else
+ gimp_rgb_add(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_subtract(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "color", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:subtract", kwlist,
+ &PyGimpRGB_Type, &color, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_subtract(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+ else
+ gimp_rgb_subtract(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_multiply(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ double factor;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "factor", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d|i:multiply", kwlist,
+ &factor, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_multiply(pyg_boxed_get(self, GimpRGB), factor);
+ else
+ gimp_rgb_multiply(pyg_boxed_get(self, GimpRGB), factor);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_distance(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean alpha = FALSE;
+ double ret;
+ static char *kwlist[] = { "color", "alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:distance", kwlist,
+ &PyGimpRGB_Type, &color, &alpha))
+ return NULL;
+
+ ret = gimp_rgb_distance(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+rgb_max(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_max(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_min(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_min(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_clamp(PyObject *self)
+{
+ gimp_rgb_clamp(pyg_boxed_get(self, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_gamma(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ double gamma;
+ static char *kwlist[] = { "gamma", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:gamma", kwlist, &gamma))
+ return NULL;
+
+ gimp_rgb_gamma(pyg_boxed_get(self, GimpRGB), gamma);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_luminance(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_luminance(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_composite(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ int mode = GIMP_RGB_COMPOSITE_NORMAL;
+ static char *kwlist[] = { "color", "mode", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|i:composite", kwlist,
+ &PyGimpRGB_Type, &color, &mode))
+ return NULL;
+
+ if (mode < GIMP_RGB_COMPOSITE_NONE || mode > GIMP_RGB_COMPOSITE_BEHIND) {
+ PyErr_SetString(PyExc_TypeError, "composite type is not valid");
+ return NULL;
+ }
+
+ gimp_rgb_composite(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB),
+ mode);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int len;
+ gboolean success;
+ static char *kwlist[] = { "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:parse_name", kwlist,
+ &name, &len))
+ return NULL;
+
+ success = gimp_rgb_parse_name(pyg_boxed_get(self, GimpRGB), name, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse color name");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_hex(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *hex;
+ int len;
+ gboolean success;
+ static char *kwlist[] = { "hex", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:parse_hex", kwlist,
+ &hex, &len))
+ return NULL;
+
+ success = gimp_rgb_parse_hex(pyg_boxed_get(self, GimpRGB), hex, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse hex value");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_css(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *css;
+ int len;
+ gboolean success, with_alpha = FALSE;
+ static char *kwlist[] = { "css", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s#|i:parse_css", kwlist,
+ &css, &len, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ success = gimp_rgba_parse_css(pyg_boxed_get(self, GimpRGB), css, len);
+ else
+ success = gimp_rgb_parse_css(pyg_boxed_get(self, GimpRGB), css, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse CSS color");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_to_hsv(PyObject *self)
+{
+ GimpRGB *rgb;
+ GimpHSV hsv;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_hsv(rgb, &hsv);
+
+ return pygimp_hsv_new(&hsv);
+}
+
+static PyObject *
+rgb_to_hsl(PyObject *self)
+{
+ GimpRGB *rgb;
+ GimpHSL hsl;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_hsl(rgb, &hsl);
+
+ return pygimp_hsl_new(&hsl);
+}
+
+static PyObject *
+rgb_to_cmyk(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ GimpRGB *rgb;
+ GimpCMYK cmyk;
+ gdouble pullout = 1.0;
+ static char *kwlist[] = { "pullout", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|d:to_cmyk", kwlist,
+ &pullout))
+ return NULL;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_cmyk(rgb, pullout, &cmyk);
+
+ return pygimp_cmyk_new(&cmyk);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+rgb_getstate(PyObject *self)
+{
+ GimpRGB *rgb;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ return Py_BuildValue("dddd", rgb->r, rgb->g, rgb->b, rgb->a);
+}
+
+static PyObject *
+rgb_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, rgb_getstate(self));
+}
+
+static PyMethodDef rgb_methods[] = {
+ { "set", (PyCFunction)rgb_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)rgb_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "add", (PyCFunction)rgb_add, METH_VARARGS|METH_KEYWORDS },
+ { "subtract", (PyCFunction)rgb_subtract, METH_VARARGS|METH_KEYWORDS },
+ { "multiply", (PyCFunction)rgb_multiply, METH_VARARGS|METH_KEYWORDS },
+ { "distance", (PyCFunction)rgb_distance, METH_VARARGS|METH_KEYWORDS },
+ { "max", (PyCFunction)rgb_max, METH_NOARGS },
+ { "min", (PyCFunction)rgb_min, METH_NOARGS },
+ { "clamp", (PyCFunction)rgb_clamp, METH_NOARGS },
+ { "gamma", (PyCFunction)rgb_gamma, METH_VARARGS|METH_KEYWORDS },
+ { "luminance", (PyCFunction)rgb_luminance, METH_NOARGS },
+ { "composite", (PyCFunction)rgb_composite, METH_VARARGS|METH_KEYWORDS },
+ { "parse_name", (PyCFunction)rgb_parse_name, METH_VARARGS|METH_KEYWORDS },
+ { "parse_hex", (PyCFunction)rgb_parse_hex, METH_VARARGS|METH_KEYWORDS },
+ { "parse_css", (PyCFunction)rgb_parse_css, METH_VARARGS|METH_KEYWORDS },
+ { "to_hsv", (PyCFunction)rgb_to_hsv, METH_NOARGS },
+ { "to_hsl", (PyCFunction)rgb_to_hsl, METH_NOARGS },
+ { "to_cmyk", (PyCFunction)rgb_to_cmyk, METH_VARARGS|METH_KEYWORDS },
+ { "__reduce__", (PyCFunction)rgb_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m) \
+static PyObject * \
+rgb_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpRGB)->m); \
+} \
+static int \
+rgb_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpRGB *rgb = pyg_boxed_get(self, GimpRGB); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ rgb->m = (double) PyInt_AS_LONG(value) / 255.0; \
+ else if (PyFloat_Check(value)) \
+ rgb->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(r);
+MEMBER_ACCESSOR(g);
+MEMBER_ACCESSOR(b);
+MEMBER_ACCESSOR(a);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef rgb_getsets[] = {
+ { "r", (getter)rgb_get_r, (setter)rgb_set_r },
+ { "g", (getter)rgb_get_g, (setter)rgb_set_g },
+ { "b", (getter)rgb_get_b, (setter)rgb_set_b },
+ { "a", (getter)rgb_get_a, (setter)rgb_set_a },
+ { "red", (getter)rgb_get_r, (setter)rgb_set_r },
+ { "green", (getter)rgb_get_g, (setter)rgb_set_g },
+ { "blue", (getter)rgb_get_b, (setter)rgb_set_b },
+ { "alpha", (getter)rgb_get_a, (setter)rgb_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+rgb_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+rgb_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpRGB *rgb;
+ double val;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ switch (pos) {
+ case 0: val = rgb->r; break;
+ case 1: val = rgb->g; break;
+ case 2: val = rgb->b; break;
+ case 3: val = rgb->a; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * 255.0));
+}
+
+static int
+rgb_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return rgb_set_r(self, value, NULL);
+ case 1: return rgb_set_g(self, value, NULL);
+ case 2: return rgb_set_b(self, value, NULL);
+ case 3: return rgb_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+rgb_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, rgb_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods rgb_as_sequence = {
+ rgb_length,
+ (binaryfunc)0,
+ 0,
+ rgb_getitem,
+ rgb_slice,
+ rgb_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+rgb_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return rgb_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return rgb_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, rgb_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "r") == 0 ||
+ g_ascii_strcasecmp(s, "red") == 0)
+ return rgb_get_r(self, NULL);
+ else if (g_ascii_strcasecmp(s, "g") == 0 ||
+ g_ascii_strcasecmp(s, "green") == 0)
+ return rgb_get_g(self, NULL);
+ else if (g_ascii_strcasecmp(s, "b") == 0 ||
+ g_ascii_strcasecmp(s, "blue") == 0)
+ return rgb_get_b(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return rgb_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods rgb_as_mapping = {
+ rgb_length,
+ (binaryfunc)rgb_subscript,
+ (objobjargproc)0
+};
+
+static long
+rgb_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = rgb_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+rgb_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpRGB *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_rgb_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpRGB);
+ c2 = pyg_boxed_get(other, GimpRGB);
+
+ if ((c1->r == c2->r && c1->g == c2->g && c1->b == c2->b && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+rgb_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpRGB *rgb;
+ PyObject *ret = NULL;
+ PyObject *r_f = NULL, *g_f = NULL, *b_f = NULL, *a_f = NULL;
+ PyObject *r = NULL, *g = NULL, *b = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "RGB ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ if ((r_f = PyFloat_FromDouble(rgb->r)) == NULL) goto cleanup;
+ if ((g_f = PyFloat_FromDouble(rgb->g)) == NULL) goto cleanup;
+ if ((b_f = PyFloat_FromDouble(rgb->b)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(rgb->a)) == NULL) goto cleanup;
+
+ if ((r = repr(r_f)) == NULL) goto cleanup;
+ if ((g = repr(g_f)) == NULL) goto cleanup;
+ if ((b = repr(b_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(r),
+ PyString_AsString(g),
+ PyString_AsString(b),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(r); Py_XDECREF(g); Py_XDECREF(b); Py_XDECREF(a);
+ Py_XDECREF(r_f); Py_XDECREF(g_f); Py_XDECREF(b_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+rgb_repr(PyObject *self)
+{
+ return rgb_pretty_print(self, FALSE);
+}
+
+static PyObject *
+rgb_str(PyObject *self)
+{
+ return rgb_pretty_print(self, TRUE);
+}
+
+static int
+rgb_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *r, *g, *b, *a = NULL;
+ GimpRGB rgb;
+ static char *kwlist[] = { "r", "g", "b", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &r, &g, &b, &a))
+ return -1;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ rgb.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ rgb.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ rgb.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_RGB;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_RGB, &rgb);
+
+ return 0;
+}
+
+PyTypeObject PyGimpRGB_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.RGB", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)rgb_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &rgb_as_sequence, /* tp_as_sequence */
+ &rgb_as_mapping, /* tp_as_mapping */
+ (hashfunc)rgb_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)rgb_str, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)rgb_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ rgb_methods, /* tp_methods */
+ 0, /* tp_members */
+ rgb_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)rgb_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_rgb_new(const GimpRGB *rgb)
+{
+ return pyg_boxed_new(GIMP_TYPE_RGB, (gpointer)rgb, TRUE, TRUE);
+}
+
+
+static PyObject *
+hsv_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h = NULL, *s = NULL, *v = NULL, *a = NULL;
+ GimpHSV tmphsv, *hsv;
+ static char *kwlist[] = { "h", "s", "v", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &h, &s, &v, &a))
+ return NULL;
+
+ if (!h && !s && !v && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide h,s,v or a arguments");
+ return NULL;
+ }
+
+ if ((h && (!s || !v)) ||
+ (s && (!h || !v)) ||
+ (v && (!h || !s))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 h,s,v arguments");
+ return NULL;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+ tmphsv = *hsv;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmphsv.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ tmphsv.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (h) {
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(v, 100.0);
+ }
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+
+#undef SET_MEMBER
+
+ *hsv = tmphsv;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpHSV *hsv;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ if (PyInt_Check(py_a))
+ hsv->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ hsv->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_clamp(PyObject *self)
+{
+ gimp_hsv_clamp(pyg_boxed_get(self, GimpHSV));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_to_rgb(PyObject *self)
+{
+ GimpHSV *hsv;
+ GimpRGB rgb;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ gimp_hsv_to_rgb(hsv, &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+hsv_getstate(PyObject *self)
+{
+ GimpHSV *hsv;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ return Py_BuildValue("dddd", hsv->h, hsv->s, hsv->v, hsv->a);
+}
+
+static PyObject *
+hsv_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, hsv_getstate(self));
+}
+
+static PyMethodDef hsv_methods[] = {
+ { "set", (PyCFunction)hsv_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)hsv_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "clamp", (PyCFunction)hsv_clamp, METH_NOARGS },
+ { "to_rgb", (PyCFunction)hsv_to_rgb, METH_NOARGS },
+ { "__reduce__", (PyCFunction)hsv_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m, s) \
+static PyObject * \
+hsv_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpHSV)->m); \
+} \
+static int \
+hsv_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpHSV *hsv = pyg_boxed_get(self, GimpHSV); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ hsv->m = (double) PyInt_AS_LONG(value) / s; \
+ else if (PyFloat_Check(value)) \
+ hsv->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(h, 360.0);
+MEMBER_ACCESSOR(s, 100.0);
+MEMBER_ACCESSOR(v, 100.0);
+MEMBER_ACCESSOR(a, 255.0);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef hsv_getsets[] = {
+ { "h", (getter)hsv_get_h, (setter)hsv_set_h },
+ { "s", (getter)hsv_get_s, (setter)hsv_set_s },
+ { "v", (getter)hsv_get_v, (setter)hsv_set_v },
+ { "a", (getter)hsv_get_a, (setter)hsv_set_a },
+ { "hue", (getter)hsv_get_h, (setter)hsv_set_h },
+ { "saturation", (getter)hsv_get_s, (setter)hsv_set_s },
+ { "value", (getter)hsv_get_v, (setter)hsv_set_v },
+ { "alpha", (getter)hsv_get_a, (setter)hsv_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+hsv_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+hsv_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpHSV *hsv;
+ double val, scale_factor;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ switch (pos) {
+ case 0: val = hsv->h; scale_factor = 360.0; break;
+ case 1: val = hsv->s; scale_factor = 100.0; break;
+ case 2: val = hsv->v; scale_factor = 100.0; break;
+ case 3: val = hsv->a; scale_factor = 255.0; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * scale_factor));
+}
+
+static int
+hsv_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return hsv_set_h(self, value, NULL);
+ case 1: return hsv_set_s(self, value, NULL);
+ case 2: return hsv_set_v(self, value, NULL);
+ case 3: return hsv_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+hsv_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, hsv_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods hsv_as_sequence = {
+ hsv_length,
+ (binaryfunc)0,
+ 0,
+ hsv_getitem,
+ hsv_slice,
+ hsv_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+hsv_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return hsv_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return hsv_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, hsv_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "h") == 0 ||
+ g_ascii_strcasecmp(s, "hue") == 0)
+ return hsv_get_h(self, NULL);
+ else if (g_ascii_strcasecmp(s, "s") == 0 ||
+ g_ascii_strcasecmp(s, "saturation") == 0)
+ return hsv_get_s(self, NULL);
+ else if (g_ascii_strcasecmp(s, "v") == 0 ||
+ g_ascii_strcasecmp(s, "value") == 0)
+ return hsv_get_v(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return hsv_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods hsv_as_mapping = {
+ hsv_length,
+ (binaryfunc)hsv_subscript,
+ (objobjargproc)0
+};
+
+static long
+hsv_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = hsv_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+hsv_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpHSV *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_hsv_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpHSV);
+ c2 = pyg_boxed_get(other, GimpHSV);
+
+ if ((c1->h == c2->h && c1->s == c2->s && c1->v == c2->v && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+hsv_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpHSV *hsv;
+ PyObject *ret = NULL;
+ PyObject *h_f = NULL, *s_f = NULL, *v_f = NULL, *a_f = NULL;
+ PyObject *h = NULL, *s = NULL, *v = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "HSV ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ if ((h_f = PyFloat_FromDouble(hsv->h)) == NULL) goto cleanup;
+ if ((s_f = PyFloat_FromDouble(hsv->s)) == NULL) goto cleanup;
+ if ((v_f = PyFloat_FromDouble(hsv->v)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(hsv->a)) == NULL) goto cleanup;
+
+ if ((h = repr(h_f)) == NULL) goto cleanup;
+ if ((s = repr(s_f)) == NULL) goto cleanup;
+ if ((v = repr(v_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(h),
+ PyString_AsString(s),
+ PyString_AsString(v),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(h); Py_XDECREF(s); Py_XDECREF(v); Py_XDECREF(a);
+ Py_XDECREF(h_f); Py_XDECREF(s_f); Py_XDECREF(v_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+hsv_repr(PyObject *self)
+{
+ return hsv_pretty_print(self, FALSE);
+}
+
+static PyObject *
+hsv_str(PyObject *self)
+{
+ return hsv_pretty_print(self, TRUE);
+}
+
+static int
+hsv_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h, *s, *v, *a = NULL;
+ GimpHSV hsv;
+ static char *kwlist[] = { "h", "s", "v", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &h, &s, &v, &a))
+ return -1;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ hsv.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ hsv.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(v, 100.0);
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+ else
+ hsv.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_HSV;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_HSV, &hsv);
+
+ return 0;
+}
+
+PyTypeObject PyGimpHSV_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.HSV", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)hsv_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &hsv_as_sequence, /* tp_as_sequence */
+ &hsv_as_mapping, /* tp_as_mapping */
+ (hashfunc)hsv_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)hsv_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)hsv_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ hsv_methods, /* tp_methods */
+ 0, /* tp_members */
+ hsv_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)hsv_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_hsv_new(const GimpHSV *hsv)
+{
+ return pyg_boxed_new(GIMP_TYPE_HSV, (gpointer)hsv, TRUE, TRUE);
+}
+
+
+static PyObject *
+hsl_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h = NULL, *s = NULL, *l = NULL, *a = NULL;
+ GimpHSL tmphsl, *hsl;
+ static char *kwlist[] = { "h", "s", "l", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &h, &s, &l, &a))
+ return NULL;
+
+ if (!h && !s && !l && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide h,s,l or a arguments");
+ return NULL;
+ }
+
+ if ((h && (!s || !l)) ||
+ (s && (!h || !l)) ||
+ (l && (!h || !s))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 h,s,l arguments");
+ return NULL;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+ tmphsl = *hsl;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmphsl.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ tmphsl.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (h) {
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(l, 100.0);
+ }
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+
+#undef SET_MEMBER
+
+ *hsl = tmphsl;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsl_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpHSL *hsl;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ if (PyInt_Check(py_a))
+ hsl->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ hsl->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsl_to_rgb(PyObject *self)
+{
+ GimpHSL *hsl;
+ GimpRGB rgb;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ gimp_hsl_to_rgb(hsl, &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+hsl_getstate(PyObject *self)
+{
+ GimpHSL *hsl;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ return Py_BuildValue("dddd", hsl->h, hsl->s, hsl->l, hsl->a);
+}
+
+static PyObject *
+hsl_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, hsl_getstate(self));
+}
+
+static PyMethodDef hsl_methods[] = {
+ { "set", (PyCFunction)hsl_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)hsl_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "to_rgb", (PyCFunction)hsl_to_rgb, METH_NOARGS },
+ { "__reduce__", (PyCFunction)hsl_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m, s) \
+static PyObject * \
+hsl_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpHSL)->m); \
+} \
+static int \
+hsl_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpHSL *hsl = pyg_boxed_get(self, GimpHSL); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ hsl->m = (double) PyInt_AS_LONG(value) / s; \
+ else if (PyFloat_Check(value)) \
+ hsl->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(h, 360.0);
+MEMBER_ACCESSOR(s, 100.0);
+MEMBER_ACCESSOR(l, 100.0);
+MEMBER_ACCESSOR(a, 255.0);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef hsl_getsets[] = {
+ { "h", (getter)hsl_get_h, (setter)hsl_set_h },
+ { "s", (getter)hsl_get_s, (setter)hsl_set_s },
+ { "l", (getter)hsl_get_l, (setter)hsl_set_l },
+ { "a", (getter)hsl_get_a, (setter)hsl_set_a },
+ { "hue", (getter)hsl_get_h, (setter)hsl_set_h },
+ { "saturation", (getter)hsl_get_s, (setter)hsl_set_s },
+ { "lightness", (getter)hsl_get_l, (setter)hsl_set_l },
+ { "alpha", (getter)hsl_get_a, (setter)hsl_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+hsl_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+hsl_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpHSL *hsl;
+ double val, scale_factor;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ switch (pos) {
+ case 0: val = hsl->h; scale_factor = 360.0; break;
+ case 1: val = hsl->s; scale_factor = 100.0; break;
+ case 2: val = hsl->l; scale_factor = 100.0; break;
+ case 3: val = hsl->a; scale_factor = 255.0; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * scale_factor));
+}
+
+static int
+hsl_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return hsl_set_h(self, value, NULL);
+ case 1: return hsl_set_s(self, value, NULL);
+ case 2: return hsl_set_l(self, value, NULL);
+ case 3: return hsl_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+hsl_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, hsl_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods hsl_as_sequence = {
+ hsl_length,
+ (binaryfunc)0,
+ 0,
+ hsl_getitem,
+ hsl_slice,
+ hsl_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+hsl_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return hsl_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return hsl_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, hsl_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "h") == 0 ||
+ g_ascii_strcasecmp(s, "hue") == 0)
+ return hsl_get_h(self, NULL);
+ else if (g_ascii_strcasecmp(s, "s") == 0 ||
+ g_ascii_strcasecmp(s, "saturation") == 0)
+ return hsl_get_s(self, NULL);
+ else if (g_ascii_strcasecmp(s, "l") == 0 ||
+ g_ascii_strcasecmp(s, "lightness") == 0)
+ return hsl_get_l(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return hsl_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods hsl_as_mapping = {
+ hsl_length,
+ (binaryfunc)hsl_subscript,
+ (objobjargproc)0
+};
+
+static long
+hsl_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = hsl_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+hsl_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpHSL *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_hsl_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpHSL);
+ c2 = pyg_boxed_get(other, GimpHSL);
+
+ if ((c1->h == c2->h && c1->s == c2->s && c1->l == c2->l && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+hsl_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpHSL *hsl;
+ PyObject *ret = NULL;
+ PyObject *h_f = NULL, *s_f = NULL, *l_f = NULL, *a_f = NULL;
+ PyObject *h = NULL, *s = NULL, *l = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "HSL ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ if ((h_f = PyFloat_FromDouble(hsl->h)) == NULL) goto cleanup;
+ if ((s_f = PyFloat_FromDouble(hsl->s)) == NULL) goto cleanup;
+ if ((l_f = PyFloat_FromDouble(hsl->l)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(hsl->a)) == NULL) goto cleanup;
+
+ if ((h = repr(h_f)) == NULL) goto cleanup;
+ if ((s = repr(s_f)) == NULL) goto cleanup;
+ if ((l = repr(l_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(h),
+ PyString_AsString(s),
+ PyString_AsString(l),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(h); Py_XDECREF(s); Py_XDECREF(l); Py_XDECREF(a);
+ Py_XDECREF(h_f); Py_XDECREF(s_f); Py_XDECREF(l_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+hsl_repr(PyObject *self)
+{
+ return hsl_pretty_print(self, FALSE);
+}
+
+static PyObject *
+hsl_str(PyObject *self)
+{
+ return hsl_pretty_print(self, TRUE);
+}
+
+static int
+hsl_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h, *s, *l, *a = NULL;
+ GimpHSL hsl;
+ static char *kwlist[] = { "h", "s", "l", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &h, &s, &l, &a))
+ return -1;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ hsl.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ hsl.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(l, 100.0);
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+ else
+ hsl.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_HSL;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_HSL, &hsl);
+
+ return 0;
+}
+
+PyTypeObject PyGimpHSL_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.HSL", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)hsl_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &hsl_as_sequence, /* tp_as_sequence */
+ &hsl_as_mapping, /* tp_as_mapping */
+ (hashfunc)hsl_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)hsl_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)hsl_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ hsl_methods, /* tp_methods */
+ 0, /* tp_members */
+ hsl_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)hsl_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_hsl_new(const GimpHSL *hsl)
+{
+ return pyg_boxed_new(GIMP_TYPE_HSL, (gpointer)hsl, TRUE, TRUE);
+}
+
+
+static PyObject *
+cmyk_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *c = NULL, *m = NULL, *y = NULL, *k = NULL, *a = NULL;
+ GimpCMYK tmpcmyk, *cmyk;
+ static char *kwlist[] = { "c", "m", "y", "k", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOO:set", kwlist,
+ &c, &m, &y, &k, &a))
+ return NULL;
+
+ if (!c && !y && !m && !k && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide c,m,y,k or a arguments");
+ return NULL;
+ }
+
+ if ((c && (!m || !y || !k)) ||
+ (m && (!c || !y || !k)) ||
+ (y && (!c || !m || !k)) ||
+ (k && (!c || !m || !y))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 4 c,m,y,k arguments");
+ return NULL;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+ tmpcmyk = *cmyk;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmpcmyk.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ tmpcmyk.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (c) {
+ SET_MEMBER(c);
+ SET_MEMBER(y);
+ SET_MEMBER(m);
+ SET_MEMBER(k);
+ }
+
+ if (a)
+ SET_MEMBER(a);
+
+#undef SET_MEMBER
+
+ *cmyk = tmpcmyk;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+cmyk_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpCMYK *cmyk;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ if (PyInt_Check(py_a))
+ cmyk->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ cmyk->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be an int or a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+cmyk_getstate(PyObject *self)
+{
+ GimpCMYK *cmyk;
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ return Py_BuildValue("ddddd", cmyk->c, cmyk->m, cmyk->y, cmyk->k, cmyk->a);
+}
+
+static PyObject *
+cmyk_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, cmyk_getstate(self));
+}
+
+static PyMethodDef cmyk_methods[] = {
+ { "set", (PyCFunction)cmyk_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)cmyk_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "__reduce__", (PyCFunction)cmyk_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m) \
+static PyObject * \
+cmyk_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpCMYK)->m); \
+} \
+static int \
+cmyk_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpCMYK *cmyk = pyg_boxed_get(self, GimpCMYK); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ cmyk->m = (double) PyInt_AS_LONG(value) / 255.0; \
+ else if (PyFloat_Check(value)) \
+ cmyk->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(c);
+MEMBER_ACCESSOR(m);
+MEMBER_ACCESSOR(y);
+MEMBER_ACCESSOR(k);
+MEMBER_ACCESSOR(a);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef cmyk_getsets[] = {
+ { "c", (getter)cmyk_get_c, (setter)cmyk_set_c },
+ { "m", (getter)cmyk_get_m, (setter)cmyk_set_m },
+ { "y", (getter)cmyk_get_y, (setter)cmyk_set_y },
+ { "k", (getter)cmyk_get_k, (setter)cmyk_set_k },
+ { "a", (getter)cmyk_get_a, (setter)cmyk_set_a },
+ { "cyan", (getter)cmyk_get_c, (setter)cmyk_set_c },
+ { "magenta", (getter)cmyk_get_m, (setter)cmyk_set_m },
+ { "yellow", (getter)cmyk_get_y, (setter)cmyk_set_y },
+ { "black", (getter)cmyk_get_k, (setter)cmyk_set_k },
+ { "alpha", (getter)cmyk_get_a, (setter)cmyk_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+cmyk_length(PyObject *self)
+{
+ return 5;
+}
+
+static PyObject *
+cmyk_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpCMYK *cmyk;
+ double val;
+
+ if (pos < 0)
+ pos += 5;
+
+ if (pos < 0 || pos >= 5) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ switch (pos) {
+ case 0: val = cmyk->c; break;
+ case 1: val = cmyk->m; break;
+ case 2: val = cmyk->y; break;
+ case 3: val = cmyk->k; break;
+ case 4: val = cmyk->a; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * 255.0));
+}
+
+static int
+cmyk_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 5;
+
+ if (pos < 0 || pos >= 5) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return cmyk_set_c(self, value, NULL);
+ case 1: return cmyk_set_m(self, value, NULL);
+ case 2: return cmyk_set_y(self, value, NULL);
+ case 3: return cmyk_set_k(self, value, NULL);
+ case 4: return cmyk_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+cmyk_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 5)
+ end = 5;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, cmyk_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods cmyk_as_sequence = {
+ cmyk_length,
+ (binaryfunc)0,
+ 0,
+ cmyk_getitem,
+ cmyk_slice,
+ cmyk_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+cmyk_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return cmyk_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return cmyk_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 5,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, cmyk_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "c") == 0 ||
+ g_ascii_strcasecmp(s, "cyan") == 0)
+ return cmyk_get_c(self, NULL);
+ else if (g_ascii_strcasecmp(s, "m") == 0 ||
+ g_ascii_strcasecmp(s, "magenta") == 0)
+ return cmyk_get_m(self, NULL);
+ else if (g_ascii_strcasecmp(s, "y") == 0 ||
+ g_ascii_strcasecmp(s, "yellow") == 0)
+ return cmyk_get_y(self, NULL);
+ else if (g_ascii_strcasecmp(s, "k") == 0 ||
+ g_ascii_strcasecmp(s, "black") == 0)
+ return cmyk_get_k(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return cmyk_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods cmyk_as_mapping = {
+ cmyk_length,
+ (binaryfunc)cmyk_subscript,
+ (objobjargproc)0
+};
+
+static long
+cmyk_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = cmyk_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+cmyk_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpCMYK *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_cmyk_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpCMYK);
+ c2 = pyg_boxed_get(other, GimpCMYK);
+
+ if ((c1->c == c2->c && c1->m == c2->m && c1->y == c2->y && c1->k == c2->k && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+cmyk_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpCMYK *cmyk;
+ PyObject *ret = NULL;
+ PyObject *c_f = NULL, *m_f = NULL, *y_f = NULL, *k_f = NULL, *a_f = NULL;
+ PyObject *c = NULL, *m = NULL, *y = NULL, *k = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "CMYK ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ if ((c_f = PyFloat_FromDouble(cmyk->c)) == NULL) goto cleanup;
+ if ((m_f = PyFloat_FromDouble(cmyk->m)) == NULL) goto cleanup;
+ if ((y_f = PyFloat_FromDouble(cmyk->y)) == NULL) goto cleanup;
+ if ((k_f = PyFloat_FromDouble(cmyk->k)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(cmyk->a)) == NULL) goto cleanup;
+
+ if ((c = repr(c_f)) == NULL) goto cleanup;
+ if ((m = repr(m_f)) == NULL) goto cleanup;
+ if ((y = repr(y_f)) == NULL) goto cleanup;
+ if ((k = repr(k_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(c),
+ PyString_AsString(m),
+ PyString_AsString(y),
+ PyString_AsString(k),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(c); Py_XDECREF(m); Py_XDECREF(y); Py_XDECREF(k); Py_XDECREF(a);
+ Py_XDECREF(c_f); Py_XDECREF(m_f); Py_XDECREF(y_f); Py_XDECREF(k_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+cmyk_repr(PyObject *self)
+{
+ return cmyk_pretty_print(self, FALSE);
+}
+
+static PyObject *
+cmyk_str(PyObject *self)
+{
+ return cmyk_pretty_print(self, TRUE);
+}
+
+static int
+cmyk_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *c, *m, *y, *k, *a = NULL;
+ GimpCMYK cmyk;
+ static char *kwlist[] = { "c", "m", "y", "k", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOOO|O:set", kwlist,
+ &c, &m, &y, &k, &a))
+ return -1;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ cmyk.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ cmyk.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(c);
+ SET_MEMBER(m);
+ SET_MEMBER(y);
+ SET_MEMBER(k);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ cmyk.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_CMYK;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_CMYK, &cmyk);
+
+ return 0;
+}
+
+PyTypeObject PyGimpCMYK_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.CMYK", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)cmyk_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &cmyk_as_sequence, /* tp_as_sequence */
+ &cmyk_as_mapping, /* tp_as_mapping */
+ (hashfunc)cmyk_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)cmyk_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)cmyk_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ cmyk_methods, /* tp_methods */
+ 0, /* tp_members */
+ cmyk_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)cmyk_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_cmyk_new(const GimpCMYK *cmyk)
+{
+ return pyg_boxed_new(GIMP_TYPE_CMYK, (gpointer)cmyk, TRUE, TRUE);
+}
+
+int
+pygimp_rgb_from_pyobject(PyObject *object, GimpRGB *color)
+{
+ g_return_val_if_fail(color != NULL, FALSE);
+
+ if (pygimp_rgb_check(object)) {
+ *color = *pyg_boxed_get(object, GimpRGB);
+ return 1;
+ } else if (PyString_Check(object)) {
+ if (gimp_rgb_parse_css (color, PyString_AsString(object), -1)) {
+ return 1;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "unable to parse color string");
+ return 0;
+ }
+ } else if (PySequence_Check(object)) {
+ PyObject *r, *g, *b, *a = NULL;
+
+ if (!PyArg_ParseTuple(object, "OOO|O", &r, &g, &b, &a))
+ return 0;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ color->m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ color->m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return 0; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ color->a = 1.0;
+
+ gimp_rgb_clamp(color);
+
+ return 1;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "could not convert to GimpRGB");
+ return 0;
+}