1 /++ 2 A module for rendering objects. 3 4 Macros: 5 LREF = <a href="#$1">$1</a> 6 HREF = <a href="$1">$2</a> 7 8 Authors: $(HREF https://github.com/TodNaz,TodNaz) 9 Copyright: Copyright (c) 2020 - 2021, TodNaz. 10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 11 +/ 12 module tida.render; 13 14 /++ 15 Camera control object in render, 16 +/ 17 class Camera 18 { 19 import tida.vector, 20 tida.shape; 21 22 private: 23 Shape!float _port; 24 Shape!float _shape; 25 26 public @safe nothrow pure: 27 /++ 28 The port is the immediate visible part in the "room". The entire area in 29 the world that must be covered in the field of view. 30 +/ 31 @property Shape!float port() 32 { 33 return _port; 34 } 35 36 /// ditto 37 @property void port(Shape!float value) 38 { 39 _port = value; 40 } 41 42 /++ 43 The size of the visible part in the plane of the window. 44 +/ 45 @property Shape!float shape() 46 { 47 return _shape; 48 } 49 50 /// ditto 51 @property void shape(Shape!float value) 52 { 53 _shape = value; 54 } 55 56 /++ 57 Moves the visible field. 58 59 Params: 60 value = Factor movement. 61 +/ 62 void moveView(Vecf value) 63 { 64 _port = Shape!float.Rectangle(_port.begin + value, _port.end); 65 } 66 } 67 68 /// Renderer type 69 enum RenderType 70 { 71 unknown, 72 software, 73 opengl, 74 directx, // Not implement 75 vulkan // Not implement 76 } 77 78 /// A property that explains whether blending should be applied or not. 79 enum BlendMode 80 { 81 withoutBlend, /// Without blending 82 withBlend /// With blending 83 } 84 85 /++ 86 An interface for rendering objects to a display or other storehouse of pixels. 87 +/ 88 interface IRenderer 89 { 90 import tida.color, 91 tida.vector, 92 tida.shader, 93 tida.drawable, 94 tida.matrix; 95 96 @safe: 97 /// Updates the rendering surface if, for example, the window is resized. 98 void reshape(); 99 100 ///Camera for rendering. 101 @property void camera(Camera camera); 102 103 /// Camera for rendering. 104 @property Camera camera(); 105 106 /++ 107 Drawing a point. 108 109 Params: 110 vec = Point position. 111 color = Point color. 112 +/ 113 void point(Vecf vec, Color!ubyte color) @safe; 114 115 /++ 116 Line drawing. 117 118 Params: 119 points = Tops of lines. 120 color = Line color. 121 +/ 122 void line(Vecf[2] points, Color!ubyte color) @safe; 123 124 /++ 125 Drawing a rectangle. 126 127 Params: 128 position = Rectangle position. 129 width = Rectangle width. 130 height = Rectangle height. 131 color = Rectangle color. 132 isFill = Whether to fill the rectangle with color. 133 +/ 134 void rectangle( Vecf position, 135 uint width, 136 uint height, 137 Color!ubyte color, 138 bool isFill) @safe; 139 140 /++ 141 Drawning a circle. 142 143 Params: 144 position = Circle position. 145 radius = Circle radius. 146 color = Circle color. 147 isFill = Whether to fill the circle with color. 148 +/ 149 void circle(Vecf position, 150 float radius, 151 Color!ubyte color, 152 bool isFill) @safe; 153 154 /++ 155 Drawing a triangle by its three vertices. 156 157 Params: 158 points = Triangle vertices 159 color = Triangle color. 160 isFill = Whether it is necessary to fill the triangle with color. 161 +/ 162 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) @safe; 163 164 /++ 165 Draws a rectangle with rounded edges. 166 (Rendering is available only through hardware acceleration). 167 168 Params: 169 position = Position roundrectangle. 170 width = Width roundrectangle. 171 height = Height roundrectangle. 172 radius = Radius rounded edges. 173 color = Color roundrect. 174 isFill = Roundrect is filled color? 175 +/ 176 void roundrect( Vecf position, 177 uint width, 178 uint height, 179 float radius, 180 Color!ubyte color, 181 bool isFill) @safe; 182 183 /++ 184 Drawing a polygon from an array of vertices. 185 186 Params: 187 position = Polygon position. 188 points = Polygon vertices/ 189 color = Polygon color. 190 isFill = Whether it is necessary to fill the polygon with color. 191 +/ 192 void polygon( Vecf position, 193 Vecf[] points, 194 Color!ubyte color, 195 bool isFill) @safe; 196 197 /// Cleans the surface by filling it with color. 198 void clear() @safe; 199 200 /// Outputs the buffer to the window. 201 void drawning() @safe; 202 203 /// Gives the type of render. 204 RenderType type() @safe; 205 206 /// Set the coloring method. Those. with or without alpha blending. 207 void blendMode(BlendMode mode) @safe; 208 209 /// Set factor blend 210 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) @safe; 211 212 /// The color to fill when clearing. 213 void background(Color!ubyte background) @safe @property; 214 215 /// ditto 216 Color!ubyte background() @safe @property; 217 218 /++ 219 Memorize the shader for future reference. 220 221 Params: 222 name = The name of the shader by which it will be possible to pick up 223 the shader in the future. 224 program = Shader program. 225 +/ 226 void setShader(string name, Shader!Program program) @safe; 227 228 /++ 229 Pulls a shader from memory, getting it by name. Returns a null pointer 230 if no shader is found. 231 232 Params: 233 name = Shader name. 234 +/ 235 Shader!Program getShader(string name) @safe; 236 237 /// The current shader for the next object rendering. 238 void currentShader(Shader!Program program) @safe @property; 239 240 /// The current shader for the next object rendering. 241 Shader!Program currentShader() @safe @property; 242 243 /// Reset the shader to main. 244 void resetShader() @safe; 245 246 /// Current model matrix. 247 float[4][4] currentModelMatrix() @safe @property; 248 249 /// ditto 250 void currentModelMatrix(float[4][4] matrix) @safe @property; 251 252 /// Reset current model matrix. 253 final void resetModelMatrix() @safe 254 { 255 this.currentModelMatrix = identity(); 256 } 257 258 /++ 259 Renders an object. 260 261 See_Also: `tida.graph.drawable`. 262 +/ 263 final void draw(IDrawable drawable, Vecf position) @safe 264 { 265 position -= camera.port.begin; 266 drawable.draw(this, position); 267 } 268 269 /// ditto 270 final void drawEx( IDrawableEx drawable, 271 Vecf position, 272 float angle, 273 Vecf center, 274 Vecf size, 275 ubyte alpha, 276 Color!ubyte color = rgb(255, 255, 255)) @safe 277 { 278 position -= camera.port.begin; 279 drawable.drawEx(this, position, angle, center, size, alpha, color); 280 } 281 } 282 283 /++ 284 Render objects using hardware acceleration through an open graphics library. 285 +/ 286 class GLRender : IRenderer 287 { 288 import tida.window; 289 import tida.gl; 290 import tida.shader; 291 import tida.vertgen; 292 import tida.color; 293 import tida.vector; 294 import tida.matrix; 295 import tida.shape; 296 297 enum deprecatedVertex = 298 " 299 #version 130 300 in vec3 position; 301 302 uniform mat4 projection; 303 uniform mat4 model; 304 305 void main() 306 { 307 gl_Position = projection * model * vec4(position, 1.0f); 308 } 309 "; 310 311 enum deprecatedFragment = 312 " 313 #version 130 314 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 315 316 void main() 317 { 318 gl_FragColor = color; 319 } 320 "; 321 322 enum modernVertex = 323 " 324 #version 330 core 325 layout (location = 0) in vec3 position; 326 327 uniform mat4 projection; 328 uniform mat4 model; 329 330 void main() 331 { 332 gl_Position = projection * model * vec4(position, 1.0f); 333 } 334 "; 335 336 enum modernFragment = 337 " 338 #version 330 core 339 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 340 341 out vec4 fragColor; 342 343 void main() 344 { 345 fragColor = color; 346 } 347 "; 348 349 private: 350 IWindow window; 351 Color!ubyte _background; 352 Camera _camera; 353 mat4 _projection; 354 355 Shader!Program[string] shaders; 356 Shader!Program current; 357 358 mat4 _model; 359 360 bool _isModern = false; 361 362 public @trusted: 363 this(IWindow window) 364 { 365 this.window = window; 366 367 _camera = new Camera(); 368 _camera.shape = Shapef.Rectangle(vecf(0, 0), vecf(window.width, window.height)); 369 _camera.port = _camera.shape; 370 371 Shader!Program defaultShader = new Shader!Program(); 372 373 string vsource, fsource; 374 375 if (glslVersion == "1.10" || glslVersion == "1.20") 376 { 377 vsource = deprecatedVertex; 378 fsource = deprecatedFragment; 379 _isModern = false; 380 } else 381 { 382 vsource = modernVertex; 383 fsource = modernFragment; 384 _isModern = true; 385 } 386 387 Shader!Vertex defaultVertex = new Shader!Vertex(); 388 defaultVertex.bindSource(vsource); 389 390 Shader!Fragment defaultFragment = new Shader!Fragment(); 391 defaultFragment.bindSource(fsource); 392 393 defaultShader.attach(defaultVertex); 394 defaultShader.attach(defaultFragment); 395 defaultShader.link(); 396 397 setShader("Default", defaultShader); 398 399 _model = identity(); 400 blendMode(BlendMode.withBlend); 401 blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 402 403 this.reshape(); 404 } 405 406 @property mat4 projection() 407 { 408 return _projection; 409 } 410 411 int glBlendFactor(BlendFactor factor) 412 { 413 if (factor == BlendFactor.Zero) 414 return GL_ZERO; 415 else 416 if (factor == BlendFactor.One) 417 return GL_ONE; 418 else 419 if (factor == BlendFactor.SrcColor) 420 return GL_SRC_COLOR; 421 else 422 if (factor == BlendFactor.DstColor) 423 return GL_DST_COLOR; 424 else 425 if (factor == BlendFactor.OneMinusSrcColor) 426 return GL_ONE_MINUS_SRC_COLOR; 427 else 428 if (factor == BlendFactor.OneMinusDstColor) 429 return GL_ONE_MINUS_DST_COLOR; 430 else 431 if (factor == BlendFactor.SrcAlpha) 432 return GL_SRC_ALPHA; 433 else 434 if (factor == BlendFactor.DstAlpha) 435 return GL_DST_ALPHA; 436 else 437 if (factor == BlendFactor.OneMinusSrcAlpha) 438 return GL_ONE_MINUS_SRC_ALPHA; 439 else 440 if (factor == BlendFactor.OneMinusDstAlpha) 441 return GL_ONE_MINUS_DST_ALPHA; 442 443 return 0; 444 } 445 446 void setDefaultUniform(Color!ubyte color) 447 { 448 if (currentShader.getUniformLocation("projection") != -1) 449 currentShader.setUniform("projection", _projection); 450 451 if (currentShader.getUniformLocation("color") != -1) 452 currentShader.setUniform("color", color); 453 454 if (currentShader.getUniformLocation("model") != -1) 455 currentShader.setUniform("model", _model); 456 } 457 458 @property bool isModern() 459 { 460 return _isModern; 461 } 462 463 override: 464 void reshape() 465 { 466 import std.conv : to; 467 468 int yborder = 0; 469 470 version (Windows) 471 { 472 import core.sys.windows.windows; 473 474 if (window.border) 475 { 476 RECT crect, wrect; 477 GetClientRect((cast(Window) window).handle, &crect); 478 GetWindowRect((cast(Window) window).handle, &wrect); 479 480 481 yborder = -((wrect.bottom - wrect.top) - (crect.bottom - crect.top)); 482 } 483 } 484 485 glViewport(0, yborder, _camera.shape.end.x.to!int, _camera.shape.end.y.to!int); 486 this._projection = ortho(0.0, _camera.port.end.x, _camera.port.end.y, 0.0, -1.0, 1.0); 487 } 488 489 @property void camera(Camera camera) 490 { 491 _camera = camera; 492 } 493 494 @property Camera camera() 495 { 496 return _camera; 497 } 498 499 void point(Vecf vec, Color!ubyte color) 500 { 501 if (currentShader is null) 502 currentShader = getShader("Default"); 503 504 Shapef shape = Shapef.Point(vec); 505 VertexInfo!float vinfo = generateVertex!(float)(shape); 506 507 vinfo.bindBuffer(); 508 vinfo.bindVertexArray(); 509 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 510 511 currentShader.using(); 512 currentShader.enableVertex("position"); 513 514 setDefaultUniform(color); 515 516 vinfo.draw(vinfo.shapeinfo.type); 517 518 currentShader.disableVertex("position"); 519 vinfo.unbindBuffer(); 520 vinfo.unbindVertexArray(); 521 vinfo.deleting(); 522 523 resetShader(); 524 resetModelMatrix(); 525 } 526 527 void line(Vecf[2] points, Color!ubyte color) 528 { 529 if (currentShader is null) 530 currentShader = getShader("Default"); 531 532 auto shape = Shapef.Line(points[0], points[1]); 533 VertexInfo!float vinfo = generateVertex!(float)(shape); 534 535 vinfo.bindBuffer(); 536 vinfo.bindVertexArray(); 537 538 currentShader.enableVertex("position"); 539 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 540 541 vinfo.unbindBuffer(); 542 543 currentShader.using(); 544 setDefaultUniform(color); 545 vinfo.draw(vinfo.shapeinfo.type); 546 547 currentShader.disableVertex("position"); 548 vinfo.unbindVertexArray(); 549 vinfo.deleting(); 550 551 resetShader(); 552 resetModelMatrix(); 553 } 554 555 void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill) 556 { 557 if (currentShader is null) 558 currentShader = getShader("Default"); 559 560 Shapef shape; 561 562 if (isFill) 563 { 564 shape = Shapef.Rectangle(position, position + vecf(width, height)); 565 } else 566 { 567 shape = Shapef.RectangleLine(position, position + vecf(width, height)); 568 } 569 570 VertexInfo!float vinfo = generateVertex!(float)(shape); 571 572 vinfo.bindVertexArray(); 573 vinfo.bindBuffer(); 574 if (isFill) vinfo.bindElementBuffer(); 575 576 currentShader.enableVertex("position"); 577 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position"), 2); 578 579 vinfo.unbindBuffer(); 580 581 currentShader.using(); 582 setDefaultUniform(color); 583 584 if (isFill) 585 vinfo.draw(vinfo.shapeinfo.type, 1); 586 else 587 vinfo.draw(ShapeType.line, 4); 588 589 currentShader.disableVertex("position"); 590 vinfo.unbindVertexArray(); 591 if (isFill) vinfo.unbindElementBuffer(); 592 vinfo.deleting(); 593 594 resetShader(); 595 resetModelMatrix(); 596 } 597 598 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 599 { 600 if (currentShader is null) 601 currentShader = getShader("Default"); 602 603 Shapef shape; 604 605 if (isFill) 606 { 607 shape = Shapef.Circle(position, radius); 608 } else 609 { 610 shape = Shapef.CircleLine(position, radius); 611 } 612 613 VertexInfo!float vinfo = generateVertex!(float)(shape); 614 615 vinfo.bindVertexArray(); 616 vinfo.bindBuffer(); 617 618 currentShader.enableVertex("position"); 619 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 620 621 vinfo.unbindBuffer(); 622 623 currentShader.using(); 624 setDefaultUniform(color); 625 626 if (isFill) 627 vinfo.draw(vinfo.shapeinfo.type, 1); 628 else 629 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 630 631 currentShader.disableVertex("position"); 632 vinfo.unbindVertexArray(); 633 vinfo.deleting(); 634 635 resetShader(); 636 resetModelMatrix(); 637 } 638 639 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) 640 { 641 if (currentShader is null) 642 currentShader = getShader("Default"); 643 644 Shapef shape; 645 646 if (isFill) 647 { 648 shape = Shapef.RoundRectangle(position, position + vecf(width, height), radius); 649 } else 650 { 651 shape = Shapef.RoundRectangleLine(position, position + vecf(width, height), radius); 652 } 653 654 VertexInfo!float vinfo = generateVertex!(float)(shape); 655 656 vinfo.bindVertexArray(); 657 vinfo.bindBuffer(); 658 659 currentShader.enableVertex("position"); 660 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 661 662 vinfo.unbindBuffer(); 663 664 currentShader.using(); 665 setDefaultUniform(color); 666 667 if (isFill) 668 vinfo.draw(vinfo.shapeinfo.type, 1); 669 else 670 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 671 672 currentShader.disableVertex("position"); 673 vinfo.unbindVertexArray(); 674 vinfo.deleting(); 675 676 resetShader(); 677 resetModelMatrix(); 678 } 679 680 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) 681 { 682 if (currentShader is null) 683 currentShader = getShader("Default"); 684 685 Shapef shape; 686 687 if (isFill) 688 { 689 shape = Shapef.Triangle(points); 690 } else 691 { 692 shape = Shapef.TriangleLine(points); 693 } 694 695 VertexInfo!float vinfo = generateVertex!(float)(shape); 696 697 vinfo.bindVertexArray(); 698 vinfo.bindBuffer(); 699 700 currentShader.enableVertex("position"); 701 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 702 703 vinfo.unbindBuffer(); 704 705 currentShader.using(); 706 setDefaultUniform(color); 707 708 if (isFill) 709 vinfo.draw(vinfo.shapeinfo.type, 1); 710 else 711 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 712 713 currentShader.disableVertex("position"); 714 vinfo.unbindVertexArray(); 715 vinfo.deleting(); 716 717 resetShader(); 718 resetModelMatrix(); 719 } 720 721 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 722 { 723 import std.algorithm : each; 724 725 if (currentShader is null) 726 currentShader = getShader("Default"); 727 728 Shapef shape; 729 points.each!((ref e) => e = position + e); 730 731 if (isFill) 732 { 733 shape = Shapef.Polygon(points); 734 } else 735 { 736 shape = Shapef.Polygon(points ~ points[0]); 737 } 738 739 VertexInfo!float vinfo = generateVertex!(float)(shape); 740 741 vinfo.bindVertexArray(); 742 vinfo.bindBuffer(); 743 744 currentShader.enableVertex("position"); 745 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 746 747 vinfo.unbindBuffer(); 748 749 currentShader.using(); 750 setDefaultUniform(color); 751 752 if (isFill) 753 vinfo.draw(vinfo.shapeinfo.type, 1); 754 else 755 glDrawArrays(GL_LINE_LOOP, 0, 2 * cast(int) vinfo.shapeinfo.data.length); 756 757 currentShader.disableVertex("position"); 758 vinfo.unbindVertexArray(); 759 vinfo.deleting(); 760 761 resetShader(); 762 resetModelMatrix(); 763 } 764 765 @property RenderType type() 766 { 767 return RenderType.opengl; 768 } 769 770 @property void background(Color!ubyte color) 771 { 772 _background = color; 773 glClearColor(color.rf, color.gf, color.bf, color.af); 774 } 775 776 @property Color!ubyte background() 777 { 778 return _background; 779 } 780 781 void clear() 782 { 783 glClear(GL_COLOR_BUFFER_BIT); 784 } 785 786 void drawning() 787 { 788 window.swapBuffers(); 789 } 790 791 void blendMode(BlendMode mode) 792 { 793 if (mode == BlendMode.withBlend) 794 { 795 glEnable(GL_BLEND); 796 } else 797 if (mode == BlendMode.withoutBlend) 798 { 799 glDisable(GL_BLEND); 800 } 801 } 802 803 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 804 { 805 glBlendFunc(glBlendFactor(sfactor), glBlendFactor(dfactor)); 806 } 807 808 void currentShader(Shader!Program program) 809 { 810 current = program; 811 } 812 813 Shader!Program currentShader() 814 { 815 return current; 816 } 817 818 void currentModelMatrix(float[4][4] matrix) 819 { 820 _model = matrix; 821 } 822 823 float[4][4] currentModelMatrix() 824 { 825 return _model; 826 } 827 828 void setShader(string name, Shader!Program program) 829 { 830 shaders[name] = program; 831 } 832 833 Shader!Program getShader(string name) 834 { 835 if (name in shaders) 836 return shaders[name]; 837 else 838 return null; 839 } 840 841 void resetShader() 842 { 843 current = null; 844 } 845 } 846 847 /++ 848 Implementation of the interface for interacting with the rendering canvas. 849 +/ 850 interface ICanvas 851 { 852 import tida.color; 853 import tida.vector; 854 855 @safe: 856 /++ 857 Allocate memory for the canvas at the specified size. 858 859 Params: 860 width = Canvas width. 861 height = Canvas height. 862 +/ 863 void allocatePlace(uint width, uint height); 864 865 /++ 866 Cleared the canvas with one color. 867 868 Params: 869 color = Cleared color. 870 +/ 871 void clearPlane(Color!ubyte color); 872 873 /++ 874 Draws a buffer to a storage object. 875 +/ 876 void drawTo(); 877 878 /// Blending mode (blend or not). 879 @property void blendMode(BlendMode mode); 880 881 /// ditto 882 @property BlendMode blendMode(); 883 884 /++ 885 Sets the color mixing factor (which formula to mix colors with). 886 +/ 887 void blendOperation(BlendFactor sfactor, BlendFactor dfactor); 888 889 /++ 890 Mixing factor (sfactor, dfactor). 891 +/ 892 BlendFactor[2] blendOperation(); 893 894 /++ 895 Draw a point on the canvas. 896 Draw only a point, the rest of the shapes are rendered. 897 898 Params: 899 position = Point position. 900 color = Point color. 901 +/ 902 void pointTo(Vecf position, Color!ubyte color); 903 /++ 904 Set port of visibility. 905 906 Params: 907 w = Port width. 908 h = Port height. 909 +/ 910 void viewport(uint w, uint h); 911 912 /++ 913 Move the visibility port to the specified coordinates. 914 915 Params: 916 x = Port x-axis position. 917 y = Port y-axis position. 918 +/ 919 void move(int x,int y); 920 921 /++ 922 Canvas data. 923 +/ 924 @property ref ubyte[] data(); 925 926 /// Canvas size. 927 @property uint[2] size(); 928 929 /++ 930 The real size of the world, where from the world it will be drawn to 931 the size of the canvas. 932 +/ 933 @property uint[2] portSize(); 934 935 /++ 936 Camera position (offset of all drawing points). 937 +/ 938 @property int[2] cameraPosition(); 939 } 940 941 template PointToImpl(int pixelformat, int bpc) 942 { 943 import tida.vector; 944 import tida.color; 945 946 static assert(isValidFormat!pixelformat); 947 948 static if(bpc == 0) 949 enum bytesperpixel = bytesPerColor!pixelformat; 950 else 951 enum bytesperpixel = bpc; 952 953 override void pointTo(Vecf position, Color!ubyte color) 954 { 955 import tida.each : Coord; 956 import std.conv : to; 957 958 position = position - vecf(cameraPosition); 959 960 immutable scaleWidth = (cast(float) portSize[0]) / (cast(float) size[0]); 961 immutable scaleHeight = (cast(float) portSize[1]) / (cast(float) size[1]); 962 int w = size[0] / portSize[0] + 1; 963 int h = size[0] / portSize[1] + 1; 964 965 position = position / vecf(scaleWidth, scaleHeight); 966 967 Color!ubyte bcolor; 968 969 foreach (ix, iy; Coord( position.x.to!int + w, position.y.to!int + h, 970 position.x.to!int, position.y.to!int)) 971 { 972 if (ix >= size[0] || iy >= size[1] || ix < 0 || iy < 0) continue; 973 immutable pos = ((iy * size[0]) + ix) * bytesperpixel; 974 975 if (blendMode == BlendMode.withBlend) { 976 Color!ubyte blendcolor; 977 978 static if (pixelformat == PixelFormat.BGRA) 979 { 980 blendcolor = rgba( data[pos+3], 981 data[pos+2], 982 data[pos+1], 983 data[pos]); 984 }else 985 static if (pixelformat == PixelFormat.BGR) 986 { 987 blendcolor = rgba( data[pos+2], 988 data[pos+1], 989 data[pos], 990 255); 991 }else 992 static if (pixelformat == PixelFormat.RGBA) 993 { 994 blendcolor = rgba( data[pos], 995 data[pos+1], 996 data[pos+2], 997 data[pos+3]); 998 }else 999 static if (pixelformat == PixelFormat.RGB) 1000 { 1001 blendcolor = rgba( data[pos], 1002 data[pos+1], 1003 data[pos+2], 1004 255); 1005 } 1006 1007 BlendFactor[2] factors = blendOperation(); 1008 bcolor = BlendFunc!ubyte(factors[0], factors[1])(color, blendcolor); 1009 }else 1010 bcolor = color; 1011 1012 if (pos < data.length) 1013 { 1014 static if (pixelformat == PixelFormat.BGRA) 1015 { 1016 data[pos] = bcolor.b; 1017 data[pos+1] = bcolor.g; 1018 data[pos+2] = bcolor.r; 1019 data[pos+3] = bcolor.a; 1020 }else 1021 static if (pixelformat == PixelFormat.BGR) 1022 { 1023 data[pos] = bcolor.b; 1024 data[pos+1] = bcolor.g; 1025 data[pos+2] = bcolor.r; 1026 }else 1027 static if (pixelformat == PixelFormat.RGBA) 1028 { 1029 data[pos] = bcolor.r; 1030 data[pos+1] = bcolor.g; 1031 data[pos+2] = bcolor.b; 1032 data[pos+3] = bcolor.a; 1033 }else 1034 static if (pixelformat == PixelFormat.RGB) 1035 { 1036 data[pos] = bcolor.r; 1037 data[pos+1] = bcolor.g; 1038 data[pos+2] = bcolor.b; 1039 } 1040 } 1041 } 1042 } 1043 } 1044 1045 import tida.color : PixelFormat; 1046 1047 version(Posix) 1048 class Canvas : ICanvas 1049 { 1050 import x11.X, x11.Xlib, x11.Xutil; 1051 import tida.window; 1052 import tida.runtime; 1053 import tida.color; 1054 import std.exception : enforce; 1055 1056 private: 1057 GC context; 1058 XImage* ximage; 1059 tida.window.Window window; 1060 1061 ubyte[] buffer; 1062 uint width; 1063 uint height; 1064 1065 uint pwidth; 1066 uint pheight; 1067 1068 int xput = 0; 1069 int yput = 0; 1070 1071 bool isAlloc = true; 1072 1073 BlendFactor[2] bfactor; 1074 BlendMode bmode; 1075 1076 @trusted: 1077 public: 1078 this(tida.window.Window window, bool isAlloc = true) 1079 { 1080 this.isAlloc = isAlloc; 1081 1082 this.window = window; 1083 if (isAlloc) 1084 { 1085 context = XCreateGC(runtime.display, this.window.handle, 0, null); 1086 enforce!Exception(context, "Software context is not a create!"); 1087 } 1088 } 1089 1090 override: 1091 void allocatePlace(uint width, uint height) 1092 { 1093 this.width = width; 1094 this.height = height; 1095 1096 buffer = new ubyte[](width * height * bytesPerColor!(PixelFormat.BGRA)); 1097 if (isAlloc) 1098 { 1099 if(ximage !is null) { 1100 XFree(ximage); 1101 ximage = null; 1102 } 1103 1104 Visual* visual = window.getVisual(); 1105 int depth = window.getDepth(); 1106 1107 ximage = XCreateImage(runtime.display, visual, depth, 1108 ZPixmap, 0, cast(char*) buffer, width, height, 32, 0); 1109 1110 enforce!Exception(ximage, "[X11] XImage is not create!"); 1111 } 1112 } 1113 1114 void viewport(uint w, uint h) 1115 { 1116 pwidth = w; 1117 pheight = h; 1118 } 1119 1120 void move(int x, int y) 1121 { 1122 xput = x; 1123 yput = y; 1124 } 1125 1126 void clearPlane(Color!ubyte color) 1127 { 1128 for (size_t i = 0; 1129 i < width * height * bytesPerColor!(PixelFormat.BGRA); 1130 i += bytesPerColor!(PixelFormat.BGRA)) 1131 { 1132 buffer[i] = color.b; 1133 buffer[i+1] = color.g; 1134 buffer[i+2] = color.r; 1135 buffer[i+3] = color.a; 1136 } 1137 } 1138 1139 void drawTo() 1140 { 1141 if (isAlloc) 1142 { 1143 XPutImage( runtime.display, window.handle, context, ximage, 1144 xput, yput, xput, yput, width, height); 1145 1146 XSync(runtime.display, false); 1147 } 1148 } 1149 1150 BlendFactor[2] blendOperation() 1151 { 1152 return bfactor; 1153 } 1154 1155 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1156 { 1157 bfactor = [sfactor, dfactor]; 1158 } 1159 1160 BlendMode blendMode() 1161 { 1162 return bmode; 1163 } 1164 1165 void blendMode(BlendMode mode) 1166 { 1167 bmode = mode; 1168 } 1169 1170 @property ref ubyte[] data() 1171 { 1172 return buffer; 1173 } 1174 1175 @property uint[2] size() 1176 { 1177 return [width, height]; 1178 } 1179 1180 @property uint[2] portSize() 1181 { 1182 return [pwidth, pheight]; 1183 } 1184 1185 @property int[2] cameraPosition() 1186 { 1187 return [xput, yput]; 1188 } 1189 1190 mixin PointToImpl!(PixelFormat.BGRA, 4); 1191 } 1192 1193 version(Windows) 1194 class Canvas : ICanvas 1195 { 1196 import core.sys.windows.windows; 1197 import tida.color; 1198 import std.exception : enforce; 1199 1200 private: 1201 PAINTSTRUCT paintstr; 1202 HDC hdc; 1203 HDC pdc; 1204 HBITMAP bitmap; 1205 1206 tida.window.Window window; 1207 1208 ubyte[] buffer; 1209 uint _width; 1210 uint _height; 1211 uint _pwidth; 1212 uint _pheight; 1213 int xput; 1214 int yput; 1215 1216 Color!ubyte _background; 1217 BlendMode bmode; 1218 BlendFactor sfactor; 1219 BlendFactor dfactor; 1220 1221 bool _isAlloc = true; 1222 1223 public @trusted: 1224 this(tida.window.Window window, bool isAlloc = true) 1225 { 1226 this.window = window; 1227 _isAlloc = isAlloc; 1228 } 1229 1230 void recreateBitmap() 1231 { 1232 if (bitmap !is null) 1233 DeleteObject(bitmap); 1234 1235 if (hdc is null) 1236 hdc = GetDC((cast(Window) window).handle); 1237 1238 bitmap = CreateBitmap(_width, _height, 1, 32, cast(LPBYTE) buffer); 1239 enforce(bitmap, "[WINAPI] bitmap is not a create!"); 1240 1241 if (pdc is null) 1242 pdc = CreateCompatibleDC(hdc); 1243 1244 SelectObject(pdc, bitmap); 1245 } 1246 1247 override: 1248 void allocatePlace(uint width, uint height) 1249 { 1250 _width = width; 1251 _height = height; 1252 1253 buffer = new ubyte[](_width * _height * 4); 1254 } 1255 1256 void viewport(uint width, uint height) 1257 { 1258 _pwidth = width; 1259 _pheight = height; 1260 } 1261 1262 void move(int x, int y) 1263 { 1264 xput = x; 1265 yput = y; 1266 } 1267 1268 void clearPlane(Color!ubyte color) 1269 { 1270 for (size_t i = 0; i < _width * _height * 4; i += 4) 1271 { 1272 buffer[i] = color.b; 1273 buffer[i + 1] = color.g; 1274 buffer[i + 2] = color.r; 1275 buffer[i + 3] = color.Max; 1276 } 1277 } 1278 1279 void drawTo() 1280 { 1281 if (_isAlloc) 1282 { 1283 recreateBitmap(); 1284 BitBlt(hdc, 0, 0, _width, _height, pdc, 0, 0, SRCCOPY); 1285 } 1286 } 1287 1288 BlendMode blendMode() 1289 { 1290 return bmode; 1291 } 1292 1293 void blendMode(BlendMode mode) 1294 { 1295 bmode = mode; 1296 } 1297 1298 BlendFactor[2] blendOperation() 1299 { 1300 return [sfactor, dfactor]; 1301 } 1302 1303 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1304 { 1305 this.sfactor = sfactor; 1306 this.dfactor = dfactor; 1307 } 1308 1309 @property ref ubyte[] data() 1310 { 1311 return buffer; 1312 } 1313 1314 @property uint[2] size() 1315 { 1316 return [_width, _height]; 1317 } 1318 1319 @property uint[2] portSize() 1320 { 1321 return [_pwidth, _pheight]; 1322 } 1323 1324 @property int[2] cameraPosition() 1325 { 1326 return [xput, yput]; 1327 } 1328 1329 mixin PointToImpl!(PixelFormat.BGRA, 4); 1330 } 1331 1332 class Software : IRenderer 1333 { 1334 import tida.window; 1335 import tida.color; 1336 import tida.vector; 1337 import tida.shape; 1338 1339 private: 1340 ICanvas canvas; 1341 Camera _camera; 1342 Color!ubyte _background; 1343 1344 public @safe: 1345 this(IWindow window, bool isAlloc = true) 1346 { 1347 _camera = new Camera(); 1348 canvas = new Canvas(cast(Window) window); 1349 1350 _camera.port = Shape!float.Rectangle(vecf(0, 0), vecf(window.width, window.height)); 1351 _camera.shape = _camera.port; 1352 1353 canvas.blendMode(BlendMode.withBlend); 1354 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 1355 1356 reshape(); 1357 } 1358 1359 this(ICanvas canvas, bool isAlloc = true) 1360 { 1361 _camera = new Camera(); 1362 this.canvas = canvas; 1363 1364 canvas.blendMode(BlendMode.withBlend); 1365 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 1366 } 1367 1368 override: 1369 @property RenderType type() 1370 { 1371 return RenderType.software; 1372 } 1373 1374 void reshape() 1375 { 1376 import std.conv : to; 1377 1378 canvas.allocatePlace(_camera.shape.end.x.to!int,_camera.shape.end.y.to!int); 1379 canvas.viewport(_camera.port.end.x.to!int,_camera.port.end.y.to!int); 1380 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 1381 } 1382 1383 @property Camera camera() 1384 { 1385 return _camera; 1386 } 1387 1388 @property void camera(Camera cam) 1389 { 1390 _camera = cam; 1391 } 1392 1393 @property Color!ubyte background() 1394 { 1395 return _background; 1396 } 1397 1398 @property void background(Color!ubyte color) 1399 { 1400 _background = color; 1401 } 1402 1403 void point(Vecf position, Color!ubyte color) 1404 { 1405 canvas.pointTo(position, color); 1406 } 1407 1408 void line(Vecf[2] points, Color!ubyte color) 1409 { 1410 import tida.each : Line; 1411 1412 foreach (x, y; Line(points[0], points[1])) 1413 canvas.pointTo(vecf(x, y), color); 1414 } 1415 1416 void rectangle( Vecf position, 1417 uint width, 1418 uint height, 1419 Color!ubyte color, 1420 bool isFill) 1421 { 1422 import tida.each : Coord; 1423 import std.conv : to; 1424 1425 if (isFill) 1426 { 1427 foreach (ix, iy; Coord(width.to!int, height.to!int)) 1428 { 1429 point(vecf(position.x.to!int + ix, position.y.to!int + iy), color); 1430 } 1431 }else 1432 { 1433 foreach (ix, iy; Coord(width.to!int, height.to!int)) 1434 { 1435 point(vecf(position.x.to!int + ix,position.y.to!int), color); 1436 point(vecf(position.x.to!int + ix,position.y.to!int + height.to!int), color); 1437 1438 point(vecf(position.x.to!int,position.y.to!int + iy), color); 1439 point(vecf(position.x.to!int + width.to!int,position.y.to!int + iy), color); 1440 } 1441 } 1442 } 1443 1444 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) @safe 1445 { 1446 import std.math : cos, sin; 1447 1448 immutable size = vecf(width, height); 1449 immutable iter = 0.25; 1450 1451 position += camera.port.begin; 1452 1453 if (isFill) 1454 { 1455 rectangle(position + vecf(radius, 0), cast(int) (width - radius * 2), height, color, true); 1456 rectangle(position + vecf(0, radius), width, cast(int) (height - radius * 2), color, true); 1457 1458 void rounded(Vecf pos, float a, float b, float iter) @safe 1459 { 1460 import tida.angle; 1461 1462 for (float i = a; i <= b;) 1463 { 1464 Vecf temp; 1465 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 1466 1467 line([pos, temp], color); 1468 i += iter; 1469 } 1470 } 1471 1472 rounded(position + vecf(radius, radius), 180, 270, iter); 1473 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 1474 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 1475 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 1476 }else 1477 { 1478 void rounded(Vecf pos, float a, float b,float iter) @safe 1479 { 1480 import tida.angle; 1481 1482 for (float i = a; i <= b;) 1483 { 1484 Vecf temp; 1485 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 1486 point(temp, color); 1487 i += iter; 1488 } 1489 } 1490 1491 rounded(position + vecf(radius, radius), 180, 270, iter); 1492 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 1493 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 1494 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 1495 1496 line([position + vecf(radius, 0), position + vecf(width - radius, 0)], color); 1497 line([position + vecf(width, radius), position + vecf(width, height - radius)], color); 1498 line([position + vecf(radius, height), position + vecf(width - radius, height)], color); 1499 line([position + vecf(0, radius), position + vecf(0, height - radius)], color); 1500 } 1501 } 1502 1503 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 1504 { 1505 import tida.image, tida.each; 1506 1507 int rad = cast(int) radius; 1508 Image buffer = new Image(rad * 2, rad * 2); 1509 buffer.fill(rgba(255,255,255,0)); 1510 1511 int x = 0; 1512 int y = rad; 1513 1514 int X1 = rad; 1515 int Y1 = rad; 1516 1517 int delta = 1 - 2 * cast(int) radius; 1518 int error = 0; 1519 1520 void bufferLine(Vecf[2] points, Color!ubyte color) @safe 1521 { 1522 foreach (ix, iy; Line(points[0], points[1])) 1523 { 1524 buffer.setPixel(ix, iy, color); 1525 } 1526 } 1527 1528 while (y >= 0) 1529 { 1530 if (isFill) 1531 { 1532 bufferLine([Vecf(X1 + x, Y1 + y),Vecf(X1 + x, Y1 - y)],color); 1533 bufferLine([Vecf(X1 - x, Y1 + y),Vecf(X1 - x, Y1 - y)],color); 1534 }else 1535 { 1536 buffer.setPixel(X1 + x, Y1 + y,color); 1537 buffer.setPixel(X1 + x, Y1 - y,color); 1538 buffer.setPixel(X1 - x, Y1 + y,color); 1539 buffer.setPixel(X1 - x, Y1 - y,color); 1540 } 1541 1542 error = 2 * (delta + y) - 1; 1543 if ((delta < 0) && (error <= 0)) 1544 { 1545 delta += 2 * ++x + 1; 1546 continue; 1547 } 1548 1549 if ((delta > 0) && (error > 0)) 1550 { 1551 delta -= 2 * --y + 1; 1552 continue; 1553 } 1554 delta += 2 * (++x - --y); 1555 } 1556 1557 foreach (ix, iy; Coord(buffer.width, buffer.height)) 1558 { 1559 Color!ubyte pixel; 1560 1561 if ((pixel = buffer.getPixel(ix,iy)).a != 0) 1562 { 1563 point(position + vecf(ix, iy), pixel); 1564 } 1565 } 1566 } 1567 1568 void triangle(Vecf[3] position,Color!ubyte color,bool isFill) @trusted 1569 { 1570 import tida.each; 1571 1572 if (isFill) 1573 { 1574 foreach (x, y; Line(position[0], position[1])) { 1575 auto p = vecf(x,y); 1576 1577 line([p, position[2]], color); 1578 } 1579 } else 1580 { 1581 line([position[0], position[1]], color); 1582 line([position[1], position[2]], color); 1583 line([position[2], position[0]], color); 1584 } 1585 } 1586 1587 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 1588 { 1589 import std.algorithm : each; 1590 points = points.dup; 1591 points.each!((ref e) => e = e + position); 1592 1593 if (!isFill) 1594 { 1595 int next = 0; 1596 for (int i = 0; i < points.length; i++) 1597 { 1598 next = (i + 1 == points.length) ? 0 : i + 1; 1599 line([points[i],points[next]], color); 1600 } 1601 }else 1602 { 1603 import std.algorithm : minElement, maxElement; 1604 import tida.collision : placeLineLineImpl; 1605 1606 float maxX = points.maxElement!"a.x".x; 1607 float minY = points.minElement!"a.y".y; 1608 float maxY = points.maxElement!"a.y".y; 1609 float minX = points.minElement!"a.x".x; 1610 1611 alias LineIter = Vecf[2]; 1612 1613 LineIter[] drowning; 1614 1615 for (float i = minY; i <= maxY; i += 1.0f) 1616 { 1617 Vecf firstPoint = vecfNaN; 1618 float lastX = minX > position.x ? position.x : minX; 1619 1620 for (float j = lastX; j <= maxX; j += 1.0f) 1621 { 1622 size_t next = 0; 1623 for (size_t currPointI = 0; currPointI < points.length; currPointI++) 1624 { 1625 next = (currPointI + 1 == points.length) ? 0 : currPointI + 1; 1626 1627 auto iter = placeLineLineImpl( [vecf(lastX, i), vecf(j, i)], 1628 [points[currPointI], points[next]]); 1629 if (!iter.isVecfNaN) { 1630 if (firstPoint.isVecfNaN) 1631 { 1632 firstPoint = vecf(j, i); 1633 } else 1634 { 1635 drowning ~= [firstPoint, vecf(j, i)]; 1636 firstPoint = vecfNaN; 1637 } 1638 1639 lastX = j; 1640 } 1641 } 1642 } 1643 } 1644 1645 foreach (e; drowning) 1646 { 1647 line(e, color); 1648 } 1649 } 1650 } 1651 1652 void clear() 1653 { 1654 import std.conv : to; 1655 1656 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 1657 canvas.clearPlane(_background); 1658 } 1659 1660 void drawning() 1661 { 1662 canvas.drawTo(); 1663 } 1664 1665 @property void blendMode(BlendMode mode) 1666 { 1667 canvas.blendMode(mode); 1668 } 1669 1670 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1671 { 1672 canvas.blendOperation(sfactor, dfactor); 1673 } 1674 1675 import tida.shader; 1676 1677 override Shader!Program getShader(string name) @safe 1678 { 1679 assert(null, "There are no shaders in this version of the render."); 1680 } 1681 1682 override void setShader(string name,Shader!Program program) @safe 1683 { 1684 assert(null, "There are no shaders in this version of the render."); 1685 } 1686 1687 override void currentShader(Shader!Program program) @safe @property 1688 { 1689 assert(null, "There are no shaders in this version of the render."); 1690 } 1691 1692 override Shader!Program currentShader() @safe @property 1693 { 1694 assert(null, "There are no shaders in this version of the render."); 1695 } 1696 1697 override void resetShader() @safe 1698 { 1699 assert(null, "There are no shaders in this version of the render."); 1700 } 1701 1702 override void currentModelMatrix(float[4][4] matrix) @safe @property 1703 { 1704 assert(null, "There are no matrix in this version of the render."); 1705 } 1706 1707 override float[4][4] currentModelMatrix() @safe @property 1708 { 1709 assert(null, "There are not matrix in this version of the render."); 1710 } 1711 } 1712 1713 import tida.window; 1714 1715 /++ 1716 Creates a render based on hardware acceleration capabilities. 1717 It should be used if the program does not use intentional hardware 1718 acceleration objects. 1719 1720 Params: 1721 window = Window object. 1722 +/ 1723 IRenderer createRenderer(IWindow window) @trusted 1724 { 1725 import bindbc.opengl; 1726 1727 if (isOpenGLLoaded()) 1728 { 1729 GLSupport ver = loadedOpenGLVersion(); 1730 if (ver != GLSupport.gl11 && ver != GLSupport.gl12 && 1731 ver != GLSupport.gl13 && ver != GLSupport.gl14 && 1732 ver != GLSupport.gl15) 1733 { 1734 return new GLRender(window); 1735 } 1736 } 1737 1738 return new Software(window, true); 1739 }