1 /++ 2 + Authors: Avaxar <avaxar@nekkl.org> 3 + Copyright: Copyright © 2023, Avaxar 4 + License: $(LINK2 https://mit-license.org, MIT License) 5 +/ 6 7 module dsdl2.image; 8 @safe: 9 10 // dfmt off 11 import bindbc.sdl; 12 static if (bindSDLImage): 13 // dfmt on 14 15 import dsdl2.sdl; 16 import dsdl2.renderer; 17 import dsdl2.surface; 18 import dsdl2.texture; 19 20 import core.memory : GC; 21 import std.conv : to; 22 import std.format : format; 23 import std.string : toStringz; 24 25 version (BindSDL_Static) { 26 } 27 else { 28 /++ 29 + Loads the SDL2_image shared dynamic library, which wraps bindbc-sdl's `loadSDLImage` function 30 + 31 + Unless if bindbc-sdl is on static mode (by adding a `BindSDL_Static` version), this function will exist and must 32 + be called before any calls are made to the library. Otherwise, a segfault will happen upon any function calls. 33 + 34 + Params: 35 + libName = name or path to look the SDL2_image SO/DLL for, otherwise `null` for default searching path 36 + Throws: `dsdl2.SDLException` if failed to find the library 37 +/ 38 void loadSO(string libName = null) @trusted { 39 SDLImageSupport current = libName is null ? loadSDLImage() : loadSDLImage(libName.toStringz()); 40 if (current == sdlImageSupport) { 41 return; 42 } 43 44 Version wanted = Version(sdlImageSupport); 45 if (current == SDLImageSupport.badLibrary) { 46 import std.stdio : writeln; 47 48 writeln("WARNING: dsdl2 expects SDL_image ", wanted.format(), ", but got ", getVersion().format(), "."); 49 } 50 else if (current == SDLImageSupport.noLibrary) { 51 throw new SDLException("No SDL2_image library found, especially of version " ~ wanted.format(), 52 __FILE__, __LINE__); 53 } 54 } 55 } 56 57 /++ 58 + Wraps `IMG_Init` which initializes selected SDL2_image image format subsystems 59 + 60 + Params: 61 + jpg = selects the `IMG_INIT_JPG` subsystem 62 + png = selects the `IMG_INIT_PNG` subsystem 63 + tif = selects the `IMG_INIT_TIF` subsystem 64 + webp = selects the `IMG_INIT_WEBP` subsystem 65 + jxl = selects the `IMG_INIT_JXL` subsystem (from SDL_image 2.6) 66 + avif = selects the `IMG_INIT_AVIF` subsystem (from SDL_image 2.6) 67 + everything = selects every available subsystem 68 + Throws: `dsdl2.SDLException` if any selected subsystem failed to initialize 69 + Example: 70 + --- 71 + dsdl2.image.init(everything : true); 72 + --- 73 +/ 74 void init(bool jpg = false, bool png = false, bool tif = false, bool webp = false, bool jxl = false, bool avif = false, 75 bool everything = false) @trusted 76 in { 77 static if (sdlImageSupport < SDLImageSupport.v2_6) { 78 assert(jxl == false); 79 assert(avif == false); 80 } 81 else { 82 if (jxl || avif) { 83 assert(dsdl2.image.getVersion() >= Version(2, 6)); 84 } 85 } 86 } 87 do { 88 int flags = 0; 89 90 flags |= jpg ? IMG_INIT_JPG : 0; 91 flags |= png ? IMG_INIT_PNG : 0; 92 flags |= tif ? IMG_INIT_TIF : 0; 93 flags |= webp ? IMG_INIT_WEBP : 0; 94 flags |= everything ? IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP : 0; 95 96 static if (sdlImageSupport >= SDLImageSupport.v2_6) { 97 flags |= jxl ? IMG_INIT_JXL : 0; 98 flags |= avif ? IMG_INIT_AVIF : 0; 99 flags |= everything ? IMG_INIT_JXL | IMG_INIT_AVIF : 0; 100 } 101 102 if ((IMG_Init(flags) & flags) != flags) { 103 throw new SDLException; 104 } 105 } 106 107 version (unittest) { 108 static this() { 109 version (BindSDL_Static) { 110 } 111 else { 112 dsdl2.image.loadSO(); 113 } 114 115 dsdl2.image.init(everything : true); 116 } 117 } 118 119 /++ 120 + Wraps `IMG_Quit` which entirely deinitializes SDL2_image 121 +/ 122 void quit() @trusted { 123 IMG_Quit(); 124 } 125 126 version (unittest) { 127 static ~this() { 128 dsdl2.image.quit(); 129 } 130 } 131 132 /++ 133 + Wraps `IMG_Linked_version` which gets the version of the linked SDL2_image library 134 + 135 + Returns: `dsdl2.Version` of the linked SDL2_image library 136 +/ 137 Version getVersion() @trusted { 138 return Version(*IMG_Linked_Version()); 139 } 140 141 /++ 142 + Wraps `IMG_Load` which loads an image from a filesystem path into a software `dsdl2.Surface` 143 + 144 + Params: 145 + file = path to the image file 146 + Returns: `dsdl2.Surface` of the loaded image 147 + Throws: `dsdl2.SDLException` if failed to load the image 148 +/ 149 Surface load(string file) @trusted { 150 if (SDL_Surface* sdlSurface = IMG_Load(file.toStringz())) { 151 return new Surface(sdlSurface); 152 } 153 else { 154 throw new SDLException; 155 } 156 } 157 158 /++ 159 + Wraps `IMG_Load_RW` which loads an image from a data buffer into a software `dsdl2.Surface` 160 + 161 + Params: 162 + data = data buffer of the image 163 + Returns: `dsdl2.Surface` of the loaded image 164 + Throws: `dsdl2.SDLException` if failed to load the image 165 +/ 166 Surface loadRaw(const void[] data) @trusted { 167 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 168 if (sdlRWops is null) { 169 throw new SDLException; 170 } 171 172 if (SDL_Surface* sdlSurface = IMG_Load_RW(sdlRWops, 1)) { 173 return new Surface(sdlSurface); 174 } 175 else { 176 throw new SDLException; 177 } 178 } 179 180 /++ 181 + Wraps `IMG_LoadTyped_RW` which loads a typed image from a data buffer into a software `dsdl2.Surface` 182 + 183 + Params: 184 + data = data buffer of the image 185 + type = specified type of the image 186 + Returns: `dsdl2.Surface` of the loaded image 187 + Throws: `dsdl2.SDLException` if failed to load the image 188 +/ 189 Surface loadRaw(const void[] data, string type) @trusted { 190 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 191 if (sdlRWops is null) { 192 throw new SDLException; 193 } 194 195 if (SDL_Surface* sdlSurface = IMG_LoadTyped_RW(sdlRWops, 1, type.toStringz())) { 196 return new Surface(sdlSurface); 197 } 198 else { 199 throw new SDLException; 200 } 201 } 202 203 /++ 204 + Wraps `IMG_LoadTexture` which loads an image from a filesystem path into a hardware `dsdl2.Texture` 205 + 206 + Params: 207 + renderer = given `dsdl2.Renderer` to initialize the texture 208 + file = path to the image file 209 + Returns: `dsdl2.Texture` of the loaded image 210 + Throws: `dsdl2.SDLException` if failed to load the image 211 +/ 212 Texture loadTexture(Renderer renderer, string file) @trusted 213 in { 214 assert(renderer !is null); 215 } 216 do { 217 if (SDL_Texture* sdlTexture = IMG_LoadTexture(renderer.sdlRenderer, file.toStringz())) { 218 return new Texture(sdlTexture); 219 } 220 else { 221 throw new SDLException; 222 } 223 } 224 225 /++ 226 + Wraps `IMG_LoadTexture_RW` which loads an image from a data buffer into a hardware `dsdl2.Texture` 227 + 228 + Params: 229 + renderer = given `dsdl2.Renderer` to initialize the texture 230 + data = data buffer of the image 231 + Returns: `dsdl2.Texture` of the loaded image 232 + Throws: `dsdl2.SDLException` if failed to load the image 233 +/ 234 Texture loadTextureRaw(Renderer renderer, const void[] data) @trusted 235 in { 236 assert(renderer !is null); 237 } 238 do { 239 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 240 if (sdlRWops is null) { 241 throw new SDLException; 242 } 243 244 if (SDL_Texture* sdlTexture = IMG_LoadTexture_RW(renderer.sdlRenderer, sdlRWops, 1)) { 245 return new Texture(sdlTexture); 246 } 247 else { 248 throw new SDLException; 249 } 250 } 251 252 /++ 253 + Wraps `IMG_LoadTextureTyped_RW` which loads a typed image from a data buffer into a hardware `dsdl2.Texture` 254 + 255 + Params: 256 + renderer = given `dsdl2.Renderer` to initialize the texture 257 + data = data buffer of the image 258 + type = specified type of the image 259 + Returns: `dsdl2.Texture` of the loaded image 260 + Throws: `dsdl2.SDLException` if failed to load the image 261 +/ 262 Texture loadTextureRaw(Renderer renderer, const void[] data, string type) @trusted 263 in { 264 assert(renderer !is null); 265 } 266 do { 267 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 268 if (sdlRWops is null) { 269 throw new SDLException; 270 } 271 272 if (SDL_Texture* sdlTexture = IMG_LoadTextureTyped_RW(renderer.sdlRenderer, sdlRWops, 1, type.toStringz())) { 273 return new Texture(sdlTexture); 274 } 275 else { 276 throw new SDLException; 277 } 278 } 279 280 bool _isType(alias func, ubyte minMinorVer = 0, ubyte minPatchVer = 0)(const void[] data) @trusted 281 in { 282 assert(dsdl2.image.getVersion() >= Version(2, minMinorVer, minPatchVer)); 283 } 284 do { 285 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 286 if (sdlRWops is null) { 287 throw new SDLException; 288 } 289 scope (exit) 290 if (SDL_RWclose(sdlRWops) != 0) { 291 throw new SDLException; 292 } 293 294 return func(sdlRWops) != 0; 295 } 296 297 alias isICO = _isType!IMG_isICO; /// Wraps `IMG_isICO` which checks whether `data` is an ICO file 298 alias isCUR = _isType!IMG_isCUR; /// Wraps `IMG_isCUR` which checks whether `data` is a CUR file 299 alias isBMP = _isType!IMG_isBMP; /// Wraps `IMG_isBMP` which checks whether `data` is a BMP file 300 alias isGIF = _isType!IMG_isGIF; /// Wraps `IMG_isGIF` which checks whether `data` is a GIF file 301 alias isJPG = _isType!IMG_isJPG; /// Wraps `IMG_isJPG` which checks whether `data` is a JPG file 302 alias isLBM = _isType!IMG_isLBM; /// Wraps `IMG_isLBM` which checks whether `data` is a LBM file 303 alias isPCX = _isType!IMG_isPCX; /// Wraps `IMG_isPCX` which checks whether `data` is a PCX file 304 alias isPNG = _isType!IMG_isPNG; /// Wraps `IMG_isPNG` which checks whether `data` is a PNG file 305 alias isPNM = _isType!IMG_isPNM; /// Wraps `IMG_isPNM` which checks whether `data` is a PNM file 306 alias isTIF = _isType!IMG_isTIF; /// Wraps `IMG_isTIF` which checks whether `data` is a TIF file 307 alias isXCF = _isType!IMG_isXCF; /// Wraps `IMG_isXCF` which checks whether `data` is an XCF file 308 alias isXPM = _isType!IMG_isXPM; /// Wraps `IMG_isXPM` which checks whether `data` is an XPM file 309 alias isXV = _isType!IMG_isXV; /// Wraps `IMG_isXV` which checks whether `data` is an XV file 310 alias isWEBP = _isType!IMG_isWEBP; /// Wraps `IMG_isWEBP` which checks whether `data` is a WEBP file 311 312 static if (sdlImageSupport >= SDLImageSupport.v2_0_2) { 313 alias isSVG = _isType!(IMG_isSVG, 0, 2); /// Wraps `IMG_isSVG` which checks whether `data` is a SVG file (from SDL_image 2.0.2) 314 } 315 316 static if (sdlImageSupport >= SDLImageSupport.v2_6) { 317 alias isAVIF = _isType!(IMG_isAVIF, 6); /// Wraps `IMG_isAVIF` which checks whether `data` is an AVIF file (from SDL_image 2.6) 318 alias isJXL = _isType!(IMG_isJXL, 6); /// Wraps `IMG_isJXL` which checks whether `data` is a JXL file (from SDL_image 2.6) 319 alias isQOI = _isType!(IMG_isQOI, 6); /// Wraps `IMG_isQOI` which checks whether `data` is a QOI file (from SDL_image 2.6) 320 } 321 322 Surface _loadTypeRaw(alias func, ubyte minMinorVer = 0, ubyte minPatchVer = 0)(const void[] data) @trusted 323 in { 324 assert(dsdl2.image.getVersion() >= Version(2, minMinorVer, minPatchVer)); 325 } 326 do { 327 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 328 if (sdlRWops is null) { 329 throw new SDLException; 330 } 331 scope (exit) 332 if (SDL_RWclose(sdlRWops) != 0) { 333 throw new SDLException; 334 } 335 336 if (SDL_Surface* sdlSurface = func(sdlRWops)) { 337 return new Surface(sdlSurface); 338 } 339 else { 340 throw new SDLException; 341 } 342 } 343 344 alias loadICORaw = _loadTypeRaw!IMG_LoadICO_RW; /// Wraps `IMG_LoadICO_RW` which loads `data` as an ICO image to surface 345 alias loadCURRaw = _loadTypeRaw!IMG_LoadCUR_RW; /// Wraps `IMG_LoadCUR_RW` which loads `data` as a CUR image to surface 346 alias loadBMPRaw = _loadTypeRaw!IMG_LoadBMP_RW; /// Wraps `IMG_LoadBMP_RW` which loads `data` as a BMP image to surface 347 alias loadGIFRaw = _loadTypeRaw!IMG_LoadGIF_RW; /// Wraps `IMG_LoadGIF_RW` which loads `data` as a GIF image to surface 348 alias loadJPGRaw = _loadTypeRaw!IMG_LoadJPG_RW; /// Wraps `IMG_LoadJPG_RW` which loads `data` as a JPG image to surface 349 alias loadLBRaw = _loadTypeRaw!IMG_LoadLBM_RW; /// Wraps `IMG_LoadLBM_RW` which loads `data` as a LBM image to surface 350 alias loadPCXRaw = _loadTypeRaw!IMG_LoadPCX_RW; /// Wraps `IMG_LoadPCX_RW` which loads `data` as a PCX image to surface 351 alias loadPNGRaw = _loadTypeRaw!IMG_LoadPNG_RW; /// Wraps `IMG_LoadPNG_RW` which loads `data` as a PNG image to surface 352 alias loadPNMRaw = _loadTypeRaw!IMG_LoadPNM_RW; /// Wraps `IMG_LoadPNM_RW` which loads `data` as a PNM image to surface 353 alias loadTIFRaw = _loadTypeRaw!IMG_LoadTIF_RW; /// Wraps `IMG_LoadTIF_RW` which loads `data` as a TIF image to surface 354 alias loadXCFRaw = _loadTypeRaw!IMG_LoadXCF_RW; /// Wraps `IMG_LoadXCF_RW` which loads `data` as an XCF image to surface 355 alias loadXPMRaw = _loadTypeRaw!IMG_LoadXPM_RW; /// Wraps `IMG_LoadXPM_RW` which loads `data` as an XPM image to surface 356 alias loadXVRaw = _loadTypeRaw!IMG_LoadXV_RW; /// Wraps `IMG_LoadXV_RW` which loads `data` as an XV image as surface 357 alias loadWEBPRaw = _loadTypeRaw!IMG_LoadWEBP_RW; /// Wraps `IMG_LoadWEBP_RW` which loads `data` as a WEBP image to surface 358 359 static if (sdlImageSupport >= SDLImageSupport.v2_0_2) { 360 alias loadSVGRaw = _loadTypeRaw!(IMG_LoadSVG_RW, 0, 2); /// Wraps `IMG_LoadSVG_RW` which loads `data` as a SVG image (from SDL_image 2.0.2) 361 } 362 363 static if (sdlImageSupport >= SDLImageSupport.v2_6) { 364 alias loadAVIFRaw = _loadTypeRaw!(IMG_LoadAVIF_RW, 6); /// Wraps `IMG_LoadAVIF_RW` which loads `data` as an AVIF image (from SDL_image 2.6) 365 alias loadJXLRaw = _loadTypeRaw!(IMG_LoadJXL_RW, 6); /// Wraps `IMG_LoadJXL_RW` which loads `data` as a JXL image (from SDL_image 2.6) 366 alias loadQOIRaw = _loadTypeRaw!(IMG_LoadQOI_RW, 6); /// Wraps `IMG_LoadQOI_RW` which loads `data` as a QOI image (from SDL_image 2.6) 367 368 /++ 369 + Wraps `IMG_LoadSizedSVG_RW` (from SDL_image 2.6) which loads a `dsdl2.Surface` image from a buffer of SVG file 370 + format, while providing the desired flattened size of the vector image 371 + 372 + Params: 373 + data = data buffer of the image 374 + size = desized size in pixels (width and height) of the flattened SVG image; `0` to either one of the 375 + dimensions to be adjusted for aspect ratio 376 + Returns: `dsdl2.Surface` of the loaded image 377 + Throws: `dsdl2.SDLException` if failed to load 378 +/ 379 Surface loadSizedSVGRaw(const void[] data, uint[2] size) @trusted 380 in { 381 assert(dsdl2.image.getVersion() >= Version(2, 6)); 382 } 383 do { 384 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 385 if (sdlRWops is null) { 386 throw new SDLException; 387 } 388 scope (exit) 389 if (SDL_RWclose(sdlRWops) != 0) { 390 throw new SDLException; 391 } 392 393 if (SDL_Surface* sdlSurface = IMG_LoadSizedSVG_RW(sdlRWops, size[0].to!int, size[1].to!int)) { 394 return new Surface(sdlSurface); 395 } 396 else { 397 throw new SDLException; 398 } 399 } 400 } 401 402 /++ 403 + Wraps `IMG_SavePNG` which saves a `dsdl2.Surface` image into a PNG file in the filesystem 404 + 405 + Params: 406 + surface = given `dsdl2.Surface` of the image to save 407 + file = target file path to save 408 + Throws: `dsdl2.SDLException` if failed to save 409 +/ 410 void savePNG(Surface surface, string file) @trusted 411 in { 412 assert(surface !is null); 413 } 414 do { 415 if (IMG_SavePNG(surface.sdlSurface, file.toStringz()) != 0) { 416 throw new SDLException; 417 } 418 } 419 420 /++ 421 + Wraps `IMG_SavePNG_RW` which saves a `dsdl2.Surface` image into a buffer of PNG file format 422 + 423 + Params: 424 + surface = given `dsdl2.Surface` of the image to save 425 + Returns: `dsdl2.SDLException` if failed to save 426 +/ 427 void[] savePNGRaw(Surface surface) @trusted 428 in { 429 assert(surface !is null); 430 } 431 do { 432 // TODO: make a writable `SDL_RWops` to dynamic memory 433 assert(false, "Not implemented"); 434 } 435 436 static if (sdlImageSupport >= SDLImageSupport.v2_0_2) { 437 /++ 438 + Wraps `IMG_SaveJPG` (from SDL_image 2.0.2) which saves a `dsdl2.Surface` image into a JPG file in the filesystem 439 + 440 + Params: 441 + surface = given `dsdl2.Surface` of the image to save 442 + file = target file path to save 443 + quality = value ranging from `0` to `100` specifying the image quality compensating for compression 444 + Throws: `dsdl2.SDLException` if failed to save 445 +/ 446 void saveJPG(Surface surface, string file, ubyte quality) @trusted 447 in { 448 assert(dsdl2.image.getVersion() >= Version(2, 0, 2)); 449 assert(surface !is null); 450 } 451 do { 452 if (IMG_SaveJPG(surface.sdlSurface, file.toStringz(), quality) != 0) { 453 throw new SDLException; 454 } 455 } 456 457 /++ 458 + Wraps `IMG_SaveJPG_RW` (from SDL_image 2.0.2) which saves a `dsdl2.Surface` image into a buffer of JPG file format 459 + 460 + Params: 461 + surface = given `dsdl2.Surface` of the image to save 462 + quality = value ranging from `0` to `100` specifying the image quality compensating for compression 463 + Returns: `dsdl2.SDLException` if failed to save 464 +/ 465 void[] saveJPGRaw(Surface surface, ubyte quality) @trusted 466 in { 467 assert(dsdl2.image.getVersion() >= Version(2, 0, 2)); 468 assert(surface !is null); 469 } 470 do { 471 // TODO: make a writable `SDL_RWops` to dynamic memory 472 assert(false, "Not implemented"); 473 } 474 } 475 476 static if (sdlImageSupport >= SDLImageSupport.v2_6) { 477 /++ 478 + D class that wraps `IMG_Animation` (from SDL_image 2.6) storing multiple `dsdl2.Surface`s of an animation 479 +/ 480 class Animation { 481 private Surface[] framesProxy = null; 482 private bool isOwner = true; 483 private void* userRef = null; 484 485 @system IMG_Animation* imgAnimation = null; /// Internal `IMG_Animation` pointer 486 487 /++ 488 + Constructs a `dsdl2.image.Animation` from a vanilla `IMG_Animation*` from bindbc-sdl 489 + 490 + Params: 491 + imgAnimation = the `IMG_Animation` pointer to manage 492 + isOwner = whether the instance owns the given `IMG_Animation*` and should destroy it on its own 493 + userRef = optional pointer to maintain reference link, avoiding GC cleanup 494 +/ 495 this(IMG_Animation* imgAnimation, bool isOwner = true, void* userRef = null) @system 496 in { 497 assert(imgAnimation !is null); 498 } 499 do { 500 this.imgAnimation = imgAnimation; 501 this.isOwner = isOwner; 502 this.userRef = userRef; 503 } 504 505 ~this() @trusted { 506 if (this.isOwner) { 507 IMG_FreeAnimation(this.imgAnimation); 508 } 509 } 510 511 @trusted invariant { // @suppress(dscanner.trust_too_much) 512 // Instance might be in an invalid state due to holding a non-owned externally-freed object when 513 // destructed in an unpredictable order. 514 if (!this.isOwner && GC.inFinalizer) { 515 return; 516 } 517 518 assert(this.imgAnimation !is null); 519 } 520 521 /++ 522 + Equality operator overload 523 +/ 524 bool opEquals(const Animation rhs) const @trusted { 525 return this.imgAnimation is rhs.imgAnimation; 526 } 527 528 /++ 529 + Gets the hash of the `dsdl2.image.Animation` 530 + 531 + Returns: unique hash for the instance being the pointer of the internal `IMG_Animation` pointer 532 +/ 533 override hash_t toHash() const @trusted { 534 return cast(hash_t) this.imgAnimation; 535 } 536 537 /++ 538 + Formats the `dsdl2.image.Animation` into its construction representation: 539 + `"dsdl2.image.Animation(<imgAnimation>)"` 540 + 541 + Returns: the formatted `string` 542 +/ 543 override string toString() const @trusted { 544 return "dsdl2.image.Animation(0x%x)".format(this.imgAnimation); 545 } 546 547 /++ 548 + Gets the width of the `dsdl2.image.Animation` in pixels 549 + 550 + Returns: width of the `dsdl2.image.Animation` in pixels 551 +/ 552 uint width() const @property @trusted { 553 return this.imgAnimation.w.to!uint; 554 } 555 556 /++ 557 + Gets the height of the `dsdl2.image.Animation` in pixels 558 + 559 + Returns: height of the `dsdl2.image.Animation` in pixels 560 +/ 561 uint height() const @property @trusted { 562 return this.imgAnimation.h.to!uint; 563 } 564 565 /++ 566 + Gets the size of the `dsdl2.image.Animation` in pixels 567 + 568 + Returns: size of the `dsdl2.image.Animation` in pixels 569 +/ 570 uint[2] size() const @property @trusted { 571 return [this.imgAnimation.w.to!uint, this.imgAnimation.h.to!uint]; 572 } 573 574 /++ 575 + Gets the frame count of the `dsdl2.image.Animation` 576 + 577 + Returns: frame count of the `dsdl2.image.Animation` 578 +/ 579 size_t count() const @property @trusted { 580 return cast(size_t) this.imgAnimation.count; 581 } 582 583 /++ 584 + Gets an array of `dsdl2.Surface` frames of the `dsdl2.image.Animation` 585 + 586 + Returns: array of `dsdl2.Surface` frames of the `dsdl2.image.Animation` 587 +/ 588 const(Surface[]) frames() const @property @trusted { 589 if (this.framesProxy is null) { 590 (cast(Animation) this).framesProxy = new Surface[this.count]; 591 foreach (i; 0 .. this.count) { 592 (cast(Animation) this).framesProxy[i] = new Surface( 593 cast(SDL_Surface*) this.imgAnimation.frames[i], false, cast(void*) this); 594 } 595 } 596 597 return this.framesProxy; 598 } 599 600 /++ 601 + Gets an array of delay per frame of the `dsdl2.image.Animation` 602 + 603 + Returns: array of delay per frame of the `dsdl2.image.Animation` 604 +/ 605 const(uint[]) delays() const @property @trusted { 606 return (cast(uint*)&this.imgAnimation.delays)[0 .. this.count]; 607 } 608 } 609 610 /++ 611 + Wraps `IMG_LoadAnimation` (from SDL_image 2.6) which loads an animation from a filesystem path into an 612 + `dsdl2.image.Animation` 613 + 614 + Params: 615 + file = path to the animation file 616 + Returns: `dsdl2.image.Animation` of the loaded animated image 617 + Throws: `dsdl2.SDLException` if failed to load the animation 618 +/ 619 Animation loadAnimation(string file) @trusted 620 in { 621 assert(dsdl2.image.getVersion() >= Version(2, 6)); 622 } 623 do { 624 if (IMG_Animation* imgAnimation = IMG_LoadAnimation(file.toStringz())) { 625 return new Animation(imgAnimation); 626 } 627 else { 628 throw new SDLException; 629 } 630 } 631 632 /++ 633 + Wraps `IMG_LoadAnimation_RW` (from SDL_image 2.6) which loads an animation from a data buffer into a 634 + `dsdl2.image.Animation` 635 + 636 + Params: 637 + data = data buffer of the animation 638 + Returns: `dsdl2.image.Animation` of the loaded animated image 639 + Throws: `dsdl2.SDLException` if failed to load the animation 640 +/ 641 Animation loadAnimationRaw(const void[] data) @trusted 642 in { 643 assert(dsdl2.image.getVersion() >= Version(2, 6)); 644 } 645 do { 646 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 647 if (sdlRWops is null) { 648 throw new SDLException; 649 } 650 651 if (IMG_Animation* imgAnimation = IMG_LoadAnimation_RW(sdlRWops, 1)) { 652 return new Animation(imgAnimation); 653 } 654 else { 655 throw new SDLException; 656 } 657 } 658 659 /++ 660 + Wraps `IMG_LoadAnimationTyped_RW` (from SDL_image 2.6) which loads a typed image from a data buffer into a 661 + `dsdl2.image.Animation` 662 + 663 + Params: 664 + data = data buffer of the animation 665 + type = specified type of the animation 666 + Returns: `dsdl2.image.Animation` of the loaded animated image 667 + Throws: `dsdl2.SDLException` if failed to load the animation 668 +/ 669 Animation loadAnimationTypedRaw(const void[] data, string type) @trusted 670 in { 671 assert(dsdl2.image.getVersion() >= Version(2, 6)); 672 } 673 do { 674 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 675 if (sdlRWops is null) { 676 throw new SDLException; 677 } 678 679 if (IMG_Animation* imgAnimation = IMG_LoadAnimationTyped_RW(sdlRWops, 1, type.toStringz())) { 680 return new Animation(imgAnimation); 681 } 682 else { 683 throw new SDLException; 684 } 685 } 686 687 /++ 688 + Wraps `IMG_LoadGIFAnimation_RW` (from SDL_image 2.6) which loads a `dsdl2.image.Animation` from a buffer of 689 + animated GIF file format 690 + 691 + Params: 692 + data = data buffer of the animation 693 + Returns: `dsdl2.image.Animation` of the animated GIF 694 + Throws: `dsdl2.SDLException` if failed to load 695 +/ 696 Animation loadGIFAnimationRaw(const void[] data) @trusted 697 in { 698 assert(dsdl2.image.getVersion() >= Version(2, 6)); 699 } 700 do { 701 SDL_RWops* sdlRWops = SDL_RWFromConstMem(data.ptr, data.length.to!int); 702 if (sdlRWops is null) { 703 throw new SDLException; 704 } 705 scope (exit) 706 if (SDL_RWclose(sdlRWops) != 0) { 707 throw new SDLException; 708 } 709 710 if (IMG_Animation* imgAnimation = IMG_LoadGIFAnimation_RW(sdlRWops)) { 711 return new Animation(imgAnimation); 712 } 713 else { 714 throw new SDLException; 715 } 716 } 717 }