NativeImage.java

com.mojang.blaze3d.platform.NativeImage

信息

  • 全限定名:com.mojang.blaze3d.platform.NativeImage
  • 类型:public final class
  • 包:com.mojang.blaze3d.platform
  • 源码路径:src/main/java/com/mojang/blaze3d/platform/NativeImage.java
  • 起始行号:L40
  • 实现:AutoCloseable
  • 职责:

    TODO

字段/常量

  • LOGGER

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

      TODO

  • MEMORY_POOL

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

      TODO

  • OPEN_OPTIONS

    • 类型: Set<StandardOpenOption>
    • 修饰符: private static final
    • 源码定位: L43
    • 说明:

      TODO

  • format

    • 类型: NativeImage.Format
    • 修饰符: private final
    • 源码定位: L46
    • 说明:

      TODO

  • width

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

      TODO

  • height

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

      TODO

  • useStbFree

    • 类型: boolean
    • 修饰符: private final
    • 源码定位: L49
    • 说明:

      TODO

  • pixels

    • 类型: long
    • 修饰符: private
    • 源码定位: L50
    • 说明:

      TODO

  • size

    • 类型: long
    • 修饰符: private final
    • 源码定位: L51
    • 说明:

      TODO

内部类/嵌套类型

  • com.mojang.blaze3d.platform.NativeImage.Format

    • 类型: enum
    • 修饰符: public static
    • 源码定位: L502
    • 说明:

      TODO

  • com.mojang.blaze3d.platform.NativeImage.WriteCallback

    • 类型: class
    • 修饰符: private static
    • 源码定位: L645
    • 说明:

      TODO

构造器

public NativeImage(int width, int height, boolean zero) @ L53

  • 构造器名:NativeImage
  • 源码定位:L53
  • 修饰符:public

参数:

  • width: int
  • height: int
  • zero: boolean

说明:

TODO

public NativeImage(NativeImage.Format format, int width, int height, boolean zero) @ L57

  • 构造器名:NativeImage
  • 源码定位:L57
  • 修饰符:public

参数:

  • format: NativeImage.Format
  • width: int
  • height: int
  • zero: boolean

说明:

TODO

public NativeImage(NativeImage.Format format, int width, int height, boolean useStbFree, long pixels) @ L79

  • 构造器名:NativeImage
  • 源码定位:L79
  • 修饰符:public

参数:

  • format: NativeImage.Format
  • width: int
  • height: int
  • useStbFree: boolean
  • pixels: long

说明:

TODO

方法

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

public String toString() @ L92

  • 方法名:toString
  • 源码定位:L92
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

private boolean isOutsideBounds(int x, int y) @ L97

  • 方法名:isOutsideBounds
  • 源码定位:L97
  • 返回类型:boolean
  • 修饰符:private

参数:

  • x: int
  • y: int

说明:

TODO

public static NativeImage read(InputStream inputStream) @ L101

  • 方法名:read
  • 源码定位:L101
  • 返回类型:NativeImage
  • 修饰符:public static

参数:

  • inputStream: InputStream

说明:

TODO

public static NativeImage read(NativeImage.Format format, InputStream inputStream) @ L105

  • 方法名:read
  • 源码定位:L105
  • 返回类型:NativeImage
  • 修饰符:public static

参数:

  • format: NativeImage.Format
  • inputStream: InputStream

说明:

TODO

public static NativeImage read(ByteBuffer bytes) @ L120

  • 方法名:read
  • 源码定位:L120
  • 返回类型:NativeImage
  • 修饰符:public static

参数:

  • bytes: ByteBuffer

说明:

TODO

public static NativeImage read(byte[] bytes) @ L124

  • 方法名:read
  • 源码定位:L124
  • 返回类型:NativeImage
  • 修饰符:public static

参数:

  • bytes: byte[]

说明:

TODO

private static NativeImage putAndRead(ByteBuffer nativeBuffer, byte[] bytes) @ L149

  • 方法名:putAndRead
  • 源码定位:L149
  • 返回类型:NativeImage
  • 修饰符:private static

参数:

  • nativeBuffer: ByteBuffer
  • bytes: byte[]

说明:

TODO

public static NativeImage read(NativeImage.Format format, ByteBuffer bytes) @ L155

  • 方法名:read
  • 源码定位:L155
  • 返回类型:NativeImage
  • 修饰符:public static

参数:

  • format: NativeImage.Format
  • bytes: ByteBuffer

说明:

TODO

private void checkAllocated() @ L182

  • 方法名:checkAllocated
  • 源码定位:L182
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void close() @ L188

  • 方法名:close
  • 源码定位:L188
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public boolean isClosed() @ L203

  • 方法名:isClosed
  • 源码定位:L203
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public int getWidth() @ L207

  • 方法名:getWidth
  • 源码定位:L207
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

public int getHeight() @ L211

  • 方法名:getHeight
  • 源码定位:L211
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

public NativeImage.Format format() @ L215

  • 方法名:format
  • 源码定位:L215
  • 返回类型:NativeImage.Format
  • 修饰符:public

参数:

说明:

TODO

private int getPixelABGR(int x, int y) @ L219

  • 方法名:getPixelABGR
  • 源码定位:L219
  • 返回类型:int
  • 修饰符:private

参数:

  • x: int
  • y: int

说明:

TODO

public int getPixel(int x, int y) @ L231

  • 方法名:getPixel
  • 源码定位:L231
  • 返回类型:int
  • 修饰符:public

参数:

  • x: int
  • y: int

说明:

TODO

public void setPixelABGR(int x, int y, int pixel) @ L235

  • 方法名:setPixelABGR
  • 源码定位:L235
  • 返回类型:void
  • 修饰符:public

参数:

  • x: int
  • y: int
  • pixel: int

说明:

TODO

public void setPixel(int x, int y, int pixel) @ L247

  • 方法名:setPixel
  • 源码定位:L247
  • 返回类型:void
  • 修饰符:public

参数:

  • x: int
  • y: int
  • pixel: int

说明:

TODO

public NativeImage mappedCopy(IntUnaryOperator function) @ L251

  • 方法名:mappedCopy
  • 源码定位:L251
  • 返回类型:NativeImage
  • 修饰符:public

参数:

  • function: IntUnaryOperator

说明:

TODO

public int[] getPixelsABGR() @ L271

  • 方法名:getPixelsABGR
  • 源码定位:L271
  • 返回类型:int[]
  • 修饰符:public

参数:

说明:

TODO

public int[] getPixels() @ L282

  • 方法名:getPixels
  • 源码定位:L282
  • 返回类型:int[]
  • 修饰符:public

参数:

说明:

TODO

public byte getLuminanceOrAlpha(int x, int y) @ L292

  • 方法名:getLuminanceOrAlpha
  • 源码定位:L292
  • 返回类型:byte
  • 修饰符:public

参数:

  • x: int
  • y: int

说明:

TODO

public int[] makePixelArray() @ L303

  • 方法名:makePixelArray
  • 源码定位:L303
  • 返回类型:int[]
  • 修饰符:public

参数:

说明:

TODO

public void writeToFile(File file) @ L321

  • 方法名:writeToFile
  • 源码定位:L321
  • 返回类型:void
  • 修饰符:public

参数:

  • file: File

说明:

TODO

public boolean copyFromFont(FT_Face face, int index) @ L325

  • 方法名:copyFromFont
  • 源码定位:L325
  • 返回类型:boolean
  • 修饰符:public

参数:

  • face: FT_Face
  • index: int

说明:

TODO

public void writeToFile(Path file) @ L355

  • 方法名:writeToFile
  • 源码定位:L355
  • 返回类型:void
  • 修饰符:public

参数:

  • file: Path

说明:

TODO

private boolean writeToChannel(WritableByteChannel output) @ L369

  • 方法名:writeToChannel
  • 源码定位:L369
  • 返回类型:boolean
  • 修饰符:private

参数:

  • output: WritableByteChannel

说明:

TODO

public void copyFrom(NativeImage from) @ L392

  • 方法名:copyFrom
  • 源码定位:L392
  • 返回类型:void
  • 修饰符:public

参数:

  • from: NativeImage

说明:

TODO

public void fillRect(int xs, int ys, int width, int height, int pixel) @ L414

  • 方法名:fillRect
  • 源码定位:L414
  • 返回类型:void
  • 修饰符:public

参数:

  • xs: int
  • ys: int
  • width: int
  • height: int
  • pixel: int

说明:

TODO

public void copyRect(int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY, boolean swapX, boolean swapY) @ L422

  • 方法名:copyRect
  • 源码定位:L422
  • 返回类型:void
  • 修饰符:public

参数:

  • startX: int
  • startY: int
  • offsetX: int
  • offsetY: int
  • sizeX: int
  • sizeY: int
  • swapX: boolean
  • swapY: boolean

说明:

TODO

public void copyRect(NativeImage target, int sourceX, int sourceY, int targetX, int targetY, int sizeX, int sizeY, boolean swapX, boolean swapY) @ L426

  • 方法名:copyRect
  • 源码定位:L426
  • 返回类型:void
  • 修饰符:public

参数:

  • target: NativeImage
  • sourceX: int
  • sourceY: int
  • targetX: int
  • targetY: int
  • sizeX: int
  • sizeY: int
  • swapX: boolean
  • swapY: boolean

说明:

TODO

public void resizeSubRectTo(int sourceX, int sourceY, int sizeX, int sizeY, NativeImage to) @ L437

  • 方法名:resizeSubRectTo
  • 源码定位:L437
  • 返回类型:void
  • 修饰符:public

参数:

  • sourceX: int
  • sourceY: int
  • sizeX: int
  • sizeY: int
  • to: NativeImage

说明:

TODO

public void untrack() @ L457

  • 方法名:untrack
  • 源码定位:L457
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public long getPointer() @ L461

  • 方法名:getPointer
  • 源码定位:L461
  • 返回类型:long
  • 修饰符:public

参数:

说明:

TODO

public Transparency computeTransparency(int x0, int y0, int x1, int y1) @ L465

  • 方法名:computeTransparency
  • 源码定位:L465
  • 返回类型:Transparency
  • 修饰符:public

参数:

  • x0: int
  • y0: int
  • x1: int
  • y1: int

说明:

TODO

public Transparency computeTransparency() @ L497

  • 方法名:computeTransparency
  • 源码定位:L497
  • 返回类型:Transparency
  • 修饰符:public

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public final class NativeImage implements AutoCloseable {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final MemoryPool MEMORY_POOL = TracyClient.createMemoryPool("NativeImage");
    private static final Set<StandardOpenOption> OPEN_OPTIONS = EnumSet.of(
        StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
    );
    private final NativeImage.Format format;
    private final int width;
    private final int height;
    private final boolean useStbFree;
    private long pixels;
    private final long size;
 
    public NativeImage(int width, int height, boolean zero) {
        this(NativeImage.Format.RGBA, width, height, zero);
    }
 
    public NativeImage(NativeImage.Format format, int width, int height, boolean zero) {
        if (width > 0 && height > 0) {
            this.format = format;
            this.width = width;
            this.height = height;
            this.size = (long)width * height * format.components();
            this.useStbFree = false;
            if (zero) {
                this.pixels = MemoryUtil.nmemCalloc(1L, this.size);
            } else {
                this.pixels = MemoryUtil.nmemAlloc(this.size);
            }
 
            MEMORY_POOL.malloc(this.pixels, (int)this.size);
            if (this.pixels == 0L) {
                throw new IllegalStateException("Unable to allocate texture of size " + width + "x" + height + " (" + format.components() + " channels)");
            }
        } else {
            throw new IllegalArgumentException("Invalid texture size: " + width + "x" + height);
        }
    }
 
    public NativeImage(NativeImage.Format format, int width, int height, boolean useStbFree, long pixels) {
        if (width > 0 && height > 0) {
            this.format = format;
            this.width = width;
            this.height = height;
            this.useStbFree = useStbFree;
            this.pixels = pixels;
            this.size = (long)width * height * format.components();
        } else {
            throw new IllegalArgumentException("Invalid texture size: " + width + "x" + height);
        }
    }
 
    @Override
    public String toString() {
        return "NativeImage[" + this.format + " " + this.width + "x" + this.height + "@" + this.pixels + (this.useStbFree ? "S" : "N") + "]";
    }
 
    private boolean isOutsideBounds(int x, int y) {
        return x < 0 || x >= this.width || y < 0 || y >= this.height;
    }
 
    public static NativeImage read(InputStream inputStream) throws IOException {
        return read(NativeImage.Format.RGBA, inputStream);
    }
 
    public static NativeImage read(NativeImage.@Nullable Format format, InputStream inputStream) throws IOException {
        ByteBuffer file = null;
 
        NativeImage var3;
        try {
            file = TextureUtil.readResource(inputStream);
            var3 = read(format, file);
        } finally {
            MemoryUtil.memFree(file);
            IOUtils.closeQuietly(inputStream);
        }
 
        return var3;
    }
 
    public static NativeImage read(ByteBuffer bytes) throws IOException {
        return read(NativeImage.Format.RGBA, bytes);
    }
 
    public static NativeImage read(byte[] bytes) throws IOException {
        MemoryStack memoryStack = MemoryStack.stackGet();
        int bytesAvailable = memoryStack.getPointer();
        if (bytesAvailable < bytes.length) {
            ByteBuffer buffer = MemoryUtil.memAlloc(bytes.length);
 
            NativeImage var13;
            try {
                var13 = putAndRead(buffer, bytes);
            } finally {
                MemoryUtil.memFree(buffer);
            }
 
            return var13;
        } else {
            NativeImage var5;
            try (MemoryStack stack = MemoryStack.stackPush()) {
                ByteBuffer buffer = stack.malloc(bytes.length);
                var5 = putAndRead(buffer, bytes);
            }
 
            return var5;
        }
    }
 
    private static NativeImage putAndRead(ByteBuffer nativeBuffer, byte[] bytes) throws IOException {
        nativeBuffer.put(bytes);
        nativeBuffer.rewind();
        return read(nativeBuffer);
    }
 
    public static NativeImage read(NativeImage.@Nullable Format format, ByteBuffer bytes) throws IOException {
        if (format != null && !format.supportedByStb()) {
            throw new UnsupportedOperationException("Don't know how to read format " + format);
        } else if (MemoryUtil.memAddress(bytes) == 0L) {
            throw new IllegalArgumentException("Invalid buffer");
        } else {
            PngInfo.validateHeader(bytes);
 
            NativeImage var9;
            try (MemoryStack stack = MemoryStack.stackPush()) {
                IntBuffer w = stack.mallocInt(1);
                IntBuffer h = stack.mallocInt(1);
                IntBuffer comp = stack.mallocInt(1);
                ByteBuffer pixels = STBImage.stbi_load_from_memory(bytes, w, h, comp, format == null ? 0 : format.components);
                if (pixels == null) {
                    throw new IOException("Could not load image: " + STBImage.stbi_failure_reason());
                }
 
                long address = MemoryUtil.memAddress(pixels);
                MEMORY_POOL.malloc(address, pixels.limit());
                var9 = new NativeImage(format == null ? NativeImage.Format.getStbFormat(comp.get(0)) : format, w.get(0), h.get(0), true, address);
            }
 
            return var9;
        }
    }
 
    private void checkAllocated() {
        if (this.pixels == 0L) {
            throw new IllegalStateException("Image is not allocated.");
        }
    }
 
    @Override
    public void close() {
        if (this.pixels != 0L) {
            if (this.useStbFree) {
                STBImage.nstbi_image_free(this.pixels);
            } else {
                MemoryUtil.nmemFree(this.pixels);
            }
 
            MEMORY_POOL.free(this.pixels);
        }
 
        this.pixels = 0L;
    }
 
    public boolean isClosed() {
        return this.pixels == 0L;
    }
 
    public int getWidth() {
        return this.width;
    }
 
    public int getHeight() {
        return this.height;
    }
 
    public NativeImage.Format format() {
        return this.format;
    }
 
    private int getPixelABGR(int x, int y) {
        if (this.format != NativeImage.Format.RGBA) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "getPixelRGBA only works on RGBA images; have %s", this.format));
        } else if (this.isOutsideBounds(x, y)) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "(%s, %s) outside of image bounds (%s, %s)", x, y, this.width, this.height));
        } else {
            this.checkAllocated();
            long offset = (x + (long)y * this.width) * 4L;
            return MemoryUtil.memGetInt(this.pixels + offset);
        }
    }
 
    public int getPixel(int x, int y) {
        return ARGB.fromABGR(this.getPixelABGR(x, y));
    }
 
    public void setPixelABGR(int x, int y, int pixel) {
        if (this.format != NativeImage.Format.RGBA) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "setPixelRGBA only works on RGBA images; have %s", this.format));
        } else if (this.isOutsideBounds(x, y)) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "(%s, %s) outside of image bounds (%s, %s)", x, y, this.width, this.height));
        } else {
            this.checkAllocated();
            long offset = (x + (long)y * this.width) * 4L;
            MemoryUtil.memPutInt(this.pixels + offset, pixel);
        }
    }
 
    public void setPixel(int x, int y, int pixel) {
        this.setPixelABGR(x, y, ARGB.toABGR(pixel));
    }
 
    public NativeImage mappedCopy(IntUnaryOperator function) {
        if (this.format != NativeImage.Format.RGBA) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "function application only works on RGBA images; have %s", this.format));
        } else {
            this.checkAllocated();
            NativeImage result = new NativeImage(this.width, this.height, false);
            int pixelCount = this.width * this.height;
            IntBuffer sourceBuffer = MemoryUtil.memIntBuffer(this.pixels, pixelCount);
            IntBuffer targetBuffer = MemoryUtil.memIntBuffer(result.pixels, pixelCount);
 
            for (int i = 0; i < pixelCount; i++) {
                int pixel = ARGB.fromABGR(sourceBuffer.get(i));
                int modified = function.applyAsInt(pixel);
                targetBuffer.put(i, ARGB.toABGR(modified));
            }
 
            return result;
        }
    }
 
    public int[] getPixelsABGR() {
        if (this.format != NativeImage.Format.RGBA) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "getPixels only works on RGBA images; have %s", this.format));
        } else {
            this.checkAllocated();
            int[] result = new int[this.width * this.height];
            MemoryUtil.memIntBuffer(this.pixels, this.width * this.height).get(result);
            return result;
        }
    }
 
    public int[] getPixels() {
        int[] result = this.getPixelsABGR();
 
        for (int i = 0; i < result.length; i++) {
            result[i] = ARGB.fromABGR(result[i]);
        }
 
        return result;
    }
 
    public byte getLuminanceOrAlpha(int x, int y) {
        if (!this.format.hasLuminanceOrAlpha()) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "no luminance or alpha in %s", this.format));
        } else if (this.isOutsideBounds(x, y)) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "(%s, %s) outside of image bounds (%s, %s)", x, y, this.width, this.height));
        } else {
            int offset = (x + y * this.width) * this.format.components() + this.format.luminanceOrAlphaOffset() / 8;
            return MemoryUtil.memGetByte(this.pixels + offset);
        }
    }
 
    @Deprecated
    public int[] makePixelArray() {
        if (this.format != NativeImage.Format.RGBA) {
            throw new UnsupportedOperationException("can only call makePixelArray for RGBA images.");
        } else {
            this.checkAllocated();
            int[] pixels = new int[this.getWidth() * this.getHeight()];
 
            for (int y = 0; y < this.getHeight(); y++) {
                for (int x = 0; x < this.getWidth(); x++) {
                    pixels[x + y * this.getWidth()] = this.getPixel(x, y);
                }
            }
 
            return pixels;
        }
    }
 
    public void writeToFile(File file) throws IOException {
        this.writeToFile(file.toPath());
    }
 
    public boolean copyFromFont(FT_Face face, int index) {
        if (this.format.components() != 1) {
            throw new IllegalArgumentException("Can only write fonts into 1-component images.");
        } else if (FreeTypeUtil.checkError(FreeType.FT_Load_Glyph(face, index, 4), "Loading glyph")) {
            return false;
        } else {
            FT_GlyphSlot glyph = Objects.requireNonNull(face.glyph(), "Glyph not initialized");
            FT_Bitmap bitmap = glyph.bitmap();
            if (bitmap.pixel_mode() != 2) {
                throw new IllegalStateException("Rendered glyph was not 8-bit grayscale");
            } else if (bitmap.width() == this.getWidth() && bitmap.rows() == this.getHeight()) {
                int bufferSize = bitmap.width() * bitmap.rows();
                ByteBuffer buffer = Objects.requireNonNull(bitmap.buffer(bufferSize), "Glyph has no bitmap");
                MemoryUtil.memCopy(MemoryUtil.memAddress(buffer), this.pixels, bufferSize);
                return true;
            } else {
                throw new IllegalArgumentException(
                    String.format(
                        Locale.ROOT,
                        "Glyph bitmap of size %sx%s does not match image of size: %sx%s",
                        bitmap.width(),
                        bitmap.rows(),
                        this.getWidth(),
                        this.getHeight()
                    )
                );
            }
        }
    }
 
    public void writeToFile(Path file) throws IOException {
        if (!this.format.supportedByStb()) {
            throw new UnsupportedOperationException("Don't know how to write format " + this.format);
        } else {
            this.checkAllocated();
 
            try (WritableByteChannel out = Files.newByteChannel(file, OPEN_OPTIONS)) {
                if (!this.writeToChannel(out)) {
                    throw new IOException("Could not write image to the PNG file \"" + file.toAbsolutePath() + "\": " + STBImage.stbi_failure_reason());
                }
            }
        }
    }
 
    private boolean writeToChannel(WritableByteChannel output) throws IOException {
        NativeImage.WriteCallback writer = new NativeImage.WriteCallback(output);
 
        boolean var4;
        try {
            int height = Math.min(this.getHeight(), Integer.MAX_VALUE / this.getWidth() / this.format.components());
            if (height < this.getHeight()) {
                LOGGER.warn("Dropping image height from {} to {} to fit the size into 32-bit signed int", this.getHeight(), height);
            }
 
            if (STBImageWrite.nstbi_write_png_to_func(writer.address(), 0L, this.getWidth(), height, this.format.components(), this.pixels, 0) != 0) {
                writer.throwIfException();
                return true;
            }
 
            var4 = false;
        } finally {
            writer.free();
        }
 
        return var4;
    }
 
    public void copyFrom(NativeImage from) {
        if (from.format() != this.format) {
            throw new UnsupportedOperationException("Image formats don't match.");
        } else {
            int components = this.format.components();
            this.checkAllocated();
            from.checkAllocated();
            if (this.width == from.width) {
                MemoryUtil.memCopy(from.pixels, this.pixels, Math.min(this.size, from.size));
            } else {
                int minWidth = Math.min(this.getWidth(), from.getWidth());
                int minHeight = Math.min(this.getHeight(), from.getHeight());
 
                for (int y = 0; y < minHeight; y++) {
                    int fromOffset = y * from.getWidth() * components;
                    int toOffset = y * this.getWidth() * components;
                    MemoryUtil.memCopy(from.pixels + fromOffset, this.pixels + toOffset, minWidth);
                }
            }
        }
    }
 
    public void fillRect(int xs, int ys, int width, int height, int pixel) {
        for (int y = ys; y < ys + height; y++) {
            for (int x = xs; x < xs + width; x++) {
                this.setPixel(x, y, pixel);
            }
        }
    }
 
    public void copyRect(int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY, boolean swapX, boolean swapY) {
        this.copyRect(this, startX, startY, startX + offsetX, startY + offsetY, sizeX, sizeY, swapX, swapY);
    }
 
    public void copyRect(NativeImage target, int sourceX, int sourceY, int targetX, int targetY, int sizeX, int sizeY, boolean swapX, boolean swapY) {
        for (int y = 0; y < sizeY; y++) {
            for (int x = 0; x < sizeX; x++) {
                int dx = swapX ? sizeX - 1 - x : x;
                int dy = swapY ? sizeY - 1 - y : y;
                int source = this.getPixelABGR(sourceX + x, sourceY + y);
                target.setPixelABGR(targetX + dx, targetY + dy, source);
            }
        }
    }
 
    public void resizeSubRectTo(int sourceX, int sourceY, int sizeX, int sizeY, NativeImage to) {
        this.checkAllocated();
        if (to.format() != this.format) {
            throw new UnsupportedOperationException("resizeSubRectTo only works for images of the same format.");
        } else {
            int components = this.format.components();
            STBImageResize.nstbir_resize_uint8_linear(
                this.pixels + (sourceX + sourceY * this.getWidth()) * components,
                sizeX,
                sizeY,
                this.getWidth() * components,
                to.pixels,
                to.getWidth(),
                to.getHeight(),
                0,
                components
            );
        }
    }
 
    public void untrack() {
        DebugMemoryUntracker.untrack(this.pixels);
    }
 
    public long getPointer() {
        return this.pixels;
    }
 
    public Transparency computeTransparency(int x0, int y0, int x1, int y1) {
        this.checkAllocated();
        if (this.format != NativeImage.Format.RGBA) {
            return Transparency.NONE;
        } else if (x0 >= 0 && y0 >= 0 && x1 <= this.width && y1 <= this.height) {
            if ((long)this.width * this.height * 4L > 2147483647L) {
                throw new IllegalArgumentException("Image of size " + this.width + "x" + this.height + " is too large to compute translucency");
            } else {
                boolean hasTransparentPixel = false;
                boolean hasTranslucentPixel = false;
                IntBuffer buffer = MemoryUtil.memIntBuffer(this.pixels, this.width * this.height);
 
                for (int y = y0; y < y1; y++) {
                    for (int x = x0; x < x1; x++) {
                        int alpha = ARGB.alpha(buffer.get(x + y * this.width));
                        if (alpha == 0) {
                            hasTransparentPixel = true;
                        } else if (alpha != 255) {
                            hasTranslucentPixel = true;
                        }
                    }
                }
 
                return Transparency.of(hasTransparentPixel, hasTranslucentPixel);
            }
        } else {
            throw new IllegalArgumentException(
                "Cannot compute translucency out of bounds: [" + x0 + ", " + y0 + ", " + x1 + ", " + y1 + "] in " + this.width + "x" + this.height + " image"
            );
        }
    }
 
    public Transparency computeTransparency() {
        return this.computeTransparency(0, 0, this.width, this.height);
    }
 
    @OnlyIn(Dist.CLIENT)
    public static enum Format {
        RGBA(4, true, true, true, false, true, 0, 8, 16, 255, 24, true),
        RGB(3, true, true, true, false, false, 0, 8, 16, 255, 255, true),
        LUMINANCE_ALPHA(2, false, false, false, true, true, 255, 255, 255, 0, 8, true),
        LUMINANCE(1, false, false, false, true, false, 0, 0, 0, 0, 255, true);
 
        private final int components;
        private final boolean hasRed;
        private final boolean hasGreen;
        private final boolean hasBlue;
        private final boolean hasLuminance;
        private final boolean hasAlpha;
        private final int redOffset;
        private final int greenOffset;
        private final int blueOffset;
        private final int luminanceOffset;
        private final int alphaOffset;
        private final boolean supportedByStb;
 
        private Format(
            int components,
            boolean hasRed,
            boolean hasGreen,
            boolean hasBlue,
            boolean hasLuminance,
            boolean hasAlpha,
            int redOffset,
            int greenOffset,
            int blueOffset,
            int luminanceOffset,
            int alphaOffset,
            boolean supportedByStb
        ) {
            this.components = components;
            this.hasRed = hasRed;
            this.hasGreen = hasGreen;
            this.hasBlue = hasBlue;
            this.hasLuminance = hasLuminance;
            this.hasAlpha = hasAlpha;
            this.redOffset = redOffset;
            this.greenOffset = greenOffset;
            this.blueOffset = blueOffset;
            this.luminanceOffset = luminanceOffset;
            this.alphaOffset = alphaOffset;
            this.supportedByStb = supportedByStb;
        }
 
        public int components() {
            return this.components;
        }
 
        public boolean hasRed() {
            return this.hasRed;
        }
 
        public boolean hasGreen() {
            return this.hasGreen;
        }
 
        public boolean hasBlue() {
            return this.hasBlue;
        }
 
        public boolean hasLuminance() {
            return this.hasLuminance;
        }
 
        public boolean hasAlpha() {
            return this.hasAlpha;
        }
 
        public int redOffset() {
            return this.redOffset;
        }
 
        public int greenOffset() {
            return this.greenOffset;
        }
 
        public int blueOffset() {
            return this.blueOffset;
        }
 
        public int luminanceOffset() {
            return this.luminanceOffset;
        }
 
        public int alphaOffset() {
            return this.alphaOffset;
        }
 
        public boolean hasLuminanceOrRed() {
            return this.hasLuminance || this.hasRed;
        }
 
        public boolean hasLuminanceOrGreen() {
            return this.hasLuminance || this.hasGreen;
        }
 
        public boolean hasLuminanceOrBlue() {
            return this.hasLuminance || this.hasBlue;
        }
 
        public boolean hasLuminanceOrAlpha() {
            return this.hasLuminance || this.hasAlpha;
        }
 
        public int luminanceOrRedOffset() {
            return this.hasLuminance ? this.luminanceOffset : this.redOffset;
        }
 
        public int luminanceOrGreenOffset() {
            return this.hasLuminance ? this.luminanceOffset : this.greenOffset;
        }
 
        public int luminanceOrBlueOffset() {
            return this.hasLuminance ? this.luminanceOffset : this.blueOffset;
        }
 
        public int luminanceOrAlphaOffset() {
            return this.hasLuminance ? this.luminanceOffset : this.alphaOffset;
        }
 
        public boolean supportedByStb() {
            return this.supportedByStb;
        }
 
        private static NativeImage.Format getStbFormat(int i) {
            switch (i) {
                case 1:
                    return LUMINANCE;
                case 2:
                    return LUMINANCE_ALPHA;
                case 3:
                    return RGB;
                case 4:
                default:
                    return RGBA;
            }
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class WriteCallback extends STBIWriteCallback {
        private final WritableByteChannel output;
        private @Nullable IOException exception;
 
        private WriteCallback(WritableByteChannel output) {
            this.output = output;
        }
 
        @Override
        public void invoke(long context, long data, int size) {
            ByteBuffer dataBuf = getData(data, size);
 
            try {
                this.output.write(dataBuf);
            } catch (IOException var8) {
                this.exception = var8;
            }
        }
 
        public void throwIfException() throws IOException {
            if (this.exception != null) {
                throw this.exception;
            }
        }
    }
}

引用的其他类

  • DebugMemoryUntracker

    • 引用位置: 方法调用
    • 关联成员: DebugMemoryUntracker.untrack()
  • TextureUtil

    • 引用位置: 方法调用
    • 关联成员: TextureUtil.readResource()
  • Transparency

    • 引用位置: 方法调用/返回值
    • 关联成员: Transparency.of()
  • FreeTypeUtil

    • 引用位置: 方法调用
    • 关联成员: FreeTypeUtil.checkError()
  • ARGB

    • 引用位置: 方法调用
    • 关联成员: ARGB.alpha(), ARGB.fromABGR(), ARGB.toABGR()
  • PngInfo

    • 引用位置: 方法调用
    • 关联成员: PngInfo.validateHeader()
  • EventLogDirectory

    • 引用位置: 参数