1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | #include "SDL_internal.h" |
22 | |
23 | #include "SDL_sysvideo.h" |
24 | #include "SDL_video_c.h" |
25 | #include "SDL_RLEaccel_c.h" |
26 | #include "SDL_pixels_c.h" |
27 | #include "SDL_stb_c.h" |
28 | #include "SDL_yuv_c.h" |
29 | #include "../render/SDL_sysrender.h" |
30 | |
31 | #include "SDL_surface_c.h" |
32 | |
33 | |
34 | // Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t |
35 | SDL_COMPILE_TIME_ASSERT(surface_size_assumptions, |
36 | sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32)); |
37 | |
38 | SDL_COMPILE_TIME_ASSERT(can_indicate_overflow, SDL_SIZE_MAX > SDL_MAX_SINT32); |
39 | |
40 | // Magic! |
41 | static char SDL_surface_magic; |
42 | |
43 | // Public routines |
44 | |
45 | bool SDL_SurfaceValid(SDL_Surface *surface) |
46 | { |
47 | return (surface && surface->reserved == &SDL_surface_magic); |
48 | } |
49 | |
50 | void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface) |
51 | { |
52 | if (SDL_SurfaceHasRLE(surface)) { |
53 | surface->flags |= SDL_SURFACE_LOCK_NEEDED; |
54 | } else { |
55 | surface->flags &= ~SDL_SURFACE_LOCK_NEEDED; |
56 | } |
57 | } |
58 | |
59 | /* |
60 | * Calculate the pad-aligned scanline width of a surface. |
61 | * |
62 | * for FOURCC, use SDL_CalculateYUVSize() |
63 | */ |
64 | static bool SDL_CalculateRGBSize(Uint32 format, size_t width, size_t height, size_t *size, size_t *pitch, bool minimal) |
65 | { |
66 | if (SDL_BITSPERPIXEL(format) >= 8) { |
67 | if (!SDL_size_mul_check_overflow(width, SDL_BYTESPERPIXEL(format), pitch)) { |
68 | return SDL_SetError("width * bpp would overflow" ); |
69 | } |
70 | } else { |
71 | if (!SDL_size_mul_check_overflow(width, SDL_BITSPERPIXEL(format), pitch)) { |
72 | return SDL_SetError("width * bpp would overflow" ); |
73 | } |
74 | if (!SDL_size_add_check_overflow(*pitch, 7, pitch)) { |
75 | return SDL_SetError("aligning pitch would overflow" ); |
76 | } |
77 | *pitch /= 8; |
78 | } |
79 | if (!minimal) { |
80 | // 4-byte aligning for speed |
81 | if (!SDL_size_add_check_overflow(*pitch, 3, pitch)) { |
82 | return SDL_SetError("aligning pitch would overflow" ); |
83 | } |
84 | *pitch &= ~3; |
85 | } |
86 | |
87 | if (!SDL_size_mul_check_overflow(height, *pitch, size)) { |
88 | return SDL_SetError("height * pitch would overflow" ); |
89 | } |
90 | |
91 | return true; |
92 | } |
93 | |
94 | bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch) |
95 | { |
96 | size_t p = 0, sz = 0; |
97 | |
98 | if (size) { |
99 | *size = 0; |
100 | } |
101 | |
102 | if (pitch) { |
103 | *pitch = 0; |
104 | } |
105 | |
106 | if (SDL_ISPIXELFORMAT_FOURCC(format)) { |
107 | if (format == SDL_PIXELFORMAT_MJPG) { |
108 | // We don't know in advance what it will be, we'll figure it out later. |
109 | return true; |
110 | } |
111 | |
112 | if (!SDL_CalculateYUVSize(format, width, height, &sz, &p)) { |
113 | // Overflow... |
114 | return false; |
115 | } |
116 | } else { |
117 | if (!SDL_CalculateRGBSize(format, width, height, &sz, &p, minimalPitch)) { |
118 | // Overflow... |
119 | return false; |
120 | } |
121 | } |
122 | |
123 | if (size) { |
124 | *size = sz; |
125 | } |
126 | |
127 | if (pitch) { |
128 | *pitch = p; |
129 | } |
130 | |
131 | return true; |
132 | } |
133 | |
134 | static bool SDL_InitializeSurface(SDL_Surface *surface, int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, SDL_PropertiesID props, void *pixels, int pitch, bool onstack) |
135 | { |
136 | SDL_zerop(surface); |
137 | |
138 | surface->flags = SDL_SURFACE_PREALLOCATED; |
139 | surface->format = format; |
140 | surface->w = width; |
141 | surface->h = height; |
142 | surface->pixels = pixels; |
143 | surface->pitch = pitch; |
144 | surface->reserved = &SDL_surface_magic; |
145 | |
146 | if (onstack) { |
147 | surface->internal_flags |= SDL_INTERNAL_SURFACE_STACK; |
148 | } |
149 | |
150 | surface->fmt = SDL_GetPixelFormatDetails(format); |
151 | if (!surface->fmt) { |
152 | SDL_DestroySurface(surface); |
153 | return false; |
154 | } |
155 | |
156 | // Initialize the clip rect |
157 | surface->clip_rect.w = width; |
158 | surface->clip_rect.h = height; |
159 | |
160 | // Allocate an empty mapping |
161 | surface->map.info.r = 0xFF; |
162 | surface->map.info.g = 0xFF; |
163 | surface->map.info.b = 0xFF; |
164 | surface->map.info.a = 0xFF; |
165 | |
166 | if (colorspace == SDL_COLORSPACE_UNKNOWN) { |
167 | surface->colorspace = SDL_GetDefaultColorspaceForFormat(format); |
168 | } else { |
169 | surface->colorspace = colorspace; |
170 | } |
171 | |
172 | if (props) { |
173 | if (!SDL_CopyProperties(props, SDL_GetSurfaceProperties(surface))) { |
174 | SDL_DestroySurface(surface); |
175 | return false; |
176 | } |
177 | } |
178 | |
179 | // By default surfaces with an alpha mask are set up for blending |
180 | if (SDL_ISPIXELFORMAT_ALPHA(surface->format)) { |
181 | SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); |
182 | } |
183 | |
184 | // The surface is ready to go |
185 | surface->refcount = 1; |
186 | return true; |
187 | } |
188 | |
189 | /* |
190 | * Create an empty surface of the appropriate depth using the given format |
191 | */ |
192 | SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format) |
193 | { |
194 | size_t pitch, size; |
195 | SDL_Surface *surface; |
196 | |
197 | if (width < 0) { |
198 | SDL_InvalidParamError("width" ); |
199 | return NULL; |
200 | } |
201 | |
202 | if (height < 0) { |
203 | SDL_InvalidParamError("height" ); |
204 | return NULL; |
205 | } |
206 | |
207 | if (!SDL_CalculateSurfaceSize(format, width, height, &size, &pitch, false /* not minimal pitch */)) { |
208 | // Overflow... |
209 | return NULL; |
210 | } |
211 | |
212 | // Allocate and initialize the surface |
213 | surface = (SDL_Surface *)SDL_malloc(sizeof(*surface)); |
214 | if (!surface) { |
215 | return NULL; |
216 | } |
217 | |
218 | if (!SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, NULL, (int)pitch, false)) { |
219 | return NULL; |
220 | } |
221 | |
222 | if (surface->w && surface->h && format != SDL_PIXELFORMAT_MJPG) { |
223 | surface->flags &= ~SDL_SURFACE_PREALLOCATED; |
224 | surface->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), size); |
225 | if (!surface->pixels) { |
226 | SDL_DestroySurface(surface); |
227 | return NULL; |
228 | } |
229 | surface->flags |= SDL_SURFACE_SIMD_ALIGNED; |
230 | |
231 | // This is important for bitmaps |
232 | SDL_memset(surface->pixels, 0, size); |
233 | } |
234 | return surface; |
235 | } |
236 | |
237 | /* |
238 | * Create an RGB surface from an existing memory buffer using the given |
239 | * enum SDL_PIXELFORMAT_* format |
240 | */ |
241 | SDL_Surface *SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format, void *pixels, int pitch) |
242 | { |
243 | if (width < 0) { |
244 | SDL_InvalidParamError("width" ); |
245 | return NULL; |
246 | } |
247 | |
248 | if (height < 0) { |
249 | SDL_InvalidParamError("height" ); |
250 | return NULL; |
251 | } |
252 | |
253 | if (pitch == 0 && !pixels) { |
254 | // The application will fill these in later with valid values |
255 | } else { |
256 | size_t minimalPitch; |
257 | |
258 | if (!SDL_CalculateSurfaceSize(format, width, height, NULL, &minimalPitch, true /* minimal pitch */)) { |
259 | // Overflow... |
260 | return NULL; |
261 | } |
262 | |
263 | if (pitch < 0 || (size_t)pitch < minimalPitch) { |
264 | SDL_InvalidParamError("pitch" ); |
265 | return NULL; |
266 | } |
267 | } |
268 | |
269 | // Allocate and initialize the surface |
270 | SDL_Surface *surface = (SDL_Surface *)SDL_malloc(sizeof(*surface)); |
271 | if (!surface || |
272 | !SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, pixels, pitch, false)) { |
273 | return NULL; |
274 | } |
275 | return surface; |
276 | } |
277 | |
278 | SDL_PropertiesID SDL_GetSurfaceProperties(SDL_Surface *surface) |
279 | { |
280 | if (!SDL_SurfaceValid(surface)) { |
281 | SDL_InvalidParamError("surface" ); |
282 | return 0; |
283 | } |
284 | |
285 | if (!surface->props) { |
286 | surface->props = SDL_CreateProperties(); |
287 | } |
288 | return surface->props; |
289 | } |
290 | |
291 | bool SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace) |
292 | { |
293 | if (!SDL_SurfaceValid(surface)) { |
294 | return SDL_InvalidParamError("surface" ); |
295 | } |
296 | |
297 | surface->colorspace = colorspace; |
298 | return true; |
299 | } |
300 | |
301 | SDL_Colorspace SDL_GetSurfaceColorspace(SDL_Surface *surface) |
302 | { |
303 | if (!SDL_SurfaceValid(surface)) { |
304 | return SDL_COLORSPACE_UNKNOWN; |
305 | } |
306 | |
307 | return surface->colorspace; |
308 | } |
309 | |
310 | float SDL_GetDefaultSDRWhitePoint(SDL_Colorspace colorspace) |
311 | { |
312 | return SDL_GetSurfaceSDRWhitePoint(NULL, colorspace); |
313 | } |
314 | |
315 | float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace) |
316 | { |
317 | SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); |
318 | |
319 | if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || |
320 | transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { |
321 | SDL_PropertiesID props; |
322 | float default_value = 1.0f; |
323 | |
324 | if (SDL_SurfaceValid(surface)) { |
325 | props = surface->props; |
326 | } else { |
327 | props = 0; |
328 | } |
329 | if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { |
330 | /* The older standards use an SDR white point of 100 nits. |
331 | * ITU-R BT.2408-6 recommends using an SDR white point of 203 nits. |
332 | * This is the default Chrome uses, and what a lot of game content |
333 | * assumes, so we'll go with that. |
334 | */ |
335 | const float DEFAULT_PQ_SDR_WHITE_POINT = 203.0f; |
336 | default_value = DEFAULT_PQ_SDR_WHITE_POINT; |
337 | } |
338 | return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, default_value); |
339 | } |
340 | return 1.0f; |
341 | } |
342 | |
343 | float SDL_GetDefaultHDRHeadroom(SDL_Colorspace colorspace) |
344 | { |
345 | return SDL_GetSurfaceHDRHeadroom(NULL, colorspace); |
346 | } |
347 | |
348 | float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) |
349 | { |
350 | SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); |
351 | |
352 | if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || |
353 | transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { |
354 | SDL_PropertiesID props; |
355 | float default_value = 0.0f; |
356 | |
357 | if (SDL_SurfaceValid(surface)) { |
358 | props = surface->props; |
359 | } else { |
360 | props = 0; |
361 | } |
362 | return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value); |
363 | } |
364 | return 1.0f; |
365 | } |
366 | |
367 | SDL_Palette *SDL_CreateSurfacePalette(SDL_Surface *surface) |
368 | { |
369 | SDL_Palette *palette; |
370 | |
371 | if (!SDL_SurfaceValid(surface)) { |
372 | SDL_InvalidParamError("surface" ); |
373 | return NULL; |
374 | } |
375 | |
376 | if (!SDL_ISPIXELFORMAT_INDEXED(surface->format)) { |
377 | SDL_SetError("The surface is not indexed format" ); |
378 | return NULL; |
379 | } |
380 | |
381 | palette = SDL_CreatePalette((1 << SDL_BITSPERPIXEL(surface->format))); |
382 | if (!palette) { |
383 | return NULL; |
384 | } |
385 | |
386 | if (palette->ncolors == 2) { |
387 | // Create a black and white bitmap palette |
388 | palette->colors[0].r = 0xFF; |
389 | palette->colors[0].g = 0xFF; |
390 | palette->colors[0].b = 0xFF; |
391 | palette->colors[1].r = 0x00; |
392 | palette->colors[1].g = 0x00; |
393 | palette->colors[1].b = 0x00; |
394 | } |
395 | |
396 | if (!SDL_SetSurfacePalette(surface, palette)) { |
397 | SDL_DestroyPalette(palette); |
398 | return NULL; |
399 | } |
400 | |
401 | // The surface has retained the palette, we can remove the reference here |
402 | SDL_assert(palette->refcount == 2); |
403 | SDL_DestroyPalette(palette); |
404 | return palette; |
405 | } |
406 | |
407 | bool SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) |
408 | { |
409 | if (!SDL_SurfaceValid(surface)) { |
410 | return SDL_InvalidParamError("surface" ); |
411 | } |
412 | |
413 | if (palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(surface->format))) { |
414 | return SDL_SetError("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format" ); |
415 | } |
416 | |
417 | if (palette != surface->palette) { |
418 | if (surface->palette) { |
419 | SDL_DestroyPalette(surface->palette); |
420 | } |
421 | |
422 | surface->palette = palette; |
423 | |
424 | if (surface->palette) { |
425 | ++surface->palette->refcount; |
426 | } |
427 | } |
428 | |
429 | SDL_InvalidateMap(&surface->map); |
430 | |
431 | return true; |
432 | } |
433 | |
434 | SDL_Palette *SDL_GetSurfacePalette(SDL_Surface *surface) |
435 | { |
436 | if (!SDL_SurfaceValid(surface)) { |
437 | return NULL; |
438 | } |
439 | |
440 | return surface->palette; |
441 | } |
442 | |
443 | bool SDL_AddSurfaceAlternateImage(SDL_Surface *surface, SDL_Surface *image) |
444 | { |
445 | if (!SDL_SurfaceValid(surface)) { |
446 | return SDL_InvalidParamError("surface" ); |
447 | } |
448 | |
449 | if (!SDL_SurfaceValid(image)) { |
450 | return SDL_InvalidParamError("image" ); |
451 | } |
452 | |
453 | SDL_Surface **images = (SDL_Surface **)SDL_realloc(surface->images, (surface->num_images + 1) * sizeof(*images)); |
454 | if (!images) { |
455 | return false; |
456 | } |
457 | images[surface->num_images] = image; |
458 | surface->images = images; |
459 | ++surface->num_images; |
460 | ++image->refcount; |
461 | return true; |
462 | } |
463 | |
464 | bool SDL_SurfaceHasAlternateImages(SDL_Surface *surface) |
465 | { |
466 | if (!SDL_SurfaceValid(surface)) { |
467 | return false; |
468 | } |
469 | |
470 | return (surface->num_images > 0); |
471 | } |
472 | |
473 | SDL_Surface **SDL_GetSurfaceImages(SDL_Surface *surface, int *count) |
474 | { |
475 | if (count) { |
476 | *count = 0; |
477 | } |
478 | |
479 | if (!SDL_SurfaceValid(surface)) { |
480 | SDL_InvalidParamError("surface" ); |
481 | return NULL; |
482 | } |
483 | |
484 | int num_images = 1 + surface->num_images; |
485 | SDL_Surface **images = (SDL_Surface **)SDL_malloc((num_images + 1) * sizeof(*images)); |
486 | if (!images) { |
487 | return NULL; |
488 | } |
489 | images[0] = surface; |
490 | if (surface->num_images > 0) { |
491 | SDL_memcpy(&images[1], surface->images, (surface->num_images * sizeof(images[1]))); |
492 | } |
493 | images[num_images] = NULL; |
494 | |
495 | if (count) { |
496 | *count = num_images; |
497 | } |
498 | return images; |
499 | } |
500 | |
501 | SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale) |
502 | { |
503 | if (!SDL_SurfaceValid(surface)) { |
504 | SDL_InvalidParamError("surface" ); |
505 | return NULL; |
506 | } |
507 | |
508 | if (!SDL_SurfaceHasAlternateImages(surface)) { |
509 | ++surface->refcount; |
510 | return surface; |
511 | } |
512 | |
513 | // This surface has high DPI images, pick the best one available, or scale one to the correct size |
514 | SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL); |
515 | if (!images) { |
516 | // Failure, fall back to the existing surface |
517 | ++surface->refcount; |
518 | return surface; |
519 | } |
520 | |
521 | // Find closest image. Images that are larger than the |
522 | // desired size are preferred over images that are smaller. |
523 | SDL_Surface *closest = NULL; |
524 | int desired_w = (int)SDL_round(surface->w * display_scale); |
525 | int desired_h = (int)SDL_round(surface->h * display_scale); |
526 | int desired_size = desired_w * desired_h; |
527 | int closest_distance = -1; |
528 | int closest_size = -1; |
529 | for (int i = 0; images[i]; ++i) { |
530 | SDL_Surface *candidate = images[i]; |
531 | int size = candidate->w * candidate->h; |
532 | int delta_w = (candidate->w - desired_w); |
533 | int delta_h = (candidate->h - desired_h); |
534 | int distance = (delta_w * delta_w) + (delta_h * delta_h); |
535 | if (closest_distance < 0 || distance < closest_distance || |
536 | (size > desired_size && closest_size < desired_size)) { |
537 | closest = candidate; |
538 | closest_distance = distance; |
539 | closest_size = size; |
540 | } |
541 | } |
542 | SDL_free(images); |
543 | SDL_assert(closest != NULL); // We should always have at least one surface |
544 | |
545 | if (closest->w == desired_w && closest->h == desired_h) { |
546 | ++closest->refcount; |
547 | return closest; |
548 | } |
549 | |
550 | // We need to scale the image to the correct size. To maintain good image quality, downscaling |
551 | // is done in steps, never reducing the width and height by more than half each time. |
552 | SDL_Surface *scaled = closest; |
553 | do { |
554 | int next_scaled_w = SDL_max(desired_w, (scaled->w + 1) / 2); |
555 | int next_scaled_h = SDL_max(desired_h, (scaled->h + 1) / 2); |
556 | SDL_Surface *next_scaled = SDL_ScaleSurface(scaled, next_scaled_w, next_scaled_h, SDL_SCALEMODE_LINEAR); |
557 | if (scaled != closest) { |
558 | SDL_DestroySurface(scaled); |
559 | } |
560 | scaled = next_scaled; |
561 | if (!scaled) { |
562 | // Failure, fall back to the closest surface |
563 | ++closest->refcount; |
564 | return closest; |
565 | } |
566 | } while (scaled->w != desired_w || scaled->h != desired_h); |
567 | |
568 | return scaled; |
569 | } |
570 | |
571 | void SDL_RemoveSurfaceAlternateImages(SDL_Surface *surface) |
572 | { |
573 | if (!SDL_SurfaceValid(surface)) { |
574 | return; |
575 | } |
576 | |
577 | if (surface->num_images > 0) { |
578 | for (int i = 0; i < surface->num_images; ++i) { |
579 | SDL_DestroySurface(surface->images[i]); |
580 | } |
581 | SDL_free(surface->images); |
582 | surface->images = NULL; |
583 | surface->num_images = 0; |
584 | } |
585 | } |
586 | |
587 | bool SDL_SetSurfaceRLE(SDL_Surface *surface, bool enabled) |
588 | { |
589 | int flags; |
590 | |
591 | if (!SDL_SurfaceValid(surface)) { |
592 | return SDL_InvalidParamError("surface" ); |
593 | } |
594 | |
595 | flags = surface->map.info.flags; |
596 | if (enabled) { |
597 | surface->map.info.flags |= SDL_COPY_RLE_DESIRED; |
598 | } else { |
599 | surface->map.info.flags &= ~SDL_COPY_RLE_DESIRED; |
600 | } |
601 | if (surface->map.info.flags != flags) { |
602 | SDL_InvalidateMap(&surface->map); |
603 | } |
604 | SDL_UpdateSurfaceLockFlag(surface); |
605 | return true; |
606 | } |
607 | |
608 | bool SDL_SurfaceHasRLE(SDL_Surface *surface) |
609 | { |
610 | if (!SDL_SurfaceValid(surface)) { |
611 | return false; |
612 | } |
613 | |
614 | if (!(surface->map.info.flags & SDL_COPY_RLE_DESIRED)) { |
615 | return false; |
616 | } |
617 | |
618 | return true; |
619 | } |
620 | |
621 | bool SDL_SetSurfaceColorKey(SDL_Surface *surface, bool enabled, Uint32 key) |
622 | { |
623 | int flags; |
624 | |
625 | if (!SDL_SurfaceValid(surface)) { |
626 | return SDL_InvalidParamError("surface" ); |
627 | } |
628 | |
629 | if (surface->palette && key >= ((Uint32)surface->palette->ncolors)) { |
630 | return SDL_InvalidParamError("key" ); |
631 | } |
632 | |
633 | flags = surface->map.info.flags; |
634 | if (enabled) { |
635 | surface->map.info.flags |= SDL_COPY_COLORKEY; |
636 | surface->map.info.colorkey = key; |
637 | } else { |
638 | surface->map.info.flags &= ~SDL_COPY_COLORKEY; |
639 | } |
640 | if (surface->map.info.flags != flags) { |
641 | SDL_InvalidateMap(&surface->map); |
642 | } |
643 | |
644 | return true; |
645 | } |
646 | |
647 | bool SDL_SurfaceHasColorKey(SDL_Surface *surface) |
648 | { |
649 | if (!SDL_SurfaceValid(surface)) { |
650 | return false; |
651 | } |
652 | |
653 | if (!(surface->map.info.flags & SDL_COPY_COLORKEY)) { |
654 | return false; |
655 | } |
656 | |
657 | return true; |
658 | } |
659 | |
660 | bool SDL_GetSurfaceColorKey(SDL_Surface *surface, Uint32 *key) |
661 | { |
662 | if (key) { |
663 | *key = 0; |
664 | } |
665 | |
666 | if (!SDL_SurfaceValid(surface)) { |
667 | return SDL_InvalidParamError("surface" ); |
668 | } |
669 | |
670 | if (!(surface->map.info.flags & SDL_COPY_COLORKEY)) { |
671 | return SDL_SetError("Surface doesn't have a colorkey" ); |
672 | } |
673 | |
674 | if (key) { |
675 | *key = surface->map.info.colorkey; |
676 | } |
677 | return true; |
678 | } |
679 | |
680 | /* This is a fairly slow function to switch from colorkey to alpha |
681 | NB: it doesn't handle bpp 1 or 3, because they have no alpha channel */ |
682 | static void SDL_ConvertColorkeyToAlpha(SDL_Surface *surface, bool ignore_alpha) |
683 | { |
684 | int x, y, bpp; |
685 | |
686 | if (!SDL_SurfaceValid(surface)) { |
687 | return; |
688 | } |
689 | |
690 | if (!(surface->map.info.flags & SDL_COPY_COLORKEY) || |
691 | !SDL_ISPIXELFORMAT_ALPHA(surface->format)) { |
692 | return; |
693 | } |
694 | |
695 | bpp = SDL_BYTESPERPIXEL(surface->format); |
696 | |
697 | SDL_LockSurface(surface); |
698 | |
699 | if (bpp == 2) { |
700 | Uint16 *row, *spot; |
701 | Uint16 ckey = (Uint16)surface->map.info.colorkey; |
702 | Uint16 mask = (Uint16)(~surface->fmt->Amask); |
703 | |
704 | // Ignore, or not, alpha in colorkey comparison |
705 | if (ignore_alpha) { |
706 | ckey &= mask; |
707 | row = (Uint16 *)surface->pixels; |
708 | for (y = surface->h; y--;) { |
709 | spot = row; |
710 | for (x = surface->w; x--;) { |
711 | if ((*spot & mask) == ckey) { |
712 | *spot &= mask; |
713 | } |
714 | ++spot; |
715 | } |
716 | row += surface->pitch / 2; |
717 | } |
718 | } else { |
719 | row = (Uint16 *)surface->pixels; |
720 | for (y = surface->h; y--;) { |
721 | spot = row; |
722 | for (x = surface->w; x--;) { |
723 | if (*spot == ckey) { |
724 | *spot &= mask; |
725 | } |
726 | ++spot; |
727 | } |
728 | row += surface->pitch / 2; |
729 | } |
730 | } |
731 | } else if (bpp == 4) { |
732 | Uint32 *row, *spot; |
733 | Uint32 ckey = surface->map.info.colorkey; |
734 | Uint32 mask = ~surface->fmt->Amask; |
735 | |
736 | // Ignore, or not, alpha in colorkey comparison |
737 | if (ignore_alpha) { |
738 | ckey &= mask; |
739 | row = (Uint32 *)surface->pixels; |
740 | for (y = surface->h; y--;) { |
741 | spot = row; |
742 | for (x = surface->w; x--;) { |
743 | if ((*spot & mask) == ckey) { |
744 | *spot &= mask; |
745 | } |
746 | ++spot; |
747 | } |
748 | row += surface->pitch / 4; |
749 | } |
750 | } else { |
751 | row = (Uint32 *)surface->pixels; |
752 | for (y = surface->h; y--;) { |
753 | spot = row; |
754 | for (x = surface->w; x--;) { |
755 | if (*spot == ckey) { |
756 | *spot &= mask; |
757 | } |
758 | ++spot; |
759 | } |
760 | row += surface->pitch / 4; |
761 | } |
762 | } |
763 | } |
764 | |
765 | SDL_UnlockSurface(surface); |
766 | |
767 | SDL_SetSurfaceColorKey(surface, false, 0); |
768 | SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); |
769 | } |
770 | |
771 | bool SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b) |
772 | { |
773 | int flags; |
774 | |
775 | if (!SDL_SurfaceValid(surface)) { |
776 | return SDL_InvalidParamError("surface" ); |
777 | } |
778 | |
779 | surface->map.info.r = r; |
780 | surface->map.info.g = g; |
781 | surface->map.info.b = b; |
782 | |
783 | flags = surface->map.info.flags; |
784 | if (r != 0xFF || g != 0xFF || b != 0xFF) { |
785 | surface->map.info.flags |= SDL_COPY_MODULATE_COLOR; |
786 | } else { |
787 | surface->map.info.flags &= ~SDL_COPY_MODULATE_COLOR; |
788 | } |
789 | if (surface->map.info.flags != flags) { |
790 | SDL_InvalidateMap(&surface->map); |
791 | } |
792 | return true; |
793 | } |
794 | |
795 | bool SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b) |
796 | { |
797 | if (!SDL_SurfaceValid(surface)) { |
798 | if (r) { |
799 | *r = 255; |
800 | } |
801 | if (g) { |
802 | *g = 255; |
803 | } |
804 | if (b) { |
805 | *b = 255; |
806 | } |
807 | return SDL_InvalidParamError("surface" ); |
808 | } |
809 | |
810 | if (r) { |
811 | *r = surface->map.info.r; |
812 | } |
813 | if (g) { |
814 | *g = surface->map.info.g; |
815 | } |
816 | if (b) { |
817 | *b = surface->map.info.b; |
818 | } |
819 | return true; |
820 | } |
821 | |
822 | bool SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha) |
823 | { |
824 | int flags; |
825 | |
826 | if (!SDL_SurfaceValid(surface)) { |
827 | return SDL_InvalidParamError("surface" ); |
828 | } |
829 | |
830 | surface->map.info.a = alpha; |
831 | |
832 | flags = surface->map.info.flags; |
833 | if (alpha != 0xFF) { |
834 | surface->map.info.flags |= SDL_COPY_MODULATE_ALPHA; |
835 | } else { |
836 | surface->map.info.flags &= ~SDL_COPY_MODULATE_ALPHA; |
837 | } |
838 | if (surface->map.info.flags != flags) { |
839 | SDL_InvalidateMap(&surface->map); |
840 | } |
841 | return true; |
842 | } |
843 | |
844 | bool SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha) |
845 | { |
846 | if (!SDL_SurfaceValid(surface)) { |
847 | if (alpha) { |
848 | *alpha = 255; |
849 | } |
850 | return SDL_InvalidParamError("surface" ); |
851 | } |
852 | |
853 | if (alpha) { |
854 | *alpha = surface->map.info.a; |
855 | } |
856 | return true; |
857 | } |
858 | |
859 | bool SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode) |
860 | { |
861 | int flags; |
862 | bool result = true; |
863 | |
864 | if (!SDL_SurfaceValid(surface)) { |
865 | return SDL_InvalidParamError("surface" ); |
866 | } |
867 | |
868 | if (blendMode == SDL_BLENDMODE_INVALID) { |
869 | return SDL_InvalidParamError("blendMode" ); |
870 | } |
871 | |
872 | flags = surface->map.info.flags; |
873 | surface->map.info.flags &= ~SDL_COPY_BLEND_MASK; |
874 | switch (blendMode) { |
875 | case SDL_BLENDMODE_NONE: |
876 | break; |
877 | case SDL_BLENDMODE_BLEND: |
878 | surface->map.info.flags |= SDL_COPY_BLEND; |
879 | break; |
880 | case SDL_BLENDMODE_BLEND_PREMULTIPLIED: |
881 | surface->map.info.flags |= SDL_COPY_BLEND_PREMULTIPLIED; |
882 | break; |
883 | case SDL_BLENDMODE_ADD: |
884 | surface->map.info.flags |= SDL_COPY_ADD; |
885 | break; |
886 | case SDL_BLENDMODE_ADD_PREMULTIPLIED: |
887 | surface->map.info.flags |= SDL_COPY_ADD_PREMULTIPLIED; |
888 | break; |
889 | case SDL_BLENDMODE_MOD: |
890 | surface->map.info.flags |= SDL_COPY_MOD; |
891 | break; |
892 | case SDL_BLENDMODE_MUL: |
893 | surface->map.info.flags |= SDL_COPY_MUL; |
894 | break; |
895 | default: |
896 | result = SDL_Unsupported(); |
897 | break; |
898 | } |
899 | |
900 | if (surface->map.info.flags != flags) { |
901 | SDL_InvalidateMap(&surface->map); |
902 | } |
903 | |
904 | return result; |
905 | } |
906 | |
907 | bool SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode) |
908 | { |
909 | if (blendMode) { |
910 | *blendMode = SDL_BLENDMODE_INVALID; |
911 | } |
912 | |
913 | if (!SDL_SurfaceValid(surface)) { |
914 | return SDL_InvalidParamError("surface" ); |
915 | } |
916 | |
917 | if (!blendMode) { |
918 | return true; |
919 | } |
920 | |
921 | switch (surface->map.info.flags & SDL_COPY_BLEND_MASK) { |
922 | case SDL_COPY_BLEND: |
923 | *blendMode = SDL_BLENDMODE_BLEND; |
924 | break; |
925 | case SDL_COPY_BLEND_PREMULTIPLIED: |
926 | *blendMode = SDL_BLENDMODE_BLEND_PREMULTIPLIED; |
927 | break; |
928 | case SDL_COPY_ADD: |
929 | *blendMode = SDL_BLENDMODE_ADD; |
930 | break; |
931 | case SDL_COPY_ADD_PREMULTIPLIED: |
932 | *blendMode = SDL_BLENDMODE_ADD_PREMULTIPLIED; |
933 | break; |
934 | case SDL_COPY_MOD: |
935 | *blendMode = SDL_BLENDMODE_MOD; |
936 | break; |
937 | case SDL_COPY_MUL: |
938 | *blendMode = SDL_BLENDMODE_MUL; |
939 | break; |
940 | default: |
941 | *blendMode = SDL_BLENDMODE_NONE; |
942 | break; |
943 | } |
944 | return true; |
945 | } |
946 | |
947 | bool SDL_SetSurfaceClipRect(SDL_Surface *surface, const SDL_Rect *rect) |
948 | { |
949 | SDL_Rect full_rect; |
950 | |
951 | // Don't do anything if there's no surface to act on |
952 | if (!SDL_SurfaceValid(surface)) { |
953 | return false; |
954 | } |
955 | |
956 | // Set up the full surface rectangle |
957 | full_rect.x = 0; |
958 | full_rect.y = 0; |
959 | full_rect.w = surface->w; |
960 | full_rect.h = surface->h; |
961 | |
962 | // Set the clipping rectangle |
963 | if (!rect) { |
964 | surface->clip_rect = full_rect; |
965 | return true; |
966 | } |
967 | return SDL_GetRectIntersection(rect, &full_rect, &surface->clip_rect); |
968 | } |
969 | |
970 | bool SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect) |
971 | { |
972 | if (!SDL_SurfaceValid(surface)) { |
973 | if (rect) { |
974 | SDL_zerop(rect); |
975 | } |
976 | return SDL_InvalidParamError("surface" ); |
977 | } |
978 | if (!rect) { |
979 | return SDL_InvalidParamError("rect" ); |
980 | } |
981 | *rect = surface->clip_rect; |
982 | return true; |
983 | } |
984 | |
985 | /* |
986 | * Set up a blit between two surfaces -- split into three parts: |
987 | * The upper part, SDL_BlitSurface(), performs clipping and rectangle |
988 | * verification. The lower part is a pointer to a low level |
989 | * accelerated blitting function. |
990 | * |
991 | * These parts are separated out and each used internally by this |
992 | * library in the optimum places. They are exported so that if |
993 | * you know exactly what you are doing, you can optimize your code |
994 | * by calling the one(s) you need. |
995 | */ |
996 | bool SDL_BlitSurfaceUnchecked(SDL_Surface *src, const SDL_Rect *srcrect, |
997 | SDL_Surface *dst, const SDL_Rect *dstrect) |
998 | { |
999 | // Check to make sure the blit mapping is valid |
1000 | if (!SDL_ValidateMap(src, dst)) { |
1001 | return false; |
1002 | } |
1003 | return src->map.blit(src, srcrect, dst, dstrect); |
1004 | } |
1005 | |
1006 | bool SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect) |
1007 | { |
1008 | SDL_Rect r_src, r_dst; |
1009 | |
1010 | // Make sure the surfaces aren't locked |
1011 | if (!SDL_SurfaceValid(src)) { |
1012 | return SDL_InvalidParamError("src" ); |
1013 | } else if (!SDL_SurfaceValid(dst)) { |
1014 | return SDL_InvalidParamError("dst" ); |
1015 | } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { |
1016 | return SDL_SetError("Surfaces must not be locked during blit" ); |
1017 | } |
1018 | |
1019 | // Full src surface |
1020 | r_src.x = 0; |
1021 | r_src.y = 0; |
1022 | r_src.w = src->w; |
1023 | r_src.h = src->h; |
1024 | |
1025 | if (dstrect) { |
1026 | r_dst.x = dstrect->x; |
1027 | r_dst.y = dstrect->y; |
1028 | } else { |
1029 | r_dst.x = 0; |
1030 | r_dst.y = 0; |
1031 | } |
1032 | |
1033 | // clip the source rectangle to the source surface |
1034 | if (srcrect) { |
1035 | SDL_Rect tmp; |
1036 | if (SDL_GetRectIntersection(srcrect, &r_src, &tmp) == false) { |
1037 | return true; |
1038 | } |
1039 | |
1040 | // Shift dstrect, if srcrect origin has changed |
1041 | r_dst.x += tmp.x - srcrect->x; |
1042 | r_dst.y += tmp.y - srcrect->y; |
1043 | |
1044 | // Update srcrect |
1045 | r_src = tmp; |
1046 | } |
1047 | |
1048 | // There're no dstrect.w/h parameters. It's the same as srcrect |
1049 | r_dst.w = r_src.w; |
1050 | r_dst.h = r_src.h; |
1051 | |
1052 | // clip the destination rectangle against the clip rectangle |
1053 | { |
1054 | SDL_Rect tmp; |
1055 | if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &tmp) == false) { |
1056 | return true; |
1057 | } |
1058 | |
1059 | // Shift srcrect, if dstrect has changed |
1060 | r_src.x += tmp.x - r_dst.x; |
1061 | r_src.y += tmp.y - r_dst.y; |
1062 | r_src.w = tmp.w; |
1063 | r_src.h = tmp.h; |
1064 | |
1065 | // Update dstrect |
1066 | r_dst = tmp; |
1067 | } |
1068 | |
1069 | if (r_dst.w <= 0 || r_dst.h <= 0) { |
1070 | // No-op. |
1071 | return true; |
1072 | } |
1073 | |
1074 | // Switch back to a fast blit if we were previously stretching |
1075 | if (src->map.info.flags & SDL_COPY_NEAREST) { |
1076 | src->map.info.flags &= ~SDL_COPY_NEAREST; |
1077 | SDL_InvalidateMap(&src->map); |
1078 | } |
1079 | |
1080 | return SDL_BlitSurfaceUnchecked(src, &r_src, dst, &r_dst); |
1081 | } |
1082 | |
1083 | bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode) |
1084 | { |
1085 | SDL_Rect *clip_rect; |
1086 | double src_x0, src_y0, src_x1, src_y1; |
1087 | double dst_x0, dst_y0, dst_x1, dst_y1; |
1088 | SDL_Rect final_src, final_dst; |
1089 | double scaling_w, scaling_h; |
1090 | int src_w, src_h; |
1091 | int dst_w, dst_h; |
1092 | |
1093 | // Make sure the surfaces aren't locked |
1094 | if (!SDL_SurfaceValid(src)) { |
1095 | return SDL_InvalidParamError("src" ); |
1096 | } else if (!SDL_SurfaceValid(dst)) { |
1097 | return SDL_InvalidParamError("dst" ); |
1098 | } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { |
1099 | return SDL_SetError("Surfaces must not be locked during blit" ); |
1100 | } |
1101 | |
1102 | switch (scaleMode) { |
1103 | case SDL_SCALEMODE_NEAREST: |
1104 | break; |
1105 | case SDL_SCALEMODE_LINEAR: |
1106 | break; |
1107 | case SDL_SCALEMODE_PIXELART: |
1108 | scaleMode = SDL_SCALEMODE_NEAREST; |
1109 | break; |
1110 | default: |
1111 | return SDL_InvalidParamError("scaleMode" ); |
1112 | } |
1113 | |
1114 | if (!srcrect) { |
1115 | src_w = src->w; |
1116 | src_h = src->h; |
1117 | } else { |
1118 | src_w = srcrect->w; |
1119 | src_h = srcrect->h; |
1120 | } |
1121 | |
1122 | if (!dstrect) { |
1123 | dst_w = dst->w; |
1124 | dst_h = dst->h; |
1125 | } else { |
1126 | dst_w = dstrect->w; |
1127 | dst_h = dstrect->h; |
1128 | } |
1129 | |
1130 | if (dst_w == src_w && dst_h == src_h) { |
1131 | // No scaling, defer to regular blit |
1132 | return SDL_BlitSurface(src, srcrect, dst, dstrect); |
1133 | } |
1134 | |
1135 | scaling_w = (double)dst_w / src_w; |
1136 | scaling_h = (double)dst_h / src_h; |
1137 | |
1138 | if (!dstrect) { |
1139 | dst_x0 = 0; |
1140 | dst_y0 = 0; |
1141 | dst_x1 = dst_w; |
1142 | dst_y1 = dst_h; |
1143 | } else { |
1144 | dst_x0 = dstrect->x; |
1145 | dst_y0 = dstrect->y; |
1146 | dst_x1 = dst_x0 + dst_w; |
1147 | dst_y1 = dst_y0 + dst_h; |
1148 | } |
1149 | |
1150 | if (!srcrect) { |
1151 | src_x0 = 0; |
1152 | src_y0 = 0; |
1153 | src_x1 = src_w; |
1154 | src_y1 = src_h; |
1155 | } else { |
1156 | src_x0 = srcrect->x; |
1157 | src_y0 = srcrect->y; |
1158 | src_x1 = src_x0 + src_w; |
1159 | src_y1 = src_y0 + src_h; |
1160 | |
1161 | // Clip source rectangle to the source surface |
1162 | |
1163 | if (src_x0 < 0) { |
1164 | dst_x0 -= src_x0 * scaling_w; |
1165 | src_x0 = 0; |
1166 | } |
1167 | |
1168 | if (src_x1 > src->w) { |
1169 | dst_x1 -= (src_x1 - src->w) * scaling_w; |
1170 | src_x1 = src->w; |
1171 | } |
1172 | |
1173 | if (src_y0 < 0) { |
1174 | dst_y0 -= src_y0 * scaling_h; |
1175 | src_y0 = 0; |
1176 | } |
1177 | |
1178 | if (src_y1 > src->h) { |
1179 | dst_y1 -= (src_y1 - src->h) * scaling_h; |
1180 | src_y1 = src->h; |
1181 | } |
1182 | } |
1183 | |
1184 | // Clip destination rectangle to the clip rectangle |
1185 | clip_rect = &dst->clip_rect; |
1186 | |
1187 | // Translate to clip space for easier calculations |
1188 | dst_x0 -= clip_rect->x; |
1189 | dst_x1 -= clip_rect->x; |
1190 | dst_y0 -= clip_rect->y; |
1191 | dst_y1 -= clip_rect->y; |
1192 | |
1193 | if (dst_x0 < 0) { |
1194 | src_x0 -= dst_x0 / scaling_w; |
1195 | dst_x0 = 0; |
1196 | } |
1197 | |
1198 | if (dst_x1 > clip_rect->w) { |
1199 | src_x1 -= (dst_x1 - clip_rect->w) / scaling_w; |
1200 | dst_x1 = clip_rect->w; |
1201 | } |
1202 | |
1203 | if (dst_y0 < 0) { |
1204 | src_y0 -= dst_y0 / scaling_h; |
1205 | dst_y0 = 0; |
1206 | } |
1207 | |
1208 | if (dst_y1 > clip_rect->h) { |
1209 | src_y1 -= (dst_y1 - clip_rect->h) / scaling_h; |
1210 | dst_y1 = clip_rect->h; |
1211 | } |
1212 | |
1213 | // Translate back to surface coordinates |
1214 | dst_x0 += clip_rect->x; |
1215 | dst_x1 += clip_rect->x; |
1216 | dst_y0 += clip_rect->y; |
1217 | dst_y1 += clip_rect->y; |
1218 | |
1219 | final_src.x = (int)SDL_round(src_x0); |
1220 | final_src.y = (int)SDL_round(src_y0); |
1221 | final_src.w = (int)SDL_round(src_x1 - src_x0); |
1222 | final_src.h = (int)SDL_round(src_y1 - src_y0); |
1223 | |
1224 | final_dst.x = (int)SDL_round(dst_x0); |
1225 | final_dst.y = (int)SDL_round(dst_y0); |
1226 | final_dst.w = (int)SDL_round(dst_x1 - dst_x0); |
1227 | final_dst.h = (int)SDL_round(dst_y1 - dst_y0); |
1228 | |
1229 | // Clip again |
1230 | { |
1231 | SDL_Rect tmp; |
1232 | tmp.x = 0; |
1233 | tmp.y = 0; |
1234 | tmp.w = src->w; |
1235 | tmp.h = src->h; |
1236 | SDL_GetRectIntersection(&tmp, &final_src, &final_src); |
1237 | } |
1238 | |
1239 | // Clip again |
1240 | SDL_GetRectIntersection(clip_rect, &final_dst, &final_dst); |
1241 | |
1242 | if (final_dst.w == 0 || final_dst.h == 0 || |
1243 | final_src.w < 0 || final_src.h < 0) { |
1244 | // No-op. |
1245 | return true; |
1246 | } |
1247 | |
1248 | return SDL_BlitSurfaceUncheckedScaled(src, &final_src, dst, &final_dst, scaleMode); |
1249 | } |
1250 | |
1251 | /** |
1252 | * This is a semi-private blit function and it performs low-level surface |
1253 | * scaled blitting only. |
1254 | */ |
1255 | bool SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode) |
1256 | { |
1257 | static const Uint32 complex_copy_flags = (SDL_COPY_MODULATE_MASK | SDL_COPY_BLEND_MASK | SDL_COPY_COLORKEY); |
1258 | |
1259 | if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 || |
1260 | dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) { |
1261 | return SDL_SetError("Size too large for scaling" ); |
1262 | } |
1263 | |
1264 | if (!(src->map.info.flags & SDL_COPY_NEAREST)) { |
1265 | src->map.info.flags |= SDL_COPY_NEAREST; |
1266 | SDL_InvalidateMap(&src->map); |
1267 | } |
1268 | |
1269 | if (scaleMode == SDL_SCALEMODE_NEAREST || scaleMode == SDL_SCALEMODE_PIXELART) { |
1270 | if (!(src->map.info.flags & complex_copy_flags) && |
1271 | src->format == dst->format && |
1272 | !SDL_ISPIXELFORMAT_INDEXED(src->format) && |
1273 | SDL_BYTESPERPIXEL(src->format) <= 4) { |
1274 | return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST); |
1275 | } else if (SDL_BITSPERPIXEL(src->format) < 8) { |
1276 | // Scaling bitmap not yet supported, convert to RGBA for blit |
1277 | bool result = false; |
1278 | SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888); |
1279 | if (tmp) { |
1280 | result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST); |
1281 | SDL_DestroySurface(tmp); |
1282 | } |
1283 | return result; |
1284 | } else { |
1285 | return SDL_BlitSurfaceUnchecked(src, srcrect, dst, dstrect); |
1286 | } |
1287 | } else { |
1288 | if (!(src->map.info.flags & complex_copy_flags) && |
1289 | src->format == dst->format && |
1290 | !SDL_ISPIXELFORMAT_INDEXED(src->format) && |
1291 | SDL_BYTESPERPIXEL(src->format) == 4 && |
1292 | src->format != SDL_PIXELFORMAT_ARGB2101010) { |
1293 | // fast path |
1294 | return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_LINEAR); |
1295 | } else if (SDL_BITSPERPIXEL(src->format) < 8) { |
1296 | // Scaling bitmap not yet supported, convert to RGBA for blit |
1297 | bool result = false; |
1298 | SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888); |
1299 | if (tmp) { |
1300 | result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, scaleMode); |
1301 | SDL_DestroySurface(tmp); |
1302 | } |
1303 | return result; |
1304 | } else { |
1305 | // Use intermediate surface(s) |
1306 | SDL_Surface *tmp1 = NULL; |
1307 | bool result; |
1308 | SDL_Rect srcrect2; |
1309 | int is_complex_copy_flags = (src->map.info.flags & complex_copy_flags); |
1310 | |
1311 | Uint8 r, g, b; |
1312 | Uint8 alpha; |
1313 | SDL_BlendMode blendMode; |
1314 | |
1315 | // Save source infos |
1316 | SDL_GetSurfaceColorMod(src, &r, &g, &b); |
1317 | SDL_GetSurfaceAlphaMod(src, &alpha); |
1318 | SDL_GetSurfaceBlendMode(src, &blendMode); |
1319 | srcrect2.x = srcrect->x; |
1320 | srcrect2.y = srcrect->y; |
1321 | srcrect2.w = srcrect->w; |
1322 | srcrect2.h = srcrect->h; |
1323 | |
1324 | // Change source format if not appropriate for scaling |
1325 | if (SDL_BYTESPERPIXEL(src->format) != 4 || src->format == SDL_PIXELFORMAT_ARGB2101010) { |
1326 | SDL_Rect tmprect; |
1327 | SDL_PixelFormat fmt; |
1328 | tmprect.x = 0; |
1329 | tmprect.y = 0; |
1330 | tmprect.w = src->w; |
1331 | tmprect.h = src->h; |
1332 | if (SDL_BYTESPERPIXEL(dst->format) == 4 && dst->format != SDL_PIXELFORMAT_ARGB2101010) { |
1333 | fmt = dst->format; |
1334 | } else { |
1335 | fmt = SDL_PIXELFORMAT_ARGB8888; |
1336 | } |
1337 | tmp1 = SDL_CreateSurface(src->w, src->h, fmt); |
1338 | SDL_BlitSurfaceUnchecked(src, srcrect, tmp1, &tmprect); |
1339 | |
1340 | srcrect2.x = 0; |
1341 | srcrect2.y = 0; |
1342 | SDL_SetSurfaceColorMod(tmp1, r, g, b); |
1343 | SDL_SetSurfaceAlphaMod(tmp1, alpha); |
1344 | SDL_SetSurfaceBlendMode(tmp1, blendMode); |
1345 | |
1346 | src = tmp1; |
1347 | } |
1348 | |
1349 | // Intermediate scaling |
1350 | if (is_complex_copy_flags || src->format != dst->format) { |
1351 | SDL_Rect tmprect; |
1352 | SDL_Surface *tmp2 = SDL_CreateSurface(dstrect->w, dstrect->h, src->format); |
1353 | SDL_StretchSurface(src, &srcrect2, tmp2, NULL, SDL_SCALEMODE_LINEAR); |
1354 | |
1355 | SDL_SetSurfaceColorMod(tmp2, r, g, b); |
1356 | SDL_SetSurfaceAlphaMod(tmp2, alpha); |
1357 | SDL_SetSurfaceBlendMode(tmp2, blendMode); |
1358 | |
1359 | tmprect.x = 0; |
1360 | tmprect.y = 0; |
1361 | tmprect.w = dstrect->w; |
1362 | tmprect.h = dstrect->h; |
1363 | result = SDL_BlitSurfaceUnchecked(tmp2, &tmprect, dst, dstrect); |
1364 | SDL_DestroySurface(tmp2); |
1365 | } else { |
1366 | result = SDL_StretchSurface(src, &srcrect2, dst, dstrect, SDL_SCALEMODE_LINEAR); |
1367 | } |
1368 | |
1369 | SDL_DestroySurface(tmp1); |
1370 | return result; |
1371 | } |
1372 | } |
1373 | } |
1374 | |
1375 | bool SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect) |
1376 | { |
1377 | SDL_Rect r_src, r_dst; |
1378 | |
1379 | // Make sure the surfaces aren't locked |
1380 | if (!SDL_SurfaceValid(src)) { |
1381 | return SDL_InvalidParamError("src" ); |
1382 | } else if (!SDL_SurfaceValid(dst)) { |
1383 | return SDL_InvalidParamError("dst" ); |
1384 | } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { |
1385 | return SDL_SetError("Surfaces must not be locked during blit" ); |
1386 | } |
1387 | |
1388 | // Full src surface |
1389 | r_src.x = 0; |
1390 | r_src.y = 0; |
1391 | r_src.w = src->w; |
1392 | r_src.h = src->h; |
1393 | |
1394 | if (dstrect) { |
1395 | r_dst.x = dstrect->x; |
1396 | r_dst.y = dstrect->y; |
1397 | r_dst.w = dstrect->w; |
1398 | r_dst.h = dstrect->h; |
1399 | } else { |
1400 | r_dst.x = 0; |
1401 | r_dst.y = 0; |
1402 | r_dst.w = dst->w; |
1403 | r_dst.h = dst->h; |
1404 | } |
1405 | |
1406 | // clip the source rectangle to the source surface |
1407 | if (srcrect) { |
1408 | if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) { |
1409 | return true; |
1410 | } |
1411 | |
1412 | // For tiling we don't adjust the destination rectangle |
1413 | } |
1414 | |
1415 | // clip the destination rectangle against the clip rectangle |
1416 | { |
1417 | if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) { |
1418 | return true; |
1419 | } |
1420 | |
1421 | // For tiling we don't adjust the source rectangle |
1422 | } |
1423 | |
1424 | // Switch back to a fast blit if we were previously stretching |
1425 | if (src->map.info.flags & SDL_COPY_NEAREST) { |
1426 | src->map.info.flags &= ~SDL_COPY_NEAREST; |
1427 | SDL_InvalidateMap(&src->map); |
1428 | } |
1429 | |
1430 | int rows = r_dst.h / r_src.h; |
1431 | int cols = r_dst.w / r_src.w; |
1432 | int remaining_w = r_dst.w % r_src.w; |
1433 | int remaining_h = r_dst.h % r_src.h; |
1434 | SDL_Rect curr_src, curr_dst; |
1435 | |
1436 | SDL_copyp(&curr_src, &r_src); |
1437 | curr_dst.y = r_dst.y; |
1438 | curr_dst.w = r_src.w; |
1439 | curr_dst.h = r_src.h; |
1440 | for (int y = 0; y < rows; ++y) { |
1441 | curr_dst.x = r_dst.x; |
1442 | for (int x = 0; x < cols; ++x) { |
1443 | if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { |
1444 | return false; |
1445 | } |
1446 | curr_dst.x += curr_dst.w; |
1447 | } |
1448 | if (remaining_w) { |
1449 | curr_src.w = remaining_w; |
1450 | curr_dst.w = remaining_w; |
1451 | if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { |
1452 | return false; |
1453 | } |
1454 | curr_src.w = r_src.w; |
1455 | curr_dst.w = r_src.w; |
1456 | } |
1457 | curr_dst.y += curr_dst.h; |
1458 | } |
1459 | if (remaining_h) { |
1460 | curr_src.h = remaining_h; |
1461 | curr_dst.h = remaining_h; |
1462 | curr_dst.x = r_dst.x; |
1463 | for (int x = 0; x < cols; ++x) { |
1464 | if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { |
1465 | return false; |
1466 | } |
1467 | curr_dst.x += curr_dst.w; |
1468 | } |
1469 | if (remaining_w) { |
1470 | curr_src.w = remaining_w; |
1471 | curr_dst.w = remaining_w; |
1472 | if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) { |
1473 | return false; |
1474 | } |
1475 | } |
1476 | } |
1477 | return true; |
1478 | } |
1479 | |
1480 | bool SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect) |
1481 | { |
1482 | SDL_Rect r_src, r_dst; |
1483 | |
1484 | // Make sure the surfaces aren't locked |
1485 | if (!SDL_SurfaceValid(src)) { |
1486 | return SDL_InvalidParamError("src" ); |
1487 | } else if (!SDL_SurfaceValid(dst)) { |
1488 | return SDL_InvalidParamError("dst" ); |
1489 | } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) { |
1490 | return SDL_SetError("Surfaces must not be locked during blit" ); |
1491 | } |
1492 | |
1493 | if (scale <= 0.0f) { |
1494 | return SDL_InvalidParamError("scale" ); |
1495 | } |
1496 | |
1497 | // Full src surface |
1498 | r_src.x = 0; |
1499 | r_src.y = 0; |
1500 | r_src.w = src->w; |
1501 | r_src.h = src->h; |
1502 | |
1503 | if (dstrect) { |
1504 | r_dst.x = dstrect->x; |
1505 | r_dst.y = dstrect->y; |
1506 | r_dst.w = dstrect->w; |
1507 | r_dst.h = dstrect->h; |
1508 | } else { |
1509 | r_dst.x = 0; |
1510 | r_dst.y = 0; |
1511 | r_dst.w = dst->w; |
1512 | r_dst.h = dst->h; |
1513 | } |
1514 | |
1515 | // clip the source rectangle to the source surface |
1516 | if (srcrect) { |
1517 | if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) { |
1518 | return true; |
1519 | } |
1520 | |
1521 | // For tiling we don't adjust the destination rectangle |
1522 | } |
1523 | |
1524 | // clip the destination rectangle against the clip rectangle |
1525 | { |
1526 | if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) { |
1527 | return true; |
1528 | } |
1529 | |
1530 | // For tiling we don't adjust the source rectangle |
1531 | } |
1532 | |
1533 | // Switch back to a fast blit if we were previously stretching |
1534 | if (src->map.info.flags & SDL_COPY_NEAREST) { |
1535 | src->map.info.flags &= ~SDL_COPY_NEAREST; |
1536 | SDL_InvalidateMap(&src->map); |
1537 | } |
1538 | |
1539 | int tile_width = (int)(r_src.w * scale); |
1540 | int tile_height = (int)(r_src.h * scale); |
1541 | int rows = r_dst.h / tile_height; |
1542 | int cols = r_dst.w / tile_width; |
1543 | int remaining_dst_w = (r_dst.w - cols * tile_width); |
1544 | int remaining_dst_h = (r_dst.h - rows * tile_height); |
1545 | int remaining_src_w = (int)(remaining_dst_w / scale); |
1546 | int remaining_src_h = (int)(remaining_dst_h / scale); |
1547 | SDL_Rect curr_src, curr_dst; |
1548 | |
1549 | SDL_copyp(&curr_src, &r_src); |
1550 | curr_dst.y = r_dst.y; |
1551 | curr_dst.w = tile_width; |
1552 | curr_dst.h = tile_height; |
1553 | for (int y = 0; y < rows; ++y) { |
1554 | curr_dst.x = r_dst.x; |
1555 | for (int x = 0; x < cols; ++x) { |
1556 | if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1557 | return false; |
1558 | } |
1559 | curr_dst.x += curr_dst.w; |
1560 | } |
1561 | if (remaining_dst_w > 0) { |
1562 | curr_src.w = remaining_src_w; |
1563 | curr_dst.w = remaining_dst_w; |
1564 | if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1565 | return false; |
1566 | } |
1567 | curr_src.w = r_src.w; |
1568 | curr_dst.w = tile_width; |
1569 | } |
1570 | curr_dst.y += curr_dst.h; |
1571 | } |
1572 | if (remaining_dst_h > 0) { |
1573 | curr_src.h = remaining_src_h; |
1574 | curr_dst.h = remaining_dst_h; |
1575 | curr_dst.x = r_dst.x; |
1576 | for (int x = 0; x < cols; ++x) { |
1577 | if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1578 | return false; |
1579 | } |
1580 | curr_dst.x += curr_dst.w; |
1581 | } |
1582 | if (remaining_dst_w > 0) { |
1583 | curr_src.w = remaining_src_w; |
1584 | curr_dst.w = remaining_dst_w; |
1585 | if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1586 | return false; |
1587 | } |
1588 | } |
1589 | } |
1590 | return true; |
1591 | } |
1592 | |
1593 | bool SDL_BlitSurface9Grid(SDL_Surface *src, const SDL_Rect *srcrect, int left_width, int right_width, int top_height, int bottom_height, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect) |
1594 | { |
1595 | SDL_Rect full_src, full_dst; |
1596 | SDL_Rect curr_src, curr_dst; |
1597 | int dst_left_width; |
1598 | int dst_right_width; |
1599 | int dst_top_height; |
1600 | int dst_bottom_height; |
1601 | |
1602 | // Make sure the surfaces aren't locked |
1603 | if (!SDL_SurfaceValid(src)) { |
1604 | return SDL_InvalidParamError("src" ); |
1605 | } else if (!SDL_SurfaceValid(dst)) { |
1606 | return SDL_InvalidParamError("dst" ); |
1607 | } |
1608 | |
1609 | if (!srcrect) { |
1610 | full_src.x = 0; |
1611 | full_src.y = 0; |
1612 | full_src.w = src->w; |
1613 | full_src.h = src->h; |
1614 | srcrect = &full_src; |
1615 | } |
1616 | |
1617 | if (!dstrect) { |
1618 | full_dst.x = 0; |
1619 | full_dst.y = 0; |
1620 | full_dst.w = dst->w; |
1621 | full_dst.h = dst->h; |
1622 | dstrect = &full_dst; |
1623 | } |
1624 | |
1625 | if (scale <= 0.0f || scale == 1.0f) { |
1626 | dst_left_width = left_width; |
1627 | dst_right_width = right_width; |
1628 | dst_top_height = top_height; |
1629 | dst_bottom_height = bottom_height; |
1630 | } else { |
1631 | dst_left_width = (int)SDL_roundf(left_width * scale); |
1632 | dst_right_width = (int)SDL_roundf(right_width * scale); |
1633 | dst_top_height = (int)SDL_roundf(top_height * scale); |
1634 | dst_bottom_height = (int)SDL_roundf(bottom_height * scale); |
1635 | } |
1636 | |
1637 | // Upper-left corner |
1638 | curr_src.x = srcrect->x; |
1639 | curr_src.y = srcrect->y; |
1640 | curr_src.w = left_width; |
1641 | curr_src.h = top_height; |
1642 | curr_dst.x = dstrect->x; |
1643 | curr_dst.y = dstrect->y; |
1644 | curr_dst.w = dst_left_width; |
1645 | curr_dst.h = dst_top_height; |
1646 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1647 | return false; |
1648 | } |
1649 | |
1650 | // Upper-right corner |
1651 | curr_src.x = srcrect->x + srcrect->w - right_width; |
1652 | curr_src.w = right_width; |
1653 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
1654 | curr_dst.w = dst_right_width; |
1655 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1656 | return false; |
1657 | } |
1658 | |
1659 | // Lower-right corner |
1660 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
1661 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
1662 | curr_dst.h = dst_bottom_height; |
1663 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1664 | return false; |
1665 | } |
1666 | |
1667 | // Lower-left corner |
1668 | curr_src.x = srcrect->x; |
1669 | curr_src.w = left_width; |
1670 | curr_dst.x = dstrect->x; |
1671 | curr_dst.w = dst_left_width; |
1672 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1673 | return false; |
1674 | } |
1675 | |
1676 | // Left |
1677 | curr_src.y = srcrect->y + top_height; |
1678 | curr_src.h = srcrect->h - top_height - bottom_height; |
1679 | curr_dst.y = dstrect->y + dst_top_height; |
1680 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
1681 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1682 | return false; |
1683 | } |
1684 | |
1685 | // Right |
1686 | curr_src.x = srcrect->x + srcrect->w - right_width; |
1687 | curr_src.w = right_width; |
1688 | curr_dst.x = dstrect->x + dstrect->w - dst_right_width; |
1689 | curr_dst.w = dst_right_width; |
1690 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1691 | return false; |
1692 | } |
1693 | |
1694 | // Top |
1695 | curr_src.x = srcrect->x + left_width; |
1696 | curr_src.y = srcrect->y; |
1697 | curr_src.w = srcrect->w - left_width - right_width; |
1698 | curr_src.h = top_height; |
1699 | curr_dst.x = dstrect->x + dst_left_width; |
1700 | curr_dst.y = dstrect->y; |
1701 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
1702 | curr_dst.h = dst_top_height; |
1703 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1704 | return false; |
1705 | } |
1706 | |
1707 | // Bottom |
1708 | curr_src.y = srcrect->y + srcrect->h - bottom_height; |
1709 | curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height; |
1710 | curr_dst.h = dst_bottom_height; |
1711 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1712 | return false; |
1713 | } |
1714 | |
1715 | // Center |
1716 | curr_src.x = srcrect->x + left_width; |
1717 | curr_src.y = srcrect->y + top_height; |
1718 | curr_src.w = srcrect->w - left_width - right_width; |
1719 | curr_src.h = srcrect->h - top_height - bottom_height; |
1720 | curr_dst.x = dstrect->x + dst_left_width; |
1721 | curr_dst.y = dstrect->y + dst_top_height; |
1722 | curr_dst.w = dstrect->w - dst_left_width - dst_right_width; |
1723 | curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height; |
1724 | if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) { |
1725 | return false; |
1726 | } |
1727 | |
1728 | return true; |
1729 | } |
1730 | |
1731 | /* |
1732 | * Lock a surface to directly access the pixels |
1733 | */ |
1734 | bool SDL_LockSurface(SDL_Surface *surface) |
1735 | { |
1736 | if (!SDL_SurfaceValid(surface)) { |
1737 | return SDL_InvalidParamError("surface" ); |
1738 | } |
1739 | |
1740 | if (!surface->locked) { |
1741 | #ifdef SDL_HAVE_RLE |
1742 | // Perform the lock |
1743 | if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { |
1744 | SDL_UnRLESurface(surface, true); |
1745 | surface->internal_flags |= SDL_INTERNAL_SURFACE_RLEACCEL; // save accel'd state |
1746 | } |
1747 | #endif |
1748 | } |
1749 | |
1750 | // Increment the surface lock count, for recursive locks |
1751 | ++surface->locked; |
1752 | surface->flags |= SDL_SURFACE_LOCKED; |
1753 | |
1754 | // Ready to go.. |
1755 | return true; |
1756 | } |
1757 | |
1758 | /* |
1759 | * Unlock a previously locked surface |
1760 | */ |
1761 | void SDL_UnlockSurface(SDL_Surface *surface) |
1762 | { |
1763 | if (!SDL_SurfaceValid(surface)) { |
1764 | return; |
1765 | } |
1766 | |
1767 | // Only perform an unlock if we are locked |
1768 | if (!surface->locked || (--surface->locked > 0)) { |
1769 | return; |
1770 | } |
1771 | |
1772 | #ifdef SDL_HAVE_RLE |
1773 | // Update RLE encoded surface with new data |
1774 | if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { |
1775 | surface->internal_flags &= ~SDL_INTERNAL_SURFACE_RLEACCEL; // stop lying |
1776 | SDL_RLESurface(surface); |
1777 | } |
1778 | #endif |
1779 | |
1780 | surface->flags &= ~SDL_SURFACE_LOCKED; |
1781 | } |
1782 | |
1783 | static bool SDL_FlipSurfaceHorizontal(SDL_Surface *surface) |
1784 | { |
1785 | bool isstack; |
1786 | Uint8 *row, *a, *b, *tmp; |
1787 | int i, j, bpp; |
1788 | |
1789 | if (SDL_BITSPERPIXEL(surface->format) < 8) { |
1790 | // We could implement this if needed, but we'd have to flip sets of bits within a byte |
1791 | return SDL_Unsupported(); |
1792 | } |
1793 | |
1794 | if (surface->h <= 0) { |
1795 | return true; |
1796 | } |
1797 | |
1798 | if (surface->w <= 1) { |
1799 | return true; |
1800 | } |
1801 | |
1802 | bpp = SDL_BYTESPERPIXEL(surface->format); |
1803 | row = (Uint8 *)surface->pixels; |
1804 | tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); |
1805 | if (!tmp) { |
1806 | return false; |
1807 | } |
1808 | for (i = surface->h; i--; ) { |
1809 | a = row; |
1810 | b = a + (surface->w - 1) * bpp; |
1811 | for (j = surface->w / 2; j--; ) { |
1812 | SDL_memcpy(tmp, a, bpp); |
1813 | SDL_memcpy(a, b, bpp); |
1814 | SDL_memcpy(b, tmp, bpp); |
1815 | a += bpp; |
1816 | b -= bpp; |
1817 | } |
1818 | row += surface->pitch; |
1819 | } |
1820 | SDL_small_free(tmp, isstack); |
1821 | return true; |
1822 | } |
1823 | |
1824 | static bool SDL_FlipSurfaceVertical(SDL_Surface *surface) |
1825 | { |
1826 | bool isstack; |
1827 | Uint8 *a, *b, *tmp; |
1828 | int i; |
1829 | |
1830 | if (surface->h <= 1) { |
1831 | return true; |
1832 | } |
1833 | |
1834 | a = (Uint8 *)surface->pixels; |
1835 | b = a + (surface->h - 1) * surface->pitch; |
1836 | tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack); |
1837 | if (!tmp) { |
1838 | return false; |
1839 | } |
1840 | for (i = surface->h / 2; i--; ) { |
1841 | SDL_memcpy(tmp, a, surface->pitch); |
1842 | SDL_memcpy(a, b, surface->pitch); |
1843 | SDL_memcpy(b, tmp, surface->pitch); |
1844 | a += surface->pitch; |
1845 | b -= surface->pitch; |
1846 | } |
1847 | SDL_small_free(tmp, isstack); |
1848 | return true; |
1849 | } |
1850 | |
1851 | bool SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) |
1852 | { |
1853 | if (!SDL_SurfaceValid(surface)) { |
1854 | return SDL_InvalidParamError("surface" ); |
1855 | } |
1856 | if (!surface->pixels) { |
1857 | return true; |
1858 | } |
1859 | |
1860 | switch (flip) { |
1861 | case SDL_FLIP_HORIZONTAL: |
1862 | return SDL_FlipSurfaceHorizontal(surface); |
1863 | case SDL_FLIP_VERTICAL: |
1864 | return SDL_FlipSurfaceVertical(surface); |
1865 | default: |
1866 | return SDL_InvalidParamError("flip" ); |
1867 | } |
1868 | } |
1869 | |
1870 | SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props) |
1871 | { |
1872 | SDL_Palette *temp_palette = NULL; |
1873 | SDL_Surface *convert = NULL; |
1874 | SDL_Colorspace src_colorspace; |
1875 | SDL_PropertiesID src_properties; |
1876 | Uint32 copy_flags; |
1877 | SDL_Color copy_color; |
1878 | SDL_Rect bounds; |
1879 | bool result; |
1880 | bool palette_ck_transform = false; |
1881 | Uint8 palette_ck_value = 0; |
1882 | Uint8 *palette_saved_alpha = NULL; |
1883 | int palette_saved_alpha_ncolors = 0; |
1884 | |
1885 | if (!SDL_SurfaceValid(surface)) { |
1886 | SDL_InvalidParamError("surface" ); |
1887 | goto error; |
1888 | } |
1889 | |
1890 | if (format == SDL_PIXELFORMAT_UNKNOWN) { |
1891 | SDL_InvalidParamError("format" ); |
1892 | goto error; |
1893 | } |
1894 | |
1895 | // Check for empty destination palette! (results in empty image) |
1896 | if (palette) { |
1897 | int i; |
1898 | for (i = 0; i < palette->ncolors; ++i) { |
1899 | if ((palette->colors[i].r != 0xFF) || (palette->colors[i].g != 0xFF) || (palette->colors[i].b != 0xFF)) { |
1900 | break; |
1901 | } |
1902 | } |
1903 | if (i == palette->ncolors) { |
1904 | SDL_SetError("Empty destination palette" ); |
1905 | goto error; |
1906 | } |
1907 | } else if (SDL_ISPIXELFORMAT_INDEXED(format)) { |
1908 | // Create a dither palette for conversion |
1909 | temp_palette = SDL_CreatePalette(1 << SDL_BITSPERPIXEL(format)); |
1910 | if (temp_palette) { |
1911 | SDL_DitherPalette(temp_palette); |
1912 | palette = temp_palette; |
1913 | } |
1914 | } |
1915 | |
1916 | src_colorspace = surface->colorspace; |
1917 | src_properties = surface->props; |
1918 | |
1919 | // Create a new surface with the desired format |
1920 | convert = SDL_CreateSurface(surface->w, surface->h, format); |
1921 | if (!convert) { |
1922 | goto error; |
1923 | } |
1924 | if (SDL_ISPIXELFORMAT_INDEXED(format)) { |
1925 | SDL_SetSurfacePalette(convert, palette); |
1926 | } |
1927 | |
1928 | if (colorspace == SDL_COLORSPACE_UNKNOWN) { |
1929 | colorspace = src_colorspace; |
1930 | } |
1931 | SDL_SetSurfaceColorspace(convert, colorspace); |
1932 | |
1933 | if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
1934 | if (surface->format == SDL_PIXELFORMAT_MJPG && format == SDL_PIXELFORMAT_MJPG) { |
1935 | // Just do a straight pixel copy of the JPEG image |
1936 | size_t size = (size_t)surface->pitch; |
1937 | convert->pixels = SDL_malloc(size); |
1938 | if (!convert->pixels) { |
1939 | goto error; |
1940 | } |
1941 | convert->pitch = surface->pitch; |
1942 | SDL_memcpy(convert->pixels, surface->pixels, size); |
1943 | |
1944 | } else if (!SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format, colorspace, props, convert->pixels, convert->pitch)) { |
1945 | goto error; |
1946 | } |
1947 | |
1948 | // Save the original copy flags |
1949 | copy_flags = surface->map.info.flags; |
1950 | |
1951 | goto end; |
1952 | } |
1953 | |
1954 | // Save the original copy flags |
1955 | copy_flags = surface->map.info.flags; |
1956 | copy_color.r = surface->map.info.r; |
1957 | copy_color.g = surface->map.info.g; |
1958 | copy_color.b = surface->map.info.b; |
1959 | copy_color.a = surface->map.info.a; |
1960 | surface->map.info.r = 0xFF; |
1961 | surface->map.info.g = 0xFF; |
1962 | surface->map.info.b = 0xFF; |
1963 | surface->map.info.a = 0xFF; |
1964 | surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); |
1965 | SDL_InvalidateMap(&surface->map); |
1966 | |
1967 | // Copy over the image data |
1968 | bounds.x = 0; |
1969 | bounds.y = 0; |
1970 | bounds.w = surface->w; |
1971 | bounds.h = surface->h; |
1972 | |
1973 | /* Source surface has a palette with no real alpha (0 or OPAQUE). |
1974 | * Destination format has alpha. |
1975 | * -> set alpha channel to be opaque */ |
1976 | if (surface->palette && SDL_ISPIXELFORMAT_ALPHA(format)) { |
1977 | bool set_opaque = false; |
1978 | |
1979 | bool is_opaque, has_alpha_channel; |
1980 | SDL_DetectPalette(surface->palette, &is_opaque, &has_alpha_channel); |
1981 | |
1982 | if (is_opaque) { |
1983 | if (!has_alpha_channel) { |
1984 | set_opaque = true; |
1985 | } |
1986 | } |
1987 | |
1988 | // Set opaque and backup palette alpha values |
1989 | if (set_opaque) { |
1990 | int i; |
1991 | palette_saved_alpha_ncolors = surface->palette->ncolors; |
1992 | if (palette_saved_alpha_ncolors > 0) { |
1993 | palette_saved_alpha = SDL_stack_alloc(Uint8, palette_saved_alpha_ncolors); |
1994 | for (i = 0; i < palette_saved_alpha_ncolors; i++) { |
1995 | palette_saved_alpha[i] = surface->palette->colors[i].a; |
1996 | surface->palette->colors[i].a = SDL_ALPHA_OPAQUE; |
1997 | } |
1998 | } |
1999 | } |
2000 | } |
2001 | |
2002 | // Transform colorkey to alpha. for cases where source palette has duplicate values, and colorkey is one of them |
2003 | if (copy_flags & SDL_COPY_COLORKEY) { |
2004 | if (surface->palette && !palette) { |
2005 | palette_ck_transform = true; |
2006 | palette_ck_value = surface->palette->colors[surface->map.info.colorkey].a; |
2007 | surface->palette->colors[surface->map.info.colorkey].a = SDL_ALPHA_TRANSPARENT; |
2008 | } |
2009 | } |
2010 | |
2011 | result = SDL_BlitSurfaceUnchecked(surface, &bounds, convert, &bounds); |
2012 | |
2013 | // Restore colorkey alpha value |
2014 | if (palette_ck_transform) { |
2015 | surface->palette->colors[surface->map.info.colorkey].a = palette_ck_value; |
2016 | } |
2017 | |
2018 | // Restore palette alpha values |
2019 | if (palette_saved_alpha) { |
2020 | int i; |
2021 | for (i = 0; i < palette_saved_alpha_ncolors; i++) { |
2022 | surface->palette->colors[i].a = palette_saved_alpha[i]; |
2023 | } |
2024 | SDL_stack_free(palette_saved_alpha); |
2025 | } |
2026 | |
2027 | // Clean up the original surface, and update converted surface |
2028 | convert->map.info.r = copy_color.r; |
2029 | convert->map.info.g = copy_color.g; |
2030 | convert->map.info.b = copy_color.b; |
2031 | convert->map.info.a = copy_color.a; |
2032 | convert->map.info.flags = |
2033 | (copy_flags & |
2034 | ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY | |
2035 | SDL_COPY_RLE_ALPHAKEY)); |
2036 | surface->map.info.r = copy_color.r; |
2037 | surface->map.info.g = copy_color.g; |
2038 | surface->map.info.b = copy_color.b; |
2039 | surface->map.info.a = copy_color.a; |
2040 | surface->map.info.flags = copy_flags; |
2041 | SDL_InvalidateMap(&surface->map); |
2042 | |
2043 | // SDL_BlitSurfaceUnchecked failed, and so the conversion |
2044 | if (!result) { |
2045 | goto error; |
2046 | } |
2047 | |
2048 | if (copy_flags & SDL_COPY_COLORKEY) { |
2049 | bool set_colorkey_by_color = false; |
2050 | bool convert_colorkey = true; |
2051 | |
2052 | if (surface->palette) { |
2053 | if (palette && |
2054 | surface->palette->ncolors <= palette->ncolors && |
2055 | (SDL_memcmp(surface->palette->colors, palette->colors, |
2056 | surface->palette->ncolors * sizeof(SDL_Color)) == 0)) { |
2057 | // The palette is identical, just set the same colorkey |
2058 | SDL_SetSurfaceColorKey(convert, true, surface->map.info.colorkey); |
2059 | } else if (!palette) { |
2060 | if (SDL_ISPIXELFORMAT_ALPHA(format)) { |
2061 | // No need to add the colorkey, transparency is in the alpha channel |
2062 | } else { |
2063 | // Only set the colorkey information |
2064 | set_colorkey_by_color = true; |
2065 | convert_colorkey = false; |
2066 | } |
2067 | } else { |
2068 | set_colorkey_by_color = true; |
2069 | } |
2070 | } else { |
2071 | set_colorkey_by_color = true; |
2072 | } |
2073 | |
2074 | if (set_colorkey_by_color) { |
2075 | SDL_Surface *tmp; |
2076 | SDL_Surface *tmp2; |
2077 | int converted_colorkey = 0; |
2078 | |
2079 | // Create a dummy surface to get the colorkey converted |
2080 | tmp = SDL_CreateSurface(1, 1, surface->format); |
2081 | if (!tmp) { |
2082 | goto error; |
2083 | } |
2084 | |
2085 | // Share the palette, if any |
2086 | if (surface->palette) { |
2087 | SDL_SetSurfacePalette(tmp, surface->palette); |
2088 | } |
2089 | |
2090 | SDL_FillSurfaceRect(tmp, NULL, surface->map.info.colorkey); |
2091 | |
2092 | tmp->map.info.flags &= ~SDL_COPY_COLORKEY; |
2093 | |
2094 | // Conversion of the colorkey |
2095 | tmp2 = SDL_ConvertSurfaceAndColorspace(tmp, format, palette, colorspace, props); |
2096 | if (!tmp2) { |
2097 | SDL_DestroySurface(tmp); |
2098 | goto error; |
2099 | } |
2100 | |
2101 | // Get the converted colorkey |
2102 | SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->fmt->bytes_per_pixel); |
2103 | |
2104 | SDL_DestroySurface(tmp); |
2105 | SDL_DestroySurface(tmp2); |
2106 | |
2107 | // Set the converted colorkey on the new surface |
2108 | SDL_SetSurfaceColorKey(convert, true, converted_colorkey); |
2109 | |
2110 | // This is needed when converting for 3D texture upload |
2111 | if (convert_colorkey) { |
2112 | SDL_ConvertColorkeyToAlpha(convert, true); |
2113 | } |
2114 | } |
2115 | } |
2116 | |
2117 | end: |
2118 | if (temp_palette) { |
2119 | SDL_DestroyPalette(temp_palette); |
2120 | } |
2121 | |
2122 | SDL_SetSurfaceClipRect(convert, &surface->clip_rect); |
2123 | |
2124 | /* Enable alpha blending by default if the new surface has an |
2125 | * alpha channel or alpha modulation */ |
2126 | if (SDL_ISPIXELFORMAT_ALPHA(format) || |
2127 | (copy_flags & SDL_COPY_MODULATE_ALPHA)) { |
2128 | SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND); |
2129 | } |
2130 | if (copy_flags & SDL_COPY_RLE_DESIRED) { |
2131 | SDL_SetSurfaceRLE(convert, true); |
2132 | } |
2133 | |
2134 | // Copy alternate images |
2135 | for (int i = 0; i < surface->num_images; ++i) { |
2136 | if (!SDL_AddSurfaceAlternateImage(convert, surface->images[i])) { |
2137 | goto error; |
2138 | } |
2139 | } |
2140 | |
2141 | // We're ready to go! |
2142 | return convert; |
2143 | |
2144 | error: |
2145 | if (temp_palette) { |
2146 | SDL_DestroyPalette(temp_palette); |
2147 | } |
2148 | if (convert) { |
2149 | SDL_DestroySurface(convert); |
2150 | } |
2151 | return NULL; |
2152 | } |
2153 | |
2154 | SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) |
2155 | { |
2156 | if (!SDL_SurfaceValid(surface)) { |
2157 | SDL_InvalidParamError("surface" ); |
2158 | return NULL; |
2159 | } |
2160 | |
2161 | return SDL_ConvertSurfaceAndColorspace(surface, surface->format, surface->palette, surface->colorspace, surface->props); |
2162 | } |
2163 | |
2164 | SDL_Surface *SDL_ScaleSurface(SDL_Surface *surface, int width, int height, SDL_ScaleMode scaleMode) |
2165 | { |
2166 | SDL_Surface *convert = NULL; |
2167 | Uint32 copy_flags; |
2168 | SDL_Color copy_color; |
2169 | bool rc; |
2170 | |
2171 | if (!SDL_SurfaceValid(surface)) { |
2172 | SDL_InvalidParamError("surface" ); |
2173 | goto error; |
2174 | } |
2175 | |
2176 | if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2177 | // We can't directly scale a YUV surface (yet!) |
2178 | SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888); |
2179 | if (!tmp) { |
2180 | return NULL; |
2181 | } |
2182 | |
2183 | SDL_Surface *scaled = SDL_ScaleSurface(tmp, width, height, scaleMode); |
2184 | SDL_DestroySurface(tmp); |
2185 | if (!scaled) { |
2186 | return NULL; |
2187 | } |
2188 | tmp = scaled; |
2189 | |
2190 | SDL_Surface *result = SDL_ConvertSurfaceAndColorspace(tmp, surface->format, NULL, surface->colorspace, surface->props); |
2191 | SDL_DestroySurface(tmp); |
2192 | return result; |
2193 | } |
2194 | |
2195 | // Create a new surface with the desired size |
2196 | convert = SDL_CreateSurface(width, height, surface->format); |
2197 | if (!convert) { |
2198 | goto error; |
2199 | } |
2200 | SDL_SetSurfacePalette(convert, surface->palette); |
2201 | SDL_SetSurfaceColorspace(convert, surface->colorspace); |
2202 | |
2203 | // Save the original copy flags |
2204 | copy_flags = surface->map.info.flags; |
2205 | copy_color.r = surface->map.info.r; |
2206 | copy_color.g = surface->map.info.g; |
2207 | copy_color.b = surface->map.info.b; |
2208 | copy_color.a = surface->map.info.a; |
2209 | surface->map.info.r = 0xFF; |
2210 | surface->map.info.g = 0xFF; |
2211 | surface->map.info.b = 0xFF; |
2212 | surface->map.info.a = 0xFF; |
2213 | surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); |
2214 | SDL_InvalidateMap(&surface->map); |
2215 | |
2216 | rc = SDL_BlitSurfaceScaled(surface, NULL, convert, NULL, scaleMode); |
2217 | |
2218 | // Clean up the original surface, and update converted surface |
2219 | convert->map.info.r = copy_color.r; |
2220 | convert->map.info.g = copy_color.g; |
2221 | convert->map.info.b = copy_color.b; |
2222 | convert->map.info.a = copy_color.a; |
2223 | convert->map.info.flags = (copy_flags & ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY)); |
2224 | surface->map.info.r = copy_color.r; |
2225 | surface->map.info.g = copy_color.g; |
2226 | surface->map.info.b = copy_color.b; |
2227 | surface->map.info.a = copy_color.a; |
2228 | surface->map.info.flags = copy_flags; |
2229 | SDL_InvalidateMap(&surface->map); |
2230 | |
2231 | // SDL_BlitSurfaceScaled failed, and so the conversion |
2232 | if (!rc) { |
2233 | goto error; |
2234 | } |
2235 | |
2236 | // We're ready to go! |
2237 | return convert; |
2238 | |
2239 | error: |
2240 | if (convert) { |
2241 | SDL_DestroySurface(convert); |
2242 | } |
2243 | return NULL; |
2244 | } |
2245 | |
2246 | SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, SDL_PixelFormat format) |
2247 | { |
2248 | if (!SDL_SurfaceValid(surface)) { |
2249 | SDL_InvalidParamError("surface" ); |
2250 | return NULL; |
2251 | } |
2252 | |
2253 | return SDL_ConvertSurfaceAndColorspace(surface, format, NULL, SDL_GetDefaultColorspaceForFormat(format), surface->props); |
2254 | } |
2255 | |
2256 | SDL_Surface *SDL_DuplicatePixels(int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, void *pixels, int pitch) |
2257 | { |
2258 | SDL_Surface *surface = SDL_CreateSurface(width, height, format); |
2259 | if (surface) { |
2260 | int length = width * SDL_BYTESPERPIXEL(format); |
2261 | Uint8 *src = (Uint8 *)pixels; |
2262 | Uint8 *dst = (Uint8 *)surface->pixels; |
2263 | int rows = height; |
2264 | while (rows--) { |
2265 | SDL_memcpy(dst, src, length); |
2266 | dst += surface->pitch; |
2267 | src += pitch; |
2268 | } |
2269 | |
2270 | SDL_SetSurfaceColorspace(surface, colorspace); |
2271 | } |
2272 | return surface; |
2273 | } |
2274 | |
2275 | bool SDL_ConvertPixelsAndColorspace(int width, int height, |
2276 | SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, |
2277 | SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) |
2278 | { |
2279 | SDL_Surface src_surface; |
2280 | SDL_Surface dst_surface; |
2281 | SDL_Rect rect; |
2282 | void *nonconst_src = (void *)src; |
2283 | bool result; |
2284 | |
2285 | if (!src) { |
2286 | return SDL_InvalidParamError("src" ); |
2287 | } |
2288 | if (!src_pitch) { |
2289 | return SDL_InvalidParamError("src_pitch" ); |
2290 | } |
2291 | if (!dst) { |
2292 | return SDL_InvalidParamError("dst" ); |
2293 | } |
2294 | if (!dst_pitch) { |
2295 | return SDL_InvalidParamError("dst_pitch" ); |
2296 | } |
2297 | |
2298 | if (src_colorspace == SDL_COLORSPACE_UNKNOWN) { |
2299 | src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); |
2300 | } |
2301 | if (dst_colorspace == SDL_COLORSPACE_UNKNOWN) { |
2302 | dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); |
2303 | } |
2304 | |
2305 | if (src_format == SDL_PIXELFORMAT_MJPG) { |
2306 | return SDL_ConvertPixels_STB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); |
2307 | } |
2308 | |
2309 | #ifdef SDL_HAVE_YUV |
2310 | if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { |
2311 | return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); |
2312 | } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { |
2313 | return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); |
2314 | } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { |
2315 | return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); |
2316 | } |
2317 | #else |
2318 | if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) { |
2319 | return SDL_SetError("SDL not built with YUV support" ); |
2320 | } |
2321 | #endif |
2322 | |
2323 | // Fast path for same format copy |
2324 | if (src_format == dst_format && src_colorspace == dst_colorspace) { |
2325 | if (src_pitch == dst_pitch) { |
2326 | SDL_memcpy(dst, src, height * src_pitch); |
2327 | } else { |
2328 | int i; |
2329 | const int bpp = SDL_BYTESPERPIXEL(src_format); |
2330 | width *= bpp; |
2331 | for (i = height; i--;) { |
2332 | SDL_memcpy(dst, src, width); |
2333 | src = (const Uint8 *)src + src_pitch; |
2334 | dst = (Uint8 *)dst + dst_pitch; |
2335 | } |
2336 | } |
2337 | return true; |
2338 | } |
2339 | |
2340 | if (!SDL_InitializeSurface(&src_surface, width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, true)) { |
2341 | return false; |
2342 | } |
2343 | SDL_SetSurfaceBlendMode(&src_surface, SDL_BLENDMODE_NONE); |
2344 | |
2345 | if (!SDL_InitializeSurface(&dst_surface, width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, true)) { |
2346 | return false; |
2347 | } |
2348 | |
2349 | // Set up the rect and go! |
2350 | rect.x = 0; |
2351 | rect.y = 0; |
2352 | rect.w = width; |
2353 | rect.h = height; |
2354 | result = SDL_BlitSurfaceUnchecked(&src_surface, &rect, &dst_surface, &rect); |
2355 | |
2356 | SDL_DestroySurface(&src_surface); |
2357 | SDL_DestroySurface(&dst_surface); |
2358 | |
2359 | return result; |
2360 | } |
2361 | |
2362 | bool SDL_ConvertPixels(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch) |
2363 | { |
2364 | return SDL_ConvertPixelsAndColorspace(width, height, |
2365 | src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch, |
2366 | dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch); |
2367 | } |
2368 | |
2369 | /* |
2370 | * Premultiply the alpha on a block of pixels |
2371 | * |
2372 | * Here are some ideas for optimization: |
2373 | * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha |
2374 | * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data |
2375 | */ |
2376 | |
2377 | static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) |
2378 | { |
2379 | int c; |
2380 | Uint32 srcpixel; |
2381 | Uint32 srcR, srcG, srcB, srcA; |
2382 | Uint32 dstpixel; |
2383 | Uint32 dstR, dstG, dstB, dstA; |
2384 | |
2385 | while (height--) { |
2386 | const Uint32 *src_px = (const Uint32 *)src; |
2387 | Uint32 *dst_px = (Uint32 *)dst; |
2388 | for (c = width; c; --c) { |
2389 | // Component bytes extraction. |
2390 | srcpixel = *src_px++; |
2391 | RGBA_FROM_ARGB8888(srcpixel, srcR, srcG, srcB, srcA); |
2392 | |
2393 | // Alpha pre-multiplication of each component. |
2394 | dstA = srcA; |
2395 | dstR = (srcA * srcR) / 255; |
2396 | dstG = (srcA * srcG) / 255; |
2397 | dstB = (srcA * srcB) / 255; |
2398 | |
2399 | // ARGB8888 pixel recomposition. |
2400 | ARGB8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); |
2401 | *dst_px++ = dstpixel; |
2402 | } |
2403 | src = (const Uint8 *)src + src_pitch; |
2404 | dst = (Uint8 *)dst + dst_pitch; |
2405 | } |
2406 | } |
2407 | |
2408 | static void SDL_PremultiplyAlpha_XYZA8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) |
2409 | { |
2410 | int c; |
2411 | Uint32 srcpixel; |
2412 | Uint32 srcR, srcG, srcB, srcA; |
2413 | Uint32 dstpixel; |
2414 | Uint32 dstR, dstG, dstB, dstA; |
2415 | |
2416 | while (height--) { |
2417 | const Uint32 *src_px = (const Uint32 *)src; |
2418 | Uint32 *dst_px = (Uint32 *)dst; |
2419 | for (c = width; c; --c) { |
2420 | // Component bytes extraction. |
2421 | srcpixel = *src_px++; |
2422 | RGBA_FROM_RGBA8888(srcpixel, srcR, srcG, srcB, srcA); |
2423 | |
2424 | // Alpha pre-multiplication of each component. |
2425 | dstA = srcA; |
2426 | dstR = (srcA * srcR) / 255; |
2427 | dstG = (srcA * srcG) / 255; |
2428 | dstB = (srcA * srcB) / 255; |
2429 | |
2430 | // RGBA8888 pixel recomposition. |
2431 | RGBA8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA); |
2432 | *dst_px++ = dstpixel; |
2433 | } |
2434 | src = (const Uint8 *)src + src_pitch; |
2435 | dst = (Uint8 *)dst + dst_pitch; |
2436 | } |
2437 | } |
2438 | |
2439 | static void SDL_PremultiplyAlpha_AXYZ128(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) |
2440 | { |
2441 | int c; |
2442 | float flR, flG, flB, flA; |
2443 | |
2444 | while (height--) { |
2445 | const float *src_px = (const float *)src; |
2446 | float *dst_px = (float *)dst; |
2447 | for (c = width; c; --c) { |
2448 | flA = *src_px++; |
2449 | flR = *src_px++; |
2450 | flG = *src_px++; |
2451 | flB = *src_px++; |
2452 | |
2453 | // Alpha pre-multiplication of each component. |
2454 | flR *= flA; |
2455 | flG *= flA; |
2456 | flB *= flA; |
2457 | |
2458 | *dst_px++ = flA; |
2459 | *dst_px++ = flR; |
2460 | *dst_px++ = flG; |
2461 | *dst_px++ = flB; |
2462 | } |
2463 | src = (const Uint8 *)src + src_pitch; |
2464 | dst = (Uint8 *)dst + dst_pitch; |
2465 | } |
2466 | } |
2467 | |
2468 | static bool SDL_PremultiplyAlphaPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch, bool linear) |
2469 | { |
2470 | SDL_Surface *convert = NULL; |
2471 | void *final_dst = dst; |
2472 | int final_dst_pitch = dst_pitch; |
2473 | SDL_PixelFormat format; |
2474 | SDL_Colorspace colorspace; |
2475 | bool result = false; |
2476 | |
2477 | if (!src) { |
2478 | return SDL_InvalidParamError("src" ); |
2479 | } |
2480 | if (!src_pitch) { |
2481 | return SDL_InvalidParamError("src_pitch" ); |
2482 | } |
2483 | if (!dst) { |
2484 | return SDL_InvalidParamError("dst" ); |
2485 | } |
2486 | if (!dst_pitch) { |
2487 | return SDL_InvalidParamError("dst_pitch" ); |
2488 | } |
2489 | |
2490 | // Use a high precision format if we're converting to linear colorspace or using high precision pixel formats |
2491 | if (linear || |
2492 | SDL_ISPIXELFORMAT_10BIT(src_format) || SDL_BITSPERPIXEL(src_format) > 32 || |
2493 | SDL_ISPIXELFORMAT_10BIT(dst_format) || SDL_BITSPERPIXEL(dst_format) > 32) { |
2494 | if (src_format == SDL_PIXELFORMAT_ARGB128_FLOAT || |
2495 | src_format == SDL_PIXELFORMAT_ABGR128_FLOAT) { |
2496 | format = src_format; |
2497 | } else { |
2498 | format = SDL_PIXELFORMAT_ARGB128_FLOAT; |
2499 | } |
2500 | } else { |
2501 | if (src_format == SDL_PIXELFORMAT_ARGB8888 || |
2502 | src_format == SDL_PIXELFORMAT_ABGR8888 || |
2503 | src_format == SDL_PIXELFORMAT_RGBA8888 || |
2504 | src_format == SDL_PIXELFORMAT_BGRA8888) { |
2505 | format = src_format; |
2506 | } else { |
2507 | format = SDL_PIXELFORMAT_ARGB8888; |
2508 | } |
2509 | } |
2510 | if (linear) { |
2511 | colorspace = SDL_COLORSPACE_SRGB_LINEAR; |
2512 | } else { |
2513 | colorspace = SDL_COLORSPACE_SRGB; |
2514 | } |
2515 | |
2516 | if (src_format != format || src_colorspace != colorspace) { |
2517 | convert = SDL_CreateSurface(width, height, format); |
2518 | if (!convert) { |
2519 | goto done; |
2520 | } |
2521 | if (!SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, format, colorspace, 0, convert->pixels, convert->pitch)) { |
2522 | goto done; |
2523 | } |
2524 | |
2525 | src = convert->pixels; |
2526 | src_pitch = convert->pitch; |
2527 | dst = convert->pixels; |
2528 | dst_pitch = convert->pitch; |
2529 | |
2530 | } else if (dst_format != format || dst_colorspace != colorspace) { |
2531 | convert = SDL_CreateSurface(width, height, format); |
2532 | if (!convert) { |
2533 | goto done; |
2534 | } |
2535 | dst = convert->pixels; |
2536 | dst_pitch = convert->pitch; |
2537 | } |
2538 | |
2539 | switch (format) { |
2540 | case SDL_PIXELFORMAT_ARGB8888: |
2541 | case SDL_PIXELFORMAT_ABGR8888: |
2542 | SDL_PremultiplyAlpha_AXYZ8888(width, height, src, src_pitch, dst, dst_pitch); |
2543 | break; |
2544 | case SDL_PIXELFORMAT_RGBA8888: |
2545 | case SDL_PIXELFORMAT_BGRA8888: |
2546 | SDL_PremultiplyAlpha_XYZA8888(width, height, src, src_pitch, dst, dst_pitch); |
2547 | break; |
2548 | case SDL_PIXELFORMAT_ARGB128_FLOAT: |
2549 | case SDL_PIXELFORMAT_ABGR128_FLOAT: |
2550 | SDL_PremultiplyAlpha_AXYZ128(width, height, src, src_pitch, dst, dst_pitch); |
2551 | break; |
2552 | default: |
2553 | SDL_SetError("Unexpected internal pixel format" ); |
2554 | goto done; |
2555 | } |
2556 | |
2557 | if (dst != final_dst) { |
2558 | if (!SDL_ConvertPixelsAndColorspace(width, height, format, colorspace, 0, convert->pixels, convert->pitch, dst_format, dst_colorspace, dst_properties, final_dst, final_dst_pitch)) { |
2559 | goto done; |
2560 | } |
2561 | } |
2562 | result = true; |
2563 | |
2564 | done: |
2565 | if (convert) { |
2566 | SDL_DestroySurface(convert); |
2567 | } |
2568 | return result; |
2569 | } |
2570 | |
2571 | bool SDL_PremultiplyAlpha(int width, int height, |
2572 | SDL_PixelFormat src_format, const void *src, int src_pitch, |
2573 | SDL_PixelFormat dst_format, void *dst, int dst_pitch, bool linear) |
2574 | { |
2575 | SDL_Colorspace src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format); |
2576 | SDL_Colorspace dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format); |
2577 | |
2578 | return SDL_PremultiplyAlphaPixelsAndColorspace(width, height, src_format, src_colorspace, 0, src, src_pitch, dst_format, dst_colorspace, 0, dst, dst_pitch, linear); |
2579 | } |
2580 | |
2581 | bool SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, bool linear) |
2582 | { |
2583 | SDL_Colorspace colorspace; |
2584 | |
2585 | if (!SDL_SurfaceValid(surface)) { |
2586 | return SDL_InvalidParamError("surface" ); |
2587 | } |
2588 | |
2589 | colorspace = surface->colorspace; |
2590 | |
2591 | return SDL_PremultiplyAlphaPixelsAndColorspace(surface->w, surface->h, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, linear); |
2592 | } |
2593 | |
2594 | bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a) |
2595 | { |
2596 | SDL_Rect clip_rect; |
2597 | bool result = false; |
2598 | |
2599 | if (!SDL_SurfaceValid(surface)) { |
2600 | return SDL_InvalidParamError("surface" ); |
2601 | } |
2602 | |
2603 | SDL_GetSurfaceClipRect(surface, &clip_rect); |
2604 | SDL_SetSurfaceClipRect(surface, NULL); |
2605 | |
2606 | if (!SDL_ISPIXELFORMAT_FOURCC(surface->format) && |
2607 | SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32)) { |
2608 | Uint32 color; |
2609 | |
2610 | color = SDL_MapSurfaceRGBA(surface, |
2611 | (Uint8)SDL_roundf(SDL_clamp(r, 0.0f, 1.0f) * 255.0f), |
2612 | (Uint8)SDL_roundf(SDL_clamp(g, 0.0f, 1.0f) * 255.0f), |
2613 | (Uint8)SDL_roundf(SDL_clamp(b, 0.0f, 1.0f) * 255.0f), |
2614 | (Uint8)SDL_roundf(SDL_clamp(a, 0.0f, 1.0f) * 255.0f)); |
2615 | result = SDL_FillSurfaceRect(surface, NULL, color); |
2616 | } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2617 | // We can't directly set an RGB value on a YUV surface |
2618 | SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888); |
2619 | if (!tmp) { |
2620 | goto done; |
2621 | } |
2622 | |
2623 | if (SDL_ClearSurface(tmp, r, g, b, a)) { |
2624 | result = SDL_ConvertPixelsAndColorspace(surface->w, surface->h, tmp->format, tmp->colorspace, tmp->props, tmp->pixels, tmp->pitch, surface->format, surface->colorspace, surface->props, surface->pixels, surface->pitch); |
2625 | } |
2626 | SDL_DestroySurface(tmp); |
2627 | } else { |
2628 | // Take advantage of blit color conversion |
2629 | SDL_Surface *tmp = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT); |
2630 | if (!tmp) { |
2631 | goto done; |
2632 | } |
2633 | SDL_SetSurfaceColorspace(tmp, surface->colorspace); |
2634 | SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_NONE); |
2635 | |
2636 | float *pixels = (float *)tmp->pixels; |
2637 | pixels[0] = r; |
2638 | pixels[1] = g; |
2639 | pixels[2] = b; |
2640 | pixels[3] = a; |
2641 | |
2642 | result = SDL_BlitSurfaceScaled(tmp, NULL, surface, NULL, SDL_SCALEMODE_NEAREST); |
2643 | SDL_DestroySurface(tmp); |
2644 | } |
2645 | |
2646 | done: |
2647 | SDL_SetSurfaceClipRect(surface, &clip_rect); |
2648 | |
2649 | return result; |
2650 | } |
2651 | |
2652 | Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b) |
2653 | { |
2654 | return SDL_MapSurfaceRGBA(surface, r, g, b, SDL_ALPHA_OPAQUE); |
2655 | } |
2656 | |
2657 | Uint32 SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
2658 | { |
2659 | if (!SDL_SurfaceValid(surface)) { |
2660 | SDL_InvalidParamError("surface" ); |
2661 | return true; |
2662 | } |
2663 | return SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); |
2664 | } |
2665 | |
2666 | // This function Copyright 2023 Collabora Ltd., contributed to SDL under the ZLib license |
2667 | bool SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) |
2668 | { |
2669 | Uint32 pixel = 0; |
2670 | size_t bytes_per_pixel; |
2671 | Uint8 unused; |
2672 | Uint8 *p; |
2673 | bool result = false; |
2674 | |
2675 | if (r) { |
2676 | *r = 0; |
2677 | } else { |
2678 | r = &unused; |
2679 | } |
2680 | |
2681 | if (g) { |
2682 | *g = 0; |
2683 | } else { |
2684 | g = &unused; |
2685 | } |
2686 | |
2687 | if (b) { |
2688 | *b = 0; |
2689 | } else { |
2690 | b = &unused; |
2691 | } |
2692 | |
2693 | if (a) { |
2694 | *a = 0; |
2695 | } else { |
2696 | a = &unused; |
2697 | } |
2698 | |
2699 | if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { |
2700 | return SDL_InvalidParamError("surface" ); |
2701 | } |
2702 | |
2703 | if (x < 0 || x >= surface->w) { |
2704 | return SDL_InvalidParamError("x" ); |
2705 | } |
2706 | |
2707 | if (y < 0 || y >= surface->h) { |
2708 | return SDL_InvalidParamError("y" ); |
2709 | } |
2710 | |
2711 | bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); |
2712 | |
2713 | if (SDL_MUSTLOCK(surface)) { |
2714 | if (!SDL_LockSurface(surface)) { |
2715 | return false; |
2716 | } |
2717 | } |
2718 | |
2719 | p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; |
2720 | |
2721 | if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2722 | /* Fill the appropriate number of least-significant bytes of pixel, |
2723 | * leaving the most-significant bytes set to zero */ |
2724 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN |
2725 | SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel); |
2726 | #else |
2727 | SDL_memcpy(&pixel, p, bytes_per_pixel); |
2728 | #endif |
2729 | SDL_GetRGBA(pixel, surface->fmt, surface->palette, r, g, b, a); |
2730 | result = true; |
2731 | } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2732 | // FIXME: We need code to extract a single macroblock from a YUV surface |
2733 | SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); |
2734 | if (converted) { |
2735 | result = SDL_ReadSurfacePixel(converted, x, y, r, g, b, a); |
2736 | SDL_DestroySurface(converted); |
2737 | } |
2738 | } else { |
2739 | // This is really slow, but it gets the job done |
2740 | Uint8 rgba[4]; |
2741 | |
2742 | if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, surface->colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba))) { |
2743 | *r = rgba[0]; |
2744 | *g = rgba[1]; |
2745 | *b = rgba[2]; |
2746 | *a = rgba[3]; |
2747 | result = true; |
2748 | } |
2749 | } |
2750 | |
2751 | if (SDL_MUSTLOCK(surface)) { |
2752 | SDL_UnlockSurface(surface); |
2753 | } |
2754 | return result; |
2755 | } |
2756 | |
2757 | bool SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a) |
2758 | { |
2759 | float unused; |
2760 | bool result = false; |
2761 | |
2762 | if (r) { |
2763 | *r = 0.0f; |
2764 | } else { |
2765 | r = &unused; |
2766 | } |
2767 | |
2768 | if (g) { |
2769 | *g = 0.0f; |
2770 | } else { |
2771 | g = &unused; |
2772 | } |
2773 | |
2774 | if (b) { |
2775 | *b = 0.0f; |
2776 | } else { |
2777 | b = &unused; |
2778 | } |
2779 | |
2780 | if (a) { |
2781 | *a = 0.0f; |
2782 | } else { |
2783 | a = &unused; |
2784 | } |
2785 | |
2786 | if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { |
2787 | return SDL_InvalidParamError("surface" ); |
2788 | } |
2789 | |
2790 | if (x < 0 || x >= surface->w) { |
2791 | return SDL_InvalidParamError("x" ); |
2792 | } |
2793 | |
2794 | if (y < 0 || y >= surface->h) { |
2795 | return SDL_InvalidParamError("y" ); |
2796 | } |
2797 | |
2798 | if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2799 | Uint8 r8, g8, b8, a8; |
2800 | |
2801 | if (SDL_ReadSurfacePixel(surface, x, y, &r8, &g8, &b8, &a8)) { |
2802 | *r = (float)r8 / 255.0f; |
2803 | *g = (float)g8 / 255.0f; |
2804 | *b = (float)b8 / 255.0f; |
2805 | *a = (float)a8 / 255.0f; |
2806 | result = true; |
2807 | } |
2808 | } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2809 | // FIXME: We need code to extract a single macroblock from a YUV surface |
2810 | SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); |
2811 | if (converted) { |
2812 | result = SDL_ReadSurfacePixelFloat(converted, x, y, r, g, b, a); |
2813 | SDL_DestroySurface(converted); |
2814 | } |
2815 | } else { |
2816 | // This is really slow, but it gets the job done |
2817 | float rgba[4]; |
2818 | Uint8 *p; |
2819 | |
2820 | if (SDL_MUSTLOCK(surface)) { |
2821 | if (!SDL_LockSurface(surface)) { |
2822 | return false; |
2823 | } |
2824 | } |
2825 | |
2826 | p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); |
2827 | |
2828 | if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { |
2829 | SDL_memcpy(rgba, p, sizeof(rgba)); |
2830 | result = true; |
2831 | } else { |
2832 | SDL_Colorspace src_colorspace = surface->colorspace; |
2833 | SDL_Colorspace dst_colorspace = (src_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); |
2834 | |
2835 | if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, src_colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA128_FLOAT, dst_colorspace, 0, rgba, sizeof(rgba))) { |
2836 | result = true; |
2837 | } |
2838 | } |
2839 | |
2840 | if (result) { |
2841 | *r = rgba[0]; |
2842 | *g = rgba[1]; |
2843 | *b = rgba[2]; |
2844 | *a = rgba[3]; |
2845 | } |
2846 | |
2847 | if (SDL_MUSTLOCK(surface)) { |
2848 | SDL_UnlockSurface(surface); |
2849 | } |
2850 | } |
2851 | return result; |
2852 | } |
2853 | |
2854 | bool SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) |
2855 | { |
2856 | Uint32 pixel = 0; |
2857 | size_t bytes_per_pixel; |
2858 | Uint8 *p; |
2859 | bool result = false; |
2860 | |
2861 | if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { |
2862 | return SDL_InvalidParamError("surface" ); |
2863 | } |
2864 | |
2865 | if (x < 0 || x >= surface->w) { |
2866 | return SDL_InvalidParamError("x" ); |
2867 | } |
2868 | |
2869 | if (y < 0 || y >= surface->h) { |
2870 | return SDL_InvalidParamError("y" ); |
2871 | } |
2872 | |
2873 | bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format); |
2874 | |
2875 | if (SDL_MUSTLOCK(surface)) { |
2876 | if (!SDL_LockSurface(surface)) { |
2877 | return false; |
2878 | } |
2879 | } |
2880 | |
2881 | p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel; |
2882 | |
2883 | if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2884 | pixel = SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a); |
2885 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN |
2886 | SDL_memcpy(p, ((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), bytes_per_pixel); |
2887 | #else |
2888 | SDL_memcpy(p, &pixel, bytes_per_pixel); |
2889 | #endif |
2890 | result = true; |
2891 | } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2892 | result = SDL_Unsupported(); |
2893 | } else { |
2894 | // This is really slow, but it gets the job done |
2895 | Uint8 rgba[4]; |
2896 | |
2897 | rgba[0] = r; |
2898 | rgba[1] = g; |
2899 | rgba[2] = b; |
2900 | rgba[3] = a; |
2901 | result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba), surface->format, surface->colorspace, surface->props, p, surface->pitch); |
2902 | } |
2903 | |
2904 | if (SDL_MUSTLOCK(surface)) { |
2905 | SDL_UnlockSurface(surface); |
2906 | } |
2907 | return result; |
2908 | } |
2909 | |
2910 | bool SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a) |
2911 | { |
2912 | bool result = false; |
2913 | |
2914 | if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) { |
2915 | return SDL_InvalidParamError("surface" ); |
2916 | } |
2917 | |
2918 | if (x < 0 || x >= surface->w) { |
2919 | return SDL_InvalidParamError("x" ); |
2920 | } |
2921 | |
2922 | if (y < 0 || y >= surface->h) { |
2923 | return SDL_InvalidParamError("y" ); |
2924 | } |
2925 | |
2926 | if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2927 | Uint8 r8, g8, b8, a8; |
2928 | |
2929 | r8 = (Uint8)SDL_round(SDL_clamp(r, 0.0f, 1.0f) * 255.0f); |
2930 | g8 = (Uint8)SDL_round(SDL_clamp(g, 0.0f, 1.0f) * 255.0f); |
2931 | b8 = (Uint8)SDL_round(SDL_clamp(b, 0.0f, 1.0f) * 255.0f); |
2932 | a8 = (Uint8)SDL_round(SDL_clamp(a, 0.0f, 1.0f) * 255.0f); |
2933 | if (SDL_WriteSurfacePixel(surface, x, y, r8, g8, b8, a8)) { |
2934 | result = true; |
2935 | } |
2936 | } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) { |
2937 | result = SDL_Unsupported(); |
2938 | } else { |
2939 | // This is really slow, but it gets the job done |
2940 | float rgba[4]; |
2941 | Uint8 *p; |
2942 | |
2943 | if (SDL_MUSTLOCK(surface)) { |
2944 | if (!SDL_LockSurface(surface)) { |
2945 | return false; |
2946 | } |
2947 | } |
2948 | |
2949 | p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format); |
2950 | |
2951 | rgba[0] = r; |
2952 | rgba[1] = g; |
2953 | rgba[2] = b; |
2954 | rgba[3] = a; |
2955 | |
2956 | if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) { |
2957 | SDL_memcpy(p, rgba, sizeof(rgba)); |
2958 | result = true; |
2959 | } else { |
2960 | SDL_Colorspace dst_colorspace = surface->colorspace; |
2961 | SDL_Colorspace src_colorspace = (dst_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB); |
2962 | |
2963 | result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT, src_colorspace, 0, rgba, sizeof(rgba), surface->format, dst_colorspace, surface->props, p, surface->pitch); |
2964 | } |
2965 | |
2966 | if (SDL_MUSTLOCK(surface)) { |
2967 | SDL_UnlockSurface(surface); |
2968 | } |
2969 | } |
2970 | return result; |
2971 | } |
2972 | |
2973 | /* |
2974 | * Free a surface created by the above function. |
2975 | */ |
2976 | void SDL_DestroySurface(SDL_Surface *surface) |
2977 | { |
2978 | if (!SDL_SurfaceValid(surface)) { |
2979 | return; |
2980 | } |
2981 | if (surface->internal_flags & SDL_INTERNAL_SURFACE_DONTFREE) { |
2982 | return; |
2983 | } |
2984 | if (--surface->refcount > 0) { |
2985 | return; |
2986 | } |
2987 | |
2988 | SDL_RemoveSurfaceAlternateImages(surface); |
2989 | |
2990 | SDL_DestroyProperties(surface->props); |
2991 | |
2992 | SDL_InvalidateMap(&surface->map); |
2993 | |
2994 | while (surface->locked > 0) { |
2995 | SDL_UnlockSurface(surface); |
2996 | } |
2997 | #ifdef SDL_HAVE_RLE |
2998 | if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) { |
2999 | SDL_UnRLESurface(surface, false); |
3000 | } |
3001 | #endif |
3002 | SDL_SetSurfacePalette(surface, NULL); |
3003 | |
3004 | if (surface->flags & SDL_SURFACE_PREALLOCATED) { |
3005 | // Don't free |
3006 | } else if (surface->flags & SDL_SURFACE_SIMD_ALIGNED) { |
3007 | // Free aligned |
3008 | SDL_aligned_free(surface->pixels); |
3009 | } else { |
3010 | // Normal |
3011 | SDL_free(surface->pixels); |
3012 | } |
3013 | |
3014 | surface->reserved = NULL; |
3015 | |
3016 | if (!(surface->internal_flags & SDL_INTERNAL_SURFACE_STACK)) { |
3017 | SDL_free(surface); |
3018 | } |
3019 | } |
3020 | |