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.surface; 8 @safe: 9 10 import bindbc.sdl; 11 import dsdl2.sdl; 12 import dsdl2.blend; 13 import dsdl2.pixels; 14 import dsdl2.rect; 15 16 import core.memory : GC; 17 import std.conv : to; 18 import std.format : format; 19 import std.typecons : Nullable, nullable; 20 21 /++ 22 + D class that wraps `SDL_Surface` storing a 2D image in the RAM 23 + 24 + `dsdl2.Surface` stores a 2D image out of pixels with a `width` and `height`, where each pixel stored in the 25 + RAM according to its defined `dsdl2.PixelFormat`. 26 +/ 27 final class Surface { 28 private PixelFormat pixelFormatProxy = null; 29 private bool isOwner = true; 30 private void* userRef = null; 31 32 @system SDL_Surface* sdlSurface = null; /// Internal `SDL_Surface` pointer 33 34 /++ 35 + Constructs a `dsdl2.Surface` from a vanilla `SDL_Surface*` from bindbc-sdl 36 + 37 + Params: 38 + sdlSurface = the `SDL_Surface` pointer to manage 39 + isOwner = whether the instance owns the given `SDL_Surface*` and should destroy it on its own 40 + userRef = optional pointer to maintain reference link, avoiding GC cleanup 41 +/ 42 this(SDL_Surface* sdlSurface, bool isOwner = true, void* userRef = null) @system 43 in { 44 assert(sdlSurface !is null); 45 } 46 do { 47 this.sdlSurface = sdlSurface; 48 this.isOwner = isOwner; 49 this.userRef = userRef; 50 } 51 52 /++ 53 + Constructs a blank RGB(A) `dsdl2.Surface` with a set width, height, and `dsdl2.PixelFormat` that wraps 54 + `SDL_CreateRGBSurface` 55 + 56 + Params: 57 + size = size (width and height) of the `dsdl2.Surface` in pixels 58 + rgbPixelFormat = an RGB(A) `dsdl2.PixelFormat` 59 + Throws: `dsdl2.SDLException` if allocation failed 60 +/ 61 this(uint[2] size, const PixelFormat rgbPixelFormat) @trusted 62 in { 63 assert(rgbPixelFormat !is null); 64 assert(!rgbPixelFormat.indexed); 65 } 66 do { 67 uint[4] masks = rgbPixelFormat.toMasks(); 68 69 this.sdlSurface = SDL_CreateRGBSurface(0, size[0].to!int, size[1].to!int, rgbPixelFormat.bitsPerPixel, 70 masks[0], masks[1], masks[2], masks[3]); 71 if (this.sdlSurface is null) { 72 throw new SDLException; 73 } 74 } 75 76 /++ 77 + Constructs an RGB(A) `dsdl2.Surface` from an array of `pixels` 78 + 79 + Params: 80 + pixels = array of pixel data (copied internally) 81 + size = size (width and height) of the `dsdl2.Surface` in pixels 82 + pitch = skips in bytes per line/row of the `dsdl2.Surface` 83 + rgbPixelFormat = an RGB(A) `dsdl2.PixelFormat` 84 + Throws: `dsdl2.SDLException` if allocation failed 85 +/ 86 this(const void[] pixels, uint[2] size, size_t pitch, const PixelFormat rgbPixelFormat) @trusted 87 in { 88 assert(pixels !is null); 89 assert(rgbPixelFormat !is null); 90 assert(!rgbPixelFormat.indexed); 91 assert(pitch * 8 >= size[0] * rgbPixelFormat.bitsPerPixel); 92 assert(pixels.length == pitch * size[1]); 93 } 94 do { 95 this(size, rgbPixelFormat); 96 97 size_t lineBitSize = cast(size_t) size[0] * rgbPixelFormat.bitsPerPixel; 98 size_t lineSize = lineBitSize / 8 + (lineBitSize % 8 != 0); 99 100 foreach (line; 0 .. size[1]) { 101 ubyte* srcLine = cast(ubyte*) pixels.ptr + line * pitch; 102 ubyte* destLine = cast(ubyte*) this.sdlSurface.pixels + line * this.pitch; 103 destLine[0 .. lineSize] = srcLine[0 .. lineSize]; 104 } 105 } 106 107 /++ 108 + Constructs a blank indexed palette-using `dsdl2.Surface` with a set width, height, and index bit depth, 109 + which wraps `SDL_CreateRGBSurface` 110 + 111 + Params: 112 + size = size (width and height) of the `dsdl2.Surface` in pixels 113 + bitDepth = bit depth of the palette index (1, 4, or 8) 114 + palette = `dsdl2.Palette` to use 115 + Throws: `dsdl2.SDLException` if allocation failed or palette-setting failed 116 +/ 117 this(uint[2] size, ubyte bitDepth, Palette palette) @trusted 118 in { 119 assert(bitDepth == 1 || bitDepth == 4 || bitDepth == 8); 120 assert(palette !is null); 121 } 122 do { 123 this.sdlSurface = SDL_CreateRGBSurface(0, size[0], size[1], bitDepth, 0, 0, 0, 0); 124 if (this.sdlSurface is null) { 125 throw new SDLException; 126 } 127 128 this.palette = palette; 129 } 130 131 /++ 132 + Constructs a blank indexed palette-using `dsdl2.Surface` from an array of `pixels` 133 + 134 + Params: 135 + pixels = array of pixel data (copied internally) 136 + size = size (width and height) of the `dsdl2.Surface` in pixels 137 + pitch = skips in bytes per line/row of the `dsdl2.Surface` 138 + bitDepth = bit depth of the palette index (1, 4, or 8) 139 + palette = `dsdl2.Palette` to use 140 + Throws: `dsdl2.SDLException` if allocation failed or palette-setting failed 141 +/ 142 this(const void[] pixels, uint[2] size, size_t pitch, ubyte bitDepth, Palette palette) @trusted 143 in { 144 assert(pixels !is null); 145 assert(size[0] > 0 && size[1] > 0); 146 assert(pitch * 8 >= size[0] * bitDepth); 147 assert(pixels.length == pitch * size[1]); 148 assert(palette !is null); 149 } 150 do { 151 this(size, bitDepth, palette); 152 153 size_t lineBitSize = size[0] * bitDepth; 154 size_t lineSize = lineBitSize / 8 + (lineBitSize % 8 != 0); 155 156 foreach (line; 0 .. size[1]) { 157 ubyte* srcLine = cast(ubyte*) pixels.ptr + line * pitch; 158 ubyte* destLine = cast(ubyte*) this.sdlSurface.pixels + line * this.pitch; 159 destLine[0 .. lineSize] = srcLine[0 .. lineSize]; 160 } 161 } 162 163 ~this() @trusted { 164 if (this.isOwner) { 165 SDL_FreeSurface(this.sdlSurface); 166 } 167 } 168 169 @trusted invariant { // @suppress(dscanner.trust_too_much) 170 // Instance might be in an invalid state due to holding a non-owned externally-freed object when 171 // destructed in an unpredictable order. 172 if (!this.isOwner && GC.inFinalizer) { 173 return; 174 } 175 176 assert(this.sdlSurface !is null); 177 } 178 179 /++ 180 + Equality operator overload 181 +/ 182 bool opEquals(const Surface rhs) const @trusted { 183 return this.sdlSurface is rhs.sdlSurface; 184 } 185 186 /++ 187 + Gets the hash of the `dsdl2.Surface` 188 + 189 + Returns: unique hash for the instance being the pointer of the internal `SDL_Surface` pointer 190 +/ 191 override hash_t toHash() const @trusted { 192 return cast(hash_t) this.sdlSurface; 193 } 194 195 /++ 196 + Formats the `dsdl2.Surface` into its construction representation: 197 + `"dsdl2.PixelFormat([<bytes>], [<width>, <height>], <pitch>, <pixelFormat>)"` 198 + 199 + Returns: the formatted `string` 200 +/ 201 override string toString() const @trusted { 202 return "dsdl2.Surface(%s, %s, %d, %s)".format(this.buffer, this.size, this.pitch, 203 this.pixelFormat); 204 } 205 206 /++ 207 + Gets the `dsdl2.PixelFormat` of the `dsdl2.Surface` 208 + 209 + Returns: `const` proxy to the `dsdl2.PixelFormat` of the `dsdl2.Surface` 210 +/ 211 const(PixelFormat) pixelFormat() const @property @trusted { 212 if (this.pixelFormatProxy is null) { 213 (cast(Surface) this).pixelFormatProxy = 214 new PixelFormat(cast(SDL_PixelFormat*) this.sdlSurface.format, false, cast(void*) this); 215 } 216 217 // If the internal pixel format pointer happens to change, rewire the proxy. 218 if (this.pixelFormatProxy.sdlPixelFormat !is this.sdlSurface.format) { 219 (cast(Surface) this).pixelFormatProxy.sdlPixelFormat = 220 cast(SDL_PixelFormat*) this.sdlSurface.format; 221 } 222 223 return this.pixelFormatProxy; 224 } 225 226 /++ 227 + Gets the internal pixel buffer of the `dsdl2.Surface` 228 + 229 + This function is marked as `@system` due to the potential of referencing invalid memory. 230 + 231 + Returns: slice of the buffer 232 +/ 233 inout(void[]) buffer() inout @property @trusted { 234 return (cast(inout(void*)) this.sdlSurface.pixels)[0 .. this.pitch * this.height]; 235 } 236 237 /++ 238 + Gets the width of the `dsdl2.Surface` in pixels 239 + 240 + Returns: width of the `dsdl2.Surface` in pixels 241 +/ 242 uint width() const @property @trusted { 243 return this.sdlSurface.w; 244 } 245 246 /++ 247 + Gets the height of the `dsdl2.Surface` in pixels 248 + 249 + Returns: height of the `dsdl2.Surface` in pixels 250 +/ 251 uint height() const @property @trusted { 252 return this.sdlSurface.h; 253 } 254 255 /++ 256 + Gets the size of the `dsdl2.Surface` in pixels 257 + 258 + Returns: array of width and height of the `dsdl2.Surface` in pixels 259 +/ 260 uint[2] size() const @property @trusted { 261 return [this.sdlSurface.w, this.sdlSurface.h]; 262 } 263 264 /++ 265 + Gets the pitch of the `dsdl2.Surface` in bytes (multiple of bytes for each line/row) 266 + 267 + Returns: pitch of the `dsdl2.Surface` in bytes 268 +/ 269 size_t pitch() const @property @trusted { 270 return this.sdlSurface.pitch; 271 } 272 273 /++ 274 + Gets the used color palette of the `dsdl2.Surface` 275 + 276 + Returns: `dsdl2.Palette` instance of the `dsdl2.Surface` 277 +/ 278 inout(Palette) palette() inout @property @trusted 279 in { 280 assert(this.pixelFormat.indexed); 281 } 282 do { 283 return (cast(inout PixelFormat) this.pixelFormat).palette; 284 } 285 286 /++ 287 + Sets the color palette of the `dsdl2.Surface` 288 + 289 + Params: 290 + newPalette = new `dsdl2.Palette` instance to use for the `dsdl2.Surface` 291 +/ 292 void palette(Palette newPalette) @property @trusted 293 in { 294 assert(this.pixelFormat.indexed); 295 } 296 do { 297 if (SDL_SetSurfacePalette(this.sdlSurface, newPalette.sdlPalette) != 0) { 298 throw new SDLException; 299 } 300 301 (cast(PixelFormat) this.pixelFormat).palette = newPalette; 302 } 303 304 /++ 305 + Gets the pixel value in the `dsdl2.Surface` at the given coordinate 306 + 307 + Params: 308 + xy = X and Y values of the coordinate 309 + Returns: the pixel value 310 +/ 311 uint getPixelAt(uint[2] xy) const @trusted 312 in { 313 assert(xy[0] < this.width); 314 assert(xy[1] < this.height); 315 } 316 do { 317 const ubyte* row = cast(ubyte*) this.sdlSurface.pixels + xy[1] * this.pitch; 318 319 if (this.pixelFormat.bitsPerPixel >= 8) { 320 const ubyte* pixelPtr = row + xy[0] * this.pixelFormat.bytesPerPixel; 321 align(4) ubyte[4] pixel; 322 pixel[0 .. this.pixelFormat.bytesPerPixel] = 323 pixelPtr[0 .. this.pixelFormat.bytesPerPixel]; 324 325 return *cast(uint*) pixel.ptr; 326 } 327 else { 328 ubyte pixelByte = *(row + (xy[0] * this.pixelFormat.bitsPerPixel) / 8); 329 ubyte bitOffset = (xy[0] * this.pixelFormat.bitsPerPixel) % 8; 330 331 switch (this.pixelFormat.sdlPixelFormatEnum) { 332 case SDL_PIXELFORMAT_INDEX1LSB: 333 return (pixelByte & (0b00000001 << bitOffset)) != 0; 334 335 case SDL_PIXELFORMAT_INDEX1MSB: 336 return (pixelByte & (0b10000000 >> bitOffset)) != 0; 337 338 case SDL_PIXELFORMAT_INDEX4LSB: 339 return pixelByte & (0b00001111 << (bitOffset * 4)) >> (bitOffset * 4); 340 341 case SDL_PIXELFORMAT_INDEX4MSB: 342 return pixelByte & (0b11110000 >> (bitOffset * 4)) >> (4 - bitOffset * 4); 343 344 default: 345 assert(false); 346 } 347 } 348 } 349 350 /++ 351 + Sets the pixel value in the `dsdl2.Surface` at the given coordinate 352 + 353 + Params: 354 + xy = X and Y values of the coordinate 355 + value = pixel value to set at the given coordinate 356 +/ 357 void setPixelAt(uint[2] xy, uint value) @trusted 358 in { 359 assert(xy[0] < this.width); 360 assert(xy[1] < this.height); 361 if (this.pixelFormat.bitsPerPixel != 32) { // Overflow protection 362 assert(value < 1 << this.pixelFormat.bitsPerPixel); 363 } 364 } 365 do { 366 ubyte* row = cast(ubyte*) this.sdlSurface.pixels + xy[1] * this.pitch; 367 368 if (this.pixelFormat.bitsPerPixel >= 8) { 369 ubyte* pixelPtr = row + xy[0] * this.pixelFormat.bytesPerPixel; 370 pixelPtr[0 .. this.pixelFormat.bytesPerPixel] = 371 (*cast(ubyte[4]*)&value)[0 .. this.pixelFormat.bytesPerPixel]; 372 } 373 else { 374 ubyte* pixelPtr = row + (xy[0] * this.pixelFormat.bitsPerPixel) / 8; 375 ubyte bitOffset = (xy[0] * this.pixelFormat.bitsPerPixel) % 8; 376 377 switch (this.pixelFormat.sdlPixelFormatEnum) { 378 case SDL_PIXELFORMAT_INDEX1LSB: 379 *pixelPtr &= !(0b00000001 << bitOffset); 380 *pixelPtr |= value << bitOffset; 381 break; 382 383 case SDL_PIXELFORMAT_INDEX1MSB: 384 *pixelPtr &= !(0b10000000 >> bitOffset); 385 *pixelPtr |= value << (7 - bitOffset); 386 break; 387 388 case SDL_PIXELFORMAT_INDEX4LSB: 389 *pixelPtr &= !(0b00001111 << (bitOffset * 4)); 390 *pixelPtr |= value << (bitOffset * 4); 391 break; 392 393 case SDL_PIXELFORMAT_INDEX4MSB: 394 *pixelPtr &= !(0b11110000 >> (bitOffset * 4)); 395 *pixelPtr |= value << (4 - bitOffset * 4); 396 break; 397 398 default: 399 assert(false); 400 } 401 } 402 } 403 404 /++ 405 + Gets the pixel color in the `dsdl2.Surface` at the given coordinate 406 + 407 + Params: 408 + xy = X and Y values of the coordinate 409 + Returns: the pixel color 410 +/ 411 Color getAt(uint[2] xy) const 412 in { 413 assert(xy[0] < this.width); 414 assert(xy[1] < this.height); 415 } 416 do { 417 if (this.pixelFormat.hasAlpha) { 418 return this.pixelFormat.getRGBA(this.getPixelAt(xy)); 419 } 420 else { 421 return this.pixelFormat.getRGB(this.getPixelAt(xy)); 422 } 423 } 424 425 /++ 426 + Sets the pixel color in the `dsdl2.Surface` at the given coordinate 427 + 428 + Params: 429 + xy = X and Y values of the coordinate 430 + color = pixel color to set at the given coordinate 431 +/ 432 void setAt(uint[2] xy, Color color) 433 in { 434 assert(xy[0] < this.width); 435 assert(xy[1] < this.height); 436 } 437 do { 438 if (this.pixelFormat.hasAlpha) { 439 this.setPixelAt(xy, this.pixelFormat.mapRGBA(color)); 440 } 441 else { 442 this.setPixelAt(xy, this.pixelFormat.mapRGB(color)); 443 } 444 } 445 446 /++ 447 + Gets the color and alpha multipliers of the `dsdl2.Surface` that wraps `SDL_GetSurfaceColorMod` and 448 + `SDL_GetSurfaceAlphaMod` 449 + 450 + Returns: color and alpha multipliers of the `dsdl2.Surface` 451 +/ 452 Color mod() const @property @trusted { 453 Color multipliers = void; 454 SDL_GetSurfaceColorMod(cast(SDL_Surface*) this.sdlSurface, &multipliers.sdlColor.r, 455 &multipliers.sdlColor.g, &multipliers.sdlColor.b); 456 SDL_GetSurfaceAlphaMod(cast(SDL_Surface*) this.sdlSurface, &multipliers.sdlColor.a); 457 return multipliers; 458 } 459 460 /++ 461 + Sets the color and alpha multipliers of the `dsdl2.Surface` that wraps `SDL_SetSurfaceColorMod` and 462 + `SDL_SetSurfaceAlphaMod` 463 + 464 + Params: 465 + newMod = `dsdl2.Color` with `.r`, `.g`, `.b` as the color multipliers, and `.a` as the alpha multiplier 466 +/ 467 void mod(Color newMod) @property @trusted { 468 SDL_SetSurfaceColorMod(this.sdlSurface, newMod.r, newMod.g, newMod.b); 469 SDL_SetSurfaceAlphaMod(this.sdlSurface, newMod.a); 470 } 471 472 /++ 473 + Wraps `SDL_GetSurfaceColorMod` which gets the color multipliers of the `dsdl2.Surface` 474 + 475 + Returns: color multipliers of the `dsdl2.Surface` 476 +/ 477 ubyte[3] colorMod() const @property @trusted { 478 ubyte[3] rgbMod = void; 479 SDL_GetSurfaceColorMod(cast(SDL_Surface*) this.sdlSurface, &rgbMod[0], &rgbMod[1], &rgbMod[2]); 480 return rgbMod; 481 } 482 483 /++ 484 + Wraps `SDL_SetSurfaceColorMod` which sets the color multipliers of the `dsdl2.Surface` 485 + 486 + Params: 487 + newColorMod = an array of `ubyte`s representing red, green, and blue multipliers (each 0-255) 488 +/ 489 void colorMod(ubyte[3] newColorMod) @property @trusted { 490 SDL_SetSurfaceColorMod(this.sdlSurface, newColorMod[0], newColorMod[1], newColorMod[2]); 491 } 492 493 /++ 494 + Wraps `SDL_GetSurfaceAlphaMod` which gets the alpha multiplier of the `dsdl2.Surface` 495 + 496 + Returns: alpha multiplier of the `dsdl2.Surface` 497 +/ 498 ubyte alphaMod() const @property @trusted { 499 ubyte aMod = void; 500 SDL_GetSurfaceAlphaMod(cast(SDL_Surface*) this.sdlSurface, &aMod); 501 return aMod; 502 } 503 504 /++ 505 + Wraps `SDL_SetSurfaceAlphaMod` which sets the alpha multiplier of the `dsdl2.Surface` 506 + 507 + Params: 508 + newAlphaMod = alpha multiplier (0-255) 509 +/ 510 void alphaMod(ubyte newAlphaMod) @property @trusted { 511 SDL_SetSurfaceAlphaMod(this.sdlSurface, newAlphaMod); 512 } 513 514 /++ 515 + Wraps `SDL_GetClipRect` which gets the clipping `dsdl2.Rect` of the `dsdl2.Surface` 516 + 517 + Returns: clipping `dsdl2.Rect` of the `dsdl2.Surface` 518 +/ 519 Rect clipRect() const @property @trusted { 520 Rect rect = void; 521 SDL_GetClipRect(cast(SDL_Surface*) this.sdlSurface, &rect.sdlRect); 522 return rect; 523 } 524 525 /++ 526 + Wraps `SDL_SetClipRect` which sets the clipping `dsdl2.Rect` of the `dsdl2.Surface` 527 + 528 + Params: 529 + newRect = `dsdl2.Rect` to set as the clipping rectangle 530 +/ 531 void clipRect(Rect newRect) @property @trusted { 532 SDL_SetClipRect(this.sdlSurface, &newRect.sdlRect); 533 } 534 535 /++ 536 + Acts as `SDL_SetClipRect(surface, NULL)` which removes the clipping `dsdl2.Rect` of the 537 + `dsdl2.Surface` 538 +/ 539 void clipRect(typeof(null) _) @property @trusted { 540 SDL_SetClipRect(this.sdlSurface, null); 541 } 542 543 /++ 544 + Wraps `SDL_SetClipRect` which sets or removes the clipping `dsdl2.Rect` of the `dsdl2.Surface` 545 + 546 + Params: 547 + newRect = `dsdl2.Rect` to set as the clipping rectangle; `null` to remove the clipping rectangle 548 +/ 549 void clipRect(Nullable!Rect newRect) @property @trusted { 550 if (newRect.isNull) { 551 this.clipRect = null; 552 } 553 else { 554 this.clipRect = newRect.get; 555 } 556 } 557 558 /++ 559 + Wraps `SDL_GetColorKey` which gets the color key used by the `dsdl2.Surface` for transparency 560 + 561 + Returns: transparency color key in `dsdl2.Color` 562 + Throws: `dsdl2.SDLException` if color key unable to be fetched 563 +/ 564 Color colorKey() const @property @trusted { 565 uint key = void; 566 if (SDL_GetColorKey(cast(SDL_Surface*) this.sdlSurface, &key) != 0) { 567 throw new SDLException; 568 } 569 570 if (this.pixelFormat.hasAlpha) { 571 return this.pixelFormat.getRGBA(key); 572 } 573 else { 574 return this.pixelFormat.getRGB(key); 575 } 576 } 577 578 /++ 579 + Wraps `SDL_SetColorKey` which sets the color key used for the `dsdl2.Surface` making pixels of the same 580 + color transparent 581 + 582 + Params: 583 + newPixelKey = pixel value of the color key that get transparency 584 + Throws: `dsdl2.SDLException` if color key unable to set 585 +/ 586 void colorKey(uint newPixelKey) @property @trusted { 587 if (SDL_SetColorKey(this.sdlSurface, SDL_TRUE, newPixelKey) != 0) { 588 throw new SDLException; 589 } 590 } 591 592 /++ 593 + Wraps `SDL_SetColorKey` which sets the color key used for the `dsdl2.Surface` making pixels of the same 594 + color transparent 595 + 596 + Params: 597 + newColorKey = `dsdl2.Color` specifying pixels that get transparency 598 + Throws: `dsdl2.SDLException` if color key unable to set 599 +/ 600 void colorKey(Color newColorKey) @property @trusted { 601 if (this.pixelFormat.hasAlpha) { 602 this.colorKey = this.pixelFormat.mapRGBA(newColorKey); 603 } 604 else { 605 this.colorKey = this.pixelFormat.mapRGB(newColorKey); 606 } 607 } 608 609 /++ 610 + Acts as `SDL_SetColorKey(surface, NULL)` which disables color-keying 611 +/ 612 void colorKey(typeof(null) _) @property @trusted { 613 if (SDL_SetColorKey(this.sdlSurface, SDL_FALSE, 0) != 0) { 614 throw new SDLException; 615 } 616 } 617 618 /++ 619 + Wraps `SDL_SetColorKey` which sets or removes the color key used for the `dsdl2.Surface` making pixels 620 + of the same color transparent 621 + 622 + Params: 623 + newColorKey = `dsdl2.Color` specifying pixels that get transparency; `null` to remove the color key 624 + Throws: `dsdl2.SDLException` if color key unable to set or removed 625 +/ 626 void colorKey(Nullable!Color newColorKey) @property @trusted { 627 if (newColorKey.isNull) { 628 this.colorKey = null; 629 } 630 else { 631 this.colorKey = newColorKey.get; 632 } 633 } 634 635 static if (sdlSupport >= SDLSupport.v2_0_9) { 636 /++ 637 + Wraps `SDL_HasColorKey` (from SDL 2.0.9) which checks whether the `dsdl2.Surface` has a color key 638 + for transparency 639 + 640 + Returns: `true` if it does, otherwise `false` 641 +/ 642 bool hasColorKey() const @property @trusted 643 in { 644 assert(getVersion() >= Version(2, 0, 9)); 645 } 646 do { 647 return SDL_HasColorKey(cast(SDL_Surface*) this.sdlSurface) == SDL_TRUE; 648 } 649 } 650 651 /++ 652 + Wraps `SDL_GetSurfaceBlendMode` which gets the `dsdl2.Surface`'s `dsdl2.BlendMode` defining blitting 653 + 654 + Returns: `dsdl2.BlendMode` of the `dsdl2.Surface` 655 + Throws: `dsdl2.SDLException` if `dsdl2.BlendMode` unable to get 656 +/ 657 BlendMode blendMode() const @property @trusted { 658 BlendMode mode = void; 659 if (SDL_GetSurfaceBlendMode(cast(SDL_Surface*) this.sdlSurface, &mode.sdlBlendMode) != 0) { 660 throw new SDLException; 661 } 662 663 return mode; 664 } 665 666 /++ 667 + Wraps `SDL_SetSurfaceBlendMode` which sets the `dsdl2.Surface`'s `dsdl2.BlendMode` defining blitting 668 + 669 + Params: 670 + newMode = `dsdl2.BlendMode` to set 671 + Throws: `dsdl2.SDLException` if `dsdl2.BlendMode` unable to set 672 +/ 673 void blendMode(BlendMode newMode) @property @trusted { 674 if (SDL_SetSurfaceBlendMode(this.sdlSurface, newMode.sdlBlendMode) != 0) { 675 throw new SDLException; 676 } 677 } 678 679 /++ 680 + Wraps `SDL_ConvertPixels` which converts the `dsdl2.Surface` from its RGB(A) `dsdl2.PixelFormat` to another 681 + `dsdl2.Surface` with a different RGB(A) `dsdl2.PixelFormat` 682 + 683 + Params: 684 + rgbPixelFormat = the RGB(A) `dsdl2.PixelFormat` to target the conversion 685 + Returns: result `dsdl2.Surface` with the `targetPixelFormat` 686 + Throws: `dsdl2.SDLException` if pixels failed to convert 687 +/ 688 Surface convert(const PixelFormat rgbPixelFormat) const @trusted 689 in { 690 assert(!this.pixelFormat.indexed); 691 assert(rgbPixelFormat !is null); 692 } 693 do { 694 auto target = new Surface([this.width, this.height], rgbPixelFormat); 695 if (SDL_ConvertPixels(this.width, this.height, this.pixelFormat.sdlPixelFormatEnum, 696 this.sdlSurface.pixels, this.pitch.to!int, target.pixelFormat.sdlPixelFormatEnum, 697 target.sdlSurface.pixels, target.pitch.to!int) != 0) { 698 throw new SDLException; 699 } 700 701 return target; 702 } 703 704 /++ 705 + Acts as `SDL_FillRect(surface, NULL)` which fills the entire `dsdl2.Surface` with a pixel value 706 + 707 + Params: 708 + pixel = pixel value of the color to fill the entire `dsdl2.Surface` 709 +/ 710 void fill(uint pixel) @trusted { 711 if (SDL_FillRect(this.sdlSurface, null, pixel) != 0) { 712 throw new SDLException; 713 } 714 } 715 716 /++ 717 + Acts as `SDL_FillRect(surface, NULL)` which fills the entire `dsdl2.Surface` with a `dsdl2.Color` value 718 + 719 + Params: 720 + color = `dsdl2.Color` of the color to fill the entire `dsdl2.Surface` 721 +/ 722 void fill(Color color) @trusted { 723 if (this.pixelFormat.hasAlpha) { 724 this.fill(this.pixelFormat.mapRGBA(color)); 725 } 726 else { 727 this.fill(this.pixelFormat.mapRGB(color)); 728 } 729 } 730 731 /++ 732 + Wraps `SDL_FillRect` which draws a filled rectangle in the `dsdl2.Surface` with specifying a pixel color value 733 + 734 + Params: 735 + rect = `dsdl2.Rect` specifying the position and size 736 + pixel = pixel value of the color to fill the rectangle 737 + Throws: `dsdl2.SDLException` if rectangle failed to draw 738 +/ 739 void fillRect(Rect rect, uint pixel) @trusted { 740 if (SDL_FillRect(this.sdlSurface, &rect.sdlRect, pixel) != 0) { 741 throw new SDLException; 742 } 743 } 744 745 /++ 746 + Wraps `SDL_FillRect` which draws a filled rectangle in the `dsdl2.Surface` with specifying a `dsdl2.Color` value 747 + 748 + Params: 749 + rect = `dsdl2.Rect` specifying the position and size 750 + color = `dsdl2.Color` of the color to fill the rectangle 751 + Throws: `dsdl2.SDLException` if rectangle failed to draw 752 +/ 753 void fillRect(Rect rect, Color color) @trusted { 754 if (this.pixelFormat.hasAlpha) { 755 this.fillRect(rect, this.pixelFormat.mapRGBA(color)); 756 } 757 else { 758 this.fillRect(rect, this.pixelFormat.mapRGB(color)); 759 } 760 } 761 762 /++ 763 + Wraps `SDL_FillRects` which draws multiple filled rectangles in the `dsdl2.Surface` with specifying a pixel 764 + color value 765 + 766 + Params: 767 + rects = an array of `dsdl2.Rect`s specifying the drawn rectangles' positions and sizes 768 + pixel = pixel value of the color to fill the rectangles 769 + Throws: `dsdl2.SDLException` if the rectangles failed to draw 770 +/ 771 void fillRects(const Rect[] rects, uint pixel) @trusted { 772 if (SDL_FillRects(this.sdlSurface, cast(SDL_Rect*) rects.ptr, rects.length.to!int, pixel) != 0) { 773 throw new SDLException; 774 } 775 } 776 777 /++ 778 + Wraps `SDL_FillRects` which draws multiple filled rectangles in the `dsdl2.Surface` with specifying a 779 + `dsdl2.Color` value 780 + 781 + Params: 782 + rects = an array of `dsdl2.Rect`s specifying the drawn rectangles' positions and sizes 783 + color = `dsdl2.Color` of the color to fill the rectangles 784 + Throws: `dsdl2.SDLException` if the rectangles failed to draw 785 +/ 786 void fillRects(const Rect[] rects, Color color) @trusted { 787 if (this.pixelFormat.hasAlpha) { 788 this.fillRects(rects, this.pixelFormat.mapRGBA(color)); 789 } 790 else { 791 this.fillRects(rects, this.pixelFormat.mapRGB(color)); 792 } 793 } 794 795 /++ 796 + Wraps `SDL_BlitSurface` which blits/draws a `dsdl2.Surface` on top of the `dsdl2.Surface` at a specific 797 + point as the top-left point of the drawn `dsdl2.Surface` without any scaling done 798 + 799 + Params: 800 + source = `dsdl2.Surface` to blit/draw 801 + destPoint = top-left `dsdl2.Point` of where `source` is drawn 802 +/ 803 void blit(const Surface source, Point destPoint) @trusted { 804 SDL_Rect dstrect = SDL_Rect(destPoint.x, destPoint.y, 0, 0); 805 if (SDL_BlitSurface(cast(SDL_Surface*) source.sdlSurface, null, this.sdlSurface, &dstrect) != 0) { 806 throw new SDLException; 807 } 808 } 809 810 /++ 811 + Wraps `SDL_BlitSurface` which blits/draws a `dsdl2.Surface` on top of the `dsdl2.Surface` at a specific 812 + point as the top-left point of the drawn `dsdl2.Surface` without any scaling done 813 + 814 + Params: 815 + source = `dsdl2.Surface` to blit/draw 816 + destPoint = top-left `dsdl2.Point` of where `source` is drawn 817 + srcRect = the clipping rect of `source` specifying which part is drawn 818 +/ 819 void blit(const Surface source, Point destPoint, Rect srcRect) @trusted { 820 SDL_Rect dstrect = SDL_Rect(destPoint.x, destPoint.y, 0, 0); 821 if (SDL_BlitSurface(cast(SDL_Surface*) source.sdlSurface, &srcRect.sdlRect, this.sdlSurface, 822 &dstrect) != 0) { 823 throw new SDLException; 824 } 825 } 826 827 /++ 828 + Wraps `SDL_BlitScaled` which blits/draws a `dsdl2.Surface` on top of the `dsdl2.Surface` at a specific 829 + point as the top-left point of the drawn `dsdl2.Surface` with scaling 830 + 831 + Params: 832 + source = `dsdl2.Surface` to blit/draw 833 + destRect = `dsdl2.Rect` of where `source` should be drawn (squeezes/stretches to the dimensions as well) 834 +/ 835 void blitScaled(const Surface source, Rect destRect) @trusted { 836 if (SDL_BlitScaled(cast(SDL_Surface*) source.sdlSurface, null, this.sdlSurface, 837 &destRect.sdlRect) != 0) { 838 throw new SDLException; 839 } 840 } 841 842 /++ 843 + Wraps `SDL_BlitScaled` which blits/draws a `dsdl2.Surface` on top of the `dsdl2.Surface` at a specific 844 + point as the top-left point of the drawn `dsdl2.Surface` with scaling 845 + 846 + Params: 847 + source = `dsdl2.Surface` to blit/draw 848 + destRect = `dsdl2.Rect` of where `source` should be drawn (squeezes/stretches to the dimensions as well) 849 + srcRect = the clipping rect of `source` specifying which part is drawn 850 +/ 851 void blitScaled(const Surface source, Rect destRect, Rect srcRect) @trusted { 852 if (SDL_BlitSurface(cast(SDL_Surface*) source.sdlSurface, &srcRect.sdlRect, this.sdlSurface, 853 &destRect.sdlRect) != 0) { 854 throw new SDLException; 855 } 856 } 857 } 858 /// 859 unittest { 860 auto surface = new dsdl2.Surface([100, 100], dsdl2.PixelFormat.rgba8888); 861 surface.fill(dsdl2.Color(24, 24, 24)); 862 surface.fillRect(dsdl2.Rect(25, 25, 50, 50), dsdl2.Color(42, 42, 42)); 863 864 assert(surface.getAt([0, 0]) == dsdl2.Color(24, 24, 24)); 865 assert(surface.getAt([50, 50]) == dsdl2.Color(42, 42, 42)); 866 }