Skip to content

Commit 88d3eff

Browse files
committed
rewrote transform.pixelate and wrote tests
1 parent 463eeca commit 88d3eff

File tree

2 files changed

+59
-85
lines changed

2 files changed

+59
-85
lines changed

src_c/transform.c

Lines changed: 28 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include <math.h>
3434
#include <string.h>
35+
#include <limits.h>
3536

3637
#include "simd_shared.h"
3738
#include "simd_transform.h"
@@ -4225,96 +4226,14 @@ surf_invert(PyObject *self, PyObject *args, PyObject *kwargs)
42254226
return (PyObject *)pgSurface_New(newsurf);
42264227
}
42274228

4228-
SDL_Surface *
4229-
pixelate(pgSurfaceObject *srcobj, pgSurfaceObject *dstobj, int pixel_size)
4230-
{
4231-
SDL_Surface *src = pgSurface_AsSurface(srcobj);
4232-
SDL_Surface *newsurf;
4233-
4234-
if (!dstobj) {
4235-
newsurf = newsurf_fromsurf(src, srcobj->surf->w, srcobj->surf->h);
4236-
if (!newsurf)
4237-
return NULL;
4238-
}
4239-
else {
4240-
newsurf = pgSurface_AsSurface(dstobj);
4241-
}
4242-
4243-
if (pixel_size < 1) {
4244-
return (SDL_Surface *)(RAISE(PyExc_ValueError,
4245-
"pixel_size must be greater than 0."));
4246-
}
4247-
4248-
if (newsurf->w != src->w || newsurf->h != src->h) {
4249-
return (SDL_Surface *)(RAISE(
4250-
PyExc_ValueError,
4251-
"Destination surface must be the same size as source surface."));
4252-
}
4253-
4254-
if (src->format->BytesPerPixel != newsurf->format->BytesPerPixel) {
4255-
return (SDL_Surface *)(RAISE(
4256-
PyExc_ValueError,
4257-
"Source and destination surfaces need the same format."));
4258-
}
4259-
4260-
int x, y;
4261-
for (y = 0; y < src->h; y += pixel_size) {
4262-
for (x = 0; x < src->w; x += pixel_size) {
4263-
unsigned char r, g, b, a; // current
4264-
Uint32 ra, ga, ba, aa; // averages
4265-
Uint16 size;
4266-
Uint32 color, average;
4267-
Uint8 *pix;
4268-
int width = (pixel_size > (src->w - x)) ? src->w - x : pixel_size;
4269-
int height = (pixel_size > (src->h - y)) ? src->h - y : pixel_size;
4270-
4271-
ra = 0;
4272-
ga = 0;
4273-
ba = 0;
4274-
aa = 0;
4275-
for (int w = 0; w < width; w++) {
4276-
for (int h = 0; h < height; h++) {
4277-
SURF_GET_AT(color, src, x + w, y + h, (Uint8 *)src->pixels,
4278-
src->format, pix);
4279-
SDL_GetRGBA(color, src->format, &r, &g, &b, &a);
4280-
ra += r;
4281-
ga += g;
4282-
ba += b;
4283-
aa += a;
4284-
}
4285-
}
4286-
size = width * height;
4287-
4288-
average = SDL_MapRGBA(newsurf->format, (Uint8)(ra / size),
4289-
(Uint8)(ga / size), (Uint8)(ba / size),
4290-
(Uint8)(aa / size));
4291-
4292-
for (int w = 0; w < width; w++) {
4293-
for (int h = 0; h < height; h++) {
4294-
SURF_SET_AT(average, newsurf, x + w, y + h,
4295-
(Uint8 *)newsurf->pixels, newsurf->format,
4296-
pix);
4297-
}
4298-
}
4299-
}
4300-
}
4301-
4302-
SDL_UnlockSurface(newsurf);
4303-
4304-
return newsurf;
4305-
}
4306-
4307-
/*
4308-
* anticipated API: pygame.transform.pixelate(surface, pixel_size, dest_surface
4309-
* = None)
4310-
*/
43114229
static PyObject *
43124230
surf_pixelate(PyObject *self, PyObject *args, PyObject *kwargs)
43134231
{
43144232
pgSurfaceObject *src;
43154233
pgSurfaceObject *dst = NULL;
43164234
int pixel_size;
43174235
SDL_Surface *new_surf;
4236+
pgSurfaceObject *intermediate;
43184237

43194238
static char *kwds[] = {"surface", "pixel_size", "dest_surface", NULL};
43204239

@@ -4324,12 +4243,36 @@ surf_pixelate(PyObject *self, PyObject *args, PyObject *kwargs)
43244243
return NULL;
43254244
}
43264245

4327-
new_surf = pixelate(src, dst, pixel_size);
4246+
double testWidth = round((double)src->surf->w / pixel_size);
4247+
double testHeight = round((double)src->surf->h / pixel_size);
43284248

4329-
if (!new_surf) {
4249+
if (testWidth > INT_MAX || testWidth <= 0)
4250+
{
4251+
PyErr_SetString(PyExc_OverflowError, "Cannot scale width outside the range [0, INT_MAX]");
4252+
return NULL;
4253+
}
4254+
4255+
if (testHeight > INT_MAX || testHeight <= 0)
4256+
{
4257+
PyErr_SetString(PyExc_OverflowError, "Cannot scale height outside the range [0, INT_MAX]");
43304258
return NULL;
43314259
}
43324260

4261+
int width = (int)testWidth;
4262+
int height = (int)testHeight;
4263+
4264+
SDL_Surface* temp = scale_to(src, NULL, width, height);
4265+
intermediate = pgSurface_New(temp);
4266+
if (intermediate == NULL)
4267+
{
4268+
return NULL; /* Exception already set in scale_to */
4269+
}
4270+
new_surf = scale_to(intermediate, dst, src->surf->w, src->surf->h);
4271+
if (new_surf == NULL)
4272+
{
4273+
return NULL; /* Exception already set in scale_to */
4274+
}
4275+
43334276
if (dst) {
43344277
Py_INCREF(dst);
43354278
return (PyObject *)dst;

test/transform_test.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
from pygame.tests import test_utils
99
from pygame.tests.test_utils import example_path
1010

11+
def surfaces_have_same_pixels(surf1, surf2):
12+
if surf1.get_size() != surf2.get_size():
13+
return False
14+
15+
for row in range(surf1.get_height()):
16+
for col in range(surf1.get_width()):
17+
if surf1.get_at((col, row)) != surf2.get_at((col, row)):
18+
return False
19+
20+
return True
1121

1222
def show_image(s, images=[]):
1323
# pygame.display.init()
@@ -1759,7 +1769,28 @@ def smoothscale_invalid_scale():
17591769
smaller_surface.get_at(((k // 2), 0)), pygame.Color(127, 127, 127)
17601770
)
17611771
self.assertEqual(smaller_surface.get_size(), (k, 1))
1772+
1773+
def test_pixelate(self):
1774+
"""Test pygame.transform.pixelate"""
1775+
# test that pixelating the original with a pixel_size of 1 yields the original back
1776+
data_fname = example_path("data")
1777+
path = os.path.join(data_fname, "alien3.png")
1778+
image = pygame.image.load(path) # Get an indexed surface.
1779+
1780+
no_change = pygame.transform.pixelate(image, 1)
1781+
1782+
self.assertTrue(surfaces_have_same_pixels(image, no_change))
1783+
1784+
# test that pixelating a square image with a pixel_size equal to the side length
1785+
# yields a surface of a single color, which is the average of the surf
1786+
square = pygame.transform.scale(image, (50, 50))
1787+
square_pixelated = pygame.transform.pixelate(square, 50)
1788+
square_resized = pygame.transform.scale(
1789+
pygame.transform.scale(square, (1, 1)),
1790+
square.get_size()
1791+
)
17621792

1793+
self.assertTrue(surfaces_have_same_pixels(square_pixelated, square_resized))
17631794

17641795
class TransformDisplayModuleTest(unittest.TestCase):
17651796
def setUp(self):

0 commit comments

Comments
 (0)