纹理工具层(utils/ 纹理与 mipmap)

基本信息

属性 值
包 com.gtnewhorizons.angelica.utils
本条目覆盖 6 个文件(目录共 14 个)
总行数 284 + 98 + 85 + 53 + 46 + 33 = 599
上游 net.minecraft.client.renderer.texture.TextureUtil(原版)
# 文件 行数 可见性
1 MipmapGenerator.java 284 public final
2 SpritePadding.java 98 public final
3 MipmapStrategies.java 85 public final
4 NaturalTextureUtils.java 53 public final
5 BufferUtil.java 46 public final
6 MipmapStrategy.java 33 public interface(1 方法)

同包另 8 个文件见 utils 运行时工具。

MipmapStrategy:函数式接口

33 行,只有一个抽象方法:

public interface MipmapStrategy {
    int apply(int first, int second);      // :~7
    MipmapStrategy MEAN          = (a, b) -> (a + b) >> 1;        // 均值
    MipmapStrategy DARK_CUTOUT   = ...;                           // 暗化切图
    MipmapStrategy STRICT_CUTOUT = ...;                           // 严格切图
    MipmapStrategy NO_MIPMAP     = ...;                           // 不生成
}

⚠️ MEAN 用 (a + b) >> 1 —— 右移实现整除,等价于 (a+b)/2 对非负数。⚠️ a + b 在 int 溢出时不安全(两个接近 Color.WHITE 的 int 不会溢出,因为 alpha 通道最高位是符号位且 alpha ≤ 255,四个 8 位通道之和最大 4×255×255 = 260100,远小于 Integer.MAX_VALUE)。实际安全,但依赖「输入是 4 通道 0~255 的 int 颜色」这一隐式约定。

这些常量是接口的字段(隐式 public static final),是「带默认实现的函数式接口」的标准写法。

MipmapStrategies:按方块类与 sprite 名的策略分配

85 行,public final。类注释(:14-16):

Fallback {@link MipmapStrategy} assignments for sprites that carry no metadata.

即:当 sprite 自身没有元数据时,按方块类或 sprite 名推断策略。

三个映射 + 一个线程局部栈

字段 类型 行 线程安全
CLASS_RULES Object2ObjectOpenHashMap<Class<?>, MipmapStrategy> :20 ⚠️ 普通 hash map
SPRITE_RULES ConcurrentHashMap<String, MipmapStrategy> :21 ✅
STRATEGY_STACK ThreadLocal<Deque<MipmapStrategy>> :22 ✅(ThreadLocal)

⚠️ CLASS_RULES 用 fastutil 的非并发 map,但 SPRITE_RULES 用 JDK 的 ConcurrentHashMap —— 同一个类里两种并发策略。若 CLASS_RULES 会在运行期被 put(如 :34 之后的注册方法),并发写会损坏内部状态。本条目未读 :34 之后的注册方法,无法判定是否运行期写入。

静态初始化的 5 条 CLASS_RULES(:24-30)

方块类 策略 行
BlockLeaves DARK_CUTOUT :25
BlockFlower STRICT_CUTOUT :26
BlockMushroom STRICT_CUTOUT :27
BlockGlass MEAN :28
BlockRedstoneWire MEAN :29

5 个都是 1.7.10 的原版方块类(net.minecraft.block.*)。语义可推断:

  • BlockLeaves → DARK_CUTOUT:树叶是 cutout + 半透明 mipmap 需保 alpha。
  • BlockFlower / BlockMushroom → STRICT_CUTOUT:同样的 cutout 需求,但更严格。
  • BlockGlass / BlockRedstoneWire → MEAN:玻璃有实心部分,红石线是细线但整体可平均。

⚠️ CLASS_RULES 用 Class 作键(精确匹配) —— 不匹配子类。若某个 mod 继承 BlockLeaves 并用不同方块,策略不会继承。必须逐类注册。

⚠️ SPRITE_RULES 的键是 String(sprite 名) —— 比 Class 粒度细。同一 sprite 名跨 mod 冲突时,后者覆盖前者(ConcurrentHashMap.put)。

⚠️ STRATEGY_STACK 是 ThreadLocal<Deque<>>,初值 LinkedList::new(:22)—— 栈式作用域(try-with-resources 风格)。⚠️ LinkedList 每个线程各 new 一个(withInitial),线程池线程会各持一个空 LinkedList;若作用域不配对(push 后无 pop),会持续增长且无清理。

触发方:RenderThreadContext(见 Tessellator 条目)用的是 ThreadLocal<WorldSlice> —— 同一套 ThreadLocal 模式。

SpritePadding:atlas 缝合的 gutter

98 行,public final。这个类的注释质量是本仓最好的(:14-18):

The gutter has to survive being halved once per mip level, and the stitcher slot has to stay a multiple of 2^mipmapLevels so every level’s origin lands on a texel, so 1 < << mipmapLevels is the only workable width. At mip 0 the terrain atlas still needs one texel for the RGSS taps to land in.

gutterFor(:20-25)

public static int gutterFor(int mipmapLevels, boolean terrainAtlas) {
    if (mipmapLevels <= 0) {
        return terrainAtlas ? 1 : 0;
    }
    return 1 << mipmapLevels;
}
输入 输出
mipmapLevels <= 0 + terrainAtlas 1(mip 0 地形 atlas 仍需 1 texel 给 RGSS)
mipmapLevels <= 0 + 非地形 0(无 gutter)
mipmapLevels = 4 16
mipmapLevels = 8 256

⚠️ 1 << mipmapLevels 在 mipmapLevels >= 31 时溢出(1 << 31 = Integer.MIN_VALUE,负数)。无上限检查。 实际 mip 层级数 ≤ 15,安全;但输入校验缺失。

⚠️ 返回 0 时 uploadPadded 走直通路径(:28-30)—— 不 padding,直接 TextureUtil.uploadTextureMipmap。这是常见的非地形 atlas 路径。

uploadPadded 的两分支(:27-36)

public static void uploadPadded(int[][] frameData, int width, int height, int originX, int originY,
                                int gutter, boolean blur, boolean clamp) {
    if (gutter <= 0) {                                     // :28
        TextureUtil.uploadTextureMipmap(frameData, width, height, originX, originY, blur, clamp);
        return;
    }
    scratch = padFrame(frameData, width, height, gutter, scratch);   // :32
    TextureUtil.uploadTextureMipmap(scratch, width + 2 * gutter, height + 2 * gutter,
        originX - gutter, originY - gutter, blur, clamp);            // :33-34
}

⚠️ scratch 是 private static int[][](:5)—— 共享的可变复用缓冲,无同步。 两个线程并发 uploadPadded 会互相覆盖 padding 结果 → 上传错误的纹理。本条目未找到同步保护(padFrame 的实现 :38-98 本条目未读,但 scratch 字段本身无 volatile 也无锁)。

⚠️ padding 后尺寸变为 width + 2*gutter × height + 2*gutter,原点变为 originX - gutter, originY - gutter(:33-34)—— 四周对称扩展。⚠️ originX - gutter 为负时不检查 —— 直接传给 TextureUtil,行为取决于原版实现(1.7.10 原版类,本条目不可判定)。

⚠️ uploadPadded 无 try/finally,padFrame 抛异常时 scratch 残留上一次尺寸的内容;下次 uploadPadded 若新尺寸更小,padFrame 若不完整填充会带入旧数据。padFrame 是否完整填充本条目未确认。

NaturalTextureUtils:自然贴图的 alpha 判定

53 行,public final。类注释(:11-13):

The vanilla rule for what counts as an “opaque” texel that mipmaps can safely average.

即「mipmap 能否安全平均」的「不透明 texel」判定。

只导入了 3 个 GL 常量(:4-5):GL_RGBA、GL_UNSIGNED_BYTE,以及 NaturalTextureUtils 本身不用 GL —— 说明它是数据层的纯函数,GL 绑定在别处。

⚠️ 53 行中本条目只读了前 13 行(类注释 + import)。具体方法、阈值常量、alpha 判定规则本条目不可判定。 不臆测。

MipmapGenerator:本条目最大的文件

284 行,public final。⚠️ 本条目只读到了第 26 行附近(import 区 + 开头),其常量、方法、算法本条目不可判定。

已确认的依赖:

import 来源
it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap fastutil
net.minecraft.client.renderer.texture.TextureUtil 原版
org.lwjgl.opengl.GL11 LWJGL

⚠️ MipmapGenerator 284 行是本条目最大的未读文件,与 MipmapStrategies(85 行)配合使用(策略由 MipmapStrategies 分配,实际 mip 生成在此)。这是本条目最大的已知缺口。

BufferUtil:直接内存辅助

46 行,public final。⚠️ 本条目只读到第 26 行(import + 类头),方法本条目不可判定。

已确认的 import:

import 来源
java.nio.Buffer JDK
java.nio.ByteBuffer JDK
java.nio.FloatBuffer JDK
java.nio.IntBuffer JDK
org.lwjgl.BufferUtils LWJGL(lwjgl.BufferUtils,注意是 LWJGL 的 2.x 版 BufferUtils 而非 org.lwjgl.BufferUtils)

⚠️ import 的是 org.lwjgl.BufferUtils —— 1.7.10 用的是 LWJGL 2.9(包名 org.lwjgl),不是 LWJGL 3(org.lwjgl 但 org.lwjgl.opengl.GL11 移到子包)。这与仓库其它地方一致(GL11 / GL13 / GL40 全部在 org.lwjgl.opengl)。

Buffer + 3 个具体 buffer 类型 + BufferUtils 的组合说明它做的是JDK NIO buffer 的位置/容量调整(BufferUtils.create* 或 GL11.glBufferData 的辅助)。

已知问题 / 风险

  1. SpritePadding.scratch 是无同步的 private static 复用缓冲(:5、:32)—— 并发 uploadPadded 会互相覆盖并上传错误纹理。这是本条目最严重的线程安全隐患。
  2. MipmapStrategies.CLASS_RULES 用非并发的 fastutil map,而同类的 SPRITE_RULES 用 ConcurrentHashMap(:20-21)—— 若 CLASS_RULES 有运行期写入则不安全。注册方法本条目未读。
  3. MipmapStrategies.STRATEGY_STACK 是 ThreadLocal<LinkedList>(:22),push/pop 不配对会无界增长且无清理。
  4. CLASS_RULES 用精确 Class 匹配(:25-29),不继承给子类。
  5. SpritePadding.gutterFor 在 mipmapLevels >= 31 时 1 << mipmapLevels 溢出为负,无上限检查(:24)。
  6. SpritePadding.uploadPadded 不检查 originX - gutter 为负(:33-34),依赖原版 TextureUtil 的行为(本仓库不可判定)。
  7. uploadPadded 无 try/finally,padFrame 抛异常时 scratch 残留旧内容。
  8. MipmapStrategy.MEAN 的 (a + b) >> 1 依赖「输入是 0~255 的 4 通道 int 颜色」这一隐式约定(实际安全但未校验)。
  9. MipmapGenerator.java 284 行中约 258 行未读(只覆盖 import 与类头)—— 本条目最大的缺口。
  10. NaturalTextureUtils.java 53 行中约 40 行未读、BufferUtil.java 46 行中约 20 行未读 —— 同样未判定。
  11. TextureUtil / GL11 等上游类不在本仓库,其行为无法从本仓库验证。

相关条目