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             tida.instance;
22 
23     struct CameraObject
24     {
25         Vector!float* position;
26         Vector!float size;
27     }
28 
29 private:
30     Shape!float _port;
31     Shape!float _shape;
32     CameraObject object;
33     Vector!float _trackDistance = vec!float(4.0f, 4.0f);
34     Vector!float _sizeRoom = vecNaN!float;
35     
36 
37 public @safe nothrow pure:
38     /// The allowed room size for camera scrolling.
39     @property Vector!float sizeRoom()
40     {
41         return _sizeRoom;
42     }
43     
44     /// The allowed room size for camera scrolling.
45     @property Vector!float sizeRoom(Vector!float value)
46     {
47         return _sizeRoom = value;
48     }
49     
50     /// A method to change the allowed size of a scrolling room for a camera.
51     void resizeRoom(Vector!float value)
52     {
53         _sizeRoom = value;
54     }
55 
56     /++
57     A method for binding a specific object to a camera to track it.
58     
59     Params:
60         position =  The reference to the variable for which the tracking
61                     will be performed. We need a variable that will be 
62                     alive during the camera's tracking cycle.
63         size     =  The size of the object. (Each object is represented as a rectangle.)
64     +/
65     void bindObject(    ref Vector!float position, 
66                         Vector!float size = vecNaN!float)  @trusted
67     {
68         object.position = &position;
69         object.size = size.isVectorNaN ? vec!float(1, 1) : size;
70     }
71     
72     /++
73     A method for binding a specific object to a camera to track it.
74     
75     Params:
76         position =  The reference to the variable for which the tracking
77                     will be performed. We need a variable that will be 
78                     alive during the camera's tracking cycle.
79         size     =  The size of the object. (Each object is represented as a rectangle.)
80     +/
81     void bindObject(    Vector!float* position,
82                         Vector!float size = vecNaN!float)
83     {
84         object.position = position;
85         object.size = size.isVectorNaN ? vec!float(1, 1) : size;
86     }
87     
88     /++
89     A method for binding a specific object to a camera to track it.
90     
91     Params:
92         instance =  An object in the scene that will be monitored by the camera. 
93                     The size is calculated from the object's touch mask.
94     +/
95     void bindObject(Instance instance)
96     {
97         object.position = &instance.position;
98         object.size = instance.mask.calculateSize();
99     }
100     
101     /++
102     A method that reproduces the process of tracking an object.
103     +/
104     void followObject()
105     {
106         Vector!float velocity = vecZero!float;
107     
108         if (object.position.x < port.begin.x + _trackDistance.x)
109         {
110             velocity.x = (port.begin.x + _trackDistance.x) - object.position.x;
111         } else
112         if (object.position.x + object.size.x> port.begin.x + port.end.x - _trackDistance.x)
113         {
114             velocity.x = (port.begin.x + port.end.x - _trackDistance.x) - (object.position.x + object.size.x);
115         }
116         
117         if (object.position.y < port.begin.y + _trackDistance.y)
118         {
119             velocity.y = (port.begin.y + _trackDistance.y) - object.position.y;
120         } else
121         if (object.position.y + object.size.y > port.begin.y + port.end.y - _trackDistance.y)
122         {
123             velocity.y = (port.begin.y + port.end.y - _trackDistance.y) - (object.position.y + object.size.y);
124         }
125         
126         immutable preBegin = port.begin - velocity;
127         
128         if (!_sizeRoom.isVectorNaN)
129         {
130             if (preBegin.x > 0 &&
131                 preBegin.x + port.end.x < _sizeRoom.x)
132             {
133                 port = Shapef.Rectangle(vec!float(preBegin.x, port.begin.y), port.end);
134             }
135 
136             if (preBegin.y > 0 &&
137                 preBegin.y + port.end.y < _sizeRoom.y)
138             {
139                 port = Shapef.Rectangle(vec!float(port.begin.x, preBegin.y), port.end);
140             }
141         } else
142             port = Shapef.Rectangle(preBegin, port.end);
143     }
144     
145     /// Distance between camera boundaries and subject for the scene to move the camera's view.
146     @property Vector!float trackDistance()
147     {
148         return _trackDistance;
149     }
150     
151     /// Distance between camera boundaries and subject for the scene to move the camera's view.
152     @property Vector!float trackDistance(Vector!float value)
153     {
154         return _trackDistance = value;
155     }
156 
157     /++
158     The port is the immediate visible part in the "room". The entire area in 
159     the world that must be covered in the field of view.
160     +/
161     @property Shape!float port()
162     {
163         return _port;
164     }
165 
166     /// ditto
167     @property Shape!float port(Shape!float value)
168     {
169         return _port = value;
170     }
171 
172     /++
173     The size of the visible part in the plane of the window.
174     +/
175     @property Shape!float shape()
176     {
177         return _shape;
178     }
179 
180     /// ditto
181     @property Shape!float shape(Shape!float value)
182     {
183         return _shape = value;
184     }
185 
186     /++
187     Moves the visible field.
188 
189     Params:
190         value = Factor movement.
191     +/
192     void moveView(Vecf value)
193     {
194         _port = Shape!float.Rectangle(_port.begin + value, _port.end);
195     }
196 }
197 
198 /// Renderer type
199 enum RenderType
200 {
201     unknown,
202     software,
203     opengl,
204     directx, // Not implement
205     vulkan // Not implement
206 }
207 
208 /// A property that explains whether blending should be applied or not.
209 enum BlendMode
210 {
211     withoutBlend, /// Without blending
212     withBlend /// With blending
213 }
214 
215 interface ITarget
216 {
217     void bind(IRenderer render) @safe;
218     
219     void unbind(IRenderer render) @safe;
220     
221     void drawning(IRenderer render) @safe;
222 }
223 
224 /++
225 An interface for rendering objects to a display or other storehouse of pixels.
226 +/
227 interface IRenderer
228 {
229     import  tida.color,
230             tida.vector,
231             tida.shader,
232             tida.drawable,
233             tida.matrix;
234 
235 @safe:
236     // null - default
237     void bindTarget(ITarget target) @safe;
238     
239     @property ITarget currentTarget() @safe;
240 
241     /// Updates the rendering surface if, for example, the window is resized.
242     void reshape();
243 
244     ///Camera for rendering.
245     @property void camera(Camera camera);
246 
247     /// Camera for rendering.
248     @property Camera camera();
249 
250     /++
251     Drawing a point.
252 
253     Params:
254         vec = Point position.
255         color = Point color.
256     +/
257     void point(Vecf vec, Color!ubyte color) @safe;
258 
259     /++
260     Line drawing.
261 
262     Params:
263         points = Tops of lines.
264         color = Line color.
265     +/
266     void line(Vecf[2] points, Color!ubyte color) @safe;
267 
268     /++
269     Drawing a rectangle.
270 
271     Params:
272         position = Rectangle position.
273         width = Rectangle width.
274         height = Rectangle height.
275         color = Rectangle color.
276         isFill = Whether to fill the rectangle with color.
277     +/
278     void rectangle( Vecf position, 
279                     uint width, 
280                     uint height, 
281                     Color!ubyte color, 
282                     bool isFill) @safe;
283 
284     /++
285     Drawning a circle.
286 
287     Params:
288         position = Circle position.
289         radius = Circle radius.
290         color = Circle color.
291         isFill = Whether to fill the circle with color.
292     +/
293     void circle(Vecf position, 
294                 float radius, 
295                 Color!ubyte color, 
296                 bool isFill) @safe;
297 
298     /++
299     Drawing a triangle by its three vertices.
300 
301     Params:
302         points = Triangle vertices
303         color = Triangle color.
304         isFill = Whether it is necessary to fill the triangle with color.
305     +/
306     void triangle(Vecf[3] points, Color!ubyte color, bool isFill) @safe;
307 
308     /++
309     Draws a rectangle with rounded edges.
310     (Rendering is available only through hardware acceleration).
311 
312     Params:
313         position = Position roundrectangle.
314         width = Width roundrectangle.
315         height = Height roundrectangle.
316         radius = Radius rounded edges.
317         color = Color roundrect.
318         isFill = Roundrect is filled color?
319     +/
320     void roundrect( Vecf position, 
321                     uint width, 
322                     uint height, 
323                     float radius, 
324                     Color!ubyte color, 
325                     bool isFill) @safe;
326 
327     /++
328     Drawing a polygon from an array of vertices.
329 
330     Params:
331         position = Polygon position.
332         points = Polygon vertices/
333         color = Polygon color.
334         isFill = Whether it is necessary to fill the polygon with color.
335     +/
336     void polygon(   Vecf position, 
337                     Vecf[] points, 
338                     Color!ubyte color, 
339                     bool isFill) @safe;
340 
341     /// Cleans the surface by filling it with color.
342     void clear() @safe;
343 
344     /// Outputs the buffer to the window.
345     void drawning() @safe;
346 
347     /// Gives the type of render.
348     RenderType type() @safe;
349 
350     /// Set the coloring method. Those. with or without alpha blending.
351     void blendMode(BlendMode mode) @safe;
352 
353     /// Set factor blend
354     void blendOperation(BlendFactor sfactor, BlendFactor dfactor) @safe;
355 
356     /// The color to fill when clearing.
357     void background(Color!ubyte background) @safe @property;
358 
359     /// ditto
360     Color!ubyte background() @safe @property;
361 
362     /++
363     Memorize the shader for future reference.
364 
365     Params:
366         name =  The name of the shader by which it will be possible to pick up 
367                 the shader in the future.
368         program = Shader program.
369     +/
370     void setShader(string name, Shader!Program program) @safe;
371 
372     /++
373     Pulls a shader from memory, getting it by name. Returns a null pointer 
374     if no shader is found.
375 
376     Params:
377         name = Shader name.
378     +/
379     Shader!Program getShader(string name) @safe;
380 
381     /// The current shader for the next object rendering.
382     void currentShader(Shader!Program program) @safe @property; 
383 
384     /// The current shader for the next object rendering.
385     Shader!Program currentShader() @safe @property;
386 
387     /// Reset the shader to main.
388     void resetShader() @safe;
389 
390     /// Current model matrix.
391     float[4][4] currentModelMatrix() @safe @property;
392 
393     /// ditto
394     void currentModelMatrix(float[4][4] matrix) @safe @property;
395 
396     /// Reset current model matrix.
397     final void resetModelMatrix() @safe
398     {
399         this.currentModelMatrix = identity();
400     }
401 
402     /++
403     Renders an object.
404 
405     See_Also: `tida.graph.drawable`.
406     +/
407     void draw(IDrawable drawable, Vecf position) @safe;
408 
409     /// ditto
410     void drawEx(    IDrawableEx drawable, 
411                     Vecf position, 
412                     float angle,
413                     Vecf center,
414                     Vecf size,
415                     ubyte alpha,
416                     Color!ubyte color = rgb(255, 255, 255)) @safe;
417 }
418 
419 /++
420 Render objects using hardware acceleration through an open graphics library.
421 +/
422 class GLRender : IRenderer
423 {
424     import tida.window;
425     import tida.gl;
426     import tida.shader;
427     import tida.vertgen;
428     import tida.color;
429     import tida.vector;
430     import tida.matrix;
431     import tida.shape;
432     import tida.drawable;
433 
434     enum deprecatedVertex =
435     "
436     #version 130
437     in vec3 position;
438 
439     uniform mat4 projection;
440     uniform mat4 model;
441 
442     void main()
443     {
444         gl_Position = projection * model * vec4(position, 1.0f);
445     }
446     ";
447 
448     enum deprecatedFragment =
449     "
450     #version 130
451     uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
452 
453     void main()
454     {
455         gl_FragColor = color;
456     }
457     ";
458 
459     enum modernVertex =
460     "
461     #version 330 core
462     layout (location = 0) in vec3 position;
463 
464     uniform mat4 projection;
465     uniform mat4 model;
466 
467     void main()
468     {
469         gl_Position = projection * model * vec4(position, 1.0f);
470     }
471     ";
472 
473     enum modernFragment =
474     "
475     #version 330 core
476     uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
477 
478     out vec4 fragColor;
479 
480     void main()
481     {
482         fragColor = color;
483     }
484     ";
485 
486 private:
487     IWindow window;
488     Color!ubyte _background;
489     Camera _camera;
490     mat4 _projection;
491 
492     Shader!Program[string] shaders;
493     Shader!Program current;
494 
495     mat4 _model;
496 
497     bool _isModern = false;
498     ITarget _target;
499 
500 public @trusted:
501     this(IWindow window)
502     {
503         this.window = window;
504 
505         _camera = new Camera();
506         _camera.shape = Shapef.Rectangle(vecf(0, 0), vecf(window.width, window.height));
507         _camera.port = _camera.shape;
508 
509         Shader!Program defaultShader = new Shader!Program();
510 
511         string vsource, fsource;
512 
513         if (glslVersion == "1.10" || glslVersion == "1.20" || glslVersion == "1.30")
514         {
515             vsource = deprecatedVertex;
516             fsource = deprecatedFragment;
517             _isModern = false;
518         } else
519         {
520             vsource = modernVertex;
521             fsource = modernFragment;
522             _isModern = true;
523         }
524 
525         Shader!Vertex defaultVertex = new Shader!Vertex();
526         defaultVertex.bindSource(vsource);
527 
528         Shader!Fragment defaultFragment = new Shader!Fragment();
529         defaultFragment.bindSource(fsource);
530 
531         defaultShader.attach(defaultVertex);
532         defaultShader.attach(defaultFragment);
533         defaultShader.link();
534 
535         setShader("Default", defaultShader);
536 
537         _model = identity();
538         blendMode(BlendMode.withBlend);
539         blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha);
540 
541         this.reshape();
542     }
543 
544     @property Shader!Program[string] getShaders()
545     {
546         return shaders;
547     }
548 
549     @property mat4 projection()
550     {
551         return _projection;
552     }
553 
554     int glBlendFactor(BlendFactor factor)
555     {
556         if (factor == BlendFactor.Zero)
557             return GL_ZERO;
558         else
559         if (factor == BlendFactor.One)
560             return GL_ONE;
561         else
562         if (factor == BlendFactor.SrcColor)
563             return GL_SRC_COLOR;
564         else
565         if (factor == BlendFactor.DstColor)
566             return GL_DST_COLOR;
567         else
568         if (factor == BlendFactor.OneMinusSrcColor)
569             return GL_ONE_MINUS_SRC_COLOR;
570         else
571         if (factor == BlendFactor.OneMinusDstColor)
572             return GL_ONE_MINUS_DST_COLOR;
573         else
574         if (factor == BlendFactor.SrcAlpha)
575             return GL_SRC_ALPHA;
576         else
577         if (factor == BlendFactor.DstAlpha)
578             return GL_DST_ALPHA;
579         else
580         if (factor == BlendFactor.OneMinusSrcAlpha)
581             return GL_ONE_MINUS_SRC_ALPHA;
582         else
583         if (factor == BlendFactor.OneMinusDstAlpha)
584             return GL_ONE_MINUS_DST_ALPHA;
585 
586         return 0;
587     }
588 
589     void setDefaultUniform(Color!ubyte color)
590     {
591         if (currentShader.getUniformLocation("projection") != -1)
592             currentShader.setUniform("projection", _projection);
593 
594         if (currentShader.getUniformLocation("color") != -1)
595             currentShader.setUniform("color", color);
596 
597         if (currentShader.getUniformLocation("model") != -1)
598             currentShader.setUniform("model", _model);
599     }
600 
601     @property bool isModern()
602     {
603         return _isModern;
604     }
605 
606 override:
607     void draw(IDrawable drawable, Vecf position) @safe
608     {
609         drawable.draw(this, position - camera.port.begin);
610     }
611 
612     /// ditto
613     void drawEx(IDrawableEx drawable, 
614                 Vecf position, 
615                 float angle,
616                 Vecf center,
617                 Vecf size,
618                 ubyte alpha,
619                 Color!ubyte color = rgb(255, 255, 255)) @safe
620     {
621         drawable.drawEx(this, position - camera.port.begin, angle, center, size, alpha, color);
622     }
623 
624     void reshape()
625     {
626         import std.conv : to;
627 
628         int yborder = 0;
629 
630         version (Windows)
631         {
632             if (_target is null)
633                 yborder = (cast(Window) window).windowBorderSize;
634         }
635 
636         glViewport(
637             -_camera.shape.begin.x.to!int,
638             -_camera.shape.begin.y.to!int - yborder, 
639             _camera.shape.end.x.to!int, 
640             _camera.shape.end.y.to!int
641         );
642         
643         this._projection = ortho(0.0, _camera.port.end.x, _camera.port.end.y, 0.0, -1.0, 1.0);
644     }
645 
646     @property void camera(Camera camera)
647     {
648         _camera = camera;
649     }
650 
651     @property Camera camera()
652     {
653         return _camera;
654     }
655     
656     void bindTarget(ITarget target) @safe
657     {
658         if (_target !is null)
659             _target.unbind(this);
660             
661         if (target !is null)
662             target.bind(this);
663             
664         _target = target;
665     }
666     
667     @property ITarget currentTarget() @safe
668     {
669         return _target;
670     }
671 
672     void point(Vecf vec, Color!ubyte color)
673     {
674         if (currentShader is null)
675             currentShader = getShader("Default");
676             
677         vec -= camera.port.begin;
678 
679         scope vertexInfo = new VertexInfo!(float)();
680         scope buffer = new BufferInfo!float();
681 
682         buffer.append (vec);
683 
684         buffer.bind();
685         vertexInfo.bind();
686         buffer.move();
687 
688         vertexInfo.vertexAttribPointer(
689             currentShader.getAttribLocation("position"),
690             2,
691             2,
692             0
693         );
694         buffer.unbind();
695 
696         debug (GLDebug) checkGLError();
697 
698         currentShader.using();
699         currentShader.enableVertex("position");
700 
701         setDefaultUniform(color);
702         if (currentShader.getUniformLocation("size") != -1)
703             currentShader.setUniform("size", tida.vector.vec!float(1, 1));
704 
705         vertexInfo.draw (ShapeType.point);
706 
707         currentShader.disableVertex("position");
708         vertexInfo.unbind();
709 
710         resetShader();
711         resetModelMatrix();
712     }
713 
714     void line(Vecf[2] points, Color!ubyte color)
715     {
716         if (currentShader is null)
717             currentShader = getShader("Default");
718 
719         scope vertexInfo = new VertexInfo!(float)();
720         scope buffer = new BufferInfo!float();
721 
722         buffer.append (points[0] - camera.port.begin);
723         buffer.append (points[1] - camera.port.begin);
724 
725         buffer.bind();
726         vertexInfo.bind();
727         buffer.move();
728 
729         vertexInfo.vertexAttribPointer(
730             currentShader.getAttribLocation("position"),
731             2,
732             2,
733             0
734         );
735         buffer.unbind();
736 
737         debug (GLDebug) checkGLError();
738 
739         currentShader.using();
740         currentShader.enableVertex("position");
741 
742         setDefaultUniform(color);
743         if (currentShader.getUniformLocation("size") != -1)
744             currentShader.setUniform("size", abs(points[1] - points[0]));
745 
746         vertexInfo.draw (ShapeType.line);
747 
748         currentShader.disableVertex("position");
749         vertexInfo.unbind();
750 
751         resetShader();
752         resetModelMatrix();
753     }
754 
755     void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill)
756     {
757         if (currentShader is null)
758             currentShader = getShader("Default");
759 
760         position -= camera.port.begin;
761         
762         scope vertexInfo = new VertexInfo!(float)();
763         scope buffer = new BufferInfo!float();
764         scope elements = new ElementInfo!uint();
765         vertexInfo.elements = elements;
766 
767         buffer.append (position);
768         buffer.append (position + vec!float (width, 0));
769         buffer.append (position + vec!float (width, height));
770         buffer.append (position + vec!float (0, height));
771 
772         if (isFill)
773         {
774             elements.data = [0, 1, 3, 1, 3, 2];
775         } else
776         {
777             elements.data = [0, 1, 1, 2, 2, 3, 3, 0];
778         }
779 
780         buffer.bind();
781         vertexInfo.bind();
782         elements.bind();
783 
784         elements.attach();
785         buffer.move();
786 
787         vertexInfo.vertexAttribPointer(
788             currentShader.getAttribLocation("position"),
789             2,
790             2,
791             0
792         );
793         buffer.unbind();
794 
795         debug (GLDebug) checkGLError();
796 
797         currentShader.using();
798         currentShader.enableVertex("position");
799 
800         setDefaultUniform(color);
801         if (currentShader.getUniformLocation("size") != -1)
802             currentShader.setUniform("size", vec!float(width, height));
803 
804         if (isFill)
805         {
806             vertexInfo.draw (ShapeType.rectangle);
807         } else
808         {
809             vertexInfo.draw (ShapeType.line);
810         }
811 
812         currentShader.disableVertex("position");
813         elements.unbind();
814         vertexInfo.unbind();
815 
816         resetShader();
817         resetModelMatrix();
818     }
819 
820     void circle(Vecf position, float radius, Color!ubyte color, bool isFill)
821     {
822         if (currentShader is null)
823             currentShader = getShader("Default");
824 
825         position -= camera.port.begin;
826 
827         scope vertexInfo = new VertexInfo!(float)();
828         scope buffer = new BufferInfo!float();
829         vertexInfo.buffer = buffer;
830 
831         auto shape = isFill ? 
832                 Shape!float.Circle (position, radius) :
833                 Shape!float.CircleLine (position, radius);
834 
835         buffer.vertexData = generateBuffer!float(shape);
836 
837         buffer.bind();
838         vertexInfo.bind();
839         buffer.attach();
840 
841         vertexInfo.vertexAttribPointer(
842             currentShader.getAttribLocation("position"),
843             2,
844             2,
845             0
846         );
847         buffer.unbind();
848 
849         debug (GLDebug) checkGLError();
850 
851         currentShader.using();
852         currentShader.enableVertex("position");
853 
854         setDefaultUniform(color);
855         if (currentShader.getUniformLocation("size") != -1)
856             currentShader.setUniform("size", vec!float(radius * 2, radius * 2));
857 
858         if (isFill)
859         {
860             vertexInfo.draw (ShapeType.circle);
861         } else
862         {
863             vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length);
864         }
865 
866         currentShader.disableVertex("position");
867         vertexInfo.unbind();
868 
869         resetShader();
870         resetModelMatrix();
871     }
872 
873     void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill)
874     {
875         if (currentShader is null)
876             currentShader = getShader("Default");
877 
878         position -= camera.port.begin;
879         
880         scope vertexInfo = new VertexInfo!(float)();
881         scope buffer = new BufferInfo!float();
882         vertexInfo.buffer = buffer;
883 
884         auto shape = isFill ? 
885                 Shape!float.RoundRectangle (position, position + vec!float(width, height), radius) :
886                 Shape!float.RoundRectangleLine (position, position + vec!float(width, height), radius);
887 
888         buffer.vertexData = generateBuffer!float(shape);
889 
890         buffer.bind();
891         vertexInfo.bind();
892         buffer.attach();
893 
894         vertexInfo.vertexAttribPointer(
895             currentShader.getAttribLocation("position"),
896             2,
897             2,
898             0
899         );
900         buffer.unbind();
901 
902         debug (GLDebug) checkGLError();
903 
904         currentShader.using();
905         currentShader.enableVertex("position");
906 
907         setDefaultUniform(color);
908         if (currentShader.getUniformLocation("size") != -1)
909             currentShader.setUniform("size", vec!float(width, height));
910 
911         if (isFill)
912         {
913             vertexInfo.draw (ShapeType.roundrect);
914         } else
915         {
916             vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length);
917         }
918 
919         currentShader.disableVertex("position");
920         vertexInfo.unbind();
921 
922         resetShader();
923         resetModelMatrix();
924     }
925 
926     void triangle(Vecf[3] points, Color!ubyte color, bool isFill)
927     {
928         if (currentShader is null)
929             currentShader = getShader("Default");
930 
931         scope vertexInfo = new VertexInfo!(float)();
932         scope buffer = new BufferInfo!float();
933 
934         auto shape = Shape!float();
935         if (isFill)
936             shape = Shape!float.Triangle (points);
937         else
938             shape = Shape!float.TriangleLine (points);
939 
940         buffer.vertexData = generateBuffer!float(shape);
941 
942         buffer.bind();
943         vertexInfo.bind();
944         buffer.move();
945 
946         vertexInfo.vertexAttribPointer(
947             currentShader.getAttribLocation("position"),
948             2,
949             2,
950             0
951         );
952         buffer.unbind();
953 
954         debug (GLDebug) checkGLError();
955 
956         currentShader.using();
957         currentShader.enableVertex("position");
958 
959         setDefaultUniform(color);
960         if (currentShader.getUniformLocation("size") != -1)
961             currentShader.setUniform("size", abs(points[2] - points[0]));
962 
963         if (isFill)
964             vertexInfo.draw (ShapeType.triangle);
965         else
966             vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length);
967 
968         currentShader.disableVertex("position");
969         vertexInfo.unbind();
970 
971         resetShader();
972         resetModelMatrix();
973     }
974 
975     void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill)
976     {
977         import std.algorithm : each;
978 
979         if (currentShader is null)
980             currentShader = getShader("Default");
981             
982         position -= camera.port.begin;
983 
984         scope vertexInfo = new VertexInfo!(float)();
985         scope buffer = new BufferInfo!float();
986         vertexInfo.buffer = buffer;
987 
988         auto shape = Shape!float();
989 
990         if (isFill)
991             Shape!float.Polygon (points, position);
992         else
993             Shape!float.PolygonLine (points, position);
994 
995         buffer.vertexData = generateBuffer!float(shape);
996 
997         buffer.bind();
998         vertexInfo.bind();
999         buffer.attach();
1000 
1001         vertexInfo.vertexAttribPointer(
1002             currentShader.getAttribLocation("position"),
1003             2,
1004             2,
1005             0
1006         );
1007         buffer.unbind();
1008 
1009         debug (GLDebug) checkGLError();
1010 
1011         currentShader.using();
1012         currentShader.enableVertex("position");
1013 
1014         setDefaultUniform(color);
1015         if (currentShader.getUniformLocation("size") != -1)
1016             currentShader.setUniform("size", vec!float(1, 1));
1017 
1018         if (isFill)
1019             vertexInfo.draw (ShapeType.polygon);
1020         else
1021             vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length);
1022 
1023         currentShader.disableVertex("position");
1024         vertexInfo.unbind();
1025 
1026         resetShader();
1027         resetModelMatrix();
1028     }
1029 
1030     @property RenderType type()
1031     {
1032         return RenderType.opengl;
1033     }
1034 
1035     @property void background(Color!ubyte color)
1036     {
1037         _background = color;
1038         glClearColor(color.rf, color.gf, color.bf, color.af);
1039     }
1040 
1041     @property Color!ubyte background()
1042     {
1043         return _background;
1044     }
1045 
1046     void clear()
1047     {
1048         glClear(GL_COLOR_BUFFER_BIT);
1049     }
1050 
1051     void drawning()
1052     {
1053         if (_target is null)
1054             window.swapBuffers();
1055         else
1056             _target.drawning(this);
1057     }
1058 
1059     void blendMode(BlendMode mode)
1060     {
1061         if (mode == BlendMode.withBlend)
1062         {
1063             glEnable(GL_BLEND);
1064         } else
1065         if (mode == BlendMode.withoutBlend)
1066         {
1067             glDisable(GL_BLEND);
1068         }
1069     }
1070 
1071     void blendOperation(BlendFactor sfactor, BlendFactor dfactor)
1072     {
1073         glBlendFunc(glBlendFactor(sfactor), glBlendFactor(dfactor));
1074     }
1075 
1076     void currentShader(Shader!Program program)
1077     {
1078         current = program;
1079     }
1080 
1081     Shader!Program currentShader()
1082     {
1083         return current;
1084     }
1085 
1086     void currentModelMatrix(float[4][4] matrix)
1087     {
1088         _model = matrix;
1089     }
1090 
1091     float[4][4] currentModelMatrix()
1092     {
1093         return _model;
1094     }
1095 
1096     void setShader(string name, Shader!Program program)
1097     {
1098         shaders[name] = program;
1099     }
1100 
1101     Shader!Program getShader(string name)
1102     {
1103         if (name in shaders)
1104             return shaders[name];
1105         else
1106             return null;
1107     }
1108 
1109     void resetShader()
1110     {
1111         current = null;
1112     }
1113 }
1114 
1115 debug (GLDebug) class GLDebugRender : IRenderer
1116 {
1117     import tida.vector;
1118     import tida.color;
1119     import tida.drawable;
1120     import tida.shader;
1121 
1122     public struct Operation
1123     {
1124         string name;
1125         
1126         IDrawable object;
1127         IDrawableEx objectEx;
1128         
1129         Vecf size;
1130         Vecf center;
1131         float angle;
1132         ubyte alpha;
1133 
1134         Color!ubyte color;
1135         bool isFill = true;
1136 
1137         Shader!Program shader;
1138         string shaderName;
1139         float[4][4] matrix;
1140 
1141         Vecf[] vertexs;
1142         float radius;
1143 
1144         BlendFactor sfactor;
1145         BlendFactor dfactor;
1146     }
1147 
1148 private:
1149     GLRender render;
1150     size_t frame;
1151     Color!ubyte _background;
1152 
1153 public:
1154     Operation[][] operations;
1155 
1156 @safe:
1157     this(GLRender render)
1158     {
1159         this.render = render;
1160 
1161         operations ~= [[]];
1162         frame = 1;
1163     }
1164 
1165     string findShadername(Shader!Program shader)
1166     {
1167         auto shaders = render.getShaders();
1168 
1169         foreach (key, value; shaders)
1170         {
1171             if (value is shader)
1172                 return key;
1173         }
1174 
1175         return "unknown";
1176     }
1177 
1178     string textReport() @safe
1179     {
1180         import std.conv : to;
1181 
1182         string data;
1183         size_t iframe = 0;
1184 
1185         foreach (frame; operations)
1186         {
1187             iframe++;
1188             data ~= "Frame " ~ iframe.to!string ~ ":\n";
1189             foreach (operand; frame)
1190             {
1191                 data ~= operand.name ~ ":\n";
1192                 data ~= "L color: " ~ operand.color.to!string ~ "\n";
1193                 data ~= "L isFill: " ~ operand.isFill.to!string ~ "\n";
1194                 data ~= "L shader: " ~ operand.shaderName ~ "\n";
1195                 data ~= "L matrix: " ~ operand.matrix.to!string ~ "\n";
1196                 data ~= "L vertexs: " ~ operand.vertexs.to!string ~ "\n\n";
1197             }
1198         }
1199 
1200         return data;
1201     }
1202 
1203     void drawFrame(int idframe) @safe
1204     {
1205         auto frame = operations[idframe];
1206 
1207         foreach (i; 0 .. cast(int) frame.length)
1208             drawOperation(idframe, i);
1209     }
1210 
1211     void drawOperation(int idframe, int idoper) @safe
1212     {
1213         auto operation = operations[idframe][idoper];
1214         
1215         if (operation.object !is null)
1216         {
1217             render.draw(operation.object, operation.vertexs[0]);
1218         } else
1219         if (operation.objectEx !is null)
1220         {
1221             render.drawEx(operation.objectEx,   operation.vertexs[0], 
1222                                                 operation.angle, 
1223                                                 operation.center,
1224                                                 operation.size,
1225                                                 operation.alpha,
1226                                                 operation.color);
1227         } else
1228         {
1229             switch (operation.name)
1230             {
1231                 case "tida.render.GLRender.point":
1232                     render.point(operation.vertexs[0], operation.color);
1233                 break;
1234 
1235                 case "tida.render.GLRender.line":
1236                     render.line([operation.vertexs[0], operation.vertexs[1]], operation.color);
1237                 break;
1238 
1239                 case "tida.render.GLRender.rectangle": {
1240                     uint width = cast(uint) (operation.vertexs[1].x - operation.vertexs[0].x);
1241                     uint height = cast(uint) (operation.vertexs[1].y - operation.vertexs[0].y);
1242 
1243                     render.rectangle(operation.vertexs[0], width, height, operation.color, operation.isFill);
1244                 }
1245                 break;
1246 
1247                 case "tida.render.GLRender.triangle": 
1248                     render.triangle([operation.vertexs[0], operation.vertexs[1], operation.vertexs[2]],
1249                                     operation.color, operation.isFill);
1250                 break;
1251 
1252                 case "tida.render.GLRender.circle":
1253                     render.circle(operation.vertexs[0], operation.radius, operation.color, operation.isFill);
1254                 break;
1255 
1256                 case "tida.render.GLRender.roundrect": {
1257                     uint width = cast(uint) (operation.vertexs[1].x - operation.vertexs[0].x);
1258                     uint height = cast(uint) (operation.vertexs[1].y - operation.vertexs[0].y);
1259 
1260                     render.roundrect(operation.vertexs[0], width, height, operation.radius, operation.color, operation.isFill);   
1261                 }
1262                 break;
1263 
1264                 case "tida.render.GLRender.polygon":
1265                     render.polygon(operation.vertexs[0], operation.vertexs[1 .. $], operation.color, operation.isFill);
1266                 break;
1267 
1268                 default:
1269                     return;
1270             }
1271         }
1272     }
1273 
1274 override:
1275     void bindTarget(ITarget target) @trusted
1276     {
1277         Operation operation;
1278         operation.name = "bindTarget";
1279 
1280         operations[frame - 1] ~=  operation;
1281 
1282         render.bindTarget(target);
1283     }
1284     
1285     @property ITarget currentTarget() @trusted
1286     {
1287         Operation operation;
1288         operation.name = "currentTarget";
1289 
1290         operations[frame - 1] ~=  operation;
1291 
1292         return render.currentTarget;
1293     }
1294 
1295     void draw(IDrawable drawable, Vecf position) @trusted
1296     {
1297         Operation operation;
1298         operation.name = (cast(Object) drawable).toString;
1299         operation.object = drawable;
1300         operation.vertexs ~= position;
1301         operation.shader = render.currentShader !is null ? 
1302             render.currentShader : 
1303             render.getShader("Default");
1304         operation.shaderName = render.currentShader !is null ? 
1305             findShadername(render.currentShader) :
1306             "Default";
1307 
1308         operations[frame - 1] ~=  operation;
1309 
1310         render.draw(drawable, position);
1311     }
1312 
1313     /// ditto
1314     void drawEx(    IDrawableEx drawable, 
1315                     Vecf position, 
1316                     float angle,
1317                     Vecf center,
1318                     Vecf size,
1319                     ubyte alpha,
1320                     Color!ubyte color = rgb(255, 255, 255)) @trusted
1321     {
1322         Operation operation;
1323         operation.name = (cast(Object) drawable).toString;
1324         operation.objectEx = drawable;
1325         operation.vertexs ~= [position, position + (size.isVecfNaN ? vecfZero : size)];
1326         operation.shader = render.currentShader !is null ? 
1327             render.currentShader : 
1328             render.getShader("Default");
1329 
1330         operation.angle = angle;
1331         operation.center = center;
1332         operation.size = size;
1333         operation.alpha = alpha;
1334 
1335         operation.shaderName = render.currentShader !is null ? 
1336             findShadername(render.currentShader) :
1337             "Default";
1338 
1339         operation.color = color;
1340 
1341         operations[frame - 1] ~=  operation;
1342 
1343         render.drawEx(drawable, position, angle, center, size, alpha, color);
1344     }
1345 
1346     void clear()
1347     {
1348         frame++;
1349         operations.length += 1;
1350 
1351         Operation operation;
1352         operation.name = "tida.render.GLRender.clear";
1353         operation.color = render.background;
1354         operations[frame - 1] ~= operation;
1355 
1356         render.clear();
1357     }
1358 
1359     void drawning()
1360     {
1361         Operation operation;
1362         operation.name = "tida.render.GLRender.drawning";
1363         operations[frame - 1] ~= operation;
1364 
1365         render.drawning();
1366     }
1367 
1368     void point(Vecf position, Color!ubyte color)
1369     {
1370         Operation operation;
1371         operation.name = "tida.render.GLRender.point";
1372         operation.color = color;
1373         operation.shader = render.currentShader !is null ? 
1374             render.currentShader : 
1375             render.getShader("Default");
1376 
1377         operation.shaderName = render.currentShader !is null ? 
1378             findShadername(render.currentShader) :
1379             "Default";
1380 
1381         operation.vertexs ~= position;
1382         operations[frame - 1] ~= operation;
1383 
1384         render.point(position, color);                            
1385     }
1386     
1387     void line(Vecf[2] points, Color!ubyte color)
1388     {
1389         Operation operation;
1390         operation.name = "tida.render.GLRender.line";
1391         operation.color = color;
1392         operation.shader = render.currentShader !is null ? 
1393             render.currentShader : 
1394             render.getShader("Default");
1395 
1396         operation.shaderName = render.currentShader !is null ? 
1397             findShadername(render.currentShader) :
1398             "Default";
1399 
1400         operation.vertexs ~= [points[0], points[1]];
1401         operations[frame - 1] ~= operation;
1402 
1403         render.line(points, color);
1404     }
1405 
1406     void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill)
1407     {
1408         Operation operation;
1409         operation.name = "tida.render.GLRender.rectangle";
1410         operation.color = color;
1411         operation.shader = render.currentShader !is null ? 
1412             render.currentShader : 
1413             render.getShader("Default");
1414 
1415         operation.shaderName = render.currentShader !is null ? 
1416             findShadername(render.currentShader) :
1417             "Default";
1418 
1419         operation.vertexs ~= [position, position + vecf(width, height)];
1420         operation.isFill = isFill;
1421 
1422         operations[frame - 1] ~= operation;
1423 
1424         render.rectangle(position, width, height, color, isFill);
1425     }
1426 
1427     void triangle(Vecf[3] points, Color!ubyte color, bool isFill)
1428     {
1429         Operation operation;
1430         operation.name = "tida.render.GLRender.triangle";
1431         operation.color = color;
1432         operation.shader = render.currentShader !is null ? 
1433             render.currentShader : 
1434             render.getShader("Default");
1435 
1436         operation.shaderName = render.currentShader !is null ? 
1437             findShadername(render.currentShader) :
1438             "Default";
1439 
1440         operation.vertexs ~= [points[0], points[1], points[2]];
1441         operation.isFill = isFill;
1442 
1443         operations[frame - 1] ~= operation;
1444 
1445         render.triangle(points, color, isFill);
1446     }
1447 
1448     void circle(Vecf position, float radius, Color!ubyte color, bool isFill)
1449     {
1450         Operation operation;
1451         operation.name = "tida.render.GLRender.circle";
1452         operation.color = color;
1453         operation.shader = render.currentShader !is null ? 
1454             render.currentShader : 
1455             render.getShader("Default");
1456 
1457         operation.shaderName = render.currentShader !is null ? 
1458             findShadername(render.currentShader) :
1459             "Default";
1460 
1461         operation.vertexs ~= [position];
1462         operation.isFill = isFill;
1463         operation.radius = radius;
1464 
1465         operations[frame - 1] ~= operation;
1466 
1467         render.circle(position, radius, color, isFill);
1468     }
1469 
1470     void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill)
1471     {
1472         Operation operation;
1473         operation.name = "tida.render.GLRender.polygon";
1474         operation.color = color;
1475         operation.shader = render.currentShader !is null ? 
1476             render.currentShader : 
1477             render.getShader("Default");
1478 
1479         operation.shaderName = render.currentShader !is null ? 
1480             findShadername(render.currentShader) :
1481             "Default";
1482 
1483         operation.vertexs ~= [position] ~ points;
1484         operation.isFill = isFill;
1485 
1486         operations[frame - 1] ~= operation;
1487 
1488         render.polygon(position, points, color, isFill);
1489     }
1490 
1491     void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill)
1492     {
1493         Operation operation;
1494         operation.name = "tida.render.GLRender.line";
1495         operation.color = color;
1496         operation.shader = render.currentShader !is null ? 
1497             render.currentShader : 
1498             render.getShader("Default");
1499 
1500         operation.shaderName = render.currentShader !is null ? 
1501             findShadername(render.currentShader) :
1502             "Default";
1503 
1504         operation.vertexs ~= [position, position + vecf(width, height)];
1505         operation.isFill = isFill;
1506         operation.radius = radius;
1507 
1508         operations[frame - 1] ~= operation;
1509 
1510         render.roundrect(position, width, height, radius, color, isFill);
1511     }
1512 
1513     Shader!Program getShader(string name)
1514     {
1515         Operation operation;
1516         operation.name = "tida.render.GLRender.getShader";
1517         operation.shader = render.getShader(name);
1518         operation.shaderName = name;
1519 
1520         operations[frame - 1] ~= operation;
1521 
1522         return render.getShader(name);
1523     }
1524 
1525     void setShader(string name, Shader!Program shader)
1526     {
1527         Operation operation;
1528         operation.name = "tida.render.GLRender.setShader";
1529         operation.shader = shader;
1530         operation.shaderName = name;
1531 
1532         operations[frame - 1] ~= operation;
1533 
1534         render.setShader(name, shader);
1535     }
1536 
1537     @property RenderType type()
1538     {
1539         return RenderType.opengl;
1540     }
1541 
1542     @property void currentModelMatrix(float[4][4] matrix)
1543     {
1544         Operation operation;
1545         operation.name = "tida.render.GLRender.currentModelMatrix[set]";
1546         operation.matrix = matrix;
1547 
1548         operations[frame - 1] ~= operation;
1549 
1550         render.currentModelMatrix = matrix;
1551     }
1552 
1553     @property float[4][4] currentModelMatrix()
1554     {
1555         Operation operation;
1556         operation.name = "tida.render.GLRender.currentModeMatrix[get]";
1557 
1558         operations[frame - 1] ~= operation;
1559 
1560         return render.currentModelMatrix;
1561     }
1562 
1563     @property Camera camera()
1564     {
1565         Operation operation;
1566         operation.name = "tida.render.GLRender.camera[get]";
1567 
1568         operations[frame - 1] ~= operation;
1569 
1570         return render.camera;
1571     }
1572 
1573     @property void camera(Camera value)
1574     {
1575         Operation operation;
1576         operation.name = "tida.render.GLRender.camera[set]";
1577 
1578         operations[frame - 1] ~= operation;
1579 
1580         render.camera = value;
1581     }
1582 
1583     @property void blendMode(BlendMode mode)
1584     {
1585         Operation operation;
1586         operation.name = "tida.render.GLRender.blendMode[set]";
1587 
1588         operations[frame - 1] ~= operation;
1589 
1590         render.blendMode(mode);
1591     }
1592 
1593     @property void reshape()
1594     {
1595         Operation operation;
1596         operation.name = "tida.render.GLRender.reshape";
1597         operation.matrix = render.projection;
1598         operation.vertexs = [camera.port.begin, camera.port.end];
1599 
1600         operations[frame - 1] ~= operation;
1601 
1602         render.reshape();
1603     }
1604 
1605     @property Color!ubyte background()
1606     {
1607         Operation operation;
1608         operation.name = "tida.render.GLRender.background[get]";
1609         operation.color = render.background;
1610 
1611         operations[frame - 1] ~= operation;
1612 
1613         return render.background;
1614     }                            
1615 
1616     @property void background(Color!ubyte color)
1617     {
1618         Operation operation;
1619         operation.name = "tida.render.GLRender.background[set]";
1620         operation.color = color;
1621 
1622         operations[frame - 1] ~= operation;
1623 
1624         render.background = color;
1625     }
1626 
1627     @property void currentShader(Shader!Program shader)
1628     {
1629         Operation operation;
1630         operation.name = "tida.render.GLRender.currentShader[set]";
1631         operation.shader = shader;
1632         operation.shaderName = findShadername(shader);
1633 
1634         operations[frame - 1] ~= operation;
1635 
1636         render.currentShader = shader;
1637     }
1638 
1639     @property Shader!Program currentShader()
1640     {
1641         Operation operation;
1642         operation.name = "tida.render.GLRender.currentShader[get]";
1643         operation.shader = render.currentShader;
1644         operation.shaderName = findShadername(render.currentShader);
1645 
1646         operations[frame - 1] ~= operation;
1647 
1648         return render.currentShader;
1649     }
1650 
1651     void resetShader()
1652     {
1653         Operation operation;
1654         operation.name = "tida.render.GLRender.resetShader";
1655         operation.shader = render.currentShader;
1656         operation.shaderName = findShadername(render.currentShader);
1657 
1658         operations[frame - 1] ~= operation;
1659 
1660         render.resetShader();
1661     }
1662 
1663     void blendOperation(BlendFactor sfactor, BlendFactor dfactor)
1664     {
1665         Operation operation;
1666         operation.name = "tida.render.GLRender.blendOperation";
1667         operation.sfactor = sfactor;
1668         operation.dfactor = dfactor;
1669 
1670         operations[frame - 1] ~= operation;
1671 
1672         render.blendOperation(sfactor, dfactor);
1673     }
1674 }
1675 
1676 /++
1677 Implementation of the interface for interacting with the rendering canvas.
1678 +/
1679 interface ICanvas
1680 {
1681     import tida.color;
1682     import tida.vector;
1683 
1684 @safe:
1685     /++
1686     Allocate memory for the canvas at the specified size.
1687 
1688     Params:
1689         width = Canvas width.
1690         height = Canvas height.
1691     +/
1692     void allocatePlace(uint width, uint height);
1693 
1694     /++
1695     Cleared the canvas with one color.
1696 
1697     Params:
1698         color = Cleared color.
1699     +/
1700     void clearPlane(Color!ubyte color);
1701 
1702     /++
1703     Draws a buffer to a storage object.
1704     +/
1705     void drawTo();
1706 
1707     /// Blending mode (blend or not).
1708     @property void blendMode(BlendMode mode);
1709 
1710     /// ditto
1711     @property BlendMode blendMode();
1712 
1713     /++
1714     Sets the color mixing factor (which formula to mix colors with).
1715     +/
1716     void blendOperation(BlendFactor sfactor, BlendFactor dfactor);
1717 
1718     /++
1719     Mixing factor (sfactor, dfactor).
1720     +/
1721     BlendFactor[2] blendOperation();
1722 
1723     /++
1724     Draw a point on the canvas.
1725     Draw only a point, the rest of the shapes are rendered.
1726 
1727     Params:
1728         position = Point position.
1729         color = Point color.
1730     +/
1731     void pointTo(Vecf position, Color!ubyte color);
1732     /++
1733     Set port of visibility.
1734 
1735     Params:
1736         w = Port width.
1737         h = Port height.
1738     +/
1739     void viewport(uint w, uint h);
1740 
1741     /++
1742     Move the visibility port to the specified coordinates.
1743 
1744     Params:
1745         x = Port x-axis position.
1746         y = Port y-axis position.
1747     +/
1748     void move(int x,int y);
1749 
1750     /++
1751     Canvas data.
1752     +/
1753     @property ref ubyte[] data();
1754 
1755     /// Canvas size.
1756     @property uint[2] size();
1757 
1758     /++
1759     The real size of the world, where from the world it will be drawn to 
1760     the size of the canvas.
1761     +/
1762     @property uint[2] portSize();
1763 
1764     /++
1765     Camera position (offset of all drawing points).
1766     +/
1767     @property int[2] cameraPosition();
1768 }
1769 
1770 template PointToImpl(int pixelformat, int bpc)
1771 {
1772     import tida.vector;
1773     import tida.color;
1774 
1775     static assert(isValidFormat!pixelformat);
1776 
1777     static if(bpc == 0)
1778         enum bytesperpixel = bytesPerColor!pixelformat;
1779     else
1780         enum bytesperpixel = bpc;
1781 
1782     override void pointTo(Vecf position, Color!ubyte color)
1783     {
1784         import tida.each : Coord;
1785         import std.conv : to;
1786 
1787         position = position - vecf(cameraPosition);
1788 
1789         immutable scaleWidth = (cast(float) portSize[0]) / (cast(float) size[0]);
1790         immutable scaleHeight = (cast(float) portSize[1]) / (cast(float) size[1]);
1791         int w = size[0] / portSize[0] + 1;
1792         int h = size[0] / portSize[1] + 1;
1793 
1794         position = position / vecf(scaleWidth, scaleHeight);
1795         
1796         Color!ubyte bcolor;
1797 
1798         foreach (ix, iy; Coord(  position.x.to!int + w, position.y.to!int + h,
1799                                 position.x.to!int, position.y.to!int))
1800         {
1801             if (ix >= size[0] || iy >= size[1] || ix < 0 || iy < 0) continue;
1802             immutable pos = ((iy * size[0]) + ix) * bytesperpixel;
1803 
1804             if (blendMode == BlendMode.withBlend) {
1805                 Color!ubyte blendcolor;
1806 
1807                 static if (pixelformat == PixelFormat.BGRA)
1808                 {
1809                     blendcolor = rgba(  data[pos+3], 
1810                                         data[pos+2], 
1811                                         data[pos+1], 
1812                                         data[pos]);
1813                 }else
1814                 static if (pixelformat == PixelFormat.BGR)
1815                 {
1816                     blendcolor = rgba(  data[pos+2],
1817                                         data[pos+1],
1818                                         data[pos],
1819                                         255);
1820                 }else
1821                 static if (pixelformat == PixelFormat.RGBA)
1822                 {
1823                     blendcolor = rgba(  data[pos],
1824                                         data[pos+1],
1825                                         data[pos+2],
1826                                         data[pos+3]);
1827                 }else
1828                 static if (pixelformat == PixelFormat.RGB)
1829                 {
1830                     blendcolor = rgba(  data[pos],
1831                                         data[pos+1],
1832                                         data[pos+2],
1833                                         255);
1834                 }
1835 
1836                 BlendFactor[2] factors = blendOperation();
1837                 bcolor = BlendFunc!ubyte(factors[0], factors[1])(color, blendcolor);
1838             }else
1839                 bcolor = color;
1840 
1841             if (pos < data.length)
1842             {
1843                 static if (pixelformat == PixelFormat.BGRA)
1844                 {
1845                     data[pos] = bcolor.b;
1846                     data[pos+1] = bcolor.g;
1847                     data[pos+2] = bcolor.r;
1848                     data[pos+3] = bcolor.a;
1849                 }else
1850                 static if (pixelformat == PixelFormat.BGR)
1851                 {
1852                     data[pos] = bcolor.b;
1853                     data[pos+1] = bcolor.g;
1854                     data[pos+2] = bcolor.r;
1855                 }else
1856                 static if (pixelformat == PixelFormat.RGBA)
1857                 {
1858                     data[pos] = bcolor.r;
1859                     data[pos+1] = bcolor.g;
1860                     data[pos+2] = bcolor.b;
1861                     data[pos+3] = bcolor.a;
1862                 }else
1863                 static if (pixelformat == PixelFormat.RGB)
1864                 {
1865                     data[pos] = bcolor.r;
1866                     data[pos+1] = bcolor.g;
1867                     data[pos+2] = bcolor.b;
1868                 }
1869             }
1870         }
1871     }
1872 }
1873 
1874 import tida.color : PixelFormat;
1875 
1876 version(Posix)
1877 class Canvas : ICanvas
1878 {
1879     import x11.X, x11.Xlib, x11.Xutil;
1880     import tida.window;
1881     import tida.runtime;
1882     import tida.color;
1883     import std.exception : enforce;
1884 
1885 private:
1886     GC context;
1887     XImage* ximage;
1888     tida.window.Window window;
1889 
1890     ubyte[] buffer;
1891     uint width;
1892     uint height;
1893 
1894     uint pwidth;
1895     uint pheight;
1896 
1897     int xput = 0;
1898     int yput = 0;
1899 
1900     bool isAlloc = true;
1901 
1902     BlendFactor[2] bfactor;
1903     BlendMode bmode;
1904 
1905 @trusted:
1906 public:
1907     this(tida.window.Window window, bool isAlloc = true)
1908     {
1909         this.isAlloc = isAlloc;
1910 
1911         this.window = window;
1912         if (isAlloc)
1913         {
1914             context = XCreateGC(runtime.display, this.window.handle, 0, null);
1915             enforce!Exception(context, "Software context is not a create!");
1916         }
1917     }
1918 
1919 override:
1920     void allocatePlace(uint width, uint height)
1921     {
1922         this.width = width;
1923         this.height = height;
1924 
1925         buffer = new ubyte[](width * height * bytesPerColor!(PixelFormat.BGRA));
1926         if (isAlloc)
1927         {
1928             if(ximage !is null) {
1929                 XFree(ximage);
1930                 ximage = null;
1931             }
1932 
1933             Visual* visual = window.getVisual();
1934             int depth = window.getDepth();
1935 
1936             ximage = XCreateImage(runtime.display, visual, depth,
1937                 ZPixmap, 0, cast(char*) buffer, width, height, 32, 0);
1938 
1939             enforce!Exception(ximage, "[X11] XImage is not create!");
1940         }
1941     }
1942 
1943     void viewport(uint w, uint h)
1944     {
1945         pwidth = w;
1946         pheight = h;
1947     }
1948 
1949     void move(int x, int y)
1950     {
1951         xput = x;
1952         yput = y;
1953     }
1954 
1955     void clearPlane(Color!ubyte color)
1956     {
1957         for (size_t i = 0; 
1958             i < width * height * bytesPerColor!(PixelFormat.BGRA); 
1959             i += bytesPerColor!(PixelFormat.BGRA))
1960         {
1961             buffer[i]   = color.b;
1962             buffer[i+1] = color.g;
1963             buffer[i+2] = color.r;
1964             buffer[i+3] = color.a;
1965         }
1966     }
1967 
1968     void drawTo()
1969     {
1970         if (isAlloc)
1971         {
1972             XPutImage(  runtime.display, window.handle, context, ximage,
1973                         xput, yput, xput, yput, width, height);
1974 
1975             XSync(runtime.display, false);
1976         }
1977     }
1978 
1979     BlendFactor[2] blendOperation()
1980     {
1981         return bfactor;
1982     }
1983 
1984     void blendOperation(BlendFactor sfactor, BlendFactor dfactor)
1985     {
1986         bfactor = [sfactor, dfactor];
1987     }
1988 
1989     BlendMode blendMode()
1990     {
1991         return bmode;
1992     }
1993 
1994     void blendMode(BlendMode mode)
1995     {
1996         bmode = mode;
1997     }
1998 
1999     @property ref ubyte[] data()
2000     {
2001         return buffer;
2002     }
2003 
2004     @property uint[2] size()
2005     {
2006         return [width, height];
2007     }
2008 
2009     @property uint[2] portSize()
2010     {
2011         return [pwidth, pheight];
2012     }
2013 
2014     @property int[2] cameraPosition()
2015     {
2016         return [xput, yput];
2017     }
2018 
2019     mixin PointToImpl!(PixelFormat.BGRA, 4);
2020 }
2021 
2022 version(Windows)
2023 class Canvas : ICanvas
2024 {
2025     import core.sys.windows.windows;
2026     import tida.color;
2027     import std.exception : enforce;
2028 
2029 private:
2030     PAINTSTRUCT paintstr;
2031     HDC hdc;
2032     HDC pdc;
2033     HBITMAP bitmap;
2034 
2035     tida.window.Window window;
2036 
2037     ubyte[] buffer;
2038     uint _width;
2039     uint _height;
2040     uint _pwidth;
2041     uint _pheight;
2042     int xput;
2043     int yput;
2044 
2045     Color!ubyte _background;
2046     BlendMode bmode;
2047     BlendFactor sfactor;
2048     BlendFactor dfactor;
2049 
2050     bool _isAlloc = true;
2051 
2052 public @trusted:
2053     this(tida.window.Window window, bool isAlloc = true)
2054     {
2055         this.window = window;
2056         _isAlloc = isAlloc;
2057     }
2058 
2059     void recreateBitmap()
2060     {
2061         if (bitmap !is null)
2062             DeleteObject(bitmap);
2063 
2064         if (hdc is null)
2065             hdc = GetDC((cast(Window) window).handle);
2066 
2067         bitmap = CreateBitmap(_width, _height, 1, 32, cast(LPBYTE) buffer);
2068         enforce(bitmap, "[WINAPI] bitmap is not a create!");
2069 
2070         if (pdc is null)
2071             pdc = CreateCompatibleDC(hdc);
2072 
2073         SelectObject(pdc, bitmap);
2074     }
2075 
2076 override:
2077     void allocatePlace(uint width, uint height)
2078     {
2079         _width = width;
2080         _height = height;
2081 
2082         buffer = new ubyte[](_width * _height * 4);
2083     }
2084 
2085     void viewport(uint width, uint height)
2086     {
2087         _pwidth = width;
2088         _pheight = height;
2089     }
2090 
2091     void move(int x, int y)
2092     {
2093         xput = x;
2094         yput = y;
2095     }
2096 
2097     void clearPlane(Color!ubyte color)
2098     {
2099         for (size_t i = 0; i < _width * _height * 4; i += 4)
2100         {
2101             buffer[i] = color.b;
2102             buffer[i + 1] = color.g;
2103             buffer[i + 2] = color.r;
2104             buffer[i + 3] = color.Max;
2105         }
2106     }
2107 
2108     void drawTo()
2109     {
2110         if (_isAlloc)
2111         {
2112             recreateBitmap();
2113             BitBlt(hdc, 0, 0, _width, _height, pdc, 0, 0, SRCCOPY);
2114         }
2115     }
2116 
2117     BlendMode blendMode()
2118     {
2119         return bmode;
2120     }
2121 
2122     void blendMode(BlendMode mode)
2123     {
2124         bmode = mode;
2125     }
2126 
2127     BlendFactor[2] blendOperation()
2128     {
2129         return [sfactor, dfactor];
2130     }
2131 
2132     void blendOperation(BlendFactor sfactor, BlendFactor dfactor)
2133     {
2134         this.sfactor = sfactor;
2135         this.dfactor = dfactor;
2136     }
2137 
2138     @property ref ubyte[] data()
2139     {
2140         return buffer;
2141     }
2142 
2143     @property uint[2] size()
2144     {
2145         return [_width, _height];
2146     }
2147 
2148     @property uint[2] portSize()
2149     {
2150         return [_pwidth, _pheight];
2151     }
2152 
2153     @property int[2] cameraPosition()
2154     {
2155         return [xput, yput];
2156     }
2157 
2158     mixin PointToImpl!(PixelFormat.BGRA, 4);
2159 }
2160 
2161 class Software : IRenderer
2162 {
2163     import tida.window;
2164     import tida.color;
2165     import tida.vector;
2166     import tida.shape;
2167     import tida.drawable;
2168 
2169 private:
2170     ICanvas canvas;
2171     Camera _camera;
2172     Color!ubyte _background;
2173 
2174 public @safe:
2175     this(IWindow window, bool isAlloc = true)
2176     {
2177         _camera = new Camera();
2178         canvas = new Canvas(cast(Window) window);
2179 
2180         _camera.port = Shape!float.Rectangle(vecf(0, 0), vecf(window.width, window.height));
2181         _camera.shape = _camera.port;
2182 
2183         canvas.blendMode(BlendMode.withBlend);
2184         canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha);
2185 
2186         reshape();
2187     }
2188 
2189     this(ICanvas canvas, bool isAlloc = true)
2190     {
2191         _camera = new Camera();
2192         this.canvas = canvas;
2193 
2194         canvas.blendMode(BlendMode.withBlend);
2195         canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha);
2196     }
2197 
2198 override:
2199     @property RenderType type()
2200     {
2201         return RenderType.software; 
2202     }
2203     
2204     void bindTarget(ITarget target) @safe
2205     {
2206         assert(null, "Target not a support with software renderer!");
2207     }
2208     
2209     @property ITarget currentTarget() @safe
2210     {
2211         return null;
2212     }
2213 
2214     void reshape()
2215     {
2216         import std.conv : to;
2217 
2218         canvas.allocatePlace(_camera.shape.end.x.to!int,_camera.shape.end.y.to!int);
2219         canvas.viewport(_camera.port.end.x.to!int,_camera.port.end.y.to!int);
2220         canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int);
2221     }
2222 
2223     @property Camera camera()
2224     {
2225         return _camera;
2226     }
2227 
2228     @property void camera(Camera cam)
2229     {
2230         _camera = cam;
2231     }
2232 
2233     @property Color!ubyte background()
2234     {
2235         return _background;
2236     }
2237 
2238     @property void background(Color!ubyte color)
2239     {
2240         _background = color;
2241     }
2242 
2243     void point(Vecf position, Color!ubyte color)
2244     {
2245         canvas.pointTo(position, color);
2246     }
2247 
2248     void line(Vecf[2] points, Color!ubyte color)
2249     {
2250         import tida.each : Line;
2251 
2252         foreach (x, y; Line(points[0], points[1]))
2253             canvas.pointTo(vecf(x, y), color);
2254     }
2255 
2256     void rectangle( Vecf position, 
2257                     uint width, 
2258                     uint height, 
2259                     Color!ubyte color, 
2260                     bool isFill)
2261     {
2262         import tida.each : Coord;
2263         import std.conv : to;
2264 
2265         if (isFill)
2266         {
2267             foreach (ix, iy; Coord(width.to!int, height.to!int))
2268             {
2269                 point(vecf(position.x.to!int + ix, position.y.to!int + iy), color);
2270             }
2271         }else
2272         {
2273             foreach (ix, iy; Coord(width.to!int, height.to!int))
2274             {
2275                 point(vecf(position.x.to!int + ix,position.y.to!int), color);
2276                 point(vecf(position.x.to!int + ix,position.y.to!int + height.to!int), color);
2277  
2278                 point(vecf(position.x.to!int,position.y.to!int + iy), color);
2279                 point(vecf(position.x.to!int + width.to!int,position.y.to!int + iy), color);
2280             }
2281         }
2282     }
2283 
2284     void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) @safe
2285     {
2286         import std.math : cos, sin;
2287 
2288         immutable size = vecf(width, height);
2289         immutable iter = 0.25;
2290 
2291         position += camera.port.begin;
2292 
2293         if (isFill)
2294         {
2295             rectangle(position + vecf(radius, 0), cast(int) (width - radius * 2), height, color, true);
2296             rectangle(position + vecf(0, radius), width, cast(int) (height - radius * 2), color, true);
2297 
2298             void rounded(Vecf pos, float a, float b, float iter) @safe
2299             {
2300                 import tida.angle;
2301 
2302                 for (float i = a; i <= b;)
2303                 {
2304                     Vecf temp;
2305                     temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius;
2306 
2307                     line([pos, temp], color);
2308                     i += iter;
2309                 }
2310             }
2311 
2312             rounded(position + vecf(radius, radius), 180, 270, iter);
2313             rounded(position + vecf(size.x - radius, radius), 270, 360, iter);
2314             rounded(position + vecf(radius, size.y - radius), 90, 180, iter);
2315             rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter);
2316         }else
2317         {
2318             void rounded(Vecf pos, float a, float b,float iter) @safe
2319             {
2320                 import tida.angle;
2321 
2322                 for (float i = a; i <= b;)
2323                 {
2324                     Vecf temp;
2325                     temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius;
2326                     point(temp, color);
2327                     i += iter;
2328                 }
2329             }
2330 
2331             rounded(position + vecf(radius, radius), 180, 270, iter);
2332             rounded(position + vecf(size.x - radius, radius), 270, 360, iter);
2333             rounded(position + vecf(radius, size.y - radius), 90, 180, iter);
2334             rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter);
2335 
2336             line([position + vecf(radius, 0), position + vecf(width - radius, 0)], color);
2337             line([position + vecf(width, radius), position + vecf(width, height - radius)], color);
2338             line([position + vecf(radius, height), position + vecf(width - radius, height)], color);
2339             line([position + vecf(0, radius), position + vecf(0, height - radius)], color);
2340         }
2341     }
2342 
2343     void circle(Vecf position, float radius, Color!ubyte color, bool isFill)
2344     {
2345         import tida.image, tida.each;
2346 
2347         int rad = cast(int) radius;
2348         Image buffer = new Image(rad * 2, rad * 2);
2349         buffer.fill(rgba(255,255,255,0));
2350 
2351         int x = 0;
2352         int y = rad;
2353 
2354         int X1 = rad;
2355         int Y1 = rad;
2356 
2357         int delta = 1 - 2 * cast(int) radius;
2358         int error = 0;
2359 
2360         void bufferLine(Vecf[2] points, Color!ubyte color) @safe 
2361         {
2362             foreach (ix, iy; Line(points[0], points[1]))
2363             {
2364                 buffer.setPixel(ix, iy, color);
2365             }
2366         }
2367 
2368         while (y >= 0)
2369         {
2370             if (isFill)
2371             {
2372                 bufferLine([Vecf(X1 + x, Y1 + y),Vecf(X1 + x, Y1 - y)],color);
2373                 bufferLine([Vecf(X1 - x, Y1 + y),Vecf(X1 - x, Y1 - y)],color);
2374             }else
2375             {
2376                 buffer.setPixel(X1 + x, Y1 + y,color);
2377                 buffer.setPixel(X1 + x, Y1 - y,color);
2378                 buffer.setPixel(X1 - x, Y1 + y,color);
2379                 buffer.setPixel(X1 - x, Y1 - y,color);
2380             }
2381 
2382             error = 2 * (delta + y) - 1;
2383             if ((delta < 0) && (error <= 0))
2384             {
2385                 delta += 2 * ++x + 1;
2386                 continue;
2387             }
2388 
2389             if ((delta > 0) && (error > 0))
2390             {
2391                 delta -= 2 * --y + 1;
2392                 continue;
2393             }
2394             delta += 2 * (++x - --y);
2395         }
2396 
2397         foreach (ix, iy; Coord(buffer.width, buffer.height))
2398         {
2399             Color!ubyte pixel;
2400 
2401             if ((pixel = buffer.getPixel(ix,iy)).a != 0)
2402             {
2403                 point(position + vecf(ix, iy), pixel);
2404             }
2405         }
2406     }
2407 
2408     void triangle(Vecf[3] position,Color!ubyte color,bool isFill) @trusted
2409     {
2410         import tida.each;
2411 
2412         if (isFill)
2413         {
2414             foreach (x, y; Line(position[0], position[1])) {
2415                 auto p = vecf(x,y);
2416 
2417                 line([p, position[2]], color);
2418             }
2419         } else
2420         {
2421             line([position[0], position[1]], color);
2422             line([position[1], position[2]], color);
2423             line([position[2], position[0]], color);
2424         }
2425     }
2426 
2427     void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill)
2428     {
2429         import std.algorithm : each;
2430         points = points.dup;
2431         points.each!((ref e) => e = e + position);
2432 
2433         if (!isFill)
2434         {
2435             int next = 0;
2436             for (int i = 0; i < points.length; i++)
2437             {
2438                 next = (i + 1 == points.length) ? 0 : i + 1;
2439                 line([points[i],points[next]], color);
2440             }
2441         }else
2442         {
2443             import std.algorithm : minElement, maxElement;
2444             import tida.collision : placeLineLineImpl;
2445 
2446             float maxX = points.maxElement!"a.x".x;
2447             float minY = points.minElement!"a.y".y;
2448             float maxY = points.maxElement!"a.y".y;
2449             float minX = points.minElement!"a.x".x;
2450 
2451             alias LineIter = Vecf[2];
2452 
2453             LineIter[] drowning;
2454 
2455             for (float i = minY; i <= maxY; i += 1.0f)
2456             {
2457                 Vecf firstPoint = vecfNaN;
2458                 float lastX = minX > position.x ? position.x : minX;
2459 
2460                 for (float j = lastX; j <= maxX; j += 1.0f)
2461                 {
2462                     size_t next = 0;
2463                     for (size_t currPointI = 0; currPointI < points.length; currPointI++)
2464                     {
2465                         next = (currPointI + 1 == points.length) ? 0 : currPointI + 1;
2466 
2467                         auto iter = placeLineLineImpl(  [vecf(lastX, i), vecf(j, i)],
2468                                                         [points[currPointI], points[next]]);
2469                         if (!iter.isVecfNaN) {
2470                             if (firstPoint.isVecfNaN)
2471                             {
2472                                 firstPoint = vecf(j, i);
2473                             } else
2474                             {
2475                                 drowning ~= [firstPoint, vecf(j, i)];
2476                                 firstPoint = vecfNaN;
2477                             }
2478 
2479                             lastX = j;
2480                         }
2481                     }             
2482                 }
2483             }
2484 
2485             foreach (e; drowning)
2486             {
2487                 line(e, color);
2488             }
2489         }
2490     }
2491 
2492     void clear()
2493     {
2494         import std.conv : to;
2495 
2496         canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int);
2497         canvas.clearPlane(_background);
2498     }
2499 
2500     void drawning()
2501     {
2502         canvas.drawTo();
2503     }
2504 
2505     @property void blendMode(BlendMode mode)
2506     {
2507         canvas.blendMode(mode);
2508     }
2509 
2510     void blendOperation(BlendFactor sfactor, BlendFactor dfactor)
2511     {
2512         canvas.blendOperation(sfactor, dfactor);
2513     }
2514 
2515     void draw(IDrawable drawable, Vecf position) @safe
2516     {
2517         position -= camera.port.begin;
2518         drawable.draw(this, position);
2519     }
2520 
2521     /// ditto
2522     void drawEx(    IDrawableEx drawable, 
2523                     Vecf position, 
2524                 float angle,
2525                 Vecf center,
2526                 Vecf size,
2527                 ubyte alpha,
2528                 Color!ubyte color = rgb(255, 255, 255)) @safe
2529     {
2530         position -= camera.port.begin;
2531         drawable.drawEx(this, position, angle, center, size, alpha, color);
2532     }
2533 
2534     import tida.shader;
2535 
2536     override Shader!Program getShader(string name) @safe
2537     {
2538         assert(null, "There are no shaders in this version of the render.");
2539     }
2540 
2541     override void setShader(string name,Shader!Program program) @safe
2542     {
2543         assert(null, "There are no shaders in this version of the render.");
2544     }
2545 
2546     override void currentShader(Shader!Program program) @safe @property
2547     {
2548         assert(null, "There are no shaders in this version of the render.");
2549     }
2550 
2551     override Shader!Program currentShader() @safe @property
2552     {
2553         assert(null, "There are no shaders in this version of the render.");
2554     }
2555 
2556     override void resetShader() @safe
2557     {
2558         assert(null, "There are no shaders in this version of the render.");
2559     }
2560 
2561     override void currentModelMatrix(float[4][4] matrix) @safe @property
2562     {
2563         assert(null, "There are no matrix in this version of the render.");
2564     }
2565 
2566     override float[4][4] currentModelMatrix() @safe @property
2567     {
2568         assert(null, "There are not matrix in this version of the render.");
2569     }
2570 }
2571 
2572 import tida.window;
2573 
2574 /++
2575 Creates a render based on hardware acceleration capabilities.
2576 It should be used if the program does not use intentional hardware
2577 acceleration objects.
2578 
2579 Params:
2580     window = Window object.
2581 +/
2582 IRenderer createRenderer(IWindow window) @trusted
2583 {
2584     import bindbc.opengl;
2585 
2586     if (isOpenGLLoaded())
2587     {
2588         immutable ver = loadedOpenGLVersion();
2589         if (ver != GLSupport.gl11 && ver != GLSupport.gl12 &&
2590             ver != GLSupport.gl13 && ver != GLSupport.gl14 &&
2591             ver != GLSupport.gl15)
2592         {
2593             return new GLRender(window);
2594         }
2595     }
2596 
2597     return new Software(window, true);
2598 }
2599 
2600 import tida.image;
2601 import tida.vector;
2602 
2603 /++
2604 Reads frame image data. Time consuming operation.
2605 
2606 Params:
2607     render = render instance.
2608     position = Begin position read.
2609     width = frame width.
2610     height = frame height.
2611 +/
2612 Image renderRead(IRenderer render, Vecf position, int width, int height) @trusted
2613 {
2614     import tida.gl;
2615     import std.conv : to;
2616 
2617     Image image = new Image(width, height);
2618     glReadPixels(   position.x.to!int, position.y.to!int,
2619                     width, height, GL_RGBA, GL_UNSIGNED_BYTE, cast(void*) image.pixels);
2620 
2621     return image;
2622 }