1 /* GG is a GUI for OpenGL.
2    Copyright (C) 2003-2008 T. Zachary Laine
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License
6    as published by the Free Software Foundation; either version 2.1
7    of the License, or (at your option) any later version.
8 
9    This library 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 GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA
18 
19    If you do not wish to comply with the terms of the LGPL please
20    contact the author as other terms are available for a fee.
21 
22    Zach Laine
23    whatwasthataddress@gmail.com */
24 
25 #include <GG/DrawUtil.h>
26 
27 #include <GG/ClrConstants.h>
28 #include <GG/GUI.h>
29 #include <GG/GLClientAndServerBuffer.h>
30 
31 
32 #include <valarray>
33 
34 namespace { // file-scope constants and functions
35     using namespace GG;
36 
37     const double   PI = 3.14159426;
38     const double   SQRT2OVER2 = std::sqrt(2.0) / 2.0;
39 
40     /// a stack of the currently-active clipping rects, in GG coordinates, not OpenGL scissor coordinates
41     std::vector<Rect> g_scissor_clipping_rects;
42 
43     GLboolean g_prev_color_writemask[4];
44     GLboolean g_prev_depth_writemask;
45 
46     /// the index of the next stencil bit to use for stencil clipping
47     unsigned int g_stencil_bit = 0;
48 
49     /// whenever points on the unit circle are calculated with expensive sin() and cos() calls, the results are cached here
50     std::map<int, std::valarray<double>> unit_circle_coords;
51     /// this doesn't serve as a cache, but does allow us to prevent numerous constructions and destructions of Clr valarrays.
52     std::map<int, std::valarray<Clr>> color_arrays;
53 
Rectangle(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int bevel_thick,bool bevel_left,bool bevel_top,bool bevel_right,bool bevel_bottom)54     void Rectangle(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2, unsigned int bevel_thick,
55                    bool bevel_left, bool bevel_top, bool bevel_right, bool bevel_bottom)
56     {
57         X inner_x1 = ul.x + (bevel_left ? static_cast<int>(bevel_thick) : 0);
58         Y inner_y1 = ul.y + (bevel_top ? static_cast<int>(bevel_thick) : 0);
59         X inner_x2 = lr.x - (bevel_right ? static_cast<int>(bevel_thick) : 0);
60         Y inner_y2 = lr.y - (bevel_bottom ? static_cast<int>(bevel_thick) : 0);
61 
62         GL2DVertexBuffer verts;
63         verts.reserve(14);
64         verts.store(inner_x2,   inner_y1);
65         verts.store(lr.x,       ul.y);
66         verts.store(inner_x1,   inner_y1);
67         verts.store(ul.x,       ul.y);
68         verts.store(inner_x1,   inner_y2);
69         verts.store(ul.x,       lr.y);
70         verts.store(inner_x2,   inner_y2);
71         verts.store(lr.x,       lr.y);
72         verts.store(inner_x2,   inner_y1);
73         verts.store(lr.x,       ul.y);
74 
75         verts.store(inner_x2,   inner_y1);
76         verts.store(inner_x1,   inner_y1);
77         verts.store(inner_x1,   inner_y2);
78         verts.store(inner_x2,   inner_y2);
79 
80         verts.activate();
81 
82         glDisable(GL_TEXTURE_2D);
83         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
84         glEnableClientState(GL_VERTEX_ARRAY);
85         glDisableClientState(GL_COLOR_ARRAY);
86         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
87 
88         // draw beveled edges
89         if (bevel_thick && (border_color1 != CLR_ZERO || border_color2 != CLR_ZERO)) {
90             glColor(border_color1);
91             if (border_color1 == border_color2) {
92                 glDrawArrays(GL_QUAD_STRIP, 0, 10);
93             } else {
94                 glDrawArrays(GL_QUAD_STRIP, 0, 6);
95                 glColor(border_color2);
96                 glDrawArrays(GL_QUAD_STRIP, 4, 6);
97             }
98         }
99 
100         // draw interior of rectangle
101         if (color != CLR_ZERO) {
102             glColor(color);
103             glDrawArrays(GL_QUADS, 10, 4);
104         }
105 
106         glPopClientAttrib();
107         glEnable(GL_TEXTURE_2D);
108     }
109 
Check(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3)110     void Check(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3)
111     {
112         X wd = lr.x - ul.x;
113         Y ht = lr.y - ul.y;
114 
115         // all vertices
116         GLfloat verts[][2] = {{-0.2f,  0.2f}, {-0.6f, -0.2f}, {-0.6f,  0.0f}, {-0.2f,  0.4f}, {-0.8f,  0.0f},
117                              { -0.2f,  0.6f}, { 0.8f, -0.4f}, { 0.6f, -0.4f}, { 0.8f, -0.8f}};
118 
119         glPushMatrix();
120         const float sf = 1.25f;                                                     // scale factor to make the check look right
121         glTranslatef(Value(ul.x + wd / 2.0f), Value(ul.y + ht / 2.0f * sf), 0.0f);  // move origin to the center of the rectangle
122         glScalef(Value(wd / 2.0f * sf), Value(ht / 2.0f * sf), 1.0f);               // map the range [-1,1] to the rectangle in both directions
123 
124         static std::size_t indices[22] = { 1,  4,  2,
125                                            8,  0,  3,  7,
126                                            2,  4,  5,  3,  7,  3,  5,  6,
127                                            8,  7,  6,
128                                            0,  1,  2,  3};
129 
130         GL2DVertexBuffer vert_buf;
131         vert_buf.reserve(22);
132         for (std::size_t i = 0; i < 22; ++i)
133             vert_buf.store(verts[indices[i]][0], verts[indices[i]][1]);
134 
135         glDisable(GL_TEXTURE_2D);
136         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
137         glEnableClientState(GL_VERTEX_ARRAY);
138         glDisableClientState(GL_COLOR_ARRAY);
139         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
140 
141         vert_buf.activate();
142 
143         glColor(color3);
144         glDrawArrays(GL_TRIANGLES, 0, 3);
145         glDrawArrays(GL_QUADS, 3, 4);
146 
147         glColor(color2);
148         glDrawArrays(GL_QUADS, 7, 8);
149 
150         glColor(color1);
151         glDrawArrays(GL_TRIANGLES, 15, 3);
152         glDrawArrays(GL_QUADS, 18, 4);
153 
154         glPopClientAttrib();
155 
156         glPopMatrix();
157         glEnable(GL_TEXTURE_2D);
158     }
159 
XMark(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3)160     void XMark(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3)
161     {
162         X wd = lr.x - ul.x;
163         Y ht = lr.y - ul.y;
164         glDisable(GL_TEXTURE_2D);
165 
166         // all vertices
167         GLfloat verts[][2] = {{-0.4f, -0.6f}, {-0.6f, -0.4f}, {-0.4f, -0.4f}, {-0.2f,  0.0f}, {-0.6f,  0.4f},
168                               {-0.4f,  0.6f}, {-0.4f,  0.4f}, { 0.0f,  0.2f}, { 0.4f,  0.6f}, { 0.6f,  0.4f},
169                               { 0.4f,  0.4f}, { 0.2f,  0.0f}, { 0.6f, -0.4f}, { 0.4f, -0.6f}, { 0.4f, -0.4f},
170                               { 0.0f, -0.2f}, { 0.0f,  0.0f}};
171 
172         glPushMatrix();
173         const float sf = 1.75f;                                                 // scale factor; the check wasn't the right size as drawn originally
174         glTranslatef(Value(ul.x + wd / 2.0f), Value(ul.y + ht / 2.0f), 0.0f);   // move origin to the center of the rectangle
175         glScalef(Value(wd / 2.0f * sf), Value(ht / 2.0f * sf), 1.0f);           // map the range [-1,1] to the rectangle in both directions
176 
177         static std::size_t indices[44] = {12, 13, 14,
178                                           15,  0,  2, 16,  9, 11, 16, 10,
179                                            0,  1,  2,
180                                           13, 15, 16, 14,  3,  4,  6, 16,
181                                            4,  5,  6,  8,  9, 10,
182                                           14, 16, 11, 12,  2,  1,  3, 16, 16,  6,  5,  7, 16,  7,  8, 10};
183 
184         GL2DVertexBuffer vert_buf;
185         vert_buf.reserve(44);
186         for (std::size_t i = 0; i < 44; ++i)
187             vert_buf.store(verts[indices[i]][0], verts[indices[i]][1]);
188 
189         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
190         glEnableClientState(GL_VERTEX_ARRAY);
191         glDisableClientState(GL_COLOR_ARRAY);
192         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
193 
194         vert_buf.activate();
195 
196         glColor(color1);
197         glDrawArrays(GL_TRIANGLES, 0, 3);
198         glDrawArrays(GL_QUADS, 3, 8);
199 
200         glColor(color2);
201         glDrawArrays(GL_TRIANGLES, 11, 3);
202         glDrawArrays(GL_QUADS, 14, 8);
203 
204         glColor(color3);
205         glDrawArrays(GL_TRIANGLES, 22, 6);
206         glDrawArrays(GL_QUADS, 28, 16);
207 
208         glPopClientAttrib();
209 
210         glPopMatrix();
211         glEnable(GL_TEXTURE_2D);
212     }
213 
BubbleArc(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3,double theta1,double theta2)214     void BubbleArc(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3, double theta1, double theta2)
215     {
216         X wd = lr.x - ul.x;
217         Y ht = lr.y - ul.y;
218         glDisable(GL_TEXTURE_2D);
219 
220         // correct theta* values to range [0, 2pi)
221         if (theta1 < 0)
222             theta1 += (int(-theta1 / (2 * PI)) + 1) * 2 * PI;
223         else if (theta1 >= 2 * PI)
224             theta1 -= int(theta1 / (2 * PI)) * 2 * PI;
225         if (theta2 < 0)
226             theta2 += (int(-theta2 / (2 * PI)) + 1) * 2 * PI;
227         else if (theta2 >= 2 * PI)
228             theta2 -= int(theta2 / (2 * PI)) * 2 * PI;
229 
230         const int      SLICES = std::min(3 + std::max(Value(wd), Value(ht)), 50);  // this is a good guess at how much to tesselate the circle coordinates (50 segments max)
231         const double   HORZ_THETA = (2 * PI) / SLICES;
232 
233         std::valarray<double>& unit_vertices = unit_circle_coords[SLICES];
234         std::valarray<Clr>&    colors = color_arrays[SLICES];
235         bool calc_vertices = unit_vertices.size() == 0;
236         if (calc_vertices) {
237             unit_vertices.resize(2 * (SLICES + 1), 0.0);
238             double theta = 0.0f;
239             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
240                 unit_vertices[j*2] = cos(-theta);
241                 unit_vertices[j*2+1] = sin(-theta);
242             }
243             colors.resize(SLICES + 1, Clr()); // create but don't initialize (this is essentially just scratch space, since the colors are different call-to-call)
244         }
245         int first_slice_idx = int(theta1 / HORZ_THETA + 1);
246         int last_slice_idx = int(theta2 / HORZ_THETA - 1);
247         if (theta1 >= theta2)
248             last_slice_idx += SLICES;
249         for (int j = first_slice_idx; j <= last_slice_idx; ++j) { // calculate the color value for each needed point
250             int X = (j > SLICES ? (j - SLICES) : j) * 2, Y = X + 1;
251             double color_scale_factor = (SQRT2OVER2 * (unit_vertices[X] + unit_vertices[Y]) + 1) / 2; // this is essentially the dot product of (x,y) with (sqrt2over2,sqrt2over2), the direction of the light source, scaled to the range [0,1]
252             colors[j] = BlendClr(color2, color3, color_scale_factor);
253         }
254 
255         glPushMatrix();
256         glTranslatef(Value(ul.x + wd / 2.0), Value(ul.y + ht / 2.0), 0.0);   // move origin to the center of the rectangle
257         glScalef(Value(wd / 2.0), Value(ht / 2.0), 1.0);                 // map the range [-1,1] to the rectangle in both (x- and y-) directions
258 
259         glColor(color1);
260         glBegin(GL_TRIANGLE_FAN);
261         glVertex2f(0, 0);
262         // point on circle at angle theta1
263         double x = cos(-theta1),
264             y = sin(-theta1);
265         double color_scale_factor = (SQRT2OVER2 * (x + y) + 1) / 2;
266         Clr clr = BlendClr(color2, color3, color_scale_factor);
267         glColor4ub(clr.r, clr.g, clr.b, clr.a);
268         glVertex2f(x, y);
269         // angles in between theta1 and theta2, if any
270         for (int i = first_slice_idx; i <= last_slice_idx; ++i) {
271             int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1;
272             glColor(colors[i]);
273             glVertex2f(unit_vertices[X], unit_vertices[Y]);
274         }
275         // theta2
276         x = cos(-theta2);
277         y = sin(-theta2);
278         color_scale_factor = (SQRT2OVER2 * (x + y) + 1) / 2;
279         clr = BlendClr(color2, color3, color_scale_factor);
280         glColor4ub(clr.r, clr.g, clr.b, clr.a);
281         glVertex2f(x, y);
282         glEnd();
283         glPopMatrix();
284         glEnable(GL_TEXTURE_2D);
285     }
286 
CircleArc(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int bevel_thick,double theta1,double theta2)287     void CircleArc(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2,
288                    unsigned int bevel_thick, double theta1, double theta2)
289     {
290         //std::cout << "GG::CircleArc ul: " << ul << "  lr: " << lr << " bevel thick: " << bevel_thick << "  theta1: " << theta1 << "  theta2: " << theta2 << std::endl;
291         X wd = lr.x - ul.x;
292         Y ht = lr.y - ul.y;
293         glDisable(GL_TEXTURE_2D);
294 
295         // correct theta* values to range [0, 2pi)
296         if (theta1 < 0)
297             theta1 += (int(-theta1 / (2 * PI)) + 1) * 2 * PI;
298         else if (theta1 >= 2 * PI)
299             theta1 -= int(theta1 / (2 * PI)) * 2 * PI;
300         if (theta2 < 0)
301             theta2 += (int(-theta2 / (2 * PI)) + 1) * 2 * PI;
302         else if (theta2 >= 2 * PI)
303             theta2 -= int(theta2 / (2 * PI)) * 2 * PI;
304 
305         const int      SLICES = std::min(3 + std::max(Value(wd), Value(ht)), 50);  // this is a good guess at how much to tesselate the circle coordinates (50 segments max)
306         const double   HORZ_THETA = (2 * PI) / SLICES;
307 
308         std::valarray<double>& unit_vertices = unit_circle_coords[SLICES];
309         std::valarray<Clr>&    colors = color_arrays[SLICES];
310         bool calc_vertices = unit_vertices.size() == 0;
311         if (calc_vertices) {
312             unit_vertices.resize(2 * (SLICES + 1), 0.0);
313             double theta = 0.0f;
314             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
315                 unit_vertices[j*2] = cos(-theta);
316                 unit_vertices[j*2+1] = sin(-theta);
317             }
318             colors.resize(SLICES + 1, Clr()); // create but don't initialize (this is essentially just scratch space, since the colors are different call-to-call)
319         }
320         int first_slice_idx = int(theta1 / HORZ_THETA + 1);
321         int last_slice_idx = int(theta2 / HORZ_THETA - 1);
322         if (theta1 >= theta2)
323             last_slice_idx += SLICES;
324         for (int j = first_slice_idx; j <= last_slice_idx; ++j) { // calculate the color value for each needed point
325             int X = (j > SLICES ? (j - SLICES) : j) * 2, Y = X + 1;
326             double color_scale_factor = (SQRT2OVER2 * (unit_vertices[X] + unit_vertices[Y]) + 1) / 2; // this is essentially the dot product of (x,y) with (sqrt2over2,sqrt2over2), the direction of the light source, scaled to the range [0,1]
327             colors[j] = BlendClr(border_color1, border_color2, color_scale_factor);
328         }
329 
330         glPushMatrix();
331         glTranslatef(Value(ul.x + wd / 2.0), Value(ul.y + ht / 2.0), 0.0);   // move origin to the center of the rectangle
332         glScalef(Value(wd / 2.0), Value(ht / 2.0), 1.0);                 // map the range [-1,1] to the rectangle in both (x- and y-) directions
333 
334         double inner_radius = (std::min(Value(wd), Value(ht)) - 2.0 * bevel_thick) / std::min(Value(wd), Value(ht));
335         glColor(color);
336         glBegin(GL_TRIANGLE_FAN);
337         glVertex2f(0, 0);
338         // point on circle at angle theta1
339         double theta1_x = cos(-theta1), theta1_y = sin(-theta1);
340         glVertex2f(theta1_x * inner_radius, theta1_y * inner_radius);
341         // angles in between theta1 and theta2, if any
342         for (int i = first_slice_idx; i <= last_slice_idx; ++i) {
343             int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1;
344             glVertex2f(unit_vertices[X] * inner_radius, unit_vertices[Y] * inner_radius);
345         }      // theta2
346         double theta2_x = cos(-theta2), theta2_y = sin(-theta2);
347         glVertex2f(theta2_x * inner_radius, theta2_y * inner_radius);
348         glEnd();
349         glBegin(GL_QUAD_STRIP);
350         // point on circle at angle theta1
351         double color_scale_factor = (SQRT2OVER2 * (theta1_x + theta1_y) + 1) / 2;
352         Clr clr = BlendClr(border_color1, border_color2, color_scale_factor);
353         glColor4ub(clr.r, clr.g, clr.b, clr.a);
354         glVertex2f(theta1_x, theta1_y);
355         glVertex2f(theta1_x * inner_radius, theta1_y * inner_radius);
356         // angles in between theta1 and theta2, if any
357         for (int i = first_slice_idx; i <= last_slice_idx; ++i) {
358             int X = (i > SLICES ? (i - SLICES) : i) * 2, Y = X + 1;
359             glColor(colors[i]);
360             glVertex2f(unit_vertices[X], unit_vertices[Y]);
361             glVertex2f(unit_vertices[X] * inner_radius, unit_vertices[Y] * inner_radius);
362         }
363         // theta2
364         color_scale_factor = (SQRT2OVER2 * (theta2_x + theta2_y) + 1) / 2;
365         clr = BlendClr(border_color1, border_color2, color_scale_factor);
366         glColor4ub(clr.r, clr.g, clr.b, clr.a);
367         glVertex2f(theta2_x, theta2_y);
368         glVertex2f(theta2_x * inner_radius, theta2_y * inner_radius);
369         glEnd();
370         glPopMatrix();
371         glEnable(GL_TEXTURE_2D);
372     }
373 
RoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color1,Clr border_color2,unsigned int corner_radius,int thick)374     void RoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color1, Clr border_color2,
375                           unsigned int corner_radius, int thick)
376     {
377         int circle_diameter = corner_radius * 2;
378         CircleArc(Pt(lr.x - circle_diameter, ul.y),                     Pt(lr.x, ul.y + circle_diameter),
379                   color, border_color2, border_color1, thick, 0, 0.5 * PI);  // ur corner
380         CircleArc(Pt(ul.x, ul.y),                                       Pt(ul.x + circle_diameter, ul.y + circle_diameter),
381                   color, border_color2, border_color1, thick, 0.5 * PI, PI); // ul corner
382         CircleArc(Pt(ul.x, lr.y - circle_diameter),                     Pt(ul.x + circle_diameter, lr.y),
383                   color, border_color2, border_color1, thick, PI, 1.5 * PI); // ll corner
384         CircleArc(Pt(lr.x - circle_diameter, lr.y - circle_diameter),   Pt(lr.x, lr.y),
385                   color, border_color2, border_color1, thick, 1.5 * PI, 0);  // lr corner
386 
387 
388         // lines connecting circle arcs and giving bevel appearance
389         GL2DVertexBuffer vert_buf;
390         vert_buf.reserve(28);
391         GLRGBAColorBuffer colour_buf;   // need to give each vertex in lightness bar its own colour so can't just use a glColor call
392         colour_buf.reserve(28);
393 
394         int rad = static_cast<int>(corner_radius);
395 
396         double color_scale_factor = (SQRT2OVER2 * (0 + 1) + 1) / 2;
397         Clr clr = BlendClr(border_color1, border_color2, color_scale_factor);
398         // top
399         vert_buf.store(lr.x - rad,      ul.y);
400         vert_buf.store(ul.x + rad,      ul.y);
401         vert_buf.store(ul.x + rad,      ul.y + thick);
402         vert_buf.store(lr.x - rad,      ul.y + thick);
403         // left
404         vert_buf.store(ul.x + thick,    ul.y + rad);
405         vert_buf.store(ul.x,            ul.y + rad);
406         vert_buf.store(ul.x,            lr.y - rad);
407         vert_buf.store(ul.x + thick,    lr.y - rad);
408         for (unsigned int i = 0; i < 8; ++i)
409             colour_buf.store(clr);
410 
411 
412         color_scale_factor = (SQRT2OVER2 * (-1 + 0) + 1) / 2;
413         clr = BlendClr(border_color1, border_color2, color_scale_factor);
414         // right
415         vert_buf.store(lr.x,            ul.y + rad);
416         vert_buf.store(lr.x - thick,    ul.y + rad);
417         vert_buf.store(lr.x - thick,    lr.y - rad);
418         vert_buf.store(lr.x,            lr.y - rad);
419         // bottom (uses color scale factor (SQRT2OVER2 * (0 + -1) + 1) / 2, which equals that of right
420         vert_buf.store(lr.x - rad,      lr.y - thick);
421         vert_buf.store(ul.x + rad,      lr.y - thick);
422         vert_buf.store(ul.x + rad,      lr.y);
423         vert_buf.store(lr.x - rad,      lr.y);
424         for (unsigned int i = 0; i < 8; ++i)
425             colour_buf.store(clr);
426 
427 
428         // middle
429         vert_buf.store(lr.x - rad,      ul.y + thick);
430         vert_buf.store(ul.x + rad,      ul.y + thick);
431         vert_buf.store(ul.x + rad,      lr.y - thick);
432         vert_buf.store(lr.x - rad,      lr.y - thick);
433 
434         vert_buf.store(lr.x - thick,    ul.y + rad);
435         vert_buf.store(lr.x - rad,      ul.y + rad);
436         vert_buf.store(lr.x - rad,      lr.y - rad);
437         vert_buf.store(lr.x - thick,    lr.y - rad);
438 
439         vert_buf.store(ul.x + thick,    ul.y + rad);
440         vert_buf.store(ul.x + rad,      ul.y + rad);
441         vert_buf.store(ul.x + rad,      lr.y - rad);
442         vert_buf.store(ul.x + thick,    lr.y - rad);
443         for (unsigned int i = 0; i < 12; ++i)
444             colour_buf.store(color);
445 
446 
447         glDisable(GL_TEXTURE_2D);
448         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
449         glEnableClientState(GL_VERTEX_ARRAY);
450         glEnableClientState(GL_COLOR_ARRAY);
451         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
452 
453         vert_buf.activate();
454         colour_buf.activate();
455 
456         glDrawArrays(GL_QUADS, 0, vert_buf.size());
457 
458         glPopClientAttrib();
459         glEnable(GL_TEXTURE_2D);
460     }
461 
BubbleRectangle(Pt ul,Pt lr,Clr color1,Clr color2,Clr color3,unsigned int corner_radius)462     void BubbleRectangle(Pt ul, Pt lr, Clr color1, Clr color2, Clr color3, unsigned int corner_radius)
463     {
464         int circle_diameter = corner_radius * 2;
465         BubbleArc(Pt(lr.x - circle_diameter, ul.y), Pt(lr.x, ul.y + circle_diameter), color1, color3, color2, 0, 0.5 * PI);  // ur corner
466         BubbleArc(Pt(ul.x, ul.y), Pt(ul.x + circle_diameter, ul.y + circle_diameter), color1, color3, color2, 0.5 * PI, PI); // ul corner
467         BubbleArc(Pt(ul.x, lr.y - circle_diameter), Pt(ul.x + circle_diameter, lr.y), color1, color3, color2, PI, 1.5 * PI); // ll corner
468         BubbleArc(Pt(lr.x - circle_diameter, lr.y - circle_diameter), Pt(lr.x, lr.y), color1, color3, color2, 1.5 * PI, 0);  // lr corner
469 
470         int rad = static_cast<int>(corner_radius);
471 
472         // top
473         double color_scale_factor = (SQRT2OVER2 * (0 + 1) + 1) / 2;
474         Clr scaled_color = BlendClr(color2, color3, color_scale_factor);
475 
476         GL2DVertexBuffer verts;
477         verts.reserve(20);
478         GLRGBAColorBuffer colours;
479         colours.reserve(20);
480 
481         colours.store(scaled_color);
482         colours.store(scaled_color);
483         verts.store(lr.x - rad, ul.y);
484         verts.store(ul.x + rad, ul.y);
485         colours.store(color1);
486         colours.store(color1);
487         verts.store(ul.x + rad, ul.y + rad);
488         verts.store(lr.x - rad, ul.y + rad);
489 
490         // left (uses color scale factor (SQRT2OVER2 * (1 + 0) + 1) / 2, which equals that of top
491         colours.store(scaled_color);
492         colours.store(scaled_color);
493         verts.store(ul.x, ul.y + rad);
494         verts.store(ul.x, lr.y - rad);
495         colours.store(color1);
496         colours.store(color1);
497         verts.store(ul.x + rad, lr.y - rad);
498         verts.store(ul.x + rad, ul.y + rad);
499 
500         // right
501         color_scale_factor = (SQRT2OVER2 * (-1 + 0) + 1) / 2;
502         scaled_color = BlendClr(color2, color3, color_scale_factor);
503         colours.store(color1);
504         colours.store(color1);
505         verts.store(lr.x - rad, ul.y + rad);
506         verts.store(lr.x - rad, lr.y - rad);
507         colours.store(scaled_color);
508         colours.store(scaled_color);
509         verts.store(lr.x, lr.y - rad);
510         verts.store(lr.x, ul.y + rad);
511 
512         // bottom (uses color scale factor (SQRT2OVER2 * (0 + -1) + 1) / 2, which equals that of left
513         colours.store(color1);
514         colours.store(color1);
515         verts.store(lr.x - rad, lr.y - rad);
516         verts.store(ul.x + rad, lr.y - rad);
517         colours.store(scaled_color);
518         colours.store(scaled_color);
519         verts.store(ul.x + rad, lr.y);
520         verts.store(lr.x - rad, lr.y);
521 
522         // middle
523         colours.store(color1);
524         colours.store(color1);
525         verts.store(lr.x - rad, ul.y + rad);
526         verts.store(ul.x + rad, ul.y + rad);
527         colours.store(color1);
528         colours.store(color1);
529         verts.store(ul.x + rad, lr.y - rad);
530         verts.store(lr.x - rad, lr.y - rad);
531 
532 
533         glDisable(GL_TEXTURE_2D);
534         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
535         glEnableClientState(GL_VERTEX_ARRAY);
536         glEnableClientState(GL_COLOR_ARRAY);
537         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
538 
539         verts.activate();
540         colours.activate();
541         glDrawArrays(GL_QUADS, 0, verts.size());
542 
543         glPopClientAttrib();
544         glEnable(GL_TEXTURE_2D);
545     }
546 } // namespace
547 
548 
549 namespace GG {
operator <<(std::ostream & os,const Clr & clr)550     std::ostream& operator<<(std::ostream& os, const Clr& clr)
551     {
552         os << "(" << +clr.r << ", " << +clr.g << ", " << +clr.b << ", " << +clr.a << ")";
553         return os;
554     }
555 
glColor(Clr clr)556     void glColor(Clr clr)
557     { glColor4ub(clr.r, clr.g, clr.b, clr.a); }
558 
DisabledColor(Clr clr)559     Clr DisabledColor(Clr clr)
560     {
561         Clr retval = clr;
562         const double gray_factor = 0.75; // amount to move clr in the direction of gray
563         retval.r = static_cast<int>(retval.r + (CLR_GRAY.r - retval.r) * gray_factor);
564         retval.g = static_cast<int>(retval.g + (CLR_GRAY.g - retval.g) * gray_factor);
565         retval.b = static_cast<int>(retval.b + (CLR_GRAY.b - retval.b) * gray_factor);
566         return retval;
567     }
568 
BeginScissorClipping(Pt ul,Pt lr)569     void BeginScissorClipping(Pt ul, Pt lr)
570     {
571         if (g_scissor_clipping_rects.empty()) {
572             glPushAttrib(GL_SCISSOR_BIT | GL_ENABLE_BIT);
573             glEnable(GL_SCISSOR_TEST);
574             if (g_stencil_bit)
575                 glDisable(GL_STENCIL_TEST);
576         } else {
577             const Rect& r = g_scissor_clipping_rects.back();
578             ul.x = std::max(r.Left(), std::min(ul.x, r.Right()));
579             ul.y = std::max(r.Top(), std::min(ul.y, r.Bottom()));
580             lr.x = std::max(r.Left(), std::min(lr.x, r.Right()));
581             lr.y = std::max(r.Top(), std::min(lr.y, r.Bottom()));
582         }
583         glScissor(Value(ul.x), Value(GUI::GetGUI()->AppHeight() - lr.y),
584                   Value(lr.x - ul.x), Value(lr.y - ul.y));
585         g_scissor_clipping_rects.push_back(Rect(ul, lr));
586     }
587 
EndScissorClipping()588     void EndScissorClipping()
589     {
590         assert(!g_scissor_clipping_rects.empty());
591         g_scissor_clipping_rects.pop_back();
592         if (g_scissor_clipping_rects.empty()) {
593             if (g_stencil_bit)
594                 glEnable(GL_STENCIL_TEST);
595             glPopAttrib();
596         } else {
597             const Rect& r = g_scissor_clipping_rects.back();
598             glScissor(Value(r.Left()), Value(GUI::GetGUI()->AppHeight() - r.Bottom()),
599                       Value(r.Width()), Value(r.Height()));
600         }
601     }
602 
ActiveScissorClippingRegion()603     Rect ActiveScissorClippingRegion()
604     {
605         if (g_scissor_clipping_rects.empty())
606             return Rect();
607         return g_scissor_clipping_rects.back();
608     }
609 
BeginStencilClipping(Pt inner_ul,Pt inner_lr,Pt outer_ul,Pt outer_lr)610     void BeginStencilClipping(Pt inner_ul, Pt inner_lr, Pt outer_ul, Pt outer_lr)
611     {
612         if (!g_stencil_bit) {
613             glPushAttrib(GL_STENCIL_BUFFER_BIT | GL_ENABLE_BIT);
614             glClearStencil(0);
615             glClear(GL_STENCIL_BUFFER_BIT);
616             glEnable(GL_STENCIL_TEST);
617             if (!g_scissor_clipping_rects.empty())
618                 glDisable(GL_SCISSOR_TEST);
619         }
620 
621         glGetBooleanv(GL_COLOR_WRITEMASK, g_prev_color_writemask);
622         glGetBooleanv(GL_DEPTH_WRITEMASK, &g_prev_depth_writemask);
623 
624         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
625         glDepthMask(GL_FALSE);
626 
627         GLuint mask = 1u << g_stencil_bit;
628 
629         glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
630         glEnableClientState(GL_VERTEX_ARRAY);
631         glDisableClientState(GL_COLOR_ARRAY);
632         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
633 
634         glStencilFunc(GL_ALWAYS, mask, mask);
635         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
636         GLint outer_vertices[] = {
637             Value(outer_ul.x), Value(outer_ul.y),
638             Value(outer_ul.x), Value(outer_lr.y),
639             Value(outer_lr.x), Value(outer_lr.y),
640             Value(outer_lr.x), Value(outer_ul.y)
641         };
642         glVertexPointer(2, GL_INT, 0, outer_vertices);
643         glDrawArrays(GL_QUADS, 0, 4);
644 
645         glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
646         GLint inner_vertices[] = {
647             Value(inner_ul.x), Value(inner_ul.y),
648             Value(inner_ul.x), Value(inner_lr.y),
649             Value(inner_lr.x), Value(inner_lr.y),
650             Value(inner_lr.x), Value(inner_ul.y)
651         };
652         glVertexPointer(2, GL_INT, 0, inner_vertices);
653         glDrawArrays(GL_QUADS, 0, 4);
654 
655         glColorMask(g_prev_color_writemask[0],
656                     g_prev_color_writemask[1],
657                     g_prev_color_writemask[2],
658                     g_prev_color_writemask[3]);
659         glDepthMask(g_prev_depth_writemask);
660 
661         glStencilFunc(GL_EQUAL, mask, mask);
662         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
663         ++g_stencil_bit;
664 
665         glPopClientAttrib();
666     }
667 
EndStencilClipping()668     void EndStencilClipping()
669     {
670         assert(g_stencil_bit);
671         --g_stencil_bit;
672         if (!g_stencil_bit) {
673             if (!g_scissor_clipping_rects.empty())
674                 glEnable(GL_SCISSOR_TEST);
675             glPopAttrib();
676         }
677     }
678 
Line(Pt pt1,Pt pt2,Clr color,float thick)679     void Line(Pt pt1, Pt pt2, Clr color, float thick)
680     {
681         glLineWidth(thick);
682         glColor(color);
683         Line(pt1.x, pt1.y, pt2.x, pt2.y);
684     }
685 
Line(X x1,Y y1,X x2,Y y2)686     void Line(X x1, Y y1, X x2, Y y2)
687     {
688         GLfloat vertices[4] = {GLfloat(Value(x1)), GLfloat(Value(y1)),
689                                GLfloat(Value(x2)), GLfloat(Value(y2))};
690 
691         glDisable(GL_TEXTURE_2D);
692 
693         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
694         glEnableClientState(GL_VERTEX_ARRAY);
695         glDisableClientState(GL_COLOR_ARRAY);
696         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
697 
698         glVertexPointer(2, GL_FLOAT, 0, vertices);
699         glDrawArrays(GL_LINES, 0, 2);
700 
701         glPopClientAttrib();
702         glLineWidth(1.0f);
703         glEnable(GL_TEXTURE_2D);
704     }
705 
Triangle(Pt pt1,Pt pt2,Pt pt3,Clr color,Clr border_color,float border_thick)706     void Triangle(Pt pt1, Pt pt2, Pt pt3, Clr color, Clr border_color, float border_thick)
707     {
708         GLfloat vertices[6] = {GLfloat(Value(pt1.x)), GLfloat(Value(pt1.y)), GLfloat(Value(pt2.x)),
709                                GLfloat(Value(pt2.y)), GLfloat(Value(pt3.x)), GLfloat(Value(pt3.y))};
710 
711         glDisable(GL_TEXTURE_2D);
712 
713         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
714         glEnableClientState(GL_VERTEX_ARRAY);
715         glDisableClientState(GL_COLOR_ARRAY);
716         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
717 
718         glVertexPointer(2, GL_FLOAT, 0, vertices);
719         glColor(color);
720         glDrawArrays(GL_TRIANGLES, 0, 3);
721 
722         if (border_color != GG::CLR_ZERO) {
723             glLineWidth(border_thick);
724             glColor(border_color);
725 
726             glDrawArrays(GL_LINE_LOOP, 0, 3);
727             glLineWidth(1.0f);
728         }
729 
730         glPopClientAttrib();
731         glEnable(GL_TEXTURE_2D);
732     }
733 
Triangle(X x1,Y y1,X x2,Y y2,X x3,Y y3,bool filled)734     void Triangle(X x1, Y y1, X x2, Y y2, X x3, Y y3, bool filled)
735     {
736         GLfloat vertices[6] = {GLfloat(Value(x1)), GLfloat(Value(y1)), GLfloat(Value(x2)),
737                                GLfloat(Value(y2)), GLfloat(Value(x3)), GLfloat(Value(y3))};
738 
739         glDisable(GL_TEXTURE_2D);
740 
741         glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
742         glEnableClientState(GL_VERTEX_ARRAY);
743         glDisableClientState(GL_COLOR_ARRAY);
744         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
745 
746         glVertexPointer(2, GL_FLOAT, 0, vertices);
747 
748         if (filled)
749             glDrawArrays(GL_TRIANGLE_FAN, 0, 3);
750         else
751             glDrawArrays(GL_TRIANGLES, 0, 3);
752 
753         glPopClientAttrib();
754         glEnable(GL_TEXTURE_2D);
755     }
756 
FlatRectangle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int border_thick)757     void FlatRectangle(Pt ul, Pt lr, Clr color, Clr border_color,
758                        unsigned int border_thick/* = 2*/)
759     {
760         Rectangle(ul, lr, color, border_color, border_color, border_thick,
761                   true, true, true, true);
762     }
763 
BeveledRectangle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int bevel_thick,bool bevel_left,bool bevel_top,bool bevel_right,bool bevel_bottom)764     void BeveledRectangle(Pt ul, Pt lr, Clr color, Clr border_color, bool up,
765                           unsigned int bevel_thick/* = 2*/, bool bevel_left/* = true*/,
766                           bool bevel_top/* = true*/, bool bevel_right/* = true*/,
767                           bool bevel_bottom/* = true*/)
768     {
769         Rectangle(ul, lr, color,
770                   (up ? LightenClr(border_color) : DarkenClr(border_color)),
771                   (up ? DarkenClr(border_color) : LightenClr(border_color)),
772                   bevel_thick, bevel_left, bevel_top, bevel_right, bevel_bottom);
773     }
774 
FlatRoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int corner_radius,unsigned int border_thick)775     void FlatRoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color,
776                               unsigned int corner_radius/* = 5*/,
777                               unsigned int border_thick/* = 2*/)
778     {
779         RoundedRectangle(ul, lr, color, border_color, border_color,
780                          corner_radius, border_thick);
781     }
782 
BeveledRoundedRectangle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int corner_radius,unsigned int bevel_thick)783     void BeveledRoundedRectangle(Pt ul, Pt lr, Clr color, Clr border_color, bool up,
784                                  unsigned int corner_radius/* = 5*/,
785                                  unsigned int bevel_thick/* = 2*/)
786     {
787         RoundedRectangle(ul, lr, color,
788                          (up ? LightenClr(border_color) : DarkenClr(border_color)),
789                          (up ? DarkenClr(border_color) : LightenClr(border_color)),
790                          corner_radius, bevel_thick);
791     }
792 
FlatCheck(Pt ul,Pt lr,Clr color)793     void FlatCheck(Pt ul, Pt lr, Clr color)
794     { Check(ul, lr, color, color, color); }
795 
BeveledCheck(Pt ul,Pt lr,Clr color)796     void BeveledCheck(Pt ul, Pt lr, Clr color)
797     { Check(ul, lr, color, LightenClr(color), DarkenClr(color)); }
798 
FlatX(Pt ul,Pt lr,Clr color)799     void FlatX(Pt ul, Pt lr, Clr color)
800     { XMark(ul, lr, color, color, color); }
801 
Bubble(Pt ul,Pt lr,Clr color,bool up)802     void Bubble(Pt ul, Pt lr, Clr color, bool up/* = true*/)
803     {
804         BubbleArc(ul, lr, color,
805                   (up ? DarkenClr(color) : LightenClr(color)),
806                   (up ? LightenClr(color) : DarkenClr(color)),
807                   0, 0);
808     }
809 
FlatCircle(Pt ul,Pt lr,Clr color,Clr border_color,unsigned int thick)810     void FlatCircle(Pt ul, Pt lr, Clr color, Clr border_color, unsigned int thick/* = 2*/)
811     { CircleArc(ul, lr, color, border_color, border_color, thick, 0, 0); }
812 
BeveledCircle(Pt ul,Pt lr,Clr color,Clr border_color,bool up,unsigned int bevel_thick)813     void BeveledCircle(Pt ul, Pt lr, Clr color, Clr border_color, bool up/* = true*/, unsigned int bevel_thick/* = 2*/)
814     {
815         CircleArc(ul, lr, color,
816                   (up ? DarkenClr(border_color) : LightenClr(border_color)),
817                   (up ? LightenClr(border_color) : DarkenClr(border_color)),
818                   bevel_thick, 0, 0);
819     }
820 
BubbleRectangle(Pt ul,Pt lr,Clr color,bool up,unsigned int corner_radius)821     void BubbleRectangle(Pt ul, Pt lr, Clr color, bool up, unsigned int corner_radius/* = 5*/)
822     {
823         ::BubbleRectangle(ul, lr, color,
824                           (up ? LightenClr(color) : DarkenClr(color)),
825                           (up ? DarkenClr(color) : LightenClr(color)),
826                           corner_radius);
827     }
828 } // namespace GG
829