Atlas - SDL_stb.c
Home / ext / SDL / src / video Lines: 1 | Size: 16050 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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_stb_c.h" 24#include "SDL_surface_c.h" 25#include "SDL_yuv_c.h" 26 27/* STB image conversion */ 28#ifndef SDL_DISABLE_STB 29#define SDL_HAVE_STB 30#endif 31 32#ifdef SDL_HAVE_STB 33//////////////////////////////////////////////////////////////////////////// 34#define malloc SDL_malloc 35#define realloc SDL_realloc 36#define free SDL_free 37#undef memcpy 38#define memcpy SDL_memcpy 39#undef memset 40#define memset SDL_memset 41#undef strcmp 42#define strcmp SDL_strcmp 43#undef strncmp 44#define strncmp SDL_strncmp 45#define strtol SDL_strtol 46 47#define abs SDL_abs 48#define pow SDL_pow 49#define ldexp SDL_scalbn 50 51#define STB_IMAGE_STATIC 52#define STBI_NO_THREAD_LOCALS 53#define STBI_FAILURE_USERMSG 54#if defined(SDL_NEON_INTRINSICS) 55#define STBI_NEON 56#endif 57#define STBI_ONLY_PNG 58#define STBI_ONLY_JPEG 59#define STBI_NO_GIF 60#define STBI_NO_HDR 61#define STBI_NO_LINEAR 62#define STBI_NO_STDIO 63#define STBI_ASSERT SDL_assert 64#define STB_IMAGE_IMPLEMENTATION 65#include "stb_image.h" 66 67//////////////////////////////////////////////////////////////////////////// 68#define MZ_ASSERT(x) SDL_assert(x) 69//#undef memcpy 70//#define memcpy SDL_memcpy 71//#undef memset 72//#define memset SDL_memset 73#if SDL_BYTEORDER == SDL_LIL_ENDIAN 74#define MINIZ_LITTLE_ENDIAN 1 75#else 76#define MINIZ_LITTLE_ENDIAN 0 77#endif 78#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 79#define MINIZ_SDL_NOUNUSED 80#include "miniz.h" 81 82#undef memset 83#endif // SDL_HAVE_STB 84 85#ifdef SDL_HAVE_STB 86static bool SDL_ConvertPixels_MJPG_to_NV12(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) 87{ 88 int w = 0, h = 0, format = 0; 89 stbi__context s; 90 stbi__start_mem(&s, src, src_pitch); 91 92 stbi__result_info ri; 93 SDL_zero(ri); 94 ri.bits_per_channel = 8; 95 ri.channel_order = STBI_ORDER_RGB; 96 ri.num_channels = 0; 97 98 stbi__nv12 nv12; 99 nv12.w = width; 100 nv12.h = height; 101 nv12.pitch = dst_pitch; 102 nv12.y = (stbi_uc *)dst; 103 nv12.uv = nv12.y + (nv12.h * nv12.pitch); 104 105 void *pixels = stbi__jpeg_load(&s, &w, &h, &format, 4, &nv12, &ri); 106 if (!pixels) { 107 return false; 108 } 109 return true; 110} 111#endif // SDL_HAVE_STB 112 113bool SDL_ConvertPixels_STB(int width, int height, 114 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, 115 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) 116{ 117#ifdef SDL_HAVE_STB 118 if (src_format == SDL_PIXELFORMAT_MJPG) { 119 if (dst_format == SDL_PIXELFORMAT_NV12) { 120 return SDL_ConvertPixels_MJPG_to_NV12(width, height, src, src_pitch, dst, dst_pitch); 121 } else if ( 122 dst_format == SDL_PIXELFORMAT_YV12 || 123 dst_format == SDL_PIXELFORMAT_IYUV || 124 dst_format == SDL_PIXELFORMAT_YUY2 || 125 dst_format == SDL_PIXELFORMAT_UYVY || 126 dst_format == SDL_PIXELFORMAT_YVYU || 127 dst_format == SDL_PIXELFORMAT_NV21 || 128 dst_format == SDL_PIXELFORMAT_P010 129 ) { 130 size_t temp_size = 0; 131 size_t temp_pitch = 0; 132 if (!SDL_CalculateYUVSize(dst_format, width, height, &temp_size, &temp_pitch)) { 133 return false; 134 } 135 void *temp_pixels = SDL_malloc(temp_size); 136 if (!temp_pixels) { 137 return false; 138 } 139 140 if (!SDL_ConvertPixels_MJPG_to_NV12(width, height, src, src_pitch, temp_pixels, (int)temp_pitch)) { 141 SDL_free(temp_pixels); 142 return false; 143 } 144 145 bool result = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_NV12, src_colorspace, 0 /*props*/, temp_pixels, (int)temp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 146 SDL_free(temp_pixels); 147 return result; 148 } 149 } 150 151 bool result; 152 int w = 0, h = 0, format = 0; 153 int len = (src_format == SDL_PIXELFORMAT_MJPG) ? src_pitch : (height * src_pitch); 154 void *pixels = stbi_load_from_memory(src, len, &w, &h, &format, 4); 155 if (!pixels) { 156 return false; 157 } 158 159 if (w == width && h == height) { 160 result = SDL_ConvertPixelsAndColorspace(w, h, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, pixels, width * 4, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); 161 } else { 162 result = SDL_SetError("Expected image size %dx%d, actual size %dx%d", width, height, w, h); 163 } 164 stbi_image_free(pixels); 165 166 return result; 167#else 168 return SDL_SetError("SDL not built with STB image support"); 169#endif 170} 171 172#ifdef SDL_HAVE_STB 173static int IMG_LoadSTB_IO_read(void *user, char *data, int size) 174{ 175 size_t amount = SDL_ReadIO((SDL_IOStream*)user, data, size); 176 return (int)amount; 177} 178 179static void IMG_LoadSTB_IO_skip(void *user, int n) 180{ 181 SDL_SeekIO((SDL_IOStream*)user, n, SDL_IO_SEEK_CUR); 182} 183 184static int IMG_LoadSTB_IO_eof(void *user) 185{ 186 SDL_IOStream *src = (SDL_IOStream*)user; 187 return SDL_GetIOStatus(src) == SDL_IO_STATUS_EOF; 188} 189 190static SDL_Surface *SDL_LoadSTB_IO(SDL_IOStream *src) 191{ 192 Sint64 start; 193 Uint8 magic[26]; 194 int w, h, format; 195 stbi_uc *pixels; 196 stbi_io_callbacks rw_callbacks; 197 SDL_Surface *surface = NULL; 198 bool use_palette = false; 199 unsigned int palette_colors[256]; 200 201 // src has already been validated 202 start = SDL_TellIO(src); 203 204 if (SDL_ReadIO(src, magic, sizeof(magic)) == sizeof(magic)) { 205 const Uint8 PNG_COLOR_INDEXED = 3; 206 if (magic[0] == 0x89 && 207 magic[1] == 'P' && 208 magic[2] == 'N' && 209 magic[3] == 'G' && 210 magic[12] == 'I' && 211 magic[13] == 'H' && 212 magic[14] == 'D' && 213 magic[15] == 'R' && 214 magic[25] == PNG_COLOR_INDEXED) { 215 use_palette = true; 216 } 217 } 218 SDL_SeekIO(src, start, SDL_IO_SEEK_SET); 219 220 /* Load the image data */ 221 rw_callbacks.read = IMG_LoadSTB_IO_read; 222 rw_callbacks.skip = IMG_LoadSTB_IO_skip; 223 rw_callbacks.eof = IMG_LoadSTB_IO_eof; 224 w = h = format = 0; /* silence warning */ 225 if (use_palette) { 226 /* Unused palette entries will be opaque white */ 227 SDL_memset(palette_colors, 0xff, sizeof(palette_colors)); 228 229 pixels = stbi_load_from_callbacks_with_palette( 230 &rw_callbacks, 231 src, 232 &w, 233 &h, 234 palette_colors, 235 SDL_arraysize(palette_colors) 236 ); 237 } else { 238 pixels = stbi_load_from_callbacks( 239 &rw_callbacks, 240 src, 241 &w, 242 &h, 243 &format, 244 STBI_default 245 ); 246 } 247 if (!pixels) { 248 SDL_SeekIO(src, start, SDL_IO_SEEK_SET); 249 return NULL; 250 } 251 252 if (use_palette) { 253 surface = SDL_CreateSurfaceFrom( 254 w, 255 h, 256 SDL_PIXELFORMAT_INDEX8, 257 pixels, 258 w 259 ); 260 if (surface) { 261 bool has_colorkey = false; 262 int colorkey_index = -1; 263 bool has_alpha = false; 264 SDL_Palette *palette = SDL_CreateSurfacePalette(surface); 265 if (palette) { 266 int i; 267 Uint8 *palette_bytes = (Uint8 *)palette_colors; 268 269 for (i = 0; i < palette->ncolors; i++) { 270 palette->colors[i].r = *palette_bytes++; 271 palette->colors[i].g = *palette_bytes++; 272 palette->colors[i].b = *palette_bytes++; 273 palette->colors[i].a = *palette_bytes++; 274 if (palette->colors[i].a != SDL_ALPHA_OPAQUE) { 275 if (palette->colors[i].a == SDL_ALPHA_TRANSPARENT && !has_colorkey) { 276 has_colorkey = true; 277 colorkey_index = i; 278 } else { 279 /* Partial opacity or multiple colorkeys */ 280 has_alpha = true; 281 } 282 } 283 } 284 } 285 if (has_alpha) { 286 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); 287 } else if (has_colorkey) { 288 SDL_SetSurfaceColorKey(surface, true, colorkey_index); 289 } 290 291 /* FIXME: This sucks. It'd be better to allocate the surface first, then 292 * write directly to the pixel buffer: 293 * https://github.com/nothings/stb/issues/58 294 * -flibit 295 */ 296 surface->flags &= ~SDL_SURFACE_PREALLOCATED; 297 } 298 299 } else if (format == STBI_grey || format == STBI_rgb || format == STBI_rgb_alpha) { 300 surface = SDL_CreateSurfaceFrom( 301 w, 302 h, 303 (format == STBI_rgb_alpha) ? SDL_PIXELFORMAT_RGBA32 : 304 (format == STBI_rgb) ? SDL_PIXELFORMAT_RGB24 : 305 SDL_PIXELFORMAT_INDEX8, 306 pixels, 307 w * format 308 ); 309 if (surface) { 310 /* Set a grayscale palette for gray images */ 311 if (surface->format == SDL_PIXELFORMAT_INDEX8) { 312 SDL_Palette *palette = SDL_CreateSurfacePalette(surface); 313 if (palette) { 314 int i; 315 316 for (i = 0; i < palette->ncolors; i++) { 317 palette->colors[i].r = (Uint8)i; 318 palette->colors[i].g = (Uint8)i; 319 palette->colors[i].b = (Uint8)i; 320 } 321 } 322 } 323 324 /* FIXME: This sucks. It'd be better to allocate the surface first, then 325 * write directly to the pixel buffer: 326 * https://github.com/nothings/stb/issues/58 327 * -flibit 328 */ 329 surface->flags &= ~SDL_SURFACE_PREALLOCATED; 330 } 331 332 } else if (format == STBI_grey_alpha) { 333 surface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_RGBA32); 334 if (surface) { 335 Uint8 *src_ptr = pixels; 336 Uint8 *dst = (Uint8 *)surface->pixels; 337 int skip = surface->pitch - (surface->w * 4); 338 int row, col; 339 340 for (row = 0; row < h; ++row) { 341 for (col = 0; col < w; ++col) { 342 Uint8 c = *src_ptr++; 343 Uint8 a = *src_ptr++; 344 *dst++ = c; 345 *dst++ = c; 346 *dst++ = c; 347 *dst++ = a; 348 } 349 dst += skip; 350 } 351 stbi_image_free(pixels); 352 } 353 } else { 354 SDL_SetError("Unknown image format: %d", format); 355 } 356 357 if (!surface) { 358 /* The error message should already be set */ 359 stbi_image_free(pixels); /* calls SDL_free() */ 360 SDL_SeekIO(src, start, SDL_IO_SEEK_SET); 361 } 362 return surface; 363} 364#endif // SDL_HAVE_STB 365 366bool SDL_IsPNG(SDL_IOStream *src) 367{ 368 Sint64 start; 369 Uint8 magic[4]; 370 bool is_PNG; 371 372 is_PNG = false; 373 start = SDL_TellIO(src); 374 if (start >= 0) { 375 if (SDL_ReadIO(src, magic, sizeof(magic)) == sizeof(magic)) { 376 if (magic[0] == 0x89 && 377 magic[1] == 'P' && 378 magic[2] == 'N' && 379 magic[3] == 'G') { 380 is_PNG = true; 381 } 382 } 383 SDL_SeekIO(src, start, SDL_IO_SEEK_SET); 384 } 385 386 return is_PNG; 387} 388 389SDL_Surface *SDL_LoadPNG_IO(SDL_IOStream *src, bool closeio) 390{ 391 SDL_Surface *surface = NULL; 392 393 CHECK_PARAM(!src) { 394 SDL_InvalidParamError("src"); 395 goto done; 396 } 397 398 if (!SDL_IsPNG(src)) { 399 SDL_SetError("File is not a PNG file"); 400 goto done; 401 } 402 403#ifdef SDL_HAVE_STB 404 surface = SDL_LoadSTB_IO(src); 405#else 406 SDL_SetError("SDL not built with STB image support"); 407#endif // SDL_HAVE_STB 408 409done: 410 if (src && closeio) { 411 SDL_CloseIO(src); 412 } 413 return surface; 414} 415 416SDL_Surface *SDL_LoadPNG(const char *file) 417{ 418 SDL_IOStream *stream = SDL_IOFromFile(file, "rb"); 419 if (!stream) { 420 return NULL; 421 } 422 423 return SDL_LoadPNG_IO(stream, true); 424} 425 426bool SDL_SavePNG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio) 427{ 428 bool retval = false; 429 Uint8 *plte = NULL; 430 Uint8 *trns = NULL; 431 bool free_surface = false; 432 433 // Make sure we have something to save 434 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 435 SDL_InvalidParamError("surface"); 436 goto done; 437 } 438 CHECK_PARAM(!dst) { 439 SDL_InvalidParamError("dst"); 440 goto done; 441 } 442 443#ifdef SDL_HAVE_STB 444 int plte_size = 0; 445 int trns_size = 0; 446 447 if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) { 448 if (!surface->palette) { 449 SDL_SetError("Indexed surfaces must have a palette"); 450 goto done; 451 } 452 453 if (surface->format != SDL_PIXELFORMAT_INDEX8) { 454 surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_INDEX8); 455 if (!surface) { 456 goto done; 457 } 458 free_surface = true; 459 } 460 461 plte_size = surface->palette->ncolors * 3; 462 trns_size = surface->palette->ncolors; 463 plte = (Uint8 *)SDL_malloc(plte_size); 464 trns = (Uint8 *)SDL_malloc(trns_size); 465 if (!plte || !trns) { 466 goto done; 467 } 468 SDL_Color *colors = surface->palette->colors; 469 for (int i = 0; i < surface->palette->ncolors; ++i) { 470 plte[i * 3 + 0] = colors[i].r; 471 plte[i * 3 + 1] = colors[i].g; 472 plte[i * 3 + 2] = colors[i].b; 473 trns[i] = colors[i].a; 474 } 475 } else { 476 if (surface->format != SDL_PIXELFORMAT_RGBA32) { 477 surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32); 478 if (!surface) { 479 goto done; 480 } 481 free_surface = true; 482 } 483 } 484 485 size_t size = 0; 486 void *png = tdefl_write_image_to_png_file_in_memory_ex(surface->pixels, surface->w, surface->h, SDL_BYTESPERPIXEL(surface->format), surface->pitch, &size, 6, MZ_FALSE, plte, plte_size, trns, trns_size); 487 if (png) { 488 if (SDL_WriteIO(dst, png, size)) { 489 retval = true; 490 } 491 mz_free(png); /* calls SDL_free() */ 492 } else { 493 SDL_SetError("Failed to convert and save image"); 494 } 495 496#else 497 SDL_SetError("SDL not built with STB image support"); 498#endif 499 500done: 501 if (free_surface) { 502 SDL_DestroySurface(surface); 503 } 504 SDL_free(plte); 505 SDL_free(trns); 506 507 if (dst && closeio) { 508 retval &= SDL_CloseIO(dst); 509 } 510 return retval; 511} 512 513bool SDL_SavePNG(SDL_Surface *surface, const char *file) 514{ 515#ifdef SDL_HAVE_STB 516 // Make sure we have something to save 517 CHECK_PARAM(!SDL_SurfaceValid(surface)) { 518 return SDL_InvalidParamError("surface"); 519 } 520 521 if (SDL_ISPIXELFORMAT_INDEXED(surface->format) && !surface->palette) { 522 return SDL_SetError("Indexed surfaces must have a palette"); 523 } 524 SDL_IOStream *stream = SDL_IOFromFile(file, "wb"); 525 if (!stream) { 526 return false; 527 } 528 529 return SDL_SavePNG_IO(surface, stream, true); 530#else 531 return SDL_SetError("SDL not built with STB image support"); 532#endif 533} 534[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.