1 module prova.assets.models.texture;
2 
3 import imageformats;
4 import prova.assets;
5 import prova.graphics;
6 import std.algorithm.mutation;
7 import std.string;
8 
9 ///
10 final class Texture : Asset
11 {
12   private static uint bindedId;
13   ///
14   immutable uint id;
15   private int _width;
16   private int _height;
17 
18   /// Creates a new blank texture using the specified width and height
19   this(int width, int height)
20   {
21     this(null, width, height);
22   }
23 
24   /**
25    * Params:
26    *   data = RGBA by row
27    */
28   this(ubyte[] data, int width, int height)
29   {
30     uint id;
31     glGenTextures(1, &id);
32 
33     this.id = id;
34 
35     recreate(data, width, height);
36   }
37 
38   ///
39   this(string path)
40   {
41     IFImage image = read_image(path, ColFmt.RGBA);
42 
43     flipImage(image);
44 
45     this(image.pixels, image.w, image.h);
46   }
47 
48   ///
49   @property int width()
50   {
51     return _width;
52   }
53 
54   ///
55   @property int height()
56   {
57     return _height;
58   }
59 
60   /**
61    * Params:
62    *   data = RGBA by row
63    *   width = new width of the texture
64    *   width = new height of the texture
65    */
66   void recreate(ubyte[] data, int width, int height)
67   {
68     bind();
69 
70     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
71     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
72 
73     glTexImage2D(
74       GL_TEXTURE_2D,
75       0,
76       GL_RGBA,
77       width,
78       height,
79       0,
80       GL_RGBA,
81       GL_UNSIGNED_BYTE,
82       data ? data.ptr : null
83     );
84 
85     _width = width;
86     _height = height;
87   }
88 
89   /**
90    * Updates the texture
91    *
92    * Params:
93    *   data = RGBA by row
94    */
95   void update(ubyte[] data)
96   {
97     update(data, 0, 0, _width, _height);
98   }
99 
100   /**
101    * Updates part of the texture
102    *
103    * Params:
104    *   data = RGBA by row
105    *   x = x offset of the change
106    *   y = y offset of the change
107    *   width = width of the change
108    *   height = height of the change
109    */
110   void update(ubyte[] data, int x, int y, int width, int height)
111   {
112     bind();
113 
114     glTexSubImage2D(
115       GL_TEXTURE_2D,
116       0,
117       x,
118       y,
119       width,
120       height,
121       GL_RGBA,
122       GL_UNSIGNED_BYTE,
123       data ? data.ptr : null
124     );
125   }
126 
127   /// Resizes texture, preserving data
128   void resize(int width, int height)
129   {
130     ubyte[] data = getData();
131 
132     recreate(null, width, height);
133     update(data, 0, 0, _width, _height);
134   }
135 
136   /// 
137   ubyte[] getData()
138   {
139     ubyte[] data;
140     data.length = _width * _height * 4;
141 
142     bind();
143 
144     glGetTexImage(
145       GL_TEXTURE_2D,
146       0,
147       GL_RGBA,
148       GL_UNSIGNED_BYTE,
149       data.ptr
150     );
151 
152     return data;
153   }
154 
155   private static void flipImage(ref IFImage image)
156   {
157     int rowLength = image.w * 4;
158 
159     foreach(i; 0 .. image.h)
160     {
161       const int start = i * rowLength;
162 
163       ubyte[] row = image.pixels[start .. start + rowLength];
164 
165       reverse(row);
166 
167       copy(image.pixels[start .. start + rowLength], row[0 .. rowLength]);
168     }
169 
170     reverse(image.pixels);
171   }
172 
173   ~this()
174   {
175     glDeleteTextures(1, &id);
176   }
177 
178   package(prova) void bind()
179   {
180     bind(id);
181   }
182 
183   package(prova) static void bind(uint id)
184   {
185     if(bindedId == id)
186       return;
187 
188     bindedId = id;
189     glBindTexture(GL_TEXTURE_2D, id);
190   }
191 }