1 //
2 // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
3 //
4 // This software is provided 'as-is', without any express or implied
5 // warranty. In no event will the authors be held liable for any damages
6 // arising from the use of this software.
7 // Permission is granted to anyone to use this software for any purpose,
8 // including commercial applications, and to alter it and redistribute it
9 // freely, subject to the following restrictions:
10 // 1. The origin of this software must not be misrepresented; you must not
11 // claim that you wrote the original software. If you use this software
12 // in a product, an acknowledgment in the product documentation would be
13 // appreciated but is not required.
14 // 2. Altered source versions must be plainly marked as such, and must not be
15 // misrepresented as being the original software.
16 // 3. This notice may not be removed or altered from any source distribution.
17 //
18
19 #define _USE_MATH_DEFINES
20 #include <cmath>
21 #include <cstdio>
22 #include "imgui.h"
23 #include "SDL.h"
24 #include "SDL_opengl.h"
25
26 // Some math headers don't have PI defined.
27 static const float PI = 3.14159265f;
28
29 void imguifree(void* ptr, void* userptr);
30 void* imguimalloc(size_t size, void* userptr);
31
32 #define STBTT_malloc(x,y) imguimalloc(x,y)
33 #define STBTT_free(x,y) imguifree(x,y)
34 #define STB_TRUETYPE_IMPLEMENTATION
35 #include "stb_truetype.h"
36
imguifree(void * ptr,void *)37 void imguifree(void* ptr, void* /*userptr*/)
38 {
39 free(ptr);
40 }
41
imguimalloc(size_t size,void *)42 void* imguimalloc(size_t size, void* /*userptr*/)
43 {
44 return malloc(size);
45 }
46
47 static const unsigned TEMP_COORD_COUNT = 100;
48 static float g_tempCoords[TEMP_COORD_COUNT*2];
49 static float g_tempNormals[TEMP_COORD_COUNT*2];
50
51 static const int CIRCLE_VERTS = 8*4;
52 static float g_circleVerts[CIRCLE_VERTS*2];
53
54 static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs
55 static GLuint g_ftex = 0;
56
RGBA(unsigned char r,unsigned char g,unsigned char b,unsigned char a)57 inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
58 {
59 return (r) | (g << 8) | (b << 16) | (a << 24);
60 }
61
drawPolygon(const float * coords,unsigned numCoords,float r,unsigned int col)62 static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col)
63 {
64 if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT;
65
66 for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
67 {
68 const float* v0 = &coords[j*2];
69 const float* v1 = &coords[i*2];
70 float dx = v1[0] - v0[0];
71 float dy = v1[1] - v0[1];
72 float d = sqrtf(dx*dx+dy*dy);
73 if (d > 0)
74 {
75 d = 1.0f/d;
76 dx *= d;
77 dy *= d;
78 }
79 g_tempNormals[j*2+0] = dy;
80 g_tempNormals[j*2+1] = -dx;
81 }
82
83 for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
84 {
85 float dlx0 = g_tempNormals[j*2+0];
86 float dly0 = g_tempNormals[j*2+1];
87 float dlx1 = g_tempNormals[i*2+0];
88 float dly1 = g_tempNormals[i*2+1];
89 float dmx = (dlx0 + dlx1) * 0.5f;
90 float dmy = (dly0 + dly1) * 0.5f;
91 float dmr2 = dmx*dmx + dmy*dmy;
92 if (dmr2 > 0.000001f)
93 {
94 float scale = 1.0f / dmr2;
95 if (scale > 10.0f) scale = 10.0f;
96 dmx *= scale;
97 dmy *= scale;
98 }
99 g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r;
100 g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r;
101 }
102
103 unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0);
104
105 glBegin(GL_TRIANGLES);
106
107 glColor4ubv((GLubyte*)&col);
108
109 for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
110 {
111 glVertex2fv(&coords[i*2]);
112 glVertex2fv(&coords[j*2]);
113 glColor4ubv((GLubyte*)&colTrans);
114 glVertex2fv(&g_tempCoords[j*2]);
115
116 glVertex2fv(&g_tempCoords[j*2]);
117 glVertex2fv(&g_tempCoords[i*2]);
118
119 glColor4ubv((GLubyte*)&col);
120 glVertex2fv(&coords[i*2]);
121 }
122
123 glColor4ubv((GLubyte*)&col);
124 for (unsigned i = 2; i < numCoords; ++i)
125 {
126 glVertex2fv(&coords[0]);
127 glVertex2fv(&coords[(i-1)*2]);
128 glVertex2fv(&coords[i*2]);
129 }
130
131 glEnd();
132 }
133
drawRect(float x,float y,float w,float h,float fth,unsigned int col)134 static void drawRect(float x, float y, float w, float h, float fth, unsigned int col)
135 {
136 float verts[4*2] =
137 {
138 x+0.5f, y+0.5f,
139 x+w-0.5f, y+0.5f,
140 x+w-0.5f, y+h-0.5f,
141 x+0.5f, y+h-0.5f,
142 };
143 drawPolygon(verts, 4, fth, col);
144 }
145
146 /*
147 static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col)
148 {
149 float verts[CIRCLE_VERTS*2];
150 const float* cverts = g_circleVerts;
151 float* v = verts;
152
153 for (int i = 0; i < CIRCLE_VERTS; ++i)
154 {
155 *v++ = x + cverts[i*2]*w;
156 *v++ = y + cverts[i*2+1]*h;
157 }
158
159 drawPolygon(verts, CIRCLE_VERTS, fth, col);
160 }
161 */
162
drawRoundedRect(float x,float y,float w,float h,float r,float fth,unsigned int col)163 static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col)
164 {
165 const unsigned n = CIRCLE_VERTS/4;
166 float verts[(n+1)*4*2];
167 const float* cverts = g_circleVerts;
168 float* v = verts;
169
170 for (unsigned i = 0; i <= n; ++i)
171 {
172 *v++ = x+w-r + cverts[i*2]*r;
173 *v++ = y+h-r + cverts[i*2+1]*r;
174 }
175
176 for (unsigned i = n; i <= n*2; ++i)
177 {
178 *v++ = x+r + cverts[i*2]*r;
179 *v++ = y+h-r + cverts[i*2+1]*r;
180 }
181
182 for (unsigned i = n*2; i <= n*3; ++i)
183 {
184 *v++ = x+r + cverts[i*2]*r;
185 *v++ = y+r + cverts[i*2+1]*r;
186 }
187
188 for (unsigned i = n*3; i < n*4; ++i)
189 {
190 *v++ = x+w-r + cverts[i*2]*r;
191 *v++ = y+r + cverts[i*2+1]*r;
192 }
193 *v++ = x+w-r + cverts[0]*r;
194 *v++ = y+r + cverts[1]*r;
195
196 drawPolygon(verts, (n+1)*4, fth, col);
197 }
198
199
drawLine(float x0,float y0,float x1,float y1,float r,float fth,unsigned int col)200 static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col)
201 {
202 float dx = x1-x0;
203 float dy = y1-y0;
204 float d = sqrtf(dx*dx+dy*dy);
205 if (d > 0.0001f)
206 {
207 d = 1.0f/d;
208 dx *= d;
209 dy *= d;
210 }
211 float nx = dy;
212 float ny = -dx;
213 float verts[4*2];
214 r -= fth;
215 r *= 0.5f;
216 if (r < 0.01f) r = 0.01f;
217 dx *= r;
218 dy *= r;
219 nx *= r;
220 ny *= r;
221
222 verts[0] = x0-dx-nx;
223 verts[1] = y0-dy-ny;
224
225 verts[2] = x0-dx+nx;
226 verts[3] = y0-dy+ny;
227
228 verts[4] = x1+dx+nx;
229 verts[5] = y1+dy+ny;
230
231 verts[6] = x1+dx-nx;
232 verts[7] = y1+dy-ny;
233
234 drawPolygon(verts, 4, fth, col);
235 }
236
237
imguiRenderGLInit(const char * fontpath)238 bool imguiRenderGLInit(const char* fontpath)
239 {
240 for (int i = 0; i < CIRCLE_VERTS; ++i)
241 {
242 float a = (float)i/(float)CIRCLE_VERTS * PI*2;
243 g_circleVerts[i*2+0] = cosf(a);
244 g_circleVerts[i*2+1] = sinf(a);
245 }
246
247 // Load font.
248 FILE* fp = fopen(fontpath, "rb");
249 if (!fp) return false;
250 if (fseek(fp, 0, SEEK_END) != 0)
251 {
252 fclose(fp);
253 return false;
254 }
255 long size = ftell(fp);
256 if (size < 0)
257 {
258 fclose(fp);
259 return false;
260 }
261 if (fseek(fp, 0, SEEK_SET) != 0)
262 {
263 fclose(fp);
264 return false;
265 }
266
267 unsigned char* ttfBuffer = (unsigned char*)malloc(size);
268 if (!ttfBuffer)
269 {
270 fclose(fp);
271 return false;
272 }
273
274 size_t readLen = fread(ttfBuffer, 1, size, fp);
275 fclose(fp);
276 if (readLen != static_cast<size_t>(size))
277 {
278 free(ttfBuffer);
279 return false;
280 }
281
282 fp = 0;
283
284 unsigned char* bmap = (unsigned char*)malloc(512*512);
285 if (!bmap)
286 {
287 free(ttfBuffer);
288 return false;
289 }
290
291 stbtt_BakeFontBitmap(ttfBuffer,0, 15.0f, bmap,512,512, 32,96, g_cdata);
292
293 // can free ttf_buffer at this point
294 glGenTextures(1, &g_ftex);
295 glBindTexture(GL_TEXTURE_2D, g_ftex);
296 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmap);
297 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
299
300 free(ttfBuffer);
301 free(bmap);
302
303 return true;
304 }
305
imguiRenderGLDestroy()306 void imguiRenderGLDestroy()
307 {
308 if (g_ftex)
309 {
310 glDeleteTextures(1, &g_ftex);
311 g_ftex = 0;
312 }
313 }
314
getBakedQuad(stbtt_bakedchar * chardata,int pw,int ph,int char_index,float * xpos,float * ypos,stbtt_aligned_quad * q)315 static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index,
316 float *xpos, float *ypos, stbtt_aligned_quad *q)
317 {
318 stbtt_bakedchar *b = chardata + char_index;
319 int round_x = STBTT_ifloor(*xpos + b->xoff);
320 int round_y = STBTT_ifloor(*ypos - b->yoff);
321
322 q->x0 = (float)round_x;
323 q->y0 = (float)round_y;
324 q->x1 = (float)round_x + b->x1 - b->x0;
325 q->y1 = (float)round_y - b->y1 + b->y0;
326
327 q->s0 = b->x0 / (float)pw;
328 q->t0 = b->y0 / (float)pw;
329 q->s1 = b->x1 / (float)ph;
330 q->t1 = b->y1 / (float)ph;
331
332 *xpos += b->xadvance;
333 }
334
335 static const float g_tabStops[4] = {150, 210, 270, 330};
336
getTextLength(stbtt_bakedchar * chardata,const char * text)337 static float getTextLength(stbtt_bakedchar *chardata, const char* text)
338 {
339 float xpos = 0;
340 float len = 0;
341 while (*text)
342 {
343 int c = (unsigned char)*text;
344 if (c == '\t')
345 {
346 for (int i = 0; i < 4; ++i)
347 {
348 if (xpos < g_tabStops[i])
349 {
350 xpos = g_tabStops[i];
351 break;
352 }
353 }
354 }
355 else if (c >= 32 && c < 128)
356 {
357 stbtt_bakedchar *b = chardata + c-32;
358 int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5);
359 len = round_x + b->x1 - b->x0 + 0.5f;
360 xpos += b->xadvance;
361 }
362 ++text;
363 }
364 return len;
365 }
366
drawText(float x,float y,const char * text,int align,unsigned int col)367 static void drawText(float x, float y, const char *text, int align, unsigned int col)
368 {
369 if (!g_ftex) return;
370 if (!text) return;
371
372 if (align == IMGUI_ALIGN_CENTER)
373 x -= getTextLength(g_cdata, text)/2;
374 else if (align == IMGUI_ALIGN_RIGHT)
375 x -= getTextLength(g_cdata, text);
376
377 glColor4ub(col&0xff, (col>>8)&0xff, (col>>16)&0xff, (col>>24)&0xff);
378
379 glEnable(GL_TEXTURE_2D);
380
381 // assume orthographic projection with units = screen pixels, origin at top left
382 glBindTexture(GL_TEXTURE_2D, g_ftex);
383
384 glBegin(GL_TRIANGLES);
385
386 const float ox = x;
387
388 while (*text)
389 {
390 int c = (unsigned char)*text;
391 if (c == '\t')
392 {
393 for (int i = 0; i < 4; ++i)
394 {
395 if (x < g_tabStops[i]+ox)
396 {
397 x = g_tabStops[i]+ox;
398 break;
399 }
400 }
401 }
402 else if (c >= 32 && c < 128)
403 {
404 stbtt_aligned_quad q;
405 getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q);
406
407 glTexCoord2f(q.s0, q.t0);
408 glVertex2f(q.x0, q.y0);
409 glTexCoord2f(q.s1, q.t1);
410 glVertex2f(q.x1, q.y1);
411 glTexCoord2f(q.s1, q.t0);
412 glVertex2f(q.x1, q.y0);
413
414 glTexCoord2f(q.s0, q.t0);
415 glVertex2f(q.x0, q.y0);
416 glTexCoord2f(q.s0, q.t1);
417 glVertex2f(q.x0, q.y1);
418 glTexCoord2f(q.s1, q.t1);
419 glVertex2f(q.x1, q.y1);
420 }
421 ++text;
422 }
423
424 glEnd();
425 glDisable(GL_TEXTURE_2D);
426 }
427
428
imguiRenderGLDraw()429 void imguiRenderGLDraw()
430 {
431 const imguiGfxCmd* q = imguiGetRenderQueue();
432 int nq = imguiGetRenderQueueSize();
433
434 const float s = 1.0f/8.0f;
435
436 glDisable(GL_SCISSOR_TEST);
437 for (int i = 0; i < nq; ++i)
438 {
439 const imguiGfxCmd& cmd = q[i];
440 if (cmd.type == IMGUI_GFXCMD_RECT)
441 {
442 if (cmd.rect.r == 0)
443 {
444 drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
445 (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
446 1.0f, cmd.col);
447 }
448 else
449 {
450 drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
451 (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1,
452 (float)cmd.rect.r*s, 1.0f, cmd.col);
453 }
454 }
455 else if (cmd.type == IMGUI_GFXCMD_LINE)
456 {
457 drawLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col);
458 }
459 else if (cmd.type == IMGUI_GFXCMD_TRIANGLE)
460 {
461 if (cmd.flags == 1)
462 {
463 const float verts[3*2] =
464 {
465 (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f,
466 (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f,
467 (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
468 };
469 drawPolygon(verts, 3, 1.0f, cmd.col);
470 }
471 if (cmd.flags == 2)
472 {
473 const float verts[3*2] =
474 {
475 (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
476 (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f,
477 (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1,
478 };
479 drawPolygon(verts, 3, 1.0f, cmd.col);
480 }
481 }
482 else if (cmd.type == IMGUI_GFXCMD_TEXT)
483 {
484 drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col);
485 }
486 else if (cmd.type == IMGUI_GFXCMD_SCISSOR)
487 {
488 if (cmd.flags)
489 {
490 glEnable(GL_SCISSOR_TEST);
491 glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h);
492 }
493 else
494 {
495 glDisable(GL_SCISSOR_TEST);
496 }
497 }
498 }
499 glDisable(GL_SCISSOR_TEST);
500 }
501