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