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