view src/pixelcopy.c @ 3194:9fcb7abf3185 default tip

Remove some gcc 4.7.2 compiler warnings
author Lenard Lindstrom <len-l@telus.net>
date Tue, 23 Apr 2013 19:53:36 -0700
parents 841b902f3d15
children
line wrap: on
line source

/*
  pygame - Python Game Library
  Copyright (C) 2000-2001  Pete Shinners
  
  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

  Pete Shinners
  pete@shinners.org
*/

#include <stddef.h>
#include "pygame.h"
#include "pgcompat.h"
#include "doc/pixelcopy_doc.h"
#include "pgarrinter.h"
#include "pgview.h"
#include <SDL_byteorder.h>

#if !defined(DOC_PYGAMEBLITARRAY)
#define DOC_PYGAMEBLITARRAY DOC_PYGAMESURFARRAYBLITARRAY
#endif
#if !defined(DOC_PYGAMECOPYSURFACE)
#define DOC_PYGAMECOPYSURFACE "Copy surface pixels to an array object"
#endif

typedef enum {
    VIEWKIND_RED,
    VIEWKIND_GREEN,
    VIEWKIND_BLUE,
    VIEWKIND_ALPHA,
    VIEWKIND_COLORKEY,
    VIEWKIND_RGB
} _pc_view_kind_t;

typedef union {
    Uint32 value;
    Uint8 bytes[sizeof(Uint32)];
} _pc_pixel_t;

static int
_view_kind(PyObject *obj, void *view_kind_vptr)
{
    unsigned long ch;
    _pc_view_kind_t *view_kind_ptr = (_pc_view_kind_t *)view_kind_vptr;

    if (PyUnicode_Check(obj)) {
        if (PyUnicode_GET_SIZE(obj) != 1) {
            PyErr_SetString(PyExc_TypeError,
                            "expected a length 1 string for argument 3");
            return 0;
        }
        ch = *PyUnicode_AS_UNICODE(obj);
    }
    else if (Bytes_Check(obj)) {
        if (Bytes_GET_SIZE(obj) != 1) {
            PyErr_SetString(PyExc_TypeError,
                            "expected a length 1 string for argument 3");
            return 0;
        }
        ch = *Bytes_AS_STRING(obj);
    }
    else {
        PyErr_Format(PyExc_TypeError,
                     "expected a length one string for argument 3: got '%s'",
                     Py_TYPE(obj)->tp_name);
        return 0;
    }
    switch (ch) {

    case 'R':
    case 'r':
        *view_kind_ptr = VIEWKIND_RED;
        break;
    case 'G':
    case 'g':
        *view_kind_ptr = VIEWKIND_GREEN;
        break;
    case 'B':
    case 'b':
        *view_kind_ptr = VIEWKIND_BLUE;
        break;
    case 'A':
    case 'a':
        *view_kind_ptr = VIEWKIND_ALPHA;
        break;
    case 'C':
    case 'c':
        *view_kind_ptr = VIEWKIND_COLORKEY;
        break;
    case 'P':
    case 'p':
        *view_kind_ptr = VIEWKIND_RGB;
        break;
    default:
        PyErr_Format(PyExc_TypeError,
                     "unrecognized view kind '%c' for argument 3", (int)ch);
        return 0;
    }
    return 1;
}

static int
_copy_mapped(PyArrayInterface *inter, SDL_Surface *surf)
{
    int pixelsize = surf->format->BytesPerPixel;
    int intsize = inter->itemsize;
    char *src = (char *)surf->pixels;
    char *dst = (char *)inter->data;
    int w = surf->w;
    int h = surf->h;
    Py_intptr_t dx_src = surf->format->BytesPerPixel;
    Py_intptr_t dy_src = surf->pitch;
    Py_intptr_t dz_src = 1;
    Py_intptr_t dx_dst = inter->strides[0];
    Py_intptr_t dy_dst = inter->strides[1];
    Py_intptr_t dz_dst = 1;
    Py_intptr_t x, y, z;

    if (inter->shape[0] != w || inter->shape[1] != h) {
        PyErr_Format(PyExc_ValueError,
                     "Expected a (%d, %d) target: got (%d, %d)",
                     w, h, (int)inter->shape[0], (int)inter->shape[1]);
        return -1;
    }
    if (intsize < pixelsize) {
        PyErr_Format(PyExc_ValueError,
                     "Expected at least a target byte size of %d: got %d",
                     pixelsize, intsize);
        return -1;
    }
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
    if (!(inter->flags & PAI_NOTSWAPPED)) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#else
    if (inter->flags & PAI_NOTSWAPPED) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#endif
    for (x = 0; x < w; ++x) {
        for (y = 0; y < h; ++y) {
            for (z = 0; z < pixelsize; ++z) {
                dst[dx_dst * x + dy_dst * y + dz_dst * z] =
                    src[dx_src * x + dy_src * y + dz_src * z];
            }
            while (z < intsize) {
                dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
                ++z;
            }
        }
    }

    return 0;
}

static int
_copy_colorplane(PyArrayInterface *inter,
                 SDL_Surface *surf,
                 _pc_view_kind_t view_kind,
                 Uint8 opaque,
                 Uint8 clear)
{
    SDL_PixelFormat *format = surf->format;
    int pixelsize = surf->format->BytesPerPixel;
    Uint32 flags = surf->flags;
    int intsize = inter->itemsize;
    char *src = (char *)surf->pixels;
    char *dst = (char *)inter->data;
    int w = surf->w;
    int h = surf->h;
    Py_intptr_t dx_src = surf->format->BytesPerPixel;
    Py_intptr_t dy_src = surf->pitch;
    Py_intptr_t dx_dst = inter->strides[0];
    Py_intptr_t dy_dst = inter->strides[1];
    Py_intptr_t dz_dst = 1;
    Py_intptr_t dz_pix;
    Py_intptr_t x, y, z;
    Uint8 r, g, b, a;
    Uint8 *element = 0;
    _pc_pixel_t pixel = { 0 };
    Uint32 colorkey;

    if (inter->shape[0] != w || inter->shape[1] != h) {
        PyErr_Format(PyExc_ValueError,
                     "Expected a (%d, %d) target: got (%d, %d)",
                     w, h, (int)inter->shape[0], (int)inter->shape[1]);
        return -1;
    }
    if (intsize < 1) {
        PyErr_Format(PyExc_ValueError,
                     "Expected at least a target byte size of 1: got %d",
                     intsize);
        return -1;
    }
    switch (view_kind) {
    case VIEWKIND_RED:
        element = &r;
        break;
    case VIEWKIND_GREEN:
        element = &g;
        break;
    case VIEWKIND_BLUE:
        element = &b;
        break;
    case VIEWKIND_ALPHA:
        element = &a;
        break;
    case VIEWKIND_COLORKEY:
        break;
    default:
        PyErr_Format(PyExc_SystemError,
                     "pygame bug in _copy_colorplane: unknown view kind %d",
                     (int)view_kind);
        return -1;
    }
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
    dz_pix = 0;
    if (!(inter->flags & PAI_NOTSWAPPED)) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#else
    dz_pix = (unsigned)(sizeof(Uint32) - pixelsize - 1);
    if (inter->flags & PAI_NOTSWAPPED) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#endif
    if (view_kind == VIEWKIND_COLORKEY && flags & SDL_SRCCOLORKEY) {
        colorkey = format->colorkey;
        for (x = 0; x < w; ++x) {
            for (y = 0; y < h; ++y) {
                for (z = 0; z < pixelsize; ++z) {
                    pixel.bytes[dz_pix + z] = src[dx_src * x + dy_src * y + z];
                }
                dst[dx_dst * x + dy_dst * y] =
                    pixel.value == colorkey ? clear : opaque;
                for (z = 1; z < intsize; ++z) {
                    dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
                }
            }
        }
    }
    else if ((view_kind != VIEWKIND_COLORKEY) &&
             (view_kind != VIEWKIND_ALPHA || flags & SDL_SRCALPHA)) {
        for (x = 0; x < w; ++x) {
            for (y = 0; y < h; ++y) {
                for (z = 0; z < pixelsize; ++z) {
                    pixel.bytes[dz_pix + z] = src[dx_src * x + dy_src * y + z];
                }
                SDL_GetRGBA(pixel.value, format, &r, &g, &b, &a);
                dst[dx_dst * x + dy_dst * y] = *element;
                for (z = 1; z < intsize; ++z) {
                    dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
                }
            }
        }
    }
    else {
        for (x = 0; x < w; ++x) {
            for (y = 0; y < h; ++y) {
                dst[dx_dst * x + dy_dst * y] = opaque;
                for (z = 1; z < intsize; ++z) {
                    dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
                }
            }
        }
    }

    return 0;
}

static int
_copy_unmapped(PyArrayInterface *inter, SDL_Surface *surf)
{
    SDL_PixelFormat *format = surf->format;
    int pixelsize = surf->format->BytesPerPixel;
    int intsize = inter->itemsize;
    char *src = (char *)surf->pixels;
    char *dst = (char *)inter->data;
    int w = surf->w;
    int h = surf->h;
    Py_intptr_t dx_src = surf->format->BytesPerPixel;
    Py_intptr_t dy_src = surf->pitch;
    Py_intptr_t dx_dst = inter->strides[0];
    Py_intptr_t dy_dst = inter->strides[1];
    Py_intptr_t dp_dst = inter->strides[2];
    Py_intptr_t dz_dst = 1;
    Py_intptr_t dz_pix;
    Py_intptr_t x, y, z;
    _pc_pixel_t pixel = { 0 };
    Uint8 r, g, b;

    if (inter->shape[0] != w ||
        inter->shape[1] != h ||
        inter->shape[2] != 3    ) {
        PyErr_Format(PyExc_ValueError,
                     "Expected a (%d, %d, 3) target: got (%d, %d, %d)",
                     w, h,
                     (int)inter->shape[0],
                     (int)inter->shape[1],
                     (int)inter->shape[2]);
        return -1;
    }
    if (intsize < 1) {
        PyErr_Format(PyExc_ValueError,
                     "Expected at least a target byte size of 1: got %d",
                     intsize);
        return -1;
    }
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
    dz_pix = 0;
    if (!(inter->flags & PAI_NOTSWAPPED)) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#else
    dz_pix = (unsigned)(sizeof(Uint32) - pixelsize - 1);
    if (inter->flags & PAI_NOTSWAPPED) {
        dst += intsize - 1;
        dz_dst = -1;
    }
#endif
    for (x = 0; x < w; ++x) {
        for (y = 0; y < h; ++y) {
            for (z = 0; z < pixelsize; ++z) {
                pixel.bytes[dz_pix + z] = src[dx_src * x + dy_src * y + z];
            }
            SDL_GetRGB(pixel.value, format, &r, &g, &b);
            dst[dx_dst * x + dy_dst * y] = r;
            for (z = 1; z < intsize; ++z) {
                dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
            }
            dst[dx_dst * x + dy_dst * y + dp_dst] = g;
            for (z = 1; z < intsize; ++z) {
                dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
            }
            dst[dx_dst * x + dy_dst * y + 2 * dp_dst] = b;
            for (z = 1; z < intsize; ++z) {
                dst[dx_dst * x + dy_dst * y + dz_dst * z] = 0;
            }
        }
    }

    return 0;
}

/*macros used to blit arrays*/
#define COPYMACRO_2D(DST, SRC)                                            \
    for (loopy = 0; loopy < sizey; ++loopy)                               \
    {                                                                     \
        DST* imgrow = (DST*)(((char*)surf->pixels)+loopy*surf->pitch);    \
        Uint8* datarow = (Uint8*)array_data + stridey * loopy;            \
        for (loopx = 0; loopx < sizex; ++loopx)                           \
            *(imgrow + loopx) = (DST)*(SRC*)(datarow + stridex * loopx);  \
    }

#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define COPYMACRO_2D_24_PIXEL(pix, data, SRC)                             \
    *pix++ = *data;                                                       \
    *pix++ = *(data + 1);                                                 \
    *pix++ = *(data + 2);
#else
#define COPYMACRO_2D_24_PIXEL(pix, data, SRC)                             \
    *pix++ = *(data + sizeof (SRC) - 1);                                  \
    *pix++ = *(data + sizeof (SRC) - 2);                                  \
    *pix++ = *(data + sizeof (SRC) - 3);
#endif

#define COPYMACRO_2D_24(SRC)                                              \
    for (loopy = 0; loopy < sizey; ++loopy)                               \
    {                                                                     \
        Uint8 *pix = ((Uint8 *)surf->pixels) + loopy * surf->pitch;       \
        Uint8 *data = (Uint8 *)array_data + stridey * loopy;              \
        Uint8 *end = pix + 3 * sizex;                                     \
        while (pix != end) {                                              \
            COPYMACRO_2D_24_PIXEL(pix, data, SRC)                         \
            data += stridex;                                              \
        }                                                                 \
    }

#define COPYMACRO_3D(DST, SRC)                                            \
    for (loopy = 0; loopy < sizey; ++loopy)                               \
    {                                                                     \
        DST *pix = (DST *)(((char *)surf->pixels) + surf->pitch * loopy); \
        char *data = array_data + stridey * loopy;                        \
        for (loopx = 0; loopx < sizex; ++loopx) {                         \
            *pix++ = (DST)((*(SRC *)(data) >> Rloss << Rshift) |          \
                (*(SRC *)(data+stridez) >> Gloss << Gshift) |             \
                (*(SRC *)(data+stridez2) >> Bloss << Bshift) |            \
                alpha);                                                   \
            data += stridex;                                              \
        }                                                                 \
    }

#define COPYMACRO_3D_24(SRC)                                            \
    for (loopy = 0; loopy < sizey; ++loopy)                             \
    {                                                                   \
        Uint8 *pix = ((Uint8*)surf->pixels) + surf->pitch * loopy;      \
        Uint8 *data = (Uint8*)array_data + stridey * loopy;             \
        Uint8 *end = pix + 3 * sizex;                                   \
        while (pix != end) {                                            \
            *pix++ = (Uint8)*(SRC*)(data + stridez_0);                  \
            *pix++ = (Uint8)*(SRC*)(data + stridez_1);                  \
            *pix++ = (Uint8)*(SRC*)(data + stridez_2);                  \
            data += stridex;                                            \
        }                                                               \
    }

static PyObject*
array_to_surface(PyObject *self, PyObject *arg)
{
    PyObject *surfobj, *arrayobj;
    PyObject *cobj;
    PyArrayInterface *inter;
    char *array_data;
    SDL_Surface* surf;
    SDL_PixelFormat* format;
    int loopx, loopy;
    int stridex, stridey, stridez=0, stridez2=0, sizex, sizey;
    int Rloss, Gloss, Bloss, Rshift, Gshift, Bshift;
    
    if (!PyArg_ParseTuple(arg, "O!O", &PySurface_Type, &surfobj, &arrayobj)) {
        return NULL;
    }
    surf = PySurface_AsSurface(surfobj);
    format = surf->format;
    
    if (Pg_GetArrayInterface(arrayobj, &cobj, &inter)) {
        return 0;
    }

    switch (inter->typekind) {
    case 'i':  /* integer */
        break;
    case 'u':  /* unsigned integer */ 
        break;
    case 'S':  /* fixed length character field */
        break;
    case 'V':  /* structured element: record */
        break;
    default:
        Py_DECREF(cobj);
        PyErr_Format(PyExc_ValueError, "unsupported array type '%c'",
                     inter->typekind);
        return NULL;
    }

    if (!(inter->nd == 2 || (inter->nd == 3 && inter->shape[2] == 3)))
        return RAISE(PyExc_ValueError, "must be a valid 2d or 3d array\n");

    if (surf->format->BytesPerPixel <= 0 || surf->format->BytesPerPixel > 4)
        return RAISE(PyExc_ValueError, "unsupport bit depth for surface");

    stridex = inter->strides[0];
    stridey = inter->strides[1];
    if (inter->nd == 3) {
        stridez = inter->strides[2];
        stridez2 = stridez*2;
    }
    sizex = inter->shape[0];
    sizey = inter->shape[1];
    Rloss = format->Rloss; Gloss = format->Gloss; Bloss = format->Bloss;
    Rshift = format->Rshift; Gshift = format->Gshift; Bshift = format->Bshift;

    /* Do any required broadcasting. */
    if (sizex == 1) {
        sizex = surf->w;
        stridex = 0;
    }
    if (sizey == 1) {
        sizey = surf->h;
        stridey = 0;
    }

    if (sizex != surf->w || sizey != surf->h) {
        Py_DECREF(cobj);
        return RAISE(PyExc_ValueError, "array must match surface dimensions");
    }
    if (!PySurface_LockBy(surfobj, arrayobj)) {
        Py_DECREF(cobj);
        return NULL;
    }
    
    array_data = (char *)inter->data;

    switch (surf->format->BytesPerPixel) {
    case 1:
        if (inter->nd == 2) {
            switch (inter->itemsize) {
            case sizeof (Uint8):
                COPYMACRO_2D(Uint8, Uint8);
                break;
            case sizeof (Uint16):
                COPYMACRO_2D(Uint8, Uint16);
                break;
            case sizeof (Uint32):
                COPYMACRO_2D(Uint8, Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_2D(Uint8, Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        else {
            Py_DECREF(cobj);
            if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                return NULL;
            }
            return RAISE(PyExc_ValueError,
                         "unsupported datatype for array\n");
        }
        break;
    case 2:
        if (inter->nd == 2) {
            switch (inter->itemsize) {
            case sizeof (Uint16):
                COPYMACRO_2D(Uint16, Uint16);
                break;
            case sizeof (Uint32):
                COPYMACRO_2D(Uint16, Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_2D(Uint16, Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        else {
            Uint16 alpha = 0;
            if (format->Amask) {
                alpha = 255 >> format->Aloss << format->Ashift;
            }
            switch (inter->itemsize) {
            case sizeof (Uint8):
                COPYMACRO_3D(Uint16, Uint8);
                break;
            case sizeof (Uint16):
                COPYMACRO_3D(Uint16, Uint16);
                break;
            case sizeof (Uint32):
                COPYMACRO_3D(Uint16, Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_3D(Uint16, Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        break;
    case 3:
        /* Assumption: The rgb components of a 24 bit pixel are in
           separate bytes.
        */
        if (inter->nd == 2) {
            switch (inter->itemsize) {
            case sizeof (Uint32):
                COPYMACRO_2D_24(Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_2D_24(Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        else {
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
            size_t stridez_0 = (Rshift ==  0 ? 0        :
                                Gshift ==  0 ? stridez  :
                                               stridez2   );
            size_t stridez_1 = (Rshift ==  8 ? 0        :
                                Gshift ==  8 ? stridez  :
                                               stridez2   );
            size_t stridez_2 = (Rshift == 16 ? 0        :
                                Gshift == 16 ? stridez  :
                                               stridez2   );
#else
            size_t stridez_2 = (Rshift ==  0 ? 0        :
                                Gshift ==  0 ? stridez  :
                                               stridez2   );
            size_t stridez_1 = (Rshift ==  8 ? 0        :
                                Gshift ==  8 ? stridez  :
                                               stridez2   );
            size_t stridez_0 = (Rshift == 16 ? 0        :
                                Gshift == 16 ? stridez  :
                                               stridez2   );
#endif
            switch (inter->itemsize) {
            case sizeof (Uint8):
                COPYMACRO_3D_24(Uint8);
                break;
            case sizeof (Uint16):
                COPYMACRO_3D_24(Uint16);
                break;
            case sizeof (Uint32):
                COPYMACRO_3D_24(Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_3D_24(Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        break;
    case 4:
        if (inter->nd == 2) {
            switch (inter->itemsize) {
            case sizeof (Uint32):
                COPYMACRO_2D(Uint32, Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_2D(Uint32, Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                            "unsupported datatype for array\n");
            }
        }
        else {
            Uint32 alpha = 0;
            if (format->Amask) {
                alpha = 255 >> format->Aloss << format->Ashift;
            }
            switch (inter->itemsize) {
            case sizeof (Uint8):
                COPYMACRO_3D(Uint32, Uint8);
                break;
            case sizeof (Uint16):
                COPYMACRO_3D(Uint32, Uint16);
                break;
            case sizeof (Uint32):
                COPYMACRO_3D(Uint32, Uint32);
                break;
            case sizeof (Uint64):
                COPYMACRO_3D(Uint32, Uint64);
                break;
            default:
                Py_DECREF(cobj);
                if (!PySurface_UnlockBy(surfobj, arrayobj)) {
                    return NULL;
                }
                return RAISE(PyExc_ValueError,
                             "unsupported datatype for array\n");
            }
        }
        break;
    default:
        Py_DECREF(cobj);
        if (!PySurface_UnlockBy(surfobj, arrayobj)) {
            return NULL;
        }
        return RAISE(PyExc_RuntimeError, "unsupported bit depth for image");
    }
    
    Py_DECREF(cobj);
    if (!PySurface_UnlockBy(surfobj, arrayobj)) {
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject *
surface_to_array(PyObject *self, PyObject *args, PyObject *kwds)
{
    PyObject *arrayobj;
    PyObject *surfobj;
    PyObject *cobj;
    _pc_view_kind_t view_kind = VIEWKIND_RGB;
    Uint8 opaque = 255;
    Uint8 clear = 0;
    SDL_Surface *surf;
    PyArrayInterface *inter;
    char *keywords[] = {"array", "surface", "kind", "opaque", "clear", 0};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!|O&BB", keywords,
                                     &arrayobj,
                                     &PySurface_Type, &surfobj,
                                     _view_kind, &view_kind,
                                     &opaque, &clear)) {
        return 0;
    }
    if (!PySurface_Lock(surfobj)) {
        return 0;
    }
    surf = PySurface_AsSurface(surfobj);
    
    if (Pg_GetArrayInterface(arrayobj, &cobj, &inter)) {
        PySurface_Unlock(surfobj);
        return 0;
    }
    if (!(inter->flags & PAI_WRITEABLE)) {
        PyErr_SetString(PyExc_ValueError, "target not writeable");
        Py_DECREF(cobj);
        PySurface_Unlock(surfobj);
        return 0;
    }
    if (!(inter->typekind == 'u' || inter->typekind == 'i')) {
        PyErr_SetString(PyExc_ValueError, "unsupported array item type");
        Py_DECREF(cobj);
        PySurface_Unlock(surfobj);
        return 0;
    }

    if (inter->nd == 2) {
        if (view_kind == VIEWKIND_RGB) {
            if (_copy_mapped(inter, surf)) {
                Py_DECREF(cobj);
                PySurface_Unlock(surfobj);
                return 0;
            }
        }
        else {
            if (_copy_colorplane(inter, surf, view_kind, opaque, clear)) {
                Py_DECREF(cobj);
                PySurface_Unlock(surfobj);
                return 0;
            }
        }
    }
    else if (inter->nd == 3) {
        if (view_kind != VIEWKIND_RGB) {
            PyErr_SetString(PyExc_ValueError,
                            "color planes only supported for 2d targets");
            Py_DECREF(cobj);
            PySurface_Unlock(surfobj);
            return 0;
        }
        if (_copy_unmapped(inter, surf)) {
            Py_DECREF(cobj);
            PySurface_Unlock(surfobj);
            return 0;
        }
    }
    else {
        Py_DECREF(cobj);
        PySurface_Unlock(surfobj);
        PyErr_Format(PyExc_ValueError,
                     "Unsupported array depth %d", (int)inter->nd);
        return 0;
    }

    Py_DECREF(cobj);
    if (!PySurface_Unlock(surfobj)) {
        return 0;
    }
    Py_RETURN_NONE;
}

static PyObject *
map_array(PyObject *self, PyObject *args)
{
#   define PIXELCOPY_MAX_DIM 10
    PyObject *src_array;
    PyObject *tar_array;
    PyObject *format_surf;
    SDL_PixelFormat *format;
    PyObject *src_cobj = 0;
    PyObject *tar_cobj = 0;
    PyArrayInterface *src_inter;
    Uint8 *src;
    int src_ndim;
    Py_intptr_t src_strides[PIXELCOPY_MAX_DIM];
    const int src_red = 0;
    int src_green;
    int src_blue;
    PyArrayInterface *tar_inter;
    Uint8 *tar;
    int ndim;
    Py_intptr_t *shape;
    Py_intptr_t *tar_strides;
    int tar_itemsize;
    int tar_byte0 = 0;
    int tar_byte1 = 0;
    int tar_byte2 = 0;
    int tar_byte3 = 0;
    int tar_byte4 = 0;
    int tar_bytes_end;
    int tar_bytes_incr = 1;
    Py_intptr_t counters[PIXELCOPY_MAX_DIM];
    int src_advances[PIXELCOPY_MAX_DIM];
    int tar_advances[PIXELCOPY_MAX_DIM];
    int dim_diff;
    int dim;
    int topdim;
    _pc_pixel_t pixel = { 0 };
    int pix_bytesize;
    int pix_byte0;
    int pix_byte1;
    int pix_byte2;
    int pix_byte3;
    int i;

    if (!PyArg_ParseTuple(args, "OOO!",
                          &tar_array, &src_array,
                          &PySurface_Type, &format_surf)) {
        return 0;
    }

    if (!PySurface_Lock(format_surf)) {
        return 0;
    }

    /* Determine array shapes and check validity
     */
    if (Pg_GetArrayInterface(tar_array, &tar_cobj, &tar_inter)) {
        goto fail;
    }
    if (!(tar_inter->flags & PAI_WRITEABLE)) {
        PyErr_SetString(PyExc_ValueError, "target not writeable");
        goto fail;
    }
    tar = (Uint8 *)tar_inter->data;
    if (tar_inter->typekind != 'u' && tar_inter->typekind != 'i') {
        PyErr_SetString(PyExc_ValueError, "expected an integer target array");
        goto fail;
    }
    ndim = tar_inter->nd;
    tar_itemsize = tar_inter->itemsize;
    shape = tar_inter->shape;
    tar_strides = tar_inter->strides;
    if (ndim < 1) {
        PyErr_SetString(PyExc_ValueError, "target array must be at least 1D");
        goto fail;
    }
    if (ndim > PIXELCOPY_MAX_DIM) {
        PyErr_Format(PyExc_ValueError,
                     "target array exceeds %d dimensions",
                     (int)PIXELCOPY_MAX_DIM);
        goto fail;
    }
    if (Pg_GetArrayInterface(src_array, &src_cobj, &src_inter)) {
        goto fail;
    }
    if (src_inter->typekind != 'u' && src_inter->typekind != 'i') {
        PyErr_SetString(PyExc_ValueError, "expected an integer source array");
        goto fail;
    }
    src = (Uint8 *)src_inter->data;
    src_ndim = src_inter->nd;
    if (src_ndim < 1) {
        PyErr_SetString(PyExc_ValueError, "source array must be at least 1D");
        goto fail;
    }
    if (src_inter->shape[src_ndim - 1] != 3) {
        PyErr_Format(PyExc_ValueError,
                     "Expected a (..., 3) source array: got (..., %d)",
                     src_inter->shape[src_ndim - 1]);
        goto fail;
    }
    if (ndim < src_ndim - 1) {
        PyErr_Format(PyExc_ValueError,
                     "%d dimensional target has too few dimensions for"
                     " %d dimensional source",
                     ndim, src_ndim);
        goto fail;
    }
    for (dim = 0; dim != ndim; ++dim) {
        src_strides[dim] = 0;
    }
    dim_diff = ndim - src_ndim + 1;
    for (dim = dim_diff; dim != ndim; ++dim) {
        if (src_inter->shape[dim - dim_diff] == 1) {
            src_strides[dim] = 0;
        }
        else if (src_inter->shape[dim - dim_diff] == shape[dim]) {
            src_strides[dim] = src_inter->strides[dim - dim_diff];
        }
        else {
            PyErr_Format(PyExc_ValueError,
                         "size mismatch between dimension %d of source and"
                         " dimension %d of target",
                         dim - dim_diff, dim);
            goto fail;
        }
    }
    for (dim = 0; dim != ndim - 1; ++dim) {
        tar_advances[dim] =
            tar_strides[dim] - shape[dim + 1] * tar_strides[dim + 1];
        src_advances[dim] =
            src_strides[dim] - shape[dim + 1] * src_strides[dim + 1];
    }

    /* Determine souce and destination pixel formats
     */
    format = PySurface_AsSurface(format_surf)->format;
    pix_bytesize = format->BytesPerPixel;
    if (tar_itemsize < pix_bytesize) {
        PyErr_SetString(PyExc_ValueError,
                        "target array itemsize is too small for pixel format");
        goto fail;
    }
    src_green = src_inter->strides[src_ndim - 1];
    src_blue = 2 * src_green;
    tar_byte4 = pix_bytesize;
    tar_bytes_end = tar_itemsize;
    switch (pix_bytesize) {

    case 1:
        break;
    case 2:
        tar_byte3 = 1;
        break;
    case 3:
        tar_byte2 = 1;
        tar_byte3 = 2;
        break;
    case 4:
        tar_byte1 = 1;
        tar_byte2 = 2;
        tar_byte3 = 3;
        break;
    default:
        PyErr_Format(PyExc_ValueError,
                     "%d bytes per pixel target format not supported",
                     pix_bytesize);
        goto fail;
    }
#if SDL_endian == SDL_lilendian
    pix_byte0 = tar_byte0;
    pix_byte1 = tar_byte1;
    pix_byte2 = tar_byte2;
    pix_byte3 = tar_byte3;

#define NEED_BYTESWAP(inter) (!((inter)->flags & PAI_NOTSWAPPED))
#else
    pix_byte0 = 3 - tar_byte0;
    pix_byte1 = 3 - tar_byte1;
    pix_byte2 = 3 - tar_byte2;
    pix_byte3 = 3 - tar_byte3;

#define NEED_BYTESWAP(inter) ((inter)->flags & PAI_NOTSWAPPED)
#endif
    if (NEED_BYTESWAP(src_inter)) {
        src += src_inter->strides[src_ndim - 1] - 1;
    }
    if (NEED_BYTESWAP(tar_inter)) {
        tar += tar_strides[ndim - 1] - 1;
        tar_byte1 = -tar_byte1;
        tar_byte2 = -tar_byte2;
        tar_byte3 = -tar_byte3;
        tar_byte4 = -tar_byte4;
        tar_bytes_end = -tar_bytes_end;
        tar_bytes_incr = -tar_bytes_incr;
    }

    /* Iterate over arrays, left index varying slowest, copying pixels
     */
    dim = 0;
    topdim = ndim - 1;
    counters[0] = shape[0];
    while (counters[0]) {
        if (!counters[dim]) {
            /* Leave loop, moving left one index
             */
            --dim;
            tar += tar_advances[dim];
            src += src_advances[dim];
            --counters[dim];
        }
        else if (dim == topdim) {
            /* Next iteration of inner most loop: copy pixel
             */
            pixel.value =
                SDL_MapRGB(format, src[src_red], src[src_green], src[src_blue]);
            tar[tar_byte0] = pixel.bytes[pix_byte0];
            tar[tar_byte1] = pixel.bytes[pix_byte1];
            tar[tar_byte2] = pixel.bytes[pix_byte2];
            tar[tar_byte3] = pixel.bytes[pix_byte3];
            for (i = tar_byte4;
                 i != tar_bytes_end;
                 i += tar_bytes_incr) {
                 tar[i] = 0;
            }
            tar += tar_strides[dim];
            src += src_strides[dim];
            --counters[dim];
        }
        else {
            /* Enter loop for next index to the right
             */
            dim += 1;
            counters[dim] = shape[dim];
        }
    }

    /* Cleanup
     */
    Py_DECREF(src_cobj);
    Py_DECREF(tar_cobj);
    if (!PySurface_Unlock(format_surf)) {
        return 0;
    }
    Py_RETURN_NONE;

  fail:
    Py_XDECREF(src_cobj);
    Py_XDECREF(tar_cobj);
    PySurface_Unlock(format_surf);
    return 0;
}

static PyObject*
make_surface (PyObject* self, PyObject* arg)
{
    PyArrayInterface *inter;
    PyObject *capsule;
    PyObject *surfobj;
    PyObject *args;
    PyObject *result;
    SDL_Surface* surf;
    int sizex, sizey, bitsperpixel;
    Uint32 rmask, gmask, bmask;

    if (Pg_GetArrayInterface(arg, &capsule, &inter)) {
        return 0;
    }
    
    if (!(inter->nd == 2 || (inter->nd == 3 && inter->shape[2] == 3))) {
        return RAISE (PyExc_ValueError, "must be a valid 2d or 3d array\n");
    }
    switch (inter->typekind) {
    case 'i':  /* integer */
        break;
    case 'u':  /* unsigned integer */ 
        break;
    case 'S':  /* fixed length character field */
        break;
    case 'V':  /* structured element: record */
        break;
    default:
        Py_DECREF(capsule);
        PyErr_Format(PyExc_ValueError, "unsupported array type '%c'",
                     inter->typekind);
        return NULL;
    }
    
    if (inter->nd == 2) {
        bitsperpixel = 8;
        rmask = 0xFF >> 6 << 5;
        gmask = 0xFF >> 5 << 2;
        bmask = 0xFF >> 6;
    }
    else {
        bitsperpixel = 32;
        rmask = 0xFF << 16;
        gmask = 0xFF << 8;
        bmask = 0xFF;
    }
    sizex = inter->shape[0];
    sizey = inter->shape[1];

    surf = SDL_CreateRGBSurface (0, sizex, sizey, bitsperpixel, rmask, gmask,
                                 bmask, 0);
    if (!surf) {
        Py_DECREF(capsule);
        return RAISE(PyExc_SDLError, SDL_GetError());
    }
    surfobj = PySurface_New(surf);
    if (!surfobj) {
        Py_DECREF(capsule);
        SDL_FreeSurface(surf);
        return 0;
    }
    
    args = Py_BuildValue("(OO)", surfobj, arg);
    if (!args) {
        Py_DECREF(capsule);
        Py_DECREF(surfobj);
        return 0;
    }
    
    result = array_to_surface(self, args);
    Py_DECREF(capsule);
    Py_DECREF(args);

    if (!result)
    {
        Py_DECREF(surfobj);
        return 0;
    }
    Py_DECREF(result);
    return surfobj;
}

static PyMethodDef _pixelcopy_methods[] =
{
    { "array_to_surface", array_to_surface,
      METH_VARARGS, DOC_PYGAMEPIXELCOPYARRAYTOSURFACE },
    { "surface_to_array", (PyCFunction)surface_to_array,
      METH_VARARGS | METH_KEYWORDS, DOC_PYGAMEPIXELCOPYSURFACETOARRAY },
    { "map_array", map_array,
      METH_VARARGS, DOC_PYGAMEPIXELCOPYMAPARRAY },
    { "make_surface", make_surface, METH_O, DOC_PYGAMEPIXELCOPYMAKESURFACE },
    { 0, 0, 0, 0}
};

MODINIT_DEFINE(pixelcopy)
{
#if PY3
    static struct PyModuleDef _module = {
        PyModuleDef_HEAD_INIT,
        "pixelcopy",
        DOC_PYGAMEPIXELCOPY,
        -1,
        _pixelcopy_methods,
        NULL, NULL, NULL, NULL
    };
#endif

    /* imported needed apis; Do this first so if there is an error
       the module is not loaded.
    */
    import_pygame_base();
    if (PyErr_Occurred()) {
        MODINIT_ERROR;
    }
    import_pygame_surface();
    if (PyErr_Occurred()) {
        MODINIT_ERROR;
    }
    import_pygame_view();
    if (PyErr_Occurred()) {
        MODINIT_ERROR;
    }

#if PY3
    return PyModule_Create(&_module);
#else
    Py_InitModule3("pixelcopy", _pixelcopy_methods, DOC_PYGAMEPIXELCOPY);
#endif
}