package com.prupe.mcpatcher.hd;
import com.prupe.mcpatcher.Config;
import com.prupe.mcpatcher.MCLogger;
import com.prupe.mcpatcher.TexturePackAPI;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.ImageObserver;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import net.minecraft.src.ResourceLocation;
import net.minecraft.src.TextureAtlasSprite;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.glu.GLU;
public class MipmapHelper {
private static final MCLogger logger = MCLogger.getLogger("Mipmap");
private static final ResourceLocation MIPMAP_PROPERTIES = TexturePackAPI.newMCPatcherResourceLocation("mipmap.properties");
private static final int TEX_FORMAT = 32993;
private static final int TEX_DATA_TYPE = 33639;
private static final int MIN_ALPHA = 26;
private static final int MAX_ALPHA = 229;
private static final boolean mipmapSupported = GLContext.getCapabilities().OpenGL12;
static final boolean mipmapEnabled = Config.getBoolean("Extended HD", "mipmap", false);
static final int maxMipmapLevel = Config.getInt("Extended HD", "maxMipmapLevel", 3);
private static final boolean useMipmap = mipmapSupported && mipmapEnabled && maxMipmapLevel > 0;
private static final int mipmapAlignment = (1 << Config.getInt("Extended HD", "mipmapAlignment", 3)) - 1;
private static final boolean anisoSupported = GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic;
static final int anisoLevel;
private static final int anisoMax;
private static final boolean lodSupported;
private static final int lodBias;
private static final Map<String, Reference<BufferedImage>> imagePool = new HashMap();
private static final Map<Integer, Reference<ByteBuffer>> bufferPool = new HashMap();
private static final Map<String, Boolean> mipmapType = new HashMap();
private static void setupTexture(int width, int height, boolean blur, boolean clamp, String textureName) {
int mipmaps = useMipmapsForTexture(textureName) ? getMipmapLevels(width, height, 1) : 0;
logger.finer("setupTexture(%s) %dx%d %d mipmaps", new Object[] {textureName, Integer.valueOf(width), Integer.valueOf(height), Integer.valueOf(mipmaps)});
int magFilter = blur ? 9729 : 9728;
int minFilter = mipmaps > 0 ? 9986 : magFilter;
int wrap = clamp ? 10496 : 10497;
if (mipmaps > 0) {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmaps);
checkGLError("%s: set GL_TEXTURE_MAX_LEVEL = %d", new Object[] {textureName, Integer.valueOf(mipmaps)});
if (anisoSupported && anisoLevel > 1) {
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, 34046, (float)anisoLevel);
checkGLError("%s: set GL_TEXTURE_MAX_ANISOTROPY_EXT = %f", new Object[] {textureName, Integer.valueOf(anisoLevel)});
}
if (lodSupported) {
GL11.glTexEnvi(GL14.GL_TEXTURE_FILTER_CONTROL, GL14.GL_TEXTURE_LOD_BIAS, lodBias);
checkGLError("%s: set GL_TEXTURE_LOD_BIAS_EXT = %d", new Object[] {textureName, Integer.valueOf(lodBias)});
}
}
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap);
for (int level = 0; level <= mipmaps; ++level) {
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA, width, height, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, (IntBuffer)null);
checkGLError("%s: glTexImage2D %dx%d level %d", new Object[] {textureName, Integer.valueOf(width), Integer.valueOf(height), Integer.valueOf(level)});
width >>= 1;
height >>= 1;
}
}
public static void setupTexture(int[] rgb, int width, int height, int x, int y, boolean blur, boolean clamp, String textureName) {
setupTexture(width, height, blur, clamp, textureName);
copySubTexture(rgb, width, height, x, y, textureName);
}
public static int setupTexture(int glTexture, BufferedImage image, boolean blur, boolean clamp, ResourceLocation textureName) {
int width = image.getWidth();
int height = image.getHeight();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
logger.finer("setupTexture(%s, %d, %dx%d, %s, %s)", new Object[] {textureName, Integer.valueOf(glTexture), Integer.valueOf(width), Integer.valueOf(height), Boolean.valueOf(blur), Boolean.valueOf(clamp)});
int[] rgb = new int[width * height];
image.getRGB(0, 0, width, height, rgb, 0, width);
setupTexture(rgb, width, height, 0, 0, blur, clamp, textureName.getResourcePath());
return glTexture;
}
public static void setupTexture(int glTexture, int width, int height, String textureName) {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
logger.finer("setupTexture(tilesheet %s, %d, %dx%d)", new Object[] {textureName, Integer.valueOf(glTexture), Integer.valueOf(width), Integer.valueOf(height)});
setupTexture(width, height, false, false, textureName);
}
public static void copySubTexture(int[] rgb, int width, int height, int x, int y, String textureName) {
if (rgb == null) {
logger.error("copySubTexture %s %d,%d %dx%d: rgb data is null", new Object[] {textureName, Integer.valueOf(x), Integer.valueOf(y), Integer.valueOf(width), Integer.valueOf(height)});
} else {
IntBuffer buffer = getPooledBuffer(width * height * 4).asIntBuffer();
buffer.put(rgb).position(0);
int mipmaps = getMipmapLevelsForCurrentTexture();
logger.finest("copySubTexture %s %d,%d %dx%d %d mipmaps", new Object[] {textureName, Integer.valueOf(x), Integer.valueOf(y), Integer.valueOf(width), Integer.valueOf(height), Integer.valueOf(mipmaps)});
for (int level = 0; width > 0 && height > 0; height >>= 1) {
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, level, x, y, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
checkGLError("%s: glTexSubImage2D(%d, %d, %d, %d, %d)", new Object[] {textureName, Integer.valueOf(level), Integer.valueOf(x), Integer.valueOf(y), Integer.valueOf(width), Integer.valueOf(height)});
if (level >= mipmaps) {
break;
}
IntBuffer newBuffer = getPooledBuffer(width * height).asIntBuffer();
scaleHalf(buffer, width, height, newBuffer, 0);
buffer = newBuffer;
++level;
x >>= 1;
y >>= 1;
width >>= 1;
}
}
}
public static void copySubTexture(TextureAtlasSprite texture, int index) {
int width = texture.getIconWidth();
int height = texture.getIconHeight();
int x;
int y;
if (texture.mipmaps == null || texture.mipmaps.size() != texture.framesTextureData.size()) {
texture.mipmaps = new ArrayList(texture.framesTextureData.size());
x = getMipmapLevelsForCurrentTexture();
if (x > 0) {
logger.fine("generating %d mipmaps for tile %s", new Object[] {Integer.valueOf(x), texture.getIconName()});
}
for (y = 0; y < texture.framesTextureData.size(); ++y) {
texture.mipmaps.add(generateMipmaps((int[])texture.framesTextureData.get(y), width, height, x));
}
}
x = texture.getOriginX();
y = texture.getOriginY();
IntBuffer[] mipmapData = (IntBuffer[])texture.mipmaps.get(index);
if (mipmapData != null) {
for (int level = 0; level < mipmapData.length; ++level) {
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, level, x, y, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, mipmapData[level]);
x >>= 1;
y >>= 1;
width >>= 1;
height >>= 1;
}
}
}
private static IntBuffer[] generateMipmaps(int[] rgb, int width, int height, int mipmaps) {
if (rgb == null) {
return null;
} else {
ArrayList mipmapData = new ArrayList();
IntBuffer buffer = newIntBuffer(width * height * 4);
buffer.put(rgb).position(0);
int level = 0;
while (true) {
mipmapData.add(buffer);
if (width <= 0 || height <= 0 || level >= mipmaps) {
return (IntBuffer[])mipmapData.toArray(new IntBuffer[mipmapData.size()]);
}
IntBuffer newBuffer = newIntBuffer(width * height);
scaleHalf(buffer, width, height, newBuffer, 0);
buffer = newBuffer;
++level;
width >>= 1;
height >>= 1;
}
}
}
static BufferedImage fixTransparency(ResourceLocation name, BufferedImage image) {
if (image == null) {
return image;
} else {
long s1 = System.currentTimeMillis();
image = convertToARGB(image);
int width = image.getWidth();
int height = image.getHeight();
IntBuffer buffer = getImageAsARGBIntBuffer(image);
IntBuffer scaledBuffer = buffer;
label33:
while (width % 2 == 0 && height % 2 == 0) {
int s2 = 0;
while (true) {
if (s2 >= scaledBuffer.limit()) {
break label33;
}
if (scaledBuffer.get(s2) >>> 24 == 0) {
IntBuffer newBuffer = getPooledBuffer(width * height).asIntBuffer();
scaleHalf(scaledBuffer, width, height, newBuffer, 8);
scaledBuffer = newBuffer;
width >>= 1;
height >>= 1;
break;
}
++s2;
}
}
long var12 = System.currentTimeMillis();
if (scaledBuffer != buffer) {
setBackgroundColor(buffer, image.getWidth(), image.getHeight(), scaledBuffer, image.getWidth() / width);
}
long s3 = System.currentTimeMillis();
logger.finer("bg fix (tile %s): scaling %dms, setbg %dms", new Object[] {name, Long.valueOf(var12 - s1), Long.valueOf(s3 - var12)});
return image;
}
}
static void reset() {
mipmapType.clear();
mipmapType.put("terrain", Boolean.valueOf(true));
mipmapType.put("items", Boolean.valueOf(false));
Properties properties = TexturePackAPI.getProperties(MIPMAP_PROPERTIES);
if (properties != null) {
Iterator i$ = properties.entrySet().iterator();
while (i$.hasNext()) {
Entry entry = (Entry)i$.next();
if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
String key = ((String)entry.getKey()).trim();
boolean value = Boolean.parseBoolean(((String)entry.getValue()).trim().toLowerCase());
if (key.endsWith(".png")) {
mipmapType.put(key, Boolean.valueOf(value));
}
}
}
}
}
static boolean useMipmapsForTexture(String texture) {
return useMipmap && texture != null ? (mipmapType.containsKey(texture) ? ((Boolean)mipmapType.get(texture)).booleanValue() : !texture.contains("item") && !texture.startsWith("textures/colormap/") && !texture.startsWith("textures/environment/") && !texture.startsWith("textures/font/") && !texture.startsWith("textures/gui/") && !texture.startsWith("textures/map/") && !texture.startsWith("textures/misc/") && !texture.startsWith("mcpatcher/colormap/") && !texture.startsWith("mcpatcher/cit/") && !texture.startsWith("mcpatcher/dial/") && !texture.startsWith("mcpatcher/font/") && !texture.startsWith("mcpatcher/lightmap/") && !texture.startsWith("mcpatcher/sky/")) : false;
}
static int getMipmapLevelsForCurrentTexture() {
int filter = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
return filter != 9986 && filter != 9984 ? 0 : Math.min(maxMipmapLevel, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL));
}
private static int gcd(int a, int b) {
return BigInteger.valueOf((long)a).gcd(BigInteger.valueOf((long)b)).intValue();
}
private static int getMipmapLevels(int width, int height, int minSize) {
int size = gcd(width, height);
int mipmap;
for (mipmap = 0; size >= minSize && (size & 1) == 0 && mipmap < maxMipmapLevel; ++mipmap) {
size >>= 1;
}
return mipmap;
}
private static BufferedImage getPooledImage(int width, int height, int index) {
String key = String.format("%dx%d#%d", new Object[] {Integer.valueOf(width), Integer.valueOf(height), Integer.valueOf(index)});
Reference ref = (Reference)imagePool.get(key);
BufferedImage image = ref == null ? null : (BufferedImage)ref.get();
if (image == null) {
image = new BufferedImage(width, height, 2);
imagePool.put(key, new SoftReference(image));
}
return image;
}
private static IntBuffer newIntBuffer(int size) {
ByteBuffer buffer = ByteBuffer.allocateDirect(size);
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer.asIntBuffer();
}
private static ByteBuffer getPooledBuffer(int size) {
Reference ref = (Reference)bufferPool.get(Integer.valueOf(size));
ByteBuffer buffer = ref == null ? null : (ByteBuffer)ref.get();
if (buffer == null) {
buffer = ByteBuffer.allocateDirect(size);
bufferPool.put(Integer.valueOf(size), new SoftReference(buffer));
}
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
return buffer;
}
private static BufferedImage convertToARGB(BufferedImage image) {
if (image == null) {
return null;
} else if (image.getType() == 2) {
return image;
} else {
int width = image.getWidth();
int height = image.getHeight();
logger.finest("converting %dx%d image to ARGB", new Object[] {Integer.valueOf(width), Integer.valueOf(height)});
BufferedImage newImage = getPooledImage(width, height, 0);
Graphics2D graphics = newImage.createGraphics();
Arrays.fill(getImageAsARGBIntBuffer(newImage).array(), 0);
graphics.drawImage(image, 0, 0, (ImageObserver)null);
return newImage;
}
}
private static IntBuffer getImageAsARGBIntBuffer(BufferedImage image) {
DataBuffer buffer = image.getRaster().getDataBuffer();
if (buffer instanceof DataBufferInt) {
return IntBuffer.wrap(((DataBufferInt)buffer).getData());
} else if (buffer instanceof DataBufferByte) {
return ByteBuffer.wrap(((DataBufferByte)buffer).getData()).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
} else {
int width = image.getWidth();
int height = image.getHeight();
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
return IntBuffer.wrap(pixels);
}
}
private static void setBackgroundColor(IntBuffer buffer, int width, int height, IntBuffer scaledBuffer, int scale) {
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
int k = width * j + i;
int pixel = buffer.get(k);
if ((pixel & -16777216) == 0) {
pixel = scaledBuffer.get(j / scale * (width / scale) + i / scale);
buffer.put(k, pixel & 16777215);
}
}
}
}
static void scaleHalf(IntBuffer in, int w, int h, IntBuffer out, int rotate) {
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);
if (rotate != 0) {
pixel00 = Integer.rotateLeft(pixel00, rotate);
pixel01 = Integer.rotateLeft(pixel01, rotate);
pixel10 = Integer.rotateLeft(pixel10, rotate);
pixel11 = Integer.rotateLeft(pixel11, rotate);
}
int pixel = average4RGBA(pixel00, pixel01, pixel10, pixel11);
if (rotate != 0) {
pixel = Integer.rotateRight(pixel, rotate);
}
out.put(w / 2 * j + i, pixel);
}
}
}
private static int average4RGBA(int pixel00, int pixel01, int pixel10, int pixel11) {
int a00 = pixel00 & 255;
int a01 = pixel01 & 255;
int a10 = pixel10 & 255;
int a11 = pixel11 & 255;
switch (a00 << 24 | a01 << 16 | a10 << 8 | a11) {
case -16777216:
return pixel00;
case -16776961:
return average2RGBA(pixel00, pixel11);
case -16711936:
return average2RGBA(pixel00, pixel10);
case -65536:
return average2RGBA(pixel00, pixel01);
case -1:
case 0:
return average2RGBA(average2RGBA(pixel00, pixel11), average2RGBA(pixel01, pixel10));
case 255:
return pixel11;
case 65280:
return pixel10;
case 65535:
return average2RGBA(pixel10, pixel11);
case 16711680:
return pixel01;
case 16711935:
return average2RGBA(pixel01, pixel11);
case 16776960:
return 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 & 255) + a01 * (pixel01 >> i & 255) + a10 * (pixel10 >> i & 255) + a11 * (pixel11 >> i & 255)) / a;
pixel |= average << i;
}
return pixel;
}
}
private static int average2RGBA(int a, int b) {
return ((a & -16843010) >>> 1) + ((b & -16843010) >>> 1) | a & b & 16843009;
}
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();
}
}
static {
if (anisoSupported) {
anisoMax = (int)GL11.glGetFloat(34047);
checkGLError("glGetFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)", new Object[0]);
anisoLevel = Math.max(Math.min(Config.getInt("Extended HD", "anisotropicFiltering", 1), anisoMax), 1);
} else {
anisoLevel = 1;
anisoMax = 1;
}
lodSupported = GLContext.getCapabilities().GL_EXT_texture_lod_bias;
if (lodSupported) {
lodBias = Config.getInt("Extended HD", "lodBias", 0);
} else {
lodBias = 0;
}
logger.config("mipmap: supported=%s, enabled=%s, level=%d", new Object[] {Boolean.valueOf(mipmapSupported), Boolean.valueOf(mipmapEnabled), Integer.valueOf(maxMipmapLevel)});
logger.config("anisotropic: supported=%s, level=%d, max=%d", new Object[] {Boolean.valueOf(anisoSupported), Integer.valueOf(anisoLevel), Integer.valueOf(anisoMax)});
logger.config("lod bias: supported=%s, bias=%d", new Object[] {Boolean.valueOf(lodSupported), Integer.valueOf(lodBias)});
}
}