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

  Pete Shinners
  pete@shinners.org
*/

#define PYGAMEAPI_SURFACE_INTERNAL

#include "surface.h"
#include "pygamedocs.h"
#include "structmember.h"
#include "pgcompat.h"

int
PySurface_Blit (PyObject * dstobj, PyObject * srcobj, SDL_Rect * dstrect,
                SDL_Rect * srcrect, int the_args);

/* statics */
static PyObject *PySurface_New (SDL_Surface * info);
static PyObject *surface_new (PyTypeObject *type, PyObject *args,
                              PyObject *kwds);
static intptr_t surface_init (PySurfaceObject *self, PyObject *args,
                              PyObject *kwds);
static PyObject* surface_str (PyObject *self);
static void surface_dealloc (PyObject *self);
static void surface_cleanup (PySurfaceObject * self);
static void surface_move (Uint8 *src, Uint8 *dst, int h,
			  int span, int srcpitch, int dstpitch);

static PyObject *surf_get_at (PyObject *self, PyObject *args);
static PyObject *surf_set_at (PyObject *self, PyObject *args);
static PyObject *surf_map_rgb (PyObject *self, PyObject *args);
static PyObject *surf_unmap_rgb (PyObject *self, PyObject *arg); 
static PyObject *surf_lock (PyObject *self);
static PyObject *surf_unlock (PyObject *self);
static PyObject *surf_mustlock (PyObject *self);
static PyObject *surf_get_locked (PyObject *self);
static PyObject *surf_get_locks (PyObject *self);
static PyObject *surf_get_palette (PyObject *self);
static PyObject *surf_get_palette_at (PyObject *self, PyObject *args);
static PyObject *surf_set_palette (PyObject *self, PyObject *args);
static PyObject *surf_set_palette_at (PyObject *self, PyObject *args);
static PyObject *surf_set_colorkey (PyObject *self, PyObject *args);
static PyObject *surf_get_colorkey (PyObject *self);
static PyObject *surf_set_alpha (PyObject *self, PyObject *args);
static PyObject *surf_get_alpha (PyObject *self);
static PyObject *surf_copy (PyObject *self);
static PyObject *surf_convert (PyObject *self, PyObject *args);
static PyObject *surf_convert_alpha (PyObject *self, PyObject *args);
static PyObject *surf_set_clip (PyObject *self, PyObject *args);
static PyObject *surf_get_clip (PyObject *self);
static PyObject *surf_blit (PyObject *self, PyObject *args, PyObject *keywds);
static PyObject *surf_fill (PyObject *self, PyObject *args, PyObject *keywds);
static PyObject *surf_scroll (PyObject *self,
			       PyObject *args, PyObject *keywds);
static PyObject *surf_get_abs_offset (PyObject *self);
static PyObject *surf_get_abs_parent (PyObject *self);
static PyObject *surf_get_bitsize (PyObject *self);
static PyObject *surf_get_bytesize (PyObject *self);
static PyObject *surf_get_flags (PyObject *self);
static PyObject *surf_get_height (PyObject *self);
static PyObject *surf_get_pitch (PyObject *self);
static PyObject *surf_get_rect (PyObject *self, PyObject *args,
                                PyObject *kwargs);
static PyObject *surf_get_width (PyObject *self);
static PyObject *surf_get_shifts (PyObject *self);
static PyObject *surf_set_shifts (PyObject *self, PyObject *args);
static PyObject *surf_get_size (PyObject *self);
static PyObject *surf_get_losses (PyObject *self);
static PyObject *surf_get_masks (PyObject *self);
static PyObject *surf_set_masks (PyObject *self, PyObject *args);
static PyObject *surf_get_offset (PyObject *self);
static PyObject *surf_get_parent (PyObject *self);
static PyObject *surf_subsurface (PyObject *self, PyObject *args);
static PyObject *surf_get_buffer (PyObject *self);
static PyObject *surf_get_bounding_rect (PyObject *self, PyObject *args,
                                          PyObject *kwargs);

static struct PyMethodDef surface_methods[] =
{
    { "get_at", surf_get_at, METH_VARARGS, DOC_SURFACEGETAT },
    { "set_at", surf_set_at, METH_VARARGS, DOC_SURFACESETAT },
    { "map_rgb", surf_map_rgb, METH_VARARGS, DOC_SURFACEMAPRGB },
    { "unmap_rgb", surf_unmap_rgb, METH_O, DOC_SURFACEUNMAPRGB },

    { "get_palette", (PyCFunction) surf_get_palette, METH_NOARGS,
      DOC_SURFACEGETPALETTE },
    { "get_palette_at", surf_get_palette_at, METH_VARARGS,
      DOC_SURFACEGETPALETTEAT },
    { "set_palette", surf_set_palette, METH_VARARGS, DOC_SURFACESETPALETTE },
    { "set_palette_at", surf_set_palette_at, METH_VARARGS,
      DOC_SURFACESETPALETTEAT },

    { "lock", (PyCFunction) surf_lock, METH_NOARGS, DOC_SURFACELOCK },
    { "unlock", (PyCFunction) surf_unlock, METH_NOARGS, DOC_SURFACEUNLOCK },
    { "mustlock", (PyCFunction) surf_mustlock, METH_NOARGS,
      DOC_SURFACEMUSTLOCK },
    { "get_locked", (PyCFunction) surf_get_locked, METH_NOARGS,
      DOC_SURFACEGETLOCKED },
    { "get_locks", (PyCFunction) surf_get_locks, METH_NOARGS,
      DOC_SURFACEGETLOCKS },

    { "set_colorkey", surf_set_colorkey, METH_VARARGS, DOC_SURFACESETCOLORKEY },
    { "get_colorkey", (PyCFunction) surf_get_colorkey, METH_NOARGS,
      DOC_SURFACEGETCOLORKEY },
    { "set_alpha", surf_set_alpha, METH_VARARGS, DOC_SURFACESETALPHA },
    { "get_alpha", (PyCFunction) surf_get_alpha, METH_NOARGS,
      DOC_SURFACEGETALPHA },

    { "copy", (PyCFunction) surf_copy, METH_NOARGS, DOC_SURFACECOPY },
    { "__copy__", (PyCFunction) surf_copy, METH_NOARGS, DOC_SURFACECOPY },
    { "convert", surf_convert, METH_VARARGS, DOC_SURFACECONVERT },
    { "convert_alpha", surf_convert_alpha, METH_VARARGS,
      DOC_SURFACECONVERTALPHA },

    { "set_clip", surf_set_clip, METH_VARARGS, DOC_SURFACESETCLIP },
    { "get_clip", (PyCFunction) surf_get_clip, METH_NOARGS,
      DOC_SURFACEGETCLIP },

    { "fill", (PyCFunction) surf_fill, METH_VARARGS | METH_KEYWORDS,
      DOC_SURFACEFILL },
    { "blit", (PyCFunction) surf_blit, METH_VARARGS | METH_KEYWORDS,
      DOC_SURFACEBLIT },

    { "scroll", (PyCFunction) surf_scroll, METH_VARARGS | METH_KEYWORDS,
      DOC_SURFACESCROLL },

    { "get_flags", (PyCFunction) surf_get_flags, METH_NOARGS,
      DOC_SURFACEGETFLAGS },
    { "get_size", (PyCFunction) surf_get_size, METH_NOARGS,
      DOC_SURFACEGETSIZE },
    { "get_width", (PyCFunction) surf_get_width, METH_NOARGS,
      DOC_SURFACEGETWIDTH },
    { "get_height", (PyCFunction) surf_get_height, METH_NOARGS,
      DOC_SURFACEGETHEIGHT },
    { "get_rect", (PyCFunction) surf_get_rect, METH_VARARGS | METH_KEYWORDS,
      DOC_SURFACEGETRECT },
    { "get_pitch", (PyCFunction) surf_get_pitch, METH_NOARGS,
      DOC_SURFACEGETPITCH },
    { "get_bitsize", (PyCFunction) surf_get_bitsize, METH_NOARGS,
      DOC_SURFACEGETBITSIZE },
    { "get_bytesize", (PyCFunction) surf_get_bytesize, METH_NOARGS,
      DOC_SURFACEGETBYTESIZE },
    { "get_masks", (PyCFunction) surf_get_masks, METH_NOARGS,
      DOC_SURFACEGETMASKS },
    { "get_shifts", (PyCFunction) surf_get_shifts, METH_NOARGS,
      DOC_SURFACEGETSHIFTS },
    { "set_masks", (PyCFunction) surf_set_masks, METH_VARARGS,
      DOC_SURFACESETMASKS },
    { "set_shifts", (PyCFunction) surf_set_shifts, METH_VARARGS,
      DOC_SURFACESETSHIFTS },

    { "get_losses", (PyCFunction) surf_get_losses, METH_NOARGS,
      DOC_SURFACEGETLOSSES },

    { "subsurface", surf_subsurface, METH_VARARGS, DOC_SURFACESUBSURFACE },
    { "get_offset", (PyCFunction) surf_get_offset, METH_NOARGS,
      DOC_SURFACEGETOFFSET },
    { "get_abs_offset", (PyCFunction) surf_get_abs_offset, METH_NOARGS,
      DOC_SURFACEGETABSOFFSET },
    { "get_parent", (PyCFunction) surf_get_parent, METH_NOARGS,
      DOC_SURFACEGETPARENT },
    { "get_abs_parent", (PyCFunction) surf_get_abs_parent, METH_NOARGS,
      DOC_SURFACEGETABSPARENT },
    { "get_bounding_rect", (PyCFunction) surf_get_bounding_rect,
      METH_VARARGS | METH_KEYWORDS,
      DOC_SURFACEGETBOUNDINGRECT},
    { "get_buffer", (PyCFunction) surf_get_buffer, METH_NOARGS,
      DOC_SURFACEGETBUFFER},

    { NULL, NULL, 0, NULL }
};

static PyTypeObject PySurface_Type =
{
    TYPE_HEAD (NULL, 0)
    "pygame.Surface",          /* name */
    sizeof (PySurfaceObject),  /* basic size */
    0,                         /* itemsize */
    surface_dealloc,           /* dealloc */
    0,                         /* print */
    NULL,                      /* getattr */
    NULL,                      /* setattr */
    NULL,                      /* compare */
    surface_str,               /* repr */
    NULL,                      /* as_number */
    NULL,                      /* as_sequence */
    NULL,                      /* as_mapping */
    (hashfunc) NULL,           /* hash */
    (ternaryfunc) NULL,        /* call */
    (reprfunc) NULL,           /* str */
    0,
    0L, 0L,
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /* tp_flags */
    DOC_PYGAMESURFACE,         /* Documentation string */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    offsetof (PySurfaceObject, weakreflist),   /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    surface_methods,           /* tp_methods */
    0,                         /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc) surface_init,   /* tp_init */
    0,                         /* tp_alloc */
    surface_new,               /* tp_new */
};

#define PySurface_Check(x) ((x)->ob_type == &PySurface_Type)

static PyObject*
PySurface_New (SDL_Surface *s)
{
    PySurfaceObject *self;

    if (!s)
        return RAISE (PyExc_SDLError, SDL_GetError ());

    self = (PySurfaceObject *)
        PySurface_Type.tp_new (&PySurface_Type, NULL, NULL);

    if (self)
        self->surf = s;

    return (PyObject *) self;
}

static PyObject*
surface_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PySurfaceObject *self;

    self = (PySurfaceObject *) type->tp_alloc (type, 0);
    if (self)
    {
        self->surf = NULL;
        self->subsurface = NULL;
        self->weakreflist = NULL;
        self->dependency = NULL;
        self->locklist = NULL;
    }
    return (PyObject *) self;
}

/* surface object internals */
static void 
surface_cleanup (PySurfaceObject *self)
{
    if (self->surf)
    {
        if (!(self->surf->flags & SDL_HWSURFACE) ||
            SDL_WasInit (SDL_INIT_VIDEO))
        {
            /* unsafe to free hardware surfaces without video init */
            /* i question SDL's ability to free a locked hardware surface */
            SDL_FreeSurface (self->surf);
        }
        self->surf = NULL;
    }
    if (self->subsurface)
    {
        Py_XDECREF (self->subsurface->owner);
        PyMem_Del (self->subsurface);
        self->subsurface = NULL;
    }
    if (self->dependency)
    {
        Py_DECREF (self->dependency);
        self->dependency = NULL;
    }

    if (self->locklist)
    {
        Py_DECREF (self->locklist);
        self->locklist = NULL;
    }
}

static void 
surface_dealloc (PyObject *self)
{
    if (((PySurfaceObject *) self)->weakreflist)
        PyObject_ClearWeakRefs (self);
    surface_cleanup ((PySurfaceObject *) self);
    self->ob_type->tp_free (self);
}

static PyObject*
surface_str (PyObject *self)
{
    char str[1024];
    SDL_Surface *surf = PySurface_AsSurface (self);
    const char *type;

    if (surf)
    {
        type = (surf->flags & SDL_HWSURFACE) ? "HW" : "SW";
        sprintf (str, "<Surface(%dx%dx%d %s)>", surf->w,
                 surf->h, surf->format->BitsPerPixel, type);
    }
    else
    {
        strcpy (str, "<Surface(Dead Display)>");
    }

    return Text_FromUTF8 (str);
}

static intptr_t 
surface_init (PySurfaceObject *self, PyObject *args, PyObject *kwds)
{
    Uint32 flags = 0;
    int width, height;
    PyObject *depth = NULL, *masks = NULL, *size = NULL;
    int bpp;
    Uint32 Rmask, Gmask, Bmask, Amask;
    SDL_Surface *surface;
    SDL_PixelFormat default_format, *format;
    int length;

    char *kwids[] = { "size", "flags", "depth", "masks", NULL };
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOO", kwids, &size, &flags, &depth, &masks))
       return -1;

    if (PySequence_Check (size) && (length = PySequence_Length (size)) == 2) {

        if ( (!IntFromObjIndex (size, 0, &width)) || 
             (!IntFromObjIndex (size, 1, &height)) ) {
            RAISE (PyExc_ValueError, "size needs to be (int width, int height)");
            return -1;
        }
    } else {
        RAISE (PyExc_ValueError, "size needs to be (int width, int height)");
        return -1;
    }

    if (width < 0 || height < 0)
    {
        RAISE (PyExc_SDLError, "Invalid resolution for Surface");
        return -1;
    }

    surface_cleanup (self);

    if (depth && masks)        /* all info supplied, most errorchecking
                                * needed */
    {
        if (PySurface_Check (depth)) {
            RAISE (PyExc_ValueError, "cannot pass surface for depth and color masks");
            return -1;
        }
        if (!IntFromObj (depth, &bpp)) {
            RAISE (PyExc_ValueError, "invalid bits per pixel depth argument");
            return -1;
        }
        if (!PySequence_Check (masks) || PySequence_Length (masks) != 4) {
            RAISE (PyExc_ValueError, "masks argument must be sequence of four numbers");
            return -1;
        }
        if (!UintFromObjIndex (masks, 0, &Rmask) ||
            !UintFromObjIndex (masks, 1, &Gmask) ||
            !UintFromObjIndex (masks, 2, &Bmask) ||
            !UintFromObjIndex (masks, 3, &Amask)) {
            RAISE (PyExc_ValueError, "invalid mask values in masks sequence");
            return -1;
        }
    }
    else if (depth && PyNumber_Check (depth))  /* use default masks */
    {
        if (!IntFromObj (depth, &bpp)) {
            RAISE (PyExc_ValueError, "invalid bits per pixel depth argument");
            return -1;
        }
        if (flags & SDL_SRCALPHA)
        {
            switch (bpp)
            {
            case 16:
                Rmask = 0xF << 8;
                Gmask = 0xF << 4;
                Bmask = 0xF;
                Amask = 0xF << 12;
                break;
            case 32:
                Rmask = 0xFF << 16;
                Gmask = 0xFF << 8;
                Bmask = 0xFF;
                Amask = 0xFF << 24;
                break;
            default:
                RAISE (PyExc_ValueError, "no standard masks exist for given bitdepth with alpha");
                return -1;
            }
        }
        else
        {
            Amask = 0;
            switch (bpp)
            {
            case 8:
                Rmask = 0xFF >> 6 << 5;
                Gmask = 0xFF >> 5 << 2;
                Bmask = 0xFF >> 6;
                break;
            case 12:
                Rmask = 0xFF >> 4 << 8;
                Gmask = 0xFF >> 4 << 4;
                Bmask = 0xFF >> 4;
                break;
            case 15:
                Rmask = 0xFF >> 3 << 10;
                Gmask = 0xFF >> 3 << 5;
                Bmask = 0xFF >> 3;
                break;
            case 16:
                Rmask = 0xFF >> 3 << 11;
                Gmask = 0xFF >> 2 << 5;
                Bmask = 0xFF >> 3;
                break;
            case 24:
            case 32:
                Rmask = 0xFF << 16;
                Gmask = 0xFF << 8;
                Bmask = 0xFF;
                break;
            default:
                RAISE (PyExc_ValueError, "nonstandard bit depth given");
                return -1;
            }
        }
    }
    else /* no depth or surface */
    {
        SDL_PixelFormat* pix;
        if (depth && PySurface_Check (depth))
            pix = ((PySurfaceObject*) depth)->surf->format;
        else if (SDL_GetVideoSurface ())
            pix = SDL_GetVideoSurface ()->format;
        else if (SDL_WasInit (SDL_INIT_VIDEO))
            pix = SDL_GetVideoInfo ()->vfmt;
        else
        {
            pix = &default_format;
            pix->BitsPerPixel = 32;
            pix->Amask = 0;
            pix->Rmask = 0xFF0000;
            pix->Gmask = 0xFF00;
            pix->Bmask = 0xFF;
        }
        bpp = pix->BitsPerPixel;

        if (flags & SDL_SRCALPHA)
        {
            switch (bpp)
            {
            case 16:
                Rmask = 0xF << 8;
                Gmask = 0xF << 4;
                Bmask = 0xF;
                Amask = 0xF << 12;
                break;
            case 24:
                bpp = 32;
                // we automatically step up to 32 if video is 24, fall through to case below
            case 32:
                Rmask = 0xFF << 16;
                Gmask = 0xFF << 8;
                Bmask = 0xFF;
                Amask = 0xFF << 24;
                break;
            default:
                RAISE (PyExc_ValueError, "no standard masks exist for given bitdepth with alpha");
                return -1;
            }
        } else {
            Rmask = pix->Rmask;
            Gmask = pix->Gmask;
            Bmask = pix->Bmask;
            Amask = pix->Amask;
        }

    }

    surface = SDL_CreateRGBSurface (flags, width, height, bpp, Rmask, Gmask,
                                    Bmask, Amask);

    if (!surface)
    {
        RAISE (PyExc_SDLError, SDL_GetError ());
        return -1;
    }
    
    if (masks)
    {
	/* Confirm the surface was created correctly (masks were valid).
	   Also ensure that 24 and 32 bit surfaces have 8 bit fields
	   (no losses).
	*/
	format = surface->format;
	Rmask = (0xFF >> format->Rloss) << format->Rshift;
	Gmask = (0xFF >> format->Gloss) << format->Gshift;
	Bmask = (0xFF >> format->Bloss) << format->Bshift;
	Amask = (0xFF >> format->Aloss) << format->Ashift;
	if (format->Rmask != Rmask ||
	    format->Gmask != Gmask ||
	    format->Bmask != Bmask ||
	    format->Amask != Amask ||
	    (format->BytesPerPixel >= 3 &&
	     (format->Rloss || format->Gloss || format->Bloss ||
	      (surface->flags & SDL_SRCALPHA ?
	       format->Aloss : format->Aloss != 8))))
	{
	    SDL_FreeSurface (surface);
	    RAISE (PyExc_ValueError, "Invalid mask values");
	    return -1;
	}
    }

    if (surface)
    {
        self->surf = surface;
        self->subsurface = NULL;
    }
    
    return 0;
}

/* surface object methods */
static PyObject*
surf_get_at (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_PixelFormat *format = surf->format;
    Uint8 *pixels = (Uint8 *) surf->pixels;
    int x, y;
    Uint32 color;
    Uint8 *pix;
    Uint8 rgba[4];

    if (!PyArg_ParseTuple (args, "(ii)", &x, &y))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (x < 0 || x >= surf->w || y < 0 || y >= surf->h)
        return RAISE (PyExc_IndexError, "pixel index out of range");

    if (format->BytesPerPixel < 1 || format->BytesPerPixel > 4)
        return RAISE (PyExc_RuntimeError, "invalid color depth for surface");

    if (!PySurface_Lock (self))
        return NULL;

    pixels = (Uint8 *) surf->pixels;

    switch (format->BytesPerPixel)
    {
    case 1:
        color = (Uint32)*((Uint8 *) pixels + y * surf->pitch + x);
        break;
    case 2:
        color = (Uint32)*((Uint16 *) (pixels + y * surf->pitch) + x);
        break;
    case 3:
        pix = ((Uint8 *) (pixels + y * surf->pitch) + x * 3);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
        color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16);
#else
        color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16);
#endif
        break;
    default:                  /* case 4: */
        color = *((Uint32 *) (pixels + y * surf->pitch) + x);
        break;
    }
    if (!PySurface_Unlock (self))
        return NULL;

    SDL_GetRGBA (color, format, rgba, rgba+1, rgba+2, rgba+3);
    return PyColor_New (rgba);
}

static PyObject*
surf_set_at (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_PixelFormat *format = surf->format;
    Uint8 *pixels;
    int x, y;
    Uint32 color;
    Uint8 rgba[4] = {0, 0, 0, 0 };
    PyObject *rgba_obj;
    Uint8 *byte_buf;

    if (!PyArg_ParseTuple (args, "(ii)O", &x, &y, &rgba_obj))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (format->BytesPerPixel < 1 || format->BytesPerPixel > 4)
        return RAISE (PyExc_RuntimeError, "invalid color depth for surface");

    if (x < surf->clip_rect.x || x >= surf->clip_rect.x + surf->clip_rect.w ||
        y < surf->clip_rect.y || y >= surf->clip_rect.y + surf->clip_rect.h)
    {
        /* out of clip area */
        Py_RETURN_NONE;
    }

    if (PyInt_Check (rgba_obj))
    {
        color = (Uint32) PyInt_AsLong (rgba_obj);
        if (PyErr_Occurred () && (Sint32) color == -1)
            return RAISE (PyExc_TypeError, "invalid color argument");
    }
    else if (PyLong_Check (rgba_obj))
    {
        color = (Uint32) PyLong_AsUnsignedLong (rgba_obj);
        if (PyErr_Occurred () && (Sint32) color == -1)
            return RAISE (PyExc_TypeError, "invalid color argument");
    }
    else if (RGBAFromColorObj (rgba_obj, rgba))
        color = SDL_MapRGBA (surf->format, rgba[0], rgba[1], rgba[2], rgba[3]);
    else
        return RAISE (PyExc_TypeError, "invalid color argument");

    if (!PySurface_Lock (self))
        return NULL;
    pixels = (Uint8 *) surf->pixels;

    switch (format->BytesPerPixel)
    {
    case 1:
        *((Uint8 *) pixels + y * surf->pitch + x) = (Uint8) color;
        break;
    case 2:
        *((Uint16 *) (pixels + y * surf->pitch) + x) = (Uint16) color;
        break;
    case 3:
        byte_buf = (Uint8 *) (pixels + y * surf->pitch) + x * 3;
#if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
        *(byte_buf + (format->Rshift >> 3)) = (Uint8) (color >> 16);
        *(byte_buf + (format->Gshift >> 3)) = (Uint8) (color >> 8);
        *(byte_buf + (format->Bshift >> 3)) = (Uint8) color;
#else
        *(byte_buf + 2 - (format->Rshift >> 3)) = (Uint8) (color >> 16);
        *(byte_buf + 2 - (format->Gshift >> 3)) = (Uint8) (color >> 8);
        *(byte_buf + 2 - (format->Bshift >> 3)) = (Uint8) color;
#endif
        break;
    default:                  /* case 4: */
        *((Uint32 *) (pixels + y * surf->pitch) + x) = color;
        break;
    }

    if (!PySurface_Unlock (self))
        return NULL;
    Py_RETURN_NONE;
}

static PyObject*
surf_map_rgb (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    Uint8 rgba[4];
    int color;

    if (!RGBAFromColorObj (args, rgba))
        return RAISE (PyExc_TypeError, "Invalid RGBA argument");
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    color = SDL_MapRGBA (surf->format, rgba[0], rgba[1], rgba[2], rgba[3]);
    return PyInt_FromLong (color);
}

static PyObject*
surf_unmap_rgb (PyObject *self, PyObject *arg)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    Uint32 col;
    Uint8  rgba[4];

    col = (Uint32)PyInt_AsLong (arg);
    if (col == (Uint32) -1 && PyErr_Occurred()) {
	PyErr_Clear();
	return RAISE (PyExc_TypeError, "unmap_rgb expects 1 number argument");
    }
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    SDL_GetRGBA (col, surf->format, rgba, rgba+1, rgba+2, rgba+3);

    return PyColor_New (rgba);
}

static PyObject*
surf_lock (PyObject *self)
{
    if (!PySurface_Lock (self))
        return NULL;
    Py_RETURN_NONE;
}

static PyObject*
surf_unlock (PyObject *self)
{
    PySurface_Unlock (self);
    Py_RETURN_NONE;
}

static PyObject*
surf_mustlock (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    return PyInt_FromLong (SDL_MUSTLOCK (surf) ||
        ((PySurfaceObject *) self)->subsurface);
}

static PyObject*
surf_get_locked (PyObject *self)
{
    PySurfaceObject *surf = (PySurfaceObject *) self;

    if (surf->locklist && PyList_Size (surf->locklist) > 0)
        Py_RETURN_TRUE;
    Py_RETURN_FALSE;
}

static PyObject*
surf_get_locks (PyObject *self)
{
    PySurfaceObject *surf = (PySurfaceObject *) self;
    Py_ssize_t len, i = 0;
    PyObject *tuple, *tmp;
    if (!surf->locklist)
        return PyTuple_New (0);

    len = PyList_Size (surf->locklist);
    tuple = PyTuple_New (len);
    if (!tuple)
        return NULL;
    
    for (i = 0; i < len; i++)
    {
        tmp = PyWeakref_GetObject (PyList_GetItem (surf->locklist, i));
        Py_INCREF (tmp);
        PyTuple_SetItem (tuple, i, tmp);
    }
    return tuple;
}

static PyObject*
surf_get_palette (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_Palette *pal = surf->format->palette;
    PyObject *list;
    int i;
    PyObject *color;
    SDL_Color *c;
    Uint8 rgba[4] = {0, 0, 0, 255};

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (!pal)
        return RAISE (PyExc_SDLError, "Surface has no palette to get\n");

    list = PyTuple_New (pal->ncolors);
    if (!list)
        return NULL;

    for (i = 0; i < pal->ncolors; i++)
    {
        c = &pal->colors[i];
        rgba[0] = c->r;
        rgba[1] = c->g;
        rgba[2] = c->b;
        color = PyColor_NewLength (rgba, 3);

        if (!color)
        {
            Py_DECREF (list);
            return NULL;
        }
        PyTuple_SET_ITEM (list, i, color);
    }

    return list;
}

static PyObject*
surf_get_palette_at (PyObject * self, PyObject * args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_Palette *pal = surf->format->palette;
    SDL_Color *c;
    int _index;
    Uint8 rgba[4];

    if (!PyArg_ParseTuple (args, "i", &_index))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (!pal)
        return RAISE (PyExc_SDLError, "Surface has no palette to set\n");
    if (_index >= pal->ncolors || _index < 0)
        return RAISE (PyExc_IndexError, "index out of bounds");

    c = &pal->colors[_index];
    rgba[0] = c->r;
    rgba[1] = c->g;
    rgba[2] = c->b;
    rgba[3] = 255;

    return PyColor_NewLength (rgba, 3);
}

static PyObject*
surf_set_palette (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_Palette *pal = surf->format->palette;
    SDL_Color *colors;
    PyObject *list, *item;
    int i, len;
    Uint8 rgba[4];
    int ecode;

    if (!PyArg_ParseTuple (args, "O", &list))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    if (!PySequence_Check (list))
        return RAISE (PyExc_ValueError, "Argument must be a sequence type");

    if (!pal)
        return RAISE (PyExc_SDLError, "Surface has no palette\n");

    if (!SDL_WasInit (SDL_INIT_VIDEO))
        return RAISE (PyExc_SDLError,
                      "cannot set palette without pygame.display initialized");

    len = MIN (pal->ncolors, PySequence_Length (list));

    colors = (SDL_Color *) malloc (len * sizeof (SDL_Color));
    if (!colors)
        return NULL;

    for (i = 0; i < len; i++)
    {
        item = PySequence_GetItem (list, i);

        ecode = RGBAFromObj (item, rgba);
        Py_DECREF (item);
        if (!ecode)
        {
            free (colors);
            return RAISE (PyExc_ValueError,
                          "takes a sequence of integers of RGB");
        }
        if (rgba[3] != 255)
        {
            free (colors);
            return RAISE (PyExc_ValueError,
                          "takes an alpha value of 255");
        }
        colors[i].r = (unsigned char) rgba[0];
        colors[i].g = (unsigned char) rgba[1];
        colors[i].b = (unsigned char) rgba[2];
    }

    SDL_SetColors (surf, colors, 0, len);
    free (colors);
    Py_RETURN_NONE;
}

static PyObject*
surf_set_palette_at (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_Palette *pal = surf->format->palette;
    SDL_Color color;
    int _index;
    PyObject *color_obj;
    Uint8 rgba[4];

    if (!PyArg_ParseTuple (args, "iO", &_index, &color_obj))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (!RGBAFromObj (color_obj, rgba))
    {
        return RAISE (PyExc_ValueError,
                      "takes a sequence of integers of RGB for argument 2");
    }
    
    if (!pal)
    {
        PyErr_SetString (PyExc_SDLError, "Surface is not palettized\n");
        return NULL;
    }

    if (_index >= pal->ncolors || _index < 0)
    {
        PyErr_SetString (PyExc_IndexError, "index out of bounds");
        return NULL;
    }

    if (!SDL_WasInit (SDL_INIT_VIDEO))
        return RAISE (PyExc_SDLError,
                      "cannot set palette without pygame.display initialized");

    color.r = rgba[0];
    color.g = rgba[1];
    color.b = rgba[2];

    SDL_SetColors (surf, &color, _index, 1);

    Py_RETURN_NONE;
}

static PyObject*
surf_set_colorkey (PyObject *self, PyObject *args)
{
    SDL_Surface    *surf = PySurface_AsSurface (self);
    Uint32 flags = 0, color = 0;
    PyObject *rgba_obj = NULL;
    Uint8 rgba[4];
    int result, hascolor = 0;

    if (!PyArg_ParseTuple (args, "|Oi", &rgba_obj, &flags))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (rgba_obj && rgba_obj != Py_None)
    {
        if (PyInt_Check (rgba_obj))
        {
            color = (Uint32) PyInt_AsLong (rgba_obj);
            if (PyErr_Occurred () && (Sint32) color == -1)
                return RAISE (PyExc_TypeError, "invalid color argument");
        }
        else if (PyLong_Check (rgba_obj))
        {
            color = (Uint32) PyLong_AsUnsignedLong (rgba_obj);
            if (PyErr_Occurred () && (Sint32) color == -1)
                return RAISE (PyExc_TypeError, "invalid color argument");
        }
        else if (RGBAFromColorObj (rgba_obj, rgba))
        {
            color = SDL_MapRGBA (surf->format, rgba[0], rgba[1], rgba[2],
                rgba[3]);
        }
        else
            return RAISE (PyExc_TypeError, "invalid color argument");
        hascolor = 1;
    }
    if (hascolor)
        flags |= SDL_SRCCOLORKEY;

    PySurface_Prep (self);
    result = SDL_SetColorKey (surf, flags, color);
    PySurface_Unprep (self);

    if (result == -1)
        return RAISE (PyExc_SDLError, SDL_GetError ());

    Py_RETURN_NONE;
}

static PyObject*
surf_get_colorkey (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    Uint8 r, g, b, a;

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (!(surf->flags & SDL_SRCCOLORKEY))
        Py_RETURN_NONE;

    SDL_GetRGBA (surf->format->colorkey, surf->format, &r, &g, &b, &a);
    return Py_BuildValue ("(bbbb)", r, g, b, a);
}

static PyObject*
surf_set_alpha (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    Uint32 flags = 0;
    PyObject *alpha_obj = NULL, *intobj = NULL;
    Uint8 alpha;
    int result, alphaval = 255, hasalpha = 0;

    if (!PyArg_ParseTuple (args, "|Oi", &alpha_obj, &flags))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (alpha_obj && alpha_obj != Py_None)
    {
        if (PyNumber_Check (alpha_obj) && (intobj = PyNumber_Int (alpha_obj)))
        {
            if (PyInt_Check (intobj))
            {
                alphaval = (int) PyInt_AsLong (intobj);
                Py_DECREF (intobj);
            }
            else
                return RAISE (PyExc_TypeError, "invalid alpha argument");
        }
        else
            return RAISE (PyExc_TypeError, "invalid alpha argument");
        hasalpha = 1;
    }
    if (hasalpha)
        flags |= SDL_SRCALPHA;

    if (alphaval > 255)
        alpha = 255;
    else if (alphaval < 0)
        alpha = 0;
    else
        alpha = (Uint8) alphaval;

    PySurface_Prep (self);
    result = SDL_SetAlpha (surf, flags, alpha);
    PySurface_Unprep (self);

    if (result == -1)
        return RAISE (PyExc_SDLError, SDL_GetError ());

    Py_RETURN_NONE;
}

static PyObject*
surf_get_alpha (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_SRCALPHA)
        return PyInt_FromLong (surf->format->alpha);

    Py_RETURN_NONE;
}

static PyObject*
surf_copy (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    PyObject *final;
    SDL_Surface *newsurf;

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot copy opengl display");

    PySurface_Prep (self);
    newsurf = SDL_ConvertSurface (surf, surf->format, surf->flags);
    PySurface_Unprep (self);

    final = PySurface_New (newsurf);
    if (!final)
        SDL_FreeSurface (newsurf);
    return final;
}

static PyObject*
surf_convert (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    PyObject *final;
    PyObject *argobject = NULL;
    SDL_Surface *src;
    SDL_Surface *newsurf;
    Uint32 flags = -1;

    if (!SDL_WasInit (SDL_INIT_VIDEO))
        return RAISE (PyExc_SDLError,
                      "cannot convert without pygame.display initialized");

    if (!PyArg_ParseTuple (args, "|Oi", &argobject, &flags))
        return NULL;

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot convert opengl display");

    PySurface_Prep (self);
    if (argobject)
    {
        if (PySurface_Check (argobject))
        {
            src = PySurface_AsSurface (argobject);
            flags = src->flags |
                (surf->flags & (SDL_SRCCOLORKEY | SDL_SRCALPHA));
            newsurf = SDL_ConvertSurface (surf, src->format, flags);
        }
        else
        {
            int bpp;
            SDL_PixelFormat format;

            memcpy (&format, surf->format, sizeof (format));
            if (IntFromObj (argobject, &bpp))
            {
                Uint32 Rmask, Gmask, Bmask, Amask;

                if (flags != -1 && flags & SDL_SRCALPHA)
                {
                    switch (bpp)
                    {
                    case 16:
                        Rmask = 0xF << 8;
                        Gmask = 0xF << 4;
                        Bmask = 0xF;
                        Amask = 0xF << 12;
                        break;
                    case 32:
                        Rmask = 0xFF << 16;
                        Gmask = 0xFF << 8;
                        Bmask = 0xFF;
                        Amask = 0xFF << 24;
                        break;
                    default:
                        return RAISE (PyExc_ValueError,
                                      "no standard masks exist for given "
                                      "bitdepth with alpha");
                    }
                }
                else
                {
                    Amask = 0;
                    switch (bpp)
                    {
                    case 8:
                        Rmask = 0xFF >> 6 << 5;
                        Gmask = 0xFF >> 5 << 2;
                        Bmask = 0xFF >> 6;
                        break;
                    case 12:
                        Rmask = 0xFF >> 4 << 8;
                        Gmask = 0xFF >> 4 << 4;
                        Bmask = 0xFF >> 4;
                        break;
                    case 15:
                        Rmask = 0xFF >> 3 << 10;
                        Gmask = 0xFF >> 3 << 5;
                        Bmask = 0xFF >> 3;
                        break;
                    case 16:
                        Rmask = 0xFF >> 3 << 11;
                        Gmask = 0xFF >> 2 << 5;
                        Bmask = 0xFF >> 3;
                        break;
                    case 24:
                    case 32:
                        Rmask = 0xFF << 16;
                        Gmask = 0xFF << 8;
                        Bmask = 0xFF;
                        break;
                    default:
                        return RAISE (PyExc_ValueError,
                                      "nonstandard bit depth given");
                    }
                }
                format.Rmask = Rmask;
                format.Gmask = Gmask;
                format.Bmask = Bmask;
                format.Amask = Amask;
            }
            else if (PySequence_Check (argobject) &&
                     PySequence_Size (argobject) == 4)
            {
                Uint32 mask;

                if (!UintFromObjIndex (argobject, 0, &format.Rmask) ||
                    !UintFromObjIndex (argobject, 1, &format.Gmask) ||
                    !UintFromObjIndex (argobject, 2, &format.Bmask) ||
                    !UintFromObjIndex (argobject, 3, &format.Amask))
                {
                    PySurface_Unprep (self);
                    return RAISE (PyExc_ValueError,
                                  "invalid color masks given");
                }
                mask = format.Rmask | format.Gmask | format.Bmask |
                    format.Amask;
                for (bpp = 0; bpp < 32; ++bpp)
                    if (!(mask >> bpp))
                        break;
            }
            else
            {
                PySurface_Unprep (self);
                return RAISE
                    (PyExc_ValueError,
                     "invalid argument specifying new format to convert to");
            }
            format.BitsPerPixel = (Uint8) bpp;
            format.BytesPerPixel = (bpp + 7) / 8;
            if (flags == -1)
                flags = surf->flags;
            if (format.Amask)
                flags |= SDL_SRCALPHA;
            newsurf = SDL_ConvertSurface (surf, &format, flags);
        }
    }
    else
    {
        if (SDL_WasInit (SDL_INIT_VIDEO))
            newsurf = SDL_DisplayFormat (surf);
        else
            newsurf = SDL_ConvertSurface (surf, surf->format, surf->flags);
    }
    PySurface_Unprep (self);

    final = PySurface_New (newsurf);
    if (!final)
        SDL_FreeSurface (newsurf);
    return final;
}

static PyObject*
surf_convert_alpha (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    PyObject *final;
    PySurfaceObject *srcsurf = NULL;
    SDL_Surface *newsurf, *src;

    if (!SDL_WasInit (SDL_INIT_VIDEO))
        return RAISE (PyExc_SDLError,
                      "cannot convert without pygame.display initialized");

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

    PySurface_Prep (self);
    if (srcsurf)
    {
        /*
         * hmm, we have to figure this out, not all depths have good
         * support for alpha
         */
        src = PySurface_AsSurface (srcsurf);
        newsurf = SDL_DisplayFormatAlpha (surf);
    }
    else
        newsurf = SDL_DisplayFormatAlpha (surf);
    PySurface_Unprep (self);

    final = PySurface_New (newsurf);
    if (!final)
        SDL_FreeSurface (newsurf);
    return final;
}

static PyObject*
surf_set_clip (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    PyObject *item;
    GAME_Rect *rect = NULL, temp;
    SDL_Rect sdlrect;
    int result;

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    if (PyTuple_Size (args))
    {
        item = PyTuple_GET_ITEM (args, 0);
        if (item == Py_None && PyTuple_Size (args) == 1)
        {
            result = SDL_SetClipRect (surf, NULL);
        }
        else
        {
            rect = GameRect_FromObject (args, &temp);
            if (!rect)
                return RAISE (PyExc_ValueError, "invalid rectstyle object");
            sdlrect.x = rect->x;
            sdlrect.y = rect->y;
            sdlrect.h = rect->h;
            sdlrect.w = rect->w;
            result = SDL_SetClipRect (surf, &sdlrect);
        }
    }
    else
    {
        result = SDL_SetClipRect (surf, NULL);
    }

    if (result == -1)
    {
        return RAISE (PyExc_SDLError, SDL_GetError ());
    }

    Py_RETURN_NONE;
}

static PyObject*
surf_get_clip (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyRect_New (&surf->clip_rect);
}


static PyObject*
surf_fill (PyObject *self, PyObject *args, PyObject *keywds)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    GAME_Rect *rect, temp;
    PyObject *r = NULL;
    Uint32 color;
    int result;
    PyObject *rgba_obj;
    Uint8 rgba[4];
    SDL_Rect sdlrect;
    int blendargs = 0;

    static char *kwids[] = {"color", "rect", "special_flags", NULL};
    if (!PyArg_ParseTupleAndKeywords (args, keywds, "O|Oi", kwids,
                                      &rgba_obj, &r, &blendargs))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (PyInt_Check (rgba_obj))
        color = (Uint32) PyInt_AsLong (rgba_obj);
    else if (PyLong_Check (rgba_obj))
        color = (Uint32) PyLong_AsUnsignedLong (rgba_obj);
    else if (RGBAFromColorObj (rgba_obj, rgba))
        color = SDL_MapRGBA (surf->format, rgba[0], rgba[1], rgba[2], rgba[3]);
    else
        return RAISE (PyExc_TypeError, "invalid color argument");

    if (!r || r == Py_None)
    {
        rect = &temp;
        temp.x = temp.y = 0;
        temp.w = surf->w;
        temp.h = surf->h;
    }
    else if (!(rect = GameRect_FromObject (r, &temp)))
        return RAISE (PyExc_ValueError, "invalid rectstyle object");

    /* we need a fresh copy so our Rect values don't get munged */
    if (rect != &temp)
    {
        memcpy (&temp, rect, sizeof (temp));
        rect = &temp;
    }

    if (rect->w < 0 || rect->h < 0)
    {
        sdlrect.x = sdlrect.y = 0;
        sdlrect.w = sdlrect.h = 0;
    }
    else
    {
        sdlrect.x = rect->x;
        sdlrect.y = rect->y;
        sdlrect.w = rect->w;
        sdlrect.h = rect->h;
        
        if (blendargs != 0)
        {
            
            /* 
            printf ("Using blendargs: %d\n", blendargs);
            */
            result = surface_fill_blend (surf, &sdlrect, color, blendargs);
        }
        else
        {
            PySurface_Prep (self);
            result = SDL_FillRect (surf, &sdlrect, color);
            PySurface_Unprep (self);
        }
        if (result == -1)
            return RAISE (PyExc_SDLError, SDL_GetError ());
    }
    return PyRect_New (&sdlrect);
}

static PyObject*
surf_blit (PyObject *self, PyObject *args, PyObject *keywds)
{
    SDL_Surface *src, *dest = PySurface_AsSurface (self);
    GAME_Rect *src_rect, temp;
    PyObject *srcobject, *argpos, *argrect = NULL;
    int dx, dy, result;
    SDL_Rect dest_rect, sdlsrc_rect;
    int sx, sy;
    int the_args = 0;

    static char *kwids[] = {"source", "dest", "area", "special_flags", NULL};
    if (!PyArg_ParseTupleAndKeywords (args, keywds, "O!O|Oi", kwids,
                                      &PySurface_Type, &srcobject, &argpos,
                                      &argrect, &the_args))
        return NULL;

    src = PySurface_AsSurface (srcobject);
    if (!dest || !src)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (dest->flags & SDL_OPENGL &&
        !(dest->flags & (SDL_OPENGLBLIT & ~SDL_OPENGL)))
        return RAISE (PyExc_SDLError,
                      "Cannot blit to OPENGL Surfaces (OPENGLBLIT is ok)");

    if ((src_rect = GameRect_FromObject (argpos, &temp)))
    {
        dx = src_rect->x;
        dy = src_rect->y;
    }
    else if (TwoIntsFromObj (argpos, &sx, &sy))
    {
        dx = sx;
        dy = sy;
    }
    else
        return RAISE (PyExc_TypeError, "invalid destination position for blit");

    if (argrect && argrect != Py_None)
    {
        if (!(src_rect = GameRect_FromObject (argrect, &temp)))
            return RAISE (PyExc_TypeError, "Invalid rectstyle argument");
    }
    else
    {
        temp.x = temp.y = 0;
        temp.w = src->w;
        temp.h = src->h;
        src_rect = &temp;
    }

    dest_rect.x = (short) dx;
    dest_rect.y = (short) dy;
    dest_rect.w = (unsigned short) src_rect->w;
    dest_rect.h = (unsigned short) src_rect->h;
    sdlsrc_rect.x = (short) src_rect->x;
    sdlsrc_rect.y = (short) src_rect->y;
    sdlsrc_rect.w = (unsigned short) src_rect->w;
    sdlsrc_rect.h = (unsigned short) src_rect->h;

    if (!the_args)
        the_args = 0;

    result = PySurface_Blit (self, srcobject, &dest_rect, &sdlsrc_rect,
                             the_args);
    if (result != 0)
        return NULL;

    return PyRect_New (&dest_rect);
}

static PyObject*
surf_scroll (PyObject *self, PyObject *args, PyObject *keywds)
{
    int dx = 0, dy = 0;
    SDL_Surface *surf;
    int bpp;
    int pitch;
    SDL_Rect *clip_rect;
    int w, h;
    Uint8 *src, *dst;

    static char *kwids[] = {"dx", "dy", NULL};
    if (!PyArg_ParseTupleAndKeywords (args, keywds, "|ii", kwids, &dx, &dy))
    {
        return NULL;
    }

    surf = PySurface_AsSurface (self);
    if (!surf)
    {
        return RAISE (PyExc_SDLError, "display Surface quit");
    }

    if (surf->flags & SDL_OPENGL &&
        !(surf->flags & (SDL_OPENGLBLIT & ~SDL_OPENGL)))
    {
        return RAISE (PyExc_SDLError,
                      "Cannot scroll an OPENGL Surfaces (OPENGLBLIT is ok)");
    }

    if (dx == 0 && dy == 0)
    {
	Py_RETURN_NONE;
    }

    clip_rect = &surf->clip_rect;
    w = clip_rect->w;
    h = clip_rect->h;
    if (dx >= w || dx <= -w || dy >= h || dy <= -h)
    {
	Py_RETURN_NONE;
    }

    if (!PySurface_Lock (self))
    {
	return NULL;
    }

    bpp = surf->format->BytesPerPixel;
    pitch = surf->pitch;
    src = dst = (Uint8 *) surf->pixels +
	        clip_rect->y * pitch + clip_rect->x * bpp;
    if (dx >= 0)
    {
	w -= dx;
	if (dy > 0)
	{
	    h -= dy;
	    dst += dy * pitch + dx * bpp;
	}
	else
	{
	    h += dy;
	    src -= dy * pitch;
	    dst += dx * bpp;
	}
    }
    else
    {
	w += dx;
	if (dy > 0)
	{
	    h -= dy;
	    src -= dx * bpp;
	    dst += dy * pitch;
	}
	else
	{
	    h += dy;
	    src -= dy * pitch + dx * bpp;
	}
    }
    surface_move (src, dst, h, w * bpp, pitch, pitch);

    if (!PySurface_Unlock (self))
    {
        return NULL;
    }

    Py_RETURN_NONE;
}

static PyObject*
surf_get_flags (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyInt_FromLong ((long)surf->flags);
}

static PyObject*
surf_get_pitch (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyInt_FromLong (surf->pitch);
}

static PyObject*
surf_get_size (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return Py_BuildValue ("(ii)", surf->w, surf->h);
}

static PyObject*
surf_get_width (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyInt_FromLong (surf->w);
}

static PyObject*
surf_get_height (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyInt_FromLong (surf->h);
}

static PyObject*
surf_get_rect (PyObject *self, PyObject *args, PyObject *kwargs)
{
    PyObject *rect;
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (PyTuple_GET_SIZE (args) > 0) {
        return RAISE (PyExc_TypeError,
                      "get_rect only accepts keyword arguments");
    }

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    rect = PyRect_New4 (0, 0, surf->w, surf->h);
    if (rect && kwargs)
    {
        PyObject *key, *value;
        Py_ssize_t pos = 0;

        while (PyDict_Next (kwargs, &pos, &key, &value))
        {
            if ((PyObject_SetAttr (rect, key, value) == -1))
            {
                Py_DECREF (rect);
                return NULL;
            }
        }
    }
    return rect;
}

static PyObject*
surf_get_bitsize (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    return PyInt_FromLong (surf->format->BitsPerPixel);
}

static PyObject*
surf_get_bytesize (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return PyInt_FromLong (surf->format->BytesPerPixel);
}

static PyObject*
surf_get_masks (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return Py_BuildValue ("(IIII)", surf->format->Rmask, surf->format->Gmask,
                          surf->format->Bmask, surf->format->Amask);
}


static PyObject*
surf_set_masks (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    /* Need to use 64bit vars so this works on 64 bit pythons. */
    Uint64 r, g, b, a;

    if (!PyArg_ParseTuple (args, "(kkkk)", &r, &g, &b, &a))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    /*
    printf("passed in: %d, %d, %d, %d\n", r,g,b,a );
    printf("what are: %d, %d, %d, %d\n", surf->format->Rmask, surf->format->Gmask, surf->format->Bmask, surf->format->Amask);
    */

    surf->format->Rmask = (Uint32)r;
    surf->format->Gmask = (Uint32)g;
    surf->format->Bmask = (Uint32)b;
    surf->format->Amask = (Uint32)a;

    Py_RETURN_NONE;
}





static PyObject*
surf_get_shifts (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return Py_BuildValue ("(iiii)", surf->format->Rshift, surf->format->Gshift,
                          surf->format->Bshift, surf->format->Ashift);
}


static PyObject*
surf_set_shifts (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    Uint64 r, g, b, a;

    if (!PyArg_ParseTuple (args, "(kkkk)", &r, &g, &b, &a))
        return NULL;
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    surf->format->Rshift = (Uint8)r;
    surf->format->Gshift = (Uint8)g;
    surf->format->Bshift = (Uint8)b;
    surf->format->Ashift = (Uint8)a;

    Py_RETURN_NONE;
}







static PyObject*
surf_get_losses (PyObject *self)
{
    SDL_Surface *surf = PySurface_AsSurface (self);

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    return Py_BuildValue ("(iiii)", surf->format->Rloss, surf->format->Gloss,
                          surf->format->Bloss, surf->format->Aloss);
}

static PyObject*
surf_subsurface (PyObject *self, PyObject *args)
{
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_PixelFormat *format = surf->format;
    GAME_Rect *rect, temp;
    SDL_Surface *sub;
    PyObject *subobj;
    int pixeloffset;
    char *startpixel;
    struct SubSurface_Data *data;

    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");
    if (surf->flags & SDL_OPENGL)
        return RAISE (PyExc_SDLError, "Cannot call on OPENGL Surfaces");

    if (!(rect = GameRect_FromObject (args, &temp)))
        return RAISE (PyExc_ValueError, "invalid rectstyle argument");
    if (rect->x < 0 || rect->y < 0 || rect->x + rect->w > surf->w
        || rect->y + rect->h > surf->h)
        return RAISE (PyExc_ValueError,
                      "subsurface rectangle outside surface area");

    PySurface_Lock (self);

    pixeloffset = rect->x * format->BytesPerPixel + rect->y * surf->pitch;
    startpixel = ((char *) surf->pixels) + pixeloffset;

    sub = SDL_CreateRGBSurfaceFrom (startpixel, rect->w, rect->h,
                                    format->BitsPerPixel, surf->pitch,
                                    format->Rmask, format->Gmask,
                                    format->Bmask, format->Amask);

    PySurface_Unlock (self);

    if (!sub)
        return RAISE (PyExc_SDLError, SDL_GetError ());

    /* copy the colormap if we need it */
    if (surf->format->BytesPerPixel == 1 && surf->format->palette)
        SDL_SetPalette (sub, SDL_LOGPAL, surf->format->palette->colors, 0,
                        surf->format->palette->ncolors);
    if (surf->flags & SDL_SRCALPHA)
        SDL_SetAlpha (sub, surf->flags & SDL_SRCALPHA, format->alpha);
    if (surf->flags & SDL_SRCCOLORKEY)
        SDL_SetColorKey (sub, surf->flags & (SDL_SRCCOLORKEY | SDL_RLEACCEL),
                         format->colorkey);

    data = PyMem_New (struct SubSurface_Data, 1);
    if (!data)
        return NULL;

    subobj = PySurface_New (sub);
    if (!subobj)
    {
        PyMem_Del (data);
        return NULL;
    }
    Py_INCREF (self);
    data->owner = self;
    data->pixeloffset = pixeloffset;
    data->offsetx = rect->x;
    data->offsety = rect->y;
    ((PySurfaceObject *) subobj)->subsurface = data;

    return subobj;
}

static PyObject*
surf_get_offset (PyObject *self)
{
    struct SubSurface_Data *subdata;

    subdata = ((PySurfaceObject *) self)->subsurface;
    if (!subdata)
        return Py_BuildValue ("(ii)", 0, 0);
    return Py_BuildValue ("(ii)", subdata->offsetx, subdata->offsety);
}

static PyObject*
surf_get_abs_offset (PyObject *self)
{
    struct SubSurface_Data *subdata;
    PyObject *owner;
    int offsetx, offsety;

    subdata = ((PySurfaceObject *) self)->subsurface;
    if (!subdata)
        return Py_BuildValue ("(ii)", 0, 0);

    subdata = ((PySurfaceObject *) self)->subsurface;
    owner = subdata->owner;
    offsetx = subdata->offsetx;
    offsety = subdata->offsety;

    while (((PySurfaceObject *) owner)->subsurface)
    {
        subdata = ((PySurfaceObject *) owner)->subsurface;
        owner = subdata->owner;
        offsetx += subdata->offsetx;
        offsety += subdata->offsety;
    }
    return Py_BuildValue ("(ii)", offsetx, offsety);
}

static PyObject*
surf_get_parent (PyObject *self)
{
    struct SubSurface_Data *subdata;

    subdata = ((PySurfaceObject *) self)->subsurface;
    if (!subdata)
        Py_RETURN_NONE;

    Py_INCREF (subdata->owner);
    return subdata->owner;
}

static PyObject*
surf_get_abs_parent (PyObject *self)
{
    struct SubSurface_Data *subdata;
    PyObject *owner;

    subdata = ((PySurfaceObject *) self)->subsurface;
    if (!subdata)
    {
        Py_INCREF (self);
        return self;
    }

    subdata = ((PySurfaceObject *) self)->subsurface;
    owner = subdata->owner;

    while (((PySurfaceObject *) owner)->subsurface)
    {
        subdata = ((PySurfaceObject *) owner)->subsurface;
        owner = subdata->owner;
    }

    Py_INCREF (owner);
    return owner;
}

static PyObject *
surf_get_bounding_rect (PyObject *self, PyObject *args, PyObject *kwargs)
{
    PyObject *rect;
    SDL_Surface *surf = PySurface_AsSurface (self);
    SDL_PixelFormat *format = surf->format;
    Uint8 *pixels = (Uint8 *) surf->pixels;
    Uint8 *pixel;
    int x, y;
    int min_x, min_y, max_x, max_y;
    int min_alpha = 1;
    int found_alpha = 0;
    Uint8 r, g, b, a;
    int has_colorkey = 0;
    Uint8 keyr, keyg, keyb;

    char *kwids[] = { "min_alpha", NULL };
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwids, &min_alpha))
       return RAISE (PyExc_ValueError,
                     "get_bounding_rect only accepts a single optional min_alpha argument");
    
    if (!surf)
        return RAISE (PyExc_SDLError, "display Surface quit");

    if (!PySurface_Lock (self))
    	return RAISE (PyExc_SDLError, "could not lock surface");

    if (surf->flags & SDL_SRCCOLORKEY)
    {
        has_colorkey = 1;
        SDL_GetRGBA (surf->format->colorkey,
                     surf->format,
                     &keyr, &keyg, &keyb, &a);
    }
    
    pixels = (Uint8 *) surf->pixels;
    
    min_y = 0;
    min_x = 0;
    max_x = surf->w;
    max_y = surf->h;

    found_alpha = 0;
    for (y = max_y - 1; y >= min_y; --y)
    {
        for (x = min_x; x < max_x; ++x)
        {
            pixel = (pixels + y * surf->pitch) + x*format->BytesPerPixel;
            SDL_GetRGBA (*((Uint32*)pixel), surf->format, &r, &g, &b, &a);
            if ((a >= min_alpha && has_colorkey == 0) ||
                (has_colorkey != 0 && (r != keyr || g != keyg || b != keyb)))
            {
                found_alpha = 1;
                break;
            }
        }
        if (found_alpha == 1)
        {
            break;
        }
        max_y = y;
    }
    found_alpha = 0;
    for (x = max_x - 1; x >= min_x; --x)
    {
        for (y = min_y; y < max_y; ++y)
        {
            pixel = (pixels + y * surf->pitch) + x*format->BytesPerPixel;
            SDL_GetRGBA (*((Uint32*)pixel), surf->format, &r, &g, &b, &a);
            if ((a >= min_alpha && has_colorkey == 0) ||
                (has_colorkey != 0 && (r != keyr || g != keyg || b != keyb)))
            {
                found_alpha = 1;
                break;
            }
        }
        if (found_alpha == 1)
        {
            break;
        }
        max_x = x;
    }
    found_alpha = 0;
    for (y = min_y; y < max_y; ++y)
    {
        min_y = y;
        for (x = min_x; x < max_x; ++x)
        {
            pixel = (pixels + y * surf->pitch) + x*format->BytesPerPixel;
            SDL_GetRGBA (*((Uint32*)pixel), surf->format, &r, &g, &b, &a);
            if ((a >= min_alpha && has_colorkey == 0) ||
                (has_colorkey != 0 && (r != keyr || g != keyg || b != keyb)))
            {
                found_alpha = 1;
                break;
            }
        }
        if (found_alpha == 1)
        {
            break;
        }
    }
    found_alpha = 0;
    for (x = min_x; x < max_x; ++x)
    {
        min_x = x;
        for (y = min_y; y < max_y; ++y)
        {
            pixel = (pixels + y * surf->pitch) + x*format->BytesPerPixel;
            SDL_GetRGBA (*((Uint32*)pixel), surf->format, &r, &g, &b, &a);
            if ((a >= min_alpha && has_colorkey == 0) ||
                (has_colorkey != 0 && (r != keyr || g != keyg || b != keyb)))
            {
                found_alpha = 1;
                break;
            }
        }
        if (found_alpha == 1)
        {
            break;
        }
    }
    if (!PySurface_Unlock (self))
    	return RAISE (PyExc_SDLError, "could not unlock surface");

    rect = PyRect_New4 (min_x, min_y, max_x - min_x, max_y - min_y);
    return rect;
}

static PyObject
*surf_get_buffer (PyObject *self)
{
    PyObject *buffer;
    PyObject *lock;
    SDL_Surface *surface = PySurface_AsSurface (self);
    Py_ssize_t length;

    length = (Py_ssize_t) surface->pitch * surface->h;

    buffer = PyBufferProxy_New (self, NULL, length, NULL);
    if (!buffer)
    {
        return RAISE (PyExc_SDLError,
            "could not acquire a buffer for the surface");
    }
    
    lock = PySurface_LockLifetime (self, buffer);
    if (!lock)
    {
        Py_DECREF (buffer);
        return RAISE (PyExc_SDLError, "could not lock surface");
    }
    ((PyBufferProxy *) buffer)->buffer = surface->pixels;
    ((PyBufferProxy *) buffer)->lock = lock;

    return buffer;
}

static void
surface_move (Uint8 *src, Uint8 *dst, int h, int span,
	      int srcpitch, int dstpitch)
{
    if (src < dst)
    {
	src += (h - 1) * srcpitch;
	dst += (h - 1) * dstpitch;
	srcpitch = -srcpitch;
	dstpitch = -dstpitch;
    }
    while (h--)
    {
	memmove(dst, src, span);
	src += srcpitch;
	dst += dstpitch;
    }
}

static int
surface_do_overlap (SDL_Surface *src, SDL_Rect *srcrect,
		    SDL_Surface *dst, SDL_Rect *dstrect)
{
    Uint8 *srcpixels;
    Uint8 *dstpixels;
    int srcx = srcrect->x, srcy = srcrect->y;
    int dstx = dstrect->x, dsty = dstrect->y;
    int x, y;
    int w = srcrect-> w, h= srcrect->h;
    int maxw, maxh;
    SDL_Rect *clip = &dst->clip_rect;
    int span;
    int dstoffset;

    /* clip the source rectangle to the source surface */
    if (srcx < 0)
    {
	w += srcx;
	dstx -= srcx;
	srcx = 0;
    }
    maxw = src->w - srcx;
    if (maxw < w)
    {
	w = maxw;
    }
    srcy = srcrect->y;
    if (srcy < 0)
    {
	h += srcy;
	dsty -= srcy;
	srcy = 0;
    }
    maxh = src->h - srcy;
    if (maxh < h)
    {
	h = maxh;
    }

    /* clip the destination rectangle against the clip rectangle */
    x = clip->x - dstx;
    if (x > 0)
    {
	w -= x;
	dstx += x;
	srcx += x;
    }
    x = dstx + w - clip->x - clip->w;
    if (x > 0)
    {
	w -= x;
    }
    y = clip->y - dsty;
    if (y > 0)
    {
	h -= y;
	dsty += y;
	srcy += y;
    }
    y = dsty + h - clip->y - clip->h;
    if (y > 0)
    {
	h -= y;
    }

    if (w <= 0 || h <= 0)
    {
	return 0;
    }

    srcpixels = ((Uint8 *) src->pixels + src->offset +
		  srcy * src->pitch +
		  srcx * src->format->BytesPerPixel);
    dstpixels = ((Uint8 *) dst->pixels + src->offset +
		  dsty * dst->pitch +
		  dstx * dst->format->BytesPerPixel);

    if (dstpixels <= srcpixels)
    {
	return 0;
    }

    span = w * src->format->BytesPerPixel;
    
    if (dstpixels >= srcpixels + (h - 1) * src->pitch + span)
    {
	return 0;
    }

    dstoffset = (dstpixels - srcpixels) % src->pitch;

    return dstoffset < span || dstoffset > src->pitch - span;
}

/*this internal blit function is accessable through the C api*/
int 
PySurface_Blit (PyObject * dstobj, PyObject * srcobj, SDL_Rect * dstrect,
                SDL_Rect * srcrect, int the_args)
{
    SDL_Surface *src = PySurface_AsSurface (srcobj);
    SDL_Surface *dst = PySurface_AsSurface (dstobj);
    SDL_Surface *subsurface = NULL;
    int result, suboffsetx = 0, suboffsety = 0;
    SDL_Rect orig_clip, sub_clip;

    /* passthrough blits to the real surface */
    if (((PySurfaceObject *) dstobj)->subsurface)
    {
        PyObject *owner;
        struct SubSurface_Data *subdata;

        subdata = ((PySurfaceObject *) dstobj)->subsurface;
        owner = subdata->owner;
        subsurface = PySurface_AsSurface (owner);
        suboffsetx = subdata->offsetx;
        suboffsety = subdata->offsety;

        while (((PySurfaceObject *) owner)->subsurface)
        {
            subdata = ((PySurfaceObject *) owner)->subsurface;
            owner = subdata->owner;
            subsurface = PySurface_AsSurface (owner);
            suboffsetx += subdata->offsetx;
            suboffsety += subdata->offsety;
        }

        SDL_GetClipRect (subsurface, &orig_clip);
        SDL_GetClipRect (dst, &sub_clip);
        sub_clip.x += suboffsetx;
        sub_clip.y += suboffsety;
        SDL_SetClipRect (subsurface, &sub_clip);
        dstrect->x += suboffsetx;
        dstrect->y += suboffsety;
        dst = subsurface;
    }
    else
    {
        PySurface_Prep (dstobj);
        subsurface = NULL;
    }

    PySurface_Prep (srcobj);

    /* see if we should handle alpha ourselves */
    if (dst->format->Amask && (dst->flags & SDL_SRCALPHA) &&
        !(src->format->Amask && !(src->flags & SDL_SRCALPHA)) &&
        /* special case, SDL works */
        (dst->format->BytesPerPixel == 2 || dst->format->BytesPerPixel == 4))
    {
	/* Py_BEGIN_ALLOW_THREADS */
        result = pygame_AlphaBlit (src, srcrect, dst, dstrect, the_args);
	/* Py_END_ALLOW_THREADS */
    }
    else if (the_args != 0 ||
	     (src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY) &&
	      /* This simplification is possible because a source subsurface
		 is converted to its owner with a clip rect and a dst
		 subsurface cannot be blitted to its owner because the
		 owner is locked.
	      */
	      dst->pixels == src->pixels &&
	      surface_do_overlap (src, srcrect, dst, dstrect)))
    {
	/* Py_BEGIN_ALLOW_THREADS */
        result = pygame_Blit (src, srcrect, dst, dstrect, the_args);
	/* Py_END_ALLOW_THREADS */
    }
    /* can't blit alpha to 8bit, crashes SDL */
    else if (dst->format->BytesPerPixel == 1 &&
	     (src->format->Amask || src->flags & SDL_SRCALPHA))
    {
	/* Py_BEGIN_ALLOW_THREADS */
	if (src->format->BytesPerPixel == 1)
	{
	    result = pygame_Blit (src, srcrect, dst, dstrect, 0);
	}
	else
	{
	    src = SDL_DisplayFormat (src);
	    if (src)
	    {
		result = SDL_BlitSurface (src, srcrect, dst, dstrect);
		SDL_FreeSurface (src);
	    }
	    else
	    {
		result = -1;
	    }
	}
	/* Py_END_ALLOW_THREADS */
    }
    else
    {
	/* Py_BEGIN_ALLOW_THREADS */
        result = SDL_BlitSurface (src, srcrect, dst, dstrect);
	/* Py_END_ALLOW_THREADS */
    }

    if (subsurface)
    {
        SDL_SetClipRect (subsurface, &orig_clip);
        dstrect->x -= suboffsetx;
        dstrect->y -= suboffsety;
    }
    else
        PySurface_Unprep (dstobj);
    PySurface_Unprep (srcobj);

    if (result == -1)
        RAISE (PyExc_SDLError, SDL_GetError ());
    if (result == -2)
        RAISE (PyExc_SDLError, "Surface was lost");

    return result != 0;
}

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

MODINIT_DEFINE (surface)
{
    PyObject *module, *dict, *apiobj, *lockmodule;
    int ecode;
    static void* c_api[PYGAMEAPI_SURFACE_NUMSLOTS];
    
#if PY3
    static struct PyModuleDef _module = {
        PyModuleDef_HEAD_INIT,
        "surface",
        DOC_PYGAMESURFACE,
        -1,
        _surface_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_color ();
    if (PyErr_Occurred ()) {
        MODINIT_ERROR;
    }
    import_pygame_rect ();
    if (PyErr_Occurred ()) {
        MODINIT_ERROR;
    }
    import_pygame_bufferproxy();
    if (PyErr_Occurred ()) {
        MODINIT_ERROR;
    }

    /* import the surflock module manually */
    lockmodule = PyImport_ImportModule (IMPPREFIX "surflock");
    if (lockmodule != NULL)
    {
        PyObject *_dict = PyModule_GetDict (lockmodule);
        PyObject *_c_api = PyDict_GetItemString (_dict, PYGAMEAPI_LOCAL_ENTRY);

        if (PyCObject_Check (_c_api))
        {
            int i;
            void **localptr = (void *)PyCObject_AsVoidPtr (_c_api);

            for (i = 0; i < PYGAMEAPI_SURFLOCK_NUMSLOTS; ++i)
                PyGAME_C_API[i + PYGAMEAPI_SURFLOCK_FIRSTSLOT] = localptr[i];
        }
        Py_DECREF (lockmodule);
    }
    else
    {
        MODINIT_ERROR;
    }

    /* type preparation */
    if (PyType_Ready(&PySurface_Type) < 0) {
        MODINIT_ERROR;
    }
    
    /* create the module */
#if PY3
    module = PyModule_Create (&_module);
#else
    module = Py_InitModule3 (MODPREFIX "surface", _surface_methods, DOC_PYGAMESURFACE);
#endif
    if (module == NULL) {
        MODINIT_ERROR;
    }
    dict = PyModule_GetDict (module);

    if (PyDict_SetItemString (dict, "SurfaceType", (PyObject *) &PySurface_Type)) {
        DECREF_MOD (module);
        MODINIT_ERROR;
    }
    if (PyDict_SetItemString (dict, "Surface", (PyObject *) &PySurface_Type)) {
        DECREF_MOD (module);
        MODINIT_ERROR;
    }

    /* export the c api */
    c_api[0] = &PySurface_Type;
    c_api[1] = PySurface_New;
    c_api[2] = PySurface_Blit;
    apiobj = PyCObject_FromVoidPtr (c_api, NULL);
    if (apiobj == NULL) {
        DECREF_MOD (module);
        MODINIT_ERROR;
    }
    ecode = PyDict_SetItemString (dict, PYGAMEAPI_LOCAL_ENTRY, apiobj);
    Py_DECREF (apiobj);
    if (ecode) {
        DECREF_MOD (module);
        MODINIT_ERROR;
    }
    /* Py_INCREF (PySurface_Type.tp_dict); INCREF's done in SetItemString */
    if (PyDict_SetItemString (dict, "_dict", PySurface_Type.tp_dict)) {
        DECREF_MOD (module);
        MODINIT_ERROR;
    }
    MODINIT_RETURN (module);
}
