| 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 | |