view src/pixelarray.c @ 852:4db78800a93a

A PixelArray class, which is meant to replace surfarray. It acts as a buffer for surface data. Written by Marcus. It's not quite complete, but basic functionality is implemented.
author Rene Dudfield <renesd@gmail.com>
date Wed, 22 Aug 2007 08:10:12 +0000
parents
children 41b22371307e
line wrap: on
line source

/*
  pygame - Python Game Library
  Copyright (C) 2007  Marcus von Appen

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#include "pygame.h"
#include "pygamedocs.h"
#include "surface.h"

typedef struct
{
    PyObject_HEAD
    PyObject *dict;     /* dict for subclassing */
    PyObject *weakrefs; /* Weakrefs for subclassing */
    PyObject *surface;  /* Surface associated with the array. */
    PyObject *lock;     /* Lock object for the surface. */
    Uint32 start;       /* Start offset for subarrays */
    Uint32 end;         /* End offset for subarrays */
    Uint32 xlen;        /* X segment length. */
    Uint32 ylen;        /* Y segment length. */
    Uint32 xstep;       /* X offset step width. */
    Uint32 ystep;       /* Y offset step width. */
    Uint32 padding;     /* Padding to get to the next x offset. */
    PyObject *parent;   /* Parent pixel array */

} PyPixelArray;

static PyPixelArray* _pxarray_new_internal (PyTypeObject *type,
                                            PyObject *surface,
                                            Uint32 start, Uint32 end,
                                            Uint32 xlen, Uint32 ylen,
                                            Uint32 xstep, Uint32 ystep,
                                            Uint32 padding, PyObject *parent);
static PyObject* _pxarray_new (PyTypeObject *type, PyObject *args,
                               PyObject *kwds);
static void _pxarray_dealloc (PyPixelArray *self);

static PyObject* _pxarray_get_dict (PyPixelArray *self, void *closure);
static PyObject* _pxarray_get_surface (PyPixelArray *self, void *closure);

static PyObject* _pxarray_repr (PyPixelArray *array);

static int _get_color_from_object (PyObject *val, SDL_PixelFormat *format,
                                   Uint32 *color);
static PyObject* _get_single_pixel (Uint8 *pixels, int bpp, Uint32 _index);
static void _set_single_pixel (Uint8 *pixels, int bpp, Uint32 _index,
                               Uint32 color);
static PyObject* _array_slice_internal (PyPixelArray *array, Uint32 _start,
                                        Uint32 _end, Uint32 _step);

/* Sequence methods */
static Py_ssize_t _pxarray_length (PyPixelArray *array);
static PyObject* _pxarray_concat (PyPixelArray *array, PyObject *value);
static PyObject* _pxarray_repeat (PyPixelArray *a, Py_ssize_t n);
static PyObject* _pxarray_item (PyPixelArray *array, Py_ssize_t _index);
static PyObject* _pxarray_slice (PyPixelArray *array, Py_ssize_t low,
                                 Py_ssize_t high);
static int _array_assign_array (PyPixelArray *array, Py_ssize_t _index,
                                PyPixelArray *val);
static int _array_assign_sequence (PyPixelArray *array, Py_ssize_t _index,
                                   PyObject *val);
static int _pxarray_ass_item (PyPixelArray *array, Py_ssize_t _index,
                              PyObject *value);
static int _pxarray_ass_slice (PyPixelArray *array, Py_ssize_t low,
                               Py_ssize_t high, PyObject *value);
static int _pxarray_contains (PyPixelArray *array, PyObject *value);
static PyObject* _pxarray_inplace_concat (PyPixelArray *array, PyObject *seq);
static PyObject* _pxarray_inplace_repeat (PyPixelArray *array, Py_ssize_t n);

/* Mapping methods */
/*
static int _get_subslice (SDL_Surface *surface, PyObject *op, Py_ssize_t *start,
                          Py_ssize_t *stop, Py_ssize_t *step);
static PyObject* _pxarray_subscript (PyPixelArray *array, PyObject *op);
static int _pxarray_ass_subscript (PyPixelArray *array, PyObject* op,
                                   PyObject* value);
*/
/* Buffer methods */
static Py_ssize_t _pxarray_getreadbuf (PyPixelArray *array, Py_ssize_t _index,
                                       const void **ptr);
static Py_ssize_t _pxarray_getwritebuf (PyPixelArray *array, Py_ssize_t _index,
                                        const void **ptr);
static Py_ssize_t _pxarray_getsegcount (PyPixelArray *array, Py_ssize_t *lenp);

/**
 * Methods, which are bound to the PyPixelArray type.
 */
static PyMethodDef _pxarray_methods[] =
{
    { NULL, NULL, 0, NULL }
};

/**
 * Getters and setters for the PyPixelArray.
 */
static PyGetSetDef _pxarray_getsets[] =
{
    { "__dict__", (getter) _pxarray_get_dict, NULL, NULL, NULL },
    { "surface", (getter) _pxarray_get_surface, NULL, NULL,
      DOC_PIXELARRAYSURFACE },
    { NULL, NULL, NULL, NULL, NULL }
};

/**
 * Sequence interface support for the PyPixelArray.
 */
static PySequenceMethods _pxarray_sequence =
{
    (lenfunc) _pxarray_length,                  /*sq_length*/
    NULL, /*(binaryfunc) _pxarray_concat,*/       /*sq_concat*/
    (ssizeargfunc) _pxarray_repeat,             /*sq_repeat*/
    (ssizeargfunc) _pxarray_item,               /*sq_item*/
    NULL, /*(ssizessizeargfunc) _pxarray_slice,*/ /*sq_slice*/
    (ssizeobjargproc) _pxarray_ass_item,        /*sq_ass_item*/
    NULL, /*(ssizessizeobjargproc) _pxarray_ass_slice,*/  /*sq_ass_slice*/
    (objobjproc) _pxarray_contains,             /*sq_contains*/
    NULL, /*(binaryfunc) _pxarray_inplace_concat,*/       /*sq_inplace_concat*/
    NULL, /*(ssizeargfunc) _pxarray_inplace_repeat*/      /*sq_inplace_repeat*/
};

/**
 * Mapping interface support for the PyPixelArray.
 */
/* static PyMappingMethods _pxarray_mapping = */
/* { */
/*     (inquiry) _pxarray_length,              /\*mp_length*\/ */
/*     (binaryfunc) _pxarray_subscript,        /\*mp_subscript*\/ */
/*     (objobjargproc) _pxarray_ass_subscript, /\*mp_ass_subscript*\/ */
/* }; */

/**
 * Buffer interface support for the PyPixelArray.
 */
static PyBufferProcs _pxarray_as_buffer =
{
        (readbufferproc) _pxarray_getreadbuf,
        (writebufferproc) _pxarray_getwritebuf,
        (segcountproc) _pxarray_getsegcount,
        NULL,
};

static PyTypeObject PyPixelArray_Type =
{
    PyObject_HEAD_INIT(NULL)
    0,
    "PixelArray",               /* tp_name */
    sizeof (PyPixelArray),      /* tp_basicsize */
    0,                          /* tp_itemsize */
    (destructor) _pxarray_dealloc, /* tp_dealloc */
    0,                          /* tp_print */
    0,                          /* tp_getattr */
    0,                          /* tp_setattr */
    0,                          /* tp_compare */
    (reprfunc) &_pxarray_repr,  /* tp_repr */
    0,                          /* tp_as_number */
    &_pxarray_sequence,         /* tp_as_sequence */
    0, /*&_pxarray_mapping ,*/          /* tp_as_mapping */
    0,                          /* tp_hash */
    0,                          /* tp_call */
    0,                          /* tp_str */
    PyObject_GenericGetAttr,    /* tp_getattro */
    0,                          /* tp_setattro */
    &_pxarray_as_buffer,        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_WEAKREFS,
    DOC_PYGAMEPIXELARRAY,       /* tp_doc */
    0,                          /* tp_traverse */
    0,                          /* tp_clear */
    0,                          /* tp_richcompare */
    offsetof (PyPixelArray, weakrefs),  /* tp_weaklistoffset */
    0,                          /* tp_iter */
    0,                          /* tp_iternext */
    _pxarray_methods,           /* tp_methods */
    0,                          /* tp_members */
    _pxarray_getsets,           /* tp_getset */
    0,                          /* tp_base */
    0,                          /* tp_dict */
    0,                          /* tp_descr_get */
    0,                          /* tp_descr_set */
    offsetof (PyPixelArray, dict), /* tp_dictoffset */
    0,                          /* tp_init */
    0,                          /* tp_alloc */
    _pxarray_new,               /* tp_new */
    0,                          /* tp_free */
    0,                          /* tp_is_gc */
    0,                          /* tp_bases */
    0,                          /* tp_mro */
    0,                          /* tp_cache */
    0,                          /* tp_subclasses */
    0,                          /* tp_weaklist */
    0                           /* tp_del */
};

#define PyPixelArray_Check(o) \
    ((o)->ob_type == (PyTypeObject *) &PyPixelArray_Type)

static PyPixelArray*
_pxarray_new_internal (PyTypeObject *type, PyObject *surface,
                       Uint32 start, Uint32 end, Uint32 xlen, Uint32 ylen,
                       Uint32 xstep, Uint32 ystep, Uint32 padding,
                       PyObject *parent)
{
    PyPixelArray *self = (PyPixelArray *) type->tp_alloc (type, 0);
    if (!self)
        return NULL;

    self->surface = (PyObject *) surface;
    if (surface)
    {
        self->lock = PySurface_LockLifetime (surface);
        if (!self->lock)
        {
            self->ob_type->tp_free ((PyObject *) self);
            return NULL;
        }
    }
    self->weakrefs = NULL;
    self->dict = NULL;
    self->start = start;
    self->end = end;
    self->xlen = xlen;
    self->ylen = ylen;
    self->xstep = xstep;
    self->ystep = ystep;
    self->padding = padding;
    self->parent = parent;
    return self;
}

/**
 * Creates a new PyPixelArray.
 */
static PyObject*
_pxarray_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyObject *surfobj;
    SDL_Surface* surface;

    if (!PyArg_ParseTuple (args, "O!", &PySurface_Type, &surfobj))
        return NULL;

    surface = PySurface_AsSurface (surfobj);
    if (surface->format->BytesPerPixel < 1  ||
        surface->format->BytesPerPixel > 4)
        return RAISE (PyExc_ValueError,
                      "unsupport bit depth for reference array");

    return (PyObject *) _pxarray_new_internal
        (type, surfobj, 0, (Uint32) surface->w * surface->h,
         (Uint32) surface->w, (Uint32) surface->h, 1, 1, (Uint32) surface->w,
         NULL);
}

/**
 * Deallocates the PyPixelArray and its members.
 */
static void
_pxarray_dealloc (PyPixelArray *self)
{
    if (self->weakrefs)
        PyObject_ClearWeakRefs ((PyObject *) self);
    if (self->lock)
    {
        Py_DECREF (self->lock);
    }
    Py_XDECREF (self->dict);
    self->ob_type->tp_free ((PyObject *) self);
}

/**** Getter and setter access ****/

/**
 * Getter for PixelArray.__dict__.
 */
static PyObject*
_pxarray_get_dict (PyPixelArray *self, void *closure)
{
    if (!self->dict)
    {
	self->dict = PyDict_New ();
        if (!self->dict)
            return NULL;
    }

    Py_INCREF (self->dict);
    return self->dict;
}

/**
 * Getter for PixelArray.surface
 */
static PyObject*
_pxarray_get_surface (PyPixelArray *self, void *closure)
{
    Py_XINCREF (self->surface);
    return self->surface;
}

/**** Methods ****/

/**
 * repr(PixelArray)
 */
static PyObject*
_pxarray_repr (PyPixelArray *array)
{
    PyObject *string;
    SDL_Surface *surface;
    int bpp;
    Uint8 *pixels;
    Uint32 pixel;
    Uint32 x = 0;
    Uint32 y = 0;

    surface = PySurface_AsSurface (array->surface);
    bpp = surface->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    string = PyString_FromString ("PixelArray(");
    switch (bpp)
    {
    case 1:
        break;
    case 2:
        break;
    case 3:
    {
        /* TODO: is this correct? */
        Uint8 *px;
        px = ((Uint8 *) pixels + y * array->xstep);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
        pixel = (px[0]) + (px[1] << 8) + (px[2] << 16);
#else
        pixel = (px[2]) + (px[1] << 8) + (px[0] << 16);
#endif
        break;
    }
    default: /* 4bpp */
        for (y = 0; y < array->ylen; y += array->ystep)
        {
            /* Construct the rows */
            PyString_ConcatAndDel (&string, PyString_FromString ("\n  ["));

            for (x = 0; x < array->xlen - array->xstep; x += array->xstep)
            {
                /* Construct the columns */
                pixel = *((Uint32 *) pixels + array->start + x +
                          y * array->padding);
                PyString_ConcatAndDel (&string, PyString_FromFormat
                                       ("%d, ", pixel));
            }
            pixel = *((Uint32 *) pixels + array->start + x +y * array->padding);
            PyString_ConcatAndDel (&string, PyString_FromFormat ("%d]", pixel));
        }
        break;
    }
    PyString_ConcatAndDel (&string, PyString_FromString ("\n)"));
    return string;
}

/**
 * Tries to retrieve a valid color for a Surface.
 */
static int
_get_color_from_object (PyObject *val, SDL_PixelFormat *format, Uint32 *color)
{
    Uint8 rgba[4];

    if (PyInt_Check (val))
    {
        int intval = PyInt_AsLong (val);
        if (intval < 0)
        {
            if (!PyErr_Occurred ())
                PyErr_SetString (PyExc_ValueError, "invalid color argument");
            return 0;
        }
        *color = (Uint32) intval;
        return 1;
    }
    else if (PyLong_Check (val))
    {
        long long longval = -1;
        /* Plain index: array[x, */

        longval = PyLong_AsLong (val);
        if ((longval < INT_MIN) || (longval > INT_MAX))
        {
            PyErr_SetString(PyExc_ValueError, "index too big for array access");
            return 0;
        }
        *color = (Uint32) longval;
        return 1;
    }
    else if (RGBAFromObj (val, rgba))
    {
        *color = (Uint32) SDL_MapRGBA
            (format, rgba[0], rgba[1], rgba[2], rgba[3]);
        return 1;
    }
    else
        PyErr_SetString (PyExc_ValueError, "invalid color argument");
    return 0;
}

/**
 * Retrieves a single pixel located at index from the surface pixel
 * array.
 */
static PyObject*
_get_single_pixel (Uint8 *pixels, int bpp, Uint32 _index)
{
    Uint32 pixel;

    switch (bpp)
    {
    case 1:
        pixel = (Uint32)*((Uint8 *) pixels + _index);
        break;
    case 2:
        pixel = (Uint32)*((Uint16 *) pixels + _index);
        break;
    case 3:
    {
        /* TODO: is this correct? */
        Uint8 *px = ((Uint8 *) pixels + _index);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
        pixel = (px[0]) + (px[1] << 8) + (px[2] << 16);
#else
        pixel = (px[2]) + (px[1] << 8) + (px[0] << 16);
#endif
        break;
    }
    default: /* 4 bpp */
        pixel = *((Uint32 *) pixels + _index);
        break;
    }
    
    return PyInt_FromLong ((long)pixel);
}

/**
 * Sets a single pixel located at index from the surface pixel array.
 */
static void
_set_single_pixel (Uint8 *pixels, int bpp, Uint32 _index, Uint32 color)
{
    Uint32 pixel;

    switch (bpp)
    {
    case 1:
        *((Uint8 *) pixels + _index) = (Uint8) color;
        break;
    case 2:
        *((Uint16 *) pixels + _index) = (Uint16) color;
        break;
    case 3:
    {
        /* TODO: is this correct? */
        Uint8 *px = ((Uint8 *) pixels + _index);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
        pixel = (px[0]) + (px[1] << 8) + (px[2] << 16);
#else
        pixel = (px[2]) + (px[1] << 8) + (px[0] << 16);
#endif
        break;
    }
    default: /* 4 bpp */
        *((Uint32 *) pixels + _index) = color;
        break;
    }
}

/**
 * Creates a 2D slice of the array.
 */
static PyObject*
_array_slice_internal (PyPixelArray *array, Uint32 _start, Uint32 _end,
                       Uint32 _step)
{
    Uint32 start;
    Uint32 end;
    Uint32 xlen;
    Uint32 ylen;
    Uint32 xstep;
    Uint32 ystep;
    Uint32 padding;

    start = array->start + _start;
    end = array->end - array->xlen + _start;

    ylen = array->ylen;
    ystep = array->ystep;

    xlen = _end - _start + 1;
    xstep = _step;

    padding = array->xlen - xlen + 1;

    return (PyObject *) _pxarray_new_internal
        (&PyPixelArray_Type, array->surface, start, end, xlen, ylen,
         xstep, ystep, padding, (PyObject *) array);
}

/**** Sequence interfaces ****/

/**
 * len (array)
 */
static Py_ssize_t
_pxarray_length (PyPixelArray *array)
{
    if (array->xlen > 1)
        return array->xlen / array->xstep;
    return array->ylen / array->ystep;
}

/**
 * TODO
 */
static PyObject*
_pxarray_concat (PyPixelArray *array, PyObject *value)
{
    /* TODO */
    return RAISE (PyExc_NotImplementedError, "method not implemented");
}

/**
 * array * 2
 */
static PyObject*
_pxarray_repeat (PyPixelArray *array, Py_ssize_t n)
{
    /* TODO */
    return RAISE (PyExc_NotImplementedError, "method not implemented");
}

/**
 * array[x]
 */
static PyObject*
_pxarray_item (PyPixelArray *array, Py_ssize_t _index)
{
    SDL_Surface *surface;
    int bpp;
    Uint8 *pixels;

    surface = PySurface_AsSurface (array->surface);
    bpp = surface->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    if (_index < 0)
        return RAISE (PyExc_IndexError, "array index out of range");

    if (array->xlen == 1) /* Access of a single column. */
        return _get_single_pixel
            (pixels, bpp, array->start + _index * array->padding *array->ystep);

    return _array_slice_internal (array, (Uint32) _index, (Uint32) _index, 1);
}

/**
 * array[x:y]
 */
static PyObject*
_pxarray_slice (PyPixelArray *array, Py_ssize_t low, Py_ssize_t high)
{
    return _array_slice_internal (array, (Uint32) low, (Uint32) high, 1);
}

static int
_array_assign_array (PyPixelArray *array, Py_ssize_t _index, PyPixelArray *val)
{
    SDL_Surface *surface;
    SDL_Surface *valsf;
    Uint32 x;
    Uint32 y;
    int bpp;
    int valbpp;
    Uint8 *pixels;
    Py_ssize_t offset = 0;

    Uint32 start;
    Uint32 end;
    Uint32 xlen;
    Uint32 ylen;
    Uint32 xstep;
    Uint32 ystep;
    Uint32 padding;

    /* Set the correct slice indices */
    start = array->start + _index;
    end = array->end - array->xlen + _index;
    ylen = array->ylen;
    ystep = array->ystep;
    xlen = 1;
    xstep = 1;
    padding = array->xlen - xlen + 1;

    if (val->ylen / val->ystep != ylen / ystep ||
        val->xlen / val->xstep != xlen / xstep)
    {
        /* Bounds do not match. */
        PyErr_SetString (PyExc_ValueError, "array sizes do not match");
        return -1;
    }

    surface = PySurface_AsSurface (array->surface);
    valsf = PySurface_AsSurface (val->surface);
    bpp = surface->format->BytesPerPixel;
    valbpp = valsf->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    if (bpp != valbpp)
    {
        /* bpp do not match. */
        /* TODO */
        PyErr_SetString (PyExc_ValueError, "bit depths do not match");
        return -1;
    }

    /* Single value assignment. */
    for (y = 0; y < ylen; y += ystep)
    {
        for (x = 0; x < xlen; x += xstep)
        {
            offset = start + x + y * padding;

            switch (bpp)
            {
            case 1:
                *((Uint8 *) pixels + offset) =
                    (Uint8)*((Uint8 *) valsf->pixels + val->start +
                             x * val->xstep + y * val->ystep * val->padding);
                break;
            case 2:
                *((Uint16 *) pixels + offset) =
                    (Uint16)*((Uint16 *) valsf->pixels + val->start +
                             x * val->xstep + y * val->ystep * val->padding);
                break;
            case 3:
                /* TODO */
                break;
            default: /* 4 bpp */
                *((Uint32 *) pixels + offset) =
                    *((Uint32 *) valsf->pixels + val->start +
                      x * val->xstep + y * val->ystep * val->padding);
                break;
            }
        }
    }

    return 0;
}

static int
_array_assign_sequence (PyPixelArray *array, Py_ssize_t _index, PyObject *val)
{
    SDL_Surface *surface;
    Uint32 x = 0;
    Uint32 y = 0;
    int bpp;
    Uint8 *pixels;
    Uint32 color = 0;
    Uint32 *colorvals = NULL;
    Py_ssize_t offset = 0;
    Py_ssize_t seqsize = PySequence_Size (val);

    Uint32 start;
    Uint32 end;
    Uint32 xlen;
    Uint32 ylen;
    Uint32 xstep;
    Uint32 ystep;
    Uint32 padding;

    surface = PySurface_AsSurface (array->surface);
    bpp = surface->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    /* Set the correct slice indices */
    start = array->start + _index;
    end = array->end - array->xlen + _index;
    ylen = array->ylen;
    ystep = array->ystep;
    xlen = 1;
    xstep = 1;
    padding = array->xlen - xlen + 1;

    if ((Uint32)seqsize != ylen / ystep)
    {
        PyErr_SetString(PyExc_ValueError, "sequence size mismatch");
        return -1;
    }
   
    if (seqsize == 1)
    {
        /* Single value assignment. */
        offset = start + x + y * padding;
        switch (bpp)
        {
        case 1:
            *((Uint8 *) pixels + offset) = (Uint8) color;
            break;
        case 2:
            *((Uint16 *) pixels + offset) = (Uint16) color;
            break;
        case 3:
            /* TODO */
            break;
        default: /* 4 bpp */
            *((Uint32 *) pixels + offset) = color;
            break;
        }
        return 0;
    }

    /* Copy the values. */
    colorvals = malloc (sizeof (Uint32) * seqsize);
    if (!colorvals)
    {
        PyErr_SetString(PyExc_ValueError, "could not copy pixels");
        return -1;
    }

    for (offset = 0; offset < seqsize; offset++)
    {
        if (!_get_color_from_object (PySequence_Fast_GET_ITEM (val, offset),
                                     surface->format, &color))
        {
            free (colorvals);
            return -1;
        }
        colorvals[offset] = color;
    }

    for (y = 0; y < ylen; y += ystep)
    {
        for (x = 0; x < xlen; x += xstep)
        {
            offset = start + x + y * padding;
            color = *colorvals++;
            switch (bpp)
            {
            case 1:
                *((Uint8 *) pixels + offset) = (Uint8) color;
                break;
            case 2:
                *((Uint16 *) pixels + offset) = (Uint16) color;
                break;
            case 3:
                /* TODO */
                break;
            default: /* 4 bpp */
                *((Uint32 *) pixels + offset) = color;
                break;
            }
        }
    }
    
    return 0;
}

/**
 * array[x] = ...
 */
static int
_pxarray_ass_item (PyPixelArray *array, Py_ssize_t _index, PyObject *value)
{
    SDL_Surface *surface;
    Uint32 x;
    Uint32 y;
    int bpp;
    Uint8 *pixels;
    Uint32 color = 0;
    Py_ssize_t offset = 0;

    Uint32 start;
    Uint32 end;
    Uint32 xlen;
    Uint32 ylen;
    Uint32 xstep;
    Uint32 ystep;
    Uint32 padding;

    surface = PySurface_AsSurface (array->surface);
    bpp = surface->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    if (!_get_color_from_object (value, surface->format, &color))
    {
        if (PyPixelArray_Check (value))
        {
            PyErr_Clear (); /* _get_color_from_object */
            return _array_assign_array (array, _index, (PyPixelArray *) value);
        }
        else if (PySequence_Check (value))
        {
            PyErr_Clear (); /* _get_color_from_object */
            return _array_assign_sequence (array, _index, value);
        }
        else /* Error already set by _get_color_from_object(). */
            return -1;
    }

    if (array->xlen == 1) /* Single pixel access. */
    {
        _set_single_pixel
            (pixels, bpp, array->start + _index * array->padding * array->ystep,
             color);
        return 0;
    }

    /* Set the correct slice indices */
    start = array->start + _index;
    end = array->end - array->xlen + _index;
    ylen = array->ylen;
    ystep = array->ystep;
    xlen = 1;
    xstep = 1;
    padding = array->xlen - xlen + 1;

    /* Single value assignment. */
    for (y = 0; y < ylen; y += ystep)
    {
        for (x = 0; x < xlen; x += xstep)
        {
            offset = start + x + y * padding;
            switch (bpp)
            {
            case 1:
                *((Uint8 *) pixels + offset) = (Uint8) color;
                break;
            case 2:
                *((Uint16 *) pixels + offset) = (Uint16) color;
                break;
            case 3:
                /* TODO */
                break;
            default: /* 4 bpp */
                *((Uint32 *) pixels + offset) = color;
                break;
            }
        }
    }

    return 0;
}

/**
 * array[x:y] = ....
 */
/* TODO: support:
   array[x:y] = [...] for 2D and 1D arrays
   array[x:y] = z for 2D and 1D arrays
 */
static int
_pxarray_ass_slice (PyPixelArray *array, Py_ssize_t low, Py_ssize_t high,
                    PyObject *value)
{
    /* TODO */
    PyErr_SetString (PyExc_NotImplementedError, "method not implemented");
    return -1;
}

/**
 * x in array
 */
/* TODO: support:
   [...] in array for 2D arrays
   x in array for 1D and 2D arrays
 */
static int
_pxarray_contains (PyPixelArray *array, PyObject *value)
{
    SDL_Surface *surface;
    Uint32 x;
    Uint32 y;
    Py_ssize_t _index;
    Uint8 *pixels;
    int bpp;
    Uint32 color;

    surface = PySurface_AsSurface (array->surface);
    bpp = surface->format->BytesPerPixel;
    pixels = (Uint8 *) surface->pixels;

    if (!_get_color_from_object (value, surface->format, &color))
    {
        /* TODO */
         PyErr_SetString (PyExc_TypeError, "invalid color argument");
         return -1;
    }

    switch (bpp)
    {
    case 1:
        for (x = 0; x < array->ylen; x += array->ystep)
        {
            for (y = 0; y < array->xlen; y += array->xstep)
            {
                _index = array->start + x + y * array->padding;
                if (*((Uint8 *) pixels + _index) == (Uint8) color)
                    return 1;
            }
        }
    case 2:
        for (x = 0; x < array->ylen; x += array->ystep)
        {
            for (y = 0; y < array->xlen; y += array->xstep)
            {
                _index = array->start + x + y * array->padding;
                if (*((Uint16 *) pixels + _index) == (Uint16) color)
                    return 1;
            }
        }
        break;
    case 3:
        /* TODO */
        break;
    default: /* 4 bpp */
        for (x = 0; x < array->ylen; x += array->ystep)
        {
            for (y = 0; y < array->xlen; y += array->xstep)
            {
                _index = array->start + x + y * array->padding;
                if (*((Uint32 *) pixels + _index) == color)
                    return 1;
            }
        }
        break;
    }
    return 0;
}

/**
 * array += ....
 */
static PyObject*
_pxarray_inplace_concat (PyPixelArray *array, PyObject *seq)
{
    /* TODO */
    return RAISE (PyExc_NotImplementedError, "method not implemented");
}

/**
 * array *= ...
 */
static PyObject*
_pxarray_inplace_repeat (PyPixelArray *array, Py_ssize_t n)
{
    /* TODO */
    return RAISE (PyExc_NotImplementedError, "method not implemented");
}

/**** Mapping interfaces ****/

/**
 * Internally used parser function for the 2D slices:
 * array[x,y], array[:,:], ...
 */
/* static int */
/* _get_subslice (SDL_Surface *surface, PyObject *op, Py_ssize_t *start, */
/*                Py_ssize_t *stop, Py_ssize_t *step) */
/* { */
/*     *start = -1; */
/*     *stop = -1; */
/*     *step = -1; */

/*     if (PyInt_Check (op)) */
/*     { */
/*         printf ("Found int\n"); */
/*         /\* Plain index: array[x, *\/ */
/*         *start = PyInt_AsLong (op); */
/*         return 1; */
/*     } */
/*     else if (PyLong_Check (op)) */
/*     { */
/*         long long val = -1; */
/*         /\* Plain index: array[x, *\/ */

/*         printf ("Found long\n"); */
/*         val = PyLong_AsLong (op); */
/*         if ((val < INT_MIN) || (val > INT_MAX)) */
/*         { */
/*             PyErr_SetString(PyExc_ValueError, "index too big for array access"); */
/*             return -1; */
/*         } */
/*         *start = (int) val; */
/*     } */
/*     else if (op == Py_Ellipsis) */
/*     { */
/*         /\* Operator is the ellipsis: array[..., *\/ */
/*         printf ("Found ellipsis\n"); */
/*         /\* TODO *\/ */
/*     } */
/*     else if (PySlice_Check (op)) */
/*     { */
/*         Py_ssize_t slicelen; */

/*         /\* Operator is a slice: array[x::, *\/ */
/*         printf ("Found slice\n"); */
/*         if (PySlice_GetIndicesEx ((PySliceObject *) op, surface->w * surface->h, */
/*                                   start, stop, step, &slicelen) < 0) */
/*         { */
/*             return 0; */
/*         } */
/*     } */
/*     return 0; */
/* } */

/**
 * Slicing support for 1D and 2D access.
 * array[x,y] is only supported for 2D arrays.
 */
/* static PyObject* */
/* _pxarray_subscript (PyPixelArray *array, PyObject *op) */
/* { */
/*     Py_ssize_t step; */
/*     Py_ssize_t start; */
/*     Py_ssize_t stop; */
/*     SDL_Surface *surface = PySurface_AsSurface (array->surface); */

/*     /\* Note: order matters here. */
/*      * First check array[x,y], then array[x:y:z], then array[x] */
/*      * Otherwise it'll fail. */
/*      *\/ */
/*     if (PySequence_Check (op)) */
/*     { */
/*         PyObject *sub; */
/*         Py_ssize_t i; */

/*         if (PySequence_Size (op) > 2) */
/*             return RAISE (PyExc_IndexError, */
/*                           "too many indices for the 2D array"); */

/*         for (i = 0; i < PySequence_Size (op); i++) */
/*         { */
/*             sub = PySequence_GetItem (op, i); */
/*             if (!_get_subslice (surface, sub, &start, &stop, &step)) */
/*             { */
/*                 /\* Error on retrieving the subslice. *\/ */
/*                 printf ("Error\n"); */
/*                 Py_DECREF (sub); */
/*                 return NULL; */
/*             } */

/*             if (stop == -1) */
/*             { */
/*                 /\* Not an index *\/ */
/*                 printf ("Slice: %d:%d:%d\n", start, stop, step); */
/*             } */

/*             Py_DECREF (sub); */
/*         } */
/*         Py_DECREF (sub); */
/*     } */
/*     else if (PySlice_Check (op)) */
/*     { */
/*         /\* A slice *\/ */
/*         PyObject *result; */
/*         Py_ssize_t slicelen; */

/*         if (PySlice_GetIndicesEx ((PySliceObject *) op, surface->w * surface->h, */
/*                                   &start, &stop, &step, &slicelen) < 0) */
/*             return NULL; */
        
/*         if (slicelen < 0) /\* TODO: empty surface with 0x0 px? *\/ */
/*             return NULL; */
/* /\* */
/*         result = (PyObject *) _pxarray_new_internal */
/*             (&PyPixelArray_Type, array->surface, (Uint32) start, (Uint32) stop, */
/*              1, step, 1, (PyObject *) array); */
/* *\/ */
/*         return result; */
/*     } */
/*     else if (PyIndex_Check (op)) */
/*     { */
/*         /\* A simple index. *\/ */
/*         Py_ssize_t i = PyNumber_AsSsize_t (op, PyExc_IndexError); */

/*         if (i == -1 && PyErr_Occurred ()) */
/*             return NULL; */

/*         if (i < 0) */
/*             i += surface->w * surface->h; */
/*         return _pxarray_item (array, i); */
/*     } */

/*     return RAISE (PyExc_TypeError, */
/*                   "index must be an integer, sequence or slice"); */
/* } */

/* static int */
/* _pxarray_ass_subscript (PyPixelArray *array, PyObject* op, PyObject* value) */
/* { */
/*     SDL_Surface *surface = PySurface_AsSurface (array->surface); */

/*     if (PyIndex_Check (op)) */
/*     { */
/*         /\* A simple index. *\/ */
/*         Py_ssize_t i = PyNumber_AsSsize_t (op, PyExc_IndexError); */
/*         if (i == -1 && PyErr_Occurred ()) */
/*             return -1; */

/*         if (i < 0) */
/*             i += surface->w * surface->h; */
/*         return _pxarray_ass_item (array, i, value); */
/*     } */
/*     PyErr_SetString (PyExc_NotImplementedError, "method not implemented"); */
/*     return -1; */
/* } */

/**** Buffer interfaces ****/

static Py_ssize_t
_pxarray_getreadbuf (PyPixelArray *array, Py_ssize_t _index, const void **ptr)
{
    SDL_Surface *surface;
    size_t mod = 0;

    if (_index != 0)
    {
        PyErr_SetString (PyExc_SystemError,
                         "Accessing non-existent array segment");
        return -1;
    }
    if (array->parent)
    {
        *ptr = NULL;
        return 0; /* TODO: subarrays? */
    }
    surface = PySurface_AsSurface (array->surface);

    switch (surface->format->BytesPerPixel)
    {
    case 1:
        mod = sizeof (Uint8);
        break;
    case 2:
        mod = sizeof (Uint16);
        break;
    case 3:
        mod = sizeof (Uint8) * 3;
        break;
    default:
        mod = sizeof (Uint32);
        break;
    }

    *ptr = (void *) surface->pixels;
    return surface->w * surface->h * mod;
}

static Py_ssize_t
_pxarray_getwritebuf (PyPixelArray *array, Py_ssize_t _index, const void **ptr)
{
    SDL_Surface *surface = NULL;
    size_t mod = 0;

    if (_index != 0)
    {
        PyErr_SetString (PyExc_SystemError, 
                         "Accessing non-existent array segment");
        return -1;
    }
    if (array->parent)
    {
        *ptr = NULL;
        return 0; /* TODO: subarrays? */
    }

    switch (surface->format->BytesPerPixel)
    {
    case 1:
        mod = sizeof (Uint8);
        break;
    case 2:
        mod = sizeof (Uint16);
        break;
    case 3:
        mod = sizeof (Uint8) * 3;
        break;
    default:
        mod = sizeof (Uint32);
        break;
    }

    surface = PySurface_AsSurface (array->surface);
    *ptr = (void *) surface->pixels;
    return surface->w * surface->h * mod;
}

static Py_ssize_t
_pxarray_getsegcount (PyPixelArray *array, Py_ssize_t *lenp)
{
    if (array->parent)
    {
        *lenp = 0;
        return 0; /* TODO: subarrays? */
    }
    if (lenp)
    {
        SDL_Surface *surface = PySurface_AsSurface (array->surface);
        size_t mod = 0;

        switch (surface->format->BytesPerPixel)
        {
        case 1:
            mod = sizeof (Uint8);
            break;
        case 2:
            mod = sizeof (Uint16);
            break;
        case 3:
            mod = sizeof (Uint8) * 3;
            break;
        default:
            mod = sizeof (Uint32);
        break;
        }

        *lenp = surface->w * surface->h * mod;
        if (array->parent)
        {
            *lenp = 0;
            return 1; /* TODO: subarrays? */
        }
    }
    return 1;
}

static PyMethodDef surfarray_builtins[] =
{
    { NULL, NULL, 0, NULL }
};

PYGAME_EXPORT
void initpixelarray (void)
{
    PyObject *module;

    if (PyType_Ready (&PyPixelArray_Type) < 0)
        return;
    
    /* create the module */
    module = Py_InitModule3 ("pixelarray", NULL, NULL);
    Py_INCREF (&PyPixelArray_Type);
    PyModule_AddObject (module, "PixelArray", (PyObject *) &PyPixelArray_Type);
    
    /*imported needed apis*/
    import_pygame_base ();
    import_pygame_surface ();
}