1 /*
2   libSDL2pp - C++11 bindings/wrapper for SDL2
3   Copyright (C) 2013-2016 Dmitry Marakasov <amdmi3@amdmi3.ru>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 #include <vector>
23 #include <cassert>
24 
25 #include <SDL.h>
26 
27 #include <SDL2pp/Renderer.hh>
28 #include <SDL2pp/Window.hh>
29 #include <SDL2pp/Exception.hh>
30 #include <SDL2pp/Texture.hh>
31 
32 namespace SDL2pp {
33 
Renderer(SDL_Renderer * renderer)34 Renderer::Renderer(SDL_Renderer* renderer) : renderer_(renderer) {
35 	assert(renderer);
36 }
37 
Renderer(Window & window,int index,Uint32 flags)38 Renderer::Renderer(Window& window, int index, Uint32 flags) {
39 	if ((renderer_ = SDL_CreateRenderer(window.Get(), index, flags)) == nullptr)
40 		throw Exception("SDL_CreateRenderer");
41 }
42 
~Renderer()43 Renderer::~Renderer() {
44 	if (renderer_ != nullptr)
45 		SDL_DestroyRenderer(renderer_);
46 }
47 
Renderer(Renderer && other)48 Renderer::Renderer(Renderer&& other) noexcept : renderer_(other.renderer_) {
49 	other.renderer_ = nullptr;
50 }
51 
operator =(Renderer && other)52 Renderer& Renderer::operator=(Renderer&& other) noexcept {
53 	if (&other == this)
54 		return *this;
55 	if (renderer_ != nullptr)
56 		SDL_DestroyRenderer(renderer_);
57 	renderer_ = other.renderer_;
58 	other.renderer_ = nullptr;
59 	return *this;
60 }
61 
Get() const62 SDL_Renderer* Renderer::Get() const {
63 	return renderer_;
64 }
65 
Present()66 Renderer& Renderer::Present() {
67 	SDL_RenderPresent(renderer_);
68 	return *this;
69 }
70 
Clear()71 Renderer& Renderer::Clear() {
72 	if (SDL_RenderClear(renderer_) != 0)
73 		throw Exception("SDL_RenderClear");
74 	return *this;
75 }
76 
GetInfo(SDL_RendererInfo & info)77 void Renderer::GetInfo(SDL_RendererInfo& info) {
78 	if (SDL_GetRendererInfo(renderer_, &info) != 0)
79 		throw Exception("SDL_GetRendererInfo");
80 }
81 
Copy(Texture & texture,const Optional<Rect> & srcrect,const Optional<Rect> & dstrect)82 Renderer& Renderer::Copy(Texture& texture, const Optional<Rect>& srcrect, const Optional<Rect>& dstrect) {
83 	if (SDL_RenderCopy(renderer_, texture.Get(), srcrect ? &*srcrect : nullptr, dstrect ? &*dstrect : nullptr) != 0)
84 		throw Exception("SDL_RenderCopy");
85 	return *this;
86 }
87 
Copy(Texture & texture,const Optional<Rect> & srcrect,const Point & dstpoint)88 Renderer& Renderer::Copy(Texture& texture, const Optional<Rect>& srcrect, const Point& dstpoint) {
89 	Rect dstrect(
90 			dstpoint.x,
91 			dstpoint.y,
92 			srcrect ? srcrect->w : texture.GetWidth(),
93 			srcrect ? srcrect->h : texture.GetHeight()
94 		);
95 	return Copy(texture, srcrect, dstrect);
96 }
97 
Copy(Texture & texture,const Optional<Rect> & srcrect,const Optional<Rect> & dstrect,double angle,const Optional<Point> & center,int flip)98 Renderer& Renderer::Copy(Texture& texture, const Optional<Rect>& srcrect, const Optional<Rect>& dstrect, double angle, const Optional<Point>& center, int flip) {
99 	if (SDL_RenderCopyEx(renderer_, texture.Get(), srcrect ? &*srcrect : nullptr, dstrect ? &*dstrect : nullptr, angle, center ? &*center : nullptr, static_cast<SDL_RendererFlip>(flip)) != 0)
100 		throw Exception("SDL_RenderCopyEx");
101 	return *this;
102 }
103 
Copy(Texture & texture,const Optional<Rect> & srcrect,const Point & dstpoint,double angle,const Optional<Point> & center,int flip)104 Renderer& Renderer::Copy(Texture& texture, const Optional<Rect>& srcrect, const Point& dstpoint, double angle, const Optional<Point>& center, int flip) {
105 	Rect dstrect(
106 			dstpoint.x,
107 			dstpoint.y,
108 			srcrect ? srcrect->w : texture.GetWidth(),
109 			srcrect ? srcrect->h : texture.GetHeight()
110 		);
111 	return Copy(texture, srcrect, dstrect, angle, center, flip);
112 }
113 
FillCopy(Texture & texture,const Optional<Rect> & srcrect,const Optional<Rect> & dstrect,const Point & offset,int flip)114 Renderer& Renderer::FillCopy(Texture& texture, const Optional<Rect>& srcrect, const Optional<Rect>& dstrect, const Point& offset, int flip) {
115 	// resolve rectangles
116 	Rect src = srcrect ? *srcrect : Rect(0, 0, texture.GetWidth(), texture.GetHeight());
117 	Rect dst = dstrect ? *dstrect : Rect(0, 0, GetOutputWidth(), GetOutputHeight());
118 
119 	// rectangle for single tile
120 	Rect start_tile(
121 			offset.x,
122 			offset.y,
123 			src.w,
124 			src.h
125 		);
126 
127 	// ensure tile is leftmost and topmost
128 	if (start_tile.x + start_tile.w <= 0)
129 		start_tile.x += (-start_tile.x) / start_tile.w * start_tile.w;
130 	if (start_tile.x > 0)
131 		start_tile.x -= (start_tile.x + start_tile.w - 1) / start_tile.w * start_tile.w;
132 
133 	if (start_tile.y + start_tile.h <= 0)
134 		start_tile.y += (-start_tile.y) / start_tile.h * start_tile.h;
135 	if (start_tile.y > 0)
136 		start_tile.y -= (start_tile.y + start_tile.h - 1) / start_tile.h * start_tile.h;
137 
138 	// paint tile array
139 	for (int y = start_tile.y; y < dst.h; y += start_tile.h) {
140 		for (int x = start_tile.x; x < dst.w; x += start_tile.w) {
141 			Rect tile_src = src;
142 			Rect tile_dst(x, y, start_tile.w, start_tile.h);
143 
144 			// clamp with dstrect
145 			int xunderflow = -x;
146 			if (xunderflow > 0) {
147 				tile_src.w -= xunderflow;
148 				tile_src.x += xunderflow;
149 				tile_dst.w -= xunderflow;
150 				tile_dst.x += xunderflow;
151 			}
152 
153 			int yunderflow = -y;
154 			if (yunderflow > 0) {
155 				tile_src.h -= yunderflow;
156 				tile_src.y += yunderflow;
157 				tile_dst.h -= yunderflow;
158 				tile_dst.y += yunderflow;
159 			}
160 
161 			int xoverflow = tile_dst.x + tile_dst.w - dst.w;
162 			if (xoverflow > 0) {
163 				tile_src.w -= xoverflow;
164 				tile_dst.w -= xoverflow;
165 			}
166 
167 			int yoverflow = tile_dst.y + tile_dst.h - dst.h;
168 			if (yoverflow > 0) {
169 				tile_src.h -= yoverflow;
170 				tile_dst.h -= yoverflow;
171 			}
172 
173 			// make tile_dst absolute
174 			tile_dst.x += dst.x;
175 			tile_dst.y += dst.y;
176 
177 			if (flip != 0) {
178 				// mirror tile_src inside src to take flipping into account
179 				if (flip & SDL_FLIP_HORIZONTAL)
180 					tile_src.x = src.w - tile_src.x - tile_src.w;
181 
182 				if (flip & SDL_FLIP_VERTICAL)
183 					tile_src.y = src.h - tile_src.y - tile_src.h;
184 
185 				Copy(texture, tile_src, tile_dst, 0.0, NullOpt, flip);
186 			} else {
187 				Copy(texture, tile_src, tile_dst);
188 			}
189 		}
190 	}
191 	return *this;
192 }
193 
SetDrawColor(Uint8 r,Uint8 g,Uint8 b,Uint8 a)194 Renderer& Renderer::SetDrawColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
195 	if (SDL_SetRenderDrawColor(renderer_, r, g, b, a) != 0)
196 		throw Exception("SDL_SetRenderDrawColor");
197 	return *this;
198 }
199 
SetDrawColor(const Color & color)200 Renderer& Renderer::SetDrawColor(const Color& color) {
201 	return SetDrawColor(color.r, color.g, color.b, color.a);
202 }
203 
SetTarget()204 Renderer& Renderer::SetTarget() {
205 	if (SDL_SetRenderTarget(renderer_, nullptr) != 0)
206 		throw Exception("SDL_SetRenderTarget");
207 	return *this;
208 }
209 
SetTarget(Texture & texture)210 Renderer& Renderer::SetTarget(Texture& texture) {
211 	if (SDL_SetRenderTarget(renderer_, texture.Get()) != 0)
212 		throw Exception("SDL_SetRenderTarget");
213 	return *this;
214 }
215 
SetDrawBlendMode(SDL_BlendMode blendMode)216 Renderer& Renderer::SetDrawBlendMode(SDL_BlendMode blendMode) {
217 	if (SDL_SetRenderDrawBlendMode(renderer_, blendMode) != 0)
218 		throw Exception("SDL_SetRenderDrawBlendMode");
219 	return *this;
220 }
221 
DrawPoint(int x,int y)222 Renderer& Renderer::DrawPoint(int x, int y) {
223 	if (SDL_RenderDrawPoint(renderer_, x, y) != 0)
224 		throw Exception("SDL_RenderDrawPoint");
225 	return *this;
226 }
227 
DrawPoint(const Point & p)228 Renderer& Renderer::DrawPoint(const Point& p) {
229 	DrawPoint(p.x, p.y);
230 	return *this;
231 }
232 
DrawPoints(const Point * points,int count)233 Renderer& Renderer::DrawPoints(const Point* points, int count) {
234 	std::vector<SDL_Point> sdl_points;
235 	sdl_points.reserve(static_cast<size_t>(count));
236 	for (const Point* p = points; p != points + count; ++p)
237 		sdl_points.emplace_back(*p);
238 
239 	if (SDL_RenderDrawPoints(renderer_, sdl_points.data(), count) != 0)
240 		throw Exception("SDL_RenderDrawPoints");
241 
242 	return *this;
243 }
244 
DrawLine(int x1,int y1,int x2,int y2)245 Renderer& Renderer::DrawLine(int x1, int y1, int x2, int y2) {
246 	if (SDL_RenderDrawLine(renderer_, x1, y1, x2, y2) != 0)
247 		throw Exception("SDL_RenderDrawLine");
248 	return *this;
249 }
250 
DrawLine(const Point & p1,const Point & p2)251 Renderer& Renderer::DrawLine(const Point& p1, const Point& p2) {
252 	DrawLine(p1.x, p1.y, p2.x, p2.y);
253 	return *this;
254 }
255 
DrawLines(const Point * points,int count)256 Renderer& Renderer::DrawLines(const Point* points, int count) {
257 	std::vector<SDL_Point> sdl_points;
258 	sdl_points.reserve(static_cast<size_t>(count));
259 	for (const Point* p = points; p != points + count; ++p)
260 		sdl_points.emplace_back(*p);
261 
262 	if (SDL_RenderDrawLines(renderer_, sdl_points.data(), count) != 0)
263 		throw Exception("SDL_RenderDrawLines");
264 
265 	return *this;
266 }
267 
DrawRect(int x1,int y1,int x2,int y2)268 Renderer& Renderer::DrawRect(int x1, int y1, int x2, int y2) {
269 	SDL_Rect rect = {x1, y1, x2 - x1 + 1, y2 - y1 + 1};
270 	if (SDL_RenderDrawRect(renderer_, &rect) != 0)
271 		throw Exception("SDL_RenderDrawRect");
272 	return *this;
273 }
274 
DrawRect(const Point & p1,const Point & p2)275 Renderer& Renderer::DrawRect(const Point& p1, const Point& p2) {
276 	DrawRect(p1.x, p1.y, p2.x, p2.y);
277 	return *this;
278 }
279 
DrawRect(const Rect & r)280 Renderer& Renderer::DrawRect(const Rect& r) {
281 	if (SDL_RenderDrawRect(renderer_, &r) != 0)
282 		throw Exception("SDL_RenderDrawRect");
283 	return *this;
284 }
285 
DrawRects(const Rect * rects,int count)286 Renderer& Renderer::DrawRects(const Rect* rects, int count) {
287 	std::vector<SDL_Rect> sdl_rects;
288 	sdl_rects.reserve(static_cast<size_t>(count));
289 	for (const Rect* r = rects; r != rects + count; ++r)
290 		sdl_rects.emplace_back(*r);
291 
292 	if (SDL_RenderDrawRects(renderer_, sdl_rects.data(), count) != 0)
293 		throw Exception("SDL_RenderDrawRects");
294 
295 	return *this;
296 }
297 
FillRect(int x1,int y1,int x2,int y2)298 Renderer& Renderer::FillRect(int x1, int y1, int x2, int y2) {
299 	SDL_Rect rect = {x1, y1, x2 - x1 + 1, y2 - y1 + 1};
300 	if (SDL_RenderFillRect(renderer_, &rect) != 0)
301 		throw Exception("SDL_RenderFillRect");
302 	return *this;
303 }
304 
FillRect(const Point & p1,const Point & p2)305 Renderer& Renderer::FillRect(const Point& p1, const Point& p2) {
306 	FillRect(p1.x, p1.y, p2.x, p2.y);
307 	return *this;
308 }
309 
FillRect(const Rect & r)310 Renderer& Renderer::FillRect(const Rect& r) {
311 	if (SDL_RenderFillRect(renderer_, &r) != 0)
312 		throw Exception("SDL_RenderFillRect");
313 	return *this;
314 }
315 
FillRects(const Rect * rects,int count)316 Renderer& Renderer::FillRects(const Rect* rects, int count) {
317 	std::vector<SDL_Rect> sdl_rects;
318 	sdl_rects.reserve(static_cast<size_t>(count));
319 	for (const Rect* r = rects; r != rects + count; ++r)
320 		sdl_rects.emplace_back(*r);
321 
322 	if (SDL_RenderFillRects(renderer_, sdl_rects.data(), count) != 0)
323 		throw Exception("SDL_RenderFillRects");
324 
325 	return *this;
326 }
327 
ReadPixels(const Optional<Rect> & rect,Uint32 format,void * pixels,int pitch)328 void Renderer::ReadPixels(const Optional<Rect>& rect, Uint32 format, void* pixels, int pitch) {
329 	if (SDL_RenderReadPixels(renderer_, rect ? &*rect : nullptr, format, pixels, pitch) != 0)
330 		throw Exception("SDL_RenderReadPixels");
331 }
332 
SetClipRect(const Optional<Rect> & rect)333 Renderer& Renderer::SetClipRect(const Optional<Rect>& rect) {
334 	if (SDL_RenderSetClipRect(renderer_, rect ? &*rect : nullptr) != 0)
335 		throw Exception("SDL_RenderSetClipRect");
336 	return *this;
337 }
338 
SetLogicalSize(int w,int h)339 Renderer& Renderer::SetLogicalSize(int w, int h) {
340 	if (SDL_RenderSetLogicalSize(renderer_, w, h) != 0)
341 		throw Exception("SDL_RenderSetLogicalSize");
342 	return *this;
343 }
344 
SetScale(float scaleX,float scaleY)345 Renderer& Renderer::SetScale(float scaleX, float scaleY) {
346 	if (SDL_RenderSetScale(renderer_, scaleX, scaleY) != 0)
347 		throw Exception("SDL_RenderSetScale");
348 	return *this;
349 }
350 
SetViewport(const Optional<Rect> & rect)351 Renderer& Renderer::SetViewport(const Optional<Rect>& rect) {
352 	if (SDL_RenderSetViewport(renderer_, rect ? &*rect : nullptr) != 0)
353 		throw Exception("SDL_RenderSetViewport");
354 	return *this;
355 }
356 
TargetSupported() const357 bool Renderer::TargetSupported() const {
358 	return SDL_RenderTargetSupported(renderer_) == SDL_TRUE;
359 }
360 
GetClipRect() const361 Optional<Rect> Renderer::GetClipRect() const {
362 	SDL_Rect rect;
363 	SDL_RenderGetClipRect(renderer_, &rect);
364 
365 	if (SDL_RectEmpty(&rect))
366 		return NullOpt;
367 	else
368 		return Rect(rect);
369 }
370 
GetLogicalSize() const371 Point Renderer::GetLogicalSize() const {
372 	int w, h;
373 	SDL_RenderGetLogicalSize(renderer_, &w, &h);
374 	return Point(w, h);
375 }
376 
GetLogicalWidth() const377 int Renderer::GetLogicalWidth() const {
378 	int w;
379 	SDL_RenderGetLogicalSize(renderer_, &w, nullptr);
380 	return w;
381 }
382 
GetLogicalHeight() const383 int Renderer::GetLogicalHeight() const {
384 	int h;
385 	SDL_RenderGetLogicalSize(renderer_, nullptr, &h);
386 	return h;
387 }
388 
GetScale(float & scalex,float & scaley) const389 void Renderer::GetScale(float& scalex, float& scaley) const {
390 	SDL_RenderGetScale(renderer_, &scalex, &scaley);
391 }
392 
GetXScale() const393 float Renderer::GetXScale() const {
394 	float scalex;
395 	SDL_RenderGetScale(renderer_, &scalex, nullptr);
396 	return scalex;
397 }
398 
GetYScale() const399 float Renderer::GetYScale() const {
400 	float scaley;
401 	SDL_RenderGetScale(renderer_, nullptr, &scaley);
402 	return scaley;
403 }
404 
GetViewport() const405 Rect Renderer::GetViewport() const {
406 	SDL_Rect rect;
407 	SDL_RenderGetViewport(renderer_, &rect);
408 	return rect;
409 }
410 
GetDrawBlendMode() const411 SDL_BlendMode Renderer::GetDrawBlendMode() const {
412 	SDL_BlendMode mode;
413 	if (SDL_GetRenderDrawBlendMode(renderer_, &mode) != 0)
414 		throw Exception("SDL_GetRenderDrawBlendMode");
415 	return mode;
416 }
417 
GetDrawColor() const418 Color Renderer::GetDrawColor() const {
419 	Color color;
420 	GetDrawColor(color.r, color.g, color.b, color.a);
421 	return color;
422 }
423 
GetDrawColor(Uint8 & r,Uint8 & g,Uint8 & b,Uint8 & a) const424 void Renderer::GetDrawColor(Uint8& r, Uint8& g, Uint8& b, Uint8& a) const {
425 	if (SDL_GetRenderDrawColor(renderer_, &r, &g, &b, &a) != 0)
426 		throw Exception("SDL_GetRenderDrawColor");
427 }
428 
GetOutputSize() const429 Point Renderer::GetOutputSize() const {
430 	int w, h;
431 	if (SDL_GetRendererOutputSize(renderer_, &w, &h) != 0)
432 		throw Exception("SDL_GetRendererOutputSize");
433 	return Point(w, h);
434 }
435 
GetOutputWidth() const436 int Renderer::GetOutputWidth() const {
437 	int w;
438 	if (SDL_GetRendererOutputSize(renderer_, &w, nullptr) != 0)
439 		throw Exception("SDL_GetRendererOutputSize");
440 	return w;
441 }
442 
GetOutputHeight() const443 int Renderer::GetOutputHeight() const {
444 	int h;
445 	if (SDL_GetRendererOutputSize(renderer_, nullptr, &h) != 0)
446 		throw Exception("SDL_GetRendererOutputSize");
447 	return h;
448 }
449 
450 }
451