Atlas - SDL_stb.c

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