DPF OpenGL examples
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1095 lines
29KB

  1. #include "demo.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <math.h>
  5. #include "nanovg.h"
  6. #define STB_IMAGE_WRITE_IMPLEMENTATION
  7. #include "stb_image_write.h"
  8. #ifdef _MSC_VER
  9. #define snprintf _snprintf
  10. #elif !defined(__MINGW32__)
  11. #include <iconv.h>
  12. #endif
  13. #define ICON_SEARCH 0x1F50D
  14. #define ICON_CIRCLED_CROSS 0x2716
  15. #define ICON_CHEVRON_RIGHT 0xE75E
  16. #define ICON_CHECK 0x2713
  17. #define ICON_LOGIN 0xE740
  18. #define ICON_TRASH 0xE729
  19. // Returns 1 if col.rgba is 0.0f,0.0f,0.0f,0.0f, 0 otherwise
  20. int isBlack( struct NVGcolor col )
  21. {
  22. if( col.r == 0.0f && col.g == 0.0f && col.b == 0.0f && col.a == 0.0f )
  23. {
  24. return 1;
  25. }
  26. return 0;
  27. }
  28. static char* cpToUTF8(int cp, char* str)
  29. {
  30. int n = 0;
  31. if (cp < 0x80) n = 1;
  32. else if (cp < 0x800) n = 2;
  33. else if (cp < 0x10000) n = 3;
  34. else if (cp < 0x200000) n = 4;
  35. else if (cp < 0x4000000) n = 5;
  36. else if (cp <= 0x7fffffff) n = 6;
  37. str[n] = '\0';
  38. switch (n) {
  39. case 6: str[5] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x4000000;
  40. case 5: str[4] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x200000;
  41. case 4: str[3] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x10000;
  42. case 3: str[2] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x800;
  43. case 2: str[1] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0xc0;
  44. case 1: str[0] = cp;
  45. }
  46. return str;
  47. }
  48. void drawWindow(struct NVGcontext* vg, const char* title, float x, float y, float w, float h)
  49. {
  50. float cornerRadius = 3.0f;
  51. struct NVGpaint shadowPaint;
  52. struct NVGpaint headerPaint;
  53. nvgSave(vg);
  54. // nvgClearState(vg);
  55. // Window
  56. nvgBeginPath(vg);
  57. nvgRoundedRect(vg, x,y, w,h, cornerRadius);
  58. nvgFillColor(vg, nvgRGBA(28,30,34,192));
  59. // nvgFillColor(vg, nvgRGBA(0,0,0,128));
  60. nvgFill(vg);
  61. // Drop shadow
  62. shadowPaint = nvgBoxGradient(vg, x,y+2, w,h, cornerRadius*2, 10, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
  63. nvgBeginPath(vg);
  64. nvgRect(vg, x-10,y-10, w+20,h+30);
  65. nvgRoundedRect(vg, x,y, w,h, cornerRadius);
  66. nvgPathWinding(vg, NVG_HOLE);
  67. nvgFillPaint(vg, shadowPaint);
  68. nvgFill(vg);
  69. // Header
  70. headerPaint = nvgLinearGradient(vg, x,y,x,y+15, nvgRGBA(255,255,255,8), nvgRGBA(0,0,0,16));
  71. nvgBeginPath(vg);
  72. nvgRoundedRect(vg, x+1,y+1, w-2,30, cornerRadius-1);
  73. nvgFillPaint(vg, headerPaint);
  74. nvgFill(vg);
  75. nvgBeginPath(vg);
  76. nvgMoveTo(vg, x+0.5f, y+0.5f+30);
  77. nvgLineTo(vg, x+0.5f+w-1, y+0.5f+30);
  78. nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
  79. nvgStroke(vg);
  80. nvgFontSize(vg, 18.0f);
  81. nvgFontFace(vg, "sans-bold");
  82. nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
  83. nvgFontBlur(vg,2);
  84. nvgFillColor(vg, nvgRGBA(0,0,0,128));
  85. nvgText(vg, x+w/2,y+16+1, title, NULL);
  86. nvgFontBlur(vg,0);
  87. nvgFillColor(vg, nvgRGBA(220,220,220,160));
  88. nvgText(vg, x+w/2,y+16, title, NULL);
  89. nvgRestore(vg);
  90. }
  91. void drawSearchBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
  92. {
  93. struct NVGpaint bg;
  94. char icon[8];
  95. float cornerRadius = h/2-1;
  96. // Edit
  97. bg = nvgBoxGradient(vg, x,y+1.5f, w,h, h/2,5, nvgRGBA(0,0,0,16), nvgRGBA(0,0,0,92));
  98. nvgBeginPath(vg);
  99. nvgRoundedRect(vg, x,y, w,h, cornerRadius);
  100. nvgFillPaint(vg, bg);
  101. nvgFill(vg);
  102. /* nvgBeginPath(vg);
  103. nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
  104. nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
  105. nvgStroke(vg);*/
  106. nvgFontSize(vg, h*1.3f);
  107. nvgFontFace(vg, "icons");
  108. nvgFillColor(vg, nvgRGBA(255,255,255,64));
  109. nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
  110. nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon), NULL);
  111. nvgFontSize(vg, 20.0f);
  112. nvgFontFace(vg, "sans");
  113. nvgFillColor(vg, nvgRGBA(255,255,255,32));
  114. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  115. nvgText(vg, x+h*1.05f,y+h*0.5f,text, NULL);
  116. nvgFontSize(vg, h*1.3f);
  117. nvgFontFace(vg, "icons");
  118. nvgFillColor(vg, nvgRGBA(255,255,255,32));
  119. nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
  120. nvgText(vg, x+w-h*0.55f, y+h*0.55f, cpToUTF8(ICON_CIRCLED_CROSS,icon), NULL);
  121. }
  122. void drawDropDown(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
  123. {
  124. struct NVGpaint bg;
  125. char icon[8];
  126. float cornerRadius = 4.0f;
  127. bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
  128. nvgBeginPath(vg);
  129. nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
  130. nvgFillPaint(vg, bg);
  131. nvgFill(vg);
  132. nvgBeginPath(vg);
  133. nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
  134. nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
  135. nvgStroke(vg);
  136. nvgFontSize(vg, 20.0f);
  137. nvgFontFace(vg, "sans");
  138. nvgFillColor(vg, nvgRGBA(255,255,255,160));
  139. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  140. nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL);
  141. nvgFontSize(vg, h*1.3f);
  142. nvgFontFace(vg, "icons");
  143. nvgFillColor(vg, nvgRGBA(255,255,255,64));
  144. nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
  145. nvgText(vg, x+w-h*0.5f, y+h*0.5f, cpToUTF8(ICON_CHEVRON_RIGHT,icon), NULL);
  146. }
  147. void drawLabel(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
  148. {
  149. NVG_NOTUSED(w);
  150. nvgFontSize(vg, 18.0f);
  151. nvgFontFace(vg, "sans");
  152. nvgFillColor(vg, nvgRGBA(255,255,255,128));
  153. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  154. nvgText(vg, x,y+h*0.5f,text, NULL);
  155. }
  156. void drawEditBoxBase(struct NVGcontext* vg, float x, float y, float w, float h)
  157. {
  158. struct NVGpaint bg;
  159. // Edit
  160. bg = nvgBoxGradient(vg, x+1,y+1+1.5f, w-2,h-2, 3,4, nvgRGBA(255,255,255,32), nvgRGBA(32,32,32,32));
  161. nvgBeginPath(vg);
  162. nvgRoundedRect(vg, x+1,y+1, w-2,h-2, 4-1);
  163. nvgFillPaint(vg, bg);
  164. nvgFill(vg);
  165. nvgBeginPath(vg);
  166. nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, 4-0.5f);
  167. nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
  168. nvgStroke(vg);
  169. }
  170. void drawEditBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
  171. {
  172. drawEditBoxBase(vg, x,y, w,h);
  173. nvgFontSize(vg, 20.0f);
  174. nvgFontFace(vg, "sans");
  175. nvgFillColor(vg, nvgRGBA(255,255,255,64));
  176. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  177. nvgText(vg, x+h*0.3f,y+h*0.5f,text, NULL);
  178. }
  179. void drawEditBoxNum(struct NVGcontext* vg,
  180. const char* text, const char* units, float x, float y, float w, float h)
  181. {
  182. float uw;
  183. drawEditBoxBase(vg, x,y, w,h);
  184. uw = nvgTextBounds(vg, 0,0, units, NULL, NULL);
  185. nvgFontSize(vg, 18.0f);
  186. nvgFontFace(vg, "sans");
  187. nvgFillColor(vg, nvgRGBA(255,255,255,64));
  188. nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
  189. nvgText(vg, x+w-h*0.3f,y+h*0.5f,units, NULL);
  190. nvgFontSize(vg, 20.0f);
  191. nvgFontFace(vg, "sans");
  192. nvgFillColor(vg, nvgRGBA(255,255,255,128));
  193. nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
  194. nvgText(vg, x+w-uw-h*0.5f,y+h*0.5f,text, NULL);
  195. }
  196. void drawCheckBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
  197. {
  198. struct NVGpaint bg;
  199. char icon[8];
  200. NVG_NOTUSED(w);
  201. nvgFontSize(vg, 18.0f);
  202. nvgFontFace(vg, "sans");
  203. nvgFillColor(vg, nvgRGBA(255,255,255,160));
  204. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  205. nvgText(vg, x+28,y+h*0.5f,text, NULL);
  206. bg = nvgBoxGradient(vg, x+1,y+(int)(h*0.5f)-9+1, 18,18, 3,3, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
  207. nvgBeginPath(vg);
  208. nvgRoundedRect(vg, x+1,y+(int)(h*0.5f)-9, 18,18, 3);
  209. nvgFillPaint(vg, bg);
  210. nvgFill(vg);
  211. nvgFontSize(vg, 40);
  212. nvgFontFace(vg, "icons");
  213. nvgFillColor(vg, nvgRGBA(255,255,255,128));
  214. nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
  215. nvgText(vg, x+9+2, y+h*0.5f, cpToUTF8(ICON_CHECK,icon), NULL);
  216. }
  217. void drawButton(struct NVGcontext* vg, int preicon, const char* text, float x, float y, float w, float h, struct NVGcolor col)
  218. {
  219. struct NVGpaint bg;
  220. char icon[8];
  221. float cornerRadius = 4.0f;
  222. float tw = 0, iw = 0;
  223. bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,isBlack(col)?16:32), nvgRGBA(0,0,0,isBlack(col)?16:32));
  224. nvgBeginPath(vg);
  225. nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
  226. if (!isBlack(col)) {
  227. nvgFillColor(vg, col);
  228. nvgFill(vg);
  229. }
  230. nvgFillPaint(vg, bg);
  231. nvgFill(vg);
  232. nvgBeginPath(vg);
  233. nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
  234. nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
  235. nvgStroke(vg);
  236. nvgFontSize(vg, 20.0f);
  237. nvgFontFace(vg, "sans-bold");
  238. tw = nvgTextBounds(vg, 0,0, text, NULL, NULL);
  239. if (preicon != 0) {
  240. nvgFontSize(vg, h*1.3f);
  241. nvgFontFace(vg, "icons");
  242. iw = nvgTextBounds(vg, 0,0, cpToUTF8(preicon,icon), NULL, NULL);
  243. iw += h*0.15f;
  244. }
  245. if (preicon != 0) {
  246. nvgFontSize(vg, h*1.3f);
  247. nvgFontFace(vg, "icons");
  248. nvgFillColor(vg, nvgRGBA(255,255,255,96));
  249. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  250. nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon), NULL);
  251. }
  252. nvgFontSize(vg, 20.0f);
  253. nvgFontFace(vg, "sans-bold");
  254. nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
  255. nvgFillColor(vg, nvgRGBA(0,0,0,160));
  256. nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f-1,text, NULL);
  257. nvgFillColor(vg, nvgRGBA(255,255,255,160));
  258. nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f,text, NULL);
  259. }
  260. void drawSlider(struct NVGcontext* vg, float pos, float x, float y, float w, float h)
  261. {
  262. struct NVGpaint bg, knob;
  263. float cy = y+(int)(h*0.5f);
  264. float kr = (int)(h*0.25f);
  265. nvgSave(vg);
  266. // nvgClearState(vg);
  267. // Slot
  268. bg = nvgBoxGradient(vg, x,cy-2+1, w,4, 2,2, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,128));
  269. nvgBeginPath(vg);
  270. nvgRoundedRect(vg, x,cy-2, w,4, 2);
  271. nvgFillPaint(vg, bg);
  272. nvgFill(vg);
  273. // Knob Shadow
  274. bg = nvgRadialGradient(vg, x+(int)(pos*w),cy+1, kr-3,kr+3, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
  275. nvgBeginPath(vg);
  276. nvgRect(vg, x+(int)(pos*w)-kr-5,cy-kr-5,kr*2+5+5,kr*2+5+5+3);
  277. nvgCircle(vg, x+(int)(pos*w),cy, kr);
  278. nvgPathWinding(vg, NVG_HOLE);
  279. nvgFillPaint(vg, bg);
  280. nvgFill(vg);
  281. // Knob
  282. knob = nvgLinearGradient(vg, x,cy-kr,x,cy+kr, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
  283. nvgBeginPath(vg);
  284. nvgCircle(vg, x+(int)(pos*w),cy, kr-1);
  285. nvgFillColor(vg, nvgRGBA(40,43,48,255));
  286. nvgFill(vg);
  287. nvgFillPaint(vg, knob);
  288. nvgFill(vg);
  289. nvgBeginPath(vg);
  290. nvgCircle(vg, x+(int)(pos*w),cy, kr-0.5f);
  291. nvgStrokeColor(vg, nvgRGBA(0,0,0,92));
  292. nvgStroke(vg);
  293. nvgRestore(vg);
  294. }
  295. void drawEyes(struct NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t)
  296. {
  297. struct NVGpaint gloss, bg;
  298. float ex = w *0.23f;
  299. float ey = h * 0.5f;
  300. float lx = x + ex;
  301. float ly = y + ey;
  302. float rx = x + w - ex;
  303. float ry = y + ey;
  304. float dx,dy,d;
  305. float br = (ex < ey ? ex : ey) * 0.5f;
  306. float blink = 1 - pow(sinf(t*0.5f),200)*0.8f;
  307. bg = nvgLinearGradient(vg, x,y+h*0.5f,x+w*0.1f,y+h, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,16));
  308. nvgBeginPath(vg);
  309. nvgEllipse(vg, lx+3.0f,ly+16.0f, ex,ey);
  310. nvgEllipse(vg, rx+3.0f,ry+16.0f, ex,ey);
  311. nvgFillPaint(vg, bg);
  312. nvgFill(vg);
  313. bg = nvgLinearGradient(vg, x,y+h*0.25f,x+w*0.1f,y+h, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
  314. nvgBeginPath(vg);
  315. nvgEllipse(vg, lx,ly, ex,ey);
  316. nvgEllipse(vg, rx,ry, ex,ey);
  317. nvgFillPaint(vg, bg);
  318. nvgFill(vg);
  319. dx = (mx - rx) / (ex * 10);
  320. dy = (my - ry) / (ey * 10);
  321. d = sqrtf(dx*dx+dy*dy);
  322. if (d > 1.0f) {
  323. dx /= d; dy /= d;
  324. }
  325. dx *= ex*0.4f;
  326. dy *= ey*0.5f;
  327. nvgBeginPath(vg);
  328. nvgEllipse(vg, lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink);
  329. nvgFillColor(vg, nvgRGBA(32,32,32,255));
  330. nvgFill(vg);
  331. dx = (mx - rx) / (ex * 10);
  332. dy = (my - ry) / (ey * 10);
  333. d = sqrtf(dx*dx+dy*dy);
  334. if (d > 1.0f) {
  335. dx /= d; dy /= d;
  336. }
  337. dx *= ex*0.4f;
  338. dy *= ey*0.5f;
  339. nvgBeginPath(vg);
  340. nvgEllipse(vg, rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink);
  341. nvgFillColor(vg, nvgRGBA(32,32,32,255));
  342. nvgFill(vg);
  343. gloss = nvgRadialGradient(vg, lx-ex*0.25f,ly-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
  344. nvgBeginPath(vg);
  345. nvgEllipse(vg, lx,ly, ex,ey);
  346. nvgFillPaint(vg, gloss);
  347. nvgFill(vg);
  348. gloss = nvgRadialGradient(vg, rx-ex*0.25f,ry-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
  349. nvgBeginPath(vg);
  350. nvgEllipse(vg, rx,ry, ex,ey);
  351. nvgFillPaint(vg, gloss);
  352. nvgFill(vg);
  353. }
  354. void drawGraph(struct NVGcontext* vg, float x, float y, float w, float h, float t)
  355. {
  356. struct NVGpaint bg;
  357. float samples[6];
  358. float sx[6], sy[6];
  359. float dx = w/5.0f;
  360. int i;
  361. samples[0] = (1+sinf(t*1.2345f+cosf(t*0.33457f)*0.44f))*0.5f;
  362. samples[1] = (1+sinf(t*0.68363f+cosf(t*1.3f)*1.55f))*0.5f;
  363. samples[2] = (1+sinf(t*1.1642f+cosf(t*0.33457)*1.24f))*0.5f;
  364. samples[3] = (1+sinf(t*0.56345f+cosf(t*1.63f)*0.14f))*0.5f;
  365. samples[4] = (1+sinf(t*1.6245f+cosf(t*0.254f)*0.3f))*0.5f;
  366. samples[5] = (1+sinf(t*0.345f+cosf(t*0.03f)*0.6f))*0.5f;
  367. for (i = 0; i < 6; i++) {
  368. sx[i] = x+i*dx;
  369. sy[i] = y+h*samples[i]*0.8f;
  370. }
  371. // Graph background
  372. bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(0,160,192,0), nvgRGBA(0,160,192,64));
  373. nvgBeginPath(vg);
  374. nvgMoveTo(vg, sx[0], sy[0]);
  375. for (i = 1; i < 6; i++)
  376. nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
  377. nvgLineTo(vg, x+w, y+h);
  378. nvgLineTo(vg, x, y+h);
  379. nvgFillPaint(vg, bg);
  380. nvgFill(vg);
  381. // Graph line
  382. nvgBeginPath(vg);
  383. nvgMoveTo(vg, sx[0], sy[0]+2);
  384. for (i = 1; i < 6; i++)
  385. nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1]+2, sx[i]-dx*0.5f,sy[i]+2, sx[i],sy[i]+2);
  386. nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
  387. nvgStrokeWidth(vg, 3.0f);
  388. nvgStroke(vg);
  389. nvgBeginPath(vg);
  390. nvgMoveTo(vg, sx[0], sy[0]);
  391. for (i = 1; i < 6; i++)
  392. nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
  393. nvgStrokeColor(vg, nvgRGBA(0,160,192,255));
  394. nvgStrokeWidth(vg, 3.0f);
  395. nvgStroke(vg);
  396. // Graph sample pos
  397. for (i = 0; i < 6; i++) {
  398. bg = nvgRadialGradient(vg, sx[i],sy[i]+2, 3.0f,8.0f, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,0));
  399. nvgBeginPath(vg);
  400. nvgRect(vg, sx[i]-10, sy[i]-10+2, 20,20);
  401. nvgFillPaint(vg, bg);
  402. nvgFill(vg);
  403. }
  404. nvgBeginPath(vg);
  405. for (i = 0; i < 6; i++)
  406. nvgCircle(vg, sx[i], sy[i], 4.0f);
  407. nvgFillColor(vg, nvgRGBA(0,160,192,255));
  408. nvgFill(vg);
  409. nvgBeginPath(vg);
  410. for (i = 0; i < 6; i++)
  411. nvgCircle(vg, sx[i], sy[i], 2.0f);
  412. nvgFillColor(vg, nvgRGBA(220,220,220,255));
  413. nvgFill(vg);
  414. nvgStrokeWidth(vg, 1.0f);
  415. }
  416. void drawThumbnails(struct NVGcontext* vg, float x, float y, float w, float h, const int* images, int nimages, float t)
  417. {
  418. float cornerRadius = 3.0f;
  419. struct NVGpaint shadowPaint, imgPaint, fadePaint;
  420. float ix,iy,iw,ih;
  421. float thumb = 60.0f;
  422. float arry = 30.5f;
  423. int imgw, imgh;
  424. float stackh = (nimages/2) * (thumb+10) + 10;
  425. int i;
  426. float u = (1+cosf(t*0.5f))*0.5f;
  427. float scrollh;
  428. nvgSave(vg);
  429. // nvgClearState(vg);
  430. // Drop shadow
  431. shadowPaint = nvgBoxGradient(vg, x,y+4, w,h, cornerRadius*2, 20, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
  432. nvgBeginPath(vg);
  433. nvgRect(vg, x-10,y-10, w+20,h+30);
  434. nvgRoundedRect(vg, x,y, w,h, cornerRadius);
  435. nvgPathWinding(vg, NVG_HOLE);
  436. nvgFillPaint(vg, shadowPaint);
  437. nvgFill(vg);
  438. // Window
  439. nvgBeginPath(vg);
  440. nvgRoundedRect(vg, x,y, w,h, cornerRadius);
  441. nvgMoveTo(vg, x-10,y+arry);
  442. nvgLineTo(vg, x+1,y+arry-11);
  443. nvgLineTo(vg, x+1,y+arry+11);
  444. nvgFillColor(vg, nvgRGBA(200,200,200,255));
  445. nvgFill(vg);
  446. nvgSave(vg);
  447. nvgScissor(vg, x,y,w,h);
  448. nvgTranslate(vg, 0, -(stackh - h)*u);
  449. for (i = 0; i < nimages; i++) {
  450. float tx, ty;
  451. tx = x+10;
  452. ty = y+10;
  453. tx += (i%2) * (thumb+10);
  454. ty += (i/2) * (thumb+10);
  455. nvgImageSize(vg, images[i], &imgw, &imgh);
  456. if (imgw < imgh) {
  457. iw = thumb;
  458. ih = iw * (float)imgh/(float)imgw;
  459. ix = 0;
  460. iy = -(ih-thumb)*0.5f;
  461. } else {
  462. ih = thumb;
  463. iw = ih * (float)imgw/(float)imgh;
  464. ix = -(iw-thumb)*0.5f;
  465. iy = 0;
  466. }
  467. imgPaint = nvgImagePattern(vg, tx+ix, ty+iy, iw,ih, 0.0f/180.0f*NVG_PI, images[i], 0);
  468. nvgBeginPath(vg);
  469. nvgRoundedRect(vg, tx,ty, thumb,thumb, 5);
  470. nvgFillPaint(vg, imgPaint);
  471. nvgFill(vg);
  472. shadowPaint = nvgBoxGradient(vg, tx-1,ty, thumb+2,thumb+2, 5, 3, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
  473. nvgBeginPath(vg);
  474. nvgRect(vg, tx-5,ty-5, thumb+10,thumb+10);
  475. nvgRoundedRect(vg, tx,ty, thumb,thumb, 6);
  476. nvgPathWinding(vg, NVG_HOLE);
  477. nvgFillPaint(vg, shadowPaint);
  478. nvgFill(vg);
  479. nvgBeginPath(vg);
  480. nvgRoundedRect(vg, tx+0.5f,ty+0.5f, thumb-1,thumb-1, 4-0.5f);
  481. nvgStrokeWidth(vg,1.0f);
  482. nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
  483. nvgStroke(vg);
  484. }
  485. nvgRestore(vg);
  486. // Hide fades
  487. fadePaint = nvgLinearGradient(vg, x,y,x,y+6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
  488. nvgBeginPath(vg);
  489. nvgRect(vg, x+4,y,w-8,6);
  490. nvgFillPaint(vg, fadePaint);
  491. nvgFill(vg);
  492. fadePaint = nvgLinearGradient(vg, x,y+h,x,y+h-6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
  493. nvgBeginPath(vg);
  494. nvgRect(vg, x+4,y+h-6,w-8,6);
  495. nvgFillPaint(vg, fadePaint);
  496. nvgFill(vg);
  497. // Scroll bar
  498. shadowPaint = nvgBoxGradient(vg, x+w-12+1,y+4+1, 8,h-8, 3,4, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
  499. nvgBeginPath(vg);
  500. nvgRoundedRect(vg, x+w-12,y+4, 8,h-8, 3);
  501. nvgFillPaint(vg, shadowPaint);
  502. // nvgFillColor(vg, nvgRGBA(255,0,0,128));
  503. nvgFill(vg);
  504. scrollh = (h/stackh) * (h-8);
  505. shadowPaint = nvgBoxGradient(vg, x+w-12-1,y+4+(h-8-scrollh)*u-1, 8,scrollh, 3,4, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
  506. nvgBeginPath(vg);
  507. nvgRoundedRect(vg, x+w-12+1,y+4+1 + (h-8-scrollh)*u, 8-2,scrollh-2, 2);
  508. nvgFillPaint(vg, shadowPaint);
  509. // nvgFillColor(vg, nvgRGBA(0,0,0,128));
  510. nvgFill(vg);
  511. nvgRestore(vg);
  512. }
  513. void drawColorwheel(struct NVGcontext* vg, float x, float y, float w, float h, float t)
  514. {
  515. int i;
  516. float r0, r1, ax,ay, bx,by, cx,cy, aeps, r;
  517. float hue = sinf(t * 0.12f);
  518. struct NVGpaint paint;
  519. nvgSave(vg);
  520. /* nvgBeginPath(vg);
  521. nvgRect(vg, x,y,w,h);
  522. nvgFillColor(vg, nvgRGBA(255,0,0,128));
  523. nvgFill(vg);*/
  524. cx = x + w*0.5f;
  525. cy = y + h*0.5f;
  526. r1 = (w < h ? w : h) * 0.5f - 5.0f;
  527. r0 = r1 - 20.0f;
  528. aeps = 0.5f / r1; // half a pixel arc length in radians (2pi cancels out).
  529. for (i = 0; i < 6; i++) {
  530. float a0 = (float)i / 6.0f * NVG_PI * 2.0f - aeps;
  531. float a1 = (float)(i+1.0f) / 6.0f * NVG_PI * 2.0f + aeps;
  532. nvgBeginPath(vg);
  533. nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW);
  534. nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW);
  535. nvgClosePath(vg);
  536. ax = cx + cosf(a0) * (r0+r1)*0.5f;
  537. ay = cy + sinf(a0) * (r0+r1)*0.5f;
  538. bx = cx + cosf(a1) * (r0+r1)*0.5f;
  539. by = cy + sinf(a1) * (r0+r1)*0.5f;
  540. paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgHSLA(a0/(NVG_PI*2),1.0f,0.55f,255), nvgHSLA(a1/(NVG_PI*2),1.0f,0.55f,255));
  541. nvgFillPaint(vg, paint);
  542. nvgFill(vg);
  543. }
  544. nvgBeginPath(vg);
  545. nvgCircle(vg, cx,cy, r0-0.5f);
  546. nvgCircle(vg, cx,cy, r1+0.5f);
  547. nvgStrokeColor(vg, nvgRGBA(0,0,0,64));
  548. nvgStrokeWidth(vg, 1.0f);
  549. nvgStroke(vg);
  550. // Selector
  551. nvgSave(vg);
  552. nvgTranslate(vg, cx,cy);
  553. nvgRotate(vg, hue*NVG_PI*2);
  554. // Marker on
  555. nvgStrokeWidth(vg, 2.0f);
  556. nvgBeginPath(vg);
  557. nvgRect(vg, r0-1,-3,r1-r0+2,6);
  558. nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
  559. nvgStroke(vg);
  560. paint = nvgBoxGradient(vg, r0-3,-5,r1-r0+6,10, 2,4, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
  561. nvgBeginPath(vg);
  562. nvgRect(vg, r0-2-10,-4-10,r1-r0+4+20,8+20);
  563. nvgRect(vg, r0-2,-4,r1-r0+4,8);
  564. nvgPathWinding(vg, NVG_HOLE);
  565. nvgFillPaint(vg, paint);
  566. nvgFill(vg);
  567. // Center triangle
  568. r = r0 - 6;
  569. ax = cosf(120.0f/180.0f*NVG_PI) * r;
  570. ay = sinf(120.0f/180.0f*NVG_PI) * r;
  571. bx = cosf(-120.0f/180.0f*NVG_PI) * r;
  572. by = sinf(-120.0f/180.0f*NVG_PI) * r;
  573. nvgBeginPath(vg);
  574. nvgMoveTo(vg, r,0);
  575. nvgLineTo(vg, ax,ay);
  576. nvgLineTo(vg, bx,by);
  577. nvgClosePath(vg);
  578. paint = nvgLinearGradient(vg, r,0, ax,ay, nvgHSLA(hue,1.0f,0.5f,255), nvgRGBA(255,255,255,255));
  579. nvgFillPaint(vg, paint);
  580. nvgFill(vg);
  581. paint = nvgLinearGradient(vg, (r+ax)*0.5f,(0+ay)*0.5f, bx,by, nvgRGBA(0,0,0,0), nvgRGBA(0,0,0,255));
  582. nvgFillPaint(vg, paint);
  583. nvgFill(vg);
  584. nvgStrokeColor(vg, nvgRGBA(0,0,0,64));
  585. nvgStroke(vg);
  586. // Select circle on triangle
  587. ax = cosf(120.0f/180.0f*NVG_PI) * r*0.3f;
  588. ay = sinf(120.0f/180.0f*NVG_PI) * r*0.4f;
  589. nvgStrokeWidth(vg, 2.0f);
  590. nvgBeginPath(vg);
  591. nvgCircle(vg, ax,ay,5);
  592. nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
  593. nvgStroke(vg);
  594. paint = nvgRadialGradient(vg, ax,ay, 7,9, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
  595. nvgBeginPath(vg);
  596. nvgRect(vg, ax-20,ay-20,40,40);
  597. nvgCircle(vg, ax,ay,7);
  598. nvgPathWinding(vg, NVG_HOLE);
  599. nvgFillPaint(vg, paint);
  600. nvgFill(vg);
  601. nvgRestore(vg);
  602. nvgRestore(vg);
  603. }
  604. void drawLines(struct NVGcontext* vg, float x, float y, float w, float h, float t)
  605. {
  606. int i, j;
  607. float pad = 5.0f, s = w/9.0f - pad*2;
  608. float pts[4*2], fx, fy;
  609. int joins[3] = {NVG_MITER, NVG_ROUND, NVG_BEVEL};
  610. int caps[3] = {NVG_BUTT, NVG_ROUND, NVG_SQUARE};
  611. NVG_NOTUSED(h);
  612. nvgSave(vg);
  613. pts[0] = -s*0.25f + cosf(t*0.3f) * s*0.5f;
  614. pts[1] = sinf(t*0.3f) * s*0.5f;
  615. pts[2] = -s*0.25;
  616. pts[3] = 0;
  617. pts[4] = s*0.25f;
  618. pts[5] = 0;
  619. pts[6] = s*0.25f + cosf(-t*0.3f) * s*0.5f;
  620. pts[7] = sinf(-t*0.3f) * s*0.5f;
  621. for (i = 0; i < 3; i++) {
  622. for (j = 0; j < 3; j++) {
  623. fx = x + s*0.5f + (i*3+j)/9.0f*w + pad;
  624. fy = y - s*0.5f + pad;
  625. nvgLineCap(vg, caps[i]);
  626. nvgLineJoin(vg, joins[j]);
  627. nvgStrokeWidth(vg, s*0.3f);
  628. nvgStrokeColor(vg, nvgRGBA(0,0,0,160));
  629. nvgBeginPath(vg);
  630. nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
  631. nvgLineTo(vg, fx+pts[2], fy+pts[3]);
  632. nvgLineTo(vg, fx+pts[4], fy+pts[5]);
  633. nvgLineTo(vg, fx+pts[6], fy+pts[7]);
  634. nvgStroke(vg);
  635. nvgLineCap(vg, NVG_BUTT);
  636. nvgLineJoin(vg, NVG_BEVEL);
  637. nvgStrokeWidth(vg, 1.0f);
  638. nvgStrokeColor(vg, nvgRGBA(0,192,255,255));
  639. nvgBeginPath(vg);
  640. nvgMoveTo(vg, fx+pts[0], fy+pts[1]);
  641. nvgLineTo(vg, fx+pts[2], fy+pts[3]);
  642. nvgLineTo(vg, fx+pts[4], fy+pts[5]);
  643. nvgLineTo(vg, fx+pts[6], fy+pts[7]);
  644. nvgStroke(vg);
  645. }
  646. }
  647. nvgRestore(vg);
  648. }
  649. int loadDemoData(struct NVGcontext* vg, struct DemoData* data)
  650. {
  651. int i;
  652. if (vg == NULL)
  653. return -1;
  654. for (i = 0; i < 12; i++) {
  655. char file[128];
  656. snprintf(file, 128, "./nanovg_res/images/image%d.jpg", i+1);
  657. data->images[i] = nvgCreateImage(vg, file);
  658. if (data->images[i] == 0) {
  659. printf("Could not load %s.\n", file);
  660. return -1;
  661. }
  662. }
  663. data->fontIcons = nvgCreateFont(vg, "icons", "./nanovg_res/entypo.ttf");
  664. if (data->fontIcons == -1) {
  665. printf("Could not add font icons.\n");
  666. return -1;
  667. }
  668. data->fontNormal = nvgCreateFont(vg, "sans", "./nanovg_res/Roboto-Regular.ttf");
  669. if (data->fontNormal == -1) {
  670. printf("Could not add font italic.\n");
  671. return -1;
  672. }
  673. data->fontBold = nvgCreateFont(vg, "sans-bold", "./nanovg_res/Roboto-Bold.ttf");
  674. if (data->fontBold == -1) {
  675. printf("Could not add font bold.\n");
  676. return -1;
  677. }
  678. return 0;
  679. }
  680. void freeDemoData(struct NVGcontext* vg, struct DemoData* data)
  681. {
  682. int i;
  683. if (vg == NULL)
  684. return;
  685. for (i = 0; i < 12; i++)
  686. nvgDeleteImage(vg, data->images[i]);
  687. }
  688. void drawParagraph(struct NVGcontext* vg, float x, float y, float width, float height, float mx, float my)
  689. {
  690. struct NVGtextRow rows[3];
  691. struct NVGglyphPosition glyphs[100];
  692. const char* text = "This is longer chunk of text.\n \n Would have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party.";
  693. const char* start;
  694. const char* end;
  695. int nrows, i, nglyphs, j, lnum = 0;
  696. float lineh;
  697. float caretx, px;
  698. float bounds[4];
  699. float gx,gy;
  700. int gutter = 0;
  701. NVG_NOTUSED(height);
  702. nvgSave(vg);
  703. nvgFontSize(vg, 18.0f);
  704. nvgFontFace(vg, "sans");
  705. nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP);
  706. nvgTextMetrics(vg, NULL, NULL, &lineh);
  707. // The text break API can be used to fill a large buffer of rows,
  708. // or to iterate over the text just few lines (or just one) at a time.
  709. // The "next" variable of the last returned item tells where to continue.
  710. start = text;
  711. end = text + strlen(text);
  712. while ((nrows = nvgTextBreakLines(vg, start, end, width, rows, 3))) {
  713. for (i = 0; i < nrows; i++) {
  714. struct NVGtextRow* row = &rows[i];
  715. int hit = mx > x && mx < (x+width) && my >= y && my < (y+lineh);
  716. nvgBeginPath(vg);
  717. nvgFillColor(vg, nvgRGBA(255,255,255,hit?64:8));
  718. nvgRect(vg, x, y, row->width, lineh);
  719. nvgFill(vg);
  720. nvgFillColor(vg, nvgRGBA(255,255,255,255));
  721. nvgText(vg, x, y, row->start, row->end);
  722. if (hit) {
  723. caretx = (mx < x+row->width/2) ? x : x+row->width;
  724. px = x;
  725. nglyphs = nvgTextGlyphPositions(vg, x, y, row->start, row->end, glyphs, 100);
  726. for (j = 0; j < nglyphs; j++) {
  727. float x0 = glyphs[j].x;
  728. float x1 = (j+1 < nglyphs) ? glyphs[j+1].x : x+row->width;
  729. float gx = x0 * 0.3f + x1 * 0.7f;
  730. if (mx >= px && mx < gx)
  731. caretx = glyphs[j].x;
  732. px = gx;
  733. }
  734. nvgBeginPath(vg);
  735. nvgFillColor(vg, nvgRGBA(255,192,0,255));
  736. nvgRect(vg, caretx, y, 1, lineh);
  737. nvgFill(vg);
  738. gutter = lnum+1;
  739. gx = x - 10;
  740. gy = y + lineh/2;
  741. }
  742. lnum++;
  743. y += lineh;
  744. }
  745. // Keep going...
  746. start = rows[nrows-1].next;
  747. }
  748. if (gutter) {
  749. char txt[16];
  750. snprintf(txt, sizeof(txt), "%d", gutter);
  751. nvgFontSize(vg, 13.0f);
  752. nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
  753. nvgTextBounds(vg, gx,gy, txt, NULL, bounds);
  754. nvgBeginPath(vg);
  755. nvgFillColor(vg, nvgRGBA(255,192,0,255));
  756. nvgRoundedRect(vg, (int)bounds[0]-4,(int)bounds[1]-2, (int)(bounds[2]-bounds[0])+8, (int)(bounds[3]-bounds[1])+4, ((int)(bounds[3]-bounds[1])+4)/2-1);
  757. nvgFill(vg);
  758. nvgFillColor(vg, nvgRGBA(32,32,32,255));
  759. nvgText(vg, gx,gy, txt, NULL);
  760. }
  761. y += 20.0f;
  762. nvgFontSize(vg, 13.0f);
  763. nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP);
  764. nvgTextLineHeight(vg, 1.2f);
  765. nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds);
  766. nvgBeginPath(vg);
  767. nvgFillColor(vg, nvgRGBA(220,220,220,255));
  768. nvgRoundedRect(vg, bounds[0]-2,bounds[1]-2, (int)(bounds[2]-bounds[0])+4, (int)(bounds[3]-bounds[1])+4, 3);
  769. px = (int)((bounds[2]+bounds[0])/2);
  770. nvgMoveTo(vg, px,bounds[1] - 10);
  771. nvgLineTo(vg, px+7,bounds[1]+1);
  772. nvgLineTo(vg, px-7,bounds[1]+1);
  773. nvgFill(vg);
  774. nvgFillColor(vg, nvgRGBA(0,0,0,220));
  775. nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL);
  776. nvgRestore(vg);
  777. }
  778. void drawWidths(struct NVGcontext* vg, float x, float y, float width)
  779. {
  780. int i;
  781. nvgSave(vg);
  782. nvgStrokeColor(vg, nvgRGBA(0,0,0,255));
  783. for (i = 0; i < 20; i++) {
  784. float w = (i+0.5f)*0.1f;
  785. nvgStrokeWidth(vg, w);
  786. nvgBeginPath(vg);
  787. nvgMoveTo(vg, x,y);
  788. nvgLineTo(vg, x+width,y+width*0.3f);
  789. nvgStroke(vg);
  790. y += 10;
  791. }
  792. nvgRestore(vg);
  793. }
  794. void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float height,
  795. float t, int blowup, struct DemoData* data)
  796. {
  797. float x,y,popy;
  798. drawEyes(vg, width - 250, 50, 150, 100, mx, my, t);
  799. drawParagraph(vg, width - 450, 50, 150, 100, mx, my);
  800. drawGraph(vg, 0, height/2, width, height/2, t);
  801. drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t);
  802. // Line joints
  803. drawLines(vg, 50, height-50, 600, 50, t);
  804. // Line width;
  805. drawWidths(vg, 10, 50, 30);
  806. nvgSave(vg);
  807. if (blowup) {
  808. nvgRotate(vg, sinf(t*0.3f)*5.0f/180.0f*NVG_PI);
  809. nvgScale(vg, 2.0f, 2.0f);
  810. }
  811. // Widgets
  812. drawWindow(vg, "Widgets `n Stuff", 50, 50, 300, 400);
  813. x = 60; y = 95;
  814. drawSearchBox(vg, "Search", x,y,280,25);
  815. y += 40;
  816. drawDropDown(vg, "Effects", x,y,280,28);
  817. popy = y + 14;
  818. y += 45;
  819. // Form
  820. drawLabel(vg, "Login", x,y, 280,20);
  821. y += 25;
  822. drawEditBox(vg, "Email", x,y, 280,28);
  823. y += 35;
  824. drawEditBox(vg, "Password", x,y, 280,28);
  825. y += 38;
  826. drawCheckBox(vg, "Remember me", x,y, 140,28);
  827. drawButton(vg, ICON_LOGIN, "Sign in", x+138, y, 140, 28, nvgRGBA(0,96,128,255));
  828. y += 45;
  829. // Slider
  830. drawLabel(vg, "Diameter", x,y, 280,20);
  831. y += 25;
  832. drawEditBoxNum(vg, "123.00", "px", x+180,y, 100,28);
  833. drawSlider(vg, 0.4f, x,y, 170,28);
  834. y += 55;
  835. drawButton(vg, ICON_TRASH, "Delete", x, y, 160, 28, nvgRGBA(128,16,8,255));
  836. drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0));
  837. // Thumbnails box
  838. drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t);
  839. nvgRestore(vg);
  840. }
  841. static int mini(int a, int b) { return a < b ? a : b; }
  842. static void unpremultiplyAlpha(unsigned char* image, int w, int h, int stride)
  843. {
  844. int x,y;
  845. // Unpremultiply
  846. for (y = 0; y < h; y++) {
  847. unsigned char *row = &image[y*stride];
  848. for (x = 0; x < w; x++) {
  849. int r = row[0], g = row[1], b = row[2], a = row[3];
  850. if (a != 0) {
  851. row[0] = (int)mini(r*255/a, 255);
  852. row[1] = (int)mini(g*255/a, 255);
  853. row[2] = (int)mini(b*255/a, 255);
  854. }
  855. row += 4;
  856. }
  857. }
  858. // Defringe
  859. for (y = 0; y < h; y++) {
  860. unsigned char *row = &image[y*stride];
  861. for (x = 0; x < w; x++) {
  862. int r = 0, g = 0, b = 0, a = row[3], n = 0;
  863. if (a == 0) {
  864. if (x-1 > 0 && row[-1] != 0) {
  865. r += row[-4];
  866. g += row[-3];
  867. b += row[-2];
  868. n++;
  869. }
  870. if (x+1 < w && row[7] != 0) {
  871. r += row[4];
  872. g += row[5];
  873. b += row[6];
  874. n++;
  875. }
  876. if (y-1 > 0 && row[-stride+3] != 0) {
  877. r += row[-stride];
  878. g += row[-stride+1];
  879. b += row[-stride+2];
  880. n++;
  881. }
  882. if (y+1 < h && row[stride+3] != 0) {
  883. r += row[stride];
  884. g += row[stride+1];
  885. b += row[stride+2];
  886. n++;
  887. }
  888. if (n > 0) {
  889. row[0] = r/n;
  890. row[1] = g/n;
  891. row[2] = b/n;
  892. }
  893. }
  894. row += 4;
  895. }
  896. }
  897. }
  898. static void setAlpha(unsigned char* image, int w, int h, int stride, unsigned char a)
  899. {
  900. int x, y;
  901. for (y = 0; y < h; y++) {
  902. unsigned char* row = &image[y*stride];
  903. for (x = 0; x < w; x++)
  904. row[x*4+3] = a;
  905. }
  906. }
  907. static void flipHorizontal(unsigned char* image, int w, int h, int stride)
  908. {
  909. int i = 0, j = h-1, k;
  910. while (i < j) {
  911. unsigned char* ri = &image[i * stride];
  912. unsigned char* rj = &image[j * stride];
  913. for (k = 0; k < w*4; k++) {
  914. unsigned char t = ri[k];
  915. ri[k] = rj[k];
  916. rj[k] = t;
  917. }
  918. i++;
  919. j--;
  920. }
  921. }
  922. void saveScreenShot(int w, int h, int premult, const char* name)
  923. {
  924. unsigned char* image = (unsigned char*)malloc(w*h*4);
  925. if (image == NULL)
  926. return;
  927. glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image);
  928. if (premult)
  929. unpremultiplyAlpha(image, w, h, w*4);
  930. else
  931. setAlpha(image, w, h, w*4, 255);
  932. flipHorizontal(image, w, h, w*4);
  933. stbi_write_png(name, w, h, 4, image, w*4);
  934. free(image);
  935. }