1 package org.newdawn.slick.opengl;
2
3 import java.io.BufferedInputStream;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.lang.ref.SoftReference;
9 import java.nio.ByteBuffer;
10 import java.nio.ByteOrder;
11 import java.nio.IntBuffer;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.Map;
15
16 import org.lwjgl.BufferUtils;
17 import org.lwjgl.opengl.ContextCapabilities;
18 import org.lwjgl.opengl.EXTFramebufferObject;
19 import org.lwjgl.opengl.GL11;
20 import org.lwjgl.opengl.GL14;
21 import org.lwjgl.opengl.GL30;
22 import org.lwjgl.opengl.GLContext;
23 import org.newdawn.slick.opengl.renderer.Renderer;
24 import org.newdawn.slick.opengl.renderer.SGL;
25 import org.newdawn.slick.util.ResourceLoader;
26
27 import javax.annotation.Nonnull;
28 import javax.annotation.Nullable;
29
30
31
32
33
34
35
36
37
38 public class InternalTextureLoader {
39
40
41 private static int textureCount = 0;
42
43 private static boolean forcePOT = true;
44
45 public static boolean isPowerOfTwo(int n) {
46 return (n & -n) == n;
47 }
48
49
50
51
52
53
54
55
56
57 private static boolean isForcePOT() {
58 return forcePOT;
59 }
60
61
62
63
64
65
66
67
68 public static void setForcePOT(boolean b) {
69 forcePOT = b;
70 }
71
72
73
74
75
76
77
78
79 public static int getTextureCount() {
80 return textureCount;
81 }
82
83
84
85
86
87
88 private static int createTextureID() {
89 IntBuffer tmp = createIntBuffer(1);
90 GL.glGenTextures(tmp);
91 textureCount++;
92 return tmp.get(0);
93 }
94
95
96
97
98
99 public static void deleteTextureID(int id) {
100 IntBuffer texBuf = createIntBuffer(1);
101 texBuf.put(id);
102 texBuf.flip();
103 GL.glDeleteTextures(texBuf);
104 textureCount--;
105 }
106
107
108
109
110
111
112
113
114
115 private static boolean isGenerateMipmapSupported() {
116 return GLContext.getCapabilities().OpenGL14 || GLContext.getCapabilities().GL_EXT_framebuffer_object;
117 }
118
119
120
121
122
123
124
125
126 private static boolean isNPOTSupported() {
127
128
129 return GLContext.getCapabilities().GL_ARB_texture_non_power_of_two;
130 }
131
132
133 private static final SGL GL = Renderer.get();
134
135 private static final InternalTextureLoader loader = new InternalTextureLoader();
136
137
138
139
140
141
142 @Nonnull
143 public static InternalTextureLoader get() {
144 return loader;
145 }
146
147
148 @Nonnull
149 private final HashMap<String, TextureImpl> texturesLinear = new HashMap<>();
150
151 @Nonnull
152 private final HashMap<String, TextureImpl> texturesNearest = new HashMap<>();
153
154 private int dstPixelFormat = SGL.GL_RGBA8;
155
156 private boolean deferred;
157
158 private boolean holdTextureData;
159
160
161
162
163 private InternalTextureLoader() {
164 }
165
166
167
168
169
170
171
172 public void setHoldTextureData(boolean holdTextureData) {
173 this.holdTextureData = holdTextureData;
174 }
175
176
177
178
179
180
181
182 public void setDeferredLoading(boolean deferred) {
183 this.deferred = deferred;
184 }
185
186
187
188
189
190
191 public boolean isDeferredLoading() {
192 return deferred;
193 }
194
195
196
197
198
199
200 public void clear(String name) {
201 texturesLinear.remove(name);
202 texturesNearest.remove(name);
203 }
204
205
206
207
208 public void clear() {
209 texturesLinear.clear();
210 texturesNearest.clear();
211 }
212
213
214
215
216 public void set16BitMode() {
217 dstPixelFormat = SGL.GL_RGBA16;
218 }
219
220
221
222
223
224
225
226
227
228
229
230 @Nullable
231 public Texture getTexture(@Nonnull File source, boolean flipped,int filter) throws IOException {
232 String resourceName = source.getAbsolutePath();
233 InputStream in = new FileInputStream(source);
234
235 return getTexture(in, resourceName, flipped, filter, null);
236 }
237
238
239
240
241
242
243
244
245
246
247
248 @Nullable
249 public Texture getTexture(@Nonnull File source, boolean flipped,int filter, int[] transparent) throws IOException {
250 String resourceName = source.getAbsolutePath();
251 InputStream in = new FileInputStream(source);
252
253 return getTexture(in, resourceName, flipped, filter, transparent);
254 }
255
256
257
258
259
260
261
262
263
264
265 @Nullable
266 public Texture getTexture(String resourceName, boolean flipped, int filter) throws IOException {
267 InputStream in = ResourceLoader.getResourceAsStream(resourceName);
268
269 return getTexture(in, resourceName, flipped, filter, null);
270 }
271
272
273
274
275
276
277
278
279
280
281
282 @Nullable
283 public Texture getTexture(String resourceName, boolean flipped, int filter, @Nullable int[] transparent) throws IOException {
284 InputStream in = ResourceLoader.getResourceAsStream(resourceName);
285
286 return getTexture(in, resourceName, flipped, filter, transparent);
287 }
288
289
290
291
292
293
294
295
296
297
298 @Nullable
299 public Texture getTexture(@Nonnull InputStream in, String resourceName, boolean flipped, int filter) throws IOException {
300 return getTexture(in, resourceName, flipped, filter, null);
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314 @Nullable
315 public TextureImpl getTexture(@Nonnull InputStream in, String resourceName, boolean flipped, int filter, @Nullable int[] transparent) throws IOException {
316 if (deferred) {
317 return new DeferredTexture(in, resourceName, flipped, filter, transparent);
318 }
319
320 Map<String, TextureImpl> hash = texturesLinear;
321 if (filter == SGL.GL_NEAREST) {
322 hash = texturesNearest;
323 }
324
325 String resName = resourceName;
326 if (transparent != null) {
327 resName += ":"+transparent[0]+":"+transparent[1]+":"+transparent[2];
328 }
329 resName += ":"+flipped;
330
331 if (holdTextureData) {
332 TextureImpl tex = hash.get(resName);
333 if (tex != null) {
334 return tex;
335 }
336 } else {
337 SoftReference<TextureImpl> ref = new SoftReference<>(hash.get(resName));
338 TextureImpl tex = ref.get();
339 if (tex != null) {
340 return tex;
341 } else {
342 hash.remove(resName);
343 }
344 }
345
346
347 try {
348 GL.glGetError();
349 } catch (NullPointerException e) {
350 throw new RuntimeException("Image based resources must be loaded as part of init() or the game loop. They cannot be loaded before initialisation.");
351 }
352
353 TextureImpl tex = getTexture(in, resourceName,
354 SGL.GL_TEXTURE_2D,
355 filter, filter, flipped, transparent);
356
357 tex.setCacheName(resName);
358 if (holdTextureData) {
359 hash.put(resName, tex);
360 } else {
361 final SoftReference<TextureImpl> textureSoftReference = new SoftReference<>(tex);
362 hash.put(resName, textureSoftReference.get());
363 }
364
365 return tex;
366 }
367
368 @Nonnull
369 private TextureImpl getTexture(@Nonnull InputStream in, String resourceName,
370 int target, int minFilter, int magFilter,
371 boolean flipped, @Nullable int[] transparent) throws IOException {
372
373 ByteBuffer textureBuffer;
374
375 LoadableImageData imageData = ImageDataFactory.getImageDataFor(resourceName);
376 textureBuffer = imageData.loadImage(new BufferedInputStream(in), flipped, transparent);
377
378 int textureID = createTextureID();
379 TextureImpl texture = new TextureImpl(resourceName, target, textureID);
380
381 GL.glEnable(target);
382 GL.glBindTexture(target, textureID);
383
384 int width;
385 int height;
386 int texWidth;
387 int texHeight;
388
389 ImageData.Format format;
390
391 width = imageData.getWidth();
392 height = imageData.getHeight();
393 format = imageData.getFormat();
394
395 texture.setTextureWidth(imageData.getTexWidth());
396 texture.setTextureHeight(imageData.getTexHeight());
397
398 texWidth = texture.getTextureWidth();
399 texHeight = texture.getTextureHeight();
400
401 IntBuffer temp = BufferUtils.createIntBuffer(16);
402 GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
403 int max = temp.get(0);
404 if ((texWidth > max) || (texHeight > max)) {
405 throw new IOException("Attempt to allocate a texture to big for the current hardware");
406 }
407
408 int srcPixelFormat = format.getOGLType();
409 int componentCount = format.getColorComponents();
410
411 texture.setWidth(width);
412 texture.setHeight(height);
413 texture.setImageFormat(format);
414
415 if (holdTextureData) {
416 texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
417 }
418
419 GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
420 GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
421
422
423 GL.glTexImage2D(target,
424 0,
425 dstPixelFormat,
426 get2Fold(width),
427 get2Fold(height),
428 0,
429 srcPixelFormat,
430 SGL.GL_UNSIGNED_BYTE,
431 textureBuffer);
432 return texture;
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 @Nonnull
471 public TextureImpl createTexture(@Nonnull ImageData data, ByteBuffer buffer,
472 String ref,
473 int target,
474 int minFilter,
475 int magFilter,
476 boolean genMipmaps,
477 @Nullable ImageData.Format internalFormat) throws IOException {
478 int textureID = createTextureID();
479 TextureImpl texture = new TextureImpl(ref, target, textureID);
480
481 GL.glEnable(target);
482 GL.glBindTexture(target, textureID);
483
484 int width = data.getWidth();
485 int height = data.getHeight();
486 int texWidth = data.getTexWidth();
487 int texHeight = data.getTexHeight();
488
489 boolean usePOT = !isNPOTSupported() || isForcePOT();
490 if (usePOT) {
491 texWidth = get2Fold(width);
492 texHeight = get2Fold(height);
493 }
494
495 int max = GL11.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE);
496 if (texWidth>max || texHeight>max)
497 throw new IOException("Attempt to allocate a texture to big for the current hardware");
498
499 ImageData.Format dataFormat = data.getFormat();
500 int dstFmt = internalFormat!=null ? internalFormat.getOGLType() : dstPixelFormat;
501 int srcFmt = dataFormat.getOGLType();
502
503 texture.setTextureWidth(texWidth);
504 texture.setTextureHeight(texHeight);
505 texture.setWidth(width);
506 texture.setHeight(height);
507
508 texture.setImageFormat(internalFormat!=null ? internalFormat : ImageData.Format.RGBA);
509
510 if (holdTextureData) {
511
512
513 int componentCount = dataFormat.getColorComponents();
514 texture.setTextureData(srcFmt, componentCount, minFilter, magFilter, buffer);
515 }
516
517 ContextCapabilities cx = GLContext.getCapabilities();
518 if (genMipmaps && !isGenerateMipmapSupported()) {
519 minFilter = magFilter;
520 genMipmaps = false;
521 }
522
523 GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
524 GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
525
526
527 if (genMipmaps && !cx.OpenGL30 && !cx.GL_EXT_framebuffer_object) {
528 GL.glTexParameteri(target, GL14.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
529 genMipmaps = false;
530 }
531
532
533 GL.glTexImage2D(target, 0, dstFmt, texWidth, texHeight, 0, srcFmt, SGL.GL_UNSIGNED_BYTE, buffer);
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 if (genMipmaps) {
554 GL11.glEnable(target);
555 if (cx.OpenGL30)
556 GL30.glGenerateMipmap(target);
557 else
558 EXTFramebufferObject.glGenerateMipmapEXT(target);
559 }
560 return texture;
561 }
562
563
564
565
566
567
568
569
570
571 @Nonnull
572 public Texture createTexture(final int width, final int height) throws IOException {
573 return createTexture(width, height, SGL.GL_NEAREST);
574 }
575
576
577
578
579
580
581
582
583
584 @Nonnull
585 public Texture createTexture(final int width, final int height, final int filter) throws IOException {
586 ImageData ds = new EmptyImageData(width, height);
587
588 return getTexture(ds, filter);
589 }
590
591
592
593
594
595
596
597
598
599 @Nonnull
600 public Texture getTexture(@Nonnull ImageData dataSource, int filter) throws IOException
601 {
602 int target = SGL.GL_TEXTURE_2D;
603
604 ByteBuffer textureBuffer;
605 textureBuffer = dataSource.getImageBufferData();
606
607
608 int textureID = createTextureID();
609 TextureImpl texture = new TextureImpl("generated:"+dataSource, target ,textureID);
610
611 int minFilter = filter;
612 int magFilter = filter;
613
614 GL.glEnable(target);
615 GL.glBindTexture(target, textureID);
616
617 int width;
618 int height;
619 int texWidth;
620 int texHeight;
621
622 ImageData.Format format;
623
624 width = dataSource.getWidth();
625 height = dataSource.getHeight();
626 format = dataSource.getFormat();
627
628 texture.setTextureWidth(dataSource.getTexWidth());
629 texture.setTextureHeight(dataSource.getTexHeight());
630
631 texWidth = texture.getTextureWidth();
632 texHeight = texture.getTextureHeight();
633
634 int srcPixelFormat = format.getOGLType();
635 int componentCount = format.getColorComponents();
636
637 texture.setWidth(width);
638 texture.setHeight(height);
639 texture.setImageFormat(format);
640
641 IntBuffer temp = BufferUtils.createIntBuffer(16);
642 GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
643 int max = temp.get(0);
644 if ((texWidth > max) || (texHeight > max)) {
645 throw new IOException("Attempt to allocate a texture to big for the current hardware");
646 }
647
648 if (holdTextureData) {
649 texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
650 }
651
652 GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
653 GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
654
655
656 GL.glTexImage2D(target,
657 0,
658 dstPixelFormat,
659 get2Fold(width),
660 get2Fold(height),
661 0,
662 srcPixelFormat,
663 SGL.GL_UNSIGNED_BYTE,
664 textureBuffer);
665 return texture;
666 }
667
668
669
670
671
672
673
674 public static int get2Fold(int fold) {
675
676 int ret = 2;
677 while (ret < fold) {
678 ret *= 2;
679 }
680 return ret;
681 }
682
683
684
685
686
687
688
689
690 @Nonnull
691 private static IntBuffer createIntBuffer(int size) {
692 ByteBuffer temp = ByteBuffer.allocateDirect(4 * size);
693 temp.order(ByteOrder.nativeOrder());
694
695 return temp.asIntBuffer();
696 }
697
698
699
700
701 public void reload() {
702 Iterator<TextureImpl> texs = texturesLinear.values().iterator();
703 while (texs.hasNext()) {
704 (texs.next()).reload();
705 }
706 texs = texturesNearest.values().iterator();
707 while (texs.hasNext()) {
708 (texs.next()).reload();
709 }
710 }
711
712
713
714
715
716
717
718
719
720
721
722
723
724 public int reload(@Nonnull TextureImpl texture, int srcPixelFormat, int componentCount,
725 int minFilter, int magFilter, ByteBuffer textureBuffer) {
726 int target = SGL.GL_TEXTURE_2D;
727 int textureID = createTextureID();
728 GL.glEnable(target);
729 GL.glBindTexture(target, textureID);
730
731 GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
732 GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
733
734
735 GL.glTexImage2D(target,
736 0,
737 dstPixelFormat,
738 texture.getTextureWidth(),
739 texture.getTextureHeight(),
740 0,
741 srcPixelFormat,
742 SGL.GL_UNSIGNED_BYTE,
743 textureBuffer);
744 return textureID;
745 }
746 }