1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.graphics.g2d; 18 19 import com.badlogic.gdx.graphics.Texture; 20 21 /** Defines a rectangular area of a texture. The coordinate system used has its origin in the upper left corner with the x-axis 22 * pointing to the right and the y axis pointing downwards. 23 * @author mzechner 24 * @author Nathan Sweet */ 25 public class TextureRegion { 26 Texture texture; 27 float u, v; 28 float u2, v2; 29 int regionWidth, regionHeight; 30 31 /** Constructs a region with no texture and no coordinates defined. */ 32 public TextureRegion () { 33 } 34 35 /** Constructs a region the size of the specified texture. */ 36 public TextureRegion (Texture texture) { 37 if (texture == null) throw new IllegalArgumentException("texture cannot be null."); 38 this.texture = texture; 39 setRegion(0, 0, texture.getWidth(), texture.getHeight()); 40 } 41 42 /** @param width The width of the texture region. May be negative to flip the sprite when drawn. 43 * @param height The height of the texture region. May be negative to flip the sprite when drawn. */ 44 public TextureRegion (Texture texture, int width, int height) { 45 this.texture = texture; 46 setRegion(0, 0, width, height); 47 } 48 49 /** @param width The width of the texture region. May be negative to flip the sprite when drawn. 50 * @param height The height of the texture region. May be negative to flip the sprite when drawn. */ 51 public TextureRegion (Texture texture, int x, int y, int width, int height) { 52 this.texture = texture; 53 setRegion(x, y, width, height); 54 } 55 56 public TextureRegion (Texture texture, float u, float v, float u2, float v2) { 57 this.texture = texture; 58 setRegion(u, v, u2, v2); 59 } 60 61 /** Constructs a region with the same texture and coordinates of the specified region. */ 62 public TextureRegion (TextureRegion region) { 63 setRegion(region); 64 } 65 66 /** Constructs a region with the same texture as the specified region and sets the coordinates relative to the specified region. 67 * @param width The width of the texture region. May be negative to flip the sprite when drawn. 68 * @param height The height of the texture region. May be negative to flip the sprite when drawn. */ 69 public TextureRegion (TextureRegion region, int x, int y, int width, int height) { 70 setRegion(region, x, y, width, height); 71 } 72 73 /** Sets the texture and sets the coordinates to the size of the specified texture. */ 74 public void setRegion (Texture texture) { 75 this.texture = texture; 76 setRegion(0, 0, texture.getWidth(), texture.getHeight()); 77 } 78 79 /** @param width The width of the texture region. May be negative to flip the sprite when drawn. 80 * @param height The height of the texture region. May be negative to flip the sprite when drawn. */ 81 public void setRegion (int x, int y, int width, int height) { 82 float invTexWidth = 1f / texture.getWidth(); 83 float invTexHeight = 1f / texture.getHeight(); 84 setRegion(x * invTexWidth, y * invTexHeight, (x + width) * invTexWidth, (y + height) * invTexHeight); 85 regionWidth = Math.abs(width); 86 regionHeight = Math.abs(height); 87 } 88 89 public void setRegion (float u, float v, float u2, float v2) { 90 int texWidth = texture.getWidth(), texHeight = texture.getHeight(); 91 regionWidth = Math.round(Math.abs(u2 - u) * texWidth); 92 regionHeight = Math.round(Math.abs(v2 - v) * texHeight); 93 94 // For a 1x1 region, adjust UVs toward pixel center to avoid filtering artifacts on AMD GPUs when drawing very stretched. 95 if (regionWidth == 1 && regionHeight == 1) { 96 float adjustX = 0.25f / texWidth; 97 u += adjustX; 98 u2 -= adjustX; 99 float adjustY = 0.25f / texHeight; 100 v += adjustY; 101 v2 -= adjustY; 102 } 103 104 this.u = u; 105 this.v = v; 106 this.u2 = u2; 107 this.v2 = v2; 108 } 109 110 /** Sets the texture and coordinates to the specified region. */ 111 public void setRegion (TextureRegion region) { 112 texture = region.texture; 113 setRegion(region.u, region.v, region.u2, region.v2); 114 } 115 116 /** Sets the texture to that of the specified region and sets the coordinates relative to the specified region. */ 117 public void setRegion (TextureRegion region, int x, int y, int width, int height) { 118 texture = region.texture; 119 setRegion(region.getRegionX() + x, region.getRegionY() + y, width, height); 120 } 121 122 public Texture getTexture () { 123 return texture; 124 } 125 126 public void setTexture (Texture texture) { 127 this.texture = texture; 128 } 129 130 public float getU () { 131 return u; 132 } 133 134 public void setU (float u) { 135 this.u = u; 136 regionWidth = Math.round(Math.abs(u2 - u) * texture.getWidth()); 137 } 138 139 public float getV () { 140 return v; 141 } 142 143 public void setV (float v) { 144 this.v = v; 145 regionHeight = Math.round(Math.abs(v2 - v) * texture.getHeight()); 146 } 147 148 public float getU2 () { 149 return u2; 150 } 151 152 public void setU2 (float u2) { 153 this.u2 = u2; 154 regionWidth = Math.round(Math.abs(u2 - u) * texture.getWidth()); 155 } 156 157 public float getV2 () { 158 return v2; 159 } 160 161 public void setV2 (float v2) { 162 this.v2 = v2; 163 regionHeight = Math.round(Math.abs(v2 - v) * texture.getHeight()); 164 } 165 166 public int getRegionX () { 167 return Math.round(u * texture.getWidth()); 168 } 169 170 public void setRegionX (int x) { 171 setU(x / (float)texture.getWidth()); 172 } 173 174 public int getRegionY () { 175 return Math.round(v * texture.getHeight()); 176 } 177 178 public void setRegionY (int y) { 179 setV(y / (float)texture.getHeight()); 180 } 181 182 /** Returns the region's width. */ 183 public int getRegionWidth () { 184 return regionWidth; 185 } 186 187 public void setRegionWidth (int width) { 188 if (isFlipX()) { 189 setU(u2 + width / (float)texture.getWidth()); 190 } else { 191 setU2(u + width / (float)texture.getWidth()); 192 } 193 } 194 195 /** Returns the region's height. */ 196 public int getRegionHeight () { 197 return regionHeight; 198 } 199 200 public void setRegionHeight (int height) { 201 if (isFlipY()) { 202 setV(v2 + height / (float)texture.getHeight()); 203 } else { 204 setV2(v + height / (float)texture.getHeight()); 205 } 206 } 207 208 public void flip (boolean x, boolean y) { 209 if (x) { 210 float temp = u; 211 u = u2; 212 u2 = temp; 213 } 214 if (y) { 215 float temp = v; 216 v = v2; 217 v2 = temp; 218 } 219 } 220 221 public boolean isFlipX () { 222 return u > u2; 223 } 224 225 public boolean isFlipY () { 226 return v > v2; 227 } 228 229 /** Offsets the region relative to the current region. Generally the region's size should be the entire size of the texture in 230 * the direction(s) it is scrolled. 231 * @param xAmount The percentage to offset horizontally. 232 * @param yAmount The percentage to offset vertically. This is done in texture space, so up is negative. */ 233 public void scroll (float xAmount, float yAmount) { 234 if (xAmount != 0) { 235 float width = (u2 - u) * texture.getWidth(); 236 u = (u + xAmount) % 1; 237 u2 = u + width / texture.getWidth(); 238 } 239 if (yAmount != 0) { 240 float height = (v2 - v) * texture.getHeight(); 241 v = (v + yAmount) % 1; 242 v2 = v + height / texture.getHeight(); 243 } 244 } 245 246 /** Helper function to create tiles out of this TextureRegion starting from the top left corner going to the right and ending at 247 * the bottom right corner. Only complete tiles will be returned so if the region's width or height are not a multiple of the 248 * tile width and height not all of the region will be used. This will not work on texture regions returned form a TextureAtlas 249 * that either have whitespace removed or where flipped before the region is split. 250 * 251 * @param tileWidth a tile's width in pixels 252 * @param tileHeight a tile's height in pixels 253 * @return a 2D array of TextureRegions indexed by [row][column]. */ 254 public TextureRegion[][] split (int tileWidth, int tileHeight) { 255 int x = getRegionX(); 256 int y = getRegionY(); 257 int width = regionWidth; 258 int height = regionHeight; 259 260 int rows = height / tileHeight; 261 int cols = width / tileWidth; 262 263 int startX = x; 264 TextureRegion[][] tiles = new TextureRegion[rows][cols]; 265 for (int row = 0; row < rows; row++, y += tileHeight) { 266 x = startX; 267 for (int col = 0; col < cols; col++, x += tileWidth) { 268 tiles[row][col] = new TextureRegion(texture, x, y, tileWidth, tileHeight); 269 } 270 } 271 272 return tiles; 273 } 274 275 /** Helper function to create tiles out of the given {@link Texture} starting from the top left corner going to the right and 276 * ending at the bottom right corner. Only complete tiles will be returned so if the texture's width or height are not a 277 * multiple of the tile width and height not all of the texture will be used. 278 * 279 * @param texture the Texture 280 * @param tileWidth a tile's width in pixels 281 * @param tileHeight a tile's height in pixels 282 * @return a 2D array of TextureRegions indexed by [row][column]. */ 283 public static TextureRegion[][] split (Texture texture, int tileWidth, int tileHeight) { 284 TextureRegion region = new TextureRegion(texture); 285 return region.split(tileWidth, tileHeight); 286 } 287 } 288