Package com.pclewis.mcpatcher.mod

Source Code of com.pclewis.mcpatcher.mod.MipmapHelper

package com.pclewis.mcpatcher.mod;

import com.pclewis.mcpatcher.MCLogger;
import com.pclewis.mcpatcher.MCPatcherUtils;
import com.pclewis.mcpatcher.TexturePackAPI;
import net.minecraft.src.RenderEngine;
import net.minecraft.src.TextureFX;
import org.lwjgl.opengl.*;
import org.lwjgl.util.glu.GLU;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class MipmapHelper {
    private static final MCLogger logger = MCLogger.getLogger("Mipmap");

    private static final String MIPMAP_PROPERTIES = "/mipmap.properties";

    private static final boolean mipmapSupported;
    private static final boolean mipmapEnabled = MCPatcherUtils.getBoolean(MCPatcherUtils.HD_TEXTURES, "mipmap", false);
    private static final int maxMipmapLevel = MCPatcherUtils.getInt(MCPatcherUtils.HD_TEXTURES, "maxMipmapLevel", 3);
    private static final boolean useMipmap;
    private static final int mipmapAlignment = (1 << MCPatcherUtils.getInt(MCPatcherUtils.HD_TEXTURES, "mipmapAlignment", 3)) - 1;

    private static final boolean anisoSupported;
    private static final int anisoLevel;
    private static final int anisoMax;

    private static final boolean lodSupported;
    private static int lodBias;

    private static int bgColorFix;

    public static int currentLevel;

    private static final HashMap<String, Integer> mipmapType = new HashMap<String, Integer>();
    private static final int MIPMAP_NONE = 0;
    private static final int MIPMAP_BASIC = 1;
    private static final int MIPMAP_ALPHA = 2;

    static {
        mipmapSupported = GLContext.getCapabilities().OpenGL12;
        useMipmap = mipmapSupported && mipmapEnabled && maxMipmapLevel > 0;

        anisoSupported = GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic;
        if (anisoSupported) {
            anisoMax = (int) GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
            checkGLError("glGetFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)");
            anisoLevel = Math.max(Math.min(MCPatcherUtils.getInt(MCPatcherUtils.HD_TEXTURES, "anisotropicFiltering", 1), anisoMax), 1);
        } else {
            anisoMax = anisoLevel = 1;
        }

        lodSupported = GLContext.getCapabilities().GL_EXT_texture_lod_bias;
        if (lodSupported) {
            lodBias = MCPatcherUtils.getInt(MCPatcherUtils.HD_TEXTURES, "lodBias", 0);
        }

        logger.config("mipmap: supported=%s, enabled=%s, level=%d", mipmapSupported, mipmapEnabled, maxMipmapLevel);
        logger.config("anisotropic: supported=%s, level=%d, max=%d", anisoSupported, anisoLevel, anisoMax);
        logger.config("lod bias: supported=%s, bias=%d", lodSupported, lodBias);
    }

    public static void setupTexture(RenderEngine renderEngine, BufferedImage image, int texture, String textureName) {
        if (texture < 0 || image == null) {
            return;
        }
        ArrayList<BufferedImage> mipmapImages = getMipmapsForTexture(image, textureName);
        setupTextureMipmaps(renderEngine, mipmapImages, texture, textureName);
    }

    private static ArrayList<BufferedImage> getMipmapsForTexture(BufferedImage image, String textureName) {
        int type = getMipmapType(textureName, image);
        ArrayList<BufferedImage> mipmapImages = new ArrayList<BufferedImage>();
        mipmapImages.add(image);
        if (type < MIPMAP_BASIC) {
            // nothing
        } else {
            int width = image.getWidth();
            int height = image.getHeight();
            if (getCustomMipmaps(mipmapImages, textureName, width, height)) {
                logger.fine("using %d custom mipmaps for %s", mipmapImages.size() - 1, textureName);
            } else {
                int mipmaps = getMipmapLevels(textureName, image);
                if (mipmaps > 0) {
                    logger.fine("generating %d mipmaps for %s, alpha=%s", mipmaps, textureName, type >= MIPMAP_ALPHA);
                    BufferedImage origImage = image;
                    if (bgColorFix > 0 && type == MIPMAP_BASIC) {
                        int fix;
                        for (fix = 0; fix < bgColorFix && (width >> fix) > 1 && (height >> fix) > 1; fix++) {
                        }
                        BufferedImage scaledImage = new BufferedImage(width >> fix, height >> fix, BufferedImage.TYPE_INT_ARGB);
                        Graphics2D graphics2D = scaledImage.createGraphics();
                        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                        graphics2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                        graphics2D.drawImage(image, 0, 0, width, height, null);
                        setBackgroundColor(image, scaledImage);
                    }
                    for (int i = 0; i < mipmaps; i++) {
                        origImage = scaleHalf(origImage);
                        if (type >= MIPMAP_ALPHA) {
                            image = origImage;
                        } else {
                            image = new BufferedImage(origImage.getColorModel(), origImage.copyData(null), origImage.getColorModel().isAlphaPremultiplied(), null);
                            resetOnOffTransparency(image);
                        }
                        mipmapImages.add(image);
                    }
                } else {
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmaps);
                    type = MIPMAP_NONE;
                    if (textureName != null) {
                        mipmapType.put(textureName, type);
                    }
                }
            }
        }
        return mipmapImages;
    }

    private static void setupTextureMipmaps(RenderEngine renderEngine, ArrayList<BufferedImage> mipmapImages, int texture, String textureName) {
        try {
            int mipmaps = mipmapImages.size() - 1;
            for (currentLevel = 0; currentLevel <= mipmaps; currentLevel++) {
                if (currentLevel == 1) {
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_LINEAR);
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
                    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmaps);
                    checkGLError("set GL_TEXTURE_MAX_LEVEL = %d", mipmaps);
                    if (anisoSupported && anisoLevel > 1) {
                        GL11.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel);
                        checkGLError("set GL_TEXTURE_MAX_ANISOTROPY_EXT = %f", anisoLevel);
                    }
                    if (lodSupported) {
                        GL11.glTexEnvi(EXTTextureLODBias.GL_TEXTURE_FILTER_CONTROL_EXT, EXTTextureLODBias.GL_TEXTURE_LOD_BIAS_EXT, lodBias);
                        checkGLError("set GL_TEXTURE_LOD_BIAS_EXT = %d", lodBias);
                    }
                }
                BufferedImage image = mipmapImages.get(currentLevel);
                renderEngine.setupTexture(image, texture);
                checkGLError("setupTexture %s#%d", textureName, currentLevel);
                if (currentLevel > 0) {
                    logger.finest("%s mipmap level %d (%dx%d)", textureName, currentLevel, image.getWidth(), image.getHeight());
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            currentLevel = 0;
        }
    }

    public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels, TextureFX textureFX) {
        GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
        if (textureFX.tileImage == 0) {
            ByteOrder saveOrder = pixels.order();
            pixels.order(ByteOrder.BIG_ENDIAN);
            update("/terrain.png", xoffset, yoffset, width, height, pixels);
            pixels.order(saveOrder);
        }
    }

    private static void update(String texture, int x, int y, int w, int h, ByteBuffer imageData) {
        int mipmaps = getMipmapLevels();
        for (int i = 1; i <= mipmaps && ((x | y | w | h) & mipmapAlignment) == 0; i++) {
            ByteBuffer newImage = ByteBuffer.allocateDirect(w * h);
            scaleHalf(imageData.asIntBuffer(), w, h, newImage.asIntBuffer());
            x >>= 1;
            y >>= 1;
            w >>= 1;
            h >>= 1;
            int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, i, GL11.GL_TEXTURE_WIDTH);
            int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, i, GL11.GL_TEXTURE_HEIGHT);
            if (width < 0 || height < 0) {
                break;
            }
            GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, i, x, y, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) newImage.position(0));
            checkGLError("glTexSubImage2D(%d, %d, %d, %d, %d, %d), width %d, height %d, mipmaps %d", i, x, y, w, h, newImage.limit(), width, height, mipmaps);
            imageData = newImage;
        }
    }

    static void reset() {
        bgColorFix = 4;
        mipmapType.clear();
        forceMipmapType("/terrain.png", MIPMAP_BASIC);
        Properties properties = TexturePackAPI.getProperties(MIPMAP_PROPERTIES);
        if (properties != null) {
            try {
                bgColorFix = Integer.parseInt(properties.getProperty("bgColorFix", "4"));
            } catch (NumberFormatException e) {
            }
            for (Map.Entry entry : properties.entrySet()) {
                if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
                    String key = ((String) entry.getKey()).trim();
                    String value = ((String) entry.getValue()).trim().toLowerCase();
                    if (key.startsWith("/")) {
                        if (value.equals("none")) {
                            mipmapType.put(key, MIPMAP_NONE);
                        } else if (value.equals("basic") || value.equals("opaque")) {
                            mipmapType.put(key, MIPMAP_BASIC);
                        } else if (value.equals("alpha")) {
                            mipmapType.put(key, MIPMAP_ALPHA);
                        } else {
                            logger.warning("%s: unknown value '%s' for %s", MIPMAP_PROPERTIES, value, key);
                        }
                    }
                }
            }
        }
    }

    private static boolean getCustomMipmaps(ArrayList<BufferedImage> mipmaps, String texture, int baseWidth, int baseHeight) {
        boolean added = false;
        if (useMipmap && texture != null) {
            for (int i = 1; baseWidth > 0 && baseHeight > 0 && i <= maxMipmapLevel; i++) {
                baseWidth >>>= 1;
                baseHeight >>>= 1;
                String name = texture.replace(".png", "-mipmap" + i + ".png");
                BufferedImage image = TexturePackAPI.getImage(name);
                if (image == null) {
                    break;
                }
                int width = image.getWidth();
                int height = image.getHeight();
                if (width == baseWidth && height == baseHeight) {
                    mipmaps.add(image);
                    added = true;
                } else {
                    logger.warning("%s has wrong size %dx%d (expecting %dx%d)", name, width, height, baseWidth, baseHeight);
                    break;
                }
            }
        }
        return added;
    }

    private static int getMipmapType(String texture, BufferedImage image) {
        if (!useMipmap || texture == null) {
            return MIPMAP_NONE;
        } else if (mipmapType.containsKey(texture)) {
            return mipmapType.get(texture);
        } else if (texture.startsWith("%") ||
            texture.startsWith("##") ||
            texture.startsWith("/achievement/") ||
            texture.startsWith("/environment/") ||
            texture.startsWith("/font/") ||
            texture.startsWith("/gui/") ||
            texture.startsWith("/misc/") ||
            texture.startsWith("/terrain/") ||
            texture.startsWith("/title/")) {
            return MIPMAP_NONE;
        } else if (image == null) {
            return MIPMAP_BASIC;
        } else {
            int width = image.getWidth();
            int height = image.getHeight();
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    int pixel = image.getRGB(i, j);
                    int alpha = pixel >>> 24;
                    if (alpha > 0x1a && alpha < 0xe5) {
                        logger.finer("%s alpha transparency? yes, by pixel search", texture);
                        mipmapType.put(texture, MIPMAP_ALPHA);
                        return MIPMAP_ALPHA;
                    }
                }
            }
            logger.finer("%s alpha transparency? no, by pixel search", texture);
            mipmapType.put(texture, MIPMAP_BASIC);
            return MIPMAP_BASIC;
        }
    }

    static void forceMipmapType(String texture, int type) {
        if (!useMipmap) {
            return;
        }
        boolean reload = false;
        if (mipmapType.containsKey(texture)) {
            reload = mipmapType.get(texture) != type;
            if (!reload) {
                return;
            }
        }
        mipmapType.put(texture, type);
        if (reload) {
            logger.finer("force %s -> %d (reloading)", texture, type);
            int id = TexturePackAPI.getTextureIfLoaded(texture);
            if (id >= 0) {
                setupTexture(MCPatcherUtils.getMinecraft().renderEngine, TexturePackAPI.getImage(texture), id, texture);
            }
        } else {
            logger.finer("force %s -> %d", texture, type);
        }
    }

    static int getMipmapLevels() {
        int filter = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
        if (filter != GL11.GL_NEAREST_MIPMAP_LINEAR && filter != GL11.GL_NEAREST_MIPMAP_NEAREST) {
            return 0;
        }
        return GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL);
    }

    private static int getMipmapLevels(String texture, BufferedImage image) {
        return getMipmapLevels(texture, image.getWidth(), image.getHeight());
    }

    private static int getMipmapLevels(String texture, int width, int height) {
        int size = BigInteger.valueOf(width).gcd(BigInteger.valueOf(height)).intValue();
        int minSize = getMinSize(texture);
        int mipmap;
        for (mipmap = 0; size >= minSize && ((size & 1) == 0) && mipmap < maxMipmapLevel; size >>= 1, mipmap++) {
        }
        return mipmap;
    }

    private static int getMinSize(String texture) {
        return texture.equals("/terrain.png") || texture.startsWith("/ctm/") ? 32 : 2;
    }

    private static void setBackgroundColor(BufferedImage image, BufferedImage scaledImage) {
        int width = image.getWidth();
        int height = image.getHeight();
        int scale = width / scaledImage.getWidth();
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int pixel = image.getRGB(i, j);
                if ((pixel & 0xff000000) == 0) {
                    pixel = scaledImage.getRGB(i / scale, j / scale);
                    image.setRGB(i, j, pixel & 0x00ffffff);
                }
            }
        }
    }

    private static void resetOnOffTransparency(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int pixel = image.getRGB(i, j);
                int alpha = pixel >>> 24;
                if (alpha < 0x7f) {
                    pixel &= 0x00ffffff;
                } else {
                    pixel |= 0xff000000;
                }
                image.setRGB(i, j, pixel);
            }
        }
    }

    static void scaleHalf(IntBuffer in, int w, int h, IntBuffer out) {
        for (int i = 0; i < w / 2; i++) {
            for (int j = 0; j < h / 2; j++) {
                int k = w * 2 * j + 2 * i;
                int pixel00 = in.get(k);
                int pixel01 = in.get(k + 1);
                int pixel10 = in.get(k + w);
                int pixel11 = in.get(k + w + 1);
                out.put(w / 2 * j + i, average4RGBA(pixel00, pixel01, pixel10, pixel11));
            }
        }
    }

    private static BufferedImage scaleHalf(BufferedImage in) {
        int w = in.getWidth();
        int h = in.getHeight();
        BufferedImage out = new BufferedImage(w / 2, h / 2, BufferedImage.TYPE_INT_ARGB);
        for (int i = 0; i < w / 2; i++) {
            for (int j = 0; j < h / 2; j++) {
                int pixel00 = Integer.rotateLeft(in.getRGB(2 * i, 2 * j), 8);
                int pixel01 = Integer.rotateLeft(in.getRGB(2 * i + 1, 2 * j), 8);
                int pixel10 = Integer.rotateLeft(in.getRGB(2 * i, 2 * j + 1), 8);
                int pixel11 = Integer.rotateLeft(in.getRGB(2 * i + 1, 2 * j + 1), 8);
                out.setRGB(i, j, Integer.rotateRight(average4RGBA(pixel00, pixel01, pixel10, pixel11), 8));
            }
        }
        return out;
    }

    private static int average4RGBA(int pixel00, int pixel01, int pixel10, int pixel11) {
        int a00 = pixel00 & 0xff;
        int a01 = pixel01 & 0xff;
        int a10 = pixel10 & 0xff;
        int a11 = pixel11 & 0xff;
        switch ((a00 << 24) | (a01 << 16) | (a10 << 8) | a11) {
            case 0xff000000:
                return pixel00;

            case 0x00ff0000:
                return pixel01;

            case 0x0000ff00:
                return pixel10;

            case 0x000000ff:
                return pixel11;

            case 0xffff0000:
                return average2RGBA(pixel00, pixel01);

            case 0xff00ff00:
                return average2RGBA(pixel00, pixel10);

            case 0xff0000ff:
                return average2RGBA(pixel00, pixel11);

            case 0x00ffff00:
                return average2RGBA(pixel01, pixel10);

            case 0x00ff00ff:
                return average2RGBA(pixel01, pixel11);

            case 0x0000ffff:
                return average2RGBA(pixel10, pixel11);

            case 0x00000000:
            case 0xffffffff:
                return average2RGBA(average2RGBA(pixel00, pixel11), average2RGBA(pixel01, pixel10));

            default:
                int a = a00 + a01 + a10 + a11;
                int pixel = a >> 2;
                for (int i = 8; i < 32; i += 8) {
                    int average = (a00 * ((pixel00 >> i) & 0xff) + a01 * ((pixel01 >> i) & 0xff) +
                        a10 * ((pixel10 >> i) & 0xff) + a11 * ((pixel11 >> i) & 0xff)) / a;
                    pixel |= (average << i);
                }
                return pixel;
        }
    }

    private static int average2RGBA(int a, int b) {
        return (((a & 0xfefefefe) >>> 1) + ((b & 0xfefefefe) >>> 1)) | (a & b & 0x01010101);
    }

    private static void checkGLError(String format, Object... params) {
        int error = GL11.glGetError();
        if (error != 0) {
            String message = GLU.gluErrorString(error) + ": " + String.format(format, params);
            new RuntimeException(message).printStackTrace();
        }
    }
}
TOP

Related Classes of com.pclewis.mcpatcher.mod.MipmapHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.