PageRenderTime 101ms CodeModel.GetById 81ms app.highlight 17ms RepoModel.GetById 0ms app.codeStats 1ms

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

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