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 }