Permalink
Browse files

Moved repeatingness into Image and out of ImageLayer.

This eliminates one potential cause of wackiness (that undoubtedly no one ever
ran into) which is that the same image could be used in a layer that repeats in
x but not y and in another that repeats in y but not x and the image would end
up repeating one of the two ways (depending on which was drawn first).

This also makes it possible to draw a repeated image in a Surface by just
drawing the image at a larger size, instead of having to do the cumbersome
combination of setFillPattern and fillRect.

It also allows Image to avoid using unnecessary GPU memory in the case of
repeated images. Previously they would resolve the unrepeatable texture (which
is needed to create the repeatable texture) and then leave it in memory because
it wasn't easy to know whether the image would be used for repeatyness or not.
Now an image *only* has either a repeatable texure or an unrepeatable texture.

Lastly, it simplifies some things for a forthcoming change which will introduce
SurfaceImage (which will further simplify the API and allow surfaces to be
shared between multiple layers, which is currently not possible).

This does prevent code from using the same image as a repeating image and a
non-repeating image, but I highly doubt anyone is doing that. If you are, just
make a copy of the image.
  • Loading branch information...
1 parent 18ee030 commit 6b726f6c1855bdfbc1e88a80397b91a880cf0c60 @samskivert samskivert committed Mar 23, 2013
@@ -44,14 +44,14 @@ public Image snapshot() {
}
@Override
- public int ensureTexture(boolean repeatX, boolean repeatY) {
+ public int ensureTexture() {
// if we have a canvas, and it's dirty, force the recreation of our texture which will obtain
// the latest canvas data
if (canvas.dirty()) {
canvas.clearDirty();
clearTexture();
}
- return super.ensureTexture(repeatX, repeatY);
+ return super.ensureTexture();
}
@Override
@@ -48,6 +48,23 @@
*/
void addCallback(Callback<? super Image> callback);
+ /**
+ * Returns whether this image repeats in the x direction. See {@link #setRepeatX}.
+ */
+ boolean repeatX();
+
+ /**
+ * Returns whether this image repeats in the y direction. See {@link #setRepeatX}.
+ */
+ boolean repeatY();
+
+ /**
+ * Configures whether this image repeats in the x or y direction (rather than scaling) when used
+ * in a layer whose width or height is larger or smaller than the image. Both axes default to
+ * {@code false}.
+ */
+ void setRepeat(boolean repeatX, boolean repeatY);
+
/**
* A subregion of an image. See {@link Image#subImage}.
*/
@@ -117,29 +134,27 @@ void getRgb(int startX, int startY, int width, int height, int[] rgbArray,
/**
* Configures the use of mipmaps when rendering this image at scales less than 1. This only
- * applies to GL-based backends (it is a NOOP on other backends). It must also be called before
- * the image is rendered by any layer or by being drawn to a surface.
+ * applies to GL-based backends (it is a NOOP on other backends).
*/
void setMipmapped (boolean mipmapped);
/**
* Creates a texture for this image (if one does not already exist) and returns its OpenGL
* texture id. Returns 0 if the underlying image data is not yet ready or if this platform does
- * not use OpenGL. If either {@code repeatX} or {@code repeatY} are true, the underlying image
- * data will be scaled up into a power of two texture. The image will maintain one or both of an
- * unscaled and scaled texture until a call to {@link #clearTexture} is made or until this image
- * is garbage collected (at which time the textures are cleared).
+ * not use OpenGL. The texture will remain in GPU memory until {@link #clearTexture} is called or
+ * this image is garbage collected.
*
- * @param repeatX controls S texture wrapping parameter (repeat or clamp to edge).
- * @param repeatY controls T texture wrapping parameter (repeat or clamp to edge).
+ * <p><em>Note:</em> this texture handle should not be held for longer than the scope of the
+ * enclosing function call. It is not safe to hang onto an image's texture handle as the image
+ * may delete its texture and recreate it under any number of circumstances.</p>
*/
- int ensureTexture(boolean repeatX, boolean repeatY);
+ int ensureTexture();
/**
- * Clears the GPU texture(s) associated with this image, on platforms implemented via OpenGL.
- * Does nothing on non-OpenGL platforms. In general it is not necessary to call this method.
- * Images added to {@link ImageLayer} instances automatically clear their texture when the image
- * layer is removed from the scene graph. Textures are also cleared when the image is garbage
+ * Clears the GPU texture associated with this image, on platforms implemented via OpenGL. Does
+ * nothing on non-OpenGL platforms. In general it is not necessary to call this method. Images
+ * added to {@link ImageLayer} instances automatically clear their texture when the image layer
+ * is removed from the scene graph. Textures are also cleared when the image is garbage
* collected. However, if you manually call {@link #ensureTexture}, or if you draw images to a
* {@link Surface}, you may wish to clear textures manually to avoid running out of GPU memory
* before garbage collection has a chance to run and clear the textures for you.
@@ -33,18 +33,6 @@
*/
ImageLayer setImage(Image image);
- /**
- * Configures this layer to repeat its underlying image (instead of scaling it) when this layer's
- * width differs from that of its underlying image. The default is {@code false}.
- */
- void setRepeatX(boolean repeat);
-
- /**
- * Configures this layer to repeat its underlying image (instead of scaling it) when this layer's
- * height differs from that of its underlying image. The default is {@code false}.
- */
- void setRepeatY(boolean repeat);
-
/**
* Configures the size of this layer. The size defaults to the size of the image contained by the
* layer, but if it is set manually then the image will be scaled or repeated to fill the
@@ -126,7 +126,7 @@
Surface drawImage(Image image, float dx, float dy);
/**
- * Draws a scaled image at the specified location.
+ * Draws a scaled or repeated image at the specified location.
*
* @param dx the destination x
* @param dy the destination y
@@ -64,8 +64,7 @@ public Surface drawImage(Image image, float x, float y) {
@Override
public Surface drawImage(Image image, float x, float y, float dw, float dh) {
bindFramebuffer();
- ((ImageGL) image).draw(shader, topTransform(), x, y, dw, dh,
- 0, 0, image.width(), image.height(), tint);
+ ((ImageGL) image).draw(shader, topTransform(), x, y, dw, dh, tint);
return this;
}
@@ -108,7 +107,7 @@ public Surface drawLine(float x0, float y0, float x1, float y1, float width) {
GLShader shader = ctx.quadShader(this.shader);
if (fillPattern != null) {
- int tex = fillPattern.ensureTexture(true, true);
+ int tex = fillPattern.ensureTexture();
if (tex > 0) {
shader.prepareTexture(tex, tint);
shader.addQuad(l, 0, 0, length, width,
@@ -127,7 +126,7 @@ public Surface fillRect(float x, float y, float width, float height) {
GLShader shader = ctx.quadShader(this.shader);
if (fillPattern != null) {
- int tex = fillPattern.ensureTexture(true, true);
+ int tex = fillPattern.ensureTexture();
if (tex > 0) {
shader.prepareTexture(tex, tint);
float tw = fillPattern.width(), th = fillPattern.height(), r = x+width, b = y+height;
@@ -146,7 +145,7 @@ public Surface fillTriangles(float[] xys, int[] indices) {
GLShader shader = ctx.trisShader(this.shader);
if (fillPattern != null) {
- int tex = fillPattern.ensureTexture(true, true);
+ int tex = fillPattern.ensureTexture();
if (tex > 0) {
shader.prepareTexture(tex, tint);
shader.addTriangles(topTransform(), xys, fillPattern.width(), fillPattern.height(), indices);
@@ -164,7 +163,7 @@ public Surface fillTriangles(float[] xys, float[] sxys, int[] indices) {
if (fillPattern == null)
throw new IllegalStateException("No fill pattern currently set");
- int tex = fillPattern.ensureTexture(true, true);
+ int tex = fillPattern.ensureTexture();
if (tex > 0) {
GLShader shader = ctx.trisShader(this.shader).prepareTexture(tex, tint);
shader.addTriangles(topTransform(), xys, sxys, indices);
@@ -231,6 +230,7 @@ public Surface setFillPattern(Pattern pattern) {
// TODO: Add it to the state stack.
Asserts.checkArgument(pattern instanceof GLPattern);
this.fillPattern = ((GLPattern) pattern).image();
+ this.fillPattern.setRepeat(true, true);
return this;
}
@@ -32,8 +32,11 @@
/** The current count of references to this image. */
protected int refs;
- /** Our texture and repeatable texture handles. */
- protected int tex, reptex;
+ /** Our texture handle. */
+ protected int tex;
+
+ /** Whether this image repeats in the x/y direction. */
+ protected boolean repeatX, repeatY;
/** Whether to generate mipmaps for this image. */
protected boolean mipmapped;
@@ -60,23 +63,43 @@ public void release() {
}
}
+ @Override
+ public boolean repeatX() {
+ return repeatX;
+ }
+
+ @Override
+ public boolean repeatY() {
+ return repeatY;
+ }
+
+ @Override
+ public void setRepeat(boolean repeatX, boolean repeatY) {
+ if (repeatX != this.repeatX || repeatY != this.repeatY) {
+ this.repeatX = repeatX;
+ this.repeatY = repeatY;
+ clearTexture();
+ }
+ }
+
@Override
public void setMipmapped (boolean mipmapped) {
- Asserts.checkState(tex == 0 && reptex == 0,
- "Mipmapping must be configured before the image is used.");
- this.mipmapped = mipmapped;
+ if (this.mipmapped != mipmapped) {
+ this.mipmapped = mipmapped;
+ clearTexture();
+ }
}
@Override
- public int ensureTexture(boolean repeatX, boolean repeatY) {
- if (!isReady()) {
+ public int ensureTexture() {
+ if (tex != 0) {
+ return tex;
+ } else if (!isReady()) {
return 0;
} else if (repeatX || repeatY || mipmapped) {
- scaleTexture(repeatX, repeatY);
- return reptex;
+ return (tex = scaleTexture());
} else {
- loadTexture();
- return tex;
+ return (tex = loadTexture());
}
}
@@ -86,22 +109,18 @@ public void clearTexture() {
ctx.destroyTexture(tex);
tex = 0;
}
- if (reptex > 0) {
- ctx.destroyTexture(reptex);
- reptex = 0;
- }
}
/**
* Draws this image with the supplied transform in the specified target dimensions.
*/
void draw(GLShader shader, InternalTransform xform, float dx, float dy, float dw, float dh,
- boolean repeatX, boolean repeatY, int tint) {
- int tex = ensureTexture(repeatX, repeatY);
+ int tint) {
+ int tex = ensureTexture();
if (tex > 0) {
float sl = x(), st = y();
float sr = sl + (repeatX ? dw : width()), sb = st + (repeatY ? dh : height());
- float texWidth = texWidth(repeatX), texHeight = texHeight(repeatY);
+ float texWidth = texWidth(), texHeight = texHeight();
ctx.quadShader(shader).prepareTexture(tex, tint).addQuad(
xform, dx, dy, dx + dw, dy + dh,
sl / texWidth, st / texHeight, sr / texWidth, sb / texHeight);
@@ -113,10 +132,10 @@ void draw(GLShader shader, InternalTransform xform, float dx, float dy, float dw
*/
void draw(GLShader shader, InternalTransform xform, float dx, float dy, float dw, float dh,
float sx, float sy, float sw, float sh, int tint) {
- int tex = ensureTexture(false, false);
+ int tex = ensureTexture();
if (tex > 0) {
sx += x(); sy += y();
- float texWidth = texWidth(false), texHeight = texHeight(false);
+ float texWidth = texWidth(), texHeight = texHeight();
ctx.quadShader(shader).prepareTexture(tex, tint).addQuad(
xform, dx, dy, dx + dw, dy + dh,
sx / texWidth, sy / texHeight, (sx + sw) / texWidth, (sy + sh) / texHeight);
@@ -145,14 +164,14 @@ protected float y() {
/**
* Returns the width of our underlying texture image.
*/
- protected float texWidth(boolean repeatX) {
+ protected float texWidth() {
return width();
}
/**
* Returns the height of our underlying texture image.
*/
- protected float texHeight(boolean repeatY) {
+ protected float texHeight() {
return height();
}
@@ -165,24 +184,18 @@ protected float texHeight(boolean repeatY) {
protected void finalize() {
if (tex > 0)
ctx.queueDestroyTexture(tex);
- if (reptex > 0)
- ctx.queueDeleteFramebuffer(reptex);
}
- private void loadTexture() {
- if (tex > 0)
- return;
+ private int loadTexture() {
// the mipmaps flag is always false here because we only ever generate mipmaps for our
// power-of-two textures; scaleTexture will use tex to create the POT texture, so tex should
// not have mipmaps enabled, or it will hose up that process
- tex = ctx.createTexture(false, false, false);
+ int tex = ctx.createTexture(false, false, false);
updateTexture(tex);
+ return tex;
}
- private void scaleTexture(boolean repeatX, boolean repeatY) {
- if (reptex > 0)
- return;
-
+ private int scaleTexture() {
int scaledWidth = scale.scaledCeil(width());
int scaledHeight = scale.scaledCeil(height());
@@ -194,14 +207,14 @@ private void scaleTexture(boolean repeatX, boolean repeatY) {
// no need to scale if our source data is already a power of two
if ((width == 0) && (height == 0)) {
- reptex = ctx.createTexture(scaledWidth, scaledHeight, repeatX, repeatY, mipmapped);
+ int reptex = ctx.createTexture(scaledWidth, scaledHeight, repeatX, repeatY, mipmapped);
updateTexture(reptex);
if (mipmapped) ctx.generateMipmap(reptex);
- return;
+ return reptex;
}
// otherwise we need to scale our non-repeated texture, so load that normally
- loadTexture();
+ int tex = loadTexture();
// width/height == 0 => already a power of two.
if (width == 0)
@@ -210,7 +223,7 @@ private void scaleTexture(boolean repeatX, boolean repeatY) {
height = scaledHeight;
// create our texture and point a new framebuffer at it
- reptex = ctx.createTexture(width, height, repeatX, repeatY, mipmapped);
+ int reptex = ctx.createTexture(width, height, repeatX, repeatY, mipmapped);
int fbuf = ctx.createFramebuffer(reptex);
ctx.pushFramebuffer(fbuf, width, height);
try {
@@ -221,8 +234,11 @@ private void scaleTexture(boolean repeatX, boolean repeatY) {
shader.flush();
// if we're mipmapped, we can now generate our mipmaps
if (mipmapped) ctx.generateMipmap(reptex);
+ return reptex;
} finally {
+ // delete the non-repeated texture
+ ctx.destroyTexture(tex);
// we no longer need this framebuffer; rebind the previous framebuffer and delete ours
ctx.popFramebuffer();
ctx.deleteFramebuffer(fbuf);
@@ -23,7 +23,6 @@
private float width, height;
private boolean widthSet, heightSet;
- private boolean repeatX, repeatY;
private ImageGL img;
public ImageLayerGL(GLContext ctx) {
@@ -78,16 +77,6 @@ public ImageLayer setImage(Image img) {
return this;
}
- @Override
- public void setRepeatX(boolean repeat) {
- repeatX = repeat;
- }
-
- @Override
- public void setRepeatY(boolean repeat) {
- repeatY = repeat;
- }
-
@Override
public void setWidth(float width) {
Asserts.checkArgument(width > 0, "Width must be > 0");
@@ -114,7 +103,7 @@ public void paint(InternalTransform curTransform, int curTint, GLShader curShade
if (tint != Tint.NOOP_TINT)
curTint = Tint.combine(curTint, tint);
img.draw((shader == null) ? curShader : shader, localTransform(curTransform),
- 0, 0, width(), height(), repeatX, repeatY, curTint);
+ 0, 0, width(), height(), curTint);
}
@Override
Oops, something went wrong.

0 comments on commit 6b726f6

Please sign in to comment.