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_Border) 471 version (Windows) 472 { 473 import core.sys.windows.windows; 474 475 if (window.border) 476 { 477 RECT crect, wrect; 478 GetClientRect((cast(Window) window).handle, &crect); 479 GetWindowRect((cast(Window) window).handle, &wrect); 480 481 482 yborder = -((wrect.bottom - wrect.top) - (crect.bottom - crect.top)); 483 } 484 } 485 486 glViewport(0, yborder, _camera.shape.end.x.to!int, _camera.shape.end.y.to!int); 487 this._projection = ortho(0.0, _camera.port.end.x, _camera.port.end.y, 0.0, -1.0, 1.0); 488 } 489 490 @property void camera(Camera camera) 491 { 492 _camera = camera; 493 } 494 495 @property Camera camera() 496 { 497 return _camera; 498 } 499 500 void point(Vecf vec, Color!ubyte color) 501 { 502 if (currentShader is null) 503 currentShader = getShader("Default"); 504 505 Shapef shape = Shapef.Point(vec); 506 VertexInfo!float vinfo = generateVertex!(float)(shape); 507 508 vinfo.bindBuffer(); 509 vinfo.bindVertexArray(); 510 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 511 512 currentShader.using(); 513 currentShader.enableVertex("position"); 514 515 setDefaultUniform(color); 516 517 vinfo.draw(vinfo.shapeinfo.type); 518 519 currentShader.disableVertex("position"); 520 vinfo.unbindBuffer(); 521 vinfo.unbindVertexArray(); 522 vinfo.deleting(); 523 524 resetShader(); 525 resetModelMatrix(); 526 } 527 528 void line(Vecf[2] points, Color!ubyte color) 529 { 530 if (currentShader is null) 531 currentShader = getShader("Default"); 532 533 auto shape = Shapef.Line(points[0], points[1]); 534 VertexInfo!float vinfo = generateVertex!(float)(shape); 535 536 vinfo.bindBuffer(); 537 vinfo.bindVertexArray(); 538 539 currentShader.enableVertex("position"); 540 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 541 542 vinfo.unbindBuffer(); 543 544 currentShader.using(); 545 setDefaultUniform(color); 546 vinfo.draw(vinfo.shapeinfo.type); 547 548 currentShader.disableVertex("position"); 549 vinfo.unbindVertexArray(); 550 vinfo.deleting(); 551 552 resetShader(); 553 resetModelMatrix(); 554 } 555 556 void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill) 557 { 558 if (currentShader is null) 559 currentShader = getShader("Default"); 560 561 Shapef shape; 562 563 if (isFill) 564 { 565 shape = Shapef.Rectangle(position, position + vecf(width, height)); 566 } else 567 { 568 shape = Shapef.RectangleLine(position, position + vecf(width, height)); 569 } 570 571 VertexInfo!float vinfo = generateVertex!(float)(shape); 572 573 vinfo.bindVertexArray(); 574 vinfo.bindBuffer(); 575 if (isFill) vinfo.bindElementBuffer(); 576 577 currentShader.enableVertex("position"); 578 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position"), 2); 579 580 vinfo.unbindBuffer(); 581 582 currentShader.using(); 583 setDefaultUniform(color); 584 585 if (isFill) 586 vinfo.draw(vinfo.shapeinfo.type, 1); 587 else 588 vinfo.draw(ShapeType.line, 4); 589 590 currentShader.disableVertex("position"); 591 vinfo.unbindVertexArray(); 592 if (isFill) vinfo.unbindElementBuffer(); 593 vinfo.deleting(); 594 595 resetShader(); 596 resetModelMatrix(); 597 } 598 599 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 600 { 601 if (currentShader is null) 602 currentShader = getShader("Default"); 603 604 Shapef shape; 605 606 if (isFill) 607 { 608 shape = Shapef.Circle(position, radius); 609 } else 610 { 611 shape = Shapef.CircleLine(position, radius); 612 } 613 614 VertexInfo!float vinfo = generateVertex!(float)(shape); 615 616 vinfo.bindVertexArray(); 617 vinfo.bindBuffer(); 618 619 currentShader.enableVertex("position"); 620 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 621 622 vinfo.unbindBuffer(); 623 624 currentShader.using(); 625 setDefaultUniform(color); 626 627 if (isFill) 628 vinfo.draw(vinfo.shapeinfo.type, 1); 629 else 630 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 631 632 currentShader.disableVertex("position"); 633 vinfo.unbindVertexArray(); 634 vinfo.deleting(); 635 636 resetShader(); 637 resetModelMatrix(); 638 } 639 640 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) 641 { 642 if (currentShader is null) 643 currentShader = getShader("Default"); 644 645 Shapef shape; 646 647 if (isFill) 648 { 649 shape = Shapef.RoundRectangle(position, position + vecf(width, height), radius); 650 } else 651 { 652 shape = Shapef.RoundRectangleLine(position, position + vecf(width, height), radius); 653 } 654 655 VertexInfo!float vinfo = generateVertex!(float)(shape); 656 657 vinfo.bindVertexArray(); 658 vinfo.bindBuffer(); 659 660 currentShader.enableVertex("position"); 661 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 662 663 vinfo.unbindBuffer(); 664 665 currentShader.using(); 666 setDefaultUniform(color); 667 668 if (isFill) 669 vinfo.draw(vinfo.shapeinfo.type, 1); 670 else 671 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 672 673 currentShader.disableVertex("position"); 674 vinfo.unbindVertexArray(); 675 vinfo.deleting(); 676 677 resetShader(); 678 resetModelMatrix(); 679 } 680 681 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) 682 { 683 if (currentShader is null) 684 currentShader = getShader("Default"); 685 686 Shapef shape; 687 688 if (isFill) 689 { 690 shape = Shapef.Triangle(points); 691 } else 692 { 693 shape = Shapef.TriangleLine(points); 694 } 695 696 VertexInfo!float vinfo = generateVertex!(float)(shape); 697 698 vinfo.bindVertexArray(); 699 vinfo.bindBuffer(); 700 701 currentShader.enableVertex("position"); 702 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 703 704 vinfo.unbindBuffer(); 705 706 currentShader.using(); 707 setDefaultUniform(color); 708 709 if (isFill) 710 vinfo.draw(vinfo.shapeinfo.type, 1); 711 else 712 vinfo.draw(ShapeType.line, cast(int) vinfo.shapeinfo.shapes.length); 713 714 currentShader.disableVertex("position"); 715 vinfo.unbindVertexArray(); 716 vinfo.deleting(); 717 718 resetShader(); 719 resetModelMatrix(); 720 } 721 722 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 723 { 724 import std.algorithm : each; 725 726 if (currentShader is null) 727 currentShader = getShader("Default"); 728 729 Shapef shape; 730 points.each!((ref e) => e = position + e); 731 732 if (isFill) 733 { 734 shape = Shapef.Polygon(points); 735 } else 736 { 737 shape = Shapef.Polygon(points ~ points[0]); 738 } 739 740 VertexInfo!float vinfo = generateVertex!(float)(shape); 741 742 vinfo.bindVertexArray(); 743 vinfo.bindBuffer(); 744 745 currentShader.enableVertex("position"); 746 vinfo.vertexAttribPointer(currentShader.getAttribLocation("position")); 747 748 vinfo.unbindBuffer(); 749 750 currentShader.using(); 751 setDefaultUniform(color); 752 753 if (isFill) 754 vinfo.draw(vinfo.shapeinfo.type, 1); 755 else 756 glDrawArrays(GL_LINE_LOOP, 0, 2 * cast(int) vinfo.shapeinfo.data.length); 757 758 currentShader.disableVertex("position"); 759 vinfo.unbindVertexArray(); 760 vinfo.deleting(); 761 762 resetShader(); 763 resetModelMatrix(); 764 } 765 766 @property RenderType type() 767 { 768 return RenderType.opengl; 769 } 770 771 @property void background(Color!ubyte color) 772 { 773 _background = color; 774 glClearColor(color.rf, color.gf, color.bf, color.af); 775 } 776 777 @property Color!ubyte background() 778 { 779 return _background; 780 } 781 782 void clear() 783 { 784 glClear(GL_COLOR_BUFFER_BIT); 785 } 786 787 void drawning() 788 { 789 window.swapBuffers(); 790 } 791 792 void blendMode(BlendMode mode) 793 { 794 if (mode == BlendMode.withBlend) 795 { 796 glEnable(GL_BLEND); 797 } else 798 if (mode == BlendMode.withoutBlend) 799 { 800 glDisable(GL_BLEND); 801 } 802 } 803 804 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 805 { 806 glBlendFunc(glBlendFactor(sfactor), glBlendFactor(dfactor)); 807 } 808 809 void currentShader(Shader!Program program) 810 { 811 current = program; 812 } 813 814 Shader!Program currentShader() 815 { 816 return current; 817 } 818 819 void currentModelMatrix(float[4][4] matrix) 820 { 821 _model = matrix; 822 } 823 824 float[4][4] currentModelMatrix() 825 { 826 return _model; 827 } 828 829 void setShader(string name, Shader!Program program) 830 { 831 shaders[name] = program; 832 } 833 834 Shader!Program getShader(string name) 835 { 836 if (name in shaders) 837 return shaders[name]; 838 else 839 return null; 840 } 841 842 void resetShader() 843 { 844 current = null; 845 } 846 } 847 848 /++ 849 Implementation of the interface for interacting with the rendering canvas. 850 +/ 851 interface ICanvas 852 { 853 import tida.color; 854 import tida.vector; 855 856 @safe: 857 /++ 858 Allocate memory for the canvas at the specified size. 859 860 Params: 861 width = Canvas width. 862 height = Canvas height. 863 +/ 864 void allocatePlace(uint width, uint height); 865 866 /++ 867 Cleared the canvas with one color. 868 869 Params: 870 color = Cleared color. 871 +/ 872 void clearPlane(Color!ubyte color); 873 874 /++ 875 Draws a buffer to a storage object. 876 +/ 877 void drawTo(); 878 879 /// Blending mode (blend or not). 880 @property void blendMode(BlendMode mode); 881 882 /// ditto 883 @property BlendMode blendMode(); 884 885 /++ 886 Sets the color mixing factor (which formula to mix colors with). 887 +/ 888 void blendOperation(BlendFactor sfactor, BlendFactor dfactor); 889 890 /++ 891 Mixing factor (sfactor, dfactor). 892 +/ 893 BlendFactor[2] blendOperation(); 894 895 /++ 896 Draw a point on the canvas. 897 Draw only a point, the rest of the shapes are rendered. 898 899 Params: 900 position = Point position. 901 color = Point color. 902 +/ 903 void pointTo(Vecf position, Color!ubyte color); 904 /++ 905 Set port of visibility. 906 907 Params: 908 w = Port width. 909 h = Port height. 910 +/ 911 void viewport(uint w, uint h); 912 913 /++ 914 Move the visibility port to the specified coordinates. 915 916 Params: 917 x = Port x-axis position. 918 y = Port y-axis position. 919 +/ 920 void move(int x,int y); 921 922 /++ 923 Canvas data. 924 +/ 925 @property ref ubyte[] data(); 926 927 /// Canvas size. 928 @property uint[2] size(); 929 930 /++ 931 The real size of the world, where from the world it will be drawn to 932 the size of the canvas. 933 +/ 934 @property uint[2] portSize(); 935 936 /++ 937 Camera position (offset of all drawing points). 938 +/ 939 @property int[2] cameraPosition(); 940 } 941 942 template PointToImpl(int pixelformat, int bpc) 943 { 944 import tida.vector; 945 import tida.color; 946 947 static assert(isValidFormat!pixelformat); 948 949 static if(bpc == 0) 950 enum bytesperpixel = bytesPerColor!pixelformat; 951 else 952 enum bytesperpixel = bpc; 953 954 override void pointTo(Vecf position, Color!ubyte color) 955 { 956 import tida.each : Coord; 957 import std.conv : to; 958 959 position = position - vecf(cameraPosition); 960 961 immutable scaleWidth = (cast(float) portSize[0]) / (cast(float) size[0]); 962 immutable scaleHeight = (cast(float) portSize[1]) / (cast(float) size[1]); 963 int w = size[0] / portSize[0] + 1; 964 int h = size[0] / portSize[1] + 1; 965 966 position = position / vecf(scaleWidth, scaleHeight); 967 968 Color!ubyte bcolor; 969 970 foreach (ix, iy; Coord( position.x.to!int + w, position.y.to!int + h, 971 position.x.to!int, position.y.to!int)) 972 { 973 if (ix >= size[0] || iy >= size[1] || ix < 0 || iy < 0) continue; 974 immutable pos = ((iy * size[0]) + ix) * bytesperpixel; 975 976 if (blendMode == BlendMode.withBlend) { 977 Color!ubyte blendcolor; 978 979 static if (pixelformat == PixelFormat.BGRA) 980 { 981 blendcolor = rgba( data[pos+3], 982 data[pos+2], 983 data[pos+1], 984 data[pos]); 985 }else 986 static if (pixelformat == PixelFormat.BGR) 987 { 988 blendcolor = rgba( data[pos+2], 989 data[pos+1], 990 data[pos], 991 255); 992 }else 993 static if (pixelformat == PixelFormat.RGBA) 994 { 995 blendcolor = rgba( data[pos], 996 data[pos+1], 997 data[pos+2], 998 data[pos+3]); 999 }else 1000 static if (pixelformat == PixelFormat.RGB) 1001 { 1002 blendcolor = rgba( data[pos], 1003 data[pos+1], 1004 data[pos+2], 1005 255); 1006 } 1007 1008 BlendFactor[2] factors = blendOperation(); 1009 bcolor = BlendFunc!ubyte(factors[0], factors[1])(color, blendcolor); 1010 }else 1011 bcolor = color; 1012 1013 if (pos < data.length) 1014 { 1015 static if (pixelformat == PixelFormat.BGRA) 1016 { 1017 data[pos] = bcolor.b; 1018 data[pos+1] = bcolor.g; 1019 data[pos+2] = bcolor.r; 1020 data[pos+3] = bcolor.a; 1021 }else 1022 static if (pixelformat == PixelFormat.BGR) 1023 { 1024 data[pos] = bcolor.b; 1025 data[pos+1] = bcolor.g; 1026 data[pos+2] = bcolor.r; 1027 }else 1028 static if (pixelformat == PixelFormat.RGBA) 1029 { 1030 data[pos] = bcolor.r; 1031 data[pos+1] = bcolor.g; 1032 data[pos+2] = bcolor.b; 1033 data[pos+3] = bcolor.a; 1034 }else 1035 static if (pixelformat == PixelFormat.RGB) 1036 { 1037 data[pos] = bcolor.r; 1038 data[pos+1] = bcolor.g; 1039 data[pos+2] = bcolor.b; 1040 } 1041 } 1042 } 1043 } 1044 } 1045 1046 import tida.color : PixelFormat; 1047 1048 version(Posix) 1049 class Canvas : ICanvas 1050 { 1051 import x11.X, x11.Xlib, x11.Xutil; 1052 import tida.window; 1053 import tida.runtime; 1054 import tida.color; 1055 import std.exception : enforce; 1056 1057 private: 1058 GC context; 1059 XImage* ximage; 1060 tida.window.Window window; 1061 1062 ubyte[] buffer; 1063 uint width; 1064 uint height; 1065 1066 uint pwidth; 1067 uint pheight; 1068 1069 int xput = 0; 1070 int yput = 0; 1071 1072 bool isAlloc = true; 1073 1074 BlendFactor[2] bfactor; 1075 BlendMode bmode; 1076 1077 @trusted: 1078 public: 1079 this(tida.window.Window window, bool isAlloc = true) 1080 { 1081 this.isAlloc = isAlloc; 1082 1083 this.window = window; 1084 if (isAlloc) 1085 { 1086 context = XCreateGC(runtime.display, this.window.handle, 0, null); 1087 enforce!Exception(context, "Software context is not a create!"); 1088 } 1089 } 1090 1091 override: 1092 void allocatePlace(uint width, uint height) 1093 { 1094 this.width = width; 1095 this.height = height; 1096 1097 buffer = new ubyte[](width * height * bytesPerColor!(PixelFormat.BGRA)); 1098 if (isAlloc) 1099 { 1100 if(ximage !is null) { 1101 XFree(ximage); 1102 ximage = null; 1103 } 1104 1105 Visual* visual = window.getVisual(); 1106 int depth = window.getDepth(); 1107 1108 ximage = XCreateImage(runtime.display, visual, depth, 1109 ZPixmap, 0, cast(char*) buffer, width, height, 32, 0); 1110 1111 enforce!Exception(ximage, "[X11] XImage is not create!"); 1112 } 1113 } 1114 1115 void viewport(uint w, uint h) 1116 { 1117 pwidth = w; 1118 pheight = h; 1119 } 1120 1121 void move(int x, int y) 1122 { 1123 xput = x; 1124 yput = y; 1125 } 1126 1127 void clearPlane(Color!ubyte color) 1128 { 1129 for (size_t i = 0; 1130 i < width * height * bytesPerColor!(PixelFormat.BGRA); 1131 i += bytesPerColor!(PixelFormat.BGRA)) 1132 { 1133 buffer[i] = color.b; 1134 buffer[i+1] = color.g; 1135 buffer[i+2] = color.r; 1136 buffer[i+3] = color.a; 1137 } 1138 } 1139 1140 void drawTo() 1141 { 1142 if (isAlloc) 1143 { 1144 XPutImage( runtime.display, window.handle, context, ximage, 1145 xput, yput, xput, yput, width, height); 1146 1147 XSync(runtime.display, false); 1148 } 1149 } 1150 1151 BlendFactor[2] blendOperation() 1152 { 1153 return bfactor; 1154 } 1155 1156 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1157 { 1158 bfactor = [sfactor, dfactor]; 1159 } 1160 1161 BlendMode blendMode() 1162 { 1163 return bmode; 1164 } 1165 1166 void blendMode(BlendMode mode) 1167 { 1168 bmode = mode; 1169 } 1170 1171 @property ref ubyte[] data() 1172 { 1173 return buffer; 1174 } 1175 1176 @property uint[2] size() 1177 { 1178 return [width, height]; 1179 } 1180 1181 @property uint[2] portSize() 1182 { 1183 return [pwidth, pheight]; 1184 } 1185 1186 @property int[2] cameraPosition() 1187 { 1188 return [xput, yput]; 1189 } 1190 1191 mixin PointToImpl!(PixelFormat.BGRA, 4); 1192 } 1193 1194 version(Windows) 1195 class Canvas : ICanvas 1196 { 1197 import core.sys.windows.windows; 1198 import tida.color; 1199 import std.exception : enforce; 1200 1201 private: 1202 PAINTSTRUCT paintstr; 1203 HDC hdc; 1204 HDC pdc; 1205 HBITMAP bitmap; 1206 1207 tida.window.Window window; 1208 1209 ubyte[] buffer; 1210 uint _width; 1211 uint _height; 1212 uint _pwidth; 1213 uint _pheight; 1214 int xput; 1215 int yput; 1216 1217 Color!ubyte _background; 1218 BlendMode bmode; 1219 BlendFactor sfactor; 1220 BlendFactor dfactor; 1221 1222 bool _isAlloc = true; 1223 1224 public @trusted: 1225 this(tida.window.Window window, bool isAlloc = true) 1226 { 1227 this.window = window; 1228 _isAlloc = isAlloc; 1229 } 1230 1231 void recreateBitmap() 1232 { 1233 if (bitmap !is null) 1234 DeleteObject(bitmap); 1235 1236 if (hdc is null) 1237 hdc = GetDC((cast(Window) window).handle); 1238 1239 bitmap = CreateBitmap(_width, _height, 1, 32, cast(LPBYTE) buffer); 1240 enforce(bitmap, "[WINAPI] bitmap is not a create!"); 1241 1242 if (pdc is null) 1243 pdc = CreateCompatibleDC(hdc); 1244 1245 SelectObject(pdc, bitmap); 1246 } 1247 1248 override: 1249 void allocatePlace(uint width, uint height) 1250 { 1251 _width = width; 1252 _height = height; 1253 1254 buffer = new ubyte[](_width * _height * 4); 1255 } 1256 1257 void viewport(uint width, uint height) 1258 { 1259 _pwidth = width; 1260 _pheight = height; 1261 } 1262 1263 void move(int x, int y) 1264 { 1265 xput = x; 1266 yput = y; 1267 } 1268 1269 void clearPlane(Color!ubyte color) 1270 { 1271 for (size_t i = 0; i < _width * _height * 4; i += 4) 1272 { 1273 buffer[i] = color.b; 1274 buffer[i + 1] = color.g; 1275 buffer[i + 2] = color.r; 1276 buffer[i + 3] = color.Max; 1277 } 1278 } 1279 1280 void drawTo() 1281 { 1282 if (_isAlloc) 1283 { 1284 recreateBitmap(); 1285 BitBlt(hdc, 0, 0, _width, _height, pdc, 0, 0, SRCCOPY); 1286 } 1287 } 1288 1289 BlendMode blendMode() 1290 { 1291 return bmode; 1292 } 1293 1294 void blendMode(BlendMode mode) 1295 { 1296 bmode = mode; 1297 } 1298 1299 BlendFactor[2] blendOperation() 1300 { 1301 return [sfactor, dfactor]; 1302 } 1303 1304 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1305 { 1306 this.sfactor = sfactor; 1307 this.dfactor = dfactor; 1308 } 1309 1310 @property ref ubyte[] data() 1311 { 1312 return buffer; 1313 } 1314 1315 @property uint[2] size() 1316 { 1317 return [_width, _height]; 1318 } 1319 1320 @property uint[2] portSize() 1321 { 1322 return [_pwidth, _pheight]; 1323 } 1324 1325 @property int[2] cameraPosition() 1326 { 1327 return [xput, yput]; 1328 } 1329 1330 mixin PointToImpl!(PixelFormat.BGRA, 4); 1331 } 1332 1333 class Software : IRenderer 1334 { 1335 import tida.window; 1336 import tida.color; 1337 import tida.vector; 1338 import tida.shape; 1339 1340 private: 1341 ICanvas canvas; 1342 Camera _camera; 1343 Color!ubyte _background; 1344 1345 public @safe: 1346 this(IWindow window, bool isAlloc = true) 1347 { 1348 _camera = new Camera(); 1349 canvas = new Canvas(cast(Window) window); 1350 1351 _camera.port = Shape!float.Rectangle(vecf(0, 0), vecf(window.width, window.height)); 1352 _camera.shape = _camera.port; 1353 1354 canvas.blendMode(BlendMode.withBlend); 1355 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 1356 1357 reshape(); 1358 } 1359 1360 this(ICanvas canvas, bool isAlloc = true) 1361 { 1362 _camera = new Camera(); 1363 this.canvas = canvas; 1364 1365 canvas.blendMode(BlendMode.withBlend); 1366 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 1367 } 1368 1369 override: 1370 @property RenderType type() 1371 { 1372 return RenderType.software; 1373 } 1374 1375 void reshape() 1376 { 1377 import std.conv : to; 1378 1379 canvas.allocatePlace(_camera.shape.end.x.to!int,_camera.shape.end.y.to!int); 1380 canvas.viewport(_camera.port.end.x.to!int,_camera.port.end.y.to!int); 1381 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 1382 } 1383 1384 @property Camera camera() 1385 { 1386 return _camera; 1387 } 1388 1389 @property void camera(Camera cam) 1390 { 1391 _camera = cam; 1392 } 1393 1394 @property Color!ubyte background() 1395 { 1396 return _background; 1397 } 1398 1399 @property void background(Color!ubyte color) 1400 { 1401 _background = color; 1402 } 1403 1404 void point(Vecf position, Color!ubyte color) 1405 { 1406 canvas.pointTo(position, color); 1407 } 1408 1409 void line(Vecf[2] points, Color!ubyte color) 1410 { 1411 import tida.each : Line; 1412 1413 foreach (x, y; Line(points[0], points[1])) 1414 canvas.pointTo(vecf(x, y), color); 1415 } 1416 1417 void rectangle( Vecf position, 1418 uint width, 1419 uint height, 1420 Color!ubyte color, 1421 bool isFill) 1422 { 1423 import tida.each : Coord; 1424 import std.conv : to; 1425 1426 if (isFill) 1427 { 1428 foreach (ix, iy; Coord(width.to!int, height.to!int)) 1429 { 1430 point(vecf(position.x.to!int + ix, position.y.to!int + iy), color); 1431 } 1432 }else 1433 { 1434 foreach (ix, iy; Coord(width.to!int, height.to!int)) 1435 { 1436 point(vecf(position.x.to!int + ix,position.y.to!int), color); 1437 point(vecf(position.x.to!int + ix,position.y.to!int + height.to!int), color); 1438 1439 point(vecf(position.x.to!int,position.y.to!int + iy), color); 1440 point(vecf(position.x.to!int + width.to!int,position.y.to!int + iy), color); 1441 } 1442 } 1443 } 1444 1445 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) @safe 1446 { 1447 import std.math : cos, sin; 1448 1449 immutable size = vecf(width, height); 1450 immutable iter = 0.25; 1451 1452 position += camera.port.begin; 1453 1454 if (isFill) 1455 { 1456 rectangle(position + vecf(radius, 0), cast(int) (width - radius * 2), height, color, true); 1457 rectangle(position + vecf(0, radius), width, cast(int) (height - radius * 2), color, true); 1458 1459 void rounded(Vecf pos, float a, float b, float iter) @safe 1460 { 1461 import tida.angle; 1462 1463 for (float i = a; i <= b;) 1464 { 1465 Vecf temp; 1466 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 1467 1468 line([pos, temp], color); 1469 i += iter; 1470 } 1471 } 1472 1473 rounded(position + vecf(radius, radius), 180, 270, iter); 1474 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 1475 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 1476 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 1477 }else 1478 { 1479 void rounded(Vecf pos, float a, float b,float iter) @safe 1480 { 1481 import tida.angle; 1482 1483 for (float i = a; i <= b;) 1484 { 1485 Vecf temp; 1486 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 1487 point(temp, color); 1488 i += iter; 1489 } 1490 } 1491 1492 rounded(position + vecf(radius, radius), 180, 270, iter); 1493 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 1494 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 1495 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 1496 1497 line([position + vecf(radius, 0), position + vecf(width - radius, 0)], color); 1498 line([position + vecf(width, radius), position + vecf(width, height - radius)], color); 1499 line([position + vecf(radius, height), position + vecf(width - radius, height)], color); 1500 line([position + vecf(0, radius), position + vecf(0, height - radius)], color); 1501 } 1502 } 1503 1504 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 1505 { 1506 import tida.image, tida.each; 1507 1508 int rad = cast(int) radius; 1509 Image buffer = new Image(rad * 2, rad * 2); 1510 buffer.fill(rgba(255,255,255,0)); 1511 1512 int x = 0; 1513 int y = rad; 1514 1515 int X1 = rad; 1516 int Y1 = rad; 1517 1518 int delta = 1 - 2 * cast(int) radius; 1519 int error = 0; 1520 1521 void bufferLine(Vecf[2] points, Color!ubyte color) @safe 1522 { 1523 foreach (ix, iy; Line(points[0], points[1])) 1524 { 1525 buffer.setPixel(ix, iy, color); 1526 } 1527 } 1528 1529 while (y >= 0) 1530 { 1531 if (isFill) 1532 { 1533 bufferLine([Vecf(X1 + x, Y1 + y),Vecf(X1 + x, Y1 - y)],color); 1534 bufferLine([Vecf(X1 - x, Y1 + y),Vecf(X1 - x, Y1 - y)],color); 1535 }else 1536 { 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 buffer.setPixel(X1 - x, Y1 - y,color); 1541 } 1542 1543 error = 2 * (delta + y) - 1; 1544 if ((delta < 0) && (error <= 0)) 1545 { 1546 delta += 2 * ++x + 1; 1547 continue; 1548 } 1549 1550 if ((delta > 0) && (error > 0)) 1551 { 1552 delta -= 2 * --y + 1; 1553 continue; 1554 } 1555 delta += 2 * (++x - --y); 1556 } 1557 1558 foreach (ix, iy; Coord(buffer.width, buffer.height)) 1559 { 1560 Color!ubyte pixel; 1561 1562 if ((pixel = buffer.getPixel(ix,iy)).a != 0) 1563 { 1564 point(position + vecf(ix, iy), pixel); 1565 } 1566 } 1567 } 1568 1569 void triangle(Vecf[3] position,Color!ubyte color,bool isFill) @trusted 1570 { 1571 import tida.each; 1572 1573 if (isFill) 1574 { 1575 foreach (x, y; Line(position[0], position[1])) { 1576 auto p = vecf(x,y); 1577 1578 line([p, position[2]], color); 1579 } 1580 } else 1581 { 1582 line([position[0], position[1]], color); 1583 line([position[1], position[2]], color); 1584 line([position[2], position[0]], color); 1585 } 1586 } 1587 1588 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 1589 { 1590 import std.algorithm : each; 1591 points = points.dup; 1592 points.each!((ref e) => e = e + position); 1593 1594 if (!isFill) 1595 { 1596 int next = 0; 1597 for (int i = 0; i < points.length; i++) 1598 { 1599 next = (i + 1 == points.length) ? 0 : i + 1; 1600 line([points[i],points[next]], color); 1601 } 1602 }else 1603 { 1604 import std.algorithm : minElement, maxElement; 1605 import tida.collision : placeLineLineImpl; 1606 1607 float maxX = points.maxElement!"a.x".x; 1608 float minY = points.minElement!"a.y".y; 1609 float maxY = points.maxElement!"a.y".y; 1610 float minX = points.minElement!"a.x".x; 1611 1612 alias LineIter = Vecf[2]; 1613 1614 LineIter[] drowning; 1615 1616 for (float i = minY; i <= maxY; i += 1.0f) 1617 { 1618 Vecf firstPoint = vecfNaN; 1619 float lastX = minX > position.x ? position.x : minX; 1620 1621 for (float j = lastX; j <= maxX; j += 1.0f) 1622 { 1623 size_t next = 0; 1624 for (size_t currPointI = 0; currPointI < points.length; currPointI++) 1625 { 1626 next = (currPointI + 1 == points.length) ? 0 : currPointI + 1; 1627 1628 auto iter = placeLineLineImpl( [vecf(lastX, i), vecf(j, i)], 1629 [points[currPointI], points[next]]); 1630 if (!iter.isVecfNaN) { 1631 if (firstPoint.isVecfNaN) 1632 { 1633 firstPoint = vecf(j, i); 1634 } else 1635 { 1636 drowning ~= [firstPoint, vecf(j, i)]; 1637 firstPoint = vecfNaN; 1638 } 1639 1640 lastX = j; 1641 } 1642 } 1643 } 1644 } 1645 1646 foreach (e; drowning) 1647 { 1648 line(e, color); 1649 } 1650 } 1651 } 1652 1653 void clear() 1654 { 1655 import std.conv : to; 1656 1657 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 1658 canvas.clearPlane(_background); 1659 } 1660 1661 void drawning() 1662 { 1663 canvas.drawTo(); 1664 } 1665 1666 @property void blendMode(BlendMode mode) 1667 { 1668 canvas.blendMode(mode); 1669 } 1670 1671 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1672 { 1673 canvas.blendOperation(sfactor, dfactor); 1674 } 1675 1676 import tida.shader; 1677 1678 override Shader!Program getShader(string name) @safe 1679 { 1680 assert(null, "There are no shaders in this version of the render."); 1681 } 1682 1683 override void setShader(string name,Shader!Program program) @safe 1684 { 1685 assert(null, "There are no shaders in this version of the render."); 1686 } 1687 1688 override void currentShader(Shader!Program program) @safe @property 1689 { 1690 assert(null, "There are no shaders in this version of the render."); 1691 } 1692 1693 override Shader!Program currentShader() @safe @property 1694 { 1695 assert(null, "There are no shaders in this version of the render."); 1696 } 1697 1698 override void resetShader() @safe 1699 { 1700 assert(null, "There are no shaders in this version of the render."); 1701 } 1702 1703 override void currentModelMatrix(float[4][4] matrix) @safe @property 1704 { 1705 assert(null, "There are no matrix in this version of the render."); 1706 } 1707 1708 override float[4][4] currentModelMatrix() @safe @property 1709 { 1710 assert(null, "There are not matrix in this version of the render."); 1711 } 1712 } 1713 1714 import tida.window; 1715 1716 /++ 1717 Creates a render based on hardware acceleration capabilities. 1718 It should be used if the program does not use intentional hardware 1719 acceleration objects. 1720 1721 Params: 1722 window = Window object. 1723 +/ 1724 IRenderer createRenderer(IWindow window) @trusted 1725 { 1726 import bindbc.opengl; 1727 1728 if (isOpenGLLoaded()) 1729 { 1730 GLSupport ver = loadedOpenGLVersion(); 1731 if (ver != GLSupport.gl11 && ver != GLSupport.gl12 && 1732 ver != GLSupport.gl13 && ver != GLSupport.gl14 && 1733 ver != GLSupport.gl15) 1734 { 1735 return new GLRender(window); 1736 } 1737 } 1738 1739 return new Software(window, true); 1740 } 1741 1742 import tida.image; 1743 import tida.vector; 1744 1745 Image renderRead(IRenderer render, Vecf position, int width, int height) @trusted 1746 { 1747 import tida.gl; 1748 import std.conv : to; 1749 1750 Image image = new Image(width, height); 1751 glReadPixels( position.x.to!int, position.y.to!int, 1752 width, height, GL_RGBA, GL_UNSIGNED_BYTE, cast(void*) image.pixels); 1753 1754 return image; 1755 }