1 #include "CUIDrawUtil.h"
2
3 #include "ShaderProgram.h"
4 #include "../util/Logger.h"
5 #include "../util/Directories.h"
6 #include "../util/OptionsDB.h"
7
8 #include <GG/DrawUtil.h>
9
10 #include <cmath>
11
12
13 namespace {
FindIsoscelesTriangleVertices(const GG::Pt & ul,const GG::Pt & lr,ShapeOrientation orientation,double & x1_,double & y1_,double & x2_,double & y2_,double & x3_,double & y3_)14 void FindIsoscelesTriangleVertices(const GG::Pt& ul, const GG::Pt& lr, ShapeOrientation orientation,
15 double& x1_, double& y1_, double& x2_, double& y2_, double& x3_, double& y3_)
16 {
17 switch (orientation) {
18 case ShapeOrientation::UP:
19 x1_ = Value(ul.x);
20 y1_ = Value(lr.y);
21 x2_ = Value(lr.x);
22 y2_ = Value(lr.y);
23 x3_ = Value((ul.x + lr.x) / 2.0);
24 y3_ = Value(ul.y);
25 break;
26 case ShapeOrientation::DOWN:
27 x1_ = Value(lr.x);
28 y1_ = Value(ul.y);
29 x2_ = Value(ul.x);
30 y2_ = Value(ul.y);
31 x3_ = Value((ul.x + lr.x) / 2.0);
32 y3_ = Value(lr.y);
33 break;
34 case ShapeOrientation::LEFT:
35 x1_ = Value(lr.x);
36 y1_ = Value(lr.y);
37 x2_ = Value(lr.x);
38 y2_ = Value(ul.y);
39 x3_ = Value(ul.x);
40 y3_ = Value((ul.y + lr.y) / 2.0);
41 break;
42 default:
43 ErrorLogger() << "FindIsoscelesTriangleVertices passed invalid orientation";
44 case ShapeOrientation::RIGHT:
45 x1_ = Value(ul.x);
46 y1_ = Value(ul.y);
47 x2_ = Value(ul.x);
48 y2_ = Value(lr.y);
49 x3_ = Value(lr.x);
50 y3_ = Value((ul.y + lr.y) / 2.0);
51 break;
52 }
53 }
54 }
55
BufferStoreCircleArcVertices(GG::GL2DVertexBuffer & buffer,const GG::Pt & ul,const GG::Pt & lr,double theta1,double theta2,bool filled_shape,int num_slices,bool fan)56 void BufferStoreCircleArcVertices(GG::GL2DVertexBuffer& buffer, const GG::Pt& ul, const GG::Pt& lr,
57 double theta1, double theta2, bool filled_shape, int num_slices, bool fan)
58 {
59 int wd = Value(lr.x - ul.x), ht = Value(lr.y - ul.y);
60 double center_x = Value(ul.x + wd / 2.0);
61 double center_y = Value(ul.y + ht / 2.0);
62 double r = std::min(wd / 2.0, ht / 2.0);
63 const double PI = 3.141594;
64
65 // correct theta* values to range [0, 2pi)
66 if (theta1 < 0)
67 theta1 += (int(-theta1 / (2 * PI)) + 1) * 2 * PI;
68 else if (theta1 >= 2 * PI)
69 theta1 -= int(theta1 / (2 * PI)) * 2 * PI;
70 if (theta2 < 0)
71 theta2 += (int(-theta2 / (2 * PI)) + 1) * 2 * PI;
72 else if (theta2 >= 2 * PI)
73 theta2 -= int(theta2 / (2 * PI)) * 2 * PI;
74
75 int SLICES = 50;
76 if (num_slices <= 0)
77 SLICES = std::min(std::max(12, 3 + std::max(wd, ht)), 50); // this is a good guess at how much to tesselate the circle coordinates (50 segments max)
78 else
79 SLICES = num_slices;
80 const double HORZ_THETA = (2 * PI) / SLICES;
81
82 static std::map<int, std::vector<double>> unit_circle_coords;
83 std::vector<double>& unit_vertices = unit_circle_coords[SLICES];
84 bool calc_vertices = unit_vertices.size() == 0;
85 if (calc_vertices) {
86 unit_vertices.resize(2 * (SLICES + 1), 0.0);
87 double theta = 0.0f;
88 for (int j = 0; j <= SLICES; theta += HORZ_THETA, ++j) { // calculate x,y values for each point on a unit circle divided into SLICES arcs
89 unit_vertices[j*2] = std::cos(-theta);
90 unit_vertices[j*2+1] = std::sin(-theta);
91 }
92 }
93 int first_slice_idx = int(theta1 / HORZ_THETA + 1);
94 int last_slice_idx = int(theta2 / HORZ_THETA - 1);
95 if (theta1 >= theta2)
96 last_slice_idx += SLICES;
97
98 if (fan) { // store a triangle fan vertex list, specifying each vertex just once
99
100 if (filled_shape) // specify the central vertex first, to act as the pivot vertex for the fan
101 buffer.store(static_cast<GLfloat>(center_x), static_cast<GLfloat>(center_y));
102 // if not filled_shape, assumes a previously-specified vertex in the buffer will act as the pivot for the fan
103
104 // point on circle at angle theta1
105 double theta1_x = std::cos(-theta1), theta1_y = std::sin(-theta1);
106 buffer.store(static_cast<GLfloat>(center_x + theta1_x * r), static_cast<GLfloat>(center_y + theta1_y * r));
107
108 // angles in between theta1 and theta2, if any
109 for (int i = first_slice_idx; i <= last_slice_idx + 1; ++i) {
110 int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1;
111 buffer.store(static_cast<GLfloat>(center_x + unit_vertices[X] * r), static_cast<GLfloat>(center_y + unit_vertices[Y] * r));
112 }
113
114 // theta2
115 double theta2_x = std::cos(-theta2), theta2_y = std::sin(-theta2);
116 buffer.store(static_cast<GLfloat>(center_x + theta2_x * r), static_cast<GLfloat>(center_y + theta2_y * r));
117
118 } else { // (not a fan) store a list of complete lines / triangles
119 // if storing a filled_shape, the first point in each triangle should be the centre of the arc
120 std::pair<GLfloat, GLfloat> first_point = {static_cast<GLfloat>(center_x), static_cast<GLfloat>(center_y)};
121 // (not used for non-filled-shape)
122
123 // angles in between theta1 and theta2, if any
124 for (int i = first_slice_idx - 1; i <= last_slice_idx; ++i) {
125 if (filled_shape) {
126 buffer.store(first_point.first, first_point.second);
127 // list of triangles: need two more vertices on the arc per starting vertex
128 }
129 // else: list of lines, with two vertices each
130
131 int X = (i > SLICES ? (i - SLICES) : i) * 2;
132 int Y = X + 1;
133 buffer.store(static_cast<GLfloat>(center_x + unit_vertices[X] * r), static_cast<GLfloat>(center_y + unit_vertices[Y] * r));
134
135 int next_i = i + 1;
136 X = (next_i > SLICES ? (next_i - SLICES) : next_i) * 2;
137 Y = X + 1;
138 buffer.store(static_cast<GLfloat>(center_x + unit_vertices[X] * r), static_cast<GLfloat>(center_y + unit_vertices[Y] * r));
139 }
140
141 // theta2
142 if (filled_shape) {
143 buffer.store(first_point.first, first_point.second);
144 }
145
146 int i = last_slice_idx + 1;
147 int X = (i > SLICES ? (i - SLICES) : i) * 2;
148 int Y = X + 1;
149 buffer.store(static_cast<GLfloat>(center_x + unit_vertices[X] * r), static_cast<GLfloat>(center_y + unit_vertices[Y] * r));
150
151 double theta2_x = std::cos(-theta2), theta2_y = std::sin(-theta2);
152 buffer.store(static_cast<GLfloat>(center_x + theta2_x * r), static_cast<GLfloat>(center_y + theta2_y * r));
153 }
154 }
155
AdjustBrightness(GG::Clr & color,int amount,bool jointly_capped)156 void AdjustBrightness(GG::Clr& color, int amount, bool jointly_capped)
157 {
158 if (jointly_capped) {
159 int maxVal = std::max(std::max(color.r, color.g), color.b);
160 amount = std::min(amount, 255-maxVal);
161 }
162 color.r = static_cast<unsigned char>(std::max(0, std::min(color.r + amount, 255)));
163 color.g = static_cast<unsigned char>(std::max(0, std::min(color.g + amount, 255)));
164 color.b = static_cast<unsigned char>(std::max(0, std::min(color.b + amount, 255)));
165 }
166
AdjustBrightness(GG::Clr & color,double amount,bool jointly_capped)167 void AdjustBrightness(GG::Clr& color, double amount, bool jointly_capped)
168 {
169 if (jointly_capped) {
170 int maxVal = std::max(std::max(color.r, color.g), color.b);
171 amount = std::min(amount, 255.0/maxVal);
172 }
173 color.r = static_cast<unsigned char>(std::max(0, std::min(static_cast<int>(color.r * amount), 255)));
174 color.g = static_cast<unsigned char>(std::max(0, std::min(static_cast<int>(color.g * amount), 255)));
175 color.b = static_cast<unsigned char>(std::max(0, std::min(static_cast<int>(color.b * amount), 255)));
176 }
177
OpaqueColor(const GG::Clr & color)178 GG::Clr OpaqueColor(const GG::Clr& color)
179 {
180 GG::Clr retval = color;
181 retval.a = static_cast<unsigned char>(255);
182 return retval;
183 }
184
BufferStoreRectangle(GG::GL2DVertexBuffer & buffer,const GG::Rect & area,const GG::Rect & border_thickness)185 void BufferStoreRectangle(GG::GL2DVertexBuffer& buffer,
186 const GG::Rect& area,
187 const GG::Rect& border_thickness)
188 {
189 GG::X inner_x1(area.ul.x + border_thickness.ul.x);
190 GG::Y inner_y1(area.ul.y + border_thickness.ul.y);
191 GG::X inner_x2(area.lr.x - border_thickness.lr.x);
192 GG::Y inner_y2(area.lr.y - border_thickness.lr.y);
193
194 buffer.reserve(14);
195 buffer.store(inner_x2, inner_y1);
196 buffer.store(area.lr.x, area.ul.y);
197 buffer.store(inner_x1, inner_y1);
198 buffer.store(area.ul.x, area.ul.y);
199 buffer.store(inner_x1, inner_y2);
200 buffer.store(area.ul.x, area.lr.y);
201 buffer.store(inner_x2, inner_y2);
202 buffer.store(area.lr.x, area.lr.y);
203 buffer.store(inner_x2, inner_y1);
204 buffer.store(area.lr.x, area.ul.y);
205
206 buffer.store(inner_x2, inner_y1);
207 buffer.store(inner_x1, inner_y1);
208 buffer.store(inner_x1, inner_y2);
209 buffer.store(inner_x2, inner_y2);
210 }
211
AngledCornerRectangle(const GG::Pt & ul,const GG::Pt & lr,GG::Clr color,GG::Clr border,int angle_offset,int thick,bool upper_left_angled,bool lower_right_angled,bool draw_bottom)212 void AngledCornerRectangle(const GG::Pt& ul, const GG::Pt& lr, GG::Clr color, GG::Clr border, int angle_offset, int thick,
213 bool upper_left_angled/* = true*/, bool lower_right_angled/* = true*/, bool draw_bottom/* = true*/)
214 {
215 glDisable(GL_TEXTURE_2D);
216
217 GG::GL2DVertexBuffer vert_buf;
218 vert_buf.reserve(14);
219 GG::Pt thick_pt = GG::Pt(GG::X(thick), GG::Y(thick));
220 BufferStoreAngledCornerRectangleVertices(vert_buf, ul + thick_pt, lr - thick_pt, angle_offset,
221 upper_left_angled, lower_right_angled, draw_bottom);
222
223 glDisable(GL_TEXTURE_2D);
224 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
225 glEnableClientState(GL_VERTEX_ARRAY);
226 glDisableClientState(GL_COLOR_ARRAY);
227 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
228
229 vert_buf.activate();
230
231 glColor(color);
232 glDrawArrays(GL_TRIANGLE_FAN, 0, vert_buf.size());
233 if (thick > 0) {
234 glColor(border);
235 glLineWidth(thick);
236 glDrawArrays(GL_LINE_STRIP, 0, vert_buf.size());
237 glLineWidth(1.0f);
238 }
239
240 glPopClientAttrib();
241
242 glEnable(GL_TEXTURE_2D);
243 }
244
BufferStoreAngledCornerRectangleVertices(GG::GL2DVertexBuffer & buffer,const GG::Pt & ul,const GG::Pt & lr,int angle_offset,bool upper_left_angled,bool lower_right_angled,bool connect_bottom_line)245 void BufferStoreAngledCornerRectangleVertices(GG::GL2DVertexBuffer& buffer, const GG::Pt& ul, const GG::Pt& lr,
246 int angle_offset, bool upper_left_angled,
247 bool lower_right_angled, bool connect_bottom_line)
248 {
249 // these are listed in CCW order
250 if (connect_bottom_line)
251 buffer.store(Value(ul.x), Value(lr.y));
252
253 if (lower_right_angled) {
254 buffer.store(Value(lr.x) - angle_offset - 3,Value(lr.y)); // don't know why, but - 3 here and the next line seem to make things symmetric top-left and bottom-right
255 buffer.store(Value(lr.x), Value(lr.y) - angle_offset - 3);
256 } else {
257 buffer.store(Value(lr.x), Value(lr.y));
258 }
259
260 buffer.store(Value(lr.x), Value(ul.y));
261
262 if (upper_left_angled) {
263 buffer.store(Value(ul.x) + angle_offset, Value(ul.y));
264 buffer.store(Value(ul.x), Value(ul.y) + angle_offset);
265 } else {
266 buffer.store(Value(ul.x), Value(ul.y));
267 }
268
269 buffer.store(Value(ul.x), Value(lr.y));
270 }
271
InAngledCornerRect(const GG::Pt & pt,const GG::Pt & ul,const GG::Pt & lr,int angle_offset,bool upper_left_angled,bool lower_right_angled)272 bool InAngledCornerRect(const GG::Pt& pt, const GG::Pt& ul, const GG::Pt& lr, int angle_offset,
273 bool upper_left_angled/* = true*/, bool lower_right_angled)
274 {
275 bool retval = false;
276 if ((retval = ((ul <= pt) && (pt < lr)))) {
277 GG::Pt dist_from_ul = pt - ul;
278 GG::Pt dist_from_lr = lr - pt;
279 bool inside_upper_left_corner = upper_left_angled ? (angle_offset < Value(dist_from_ul.x) + Value(dist_from_ul.y)) : true;
280 bool inside_lower_right_corner = lower_right_angled ? (angle_offset < Value(dist_from_lr.x) + Value(dist_from_lr.y)) : true;
281 retval = inside_upper_left_corner && inside_lower_right_corner;
282 }
283 return retval;
284 }
285
Triangle(double x1,double y1,double x2,double y2,double x3,double y3,GG::Clr color,bool border)286 void Triangle(double x1, double y1, double x2, double y2, double x3, double y3, GG::Clr color, bool border) {
287 GG::Clr border_clr = color;
288 if (border)
289 AdjustBrightness(border_clr, 75);
290 GG::Triangle(GG::Pt(GG::X(x1), GG::Y(y1)),
291 GG::Pt(GG::X(x2), GG::Y(y2)),
292 GG::Pt(GG::X(x3), GG::Y(y3)),
293 color, border ? border_clr : GG::CLR_ZERO);
294 }
295
InTriangle(const GG::Pt & pt,double x1,double y1,double x2,double y2,double x3,double y3)296 bool InTriangle(const GG::Pt& pt, double x1, double y1, double x2, double y2, double x3, double y3) {
297 double vec_A_x = x2 - x1; // side A is the vector from pt1 to pt2
298 double vec_A_y = y2 - y1; // side A is the vector from pt1 to pt2
299 double vec_B_x = x3 - x2; // side B is the vector from pt2 to pt3
300 double vec_B_y = y3 - y2; // side B is the vector from pt2 to pt3
301 double vec_C_x = x1 - x3; // side C is the vector from pt3 to pt1
302 double vec_C_y = y1 - y3; // side C is the vector from pt3 to pt1
303 int pt_x = Value(pt.x);
304 int pt_y = Value(pt.y);
305 // take dot products of perpendicular vectors (normals of sides) with point pt, and sum the signs of these products
306 int sum = (0 < (pt_x - x1) * vec_A_y + (pt_y - y1) * -vec_A_x ? 1 : 0) +
307 (0 < (pt_x - x2) * vec_B_y + (pt_y - y2) * -vec_B_x ? 1 : 0) +
308 (0 < (pt_x - x3) * vec_C_y + (pt_y - y3) * -vec_C_x ? 1 : 0);
309 // if the products are all the same sign, the point is in the triangle
310 return (sum == 3 || sum == 0);
311 }
312
IsoscelesTriangle(const GG::Pt & ul,const GG::Pt & lr,ShapeOrientation orientation,GG::Clr color,bool border)313 void IsoscelesTriangle(const GG::Pt& ul, const GG::Pt& lr, ShapeOrientation orientation, GG::Clr color, bool border) {
314 double x1_, y1_, x2_, y2_, x3_, y3_;
315 FindIsoscelesTriangleVertices(ul, lr, orientation, x1_, y1_, x2_, y2_, x3_, y3_);
316 Triangle(x1_, y1_, x2_, y2_, x3_, y3_, color, border);
317 }
318
BufferStoreIsoscelesTriangle(GG::GL2DVertexBuffer & buffer,const GG::Pt & ul,const GG::Pt & lr,ShapeOrientation orientation)319 void BufferStoreIsoscelesTriangle(GG::GL2DVertexBuffer& buffer, const GG::Pt& ul, const GG::Pt& lr, ShapeOrientation orientation) {
320 double x1_, y1_, x2_, y2_, x3_, y3_;
321 FindIsoscelesTriangleVertices(ul, lr, orientation, x1_, y1_, x2_, y2_, x3_, y3_);
322 buffer.store(x1_, y1_);
323 buffer.store(x2_, y2_);
324 buffer.store(x3_, y3_);
325 }
326
InIsoscelesTriangle(const GG::Pt & pt,const GG::Pt & ul,const GG::Pt & lr,ShapeOrientation orientation)327 bool InIsoscelesTriangle(const GG::Pt& pt, const GG::Pt& ul, const GG::Pt& lr,
328 ShapeOrientation orientation)
329 {
330 double x1_, y1_, x2_, y2_, x3_, y3_;
331 FindIsoscelesTriangleVertices(ul, lr, orientation, x1_, y1_, x2_, y2_, x3_, y3_);
332 return InTriangle(pt, x1_, y1_, x2_, y2_, x3_, y3_);
333 }
334
CircleArc(const GG::Pt & ul,const GG::Pt & lr,double theta1,double theta2,bool filled_shape)335 void CircleArc(const GG::Pt& ul, const GG::Pt& lr, double theta1, double theta2,
336 bool filled_shape)
337 {
338 //std::cout << "CircleArc ul: " << ul << " lr: " << lr << " theta1: " << theta1 << " theta2: " << theta2 << " filled: " << filled_shape << std::endl;
339 GG::GL2DVertexBuffer vert_buf;
340 vert_buf.reserve(50); // max number that BufferStoreCircleArcVertices might add
341
342 BufferStoreCircleArcVertices(vert_buf, ul, lr, theta1, theta2, filled_shape, 0, true);
343
344 //glDisable(GL_TEXTURE_2D);
345 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
346 glEnableClientState(GL_VERTEX_ARRAY);
347 glDisableClientState(GL_COLOR_ARRAY);
348 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
349
350 vert_buf.activate();
351
352 if (filled_shape)
353 glDrawArrays(GL_TRIANGLE_FAN, 0, vert_buf.size());
354 else
355 glDrawArrays(GL_LINE_STRIP, 0, vert_buf.size());
356
357 glPopClientAttrib();
358 //glEnable(GL_TEXTURE_2D);
359 }
360
PartlyRoundedRect(const GG::Pt & ul,const GG::Pt & lr,int radius,bool ur_round,bool ul_round,bool ll_round,bool lr_round,bool fill)361 void PartlyRoundedRect(const GG::Pt& ul, const GG::Pt& lr, int radius,
362 bool ur_round, bool ul_round,
363 bool ll_round, bool lr_round, bool fill)
364 {
365 GG::GL2DVertexBuffer vert_buf;
366 vert_buf.reserve(210); // should be enough for 4 corners with 50 verts each, plus a bit extra to be safe
367
368 BufferStorePartlyRoundedRectVertices(vert_buf, ul, lr, radius, ur_round,
369 lr_round, ll_round, lr_round);
370
371 //glDisable(GL_TEXTURE_2D);
372 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
373 glEnableClientState(GL_VERTEX_ARRAY);
374 glDisableClientState(GL_COLOR_ARRAY);
375 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
376
377 vert_buf.activate();
378
379 if (fill)
380 glDrawArrays(GL_TRIANGLE_FAN, 0, vert_buf.size());
381 else
382 glDrawArrays(GL_LINE_LOOP, 0, vert_buf.size());
383
384 glPopClientAttrib();
385 //glEnable(GL_TEXTURE_2D);
386 }
387
BufferStorePartlyRoundedRectVertices(GG::GL2DVertexBuffer & buffer,const GG::Pt & ul,const GG::Pt & lr,int radius,bool ur_round,bool ul_round,bool ll_round,bool lr_round)388 void BufferStorePartlyRoundedRectVertices(GG::GL2DVertexBuffer& buffer, const GG::Pt& ul,
389 const GG::Pt& lr, int radius, bool ur_round,
390 bool ul_round, bool ll_round, bool lr_round)
391 {
392 const double PI = 3.141594;
393
394 buffer.store(lr.x, ul.y + radius);
395
396 if (ur_round)
397 BufferStoreCircleArcVertices(buffer, GG::Pt(lr.x - 2 * radius, ul.y),
398 GG::Pt(lr.x, ul.y + 2 * radius), 0.0, PI / 2.0, false);
399 else
400 buffer.store(lr.x, ul.y);
401
402 if (ul_round)
403 BufferStoreCircleArcVertices(buffer, ul, GG::Pt(ul.x + 2 * radius, ul.y + 2 * radius),
404 PI / 2.0, PI, false);
405 else
406 buffer.store(ul.x, ul.y);
407
408 if (ll_round)
409 BufferStoreCircleArcVertices(buffer, GG::Pt(ul.x, lr.y - 2 * radius),
410 GG::Pt(ul.x + 2 * radius, lr.y),
411 PI, 3.0 * PI / 2.0, false);
412 else
413 buffer.store(ul.x, lr.y);
414
415 if (lr_round)
416 BufferStoreCircleArcVertices(buffer, GG::Pt(lr.x - 2 * radius, lr.y - 2 * radius),
417 lr, 3.0 * PI / 2.0, 0.0, false);
418 else
419 buffer.store(lr.x, lr.y);
420
421 buffer.store(lr.x, ul.y + radius);
422 }
423
424 namespace {
425 const double TWO_PI = 2.0 * 3.14159;
426 }
427
428 class ScanlineRenderer::Impl {
429 public:
Impl()430 Impl() :
431 m_scanline_shader(),
432 m_failed_init(false)
433 { m_color = GG::CLR_BLACK; }
434
StartUsing()435 void StartUsing() {
436 if (m_failed_init)
437 return;
438
439 if (!m_scanline_shader) {
440 boost::filesystem::path shader_path = GetRootDataDir() / "default" / "shaders" / "scanlines.frag";
441 std::string shader_text;
442 if (!ReadFile(shader_path, shader_text)) {
443 ErrorLogger() << "ScanlineRenderer failed to read shader at path " << shader_path.string();
444 m_failed_init = true;
445 return;
446 }
447 m_scanline_shader = ShaderProgram::shaderProgramFactory("", shader_text);
448
449 if (!m_scanline_shader) {
450 ErrorLogger() << "ScanlineRenderer failed to initialize shader.";
451 m_failed_init = true;
452 return;
453 }
454 }
455
456 float fog_scanline_spacing = static_cast<float>(GetOptionsDB().Get<double>("ui.map.system.scanlines.spacing"));
457 m_scanline_shader->Use();
458 m_scanline_shader->Bind("scanline_spacing", fog_scanline_spacing);
459 m_scanline_shader->Bind("line_color", m_color.r * (1.f / 255.f), m_color.g * (1.f / 255.f), m_color.b * (1.f / 255.f), m_color.a * (1.f / 255.f));
460 }
461
SetColor(GG::Clr clr)462 void SetColor(GG::Clr clr) {
463 m_color = clr;
464 }
465
StopUsing()466 void StopUsing()
467 { m_scanline_shader->stopUse(); }
468
RenderCircle(const GG::Pt & ul,const GG::Pt & lr)469 void RenderCircle(const GG::Pt& ul, const GG::Pt& lr) {
470 StartUsing();
471 CircleArc(ul, lr, 0.0, TWO_PI, true);
472 StopUsing();
473 }
474
RenderRectangle(const GG::Pt & ul,const GG::Pt & lr)475 void RenderRectangle(const GG::Pt& ul, const GG::Pt& lr) {
476 StartUsing();
477 GG::FlatRectangle(ul, lr, GG::CLR_WHITE, GG::CLR_WHITE, 0u);
478 StopUsing();
479 }
480
481 std::unique_ptr<ShaderProgram> m_scanline_shader;
482 bool m_failed_init;
483 GG::Clr m_color;
484 };
485
486
ScanlineRenderer()487 ScanlineRenderer::ScanlineRenderer() :
488 m_impl(new Impl())
489 {}
490
491 // This destructor is required here because ~ScanlineRendererImpl is declared here.
492 ScanlineRenderer::~ScanlineRenderer() = default;
493
RenderCircle(const GG::Pt & ul,const GG::Pt & lr)494 void ScanlineRenderer::RenderCircle(const GG::Pt& ul, const GG::Pt& lr)
495 { m_impl->RenderCircle(ul, lr); }
496
RenderRectangle(const GG::Pt & ul,const GG::Pt & lr)497 void ScanlineRenderer::RenderRectangle(const GG::Pt& ul, const GG::Pt& lr)
498 { m_impl->RenderRectangle(ul, lr); }
499
StartUsing()500 void ScanlineRenderer::StartUsing()
501 { m_impl->StartUsing(); }
502
SetColor(GG::Clr clr)503 void ScanlineRenderer::SetColor(GG::Clr clr)
504 { m_impl->SetColor(clr); }
505
StopUsing()506 void ScanlineRenderer::StopUsing()
507 { m_impl->StopUsing(); }
508