1 /* 2 Copyright Chris Jones 2020. 3 Distributed under the Boost Software License, Version 1.0. 4 See accompanying file Licence.txt or copy at... 5 https://www.boost.org/LICENSE_1_0.txt 6 */ 7 8 module app; 9 10 import std.stdio; 11 import core.time; 12 13 import window; 14 import dg2d.canvas; 15 import dg2d.font; 16 17 import dg2d; 18 import dg2d.misc; 19 20 import demo.panels; 21 22 void main() 23 { 24 loadPlatformWindow(); 25 auto wnd = createPlatformWindow(); 26 wnd.setContent(new MainPanel()); 27 wnd.create(200,200,800,800,"graphics test"); 28 runMainLoop(); 29 } 30 31 class MainPanel : Widget 32 { 33 this() 34 { 35 super(0,0,800,800); 36 37 import rawgfx; 38 font = loadFont(rawfont); 39 font.setSize(15); 40 41 testcvs = new Canvas(800,800); 42 43 panels ~= new SolidPanel1(); 44 panels ~= new SolidPanel2(); 45 46 panels ~= new LinearPanel1(); 47 48 panels ~= new AngularPanel1(); 49 50 panels ~= new RadialPanel1(); 51 panels ~= new RadialPanel2(); 52 panels ~= new RadialPanel3(); 53 addChild(panels[0]); 54 55 infobtn = new Button(32,10,600,40,"",font); 56 infobtn.setOnClick(&clicked); 57 addChild(infobtn); 58 } 59 60 override void onPaint(Canvas canvas) 61 { 62 canvas.fill(0xFF000000); 63 } 64 65 override void onTimer() 66 { 67 Duration t = profilePanel(panels[gfxidx],5); 68 for (int i = 0; i < timings.length - 1; i++) 69 timings[i] = timings[i + 1]; 70 timings[$-1] = 1000_000.0f / t.total!"usecs"; 71 72 fps = 0; 73 foreach(f; timings) fps = max(fps,f); 74 75 import std.conv; 76 infobtn.setText(panels[gfxidx].getInfo ~ ", FPS = " ~ to!string(fps)); 77 infobtn.repaint(); 78 } 79 80 void clicked() 81 { 82 removeChild(panels[gfxidx]); 83 gfxidx = (gfxidx+1) % panels.length; 84 addChild(panels[gfxidx]); 85 addChild(infobtn); 86 timings = 0; 87 repaint(); 88 } 89 90 Duration profilePanel(GFXPanel panel, int runs = 1) 91 { 92 testcvs.fill(0xFF000000); 93 panel.onPaint(testcvs); 94 Duration best = Duration.max; 95 foreach(i; 0..runs) 96 { 97 testcvs.fill(0xFF000000); 98 auto t = PerformanceClock.currTime; 99 panel.onPaint(testcvs); 100 auto d = PerformanceClock.currTime - t; 101 best = min(best, d); 102 } 103 return best; 104 } 105 106 size_t gfxidx = 0; 107 Font font; 108 Button infobtn; 109 GFXPanel[] panels; 110 Canvas testcvs; 111 float fps = 0; 112 float[25] timings = 0; 113 } 114 115 alias PerformanceClock = MonoTimeImpl!(ClockType.precise); 116 117 /* 118 class TestPanel : Widget 119 { 120 Path!float path; 121 122 this() 123 { 124 super(200,200,800,800); 125 path.moveTo(-4.48518,1.69193).lineTo( 9.89451,0.150122).lineTo(-1.97198,1.42315).close(); 126 path.moveTo(49.276596,11.177839).lineTo(12.747784,8.287876).lineTo(50.662491,11.286461).close(); 127 path.moveTo(-4.820584,34.291153).lineTo(-2.890672,34.366234).lineTo(50.161381,36.457401).close(); 128 path.moveTo(50.861496,20.968971).lineTo(-6.823852,26.630871).lineTo(46.685181,21.377645).close(); 129 path.moveTo(46.575394,54.352577).lineTo(18.876856,48.298264).lineTo(-5.532673,42.964012).close(); 130 path.moveTo(53.697834,37.919582).lineTo(16.089132,46.436161).lineTo(55.921280,37.421677).close(); 131 } 132 133 override void onPaint(Canvas canvas) 134 { 135 canvas.fill(0xFF000000); 136 canvas.draw(path,0xFFFFFFFF,WindingRule.EvenOdd); 137 } 138 } 139 */ 140 /* 141 Test paths 142 */ 143 /* 144 Path!float gfx_area; 145 Path!float gfx_borders; 146 Path!float gfx_lines50; 147 Path!float gfx_lines250; 148 Path!float gfx_rects; 149 Path!float gfx_text_l; 150 Path!float gfx_text_s; 151 Gradient gfx_grad1; 152 Gradient gfx_grad2; 153 Gradient gfx_grad3; 154 Font gfx_font; 155 156 static this() 157 { 158 import rawgfx; 159 160 RoundRect!float rect = RoundRect!float(200,200,600,600,60,60); 161 162 foreach(i; 0..36) 163 { 164 gfx_area.append(rect.asPath.scale(i*0.02,i*0.02,600,600).rotate(400,400,i*10)); 165 } 166 167 RoundRect!float[70] rrr; 168 169 foreach(i; 0..rrr.length) 170 { 171 retry: 172 rrr[i] = randomRoundRect(); 173 auto osr = rrr[i].outset(8,true); 174 foreach(q; 0..i) 175 if (!intersect(osr,rrr[q]).isEmpty) goto retry; 176 gfx_borders.append(rrr[i].asPath); 177 gfx_borders.append(rrr[i].inset(10,true).asPath.retro); 178 } 179 180 // gfx_borders = loadSvgPath(rawborders,1); 181 gfx_lines50 = randomPath(50,1); 182 gfx_lines250 = randomPath(250,20); 183 gfx_rects = loadSvgPath(rawrects,1); 184 185 gfx_font = loadFont(rawfont); 186 187 gfx_font.setSize(40); 188 gfx_text_l = buildTextPath(gfx_font,loremIpsum); 189 gfx_font.setSize(14); 190 gfx_text_s = buildTextPath(gfx_font,loremIpsum); 191 192 gfx_grad1 = new Gradient; 193 gfx_grad1.addStop(0,0x80fF0000).addStop(0.33,0xff00FF00).addStop(0.66,0xFF0000FF).addStop(1.0,0xFFFF0000); 194 gfx_grad2 = new Gradient; 195 //gfx_grad2.addStop(0,0xFFa72ac6).addStop(0.5,0x00004092).addStop(0.95,0xFFb0ae00).addStop(1,0xFFa72ac6); 196 gfx_grad2.addStop(0,0xFFff0000).addStop(0.5,0xff00ff00).addStop(1.0,0xff0000ff); 197 } 198 */ 199 /* 200 long ProfileGFX(ref Path!float path) 201 { 202 import core.sys.windows.windows; 203 import std.conv; 204 205 Canvas canvas = new Canvas(800,800); 206 207 long time = long.max; 208 209 foreach(i;0..50) 210 { 211 // canvas.setClip(100,100,700,700); 212 213 canvas.fill(0xff274634); 214 215 long t = readCycleCounter(); 216 // canvas.fill(path, 0xff00ff00, WindingRule.NonZero); 217 t = readCycleCounter()-t; 218 if (t < time) time = t; 219 } 220 221 return time; 222 } 223 */ 224 225 /* 226 void ProfileAll() 227 { 228 writeln("area : ",ProfileGFX(gfx_area)); 229 writeln("borders : ",ProfileGFX(gfx_borders)); 230 writeln("lines : ",ProfileGFX(gfx_lines50)); 231 writeln("lines2 : ",ProfileGFX(gfx_lines250)); 232 writeln("rects : ",ProfileGFX(gfx_rects)); 233 writeln("text_l : ",ProfileGFX(gfx_text_l)); 234 writeln("text_s : ",ProfileGFX(gfx_text_s)); 235 } 236 */ 237 // load an svg path string/ 238 /* 239 Path!float loadSvgPath(string txt, float scale) 240 { 241 import readpath; 242 import std.stdio; 243 244 Path!float path; 245 readPathData(txt,path); 246 247 // path.scale(scale,scale); 248 249 path = path.scale(scale,scale); 250 251 return path; 252 } 253 */ 254 // dump an svg path string 255 /* 256 void dumpPath(ref Path!float path, string filename) 257 { 258 import std.stdio; 259 import std.conv; 260 261 File file = File(filename, "w"); 262 file.rawWrite("d=\""); 263 264 foreach(i; 0..path.length) 265 { 266 if (path.cmd(i) == PathCmd.move) 267 { 268 file.rawWrite("M "); 269 file.rawWrite(to!string(path[i].x)); 270 file.rawWrite(" "); 271 file.rawWrite(to!string(path[i].y)); 272 file.rawWrite(" "); 273 } 274 if (path.cmd(i) == PathCmd.line) 275 { 276 file.rawWrite("L "); 277 file.rawWrite(to!string(path[i].x)); 278 file.rawWrite(" "); 279 file.rawWrite(to!string(path[i].y)); 280 file.rawWrite(" "); 281 } 282 if (path.cmd(i+1) == PathCmd.quad) 283 { 284 file.rawWrite("Q "); 285 file.rawWrite(to!string(path[i].x)); 286 file.rawWrite(" "); 287 file.rawWrite(to!string(path[i].y)); 288 file.rawWrite(" "); 289 file.rawWrite(to!string(path[i+1].x)); 290 file.rawWrite(" "); 291 file.rawWrite(to!string(path[i+1].y)); 292 file.rawWrite(" "); 293 i++; 294 } 295 if (path.cmd(i+1) == PathCmd.cubic) 296 { 297 file.rawWrite("C "); 298 file.rawWrite(to!string(path[i].x)); 299 file.rawWrite(" "); 300 file.rawWrite(to!string(path[i].y)); 301 file.rawWrite(" "); 302 file.rawWrite(to!string(path[i+1].x)); 303 file.rawWrite(" "); 304 file.rawWrite(to!string(path[i+1].y)); 305 file.rawWrite(" "); 306 file.rawWrite(to!string(path[i+2].x)); 307 file.rawWrite(" "); 308 file.rawWrite(to!string(path[i+2].y)); 309 file.rawWrite(" "); 310 i+=2; 311 } 312 } 313 314 file.rawWrite("\" \n"); 315 } 316 */ 317 // generate random lines 318 /* 319 Path!float randomPath(int n, int seed) 320 { 321 import std.random; 322 323 auto rnd = Random(seed); 324 325 Path!float path; 326 path.moveTo(uniform(20.0f, 780.0f, rnd),uniform(20.0f, 780.0f, rnd)); 327 328 foreach(i; 0..n) 329 { 330 path.lineTo(uniform(20.0f, 780.0f, rnd),uniform(20.0f, 780.0f, rnd)); 331 } 332 path.close(); 333 334 return path; 335 } 336 */ 337 // generate random rounded rects 338 /* 339 Path!float randomRoundRect(int n, int seed) 340 { 341 import std.random; 342 343 auto rnd = Random(seed); 344 345 Path!float path; 346 347 foreach(i; 0..n) 348 { 349 float x = uniform(20.0f, 580.0f, rnd); 350 float y = uniform(20.0f, 580.0f, rnd); 351 float w = uniform(20.0f, 200.0f, rnd); 352 float h = uniform(20.0f, 200.0f, rnd); 353 float c = uniform(2.0f, ((w<h) ? w : h)/3, rnd); 354 float lpc = c-c*0.55228; 355 356 path.moveTo(x+c,y); 357 path.lineTo(x+w-c,y); 358 path.cubicTo(x+w-lpc,y, x+w,y+lpc, x+w,y+c); 359 path.lineTo(x+w,y+h-c); 360 path.cubicTo(x+w,y+h-lpc, x+w-lpc,y+h, x+w-c,y+h); 361 path.lineTo(x+c,y+h); 362 path.cubicTo(x+lpc,y+h, x,y+h-lpc, x,y+h-c); 363 path.lineTo(x,y+c); 364 path.cubicTo(x,y+lpc, x+lpc,y, x+c,y); 365 } 366 return path; 367 } 368 */ 369 /* 370 RoundRect!float randomRoundRect() 371 { 372 import std.random; 373 static auto rnd = Random(123); 374 375 float x = uniform(0, 600.0f, rnd); 376 float y = uniform(0, 600.0f, rnd); 377 float w = uniform(20, 200, rnd); 378 float h = uniform(20, 200, rnd); 379 float c = uniform(2.0f, ((w<h) ? w : h)/2, rnd); 380 381 RoundRect!float rect = RoundRect!float(x,y,x+w,y+w,c,c); 382 383 return rect; 384 } 385 */ 386 387 // build some text as a path 388 /* 389 Path!float buildTextPath(Font font, const char[] txt) 390 { 391 Path!float path; 392 float[] adv; 393 adv.length = txt.length; 394 font.getTextSpacing(txt,adv); 395 396 int i; 397 int subi = 0; 398 float subx = 50, y = 50; 399 float pos = subx; 400 401 for (i = 0; i < txt.length; i++) 402 { 403 if ((txt[i] == ' ') || (txt[i] == '\n')) 404 { 405 for (int k = subi; k <= i; k++) 406 { 407 font.addChar(path, subx, y, txt[k]); 408 subx += adv[k]; 409 } 410 subi = i+1; 411 } 412 413 if ((pos > 750) || (txt[i] == '\n')) 414 { 415 pos = 50+pos-subx; 416 subx = 50; 417 y += font.lineHeight(); 418 } 419 420 if (y > 750) return path; 421 422 pos += adv[i]; 423 } 424 return path; 425 } 426 */