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