+
+ Using the {@link TextRenderer TextRenderer} is simple. Add a
+ "TextRenderer renderer;
" field to your {@link
+ com.jogamp.opengl.GLEventListener GLEventListener}. In your {@link
+ com.jogamp.opengl.GLEventListener#init init} method, add:
+
+
+ renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36)); ++ +
In the {@link com.jogamp.opengl.GLEventListener#display display} method of your + {@link com.jogamp.opengl.GLEventListener GLEventListener}, add: +
+ renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + // optionally set the color + renderer.setColor(1.0f, 0.2f, 0.2f, 0.8f); + renderer.draw("Text to draw", xPosition, yPosition); + // ... more draw commands, color changes, etc. + renderer.endRendering(); ++ + Unless you are sharing textures and display lists between OpenGL + contexts, you do not need to call the {@link #dispose dispose} + method of the TextRenderer; the OpenGL resources it uses + internally will be cleaned up automatically when the OpenGL + context is destroyed.
+ + Note that the TextRenderer may cause the vertex and texture + coordinate array buffer bindings to change, or to be unbound. This + is important to note if you are using Vertex Buffer Objects (VBOs) + in your application.
+
+ Internally, the renderer uses a rectangle packing algorithm to
+ pack both glyphs and full Strings' rendering results (which are
+ variable size) onto a larger OpenGL texture. The internal backing
+ store is maintained using a {@link
+ com.jogamp.opengl.util.awt.TextureRenderer TextureRenderer}. A least
+ recently used (LRU) algorithm is used to discard previously
+ rendered strings; the specific algorithm is undefined, but is
+ currently implemented by flushing unused Strings' rendering
+ results every few hundred rendering cycles, where a rendering
+ cycle is defined as a pair of calls to {@link #beginRendering
+ beginRendering} / {@link #endRendering endRendering}.
+
+ @author John Burkey
+ @author Kenneth Russell
+*/
+public class TextRenderer {
+
+ // These are occasionally useful for more in-depth debugging
+ private static final boolean DISABLE_GLYPH_CACHE = false;
+
+ static final int kSize = 256;
+
+ // Every certain number of render cycles, flush the strings which
+ // haven't been used recently
+ private static final int CYCLES_PER_FLUSH = 100;
+
+ // The amount of vertical dead space on the backing store before we
+ // force a compaction
+ private static final float MAX_VERTICAL_FRAGMENTATION = 0.7f;
+ static final int kQuadsPerBuffer = 100;
+ static final int kCoordsPerVertVerts = 3;
+ static final int kCoordsPerVertTex = 2;
+ static final int kVertsPerQuad = 4;
+ static final int kTotalBufferSizeVerts = kQuadsPerBuffer * kVertsPerQuad;
+ static final int kTotalBufferSizeCoordsVerts = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertVerts;
+ static final int kTotalBufferSizeCoordsTex = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertTex;
+ static final int kTotalBufferSizeBytesVerts = kTotalBufferSizeCoordsVerts * 4;
+ static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4;
+ static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4;
+ static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4;
+ private final Font font;
+ private final boolean antialiased;
+ private final boolean useFractionalMetrics;
+ private final GL2 gl;
+
+ // Whether we're attempting to use automatic mipmap generation support
+ private boolean mipmap;
+ private RectanglePacker packer;
+ private boolean haveMaxSize;
+ private final RenderDelegate renderDelegate;
+ private TextureRenderer cachedBackingStore;
+ private Graphics2D cachedGraphics;
+ private FontRenderContext cachedFontRenderContext;
+ private final Map
+
+ Glyphs need to be able to re-upload themselves to the backing
+ store on demand as we go along in the render sequence.
+ */
+
+ class Glyph {
+ // If this Glyph represents an individual unicode glyph, this
+ // is its unicode ID. If it represents a String, this is -1.
+ private int unicodeID;
+ // If the above field isn't -1, then these fields are used.
+ // The glyph code in the font
+ private int glyphCode;
+ // The GlyphProducer which created us
+ private GlyphProducer producer;
+ // The advance of this glyph
+ private float advance;
+ // The GlyphVector for this single character; this is passed
+ // in during construction but cleared during the upload
+ // process
+ private GlyphVector singleUnicodeGlyphVector;
+ // The rectangle of this glyph on the backing store, or null
+ // if it has been cleared due to space pressure
+ private Rect glyphRectForTextureMapping;
+ // If this Glyph represents a String, this is the sequence of
+ // characters
+ private String str;
+ // Whether we need a valid advance when rendering this string
+ // (i.e., whether it has other single glyphs coming after it)
+ private boolean needAdvance;
+
+ // Creates a Glyph representing an individual Unicode character
+ public Glyph(final int unicodeID,
+ final int glyphCode,
+ final float advance,
+ final GlyphVector singleUnicodeGlyphVector,
+ final GlyphProducer producer) {
+ this.unicodeID = unicodeID;
+ this.glyphCode = glyphCode;
+ this.advance = advance;
+ this.singleUnicodeGlyphVector = singleUnicodeGlyphVector;
+ this.producer = producer;
+ }
+
+ // Creates a Glyph representing a sequence of characters, with
+ // an indication of whether additional single glyphs are being
+ // rendered after it
+ public Glyph(final String str, final boolean needAdvance) {
+ this.str = str;
+ this.needAdvance = needAdvance;
+ }
+
+ /** Returns this glyph's unicode ID */
+ public int getUnicodeID() {
+ return unicodeID;
+ }
+
+ /** Returns this glyph's (font-specific) glyph code */
+ public int getGlyphCode() {
+ return glyphCode;
+ }
+
+ /** Returns the advance for this glyph */
+ public float getAdvance() {
+ return advance;
+ }
+
+ /** Draws this glyph and returns the (x) advance for this glyph */
+ public float draw3D(final float inX, final float inY, final float z, final float scaleFactor) {
+ if (str != null) {
+ draw3D_ROBUST(str, inX, inY, z, scaleFactor);
+ if (!needAdvance) {
+ return 0;
+ }
+ // Compute and return the advance for this string
+ final GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str);
+ float totalAdvance = 0;
+ for (int i = 0; i < gv.getNumGlyphs(); i++) {
+ totalAdvance += gv.getGlyphMetrics(i).getAdvance();
+ }
+ return totalAdvance;
+ }
+
+ // This is the code path taken for individual glyphs
+ if (glyphRectForTextureMapping == null) {
+ upload();
+ }
+
+ try {
+ if (mPipelinedQuadRenderer == null) {
+ mPipelinedQuadRenderer = new Pipelined_QuadRenderer();
+ }
+
+ final TextureRenderer renderer = getBackingStore();
+ // Handles case where NPOT texture is used for backing store
+ final TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords();
+ final float xScale = wholeImageTexCoords.right();
+ final float yScale = wholeImageTexCoords.bottom();
+
+ final Rect rect = glyphRectForTextureMapping;
+ final TextData data = (TextData) rect.getUserData();
+ data.markUsed();
+
+ final Rectangle2D origRect = data.origRect();
+
+ final float x = inX - (scaleFactor * data.origOriginX());
+ final float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY()));
+
+ final int texturex = rect.x() + (data.origin().x - data.origOriginX());
+ final int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() -
+ (data.origin().y - data.origOriginY());
+ final int width = (int) origRect.getWidth();
+ final int height = (int) origRect.getHeight();
+
+ final float tx1 = xScale * texturex / renderer.getWidth();
+ final float ty1 = yScale * (1.0f -
+ ((float) texturey / (float) renderer.getHeight()));
+ final float tx2 = xScale * (texturex + width) / renderer.getWidth();
+ final float ty2 = yScale * (1.0f -
+ ((float) (texturey + height) / (float) renderer.getHeight()));
+
+ mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1);
+ mPipelinedQuadRenderer.glVertex3f(x, y, z);
+ mPipelinedQuadRenderer.glTexCoord2f(tx2, ty1);
+ mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), y,
+ z);
+ mPipelinedQuadRenderer.glTexCoord2f(tx2, ty2);
+ mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor),
+ y + (height * scaleFactor), z);
+ mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2);
+ mPipelinedQuadRenderer.glVertex3f(x,
+ y + (height * scaleFactor), z);
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ return advance;
+ }
+
+ /** Notifies this glyph that it's been cleared out of the cache */
+ public void clear() {
+ glyphRectForTextureMapping = null;
+ }
+
+ private void upload() {
+ final GlyphVector gv = getGlyphVector();
+ final Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext()));
+ final Rectangle2D bbox = normalize(origBBox);
+ final Point origin = new Point((int) -bbox.getMinX(),
+ (int) -bbox.getMinY());
+ final Rect rect = new Rect(0, 0, (int) bbox.getWidth(),
+ (int) bbox.getHeight(),
+ new TextData(null, origin, origBBox, unicodeID));
+ packer.add(rect);
+ glyphRectForTextureMapping = rect;
+ final Graphics2D g = getGraphics2D();
+ // OK, should now have an (x, y) for this rectangle; rasterize
+ // the glyph
+ final int strx = rect.x() + origin.x;
+ final int stry = rect.y() + origin.y;
+
+ // Clear out the area we're going to draw into
+ g.setComposite(AlphaComposite.Clear);
+ g.fillRect(rect.x(), rect.y(), rect.w(), rect.h());
+ g.setComposite(AlphaComposite.Src);
+
+ // Draw the string
+ renderDelegate.drawGlyphVector(g, gv, strx, stry);
+
+ if (false) {
+ final TextData data = (TextData) rect.getUserData();
+ // Draw a bounding box on the backing store
+ g.drawRect(strx - data.origOriginX(),
+ stry - data.origOriginY(),
+ (int) data.origRect().getWidth(),
+ (int) data.origRect().getHeight());
+ g.drawRect(strx - data.origin().x,
+ stry - data.origin().y,
+ rect.w(),
+ rect.h());
+ }
+
+ // Mark this region of the TextureRenderer as dirty
+ getBackingStore().markDirty(rect.x(), rect.y(), rect.w(),
+ rect.h());
+ // Re-register ourselves with our producer
+ producer.register(this);
+ }
+
+ private GlyphVector getGlyphVector() {
+ final GlyphVector gv = singleUnicodeGlyphVector;
+ if (gv != null) {
+ singleUnicodeGlyphVector = null; // Don't need this anymore
+ return gv;
+ }
+ singleUnicode[0] = (char) unicodeID;
+ return font.createGlyphVector(getFontRenderContext(), singleUnicode);
+ }
+ }
+
+ class GlyphProducer {
+ static final int undefined = -2;
+ final FontRenderContext fontRenderContext = null; // FIXME: Never initialized!
+ List
+
+ Each component ranges from 0.0f - 1.0f. The alpha component, if
+ used, does not need to be premultiplied into the color channels
+ as described in the documentation for {@link
+ com.jogamp.opengl.util.texture.Texture Texture}, although
+ premultiplied colors are used internally. The default color is
+ opaque white.
+
+ @param r the red component of the new color
+ @param g the green component of the new color
+ @param b the blue component of the new color
+ @param a the alpha component of the new color, 0.0f = completely
+ transparent, 1.0f = completely opaque
+
+ */
+ public void setColor(final float r, final float g, final float b, final float a) {
+ this.r = r * a;
+ this.g = g * a;
+ this.b = b * a;
+ this.a = a;
+
+ gl.glColor4f(this.r, this.g, this.b, this.a);
+ }
+
+ private float[] compArray;
+ /** Changes the current color of this TextureRenderer to the
+ supplied one. The default color is opaque white. See {@link
+ #setColor(float,float,float,float) setColor} for more details.
+
+ @param color the new color to use for rendering
+
+ */
+ public void setColor(final Color color) {
+ // Get color's RGBA components as floats in the range [0,1].
+ if (compArray == null) {
+ compArray = new float[4];
+ }
+ color.getRGBComponents(compArray);
+ setColor(compArray[0], compArray[1], compArray[2], compArray[3]);
+ }
+
+ /** Draws an orthographically projected rectangle containing all of
+ the underlying texture to the specified location on the
+ screen. All (x, y) coordinates are specified relative to the
+ lower left corner of either the texture image or the current
+ OpenGL drawable. This method is equivalent to
+
+ * A documented example of how to use this code is available
+ * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and
+ * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}.
+ *
+ * Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER}
+ * was added since 2.2.1.
+ *
+ * The source and binary location names are expected w/o suffixes which are
+ * resolved and appended using the given {@code srcSuffixOpt} and {@code binSuffixOpt}
+ * if not {@code null}, otherwise {@link #getFileSuffix(boolean, int)} determines the suffixes.
+ *
+ * Additionally, the binary resource is expected within a subfolder of
+ * The location is finally being resolved using the
+ * Convenient creation method for instantiating a complete {@link ShaderCode} object
+ * either from source code using {@link #create(GL2ES2, int, int, Class, String[])},
+ * or from a binary code using {@link #create(int, int, Class, int, String)},
+ * whatever is available first.
+ *
+ * The source and binary location names are expected w/o suffixes which are
+ * resolved and appended using {@link #getFileSuffix(boolean, int)}.
+ *
+ * Additionally, the binary resource is expected within a subfolder of
+ * The location is finally being resolved using the
+ * Example:
+ *
+ * Example:
+ * TextRenderer(font, false,
+ false)
.
+
+ @param font the font to render with
+ */
+ public TextRenderer(final GL2 gl, final Font font) {
+ this(gl, font, false, false, null, false);
+ }
+
+ /** Creates a new TextRenderer with the given font, using no
+ antialiasing or fractional metrics, and the default
+ RenderDelegate. If mipmap
is true, attempts to use
+ OpenGL's automatic mipmap generation for better smoothing when
+ rendering the TextureRenderer's contents at a distance.
+ Equivalent to TextRenderer(font, false, false)
.
+
+ @param font the font to render with
+ @param mipmap whether to attempt use of automatic mipmap generation
+ */
+ public TextRenderer(final GL2 gl, final Font font, final boolean mipmap) {
+ this(gl, font, false, false, null, mipmap);
+ }
+
+ /** Creates a new TextRenderer with the given Font, specified font
+ properties, and default RenderDelegate. The
+ antialiased
and useFractionalMetrics
+ flags provide control over the same properties at the Java 2D
+ level. No mipmap support is requested. Equivalent to
+ TextRenderer(font, antialiased, useFractionalMetrics,
+ null)
.
+
+ @param font the font to render with
+ @param antialiased whether to use antialiased fonts
+ @param useFractionalMetrics whether to use fractional font
+ metrics at the Java 2D level
+ */
+ public TextRenderer(final GL2 gl, final Font font, final boolean antialiased,
+ final boolean useFractionalMetrics) {
+ this(gl, font, antialiased, useFractionalMetrics, null, false);
+ }
+
+ /** Creates a new TextRenderer with the given Font, specified font
+ properties, and given RenderDelegate. The
+ antialiased
and useFractionalMetrics
+ flags provide control over the same properties at the Java 2D
+ level. The renderDelegate
provides more control
+ over the text rendered. No mipmap support is requested.
+
+ @param font the font to render with
+ @param antialiased whether to use antialiased fonts
+ @param useFractionalMetrics whether to use fractional font
+ metrics at the Java 2D level
+ @param renderDelegate the render delegate to use to draw the
+ text's bitmap, or null to use the default one
+ */
+ public TextRenderer(final GL2 gl, final Font font, final boolean antialiased,
+ final boolean useFractionalMetrics, final RenderDelegate renderDelegate) {
+ this(gl, font, antialiased, useFractionalMetrics, renderDelegate, false);
+ }
+
+ /** Creates a new TextRenderer with the given Font, specified font
+ properties, and given RenderDelegate. The
+ antialiased
and useFractionalMetrics
+ flags provide control over the same properties at the Java 2D
+ level. The renderDelegate
provides more control
+ over the text rendered. If mipmap
is true, attempts
+ to use OpenGL's automatic mipmap generation for better smoothing
+ when rendering the TextureRenderer's contents at a distance.
+
+ @param font the font to render with
+ @param antialiased whether to use antialiased fonts
+ @param useFractionalMetrics whether to use fractional font
+ metrics at the Java 2D level
+ @param renderDelegate the render delegate to use to draw the
+ text's bitmap, or null to use the default one
+ @param mipmap whether to attempt use of automatic mipmap generation
+ */
+ public TextRenderer(final GL2 gl, final Font font, final boolean antialiased,
+ final boolean useFractionalMetrics, RenderDelegate renderDelegate,
+ final boolean mipmap) {
+ this.gl = gl;
+ this.font = font;
+ this.antialiased = antialiased;
+ this.useFractionalMetrics = useFractionalMetrics;
+ this.mipmap = mipmap;
+
+ // FIXME: consider adjusting the size based on font size
+ // (it will already automatically resize if necessary)
+ packer = new RectanglePacker(new Manager(), kSize, kSize);
+
+ if (renderDelegate == null) {
+ renderDelegate = new DefaultRenderDelegate();
+ }
+
+ this.renderDelegate = renderDelegate;
+
+ mGlyphProducer = new GlyphProducer(font.getNumGlyphs());
+ }
+
+ /** Returns the bounding rectangle of the given String, assuming it
+ was rendered at the origin. See {@link #getBounds(CharSequence)
+ getBounds(CharSequence)}. */
+ public Rectangle2D getBounds(final String str) {
+ return getBounds((CharSequence) str);
+ }
+
+ /** Returns the bounding rectangle of the given CharSequence,
+ assuming it was rendered at the origin. The coordinate system of
+ the returned rectangle is Java 2D's, with increasing Y
+ coordinates in the downward direction. The relative coordinate
+ (0, 0) in the returned rectangle corresponds to the baseline of
+ the leftmost character of the rendered string, in similar
+ fashion to the results returned by, for example, {@link
+ GlyphVector#getVisualBounds}. Most applications
+ will use only the width and height of the returned Rectangle for
+ the purposes of centering or justifying the String. It is not
+ specified which Java 2D bounds ({@link
+ GlyphVector#getVisualBounds getVisualBounds},
+ {@link GlyphVector#getPixelBounds getPixelBounds},
+ etc.) the returned bounds correspond to, although every effort
+ is made to ensure an accurate bound. */
+ public Rectangle2D getBounds(final CharSequence str) {
+ // FIXME: this should be more optimized and use the glyph cache
+ final Rect r = stringLocations.get(str);
+
+ if (r != null) {
+ final TextData data = (TextData) r.getUserData();
+
+ // Reconstitute the Java 2D results based on the cached values
+ return new Rectangle2D.Double(-data.origin().x, -data.origin().y,
+ r.w(), r.h());
+ }
+
+ // Must return a Rectangle compatible with the layout algorithm --
+ // must be idempotent
+ return normalize(renderDelegate.getBounds(str, font,
+ getFontRenderContext()));
+ }
+
+ /** Returns the Font this renderer is using. */
+ public Font getFont() {
+ return font;
+ }
+
+ /** Returns a FontRenderContext which can be used for external
+ text-related size computations. This object should be considered
+ transient and may become invalidated between {@link
+ #beginRendering beginRendering} / {@link #endRendering
+ endRendering} pairs. */
+ public FontRenderContext getFontRenderContext() {
+ if (cachedFontRenderContext == null) {
+ cachedFontRenderContext = getGraphics2D().getFontRenderContext();
+ }
+
+ return cachedFontRenderContext;
+ }
+
+ /** Begins rendering of 2D text in 3D with this {@link TextRenderer
+ TextRenderer} into the current OpenGL drawable. Assumes the end
+ user is responsible for setting up the modelview and projection
+ matrices, and will render text using the {@link #draw3D draw3D}
+ method. This method pushes some OpenGL state bits, binds and
+ enables the internal OpenGL texture object, sets the texture
+ environment mode to GL_MODULATE, and changes the current color
+ to the last color set with this TextRenderer via {@link
+ #setColor setColor}.
+
+
+ */
+ public void begin3DRendering() {
+ beginRendering(false, 0, 0, false);
+ }
+
+ /** Changes the current color of this TextRenderer to the supplied
+ one. The default color is opaque white.
+
+ @param color the new color to use for rendering text
+
+ */
+ public void setColor(final Color color) {
+ final boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) &&
+ color.equals(cachedColor));
+
+ if (!noNeedForFlush) {
+ flushGlyphPipeline();
+ }
+
+ getBackingStore().setColor(color);
+ haveCachedColor = true;
+ cachedColor = color;
+ }
+
+ /** Changes the current color of this TextRenderer to the supplied
+ one, where each component ranges from 0.0f - 1.0f. The alpha
+ component, if used, does not need to be premultiplied into the
+ color channels as described in the documentation for {@link
+ com.jogamp.opengl.util.texture.Texture Texture}, although
+ premultiplied colors are used internally. The default color is
+ opaque white.
+
+ @param r the red component of the new color
+ @param g the green component of the new color
+ @param b the blue component of the new color
+ @param a the alpha component of the new color, 0.0f = completely
+ transparent, 1.0f = completely opaque
+
+ */
+ public void setColor(final float r, final float g, final float b, final float a)
+ {
+ final boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) &&
+ (r == cachedR) && (g == cachedG) && (b == cachedB) &&
+ (a == cachedA));
+
+ if (!noNeedForFlush) {
+ flushGlyphPipeline();
+ }
+
+ getBackingStore().setColor(r, g, b, a);
+ haveCachedColor = true;
+ cachedR = r;
+ cachedG = g;
+ cachedB = b;
+ cachedA = a;
+ cachedColor = null;
+ }
+
+ /** Draws the supplied CharSequence at the desired location using
+ the renderer's current color. The baseline of the leftmost
+ character is at position (x, y) specified in OpenGL coordinates,
+ where the origin is at the lower-left of the drawable and the Y
+ coordinate increases in the upward direction.
+
+ @param str the string to draw
+ @param x the x coordinate at which to draw
+ @param y the y coordinate at which to draw
+
+ */
+ public void draw(final CharSequence str, final int x, final int y) {
+ draw3D(str, x, y, 0, 1);
+ }
+
+ /** Draws the supplied String at the desired location using the
+ renderer's current color. See {@link #draw(CharSequence, int,
+ int) draw(CharSequence, int, int)}. */
+ public void draw(final String str, final int x, final int y) {
+ draw3D(str, x, y, 0, 1);
+ }
+
+ /** Draws the supplied CharSequence at the desired 3D location using
+ the renderer's current color. The baseline of the leftmost
+ character is placed at position (x, y, z) in the current
+ coordinate system.
+
+ @param str the string to draw
+ @param x the x coordinate at which to draw
+ @param y the y coordinate at which to draw
+ @param z the z coordinate at which to draw
+ @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle
+
+ */
+ public void draw3D(final CharSequence str, final float x, final float y, final float z,
+ final float scaleFactor) {
+ internal_draw3D(str, x, y, z, scaleFactor);
+ }
+
+ /** Draws the supplied String at the desired 3D location using the
+ renderer's current color. See {@link #draw3D(CharSequence,
+ float, float, float, float) draw3D(CharSequence, float, float,
+ float, float)}. */
+ public void draw3D(final String str, final float x, final float y, final float z, final float scaleFactor) {
+ internal_draw3D(str, x, y, z, scaleFactor);
+ }
+
+ /** Returns the pixel width of the given character. */
+ public float getCharWidth(final char inChar) {
+ return mGlyphProducer.getGlyphPixelWidth(inChar);
+ }
+
+ /** Causes the TextRenderer to flush any internal caches it may be
+ maintaining and draw its rendering results to the screen. This
+ should be called after each call to draw() if you are setting
+ OpenGL state such as the modelview matrix between calls to
+ draw(). */
+ public void flush() {
+ flushGlyphPipeline();
+ }
+
+ /** Ends a render cycle with this {@link TextRenderer TextRenderer}.
+ Restores the projection and modelview matrices as well as
+ several OpenGL state bits. Should be paired with {@link
+ #beginRendering beginRendering}.
+
+
+ */
+ public void endRendering() {
+ endRendering(true);
+ }
+
+ /** Ends a 3D render cycle with this {@link TextRenderer TextRenderer}.
+ Restores several OpenGL state bits. Should be paired with {@link
+ #begin3DRendering begin3DRendering}.
+
+
+ */
+ public void end3DRendering() {
+ endRendering(false);
+ }
+
+ /** Disposes of all resources this TextRenderer is using. It is not
+ valid to use the TextRenderer after this method is called.
+
+
+ */
+ public void dispose() {
+ if( null != mPipelinedQuadRenderer ) {
+ mPipelinedQuadRenderer.dispose();
+ }
+ packer.dispose();
+ packer = null;
+ cachedBackingStore = null;
+ cachedGraphics = null;
+ cachedFontRenderContext = null;
+
+ if (dbgFrame != null) {
+ dbgFrame.dispose();
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private static Rectangle2D preNormalize(final Rectangle2D src) {
+ // Need to round to integer coordinates
+ // Also give ourselves a little slop around the reported
+ // bounds of glyphs because it looks like neither the visual
+ // nor the pixel bounds works perfectly well
+ final int minX = (int) Math.floor(src.getMinX()) - 1;
+ final int minY = (int) Math.floor(src.getMinY()) - 1;
+ final int maxX = (int) Math.ceil(src.getMaxX()) + 1;
+ final int maxY = (int) Math.ceil(src.getMaxY()) + 1;
+ return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+ }
+
+
+ private Rectangle2D normalize(final Rectangle2D src) {
+ // Give ourselves a boundary around each entity on the backing
+ // store in order to prevent bleeding of nearby Strings due to
+ // the fact that we use linear filtering
+
+ // NOTE that this boundary is quite heuristic and is related
+ // to how far away in 3D we may view the text --
+ // heuristically, 1.5% of the font's height
+ final int boundary = (int) Math.max(1, 0.015 * font.getSize());
+
+ return new Rectangle2D.Double((int) Math.floor(src.getMinX() - boundary),
+ (int) Math.floor(src.getMinY() - boundary),
+ (int) Math.ceil(src.getWidth() + 2 * boundary),
+ (int) Math.ceil(src.getHeight()) + 2 * boundary);
+ }
+
+ private TextureRenderer getBackingStore() {
+ final TextureRenderer renderer = (TextureRenderer) packer.getBackingStore();
+
+ if (renderer != cachedBackingStore) {
+ // Backing store changed since last time; discard any cached Graphics2D
+ if (cachedGraphics != null) {
+ cachedGraphics.dispose();
+ cachedGraphics = null;
+ cachedFontRenderContext = null;
+ }
+
+ cachedBackingStore = renderer;
+ }
+
+ return cachedBackingStore;
+ }
+
+ private Graphics2D getGraphics2D() {
+ final TextureRenderer renderer = getBackingStore();
+
+ if (cachedGraphics == null) {
+ cachedGraphics = renderer.createGraphics();
+
+ // Set up composite, font and rendering hints
+ cachedGraphics.setComposite(AlphaComposite.Src);
+ cachedGraphics.setColor(Color.WHITE);
+ cachedGraphics.setFont(font);
+ cachedGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+ (antialiased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
+ : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF));
+ cachedGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
+ (useFractionalMetrics
+ ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
+ : RenderingHints.VALUE_FRACTIONALMETRICS_OFF));
+ }
+
+ return cachedGraphics;
+ }
+
+ private void beginRendering(final boolean ortho, final int width, final int height,
+ final boolean disableDepthTestForOrtho) {
+
+ assert !ortho;
+
+ inBeginEndPair = true;
+
+ getBackingStore().begin3DRendering();
+
+ // Push client attrib bits used by the pipelined quad renderer
+ gl.glPushClientAttrib((int) gl.GL_ALL_CLIENT_ATTRIB_BITS());
+
+ if (!haveMaxSize) {
+ // Query OpenGL for the maximum texture size and set it in the
+ // RectanglePacker to keep it from expanding too large
+ final int[] sz = new int[1];
+ gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE(), sz);
+ packer.setMaxSize(sz[0], sz[0]);
+ haveMaxSize = true;
+ }
+
+ if (needToResetColor && haveCachedColor) {
+ if (cachedColor == null) {
+ getBackingStore().setColor(cachedR, cachedG, cachedB, cachedA);
+ } else {
+ getBackingStore().setColor(cachedColor);
+ }
+
+ needToResetColor = false;
+ }
+
+ // Disable future attempts to use mipmapping if TextureRenderer
+ // doesn't support it
+ if (mipmap && !getBackingStore().isUsingAutoMipmapGeneration()) {
+ mipmap = false;
+ }
+ }
+
+ /**
+ * emzic: here the call to glBindBuffer crashes on certain graphicscard/driver combinations
+ * this is why the ugly try-catch block has been added, which falls back to the old textrenderer
+ *
+ * @param ortho
+
+ */
+ private void endRendering(final boolean ortho) {
+ assert !ortho;
+
+ flushGlyphPipeline();
+
+ inBeginEndPair = false;
+
+ // Pop client attrib bits used by the pipelined quad renderer
+ gl.glPopClientAttrib();
+
+ // The OpenGL spec is unclear about whether this changes the
+ // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER
+ // binding
+ gl.glBindBuffer(gl.GL_ARRAY_BUFFER(), 0);
+
+ getBackingStore().end3DRendering();
+
+ if (++numRenderCycles >= CYCLES_PER_FLUSH) {
+ numRenderCycles = 0;
+
+ clearUnusedEntries();
+ }
+ }
+
+ private void clearUnusedEntries() {
+ final Listalpha
is true, allocates an alpha
+ channel in the backing store image. No mipmap support is
+ requested.
+
+ @param width the width of the texture to render into
+ @param height the height of the texture to render into
+ @param alpha whether to allocate an alpha channel for the texture
+ */
+ public TextureRenderer(final GL2 gl, final int width, final int height, final boolean alpha) {
+ this(gl, width, height, alpha, false);
+ }
+
+ /** Creates a new renderer with backing store of the specified width
+ and height. If alpha
is true, allocates an alpha channel in the
+ backing store image. If mipmap
is true, attempts to use OpenGL's
+ automatic mipmap generation for better smoothing when rendering
+ the TextureRenderer's contents at a distance.
+
+ @param width the width of the texture to render into
+ @param height the height of the texture to render into
+ @param alpha whether to allocate an alpha channel for the texture
+ @param mipmap whether to attempt use of automatic mipmap generation
+ */
+ public TextureRenderer(final GL2 gl, final int width, final int height, final boolean alpha, final boolean mipmap) {
+ this(gl, width, height, alpha, false, mipmap);
+ }
+
+ // Internal constructor to avoid confusion since alpha only makes
+ // sense when intensity is not set
+ private TextureRenderer(final GL2 gl, final int width, final int height, final boolean alpha, final boolean intensity, final boolean mipmap) {
+ this.gl = gl;
+ this.mipmap = mipmap;
+ init(width, height);
+ }
+
+ /** Creates a new renderer with a special kind of backing store
+ which acts only as an alpha channel. No mipmap support is
+ requested. Internally, this associates a GL_INTENSITY OpenGL
+ texture with the backing store. */
+ public static TextureRenderer createAlphaOnlyRenderer(final GL2 gl, final int width, final int height) {
+ return createAlphaOnlyRenderer(gl, width, height, false);
+ }
+
+ /** Creates a new renderer with a special kind of backing store
+ which acts only as an alpha channel. If mipmap
is
+ true, attempts to use OpenGL's automatic mipmap generation for
+ better smoothing when rendering the TextureRenderer's contents
+ at a distance. Internally, this associates a GL_INTENSITY OpenGL
+ texture with the backing store. */
+ public static TextureRenderer createAlphaOnlyRenderer(final GL2 gl, final int width, final int height, final boolean mipmap) {
+ return new TextureRenderer(gl, width, height, false, true, mipmap);
+ }
+
+ /** Returns the width of the backing store of this renderer.
+
+ @return the width of the backing store of this renderer
+ */
+ public int getWidth() {
+ return image.getWidth();
+ }
+
+ /** Returns the height of the backing store of this renderer.
+
+ @return the height of the backing store of this renderer
+ */
+ public int getHeight() {
+ return image.getHeight();
+ }
+
+ /** Returns the size of the backing store of this renderer in a
+ newly-allocated {@link Dimension Dimension} object.
+
+ @return the size of the backing store of this renderer
+ */
+ public Dimension getSize() {
+ return getSize(null);
+ }
+
+ /** Returns the size of the backing store of this renderer. Uses the
+ {@link Dimension Dimension} object if one is supplied,
+ or allocates a new one if null is passed.
+
+ @param d a {@link Dimension Dimension} object in which
+ to store the results, or null to allocate a new one
+
+ @return the size of the backing store of this renderer
+ */
+ public Dimension getSize(Dimension d) {
+ if (d == null)
+ d = new Dimension();
+ d.setSize(image.getWidth(), image.getHeight());
+ return d;
+ }
+
+ /** Sets the size of the backing store of this renderer. This may
+ cause the OpenGL texture object associated with this renderer to
+ be invalidated; it is not recommended to cache this texture
+ object outside this class but to instead call {@link #getTexture
+ getTexture} when it is needed.
+
+ @param width the new width of the backing store of this renderer
+ @param height the new height of the backing store of this renderer
+
+ */
+ public void setSize(final int width, final int height) {
+ init(width, height);
+ }
+
+ /** Sets the size of the backing store of this renderer. This may
+ cause the OpenGL texture object associated with this renderer to
+ be invalidated.
+
+ @param d the new size of the backing store of this renderer
+
+ */
+ public void setSize(final Dimension d) {
+ setSize(d.width, d.height);
+ }
+
+ /** Sets whether smoothing is enabled for the OpenGL texture; if so,
+ uses GL_LINEAR interpolation for the minification and
+ magnification filters. Defaults to true. Changes to this setting
+ will not take effect until the next call to {@link
+ #beginRendering beginRendering}.
+
+ @param smoothing whether smoothing is enabled for the OpenGL texture
+ */
+ public void setSmoothing(final boolean smoothing) {
+ this.smoothing = smoothing;
+ smoothingChanged = true;
+ }
+
+ /** Returns whether smoothing is enabled for the OpenGL texture; see
+ {@link #setSmoothing setSmoothing}. Defaults to true.
+
+ @return whether smoothing is enabled for the OpenGL texture
+ */
+ public boolean getSmoothing() {
+ return smoothing;
+ }
+
+ /** Creates a {@link Graphics2D Graphics2D} instance for
+ rendering to the backing store of this renderer. The returned
+ object should be disposed of using the normal {@link
+ java.awt.Graphics#dispose() Graphics.dispose()} method once it
+ is no longer being used.
+
+ @return a new {@link Graphics2D Graphics2D} object for
+ rendering into the backing store of this renderer
+ */
+ public Graphics2D createGraphics() {
+ return image.createGraphics();
+ }
+
+ /** Returns the underlying Java 2D {@link Image Image}
+ being rendered into. */
+ public Image getImage() {
+ return image;
+ }
+
+ /** Marks the given region of the TextureRenderer as dirty. This
+ region, and any previously set dirty regions, will be
+ automatically synchronized with the underlying Texture during
+ the next {@link #getTexture getTexture} operation, at which
+ point the dirty region will be cleared. It is not necessary for
+ an OpenGL context to be current when this method is called.
+
+ @param x the x coordinate (in Java 2D coordinates -- relative to
+ upper left) of the region to update
+ @param y the y coordinate (in Java 2D coordinates -- relative to
+ upper left) of the region to update
+ @param width the width of the region to update
+ @param height the height of the region to update
+ */
+ public void markDirty(final int x, final int y, final int width, final int height) {
+ final Rectangle curRegion = new Rectangle(x, y, width, height);
+ if (dirtyRegion == null) {
+ dirtyRegion = curRegion;
+ } else {
+ dirtyRegion.add(curRegion);
+ }
+ }
+
+ /** Returns the underlying OpenGL Texture object associated with
+ this renderer, synchronizing any dirty regions of the
+ TextureRenderer with the underlying OpenGL texture.
+
+
+ */
+ public Texture getTexture() {
+ if (dirtyRegion != null) {
+ sync(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
+ dirtyRegion = null;
+ }
+
+ ensureTexture();
+ return texture;
+ }
+
+ /** Disposes all resources associated with this renderer. It is not
+ valid to use this renderer after calling this method.
+
+
+ */
+ public void dispose() {
+ if (texture != null) {
+ texture.destroy(gl);
+ texture = null;
+ }
+ if (image != null) {
+ image.flush();
+ image = null;
+ }
+ }
+
+ /** Convenience method which assists in rendering portions of the
+ OpenGL texture to the screen as 2D quads in 3D space. Pushes
+ OpenGL state (GL_ENABLE_BIT); disables lighting; and enables the
+ texture in this renderer. Does not modify the depth test, back-face
+ culling, lighting, or the modelview or projection matrices. The
+ user is responsible for setting up the view matrices for correct
+ results of {@link #draw3DRect draw3DRect}. {@link
+ #end3DRendering} must be used in conjunction with this method to
+ restore all OpenGL states.
+
+
+ */
+ public void begin3DRendering() {
+ beginRendering();
+ }
+
+ /** Changes the color of the polygons, and therefore the drawn
+ images, this TextureRenderer produces. Use of this method is
+ optional. The TextureRenderer uses the GL_MODULATE texture
+ environment mode, which causes the portions of the rendered
+ texture to be multiplied by the color of the rendered
+ polygons. The polygon color can be varied to achieve effects
+ like tinting of the overall output or fading in and out by
+ changing the alpha of the color. drawOrthoRect(screenx, screeny, 0, 0, getWidth(),
+ getHeight());
.
+
+ @param screenx the on-screen x coordinate at which to draw the rectangle
+ @param screeny the on-screen y coordinate (relative to lower left) at
+ which to draw the rectangle
+
+
+ */
+ public void drawOrthoRect(final int screenx, final int screeny) {
+ drawOrthoRect(screenx, screeny, 0, 0, getWidth(), getHeight());
+ }
+
+ /** Draws an orthographically projected rectangle of the underlying
+ texture to the specified location on the screen. All (x, y)
+ coordinates are specified relative to the lower left corner of
+ either the texture image or the current OpenGL drawable.
+
+ @param screenx the on-screen x coordinate at which to draw the rectangle
+ @param screeny the on-screen y coordinate (relative to lower left) at
+ which to draw the rectangle
+ @param texturex the x coordinate of the pixel in the texture of
+ the lower left portion of the rectangle to draw
+ @param texturey the y coordinate of the pixel in the texture
+ (relative to lower left) of the lower left portion of the
+ rectangle to draw
+ @param width the width of the rectangle to draw
+ @param height the height of the rectangle to draw
+
+
+ */
+ public void drawOrthoRect(final int screenx, final int screeny,
+ final int texturex, final int texturey,
+ final int width, final int height) {
+ draw3DRect(screenx, screeny, 0, texturex, texturey, width, height, 1);
+ }
+
+ /** Draws a rectangle of the underlying texture to the specified 3D
+ location. In the current coordinate system, the lower left
+ corner of the rectangle is placed at (x, y, z), and the upper
+ right corner is placed at (x + width * scaleFactor, y + height *
+ scaleFactor, z). The lower left corner of the sub-rectangle of
+ the texture is (texturex, texturey) and the upper right corner
+ is (texturex + width, texturey + height). For back-face culling
+ purposes, the rectangle is drawn with counterclockwise
+ orientation of the vertices when viewed from the front.
+
+ @param x the x coordinate at which to draw the rectangle
+ @param y the y coordinate at which to draw the rectangle
+ @param z the z coordinate at which to draw the rectangle
+ @param texturex the x coordinate of the pixel in the texture of
+ the lower left portion of the rectangle to draw
+ @param texturey the y coordinate of the pixel in the texture
+ (relative to lower left) of the lower left portion of the
+ rectangle to draw
+ @param width the width in texels of the rectangle to draw
+ @param height the height in texels of the rectangle to draw
+ @param scaleFactor the scale factor to apply (multiplicatively)
+ to the size of the drawn rectangle
+
+
+ */
+ public void draw3DRect(final float x, final float y, final float z,
+ final int texturex, final int texturey,
+ final int width, final int height,
+ final float scaleFactor) {
+ // TODO: use VBA instead
+ final Texture texture = getTexture();
+ final TextureCoords coords = texture.getSubImageTexCoords(texturex, texturey,
+ texturex + width,
+ texturey + height);
+ gl.glBegin(gl.GL_QUADS());
+ gl.glTexCoord2f(coords.left(), coords.bottom());
+ gl.glVertex3f(x, y, z);
+ gl.glTexCoord2f(coords.right(), coords.bottom());
+ gl.glVertex3f(x + width * scaleFactor, y, z);
+ gl.glTexCoord2f(coords.right(), coords.top());
+ gl.glVertex3f(x + width * scaleFactor, y + height * scaleFactor, z);
+ gl.glTexCoord2f(coords.left(), coords.top());
+ gl.glVertex3f(x, y + height * scaleFactor, z);
+ gl.glEnd();
+ }
+
+ /** Convenience method which assists in rendering portions of the
+ OpenGL texture to the screen as 2D quads in 3D space. Must be
+ used if {@link #begin3DRendering} is used to set up the
+ rendering stage for this overlay.
+
+
+ */
+ public void end3DRendering() {
+ endRendering();
+ }
+
+ /** Indicates whether automatic mipmap generation is in use for this
+ TextureRenderer. The result of this method may change from true
+ to false if it is discovered during allocation of the
+ TextureRenderer's backing store that automatic mipmap generation
+ is not supported at the OpenGL level. */
+ public boolean isUsingAutoMipmapGeneration() {
+ return mipmap;
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void beginRendering() {
+ final int attribBits = gl.GL_ENABLE_BIT() | gl.GL_TEXTURE_BIT() | gl.GL_COLOR_BUFFER_BIT();
+ gl.glPushAttrib(attribBits);
+ gl.glDisable(gl.GL_LIGHTING());
+ gl.glEnable(gl.GL_BLEND());
+ gl.glBlendFunc(gl.GL_ONE(), gl.GL_ONE_MINUS_SRC_ALPHA());
+ final Texture texture = getTexture();
+ texture.enable(gl);
+ texture.bind(gl);
+ gl.glTexEnvi(gl.GL_TEXTURE_ENV(), gl.GL_TEXTURE_ENV_MODE(), gl.GL_MODULATE());
+ // Change polygon color to last saved
+ gl.glColor4f(r, g, b, a);
+ if (smoothingChanged) {
+ smoothingChanged = false;
+ if (smoothing) {
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MAG_FILTER(), gl.GL_LINEAR());
+ if (mipmap) {
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MIN_FILTER(), gl.GL_LINEAR_MIPMAP_LINEAR());
+ } else {
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MIN_FILTER(), gl.GL_LINEAR());
+ }
+ } else {
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MIN_FILTER(), gl.GL_NEAREST());
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MAG_FILTER(), gl.GL_NEAREST());
+ }
+ }
+ }
+
+ private void endRendering() {
+ final Texture texture = getTexture();
+ texture.disable(gl);
+ gl.glPopAttrib();
+ }
+
+ private void init(final int width, final int height) {
+ // Discard previous BufferedImage if any
+ if (image != null) {
+ image.flush();
+ image = null;
+ }
+
+ // Infer the internal format if not an intensity texture
+ final int internalFormat = gl.GL_INTENSITY();
+ final int imageType = BufferedImage.TYPE_BYTE_GRAY;
+ image = new BufferedImage(width, height, imageType);
+ // Always realllocate the TextureData associated with this
+ // BufferedImage; it's just a reference to the contents but we
+ // need it in order to update sub-regions of the underlying
+ // texture
+ textureData = new AWTTextureData(gl, internalFormat, mipmap, image);
+ // For now, always reallocate the underlying OpenGL texture when
+ // the backing store size changes
+ mustReallocateTexture = true;
+ }
+
+ /** Synchronizes the specified region of the backing store down to
+ the underlying OpenGL texture. If {@link #markDirty markDirty}
+ is used instead to indicate the regions that are out of sync,
+ this method does not need to be called.
+
+ @param x the x coordinate (in Java 2D coordinates -- relative to
+ upper left) of the region to update
+ @param y the y coordinate (in Java 2D coordinates -- relative to
+ upper left) of the region to update
+ @param width the width of the region to update
+ @param height the height of the region to update
+
+
+ */
+ private void sync(final int x, final int y, final int width, final int height) {
+ // Force allocation if necessary
+ final boolean canSkipUpdate = ensureTexture();
+
+ if (!canSkipUpdate) {
+ // Update specified region.
+ // NOTE that because BufferedImage-based TextureDatas now don't
+ // do anything to their contents, the coordinate systems for
+ // OpenGL and Java 2D actually line up correctly for
+ // updateSubImage calls, so we don't need to do any argument
+ // conversion here (i.e., flipping the Y coordinate).
+ texture.updateSubImage(gl, textureData, 0, x, y, x, y, width, height);
+ }
+ }
+
+ // Returns true if the texture was newly allocated, false if not
+ private boolean ensureTexture() {
+ if (mustReallocateTexture) {
+ if (texture != null) {
+ texture.destroy(gl);
+ texture = null;
+ }
+ mustReallocateTexture = false;
+ }
+
+ if (texture == null) {
+ texture = new Texture(gl, textureData);
+ if (mipmap && !texture.isUsingAutoMipmapGeneration()) {
+ // Only try this once
+ texture.destroy(gl);
+ mipmap = false;
+ textureData.setMipmap(false);
+ texture = new Texture(gl, textureData);
+ }
+
+ if (!smoothing) {
+ // The TextureIO classes default to GL_LINEAR filtering
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MIN_FILTER(), gl.GL_NEAREST());
+ texture.setTexParameteri(gl, gl.GL_TEXTURE_MAG_FILTER(), gl.GL_NEAREST());
+ }
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderCode.java b/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderCode.java
new file mode 100644
index 00000000..66f000b1
--- /dev/null
+++ b/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderCode.java
@@ -0,0 +1,1448 @@
+/**
+ * Copyright 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package net.opengrabeso.opengl.util.glsl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.jogamp.opengl.*;
+
+
+import jogamp.opengl.Debug;
+
+import com.jogamp.common.net.Uri;
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.util.IOUtil;
+import com.jogamp.common.util.VersionNumber;
+
+/**
+ * Convenient shader code class to use and instantiate vertex or fragment programs.
+ * {@value}
*/
+ public static final String SUFFIX_VERTEX_SOURCE = "vp" ;
+
+ /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: {@value}
*/
+ public static final String SUFFIX_VERTEX_BINARY = "bvp" ;
+
+ /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: {@value}
*/
+ public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ;
+
+ /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: {@value}
*/
+ public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ;
+
+ /**
+ * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in source code: {@value}
+ * @since 2.3.2
+ */
+ public static final String SUFFIX_COMPUTE_SOURCE = "cp" ;
+
+ /**
+ * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in binary: {@value}
+ * @since 2.3.2
+ */
+ public static final String SUFFIX_COMPUTE_BINARY = "bcp" ;
+
+ /**
+ * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: {@value}
+ * @since 2.2.1
+ */
+ public static final String SUFFIX_TESS_CONTROL_SOURCE = "tcp" ;
+
+ /**
+ * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: {@value}
+ * @since 2.2.1
+ */
+ public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ;
+
+ /**
+ * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: {@value}
+ * @since 2.2.1
+ */
+ public static final String SUFFIX_TESS_EVALUATION_SOURCE = "tep" ;
+
+ /**
+ * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: {@value}
+ * @since 2.2.1
+ */
+ public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ;
+
+ /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: {@value}
*/
+ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ;
+
+ /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: {@value}
*/
+ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ;
+
+ /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: {@value}
*/
+ public static final String SUB_PATH_NVIDIA = "nvidia" ;
+
+ /**
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param source CharSequence array containing the shader sources, organized as source[count][strings-per-shader]
.
+ * May be either an immutable String
- or mutable StringBuilder
array.
+ *
+ * @throws IllegalArgumentException if count
and source.length
do not match
+ */
+ public ShaderCode(final int type, final int count, final CharSequence[][] source) {
+ if(source.length != count) {
+ throw new IllegalArgumentException("shader number ("+count+") and sourceFiles array ("+source.length+") of different lenght.");
+ }
+ switch (type) {
+ case GL2ES2.GL_VERTEX_SHADER:
+ case GL2ES2.GL_FRAGMENT_SHADER:
+ case GL3ES3.GL_GEOMETRY_SHADER:
+ case GL3ES3.GL_TESS_CONTROL_SHADER:
+ case GL3ES3.GL_TESS_EVALUATION_SHADER:
+ case GL3ES3.GL_COMPUTE_SHADER:
+ break;
+ default:
+ throw new GLException("Unknown shader type: "+type);
+ }
+ shaderSource = source;
+ shaderBinaryFormat = -1;
+ shaderBinary = null;
+ shaderType = type;
+ shader = Buffers.newDirectIntBuffer(count);
+ id = getNextID();
+
+ if(DEBUG_CODE) {
+ System.out.println("Created: "+toString());
+ // dumpShaderSource(System.out); // already done in readShaderSource
+ }
+ }
+
+ /**
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param binary binary buffer containing the shader binaries,
+ */
+ public ShaderCode(final int type, final int count, final int binFormat, final Buffer binary) {
+ switch (type) {
+ case GL2ES2.GL_VERTEX_SHADER:
+ case GL2ES2.GL_FRAGMENT_SHADER:
+ case GL3ES3.GL_GEOMETRY_SHADER:
+ case GL3ES3.GL_TESS_CONTROL_SHADER:
+ case GL3ES3.GL_TESS_EVALUATION_SHADER:
+ case GL3ES3.GL_COMPUTE_SHADER:
+ break;
+ default:
+ throw new GLException("Unknown shader type: "+type);
+ }
+ shaderSource = null;
+ shaderBinaryFormat = binFormat;
+ shaderBinary = binary;
+ shaderType = type;
+ shader = Buffers.newDirectIntBuffer(count);
+ id = getNextID();
+ }
+
+ /**
+ * Creates a complete {@link ShaderCode} object while reading all shader source of sourceFiles
,
+ * which location is resolved using the context
class, see {@link #readShaderSource(Class, String)}.
+ *
+ * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed.
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param context class used to help resolving the source location
+ * @param sourceFiles array of source locations, organized as sourceFiles[count]
-> shaderSources[count][1]
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ *
+ * @throws IllegalArgumentException if count
and sourceFiles.length
do not match
+ * @see #readShaderSource(Class, String)
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class> context,
+ final String[] sourceFiles, final boolean mutableStringBuilder) {
+ if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) {
+ return null;
+ }
+
+ CharSequence[][] shaderSources = null;
+ if(null!=sourceFiles) {
+ // sourceFiles.length and count is validated in ctor
+ shaderSources = new CharSequence[sourceFiles.length][1];
+ for(int i=0; isourceFiles[count]
-> shaderSources[count][1]
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ *
+ * @throws IllegalArgumentException if count
and sourceFiles.length
do not match
+ * @see #readShaderSource(Uri, boolean)
+ * @since 2.3.2
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final int count,
+ final Uri[] sourceLocations, final boolean mutableStringBuilder) {
+ if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) {
+ return null;
+ }
+
+ CharSequence[][] shaderSources = null;
+ if(null!=sourceLocations) {
+ // sourceFiles.length and count is validated in ctor
+ shaderSources = new CharSequence[sourceLocations.length][1];
+ for(int i=0; icontext
class, see {@link #readShaderBinary(Class, String)}.
+ *
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param context class used to help resolving the source location
+ * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}.
+ * @param sourceFiles array of source locations, organized as sourceFiles[count]
+ *
+ * @see #readShaderBinary(Class, String)
+ * @see ShaderUtil#getShaderBinaryFormats(GL)
+ */
+ public static ShaderCode create(final int type, final int count, final Class> context, int binFormat, final String binaryFile) {
+ ByteBuffer shaderBinary = null;
+ if(null!=binaryFile && 0<=binFormat) {
+ try {
+ shaderBinary = readShaderBinary(context, binaryFile);
+ } catch (final IOException ioe) {
+ throw new RuntimeException("readShaderBinary("+binaryFile+") error: ", ioe);
+ }
+ if(null == shaderBinary) {
+ binFormat = -1;
+ }
+ }
+ if(null==shaderBinary) {
+ return null;
+ }
+ return new ShaderCode(type, count, binFormat, shaderBinary);
+ }
+
+ /**
+ * Creates a complete {@link ShaderCode} object while reading the shader binary from {@link Uri} binLocations
+ * via {@link #readShaderBinary(Uri)}.
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}.
+ * @param binLocations {@link Uri} binary location
+ *
+ * @see #readShaderBinary(Uri)
+ * @see ShaderUtil#getShaderBinaryFormats(GL)
+ * @since 2.3.2
+ */
+ public static ShaderCode create(final int type, final int count, int binFormat, final Uri binLocation) {
+ ByteBuffer shaderBinary = null;
+ if(null!=binLocation && 0<=binFormat) {
+ try {
+ shaderBinary = readShaderBinary(binLocation);
+ } catch (final IOException ioe) {
+ throw new RuntimeException("readShaderBinary("+binLocation+") error: ", ioe);
+ }
+ if(null == shaderBinary) {
+ binFormat = -1;
+ }
+ }
+ if(null==shaderBinary) {
+ return null;
+ }
+ return new ShaderCode(type, count, binFormat, shaderBinary);
+ }
+
+ /**
+ * Returns a unique suffix for shader resources as follows:
+ *
+ *
+ * @param binary true for a binary resource, false for a source resource
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ *
+
+ *
+ * @see #create(GL2ES2, int, Class, String, String, String, boolean)
+ */
+ public static String getFileSuffix(final boolean binary, final int type) {
+ switch (type) {
+ case GL2ES2.GL_VERTEX_SHADER:
+ return binary?SUFFIX_VERTEX_BINARY:SUFFIX_VERTEX_SOURCE;
+ case GL2ES2.GL_FRAGMENT_SHADER:
+ return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE;
+ case GL3ES3.GL_GEOMETRY_SHADER:
+ return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE;
+ case GL3ES3.GL_TESS_CONTROL_SHADER:
+ return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE;
+ case GL3ES3.GL_TESS_EVALUATION_SHADER:
+ return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE;
+ case GL3ES3.GL_COMPUTE_SHADER:
+ return binary?SUFFIX_COMPUTE_BINARY:SUFFIX_COMPUTE_SOURCE;
+ default:
+ throw new GLException("illegal shader type: "+type);
+ }
+ }
+
+ /**
+ * Returns a unique relative path for binary shader resources as follows:
+ *
+ *
+ *
+ *
+ *
+
+ *
+ * @see #create(GL2ES2, int, Class, String, String, String, boolean)
+ */
+ public static String getBinarySubPath(final int binFormat) {
+ switch (binFormat) {
+ case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV:
+ return SUB_PATH_NVIDIA;
+ default:
+ throw new GLException("unsupported binary format: "+binFormat);
+ }
+ }
+
+ /**
+ * Convenient creation method for instantiating a complete {@link ShaderCode} object
+ * either from source code using {@link #create(GL2ES2, int, int, Class, String[])},
+ * or from a binary code using {@link #create(int, int, Class, int, String)},
+ * whatever is available first.
+ * binRoot
+ * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}.
+ * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated
+ * using the binary subfolder, the first existing resource is being used.
+ *
+ * Your std JVM layout (plain or within a JAR):
+ *
+ * org/test/glsl/MyShaderTest.class
+ * org/test/glsl/shader/vertex.vp
+ * org/test/glsl/shader/fragment.fp
+ * org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * org/test/glsl/shader/bin/nvidia/fragment.bfp
+ *
+ * Your Android APK layout:
+ *
+ * classes.dex
+ * assets/org/test/glsl/shader/vertex.vp
+ * assets/org/test/glsl/shader/fragment.fp
+ * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
+ * ...
+ *
+ * Your invocation in org/test/glsl/MyShaderTest.java:
+ *
+ * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(),
+ * "shader", new String[] { "vertex" }, null,
+ * "shader/bin", "vertex", null, true);
+ * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
+ * "shader", new String[] { "vertex" }, null,
+ * "shader/bin", "fragment", null, true);
+ * ShaderProgram sp0 = new ShaderProgram();
+ * sp0.add(gl, vp0, System.err);
+ * sp0.add(gl, fp0, System.err);
+ * st.attachShaderProgram(gl, sp0, true);
+ *
+ * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}.
+ *
+ * context
class, see {@link #readShaderBinary(Class, String)}.
+ * source
is used),
+ * or to determine the shader binary format (if binary
is used).
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param context class used to help resolving the source and binary location
+ * @param srcRoot relative root path for srcBasenames
optional
+ * @param srcBasenames basenames w/o path or suffix relative to srcRoot
for the shader's source code
+ * @param srcSuffixOpt optional custom suffix for shader's source file,
+ * if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
+ * @param binRoot relative root path for binBasenames
+ * @param binBasename basename w/o path or suffix relative to binRoot
for the shader's binary code
+ * @param binSuffixOpt optional custom suffix for shader's binary file,
+ * if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ *
+ * @throws IllegalArgumentException if count
and srcBasenames.length
do not match
+ *
+ * @see #create(GL2ES2, int, int, Class, String[])
+ * @see #create(int, int, Class, int, String)
+ * @see #readShaderSource(Class, String)
+ * @see #getFileSuffix(boolean, int)
+ * @see ShaderUtil#getShaderBinaryFormats(GL)
+ * @see #getBinarySubPath(int)
+ *
+ * @since 2.3.2
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class> context,
+ final String srcRoot, final String[] srcBasenames, final String srcSuffixOpt,
+ final String binRoot, final String binBasename, final String binSuffixOpt,
+ final boolean mutableStringBuilder) {
+ ShaderCode res = null;
+ final String srcPath[];
+ String srcPathString = null;
+ String binFileName = null;
+
+ if( null!=srcBasenames && ShaderUtil.isShaderCompilerAvailable(gl) ) {
+ srcPath = new String[srcBasenames.length];
+ final String srcSuffix = null != srcSuffixOpt ? srcSuffixOpt : getFileSuffix(false, type);
+ if( null != srcRoot && srcRoot.length() > 0 ) {
+ for(int i=0; ibinRoot
+ * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}.
+ * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated
+ * using the binary subfolder, the first existing resource is being used.
+ *
+ * Your std JVM layout (plain or within a JAR):
+ *
+ * org/test/glsl/MyShaderTest.class
+ * org/test/glsl/shader/vertex.vp
+ * org/test/glsl/shader/fragment.fp
+ * org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * org/test/glsl/shader/bin/nvidia/fragment.bfp
+ *
+ * Your Android APK layout:
+ *
+ * classes.dex
+ * assets/org/test/glsl/shader/vertex.vp
+ * assets/org/test/glsl/shader/fragment.fp
+ * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
+ * ...
+ *
+ * Your invocation in org/test/glsl/MyShaderTest.java:
+ *
+ * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(),
+ * "shader", new String[] { "vertex" }, "shader/bin", "vertex", true);
+ * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
+ * "shader", new String[] { "vertex" }, "shader/bin", "fragment", true);
+ * ShaderProgram sp0 = new ShaderProgram();
+ * sp0.add(gl, vp0, System.err);
+ * sp0.add(gl, fp0, System.err);
+ * st.attachShaderProgram(gl, sp0, true);
+ *
+ * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}.
+ *
+ * context
class, see {@link #readShaderBinary(Class, String)}.
+ * source
is used),
+ * or to determine the shader binary format (if binary
is used).
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param count number of shaders
+ * @param context class used to help resolving the source and binary location
+ * @param srcRoot relative root path for srcBasenames
optional
+ * @param srcBasenames basenames w/o path or suffix relative to srcRoot
for the shader's source code
+ * @param binRoot relative root path for binBasenames
+ * @param binBasename basename w/o path or suffix relative to binRoot
for the shader's binary code
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ *
+ * @throws IllegalArgumentException if count
and srcBasenames.length
do not match
+ *
+ * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)
+ * @see #readShaderSource(Class, String)
+ * @see #getFileSuffix(boolean, int)
+ * @see ShaderUtil#getShaderBinaryFormats(GL)
+ * @see #getBinarySubPath(int)
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class> context,
+ final String srcRoot, final String[] srcBasenames,
+ final String binRoot, final String binBasename,
+ final boolean mutableStringBuilder) {
+ return create(gl, type, count, context, srcRoot, srcBasenames, null,
+ binRoot, binBasename, null, mutableStringBuilder);
+ }
+
+ /**
+ * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)}.
+ *
+ * Your std JVM layout (plain or within a JAR):
+ *
+ * org/test/glsl/MyShaderTest.class
+ * org/test/glsl/shader/vertex.vp
+ * org/test/glsl/shader/fragment.fp
+ * org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * org/test/glsl/shader/bin/nvidia/fragment.bfp
+ *
+ * Your Android APK layout:
+ *
+ * classes.dex
+ * assets/org/test/glsl/shader/vertex.vp
+ * assets/org/test/glsl/shader/fragment.fp
+ * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
+ * ...
+ *
+ * Your invocation in org/test/glsl/MyShaderTest.java:
+ *
+ * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
+ * "shader", "shader/bin", "vertex", null, null, true);
+ * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
+ * "shader", "shader/bin", "fragment", null, null, true);
+ * ShaderProgram sp0 = new ShaderProgram();
+ * sp0.add(gl, vp0, System.err);
+ * sp0.add(gl, fp0, System.err);
+ * st.attachShaderProgram(gl, sp0, true);
+ *
+ * source
is used),
+ * or to determine the shader binary format (if binary
is used).
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param context class used to help resolving the source and binary location
+ * @param srcRoot relative root path for basename
optional
+ * @param binRoot relative root path for basename
+ * @param basename basename w/o path or suffix relative to srcRoot
and binRoot
+ * for the shader's source and binary code.
+ * @param srcSuffixOpt optional custom suffix for shader's source file,
+ * if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
+ * @param binSuffixOpt optional custom suffix for shader's binary file,
+ * if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ * @throws IllegalArgumentException if count
is not 1
+ *
+ * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)
+ * @since 2.3.2
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final Class> context,
+ final String srcRoot, final String binRoot,
+ final String basename, final String srcSuffixOpt, final String binSuffixOpt,
+ final boolean mutableStringBuilder) {
+ return create(gl, type, 1, context, srcRoot, new String[] { basename }, srcSuffixOpt,
+ binRoot, basename, binSuffixOpt, mutableStringBuilder );
+ }
+
+ /**
+ * Simplified variation of {@link #create(GL2ES2, int, Class, String, String, String, String, String, boolean)}.
+ *
+ * Your std JVM layout (plain or within a JAR):
+ *
+ * org/test/glsl/MyShaderTest.class
+ * org/test/glsl/shader/vertex.vp
+ * org/test/glsl/shader/fragment.fp
+ * org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * org/test/glsl/shader/bin/nvidia/fragment.bfp
+ *
+ * Your Android APK layout:
+ *
+ * classes.dex
+ * assets/org/test/glsl/shader/vertex.vp
+ * assets/org/test/glsl/shader/fragment.fp
+ * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
+ * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
+ * ...
+ *
+ * Your invocation in org/test/glsl/MyShaderTest.java:
+ *
+ * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
+ * "shader", "shader/bin", "vertex", true);
+ * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
+ * "shader", "shader/bin", "fragment", true);
+ * ShaderProgram sp0 = new ShaderProgram();
+ * sp0.add(gl, vp0, System.err);
+ * sp0.add(gl, fp0, System.err);
+ * st.attachShaderProgram(gl, sp0, true);
+ *
+ *
source
is used),
+ * or to determine the shader binary format (if binary
is used).
+ * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
+ * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
+ * @param context class used to help resolving the source and binary location
+ * @param srcRoot relative root path for basename
optional
+ * @param binRoot relative root path for basename
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ * @param basenames basename w/o path or suffix relative to srcRoot
and binRoot
+ * for the shader's source and binary code.
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ * @throws IllegalArgumentException if count
is not 1
+ */
+ public static ShaderCode create(final GL2ES2 gl, final int type, final Class> context,
+ final String srcRoot, final String binRoot, final String basename,
+ final boolean mutableStringBuilder) {
+ return ShaderCode.create(gl, type, context, srcRoot, binRoot, basename, null, null, mutableStringBuilder);
+ }
+
+ /**
+ * returns the uniq shader id as an integer
+ */
+ public int id() { return id; }
+
+ public int shaderType() { return shaderType; }
+ public String shaderTypeStr() { return shaderTypeStr(shaderType); }
+
+ public static String shaderTypeStr(final int type) {
+ switch (type) {
+ case GL2ES2.GL_VERTEX_SHADER:
+ return "VERTEX_SHADER";
+ case GL2ES2.GL_FRAGMENT_SHADER:
+ return "FRAGMENT_SHADER";
+ case GL3ES3.GL_GEOMETRY_SHADER:
+ return "GEOMETRY_SHADER";
+ case GL3ES3.GL_TESS_CONTROL_SHADER:
+ return "TESS_CONTROL_SHADER";
+ case GL3ES3.GL_TESS_EVALUATION_SHADER:
+ return "TESS_EVALUATION_SHADER";
+ case GL3ES3.GL_COMPUTE_SHADER:
+ return "COMPUTE_SHADER";
+ }
+ return "UNKNOWN_SHADER";
+ }
+
+ public int shaderBinaryFormat() { return shaderBinaryFormat; }
+ public Buffer shaderBinary() { return shaderBinary; }
+ public CharSequence[][] shaderSource() { return shaderSource; }
+
+ public boolean isValid() { return valid; }
+
+ public IntBuffer shader() { return shader; }
+
+ public boolean compile(final GL2ES2 gl) {
+ return compile(gl, null);
+ }
+ public boolean compile(final GL2ES2 gl, final PrintStream verboseOut) {
+ if(isValid()) return true;
+
+ // Create & Compile the vertex/fragment shader objects
+ if(null!=shaderSource) {
+ if(DEBUG_CODE) {
+ System.err.println("ShaderCode.compile:");
+ dumpShaderSource(System.err);
+ }
+ valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType,
+ shaderSource, verboseOut);
+ } else if(null!=shaderBinary) {
+ valid=ShaderUtil.createAndLoadShader(gl, shader, shaderType,
+ shaderBinaryFormat, shaderBinary, verboseOut);
+ } else {
+ throw new GLException("no code (source or binary)");
+ }
+ return valid;
+ }
+
+ public void destroy(final GL2ES2 gl) {
+ if(isValid()) {
+ if(null!=gl) {
+ ShaderUtil.deleteShader(gl, shader());
+ }
+ valid=false;
+ }
+ if(null!=shaderBinary) {
+ shaderBinary.clear();
+ shaderBinary=null;
+ }
+ shaderSource=null;
+ shaderBinaryFormat=-1;
+ shaderType=-1;
+ id=-1;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if(this==obj) { return true; }
+ if(obj instanceof ShaderCode) {
+ return id()==((ShaderCode)obj).id();
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return id;
+ }
+ @Override
+ public String toString() {
+ final StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: ");
+ for(int i=0; itag
.
+ * + * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * + * @param shaderIdx the shader index to be used. + * @param tag search string + * @param fromIndex start searchtag
begininig with this index
+ * @param data the text to be inserted. Shall end with an EOL '\n' character.
+ * @return index after the inserted data
+ *
+ * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder
+ */
+ public int insertShaderSource(final int shaderIdx, final String tag, final int fromIndex, final CharSequence data) {
+ if(null==shaderSource) {
+ throw new IllegalStateException("no shader source");
+ }
+ final int shaderCount = (null!=shader)?shader.capacity():0;
+ if(0>shaderIdx || shaderIdx>=shaderCount) {
+ throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx);
+ }
+ final int sourceCount = shaderSource.length;
+ if(shaderIdx>=sourceCount) {
+ throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx);
+ }
+ final CharSequence[] src = shaderSource[shaderIdx];
+ int curEndIndex = 0;
+ for(int j=0; jnewName
in all shader sources.
+ *
+ * In case oldName
and newName
are equal, no action is performed.
+ *
+ * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * + * @param oldName the to be replace string + * @param newName the replacement string + * @return the number of replacements + * + * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of typeStringBuilder
+ */
+ public int replaceInShaderSource(final String oldName, final String newName) {
+ if(null==shaderSource) {
+ throw new IllegalStateException("no shader source");
+ }
+ if(oldName == newName || oldName.equals(newName)) {
+ return 0;
+ }
+ final int oldNameLen = oldName.length();
+ final int newNameLen = newName.length();
+ int num = 0;
+ final int sourceCount = shaderSource.length;
+ for(int shaderIdx = 0; shaderIdxposition
in shader source for shader shaderIdx
.
+ * + * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * + * @param shaderIdx the shader index to be used. + * @param position in shader source segments of shadershaderIdx
, -1 will append data
+ * @param data the text to be inserted. Shall end with an EOL '\n' character
+ * @return index after the inserted data
+ *
+ * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder
+ */
+ public int insertShaderSource(final int shaderIdx, int position, final CharSequence data) {
+ if(null==shaderSource) {
+ throw new IllegalStateException("no shader source");
+ }
+ final int shaderCount = (null!=shader)?shader.capacity():0;
+ if(0>shaderIdx || shaderIdx>=shaderCount) {
+ throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx);
+ }
+ final int sourceCount = shaderSource.length;
+ if(shaderIdx>=sourceCount) {
+ throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx);
+ }
+ final CharSequence[] src = shaderSource[shaderIdx];
+ int curEndIndex = 0;
+ for(int j=0; jpath
,
+ * either relative to the context
class or absolute as-is
+ * at position
in shader source for shader shaderIdx
.
+ * + * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, + * see {@link IOUtil#getResource(Class, String)}. + *
+ *+ * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * + * @param shaderIdx the shader index to be used. + * @param position in shader source segments of shadershaderIdx
, -1 will append data
+ * @param context class used to help resolve the source location
+ * @param path location of shader source
+ * @return index after the inserted code.
+ * @throws IOException
+ * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder
+ * @see IOUtil#getResource(Class, String)
+ */
+ public int insertShaderSource(final int shaderIdx, final int position, final Class> context, final String path) throws IOException {
+ final CharSequence data = readShaderSource(context, path, true);
+ if( null != data ) {
+ return insertShaderSource(shaderIdx, position, data);
+ } else {
+ return position;
+ }
+ }
+
+ private static int readShaderSource(final Class> context, final URLConnection conn, final StringBuilder result, int lineno) throws IOException {
+ if(DEBUG_CODE) {
+ if(0 == lineno) {
+ result.append("// "+conn.getURL().toExternalForm()+"\n");
+ } else {
+ result.append("// included @ line "+lineno+": "+conn.getURL().toExternalForm()+"\n");
+ }
+ }
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ lineno++;
+ if (line.startsWith("#include ")) {
+ final String includeFile;
+ {
+ String s = line.substring(9).trim();
+ // Bug 1283: Remove shader include filename quotes if exists at start and end only
+ if( s.startsWith("\"") && s.endsWith("\"")) {
+ s = s.substring(1, s.length()-1);
+ }
+ includeFile = s;
+ }
+ URLConnection nextConn = null;
+
+ // Try relative of current shader location
+ final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL ));
+ nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf ");
+ if (nextConn == null) {
+ // Try relative of class and absolute
+ nextConn = IOUtil.getResource(includeFile, context.getClassLoader(), context);
+ }
+ if (nextConn == null) {
+ // Fail
+ throw new FileNotFoundException("Can't find include file " + includeFile);
+ }
+ lineno = readShaderSource(context, nextConn, result, lineno);
+ } else {
+ result.append(line + "\n");
+ }
+ }
+ } catch (final URISyntaxException e) {
+ throw new IOException(e);
+ } finally {
+ IOUtil.close(reader, false);
+ }
+ return lineno;
+ }
+
+ /**
+ * Reads shader source located in conn
.
+ *
+ * @param context class used to help resolve the source location, may be {@code null}
+ * @param conn the {@link URLConnection} of the shader source
+ * @param result {@link StringBuilder} sink for the resulting shader source code
+ * @throws IOException
+ */
+ public static void readShaderSource(final Class> context, final URLConnection conn, final StringBuilder result) throws IOException {
+ readShaderSource(context, conn, result, 0);
+ }
+
+ /**
+ * Reads shader source located in path
,
+ * either relative to the context
class or absolute as-is.
+ * + * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, + * see {@link IOUtil#getResource(Class, String)}. + *
+ * + * @param context class used to help resolve the source location + * @param path location of shader source + * @param mutableStringBuilder iftrue
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ * @throws IOException
+ *
+ * @see IOUtil#getResource(Class, String)
+ */
+ public static CharSequence readShaderSource(final Class> context, final String path, final boolean mutableStringBuilder) throws IOException {
+ final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context);
+ if (conn == null) {
+ return null;
+ }
+ final StringBuilder result = new StringBuilder();
+ readShaderSource(context, conn, result);
+ return mutableStringBuilder ? result : result.toString();
+ }
+
+ /**
+ * Reads shader source located from {@link Uri#absolute} {@link Uri} sourceLocation
.
+ * @param sourceLocation {@link Uri} location of shader source
+ * @param mutableStringBuilder if true
method returns a mutable StringBuilder
instance
+ * which can be edited later on at the costs of a String conversion when passing to
+ * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
+ * If false
method returns an immutable String
instance,
+ * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
+ * at no additional costs.
+ * @throws IOException
+ * @since 2.3.2
+ */
+ public static CharSequence readShaderSource(final Uri sourceLocation, final boolean mutableStringBuilder) throws IOException {
+ final URLConnection conn = IOUtil.openURL(sourceLocation.toURL(), "ShaderCode ");
+ if (conn == null) {
+ return null;
+ }
+ final StringBuilder result = new StringBuilder();
+ readShaderSource(null, conn, result);
+ return mutableStringBuilder ? result : result.toString();
+ }
+
+ /**
+ * Reads shader binary located in path
,
+ * either relative to the context
class or absolute as-is.
+ * + * Final location lookup is perfomed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, + * see {@link IOUtil#getResource(Class, String)}. + *
+ * + * @param context class used to help resolve the source location + * @param path location of shader binary + * @throws IOException + * + * @see IOUtil#getResource(Class, String) + */ + public static ByteBuffer readShaderBinary(final Class> context, final String path) throws IOException { + final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context); + if (conn == null) { + return null; + } + final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() ); + try { + return IOUtil.copyStream2ByteBuffer( bis ); + } finally { + IOUtil.close(bis, false); + } + } + + /** + * Reads shader binary located from {@link Uri#absolute} {@link Uri}binLocation
.
+ * @param binLocation {@link Uri} location of shader binary
+ * @throws IOException
+ * @since 2.3.2
+ */
+ public static ByteBuffer readShaderBinary(final Uri binLocation) throws IOException {
+ final URLConnection conn = IOUtil.openURL(binLocation.toURL(), "ShaderCode ");
+ if (conn == null) {
+ return null;
+ }
+ final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() );
+ try {
+ return IOUtil.copyStream2ByteBuffer( bis );
+ } finally {
+ IOUtil.close(bis, false);
+ }
+ }
+
+ // Shall we use: #ifdef GL_FRAGMENT_PRECISION_HIGH .. #endif for using highp in fragment shader if avail ?
+ /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es2_default_precision_vp} */
+ public static final String es2_default_precision_vp = "\nprecision highp float;\nprecision highp int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
+ /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es2_default_precision_fp} */
+ public static final String es2_default_precision_fp = "\nprecision mediump float;\nprecision mediump int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
+
+ /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */
+ public static final String es3_default_precision_vp = es2_default_precision_vp;
+ /**
+ * Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp},
+ * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp},
+ * due to ES 3.x requirements of using same precision for uniforms!
+ */
+ public static final String es3_default_precision_fp = es3_default_precision_vp;
+
+ /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader} or {@link GL3#GL_GEOMETRY_SHADER geometry-shader}: {@value #gl3_default_precision_vp_gp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */
+ public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n";
+ /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #gl3_default_precision_fp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */
+ public static final String gl3_default_precision_fp = "\nprecision highp float;\nprecision mediump int;\n/*precision mediump sampler2D;*/\n";
+
+ /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
+ public static final String REQUIRE = "require";
+ /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
+ public static final String ENABLE = "enable";
+ /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
+ public static final String DISABLE = "disable";
+ /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
+ public static final String WARN = "warn";
+
+ /**
+ * Creates a GLSL extension directive.
+ * + * Prefer {@link #ENABLE} over {@link #REQUIRE}, since the latter will force a failure if not supported. + *
+ * + * @param extensionName + * @param behavior shall be either {@link #REQUIRE}, {@link #ENABLE}, {@link #DISABLE} or {@link #WARN} + * @return the complete extension directive + */ + public static String createExtensionDirective(final String extensionName, final String behavior) { + return "#extension " + extensionName + " : " + behavior + "\n"; + } + + /** + * Add GLSL version at the head of this shader source code. + *+ * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * @param gl a GL context, which must have been made current once + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addGLSLVersion(final GL2ES2 gl) { + return insertShaderSource(0, 0, gl.getContext().getGLSLVersionString()); + } + + /** + * Adds default precision to source code at given position if required, i.e. + * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp}, + * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none, + * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used. + *+ * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * @param gl a GL context, which must have been made current once + * @param pos position within this mutable shader source. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addDefaultShaderPrecision(final GL2ES2 gl, int pos) { + final String defaultPrecision; + if( gl.isGLES3() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es3_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es3_default_precision_fp; break; + case GL3ES3.GL_COMPUTE_SHADER: + defaultPrecision = es3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( gl.isGLES2() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es2_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es2_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( requiresGL3DefaultPrecision(gl) ) { + // GLSL [ 1.30 .. 1.50 [ needs at least fragement float default precision! + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + case GL3ES3.GL_GEOMETRY_SHADER: + case GL3ES3.GL_TESS_CONTROL_SHADER: + case GL3ES3.GL_TESS_EVALUATION_SHADER: + defaultPrecision = gl3_default_precision_vp_gp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = gl3_default_precision_fp; break; + case GL3ES3.GL_COMPUTE_SHADER: + defaultPrecision = gl3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else { + defaultPrecision = null; + } + if( null != defaultPrecision ) { + pos = insertShaderSource(0, pos, defaultPrecision); + } + return pos; + } + + /** Returns true, if GLSL version requires default precision, i.e. ES2 or GLSL [1.30 .. 1.50[. */ + public static final boolean requiresDefaultPrecision(final GL2ES2 gl) { + if( gl.isGLES() ) { + return true; + } + return requiresGL3DefaultPrecision(gl); + } + + /** Returns true, if GL3 GLSL version requires default precision, i.e. GLSL [1.30 .. 1.50[. */ + public static final boolean requiresGL3DefaultPrecision(final GL2ES2 gl) { + if( gl.isGL3() ) { + final VersionNumber glslVersion = gl.getContext().getGLSLVersionNumber(); + return glslVersion.compareTo(GLContext.Version1_30) >= 0 && glslVersion.compareTo(GLContext.Version1_50) < 0 ; + } else { + return false; + } + } + + /** + * Default customization of this shader source code. + *+ * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param addDefaultPrecision iftrue
default precision source code line(s) are added, i.e.
+ * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp},
+ * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none,
+ * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used.
+ * @return the index after the inserted data, maybe 0 if nothing has be inserted.
+ * @see #addGLSLVersion(GL2ES2)
+ * @see #addDefaultShaderPrecision(GL2ES2, int)
+ */
+ public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final boolean addDefaultPrecision) {
+ int pos;
+ if( preludeVersion ) {
+ pos = addGLSLVersion(gl);
+ } else {
+ pos = 0;
+ }
+ if( addDefaultPrecision ) {
+ pos = addDefaultShaderPrecision(gl, pos);
+ }
+ return pos;
+ }
+
+ /**
+ * Default customization of this shader source code.
+ * + * Note: The shader source to be edit must be created using a mutable StringBuilder. + *
+ * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param esDefaultPrecision optional default precision source code line(s) preluded if not null and if {@link GL#isGLES()}. + * You may use {@link #es2_default_precision_fp} for fragment shader and {@link #es2_default_precision_vp} for vertex shader. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + * @see #addGLSLVersion(GL2ES2) + * @see #addDefaultShaderPrecision(GL2ES2, int) + */ + public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final String esDefaultPrecision) { + int pos; + if( preludeVersion ) { + pos = addGLSLVersion(gl); + } else { + pos = 0; + } + if( gl.isGLES() && null != esDefaultPrecision ) { + pos = insertShaderSource(0, pos, esDefaultPrecision); + } else { + pos = addDefaultShaderPrecision(gl, pos); + } + return pos; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + protected CharSequence[][] shaderSource = null; + protected Buffer shaderBinary = null; + protected int shaderBinaryFormat = -1; + protected IntBuffer shader = null; + protected int shaderType = -1; + protected int id = -1; + + protected boolean valid=false; + + private static synchronized int getNextID() { + return nextID++; + } + protected static int nextID = 1; +} + diff --git a/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderProgram.java b/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderProgram.java new file mode 100644 index 00000000..d09a3685 --- /dev/null +++ b/src/main/java/net/opengrabeso/opengl/util/glsl/ShaderProgram.java @@ -0,0 +1,321 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package net.opengrabeso.opengl.util.glsl; + +import com.jogamp.opengl.*; + +import com.jogamp.common.os.Platform; + +import java.util.HashSet; +import java.util.Iterator; +import java.io.PrintStream; + +public class ShaderProgram { + + public ShaderProgram() { + id = getNextID(); + } + + public boolean linked() { + return programLinked; + } + + public boolean inUse() { + return programInUse; + } + + /** Returns the shader program name, which is non zero if valid. */ + public int program() { return shaderProgram; } + + /** + * returns the uniq shader id as an integer + */ + public int id() { return id; } + + /** + * Detaches all shader codes and deletes the program. + * Destroys the shader codes as well. + * Calls release(gl, true) + * + * @see #release(GL2ES2, boolean) + */ + public synchronized void destroy(final GL2ES2 gl) { + release(gl, true); + } + + /** + * Detaches all shader codes and deletes the program, + * but leaves the shader code intact. + * Calls release(gl, false) + * + * @see #release(GL2ES2, boolean) + */ + public synchronized void release(final GL2ES2 gl) { + release(gl, false); + } + + /** + * Detaches all shader codes and deletes the program. + * IfdestroyShaderCode
is true it destroys the shader codes as well.
+ */
+ public synchronized void release(final GL2ES2 gl, final boolean destroyShaderCode) {
+ if( programLinked ) {
+ useProgram(gl, false);
+ }
+ for(final IteratorThis command does not compile and attach the shader
+ */
+ public synchronized void add(final ShaderCode shaderCode) {
+ allShaderCode.add(shaderCode);
+ }
+
+ public synchronized boolean contains(final ShaderCode shaderCode) {
+ return allShaderCode.contains(shaderCode);
+ }
+
+ /**
+ * Warning slow O(n) operation ..
+ * @param id
+ * @return
+ */
+ public synchronized ShaderCode getShader(final int id) {
+ for(final Iterator Compiles and attaches the shader, if not done yet. Compiles and attaches the shader code to the program if not done by yet Within this process, all GL resources (shader and program objects) are created if necessary.
+ * This allows seamless switching of programs using almost same data
+ * but performing different artifacts.
+ *
+ * A {@link #useProgram(GL2ES2, boolean) used} ShaderState is attached to the current GL context
+ * and can be retrieved via {@link #getShaderState(GL)}.
+ * Attaching a shader program the first time,
+ * as well as switching to another program on the fly,
+ * while managing all attribute and uniform data. [Re]sets all data and use program in case of a program switch. Use program, {@link #useProgram(GL2ES2, boolean)},
+ * if If an attribute location is cached (ie {@link #bindAttribLocation(GL2ES2, int, String)})
+ * it is promoted to the {@link GLArrayData} instance. The attribute will be destroyed with {@link #destroy(GL2ES2)}
+ * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}. The data will not be transfered to the GPU, use {@link #vertexAttribPointer(GL2ES2, GLArrayData)} additionally. The data will also be {@link GLArrayData#associate(Object, boolean) associated} with this ShaderState.
+ * This method uses the {@link GLArrayData}'s location if valid, i.e. ≥ 0.
+ * Attribute data is bound to the GL state, i.e. VBO data itself will not be updated.
+ *
+ * Attribute location and it's data assignment is bound to the program,
+ * hence both are updated.
+ *
+ * Note: Such update could only be prevented,
+ * if tracking am attribute/program dirty flag.
+ *
+ * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)})
+ * must be in use ({@link #useProgram(GL2ES2, boolean) }) !
+ * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)})
+ * must be in use ({@link #useProgram(GL2ES2, boolean) }) !
+ * This method uses the {@link GLUniformData}'s location if valid, i.e. ≥ 0.
+ * Uniform data and location is bound to the program,
+ * hence both are updated.
+ *
+ * Note: Such update could only be prevented,
+ * if tracking a uniform/program dirty flag.
+ *
+ * One shall only call this method while debugging and only if all required
+ * resources by the shader are set.
+ *
+ * Note: It is possible that a working shader program will fail validation.
+ * This has been experienced on NVidia APX2500 and Tegra2.
+ *
+
+ Negative coordinates and sizes are not supported, since they make
+ no sense in the context of the packer, which deals only with
+ positively sized regions.
+
+ This class contains a user data field for efficient hookup to
+ external data structures as well as enough other hooks to
+ efficiently plug into the rectangle packer. */
+
+public class Rect {
+ private int x;
+ private int y;
+ private int w;
+ private int h;
+
+ // The level we're currently installed in in the parent
+ // RectanglePacker, or null if not hooked in to the table yet
+ private Level level;
+
+ // The user's object this rectangle represents.
+ private Object userData;
+
+ // Used transiently during re-layout of the backing store (when
+ // there is no room left due either to fragmentation or just being
+ // out of space)
+ private Rect nextLocation;
+
+ public Rect() {
+ this(null);
+ }
+
+ public Rect(final Object userData) {
+ this(0, 0, 0, 0, userData);
+ }
+
+ public Rect(final int x, final int y, final int w, final int h, final Object userData) {
+ setPosition(x, y);
+ setSize(w, h);
+ setUserData(userData);
+ }
+
+ public int x() { return x; }
+ public int y() { return y; }
+ public int w() { return w; }
+ public int h() { return h; }
+ public Object getUserData() { return userData; }
+ public Rect getNextLocation() { return nextLocation; }
+
+ public void setPosition(final int x, final int y) {
+ if (x < 0)
+ throw new IllegalArgumentException("Negative x");
+ if (y < 0)
+ throw new IllegalArgumentException("Negative y");
+ this.x = x;
+ this.y = y;
+ }
+
+ public void setSize(final int w, final int h) throws IllegalArgumentException {
+ if (w < 0)
+ throw new IllegalArgumentException("Negative width");
+ if (h < 0)
+ throw new IllegalArgumentException("Negative height");
+ this.w = w;
+ this.h = h;
+ }
+
+ public void setUserData(final Object obj) { userData = obj; }
+ public void setNextLocation(final Rect nextLocation) { this.nextLocation = nextLocation; }
+
+ // Helpers for computations.
+
+ /** Returns the maximum x-coordinate contained within this
+ rectangle. Note that this returns a different result than Java
+ 2D's rectangles; for a rectangle of position (0, 0) and size (1,
+ 1) this will return 0, not 1. Returns -1 if the width of this
+ rectangle is 0. */
+ public int maxX() {
+ if (w() < 1)
+ return -1;
+ return x() + w() - 1;
+ }
+
+ /** Returns the maximum y-coordinate contained within this
+ rectangle. Note that this returns a different result than Java
+ 2D's rectangles; for a rectangle of position (0, 0) and size (1,
+ 1) this will return 0, not 1. Returns -1 if the height of this
+ rectangle is 0. */
+ public int maxY() {
+ if (h() < 1)
+ return -1;
+ return y() + h() - 1;
+ }
+
+ public boolean canContain(final Rect other) {
+ return (w() >= other.w() &&
+ h() >= other.h());
+ }
+
+ @Override
+ public String toString() {
+ return "[Rect x: " + x() + " y: " + y() + " w: " + w() + " h: " + h() + "]";
+ }
+
+ // Unclear whether it's a good idea to override hashCode and equals
+ // for these objects
+ /*
+ public boolean equals(Object other) {
+ if (other == null || (!(other instanceof Rect))) {
+ return false;
+ }
+
+ Rect r = (Rect) other;
+ return (this.x() == r.x() &&
+ this.y() == r.y() &&
+ this.w() == r.w() &&
+ this.h() == r.h());
+ }
+
+ public int hashCode() {
+ return (x + y * 13 + w * 17 + h * 23);
+ }
+ */
+}
diff --git a/src/main/java/net/opengrabeso/opengl/util/packrect/RectVisitor.java b/src/main/java/net/opengrabeso/opengl/util/packrect/RectVisitor.java
new file mode 100644
index 00000000..25001879
--- /dev/null
+++ b/src/main/java/net/opengrabeso/opengl/util/packrect/RectVisitor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package net.opengrabeso.opengl.util.packrect;
+
+/** Iteration construct without exposing the internals of the
+ RectanglePacker and without implementing a complex Iterator. */
+
+public interface RectVisitor {
+ public void visit(Rect rect);
+}
diff --git a/src/main/java/net/opengrabeso/opengl/util/packrect/RectanglePacker.java b/src/main/java/net/opengrabeso/opengl/util/packrect/RectanglePacker.java
new file mode 100644
index 00000000..9ae58dba
--- /dev/null
+++ b/src/main/java/net/opengrabeso/opengl/util/packrect/RectanglePacker.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package net.opengrabeso.opengl.util.packrect;
+
+import java.util.*;
+
+/** Packs rectangles supplied by the user (typically representing
+ image regions) into a larger backing store rectangle (typically
+ representing a large texture). Supports automatic compaction of
+ the space on the backing store, and automatic expansion of the
+ backing store, when necessary. */
+
+public class RectanglePacker {
+ private final BackingStoreManager manager;
+ private Object backingStore;
+ private LevelSet levels;
+ private static final float EXPANSION_FACTOR = 0.5f;
+ private static final float SHRINK_FACTOR = 0.3f;
+
+ private final int initialWidth;
+ private final int initialHeight;
+
+ private int maxWidth = -1;
+ private int maxHeight = -1;
+
+ static class RectHComparator implements Comparator
+ * Allows to classify the {@link ImageType} of an {@link InputStream} via {@link #ImageType(InputStream)}
+ * or to simply define one {@link ImageType} via {@link #ImageType(String)}.
+ *
+ * {@code 'i' 'c' 'n' 's' ascii code}
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * {@code GIF87A or GIF89A ascii code}
+ *
+ * {@code BM ascii code}
+ *
+ * FIXME: Collision or supertype of {@link #T_DIB}?
+ *
+ * FIXME: Collision or subtype of {@link #T_BMP}?
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * Intentionally detects only the little endian tiff images ("II" in the spec).
+ *
+ * FIXME: Collision or supertype of {@link #T_LDF}?
+ *
+ * FIXME: Collision or subtype of {@link #T_TIFF}?
+ *
+ * "474 saved as a short" 474 = 0x01DA
+ *
+ * 'D' 'D' 'S' ' ' ascii code
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * FIXME: Collision or supertype of {@link #T_RAD}?
+ *
+ * FIXME: Collision or subtype of {@link #T_LBM}?
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * TODO
+ *
+ * Maybe {@code null} if undetermined, i.e. {@link #isDefined()} returns {@code false}.
+ *
+ * May be {@code null}, if {@link #type} has been determined otherwise, i.e {@link #ImageType(String)}.
+ *
+ * The header is not being used for {@link #hashCode()} and {@link #equals(Object)}!
+ *
+ * Due to many confusions w/ texture usage, following list described the order
+ * and semantics of texture unit selection, binding and enabling.
+ * Non-power-of-two restrictions
+ * Performance Tips
+ * Alpha premultiplication and blending
+ *
+ * Disclaimer: Consider performing alpha premultiplication in shader code, if really desired! Otherwise use RGBA.
+ * The Texture class does not convert RGBA image data into
+ * premultiplied data when storing it into an OpenGL texture.
+ *
+ * The mathematically correct way to perform blending in OpenGL
+ * with the SrcOver "source over destination" mode, or any other
+ * Porter-Duff rule, is to use premultiplied color components,
+ * which means the R/G/ B color components must have been multiplied by
+ * the alpha value. If using premultiplied color components
+ * it is important to use the correct blending function; for
+ * example, the SrcOver rule is expressed as:
+
+ * Call is ignored if the {@link GL} object's context
+ * is using a core profile,
+ *
+ * See the performance tips above for hints
+ * on how to maximize performance when using many Texture objects.
+ *
+ * Call is ignored if the {@link GL} object's context
+ * is using a core profile
+ *
+ * See the performance tips above for hints
+ * on how to maximize performance when using many Texture objects.
+ *
+ * No-op if no change, otherwise generates new {@link TextureCoords}.
+ *
+ * Most applications will not need to access this, since it is
+ * handled automatically by the bind(GL) and destroy(GL) APIs.
+ *
+ * Most applications will not need to access this, since it is
+ * handled automatically by the bind(GL) and destroy(GL) APIs.
+ *
+ * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type.
+ *
+ * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type.
+ *
+ * The states keys are the retrieved active texture-unit and the given texture-target
+ * for which the following states are being queried:
+ *
+ * Returns
+ * First the texture-unit is activated, then all states are restored.
+ *
+ *
+
+ *
+ * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean)
+ */
+ public synchronized void useProgram(final GL2ES2 gl, final boolean on) {
+ if(null==shaderProgram) { throw new GLException("No program is attached"); }
+ if(on) {
+ if(shaderProgram.linked()) {
+ shaderProgram.useProgram(gl, true);
+ if(resetAllShaderData) {
+ resetAllAttributes(gl);
+ resetAllUniforms(gl);
+ }
+ } else {
+ if(resetAllShaderData) {
+ setAllAttributes(gl);
+ }
+ if(!shaderProgram.link(gl, System.err)) {
+ throw new GLException("could not link program: "+shaderProgram);
+ }
+ shaderProgram.useProgram(gl, true);
+ if(resetAllShaderData) {
+ resetAllUniforms(gl);
+ }
+ }
+ resetAllShaderData = false;
+ } else {
+ shaderProgram.useProgram(gl, false);
+ }
+ }
+
+ public boolean linked() {
+ return (null!=shaderProgram)?shaderProgram.linked():false;
+ }
+
+ public boolean inUse() {
+ return (null!=shaderProgram)?shaderProgram.inUse():false;
+ }
+
+ /**
+ * Attach or switch a shader program
+ *
+ * enable
is true
.name
.
+ * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid,
+ * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.
+ * The location will be cached.
+ *
+ * @return -1 if there is no such attribute available,
+ * otherwise >= 0
+
+
+ *
+ * @see #getCachedAttribLocation(String)
+ * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
+ * @see #bindAttribLocation(GL2ES2, int, String)
+ * @see GL2ES2#glGetAttribLocation(int, String)
+ */
+ public int getAttribLocation(final GL2ES2 gl, final String name) {
+ if(null==shaderProgram) throw new GLException("No program is attached");
+ int location = getCachedAttribLocation(name);
+ if(0>location) {
+ if(!shaderProgram.linked()) throw new GLException("Program is not linked");
+ location = gl.glGetAttribLocation(shaderProgram.program(), name);
+ if(0<=location) {
+ activeAttribLocationMap.put(name, Integer.valueOf(location));
+ if(DEBUG) {
+ System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location);
+ }
+ } else if(verbose) {
+ System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location);
+ if(DEBUG) {
+ ExceptionUtils.dumpStack(System.err);
+ }
+ }
+ }
+ return location;
+ }
+
+ /**
+ * Validates and returns the location of a shader attribute.
+ * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid,
+ * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.
+ * The location will be cached and set in the
+ * {@link GLArrayData} object.
+ *
+ * @return -1 if there is no such attribute available,
+ * otherwise >= 0
+ *
+
+
+ *
+ * @see #getCachedAttribLocation(String)
+ * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
+ * @see #bindAttribLocation(GL2ES2, int, String)
+ * @see GL2ES2#glGetAttribLocation(int, String)
+ * @see #getAttribute(String)
+ */
+ public int getAttribLocation(final GL2ES2 gl, final GLArrayData data) {
+ if(null==shaderProgram) throw new GLException("No program is attached");
+ final String name = data.getName();
+ int location = getCachedAttribLocation(name);
+ if(0<=location) {
+ data.setLocation(location);
+ } else {
+ if(!shaderProgram.linked()) throw new GLException("Program is not linked");
+ location = data.setLocation(gl, shaderProgram.program());
+ if(0<=location) {
+ activeAttribLocationMap.put(name, Integer.valueOf(location));
+ if(DEBUG) {
+ System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location);
+ }
+ } else if(verbose) {
+ System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location);
+ if(DEBUG) {
+ ExceptionUtils.dumpStack(System.err);
+ }
+ }
+ }
+ activeAttribDataMap.put(data.getName(), data);
+ return location;
+ }
+
+ //
+ // Enabled Vertex Arrays and its data
+ //
+
+ /**
+ * @return true if the named attribute is enable
+ */
+ public final boolean isVertexAttribArrayEnabled(final String name) {
+ final Boolean v = activedAttribEnabledMap.get(name);
+ return null != v && v.booleanValue();
+ }
+
+ /**
+ * @return true if the {@link GLArrayData} attribute is enable
+ */
+ public final boolean isVertexAttribArrayEnabled(final GLArrayData data) {
+ return isVertexAttribArrayEnabled(data.getName());
+ }
+
+ private boolean enableVertexAttribArray(final GL2ES2 gl, final String name, int location) {
+ activedAttribEnabledMap.put(name, Boolean.TRUE);
+ if(0>location) {
+ location = getAttribLocation(gl, name);
+ if(0>location) {
+ if(verbose) {
+ System.err.println("ShaderState: glEnableVertexAttribArray failed, no index for: "+name);
+ if(DEBUG) {
+ ExceptionUtils.dumpStack(System.err);
+ }
+ }
+ return false;
+ }
+ }
+ if(DEBUG) {
+ System.err.println("ShaderState: glEnableVertexAttribArray: "+name+", loc: "+location);
+ }
+ gl.glEnableVertexAttribArray(location);
+ return true;
+ }
+
+ /**
+ * Enables a vertex attribute array.
+ *
+ * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)}
+ * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred.
+ *
+ * Even if the attribute is not found in the current shader,
+ * it is marked enabled in this state.
+ *
+ * @return false, if the name is not found, otherwise true
+ *
+
+ */
+ public boolean enableVertexAttribArray(final GL2ES2 gl, final String name) {
+ return enableVertexAttribArray(gl, name, -1);
+ }
+
+
+ /**
+ * Enables a vertex attribute array.
+ *
+ * This method uses the {@link GLArrayData}'s location if set
+ * and is the preferred alternative to {@link #enableVertexAttribArray(GL2ES2, String)}.
+ * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set
+ * and cached in this state.
+ *
+ * Even if the attribute is not found in the current shader,
+ * it is marked enabled in this state.
+ *
+ * @return false, if the name is not found, otherwise true
+ *
+
+ */
+ public boolean enableVertexAttribArray(final GL2ES2 gl, final GLArrayData data) {
+ if(0 > data.getLocation()) {
+ getAttribLocation(gl, data);
+ } else {
+ // ensure data is the current bound one
+ activeAttribDataMap.put(data.getName(), data);
+ }
+ return enableVertexAttribArray(gl, data.getName(), data.getLocation());
+ }
+
+ private boolean disableVertexAttribArray(final GL2ES2 gl, final String name, int location) {
+ activedAttribEnabledMap.put(name, Boolean.FALSE);
+ if(0>location) {
+ location = getAttribLocation(gl, name);
+ if(0>location) {
+ if(verbose) {
+ System.err.println("ShaderState: glDisableVertexAttribArray failed, no index for: "+name);
+ if(DEBUG) {
+ ExceptionUtils.dumpStack(System.err);
+ }
+ }
+ return false;
+ }
+ }
+ if(DEBUG) {
+ System.err.println("ShaderState: glDisableVertexAttribArray: "+name);
+ }
+ gl.glDisableVertexAttribArray(location);
+ return true;
+ }
+
+ /**
+ * Disables a vertex attribute array
+ *
+ * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)}
+ * hence {@link #disableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred.
+ *
+ * Even if the attribute is not found in the current shader,
+ * it is removed from this state enabled list.
+ *
+ * @return false, if the name is not found, otherwise true
+ *
+
+
+ */
+ public boolean disableVertexAttribArray(final GL2ES2 gl, final String name) {
+ return disableVertexAttribArray(gl, name, -1);
+ }
+
+ /**
+ * Disables a vertex attribute array
+ *
+ * This method uses the {@link GLArrayData}'s location if set
+ * and is the preferred alternative to {@link #disableVertexAttribArray(GL2ES2, String)}.
+ * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set
+ * and cached in this state.
+ *
+ * Even if the attribute is not found in the current shader,
+ * it is removed from this state enabled list.
+ *
+ * @return false, if the name is not found, otherwise true
+ *
+
+
+ */
+ public boolean disableVertexAttribArray(final GL2ES2 gl, final GLArrayData data) {
+ if(0 > data.getLocation()) {
+ getAttribLocation(gl, data);
+ }
+ return disableVertexAttribArray(gl, data.getName(), data.getLocation());
+ }
+
+ /**
+ * Set the {@link GLArrayData} vertex attribute data, if it's location is valid, i.e. ≥ 0.
+ *
+ * If data's location is invalid, it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)},
+ * set and cached in this state.
+ * name
.
+ * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
+ * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.
+ * The location will be cached.
+ *
+ * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
+ * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.
+ * The location will be cached and set in the
+ * {@link GLUniformData} object.
+ *
+ * If data's location is invalid, it will be retrieved via {@link #getUniformLocation(GL2ES2, GLUniformData)},
+ * set and cached in this state.
+ *
+ *
+ */
+ public static final String T_JPG = "jpg";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a PNG stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PNG = "png";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an Apple Icon Image stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_ICNS = "icns";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Microsoft Windows Icon stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_ICO = "ico";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Microsoft Windows Cursor stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CUR = "cur";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a GIF stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_GIF = "gif";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a GIF stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_BMP = "bmp";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DIB = "dib";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DCX = "dcx";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PCX = "pcx";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a PAM stream, NetPbm magic 6 - binary RGB.
+ *
+ *
+ */
+ public static final String T_PPM = "ppm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Adobe PhotoShop stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PSD = "psd";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a TIFF stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_TIFF = "tiff";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_LDF = "ldf";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an SGI RGB stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_SGI_RGB = "rgb";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a DirectDraw Surface stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DDS = "dds";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Portable Arbitrary Map stream, NetPbm magic 7 - binary RGB and RGBA.
+ *
+ *
+ */
+ public static final String T_PAM = "pam";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a PGM stream, NetPbm magic 5 - binary grayscale.
+ *
+ *
+ */
+ public static final String T_PGM = "pgm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a PGM stream, NetPbm magic 4 - binary monochrome.
+ *
+ *
+ */
+ public static final String T_PBM = "pbm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_3D2 = "3d2";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an Apple QuickDraw 3D 3DMF stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_3DMF = "3dmf";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Texas Instruments TI-92 Bitmap stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_92I = "92i";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an Amiga metafile stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_AMFF = "amff";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an America Online Art stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_ART = "art";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a United States Department of Defence Continuous Acquisition and Life-cycle Support Raster stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CALS = "cals";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CAM = "cam";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CBD = "cbd";
+
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CE2 = "ce2";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Kodak Cineon System stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CIN = "cin";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_COB = "cob";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Corel Photo Paint stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CPT = "cpt";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_CVG = "cvg";
+
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DEM = "dem";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Digital Picture Exchange stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DPX = "dpx";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DRW = "drw";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Autocad drawing stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_DWG = "dwg";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Hexagon Geospatial Enhanced Compression Wavelet stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_ECW = "ecw";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Microsoft Windows Enhanced metafile stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_EMF = "emf";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a FlashPix stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_FPX = "fpx";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_FTS = "fts";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_GRO = "gro";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_HDR = "hdr";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_HRU = "hru";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_IMG = "img";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_INFINI_D = "infini-d";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_IWC = "iwc";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_J6I = "j6i";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_JIF = "jif";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a JPEG-2000 stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_JP2 = "jp2";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_KDC = "kdc";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_L64 = "l64";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_LBM = "lbm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_RAD = "rad";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_LWF = "lwf";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MBM = "mbm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MGL = "mgl";
+
+ /**
+ * Constant which can be used as a file suffix to indicate an Imagemagick stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MIF = "mif";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Multiple-image Network Graphics stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MNG = "mng";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MPW = "mpw";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_MSP = "msp";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_N64 = "n64";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_NCR = "ncr";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_NFF = "nff";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_NGG = "ngg";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_NLM = "nlm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_NOL = "nol";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PAL = "pal";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PAX = "pax";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PCD = "pcd";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PCL = "pcl";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Softimage pic stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PIC = "pic";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PIX = "pix";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_POL = "pol";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a PaintShop Pro stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_PSP = "psp";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_QFX = "qfx";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_QTM = "qtm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Sun Raster stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_RAS = "ras";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_RIX = "rix";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_SID = "sid";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_SLD = "sld";
+
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_SOD = "sod";
+
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_WIC = "wic";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_WLM = "wlm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_WMF = "wmf";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Wordperfect Graphics vectors stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_WPG = "wpg";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_WRL = "wrl";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a {@code TBD} stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_XBM = "xbm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a X PixMap stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_XPM = "xpm";
+
+ /**
+ * Constant which can be used as a file suffix to indicate a Targa stream, value {@value}.
+ *
+ *
+ */
+ public static final String T_TGA = "tga";
+
+ /**
+ * The determined unique type, e.g. {@link #T_PNG}, {@link #T_JPG}, etc.
+ * null
+ * @throws IOException if an I/O exception occurred
+ */
+ public static String getFileSuffix(final InputStream stream) throws IOException {
+ return getFileSuffix(stream, new byte[MAGIC_MAX_SIZE]);
+ }
+ /**
+ * Determines the file suffix (i.e the image format) of the given InputStream. The given
+ * InputStream must return true from markSupported() and support a minimum of {@link #MAGIC_MAX_SIZE} bytes
+ * of read-ahead.
+ *
+ * @param stream stream to parse, {@link InputStream#available()} must be ≥ {@link #MAGIC_MAX_SIZE}
+ * @param b byte array sink, size must be ≥ {@link #MAGIC_MAX_SIZE}
+ * @return the file suffix if any, otherwise null
+ * @throws IOException if an I/O exception occurred
+ */
+ public static String getFileSuffix(InputStream stream, final byte[] b) throws IOException {
+ if (stream == null) {
+ throw new IOException("Stream was null");
+ }
+ if (!(stream instanceof BufferedInputStream)) {
+ stream = new BufferedInputStream(stream);
+ }
+ if (!stream.markSupported()) {
+ throw new IOException("Mark not supported");
+ }
+ if (stream.available() < MAGIC_MAX_SIZE) {
+ throw new IOException("Requires "+MAGIC_MAX_SIZE+" bytes, has "+stream.available()+" bytes");
+ }
+ try {
+ stream.mark(MAGIC_MAX_SIZE);
+ final int bytesRead = stream.read(b);
+ if( MAGIC_MAX_SIZE > bytesRead ) {
+ throw new IOException("Could not read "+MAGIC_MAX_SIZE+" bytes, read "+bytesRead+" bytes");
+ }
+ return getFileSuffix(b);
+ } finally {
+ stream.reset();
+ }
+
+ }
+
+ /**
+ * Determines the file suffix (i.e the image format) of the given bytes from the header
+ * of a file.
+ *
+ * @param b byte array to parse, size must be ≥ {@link #MAGIC_MAX_SIZE}
+ * @return the file suffix if any, otherwise null
+ * @throws IOException if an I/O exception occurred
+ */
+ public static String getFileSuffix(final byte[] b) {
+ if( b.length < MAGIC_MAX_SIZE ) {
+ throw new IllegalArgumentException("byte array must be >= "+MAGIC_MAX_SIZE+", has "+b.length);
+ }
+ final byte b0 = b[0];
+ final byte b1 = b[1];
+ final byte b2 = b[2];
+ final byte b3 = b[3];
+ final byte b4 = b[4];
+ final byte b5 = b[5];
+
+ // T_TGA: NO Signature!
+
+ if (b0 == (byte)0x00) {
+ if (b1 == (byte)0x00 && b2 == (byte)0x00 && b3 == (byte)0x0C &&
+ b4 == (byte)0x6A && b5 == (byte)0x50 &&
+ b[6] == (byte)0x20 && b[7] == (byte)0x20 && b[8] == (byte)0x0D && b[9] == (byte)0x0A && b[10] == (byte)0x87 &&
+ b[11] == (byte)0x0A) {
+ return T_JP2;
+ }
+ else if (b1 == (byte)0x01) {
+ return T_ICO;
+ }
+ else if (b1 == (byte)0x02) {
+ return T_CUR;
+ }
+ }
+ else if (b0 == (byte)0x01) {
+ if (b1 == (byte)0xDA /* && b2 == (byte)0x01 && b3 == (byte)0x01 && b4 == (byte)0x00 && b5 == (byte)0x03 */) {
+ return T_SGI_RGB;
+ }
+ else if (b1 == (byte)0xFF && b2 == (byte)0x02 && b3 == (byte)0x04 &&
+ b4 == (byte)0x03 && b5 == (byte)0x02) {
+ return T_DRW;
+ }
+ else if (b1 == (byte)0x00 && b2 == (byte)0x00 && b3 == (byte)0x00 &&
+ b4 == (byte)0x58 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x00) {
+ return T_EMF;
+ }
+ }
+ else if (b0 == (byte)0x07 && b1 == (byte)0x20 && b2 == (byte)0x4D && b3 == (byte)0x4D) {
+ return T_CAM;
+ }
+ else if (b0 == (byte)0x0A && b1 == (byte)0x05 && b2 == (byte)0x01 && b3 == (byte)0x08) {
+ return T_PCX;
+ }
+ else if (b0 == (byte)0x1B && b1 == (byte)0x45 && b2 == (byte)0x1B && b3 == (byte)0x26 &&
+ b4 == (byte)0x6C && b5 == (byte)0x30 &&
+ b[6] == (byte)0x4F && b[7] == (byte)0x1B && b[8] == (byte)0x26 && b[9] == (byte)0x6C && b[10] == (byte)0x30 &&
+ b[11] == (byte)0x45 && b[12] == (byte)0x1B && b[13] == (byte)0x26) {
+ return T_PCL;
+ }
+ else if (b0 == (byte)0x20 && b1 == (byte)0x77 && b2 == (byte)0x00 && b3 == (byte)0x02) {
+ return T_CBD;
+ }
+ else if (b0 == (byte)0x23) {
+ if (b1 == (byte)0x20 && b2 == (byte)0x24 && b3 == (byte)0x49 &&
+ b4 == (byte)0x64 && b5 == (byte)0x3A &&
+ b[6] == (byte)0x20) {
+ return T_SID;
+ }
+ else if (b1 == (byte)0x56 && b2 == (byte)0x52 && b3 == (byte)0x4D &&
+ b4 == (byte)0x4C && b5 == (byte)0x20 &&
+ b[6] == (byte)0x56 && b[7] == (byte)0x32 && b[8] == (byte)0x2E && b[9] == (byte)0x30) {
+ return T_WRL;
+ }
+ else if (b1 == (byte)0x64 && b2 == (byte)0x65 && b3 == (byte)0x66 &&
+ b4 == (byte)0x69 && b5 == (byte)0x6E &&
+ b[6] == (byte)0x65) {
+ return T_XBM;
+ }
+ }
+ else if (b0 == (byte)0x2A && b1 == (byte)0x2A && b2 == (byte)0x54 && b3 == (byte)0x49 &&
+ b4 == (byte)0x39 && b5 == (byte)0x32 &&
+ b[6] == (byte)0x2A && b[7] == (byte)0x2A && b[8] == (byte)0x01 && b[9] == (byte)0x00 && b[10] == (byte)0x58 &&
+ b[11] == (byte)0x6E && b[12] == (byte)0x56 && b[13] == (byte)0x69) {
+ return T_92I;
+ }
+ else if (b0 == (byte)0x2F && b1 == (byte)0x2A && b2 == (byte)0x20 && b3 == (byte)0x58 &&
+ b4 == (byte)0x50 && b5 == (byte)0x4D &&
+ b[6] == (byte)0x20 && b[7] == (byte)0x2A && b[8] == (byte)0x2F) {
+ return T_XPM;
+ }
+ else if (b0 == (byte)0x33 && b1 == (byte)0x44 && b2 == (byte)0x4D && b3 == (byte)0x46) {
+ return T_3DMF;
+ }
+ else if (b0 == (byte)0x35 && b1 == (byte)0x4B && b2 == (byte)0x50 && b3 == (byte)0x35 &&
+ b4 == (byte)0x31 && b5 == (byte)0x5D &&
+ b[6] == (byte)0x2A && b[7] == (byte)0x67 && b[8] == (byte)0x72 && b[9] == (byte)0x72 && b[10] == (byte)0x80 &&
+ b[11] == (byte)0x83 && b[12] == (byte)0x85 && b[13] == (byte)0x63) {
+ return T_HRU;
+ }
+ else if (b0 == (byte)0x36 && b1 == (byte)0x34 && b2 == (byte)0x4C && b3 == (byte)0x41 &&
+ b4 == (byte)0x4E && b5 == (byte)0x20 &&
+ b[6] == (byte)0x49 && b[7] == (byte)0x44 && b[8] == (byte)0x42 && b[9] == (byte)0x4C && b[10] == (byte)0x4F &&
+ b[11] == (byte)0x43 && b[12] == (byte)0x4B) {
+ return T_L64;
+ }
+ else if (b0 == (byte)0x37 && b1 == (byte)0x00 && b2 == (byte)0x00 && b3 == (byte)0x10 &&
+ b4 == (byte)0x42 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x10 && b[8] == (byte)0x00 && b[9] == (byte)0x00 && b[10] == (byte)0x00 &&
+ b[11] == (byte)0x00 && b[12] == (byte)0x39 && b[13] == (byte)0x64) {
+ return T_MBM;
+ }
+ else if (b0 == (byte)0x38 && b1 == (byte)0x42 && b2 == (byte)0x50 && b3 == (byte)0x53 &&
+ b4 == (byte)0x00 && b5 == (byte)0x01 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x00 && b[8] == (byte)0x00 && b[9] == (byte)0x00) {
+ return T_PSD;
+ }
+ else if (b0 == (byte)0x3A && b1 == (byte)0xDE && b2 == (byte)0x68 && b3 == (byte)0xB1) {
+ return T_DCX;
+ }
+ else if (b0 == (byte)0x3D && b1 == (byte)0x02) {
+ return T_3D2;
+ }
+ else if (b0 == (byte)0x41) {
+ if (b1 == (byte)0x43 && b2 == (byte)0x31 && b3 == (byte)0x30) {
+ return T_DWG;
+ }
+ else if (b1 == (byte)0x48) {
+ return T_PAL;
+ }
+ else if (b1 == (byte)0x4D && b2 == (byte)0x46 && b3 == (byte)0x46) {
+ return T_AMFF;
+ }
+ else if (b1 == (byte)0x75 && b2 == (byte)0x74 && b3 == (byte)0x6F &&
+ b4 == (byte)0x43 && b5 == (byte)0x41 &&
+ b[6] == (byte)0x44 && b[7] == (byte)0x20 && b[8] == (byte)0x53 && b[9] == (byte)0x6C && b[10] == (byte)0x69 &&
+ b[11] == (byte)0x64 && b[12] == (byte)0x65) {
+ return T_SLD;
+ }
+ }
+ else if (b0 == (byte)0x42 && b1 == (byte)0x4D) {
+ if (b2 == (byte)0x36) {
+ // FIXME: Collision or subtype of T_BMP?
+ return T_DIB;
+ } else {
+ return T_BMP;
+ }
+ }
+ else if (b0 == (byte)0x43) {
+ if (b1 == (byte)0x36 && b2 == (byte)0x34) {
+ return T_N64;
+ }
+ else if (b1 == (byte)0x41 && b2 == (byte)0x4C && b3 == (byte)0x41 &&
+ b4 == (byte)0x4D && b5 == (byte)0x55 &&
+ b[6] == (byte)0x53 && b[7] == (byte)0x43 && b[8] == (byte)0x56 && b[9] == (byte)0x47) {
+ return T_CVG;
+ }
+ else if (b1 == (byte)0x50 && b2 == (byte)0x54 && b3 == (byte)0x46 &&
+ b4 == (byte)0x49 && b5 == (byte)0x4C &&
+ b[6] == (byte)0x45) {
+ return T_CPT;
+ }
+ else if (b1 == (byte)0x61 && b2 == (byte)0x6C && b3 == (byte)0x69 &&
+ b4 == (byte)0x67 && b5 == (byte)0x61 &&
+ b[6] == (byte)0x72 && b[7] == (byte)0x69) {
+ return T_COB;
+ }
+ }
+ else if (b0 == (byte)0x44) {
+ if (b1 == (byte)0x44 && b2 == (byte)0x53 && b3 == (byte)0x20) {
+ return T_DDS;
+ }
+ else if (b1 == (byte)0x61 && b2 == (byte)0x6E && b3 == (byte)0x4D) {
+ return T_MSP;
+ }
+ }
+ else if (b0 == (byte)0x45) {
+ if (b1 == (byte)0x59 && b2 == (byte)0x45 && b3 == (byte)0x53) {
+ return T_CE2;
+ }
+ else if (b1 == (byte)0x78 && b2 == (byte)0x69 && b3 == (byte)0x66) { /* EXIF */
+ /**
+ * (b0 == (byte)0x45 && b1 == (byte)0x78 && b2 == (byte)0x69 && b3 == (byte)0x66) || // EXIF
+ * (b0 == (byte)0x4A && b1 == (byte)0x46 && b2 == (byte)0x49 && b3 == (byte)0x46) || // JFIF
+ * (b0 == (byte)0xff && b1 == (byte)0xd8 ) // && b2 == (byte)0xff
+ */
+ return T_JPG;
+ }
+ }
+ else if (b0 == (byte)0x46 && b1 == (byte)0x4F && b2 == (byte)0x52 && b3 == (byte)0x4D) {
+ if (b4 == (byte)0x41 && b5 == (byte)0x54 && b[6] == (byte)0x3D) {
+ // FIXME: Collision or subtype of T_LBM?
+ return T_RAD;
+ } else {
+ return T_LBM;
+ }
+ }
+ else if (b0 == (byte)0x47 && b1 == (byte)0x49 && b2 == (byte)0x46 && b3 == (byte)0x38 &&
+ (b4 == (byte)0x37 || b4 == (byte)0x39) && b5 == (byte)0x61) {
+ return T_GIF;
+ }
+ else if (b0 == (byte)0x48 && b1 == (byte)0x50 && b2 == (byte)0x48 && b3 == (byte)0x50 &&
+ b4 == (byte)0x34 && b5 == (byte)0x38 &&
+ b[6] == (byte)0x2D && b[7] == (byte)0x45 && b[8] == (byte)0x1E && b[9] == (byte)0x2B) {
+ return T_GRO;
+ }
+ else if (b0 == (byte)0x49) {
+ if (b1 == (byte)0x49 && b2 == (byte)0x2A && b3 == (byte)0x00) {
+ if (b4 == (byte)0x08 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x00 && b[8] == (byte)0x0E && b[9] == (byte)0x00 && b[10] == (byte)0x00 &&
+ b[11] == (byte)0x01 && b[12] == (byte)0x04 && b[13] == (byte)0x00) {
+ // FIXME: Collision or subtype of T_TIFF?
+ return T_LDF;
+ } else {
+ return T_TIFF;
+ }
+ }
+ else if (b1 == (byte)0x57 && b2 == (byte)0x43 && b3 == (byte)0x01) {
+ return T_IWC;
+ }
+ }
+ else if (b0 == (byte)0x4A) {
+ if (b1 == (byte)0x46 && b2 == (byte)0x49 && b3 == (byte)0x46) { /* JFIF */
+ /**
+ * (b0 == (byte)0x45 && b1 == (byte)0x78 && b2 == (byte)0x69 && b3 == (byte)0x66) || // EXIF
+ * (b0 == (byte)0x4A && b1 == (byte)0x46 && b2 == (byte)0x49 && b3 == (byte)0x46) || // JFIF
+ * (b0 == (byte)0xff && b1 == (byte)0xd8 ) // && b2 == (byte)0xff
+ */
+ return T_JPG;
+ }
+ else if (b1 == (byte)0x47 && (b2 == (byte)0x03 || b2 == (byte)0x04) && b3 == (byte)0x0E &&
+ b4 == (byte)0x00 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x00) {
+ return T_ART;
+ }
+ else if (b1 == (byte)0x49 && b2 == (byte)0x46 && b3 == (byte)0x39 &&
+ b4 == (byte)0x39 && b5 == (byte)0x61) {
+ return T_JIF;
+ }
+ }
+ else if (b0 == (byte)0x4D) {
+ if (b1 == (byte)0x47 && b2 == (byte)0x4C) {
+ return T_MGL;
+ }
+ else if (b1 == (byte)0x4D && b2 == (byte)0x00 && b3 == (byte)0x2A) {
+ return T_KDC;
+ }
+ else if (b1 == (byte)0x50 && b2 == (byte)0x46) {
+ return T_MPW;
+ }
+ }
+ else if (b0 == (byte)0x4E) {
+ if (b1 == (byte)0x47 && b2 == (byte)0x47 && b3 == (byte)0x00 &&
+ b4 == (byte)0x01 && b5 == (byte)0x00) {
+ return T_NGG;
+ }
+ else if (b1 == (byte)0x4C && b2 == (byte)0x4D && b3 == (byte)0x20 &&
+ b4 == (byte)0x01 && b5 == (byte)0x02 &&
+ b[6] == (byte)0x00) {
+ return T_NLM;
+ }
+ else if (b1 == (byte)0x4F && b2 == (byte)0x4C && b3 == (byte)0x00 &&
+ b4 == (byte)0x01 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x06 && b[7] == (byte)0x01 && b[8] == (byte)0x03 && b[9] == (byte)0x00) {
+ return T_NOL;
+ }
+ }
+ else if (b0 == (byte)0x50) {
+ if (b1 == (byte)0x31 /* plain */|| b1 == (byte)0x34) {
+ return T_PBM;
+ }
+ else if (b1 == (byte)0x32 /* plain */|| b1 == (byte)0x35) {
+ return T_PGM;
+ }
+ else if (b1 == (byte)0x33 /* plain */|| b1 == (byte)0x36) {
+ return T_PPM;
+ }
+ else if (b1 == (byte)0x37) {
+ return T_PAM;
+ }
+ else if (b1 == (byte)0x41 && b2 == (byte)0x58) {
+ return T_PAX;
+ }
+ else if (b1 == (byte)0x49 && b2 == (byte)0x58 && b3 == (byte)0x20) {
+ return T_PIX;
+ }
+ else if (b1 == (byte)0x4F && b2 == (byte)0x4C && b3 == (byte)0x20 &&
+ b4 == (byte)0x46 && b5 == (byte)0x6F &&
+ b[6] == (byte)0x72 && b[7] == (byte)0x6D && b[8] == (byte)0x61 && b[9] == (byte)0x74) {
+ return T_POL;
+ }
+ else if (b1 == (byte)0x61 && b2 == (byte)0x69 && b3 == (byte)0x6E &&
+ b4 == (byte)0x74 && b5 == (byte)0x20 &&
+ b[6] == (byte)0x53 && b[7] == (byte)0x68 && b[8] == (byte)0x6F && b[9] == (byte)0x70 && b[10] == (byte)0x20 &&
+ b[11] == (byte)0x50 && b[12] == (byte)0x72 && b[13] == (byte)0x6F && b[14] == (byte)0x20 && b[15] == (byte)0x49 &&
+ b[16] == (byte)0x6D && b[17] == (byte)0x61 && b[18] == (byte)0x67 && b[19] == (byte)0x65 && b[20] == (byte)0x20 &&
+ b[21] == (byte)0x46 && b[22] == (byte)0x69 && b[23] == (byte)0x6C && b[24] == (byte)0x65) {
+ return T_PSP;
+ }
+ }
+ else if (b0 == (byte)0x51 && b1 == (byte)0x4C && b2 == (byte)0x49 && b3 == (byte)0x49 &&
+ b4 == (byte)0x46 && b5 == (byte)0x41 &&
+ b[6] == (byte)0x58) {
+ return T_QFX;
+ }
+ else if (b0 == (byte)0x52 && b1 == (byte)0x49 && b2 == (byte)0x58 && b3 == (byte)0x33) {
+ return T_RIX;
+ }
+ else if (b0 == (byte)0x53) {
+ if (b1 == (byte)0x44 && b2 == (byte)0x50 && b3 == (byte)0x58) {
+ return T_DPX;
+ }
+ else if (b1 == (byte)0x49 && b2 == (byte)0x4D && b3 == (byte)0x50 &&
+ b4 == (byte)0x4C && b5 == (byte)0x45 &&
+ b[6] == (byte)0x20 && b[7] == (byte)0x20 && b[8] == (byte)0x3D) {
+ return T_FTS;
+ }
+ else if (b1 == (byte)0x74 && b2 == (byte)0x6F && b3 == (byte)0x72 &&
+ b4 == (byte)0x6D && b5 == (byte)0x33 &&
+ b[6] == (byte)0x44) {
+ return T_SOD;
+ }
+ else if (b1 == (byte)0x80 && b2 == (byte)0xf6 && b3 == (byte)0x34) {
+ return T_PIC;
+ }
+ }
+ else if (b0 == (byte)0x56 && b1 == (byte)0x69 && b2 == (byte)0x73 && b3 == (byte)0x74 &&
+ b4 == (byte)0x61 && b5 == (byte)0x20 &&
+ b[6] == (byte)0x44 && b[7] == (byte)0x45 && b[8] == (byte)0x4D && b[9] == (byte)0x20 && b[10] == (byte)0x46 &&
+ b[11] == (byte)0x69 && b[12] == (byte)0x6C && b[13] == (byte)0x65) {
+ return T_DEM;
+ }
+ else if (b0 == (byte)0x57 && b1 == (byte)0x56 && b2 == (byte)0x02 && b3 == (byte)0x00 &&
+ b4 == (byte)0x47 && b5 == (byte)0x45 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x0E) {
+ return T_LWF;
+ }
+ else if (b0 == (byte)0x59 && b1 == (byte)0xA6 && b2 == (byte)0x6A && b3 == (byte)0x95) {
+ return T_RAS;
+ }
+ else if (b0 == (byte)0x63 && b1 == (byte)0x52 && b2 == (byte)0x01 && b3 == (byte)0x01 &&
+ b4 == (byte)0x38 && b5 == (byte)0x09 &&
+ b[6] == (byte)0x3D && b[7] == (byte)0x00) {
+ return T_PCD;
+ }
+ else if (b0 == (byte)0x65) {
+ if (b1 == (byte)0x02 && b2 == (byte)0x01 && b3 == (byte)0x02) {
+ return T_ECW;
+ }
+ else if (b1 == (byte)0x6C && b2 == (byte)0x6D && b3 == (byte)0x6F) {
+ return T_INFINI_D;
+ }
+ }
+ else if (b0 == (byte)0x69 && b1 == (byte)0x63 && b2 == (byte)0x6E && b3 == (byte)0x73) {
+ return T_ICNS;
+ }
+ else if (b0 == (byte)0x6D && b1 == (byte)0x6F && b2 == (byte)0x6F && b3 == (byte)0x76) {
+ return T_QTM;
+ }
+ else if (b0 == (byte)0x6E) {
+ if (b1 == (byte)0x63 && b2 == (byte)0x6F && b3 == (byte)0x6C &&
+ b4 == (byte)0x73) {
+ return T_HDR;
+ }
+ else if (b1 == (byte)0x66 && b2 == (byte)0x66) {
+ return T_NFF;
+ }
+ else if (b1 == (byte)0x6E && b2 == (byte)0x0A && b3 == (byte)0x00 &&
+ b4 == (byte)0x5E && b5 == (byte)0x00) {
+ return T_NCR;
+ }
+ }
+ else if (b0 == (byte)0x73 && b1 == (byte)0x72 && b2 == (byte)0x63 && b3 == (byte)0x64 &&
+ b4 == (byte)0x6F && b5 == (byte)0x63 &&
+ b[6] == (byte)0x69 && b[7] == (byte)0x64 && b[8] == (byte)0x3A) {
+ return T_CALS;
+ }
+ else if (b0 == (byte)0x7B && b1 == (byte)0x0A && b2 == (byte)0x20 && b3 == (byte)0x20 &&
+ b4 == (byte)0x43 && b5 == (byte)0x72 &&
+ b[6] == (byte)0x65 && b[7] == (byte)0x61 && b[8] == (byte)0x74 && b[9] == (byte)0x65 && b[10] == (byte)0x64) {
+ return T_MIF;
+ }
+ else if (b0 == (byte)0x7E && b1 == (byte)0x42 && b2 == (byte)0x4B && b3 == (byte)0x00) {
+ return T_PSP;
+ }
+ else if (b0 == (byte)0x80) {
+ if (b1 == (byte)0x2A && b2 == (byte)0x5F && b3 == (byte)0xD7 &&
+ b4 == (byte)0x00 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x08 && b[7] == (byte)0x00 && b[8] == (byte)0x00 && b[9] == (byte)0x00 && b[10] == (byte)0x04 &&
+ b[11] == (byte)0x00 && b[12] == (byte)0x00 && b[13] == (byte)0x00) {
+ return T_CIN;
+ }
+ else if (b1 == (byte)0x3E && b2 == (byte)0x44 && b3 == (byte)0x53 &&
+ b4 == (byte)0x43 && b5 == (byte)0x49 &&
+ b[6] == (byte)0x4D) {
+ return T_J6I;
+ }
+ }
+ else if (b0 == (byte)0x89 && b1 == (byte)0x50 && b2 == (byte)0x4E && b3 == (byte)0x47 && /* 'P' 'N' 'G', ascii code */
+ b4 == (byte)0x0D && b5 == (byte)0x0A && b[6] == (byte)0x1A && b[7] == (byte)0x0A) {
+ // -119, 80, 78, 71, 13, 10, 26, 10
+ return T_PNG;
+ }
+ else if (b0 == (byte)0x8A && b1 == (byte)0x4D && b2 == (byte)0x4E && b3 == (byte)0x47 &&
+ b4 == (byte)0x0D && b5 == (byte)0x0A &&
+ b[6] == (byte)0x1A && b[7] == (byte)0x0A) {
+ return T_MNG;
+ }
+ else if (b0 == (byte)0xD0 && b1 == (byte)0xCF && b2 == (byte)0x11 && b3 == (byte)0xE0 &&
+ b4 == (byte)0xA1 && b5 == (byte)0xB1 &&
+ b[6] == (byte)0x1A && b[7] == (byte)0xE1 && b[8] == (byte)0x00) {
+ return T_FPX;
+ }
+ else if (b0 == (byte)0xD3 && b1 == (byte)0x23 && b2 == (byte)0x00 && b3 == (byte)0x00 &&
+ b4 == (byte)0x03 && b5 == (byte)0x00 &&
+ b[6] == (byte)0x00 && b[7] == (byte)0x00) {
+ return T_WLM;
+ }
+ else if (b0 == (byte)0xD7 && b1 == (byte)0xCD && b2 == (byte)0xC6 && b3 == (byte)0x9A) {
+ return T_WMF;
+ }
+ else if (b0 == (byte)0xEB && b1 == (byte)0x3C && b2 == (byte)0x90 && b3 == (byte)0x2A) {
+ return T_IMG;
+ }
+ else if (b0 == (byte)0xFA && b1 == (byte)0xDE && b2 == (byte)0xBA && b3 == (byte)0xBE &&
+ b4 == (byte)0x01 && b5 == (byte)0x01) {
+ return T_WIC;
+ }
+ else if (b0 == (byte)0xFF) {
+ if (b1 == (byte)0xD8 /* && b2 == (byte)0xff */) {
+ /**
+ * (b0 == (byte)0x45 && b1 == (byte)0x78 && b2 == (byte)0x69 && b3 == (byte)0x66) || // EXIF
+ * (b0 == (byte)0x4A && b1 == (byte)0x46 && b2 == (byte)0x49 && b3 == (byte)0x46) || // JFIF
+ * (b0 == (byte)0xff && b1 == (byte)0xd8 ) // && b2 == (byte)0xff
+ */
+ return T_JPG;
+ }
+ else if (b1 == (byte)0x57 && b2 == (byte)0x50 && b3 == (byte)0x43 && b4 == (byte)0x10) {
+ return T_WPG;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/net/opengrabeso/opengl/util/texture/Texture.java b/src/main/java/net/opengrabeso/opengl/util/texture/Texture.java
new file mode 100644
index 00000000..b75451fe
--- /dev/null
+++ b/src/main/java/net/opengrabeso/opengl/util/texture/Texture.java
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+package net.opengrabeso.opengl.util.texture;
+
+import com.github.opengrabeso.jaagl.GL;
+
+import java.awt.image.DataBufferByte;
+import java.nio.*;
+
+/**
+ * Represents an OpenGL texture object. Contains convenience routines
+ * for enabling/disabling OpenGL texture state, binding this texture,
+ * and computing texture coordinates for both the entire image as well
+ * as a sub-image.
+ *
+ * Order of Texture Commands
+ *
+ *
+ * gl.glActiveTexture(GL.GL_TEXTURE0 + textureUnit)
, 0
is default.textureId
-> active textureUnit
's textureTarget
via gl.glBindTexture(textureTarget, textureId)
textureUnit
's textureTarget
via glEnable(textureTarget)
.
+ * textureUnit
in your shader program, enable shader program.
When creating an OpenGL texture object, the Texture class will
+ * attempt to use non-power-of-two textures.
+ *
+ *
For best performance, try to avoid calling {@link #enable} /
+ * {@link #bind} / {@link #disable} any more than necessary. For
+ * example, applications using many Texture objects in the same scene
+ * may want to reduce the number of calls to both {@link #enable} and
+ * {@link #disable}. To do this it is necessary to call {@link
+ * #getTarget} to make sure the OpenGL texture target is the same for
+ * all of the Texture objects in use; non-power-of-two textures using
+ * the GL_ARB_texture_rectangle extension use a different target than
+ * power-of-two textures using the GL_TEXTURE_2D target. Note that
+ * when switching between textures it is necessary to call {@link
+ * #bind}, but when drawing many triangles all using the same texture,
+ * for best performance only one call to {@link #bind} should be made.
+ * User may also utilize multiple texture units,
+ * see order of texture commands above.
+ *
+ *
+ *
+ gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
+
+ * Also, when using a texture function like GL_MODULATE
where
+ * the current color plays a role, it is important to remember to make
+ * sure that the color is specified in a premultiplied form, for
+ * example:
+
+ float a = ...;
+ float r = r * a;
+ float g = g * a;
+ float b = b * a;
+ gl.glColor4f(r, g, b, a);
+
+ *
+ * For reference, here is a list of the Porter-Duff compositing rules
+ * and the associated OpenGL blend functions (source and destination
+ * factors) to use in the face of premultiplied alpha:
+ *
+
+
+ Rule Source Dest
+ Clear GL_ZERO GL_ZERO
+ Src GL_ONE GL_ZERO
+ SrcOver GL_ONE GL_ONE_MINUS_SRC_ALPHA
+ DstOver GL_ONE_MINUS_DST_ALPHA GL_ONE
+ SrcIn GL_DST_ALPHA GL_ZERO
+ DstIn GL_ZERO GL_SRC_ALPHA
+ SrcOut GL_ONE_MINUS_DST_ALPHA GL_ZERO
+ DstOut GL_ZERO GL_ONE_MINUS_SRC_ALPHA
+ Dst GL_ZERO GL_ONE
+ SrcAtop GL_DST_ALPHA GL_ONE_MINUS_SRC_ALPHA
+ DstAtop GL_ONE_MINUS_DST_ALPHA GL_SRC_ALPHA
+ AlphaXor GL_ONE_MINUS_DST_ALPHA GL_ONE_MINUS_SRC_ALPHA
+
+ * gl.glEnable(texture.getTarget());
+ *
+ *
+ * gl.glDisable(texture.getTarget());
+ *
+ *
+ gl.glBindTexture(texture.getTarget(), texture.getTextureObject());
+
+ *
+ * See the performance tips above for hints
+ * on how to maximize performance when using many Texture objects.
+ *
+ * @param gl the current GL context
+
+ * OpenGL-related errors occurred
+ */
+ public void bind(final GL gl) {
+ validateTexID(gl, true);
+ gl.glBindTexture(target, texID);
+ }
+
+ /**
+ * Destroys the native resources used by this texture object.
+ *
+
+ */
+ public void destroy(final GL gl) {
+ if(0!=texID) {
+ gl.glDeleteTextures(new int[] {texID});
+ texID = 0;
+ }
+ }
+
+ /**
+ * Returns the OpenGL "target" of this texture.
+ * @see com.jogamp.opengl.GL#GL_TEXTURE_2D
+ * @see com.jogamp.opengl.GL2#GL_TEXTURE_RECTANGLE_ARB
+ */
+ public int getTarget() {
+ return target;
+ }
+
+ /**
+ * Returns the image OpenGL "target" of this texture, or its sub-components if cubemap.
+ * @see com.jogamp.opengl.GL#GL_TEXTURE_2D
+ * @see com.jogamp.opengl.GL2#GL_TEXTURE_RECTANGLE_ARB
+ */
+ public int getImageTarget() {
+ return imageTarget;
+ }
+
+ /**
+ * Returns the width of the allocated OpenGL texture in pixels.
+ * Note that the texture width will be greater than or equal to the
+ * width of the image contained within.
+ *
+ * @return the width of the texture
+ */
+ public int getWidth() {
+ return texWidth;
+ }
+
+ /**
+ * Returns the height of the allocated OpenGL texture in pixels.
+ * Note that the texture height will be greater than or equal to the
+ * height of the image contained within.
+ *
+ * @return the height of the texture
+ */
+ public int getHeight() {
+ return texHeight;
+ }
+
+ /**
+ * Returns the width of the image contained within this texture.
+ * Note that for non-power-of-two textures in particular this may
+ * not be equal to the result of {@link #getWidth}. It is
+ * recommended that applications call {@link #getImageTexCoords} and
+ * {@link #getSubImageTexCoords} rather than using this API
+ * directly.
+ *
+ * @return the width of the image
+ */
+ public int getImageWidth() {
+ return imgWidth;
+ }
+
+ /**
+ * Returns the height of the image contained within this texture.
+ * Note that for non-power-of-two textures in particular this may
+ * not be equal to the result of {@link #getHeight}. It is
+ * recommended that applications call {@link #getImageTexCoords} and
+ * {@link #getSubImageTexCoords} rather than using this API
+ * directly.
+ *
+ * @return the height of the image
+ */
+ public int getImageHeight() {
+ return imgHeight;
+ }
+
+ /**
+ * Returns the original aspect ratio of the image, defined as (image
+ * width) / (image height), before any scaling that might have
+ * occurred as a result of using the GLU mipmap routines.
+ */
+ public float getAspectRatio() {
+ return aspectRatio;
+ }
+
+ /**
+ * Returns the set of texture coordinates corresponding to the
+ * entire image. If the TextureData indicated that the texture
+ * coordinates must be flipped vertically, the returned
+ * TextureCoords will take that into account.
+ *
+ * @return the texture coordinates corresponding to the entire image
+ */
+ public TextureCoords getImageTexCoords() {
+ return coords;
+ }
+
+ /**
+ * Returns the set of texture coordinates corresponding to the
+ * specified sub-image. The (x1, y1) and (x2, y2) points are
+ * specified in terms of pixels starting from the lower-left of the
+ * image. (x1, y1) should specify the lower-left corner of the
+ * sub-image and (x2, y2) the upper-right corner of the sub-image.
+ * If the TextureData indicated that the texture coordinates must be
+ * flipped vertically, the returned TextureCoords will take that
+ * into account; this should not be handled by the end user in the
+ * specification of the y1 and y2 coordinates.
+ *
+ * @return the texture coordinates corresponding to the specified sub-image
+ */
+ public TextureCoords getSubImageTexCoords(final int x1, final int y1, final int x2, final int y2) {
+ final float tx1 = (float)x1 / (float)texWidth;
+ final float ty1 = (float)y1 / (float)texHeight;
+ final float tx2 = (float)x2 / (float)texWidth;
+ final float ty2 = (float)y2 / (float)texHeight;
+ if (mustFlipVertically) {
+ final float yMax = (float) imgHeight / (float) texHeight;
+ return new TextureCoords(tx1, yMax - ty1, tx2, yMax - ty2);
+ } else {
+ return new TextureCoords(tx1, ty1, tx2, ty2);
+ }
+ }
+
+ /**
+ * Updates the entire content area incl. {@link TextureCoords}
+ * of this texture using the data in the given image.
+ *
+
+ */
+ public void updateImage(final GL gl, final TextureData data) {
+ updateImage(gl, data, 0);
+ }
+
+ /**
+ * Indicates whether this texture's texture coordinates must be
+ * flipped vertically in order to properly display the texture. This
+ * is handled automatically by {@link #getImageTexCoords
+ * getImageTexCoords} and {@link #getSubImageTexCoords
+ * getSubImageTexCoords}, but applications may generate or otherwise
+ * produce texture coordinates which must be corrected.
+ */
+ public boolean getMustFlipVertically() {
+ return mustFlipVertically;
+ }
+
+ /**
+ * Change whether the TextureData requires a vertical flip of
+ * the texture coords.
+ * null
.
+ * @see #getTextureObject()
+ */
+ public int getTextureObject(final GL gl) {
+ validateTexID(gl, false);
+ return texID;
+ }
+
+ /**
+ * Returns the underlying OpenGL texture object for this texture,
+ * maybe 0
if not yet generated.
+ * {s * ss, t * ts}
from this object into the given float[8+d_off]
in the following order:
+ *
+ * left, bottom
+ * right, bottom
+ * left, top
+ * right top
+ *
+ */
+ public float[] getST_LB_RB_LT_RT(final float[] d, final int d_off, final float ss, final float ts) {
+ d[0+d_off] = left *ss; d[1+d_off] = bottom*ts;
+ d[2+d_off] = right *ss; d[3+d_off] = bottom*ts;
+ d[4+d_off] = left *ss; d[5+d_off] = top *ts;
+ d[6+d_off] = right *ss; d[7+d_off] = top *ts;
+ return d;
+ }
+
+ /** Returns the leftmost (x) texture coordinate of this
+ rectangle. */
+ public float left() { return left; }
+
+ /** Returns the rightmost (x) texture coordinate of this
+ rectangle. */
+ public float right() { return right; }
+
+ /** Returns the bottommost (y) texture coordinate of this
+ rectangle. */
+ public float bottom() { return bottom; }
+
+ /** Returns the topmost (y) texture coordinate of this
+ rectangle. */
+ public float top() { return top; }
+
+ @Override
+ public String toString() { return "TexCoord[h: "+left+" - "+right+", v: "+bottom+" - "+top+"]"; }
+}
diff --git a/src/main/java/net/opengrabeso/opengl/util/texture/TextureData.java b/src/main/java/net/opengrabeso/opengl/util/texture/TextureData.java
new file mode 100644
index 00000000..5b716637
--- /dev/null
+++ b/src/main/java/net/opengrabeso/opengl/util/texture/TextureData.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+package net.opengrabeso.opengl.util.texture;
+
+import java.nio.ByteBuffer;
+
+import net.opengrabeso.opengl.util.GLPixelAttributes;
+
+/**
+ * Represents the data for an OpenGL texture. This is separated from
+ * the notion of a Texture to support things like streaming in of
+ * textures in a background thread without requiring an OpenGL context
+ * to be current on that thread.
+ *
+ * @author Chris Campbell
+ * @author Kenneth Russell
+ * @author Sven Gothel
+ */
+
+public class TextureData {
+ protected int width;
+ protected int height;
+ private int border;
+ protected GLPixelAttributes pixelAttributes;
+ protected int internalFormat; // perhaps inferred from pixelFormat?
+ protected boolean mipmap; // indicates whether mipmaps should be generated
+ protected boolean mustFlipVertically; // Must flip texture coordinates
+ // vertically to get OpenGL output
+ // to look correct
+ protected ByteBuffer buffer; // the actual data...
+ private ByteBuffer[] mipmapData; // ...or a series of mipmaps
+ private Flusher flusher;
+ protected int rowLength;
+ protected int alignment; // 1, 2, or 4 bytes
+
+ /** Used only by subclasses */
+ protected TextureData() { this.pixelAttributes = GLPixelAttributes.UNDEF; }
+
+ public TextureData(final int internalFormat,
+ final int width,
+ final int height,
+ final int border,
+ final int dataFormat,
+ final int dataType,
+ final boolean mipmap,
+ final boolean dataIsCompressed,
+ final boolean mustFlipVertically,
+ final ByteBuffer buffer,
+ final Flusher flusher) throws IllegalArgumentException {
+ if (mipmap && dataIsCompressed) {
+ throw new IllegalArgumentException("Can not generate mipmaps for compressed textures");
+ }
+
+ this.width = width;
+ this.height = height;
+ this.border = border;
+ this.pixelAttributes = new GLPixelAttributes(dataFormat, dataType);
+ this.internalFormat = internalFormat;
+ this.mipmap = mipmap;
+ this.mustFlipVertically = mustFlipVertically;
+ this.buffer = buffer;
+ this.flusher = flusher;
+ alignment = 1; // FIXME: is this correct enough in all situations?
+ }
+
+ /** Returns the width in pixels of the texture data. */
+ public int getWidth() { return width; }
+ /** Returns the height in pixels of the texture data. */
+ public int getHeight() { return height; }
+ /** Returns the border in pixels of the texture data. */
+ public int getBorder() {
+ return border;
+ }
+ /** Returns the intended OpenGL {@link GLPixelAttributes} of the texture data, i.e. format and type. */
+ public GLPixelAttributes getPixelAttributes() {
+ return pixelAttributes;
+ }
+ /** Returns the intended OpenGL pixel format of the texture data using {@link #getPixelAttributes()}. */
+ public int getPixelFormat() {
+ return pixelAttributes.format;
+ }
+ /** Returns the intended OpenGL pixel type of the texture data using {@link #getPixelAttributes()}. */
+ public int getPixelType() {
+ return pixelAttributes.type;
+ }
+ /** Returns the intended OpenGL internal format of the texture data. */
+ public int getInternalFormat() {
+ return internalFormat;
+ }
+ /** Returns whether mipmaps should be generated for the texture data. */
+ public boolean getMipmap() {
+ return mipmap;
+ }
+
+ /** Indicates whether the texture coordinates must be flipped
+ vertically for proper display. */
+ public boolean getMustFlipVertically() {
+ return mustFlipVertically;
+ }
+ /** Returns the texture data, or null if it is specified as a set of mipmaps. */
+ public ByteBuffer getBuffer() {
+ return buffer;
+ }
+ /** Returns all mipmap levels for the texture data, or null if it is
+ specified as a single image. */
+ public ByteBuffer[] getMipmapData() {
+ return mipmapData;
+ }
+ /** Returns the required byte alignment for the texture data. */
+ public int getAlignment() {
+ return alignment;
+ }
+ /** Returns the row length needed for correct GL_UNPACK_ROW_LENGTH
+ specification. This is currently only supported for
+ non-mipmapped, non-compressed textures. */
+ public int getRowLength() {
+ return rowLength;
+ }
+
+ /** Sets the width in pixels of the texture data. */
+ public void setWidth(final int width) { this.width = width; }
+ /** Sets the height in pixels of the texture data. */
+ public void setHeight(final int height) { this.height = height; }
+ /** Sets the border in pixels of the texture data. */
+ public void setBorder(final int border) { this.border = border; }
+ /** Sets the intended OpenGL pixel format of the texture data. */
+ public void setPixelAttributes(final GLPixelAttributes pixelAttributes) { this.pixelAttributes = pixelAttributes; }
+ /**
+ * Sets the intended OpenGL pixel format component of {@link GLPixelAttributes} of the texture data.
+ *
+ * - texture-object
+ * - GL.GL_TEXTURE_MAG_FILTER
+ * - GL.GL_TEXTURE_MIN_FILTER
+ * - GL.GL_TEXTURE_WRAP_S
+ * - GL.GL_TEXTURE_WRAP_T
+ *
+ */
+public class TextureState {
+ /**
+ * Returns the pname
to query the textureTarget
currently bound to the active texture-unit.
+ * 0
is textureTarget
is not supported.
+ *
+ * 0 - unit
+ * 1 - texture object
+ * 2 - GL.GL_TEXTURE_MAG_FILTER
+ * 3 - GL.GL_TEXTURE_MIN_FILTER
+ * 4 - GL.GL_TEXTURE_WRAP_S
+ * 5 - GL.GL_TEXTURE_WRAP_T
+ *
+ */
+ private final int[] state = new int[] { 0, 0, 0, 0, 0, 0 };
+
+ private static final String toHexString(final int i) { return "0x"+Integer.toHexString(i); }
+
+ private static final int activeTexture(final GL gl) {
+ final int[] vi = { 0 };
+ gl.glGetIntegerv(GL.GL_ACTIVE_TEXTURE, vi, 0);
+ return vi[0];
+ }
+
+ /**
+ * Creates a texture state for the retrieved active texture-unit and the given texture-target.
+ * See {@link TextureState}.
+ * @param gl current GL context's GL object
+ * @param textureTarget
+
+ */
+ public TextureState(final GL gl, final int textureTarget) {
+ this(gl, activeTexture(gl), textureTarget);
+ }
+
+ /**
+ * Creates a texture state for the given active texture-unit and the given texture-target.
+ * See {@link TextureState}.
+ * @param gl current GL context's GL object
+ * @param textureUnit of range [ {@link GL#GL_TEXTURE0}.. ]
+ * @param textureTarget
+
+ */
+ public TextureState(final GL gl, final int textureUnit, final int textureTarget) {
+ target = textureTarget;
+ state[0] = textureUnit;
+ final int texBindQName = getTextureTargetQueryName(textureTarget);
+ if( 0 == texBindQName ) {
+ throw new GLException("Unsupported textureTarget "+toHexString(textureTarget));
+ }
+ gl.glGetIntegerv(texBindQName, state, 1);
+ gl.glGetTexParameteriv(target, GL.GL_TEXTURE_MAG_FILTER, state, 2);
+ gl.glGetTexParameteriv(target, GL.GL_TEXTURE_MIN_FILTER, state, 3);
+ gl.glGetTexParameteriv(target, GL.GL_TEXTURE_WRAP_S, state, 4);
+ gl.glGetTexParameteriv(target, GL.GL_TEXTURE_WRAP_T, state, 5);
+ }
+
+ /**
+ * Restores the texture-unit's texture-target state.
+ *