-
Notifications
You must be signed in to change notification settings - Fork 30
/
InternalTextureLoader.java
530 lines (458 loc) · 18.2 KB
/
InternalTextureLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
package org.newdawn.slick.opengl;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Iterator;
import org.lwjgl.BufferUtils;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.ResourceLoader;
/**
* A texture loaded based on many old versions that will load image data from a file
* and produce OpenGL textures.
*
* @see ImageData
*
* @author kevin
*/
public class InternalTextureLoader {
/** The renderer to use for all GL operations */
protected static SGL GL = Renderer.get();
/** The standard texture loaded used everywhere */
private static final InternalTextureLoader loader = new InternalTextureLoader();
/**
* Get the single instance of this texture loader
*
* @return The single instance of the texture loader
*/
public static InternalTextureLoader get() {
return loader;
}
/** The table of textures that have been loaded in this loader */
private HashMap texturesLinear = new HashMap();
/** The table of textures that have been loaded in this loader */
private HashMap texturesNearest = new HashMap();
/** The destination pixel format */
private int dstPixelFormat = SGL.GL_RGBA8;
/** True if we're using deferred loading */
private boolean deferred;
/** True if we should hold texture data */
private boolean holdTextureData;
/**
* Create a new texture loader based on the game panel
*/
private InternalTextureLoader() {
}
/**
* Indicate where texture data should be held for reinitialising at a future
* point.
*
* @param holdTextureData True if we should hold texture data
*/
public void setHoldTextureData(boolean holdTextureData) {
this.holdTextureData = holdTextureData;
}
/**
* True if we should only record the request to load in the intention
* of loading the texture later
*
* @param deferred True if the we should load a token
*/
public void setDeferredLoading(boolean deferred) {
this.deferred = deferred;
}
/**
* Check if we're using deferred loading
*
* @return True if we're loading deferred textures
*/
public boolean isDeferredLoading() {
return deferred;
}
/**
* Remove a particular named image from the cache
*
* @param name The name of the image to be cleared
*/
public void clear(String name) {
texturesLinear.remove(name);
texturesNearest.remove(name);
}
/**
* Clear out the cached textures
*/
public void clear() {
texturesLinear.clear();
texturesNearest.clear();
}
/**
* Tell the loader to produce 16 bit textures
*/
public void set16BitMode() {
dstPixelFormat = SGL.GL_RGBA16;
}
/**
* Create a new texture ID
*
* @return A new texture ID
*/
public static int createTextureID()
{
IntBuffer tmp = createIntBuffer(1);
GL.glGenTextures(tmp);
return tmp.get(0);
}
/**
* Get a texture from a specific file
*
* @param source The file to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(File source, boolean flipped,int filter) throws IOException {
String resourceName = source.getAbsolutePath();
InputStream in = new FileInputStream(source);
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a specific file
*
* @param source The file to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(File source, boolean flipped,int filter, int[] transparent) throws IOException {
String resourceName = source.getAbsolutePath();
InputStream in = new FileInputStream(source);
return getTexture(in, resourceName, flipped, filter, transparent);
}
/**
* Get a texture from a resource location
*
* @param resourceName The location to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use when scaling the texture
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(String resourceName, boolean flipped, int filter) throws IOException {
InputStream in = ResourceLoader.getResourceAsStream(resourceName);
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a resource location
*
* @param resourceName The location to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use when scaling the texture
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(String resourceName, boolean flipped, int filter, int[] transparent) throws IOException {
InputStream in = ResourceLoader.getResourceAsStream(resourceName);
return getTexture(in, resourceName, flipped, filter, transparent);
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param filter The filter to use when scaling the texture
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(InputStream in, String resourceName, boolean flipped, int filter) throws IOException {
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param filter The filter to use when scaling the texture
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public TextureImpl getTexture(InputStream in, String resourceName, boolean flipped, int filter, int[] transparent) throws IOException {
if (deferred) {
return new DeferredTexture(in, resourceName, flipped, filter, transparent);
}
HashMap hash = texturesLinear;
if (filter == SGL.GL_NEAREST) {
hash = texturesNearest;
}
String resName = resourceName;
if (transparent != null) {
resName += ":"+transparent[0]+":"+transparent[1]+":"+transparent[2];
}
resName += ":"+flipped;
if (holdTextureData) {
TextureImpl tex = (TextureImpl) hash.get(resName);
if (tex != null) {
return tex;
}
} else {
SoftReference ref = (SoftReference) hash.get(resName);
if (ref != null) {
TextureImpl tex = (TextureImpl) ref.get();
if (tex != null) {
return tex;
} else {
hash.remove(resName);
}
}
}
// horrible test until I can find something more suitable
try {
GL.glGetError();
} catch (NullPointerException e) {
throw new RuntimeException("Image based resources must be loaded as part of init() or the game loop. They cannot be loaded before initialisation.");
}
TextureImpl tex = getTexture(in, resourceName,
SGL.GL_TEXTURE_2D,
filter,
filter, flipped, transparent);
tex.setCacheName(resName);
if (holdTextureData) {
hash.put(resName, tex);
} else {
hash.put(resName, new SoftReference(tex));
}
return tex;
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param target The texture target we're loading this texture into
* @param minFilter The scaling down filter
* @param magFilter The scaling up filter
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
private TextureImpl getTexture(InputStream in,
String resourceName,
int target,
int magFilter,
int minFilter, boolean flipped, int[] transparent) throws IOException
{
// create the texture ID for this texture
ByteBuffer textureBuffer;
LoadableImageData imageData = ImageDataFactory.getImageDataFor(resourceName);
textureBuffer = imageData.loadImage(new BufferedInputStream(in), flipped, transparent);
int textureID = createTextureID();
TextureImpl texture = new TextureImpl(resourceName, target, textureID);
// bind this texture
GL.glBindTexture(target, textureID);
int width;
int height;
int texWidth;
int texHeight;
boolean hasAlpha;
width = imageData.getWidth();
height = imageData.getHeight();
hasAlpha = imageData.getDepth() == 32;
texture.setTextureWidth(imageData.getTexWidth());
texture.setTextureHeight(imageData.getTexHeight());
texWidth = texture.getTextureWidth();
texHeight = texture.getTextureHeight();
IntBuffer temp = BufferUtils.createIntBuffer(16);
GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
int max = temp.get(0);
if ((texWidth > max) || (texHeight > max)) {
throw new IOException("Attempt to allocate a texture to big for the current hardware");
}
int srcPixelFormat = hasAlpha ? SGL.GL_RGBA : SGL.GL_RGB;
int componentCount = hasAlpha ? 4 : 3;
texture.setWidth(width);
texture.setHeight(height);
texture.setAlpha(hasAlpha);
if (holdTextureData) {
texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
}
GL.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
get2Fold(width),
get2Fold(height),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
/**
* Create an empty texture
*
* @param width The width of the new texture
* @param height The height of the new texture
* @return The created empty texture
* @throws IOException Indicates a failure to create the texture on the graphics hardware
*/
public Texture createTexture(final int width, final int height) throws IOException {
return createTexture(width, height, SGL.GL_NEAREST);
}
/**
* Create an empty texture
*
* @param width The width of the new texture
* @param height The height of the new texture
* @return The created empty texture
* @throws IOException Indicates a failure to create the texture on the graphics hardware
*/
public Texture createTexture(final int width, final int height, final int filter) throws IOException {
ImageData ds = new EmptyImageData(width, height);
return getTexture(ds, filter);
}
/**
* Get a texture from a image file
*
* @param dataSource The image data to generate the texture from
* @param filter The filter to use when scaling the texture
* @return The texture created
* @throws IOException Indicates the texture is too big for the hardware
*/
public Texture getTexture(ImageData dataSource, int filter) throws IOException
{
int target = SGL.GL_TEXTURE_2D;
ByteBuffer textureBuffer;
textureBuffer = dataSource.getImageBufferData();
// create the texture ID for this texture
int textureID = createTextureID();
TextureImpl texture = new TextureImpl("generated:"+dataSource, target ,textureID);
int minFilter = filter;
int magFilter = filter;
boolean flipped = false;
// bind this texture
GL.glBindTexture(target, textureID);
int width;
int height;
int texWidth;
int texHeight;
boolean hasAlpha;
width = dataSource.getWidth();
height = dataSource.getHeight();
hasAlpha = dataSource.getDepth() == 32;
texture.setTextureWidth(dataSource.getTexWidth());
texture.setTextureHeight(dataSource.getTexHeight());
texWidth = texture.getTextureWidth();
texHeight = texture.getTextureHeight();
int srcPixelFormat = hasAlpha ? SGL.GL_RGBA : SGL.GL_RGB;
int componentCount = hasAlpha ? 4 : 3;
texture.setWidth(width);
texture.setHeight(height);
texture.setAlpha(hasAlpha);
IntBuffer temp = BufferUtils.createIntBuffer(16);
GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
int max = temp.get(0);
if ((texWidth > max) || (texHeight > max)) {
throw new IOException("Attempt to allocate a texture to big for the current hardware");
}
if (holdTextureData) {
texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
}
GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
get2Fold(width),
get2Fold(height),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
/**
* Get the closest greater power of 2 to the fold number
*
* @param fold The target number
* @return The power of 2
*/
public static int get2Fold(int fold) {
int ret = 2;
while (ret < fold) {
ret *= 2;
}
return ret;
}
/**
* Creates an integer buffer to hold specified ints
* - strictly a utility method
*
* @param size how many int to contain
* @return created IntBuffer
*/
public static IntBuffer createIntBuffer(int size) {
ByteBuffer temp = ByteBuffer.allocateDirect(4 * size);
temp.order(ByteOrder.nativeOrder());
return temp.asIntBuffer();
}
/**
* Reload all the textures loaded in this loader
*/
public void reload() {
Iterator texs = texturesLinear.values().iterator();
while (texs.hasNext()) {
((TextureImpl) texs.next()).reload();
}
texs = texturesNearest.values().iterator();
while (texs.hasNext()) {
((TextureImpl) texs.next()).reload();
}
}
/**
* Reload a given texture blob
*
* @param texture The texture being reloaded
* @param srcPixelFormat The source pixel format
* @param componentCount The component count
* @param minFilter The minification filter
* @param magFilter The magnification filter
* @param textureBuffer The pixel data
* @return The ID of the newly created texture
*/
public int reload(TextureImpl texture, int srcPixelFormat, int componentCount,
int minFilter, int magFilter, ByteBuffer textureBuffer) {
int target = SGL.GL_TEXTURE_2D;
int textureID = createTextureID();
GL.glBindTexture(target, textureID);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
texture.getTextureWidth(),
texture.getTextureHeight(),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return textureID;
}
}