diff --git a/buildconfig/Setup.Emscripten.SDL2.in b/buildconfig/Setup.Emscripten.SDL2.in index cea1aefa16..f53a4edce1 100644 --- a/buildconfig/Setup.Emscripten.SDL2.in +++ b/buildconfig/Setup.Emscripten.SDL2.in @@ -61,6 +61,9 @@ rect src_c/void.c rwobject src_c/void.c system src_c/void.c _window src_c/void.c +_renderer src_c/void.c +_texture src/void.c +_image src/void.c geometry src_c/void.c #_sdl2.controller src_c/_sdl2/controller.c $(SDL) $(DEBUG) -Isrc_c diff --git a/buildconfig/Setup.SDL2.in b/buildconfig/Setup.SDL2.in index fafbd9c8fb..a388280d72 100644 --- a/buildconfig/Setup.SDL2.in +++ b/buildconfig/Setup.SDL2.in @@ -76,3 +76,6 @@ newbuffer src_c/newbuffer.c $(SDL) $(DEBUG) system src_c/system.c $(SDL) $(DEBUG) geometry src_c/geometry.c $(SDL) $(DEBUG) _window src_c/window.c $(SDL) $(DEBUG) +_renderer src_c/renderer.c $(SDL) $(DEBUG) +_texture src_c/texture.c $(SDL) $(DEBUG) +_image src_c/video_image.c $(SDL) $(DEBUG) diff --git a/src_c/_pygame.h b/src_c/_pygame.h index 93cdb5c4e2..7f08d7f59d 100644 --- a/src_c/_pygame.h +++ b/src_c/_pygame.h @@ -488,6 +488,9 @@ typedef enum { #define PYGAMEAPI_BASE_NUMSLOTS 27 #define PYGAMEAPI_EVENT_NUMSLOTS 8 #define PYGAMEAPI_WINDOW_NUMSLOTS 1 +#define PYGAMEAPI_RENDERER_NUMSLOTS 1 +#define PYGAMEAPI_TEXTURE_NUMSLOTS 1 +#define PYGAMEAPI_IMAGE_NUMSLOTS 1 #define PYGAMEAPI_GEOMETRY_NUMSLOTS 1 #endif /* _PYGAME_INTERNAL_H */ diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index b50a4e7489..2c95c5db63 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -502,6 +502,52 @@ typedef struct { #define import_pygame_window() IMPORT_PYGAME_MODULE(_window) #endif +/* + * Renderer module + */ +typedef struct { + PyObject_HEAD SDL_Renderer *renderer; + pgWindowObject *window; +} pgRendererObject; +#ifndef PYGAMEAPI_RENDERER_INTERNAL +#define pgRenderer_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_renderer, 0)) +#define pgRenderer_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgRenderer_Type)) +#define import_pygame_renderer() IMPORT_PYGAME_MODULE(_renderer) +#endif + +/* + * Texture module + */ +typedef struct { + PyObject_HEAD SDL_Texture *texture; + pgRendererObject *renderer; + int width; + int height; +} pgTextureObject; +#ifndef PYGAMEAPI_TEXTURE_INTERNAL +#define pgTexture_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_texture, 0)) +#define pgTexture_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgTexture_Type)) +#define import_pygame_texture() IMPORT_PYGAME_MODULE(_texture) +#endif + +/* + * video.Image module + */ +typedef struct { + PyObject_HEAD SDL_Texture *texture; + int angle; + SDL_Point origin; + SDL_RendererFlip flip; +} pgImageObject; +#ifndef PYGAMEAPI_IMAGE_INTERNAL +#define pgImage_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_image, 0)) +#define pgImage_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgImage_Type)) +#define import_pygame_image() IMPORT_PYGAME_MODULE(_image) +#endif + #define IMPORT_PYGAME_MODULE _IMPORT_PYGAME_MODULE /* @@ -521,6 +567,9 @@ PYGAMEAPI_DEFINE_SLOTS(pixelarray); PYGAMEAPI_DEFINE_SLOTS(color); PYGAMEAPI_DEFINE_SLOTS(math); PYGAMEAPI_DEFINE_SLOTS(_window); +PYGAMEAPI_DEFINE_SLOTS(_renderer); +PYGAMEAPI_DEFINE_SLOTS(_texture); +PYGAMEAPI_DEFINE_SLOTS(_image); PYGAMEAPI_DEFINE_SLOTS(geometry); #else /* ~PYGAME_H */ PYGAMEAPI_EXTERN_SLOTS(base); @@ -535,6 +584,9 @@ PYGAMEAPI_EXTERN_SLOTS(pixelarray); PYGAMEAPI_EXTERN_SLOTS(color); PYGAMEAPI_EXTERN_SLOTS(math); PYGAMEAPI_EXTERN_SLOTS(_window); +PYGAMEAPI_EXTERN_SLOTS(_renderer); +PYGAMEAPI_EXTERN_SLOTS(_texture); +PYGAMEAPI_EXTERN_SLOTS(_image); PYGAMEAPI_EXTERN_SLOTS(geometry); #endif /* ~PYGAME_H */ diff --git a/src_c/renderer.c b/src_c/renderer.c new file mode 100644 index 0000000000..fd30a9353d --- /dev/null +++ b/src_c/renderer.c @@ -0,0 +1,592 @@ +#define PYGAMEAPI_RENDERER_INTERNAL + +#include "pygame.h" + +#include "pgcompat.h" + +#include "doc/sdl2_video_doc.h" + +static PyTypeObject pgRenderer_Type; + +#define RENDERER_ERROR_CHECK(x) \ + if (x < 0) { \ + return RAISE(pgExc_SDLError, SDL_GetError()); \ + } \ + +#define pgRenderer_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgRenderer_Type)) + +// TODO Does from_window even work? +/*static PyObject * +from_window(PyObject *self, PyObject *arg, PyObject *kwargs) { +}*/ + +static PyObject * +compose_custom_blend_mode(PyObject *self, PyObject *args, PyObject *kwargs) { + PyObject *color_mode, *alpha_mode; + float mode[6]; + int blend_mode; + static char *keywords[] = {"color_mode", "alpha_mode", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", keywords, &color_mode, &alpha_mode)) { + return NULL; + } + if (!PySequence_Check(color_mode)) return RAISE(PyExc_TypeError, "color_mode has to be sequence"); + if (!PySequence_Check(alpha_mode)) return RAISE(PyExc_TypeError, "alpha_mode has to be sequence"); + if (PySequence_Size(color_mode) != 3) return RAISE(PyExc_TypeError, "color_mode has to have 3 elements"); + if (PySequence_Size(alpha_mode) != 3) return RAISE(PyExc_TypeError, "alpha_mode has to have 3 elements"); + if (!pg_FloatFromObjIndex(color_mode, 0, &mode[0])) return RAISE(PyExc_TypeError, "color_mode first element must be float"); + if (!pg_FloatFromObjIndex(color_mode, 1, &mode[1])) return RAISE(PyExc_TypeError, "color_mode second element must be float"); + if (!pg_FloatFromObjIndex(color_mode, 2, &mode[2])) return RAISE(PyExc_TypeError, "color_mode third element must be float"); + if (!pg_FloatFromObjIndex(alpha_mode, 0, &mode[3])) return RAISE(PyExc_TypeError, "alpha_mode first element must be float"); + if (!pg_FloatFromObjIndex(alpha_mode, 1, &mode[4])) return RAISE(PyExc_TypeError, "alpha_mode second element must be float"); + if (!pg_FloatFromObjIndex(alpha_mode, 2, &mode[5])) return RAISE(PyExc_TypeError, "alpha_mode third element must be float"); + blend_mode = SDL_ComposeCustomBlendMode(mode[0], mode[1], mode[2], mode[3], mode[4], mode[5]); + return PyLong_FromLong((long) blend_mode); +} + +static PyObject * +renderer_draw_line(pgRendererObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *start, *end; + int startx, starty, endx, endy; + static char *keywords[] = {"p1", "p2", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", keywords, &start, &end)) { + return NULL; + } + if (!pg_TwoIntsFromObj(start, &startx, &starty)) { + return RAISE(PyExc_TypeError, "invalid p1 argument"); + } + if (!pg_TwoIntsFromObj(end, &endx, &endy)) { + return RAISE(PyExc_TypeError, "invalid p2 argument"); + } + RENDERER_ERROR_CHECK(SDL_RenderDrawLine(self->renderer, startx, starty, endx, endy)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_draw_point(pgRendererObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *point; + int x, y; + static char *keywords[] = {"point", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &point)) { + return NULL; + } + if (!pg_TwoIntsFromObj(point, &x, &y)) { + return RAISE(PyExc_TypeError, "invalid point"); + } + RENDERER_ERROR_CHECK(SDL_RenderDrawPoint(self->renderer, x, y)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_draw_rect(pgRendererObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *rectobj; + SDL_Rect *rect = NULL, temp; + static char *keywords[] = {"rect", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &rectobj)) { + return NULL; + } + if (!(rect = pgRect_FromObject(rectobj, &temp))) { + return RAISE(PyExc_TypeError, "rect argument is invalid"); + } + RENDERER_ERROR_CHECK(SDL_RenderDrawRect(self->renderer, rect)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_fill_rect(pgRendererObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *rectobj; + SDL_Rect *rect = NULL, temp; + static char *keywords[] = {"rect", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &rectobj)) { + return NULL; + } + if (!(rect = pgRect_FromObject(rectobj, &temp))) { + return RAISE(PyExc_TypeError, "rect argument is invalid"); + } + RENDERER_ERROR_CHECK(SDL_RenderFillRect(self->renderer, rect)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_draw_triangle(pgRendererObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *p1, *p2, *p3; + int p1x, p1y, p2x, p2y, p3x, p3y; + SDL_Point points[4]; + static char *keywords[] = {"p1", "p2", "p3", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO", keywords, &p1, &p2, &p3)) { + return NULL; + } + if (!pg_TwoIntsFromObj(p1, &p1x, &p1y)) { + return RAISE(PyExc_TypeError, "invalid p1 argument"); + } + if (!pg_TwoIntsFromObj(p2, &p2x, &p2y)) { + return RAISE(PyExc_TypeError, "invalid p2 argument"); + } + if (!pg_TwoIntsFromObj(p3, &p3x, &p3y)) { + return RAISE(PyExc_TypeError, "invalid p3 argument"); + } + points[0].x = p1x; points[0].y = p1y; + points[1].x = p2x; points[1].y = p2y; + points[2].x = p3x; points[2].y = p3y; + points[3].x = p1x; points[3].y = p1y; + RENDERER_ERROR_CHECK(SDL_RenderDrawLines(self->renderer, points, 4)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_fill_triangle(pgRendererObject *self, PyObject *args, PyObject *kwargs) { +#if SDL_VERSION_ATLEAST(2, 0, 18) + Uint8 rgba[4]; + PyObject *p1, *p2, *p3; + float p1x, p1y, p2x, p2y, p3x, p3y; + SDL_Vertex vertices[3]; + static char *keywords[] = {"p1", "p2", "p3", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO", keywords, &p1, &p2, &p3)) { + return NULL; + } + if (!pg_TwoFloatsFromObj(p1, &p1x, &p1y)) { + return RAISE(PyExc_TypeError, "invalid p1 argument"); + } + if (!pg_TwoFloatsFromObj(p2, &p2x, &p2y)) { + return RAISE(PyExc_TypeError, "invalid p2 argument"); + } + if (!pg_TwoFloatsFromObj(p3, &p3x, &p3y)) { + return RAISE(PyExc_TypeError, "invalid p3 argument"); + } + RENDERER_ERROR_CHECK(SDL_GetRenderDrawColor(self->renderer, &rgba[0], &rgba[1], &rgba[2], &rgba[3])) + vertices[0].position.x = p1x; vertices[0].position.y = p1y; vertices[0].color.r = rgba[0]; vertices[0].color.g = rgba[1]; vertices[0].color.b = rgba[2]; vertices[0].color.a = rgba[3]; + vertices[1].position.x = p2x; vertices[1].position.y = p2y; vertices[1].color.r = rgba[0]; vertices[1].color.g = rgba[1]; vertices[1].color.b = rgba[2]; vertices[1].color.a = rgba[3]; + vertices[2].position.x = p3x; vertices[2].position.y = p3y; vertices[2].color.r = rgba[0]; vertices[2].color.g = rgba[1]; vertices[2].color.b = rgba[2]; vertices[2].color.a = rgba[3]; + RENDERER_ERROR_CHECK(SDL_RenderGeometry(self->renderer, NULL, vertices, 3, NULL, 0)) + Py_RETURN_NONE; +#else + RAISE(PyExc_TypeError, "fill_triangle() requires SDL 2.0.18 or newer"); + Py_RETURN_NONE; +#endif +} + +static PyObject * +renderer_draw_quad(pgRendererObject *self, PyObject *args, PyObject *kwargs) { + PyObject *p1, *p2, *p3, *p4; + int p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y; + SDL_Point points[5]; + static char *keywords[] = {"p1", "p2", "p3", "p4", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO", keywords, &p1, &p2, &p3, &p4)) { + return NULL; + } + if (!pg_TwoIntsFromObj(p1, &p1x, &p1y)) { + return RAISE(PyExc_TypeError, "invalid p1 argument"); + } + if (!pg_TwoIntsFromObj(p2, &p2x, &p2y)) { + return RAISE(PyExc_TypeError, "invalid p2 argument"); + } + if (!pg_TwoIntsFromObj(p3, &p3x, &p3y)) { + return RAISE(PyExc_TypeError, "invalid p3 argument"); + } + if (!pg_TwoIntsFromObj(p4, &p4x, &p4y)) { + return RAISE(PyExc_TypeError, "invalid p4 argument"); + } + points[0].x = p1x; points[0].y = p1y; + points[1].x = p2x; points[1].y = p2y; + points[2].x = p3x; points[2].y = p3y; + points[3].x = p4x; points[3].y = p4y; + points[4].x = p1x; points[4].y = p1y; + RENDERER_ERROR_CHECK(SDL_RenderDrawLines(self->renderer, points, 5)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_fill_quad(pgRendererObject *self, PyObject *args, PyObject *kwargs) { +#if SDL_VERSION_ATLEAST(2, 0, 18) + Uint8 rgba[4]; + PyObject *p1, *p2, *p3, *p4; + float p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y; + SDL_Vertex vertices[6]; + static char *keywords[] = {"p1", "p2", "p3", "p4", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO", keywords, &p1, &p2, &p3, &p4)) { + return NULL; + } + if (!pg_TwoFloatsFromObj(p1, &p1x, &p1y)) { + return RAISE(PyExc_TypeError, "invalid p1 argument"); + } + if (!pg_TwoFloatsFromObj(p2, &p2x, &p2y)) { + return RAISE(PyExc_TypeError, "invalid p2 argument"); + } + if (!pg_TwoFloatsFromObj(p3, &p3x, &p3y)) { + return RAISE(PyExc_TypeError, "invalid p3 argument"); + } + if (!pg_TwoFloatsFromObj(p4, &p4x, &p4y)) { + return RAISE(PyExc_TypeError, "invalid p4 argument"); + } + RENDERER_ERROR_CHECK(SDL_GetRenderDrawColor(self->renderer, &rgba[0], &rgba[1], &rgba[2], &rgba[3])) + vertices[0].position.x = p1x; vertices[0].position.y = p1y; vertices[0].color.r = rgba[0]; vertices[0].color.g = rgba[1]; vertices[0].color.b = rgba[2]; vertices[0].color.a = rgba[3]; + vertices[1].position.x = p2x; vertices[1].position.y = p2y; vertices[1].color.r = rgba[0]; vertices[1].color.g = rgba[1]; vertices[1].color.b = rgba[2]; vertices[1].color.a = rgba[3]; + vertices[2].position.x = p3x; vertices[2].position.y = p3y; vertices[2].color.r = rgba[0]; vertices[2].color.g = rgba[1]; vertices[2].color.b = rgba[2]; vertices[2].color.a = rgba[3]; + vertices[3].position.x = p3x; vertices[3].position.y = p3y; vertices[3].color.r = rgba[0]; vertices[3].color.g = rgba[1]; vertices[3].color.b = rgba[2]; vertices[3].color.a = rgba[3]; + vertices[4].position.x = p4x; vertices[4].position.y = p4y; vertices[4].color.r = rgba[0]; vertices[4].color.g = rgba[1]; vertices[4].color.b = rgba[2]; vertices[4].color.a = rgba[3]; + vertices[5].position.x = p1x; vertices[5].position.y = p1y; vertices[5].color.r = rgba[0]; vertices[5].color.g = rgba[1]; vertices[5].color.b = rgba[2]; vertices[5].color.a = rgba[3]; + RENDERER_ERROR_CHECK(SDL_RenderGeometry(self->renderer, NULL, vertices, 6, NULL, 0)) + Py_RETURN_NONE; +#else + RAISE(PyExc_TypeError, "fill_quad() requires SDL 2.0.18 or newer"); + Py_RETURN_NONE; +#endif +} + +static PyObject * +renderer_present(pgRendererObject *self, PyObject *_null) +{ + SDL_RenderPresent(self->renderer); + Py_RETURN_NONE; +} + +static PyObject * +renderer_clear(pgRendererObject *self, PyObject *_null) +{ + RENDERER_ERROR_CHECK(SDL_RenderClear(self->renderer)) + Py_RETURN_NONE; +} + +static PyObject * +renderer_get_viewport(pgRendererObject *self, PyObject *_null) { + SDL_Rect rect; + SDL_RenderGetViewport(self->renderer, &rect); + return pgRect_New(&rect); +} + +static PyObject * +renderer_set_viewport(pgRendererObject *self, PyObject *args, PyObject *kwargs) { + PyObject *rectobj = Py_None; + SDL_Rect rect; + static char *keywords[] = {"area", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", keywords, &rectobj)) { + return NULL; + } + if (rectobj == Py_None) { + RENDERER_ERROR_CHECK(SDL_RenderSetViewport(self->renderer, NULL)) + } + else { + rect = pgRect_AsRect(rectobj); + RENDERER_ERROR_CHECK(SDL_RenderSetViewport(self->renderer, &rect)) + } + Py_RETURN_NONE; +} + +// TODO this has small problems +/* +static PyObject * +renderer_to_surface(pgRendererObject *self, PyObject *args, PyObject *kwargs) { + PyObject *surfobj = Py_None, *rectobj = Py_None; + SDL_Surface *surf; + pgSurfaceObject *surface; + SDL_Rect rarea, tempviewport, *areaparam, *rectptr; + Uint32 format; + static char *keywords[] = {"surface", "area", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO", keywords, &surfobj, &rectobj)) { + return NULL; + } + if (rectobj != Py_None) { + if (pgRect_Check(rectobj)) { + rectptr = pgRect_FromObject(rectobj, &rarea); + SDL_RenderGetViewport(self->renderer, &tempviewport); + SDL_IntersectRect(rectptr, &tempviewport, rectptr); + areaparam = rectptr; + rarea.x = rectptr->x; + rarea.y = rectptr->y; + rarea.w = rectptr->w; + rarea.h = rectptr->h; + } + else { + RAISE(PyExc_TypeError, "rect must be Rect or None"); + } + } + else { + printf("HERE!!\n"); + SDL_RenderGetViewport(self->renderer, &rarea); + areaparam = NULL; + } + if (surfobj != Py_None) { + if (pgSurface_Check(surfobj)) { + surf = pgSurface_AsSurface(surfobj); + if (surf->w < rarea.w || surf->h < rarea.h) { + RAISE(PyExc_ValueError, "surface is too small"); + } + format = surf->format->format; + } + else { + RAISE(PyExc_TypeError, "surface must be Surface or None"); + } + } + else { + format = SDL_GetWindowPixelFormat(self->window->_win); + if (format == SDL_PIXELFORMAT_UNKNOWN) { + RAISE(PyExc_TypeError, "unknown pixelformat"); + } + surf = SDL_CreateRGBSurfaceWithFormat(0, rarea.w, rarea.h, SDL_BITSPERPIXEL(format), format); + if (surf == NULL) { + RAISE(PyExc_MemoryError, "not enough memory for the surface"); + } + surface = pgSurface_New2(surf, 1); + } + RENDERER_ERROR_CHECK(SDL_RenderReadPixels(self->renderer, areaparam, format, surf->pixels, surf->pitch)) + return surfobj; +} +*/ + +// TODO For blit need Texture/Image +/*static PyObject * +renderer_blit(pgRendererObject *self, PyObject *args, PyObject *kwargs) { +}*/ + +static PyObject * +renderer_get_draw_blend_mode(pgRendererObject *self, void *closure) { + SDL_BlendMode blend_mode; + RENDERER_ERROR_CHECK(SDL_GetRenderDrawBlendMode(self->renderer, &blend_mode)) + return PyLong_FromLong((long) blend_mode); +} + +static PyObject * +renderer_set_draw_blend_mode(pgRendererObject *self, PyObject *arg, void *closure) { + RENDERER_ERROR_CHECK(SDL_SetRenderDrawBlendMode(self->renderer, (int) PyLong_AsLong(arg))) + return 0; +} + +static PyObject * +renderer_get_draw_color(pgRendererObject *self, void *closure) +{ + Uint8 rgba[4]; + RENDERER_ERROR_CHECK(SDL_GetRenderDrawColor(self->renderer, &rgba[0], &rgba[1], &rgba[2], &rgba[3])) + return pgColor_NewLength(rgba, 4); +} + +static PyObject * +renderer_set_draw_color(pgRendererObject *self, PyObject *arg, void *closure) +{ + Uint8 color[4]; + if (!pg_RGBAFromObjEx(arg, color, PG_COLOR_HANDLE_ALL)) { + return -1; + } + RENDERER_ERROR_CHECK(SDL_SetRenderDrawColor(self->renderer, color[0], color[1], color[2], color[3])) + return 0; +} + +static PyObject * +renderer_get_logical_size(pgRendererObject *self, void *closure) { + int w, h; + SDL_RenderGetLogicalSize(self->renderer, &w, &h); + return Py_BuildValue("(ii)", w, h); +} + +static PyObject * +renderer_set_logical_size(pgRendererObject *self, PyObject *arg, void *closure) { + int w, h; + if (!pg_TwoIntsFromObj(arg, &w, &h)) { + RAISE(PyExc_TypeError, "invalid logical size"); + return -1; + } + RENDERER_ERROR_CHECK(SDL_RenderSetLogicalSize(self->renderer, w, h)) + return 0; +} + +static PyObject * +renderer_get_scale(pgRendererObject *self, void *closure) { + float x, y; + SDL_RenderGetScale(self->renderer, &x, &y); + return Py_BuildValue("(dd)", x, y); +} + +static PyObject * +renderer_set_scale(pgRendererObject *self, PyObject *arg, void *closure) { + float x, y; + if (!pg_TwoFloatsFromObj(arg, &x, &y)) { + RAISE(PyExc_TypeError, "invalid scale"); + return -1; + } + RENDERER_ERROR_CHECK(SDL_RenderSetScale(self->renderer, x, y)) + return 0; +} + +// TODO For target need Texture +/* +static PyObject * +renderer_get_target(pgRendererObject *self, void *closure) { +} +static PyObject * +renderer_set_target(pgRendererObject *self, PyObject *arg, void *closure) { +} +*/ + +static int +renderer_init(pgRendererObject *self, PyObject *args, PyObject *kwargs) { + SDL_Renderer *renderer = NULL; + pgWindowObject *window; + int index = -1; + int accelerated = -1; + int vsync = 0; + int target_texture = 0; + Uint32 flags = 0; + + char *keywords[] = {"window", "index", "accelerated", "vsync", "target_texture", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iipp", keywords, &window, &index, &accelerated, &vsync, &target_texture)) { + return -1; + } + if (accelerated >= 0) { + flags |= accelerated ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE; + } + if (vsync) { + flags |= SDL_RENDERER_PRESENTVSYNC; + } + if (target_texture) { + flags |= SDL_RENDERER_TARGETTEXTURE; + } + renderer = SDL_CreateRenderer(window->_win, index, flags); + if (!renderer) { + PyErr_SetString(pgExc_SDLError, SDL_GetError()); + return -1; + } + self->renderer = renderer; + self->window = window; + return 0; +} + +static void +renderer_dealloc(pgRendererObject *self, PyObject *_null) { + if (self->renderer) { + SDL_DestroyRenderer(self->renderer); + } + Py_TYPE(self)->tp_free(self); +} + +static PyMethodDef renderer_methods[] = { + {"draw_line", (PyCFunction)renderer_draw_line, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_DRAWLINE}, + {"draw_point", (PyCFunction)renderer_draw_point, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_DRAWPOINT}, + {"draw_rect", (PyCFunction)renderer_draw_rect, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_DRAWRECT}, + {"draw_triangle", (PyCFunction)renderer_draw_triangle, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_DRAWTRIANGLE}, + {"draw_quad", (PyCFunction)renderer_draw_quad, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_DRAWQUAD}, + {"fill_rect", (PyCFunction)renderer_fill_rect, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_FILLRECT}, + {"fill_triangle", (PyCFunction)renderer_fill_triangle, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_FILLTRIANGLE}, + {"fill_quad", (PyCFunction)renderer_fill_quad, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_FILLQUAD}, + {"present", (PyCFunction)renderer_present, METH_NOARGS, + DOC_SDL2_VIDEO_RENDERER_PRESENT}, + {"clear", (PyCFunction)renderer_clear, METH_NOARGS, + DOC_SDL2_VIDEO_RENDERER_CLEAR}, + {"set_viewport", (PyCFunction)renderer_set_viewport, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_RENDERER_SETVIEWPORT}, + {"get_viewport", (PyCFunction)renderer_get_viewport, METH_NOARGS, + DOC_SDL2_VIDEO_RENDERER_GETVIEWPORT}, + {"compose_custom_blend_mode", (PyCFunction)compose_custom_blend_mode, METH_VARARGS | METH_KEYWORDS | METH_CLASS, + DOC_SDL2_VIDEO_RENDERER_COMPOSECUSTOMBLENDMODE}, + //{"to_surface", (PyCFunction)renderer_to_surface, METH_VARARGS | METH_KEYWORDS, + // DOC_SDL2_VIDEO_RENDERER_TOSURFACE}, + //{"blit", (PyCFunction)renderer_blit, METH_VARARGS | METH_KEYWORDS, + // DOC_SDL2_VIDEO_RENDERER_SETVIEWPORT}, + {NULL, NULL, 0, NULL} +}; + +static PyGetSetDef renderer_getset[] = { + {"draw_color", (getter)renderer_get_draw_color, (setter)renderer_set_draw_color, + DOC_SDL2_VIDEO_RENDERER_DRAWCOLOR, NULL}, + {"draw_blend_mode", (getter)renderer_get_draw_blend_mode, (setter)renderer_set_draw_blend_mode, + DOC_SDL2_VIDEO_RENDERER_DRAWCOLOR, NULL}, + {"logical_size", (getter)renderer_get_logical_size, (setter)renderer_set_logical_size, + DOC_SDL2_VIDEO_RENDERER_LOGICALSIZE, NULL}, + {"scale", (getter)renderer_get_scale, (setter)renderer_set_scale, + DOC_SDL2_VIDEO_RENDERER_SCALE, NULL}, + //{"target", (getter)renderer_get_target, (setter)renderer_set_target, + // DOC_SDL2_VIDEO_RENDERER_TARGET, NULL}, + {NULL, 0, NULL, NULL, NULL} +}; + +static PyTypeObject pgRenderer_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pygame._renderer.Renderer", + .tp_basicsize = sizeof(pgRendererObject), + .tp_dealloc = (destructor)renderer_dealloc, + .tp_doc = DOC_SDL2_VIDEO_RENDERER, + .tp_methods = renderer_methods, + .tp_init = (initproc)renderer_init, + .tp_new = PyType_GenericNew, + .tp_getset = renderer_getset}; + +static PyMethodDef _renderer_methods[] = { + //{"from_window", (PyCFunction)from_window, METH_VARARGS | METH_KEYWORDS, + // DOC_SDL2_VIDEO_GETGRABBEDWINDOW}, + {NULL, NULL, 0, NULL}}; + +MODINIT_DEFINE(_renderer) +{ + PyObject *module, *apiobj; + static void *c_api[PYGAMEAPI_RENDERER_NUMSLOTS]; + + static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT, + "_renderer", + "docs_needed", + -1, + _renderer_methods, + NULL, + NULL, + NULL, + NULL}; + + /* imported needed apis; Do this first so if there is an error + the module is not loaded. + */ + import_pygame_base(); + if (PyErr_Occurred()) { + return NULL; + } + + import_pygame_surface(); + if (PyErr_Occurred()) { + return NULL; + } + + import_pygame_rect(); + if (PyErr_Occurred()) { + return NULL; + } + + import_pygame_color(); + if (PyErr_Occurred()) { + return NULL; + } + + if (PyType_Ready(&pgRenderer_Type) < 0) { + return NULL; + } + + /* create the module */ + module = PyModule_Create(&_module); + if (module == 0) { + return NULL; + } + + Py_INCREF(&pgRenderer_Type); + if (PyModule_AddObject(module, "Renderer", (PyObject *)&pgRenderer_Type)) { + Py_DECREF(&pgRenderer_Type); + Py_DECREF(module); + return NULL; + } + + c_api[0] = &pgRenderer_Type; + apiobj = encapsulate_api(c_api, "_renderer"); + if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { + Py_XDECREF(apiobj); + Py_DECREF(module); + return NULL; + } + + return module; +} \ No newline at end of file diff --git a/src_c/static.c b/src_c/static.c index 2a760bf8e2..a1c3caeb9c 100644 --- a/src_c/static.c +++ b/src_c/static.c @@ -178,6 +178,15 @@ PyInit_pixelarray(void); PyMODINIT_FUNC PyInit__window(void); +PyMODINIT_FUNC +PyInit__renderer(void); + +PyMODINIT_FUNC +PyInit__image(void); + +PyMODINIT_FUNC +PyInit__texture(void); + // pygame_static module void @@ -307,6 +316,9 @@ PyInit_pygame_static() load_submodule("pygame.mixer", PyInit_mixer_music(), "music"); load_submodule("pygame", PyInit__window(), "_window"); + load_submodule("pygame", PyInit__renderer(), "_renderer"); + load_submodule("pygame", PyInit__texture(), "_texture"); + load_submodule("pygame", PyInit__image(), "_image"); load_submodule("pygame", PyInit_pixelarray(), "pixelarray"); @@ -353,6 +365,9 @@ PyInit_pygame_static() #include "simd_blitters_sse2.c" #include "window.c" +#include "renderer.c" +#include "texture.c" +#include "video_image.c" #undef pgVidInfo_Type #undef pgVidInfo_New diff --git a/src_c/texture.c b/src_c/texture.c new file mode 100644 index 0000000000..1b8c59a7f8 --- /dev/null +++ b/src_c/texture.c @@ -0,0 +1,615 @@ +#define PYGAMEAPI_TEXTURE_INTERNAL + +#include "pygame.h" + +#include "pgcompat.h" + +#include "doc/sdl2_video_doc.h" + +static PyTypeObject pgTexture_Type; + +#define TEXTURE_ERROR_CHECK(x) \ + if (x < 0) { \ + return RAISE(pgExc_SDLError, SDL_GetError()); \ + } \ + +#define pgTexture_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgTexture_Type)) + +static Uint32 +format_from_depth(int depth) { + Uint32 Rmask, Gmask, Bmask, Amask; + if (depth == 0 || depth == 32) { + Rmask = 0xFF << 16; + Gmask = 0xFF << 8; + Bmask = 0xFF; + Amask = 0xFF << 24; + } + else if (depth == 16) { + Rmask = 0xF << 8; + Gmask = 0xF << 4; + Bmask = 0xF; + Amask = 0xF << 12; + } + else { + RAISE(PyExc_ValueError, "no standard masks exist for given bitdepth with alpha"); + return -1; + } + return SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask); +} + +// From surface.c +static inline PyObject * +_get_rect_helper(PyObject *rect, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames) +{ + if (nargs > 0) { + Py_DECREF(rect); + return PyErr_Format(PyExc_TypeError, + "get_rect only accepts keyword arguments"); + } + if (rect && kwnames) { + Py_ssize_t i, sequence_len; + PyObject **sequence_items; + sequence_items = PySequence_Fast_ITEMS(kwnames); + sequence_len = PyTuple_GET_SIZE(kwnames); + + for (i = 0; i < sequence_len; ++i) { + if ((PyObject_SetAttr(rect, sequence_items[i], args[i]) == -1)) { + Py_DECREF(rect); + return NULL; + } + } + } + return rect; +} + +static PyObject * +texture_get_alpha(pgTextureObject *self, void *closure) { + Uint8 alpha; + TEXTURE_ERROR_CHECK(SDL_GetTextureAlphaMod(self->texture, &alpha)); + return PyLong_FromLong(alpha); +} + +static int +texture_set_alpha(pgTextureObject *self, PyObject *arg, void *closure) { + if (PyLong_Check(arg)) { + unsigned long longval = PyLong_AsUnsignedLong(arg); + TEXTURE_ERROR_CHECK(SDL_SetTextureAlphaMod(self->texture, (Uint8)longval)) + return 0; + } + return -1; +} + +static PyObject * +texture_get_blend_mode(pgTextureObject *self, void *closure) { + SDL_BlendMode blend_mode; + TEXTURE_ERROR_CHECK(SDL_GetTextureBlendMode(self->texture, &blend_mode)); + return PyLong_FromLong((long) blend_mode); +} + +static int +texture_set_blend_mode(pgTextureObject *self, PyObject *arg, void *closure) { + TEXTURE_ERROR_CHECK(SDL_SetTextureBlendMode(self->texture, (int) PyLong_AsLong(arg))) + return 0; +} + +static PyObject * +texture_get_color(pgTextureObject *self, void *closure) { + Uint8 color[4]; + TEXTURE_ERROR_CHECK(SDL_GetTextureColorMod(self->texture, &color[0], &color[1], &color[2])); + color[3] = 255; + return pgColor_NewLength(color, 4); +} + +static int +texture_set_color(pgTextureObject *self, PyObject *arg, void *closure) { + Uint8 color[4]; + if (!pg_RGBAFromObjEx(arg, color, PG_COLOR_HANDLE_ALL)) { + return -1; + } + TEXTURE_ERROR_CHECK(SDL_SetTextureColorMod(self->texture, color[0], color[1], color[2])) + return 0; +} + +static PyObject * +texture_get_rect(pgTextureObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs) { + PyObject *rect = pgRect_New4(0, 0, self->width, self->height); + return _get_rect_helper(rect, args, nargs, kwargs); +} + +static PyObject * +texture_draw(pgTextureObject *self, PyObject *args, PyObject *kwargs) { + PyObject *srcrectobj = Py_None, *dstrectobj = Py_None, *originobj = Py_None; + SDL_Rect srcrect, *srcrectptr = NULL; + SDL_Rect dstrect, *dstrectptr = NULL; + SDL_Point origin, *originptr = NULL; + double angle = 0; + int flip_x = 0; + int flip_y = 0; + SDL_RendererFlip flip = SDL_FLIP_NONE; + static char *keywords[] = {"srcrect", "dstrect", "angle", "origin", "flip_x", "flip_y", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOdOpp", keywords, + &srcrectobj, &dstrectobj, &angle, &originobj, &flip_x, &flip_y)) { + return NULL; /* Exception already set. */ + } + if (srcrectobj != Py_None) { + if (!pgRect_Check(srcrectobj)) { + return RAISE(PyExc_ValueError, "srcrectobj must be a rect, or None"); + } + srcrectptr = pgRect_FromObject(srcrectobj, &srcrect); + //srcrect = pgRect_AsRect(srcrectobj); + } + if (dstrectobj != Py_None) { + dstrectptr = pgRect_FromObject(dstrectobj, &dstrect); + if (dstrectptr == NULL) { + if (PySequence_Check(dstrectobj) && PySequence_Length(dstrectobj) == 2) { + if ((!pg_IntFromObjIndex(dstrectobj, 0, &dstrect.x)) || + (!pg_IntFromObjIndex(dstrectobj, 1, &dstrect.y))) { + return RAISE(PyExc_ValueError, "dstrect must be a position, rect, or None"); + } + dstrect.w = self->width; + dstrect.h = self->height; + } + } + } + if (originobj != Py_None) { + if (PySequence_Check(originobj) && PySequence_Length(originobj) == 2) { + if ((!pg_IntFromObjIndex(originobj, 0, &origin.x)) || + (!pg_IntFromObjIndex(originobj, 1, &origin.y))) { + return RAISE(PyExc_ValueError, "origin must be a point or None"); + } + originptr = &origin; + } + } + if (flip_x) flip |= SDL_FLIP_HORIZONTAL; + if (flip_y) flip |= SDL_FLIP_VERTICAL; + TEXTURE_ERROR_CHECK(SDL_RenderCopyEx(self->renderer->renderer, self->texture, srcrectptr, dstrectptr, angle, originptr, flip)) + Py_RETURN_NONE; +} + +static PyObject * +texture_draw_triangle(pgTextureObject *self, PyObject *args, PyObject *kwargs) { +#if SDL_VERSION_ATLEAST(2, 0, 18) + PyObject *p1_xyobj, *p2_xyobj, *p3_xyobj, *p1_uvobj = Py_None, *p2_uvobj = Py_None, *p3_uvobj = Py_None, *p1_modobj = Py_None, *p2_modobj = Py_None, *p3_modobj = Py_None; + Uint8 _r_mod, _g_mod, _b_mod, _a_mod; + float r_mod, g_mod, b_mod, a_mod; + SDL_Vertex vertices[3]; + float p1_xy[2], p2_xy[2], p3_xy[2], p1_uv[] = {0.0, 0.0}, p2_uv[] = {1.0, 1.0}, p3_uv[] = {0.0, 1.0}; + int p1_mod[] = {255, 255, 255, 255}, p2_mod[] = {255, 255, 255, 255}, p3_mod[] = {255, 255, 255, 255}; + static char *keywords[] = {"p1_xy", "p2_xy", "p3_xy", "p1_uv", "p2_uv", "p3_uv", "p1_mod", "p2_mod", "p3_mod", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO|OOOOOO", keywords, + &p1_xyobj, &p2_xyobj, &p3_xyobj, &p1_uvobj, &p2_uvobj, &p3_uvobj, &p1_modobj, &p2_modobj, &p3_modobj)) { + return NULL; /* Exception already set. */ + } + if (!pg_TwoFloatsFromObj(p1_xyobj, &p1_xy[0], &p1_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p1_xy argument"); + } + if (!pg_TwoFloatsFromObj(p2_xyobj, &p2_xy[0], &p2_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p2_xy argument"); + } + if (!pg_TwoFloatsFromObj(p3_xyobj, &p3_xy[0], &p3_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p3_xy argument"); + } + if (p1_uvobj != Py_None && !pg_TwoFloatsFromObj(p1_uvobj, &p1_uv[0], &p1_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p1_uv argument"); + } + if (p2_uvobj != Py_None && !pg_TwoFloatsFromObj(p2_uvobj, &p2_uv[0], &p2_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p2_uv argument"); + } + if (p3_uvobj != Py_None && !pg_TwoFloatsFromObj(p3_uvobj, &p3_uv[0], &p3_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p3_uv argument"); + } + if (p1_modobj != Py_None) { + if (!pg_IntFromObjIndex(p1_modobj, 0, &p1_mod[0]) || !pg_IntFromObjIndex(p1_modobj, 1, &p1_mod[1]) || + !pg_IntFromObjIndex(p1_modobj, 2, &p1_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p1_mod argument"); + } + if (PySequence_Size(p1_modobj) == 4) pg_IntFromObjIndex(p1_modobj, 3, &p1_mod[3]); + } + if (p2_modobj != Py_None) { + if (!pg_IntFromObjIndex(p2_modobj, 0, &p2_mod[0]) || !pg_IntFromObjIndex(p2_modobj, 1, &p2_mod[1]) || + !pg_IntFromObjIndex(p2_modobj, 2, &p2_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p2_mod argument"); + } + if (PySequence_Size(p2_modobj) == 4) pg_IntFromObjIndex(p2_modobj, 3, &p2_mod[3]); + } + if (p3_modobj != Py_None) { + if (!pg_IntFromObjIndex(p3_modobj, 0, &p3_mod[0]) || !pg_IntFromObjIndex(p3_modobj, 1, &p3_mod[1]) || + !pg_IntFromObjIndex(p3_modobj, 2, &p3_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p3_mod argument"); + } + if (PySequence_Size(p3_modobj) == 4) pg_IntFromObjIndex(p3_modobj, 3, &p3_mod[3]); + } + TEXTURE_ERROR_CHECK(SDL_GetTextureColorMod(self->texture, &_r_mod, &_g_mod, &_b_mod)); + TEXTURE_ERROR_CHECK(SDL_GetTextureAlphaMod(self->texture, &_a_mod)); + r_mod = _r_mod / (float)255.0; + g_mod = _g_mod / (float)255.0; + b_mod = _b_mod / (float)255.0; + a_mod = _a_mod / (float)255.0; + + vertices[0].position.x = p1_xy[0]; vertices[0].position.y = p1_xy[1]; vertices[0].tex_coord.x = p1_uv[0]; vertices[0].tex_coord.y = p1_uv[1]; + vertices[0].color.r = (int)r_mod * p1_mod[0]; vertices[0].color.g = (int)g_mod * p1_mod[1]; vertices[0].color.b = (int)b_mod * p1_mod[2]; vertices[0].color.a = (int)a_mod * p1_mod[3]; + vertices[1].position.x = p2_xy[0]; vertices[1].position.y = p2_xy[1]; vertices[1].tex_coord.x = p2_uv[0]; vertices[1].tex_coord.y = p2_uv[1]; + vertices[1].color.r = (int)r_mod * p2_mod[0]; vertices[1].color.g = (int)g_mod * p2_mod[1]; vertices[1].color.b = (int)b_mod * p2_mod[2]; vertices[1].color.a = (int)a_mod * p2_mod[3]; + vertices[2].position.x = p3_xy[0]; vertices[2].position.y = p3_xy[1]; vertices[2].tex_coord.x = p3_uv[0]; vertices[2].tex_coord.y = p3_uv[1]; + vertices[2].color.r = (int)r_mod * p3_mod[0]; vertices[2].color.g = (int)g_mod * p3_mod[1]; vertices[2].color.b = (int)b_mod * p3_mod[2]; vertices[2].color.a = (int)a_mod * p3_mod[3]; + TEXTURE_ERROR_CHECK(SDL_RenderGeometry(self->renderer->renderer, self->texture, vertices, 3, NULL, 0)) + Py_RETURN_NONE; +#else + RAISE(PyExc_TypeError, "draw_triangle() requires SDL 2.0.18 or newer"); + Py_RETURN_NONE; +#endif +} + +static PyObject * +texture_draw_quad(pgTextureObject *self, PyObject *args, PyObject *kwargs) { +#if SDL_VERSION_ATLEAST(2, 0, 18) + PyObject *p1_xyobj, *p2_xyobj, *p3_xyobj, *p4_xyobj, *p1_uvobj = Py_None, *p2_uvobj = Py_None, *p3_uvobj = Py_None, *p4_uvobj = Py_None, *p1_modobj = Py_None, *p2_modobj = Py_None, *p3_modobj = Py_None, *p4_modobj = Py_None; + Uint8 _r_mod, _g_mod, _b_mod, _a_mod; + float r_mod, g_mod, b_mod, a_mod; + SDL_Vertex vertices[6]; + float p1_xy[2], p2_xy[2], p3_xy[2], p4_xy[2], p1_uv[] = {0.0, 0.0}, p2_uv[] = {1.0, 0.0}, p3_uv[] = {1.0, 1.0}, p4_uv[] = {0.0, 1.0}; + int p1_mod[] = {255, 255, 255, 255}, p2_mod[] = {255, 255, 255, 255}, p3_mod[] = {255, 255, 255, 255}, p4_mod[] = {255, 255, 255, 255}; + static char *keywords[] = {"p1_xy", "p2_xy", "p3_xy", "p4_xy", "p1_uv", "p2_uv", "p3_uv", "p4_uv", "p1_mod", "p2_mod", "p3_mod", "p4_mod", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO|OOOOOOOO", keywords, + &p1_xyobj, &p2_xyobj, &p3_xyobj, &p4_xyobj, &p1_uvobj, &p2_uvobj, &p3_uvobj, &p4_uvobj, &p1_modobj, &p2_modobj, &p3_modobj, &p4_modobj)) { + return NULL; /* Exception already set. */ + } + if (!pg_TwoFloatsFromObj(p1_xyobj, &p1_xy[0], &p1_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p1_xy argument"); + } + if (!pg_TwoFloatsFromObj(p2_xyobj, &p2_xy[0], &p2_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p2_xy argument"); + } + if (!pg_TwoFloatsFromObj(p3_xyobj, &p3_xy[0], &p3_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p3_xy argument"); + } + if (!pg_TwoFloatsFromObj(p4_xyobj, &p4_xy[0], &p4_xy[1])) { + return RAISE(PyExc_TypeError, "invalid p3_xy argument"); + } + if (p1_uvobj != Py_None && !pg_TwoFloatsFromObj(p1_uvobj, &p1_uv[0], &p1_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p1_uv argument"); + } + if (p2_uvobj != Py_None && !pg_TwoFloatsFromObj(p2_uvobj, &p2_uv[0], &p2_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p2_uv argument"); + } + if (p3_uvobj != Py_None && !pg_TwoFloatsFromObj(p3_uvobj, &p3_uv[0], &p3_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p3_uv argument"); + } + if (p4_uvobj != Py_None && !pg_TwoFloatsFromObj(p3_uvobj, &p4_uv[0], &p4_uv[1])) { + return RAISE(PyExc_TypeError, "invalid p3_uv argument"); + } + if (p1_modobj != Py_None) { + if (!pg_IntFromObjIndex(p1_modobj, 0, &p1_mod[0]) || !pg_IntFromObjIndex(p1_modobj, 1, &p1_mod[1]) || + !pg_IntFromObjIndex(p1_modobj, 2, &p1_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p1_mod argument"); + } + if (PySequence_Size(p1_modobj) == 4) pg_IntFromObjIndex(p1_modobj, 3, &p1_mod[3]); + } + if (p2_modobj != Py_None) { + if (!pg_IntFromObjIndex(p2_modobj, 0, &p2_mod[0]) || !pg_IntFromObjIndex(p2_modobj, 1, &p2_mod[1]) || + !pg_IntFromObjIndex(p2_modobj, 2, &p2_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p2_mod argument"); + } + if (PySequence_Size(p2_modobj) == 4) pg_IntFromObjIndex(p2_modobj, 3, &p2_mod[3]); + } + if (p3_modobj != Py_None) { + if (!pg_IntFromObjIndex(p3_modobj, 0, &p3_mod[0]) || !pg_IntFromObjIndex(p3_modobj, 1, &p3_mod[1]) || + !pg_IntFromObjIndex(p3_modobj, 2, &p3_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p3_mod argument"); + } + if (PySequence_Size(p3_modobj) == 4) pg_IntFromObjIndex(p3_modobj, 3, &p3_mod[3]); + } + if (p4_modobj != Py_None) { + if (!pg_IntFromObjIndex(p4_modobj, 0, &p4_mod[0]) || !pg_IntFromObjIndex(p4_modobj, 1, &p4_mod[1]) || + !pg_IntFromObjIndex(p4_modobj, 2, &p4_mod[2])) { + return RAISE(PyExc_TypeError, "invalid p4_mod argument"); + } + if (PySequence_Size(p4_modobj) == 4) pg_IntFromObjIndex(p4_modobj, 3, &p4_mod[3]); + } + TEXTURE_ERROR_CHECK(SDL_GetTextureColorMod(self->texture, &_r_mod, &_g_mod, &_b_mod)); + TEXTURE_ERROR_CHECK(SDL_GetTextureAlphaMod(self->texture, &_a_mod)); + + r_mod = _r_mod / (float)255.0; + g_mod = _g_mod / (float)255.0; + b_mod = _b_mod / (float)255.0; + a_mod = _a_mod / (float)255.0; + + vertices[0].position.x = p1_xy[0]; vertices[0].position.y = p1_xy[1]; vertices[0].tex_coord.x = p1_uv[0]; vertices[0].tex_coord.y = p1_uv[1]; + vertices[0].color.r = (int)r_mod * p1_mod[0]; vertices[0].color.g = (int)g_mod * p1_mod[1]; vertices[0].color.b = (int)b_mod * p1_mod[2]; vertices[0].color.a = (int)a_mod * p1_mod[3]; + vertices[1].position.x = p2_xy[0]; vertices[1].position.y = p2_xy[1]; vertices[1].tex_coord.x = p2_uv[0]; vertices[1].tex_coord.y = p2_uv[1]; + vertices[1].color.r = (int)r_mod * p2_mod[0]; vertices[1].color.g = (int)g_mod * p2_mod[1]; vertices[1].color.b = (int)b_mod * p2_mod[2]; vertices[1].color.a = (int)a_mod * p2_mod[3]; + vertices[2].position.x = p3_xy[0]; vertices[2].position.y = p3_xy[1]; vertices[2].tex_coord.x = p3_uv[0]; vertices[2].tex_coord.y = p3_uv[1]; + vertices[2].color.r = (int)r_mod * p3_mod[0]; vertices[2].color.g = (int)g_mod * p3_mod[1]; vertices[2].color.b = (int)b_mod * p3_mod[2]; vertices[2].color.a = (int)a_mod * p3_mod[3]; + vertices[3].position.x = p3_xy[0]; vertices[3].position.y = p3_xy[1]; vertices[3].tex_coord.x = p3_uv[0]; vertices[3].tex_coord.y = p3_uv[1]; + vertices[3].color.r = (int)r_mod * p3_mod[0]; vertices[3].color.g = (int)g_mod * p3_mod[1]; vertices[3].color.b = (int)b_mod * p3_mod[2]; vertices[3].color.a = (int)a_mod * p3_mod[3]; + vertices[4].position.x = p4_xy[0]; vertices[4].position.y = p4_xy[1]; vertices[4].tex_coord.x = p4_uv[0]; vertices[4].tex_coord.y = p4_uv[1]; + vertices[4].color.r = (int)r_mod * p4_mod[0]; vertices[4].color.g = (int)g_mod * p4_mod[1]; vertices[4].color.b = (int)b_mod * p4_mod[2]; vertices[4].color.a = (int)a_mod * p4_mod[3]; + vertices[5].position.x = p1_xy[0]; vertices[5].position.y = p1_xy[1]; vertices[5].tex_coord.x = p1_uv[0]; vertices[5].tex_coord.y = p1_uv[1]; + vertices[5].color.r = (int)r_mod * p1_mod[0]; vertices[5].color.g = (int)g_mod * p1_mod[1]; vertices[5].color.b = (int)b_mod * p1_mod[2]; vertices[5].color.a = (int)a_mod * p1_mod[3]; + TEXTURE_ERROR_CHECK(SDL_RenderGeometry(self->renderer->renderer, self->texture, vertices, 6, NULL, 0)) + Py_RETURN_NONE; +#else + RAISE(PyExc_TypeError, "draw_triangle() requires SDL 2.0.18 or newer"); + Py_RETURN_NONE; +#endif +} + +static PyObject * +texture_update(pgTextureObject *self, PyObject *args, PyObject *kwargs) { + pgSurfaceObject *surfobj; + PyObject *rectobj = Py_None; + SDL_Surface *surf = NULL; + SDL_Rect area, *areaptr = NULL; + SDL_Surface *converted_surf = NULL; + SDL_PixelFormat *pixel_format = NULL; + SDL_BlendMode blend; + Uint32 format; + int res; + int dst_width, dst_height; + static char *keywords[] = {"surface", "area", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", keywords, + &pgSurface_Type, &surfobj, &rectobj)) { + return NULL; /* Exception already set. */ + } + surf = pgSurface_AsSurface(surfobj); + SURF_INIT_CHECK(surf) + area.x = 0; + area.y = 0; + if (rectobj != Py_None) { + if (!pgRect_Check(rectobj)) { + return RAISE(PyExc_ValueError, "area must be a rectangle or None"); + } + areaptr = pgRect_FromObject(rectobj, &area); + } + if (areaptr == NULL) { + dst_width = self->width; + dst_height = self->height; + } + else { + dst_width = area.w; + dst_height = area.h; + } + if (dst_width > surf->w || dst_height > surf->h) { + areaptr = &area; + area.w = surf->w; + area.h = surf->h; + } + TEXTURE_ERROR_CHECK(SDL_QueryTexture(self->texture, &format, NULL, NULL, NULL)) + if (format != surf->format->format) { + TEXTURE_ERROR_CHECK(SDL_GetSurfaceBlendMode(surf, &blend)) + pixel_format = SDL_AllocFormat(format); + if (pixel_format == NULL) return RAISE(pgExc_SDLError, SDL_GetError()); + converted_surf = SDL_ConvertSurface(surf, pixel_format, 0); + if (SDL_SetSurfaceBlendMode(converted_surf, blend) < 0) { + SDL_FreeSurface(converted_surf); + SDL_FreeFormat(pixel_format); + return RAISE(pgExc_SDLError, SDL_GetError()); + } + + res = SDL_UpdateTexture(self->texture, areaptr, converted_surf->pixels, converted_surf->pitch); + SDL_FreeSurface(converted_surf); + SDL_FreeFormat(pixel_format); + } + else { + res = SDL_UpdateTexture(self->texture, areaptr, surf->pixels, surf->pitch); + } + if (res < 0) return RAISE(pgExc_SDLError, SDL_GetError()); + Py_RETURN_NONE; +} + +static PyObject * +from_surface(PyObject *self, PyObject *args, PyObject *kwargs) { + pgRendererObject *renderer; + pgSurfaceObject *surfobj; + pgTextureObject *new_texture; + SDL_Surface *surf = NULL; + static char *keywords[] = {"renderer", "surface", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO!", keywords, + &renderer, &pgSurface_Type, &surfobj)) { + return NULL; /* Exception already set. */ + } + new_texture = (pgTextureObject *)(&(pgTexture_Type))->tp_alloc(&pgTexture_Type, 0); + surf = pgSurface_AsSurface(surfobj); + SURF_INIT_CHECK(surf) + new_texture->texture = SDL_CreateTextureFromSurface(renderer->renderer, surf); + if (!new_texture->texture) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + new_texture->renderer = renderer; + new_texture->width = surf->w; + new_texture->height = surf->h; + return (PyObject *)new_texture; +} + +static int +texture_init(pgTextureObject *self, PyObject *args, PyObject *kwargs) { + SDL_Texture *texture = NULL; + pgRendererObject *renderer; + PyObject *sizeobj; + int width; + int height; + int depth = 0; + int staticc = 0; + int streaming = 0; + int target = 0; + int scale_quality = -1; + int access = SDL_TEXTUREACCESS_STATIC; + Uint32 format; + + char *keywords[] = {"renderer", "size", "depth", "static", "streaming", "target", "scale_quality", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ipppi", keywords, &renderer, &sizeobj, &depth, &staticc, &streaming, &target, &scale_quality)) { + return -1; + } + format = format_from_depth(depth); + if (!pg_TwoIntsFromObj(sizeobj, &width, &height)) { + RAISE(PyExc_TypeError, "invalid size argument"); + return -1; + } + if (width <= 0 || height <= 0) { + RAISE(PyExc_ValueError, "size must contain two positive values"); + return -1; + } + if (staticc) { + if (streaming || target) { + RAISE(PyExc_ValueError, "only one of static, streaming, or target can be true"); + return -1; + } + access = SDL_TEXTUREACCESS_STATIC; + } + if (streaming) { + if (staticc || target) { + RAISE(PyExc_ValueError, "only one of static, streaming, or target can be true"); + return -1; + } + access = SDL_TEXTUREACCESS_STREAMING; + } + if (target) { + if (staticc || streaming) { + RAISE(PyExc_ValueError, "only one of static, streaming, or target can be true"); + return -1; + } + access = SDL_TEXTUREACCESS_TARGET; + } + self->renderer = renderer; + self->texture = SDL_CreateTexture(renderer->renderer, format, access, width, height); + if (!self->texture) { + RAISE(pgExc_SDLError, SDL_GetError()); + return -1; + } + if (scale_quality != -1) { +#if SDL_VERSION_ATLEAST(2,0,12) + if (SDL_SetTextureScaleMode(self->texture, scale_quality) < 0) { + RAISE(pgExc_SDLError, SDL_GetError()); + return -1; + } +#else + switch (scale_quality) { + case 0: + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); break; + case 1: + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); break; + case 2: + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); break; + } +#endif + } + self->width = width; + self->height = height; + return 0; +} + +static void +texture_dealloc(pgTextureObject *self, PyObject *_null) { + if (self->texture) { + SDL_DestroyTexture(self->texture); + } + Py_TYPE(self)->tp_free(self); +} + +static PyMethodDef texture_methods[] = { + {"get_rect", (PyCFunction)texture_get_rect, METH_FASTCALL | METH_KEYWORDS, + DOC_SDL2_VIDEO_TEXTURE_GETRECT}, + {"draw", (PyCFunction)texture_draw, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_TEXTURE_DRAW}, + {"draw_triangle", (PyCFunction)texture_draw_triangle, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_TEXTURE_DRAWTRIANGLE}, + {"draw_quad", (PyCFunction)texture_draw_quad, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_TEXTURE_DRAWQUAD}, + {"update", (PyCFunction)texture_update, METH_VARARGS | METH_KEYWORDS, + DOC_SDL2_VIDEO_TEXTURE_UPDATE}, + {"from_surface", (PyCFunction)from_surface, METH_VARARGS | METH_KEYWORDS | METH_CLASS, + DOC_SDL2_VIDEO_TEXTURE_FROMSURFACE}, + {NULL, NULL, 0, NULL} +}; + +static PyGetSetDef texture_getset[] = { + {"alpha", (getter)texture_get_alpha, (setter)texture_set_alpha, + DOC_SDL2_VIDEO_TEXTURE_ALPHA, NULL}, + {"blend_mode", (getter)texture_get_blend_mode, (setter)texture_set_blend_mode, + DOC_SDL2_VIDEO_TEXTURE_BLENDMODE, NULL}, + {"color", (getter)texture_get_color, (setter)texture_set_color, + DOC_SDL2_VIDEO_TEXTURE_COLOR, NULL}, + {NULL, 0, NULL, NULL, NULL} +}; + +static PyTypeObject pgTexture_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pygame._texture.Texture", + .tp_basicsize = sizeof(pgTextureObject), + .tp_dealloc = (destructor)texture_dealloc, + .tp_doc = DOC_SDL2_VIDEO_TEXTURE, + .tp_methods = texture_methods, + .tp_init = (initproc)texture_init, + .tp_new = PyType_GenericNew, + .tp_getset = texture_getset}; + +static PyMethodDef _texture_methods[] = { + {NULL, NULL, 0, NULL}}; + +MODINIT_DEFINE(_texture) +{ + PyObject *module, *apiobj; + static void *c_api[PYGAMEAPI_TEXTURE_NUMSLOTS]; + + static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT, + "_texture", + "docs_needed", + -1, + _texture_methods, + NULL, + NULL, + NULL, + NULL}; + + /* imported needed apis; Do this first so if there is an error + the module is not loaded. + */ + import_pygame_base(); + if (PyErr_Occurred()) { + return NULL; + } + + import_pygame_surface(); + if (PyErr_Occurred()) { + return NULL; + } + + import_pygame_rect(); + if (PyErr_Occurred()) { + return NULL; + } + + + import_pygame_color(); + if (PyErr_Occurred()) { + return NULL; + } + + if (PyType_Ready(&pgTexture_Type) < 0) { + return NULL; + } + + /* create the module */ + module = PyModule_Create(&_module); + if (module == 0) { + return NULL; + } + + Py_INCREF(&pgTexture_Type); + if (PyModule_AddObject(module, "Texture", (PyObject *)&pgTexture_Type)) { + Py_DECREF(&pgTexture_Type); + Py_DECREF(module); + return NULL; + } + + c_api[0] = &pgTexture_Type; + apiobj = encapsulate_api(c_api, "_texture"); + if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { + Py_XDECREF(apiobj); + Py_DECREF(module); + return NULL; + } + + return module; +} diff --git a/src_c/video_image.c b/src_c/video_image.c new file mode 100644 index 0000000000..121a6bb53b --- /dev/null +++ b/src_c/video_image.c @@ -0,0 +1,106 @@ +#define PYGAMEAPI_IMAGE_INTERNAL + +#include "pygame.h" + +#include "pgcompat.h" + +#include "doc/sdl2_video_doc.h" + +static PyTypeObject pgImage_Type; + +#define pgImage_Check(x) \ + (PyObject_IsInstance((x), (PyObject *)&pgImage_Type)) + +static int +image_init(pgImageObject *self, PyObject *args, PyObject *kwargs) { + printf("Image: %d %d %d %d" self->angle, self->origin.x, self->origin.y, self->flip); + PyObject *texture_or_imageobj; + PyObject *srcrectobj; + char *keywords[] = {"texture_or_image", "srcrect", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", keywords, &texture_or_imageobj, &srcrectobj)) { + return -1; + } + +} + +static void +image_dealloc(pgImageObject *self, PyObject *_null) { + Py_TYPE(self)->tp_free(self); +} + +static PyMethodDef image_methods[] = { +// {"draw", (PyCFunction)image_draw, METH_VARARGS | METH_KEYWORDS, +// DOC_SDL2_VIDEO_IMAGE_DRAW}, + {NULL, NULL, 0, NULL} +}; + +static PyGetSetDef image_getset[] = { +// {"color", (getter)image_get_color, (setter)image_set_color, +// DOC_SDL2_VIDEO_IMAGE_COLOR, NULL}, + {NULL, 0, NULL, NULL, NULL} +}; + + +static PyMethodDef _image_methods[] = { + {NULL, NULL, 0, NULL}}; + +static PyTypeObject pgImage_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pygame._image.Image", + .tp_basicsize = sizeof(pgImageObject), + .tp_dealloc = (destructor)image_dealloc, + .tp_doc = DOC_SDL2_VIDEO_IMAGE, + .tp_methods = image_methods, + .tp_init = (initproc)image_init, + .tp_new = PyType_GenericNew, + .tp_getset = image_getset}; + +MODINIT_DEFINE(_image) +{ + PyObject *module, *apiobj; + static void *c_api[PYGAMEAPI_IMAGE_NUMSLOTS]; + + static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT, + "_image", + "docs_needed", + -1, + _image_methods, + NULL, + NULL, + NULL, + NULL}; + + /* imported needed apis; Do this first so if there is an error + the module is not loaded. + */ + import_pygame_base(); + if (PyErr_Occurred()) { + return NULL; + } + + if (PyType_Ready(&pgImage_Type) < 0) { + return NULL; + } + + /* create the module */ + module = PyModule_Create(&_module); + if (module == 0) { + return NULL; + } + + Py_INCREF(&pgImage_Type); + if (PyModule_AddObject(module, "Image", (PyObject *)&pgImage_Type)) { + Py_DECREF(&pgImage_Type); + Py_DECREF(module); + return NULL; + } + + c_api[0] = &pgImage_Type; + apiobj = encapsulate_api(c_api, "_image"); + if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { + Py_XDECREF(apiobj); + Py_DECREF(module); + return NULL; + } + + return module; +} \ No newline at end of file diff --git a/src_py/__init__.py b/src_py/__init__.py index 731074bcb8..4b9f2066c6 100644 --- a/src_py/__init__.py +++ b/src_py/__init__.py @@ -302,6 +302,21 @@ def PixelArray(surface): # pylint: disable=unused-argument except (ImportError, OSError): debug = MissingModule("_debug", urgent=0) +try: + import pygame._renderer +except (ImportError, OSError): + _renderer = MissingModule("_renderer", urgent=0) + +try: + import pygame._texture +except (ImportError, OSError): + _texture = MissingModule("_texture", urgent=0) + +try: + import pygame._image +except (ImportError, OSError): + _image = MissingModule("_image", urgent=0) + try: import pygame.system from pygame._data_classes import PowerState as power_state