PageRenderTime 36ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/newcode/src/com/prupe/mcpatcher/hd/CustomAnimation.java

https://bitbucket.org/prupe/mcpatcher
Java | 331 lines | 306 code | 25 blank | 0 comment | 70 complexity | 0e5fe92486cc42ba64af2e195d9e7726 MD5 | raw file
  1. package com.prupe.mcpatcher.hd;
  2. import com.prupe.mcpatcher.Config;
  3. import com.prupe.mcpatcher.MCLogger;
  4. import com.prupe.mcpatcher.MCPatcherUtils;
  5. import com.prupe.mcpatcher.mal.resource.*;
  6. import com.prupe.mcpatcher.mal.tile.IconAPI;
  7. import net.minecraft.src.ResourceLocation;
  8. import org.lwjgl.opengl.GL11;
  9. import org.lwjgl.util.glu.GLU;
  10. import java.awt.*;
  11. import java.awt.image.BufferedImage;
  12. import java.nio.ByteBuffer;
  13. import java.util.*;
  14. import java.util.List;
  15. public class CustomAnimation implements Comparable<CustomAnimation> {
  16. private static final MCLogger logger = MCLogger.getLogger(MCPatcherUtils.CUSTOM_ANIMATIONS, "Animation");
  17. private static final boolean enable = Config.getBoolean(MCPatcherUtils.EXTENDED_HD, "animations", true);
  18. private static final Set<PropertiesFile> pending = new HashSet<PropertiesFile>();
  19. private static final List<CustomAnimation> animations = new ArrayList<CustomAnimation>();
  20. private final PropertiesFile properties;
  21. private final ResourceLocation dstName;
  22. private final ResourceLocation srcName;
  23. private final int mipmapLevel;
  24. private final ByteBuffer imageData;
  25. private final int x;
  26. private final int y;
  27. private final int w;
  28. private final int h;
  29. private int currentFrame;
  30. private int currentDelay;
  31. private int numFrames;
  32. private int[] tileOrder;
  33. private int[] tileDelay;
  34. private final int numTiles;
  35. private boolean error;
  36. static {
  37. TexturePackChangeHandler.register(new TexturePackChangeHandler(MCPatcherUtils.EXTENDED_HD, 1) {
  38. @Override
  39. public void beforeChange() {
  40. if (!pending.isEmpty()) {
  41. logger.fine("%d animations were never registered:", pending.size());
  42. for (PropertiesFile properties : pending) {
  43. logger.fine(" %s", properties);
  44. }
  45. pending.clear();
  46. }
  47. animations.clear();
  48. MipmapHelper.reset();
  49. FancyDial.clearAll();
  50. }
  51. @Override
  52. public void afterChange() {
  53. if (enable) {
  54. for (ResourceLocation resource : ResourceList.getInstance().listResources(TexturePackAPI.MCPATCHER_SUBDIR + "anim", ".properties", false)) {
  55. PropertiesFile properties = PropertiesFile.get(logger, resource);
  56. if (properties != null) {
  57. pending.add(properties);
  58. }
  59. }
  60. }
  61. if (IconAPI.needRegisterTileAnimations()) {
  62. FancyDial.registerAnimations();
  63. }
  64. }
  65. });
  66. }
  67. public static void updateAll() {
  68. if (!pending.isEmpty()) {
  69. try {
  70. checkPendingAnimations();
  71. } catch (Throwable e) {
  72. e.printStackTrace();
  73. logger.error("%d remaining animations cleared", pending.size());
  74. pending.clear();
  75. }
  76. }
  77. for (CustomAnimation animation : animations) {
  78. animation.update();
  79. }
  80. }
  81. private static void checkPendingAnimations() {
  82. List<PropertiesFile> done = new ArrayList<PropertiesFile>();
  83. for (PropertiesFile properties : pending) {
  84. ResourceLocation textureName = properties.getResourceLocation("to", "");
  85. if (TexturePackAPI.isTextureLoaded(textureName)) {
  86. addStrip(properties);
  87. done.add(properties);
  88. }
  89. }
  90. if (!done.isEmpty()) {
  91. for (PropertiesFile name : done) {
  92. pending.remove(name);
  93. }
  94. Collections.sort(animations);
  95. }
  96. }
  97. private static void addStrip(PropertiesFile properties) {
  98. ResourceLocation dstName = properties.getResourceLocation("to", "");
  99. if (dstName == null) {
  100. properties.error("missing to= property");
  101. return;
  102. }
  103. ResourceLocation srcName = properties.getResourceLocation("from", "");
  104. if (srcName == null) {
  105. properties.error("missing from= property");
  106. return;
  107. }
  108. BufferedImage srcImage = TexturePackAPI.getImage(srcName);
  109. if (srcImage == null) {
  110. properties.error("image %s not found in texture pack", srcName);
  111. return;
  112. }
  113. int x = properties.getInt("x", 0);
  114. int y = properties.getInt("y", 0);
  115. int w = properties.getInt("w", 0);
  116. int h = properties.getInt("h", 0);
  117. if (dstName.toString().startsWith("minecraft:textures/atlas/")) {
  118. properties.error("animations cannot have a target of %s", dstName);
  119. return;
  120. }
  121. if (x < 0 || y < 0 || w <= 0 || h <= 0) {
  122. properties.error("%s has invalid dimensions x=%d,y=%d,w=%d,h=%d", srcName, x, y, w, h);
  123. return;
  124. }
  125. TexturePackAPI.bindTexture(dstName);
  126. int dstWidth = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
  127. int dstHeight = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
  128. int levels = MipmapHelper.getMipmapLevelsForCurrentTexture();
  129. if (x + w > dstWidth || y + h > dstHeight) {
  130. properties.error("%s dimensions x=%d,y=%d,w=%d,h=%d exceed %s size %dx%d",
  131. srcName, x, y, w, h, dstName, dstWidth, dstHeight
  132. );
  133. return;
  134. }
  135. int width = srcImage.getWidth();
  136. int height = srcImage.getHeight();
  137. if (width != w) {
  138. srcImage = resizeImage(srcImage, w);
  139. width = srcImage.getWidth();
  140. height = srcImage.getHeight();
  141. }
  142. if (width != w || height < h) {
  143. properties.error("%s dimensions %dx%d do not match %dx%d", srcName, width, height, w, h);
  144. return;
  145. }
  146. ByteBuffer imageData = ByteBuffer.allocateDirect(4 * width * height);
  147. int[] argb = new int[width * height];
  148. byte[] rgba = new byte[4 * width * height];
  149. srcImage.getRGB(0, 0, width, height, argb, 0, width);
  150. ARGBtoRGBA(argb, rgba);
  151. imageData.put(rgba).flip();
  152. for (int mipmapLevel = 0; mipmapLevel <= levels; mipmapLevel++) {
  153. add(new CustomAnimation(properties, srcName, dstName, mipmapLevel, x, y, w, h, imageData, height / h));
  154. if (((x | y | w | h) & 0x1) != 0 || w <= 0 || h <= 0) {
  155. break;
  156. }
  157. ByteBuffer newImage = ByteBuffer.allocateDirect(width * height);
  158. MipmapHelper.scaleHalf(imageData.asIntBuffer(), width, height, newImage.asIntBuffer(), 0);
  159. imageData = newImage;
  160. width >>= 1;
  161. height >>= 1;
  162. x >>= 1;
  163. y >>= 1;
  164. w >>= 1;
  165. h >>= 1;
  166. }
  167. }
  168. private static void add(CustomAnimation animation) {
  169. if (animation != null) {
  170. animations.add(animation);
  171. if (animation.mipmapLevel == 0) {
  172. logger.fine("new %s", animation);
  173. }
  174. }
  175. }
  176. private CustomAnimation(PropertiesFile properties, ResourceLocation srcName, ResourceLocation dstName, int mipmapLevel, int x, int y, int w, int h, ByteBuffer imageData, int numFrames) {
  177. this.properties = properties;
  178. this.srcName = srcName;
  179. this.dstName = dstName;
  180. this.mipmapLevel = mipmapLevel;
  181. this.x = x;
  182. this.y = y;
  183. this.w = w;
  184. this.h = h;
  185. this.imageData = imageData;
  186. this.numFrames = numFrames;
  187. currentFrame = -1;
  188. numTiles = numFrames;
  189. loadProperties(properties);
  190. }
  191. void update() {
  192. if (error) {
  193. return;
  194. }
  195. int texture = TexturePackAPI.getTextureIfLoaded(dstName);
  196. if (texture < 0) {
  197. return;
  198. }
  199. if (--currentDelay > 0) {
  200. return;
  201. }
  202. if (++currentFrame >= numFrames) {
  203. currentFrame = 0;
  204. }
  205. GLAPI.glBindTexture(texture);
  206. update(texture, 0, 0);
  207. int glError = GL11.glGetError();
  208. if (glError != 0) {
  209. logger.severe("%s: %s", this, GLU.gluErrorString(glError));
  210. error = true;
  211. return;
  212. }
  213. currentDelay = getDelay();
  214. }
  215. public int compareTo(CustomAnimation o) {
  216. return dstName.toString().compareTo(o.dstName.toString());
  217. }
  218. @Override
  219. public String toString() {
  220. return String.format("CustomAnimation{%s %s %dx%d -> %s%s @ %d,%d (%d frames)}",
  221. properties, srcName, w, h, dstName, (mipmapLevel > 0 ? "#" + mipmapLevel : ""), x, y, numFrames
  222. );
  223. }
  224. private static void ARGBtoRGBA(int[] src, byte[] dest) {
  225. for (int i = 0; i < src.length; i++) {
  226. int v = src[i];
  227. dest[(i * 4) + 3] = (byte) ((v >> 24) & 0xff);
  228. dest[(i * 4) + 0] = (byte) ((v >> 16) & 0xff);
  229. dest[(i * 4) + 1] = (byte) ((v >> 8) & 0xff);
  230. dest[(i * 4) + 2] = (byte) ((v >> 0) & 0xff);
  231. }
  232. }
  233. private static BufferedImage resizeImage(BufferedImage image, int width) {
  234. if (width == image.getWidth()) {
  235. return image;
  236. }
  237. int height = image.getHeight() * width / image.getWidth();
  238. logger.finer("resizing to %dx%d", width, height);
  239. BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  240. Graphics2D graphics2D = newImage.createGraphics();
  241. graphics2D.drawImage(image, 0, 0, width, height, null);
  242. return newImage;
  243. }
  244. private void loadProperties(PropertiesFile properties) {
  245. loadTileOrder(properties);
  246. if (tileOrder == null) {
  247. tileOrder = new int[numFrames];
  248. for (int i = 0; i < numFrames; i++) {
  249. tileOrder[i] = i % numTiles;
  250. }
  251. }
  252. tileDelay = new int[numFrames];
  253. loadTileDelay(properties);
  254. for (int i = 0; i < numFrames; i++) {
  255. tileDelay[i] = Math.max(tileDelay[i], 1);
  256. }
  257. }
  258. private void loadTileOrder(PropertiesFile properties) {
  259. if (properties == null) {
  260. return;
  261. }
  262. int i = 0;
  263. for (; getIntValue(properties, "tile.", i) != null; i++) {
  264. }
  265. if (i > 0) {
  266. numFrames = i;
  267. tileOrder = new int[numFrames];
  268. for (i = 0; i < numFrames; i++) {
  269. tileOrder[i] = Math.abs(getIntValue(properties, "tile.", i)) % numTiles;
  270. }
  271. }
  272. }
  273. private void loadTileDelay(PropertiesFile properties) {
  274. if (properties == null) {
  275. return;
  276. }
  277. Integer defaultValue = getIntValue(properties, "duration");
  278. for (int i = 0; i < numFrames; i++) {
  279. Integer value = getIntValue(properties, "duration.", i);
  280. if (value != null) {
  281. tileDelay[i] = value;
  282. } else if (defaultValue != null) {
  283. tileDelay[i] = defaultValue;
  284. }
  285. }
  286. }
  287. private static Integer getIntValue(PropertiesFile properties, String key) {
  288. try {
  289. String value = properties.getString(key, "");
  290. if (value != null && value.matches("^\\d+$")) {
  291. return Integer.parseInt(value);
  292. }
  293. } catch (NumberFormatException e) {
  294. }
  295. return null;
  296. }
  297. private static Integer getIntValue(PropertiesFile properties, String prefix, int index) {
  298. return getIntValue(properties, prefix + index);
  299. }
  300. private void update(int texture, int dx, int dy) {
  301. GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, mipmapLevel, x + dx, y + dy, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) imageData.position(4 * w * h * tileOrder[currentFrame]));
  302. }
  303. private int getDelay() {
  304. return tileDelay[currentFrame];
  305. }
  306. }