View Javadoc
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   * A texture loaded based on many old versions that will load image data from a file
32   * and produce OpenGL textures.
33   * 
34   * @see ImageData
35   * 
36   * @author kevin
37   */
38  public class InternalTextureLoader {
39  
40      /** Useful for debugging; keeps track of the current number of active textures. */
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       * Returns true if we are forcing loaded image data into power-of-two OpenGL textures (by default,
52       * this is true). If non-power-of-two textures is not supported in hardware (i.e. isNPOTSupported
53       * returns false), then the image data will be forced into POT textures regardless of isForcePOTSize().
54       *   
55       * @return true if we should ensure POT sized textures, flase if we should attempt to use NPOT if supported
56       */
57      private static boolean isForcePOT() {
58          return forcePOT;
59      }
60      
61      /**
62       * Set whether we are forcing loaded image data into power-of-two OpenGL textures (by default,
63       * this is true). If non-power-of-two textures is not supported in hardware (i.e. isNPOTSupported
64       * returns false), then the image data will be forced into POT textures regardless of isForcePOTSize().
65       *   
66       * @param b true if we should ensure POT sized textures, flase if we should attempt to use NPOT if supported
67       */
68      public static void setForcePOT(boolean b) {
69          forcePOT = b;
70      }
71  
72      /**
73       * Returns the current number of active textures. Calling InternalTextureLoader.createTextureID
74       * increases this number. Calling TextureImpl.release or InternalTextureLoader.deleteTextureID 
75       * decreases this number.
76       * 
77       * @return the number of active OpenGL textures
78       */
79      public static int getTextureCount() {
80          return textureCount;
81      }
82  
83      /**
84       * Create a new texture ID; will increase the value for getTextureCount.
85       *
86       * @return A new texture ID
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       * Used internally; call TextureImpl.release. 
97       * @param id the id of the OpenGL texture 
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      * Slick uses glGenerateMipmap() or GL14.GL_GENERATE_MIPMAP to automatically
109      * build mipmaps (for advanced users). If neither of these versions are supported,
110      * the GL_EXT_framebuffer_object is used as a fallback, and if that extension is also
111      * missing, this method returns false.
112      *
113      * @return whether the version is >= 1.4 or GL_EXT_framebuffer_object extension exists
114      */
115     private static boolean isGenerateMipmapSupported() {
116         return GLContext.getCapabilities().OpenGL14 || GLContext.getCapabilities().GL_EXT_framebuffer_object;
117     }
118 
119     /**
120      * Returns true if non-power-of-two textures are supported in hardware via the
121      * GL_ARB_texture_non_power_of_two extension. Non-power-of-two texture loading
122      * is not a current feature of Slick, although it is planned.
123      *
124      * @return true if the extension is listed
125      */
126     private static boolean isNPOTSupported() {
127         //don't check GL20, nvidia/ATI usually don't advertise this extension
128         //if it means requiring software fallback
129         return GLContext.getCapabilities().GL_ARB_texture_non_power_of_two;
130     }
131 
132     /** The renderer to use for all GL operations */
133     private static final SGL GL = Renderer.get();
134     /** The standard texture loaded used everywhere */
135     private static final InternalTextureLoader loader = new InternalTextureLoader();
136 
137     /**
138      * Get the single instance of this texture loader
139      *
140      * @return The single instance of the texture loader
141      */
142     @Nonnull
143     public static InternalTextureLoader get() {
144         return loader;
145     }
146 
147     /** The table of textures that have been loaded in this loader */
148     @Nonnull
149     private final HashMap<String, TextureImpl> texturesLinear = new HashMap<>();
150     /** The table of textures that have been loaded in this loader */
151     @Nonnull
152     private final HashMap<String, TextureImpl> texturesNearest = new HashMap<>();
153     /** The destination pixel format */
154     private int dstPixelFormat = SGL.GL_RGBA8;
155     /** True if we're using deferred loading */
156     private boolean deferred;
157     /** True if we should hold texture data */
158     private boolean holdTextureData;
159     
160     /** 
161      * Create a new texture loader based on the game panel
162      */
163     private InternalTextureLoader() {
164     }
165     
166     /**
167      * Indicate where texture data should be held for reinitialising at a future
168      * point.
169      * 
170      * @param holdTextureData True if we should hold texture data
171      */
172     public void setHoldTextureData(boolean holdTextureData) {
173         this.holdTextureData = holdTextureData;
174     }
175     
176     /**
177      * True if we should only record the request to load in the intention
178      * of loading the texture later
179      * 
180      * @param deferred True if the we should load a token
181      */
182     public void setDeferredLoading(boolean deferred) {
183         this.deferred = deferred;
184     }
185     
186     /**
187      * Check if we're using deferred loading
188      * 
189      * @return True if we're loading deferred textures
190      */
191     public boolean isDeferredLoading() {
192         return deferred;
193     }
194     
195     /**
196      * Remove a particular named image from the cache (does not release the OpenGL texture)
197      * 
198      * @param name The name of the image to be cleared
199      */
200     public void clear(String name) {
201         texturesLinear.remove(name);
202         texturesNearest.remove(name);
203     }
204     
205     /**
206      * Clear out the cached textures (does not release the OpenGL textures)
207      */
208     public void clear() {
209         texturesLinear.clear();
210         texturesNearest.clear();
211     }
212     
213     /**
214      * Tell the loader to produce 16 bit textures
215      */
216     public void set16BitMode() {
217         dstPixelFormat = SGL.GL_RGBA16;
218     }
219     
220     
221     /**
222      * Get a texture from a specific file
223      * 
224      * @param source The file to load the texture from
225      * @param flipped True if we should flip the texture on the y axis while loading
226      * @param filter The filter to use
227      * @return The texture loaded
228      * @throws IOException Indicates a failure to load the image
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      * Get a texture from a specific file
240      * 
241      * @param source The file to load the texture from
242      * @param flipped True if we should flip the texture on the y axis while loading
243      * @param filter The filter to use
244      * @param transparent The colour to interpret as transparent or null if none
245      * @return The texture loaded
246      * @throws IOException Indicates a failure to load the image
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      * Get a texture from a resource location
258      * 
259      * @param resourceName The location to load the texture from
260      * @param flipped True if we should flip the texture on the y axis while loading
261      * @param filter The filter to use when scaling the texture
262      * @return The texture loaded
263      * @throws IOException Indicates a failure to load the image
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      * Get a texture from a resource location
274      * 
275      * @param resourceName The location to load the texture from
276      * @param flipped True if we should flip the texture on the y axis while loading
277      * @param filter The filter to use when scaling the texture
278      * @param transparent The colour to interpret as transparent or null if none
279      * @return The texture loaded
280      * @throws IOException Indicates a failure to load the image
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      * Get a texture from a image file
290      * 
291      * @param in The stream from which we can load the image
292      * @param resourceName The name to give this image in the internal cache
293      * @param flipped True if we should flip the image on the y-axis while loading
294      * @param filter The filter to use when scaling the texture
295      * @return The texture loaded
296      * @throws IOException Indicates a failure to load the image
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      * Get a texture from a image file
305      * 
306      * @param in The stream from which we can load the image
307      * @param resourceName The name to give this image in the internal cache
308      * @param flipped True if we should flip the image on the y-axis while loading
309      * @param filter The filter to use when scaling the texture
310      * @param transparent The colour to interpret as transparent or null if none
311      * @return The texture loaded
312      * @throws IOException Indicates a failure to load the image
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)); //FIXME Test
338             TextureImpl tex = ref.get();
339             if (tex != null) {
340                 return tex;
341             } else {
342                 hash.remove(resName);
343             }
344         }
345         
346         // horrible test until I can find something more suitable
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         // create the texture ID for this texture
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         // bind this texture 
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         // produce a texture from the byte buffer
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      * An advanced texture loading method providing more parameters for glTexImage2D. 
437      * The created texture will not be placed in the cache.
438      * 
439      * If genMipmaps is true, the loader will attempt to automatically build mipmaps
440      * with either GL30.glGenerateMipmap() or GL14.GL_GENERATE_MIPMAP. If the GL version
441      * is less than 1.4, then no mipmaps will be built and instead the magFilter (one
442      * of GL_LINEAR or GL_NEAREST) will be used for both minification and magnification.
443      * Users can determine mipmap generation support with isGenerateMipmapSupported().
444      * 
445      * If the internalFormat is not null, then that will override the default pixel
446      * format described by this InternalTextureLoader (either GL_RGBA16 or GL_RGBA8
447      * depending on the is16BitMode() value). This parameter can be independent
448      * of the format of ImageData -- OpenGL will convert the ImageData format (e.g. BGRA)
449      * to the given internalFormat (e.g. RGB). Note that internalFormat is more limited
450      * than the ImageData's format; i.e. BGRA as an internal storage format is only 
451      * supported if GL_ext_bgra is present.
452      * 
453      * After calling this, the texture will be bound and the target (e.g. GL_TEXTURE_2D)
454      * will be enabled. If you are using a higher priority target, such as 3D textures,
455      * you should disable that afterwards to ensure compatibility with Slick.
456      * 
457      * The ByteBuffer data is assumed to match getTexWidth/getTexHeight in ImageData.
458      * 
459      * @param data the image data holding width, height, format (ImageData byte buffer is ignored)
460      * @param buffer the actual data to send to GL 
461      * @param ref The name to give the TextureImpl
462      * @param target The texture target we're loading this texture into
463      * @param minFilter The scaling down filter
464      * @param magFilter The scaling up filter
465      * @param genMipmaps true to generate mipmaps (failure will fallback to using magFilter)
466      * @param internalFormat the internal format of the texture (or null for default)
467      * @return The texture loaded
468      * @throws IOException Indicates a failure to load the image
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         // bind this texture 
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         //even though it might really be RGBA16/8, user will expect comparability with Format constants
508         texture.setImageFormat(internalFormat!=null ? internalFormat : ImageData.Format.RGBA); 
509         
510         if (holdTextureData) {
511             // TODO: fix the reload functionality; right now it causes problems and
512             // should probably just be removed or reworked
513             int componentCount = dataFormat.getColorComponents();
514             texture.setTextureData(srcFmt, componentCount, minFilter, magFilter, buffer);
515         }
516         
517         ContextCapabilities cx = GLContext.getCapabilities();
518         if (genMipmaps && !isGenerateMipmapSupported()) { //nothing for auto mipmap gen
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         //if we are < 3.0 and have no FBO support, fall back to GL_GENERATE_MIPMAP
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         //For now, just assume Slick has decoded image data into POT
533         GL.glTexImage2D(target, 0, dstFmt, texWidth, texHeight, 0, srcFmt, SGL.GL_UNSIGNED_BYTE, buffer);
534         
535 //        if (texWidth==width && texHeight==height) {
536 //            GL.glTexImage2D(target, 0, dstFmt, texWidth, texHeight,
537 //                    0, srcFmt, SGL.GL_UNSIGNED_BYTE, buffer);
538 //        } else {
539 //            //Slick2D decodes NPOT image data into padded byte buffers.
540 //            //Once we make the shift to decoding NPOT image data, then we can clean this up
541 //            GL.glTexImage2D(target, 0, dstFmt, texWidth, texHeight,
542 //                    0, srcFmt, SGL.GL_UNSIGNED_BYTE, buffer);
543 //
544 //            //first create the full texture
545 //            //we could also use a null ByteBuffer but this seems to be buggy with certain machines
546 ////            ByteBuffer empty = BufferUtils.createByteBuffer(texWidth * texHeight * 4);
547 ////            GL.glTexImage2D(target, 0, dstFmt, texWidth, texHeight,
548 ////                    0, SGL.GL_RGBA, SGL.GL_UNSIGNED_BYTE, empty);
549 ////            //then upload the sub image
550 ////            GL.glTexSubImage2D(target, 0, 0, 0, width, height, srcFmt, SGL.GL_UNSIGNED_BYTE, buffer);
551 //        }
552         
553         if (genMipmaps) {
554             GL11.glEnable(target); //fixes ATI bug
555             if (cx.OpenGL30)
556                 GL30.glGenerateMipmap(target);
557             else
558                 EXTFramebufferObject.glGenerateMipmapEXT(target);
559         }
560         return texture; 
561     } 
562     
563     /**
564      * Create an empty texture
565      * 
566      * @param width The width of the new texture
567      * @param height The height of the new texture
568      * @return The created empty texture
569      * @throws IOException Indicates a failure to create the texture on the graphics hardware
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      * Create an empty texture
578      * 
579      * @param width The width of the new texture
580      * @param height The height of the new texture
581      * @return The created empty texture
582      * @throws IOException Indicates a failure to create the texture on the graphics hardware
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      * Get a texture from an image file. 
593      * 
594      * @param dataSource The image data to generate the texture from
595      * @param filter The filter to use when scaling the texture
596      * @return The texture created
597      * @throws IOException Indicates the texture is too big for the hardware
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         // create the texture ID for this texture 
608         int textureID = createTextureID(); 
609         TextureImpl texture = new TextureImpl("generated:"+dataSource, target ,textureID); 
610         
611         int minFilter = filter;
612         int magFilter = filter;
613         // bind this texture 
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         // produce a texture from the byte buffer
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      * Get the closest greater power of 2 to the fold number
670      * 
671      * @param fold The target number
672      * @return The power of 2
673      */
674     public static int get2Fold(int fold) {
675         //new algorithm? -> return 1 << (32 - Integer.numberOfLeadingZeros(n-1));
676         int ret = 2;
677         while (ret < fold) {
678             ret *= 2;
679         }
680         return ret;
681     } 
682     
683     /**
684      * Creates an integer buffer to hold specified ints
685      * - strictly a utility method
686      *
687      * @param size how many int to contain
688      * @return created IntBuffer
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      * Reload all the textures loaded in this loader
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      * Reload a given texture blob; used internally with setHoldTextureData. 
714      * Call TextureImpl.reload instead.
715      * 
716      * @param texture The texture being reloaded
717      * @param srcPixelFormat The source pixel format
718      * @param componentCount The component count
719      * @param minFilter The minification filter
720      * @param magFilter The magnification filter 
721      * @param textureBuffer The pixel data 
722      * @return The ID of the newly created texture
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         // produce a texture from the byte buffer
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 }