1 module prova.graphics.primitives.texture;
2 
3 import imageformats,
4        prova.graphics,
5        std.algorithm.mutation,
6        std..string;
7 
8 ///
9 final class Texture
10 {
11   private static Texture[string] textureCache;
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   @property int width()
40   {
41     return _width;
42   }
43 
44   ///
45   @property int height()
46   {
47     return _height;
48   }
49 
50   /**
51    * Params:
52    *   data = RGBA by row
53    *   width = new width of the texture
54    *   width = new height of the texture
55    */
56   void recreate(ubyte[] data, int width, int height)
57   {
58     bind();
59 
60     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
61     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
62 
63     glTexImage2D(
64       GL_TEXTURE_2D,
65       0,
66       GL_RGBA,
67       width,
68       height,
69       0,
70       GL_RGBA,
71       GL_UNSIGNED_BYTE,
72       data ? data.ptr : null
73     );
74 
75     _width = width;
76     _height = height;
77   }
78 
79   /**
80    * Updates the texture
81    *
82    * Params:
83    *   data = RGBA by row
84    */
85   void update(ubyte[] data)
86   {
87     update(data, 0, 0, _width, _height);
88   }
89 
90   /**
91    * Updates part of the texture
92    *
93    * Params:
94    *   data = RGBA by row
95    *   x = x offset of the change
96    *   y = y offset of the change
97    *   width = width of the change
98    *   height = height of the change
99    */
100   void update(ubyte[] data, int x, int y, int width, int height)
101   {
102     bind();
103 
104     glTexSubImage2D(
105       GL_TEXTURE_2D,
106       0,
107       x,
108       y,
109       width,
110       height,
111       GL_RGBA,
112       GL_UNSIGNED_BYTE,
113       data ? data.ptr : null
114     );
115   }
116 
117   /// Resizes texture, preserving data
118   void resize(int width, int height)
119   {
120     ubyte[] data = getData();
121 
122     recreate(null, width, height);
123     update(data, 0, 0, _width, _height);
124   }
125 
126   /// 
127   ubyte[] getData()
128   {
129     ubyte[] data;
130     data.length = _width * _height * 4;
131 
132     bind();
133 
134     glGetTexImage(
135       GL_TEXTURE_2D,
136       0,
137       GL_RGBA,
138       GL_UNSIGNED_BYTE,
139       data.ptr
140     );
141 
142     return data;
143   }
144 
145   ///
146   public static Texture fetch(string path)
147   {
148     return path in textureCache ? textureCache[path] : cacheFile(path);
149   }
150 
151   ///
152   public static Texture cacheFile(string path)
153   {
154     IFImage image = read_image(path, ColFmt.RGBA);
155 
156     flipImage(image);
157 
158     Texture texture = new Texture(image.pixels, image.w, image.h);
159     textureCache[path] = texture;
160 
161     return texture;
162   }
163 
164   private static void flipImage(ref IFImage image)
165   {
166     int rowLength = image.w * 4;
167 
168     foreach(i; 0 .. image.h)
169     {
170       const int start = i * rowLength;
171 
172       ubyte[] row = image.pixels[start .. start + rowLength];
173 
174       reverse(row);
175 
176       copy(image.pixels[start .. start + rowLength], row[0 .. rowLength]);
177     }
178 
179     reverse(image.pixels);
180   }
181 
182   ~this()
183   {
184     glDeleteTextures(1, &id);
185   }
186 
187   package(prova) void bind()
188   {
189     bind(id);
190   }
191 
192   package(prova) static void bind(uint id)
193   {
194     if(bindedId == id)
195       return;
196 
197     bindedId = id;
198     glBindTexture(GL_TEXTURE_2D, id);
199   }
200 
201   package(prova) static cleanUp()
202   {
203     textureCache.clear();
204   }
205 }