PageRenderTime 153ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/Freso/mcpatcher
Java | 408 lines | 362 code | 46 blank | 0 comment | 78 complexity | 9cbeff29618d0a0f56971ca189272514 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.TexturePackAPI;
  6. import net.minecraft.src.ResourceLocation;
  7. import org.lwjgl.opengl.*;
  8. import org.lwjgl.util.glu.GLU;
  9. import java.awt.*;
  10. import java.awt.image.BufferedImage;
  11. import java.awt.image.DataBuffer;
  12. import java.awt.image.DataBufferByte;
  13. import java.awt.image.DataBufferInt;
  14. import java.lang.ref.Reference;
  15. import java.lang.ref.SoftReference;
  16. import java.math.BigInteger;
  17. import java.nio.ByteBuffer;
  18. import java.nio.ByteOrder;
  19. import java.nio.IntBuffer;
  20. import java.util.Arrays;
  21. import java.util.HashMap;
  22. import java.util.Map;
  23. import java.util.Properties;
  24. public class MipmapHelper {
  25. private static final MCLogger logger = MCLogger.getLogger(MCPatcherUtils.MIPMAP);
  26. private static final ResourceLocation MIPMAP_PROPERTIES = TexturePackAPI.newMCPatcherResourceLocation("mipmap.properties");
  27. private static final int TEX_FORMAT = GL12.GL_BGRA;
  28. private static final int TEX_DATA_TYPE = GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
  29. private static final int MIN_ALPHA = 0x1a;
  30. private static final int MAX_ALPHA = 0xe5;
  31. private static final boolean mipmapSupported;
  32. static final boolean mipmapEnabled = Config.getBoolean(MCPatcherUtils.EXTENDED_HD, "mipmap", false);
  33. static final int maxMipmapLevel = Config.getInt(MCPatcherUtils.EXTENDED_HD, "maxMipmapLevel", 3);
  34. private static final boolean useMipmap;
  35. private static final int mipmapAlignment = (1 << Config.getInt(MCPatcherUtils.EXTENDED_HD, "mipmapAlignment", 3)) - 1;
  36. private static final boolean anisoSupported;
  37. static final int anisoLevel;
  38. private static final int anisoMax;
  39. private static final boolean lodSupported;
  40. private static final int lodBias;
  41. private static final Map<String, Reference<BufferedImage>> imagePool = new HashMap<String, Reference<BufferedImage>>();
  42. private static final Map<Integer, Reference<ByteBuffer>> bufferPool = new HashMap<Integer, Reference<ByteBuffer>>();
  43. private static final Map<String, Boolean> mipmapType = new HashMap<String, Boolean>();
  44. static {
  45. mipmapSupported = GLContext.getCapabilities().OpenGL12;
  46. useMipmap = mipmapSupported && mipmapEnabled && maxMipmapLevel > 0;
  47. anisoSupported = GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic;
  48. if (anisoSupported) {
  49. anisoMax = (int) GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
  50. checkGLError("glGetFloat(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)");
  51. anisoLevel = Math.max(Math.min(Config.getInt(MCPatcherUtils.EXTENDED_HD, "anisotropicFiltering", 1), anisoMax), 1);
  52. } else {
  53. anisoMax = anisoLevel = 1;
  54. }
  55. lodSupported = GLContext.getCapabilities().GL_EXT_texture_lod_bias;
  56. if (lodSupported) {
  57. lodBias = Config.getInt(MCPatcherUtils.EXTENDED_HD, "lodBias", 0);
  58. } else {
  59. lodBias = 0;
  60. }
  61. logger.config("mipmap: supported=%s, enabled=%s, level=%d", mipmapSupported, mipmapEnabled, maxMipmapLevel);
  62. logger.config("anisotropic: supported=%s, level=%d, max=%d", anisoSupported, anisoLevel, anisoMax);
  63. logger.config("lod bias: supported=%s, bias=%d", lodSupported, lodBias);
  64. }
  65. private static void setupTexture(int width, int height, boolean blur, boolean clamp, String textureName) {
  66. int mipmaps = useMipmapsForTexture(textureName) ? getMipmapLevels(width, height, 1) : 0;
  67. logger.finer("setupTexture(%s) %dx%d %d mipmaps", textureName, width, height, mipmaps);
  68. int magFilter = blur ? GL11.GL_LINEAR : GL11.GL_NEAREST;
  69. int minFilter = mipmaps > 0 ? GL11.GL_NEAREST_MIPMAP_LINEAR : magFilter;
  70. int wrap = clamp ? GL11.GL_CLAMP : GL11.GL_REPEAT;
  71. if (mipmaps > 0) {
  72. GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmaps);
  73. checkGLError("%s: set GL_TEXTURE_MAX_LEVEL = %d", textureName, mipmaps);
  74. if (anisoSupported && anisoLevel > 1) {
  75. GL11.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel);
  76. checkGLError("%s: set GL_TEXTURE_MAX_ANISOTROPY_EXT = %f", textureName, anisoLevel);
  77. }
  78. if (lodSupported) {
  79. GL11.glTexEnvi(EXTTextureLODBias.GL_TEXTURE_FILTER_CONTROL_EXT, EXTTextureLODBias.GL_TEXTURE_LOD_BIAS_EXT, lodBias);
  80. checkGLError("%s: set GL_TEXTURE_LOD_BIAS_EXT = %d", textureName, lodBias);
  81. }
  82. }
  83. GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
  84. GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
  85. GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap);
  86. GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap);
  87. for (int level = 0; level <= mipmaps; level++) {
  88. GL11.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA, width, height, 0, TEX_FORMAT, TEX_DATA_TYPE, (IntBuffer) null);
  89. checkGLError("%s: glTexImage2D %dx%d level %d", textureName, width, height, level);
  90. width >>= 1;
  91. height >>= 1;
  92. }
  93. }
  94. public static void setupTexture(int[] rgb, int width, int height, int x, int y, boolean blur, boolean clamp, String textureName) {
  95. setupTexture(width, height, blur, clamp, textureName);
  96. copySubTexture(rgb, width, height, x, y, textureName);
  97. }
  98. public static int setupTexture(int glTexture, BufferedImage image, boolean blur, boolean clamp, ResourceLocation textureName) {
  99. int width = image.getWidth();
  100. int height = image.getHeight();
  101. GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
  102. logger.finer("setupTexture(%s, %d, %dx%d, %s, %s)", textureName, glTexture, width, height, blur, clamp);
  103. int[] rgb = new int[width * height];
  104. image.getRGB(0, 0, width, height, rgb, 0, width);
  105. setupTexture(rgb, width, height, 0, 0, blur, clamp, textureName.getPath());
  106. return glTexture;
  107. }
  108. public static void setupTexture(int glTexture, int width, int height, String textureName) {
  109. GL11.glBindTexture(GL11.GL_TEXTURE_2D, glTexture);
  110. logger.finer("setupTexture(tilesheet %s, %d, %dx%d)", textureName, glTexture, width, height);
  111. setupTexture(width, height, false, false, textureName);
  112. }
  113. public static void copySubTexture(int[] rgb, int width, int height, int x, int y, String textureName) {
  114. IntBuffer buffer = getPooledBuffer(width * height * 4).asIntBuffer();
  115. buffer.put(rgb).position(0);
  116. int mipmaps = getMipmapLevelsForCurrentTexture();
  117. IntBuffer newBuffer;
  118. logger.finest("copySubTexture %s %d,%d %dx%d %d mipmaps", textureName, x, y, width, height, mipmaps);
  119. for (int level = 0; ; ) {
  120. if (width <= 0 || height <= 0) {
  121. break;
  122. }
  123. GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, level, x, y, width, height, TEX_FORMAT, TEX_DATA_TYPE, buffer);
  124. checkGLError("%s: glTexSubImage2D(%d, %d, %d, %d, %d)", textureName, level, x, y, width, height);
  125. if (level >= mipmaps) {
  126. break;
  127. }
  128. newBuffer = getPooledBuffer(width * height).asIntBuffer();
  129. scaleHalf(buffer, width, height, newBuffer, 0);
  130. buffer = newBuffer;
  131. level++;
  132. x >>= 1;
  133. y >>= 1;
  134. width >>= 1;
  135. height >>= 1;
  136. }
  137. }
  138. static BufferedImage fixTransparency(ResourceLocation name, BufferedImage image) {
  139. if (image == null) {
  140. return image;
  141. }
  142. long s1 = System.currentTimeMillis();
  143. image = convertToARGB(image);
  144. int width = image.getWidth();
  145. int height = image.getHeight();
  146. IntBuffer buffer = getImageAsARGBIntBuffer(image);
  147. IntBuffer scaledBuffer = buffer;
  148. outer:
  149. while (width % 2 == 0 && height % 2 == 0) {
  150. for (int i = 0; i < scaledBuffer.limit(); i++) {
  151. if (scaledBuffer.get(i) >>> 24 == 0) {
  152. IntBuffer newBuffer = getPooledBuffer(width * height).asIntBuffer();
  153. scaleHalf(scaledBuffer, width, height, newBuffer, 8);
  154. scaledBuffer = newBuffer;
  155. width >>= 1;
  156. height >>= 1;
  157. continue outer;
  158. }
  159. }
  160. break;
  161. }
  162. long s2 = System.currentTimeMillis();
  163. if (scaledBuffer != buffer) {
  164. setBackgroundColor(buffer, image.getWidth(), image.getHeight(), scaledBuffer, image.getWidth() / width);
  165. }
  166. long s3 = System.currentTimeMillis();
  167. logger.finer("bg fix (tile %s): scaling %dms, setbg %dms", name, s2 - s1, s3 - s2);
  168. return image;
  169. }
  170. static void reset() {
  171. mipmapType.clear();
  172. mipmapType.put("terrain", true);
  173. mipmapType.put("items", false);
  174. Properties properties = TexturePackAPI.getProperties(MIPMAP_PROPERTIES);
  175. if (properties != null) {
  176. for (Map.Entry entry : properties.entrySet()) {
  177. if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
  178. String key = ((String) entry.getKey()).trim();
  179. boolean value = Boolean.parseBoolean(((String) entry.getValue()).trim().toLowerCase());
  180. if (key.endsWith(".png")) {
  181. mipmapType.put(key, value);
  182. }
  183. }
  184. }
  185. }
  186. }
  187. static boolean useMipmapsForTexture(String texture) {
  188. if (!useMipmap || texture == null) {
  189. return false;
  190. } else if (mipmapType.containsKey(texture)) {
  191. return mipmapType.get(texture);
  192. } else if (texture.contains("item") ||
  193. texture.startsWith("textures/colormap/") ||
  194. texture.startsWith("textures/environment/") ||
  195. texture.startsWith("textures/font/") ||
  196. texture.startsWith("textures/gui/") ||
  197. texture.startsWith("textures/map/") ||
  198. texture.startsWith("textures/misc/") ||
  199. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "colormap/") ||
  200. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "cit/") ||
  201. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "dial/") ||
  202. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "font/") ||
  203. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "lightmap/") ||
  204. texture.startsWith(TexturePackAPI.MCPATCHER_SUBDIR + "sky/")) {
  205. return false;
  206. } else {
  207. return true;
  208. }
  209. }
  210. static int getMipmapLevelsForCurrentTexture() {
  211. int filter = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
  212. if (filter != GL11.GL_NEAREST_MIPMAP_LINEAR && filter != GL11.GL_NEAREST_MIPMAP_NEAREST) {
  213. return 0;
  214. }
  215. return Math.min(maxMipmapLevel, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL));
  216. }
  217. private static int gcd(int a, int b) {
  218. return BigInteger.valueOf(a).gcd(BigInteger.valueOf(b)).intValue();
  219. }
  220. private static int getMipmapLevels(int width, int height, int minSize) {
  221. int size = gcd(width, height);
  222. int mipmap;
  223. for (mipmap = 0; size >= minSize && ((size & 1) == 0) && mipmap < maxMipmapLevel; size >>= 1, mipmap++) {
  224. }
  225. return mipmap;
  226. }
  227. private static BufferedImage getPooledImage(int width, int height, int index) {
  228. String key = String.format("%dx%d#%d", width, height, index);
  229. Reference<BufferedImage> ref = imagePool.get(key);
  230. BufferedImage image = (ref == null ? null : ref.get());
  231. if (image == null) {
  232. image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  233. imagePool.put(key, new SoftReference<BufferedImage>(image));
  234. }
  235. return image;
  236. }
  237. private static ByteBuffer getPooledBuffer(int size) {
  238. Reference<ByteBuffer> ref = bufferPool.get(size);
  239. ByteBuffer buffer = (ref == null ? null : ref.get());
  240. if (buffer == null) {
  241. buffer = ByteBuffer.allocateDirect(size);
  242. bufferPool.put(size, new SoftReference<ByteBuffer>(buffer));
  243. }
  244. buffer.order(ByteOrder.LITTLE_ENDIAN);
  245. buffer.position(0);
  246. return buffer;
  247. }
  248. private static BufferedImage convertToARGB(BufferedImage image) {
  249. if (image == null) {
  250. return null;
  251. } else if (image.getType() == BufferedImage.TYPE_INT_ARGB) {
  252. return image;
  253. } else {
  254. int width = image.getWidth();
  255. int height = image.getHeight();
  256. logger.finest("converting %dx%d image to ARGB", width, height);
  257. BufferedImage newImage = getPooledImage(width, height, 0);
  258. Graphics2D graphics = newImage.createGraphics();
  259. Arrays.fill(getImageAsARGBIntBuffer(newImage).array(), 0);
  260. graphics.drawImage(image, 0, 0, null);
  261. return newImage;
  262. }
  263. }
  264. private static IntBuffer getImageAsARGBIntBuffer(BufferedImage image) {
  265. DataBuffer buffer = image.getRaster().getDataBuffer();
  266. if (buffer instanceof DataBufferInt) {
  267. return IntBuffer.wrap(((DataBufferInt) buffer).getData());
  268. } else if (buffer instanceof DataBufferByte) {
  269. return ByteBuffer.wrap(((DataBufferByte) buffer).getData()).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
  270. } else {
  271. int width = image.getWidth();
  272. int height = image.getHeight();
  273. int[] pixels = new int[width * height];
  274. image.getRGB(0, 0, width, height, pixels, 0, width);
  275. return IntBuffer.wrap(pixels);
  276. }
  277. }
  278. private static void setBackgroundColor(IntBuffer buffer, int width, int height, IntBuffer scaledBuffer, int scale) {
  279. for (int i = 0; i < width; i++) {
  280. for (int j = 0; j < height; j++) {
  281. int k = width * j + i;
  282. int pixel = buffer.get(k);
  283. if ((pixel & 0xff000000) == 0) {
  284. pixel = scaledBuffer.get((j / scale) * (width / scale) + i / scale);
  285. buffer.put(k, pixel & 0x00ffffff);
  286. }
  287. }
  288. }
  289. }
  290. static void scaleHalf(IntBuffer in, int w, int h, IntBuffer out, int rotate) {
  291. for (int i = 0; i < w / 2; i++) {
  292. for (int j = 0; j < h / 2; j++) {
  293. int k = w * 2 * j + 2 * i;
  294. int pixel00 = in.get(k);
  295. int pixel01 = in.get(k + 1);
  296. int pixel10 = in.get(k + w);
  297. int pixel11 = in.get(k + w + 1);
  298. if (rotate != 0) {
  299. pixel00 = Integer.rotateLeft(pixel00, rotate);
  300. pixel01 = Integer.rotateLeft(pixel01, rotate);
  301. pixel10 = Integer.rotateLeft(pixel10, rotate);
  302. pixel11 = Integer.rotateLeft(pixel11, rotate);
  303. }
  304. int pixel = average4RGBA(pixel00, pixel01, pixel10, pixel11);
  305. if (rotate != 0) {
  306. pixel = Integer.rotateRight(pixel, rotate);
  307. }
  308. out.put(w / 2 * j + i, pixel);
  309. }
  310. }
  311. }
  312. private static int average4RGBA(int pixel00, int pixel01, int pixel10, int pixel11) {
  313. int a00 = pixel00 & 0xff;
  314. int a01 = pixel01 & 0xff;
  315. int a10 = pixel10 & 0xff;
  316. int a11 = pixel11 & 0xff;
  317. switch ((a00 << 24) | (a01 << 16) | (a10 << 8) | a11) {
  318. case 0xff000000:
  319. return pixel00;
  320. case 0x00ff0000:
  321. return pixel01;
  322. case 0x0000ff00:
  323. return pixel10;
  324. case 0x000000ff:
  325. return pixel11;
  326. case 0xffff0000:
  327. return average2RGBA(pixel00, pixel01);
  328. case 0xff00ff00:
  329. return average2RGBA(pixel00, pixel10);
  330. case 0xff0000ff:
  331. return average2RGBA(pixel00, pixel11);
  332. case 0x00ffff00:
  333. return average2RGBA(pixel01, pixel10);
  334. case 0x00ff00ff:
  335. return average2RGBA(pixel01, pixel11);
  336. case 0x0000ffff:
  337. return average2RGBA(pixel10, pixel11);
  338. case 0x00000000:
  339. case 0xffffffff:
  340. return average2RGBA(average2RGBA(pixel00, pixel11), average2RGBA(pixel01, pixel10));
  341. default:
  342. int a = a00 + a01 + a10 + a11;
  343. int pixel = a >> 2;
  344. for (int i = 8; i < 32; i += 8) {
  345. int average = (a00 * ((pixel00 >> i) & 0xff) + a01 * ((pixel01 >> i) & 0xff) +
  346. a10 * ((pixel10 >> i) & 0xff) + a11 * ((pixel11 >> i) & 0xff)) / a;
  347. pixel |= (average << i);
  348. }
  349. return pixel;
  350. }
  351. }
  352. private static int average2RGBA(int a, int b) {
  353. return (((a & 0xfefefefe) >>> 1) + ((b & 0xfefefefe) >>> 1)) | (a & b & 0x01010101);
  354. }
  355. private static void checkGLError(String format, Object... params) {
  356. int error = GL11.glGetError();
  357. if (error != 0) {
  358. String message = GLU.gluErrorString(error) + ": " + String.format(format, params);
  359. new RuntimeException(message).printStackTrace();
  360. }
  361. }
  362. }