0da8c28603c625ef8ab6b14ab4c5399e78662238
[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 "util/log.hpp"
22 #include "video/drawing_request.hpp"
23 #include "video/sdl/sdl_texture.hpp"
24
25 namespace {
26
27 SDL_BlendMode blend2sdl(const Blend& blend)
28 {
29   if (blend.sfactor == GL_ONE &&
30       blend.dfactor == GL_ZERO)
31   {
32     return SDL_BLENDMODE_NONE;
33   }
34   else if (blend.sfactor == GL_SRC_ALPHA &&
35            blend.dfactor == GL_ONE_MINUS_SRC_ALPHA)
36   {
37     return SDL_BLENDMODE_BLEND;
38   }
39   else if (blend.sfactor == GL_SRC_ALPHA &&
40            blend.dfactor == GL_ONE)
41   {
42     return SDL_BLENDMODE_ADD;
43   }
44   else if (blend.sfactor == GL_DST_COLOR &&
45            blend.dfactor == GL_ZERO)
46   {
47     return SDL_BLENDMODE_MOD;
48   }
49   else
50   {
51     log_warning << "unknown blend mode combinations: sfactor=" << blend.sfactor << " dfactor=" << blend.dfactor << std::endl;
52     return SDL_BLENDMODE_BLEND;
53   }
54 }
55
56 } // namespace
57
58 void
59 SDLPainter::draw_surface(SDL_Renderer* renderer, const DrawingRequest& request)
60 {
61   const Surface* surface = static_cast<const SurfaceRequest*>(request.request_data)->surface;
62   std::shared_ptr<SDLTexture> sdltexture = std::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
63
64   SDL_Rect dst_rect;
65   dst_rect.x = request.pos.x;
66   dst_rect.y = request.pos.y;
67   dst_rect.w = sdltexture->get_image_width();
68   dst_rect.h = sdltexture->get_image_height();
69
70   Uint8 r = static_cast<Uint8>(request.color.red * 255);
71   Uint8 g = static_cast<Uint8>(request.color.green * 255);
72   Uint8 b = static_cast<Uint8>(request.color.blue * 255);
73   Uint8 a = static_cast<Uint8>(request.color.alpha * request.alpha * 255);
74   SDL_SetTextureColorMod(sdltexture->get_texture(), r, g, b);
75   SDL_SetTextureAlphaMod(sdltexture->get_texture(), a);
76   SDL_SetTextureBlendMode(sdltexture->get_texture(), blend2sdl(request.blend));
77
78   SDL_RendererFlip flip = SDL_FLIP_NONE;
79   if (surface->get_flipx() || request.drawing_effect & HORIZONTAL_FLIP)
80   {
81     flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_HORIZONTAL);
82   }
83
84   if (request.drawing_effect & VERTICAL_FLIP)
85   {
86     flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_VERTICAL);
87   }
88
89   SDL_RenderCopyEx(renderer, sdltexture->get_texture(), NULL, &dst_rect, request.angle, NULL, flip);
90 }
91
92 void
93 SDLPainter::draw_surface_part(SDL_Renderer* renderer, const DrawingRequest& request)
94 {
95   //FIXME: support parameters request.blend
96   const SurfacePartRequest* surface = (const SurfacePartRequest*) request.request_data;
97   const SurfacePartRequest* surfacepartrequest = (SurfacePartRequest*) request.request_data;
98
99   std::shared_ptr<SDLTexture> sdltexture = std::dynamic_pointer_cast<SDLTexture>(surface->surface->get_texture());
100
101   SDL_Rect src_rect;
102   src_rect.x = surfacepartrequest->srcrect.p1.x;
103   src_rect.y = surfacepartrequest->srcrect.p1.y;
104   src_rect.w = surfacepartrequest->srcrect.get_width();
105   src_rect.h = surfacepartrequest->srcrect.get_height();
106
107   SDL_Rect dst_rect;
108   dst_rect.x = request.pos.x;
109   dst_rect.y = request.pos.y;
110   dst_rect.w = surfacepartrequest->dstsize.width;
111   dst_rect.h = surfacepartrequest->dstsize.height;
112
113   Uint8 r = static_cast<Uint8>(request.color.red * 255);
114   Uint8 g = static_cast<Uint8>(request.color.green * 255);
115   Uint8 b = static_cast<Uint8>(request.color.blue * 255);
116   Uint8 a = static_cast<Uint8>(request.color.alpha * request.alpha * 255);
117   SDL_SetTextureColorMod(sdltexture->get_texture(), r, g, b);
118   SDL_SetTextureAlphaMod(sdltexture->get_texture(), a);
119   SDL_SetTextureBlendMode(sdltexture->get_texture(), blend2sdl(request.blend));
120
121   SDL_RendererFlip flip = SDL_FLIP_NONE;
122   if (surface->surface->get_flipx() || request.drawing_effect & HORIZONTAL_FLIP)
123   {
124     flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_HORIZONTAL);
125   }
126
127   if (request.drawing_effect & VERTICAL_FLIP)
128   {
129     flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_VERTICAL);
130   }
131
132   SDL_RenderCopyEx(renderer, sdltexture->get_texture(), &src_rect, &dst_rect, request.angle, NULL, flip);
133 }
134
135 void
136 SDLPainter::draw_gradient(SDL_Renderer* renderer, const DrawingRequest& request)
137 {
138   const GradientRequest* gradientrequest
139     = (GradientRequest*) request.request_data;
140   const Color& top = gradientrequest->top;
141   const Color& bottom = gradientrequest->bottom;
142
143   // calculate the maximum number of steps needed for the gradient
144   int n = static_cast<int>(std::max(std::max(fabsf(top.red - bottom.red),
145                                              fabsf(top.green - bottom.green)),
146                                     std::max(fabsf(top.blue - bottom.blue),
147                                              fabsf(top.alpha - bottom.alpha))) * 255);
148   n = std::max(n, 1);
149   for(int i = 0; i < n; ++i)
150   {
151     SDL_Rect rect;
152     rect.x = 0;
153     rect.y = SCREEN_HEIGHT * i / n;
154     rect.w = SCREEN_WIDTH;
155     rect.h = (SCREEN_HEIGHT * (i+1) / n) - rect.y;
156
157     float p = static_cast<float>(i+1) / static_cast<float>(n);
158     Uint8 r = static_cast<Uint8>(((1.0f - p) * top.red + p * bottom.red)  * 255);
159     Uint8 g = static_cast<Uint8>(((1.0f - p) * top.green + p * bottom.green) * 255);
160     Uint8 b = static_cast<Uint8>(((1.0f - p) * top.blue + p * bottom.blue) * 255);
161     Uint8 a = static_cast<Uint8>(((1.0f - p) * top.alpha + p * bottom.alpha) * 255);
162
163     SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
164     SDL_SetRenderDrawColor(renderer, r, g, b, a);
165     SDL_RenderFillRect(renderer, &rect);
166   }
167 }
168
169 void
170 SDLPainter::draw_filled_rect(SDL_Renderer* renderer, const DrawingRequest& request)
171 {
172   const FillRectRequest* fillrectrequest
173     = (FillRectRequest*) request.request_data;
174
175   SDL_Rect rect;
176   rect.x = request.pos.x;
177   rect.y = request.pos.y;
178   rect.w = fillrectrequest->size.x;
179   rect.h = fillrectrequest->size.y;
180
181   Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
182   Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
183   Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
184   Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
185
186   int radius = std::min(std::min(rect.h / 2, rect.w / 2),
187                         static_cast<int>(fillrectrequest->radius));
188
189   if (radius)
190   {
191     int slices = radius;
192
193     // rounded top and bottom parts
194     std::vector<SDL_Rect> rects;
195     rects.reserve(2*slices + 1);
196     for(int i = 0; i < slices; ++i)
197     {
198       float p = (static_cast<float>(i) + 0.5f) / static_cast<float>(slices);
199       int xoff = radius - static_cast<int>(sqrtf(1.0f - p*p) * radius);
200
201       SDL_Rect tmp;
202       tmp.x = rect.x + xoff;
203       tmp.y = rect.y + (radius - i);
204       tmp.w = rect.w - 2*(xoff);
205       tmp.h = 1;
206       rects.push_back(tmp);
207
208       SDL_Rect tmp2;
209       tmp2.x = rect.x + xoff;
210       tmp2.y = rect.y + rect.h - radius + i;
211       tmp2.w = rect.w - 2*xoff;
212       tmp2.h = 1;
213
214       if (tmp2.y != tmp.y)
215       {
216         rects.push_back(tmp2);
217       }
218     }
219
220     if (2*radius < rect.h)
221     {
222       // center rectangle
223       SDL_Rect tmp;
224       tmp.x = rect.x;
225       tmp.y = rect.y + radius + 1;
226       tmp.w = rect.w;
227       tmp.h = rect.h - 2*radius - 1;
228       rects.push_back(tmp);
229     }
230
231     SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
232     SDL_SetRenderDrawColor(renderer, r, g, b, a);
233     SDL_RenderFillRects(renderer, &*rects.begin(), rects.size());
234   }
235   else
236   {
237     if((rect.w != 0) && (rect.h != 0))
238     {
239       SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
240       SDL_SetRenderDrawColor(renderer, r, g, b, a);
241       SDL_RenderFillRect(renderer, &rect);
242     }
243   }
244 }
245
246 void
247 SDLPainter::draw_inverse_ellipse(SDL_Renderer* renderer, const DrawingRequest& request)
248 {
249   const InverseEllipseRequest* ellipse = (InverseEllipseRequest*)request.request_data;
250
251   float x = request.pos.x;
252   float w = ellipse->size.x;
253   float h = ellipse->size.y;
254
255   int top = request.pos.y - (h / 2);
256
257   const int max_slices = 256;
258   SDL_Rect rects[2*max_slices+2];
259   int slices = std::min(static_cast<int>(ellipse->size.y), max_slices);
260   for(int i = 0; i < slices; ++i)
261   {
262     float p = ((static_cast<float>(i) + 0.5f) / static_cast<float>(slices)) * 2.0f - 1.0f;
263     int xoff = static_cast<int>(sqrtf(1.0f - p*p) * w / 2);
264
265     SDL_Rect& left  = rects[2*i+0];
266     SDL_Rect& right = rects[2*i+1];
267
268     left.x = 0;
269     left.y = top + (i * h / slices);
270     left.w = x - xoff;
271     left.h = (top + ((i+1) * h / slices)) - left.y;
272
273     right.x = x + xoff;
274     right.y = left.y;
275     right.w = SCREEN_WIDTH - right.x;
276     right.h = left.h;
277   }
278
279   SDL_Rect& top_rect = rects[2*slices+0];
280   SDL_Rect& bottom_rect = rects[2*slices+1];
281
282   top_rect.x = 0;
283   top_rect.y = 0;
284   top_rect.w = SCREEN_WIDTH;
285   top_rect.h = top;
286
287   bottom_rect.x = 0;
288   bottom_rect.y = top + h;
289   bottom_rect.w = SCREEN_WIDTH;
290   bottom_rect.h = SCREEN_HEIGHT - bottom_rect.y;
291
292   Uint8 r = static_cast<Uint8>(ellipse->color.red * 255);
293   Uint8 g = static_cast<Uint8>(ellipse->color.green * 255);
294   Uint8 b = static_cast<Uint8>(ellipse->color.blue * 255);
295   Uint8 a = static_cast<Uint8>(ellipse->color.alpha * 255);
296
297   SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
298   SDL_SetRenderDrawColor(renderer, r, g, b, a);
299   SDL_RenderFillRects(renderer, rects, 2*slices+2);
300 }
301
302 /* EOF */