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 }