1 /*
2   pygame - Python Game Library
3   Copyright (C) 2000-2001  Pete Shinners
4   Copyright (C) 2006 Rene Dudfield
5 
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public
8   License as published by the Free Software Foundation; either
9   version 2 of the License, or (at your option) any later version.
10 
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15 
16   You should have received a copy of the GNU Library General Public
17   License along with this library; if not, write to the Free
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 
20   Pete Shinners
21   pete@shinners.org
22 */
23 
24 /*
25  *  image module for pygame
26  */
27 #include "pygame.h"
28 
29 #include "pgcompat.h"
30 
31 #include "doc/image_doc.h"
32 
33 #if PG_COMPILE_SSE4_2 && SDL_VERSION_ATLEAST(2, 0, 0)
34 #include <emmintrin.h>
35 /* SSSE 3 */
36 #include <tmmintrin.h>
37 #endif
38 
39 
40 static int
41 SaveTGA(SDL_Surface *surface, const char *file, int rle);
42 static int
43 SaveTGA_RW(SDL_Surface *surface, SDL_RWops *out, int rle);
44 
45 #define DATAROW(data, row, width, height, flipped)             \
46     ((flipped) ? (((char *)data) + (height - row - 1) * width) \
47                : (((char *)data) + row * width))
48 
49 static PyObject *extloadobj = NULL;
50 static PyObject *extsaveobj = NULL;
51 static PyObject *extverobj = NULL;
52 
53 static const char *
find_extension(const char * fullname)54 find_extension(const char *fullname)
55 {
56     const char *dot;
57 
58     if (fullname == NULL) {
59         return NULL;
60     }
61 
62     dot = strrchr(fullname, '.');
63     if (dot == NULL) {
64         return fullname;
65     }
66     return dot + 1;
67 }
68 
69 static PyObject *
image_load_basic(PyObject * self,PyObject * obj)70 image_load_basic(PyObject *self, PyObject *obj)
71 {
72     PyObject *final;
73     PyObject *oencoded;
74     SDL_Surface *surf;
75 
76     SDL_RWops *rw = pgRWops_FromObject(obj);
77     if (rw == NULL) {
78         return NULL;
79     }
80     Py_BEGIN_ALLOW_THREADS;
81     surf = SDL_LoadBMP_RW(rw, 1);
82     Py_END_ALLOW_THREADS;
83 
84     if (surf == NULL) {
85         return RAISE(pgExc_SDLError, SDL_GetError());
86     }
87 
88     final = (PyObject *)pgSurface_New(surf);
89     if (final == NULL) {
90         SDL_FreeSurface(surf);
91     }
92     return final;
93 }
94 
95 static PyObject *
image_load_extended(PyObject * self,PyObject * arg)96 image_load_extended(PyObject *self, PyObject *arg)
97 {
98     if (extloadobj == NULL)
99         return RAISE(PyExc_NotImplementedError,
100                      "loading images of extended format is not available");
101     else
102         return PyObject_CallObject(extloadobj, arg);
103 }
104 
105 static PyObject *
image_load(PyObject * self,PyObject * arg)106 image_load(PyObject *self, PyObject *arg)
107 {
108     PyObject *obj;
109     const char *name = NULL;
110 
111     if (extloadobj == NULL) {
112         if (!PyArg_ParseTuple(arg, "O|s", &obj, &name)) {
113             return NULL;
114         }
115         return image_load_basic(self, obj);
116     }
117     else
118         return image_load_extended(self, arg);
119 }
120 
121 
122 #ifdef WIN32
123 #define strcasecmp _stricmp
124 #else
125 #include <strings.h>
126 #endif
127 
128 static PyObject *
image_save_extended(PyObject * self,PyObject * arg)129 image_save_extended(PyObject *self, PyObject *arg)
130 {
131     if (extsaveobj == NULL)
132         return RAISE(PyExc_NotImplementedError,
133                      "saving images of extended format is not available");
134     else
135         return PyObject_CallObject(extsaveobj, arg);
136 }
137 
138 static PyObject *
image_save(PyObject * self,PyObject * arg)139 image_save(PyObject *self, PyObject *arg)
140 {
141     pgSurfaceObject *surfobj;
142     PyObject *obj;
143     const char *namehint = NULL;
144     PyObject *oencoded;
145     PyObject *ret;
146     SDL_Surface *surf;
147     int result = 1;
148 
149     if (!PyArg_ParseTuple(arg, "O!O|s", &pgSurface_Type, &surfobj,
150                 &obj, &namehint)) {
151         return NULL;
152     }
153 
154     surf = pgSurface_AsSurface(surfobj);
155     pgSurface_Prep(surfobj);
156 
157     oencoded = pg_EncodeString(obj, "UTF-8", NULL, pgExc_SDLError);
158     if (oencoded == NULL) {
159         result = -2;
160     }
161     else {
162         const char *name = NULL;
163         const char * ext = NULL;
164         if (oencoded == Py_None) {
165             name = (namehint ? namehint: "tga");
166         }
167         else {
168             name = Bytes_AS_STRING(oencoded);
169         }
170 
171         ext = find_extension(name);
172         if (!strcasecmp(ext, "png") ||
173                 !strcasecmp(ext, "jpg") ||
174                 !strcasecmp(ext, "jpeg")) {
175             /* If it is .png .jpg .jpeg use the extended module. */
176             /* try to get extended formats */
177             ret = image_save_extended(self, arg);
178             result = (ret == NULL ? -2 : 0);
179         }
180         else if (oencoded == Py_None) {
181             SDL_RWops *rw = pgRWops_FromFileObject(obj);
182             if (rw != NULL) {
183                 if (!strcasecmp(ext, "bmp")) {
184                     /* The SDL documentation didn't specify which negative number
185                      * is returned upon error. We want to be sure that result is
186                      * either 0 or -1: */
187                     result = (SDL_SaveBMP_RW(surf, rw, 0) == 0 ? 0 : -1);
188                 }
189                 else {
190                     result = SaveTGA_RW(surf, rw, 1);
191                 }
192             }
193             else {
194                 result = -2;
195             }
196         }
197         else {
198             if (!strcasecmp(ext, "bmp")) {
199                 Py_BEGIN_ALLOW_THREADS;
200                 /* The SDL documentation didn't specify which negative number
201                  * is returned upon error. We want to be sure that result is
202                  * either 0 or -1: */
203                 result = (SDL_SaveBMP(surf, name) == 0 ? 0 : -1);
204                 Py_END_ALLOW_THREADS;
205             }
206             else {
207                 Py_BEGIN_ALLOW_THREADS;
208                 result = SaveTGA(surf, name, 1);
209                 Py_END_ALLOW_THREADS;
210             }
211         }
212     }
213     Py_XDECREF(oencoded);
214 
215     pgSurface_Unprep(surfobj);
216 
217     if (result == -2) {
218         /* Python error raised elsewhere */
219         return NULL;
220     }
221     if (result == -1) {
222         /* SDL error: translate to Python error */
223         return RAISE(pgExc_SDLError, SDL_GetError());
224     }
225     if (result == 1) {
226         /* Should never get here */
227         return RAISE(pgExc_SDLError, "Unrecognized image type");
228     }
229 
230     Py_RETURN_NONE;
231 }
232 
233 static PyObject *
image_get_extended(PyObject * self)234 image_get_extended(PyObject *self)
235 {
236     if (extverobj == NULL)
237         Py_RETURN_FALSE;
238     else
239         Py_RETURN_TRUE;
240 }
241 
242 static PyObject *
image_get_sdl_image_version(PyObject * self)243 image_get_sdl_image_version(PyObject *self)
244 {
245     if (extverobj == NULL)
246         Py_RETURN_NONE;
247     else
248         return PyObject_CallObject(extverobj, NULL);
249 }
250 
251 #if PG_COMPILE_SSE4_2 && SDL_VERSION_ATLEAST(2, 0, 0)
252 #define SSE42_ALIGN_NEEDED 16
253 #define SSE42_ALIGN __attribute__((aligned(SSE42_ALIGN_NEEDED)))
254 
255 #define _SHIFT_N_STEP2ALIGN(shift, step) (shift/8 + step * 4)
256 
257 #if PYGAME_DEBUG_SSE
258 /* Useful for debugging/comparing the SSE vectors */
_debug_print128_num(__m128i var,const char * msg)259 static void _debug_print128_num(__m128i var, const char *msg)
260 {
261     uint32_t val[4];
262     memcpy(val, &var, sizeof(val));
263     fprintf(stderr, "%s: %04x%04x%04x%04x\n",
264            msg, val[0], val[1], val[2], val[3]);
265 }
266 #define DEBUG_PRINT128_NUM(var, msg) _debug_print128_num(var, msg)
267 #else
268 #define DEBUG_PRINT128_NUM(var, msg) do { /* do nothing */ } while (0)
269 #endif
270 
271 /*
272  * Generates an SSE vector useful for reordering a SSE vector
273  * based on the "in-memory layout" to match the "tostring layout"
274  * It is only useful as second parameter to _mm_shuffle_epi8.
275  *
276  * A short _mm_shuffle_epi8 primer:
277  *
278  * - If the highest bit of a byte in the reorder vector is set,
279  *   the matching byte in the original vector is cleared:
280  * - Otherwise, the 3 lowest bit of the byte in the reorder vector
281  *   represent the byte index of where to find the relevant value
282  *   from the original vector.
283  *
284  * As an example, given the following in memory layout (bytes):
285  *
286  *    R1 G1 B1 A1 R2 G2 B2 A2 ...
287  *
288  * And we want:
289  *
290  *    A1 R1 G1 B1 A2 R2 G2 B2
291  *
292  * Then the reorder vector should look like (in hex):
293  *
294  *    03 00 01 02 07 04 05 06
295  *
296  * This is exactly the type of vector that compute_align_vector
297  * produces (based on the pixel format and where the alpha should
298  * be placed in the output).
299  */
300 static PG_INLINE __m128i
compute_align_vector(SDL_PixelFormat * format,int color_offset,int alpha_offset)301 compute_align_vector(SDL_PixelFormat *format, int color_offset,
302                      int alpha_offset) {
303     int output_align[4];
304     size_t i;
305     size_t limit = sizeof(output_align) / sizeof(int);
306     int a_shift = alpha_offset * 8;
307     int r_shift = (color_offset + 0) * 8;
308     int g_shift = (color_offset + 1) * 8;
309     int b_shift = (color_offset + 2) * 8;
310     for (i = 0; i < limit; i++) {
311         int p = 3 - i;
312         output_align[i] = _SHIFT_N_STEP2ALIGN(format->Rshift, p) << r_shift
313                         | _SHIFT_N_STEP2ALIGN(format->Gshift, p) << g_shift
314                         | _SHIFT_N_STEP2ALIGN(format->Bshift, p) << b_shift
315                         | _SHIFT_N_STEP2ALIGN(format->Ashift, p) << a_shift;
316     }
317     return _mm_set_epi32(output_align[0], output_align[1],
318                          output_align[2], output_align[3]);
319 }
320 
321 
322 static PG_INLINE PG_FUNCTION_TARGET_SSE4_2 void
tostring_pixels_32bit_sse4(const __m128i * row,__m128i * data,int loop_max,__m128i mask_vector,__m128i align_vector)323 tostring_pixels_32bit_sse4(const __m128i *row, __m128i *data, int loop_max,
324                            __m128i mask_vector, __m128i align_vector) {
325     int w;
326     for (w = 0; w < loop_max; ++w) {
327         __m128i pvector = _mm_loadu_si128(row + w);
328         DEBUG_PRINT128_NUM(pvector, "Load");
329         pvector = _mm_and_si128(pvector, mask_vector);
330         DEBUG_PRINT128_NUM(pvector, "after _mm_and_si128 (and)");
331         pvector = _mm_shuffle_epi8(pvector, align_vector);
332         DEBUG_PRINT128_NUM(pvector, "after _mm_shuffle_epi8 (reorder)");
333         _mm_storeu_si128(data + w, pvector);
334     }
335 }
336 
337 /*
338  * SSE4.2 variant of tostring_surf_32bpp.
339  *
340  * It is a lot faster but only works on a subset of the surfaces
341  * (plus requires SSE4.2 support from the CPU).
342  */
343 static PG_FUNCTION_TARGET_SSE4_2 void
tostring_surf_32bpp_sse42(SDL_Surface * surf,int flipped,char * data,int color_offset,int alpha_offset)344 tostring_surf_32bpp_sse42(SDL_Surface *surf, int flipped, char *data,
345                           int color_offset, int alpha_offset) {
346     const int step_size = 4;
347     int h;
348     SDL_PixelFormat *format = surf->format;
349     int loop_max = surf->w / step_size;
350     int mask = (format->Rloss ? 0 : format->Rmask)
351              | (format->Gloss ? 0 : format->Gmask)
352              | (format->Bloss ? 0 : format->Bmask)
353              | (format->Aloss ? 0 : format->Amask);
354 
355     __m128i mask_vector = _mm_set_epi32(mask, mask, mask, mask);
356     __m128i align_vector = compute_align_vector(surf->format,
357                                                 color_offset, alpha_offset);
358     /* How much we would overshoot if we overstep loop_max */
359     int rollback_count = surf->w % step_size;
360     if (rollback_count) {
361         rollback_count = step_size - rollback_count;
362     }
363 
364     DEBUG_PRINT128_NUM(mask_vector, "mask-vector");
365     DEBUG_PRINT128_NUM(align_vector, "align-vector");
366 
367     /* This code will be horribly wrong if these assumptions do not hold.
368      * They are intended as a debug/testing guard to ensure that nothing
369      * calls this function without ensuring the assumptions during
370      * development
371      */
372     assert(sizeof(int) == sizeof(Uint32));
373     assert(4 * sizeof(Uint32) == sizeof(__m128i));
374     /* If this assertion does not hold, the fallback code will overrun
375      * the buffers.
376      */
377     assert(surf->w >= step_size);
378     assert(format->Rloss % 8 == 0);
379     assert(format->Gloss % 8 == 0);
380     assert(format->Bloss % 8 == 0);
381     assert(format->Aloss % 8 == 0);
382 
383     for (h = 0; h < surf->h; ++h) {
384         const char *row = (char *)DATAROW(
385             surf->pixels, h, surf->pitch, surf->h, flipped);
386         tostring_pixels_32bit_sse4((const __m128i*)row, (__m128i *)data,
387                                    loop_max, mask_vector, align_vector);
388         row += sizeof(__m128i) * loop_max;
389         data += sizeof(__m128i) * loop_max;
390         if (rollback_count) {
391             /* Back up a bit to ensure we stay within the memory boundaries
392              * Technically, we end up redoing part of the computations, but
393              * it does not really matter as the runtime of these operations
394              * are fixed and the results are deterministic.
395              */
396             row -= rollback_count * sizeof(Uint32);
397             data -= rollback_count * sizeof(Uint32);
398 
399             tostring_pixels_32bit_sse4((const __m128i*)row, (__m128i *)data,
400                                        1, mask_vector, align_vector);
401 
402             row += sizeof(__m128i);
403             data += sizeof(__m128i);
404         }
405     }
406 }
407 #endif /* PG_COMPILE_SSE4_2  && SDL_VERSION_ATLEAST(2, 0, 0) */
408 
409 
410 static void
tostring_surf_32bpp(SDL_Surface * surf,int flipped,int hascolorkey,Uint32 colorkey,char * serialized_image,int color_offset,int alpha_offset)411 tostring_surf_32bpp(SDL_Surface *surf, int flipped,
412                     int hascolorkey, Uint32 colorkey,
413                     char *serialized_image,
414                     int color_offset, int alpha_offset
415 )
416 {
417     int w, h;
418 
419     Uint32 Rmask = surf->format->Rmask;
420     Uint32 Gmask = surf->format->Gmask;
421     Uint32 Bmask = surf->format->Bmask;
422     Uint32 Amask = surf->format->Amask;
423     Uint32 Rshift = surf->format->Rshift;
424     Uint32 Gshift = surf->format->Gshift;
425     Uint32 Bshift = surf->format->Bshift;
426     Uint32 Ashift = surf->format->Ashift;
427     Uint32 Rloss = surf->format->Rloss;
428     Uint32 Gloss = surf->format->Gloss;
429     Uint32 Bloss = surf->format->Bloss;
430     Uint32 Aloss = surf->format->Aloss;
431 
432 #if PG_COMPILE_SSE4_2 && SDL_VERSION_ATLEAST(2, 0, 0)
433     if (/* SDL uses Uint32, SSE uses int for building vectors.
434          * Related, we assume that Uint32 is packed so 4 of
435          * them perfectly matches an __m128i.
436          * If these assumptions do not match up, we will
437          * produce incorrect results.
438          */
439         sizeof(int) == sizeof(Uint32)
440         && 4 * sizeof(Uint32) == sizeof(__m128i)
441         && !hascolorkey /* No color key */
442         && SDL_HasSSE42() == SDL_TRUE
443         /* The SSE code assumes it will always read at least 4 pixels */
444         && surf->w >= 4
445         /* Our SSE code assumes masks are at most 0xff */
446         && (surf->format->Rmask >> surf->format->Rshift) <= 0x0ff
447         && (surf->format->Gmask >> surf->format->Gshift) <= 0x0ff
448         && (surf->format->Bmask >> surf->format->Bshift) <= 0x0ff
449         && (Amask >> Ashift) <= 0x0ff
450         /* Our SSE code cannot handle losses other than 0 or 8
451          * Note the mask check above ensures that losses can be
452          * at most be 8 (assuming the pixel format makes sense
453          * at all).
454          */
455         && (surf->format->Rloss % 8) == 0
456         && (surf->format->Bloss % 8) == 0
457         && (surf->format->Gloss % 8) == 0
458         && (Aloss % 8) == 0
459         ) {
460         tostring_surf_32bpp_sse42(surf, flipped, serialized_image,
461                                   color_offset, alpha_offset);
462         return;
463     }
464 #endif /* PG_COMPILE_SSE4_2 && SDL_VERSION_ATLEAST(2, 0, 0) */
465 
466     for (h = 0; h < surf->h; ++h) {
467         Uint32 *pixel_row = (Uint32 *)DATAROW(
468             surf->pixels, h, surf->pitch, surf->h, flipped);
469         for (w = 0; w < surf->w; ++w) {
470             Uint32 color = *pixel_row++;
471             serialized_image[color_offset + 0] =
472                  (char)(((color & Rmask) >> Rshift) << Rloss);
473             serialized_image[color_offset + 1] =
474                  (char)(((color & Gmask) >> Gshift) << Gloss);
475             serialized_image[color_offset + 2] =
476                  (char)(((color & Bmask) >> Bshift) << Bloss);
477             serialized_image[alpha_offset] =
478                 hascolorkey
479                     ? (char)(color != colorkey) * 255
480                     : (char)(Amask ? (((color & Amask) >> Ashift)
481                                       << Aloss)
482                                    : 255);
483             serialized_image += 4;
484         }
485     }
486 }
487 
488 PyObject *
image_tostring(PyObject * self,PyObject * arg)489 image_tostring(PyObject *self, PyObject *arg)
490 {
491     pgSurfaceObject *surfobj = NULL;
492     PyObject *string = NULL;
493     char *format, *data, *pixels;
494     SDL_Surface *surf;
495     int w, h, flipped = 0;
496     Py_ssize_t len;
497     Uint32 Rmask, Gmask, Bmask, Amask, Rshift, Gshift, Bshift, Ashift, Rloss,
498         Gloss, Bloss, Aloss;
499     int hascolorkey;
500     Uint32 color, colorkey;
501     Uint32 alpha;
502 
503     if (!PyArg_ParseTuple(arg, "O!s|i", &pgSurface_Type, &surfobj, &format,
504                           &flipped))
505         return NULL;
506     surf = pgSurface_AsSurface(surfobj);
507 
508     Rmask = surf->format->Rmask;
509     Gmask = surf->format->Gmask;
510     Bmask = surf->format->Bmask;
511     Amask = surf->format->Amask;
512     Rshift = surf->format->Rshift;
513     Gshift = surf->format->Gshift;
514     Bshift = surf->format->Bshift;
515     Ashift = surf->format->Ashift;
516     Rloss = surf->format->Rloss;
517     Gloss = surf->format->Gloss;
518     Bloss = surf->format->Bloss;
519     Aloss = surf->format->Aloss;
520     hascolorkey = (SDL_GetColorKey(surf, &colorkey) == 0);
521 
522     if (!strcmp(format, "P")) {
523         if (surf->format->BytesPerPixel != 1)
524             return RAISE(
525                 PyExc_ValueError,
526                 "Can only create \"P\" format data with 8bit Surfaces");
527         string = Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h);
528         if (!string)
529             return NULL;
530         Bytes_AsStringAndSize(string, &data, &len);
531 
532         pgSurface_Lock(surfobj);
533         pixels = (char *)surf->pixels;
534         for (h = 0; h < surf->h; ++h)
535             memcpy(DATAROW(data, h, surf->w, surf->h, flipped),
536                    pixels + (h * surf->pitch), surf->w);
537         pgSurface_Unlock(surfobj);
538     }
539     else if (!strcmp(format, "RGB")) {
540         string =
541             Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h * 3);
542         if (!string)
543             return NULL;
544         Bytes_AsStringAndSize(string, &data, &len);
545 
546         pgSurface_Lock(surfobj);
547 
548         pixels = (char *)surf->pixels;
549         switch (surf->format->BytesPerPixel) {
550             case 1:
551                 for (h = 0; h < surf->h; ++h) {
552                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
553                                                   surf->h, flipped);
554                     for (w = 0; w < surf->w; ++w) {
555                         color = *ptr++;
556                         data[0] = (char)surf->format->palette->colors[color].r;
557                         data[1] = (char)surf->format->palette->colors[color].g;
558                         data[2] = (char)surf->format->palette->colors[color].b;
559                         data += 3;
560                     }
561                 }
562                 break;
563             case 2:
564                 for (h = 0; h < surf->h; ++h) {
565                     Uint16 *ptr = (Uint16 *)DATAROW(
566                         surf->pixels, h, surf->pitch, surf->h, flipped);
567                     for (w = 0; w < surf->w; ++w) {
568                         color = *ptr++;
569                         data[0] = (char)(((color & Rmask) >> Rshift) << Rloss);
570                         data[1] = (char)(((color & Gmask) >> Gshift) << Gloss);
571                         data[2] = (char)(((color & Bmask) >> Bshift) << Bloss);
572                         data += 3;
573                     }
574                 }
575                 break;
576             case 3:
577                 for (h = 0; h < surf->h; ++h) {
578                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
579                                                   surf->h, flipped);
580                     for (w = 0; w < surf->w; ++w) {
581 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
582                         color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
583 #else
584                         color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16);
585 #endif
586                         ptr += 3;
587                         data[0] = (char)(((color & Rmask) >> Rshift) << Rloss);
588                         data[1] = (char)(((color & Gmask) >> Gshift) << Gloss);
589                         data[2] = (char)(((color & Bmask) >> Bshift) << Bloss);
590                         data += 3;
591                     }
592                 }
593                 break;
594             case 4:
595                 for (h = 0; h < surf->h; ++h) {
596                     Uint32 *ptr = (Uint32 *)DATAROW(
597                         surf->pixels, h, surf->pitch, surf->h, flipped);
598                     for (w = 0; w < surf->w; ++w) {
599                         color = *ptr++;
600                         data[0] = (char)(((color & Rmask) >> Rshift) << Rloss);
601                         data[1] = (char)(((color & Gmask) >> Gshift) << Rloss);
602                         data[2] = (char)(((color & Bmask) >> Bshift) << Rloss);
603                         data += 3;
604                     }
605                 }
606                 break;
607         }
608 
609         pgSurface_Unlock(surfobj);
610     }
611     else if (!strcmp(format, "RGBX") || !strcmp(format, "RGBA")) {
612         if (strcmp(format, "RGBA"))
613             hascolorkey = 0;
614 
615         string =
616             Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h * 4);
617         if (!string)
618             return NULL;
619         Bytes_AsStringAndSize(string, &data, &len);
620 
621         pgSurface_Lock(surfobj);
622         pixels = (char *)surf->pixels;
623         switch (surf->format->BytesPerPixel) {
624             case 1:
625                 for (h = 0; h < surf->h; ++h) {
626                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
627                                                   surf->h, flipped);
628                     for (w = 0; w < surf->w; ++w) {
629                         color = *ptr++;
630                         data[0] = (char)surf->format->palette->colors[color].r;
631                         data[1] = (char)surf->format->palette->colors[color].g;
632                         data[2] = (char)surf->format->palette->colors[color].b;
633                         data[3] = hascolorkey ? (char)(color != colorkey) * 255
634                                               : (char)255;
635                         data += 4;
636                     }
637                 }
638                 break;
639             case 2:
640                 for (h = 0; h < surf->h; ++h) {
641                     Uint16 *ptr = (Uint16 *)DATAROW(
642                         surf->pixels, h, surf->pitch, surf->h, flipped);
643                     for (w = 0; w < surf->w; ++w) {
644                         color = *ptr++;
645                         data[0] = (char)(((color & Rmask) >> Rshift) << Rloss);
646                         data[1] = (char)(((color & Gmask) >> Gshift) << Gloss);
647                         data[2] = (char)(((color & Bmask) >> Bshift) << Bloss);
648                         data[3] =
649                             hascolorkey
650                                 ? (char)(color != colorkey) * 255
651                                 : (char)(Amask ? (((color & Amask) >> Ashift)
652                                                   << Aloss)
653                                                : 255);
654                         data += 4;
655                     }
656                 }
657                 break;
658             case 3:
659                 for (h = 0; h < surf->h; ++h) {
660                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
661                                                   surf->h, flipped);
662                     for (w = 0; w < surf->w; ++w) {
663 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
664                         color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
665 #else
666                         color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16);
667 #endif
668                         ptr += 3;
669                         data[0] = (char)(((color & Rmask) >> Rshift) << Rloss);
670                         data[1] = (char)(((color & Gmask) >> Gshift) << Gloss);
671                         data[2] = (char)(((color & Bmask) >> Bshift) << Bloss);
672                         data[3] =
673                             hascolorkey
674                                 ? (char)(color != colorkey) * 255
675                                 : (char)(Amask ? (((color & Amask) >> Ashift)
676                                                   << Aloss)
677                                                : 255);
678                         data += 4;
679                     }
680                 }
681                 break;
682             case 4:
683                 tostring_surf_32bpp(surf, flipped, hascolorkey, colorkey,
684                                     data, 0, 3);
685                 break;
686         }
687         pgSurface_Unlock(surfobj);
688     }
689     else if (!strcmp(format, "ARGB")) {
690         hascolorkey = 0;
691 
692         string =
693             Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h * 4);
694         if (!string)
695             return NULL;
696         Bytes_AsStringAndSize(string, &data, &len);
697 
698         pgSurface_Lock(surfobj);
699         pixels = (char *)surf->pixels;
700         switch (surf->format->BytesPerPixel) {
701             case 1:
702                 for (h = 0; h < surf->h; ++h) {
703                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
704                                                   surf->h, flipped);
705                     for (w = 0; w < surf->w; ++w) {
706                         color = *ptr++;
707                         data[1] = (char)surf->format->palette->colors[color].r;
708                         data[2] = (char)surf->format->palette->colors[color].g;
709                         data[3] = (char)surf->format->palette->colors[color].b;
710                         data[0] = (char)255;
711                         data += 4;
712                     }
713                 }
714                 break;
715             case 2:
716                 for (h = 0; h < surf->h; ++h) {
717                     Uint16 *ptr = (Uint16 *)DATAROW(
718                         surf->pixels, h, surf->pitch, surf->h, flipped);
719                     for (w = 0; w < surf->w; ++w) {
720                         color = *ptr++;
721                         data[1] = (char)(((color & Rmask) >> Rshift) << Rloss);
722                         data[2] = (char)(((color & Gmask) >> Gshift) << Gloss);
723                         data[3] = (char)(((color & Bmask) >> Bshift) << Bloss);
724                         data[0] = (char)(Amask ? (((color & Amask) >> Ashift)
725                                                   << Aloss)
726                                                : 255);
727                         data += 4;
728                     }
729                 }
730                 break;
731             case 3:
732                 for (h = 0; h < surf->h; ++h) {
733                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
734                                                   surf->h, flipped);
735                     for (w = 0; w < surf->w; ++w) {
736 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
737                         color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
738 #else
739                         color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16);
740 #endif
741                         ptr += 3;
742                         data[1] = (char)(((color & Rmask) >> Rshift) << Rloss);
743                         data[2] = (char)(((color & Gmask) >> Gshift) << Gloss);
744                         data[3] = (char)(((color & Bmask) >> Bshift) << Bloss);
745                         data[0] = (char)(Amask ? (((color & Amask) >> Ashift)
746                                                   << Aloss)
747                                                : 255);
748                         data += 4;
749                     }
750                 }
751                 break;
752             case 4:
753                 tostring_surf_32bpp(surf, flipped, hascolorkey, colorkey,
754                                     data, 1, 0);
755                 break;
756         }
757         pgSurface_Unlock(surfobj);
758     }
759     else if (!strcmp(format, "RGBA_PREMULT")) {
760         if (surf->format->BytesPerPixel == 1 || surf->format->Amask == 0)
761             return RAISE(PyExc_ValueError,
762                          "Can only create pre-multiplied alpha strings if the "
763                          "surface has per-pixel alpha");
764 
765         hascolorkey = 0;
766 
767         string =
768             Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h * 4);
769         if (!string)
770             return NULL;
771         Bytes_AsStringAndSize(string, &data, &len);
772 
773         pgSurface_Lock(surfobj);
774         pixels = (char *)surf->pixels;
775         switch (surf->format->BytesPerPixel) {
776             case 2:
777                 for (h = 0; h < surf->h; ++h) {
778                     Uint16 *ptr = (Uint16 *)DATAROW(
779                         surf->pixels, h, surf->pitch, surf->h, flipped);
780                     for (w = 0; w < surf->w; ++w) {
781                         color = *ptr++;
782                         alpha = ((color & Amask) >> Ashift) << Aloss;
783                         data[0] =
784                             (char)((((color & Rmask) >> Rshift) << Rloss) *
785                                    alpha / 255);
786                         data[1] =
787                             (char)((((color & Gmask) >> Gshift) << Gloss) *
788                                    alpha / 255);
789                         data[2] =
790                             (char)((((color & Bmask) >> Bshift) << Bloss) *
791                                    alpha / 255);
792                         data[3] = (char)alpha;
793                         data += 4;
794                     }
795                 }
796                 break;
797             case 3:
798                 for (h = 0; h < surf->h; ++h) {
799                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
800                                                   surf->h, flipped);
801                     for (w = 0; w < surf->w; ++w) {
802 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
803                         color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
804 #else
805                         color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16);
806 #endif
807                         ptr += 3;
808                         alpha = ((color & Amask) >> Ashift) << Aloss;
809                         data[0] =
810                             (char)((((color & Rmask) >> Rshift) << Rloss) *
811                                    alpha / 255);
812                         data[1] =
813                             (char)((((color & Gmask) >> Gshift) << Gloss) *
814                                    alpha / 255);
815                         data[2] =
816                             (char)((((color & Bmask) >> Bshift) << Bloss) *
817                                    alpha / 255);
818                         data[3] = (char)alpha;
819                         data += 4;
820                     }
821                 }
822                 break;
823             case 4:
824                 for (h = 0; h < surf->h; ++h) {
825                     Uint32 *ptr = (Uint32 *)DATAROW(
826                         surf->pixels, h, surf->pitch, surf->h, flipped);
827                     for (w = 0; w < surf->w; ++w) {
828                         color = *ptr++;
829                         alpha = ((color & Amask) >> Ashift) << Aloss;
830                         if (alpha == 0) {
831                             data[0] = data[1] = data[2] = 0;
832                         }
833                         else {
834                             data[0] =
835                                 (char)((((color & Rmask) >> Rshift) << Rloss) *
836                                        alpha / 255);
837                             data[1] =
838                                 (char)((((color & Gmask) >> Gshift) << Gloss) *
839                                        alpha / 255);
840                             data[2] =
841                                 (char)((((color & Bmask) >> Bshift) << Bloss) *
842                                        alpha / 255);
843                         }
844                         data[3] = (char)alpha;
845                         data += 4;
846                     }
847                 }
848                 break;
849         }
850         pgSurface_Unlock(surfobj);
851     }
852     else if (!strcmp(format, "ARGB_PREMULT")) {
853         if (surf->format->BytesPerPixel == 1 || surf->format->Amask == 0)
854             return RAISE(PyExc_ValueError,
855                          "Can only create pre-multiplied alpha strings if the "
856                          "surface has per-pixel alpha");
857 
858         hascolorkey = 0;
859 
860         string =
861             Bytes_FromStringAndSize(NULL, (Py_ssize_t)surf->w * surf->h * 4);
862         if (!string)
863             return NULL;
864         Bytes_AsStringAndSize(string, &data, &len);
865 
866         pgSurface_Lock(surfobj);
867         pixels = (char *)surf->pixels;
868         switch (surf->format->BytesPerPixel) {
869             case 2:
870                 for (h = 0; h < surf->h; ++h) {
871                     Uint16 *ptr = (Uint16 *)DATAROW(
872                         surf->pixels, h, surf->pitch, surf->h, flipped);
873                     for (w = 0; w < surf->w; ++w) {
874                         color = *ptr++;
875                         alpha = ((color & Amask) >> Ashift) << Aloss;
876                         data[1] =
877                             (char)((((color & Rmask) >> Rshift) << Rloss) *
878                                    alpha / 255);
879                         data[2] =
880                             (char)((((color & Gmask) >> Gshift) << Gloss) *
881                                    alpha / 255);
882                         data[3] =
883                             (char)((((color & Bmask) >> Bshift) << Bloss) *
884                                    alpha / 255);
885                         data[0] = (char)alpha;
886                         data += 4;
887                     }
888                 }
889                 break;
890             case 3:
891                 for (h = 0; h < surf->h; ++h) {
892                     Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch,
893                                                   surf->h, flipped);
894                     for (w = 0; w < surf->w; ++w) {
895 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
896                         color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
897 #else
898                         color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16);
899 #endif
900                         ptr += 3;
901                         alpha = ((color & Amask) >> Ashift) << Aloss;
902                         data[1] =
903                             (char)((((color & Rmask) >> Rshift) << Rloss) *
904                                    alpha / 255);
905                         data[2] =
906                             (char)((((color & Gmask) >> Gshift) << Gloss) *
907                                    alpha / 255);
908                         data[3] =
909                             (char)((((color & Bmask) >> Bshift) << Bloss) *
910                                    alpha / 255);
911                         data[0] = (char)alpha;
912                         data += 4;
913                     }
914                 }
915                 break;
916             case 4:
917                 for (h = 0; h < surf->h; ++h) {
918                     Uint32 *ptr = (Uint32 *)DATAROW(
919                         surf->pixels, h, surf->pitch, surf->h, flipped);
920                     for (w = 0; w < surf->w; ++w) {
921                         color = *ptr++;
922                         alpha = ((color & Amask) >> Ashift) << Aloss;
923                         if (alpha == 0) {
924                             data[1] = data[2] = data[3] = 0;
925                         }
926                         else {
927                             data[1] =
928                                 (char)((((color & Rmask) >> Rshift) << Rloss) *
929                                        alpha / 255);
930                             data[2] =
931                                 (char)((((color & Gmask) >> Gshift) << Gloss) *
932                                        alpha / 255);
933                             data[3] =
934                                 (char)((((color & Bmask) >> Bshift) << Bloss) *
935                                        alpha / 255);
936                         }
937                         data[0] = (char)alpha;
938                         data += 4;
939                     }
940                 }
941                 break;
942         }
943         pgSurface_Unlock(surfobj);
944     }
945     else {
946 
947         return RAISE(PyExc_ValueError, "Unrecognized type of format");
948     }
949 
950 
951     return string;
952 }
953 
954 PyObject *
image_fromstring(PyObject * self,PyObject * arg)955 image_fromstring(PyObject *self, PyObject *arg)
956 {
957     PyObject *string;
958     char *format, *data;
959     SDL_Surface *surf = NULL;
960     int w, h, flipped = 0;
961     Py_ssize_t len;
962     int loopw, looph;
963 
964     if (!PyArg_ParseTuple(arg, "O!(ii)s|i", &Bytes_Type, &string, &w, &h,
965                           &format, &flipped))
966         return NULL;
967 
968     if (w < 1 || h < 1)
969         return RAISE(PyExc_ValueError, "Resolution must be positive values");
970 
971     Bytes_AsStringAndSize(string, &data, &len);
972 
973     if (!strcmp(format, "P")) {
974         if (len != (Py_ssize_t)w * h)
975             return RAISE(
976                 PyExc_ValueError,
977                 "String length does not equal format and resolution size");
978 
979         surf = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
980         if (!surf)
981             return RAISE(pgExc_SDLError, SDL_GetError());
982         SDL_LockSurface(surf);
983         for (looph = 0; looph < h; ++looph)
984             memcpy(((char *)surf->pixels) + looph * surf->pitch,
985                    DATAROW(data, looph, w, h, flipped), w);
986         SDL_UnlockSurface(surf);
987     }
988     else if (!strcmp(format, "RGB")) {
989         if (len != (Py_ssize_t)w * h * 3)
990             return RAISE(
991                 PyExc_ValueError,
992                 "String length does not equal format and resolution size");
993         surf =
994             SDL_CreateRGBSurface(0, w, h, 24, 0xFF << 16, 0xFF << 8, 0xFF, 0);
995         if (!surf)
996             return RAISE(pgExc_SDLError, SDL_GetError());
997         SDL_LockSurface(surf);
998         for (looph = 0; looph < h; ++looph) {
999             Uint8 *pix =
1000                 (Uint8 *)DATAROW(surf->pixels, looph, surf->pitch, h, flipped);
1001             for (loopw = 0; loopw < w; ++loopw) {
1002 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1003                 pix[2] = data[0];
1004                 pix[1] = data[1];
1005                 pix[0] = data[2];
1006 #else
1007                 pix[0] = data[0];
1008                 pix[1] = data[1];
1009                 pix[2] = data[2];
1010 #endif
1011                 pix += 3;
1012                 data += 3;
1013             }
1014         }
1015         SDL_UnlockSurface(surf);
1016     }
1017     else if (!strcmp(format, "RGBA") || !strcmp(format, "RGBX")) {
1018         int alphamult = !strcmp(format, "RGBA");
1019         if (len != (Py_ssize_t)w * h * 4)
1020             return RAISE(
1021                 PyExc_ValueError,
1022                 "String length does not equal format and resolution size");
1023         surf = SDL_CreateRGBSurface((alphamult ? SDL_SRCALPHA : 0), w, h, 32,
1024 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1025                                     0xFF, 0xFF << 8, 0xFF << 16,
1026                                     (alphamult ? 0xFF << 24 : 0));
1027 #else
1028                                     0xFF << 24, 0xFF << 16, 0xFF << 8,
1029                                     (alphamult ? 0xFF : 0));
1030 #endif
1031         if (!surf)
1032             return RAISE(pgExc_SDLError, SDL_GetError());
1033         SDL_LockSurface(surf);
1034         for (looph = 0; looph < h; ++looph) {
1035             Uint32 *pix = (Uint32 *)DATAROW(surf->pixels, looph, surf->pitch,
1036                                             h, flipped);
1037             memcpy(pix, data, w * sizeof(Uint32));
1038             data += w * sizeof(Uint32);
1039         }
1040         SDL_UnlockSurface(surf);
1041     }
1042     else if (!strcmp(format, "ARGB")) {
1043         if (len != (Py_ssize_t)w * h * 4)
1044             return RAISE(
1045                 PyExc_ValueError,
1046                 "String length does not equal format and resolution size");
1047         surf = SDL_CreateRGBSurface(SDL_SRCALPHA, w, h, 32,
1048 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1049                                     0xFF << 8, 0xFF << 16, 0xFF << 24, 0xFF);
1050 #else
1051                                     0xFF << 16, 0xFF << 8, 0xFF, 0xFF << 24);
1052 #endif
1053         if (!surf)
1054             return RAISE(pgExc_SDLError, SDL_GetError());
1055         SDL_LockSurface(surf);
1056         for (looph = 0; looph < h; ++looph) {
1057             Uint32 *pix = (Uint32 *)DATAROW(surf->pixels, looph, surf->pitch,
1058                                             h, flipped);
1059             memcpy(pix, data, w * sizeof(Uint32));
1060             data += w * sizeof(Uint32);
1061         }
1062         SDL_UnlockSurface(surf);
1063     }
1064     else
1065         return RAISE(PyExc_ValueError, "Unrecognized type of format");
1066 
1067     return (PyObject *)pgSurface_New(surf);
1068 }
1069 
1070 static int
_as_read_buffer(PyObject * obj,const void ** buffer,Py_ssize_t * buffer_len)1071 _as_read_buffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len)
1072 {
1073     Py_buffer view;
1074 
1075     if (obj == NULL || buffer == NULL || buffer_len == NULL) {
1076         return -1;
1077     }
1078     if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0)
1079         return -1;
1080 
1081     *buffer = view.buf;
1082     *buffer_len = view.len;
1083     PyBuffer_Release(&view);
1084     return 0;
1085 }
1086 /*
1087 pgObject_AsCharBuffer is backwards compatible for PyObject_AsCharBuffer.
1088 Because PyObject_AsCharBuffer is deprecated.
1089 */
1090 int
pgObject_AsCharBuffer(PyObject * obj,const char ** buffer,Py_ssize_t * buffer_len)1091 pgObject_AsCharBuffer(PyObject *obj, const char **buffer,
1092                       Py_ssize_t *buffer_len)
1093 {
1094     return _as_read_buffer(obj, (const void **)buffer, buffer_len);
1095 }
1096 
1097 PyObject *
image_frombuffer(PyObject * self,PyObject * arg)1098 image_frombuffer(PyObject *self, PyObject *arg)
1099 {
1100     PyObject *buffer;
1101     char *format, *data;
1102     SDL_Surface *surf = NULL;
1103     int w, h;
1104     Py_ssize_t len;
1105     pgSurfaceObject *surfobj;
1106 
1107     if (!PyArg_ParseTuple(arg, "O(ii)s|i", &buffer, &w, &h, &format))
1108         return NULL;
1109 
1110     if (w < 1 || h < 1)
1111         return RAISE(PyExc_ValueError, "Resolution must be positive values");
1112 
1113     /* breaking constness here, we should really not change this string */
1114     if (pgObject_AsCharBuffer(buffer, (const char **)&data, &len) == -1)
1115         return NULL;
1116 
1117     if (!strcmp(format, "P")) {
1118         if (len != (Py_ssize_t)w * h)
1119             return RAISE(
1120                 PyExc_ValueError,
1121                 "Buffer length does not equal format and resolution size");
1122 
1123         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 8, w, 0, 0, 0, 0);
1124     }
1125     else if (!strcmp(format, "RGB")) {
1126         if (len != (Py_ssize_t)w * h * 3)
1127             return RAISE(
1128                 PyExc_ValueError,
1129                 "Buffer length does not equal format and resolution size");
1130 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1131         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 24, w * 3, 0xFF, 0xFF << 8,
1132                                         0xFF << 16, 0);
1133 #else
1134         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 24, w * 3, 0xFF << 16,
1135                                         0xFF << 8, 0xFF, 0);
1136 #endif
1137     }
1138     else if (!strcmp(format, "BGR")) {
1139         if (len != (Py_ssize_t)w * h * 3)
1140             return RAISE(
1141                 PyExc_ValueError,
1142                 "Buffer length does not equal format and resolution size");
1143 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1144         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 24, w * 3,
1145                                         0xFF << 16, 0xFF << 8,
1146                                         0xFF, 0);
1147 #else
1148         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 24, w * 3,
1149                                         0xFF, 0xFF << 8,
1150                                         0xFF << 16, 0);
1151 #endif
1152     }
1153     else if (!strcmp(format, "RGBA") || !strcmp(format, "RGBX")) {
1154         int alphamult = !strcmp(format, "RGBA");
1155         if (len != (Py_ssize_t)w * h * 4)
1156             return RAISE(
1157                 PyExc_ValueError,
1158                 "Buffer length does not equal format and resolution size");
1159         surf = SDL_CreateRGBSurfaceFrom(data, w, h, 32, w * 4,
1160 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1161                                         0xFF, 0xFF << 8, 0xFF << 16,
1162                                         (alphamult ? 0xFF << 24 : 0));
1163 #else
1164                                         0xFF << 24, 0xFF << 16, 0xFF << 8,
1165                                         (alphamult ? 0xFF : 0));
1166 #endif
1167         if (alphamult)
1168             surf->flags |= SDL_SRCALPHA;
1169     }
1170     else if (!strcmp(format, "ARGB")) {
1171         if (len != (Py_ssize_t)w * h * 4)
1172             return RAISE(
1173                 PyExc_ValueError,
1174                 "Buffer length does not equal format and resolution size");
1175         surf =
1176             SDL_CreateRGBSurfaceFrom(data, w, h, 32, w * 4,
1177 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1178                                     0xFF << 8, 0xFF << 16, 0xFF << 24, 0xFF);
1179 #else
1180                                     0xFF << 16, 0xFF << 8, 0xFF, 0xFF << 24);
1181 #endif
1182         surf->flags |= SDL_SRCALPHA;
1183     }
1184     else
1185         return RAISE(PyExc_ValueError, "Unrecognized type of format");
1186 
1187     if (!surf)
1188         return RAISE(pgExc_SDLError, SDL_GetError());
1189     surfobj = pgSurface_New(surf);
1190     Py_INCREF(buffer);
1191     surfobj->dependency = buffer;
1192     return (PyObject *)surfobj;
1193 }
1194 
1195 /*******************************************************/
1196 /* tga code by Mattias Engdegard, in the public domain */
1197 /*******************************************************/
1198 struct TGAheader {
1199     Uint8 infolen;  /* length of info field */
1200     Uint8 has_cmap; /* 1 if image has colormap, 0 otherwise */
1201     Uint8 type;
1202 
1203     Uint8 cmap_start[2]; /* index of first colormap entry */
1204     Uint8 cmap_len[2];   /* number of entries in colormap */
1205     Uint8 cmap_bits;     /* bits per colormap entry */
1206 
1207     Uint8 yorigin[2]; /* image origin (ignored here) */
1208     Uint8 xorigin[2];
1209     Uint8 width[2]; /* image size */
1210     Uint8 height[2];
1211     Uint8 pixel_bits; /* bits/pixel */
1212     Uint8 flags;
1213 };
1214 
1215 enum tga_type {
1216     TGA_TYPE_INDEXED = 1,
1217     TGA_TYPE_RGB = 2,
1218     TGA_TYPE_BW = 3,
1219     TGA_TYPE_RLE = 8 /* additive */
1220 };
1221 
1222 #define TGA_INTERLEAVE_MASK 0xc0
1223 #define TGA_INTERLEAVE_NONE 0x00
1224 #define TGA_INTERLEAVE_2WAY 0x40
1225 #define TGA_INTERLEAVE_4WAY 0x80
1226 
1227 #define TGA_ORIGIN_MASK 0x30
1228 #define TGA_ORIGIN_LEFT 0x00
1229 #define TGA_ORIGIN_RIGHT 0x10
1230 #define TGA_ORIGIN_LOWER 0x00
1231 #define TGA_ORIGIN_UPPER 0x20
1232 
1233 /* read/write unaligned little-endian 16-bit ints */
1234 #define LE16(p) ((p)[0] + ((p)[1] << 8))
1235 #define SETLE16(p, v) ((p)[0] = (v), (p)[1] = (v) >> 8)
1236 
1237 #ifndef MIN
1238 #define MIN(a, b) ((a) < (b) ? (a) : (b))
1239 #endif
1240 
1241 #define TGA_RLE_MAX 128 /* max length of a TGA RLE chunk */
1242 /* return the number of bytes in the resulting buffer after RLE-encoding
1243    a line of TGA data */
1244 static int
rle_line(Uint8 * src,Uint8 * dst,int w,int bpp)1245 rle_line(Uint8 *src, Uint8 *dst, int w, int bpp)
1246 {
1247     int x = 0;
1248     int out = 0;
1249     int raw = 0;
1250     while (x < w) {
1251         Uint32 pix;
1252         int x0 = x;
1253         memcpy(&pix, src + x * bpp, bpp);
1254         x++;
1255         while (x < w && memcmp(&pix, src + x * bpp, bpp) == 0 &&
1256                x - x0 < TGA_RLE_MAX)
1257             x++;
1258         /* use a repetition chunk iff the repeated pixels would consume
1259            two bytes or more */
1260         if ((x - x0 - 1) * bpp >= 2 || x == w) {
1261             /* output previous raw chunks */
1262             while (raw < x0) {
1263                 int n = MIN(TGA_RLE_MAX, x0 - raw);
1264                 dst[out++] = n - 1;
1265                 memcpy(dst + out, src + raw * bpp, (size_t)n * bpp);
1266                 out += n * bpp;
1267                 raw += n;
1268             }
1269 
1270             if (x - x0 > 0) {
1271                 /* output new repetition chunk */
1272                 dst[out++] = 0x7f + x - x0;
1273                 memcpy(dst + out, &pix, bpp);
1274                 out += bpp;
1275             }
1276             raw = x;
1277         }
1278     }
1279     return out;
1280 }
1281 
1282 /*
1283  * Save a surface to an output stream in TGA format.
1284  * 8bpp surfaces are saved as indexed images with 24bpp palette, or with
1285  *     32bpp palette if colourkeying is used.
1286  * 15, 16, 24 and 32bpp surfaces are saved as 24bpp RGB images,
1287  * or as 32bpp RGBA images if alpha channel is used.
1288  *
1289  * RLE compression is not used in the output file.
1290  *
1291  * Returns -1 upon error, 0 if success
1292  */
1293 static int
SaveTGA_RW(SDL_Surface * surface,SDL_RWops * out,int rle)1294 SaveTGA_RW(SDL_Surface *surface, SDL_RWops *out, int rle)
1295 {
1296     SDL_Surface *linebuf = NULL;
1297     int alpha = 0;
1298     struct TGAheader h;
1299     int srcbpp;
1300     Uint8 surf_alpha;
1301     int have_surf_colorkey = 0;
1302     Uint32 surf_colorkey;
1303     Uint32 rmask, gmask, bmask, amask;
1304     SDL_Rect r;
1305     int bpp;
1306     Uint8 *rlebuf = NULL;
1307 
1308     h.infolen = 0;
1309     SETLE16(h.cmap_start, 0);
1310 
1311     srcbpp = surface->format->BitsPerPixel;
1312     if (srcbpp < 8) {
1313         SDL_SetError("cannot save <8bpp images as TGA");
1314         return -1;
1315     }
1316 
1317     SDL_GetSurfaceAlphaMod(surface, &surf_alpha);
1318     have_surf_colorkey = (SDL_GetColorKey(surface, &surf_colorkey) == 0);
1319 
1320     if (srcbpp == 8) {
1321         h.has_cmap = 1;
1322         h.type = TGA_TYPE_INDEXED;
1323         if (have_surf_colorkey)
1324             h.cmap_bits = 32;
1325         else
1326             h.cmap_bits = 24;
1327         SETLE16(h.cmap_len, surface->format->palette->ncolors);
1328         h.pixel_bits = 8;
1329         rmask = gmask = bmask = amask = 0;
1330     }
1331     else {
1332         h.has_cmap = 0;
1333         h.type = TGA_TYPE_RGB;
1334         h.cmap_bits = 0;
1335         SETLE16(h.cmap_len, 0);
1336         if (surface->format->Amask) {
1337             alpha = 1;
1338             h.pixel_bits = 32;
1339         }
1340         else
1341             h.pixel_bits = 24;
1342         if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
1343             int s = alpha ? 0 : 8;
1344             amask = 0x000000ff >> s;
1345             rmask = 0x0000ff00 >> s;
1346             gmask = 0x00ff0000 >> s;
1347             bmask = 0xff000000 >> s;
1348         }
1349         else {
1350             amask = alpha ? 0xff000000 : 0;
1351             rmask = 0x00ff0000;
1352             gmask = 0x0000ff00;
1353             bmask = 0x000000ff;
1354         }
1355     }
1356     bpp = h.pixel_bits >> 3;
1357     if (rle)
1358         h.type += TGA_TYPE_RLE;
1359 
1360     SETLE16(h.yorigin, 0);
1361     SETLE16(h.xorigin, 0);
1362     SETLE16(h.width, surface->w);
1363     SETLE16(h.height, surface->h);
1364     h.flags = TGA_ORIGIN_UPPER | (alpha ? 8 : 0);
1365 
1366     if (!SDL_RWwrite(out, &h, sizeof(h), 1))
1367         return -1;
1368 
1369     if (h.has_cmap) {
1370         int i;
1371         SDL_Palette *pal = surface->format->palette;
1372         Uint8 entry[4];
1373         for (i = 0; i < pal->ncolors; i++) {
1374             entry[0] = pal->colors[i].b;
1375             entry[1] = pal->colors[i].g;
1376             entry[2] = pal->colors[i].r;
1377             entry[3] = ((unsigned)i == surf_colorkey) ? 0 : 0xff;
1378             if (!SDL_RWwrite(out, entry, h.cmap_bits >> 3, 1))
1379                 return -1;
1380         }
1381     }
1382 
1383     linebuf = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w, 1, h.pixel_bits,
1384                                    rmask, gmask, bmask, amask);
1385     if (!linebuf)
1386         return -1;
1387 
1388     if (h.has_cmap) {
1389         if (0 != SDL_SetPaletteColors(linebuf->format->palette,
1390                                       surface->format->palette->colors, 0,
1391                                       surface->format->palette->ncolors)) {
1392             /* SDL error already set. */
1393             goto error;
1394         }
1395     }
1396 
1397     if (rle) {
1398         rlebuf = malloc(bpp * surface->w + 1 + surface->w / TGA_RLE_MAX);
1399         if (!rlebuf) {
1400             SDL_SetError("out of memory");
1401             goto error;
1402         }
1403     }
1404 
1405     /* Temporarily remove colourkey and alpha from surface so copies are
1406        opaque */
1407     SDL_SetSurfaceAlphaMod(surface, SDL_ALPHA_OPAQUE);
1408     if (have_surf_colorkey)
1409         SDL_SetColorKey(surface, SDL_FALSE, surf_colorkey);
1410 
1411     r.x = 0;
1412     r.w = surface->w;
1413     r.h = 1;
1414     for (r.y = 0; r.y < surface->h; r.y++) {
1415         int n;
1416         void *buf;
1417         if (SDL_BlitSurface(surface, &r, linebuf, NULL) < 0)
1418             break;
1419         if (rle) {
1420             buf = rlebuf;
1421             n = rle_line(linebuf->pixels, rlebuf, surface->w, bpp);
1422         }
1423         else {
1424             buf = linebuf->pixels;
1425             n = surface->w * bpp;
1426         }
1427         if (!SDL_RWwrite(out, buf, n, 1))
1428             break;
1429     }
1430 
1431     /* restore flags */
1432     SDL_SetSurfaceAlphaMod(surface, surf_alpha);
1433     if (have_surf_colorkey)
1434         SDL_SetColorKey(surface, SDL_TRUE, surf_colorkey);
1435 
1436     free(rlebuf);
1437     SDL_FreeSurface(linebuf);
1438     return 0;
1439 
1440 error:
1441     free(rlebuf);
1442     SDL_FreeSurface(linebuf);
1443     return -1;
1444 }
1445 
1446 static int
SaveTGA(SDL_Surface * surface,const char * file,int rle)1447 SaveTGA(SDL_Surface *surface, const char *file, int rle)
1448 {
1449     SDL_RWops *out = SDL_RWFromFile(file, "wb");
1450     int ret;
1451     if (!out)
1452         return -1;
1453     ret = SaveTGA_RW(surface, out, rle);
1454     SDL_RWclose(out);
1455     return ret;
1456 }
1457 
1458 static PyMethodDef _image_methods[] = {
1459     {"load_basic", (PyCFunction)image_load_basic, METH_O, DOC_PYGAMEIMAGELOADBASIC},
1460     {"load_extended", image_load_extended, METH_VARARGS, DOC_PYGAMEIMAGELOADEXTENDED},
1461     {"load", image_load, METH_VARARGS, DOC_PYGAMEIMAGELOAD},
1462 
1463     {"save_extended", image_save_extended, METH_VARARGS, DOC_PYGAMEIMAGESAVEEXTENDED},
1464     {"save", image_save, METH_VARARGS, DOC_PYGAMEIMAGESAVE},
1465     {"get_extended", (PyCFunction)image_get_extended, METH_NOARGS,
1466      DOC_PYGAMEIMAGEGETEXTENDED},
1467     {"get_sdl_image_version", (PyCFunction)image_get_sdl_image_version, METH_NOARGS,
1468      DOC_PYGAMEIMAGEGETSDLIMAGEVERSION},
1469 
1470     {"tostring", image_tostring, METH_VARARGS, DOC_PYGAMEIMAGETOSTRING},
1471     {"fromstring", image_fromstring, METH_VARARGS, DOC_PYGAMEIMAGEFROMSTRING},
1472     {"frombuffer", image_frombuffer, METH_VARARGS, DOC_PYGAMEIMAGEFROMBUFFER},
1473     {NULL, NULL, 0, NULL}};
1474 
MODINIT_DEFINE(image)1475 MODINIT_DEFINE(image)
1476 {
1477     PyObject *module;
1478     PyObject *extmodule;
1479 
1480     static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT,
1481                                          "image",
1482                                          DOC_PYGAMEIMAGE,
1483                                          -1,
1484                                          _image_methods,
1485                                          NULL,
1486                                          NULL,
1487                                          NULL,
1488                                          NULL};
1489 
1490     /* imported needed apis; Do this first so if there is an error
1491        the module is not loaded.
1492     */
1493     import_pygame_base();
1494     if (PyErr_Occurred()) {
1495         MODINIT_ERROR;
1496     }
1497     import_pygame_surface();
1498     if (PyErr_Occurred()) {
1499         MODINIT_ERROR;
1500     }
1501     import_pygame_rwobject();
1502     if (PyErr_Occurred()) {
1503         MODINIT_ERROR;
1504     }
1505 
1506     /* create the module */
1507     module = PyModule_Create(&_module);
1508     if (module == NULL) {
1509         MODINIT_ERROR;
1510     }
1511 
1512     /* try to get extended formats */
1513     extmodule = PyImport_ImportModule(IMPPREFIX "imageext");
1514     if (extmodule) {
1515         extloadobj = PyObject_GetAttrString(extmodule, "load_extended");
1516         if (!extloadobj) {
1517             goto error;
1518         }
1519         extsaveobj = PyObject_GetAttrString(extmodule, "save_extended");
1520         if (!extsaveobj) {
1521             goto error;
1522         }
1523         extverobj = PyObject_GetAttrString(extmodule, "_get_sdl_image_version");
1524         if (!extverobj) {
1525             goto error;
1526         }
1527         Py_DECREF(extmodule);
1528     }
1529     else {
1530         // if the module could not be loaded, dont treat it like an error
1531         PyErr_Clear();
1532     }
1533     MODINIT_RETURN(module);
1534 
1535     error:
1536         Py_XDECREF(extloadobj);
1537         Py_XDECREF(extsaveobj);
1538         Py_XDECREF(extverobj);
1539         Py_DECREF(extmodule);
1540         DECREF_MOD(module);
1541         MODINIT_ERROR;
1542 }
1543