Merged drawing code from SDLLightmap and SDLRenderer into helper class SDLPainter
[supertux.git] / src / video / sdl / sdl_painter.cpp
1 //  SuperTux
2 //  Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "video/sdl/sdl_painter.hpp"
18
19 #include "SDL.h"
20
21 #include "video/drawing_request.hpp"
22 #include "video/sdl/sdl_texture.hpp"
23
24 void
25 SDLPainter::draw_surface(SDL_Renderer* renderer, const DrawingRequest& request)
26 {
27   //FIXME: support parameters request.angle, request.blend
28   const Surface* surface = (const Surface*) request.request_data;
29   boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
30
31   SDL_Rect dst_rect;
32   dst_rect.x = request.pos.x;
33   dst_rect.y = request.pos.y;
34   dst_rect.w = sdltexture->get_image_width();
35   dst_rect.h = sdltexture->get_image_height();
36
37   Uint8 r = static_cast<Uint8>(request.color.red * 255);
38   Uint8 g = static_cast<Uint8>(request.color.green * 255);
39   Uint8 b = static_cast<Uint8>(request.color.blue * 255);
40   Uint8 a = static_cast<Uint8>(request.color.alpha * request.alpha * 255);
41   SDL_SetTextureColorMod(sdltexture->get_texture(), r, g, b);
42   SDL_SetTextureAlphaMod(sdltexture->get_texture(), a);
43
44   if (surface->get_flipx())
45   {
46     SDL_RenderCopyEx(renderer, sdltexture->get_texture(), NULL, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
47   }
48   else
49   {
50     switch(request.drawing_effect)
51     {
52       case VERTICAL_FLIP:
53         SDL_RenderCopyEx(renderer, sdltexture->get_texture(), NULL, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
54         break;
55
56       case HORIZONTAL_FLIP:
57         SDL_RenderCopyEx(renderer, sdltexture->get_texture(), NULL, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
58         break;
59
60       default:
61       case NO_EFFECT:
62         SDL_RenderCopy(renderer, sdltexture->get_texture(), NULL, &dst_rect);
63         break;
64     }
65   }
66 }
67
68 void
69 SDLPainter::draw_surface_part(SDL_Renderer* renderer, const DrawingRequest& request)
70 {
71   //FIXME: support parameters request.angle, request.blend
72   const SurfacePartRequest* surface = (const SurfacePartRequest*) request.request_data;
73   const SurfacePartRequest* surfacepartrequest = (SurfacePartRequest*) request.request_data;
74
75   boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->surface->get_texture());
76
77   SDL_Rect src_rect;
78   src_rect.x = surfacepartrequest->source.x;
79   src_rect.y = surfacepartrequest->source.y;
80   src_rect.w = surfacepartrequest->size.x;
81   src_rect.h = surfacepartrequest->size.y;
82
83   SDL_Rect dst_rect;
84   dst_rect.x = request.pos.x;
85   dst_rect.y = request.pos.y;
86   dst_rect.w = surfacepartrequest->size.x;
87   dst_rect.h = surfacepartrequest->size.y;
88
89   Uint8 r = static_cast<Uint8>(request.color.red * 255);
90   Uint8 g = static_cast<Uint8>(request.color.green * 255);
91   Uint8 b = static_cast<Uint8>(request.color.blue * 255);
92   Uint8 a = static_cast<Uint8>(request.color.alpha * request.alpha * 255);
93   SDL_SetTextureColorMod(sdltexture->get_texture(), r, g, b);
94   SDL_SetTextureAlphaMod(sdltexture->get_texture(), a);
95
96   if (surface->surface->get_flipx())
97   {
98     SDL_RenderCopyEx(renderer, sdltexture->get_texture(), &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
99   }
100   else
101   {
102     switch(request.drawing_effect)
103     {
104       case VERTICAL_FLIP:
105         SDL_RenderCopyEx(renderer, sdltexture->get_texture(), &src_rect, &dst_rect, 0, NULL, SDL_FLIP_VERTICAL);
106         break;
107
108       case HORIZONTAL_FLIP:
109         SDL_RenderCopyEx(renderer, sdltexture->get_texture(), &src_rect, &dst_rect, 0, NULL, SDL_FLIP_HORIZONTAL);
110         break;
111
112       default:
113       case NO_EFFECT:
114         SDL_RenderCopy(renderer, sdltexture->get_texture(), &src_rect, &dst_rect);
115         break;
116     }
117   }
118 }
119
120 void
121 SDLPainter::draw_gradient(SDL_Renderer* renderer, const DrawingRequest& request)
122 {
123   const GradientRequest* gradientrequest 
124     = (GradientRequest*) request.request_data;
125   const Color& top = gradientrequest->top;
126   const Color& bottom = gradientrequest->bottom;
127
128   SDL_Rect viewport;
129   SDL_RenderGetViewport(renderer, &viewport);
130
131   // calculate the maximum number of steps needed for the gradient
132   int n = static_cast<int>(std::max(std::max(fabsf(top.red - bottom.red),
133                                              fabsf(top.green - bottom.green)),
134                                     std::max(fabsf(top.blue - bottom.blue),
135                                              fabsf(top.alpha - bottom.alpha))) * 255);
136   for(int i = 0; i < n; ++i)
137   {
138     SDL_Rect rect;
139     rect.x = 0;
140     rect.y = viewport.h * i / n;
141     rect.w = viewport.w;
142     rect.h = (viewport.h * (i+1) / n) - rect.y;
143
144     float p = static_cast<float>(i+1) / static_cast<float>(n);
145     Uint8 r = static_cast<Uint8>(((1.0f - p) * top.red + p * bottom.red)  * 255);
146     Uint8 g = static_cast<Uint8>(((1.0f - p) * top.green + p * bottom.green) * 255);
147     Uint8 b = static_cast<Uint8>(((1.0f - p) * top.blue + p * bottom.blue) * 255);
148     Uint8 a = static_cast<Uint8>(((1.0f - p) * top.alpha + p * bottom.alpha) * 255);
149
150     SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
151     SDL_SetRenderDrawColor(renderer, r, g, b, a);
152     SDL_RenderFillRect(renderer, &rect);
153   }
154 }
155
156 void
157 SDLPainter::draw_filled_rect(SDL_Renderer* renderer, const DrawingRequest& request)
158 {
159   const FillRectRequest* fillrectrequest
160     = (FillRectRequest*) request.request_data;
161
162   SDL_Rect rect;
163   rect.x = request.pos.x;
164   rect.y = request.pos.y;
165   rect.w = fillrectrequest->size.x;
166   rect.h = fillrectrequest->size.y;
167
168   Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
169   Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
170   Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
171   Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
172
173   int radius = std::min(std::min(rect.h / 2, rect.w / 2),
174                         static_cast<int>(fillrectrequest->radius));
175
176   if (radius)
177   {
178     int slices = radius;
179
180     // rounded top and bottom parts
181     std::vector<SDL_Rect> rects;
182     rects.reserve(2*slices + 1);
183     for(int i = 0; i < slices; ++i)
184     {
185       float p = (static_cast<float>(i) + 0.5f) / static_cast<float>(slices);
186       int xoff = radius - static_cast<int>(sqrtf(1.0f - p*p) * radius);
187
188       SDL_Rect tmp;
189       tmp.x = rect.x + xoff;
190       tmp.y = rect.y + (radius - i);
191       tmp.w = rect.w - 2*(xoff);
192       tmp.h = 1;
193       rects.push_back(tmp);
194
195       SDL_Rect tmp2;
196       tmp2.x = rect.x + xoff;
197       tmp2.y = rect.y + rect.h - radius + i;
198       tmp2.w = rect.w - 2*xoff;
199       tmp2.h = 1;
200
201       if (tmp2.y != tmp.y)
202       {
203         rects.push_back(tmp2);
204       }
205     }
206
207     if (2*radius < rect.h)
208     {
209       // center rectangle
210       SDL_Rect tmp;
211       tmp.x = rect.x;
212       tmp.y = rect.y + radius + 1;
213       tmp.w = rect.w;
214       tmp.h = rect.h - 2*radius - 1;
215       rects.push_back(tmp);
216     }
217
218     SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
219     SDL_SetRenderDrawColor(renderer, r, g, b, a);
220     SDL_RenderFillRects(renderer, &*rects.begin(), rects.size());
221   }
222   else
223   {
224     if((rect.w != 0) && (rect.h != 0))
225     {
226       SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
227       SDL_SetRenderDrawColor(renderer, r, g, b, a);
228       SDL_RenderFillRect(renderer, &rect);
229     }
230   }
231 }
232
233 void
234 SDLPainter::draw_inverse_ellipse(SDL_Renderer* renderer, const DrawingRequest& request)
235 {
236   const InverseEllipseRequest* ellipse = (InverseEllipseRequest*)request.request_data;
237   
238   SDL_Rect viewport;
239   SDL_RenderGetViewport(renderer, &viewport);
240
241   float x = request.pos.x;
242   float w = ellipse->size.x;
243   float h = ellipse->size.y;
244
245   int top = request.pos.y - (h / 2);
246
247   const int max_slices = 256;
248   SDL_Rect rects[2*max_slices+2];
249   int slices = std::min(static_cast<int>(ellipse->size.y), max_slices);
250   for(int i = 0; i < slices; ++i)
251   {
252     float p = ((static_cast<float>(i) + 0.5f) / static_cast<float>(slices)) * 2.0f - 1.0f; 
253     int xoff = static_cast<int>(sqrtf(1.0f - p*p) * w / 2);
254
255     SDL_Rect& left  = rects[2*i+0];
256     SDL_Rect& right = rects[2*i+1];
257
258     left.x = 0;
259     left.y = top + (i * h / slices);
260     left.w = x - xoff;
261     left.h = (top + ((i+1) * h / slices)) - left.y;
262
263     right.x = x + xoff;
264     right.y = left.y;
265     right.w = viewport.w - right.x;
266     right.h = left.h;
267   }
268
269   SDL_Rect& top_rect = rects[2*slices+0];
270   SDL_Rect& bottom_rect = rects[2*slices+1];
271
272   top_rect.x = 0;
273   top_rect.y = 0;
274   top_rect.w = viewport.w;
275   top_rect.h = top;
276
277   bottom_rect.x = 0;
278   bottom_rect.y = top + h;
279   bottom_rect.w = viewport.w;
280   bottom_rect.h = viewport.h - bottom_rect.y;
281
282   Uint8 r = static_cast<Uint8>(ellipse->color.red * 255);
283   Uint8 g = static_cast<Uint8>(ellipse->color.green * 255);
284   Uint8 b = static_cast<Uint8>(ellipse->color.blue * 255);
285   Uint8 a = static_cast<Uint8>(ellipse->color.alpha * 255);
286
287   SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
288   SDL_SetRenderDrawColor(renderer, r, g, b, a);
289   SDL_RenderFillRects(renderer, rects, 2*slices+2);
290 }
291
292 /* EOF */