diff --git a/CHANGES b/CHANGES index 152b73e38c5..e1e0c977fd6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ [1.12.1] - LWJGL3 Improvement: Audio device is automatically switched if it was changed in the operating system. - Tiled Fix: TiledLayer parallax default values fix +- API Addition: TiledDrawable: Align can be set to manipulate the alignment of the rendering (TiledDrawable#setAlign, TiledDrawable#getAlign) +- API Addition: TiledDrawable#draw: Also available as a static function (with align) if you don't want to create an extra instance per texture region - Android: Removed mouse catching added on 1.12.0 due to unintended effects (see #7187). - iOS: Update to MobiVM 2.3.20 - API Addition: Using "object" property in Tiled object now fetches MapObject being pointed to, and BaseTmxMapLoader includes method for fetching map where key is id and value is MapObject instance. diff --git a/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java b/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java index fc5138832a5..3b03ee7fa92 100644 --- a/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java +++ b/gdx/src/com/badlogic/gdx/scenes/scene2d/utils/TiledDrawable.java @@ -20,12 +20,15 @@ import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.Align; /** Draws a {@link TextureRegion} repeatedly to fill the area, instead of stretching it. - * @author Nathan Sweet */ + * @author Nathan Sweet + * @author Thomas Creutzenberg */ public class TiledDrawable extends TextureRegionDrawable { private final Color color = new Color(1, 1, 1, 1); private float scale = 1; + private int align = Align.bottomLeft; public TiledDrawable () { super(); @@ -43,50 +46,218 @@ public void draw (Batch batch, float x, float y, float width, float height) { float oldColor = batch.getPackedColor(); batch.setColor(batch.getColor().mul(color)); - TextureRegion region = getRegion(); - float regionWidth = region.getRegionWidth() * scale, regionHeight = region.getRegionHeight() * scale; - int fullX = (int)(width / regionWidth), fullY = (int)(height / regionHeight); - float remainingX = width - regionWidth * fullX, remainingY = height - regionHeight * fullY; - float startX = x, startY = y; - float endX = x + width - remainingX, endY = y + height - remainingY; - for (int i = 0; i < fullX; i++) { - y = startY; - for (int ii = 0; ii < fullY; ii++) { - batch.draw(region, x, y, regionWidth, regionHeight); - y += regionHeight; + draw(batch, getRegion(), x, y, width, height, scale, align); + + batch.setPackedColor(oldColor); + } + + public static void draw (Batch batch, TextureRegion textureRegion, float x, float y, float width, float height, float scale, + int align) { + final float regionWidth = textureRegion.getRegionWidth() * scale; + final float regionHeight = textureRegion.getRegionHeight() * scale; + + final Texture texture = textureRegion.getTexture(); + final float textureWidth = texture.getWidth() * scale; + final float textureHeight = texture.getHeight() * scale; + final float u = textureRegion.getU(); + final float v = textureRegion.getV(); + final float u2 = textureRegion.getU2(); + final float v2 = textureRegion.getV2(); + + int fullX = (int)(width / regionWidth); + final float leftPartialWidth; + final float rightPartialWidth; + if (Align.isLeft(align)) { + leftPartialWidth = 0f; + rightPartialWidth = width - (regionWidth * fullX); + } else if (Align.isRight(align)) { + leftPartialWidth = width - (regionWidth * fullX); + rightPartialWidth = 0f; + } else { + if (fullX != 0) { + fullX = fullX % 2 == 1 ? fullX : fullX - 1; + final float leftRight = 0.5f * (width - (regionWidth * fullX)); + leftPartialWidth = leftRight; + rightPartialWidth = leftRight; + } else { + leftPartialWidth = 0f; + rightPartialWidth = 0f; + } + } + int fullY = (int)(height / regionHeight); + final float topPartialHeight; + final float bottomPartialHeight; + if (Align.isTop(align)) { + topPartialHeight = 0f; + bottomPartialHeight = height - (regionHeight * fullY); + } else if (Align.isBottom(align)) { + topPartialHeight = height - (regionHeight * fullY); + bottomPartialHeight = 0f; + } else { + if (fullY != 0) { + fullY = fullY % 2 == 1 ? fullY : fullY - 1; + final float topBottom = 0.5f * (height - (regionHeight * fullY)); + topPartialHeight = topBottom; + bottomPartialHeight = topBottom; + } else { + topPartialHeight = 0f; + bottomPartialHeight = 0f; } - x += regionWidth; } - Texture texture = region.getTexture(); - float u = region.getU(); - float v2 = region.getV2(); - if (remainingX > 0) { - // Right edge. - float u2 = u + remainingX / (texture.getWidth() * scale); - float v = region.getV(); - y = startY; - for (int ii = 0; ii < fullY; ii++) { - batch.draw(texture, x, y, remainingX, regionHeight, u, v2, u2, v); - y += regionHeight; + + float drawX = x; + float drawY = y; + + // Left edge + if (leftPartialWidth > 0f) { + final float leftEdgeU = u2 - (leftPartialWidth / textureWidth); + + // Left bottom partial + if (bottomPartialHeight > 0f) { + final float leftBottomV = v + (bottomPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, leftPartialWidth, bottomPartialHeight, leftEdgeU, leftBottomV, u2, v); + drawY += bottomPartialHeight; } - // Upper right corner. - if (remainingY > 0) { - v = v2 - remainingY / (texture.getHeight() * scale); - batch.draw(texture, x, y, remainingX, remainingY, u, v2, u2, v); + + // Left center partials + if (fullY == 0 && Align.isCenterVertical(align)) { + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + final float leftCenterV = v2 - vOffset; + final float leftCenterV2 = v + vOffset; + batch.draw(texture, drawX, drawY, leftPartialWidth, height, leftEdgeU, leftCenterV, u2, leftCenterV2); + drawY += height; + } else { + for (int i = 0; i < fullY; i++) { + batch.draw(texture, drawX, drawY, leftPartialWidth, regionHeight, leftEdgeU, v2, u2, v); + drawY += regionHeight; + } + } + + // Left top partial + if (topPartialHeight > 0f) { + final float leftTopV = v2 - (topPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, leftPartialWidth, topPartialHeight, leftEdgeU, v2, u2, leftTopV); } } - if (remainingY > 0) { - // Top edge. - float u2 = region.getU2(); - float v = v2 - remainingY / (texture.getHeight() * scale); - x = startX; - for (int i = 0; i < fullX; i++) { - batch.draw(texture, x, y, regionWidth, remainingY, u, v2, u2, v); - x += regionWidth; + + // Center full texture regions + { + // Center bottom partials + if (bottomPartialHeight > 0f) { + drawX = x + leftPartialWidth; + drawY = y; + + final float centerBottomV = v + (bottomPartialHeight / textureHeight); + + if (fullX == 0 && Align.isCenterHorizontal(align)) { + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + final float centerBottomU = u + uOffset; + final float centerBottomU2 = u2 - uOffset; + batch.draw(texture, drawX, drawY, width, bottomPartialHeight, centerBottomU, centerBottomV, centerBottomU2, v); + drawX += width; + } else { + for (int i = 0; i < fullX; i++) { + batch.draw(texture, drawX, drawY, regionWidth, bottomPartialHeight, u, centerBottomV, u2, v); + drawX += regionWidth; + } + } + } + + // Center full texture regions + { + drawX = x + leftPartialWidth; + + final int originalFullX = fullX; + final int originalFullY = fullY; + + float centerCenterDrawWidth = regionWidth; + float centerCenterDrawHeight = regionHeight; + float centerCenterU = u; + float centerCenterU2 = u2; + float centerCenterV = v2; + float centerCenterV2 = v; + if (fullX == 0 && Align.isCenterHorizontal(align)) { + fullX = 1; + centerCenterDrawWidth = width; + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + centerCenterU = u + uOffset; + centerCenterU2 = u2 - uOffset; + } + if (fullY == 0 && Align.isCenterVertical(align)) { + fullY = 1; + centerCenterDrawHeight = height; + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + centerCenterV = v2 - vOffset; + centerCenterV2 = v + vOffset; + } + for (int i = 0; i < fullX; i++) { + drawY = y + bottomPartialHeight; + for (int ii = 0; ii < fullY; ii++) { + batch.draw(texture, drawX, drawY, centerCenterDrawWidth, centerCenterDrawHeight, centerCenterU, centerCenterV, + centerCenterU2, centerCenterV2); + drawY += centerCenterDrawHeight; + } + drawX += centerCenterDrawWidth; + } + + fullX = originalFullX; + fullY = originalFullY; + } + + // Center top partials + if (topPartialHeight > 0f) { + drawX = x + leftPartialWidth; + + final float centerTopV = v2 - (topPartialHeight / textureHeight); + + if (fullX == 0 && Align.isCenterHorizontal(align)) { + final float uOffset = 0.5f * (u2 - u) * (1f - (width / regionWidth)); + final float centerTopU = u + uOffset; + final float centerTopU2 = u2 - uOffset; + batch.draw(texture, drawX, drawY, width, topPartialHeight, centerTopU, v2, centerTopU2, centerTopV); + drawX += width; + } else { + for (int i = 0; i < fullX; i++) { + batch.draw(texture, drawX, drawY, regionWidth, topPartialHeight, u, v2, u2, centerTopV); + drawX += regionWidth; + } + } } } - batch.setPackedColor(oldColor); + // Right edge + if (rightPartialWidth > 0f) { + drawY = y; + + final float rightEdgeU2 = u + (rightPartialWidth / textureWidth); + + // Right bottom partial + if (bottomPartialHeight > 0f) { + final float rightBottomV = v + (bottomPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, rightPartialWidth, bottomPartialHeight, u, rightBottomV, rightEdgeU2, v); + drawY += bottomPartialHeight; + } + + // Right center partials + if (fullY == 0 && Align.isCenterVertical(align)) { + final float vOffset = 0.5f * (v2 - v) * (1f - (height / regionHeight)); + final float rightCenterV = v2 - vOffset; + final float rightCenterV2 = v + vOffset; + batch.draw(texture, drawX, drawY, rightPartialWidth, height, u, rightCenterV, rightEdgeU2, rightCenterV2); + drawY += height; + } else { + for (int i = 0; i < fullY; i++) { + batch.draw(texture, drawX, drawY, rightPartialWidth, regionHeight, u, v2, rightEdgeU2, v); + drawY += regionHeight; + } + } + + // Right top partial + if (topPartialHeight > 0f) { + final float rightTopV = v2 - (topPartialHeight / textureHeight); + batch.draw(texture, drawX, drawY, rightPartialWidth, topPartialHeight, u, v2, rightEdgeU2, rightTopV); + } + } } public void draw (Batch batch, float x, float y, float originX, float originY, float width, float height, float scaleX, @@ -106,6 +277,14 @@ public float getScale () { return scale; } + public int getAlign () { + return align; + } + + public void setAlign (int align) { + this.align = align; + } + public TiledDrawable tint (Color tint) { TiledDrawable drawable = new TiledDrawable(this); drawable.color.set(tint); diff --git a/tests/gdx-tests-android/assets/data/testAtlas.atlas b/tests/gdx-tests-android/assets/data/testAtlas.atlas new file mode 100644 index 00000000000..24079c4d95c --- /dev/null +++ b/tests/gdx-tests-android/assets/data/testAtlas.atlas @@ -0,0 +1,27 @@ +testAtlas.png + size: 256, 64 + filter: Linear, Linear +black + bounds: 126, 16, 4, 8 +darkBlue + bounds: 2, 33, 26, 26 +darkBlue2 + bounds: 2, 2, 26, 26 +darkGray + bounds: 33, 33, 26, 26 +darkGray2 + bounds: 33, 2, 26, 26 +darkGreen + bounds: 64, 33, 26, 26 +darkGreen2 + bounds: 64, 2, 26, 26 +grey + bounds: 126, 29, 22, 30 +pink + bounds: 95, 33, 26, 26 +purple + bounds: 153, 17, 16, 42 +tileTester + bounds: 95, 2, 26, 26 +white + bounds: 174, 57, 4, 2 diff --git a/tests/gdx-tests-android/assets/data/testAtlas.png b/tests/gdx-tests-android/assets/data/testAtlas.png new file mode 100644 index 00000000000..756483ac6bb Binary files /dev/null and b/tests/gdx-tests-android/assets/data/testAtlas.png differ diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledDrawableTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledDrawableTest.java new file mode 100644 index 00000000000..749809c5dce --- /dev/null +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/TiledDrawableTest.java @@ -0,0 +1,111 @@ + +package com.badlogic.gdx.tests; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.utils.TiledDrawable; +import com.badlogic.gdx.tests.utils.GdxTest; +import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.ScreenUtils; + +public class TiledDrawableTest extends GdxTest { + + private static final float SCALE_CHANGE = 0.25f; + + private Stage stage; + private Batch batch; + private BitmapFont font; + private TextureAtlas atlas; + private TiledDrawable tiledDrawable; + + @Override + public void create () { + stage = new Stage(); + batch = new SpriteBatch(); + font = new BitmapFont(Gdx.files.internal("data/lsans-15.fnt"), false); + + // Must be a texture atlas so uv is not just 0 and 1 + atlas = new TextureAtlas(Gdx.files.internal("data/testAtlas.atlas")); + tiledDrawable = new TiledDrawable(atlas.findRegion("tileTester")); + + Gdx.input.setInputProcessor(this); + } + + @Override + public void render () { + ScreenUtils.clear(0.2f, 0.2f, 0.2f, 1); + + final Batch batch = stage.getBatch(); + batch.begin(); + + font.draw(batch, + "Scale: " + tiledDrawable.getScale() + " (to change scale press: 'A' -" + SCALE_CHANGE + ", 'D' +" + SCALE_CHANGE + ")", + 8, 20); + + final float leftSpacingX = 40; + final float spacingX = 80; + final float bottomSpacing = 60; + final float spacingY = 40; + float inputX = Gdx.input.getX(); + float inputY = Gdx.graphics.getHeight() - Gdx.input.getY(); + + final float clusterWidth = Math.max(13, (inputX - leftSpacingX - (2 * spacingX)) / 3f); + final float clusterHeight = Math.max(13, (inputY - bottomSpacing - (2 * spacingY)) / 3f); + + final float leftX = leftSpacingX; + final float centerX = leftSpacingX + spacingX + clusterWidth; + final float rightX = leftSpacingX + (2 * spacingX) + (2 * clusterWidth); + final float topY = bottomSpacing + (2 * spacingY) + (2 * clusterHeight); + final float centerY = bottomSpacing + spacingY + clusterHeight; + final float bottomY = bottomSpacing; + + drawTiledDrawableCluster(batch, leftX, topY, clusterWidth, clusterHeight, Align.topLeft); + drawTiledDrawableCluster(batch, centerX, topY, clusterWidth, clusterHeight, Align.top); + drawTiledDrawableCluster(batch, rightX, topY, clusterWidth, clusterHeight, Align.topRight); + + drawTiledDrawableCluster(batch, leftX, centerY, clusterWidth, clusterHeight, Align.left); + drawTiledDrawableCluster(batch, centerX, centerY, clusterWidth, clusterHeight, Align.center); + drawTiledDrawableCluster(batch, rightX, centerY, clusterWidth, clusterHeight, Align.right); + + drawTiledDrawableCluster(batch, leftX, bottomY, clusterWidth, clusterHeight, Align.bottomLeft); + drawTiledDrawableCluster(batch, centerX, bottomY, clusterWidth, clusterHeight, Align.bottom); + drawTiledDrawableCluster(batch, rightX, bottomY, clusterWidth, clusterHeight, Align.bottomRight); + + batch.end(); + } + + private final void drawTiledDrawableCluster (Batch batch, float x, float y, float clusterWidth, float clusterHeight, + int align) { + tiledDrawable.setAlign(align); + tiledDrawable.draw(batch, x, y, clusterWidth, clusterHeight); + font.draw(batch, Align.toString(align), x, y - 5); + } + + @Override + public boolean keyDown (int keycode) { + if (keycode == Input.Keys.A) { + tiledDrawable.setScale(Math.max(SCALE_CHANGE, tiledDrawable.getScale() - SCALE_CHANGE)); + } else if (keycode == Input.Keys.D) { + tiledDrawable.setScale(tiledDrawable.getScale() + SCALE_CHANGE); + } + return true; + } + + @Override + public void resize (int width, int height) { + batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height); + stage.getViewport().update(width, height, true); + } + + @Override + public void dispose () { + stage.dispose(); + font.dispose(); + atlas.dispose(); + } +} diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java index 67dbf4c8bff..894ba053ae7 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java @@ -298,6 +298,7 @@ public class GdxTests { TextureRegion3DTest.class, TideMapAssetManagerTest.class, TideMapDirectLoaderTest.class, + TiledDrawableTest.class, TileTest.class, TiledMapAnimationLoadingTest.class, TiledMapAssetManagerTest.class,