ge211  2021.5.1
A student game engine
ge211_sprites.cxx
1 #include "ge211_sprites.hxx"
2 #include "ge211_error.hxx"
3 
4 #include <SDL.h>
5 #include <SDL_image.h>
6 #include <SDL_ttf.h>
7 
8 #include <cmath>
9 
10 namespace ge211 {
11 
12 using namespace detail;
13 using namespace internal;
14 
15 Sprite_set::Sprite_set() {}
16 
17 Sprite_set&
18 Sprite_set::add_sprite(const Sprite& sprite, Posn<int> xy, int z,
19  const Transform& t)
20 {
21  sprites_.emplace_back(sprite, xy, z, t);
22  return *this;
23 }
24 
25 namespace detail {
26 
27 Placed_sprite::Placed_sprite(const Sprite& sprite, Posn<int> xy,
28  int z, const Transform& transform) NOEXCEPT
29  : sprite{&sprite}, xy{xy}, z{z}, transform{transform}
30 { }
31 
32 void Placed_sprite::render(Renderer& dst) const
33 {
34  sprite->render(dst, xy, transform);
35 }
36 
37 bool operator<(const Placed_sprite& s1, const Placed_sprite& s2) NOEXCEPT
38 {
39  return s1.z > s2.z;
40 }
41 
42 Dims<int> Texture_sprite::dimensions() const
43 {
44  return get_texture_().dimensions();
45 }
46 
47 void Texture_sprite::render(Renderer& renderer,
48  Posn<int> position,
49  const Transform& transform) const
50 {
51  if (transform.is_identity())
52  renderer.copy(get_texture_(), position);
53  else
54  renderer.copy(get_texture_(), position, transform);
55 }
56 
57 void Texture_sprite::prepare(const Renderer& renderer) const
58 {
59  renderer.prepare(get_texture_());
60 }
61 
62 } // end namespace detail
63 
64 namespace internal {
65 
67  : texture_{create_surface_(dimensions)}
68 { }
69 
71 {
72  return raw_surface_("Render_sprite::raw_surface");
73 }
74 
76 {
77  auto* surface = raw_surface_("Render_sprite::fill_surface");
78  SDL_FillRect(surface, nullptr, color.to_sdl_(surface->format));
79 }
80 
82 {
83  fill_rectangle_(rect, color, "Render_sprite::fill_rectangle");
84 }
85 
87 {
88  fill_rectangle_({xy.x, xy.y, 1, 1}, color, "Render_sprite::set_pixel");
89 }
90 
91 const Texture& Render_sprite::get_texture_() const
92 {
93  return texture_;
94 }
95 
96 void Render_sprite::fill_rectangle_(Rect<int> rect, Color color, char const* who)
97 {
98  auto* surface = raw_surface_(who);
99  SDL_Rect rect_buf = rect;
100  SDL_FillRect(surface, &rect_buf, color.to_sdl_(surface->format));
101 }
102 
103 Borrowed<SDL_Surface> Render_sprite::raw_surface_(char const* who)
104 {
105  SDL_Surface* result = texture_.raw_surface();
106  if (result) return result;
107 
108  throw Late_paint_error{who};
109 }
110 
111 Uniq_SDL_Surface Render_sprite::create_surface_(Dims<int> dimensions)
112 {
113  SDL_Surface* surface =
114  SDL_CreateRGBSurfaceWithFormat(0,
117  32,
118  SDL_PIXELFORMAT_RGBA32);
119  if (!surface)
120  throw Host_error{"Could not create sprite surface"};
121 
122  return Uniq_SDL_Surface(surface);
123 }
124 
125 } // end namespace internal
126 
127 namespace sprites {
128 
129 static Dims<int> check_rectangle_dimensions(Dims<int> dims)
130 {
131  if (dims.width <= 0 || dims.height <= 0) {
132  throw Client_logic_error(
133  "Rectangle_sprite: width and height must both be positive");
134  }
135 
136  return dims;
137 }
138 
140  : Render_sprite{check_rectangle_dimensions(dims)}
141 {
142  fill_surface(color);
143 }
144 
146 {
147  *this = Rectangle_sprite{dimensions(), color};
148 }
149 
150 static Dims<int> compute_circle_dimensions(int radius)
151 {
152  if (radius <= 0) {
153  throw Client_logic_error("Circle_sprite: radius must be positive");
154  }
155 
156  return {radius * 2, radius * 2};
157 }
158 
160  : Render_sprite{compute_circle_dimensions(radius)}
161 {
162  const int cx = radius;
163  const int cy = radius;
164 
165  for (int y = 0; y < radius; ++y) {
166  for (int x = 0; x < radius; ++x) {
167  if (x * x + y * y < radius * radius) {
168  set_pixel({cx + x, cy + y}, color);
169  set_pixel({cx + x, cy - y - 1}, color);
170  set_pixel({cx - x - 1, cy + y}, color);
171  set_pixel({cx - x - 1, cy - y - 1}, color);
172  }
173  }
174  }
175 }
176 
178 {
179  *this = Circle_sprite{radius_(), color};
180 }
181 
182 int Circle_sprite::radius_() const
183 {
184  return dimensions().width >> 1;
185 }
186 
187 Texture
188 Image_sprite::load_texture_(const std::string& filename)
189 {
190  File_resource file(filename);
191  SDL_Surface* raw = IMG_Load_RW(file.get_raw(), 0);
192  if (raw) return Texture(raw);
193 
194  throw Image_error::could_not_load(filename);
195 }
196 
198  : texture_{load_texture_(filename)} {}
199 
200 const Texture& Image_sprite::get_texture_() const
201 {
202  return texture_;
203 }
204 
205 Texture
206 Text_sprite::create_texture(const Builder& config)
207 {
208  SDL_Surface* raw;
209 
210  std::string message = config.message();
211 
212  if (message.empty())
213  return Texture{};
214 
215  if (config.word_wrap() > 0) {
216  raw = TTF_RenderUTF8_Blended_Wrapped(
217  config.font().get_raw_(),
218  message.c_str(),
219  config.color().to_sdl_(),
220  static_cast<uint32_t>(config.word_wrap()));
221  } else {
222  auto render = config.antialias() ?
223  &TTF_RenderUTF8_Blended :
224  &TTF_RenderUTF8_Solid;
225  raw = render(config.font().get_raw_(),
226  message.c_str(),
227  config.color().to_sdl_());
228  }
229 
230  if (!raw)
231  throw Host_error{"Could not render text: “" + message + "”"};
232  else
233  return Texture{raw};
234 }
235 
236 Text_sprite::Text_sprite(const Text_sprite::Builder& config)
237  : texture_{create_texture(config)} {}
238 
240  : texture_{} {}
241 
243  const Font& font)
244  : Text_sprite{Builder{font}.message(message)} {}
245 
246 const Texture& Text_sprite::get_texture_() const
247 {
248  assert_initialized_();
249  return texture_;
250 }
251 
252 void Text_sprite::assert_initialized_() const
253 {
254  if (texture_.empty())
255  throw Client_logic_error{"Attempt to render empty Text_sprite"};
256 }
257 
259  : message_{}, font_{&font}, color_{Color::white()}, antialias_{true},
260  word_wrap_{0} {}
261 
263 {
264  message_.str(message);
265  return *this;
266 }
267 
269 {
270  font_ = &font;
271  return *this;
272 }
273 
275 {
276  color_ = color;
277  return *this;
278 }
279 
281 {
282  antialias_ = antialias;
283  return *this;
284 }
285 
287 {
288  if (word_wrap < 0) word_wrap = 0;
289  word_wrap_ = static_cast<uint32_t>(word_wrap);
290  return *this;
291 }
292 
294 {
295  return Text_sprite{*this};
296 }
297 
299 {
300  return message_.str();
301 }
302 
304 {
305  return *font_;
306 }
307 
309 {
310  return color_;
311 }
312 
314 {
315  return antialias_;
316 }
317 
319 {
320  return static_cast<int>(word_wrap_);
321 }
322 
324 {
325  texture_ = create_texture(config);
326 }
327 
328 bool Text_sprite::empty() const
329 {
330  return texture_.empty();
331 }
332 
333 Text_sprite::operator bool() const
334 {
335  return !empty();
336 }
337 
339 {
340  since_.reset();
341 }
342 
343 void Multiplexed_sprite::render(detail::Renderer& renderer,
344  Posn<int> position,
345  Transform const& transform) const
346 {
347  const Sprite& selection = select_(since_.elapsed_time());
348  selection.render(renderer, position, transform);
349 }
350 
351 } // end namespace sprites
352 
353 }
ge211::sprites::Image_sprite::Image_sprite
Image_sprite(std::string const &filename)
Constructs an image sprite, given the filename of the image to display.
Definition: ge211_sprites.cxx:197
ge211::sprites::Circle_sprite::recolor
void recolor(Color)
Changes the color of this circle sprite.
Definition: ge211_sprites.cxx:177
std::string
ge211::sprites::Circle_sprite
A Sprite that renders as a solid circle.
Definition: ge211_sprites.hxx:194
ge211::sprites::Text_sprite::Builder::antialias
bool antialias() const
Gets whether anti-aliasing will be used.
Definition: ge211_sprites.cxx:313
ge211::internal::Render_sprite::fill_surface
void fill_surface(Color)
Fills the whole surface with the given color.
Definition: ge211_sprites.cxx:75
ge211::sprites::Circle_sprite::Circle_sprite
Circle_sprite(int radius, Color=Color::white())
Constructs a circle sprite from its radius and optionally a Color, which defaults to white.
Definition: ge211_sprites.cxx:159
ge211::internal::Render_sprite::fill_rectangle
void fill_rectangle(Rect< int >, Color)
Fills the given rectangle in the given color.
Definition: ge211_sprites.cxx:81
ge211::internal::Render_sprite::Render_sprite
Render_sprite(Dims< int >)
Constructs a Render_sprite with the given pixel dimensions.
Definition: ge211_sprites.cxx:66
ge211::sprites::Text_sprite::Builder::word_wrap
int word_wrap() const
Gets the wrapping width that will be used.
Definition: ge211_sprites.cxx:318
ge211::internal::Render_sprite::raw_surface
Borrowed< SDL_Surface > raw_surface()
Gains access to the underlying SDL_Surface☛.
Definition: ge211_sprites.cxx:70
ge211::Color::white
static constexpr Color white()
Solid white.
Definition: ge211_color.hxx:58
ge211
The game engine namespace.
Definition: ge211.hxx:4
ge211::sprites::Text_sprite::empty
bool empty() const
Is this Text_sprite empty? (If so, you shouldn't try to use it.)
Definition: ge211_sprites.cxx:328
ge211::sprites::Multiplexed_sprite::reset
void reset()
Resets the age of the sprite to 0.
Definition: ge211_sprites.cxx:338
ge211::sprites::Rectangle_sprite::Rectangle_sprite
Rectangle_sprite(Dims< int >, Color=Color::white())
Constructs a rectangle sprite from required Dims and an optional Color, which defaults to white.
Definition: ge211_sprites.cxx:139
ge211::sprites::Text_sprite::Builder::font
Font const & font() const
Gets the font that will be used.
Definition: ge211_sprites.cxx:303
ge211::sprites::Sprite
A sprite is an image that knows how to render itself to the screen at a given location,...
Definition: ge211_sprites.hxx:39
ge211::sprites::Text_sprite::Text_sprite
Text_sprite()
Constructs an empty text sprite.
Definition: ge211_sprites.cxx:239
ge211::sprites::Rectangle_sprite::recolor
void recolor(Color)
Changes the color of this rectangle sprite.
Definition: ge211_sprites.cxx:145
ge211::geometry::Posn::x
Coordinate x
The x coordinate.
Definition: ge211_geometry.hxx:275
ge211::geometry::Dims< int >
ge211::sprites::Text_sprite::Builder::build
Text_sprite build() const
Builds the configured Text_sprite.
Definition: ge211_sprites.cxx:293
ge211::sprites::Sprite::dimensions
virtual Dims< int > dimensions() const =0
Returns the current dimensions of this Sprite.
std::string::c_str
T c_str(T... args)
ge211::geometry::Dims::height
Coordinate height
The height of the object.
Definition: ge211_geometry.hxx:41
ge211::exceptions::Client_logic_error
An exception that indicates that a logic error was performed by the client.
Definition: ge211_error.hxx:48
ge211::geometry::Posn::y
Coordinate y
The y coordiante.
Definition: ge211_geometry.hxx:276
ge211::sprites::Rectangle_sprite
A Sprite that renders as a solid rectangle.
Definition: ge211_sprites.hxx:179
std::transform
T transform(T... args)
ge211::sprites::Text_sprite
A Sprite that displays text.
Definition: ge211_sprites.hxx:232
ge211::sprites::Text_sprite::Builder::Builder
Builder(Font const &)
Constructs a new Text_sprite::Builder with the given Font.
Definition: ge211_sprites.cxx:258
ge211::internal::Render_sprite
A Render_sprite works by allowing its derived classes to render themselves pixel-by-pixel onto an SDL...
Definition: ge211_sprites.hxx:105
ge211::geometry::Dims::width
Coordinate width
The width of the object.
Definition: ge211_geometry.hxx:40
ge211::Color
For representing colors.
Definition: ge211_color.hxx:24
std::internal
T internal(T... args)
ge211::internal::Render_sprite::set_pixel
void set_pixel(Posn< int >, Color)
Sets one pixel to the given color.
Definition: ge211_sprites.cxx:86
ge211::Font
Represents a font that can be used to render a sprites::Text_sprite.
Definition: ge211_resource.hxx:70
ge211::geometry::Transform
A rendering transformation, which can scale, flip, and rotate.
Definition: ge211_geometry.hxx:793
ge211::geometry::Posn< int >
std::string::empty
T empty(T... args)
ge211::sprites::Text_sprite::reconfigure
void reconfigure(Builder const &)
Resets this text sprite with the configuration from the given Builder.
Definition: ge211_sprites.cxx:323
ge211::geometry::Rect
Represents a positioned rectangle.
Definition: ge211_geometry.hxx:461
ge211::Sprite_set::add_sprite
Sprite_set & add_sprite(Sprite const &sprite, Posn< int > xy, int z=0, Transform const &transform=Transform())
Adds the given sprite to the sprite set to render it in the next frame.
Definition: ge211_sprites.cxx:18
ge211::sprites::Text_sprite::Builder::color
Color color() const
Gets the color that will be used.
Definition: ge211_sprites.cxx:308
ge211::sprites::Text_sprite::Builder::message
std::string message() const
Gets the configured message.
Definition: ge211_sprites.cxx:298
ge211::sprites::Text_sprite::Builder
Builder-style API for configuring and constructing Text_sprites.
Definition: ge211_sprites.hxx:363
ge211::Borrowed
OBJECT_TYPE * Borrowed
Type alias to indicate that the given pointer does not own its object.
Definition: ge211_util.hxx:19