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 }