1 /* 2 Copyright (c) 2009-2013 Peter "Corsix" Cawley and Edvin "Lego3" Linge 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy of 5 this software and associated documentation files (the "Software"), to deal in 6 the Software without restriction, including without limitation the rights to 7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 of the Software, and to permit persons to whom the Software is furnished to do 9 so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 */ 22 23 #ifndef CORSIX_TH_TH_GFX_SDL_H_ 24 #define CORSIX_TH_TH_GFX_SDL_H_ 25 #include "config.h" 26 27 #include <SDL.h> 28 29 #include <stdexcept> 30 31 #include "persist_lua.h" 32 #include "th.h" 33 34 class cursor; 35 36 struct clip_rect : public SDL_Rect { 37 typedef Sint16 x_y_type; 38 typedef Uint16 w_h_type; 39 }; 40 41 struct render_target_creation_params; 42 43 enum class scaled_items; 44 45 //! 32bpp ARGB colour. See #palette::pack_argb 46 typedef uint32_t argb_colour; 47 48 //! 8bpp palette class. 49 class palette { 50 public: // External API 51 palette(); 52 53 //! Load palette from the supplied data. 54 /*! 55 Note that the data uses palette entries of 6 bit colours. 56 @param pData Data loaded from the file. 57 @param iDataLength Size of the data. 58 @return Whether loading of the palette succeeded. 59 */ 60 bool load_from_th_file(const uint8_t* pData, size_t iDataLength); 61 62 //! Set an entry of the palette. 63 /*! 64 The RGB colour (255, 0, 255) is used as the transparent colour. 65 @param iEntry Entry number to change. 66 @param iR Amount of red in the new entry. 67 @param iG Amount of green in the new entry. 68 @param iB Amount of blue in the new entry. 69 @return Setting the entry succeeded. 70 */ 71 bool set_entry(int iEntry, uint8_t iR, uint8_t iG, uint8_t iB); 72 73 public: // Internal (this rendering engine only) API 74 //! Convert A, R, G, B values to a 32bpp colour. 75 /*! 76 @param iA Amount of opacity (0-255). 77 @param iR Amount of red (0-255). 78 @param iG Amount of green (0-255). 79 @param iB Amount of blue (0-255). 80 @return 32bpp value representing the provided colour values. 81 */ pack_argb(uint8_t iA,uint8_t iR,uint8_t iG,uint8_t iB)82 static constexpr argb_colour pack_argb(uint8_t iA, uint8_t iR, uint8_t iG, 83 uint8_t iB) { 84 return (static_cast<argb_colour>(iR) << 0) | 85 (static_cast<argb_colour>(iG) << 8) | 86 (static_cast<argb_colour>(iB) << 16) | 87 (static_cast<argb_colour>(iA) << 24); 88 } 89 90 //! Get the red component of a colour. 91 /*! 92 @param iColour Colour to examine. 93 @return The red component intensity of the colour. 94 */ get_red(argb_colour iColour)95 static constexpr uint8_t get_red(argb_colour iColour) { 96 return static_cast<uint8_t>((iColour >> 0) & 0xFF); 97 } 98 99 //! Get the green component of a colour. 100 /*! 101 @param iColour Colour to examine. 102 @return The green component intensity of the colour. 103 */ get_green(argb_colour iColour)104 static constexpr uint8_t get_green(argb_colour iColour) { 105 return static_cast<uint8_t>((iColour >> 8) & 0xFF); 106 } 107 108 //! Get the blue component of a colour. 109 /*! 110 @param iColour Colour to examine. 111 @return The blue component intensity of the colour. 112 */ get_blue(argb_colour iColour)113 static constexpr uint8_t get_blue(argb_colour iColour) { 114 return static_cast<uint8_t>((iColour >> 16) & 0xFF); 115 } 116 117 //! Get the opacity component of a colour. 118 /*! 119 @param iColour Colour to examine. 120 @return The opacity of the colour. 121 */ get_alpha(argb_colour iColour)122 static constexpr uint8_t get_alpha(argb_colour iColour) { 123 return static_cast<uint8_t>((iColour >> 24) & 0xFF); 124 } 125 126 //! Get the number of colours in the palette. 127 /*! 128 @return The number of colours in the palette. 129 */ 130 int get_colour_count() const; 131 132 //! Get the internal palette data for fast (read-only) access. 133 /*! 134 @return Table with all 256 colours of the palette. 135 */ 136 const argb_colour* get_argb_data() const; 137 138 //! Set an entry of the palette. 139 /*! 140 @param iEntry Entry to modify. 141 @param iVal Palette value to set. 142 */ set_argb(int iEntry,uint32_t iVal)143 inline void set_argb(int iEntry, uint32_t iVal) { 144 colour_index_to_argb_map[iEntry] = iVal; 145 } 146 147 private: 148 //! 32bpp palette colours associated with the 8bpp colour index. 149 uint32_t colour_index_to_argb_map[256]; 150 151 //! Number of colours in the palette. 152 int colour_count; 153 }; 154 155 /*! 156 Utility class for decoding 32bpp images. 157 */ 158 class full_colour_renderer { 159 public: 160 //! Initialize the renderer for a specific render. 161 /*! 162 @param iWidth Pixel width of the resulting image 163 @param iHeight Pixel height of the resulting image 164 */ 165 full_colour_renderer(int iWidth, int iHeight); 166 virtual ~full_colour_renderer() = default; 167 168 //! Decode a 32bpp image, and push it to the storage backend. 169 /*! 170 @param pImg Encoded 32bpp image. 171 @param pPalette Palette of a legacy sprite. 172 @param iSpriteFlags Flags how to render the sprite. 173 @return Decoding was successful. 174 */ 175 void decode_image(const uint8_t* pImg, const ::palette* pPalette, 176 uint32_t iSpriteFlags); 177 178 private: 179 //! Store a decoded pixel. Use x and y if necessary. 180 /*! 181 @param pixel Pixel to store. 182 */ 183 virtual void store_argb(uint32_t pixel) = 0; 184 185 const int width; 186 const int height; 187 int x; 188 int y; 189 190 //! Push a pixel to the storage. 191 /*! 192 @param iValue Pixel value to store. 193 */ push_pixel(uint32_t iValue)194 inline void push_pixel(uint32_t iValue) { 195 if (y < height) { 196 store_argb(iValue); 197 x++; 198 if (x >= width) { 199 x = 0; 200 y++; 201 } 202 } else { 203 throw std::logic_error("Attempt to push_pixel past the end of the image"); 204 } 205 } 206 }; 207 208 class full_colour_storing : public full_colour_renderer { 209 public: 210 full_colour_storing(uint32_t* pDest, int iWidth, int iHeight); 211 212 private: 213 void store_argb(uint32_t pixel) override; 214 215 //! Pointer to the storage (not owned by this class). 216 uint32_t* destination; 217 }; 218 219 class wx_storing : public full_colour_renderer { 220 public: 221 wx_storing(uint8_t* pRGBData, uint8_t* pAData, int iWidth, int iHeight); 222 223 private: 224 void store_argb(uint32_t pixel) override; 225 226 //! Pointer to the RGB storage (not owned by this class). 227 uint8_t* rgb_data; 228 229 //! Pointer to the Alpha channel storage (not owned by this class). 230 uint8_t* alpha_data; 231 }; 232 233 class render_target { 234 public: // External API 235 render_target(); 236 ~render_target(); 237 238 //! Encode an RGB triplet for fillRect() map_colour(uint8_t iR,uint8_t iG,uint8_t iB)239 static constexpr uint32_t map_colour(uint8_t iR, uint8_t iG, uint8_t iB) { 240 return palette::pack_argb(0xFF, iR, iG, iB); 241 } 242 243 //! Initialise the render target 244 bool create(const render_target_creation_params* pParams); 245 246 //! Update the parameters for the render target 247 bool update(const render_target_creation_params* pParams); 248 249 //! Shut down the render target 250 void destroy(); 251 252 //! Get the reason for the last operation failing 253 const char* get_last_error(); 254 255 //! Begin rendering a new frame 256 bool start_frame(); 257 258 //! Finish rendering the current frame and present it 259 bool end_frame(); 260 261 //! Paint the entire render target black 262 bool fill_black(); 263 264 //! Sets a blue filter on the current surface. 265 // Used to add the blue effect when the game is paused. 266 void set_blue_filter_active(bool bActivate); 267 268 //! Fill a rectangle of the render target with a solid colour 269 bool fill_rect(uint32_t iColour, int iX, int iY, int iW, int iH); 270 271 //! Get the current clip rectangle 272 void get_clip_rect(clip_rect* pRect) const; 273 274 //! Get the width of the render target (in pixels) 275 int get_width() const; 276 277 //! Get the height of the render target (in pixels) 278 int get_height() const; 279 280 //! Set the new clip rectangle 281 void set_clip_rect(const clip_rect* pRect); 282 283 //! Enable optimisations for non-overlapping draws 284 void start_nonoverlapping_draws(); 285 286 //! Disable optimisations for non-overlapping draws 287 void finish_nonoverlapping_draws(); 288 289 //! Set the cursor to be used 290 void set_cursor(cursor* pCursor); 291 292 //! Update the cursor position (if the cursor is being simulated) 293 void set_cursor_position(int iX, int iY); 294 295 //! Take a screenshot and save it as a bitmap 296 bool take_screenshot(const char* sFile); 297 298 //! Set the amount by which future draw operations are scaled. 299 /*! 300 @param fScale New scale to use. 301 @param eWhatToScale Th kind of items to scale. 302 @return Whether the scale could be set. 303 */ 304 bool set_scale_factor(double fScale, scaled_items eWhatToScale); 305 306 //! Set the window caption 307 void set_caption(const char* sCaption); 308 309 //! Toggle mouse capture on the window. 310 void set_window_grab(bool bActivate); 311 312 //! Get any user-displayable information to describe the renderer path used 313 const char* get_renderer_details() const; 314 315 // If you add any extra methods here which are called from outside the 316 // rendering engine, then be sure to at least add dummy implementations 317 // to the other rendering engines. 318 319 public: // Internal (this rendering engine only) API get_renderer()320 SDL_Renderer* get_renderer() const { return renderer; } 321 322 //! Should bitmaps be scaled? 323 /*! 324 @param [out] pFactor If the function returns \c true, the factor to use 325 for scaling (can be \c nullptr if not interested in the value). 326 @return Whether bitmaps should be scaled. 327 */ 328 bool should_scale_bitmaps(double* pFactor); 329 330 SDL_Texture* create_palettized_texture(int iWidth, int iHeight, 331 const uint8_t* pPixels, 332 const ::palette* pPalette, 333 uint32_t iSpriteFlags) const; 334 SDL_Texture* create_texture(int iWidth, int iHeight, 335 const uint32_t* pPixels) const; 336 void draw(SDL_Texture* pTexture, const SDL_Rect* prcSrcRect, 337 const SDL_Rect* prcDstRect, int iFlags); 338 void draw_line(line* pLine, int iX, int iY); 339 340 private: 341 SDL_Window* window; 342 SDL_Renderer* renderer; 343 SDL_Texture* zoom_texture; 344 SDL_PixelFormat* pixel_format; 345 bool blue_filter_active; 346 cursor* game_cursor; 347 double bitmap_scale_factor; ///< Bitmap scale factor. 348 double global_scale_factor; ///< Global scale factor. 349 int width; 350 int height; 351 int cursor_x; 352 int cursor_y; 353 bool scale_bitmaps; ///< Whether bitmaps should be scaled. 354 bool supports_target_textures; 355 356 // In SDL2 < 2.0.4 there is an issue with the y coordinates used for 357 // ClipRects in opengl and opengles. 358 // see: https://bugzilla.libsdl.org/show_bug.cgi?id=2700 359 bool apply_opengl_clip_fix; 360 bool direct_zoom; 361 362 void flush_zoom_buffer(); 363 }; 364 365 //! Stored image. 366 class raw_bitmap { 367 public: 368 raw_bitmap(); 369 ~raw_bitmap(); 370 371 //! Set the palette of the image. 372 /*! 373 @param pPalette Palette to set for this image. 374 */ 375 void set_palette(const ::palette* pPalette); 376 377 //! Load the image from the supplied pixel data. 378 /*! 379 Loader uses the palette supplied before. 380 @param pPixelData Image data loaded from a TH file. 381 @param iPixelDataLength Size of the loaded image data. 382 @param iWidth Width of the image. 383 @param pEventualCanvas Canvas to render the image to (eventually). 384 @return Loading was a success. 385 */ 386 void load_from_th_file(const uint8_t* pPixelData, size_t iPixelDataLength, 387 int iWidth, render_target* pEventualCanvas); 388 389 //! Draw the image at a given position at the given canvas. 390 /*! 391 @param pCanvas Canvas to draw at. 392 @param iX Destination x position. 393 @param iY Destination y position. 394 */ 395 void draw(render_target* pCanvas, int iX, int iY); 396 397 //! Draw part of the image at a given position at the given canvas. 398 /*! 399 @param pCanvas Canvas to draw at. 400 @param iX Destination x position. 401 @param iY Destination y position. 402 @param iSrcX X position of the part to display. 403 @param iSrcY Y position of the part to display. 404 @param iWidth Width of the part to display. 405 @param iHeight Height of the part to display. 406 */ 407 void draw(render_target* pCanvas, int iX, int iY, int iSrcX, int iSrcY, 408 int iWidth, int iHeight); 409 410 private: 411 //! Image stored in SDL format for quick rendering. 412 SDL_Texture* texture; 413 414 //! Palette of the image. 415 const ::palette* bitmap_palette; 416 417 //! Target canvas. 418 render_target* target; 419 420 //! Width of the stored image. 421 int width; 422 423 //! Height of the stored image. 424 int height; 425 }; 426 427 //! Sheet of sprites. 428 class sprite_sheet { 429 public: // External API 430 sprite_sheet(); 431 ~sprite_sheet(); 432 433 //! Set the palette to use for the sprites in the sheet. 434 /*! 435 @param pPalette Palette to use for the sprites at the sheet. 436 */ 437 void set_palette(const ::palette* pPalette); 438 439 //! Load the sprites from the supplied data (using the palette supplied 440 //! earlier). 441 /*! 442 @param pTableData Start of table data with TH sprite information (see 443 th_sprite_properties). 444 @param iTableDataLength Length of the table data. 445 @param pChunkData Start of image data (chunks). 446 @param iChunkDataLength Length of the chunk data. 447 @param bComplexChunks Whether the supplied chunks are 'complex'. 448 @param pEventualCanvas Canvas to draw at. 449 @return Loading succeeded. 450 */ 451 bool load_from_th_file(const uint8_t* pTableData, size_t iTableDataLength, 452 const uint8_t* pChunkData, size_t iChunkDataLength, 453 bool bComplexChunks, render_target* pEventualCanvas); 454 455 //! Set the data of a sprite. 456 /*! 457 @param iSprite Number of the sprite to set. 458 @param pData Data of the sprite. 459 @param bTakeData Whether the data block may be taken (must be new[] 460 then). 461 @param iDataLength Length of the data. 462 @param iWidth Width of the sprite. 463 @param iHeight Height of the sprite. 464 @return Setting the sprite succeeded. 465 */ 466 bool set_sprite_data(size_t iSprite, const uint8_t* pData, bool bTakeData, 467 size_t iDataLength, int iWidth, int iHeight); 468 469 //! Supply a new mapped palette to a sprite. 470 /*! 471 @param iSprite Sprite getting the mapped palette. 472 @param pMap The palette map to apply. 473 @param iAlt32 What to do for a 32bpp sprite (#thdf_alt32_mask bits). 474 */ 475 void set_sprite_alt_palette_map(size_t iSprite, const uint8_t* pMap, 476 uint32_t iAlt32); 477 478 //! Get the number of sprites at the sheet. 479 /*! 480 @return The number of sprites available at the sheet. 481 */ 482 size_t get_sprite_count() const; 483 484 //! Set the number of sprites in the sheet. 485 /*! 486 @param iCount The desired number of sprites. 487 @param pCanvas Canvas to draw at. 488 @return Whether the number of sprites could be allocated. 489 */ 490 bool set_sprite_count(size_t iCount, render_target* pCanvas); 491 492 //! Get size of a sprite. 493 /*! 494 @param iSprite Sprite to get info from. 495 @param pWidth [out] If not nullptr, the sprite width is stored in the 496 destination. 497 @param pHeight [out] If not nullptr, the sprite height is stored in the 498 destination. 499 @return Size could be provided for the sprite. 500 */ 501 bool get_sprite_size(size_t iSprite, int* pWidth, int* pHeight) const; 502 503 //! Get size of a sprite, assuming all input is correctly supplied. 504 /*! 505 @param iSprite Sprite to get info from. 506 @param pWidth [out] The sprite width is stored in the destination. 507 @param pHeight [out] The sprite height is stored in the destination. 508 */ 509 void get_sprite_size_unchecked(size_t iSprite, int* pWidth, 510 int* pHeight) const; 511 512 //! Get the best colour to represent the sprite. 513 /*! 514 @param iSprite Sprite number to analyze. 515 @param pColour [out] Resulting colour. 516 @return Best colour could be established. 517 */ 518 bool get_sprite_average_colour(size_t iSprite, argb_colour* pColour) const; 519 520 //! Draw a sprite onto the canvas. 521 /*! 522 @param pCanvas Canvas to draw on. 523 @param iSprite Sprite to draw. 524 @param iX X position to draw the sprite. 525 @param iY Y position to draw the sprite. 526 @param iFlags Flags to apply for drawing. 527 */ 528 void draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, 529 uint32_t iFlags); 530 531 //! Test whether a sprite was hit. 532 /*! 533 @param iSprite Sprite being tested. 534 @param iX X position of the point to test relative to the origin of the 535 sprite. 536 @param iY Y position of the point to test relative to the origin of the 537 sprite. 538 @param iFlags Draw flags to apply to the sprite before testing. 539 @return Whether the sprite covers the give point. 540 */ 541 bool hit_test_sprite(size_t iSprite, int iX, int iY, uint32_t iFlags) const; 542 543 public: // Internal (this rendering engine only) API 544 //! Draw a sprite into wxImage data arrays (for the Map Editor) 545 /*! 546 @param iSprite Sprite number to draw. 547 @param pRGBData Output RGB data array. 548 @param pAData Output Alpha channel array. 549 */ 550 void wx_draw_sprite(size_t iSprite, uint8_t* pRGBData, uint8_t* pAData); 551 552 private: 553 friend class cursor; 554 #if CORSIX_TH_USE_PACK_PRAGMAS 555 #pragma pack(push) 556 #pragma pack(1) 557 #endif 558 //! Sprite structure in the table file. 559 struct th_sprite_properties { 560 //! Position of the sprite in the chunk data file. 561 uint32_t position; 562 563 //! Width of the sprite. 564 uint8_t width; 565 566 //! Height of the sprite. 567 uint8_t height; 568 } CORSIX_TH_PACKED_FLAGS; 569 #if CORSIX_TH_USE_PACK_PRAGMAS 570 #pragma pack(pop) 571 #endif 572 573 //! Sprites of the sheet. 574 struct sprite { 575 //! SDL structure containing the sprite with original palette. 576 SDL_Texture* texture; 577 578 //! SDL structure containing the sprite with alternative palette. 579 SDL_Texture* alt_texture; 580 581 //! Data of the sprite. 582 const uint8_t* data; 583 584 //! Alternative palette (if available). 585 const uint8_t* alt_palette_map; 586 587 //! Flags how to render the sprite, contains #THDF_Alt32_Mask bits. 588 uint32_t sprite_flags; 589 590 //! Width of the sprite. 591 int width; 592 593 //! Height of the sprite. 594 int height; 595 } * sprites; 596 597 //! Original palette. 598 const ::palette* palette; 599 600 //! Target to render to. 601 render_target* target; 602 603 //! Number of sprites in the sprite sheet. 604 size_t sprite_count; 605 606 //! Free memory of a single sprite. 607 /*! 608 @param iNumber Number of the sprite to clear. 609 */ 610 void _freeSingleSprite(size_t iNumber); 611 612 //! Free the memory used by the sprites. Also releases the SDL bitmaps. 613 void _freeSprites(); 614 615 //! Construct an alternative version (with its alternative palette map) of 616 //! the sprite. 617 /*! 618 @param pSprite Sprite to change. 619 @return SDL texture containing the sprite. 620 */ 621 SDL_Texture* _makeAltBitmap(sprite* pSprite); 622 }; 623 624 class cursor { 625 public: 626 cursor(); 627 ~cursor(); 628 629 bool create_from_sprite(sprite_sheet* pSheet, size_t iSprite, 630 int iHotspotX = 0, int iHotspotY = 0); 631 632 void use(render_target* pTarget); 633 634 static bool set_position(render_target* pTarget, int iX, int iY); 635 636 void draw(render_target* pCanvas, int iX, int iY); 637 638 private: 639 SDL_Surface* bitmap; 640 SDL_Cursor* hidden_cursor; 641 int hotspot_x; 642 int hotspot_y; 643 }; 644 645 class line { 646 public: 647 line(); 648 ~line(); 649 650 void move_to(double fX, double fY); 651 652 void line_to(double fX, double fY); 653 654 void set_width(double lineWidth); 655 656 void draw(render_target* pCanvas, int iX, int iY); 657 658 void set_colour(uint8_t iR, uint8_t iG, uint8_t iB, uint8_t iA = 255); 659 660 void persist(lua_persist_writer* pWriter) const; 661 void depersist(lua_persist_reader* pReader); 662 663 private: 664 friend class render_target; 665 void initialize(); 666 667 enum class line_operation_type : uint32_t { move = 0, line = 1 }; 668 669 class line_operation : public link_list { 670 public: 671 line_operation_type type; 672 double x, y; line_operation(line_operation_type type,double x,double y)673 line_operation(line_operation_type type, double x, double y) 674 : type(type), x(x), y(y) { 675 next = nullptr; 676 } 677 }; 678 679 line_operation* first_operation; 680 line_operation* current_operation; 681 double width; 682 uint8_t red, green, blue, alpha; 683 }; 684 685 #endif // CORSIX_TH_TH_GFX_SDL_H_ 686