TextureUtil.java

com.mojang.blaze3d.platform.TextureUtil

信息

  • 全限定名:com.mojang.blaze3d.platform.TextureUtil
  • 类型:public class
  • 包:com.mojang.blaze3d.platform
  • 源码路径:src/main/java/com/mojang/blaze3d/platform/TextureUtil.java
  • 起始行号:L26
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L27
    • 说明:

      TODO

  • MIN_MIPMAP_LEVEL

    • 类型: int
    • 修饰符: public static final
    • 源码定位: L28
    • 说明:

      TODO

  • DEFAULT_IMAGE_BUFFER_SIZE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L29
    • 说明:

      TODO

  • DIRECTIONS

    • 类型: int[][]
    • 修饰符: private static final
    • 源码定位: L30
    • 说明:

      TODO

内部类/嵌套类型

构造器

方法

下面的方法块按源码顺序生成。

public static ByteBuffer readResource(InputStream inputStream) @ L32

  • 方法名:readResource
  • 源码定位:L32
  • 返回类型:ByteBuffer
  • 修饰符:public static

参数:

  • inputStream: InputStream

说明:

TODO

private static ByteBuffer readResource(ReadableByteChannel channel, int expectedSize) @ L37

  • 方法名:readResource
  • 源码定位:L37
  • 返回类型:ByteBuffer
  • 修饰符:private static

参数:

  • channel: ReadableByteChannel
  • expectedSize: int

说明:

TODO

public static void writeAsPNG(Path dir, String prefix, GpuTexture texture, int maxMipLevel, IntUnaryOperator pixelModifier) @ L55

  • 方法名:writeAsPNG
  • 源码定位:L55
  • 返回类型:void
  • 修饰符:public static

参数:

  • dir: Path
  • prefix: String
  • texture: GpuTexture
  • maxMipLevel: int
  • pixelModifier: IntUnaryOperator

说明:

TODO

public static Path getDebugTexturePath(Path root) @ L125

  • 方法名:getDebugTexturePath
  • 源码定位:L125
  • 返回类型:Path
  • 修饰符:public static

参数:

  • root: Path

说明:

TODO

public static Path getDebugTexturePath() @ L129

  • 方法名:getDebugTexturePath
  • 源码定位:L129
  • 返回类型:Path
  • 修饰符:public static

参数:

说明:

TODO

public static void solidify(NativeImage image) @ L133

  • 方法名:solidify
  • 源码定位:L133
  • 返回类型:void
  • 修饰符:public static

参数:

  • image: NativeImage

说明:

TODO

public static void fillEmptyAreasWithDarkColor(NativeImage image) @ L186

  • 方法名:fillEmptyAreasWithDarkColor
  • 源码定位:L186
  • 返回类型:void
  • 修饰符:public static

参数:

  • image: NativeImage

说明:

TODO

private static int pack(int x, int y, int width) @ L224

  • 方法名:pack
  • 源码定位:L224
  • 返回类型:int
  • 修饰符:private static

参数:

  • x: int
  • y: int
  • width: int

说明:

TODO

private static int x(int packed, int width) @ L228

  • 方法名:x
  • 源码定位:L228
  • 返回类型:int
  • 修饰符:private static

参数:

  • packed: int
  • width: int

说明:

TODO

private static int y(int packed, int width) @ L232

  • 方法名:y
  • 源码定位:L232
  • 返回类型:int
  • 修饰符:private static

参数:

  • packed: int
  • width: int

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class TextureUtil {
    private static final Logger LOGGER = LogUtils.getLogger();
    public static final int MIN_MIPMAP_LEVEL = 0;
    private static final int DEFAULT_IMAGE_BUFFER_SIZE = 8192;
    private static final int[][] DIRECTIONS = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
 
    public static ByteBuffer readResource(InputStream inputStream) throws IOException {
        ReadableByteChannel channel = Channels.newChannel(inputStream);
        return channel instanceof SeekableByteChannel seekableChannel ? readResource(channel, (int)seekableChannel.size() + 1) : readResource(channel, 8192);
    }
 
    private static ByteBuffer readResource(ReadableByteChannel channel, int expectedSize) throws IOException {
        ByteBuffer buffer = MemoryUtil.memAlloc(expectedSize);
 
        try {
            while (channel.read(buffer) != -1) {
                if (!buffer.hasRemaining()) {
                    buffer = MemoryUtil.memRealloc(buffer, buffer.capacity() * 2);
                }
            }
 
            buffer.flip();
            return buffer;
        } catch (IOException var4) {
            MemoryUtil.memFree(buffer);
            throw var4;
        }
    }
 
    public static void writeAsPNG(Path dir, String prefix, GpuTexture texture, int maxMipLevel, IntUnaryOperator pixelModifier) {
        RenderSystem.assertOnRenderThread();
        long bufferLength = 0L;
 
        for (int i = 0; i <= maxMipLevel; i++) {
            bufferLength += (long)texture.getFormat().pixelSize() * texture.getWidth(i) * texture.getHeight(i);
        }
 
        if (bufferLength > 2147483647L) {
            throw new IllegalArgumentException("Exporting textures larger than 2GB is not supported");
        } else {
            GpuBuffer buffer = RenderSystem.getDevice().createBuffer(() -> "Texture output buffer", 9, bufferLength);
            CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
            Runnable onCopyComplete = () -> {
                try (GpuBuffer.MappedView read = commandEncoder.mapBuffer(buffer, true, false)) {
                    ByteBuffer data = read.data();
 
                    IntUnaryOperator decodeTexel = switch (texture.getFormat()) {
                        case RED8 -> byteOffset -> {
                            int luminance = Byte.toUnsignedInt(data.get(byteOffset));
                            return ARGB.color(luminance, luminance, luminance);
                        };
                        case RED8I -> byteOffset -> {
                            int luminance = data.get(byteOffset) + 128;
                            return ARGB.color(luminance, luminance, luminance);
                        };
                        case RGBA8 -> byteOffset -> data.getInt(byteOffset);
                        case DEPTH32 -> byteOffset -> ARGB.gray(data.getFloat(byteOffset));
                    };
                    int offsetx = 0;
 
                    for (int ix = 0; ix <= maxMipLevel; ix++) {
                        int mipWidth = texture.getWidth(ix);
                        int mipHeight = texture.getHeight(ix);
 
                        try (NativeImage image = new NativeImage(mipWidth, mipHeight, false)) {
                            for (int y = 0; y < mipHeight; y++) {
                                for (int x = 0; x < mipWidth; x++) {
                                    int argb = decodeTexel.applyAsInt(offsetx + (x + y * mipWidth) * texture.getFormat().pixelSize());
                                    image.setPixelABGR(x, y, pixelModifier.applyAsInt(argb));
                                }
                            }
 
                            Path target = dir.resolve(prefix + "_" + ix + ".png");
                            image.writeToFile(target);
                            LOGGER.debug("Exported png to: {}", target.toAbsolutePath());
                        } catch (IOException var21) {
                            LOGGER.debug("Unable to write: ", (Throwable)var21);
                        }
 
                        offsetx += texture.getFormat().pixelSize() * mipWidth * mipHeight;
                    }
                }
 
                buffer.close();
            };
            AtomicInteger completedCopies = new AtomicInteger();
            int offset = 0;
 
            for (int i = 0; i <= maxMipLevel; i++) {
                commandEncoder.copyTextureToBuffer(texture, buffer, offset, () -> {
                    if (completedCopies.getAndIncrement() == maxMipLevel) {
                        onCopyComplete.run();
                    }
                }, i);
                offset += texture.getFormat().pixelSize() * texture.getWidth(i) * texture.getHeight(i);
            }
        }
    }
 
    public static Path getDebugTexturePath(Path root) {
        return root.resolve("screenshots").resolve("debug");
    }
 
    public static Path getDebugTexturePath() {
        return getDebugTexturePath(Path.of("."));
    }
 
    public static void solidify(NativeImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int[] nearestColor = new int[width * height];
        int[] distances = new int[width * height];
        Arrays.fill(distances, Integer.MAX_VALUE);
        IntArrayFIFOQueue queue = new IntArrayFIFOQueue();
 
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                int color = image.getPixel(x, y);
                if (ARGB.alpha(color) != 0) {
                    int packedCoordinates = pack(x, y, width);
                    distances[packedCoordinates] = 0;
                    nearestColor[packedCoordinates] = color;
                    queue.enqueue(packedCoordinates);
                }
            }
        }
 
        while (!queue.isEmpty()) {
            int packedCoordinates = queue.dequeueInt();
            int x = x(packedCoordinates, width);
            int yx = y(packedCoordinates, width);
 
            for (int[] direction : DIRECTIONS) {
                int neighborX = x + direction[0];
                int neighborY = yx + direction[1];
                int packedNeighborCoordinates = pack(neighborX, neighborY, width);
                if (neighborX >= 0
                    && neighborY >= 0
                    && neighborX < width
                    && neighborY < height
                    && distances[packedNeighborCoordinates] > distances[packedCoordinates] + 1) {
                    distances[packedNeighborCoordinates] = distances[packedCoordinates] + 1;
                    nearestColor[packedNeighborCoordinates] = nearestColor[packedCoordinates];
                    queue.enqueue(packedNeighborCoordinates);
                }
            }
        }
 
        for (int x = 0; x < width; x++) {
            for (int yx = 0; yx < height; yx++) {
                int color = image.getPixel(x, yx);
                if (ARGB.alpha(color) == 0) {
                    image.setPixel(x, yx, ARGB.color(0, nearestColor[pack(x, yx, width)]));
                } else {
                    image.setPixel(x, yx, color);
                }
            }
        }
    }
 
    public static void fillEmptyAreasWithDarkColor(NativeImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int darkestColor = -1;
        int minBrightness = Integer.MAX_VALUE;
 
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                int color = image.getPixel(x, y);
                int alpha = ARGB.alpha(color);
                if (alpha != 0) {
                    int red = ARGB.red(color);
                    int green = ARGB.green(color);
                    int blue = ARGB.blue(color);
                    int brightness = red + green + blue;
                    if (brightness < minBrightness) {
                        minBrightness = brightness;
                        darkestColor = color;
                    }
                }
            }
        }
 
        int darkRed = 3 * ARGB.red(darkestColor) / 4;
        int darkGreen = 3 * ARGB.green(darkestColor) / 4;
        int darkBlue = 3 * ARGB.blue(darkestColor) / 4;
        int darkenedColor = ARGB.color(0, darkRed, darkGreen, darkBlue);
 
        for (int x = 0; x < width; x++) {
            for (int yx = 0; yx < height; yx++) {
                int color = image.getPixel(x, yx);
                if (ARGB.alpha(color) == 0) {
                    image.setPixel(x, yx, darkenedColor);
                }
            }
        }
    }
 
    private static int pack(int x, int y, int width) {
        return x + y * width;
    }
 
    private static int x(int packed, int width) {
        return packed % width;
    }
 
    private static int y(int packed, int width) {
        return packed / width;
    }
}

引用的其他类

  • NativeImage

    • 引用位置: 参数/构造调用
    • 关联成员: NativeImage()
  • RenderSystem

    • 引用位置: 方法调用
    • 关联成员: RenderSystem.assertOnRenderThread(), RenderSystem.getDevice()
  • GpuTexture

    • 引用位置: 参数
  • ARGB

    • 引用位置: 方法调用
    • 关联成员: ARGB.alpha(), ARGB.blue(), ARGB.color(), ARGB.gray(), ARGB.green(), ARGB.red()