1 | // SuperTux |
2 | // Copyright (C) 2016 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/canvas.hpp" |
18 | |
19 | #include <algorithm> |
20 | |
21 | #include "supertux/globals.hpp" |
22 | #include "util/log.hpp" |
23 | #include "util/obstackpp.hpp" |
24 | #include "video/drawing_request.hpp" |
25 | #include "video/painter.hpp" |
26 | #include "video/renderer.hpp" |
27 | #include "video/surface.hpp" |
28 | #include "video/video_system.hpp" |
29 | |
30 | Canvas::Canvas(DrawingContext& context, obstack& obst) : |
31 | m_context(context), |
32 | m_obst(obst), |
33 | m_requests() |
34 | { |
35 | } |
36 | |
37 | Canvas::~Canvas() |
38 | { |
39 | clear(); |
40 | } |
41 | |
42 | void |
43 | Canvas::clear() |
44 | { |
45 | for (auto& request : m_requests) |
46 | { |
47 | request->~DrawingRequest(); |
48 | } |
49 | m_requests.clear(); |
50 | } |
51 | |
52 | void |
53 | Canvas::render(Renderer& renderer, Filter filter) |
54 | { |
55 | // On a regular level, each frame has around 50-250 requests (before |
56 | // batching it was 1000-3000), the sort comparator function is |
57 | // called approximatly 3-7 times for each request. |
58 | std::stable_sort(m_requests.begin(), m_requests.end(), |
59 | [](const DrawingRequest* r1, const DrawingRequest* r2){ |
60 | return r1->layer < r2->layer; |
61 | }); |
62 | |
63 | Painter& painter = renderer.get_painter(); |
64 | |
65 | for (const auto& i : m_requests) { |
66 | const DrawingRequest& request = *i; |
67 | |
68 | if (filter == BELOW_LIGHTMAP && request.layer >= LAYER_LIGHTMAP) |
69 | continue; |
70 | else if (filter == ABOVE_LIGHTMAP && request.layer <= LAYER_LIGHTMAP) |
71 | continue; |
72 | |
73 | switch (request.type) { |
74 | case TEXTURE: |
75 | painter.draw_texture(static_cast<const TextureRequest&>(request)); |
76 | break; |
77 | |
78 | case GRADIENT: |
79 | painter.draw_gradient(static_cast<const GradientRequest&>(request)); |
80 | break; |
81 | |
82 | case FILLRECT: |
83 | painter.draw_filled_rect(static_cast<const FillRectRequest&>(request)); |
84 | break; |
85 | |
86 | case INVERSEELLIPSE: |
87 | painter.draw_inverse_ellipse(static_cast<const InverseEllipseRequest&>(request)); |
88 | break; |
89 | |
90 | case LINE: |
91 | painter.draw_line(static_cast<const LineRequest&>(request)); |
92 | break; |
93 | |
94 | case TRIANGLE: |
95 | painter.draw_triangle(static_cast<const TriangleRequest&>(request)); |
96 | break; |
97 | |
98 | case GETPIXEL: |
99 | painter.get_pixel(static_cast<const GetPixelRequest&>(request)); |
100 | break; |
101 | } |
102 | } |
103 | } |
104 | |
105 | void |
106 | Canvas::draw_surface(const SurfacePtr& surface, |
107 | const Vector& position, float angle, const Color& color, const Blend& blend, |
108 | int layer) |
109 | { |
110 | if (!surface) return; |
111 | |
112 | const auto& cliprect = m_context.get_cliprect(); |
113 | |
114 | // discard clipped surface |
115 | if (position.x > cliprect.get_right() || |
116 | position.y > cliprect.get_bottom() || |
117 | position.x + static_cast<float>(surface->get_width()) < cliprect.get_left() || |
118 | position.y + static_cast<float>(surface->get_height()) < cliprect.get_top()) |
119 | return; |
120 | |
121 | auto request = new(m_obst) TextureRequest(); |
122 | |
123 | request->type = TEXTURE; |
124 | request->layer = layer; |
125 | request->flip = m_context.transform().flip ^ surface->get_flip(); |
126 | request->alpha = m_context.transform().alpha; |
127 | request->blend = blend; |
128 | |
129 | request->srcrects.emplace_back(Rectf(surface->get_region())); |
130 | request->dstrects.emplace_back(Rectf(apply_translate(position), Size(surface->get_width(), surface->get_height()))); |
131 | request->angles.emplace_back(angle); |
132 | request->texture = surface->get_texture().get(); |
133 | request->displacement_texture = surface->get_displacement_texture().get(); |
134 | request->color = color; |
135 | |
136 | m_requests.push_back(request); |
137 | } |
138 | |
139 | void |
140 | Canvas::draw_surface(const SurfacePtr& surface, const Vector& position, int layer) |
141 | { |
142 | draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer); |
143 | } |
144 | |
145 | void |
146 | Canvas::draw_surface_scaled(const SurfacePtr& surface, const Rectf& dstrect, |
147 | int layer, const PaintStyle& style) |
148 | { |
149 | draw_surface_part(surface, Rectf(0.0f, 0.0f, static_cast<float>(surface->get_width()), static_cast<float>(surface->get_height())), |
150 | dstrect, layer, style); |
151 | } |
152 | |
153 | void |
154 | Canvas::draw_surface_part(const SurfacePtr& surface, const Rectf& srcrect, const Rectf& dstrect, |
155 | int layer, const PaintStyle& style) |
156 | { |
157 | if (!surface) return; |
158 | |
159 | auto request = new(m_obst) TextureRequest(); |
160 | |
161 | request->type = TEXTURE; |
162 | request->layer = layer; |
163 | request->flip = m_context.transform().flip ^ surface->get_flip(); |
164 | request->alpha = m_context.transform().alpha * style.get_alpha(); |
165 | request->blend = style.get_blend(); |
166 | |
167 | request->srcrects.emplace_back(srcrect); |
168 | request->dstrects.emplace_back(apply_translate(dstrect.p1()), dstrect.get_size()); |
169 | request->angles.emplace_back(0.0f); |
170 | request->texture = surface->get_texture().get(); |
171 | request->displacement_texture = surface->get_displacement_texture().get(); |
172 | request->color = style.get_color(); |
173 | |
174 | m_requests.push_back(request); |
175 | } |
176 | |
177 | void |
178 | Canvas::draw_surface_batch(const SurfacePtr& surface, |
179 | std::vector<Rectf> srcrects, |
180 | std::vector<Rectf> dstrects, |
181 | const Color& color, |
182 | int layer) |
183 | { |
184 | const size_t len = srcrects.size(); |
185 | draw_surface_batch(surface, |
186 | std::move(srcrects), |
187 | std::move(dstrects), |
188 | std::vector<float>(len, 0.0f), |
189 | color, layer); |
190 | } |
191 | |
192 | void |
193 | Canvas::draw_surface_batch(const SurfacePtr& surface, |
194 | std::vector<Rectf> srcrects, |
195 | std::vector<Rectf> dstrects, |
196 | std::vector<float> angles, |
197 | const Color& color, |
198 | int layer) |
199 | { |
200 | if (!surface) return; |
201 | |
202 | auto request = new(m_obst) TextureRequest(); |
203 | |
204 | request->type = TEXTURE; |
205 | request->layer = layer; |
206 | request->flip = m_context.transform().flip ^ surface->get_flip(); |
207 | request->alpha = m_context.transform().alpha; |
208 | request->color = color; |
209 | |
210 | request->srcrects = std::move(srcrects); |
211 | request->dstrects = std::move(dstrects); |
212 | request->angles = std::move(angles); |
213 | |
214 | for (auto& dstrect : request->dstrects) |
215 | { |
216 | dstrect = Rectf(apply_translate(dstrect.p1()), dstrect.get_size()); |
217 | } |
218 | |
219 | request->texture = surface->get_texture().get(); |
220 | request->displacement_texture = surface->get_displacement_texture().get(); |
221 | |
222 | m_requests.push_back(request); |
223 | } |
224 | |
225 | void |
226 | Canvas::draw_text(const FontPtr& font, const std::string& text, |
227 | const Vector& pos, FontAlignment alignment, int layer, const Color& color) |
228 | { |
229 | font->draw_text(*this, text, pos, alignment, layer, color); |
230 | } |
231 | |
232 | void |
233 | Canvas::draw_center_text(const FontPtr& font, const std::string& text, |
234 | const Vector& position, int layer, const Color& color) |
235 | { |
236 | draw_text(font, text, Vector(position.x + static_cast<float>(m_context.get_width()) / 2.0f, position.y), |
237 | ALIGN_CENTER, layer, color); |
238 | } |
239 | |
240 | void |
241 | Canvas::draw_gradient(const Color& top, const Color& bottom, int layer, |
242 | const GradientDirection& direction, const Rectf& region, |
243 | const Blend& blend) |
244 | { |
245 | auto request = new(m_obst) GradientRequest(); |
246 | |
247 | request->type = GRADIENT; |
248 | request->layer = layer; |
249 | |
250 | request->flip = m_context.transform().flip; |
251 | request->alpha = m_context.transform().alpha; |
252 | request->blend = blend; |
253 | |
254 | request->top = top; |
255 | request->bottom = bottom; |
256 | request->direction = direction; |
257 | request->region = Rectf(apply_translate(region.p1()), |
258 | apply_translate(region.p2())); |
259 | |
260 | m_requests.push_back(request); |
261 | } |
262 | |
263 | void |
264 | Canvas::draw_filled_rect(const Rectf& rect, const Color& color, |
265 | int layer) |
266 | { |
267 | draw_filled_rect(rect, color, 0.0f, layer); |
268 | } |
269 | |
270 | void |
271 | Canvas::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer) |
272 | { |
273 | auto request = new(m_obst) FillRectRequest; |
274 | |
275 | request->type = FILLRECT; |
276 | request->layer = layer; |
277 | |
278 | request->flip = m_context.transform().flip; |
279 | request->alpha = m_context.transform().alpha; |
280 | |
281 | request->rect = Rectf(apply_translate(rect.p1()), |
282 | rect.get_size()); |
283 | request->color = color; |
284 | request->color.alpha = color.alpha * m_context.transform().alpha; |
285 | request->radius = radius; |
286 | |
287 | m_requests.push_back(request); |
288 | } |
289 | |
290 | void |
291 | Canvas::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer) |
292 | { |
293 | auto request = new(m_obst) InverseEllipseRequest; |
294 | |
295 | request->type = INVERSEELLIPSE; |
296 | request->layer = layer; |
297 | |
298 | request->flip = m_context.transform().flip; |
299 | request->alpha = m_context.transform().alpha; |
300 | |
301 | request->pos = apply_translate(pos); |
302 | request->color = color; |
303 | request->color.alpha = color.alpha * m_context.transform().alpha; |
304 | request->size = size; |
305 | |
306 | m_requests.push_back(request); |
307 | } |
308 | |
309 | void |
310 | Canvas::draw_line(const Vector& pos1, const Vector& pos2, const Color& color, int layer) |
311 | { |
312 | auto request = new(m_obst) LineRequest; |
313 | |
314 | request->type = LINE; |
315 | request->layer = layer; |
316 | |
317 | request->flip = m_context.transform().flip; |
318 | request->alpha = m_context.transform().alpha; |
319 | |
320 | request->pos = apply_translate(pos1); |
321 | request->color = color; |
322 | request->color.alpha = color.alpha * m_context.transform().alpha; |
323 | request->dest_pos = apply_translate(pos2); |
324 | |
325 | m_requests.push_back(request); |
326 | } |
327 | |
328 | void |
329 | Canvas::draw_triangle(const Vector& pos1, const Vector& pos2, const Vector& pos3, const Color& color, int layer) |
330 | { |
331 | auto request = new(m_obst) TriangleRequest; |
332 | |
333 | request->type = TRIANGLE; |
334 | request->layer = layer; |
335 | |
336 | request->flip = m_context.transform().flip; |
337 | request->alpha = m_context.transform().alpha; |
338 | |
339 | request->pos1 = apply_translate(pos1); |
340 | request->pos2 = apply_translate(pos2); |
341 | request->pos3 = apply_translate(pos3); |
342 | request->color = color; |
343 | request->color.alpha = color.alpha * m_context.transform().alpha; |
344 | |
345 | m_requests.push_back(request); |
346 | } |
347 | |
348 | void |
349 | Canvas::get_pixel(const Vector& position, const std::shared_ptr<Color>& color_out) |
350 | { |
351 | assert(color_out); |
352 | |
353 | Vector pos = apply_translate(position); |
354 | |
355 | // There is no light offscreen. |
356 | if (pos.x >= static_cast<float>(m_context.get_viewport().get_width()) || |
357 | pos.y >= static_cast<float>(m_context.get_viewport().get_height()) || |
358 | pos.x < 0.0f || |
359 | pos.y < 0.0f) |
360 | { |
361 | *color_out = Color(0, 0, 0); |
362 | return; |
363 | } |
364 | |
365 | auto request = new(m_obst) GetPixelRequest(); |
366 | |
367 | request->layer = LAYER_GETPIXEL; |
368 | request->pos = pos; |
369 | request->color_ptr = color_out; |
370 | |
371 | m_requests.push_back(request); |
372 | } |
373 | |
374 | Vector |
375 | Canvas::apply_translate(const Vector& pos) const |
376 | { |
377 | Vector translation = m_context.transform().translation; |
378 | return (pos - translation) + Vector(static_cast<float>(m_context.get_viewport().left), |
379 | static_cast<float>(m_context.get_viewport().top)); |
380 | } |
381 | |
382 | /* EOF */ |
383 | |