Skip to content

Commit 624955f

Browse files
authored
Merge pull request #1884 from Starbuck5/3610-fast-scale
Faster simple scaling algorithm using SDL functions
2 parents 470bb99 + 2348077 commit 624955f

File tree

4 files changed

+19
-236
lines changed

4 files changed

+19
-236
lines changed

docs/reST/ref/transform.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ Instead, always begin with the original image and scale to the desired size.)
4747
the destination must be the same size as the size (width, height) passed in. Also
4848
the destination surface must be the same format.
4949

50+
.. versionchanged:: 2.3.0 internal scaling algorithm was replaced with a nearly
51+
equivalent one that is 40% faster. Scale results will be very slightly
52+
different.
53+
5054
.. ## pygame.transform.scale ##
5155
5256
.. function:: scale_by

src_c/scale2x.c

Lines changed: 0 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -195,115 +195,3 @@ scale2x(SDL_Surface *src, SDL_Surface *dst)
195195
}
196196
}
197197
}
198-
199-
void
200-
scale2xraw(SDL_Surface *src, SDL_Surface *dst)
201-
{
202-
int looph, loopw;
203-
204-
Uint8 *srcpix = (Uint8 *)src->pixels;
205-
Uint8 *dstpix = (Uint8 *)dst->pixels;
206-
207-
const int srcpitch = src->pitch;
208-
const int dstpitch = dst->pitch;
209-
const int width = src->w;
210-
const int height = src->h;
211-
212-
switch (src->format->BytesPerPixel) {
213-
case 1: {
214-
Uint8 E0, E1, E2, E3, E;
215-
for (looph = 0; looph < height; ++looph) {
216-
for (loopw = 0; loopw < width; ++loopw) {
217-
E = *(Uint8 *)(srcpix + (looph * srcpitch) + (1 * loopw));
218-
219-
E0 = E;
220-
E1 = E;
221-
E2 = E;
222-
E3 = E;
223-
224-
*(Uint8 *)(dstpix + looph * 2 * dstpitch + loopw * 2 * 1) =
225-
E0;
226-
*(Uint8 *)(dstpix + looph * 2 * dstpitch +
227-
(loopw * 2 + 1) * 1) = E1;
228-
*(Uint8 *)(dstpix + (looph * 2 + 1) * dstpitch +
229-
loopw * 2 * 1) = E2;
230-
*(Uint8 *)(dstpix + (looph * 2 + 1) * dstpitch +
231-
(loopw * 2 + 1) * 1) = E3;
232-
}
233-
}
234-
break;
235-
}
236-
case 2: {
237-
Uint16 E0, E1, E2, E3, E;
238-
for (looph = 0; looph < height; ++looph) {
239-
for (loopw = 0; loopw < width; ++loopw) {
240-
E = *(Uint16 *)(srcpix + (looph * srcpitch) + (2 * loopw));
241-
242-
E0 = E;
243-
E1 = E;
244-
E2 = E;
245-
E3 = E;
246-
247-
*(Uint16 *)(dstpix + looph * 2 * dstpitch +
248-
loopw * 2 * 2) = E0;
249-
*(Uint16 *)(dstpix + looph * 2 * dstpitch +
250-
(loopw * 2 + 1) * 2) = E1;
251-
*(Uint16 *)(dstpix + (looph * 2 + 1) * dstpitch +
252-
loopw * 2 * 2) = E2;
253-
*(Uint16 *)(dstpix + (looph * 2 + 1) * dstpitch +
254-
(loopw * 2 + 1) * 2) = E3;
255-
}
256-
}
257-
break;
258-
}
259-
case 3: {
260-
int E0, E1, E2, E3, E;
261-
for (looph = 0; looph < height; ++looph) {
262-
for (loopw = 0; loopw < width; ++loopw) {
263-
E = READINT24(srcpix + (looph * srcpitch) + (3 * loopw));
264-
265-
E0 = E;
266-
E1 = E;
267-
E2 = E;
268-
E3 = E;
269-
270-
WRITEINT24((dstpix + looph * 2 * dstpitch + loopw * 2 * 3),
271-
E0);
272-
WRITEINT24(
273-
(dstpix + looph * 2 * dstpitch + (loopw * 2 + 1) * 3),
274-
E1);
275-
WRITEINT24(
276-
(dstpix + (looph * 2 + 1) * dstpitch + loopw * 2 * 3),
277-
E2);
278-
WRITEINT24((dstpix + (looph * 2 + 1) * dstpitch +
279-
(loopw * 2 + 1) * 3),
280-
E3);
281-
}
282-
}
283-
break;
284-
}
285-
default: { /*case 4:*/
286-
Uint32 E0, E1, E2, E3, E;
287-
for (looph = 0; looph < height; ++looph) {
288-
for (loopw = 0; loopw < width; ++loopw) {
289-
E = *(Uint32 *)(srcpix + (looph * srcpitch) + (4 * loopw));
290-
291-
E0 = E;
292-
E1 = E;
293-
E2 = E;
294-
E3 = E;
295-
296-
*(Uint32 *)(dstpix + looph * 2 * dstpitch +
297-
loopw * 2 * 4) = E0;
298-
*(Uint32 *)(dstpix + looph * 2 * dstpitch +
299-
(loopw * 2 + 1) * 4) = E1;
300-
*(Uint32 *)(dstpix + (looph * 2 + 1) * dstpitch +
301-
loopw * 2 * 4) = E2;
302-
*(Uint32 *)(dstpix + (looph * 2 + 1) * dstpitch +
303-
(loopw * 2 + 1) * 4) = E3;
304-
}
305-
}
306-
break;
307-
}
308-
}
309-
}

src_c/transform.c

Lines changed: 11 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ struct _module_state {
5353

5454
void
5555
scale2x(SDL_Surface *src, SDL_Surface *dst);
56-
void
57-
scale2xraw(SDL_Surface *src, SDL_Surface *dst);
5856
extern SDL_Surface *
5957
rotozoomSurface(SDL_Surface *src, double angle, double zoom, int smooth);
6058

@@ -411,118 +409,13 @@ rotate(SDL_Surface *src, SDL_Surface *dst, Uint32 bgcolor, double sangle,
411409
}
412410
}
413411

414-
static void
415-
stretch(SDL_Surface *src, SDL_Surface *dst)
416-
{
417-
int looph, loopw;
418-
419-
Uint8 *srcrow = (Uint8 *)src->pixels;
420-
Uint8 *dstrow = (Uint8 *)dst->pixels;
421-
422-
int srcpitch = src->pitch;
423-
int dstpitch = dst->pitch;
424-
425-
int dstwidth = dst->w;
426-
int dstheight = dst->h;
427-
int dstwidth2 = dst->w << 1;
428-
int dstheight2 = dst->h << 1;
429-
430-
int srcwidth2 = src->w << 1;
431-
int srcheight2 = src->h << 1;
432-
433-
int w_err, h_err = srcheight2 - dstheight2;
434-
435-
switch (src->format->BytesPerPixel) {
436-
case 1:
437-
for (looph = 0; looph < dstheight; ++looph) {
438-
Uint8 *srcpix = (Uint8 *)srcrow, *dstpix = (Uint8 *)dstrow;
439-
w_err = srcwidth2 - dstwidth2;
440-
for (loopw = 0; loopw < dstwidth; ++loopw) {
441-
*dstpix++ = *srcpix;
442-
while (w_err >= 0) {
443-
++srcpix;
444-
w_err -= dstwidth2;
445-
}
446-
w_err += srcwidth2;
447-
}
448-
while (h_err >= 0) {
449-
srcrow += srcpitch;
450-
h_err -= dstheight2;
451-
}
452-
dstrow += dstpitch;
453-
h_err += srcheight2;
454-
}
455-
break;
456-
case 2:
457-
for (looph = 0; looph < dstheight; ++looph) {
458-
Uint16 *srcpix = (Uint16 *)srcrow, *dstpix = (Uint16 *)dstrow;
459-
w_err = srcwidth2 - dstwidth2;
460-
for (loopw = 0; loopw < dstwidth; ++loopw) {
461-
*dstpix++ = *srcpix;
462-
while (w_err >= 0) {
463-
++srcpix;
464-
w_err -= dstwidth2;
465-
}
466-
w_err += srcwidth2;
467-
}
468-
while (h_err >= 0) {
469-
srcrow += srcpitch;
470-
h_err -= dstheight2;
471-
}
472-
dstrow += dstpitch;
473-
h_err += srcheight2;
474-
}
475-
break;
476-
case 3:
477-
for (looph = 0; looph < dstheight; ++looph) {
478-
Uint8 *srcpix = (Uint8 *)srcrow, *dstpix = (Uint8 *)dstrow;
479-
w_err = srcwidth2 - dstwidth2;
480-
for (loopw = 0; loopw < dstwidth; ++loopw) {
481-
memcpy(dstpix, srcpix, 3);
482-
dstpix += 3;
483-
while (w_err >= 0) {
484-
srcpix += 3;
485-
w_err -= dstwidth2;
486-
}
487-
w_err += srcwidth2;
488-
}
489-
while (h_err >= 0) {
490-
srcrow += srcpitch;
491-
h_err -= dstheight2;
492-
}
493-
dstrow += dstpitch;
494-
h_err += srcheight2;
495-
}
496-
break;
497-
default: /*case 4:*/
498-
for (looph = 0; looph < dstheight; ++looph) {
499-
Uint32 *srcpix = (Uint32 *)srcrow, *dstpix = (Uint32 *)dstrow;
500-
w_err = srcwidth2 - dstwidth2;
501-
for (loopw = 0; loopw < dstwidth; ++loopw) {
502-
*dstpix++ = *srcpix;
503-
while (w_err >= 0) {
504-
++srcpix;
505-
w_err -= dstwidth2;
506-
}
507-
w_err += srcwidth2;
508-
}
509-
while (h_err >= 0) {
510-
srcrow += srcpitch;
511-
h_err -= dstheight2;
512-
}
513-
dstrow += dstpitch;
514-
h_err += srcheight2;
515-
}
516-
break;
517-
}
518-
}
519-
520412
static SDL_Surface *
521413
scale_to(pgSurfaceObject *srcobj, pgSurfaceObject *dstobj, int width,
522414
int height)
523415
{
524416
SDL_Surface *src = NULL;
525417
SDL_Surface *retsurf = NULL;
418+
int stretch_result_num = 0;
526419

527420
if (width < 0 || height < 0)
528421
return RAISE(PyExc_ValueError, "Cannot scale to negative size");
@@ -544,27 +437,21 @@ scale_to(pgSurfaceObject *srcobj, pgSurfaceObject *dstobj, int width,
544437
"Destination surface not the given width or height."));
545438
}
546439

547-
if (src->format->BytesPerPixel != retsurf->format->BytesPerPixel) {
548-
return (SDL_Surface *)(RAISE(
549-
PyExc_ValueError,
550-
"Source and destination surfaces need the same format."));
551-
}
552-
440+
/* Testing width and height of src and dest, because pygame supports
441+
* surfaces with "0" as the width or height, and for those nothing should
442+
* happen here. */
553443
if ((width && height) && (src->w && src->h)) {
554-
SDL_LockSurface(retsurf);
555444
pgSurface_Lock(srcobj);
556-
557445
Py_BEGIN_ALLOW_THREADS;
558-
if (width == 2 * src->w && height == 2 * src->h) {
559-
scale2xraw(src, retsurf);
560-
}
561-
else {
562-
stretch(src, retsurf);
563-
}
564-
Py_END_ALLOW_THREADS;
565446

447+
stretch_result_num = SDL_SoftStretch(src, NULL, retsurf, NULL);
448+
449+
Py_END_ALLOW_THREADS;
566450
pgSurface_Unlock(srcobj);
567-
SDL_UnlockSurface(retsurf);
451+
452+
if (stretch_result_num < 0) {
453+
return (SDL_Surface *)(RAISE(pgExc_SDLError, SDL_GetError()));
454+
}
568455
}
569456

570457
return retsurf;

test/transform_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,10 @@ def test_scale2x(self):
11281128
self.assertEqual(s2.get_rect().size, (64, 64))
11291129

11301130
def test_scale2xraw(self):
1131+
# Even though transform.scale no longer has a special
1132+
# case for 2x upscaling, this test validates that the behavior
1133+
# is preserved.
1134+
11311135
w, h = 32, 32
11321136
s = pygame.Surface((w, h), pygame.SRCALPHA, 32)
11331137
s.fill((0, 0, 0))

0 commit comments

Comments
 (0)