ge211
ge211_render.cpp
1 #include "ge211_render.h"
2 #include "ge211_error.h"
3 #include "ge211_util.h"
4 
5 #include <SDL.h>
6 
7 #include <utility>
8 
9 static inline SDL_RendererFlip&
10 operator|=(SDL_RendererFlip& f1, SDL_RendererFlip f2)
11 {
12  return f1 = SDL_RendererFlip(f1 | f2);
13 }
14 
15 namespace ge211 {
16 
17 namespace detail {
18 
19 static const uint32_t renderer_flags_to_try[] = {
20  SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC,
21  SDL_RENDERER_SOFTWARE | SDL_RENDERER_PRESENTVSYNC,
22  SDL_RENDERER_ACCELERATED,
23  SDL_RENDERER_SOFTWARE,
24  0,
25 };
26 
27 SDL_Renderer* Renderer::create_renderer_(SDL_Window* window)
28 {
29  SDL_Renderer* result;
30 
31 #if SDL_VIDEO_RENDER_METAL
32  SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
33 #endif
34 
35  for (auto flags : renderer_flags_to_try) {
36  result = SDL_CreateRenderer(window, -1, flags);
37  if (result) {
38  SDL_SetRenderDrawBlendMode(result, SDL_BLENDMODE_BLEND);
39  return result;
40  }
41 
42  info_sdl()
43  << "Could not initialize preferred renderer ("
44  << flags << "); trying next.";
45  }
46 
47  throw Host_error{"Could not initialize renderer"};
48 }
49 
50 Renderer::Renderer(const Window& window)
51  : ptr_{create_renderer_(window.get_raw_()),
52  &SDL_DestroyRenderer}
53 { }
54 
55 bool Renderer::is_vsync() const noexcept
56 {
57  SDL_RendererInfo info;
58  SDL_GetRendererInfo(get_raw_(), &info);
59  return (info.flags & SDL_RENDERER_PRESENTVSYNC) != 0;
60 }
61 
62 void Renderer::clear()
63 {
64  if (SDL_RenderClear(get_raw_()))
65  throw Host_error{"Could not clear window"};
66 }
67 
68 SDL_Renderer* Renderer::get_raw_() const noexcept
69 {
70  return ptr_.get();
71 }
72 
73 void Renderer::set_color(Color color)
74 {
75  if (SDL_SetRenderDrawColor(
76  get_raw_(),
77  color.red(), color.green(), color.blue(), color.alpha()))
78  throw Host_error{"Could not set renderer color"};
79 }
80 
81 void Renderer::present() noexcept
82 {
83  SDL_RenderPresent(get_raw_());
84 }
85 
86 void Renderer::copy(const Texture& texture, Position xy)
87 {
88  auto raw_texture = texture.get_raw_(*this);
89  if (!raw_texture) return;
90 
91  SDL_Rect dstrect = Rectangle::from_top_left(xy, texture.dimensions());
92 
93  int render_result = SDL_RenderCopy(get_raw_(), raw_texture,
94  nullptr, &dstrect);
95  if (render_result < 0) {
96  warn_sdl() << "Could not render texture";
97  }
98 }
99 
100 void Renderer::copy(const Texture& texture,
101  Position xy,
102  const Transform& transform)
103 {
104  auto raw_texture = texture.get_raw_(*this);
105  if (!raw_texture) return;
106 
107  SDL_Rect dstrect = Rectangle::from_top_left(xy, texture.dimensions());
108  dstrect.w = int(dstrect.w * transform.get_scale_x());
109  dstrect.h = int(dstrect.h * transform.get_scale_y());
110 
111  SDL_RendererFlip flip = SDL_FLIP_NONE;
112  if (transform.get_flip_h()) flip |= SDL_FLIP_HORIZONTAL;
113  if (transform.get_flip_v()) flip |= SDL_FLIP_VERTICAL;
114 
115  int render_result = SDL_RenderCopyEx(get_raw_(), raw_texture,
116  nullptr, &dstrect,
117  transform.get_rotation(),
118  nullptr,
119  flip);
120  if (render_result < 0) {
121  warn_sdl() << "Could not render texture";
122  }
123 }
124 
125 void Renderer::prepare(const Texture& texture) const
126 {
127  texture.get_raw_(*this);
128 }
129 
130 Texture::Impl_::Impl_(SDL_Surface* surface) noexcept
131  : Impl_{{surface, &SDL_FreeSurface}}
132 { }
133 
134 Texture::Impl_::Impl_(SDL_Texture* texture) noexcept
135  : Impl_{{texture, &SDL_DestroyTexture}}
136 { }
137 
138 Texture::Impl_::Impl_(delete_ptr<SDL_Surface> surface) noexcept
139  : surface_{std::move(surface)},
140  texture_{nullptr, &no_op_deleter}
141 { }
142 
143 Texture::Impl_::Impl_(delete_ptr<SDL_Texture> texture) noexcept
144  : surface_{nullptr, &no_op_deleter},
145  texture_{std::move(texture)}
146 { }
147 
148 Texture::Texture() noexcept
149  : impl_{nullptr}
150 { }
151 
152 Texture::Texture(SDL_Surface* surface)
153  : impl_{std::make_shared<Impl_>(surface)}
154 { }
155 
156 Texture::Texture(delete_ptr<SDL_Surface> surface)
157  : impl_{std::make_shared<Impl_>(std::move(surface))}
158 { }
159 
160 SDL_Texture* Texture::get_raw_(const Renderer& renderer) const
161 {
162  if (impl_->texture_) return impl_->texture_.get();
163 
164  if (!impl_->surface_) return nullptr;
165 
166  SDL_Texture* raw = SDL_CreateTextureFromSurface(renderer.get_raw_(),
167  impl_->surface_.get());
168  if (raw) {
169  *impl_ = Impl_(raw);
170  return raw;
171  }
172 
173  throw Host_error{"Could not create texture from surface"};
174 }
175 
176 Dimensions Texture::dimensions() const noexcept
177 {
178  Dimensions result{0, 0};
179 
180  if (impl_->texture_) {
181  SDL_QueryTexture(impl_->texture_.get(), nullptr, nullptr,
182  &result.width, &result.height);
183  } else if (impl_->surface_) {
184  result.width = impl_->surface_->w;
185  result.height = impl_->surface_->h;
186  }
187 
188  return result;
189 }
190 
191 SDL_Surface* Texture::as_surface() noexcept
192 {
193  return impl_->surface_.get();
194 }
195 
196 bool Texture::empty() const noexcept
197 {
198  return impl_ == nullptr;
199 }
200 
201 } // end namespace detail
202 
203 }
static Basic_rectangle from_top_left(Position tl, Dimensions dims) noexcept(has_nothrow_arithmetic< Coordinate >())
Creates a Basic_rectangle given the position of its top left vertex and its dimensions.
The game engine namespace.
Definition: ge211.h:17
Basic_dimensions< int > Dimensions
Type alias for the most common use of Basic_dimensions, which is with a coordinate type of int...
Definition: ge211_forward.h:77