Skip to content

Commit

Permalink
Another pg_Int/Float helper optimization approach.
Browse files Browse the repository at this point in the history
  • Loading branch information
Starbuck5 committed Nov 8, 2024
1 parent 4de5466 commit 5cb1096
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 81 deletions.
109 changes: 83 additions & 26 deletions src_c/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,24 +496,19 @@ pg_base_get_init(PyObject *self, PyObject *_null)
static int
pg_IntFromObj(PyObject *obj, int *val)
{
int tmp_val;

if (PyFloat_Check(obj)) {
/* Python3.8 complains with deprecation warnings if we pass
* floats to PyLong_AsLong.
*/
double dv = PyFloat_AsDouble(obj);
tmp_val = (int)dv;
*val = (int)PyFloat_AS_DOUBLE(obj);
}
else {
tmp_val = PyLong_AsLong(obj);
}

if (tmp_val == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
*val = PyLong_AsLong(obj);
if (*val == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
}
*val = tmp_val;
return 1;
}

Expand All @@ -535,25 +530,55 @@ pg_IntFromObjIndex(PyObject *obj, int _index, int *val)
static int
pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2)
{
if (PyTuple_Check(obj) && PyTuple_Size(obj) == 1) {
// First, lets check the size. This returns -1 if invalid and may set an error.
Py_ssize_t obj_size = PySequence_Size(obj);

// If the object is a tuple of one element, try that one element.
if (obj_size == 1 && PyTuple_Check(obj)) {
return pg_TwoIntsFromObj(PyTuple_GET_ITEM(obj, 0), val1, val2);
}
if (!PySequence_Check(obj) || PySequence_Length(obj) != 2) {

// Otherwise lets make sure this is a legit sequence and has two elements.
// Some objects can passing PySequence_Size but fail PySequence_Check
// (like sets)
if (obj_size != 2 || !PySequence_Check(obj)) {
PyErr_Clear(); // Clear the potential error from PySequence_Size
return 0;
}

// Can use PySequence_ITEM because of the PySequence_Check above.
// Now we can extract the items, using this macro because we know
// obj is a PySequence.
PyObject *item1 = PySequence_ITEM(obj, 0);
PyObject *item2 = PySequence_ITEM(obj, 1);

// If either item is NULL lets get out of here
if (item1 == NULL || item2 == NULL) {
Py_XDECREF(item1);
Py_XDECREF(item2);
PyErr_Clear();
return 0;
}

if (!pg_IntFromObj(item1, val1) || !pg_IntFromObj(item2, val2)) {
// Fastest way to extract numbers I tested (in Python 3.13) is to extract
// Python floats as doubles with the below macro, and get everything else
// through PyLong_AsLong, using C casting to turn into the final type.
if (PyFloat_Check(item1)) {
*val1 = (int)PyFloat_AS_DOUBLE(item1);
}
else {
*val1 = PyLong_AsLong(item1);
}

if (PyFloat_Check(item2)) {
*val2 = (int)PyFloat_AS_DOUBLE(item2);
}
else {
*val2 = PyLong_AsLong(item2);
}

// This catches the case where either of the PyLong_AsLong's failed
if ((*val1 == -1 || *val2 == -1) && PyErr_Occurred()) {
PyErr_Clear();
Py_DECREF(item1);
Py_DECREF(item2);
return 0;
Expand All @@ -567,14 +592,16 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2)
static int
pg_FloatFromObj(PyObject *obj, float *val)
{
float f = (float)PyFloat_AsDouble(obj);

if (f == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
if (PyFloat_Check(obj)) {
*val = (float)PyFloat_AS_DOUBLE(obj);
}
else {
*val = (float)PyLong_AsLong(obj);
if (*val == -1.0f && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
}

*val = f;
return 1;
}

Expand All @@ -596,25 +623,55 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val)
static int
pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2)
{
if (PyTuple_Check(obj) && PyTuple_Size(obj) == 1) {
// First, lets check the size. This returns -1 if invalid and may set an error.
Py_ssize_t obj_size = PySequence_Size(obj);

// If the object is a tuple of one element, try that one element.
if (obj_size == 1 && PyTuple_Check(obj)) {
return pg_TwoFloatsFromObj(PyTuple_GET_ITEM(obj, 0), val1, val2);
}
if (!PySequence_Check(obj) || PySequence_Length(obj) != 2) {

// Otherwise lets make sure this is a legit sequence and has two elements.
// Some objects can passing PySequence_Size but fail PySequence_Check
// (like sets)
if (obj_size != 2 || !PySequence_Check(obj)) {
PyErr_Clear(); // Clear the potential error from PySequence_Size
return 0;
}

// Can use PySequence_ITEM because of the PySequence_Check above.
// Now we can extract the items, using this macro because we know
// obj is a PySequence.
PyObject *item1 = PySequence_ITEM(obj, 0);
PyObject *item2 = PySequence_ITEM(obj, 1);

// If either item is NULL lets get out of here
if (item1 == NULL || item2 == NULL) {
Py_XDECREF(item1);
Py_XDECREF(item2);
PyErr_Clear();
return 0;
}

if (!pg_FloatFromObj(item1, val1) || !pg_FloatFromObj(item2, val2)) {
// Fastest way to extract numbers I tested (in Python 3.13) is to extract
// Python floats as doubles with the below macro, and get everything else
// through PyLong_AsLong, using C casting to turn into the final type.
if (PyFloat_Check(item1)) {
*val1 = (float)PyFloat_AS_DOUBLE(item1);
}
else {
*val1 = (float)PyLong_AsLong(item1);
}

if (PyFloat_Check(item2)) {
*val2 = (float)PyFloat_AS_DOUBLE(item2);
}
else {
*val2 = (float)PyLong_AsLong(item2);
}

// This catches the case where either of the PyLong_AsLong's failed
if ((*val1 == -1.0f || *val2 == -1.0f) && PyErr_Occurred()) {
PyErr_Clear();
Py_DECREF(item1);
Py_DECREF(item2);
return 0;
Expand Down
1 change: 1 addition & 0 deletions src_c/rect_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@
#endif // RectOptional_FREELIST
// #endregion RectOptional

// This is the thing that is whack
#define PrimitiveType RectImport_primitiveType
#define RectObject RectImport_RectObject
#define OtherRectObject RectImport_OtherRectObject
Expand Down
76 changes: 21 additions & 55 deletions src_c/surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
&depth, &masks))
return -1;

// REPLACE with pg_TwoIntsFromObj
if (PySequence_Check(size) && PySequence_Length(size) == 2) {
if ((!pg_IntFromObjIndex(size, 0, &width)) ||
(!pg_IntFromObjIndex(size, 1, &height))) {
Expand Down Expand Up @@ -711,63 +712,28 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
static PyObject *
surf_get_at(PyObject *self, PyObject *position)
{
SDL_Surface *surf = pgSurface_AsSurface(self);
SDL_PixelFormat *format = NULL;
Uint8 *pixels = NULL;
int x, y;
Uint32 color;
Uint8 *pix;
Uint8 rgba[4] = {0, 0, 0, 255};

SURF_INIT_CHECK(surf)

if (!pg_TwoIntsFromObj(position, &x, &y)) {
return RAISE(PyExc_TypeError,
"position must be a sequence of two numbers");
}

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

format = surf->format;

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

if (!pgSurface_Lock((pgSurfaceObject *)self))
return NULL;

pixels = (Uint8 *)surf->pixels;

switch (PG_FORMAT_BytesPerPixel(format)) {
case 1:
color = (Uint32) * ((Uint8 *)pixels + y * surf->pitch + x);
SDL_GetRGB(color, format, rgba, rgba + 1, rgba + 2);
break;
case 2:
color = (Uint32) * ((Uint16 *)(pixels + y * surf->pitch) + x);
SDL_GetRGBA(color, format, rgba, rgba + 1, rgba + 2, rgba + 3);
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
SDL_GetRGB(color, format, rgba, rgba + 1, rgba + 2);
break;
default: /* case 4: */
assert(PG_FORMAT_BytesPerPixel(format) == 4);
color = *((Uint32 *)(pixels + y * surf->pitch) + x);
SDL_GetRGBA(color, format, rgba, rgba + 1, rgba + 2, rgba + 3);
break;
//int x;// y;
float fx, fy;
//float fx;
//double dx, dy;

// idea: do some more exact checks first and then go into the unexact
// checks.

// PyObject_Print(position, stdout, 0);

for (int i = 0; i < 10000000; i++) {
//if (!pg_IntFromObj(position, &x)) {
//if (!pg_FloatFromObj(position, &fx)) {
//if (!pg_TwoIntsFromObj(position, &x, &y)) {
if (!pg_TwoFloatsFromObj(position, &fx, &fy)) {
// if (!pg_TwoDoublesFromObj(position, &dx, &dy)) {
return RAISE(PyExc_TypeError,
"position must be a sequence of two numbers");
}
}
if (!pgSurface_Unlock((pgSurfaceObject *)self))
return NULL;

return pgColor_New(rgba);
Py_RETURN_NONE;
}

static PyObject *
Expand Down

0 comments on commit 5cb1096

Please sign in to comment.