纹理工具层(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 的辅助)。
已知问题 / 风险
SpritePadding.scratch是无同步的private static复用缓冲(:5、:32)—— 并发uploadPadded会互相覆盖并上传错误纹理。这是本条目最严重的线程安全隐患。MipmapStrategies.CLASS_RULES用非并发的 fastutil map,而同类的SPRITE_RULES用ConcurrentHashMap(:20-21)—— 若CLASS_RULES有运行期写入则不安全。注册方法本条目未读。MipmapStrategies.STRATEGY_STACK是ThreadLocal<LinkedList>(:22),push/pop不配对会无界增长且无清理。CLASS_RULES用精确Class匹配(:25-29),不继承给子类。SpritePadding.gutterFor在mipmapLevels >= 31时1 << mipmapLevels溢出为负,无上限检查(:24)。SpritePadding.uploadPadded不检查originX - gutter为负(:33-34),依赖原版TextureUtil的行为(本仓库不可判定)。uploadPadded无 try/finally,padFrame抛异常时scratch残留旧内容。MipmapStrategy.MEAN的(a + b) >> 1依赖「输入是 0~255 的 4 通道 int 颜色」这一隐式约定(实际安全但未校验)。MipmapGenerator.java284 行中约 258 行未读(只覆盖 import 与类头)—— 本条目最大的缺口。NaturalTextureUtils.java53 行中约 40 行未读、BufferUtil.java46 行中约 20 行未读 —— 同样未判定。TextureUtil/GL11等上游类不在本仓库,其行为无法从本仓库验证。
相关条目
- utils 运行时工具 - 同包的另 8 个文件
- Celeritas(内嵌地形渲染引擎) - mipmap 生成的上游(
SpritePadding的注释提到 RGSS) - Subprojects(内嵌子项目) - GLSM 的 GL 常量定义
- 客户端渲染服务 - 纹理跟踪的消费者
- 配置与模块开关 - mipmap 相关的门控