/*
  pygame - Python Game Library
  Copyright (C) 2008 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

*/
/*
  This is a proposed SDL_gfx draw module for Pygame. It is backported
  from Pygame 2.

  TODO:
  - fix filledPolygonRGBA to use MT versions for threaded use.
  - do a filled pie version using filledPieColor
  - Determine if SDL video must be initiated for all routines to work.
    Add check if required, else remove ASSERT_VIDEO_INIT.
  - Example (Maybe).
*/
#define PYGAME_SDLGFXPRIM_INTERNAL

#include "pygame.h"
#include "doc/gfxdraw_doc.h"
#include "surface.h"
#include "pgcompat.h"
#include "SDL_gfx/SDL_gfxPrimitives.h"


static PyObject* _gfx_pixelcolor (PyObject *self, PyObject* args);
static PyObject* _gfx_hlinecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_vlinecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_rectanglecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_boxcolor (PyObject *self, PyObject* args);
static PyObject* _gfx_linecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_circlecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_arccolor (PyObject *self, PyObject* args);
static PyObject* _gfx_aacirclecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_filledcirclecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_ellipsecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_aaellipsecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_filledellipsecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_piecolor (PyObject *self, PyObject* args);
static PyObject* _gfx_trigoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_aatrigoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_filledtrigoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_polygoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_aapolygoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_filledpolygoncolor (PyObject *self, PyObject* args);
static PyObject* _gfx_texturedpolygon (PyObject *self, PyObject* args);
static PyObject* _gfx_beziercolor (PyObject *self, PyObject* args);

static PyMethodDef _gfxdraw_methods[] = {
    { "pixel", _gfx_pixelcolor, METH_VARARGS, DOC_PYGAMEGFXDRAWPIXEL },
    { "hline", _gfx_hlinecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWHLINE },
    { "vline", _gfx_vlinecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWVLINE },
    { "rectangle", _gfx_rectanglecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWRECTANGLE },
    { "box", _gfx_boxcolor, METH_VARARGS, DOC_PYGAMEGFXDRAWRECTANGLE },
    { "line", _gfx_linecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWLINE },
    { "circle", _gfx_circlecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWCIRCLE },
    { "arc", _gfx_arccolor, METH_VARARGS, DOC_PYGAMEGFXDRAWARC },
    { "aacircle", _gfx_aacirclecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWAACIRCLE },
    { "filled_circle", _gfx_filledcirclecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWFILLEDCIRCLE },
    { "ellipse", _gfx_ellipsecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWELLIPSE },
    { "aaellipse", _gfx_aaellipsecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWAAELLIPSE },
    { "filled_ellipse", _gfx_filledellipsecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWFILLEDELLIPSE },
    { "pie", _gfx_piecolor, METH_VARARGS, DOC_PYGAMEGFXDRAWPIE },
    { "trigon", _gfx_trigoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWTRIGON },
    { "aatrigon", _gfx_aatrigoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWAATRIGON },
    { "filled_trigon", _gfx_filledtrigoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWFILLEDTRIGON },
    { "polygon", _gfx_polygoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWPOLYGON },
    { "aapolygon", _gfx_aapolygoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWAAPOLYGON },
    { "filled_polygon", _gfx_filledpolygoncolor, METH_VARARGS, DOC_PYGAMEGFXDRAWFILLEDPOLYGON },
    { "textured_polygon", _gfx_texturedpolygon, METH_VARARGS, DOC_PYGAMEGFXDRAWTEXTUREDPOLYGON },
    { "bezier", _gfx_beziercolor, METH_VARARGS, DOC_PYGAMEGFXDRAWBEZIER },
    { NULL, NULL, 0, NULL },
};

#define ASSERT_VIDEO_INIT(unused)  /* Is video really needed for gfxdraw? */

static int
Sint16FromObj (PyObject *item, Sint16 *val)
{
    PyObject* intobj;
    long tmp;

    if (PyNumber_Check (item))
    {
        if (!(intobj = PyNumber_Int (item)))
            return 0;
        tmp = PyInt_AsLong (intobj);
        Py_DECREF (intobj);
        if (tmp == -1 && PyErr_Occurred ())
            return 0;
        *val = (Sint16)tmp;
        return 1;
    }
    return 0;
}

static int
Sint16FromSeqIndex (PyObject* obj, Py_ssize_t _index, Sint16* val)
{
    int result = 0;
    PyObject* item;
    item = PySequence_GetItem (obj, _index);
    if (item)
    {
        result = Sint16FromObj (item, val);
        Py_DECREF (item);
    }
    return result;
}

static PyObject*
_gfx_pixelcolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhO:pixel", &surface, &x, &y, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (pixelRGBA (PySurface_AsSurface (surface), x, y,
                   rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_hlinecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x1, x2, y;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhO:hline", &surface, &x1, &x2, &y, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (hlineRGBA (PySurface_AsSurface (surface), x1, x2, y,
                   rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_vlinecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, _y1, y2;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhO:vline", &surface, &x, &_y1, &y2,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (vlineRGBA (PySurface_AsSurface (surface), x, _y1, y2,
                   rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_rectanglecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *rect;
    GAME_Rect temprect, *sdlrect;
    Sint16 x1, x2, _y1, y2;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOO:rectangle", &surface, &rect, &color)) {
        return NULL;
    }

    if (!PySurface_Check (surface)) {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    sdlrect = GameRect_FromObject (rect, &temprect);
    if (sdlrect == NULL) {
        return NULL;
    }

    if (!RGBAFromObj (color, rgba)) {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    x1 = sdlrect->x;
    _y1 = sdlrect->y;
    x2 = (Sint16) (sdlrect->x + sdlrect->w - 1);
    y2 = (Sint16) (sdlrect->y + sdlrect->h - 1);

    if (rectangleRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2,
                       rgba[0], rgba[1], rgba[2], rgba[3]) ==
        -1) {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_boxcolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *rect;
    GAME_Rect temprect, *sdlrect;
    Sint16 x1, x2, _y1, y2;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOO:box", &surface, &rect, &color)) {
        return NULL;
    }

    if (!PySurface_Check (surface)) {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    sdlrect = GameRect_FromObject (rect, &temprect);
    if (sdlrect == NULL) {
        return NULL;
    }
    if (!RGBAFromObj (color, rgba)) {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    x1 = sdlrect->x;
    _y1 = sdlrect->y;
    x2 = (Sint16) (sdlrect->x + sdlrect->w - 1);
    y2 = (Sint16) (sdlrect->y + sdlrect->h - 1);

    if (boxRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2,
                 rgba[0], rgba[1], rgba[2], rgba[3]) ==
        -1) {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_linecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x1, x2, _y1, y2;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhO:line", &surface, &x1, &_y1, &x2, &y2,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (lineRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2,
                  rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_circlecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, r;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhO:circle", &surface, &x, &y, &r, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (circleRGBA (PySurface_AsSurface (surface), x, y, r,
                    rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_arccolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, r, start, end;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhhO:arc", &surface, &x, &y, &r,
            &start, &end, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (arcRGBA (PySurface_AsSurface (surface), x, y, r, start, end,
                 rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_aacirclecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, r;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhO:aacircle", &surface, &x, &y, &r,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (aacircleRGBA (PySurface_AsSurface (surface), x, y, r,
                      rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_filledcirclecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, r;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhO:filledcircle", &surface, &x, &y, &r,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (filledCircleRGBA (PySurface_AsSurface (surface), x, y, r,
                          rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_ellipsecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, rx, ry;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhO:ellipse", &surface, &x, &y, &rx, &ry,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (ellipseRGBA (PySurface_AsSurface (surface), x, y, rx, ry,
                     rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_aaellipsecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, rx, ry;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhO:aaellipse", &surface, &x, &y, &rx, &ry,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (aaellipseRGBA (PySurface_AsSurface (surface), x, y, rx, ry,
                       rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_filledellipsecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, rx, ry;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhO:filled_ellipse", &surface, &x, &y,
            &rx, &ry, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (filledEllipseRGBA (PySurface_AsSurface (surface), x, y, rx, ry,
                           rgba[0], rgba[1], rgba[2], rgba[3]) ==
        -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_piecolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x, y, r, start, end;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhhO:pie", &surface, &x, &y, &r,
            &start, &end, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (pieRGBA (PySurface_AsSurface (surface), x, y, r, start, end,
                 rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_trigoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x1, x2, x3, _y1, y2, y3;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhhhO:trigon", &surface, &x1, &_y1, &x2,
            &y2, &x3, &y3, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (trigonRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2, x3, y3,
                    rgba[0], rgba[1], rgba[2], rgba[3])
        == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_aatrigoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x1, x2, x3, _y1, y2, y3;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhhhO:aatrigon", &surface, &x1, &_y1, &x2,
            &y2, &x3, &y3, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (aatrigonRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2, x3, y3,
                      rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_filledtrigoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color;
    Sint16 x1, x2, x3, _y1, y2, y3;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OhhhhhhO:filled_trigon", &surface, &x1, &_y1,
            &x2, &y2, &x3, &y3, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }

    if (filledTrigonRGBA (PySurface_AsSurface (surface), x1, _y1, x2, y2,
                          x3, y3, rgba[0], rgba[1], rgba[2], rgba[3]) == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_polygoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *points, *item;
    Sint16 *vx, *vy, x, y;
    Py_ssize_t count, i;
    int ret;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOO:polygon", &surface, &points, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }
    if (!PySequence_Check (points))
    {
        PyErr_SetString (PyExc_TypeError, "points must be a sequence");
        return NULL;
    }

    count = PySequence_Size (points);
    if (count < 3)
    {
        PyErr_SetString (PyExc_ValueError,
            "points must contain more than 2 points");
        return NULL;
    }

    vx = PyMem_New (Sint16, (size_t) count);
    vy = PyMem_New (Sint16, (size_t) count);
    if (!vx || !vy)
    {
        if (vx)
            PyMem_Free (vx);
        if (vy)
            PyMem_Free (vy);
        return NULL;
    }

    for (i = 0; i < count; i++)
    {
        item = PySequence_ITEM (points, i);
        if (!Sint16FromSeqIndex (item, 0, &x))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        if (!Sint16FromSeqIndex (item, 1, &y))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        Py_DECREF (item);
        vx[i] = x;
        vy[i] = y;
    }

    Py_BEGIN_ALLOW_THREADS;
    ret = polygonRGBA (PySurface_AsSurface (surface), vx, vy, (int)count,
                       rgba[0], rgba[1], rgba[2], rgba[3]);
    Py_END_ALLOW_THREADS;

    PyMem_Free (vx);
    PyMem_Free (vy);

    if (ret == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_aapolygoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *points, *item;
    Sint16 *vx, *vy, x, y;
    Py_ssize_t count, i;
    int ret;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOO:aapolygon", &surface, &points, &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }
    if (!PySequence_Check (points))
    {
        PyErr_SetString (PyExc_TypeError, "points must be a sequence");
        return NULL;
    }

    count = PySequence_Size (points);
    if (count < 3)
    {
        PyErr_SetString (PyExc_ValueError,
            "points must contain more than 2 points");
        return NULL;
    }

    vx = PyMem_New (Sint16, (size_t) count);
    vy = PyMem_New (Sint16, (size_t) count);
    if (!vx || !vy)
    {
        if (vx)
            PyMem_Free (vx);
        if (vy)
            PyMem_Free (vy);
        return NULL;
    }

    for (i = 0; i < count; i++)
    {
        item = PySequence_ITEM (points, i);
        if (!Sint16FromSeqIndex (item, 0, &x))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        if (!Sint16FromSeqIndex (item, 1, &y))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        Py_DECREF (item);
        vx[i] = x;
        vy[i] = y;
    }

    Py_BEGIN_ALLOW_THREADS;
    ret = aapolygonRGBA (PySurface_AsSurface (surface), vx, vy, (int)count,
                         rgba[0], rgba[1], rgba[2], rgba[3]);
    Py_END_ALLOW_THREADS;

    PyMem_Free (vx);
    PyMem_Free (vy);

    if (ret == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_filledpolygoncolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *points, *item;
    Sint16 *vx, *vy, x, y;
    Py_ssize_t count, i;
    int ret;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOO:filled_polygon", &surface, &points,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }
    if (!PySequence_Check (points))
    {
        PyErr_SetString (PyExc_TypeError, "points must be a sequence");
        return NULL;
    }

    count = PySequence_Size (points);
    if (count < 3)
    {
        PyErr_SetString (PyExc_ValueError,
            "points must contain more than 2 points");
        return NULL;
    }

    vx = PyMem_New (Sint16, (size_t) count);
    vy = PyMem_New (Sint16, (size_t) count);
    if (!vx || !vy)
    {
        if (vx)
            PyMem_Free (vx);
        if (vy)
            PyMem_Free (vy);
        return NULL;
    }

    for (i = 0; i < count; i++)
    {
        item = PySequence_ITEM (points, i);
        if (!Sint16FromSeqIndex (item, 0, &x))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        if (!Sint16FromSeqIndex (item, 1, &y))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        Py_DECREF (item);
        vx[i] = x;
        vy[i] = y;
    }

    Py_BEGIN_ALLOW_THREADS;
    ret = filledPolygonRGBA (PySurface_AsSurface (surface), vx, vy,
                             (int)count, rgba[0], rgba[1], rgba[2], rgba[3]);
    Py_END_ALLOW_THREADS;

    PyMem_Free (vx);
    PyMem_Free (vy);

    if (ret == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

static PyObject*
_gfx_texturedpolygon (PyObject *self, PyObject* args)
{
    PyObject *surface, *texture, *points, *item;
    SDL_Surface *s_surface, *s_texture;
    Sint16 *vx, *vy, x, y, tdx, tdy;
    Py_ssize_t count, i;
    int ret;

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOOhh:textured_polygon", &surface, &points,
            &texture, &tdx, &tdy))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    s_surface = PySurface_AsSurface (surface);
    if (!PySurface_Check (texture))
    {
        PyErr_SetString (PyExc_TypeError, "texture must be a Surface");
        return NULL;
    }
    s_texture = PySurface_AsSurface (texture);
    if (!PySequence_Check (points))
    {
        PyErr_SetString (PyExc_TypeError, "points must be a sequence");
        return NULL;
    }
    if (s_surface->format->BytesPerPixel == 1 &&
        (s_texture->format->Amask || s_texture->flags & SDL_SRCALPHA)) {
        PyErr_SetString (PyExc_ValueError,
                           "Per-byte alpha texture unsupported "
                           "for 8 bit surfaces");
        return NULL;
    }

    count = PySequence_Size (points);
    if (count < 3)
    {
        PyErr_SetString (PyExc_ValueError,
            "points must contain more than 2 points");
        return NULL;
    }

    vx = PyMem_New (Sint16, (size_t) count);
    vy = PyMem_New (Sint16, (size_t) count);
    if (!vx || !vy)
    {
        if (vx)
            PyMem_Free (vx);
        if (vy)
            PyMem_Free (vy);
        return NULL;
    }

    for (i = 0; i < count; i++)
    {
        item = PySequence_ITEM (points, i);
        if (!Sint16FromSeqIndex (item, 0, &x))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        if (!Sint16FromSeqIndex (item, 1, &y))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        Py_DECREF (item);
        vx[i] = x;
        vy[i] = y;
    }

    Py_BEGIN_ALLOW_THREADS;
    ret = texturedPolygon (s_surface, vx, vy, (int)count,
                           s_texture, tdx, tdy);
    Py_END_ALLOW_THREADS;

    PyMem_Free (vx);
    PyMem_Free (vy);

    if (ret == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}


static PyObject*
_gfx_beziercolor (PyObject *self, PyObject* args)
{
    PyObject *surface, *color, *points, *item;
    Sint16 *vx, *vy, x, y;
    Py_ssize_t count, i;
    int ret, steps;
    Uint8 rgba[4];

    ASSERT_VIDEO_INIT (NULL);

    if (!PyArg_ParseTuple (args, "OOiO:bezier", &surface, &points, &steps,
            &color))
        return NULL;

    if (!PySurface_Check (surface))
    {
        PyErr_SetString (PyExc_TypeError, "surface must be a Surface");
        return NULL;
    }
    if (!RGBAFromObj (color, rgba))
    {
        PyErr_SetString (PyExc_TypeError, "invalid color argument");
        return NULL;
    }
    if (!PySequence_Check (points))
    {
        PyErr_SetString (PyExc_TypeError, "points must be a sequence");
        return NULL;
    }

    count = PySequence_Size (points);
    if (count < 3)
    {
        PyErr_SetString (PyExc_ValueError,
            "points must contain more than 2 points");
        return NULL;
    }

    vx = PyMem_New (Sint16, (size_t) count);
    vy = PyMem_New (Sint16, (size_t) count);
    if (!vx || !vy)
    {
        PyErr_SetString (PyExc_MemoryError, "memory allocation failed");
        if (vx)
            PyMem_Free (vx);
        if (vy)
            PyMem_Free (vy);
        return NULL;
    }

    for (i = 0; i < count; i++)
    {
        item = PySequence_ITEM (points, i);
        if (!Sint16FromSeqIndex (item, 0, &x))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        if (!Sint16FromSeqIndex (item, 1, &y))
        {
            PyMem_Free (vx);
            PyMem_Free (vy);
            Py_XDECREF (item);
            return NULL;
        }
        Py_DECREF (item);
        vx[i] = x;
        vy[i] = y;
    }

    Py_BEGIN_ALLOW_THREADS;
    ret = bezierRGBA (PySurface_AsSurface (surface), vx, vy, (int)count,
                      steps, rgba[0], rgba[1], rgba[2], rgba[3]);
    Py_END_ALLOW_THREADS;

    PyMem_Free (vx);
    PyMem_Free (vy);

    if (ret == -1)
    {
        PyErr_SetString (PyExc_SDLError, SDL_GetError ());
        return NULL;
    }
    Py_RETURN_NONE;
}

MODINIT_DEFINE(gfxdraw)
{
    PyObject *module;

#if PY3
    static struct PyModuleDef _module = {
        PyModuleDef_HEAD_INIT,
        MODPREFIX "gfxdraw",
        DOC_PYGAMEGFXDRAW,
        -1,
        _gfxdraw_methods,
        NULL, NULL, NULL, NULL
    };
#endif

    /* import 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_surface();
    if (PyErr_Occurred ()) {
        MODINIT_ERROR;
    }

#if PY3
    module = PyModule_Create (&_module);
#else
    module = Py_InitModule3 (MODPREFIX "gfxdraw", _gfxdraw_methods, DOC_PYGAMEGFXDRAW);
#endif

    MODINIT_RETURN (module);
}
