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 | #include <algorithm> |
21 | #include <array> |
22 | #include <assert.h> |
23 | #include <math.h> |
24 | |
25 | #include "supertux/globals.hpp" |
26 | #include "math/util.hpp" |
27 | #include "util/log.hpp" |
28 | #include "video/drawing_request.hpp" |
29 | #include "video/renderer.hpp" |
30 | #include "video/sdl/sdl_texture.hpp" |
31 | #include "video/sdl/sdl_video_system.hpp" |
32 | #include "video/viewport.hpp" |
33 | |
34 | namespace { |
35 | |
36 | SDL_Rect to_sdl_rect(const Rectf& rect) |
37 | { |
38 | SDL_Rect sdl_rect; |
39 | |
40 | // floorf() here due to int(-0.5) and int(0.5) both rounding to 0, |
41 | // thus creating a jump in coordinates at 0 |
42 | sdl_rect.x = static_cast<int>(floorf(rect.get_left())); |
43 | sdl_rect.y = static_cast<int>(floorf(rect.get_top())); |
44 | |
45 | // roundf() here due to int(rect.get_right()y - rect.get_left()y) being |
46 | // off-by-one due to float errors |
47 | sdl_rect.w = static_cast<int>(roundf(rect.get_width())); |
48 | sdl_rect.h = static_cast<int>(roundf(rect.get_height())); |
49 | |
50 | return sdl_rect; |
51 | } |
52 | |
53 | SDL_BlendMode blend2sdl(const Blend& blend) |
54 | { |
55 | if (blend == Blend::NONE) |
56 | { |
57 | return SDL_BLENDMODE_NONE; |
58 | } |
59 | else if (blend == Blend::BLEND) |
60 | { |
61 | return SDL_BLENDMODE_BLEND; |
62 | } |
63 | else if (blend == Blend::ADD) |
64 | { |
65 | return SDL_BLENDMODE_ADD; |
66 | } |
67 | else if (blend == Blend::MOD) |
68 | { |
69 | return SDL_BLENDMODE_MOD; |
70 | } |
71 | else |
72 | { |
73 | log_warning << "unknown blend mode combinations: blend=" << static_cast<int>(blend) << std::endl; |
74 | return SDL_BLENDMODE_BLEND; |
75 | } |
76 | } |
77 | |
78 | /* Creates a new rectangle covering the area where srcrect and imgrect |
79 | overlap, in addition create four more rectangles for the areas |
80 | where srcrect is outside of imgrect, some of those rects will be |
81 | empty. The rectangles will be returned in the order inside, top, |
82 | left, right, bottom */ |
83 | std::tuple<Rect, Rect, Rect, Rect, Rect> |
84 | intersect(const Rect& srcrect, const Rect& imgrect) |
85 | { |
86 | return std::make_tuple( |
87 | // inside |
88 | Rect(std::max(srcrect.left, imgrect.left), std::max(srcrect.top, imgrect.top), |
89 | std::min(srcrect.right, imgrect.right), std::min(srcrect.bottom, imgrect.bottom)), |
90 | |
91 | // top |
92 | Rect(srcrect.left, srcrect.top, |
93 | srcrect.right, imgrect.top), |
94 | |
95 | // left |
96 | Rect(srcrect.left, std::max(srcrect.top, imgrect.top), |
97 | imgrect.left, std::min(srcrect.bottom, imgrect.bottom)), |
98 | |
99 | // right |
100 | Rect(imgrect.right, std::max(srcrect.top, imgrect.top), |
101 | srcrect.right, std::min(srcrect.bottom, imgrect.bottom)), |
102 | |
103 | // bottom |
104 | Rect(srcrect.left, imgrect.bottom, |
105 | srcrect.right, srcrect.bottom) |
106 | ); |
107 | } |
108 | |
109 | /* Map the area covered by inside in srcrect to dstrect */ |
110 | Rect relative_map(const Rect& inside, const Rect& srcrect, const Rect& dstrect) |
111 | { |
112 | assert(srcrect.contains(inside)); |
113 | |
114 | Rect result(dstrect.left + (inside.left - srcrect.left) * dstrect.get_width() / srcrect.get_width(), |
115 | dstrect.top + (inside.top - srcrect.top) * dstrect.get_height() / srcrect.get_height(), |
116 | dstrect.left + (inside.right - srcrect.left) * dstrect.get_width() / srcrect.get_width(), |
117 | dstrect.top + (inside.bottom - srcrect.top) * dstrect.get_height() / srcrect.get_height()); |
118 | |
119 | assert(dstrect.contains(result)); |
120 | |
121 | return result; |
122 | } |
123 | |
124 | void render_texture(SDL_Renderer* renderer, |
125 | SDL_Texture* texture, const Rect& imgrect, |
126 | const Rect& srcrect, const Rect& dstrect) |
127 | { |
128 | assert(imgrect.contains(srcrect.left, srcrect.top)); |
129 | |
130 | if (srcrect.empty() || dstrect.empty()) |
131 | return; |
132 | |
133 | if (imgrect.contains(srcrect)) |
134 | { |
135 | SDL_Rect sdl_srcrect = srcrect.to_sdl(); |
136 | SDL_Rect sdl_dstrect = dstrect.to_sdl(); |
137 | SDL_RenderCopy(renderer, texture, &sdl_srcrect, &sdl_dstrect); |
138 | } |
139 | else |
140 | { |
141 | Rect inside; |
142 | std::array<Rect, 4> rest; |
143 | std::tie(inside, rest[0], rest[1], rest[2], rest[3]) = intersect(srcrect, imgrect); |
144 | |
145 | render_texture(renderer, texture, imgrect, inside, relative_map(inside, srcrect, dstrect)); |
146 | |
147 | for (const Rect& rect : rest) |
148 | { |
149 | const Rect new_srcrect(math::positive_mod(rect.left, imgrect.get_width()), |
150 | math::positive_mod(rect.top, imgrect.get_height()), |
151 | rect.get_size()); |
152 | render_texture(renderer, texture, imgrect, |
153 | new_srcrect, relative_map(rect, srcrect, dstrect)); |
154 | } |
155 | } |
156 | } |
157 | |
158 | /* A version SDL_RenderCopyEx that supports texture animation as specified by Sampler */ |
159 | void RenderCopyEx(SDL_Renderer* renderer, |
160 | SDL_Texture* texture, |
161 | const SDL_Rect* sdl_srcrect, |
162 | const SDL_Rect* sdl_dstrect, |
163 | const double angle, |
164 | const SDL_Point* center, |
165 | const SDL_RendererFlip flip, |
166 | const Sampler& sampler) |
167 | { |
168 | Vector animate = sampler.get_animate(); |
169 | if (animate.x == 0.0f && animate.y == 0.0f) |
170 | { |
171 | SDL_RenderCopyEx(renderer, texture, sdl_srcrect, sdl_dstrect, angle, nullptr, flip); |
172 | } |
173 | else |
174 | { |
175 | // This part deals with texture animation. Texture animation is |
176 | // accomplished by shifting the srcrect across the input texture. |
177 | // If the srcrect goes out of bounds of the texture, it is broken |
178 | // up into multiple rectangles that wrap around and fall back into |
179 | // the texture space. |
180 | // |
181 | // If a srcrect is passed to SDL that goes out of bounds SDL will |
182 | // clip it to be inside the bounds, without adjusting dstrect, |
183 | // thus result in stretching artifacts. |
184 | // |
185 | // FIXME: Neither flipping nor wrap modes are supported at the |
186 | // moment. wrap is treated as if it was set to 'repeat'. |
187 | int width; |
188 | int height; |
189 | |
190 | SDL_QueryTexture(texture, nullptr, nullptr, &width, &height); |
191 | |
192 | animate *= g_game_time; |
193 | |
194 | int tex_off_x = math::positive_mod(static_cast<int>(animate.x), width); |
195 | int tex_off_y = math::positive_mod(static_cast<int>(animate.y), height); |
196 | |
197 | if ((tex_off_x == 0 && tex_off_y == 0) || |
198 | flip || |
199 | angle != 0.0) |
200 | { |
201 | SDL_RenderCopyEx(renderer, texture, sdl_srcrect, sdl_dstrect, angle, nullptr, flip); |
202 | } |
203 | else |
204 | { |
205 | Rect imgrect(0, 0, Size(width, height)); |
206 | Rect srcrect(math::positive_mod(sdl_srcrect->x + tex_off_x, width), |
207 | math::positive_mod(sdl_srcrect->y + tex_off_y, height), |
208 | Size(sdl_srcrect->w, sdl_srcrect->h)); |
209 | |
210 | render_texture(renderer, texture, imgrect, srcrect, Rect(*sdl_dstrect)); |
211 | } |
212 | } |
213 | } |
214 | |
215 | } // namespace |
216 | |
217 | SDLPainter::SDLPainter(SDLVideoSystem& video_system, Renderer& renderer, SDL_Renderer* sdl_renderer) : |
218 | m_video_system(video_system), |
219 | m_renderer(renderer), |
220 | m_sdl_renderer(sdl_renderer), |
221 | m_cliprect() |
222 | {} |
223 | |
224 | void |
225 | SDLPainter::draw_texture(const TextureRequest& request) |
226 | { |
227 | const auto& texture = static_cast<const SDLTexture&>(*request.texture); |
228 | |
229 | assert(request.srcrects.size() == request.dstrects.size()); |
230 | assert(request.srcrects.size() == request.angles.size()); |
231 | |
232 | for (size_t i = 0; i < request.srcrects.size(); ++i) |
233 | { |
234 | const SDL_Rect& src_rect = to_sdl_rect(request.srcrects[i]); |
235 | const SDL_Rect& dst_rect = to_sdl_rect(request.dstrects[i]); |
236 | |
237 | Uint8 r = static_cast<Uint8>(request.color.red * 255); |
238 | Uint8 g = static_cast<Uint8>(request.color.green * 255); |
239 | Uint8 b = static_cast<Uint8>(request.color.blue * 255); |
240 | Uint8 a = static_cast<Uint8>(request.color.alpha * request.alpha * 255); |
241 | |
242 | SDL_SetTextureColorMod(texture.get_texture(), r, g, b); |
243 | SDL_SetTextureAlphaMod(texture.get_texture(), a); |
244 | SDL_SetTextureBlendMode(texture.get_texture(), blend2sdl(request.blend)); |
245 | |
246 | SDL_RendererFlip flip = SDL_FLIP_NONE; |
247 | if ((request.flip & HORIZONTAL_FLIP) != 0) |
248 | { |
249 | flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_HORIZONTAL); |
250 | } |
251 | |
252 | if ((request.flip & VERTICAL_FLIP) != 0) |
253 | { |
254 | flip = static_cast<SDL_RendererFlip>(flip | SDL_FLIP_VERTICAL); |
255 | } |
256 | |
257 | RenderCopyEx(m_sdl_renderer, texture.get_texture(), |
258 | &src_rect, &dst_rect, |
259 | static_cast<double>(request.angles[i]), nullptr, flip, |
260 | texture.get_sampler()); |
261 | } |
262 | } |
263 | |
264 | void |
265 | SDLPainter::draw_gradient(const GradientRequest& request) |
266 | { |
267 | const Color& top = request.top; |
268 | const Color& bottom = request.bottom; |
269 | const GradientDirection& direction = request.direction; |
270 | const Rectf& region = request.region; |
271 | |
272 | // calculate the maximum number of steps needed for the gradient |
273 | int n = static_cast<int>(std::max(std::max(fabsf(top.red - bottom.red), |
274 | fabsf(top.green - bottom.green)), |
275 | std::max(fabsf(top.blue - bottom.blue), |
276 | fabsf(top.alpha - bottom.alpha))) * 255); |
277 | n = std::max(n, 1); |
278 | for (int i = 0; i < n; ++i) |
279 | { |
280 | SDL_Rect rect; |
281 | if (direction == VERTICAL || direction == VERTICAL_SECTOR) |
282 | { |
283 | rect.x = static_cast<int>(region.get_left()); |
284 | rect.y = static_cast<int>(region.get_bottom() * static_cast<float>(i) / static_cast<float>(n)); |
285 | rect.w = static_cast<int>(region.get_right()); |
286 | rect.h = static_cast<int>((region.get_bottom() * static_cast<float>(i+1) / static_cast<float>(n)) - static_cast<float>(rect.y)); |
287 | } |
288 | else |
289 | { |
290 | rect.x = static_cast<int>(region.get_right() * static_cast<float>(i) / static_cast<float>(n)); |
291 | rect.y = static_cast<int>(region.get_top()); |
292 | rect.w = static_cast<int>((region.get_right() * static_cast<float>(i+1) / static_cast<float>(n)) - static_cast<float>(rect.x)); |
293 | rect.h = static_cast<int>(region.get_bottom()); |
294 | } |
295 | |
296 | float p = static_cast<float>(i+1) / static_cast<float>(n); |
297 | Uint8 r, g, b, a; |
298 | |
299 | if ( direction == HORIZONTAL_SECTOR || direction == VERTICAL_SECTOR) |
300 | { |
301 | float begin_percentage = region.get_left() * -1 / region.get_right(); |
302 | r = static_cast<Uint8>(((1.0f - begin_percentage - p) * top.red + (p + begin_percentage) * bottom.red) * 255); |
303 | g = static_cast<Uint8>(((1.0f - begin_percentage - p) * top.green + (p + begin_percentage) * bottom.green) * 255); |
304 | b = static_cast<Uint8>(((1.0f - begin_percentage - p) * top.blue + (p + begin_percentage) * bottom.blue) * 255); |
305 | a = static_cast<Uint8>(((1.0f - begin_percentage - p) * top.alpha + (p + begin_percentage) * bottom.alpha) * 255); |
306 | } |
307 | else |
308 | { |
309 | r = static_cast<Uint8>(((1.0f - p) * top.red + p * bottom.red) * 255); |
310 | g = static_cast<Uint8>(((1.0f - p) * top.green + p * bottom.green) * 255); |
311 | b = static_cast<Uint8>(((1.0f - p) * top.blue + p * bottom.blue) * 255); |
312 | a = static_cast<Uint8>(((1.0f - p) * top.alpha + p * bottom.alpha) * 255); |
313 | } |
314 | |
315 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, blend2sdl(request.blend)); |
316 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
317 | SDL_RenderFillRect(m_sdl_renderer, &rect); |
318 | } |
319 | } |
320 | |
321 | void |
322 | SDLPainter::draw_filled_rect(const FillRectRequest& request) |
323 | { |
324 | SDL_Rect rect = to_sdl_rect(request.rect); |
325 | |
326 | Uint8 r = static_cast<Uint8>(request.color.red * 255); |
327 | Uint8 g = static_cast<Uint8>(request.color.green * 255); |
328 | Uint8 b = static_cast<Uint8>(request.color.blue * 255); |
329 | Uint8 a = static_cast<Uint8>(request.color.alpha * 255); |
330 | |
331 | int radius = std::min(std::min(rect.h / 2, rect.w / 2), |
332 | static_cast<int>(request.radius)); |
333 | |
334 | if (radius) |
335 | { |
336 | int slices = radius; |
337 | |
338 | // rounded top and bottom parts |
339 | std::vector<SDL_Rect> rects; |
340 | rects.reserve(2*slices + 1); |
341 | for (int i = 0; i < slices; ++i) |
342 | { |
343 | float p = (static_cast<float>(i) + 0.5f) / static_cast<float>(slices); |
344 | int xoff = radius - static_cast<int>(sqrtf(1.0f - p * p) * static_cast<float>(radius)); |
345 | |
346 | SDL_Rect tmp; |
347 | tmp.x = rect.x + xoff; |
348 | tmp.y = rect.y + (radius - i); |
349 | tmp.w = rect.w - 2*(xoff); |
350 | tmp.h = 1; |
351 | rects.push_back(tmp); |
352 | |
353 | SDL_Rect tmp2; |
354 | tmp2.x = rect.x + xoff; |
355 | tmp2.y = rect.y + rect.h - radius + i; |
356 | tmp2.w = rect.w - 2*xoff; |
357 | tmp2.h = 1; |
358 | |
359 | if (tmp2.y != tmp.y) |
360 | { |
361 | rects.push_back(tmp2); |
362 | } |
363 | } |
364 | |
365 | if (2*radius < rect.h) |
366 | { |
367 | // center rectangle |
368 | SDL_Rect tmp; |
369 | tmp.x = rect.x; |
370 | tmp.y = rect.y + radius + 1; |
371 | tmp.w = rect.w; |
372 | tmp.h = rect.h - 2*radius - 1; |
373 | rects.push_back(tmp); |
374 | } |
375 | |
376 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_BLEND); |
377 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
378 | SDL_RenderFillRects(m_sdl_renderer, &*rects.begin(), static_cast<int>(rects.size())); |
379 | } |
380 | else |
381 | { |
382 | if ((rect.w != 0) && (rect.h != 0)) |
383 | { |
384 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_BLEND); |
385 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
386 | SDL_RenderFillRect(m_sdl_renderer, &rect); |
387 | } |
388 | } |
389 | } |
390 | |
391 | void |
392 | SDLPainter::draw_inverse_ellipse(const InverseEllipseRequest& request) |
393 | { |
394 | float x = request.pos.x; |
395 | float w = request.size.x; |
396 | float h = request.size.y; |
397 | |
398 | int top = static_cast<int>(request.pos.y - (h / 2)); |
399 | |
400 | const Viewport& viewport = m_video_system.get_viewport(); |
401 | |
402 | const int max_slices = 256; |
403 | SDL_Rect rects[2*max_slices+2]; |
404 | int slices = std::min(static_cast<int>(request.size.y), max_slices); |
405 | for (int i = 0; i < slices; ++i) |
406 | { |
407 | float p = ((static_cast<float>(i) + 0.5f) / static_cast<float>(slices)) * 2.0f - 1.0f; |
408 | int xoff = static_cast<int>(sqrtf(1.0f - p*p) * w / 2); |
409 | |
410 | SDL_Rect& left = rects[2*i+0]; |
411 | SDL_Rect& right = rects[2*i+1]; |
412 | |
413 | left.x = 0; |
414 | left.y = top + (i * static_cast<int>(h) / slices); |
415 | left.w = static_cast<int>(x) - xoff; |
416 | left.h = top + ((i+1) * static_cast<int>(h) / slices) - left.y; |
417 | |
418 | right.x = static_cast<int>(x) + xoff; |
419 | right.y = left.y; |
420 | right.w = viewport.get_screen_width() - right.x; |
421 | right.h = left.h; |
422 | } |
423 | |
424 | SDL_Rect& top_rect = rects[2*slices+0]; |
425 | SDL_Rect& bottom_rect = rects[2*slices+1]; |
426 | |
427 | top_rect.x = 0; |
428 | top_rect.y = 0; |
429 | top_rect.w = viewport.get_screen_width(); |
430 | top_rect.h = top; |
431 | |
432 | bottom_rect.x = 0; |
433 | bottom_rect.y = top + static_cast<int>(h); |
434 | bottom_rect.w = viewport.get_screen_width(); |
435 | bottom_rect.h = viewport.get_screen_height() - bottom_rect.y; |
436 | |
437 | Uint8 r = static_cast<Uint8>(request.color.red * 255); |
438 | Uint8 g = static_cast<Uint8>(request.color.green * 255); |
439 | Uint8 b = static_cast<Uint8>(request.color.blue * 255); |
440 | Uint8 a = static_cast<Uint8>(request.color.alpha * 255); |
441 | |
442 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_BLEND); |
443 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
444 | SDL_RenderFillRects(m_sdl_renderer, rects, 2*slices+2); |
445 | } |
446 | |
447 | void |
448 | SDLPainter::draw_line(const LineRequest& request) |
449 | { |
450 | Uint8 r = static_cast<Uint8>(request.color.red * 255); |
451 | Uint8 g = static_cast<Uint8>(request.color.green * 255); |
452 | Uint8 b = static_cast<Uint8>(request.color.blue * 255); |
453 | Uint8 a = static_cast<Uint8>(request.color.alpha * 255); |
454 | |
455 | int x1 = static_cast<int>(request.pos.x); |
456 | int y1 = static_cast<int>(request.pos.y); |
457 | int x2 = static_cast<int>(request.dest_pos.x); |
458 | int y2 = static_cast<int>(request.dest_pos.y); |
459 | |
460 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_BLEND); |
461 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
462 | SDL_RenderDrawLine(m_sdl_renderer, x1, y1, x2, y2); |
463 | } |
464 | |
465 | namespace { |
466 | |
467 | Rectf |
468 | make_edge(int x1, int y1, int x2, int y2) |
469 | { |
470 | if (y1 < y2) |
471 | { |
472 | return Rectf(Vector(static_cast<float>(x1), static_cast<float>(y1)), |
473 | Vector(static_cast<float>(x2), static_cast<float>(y2))); |
474 | } |
475 | else |
476 | { |
477 | return Rectf(Vector(static_cast<float>(x2), static_cast<float>(y2)), |
478 | Vector(static_cast<float>(x1), static_cast<float>(y1))); |
479 | } |
480 | } |
481 | |
482 | void |
483 | draw_span_between_edges(SDL_Renderer* renderer, const Rectf& e1, const Rectf& e2) |
484 | { |
485 | // calculate difference between the y coordinates |
486 | // of the first edge and return if 0 |
487 | float e1ydiff = static_cast<float>(e1.get_bottom() - e1.get_top()); |
488 | if (e1ydiff == 0.0f) |
489 | return; |
490 | |
491 | // calculate difference between the y coordinates |
492 | // of the second edge and return if 0 |
493 | float e2ydiff = static_cast<float>(e2.get_bottom() - e2.get_top()); |
494 | if (e2ydiff == 0.0f) |
495 | return; |
496 | |
497 | float e1xdiff = e1.get_right() - e1.get_left(); |
498 | float e2xdiff = e2.get_right() - e2.get_left(); |
499 | float factor1 = (e2.get_top() - e1.get_top()) / e1ydiff; |
500 | float factorStep1 = 1.0f / e1ydiff; |
501 | float factor2 = 0.0f; |
502 | float factorStep2 = 1.0f / e2ydiff; |
503 | |
504 | for (int y = static_cast<int>(e2.get_top()); y < static_cast<int>(e2.get_bottom()); y++) { |
505 | SDL_RenderDrawLine(renderer, |
506 | static_cast<int>(e1.get_left() + e1xdiff * factor1), y, |
507 | static_cast<int>(e2.get_left() + e2xdiff * factor2), y); |
508 | factor1 += factorStep1; |
509 | factor2 += factorStep2; |
510 | } |
511 | } |
512 | |
513 | } //namespace |
514 | |
515 | void |
516 | SDLPainter::draw_triangle(const TriangleRequest& request) |
517 | { |
518 | Uint8 r = static_cast<Uint8>(request.color.red * 255); |
519 | Uint8 g = static_cast<Uint8>(request.color.green * 255); |
520 | Uint8 b = static_cast<Uint8>(request.color.blue * 255); |
521 | Uint8 a = static_cast<Uint8>(request.color.alpha * 255); |
522 | |
523 | int x1 = static_cast<int>(request.pos1.x); |
524 | int y1 = static_cast<int>(request.pos1.y); |
525 | int x2 = static_cast<int>(request.pos2.x); |
526 | int y2 = static_cast<int>(request.pos2.y); |
527 | int x3 = static_cast<int>(request.pos3.x); |
528 | int y3 = static_cast<int>(request.pos3.y); |
529 | |
530 | Rectf edges[3]; |
531 | edges[0] = make_edge(x1, y1, x2, y2); |
532 | edges[1] = make_edge(x2, y2, x3, y3); |
533 | edges[2] = make_edge(x3, y3, x1, y1); |
534 | |
535 | int maxLength = 0; |
536 | int longEdge = 0; |
537 | |
538 | // find edge with the greatest length in the y axis |
539 | for (int i = 0; i < 3; i++) { |
540 | int length = static_cast<int>(edges[i].get_bottom() - edges[i].get_top()); |
541 | if (length > maxLength) { |
542 | maxLength = length; |
543 | longEdge = i; |
544 | } |
545 | } |
546 | int shortEdge1 = (longEdge + 1) % 3; |
547 | int shortEdge2 = (longEdge + 2) % 3; |
548 | |
549 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_BLEND); |
550 | SDL_SetRenderDrawColor(m_sdl_renderer, r, g, b, a); |
551 | |
552 | draw_span_between_edges(m_sdl_renderer, edges[longEdge], edges[shortEdge1]); |
553 | draw_span_between_edges(m_sdl_renderer, edges[longEdge], edges[shortEdge2]); |
554 | } |
555 | |
556 | void |
557 | SDLPainter::clear(const Color& color) |
558 | { |
559 | SDL_SetRenderDrawColor(m_sdl_renderer, color.r8(), color.g8(), color.b8(), color.a8()); |
560 | |
561 | if (m_cliprect) |
562 | { |
563 | SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_NONE); |
564 | SDL_RenderFillRect(m_sdl_renderer, &*m_cliprect); |
565 | } |
566 | else |
567 | { |
568 | // This ignores the cliprect: |
569 | SDL_RenderClear(m_sdl_renderer); |
570 | } |
571 | } |
572 | |
573 | void |
574 | SDLPainter::set_clip_rect(const Rect& rect) |
575 | { |
576 | m_cliprect = SDL_Rect{ rect.left, |
577 | rect.top, |
578 | rect.get_width(), |
579 | rect.get_height() }; |
580 | |
581 | int ret = SDL_RenderSetClipRect(m_sdl_renderer, &*m_cliprect); |
582 | if (ret < 0) |
583 | { |
584 | log_warning << "SDLPainter::set_clip_rect(): SDL_RenderSetClipRect() failed: " << SDL_GetError() << std::endl; |
585 | } |
586 | } |
587 | |
588 | void |
589 | SDLPainter::clear_clip_rect() |
590 | { |
591 | m_cliprect.reset(); |
592 | |
593 | int ret = SDL_RenderSetClipRect(m_sdl_renderer, nullptr); |
594 | if (ret < 0) |
595 | { |
596 | log_warning << "SDLPainter::clear_clip_rect(): SDL_RenderSetClipRect() failed: " << SDL_GetError() << std::endl; |
597 | } |
598 | } |
599 | |
600 | void |
601 | SDLPainter::get_pixel(const GetPixelRequest& request) const |
602 | { |
603 | const Rect& rect = m_renderer.get_rect(); |
604 | const Size& logical_size = m_renderer.get_logical_size(); |
605 | |
606 | SDL_Rect srcrect; |
607 | srcrect.x = rect.left + static_cast<int>(request.pos.x * static_cast<float>(rect.get_width()) / static_cast<float>(logical_size.width)); |
608 | srcrect.y = rect.top + static_cast<int>(request.pos.y * static_cast<float>(rect.get_height()) / static_cast<float>(logical_size.height)); |
609 | srcrect.w = 1; |
610 | srcrect.h = 1; |
611 | |
612 | Uint8 pixel[4]; |
613 | int ret = SDL_RenderReadPixels(m_sdl_renderer, &srcrect, |
614 | SDL_PIXELFORMAT_RGB888, |
615 | pixel, |
616 | 1); |
617 | if (ret != 0) |
618 | { |
619 | log_warning << "failed to read pixels: " << SDL_GetError() << std::endl; |
620 | } |
621 | |
622 | *(request.color_ptr) = Color::from_rgb888(pixel[2], pixel[1], pixel[0]); |
623 | } |
624 | |
625 | /* EOF */ |
626 | |