Last active
August 10, 2023 14:01
-
-
Save isXander/6f4010c6f32fef304ccb0ec429331bc3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static AnimatedNativeImageBacked createFromImageReader(ImageReader reader, AnimFrameProvider animationProvider, ResourceLocation uniqueLocation) throws Exception { | |
if (reader.isSeekForwardOnly()) { | |
throw new RuntimeException("Image reader is not seekable"); | |
} | |
int frameCount = reader.getNumImages(true); | |
// Because this is being backed into a texture atlas, we need a maximum dimension | |
// so you can get the texture atlas size. | |
// Smaller frames are given black borders | |
int frameWidth = IntStream.range(0, frameCount).map(i -> { | |
try { | |
return reader.getWidth(i); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
}).max().orElseThrow(); | |
int frameHeight = IntStream.range(0, frameCount).map(i -> { | |
try { | |
return reader.getHeight(i); | |
} catch (IOException e) { | |
throw new RuntimeException(e); | |
} | |
}).max().orElseThrow(); | |
// Packs the frames into an optimal 1:1 texture. | |
// OpenGL can only have texture axis with a max of 32768 pixels, | |
// and packing them to that length is not efficient, apparently. | |
double ratio = frameWidth / (double)frameHeight; | |
int cols = (int)Math.ceil(Math.sqrt(frameCount) / Math.sqrt(ratio)); | |
int rows = (int)Math.ceil(frameCount / (double)cols); | |
NativeImage image = new NativeImage(NativeImage.Format.RGBA, frameWidth * cols, frameHeight * rows, true); | |
// // Fill whole atlas with black, as each frame may have different dimensions | |
// // that would cause borders of transparent pixels to appear around the frames | |
// for (int x = 0; x < frameWidth * cols; x++) { | |
// for (int y = 0; y < frameHeight * rows; y++) { | |
// image.setPixelRGBA(x, y, 0xFF000000); | |
// } | |
// } | |
BufferedImage bi = null; | |
Graphics2D graphics = null; | |
// each frame may have a different delay | |
double[] frameDelays = new double[frameCount]; | |
for (int i = 0; i < frameCount; i++) { | |
AnimFrame frame = animationProvider.get(i); | |
if (frameCount > 1) // frame will be null if not animation | |
frameDelays[i] = frame.durationMS; | |
if (bi == null) { | |
// first frame... | |
bi = reader.read(i); | |
graphics = bi.createGraphics(); | |
} else { | |
// WebP reader sometimes provides delta frames, (only the pixels that changed since the last frame) | |
// so instead of overwriting the image every frame, we draw delta frames on top of the previous frame | |
// to keep a complete image. | |
BufferedImage deltaFrame = reader.read(i); | |
graphics.drawImage(deltaFrame, frame.xOffset, frame.yOffset, null); | |
} | |
// Each frame may have different dimensions, so we need to center them. | |
int xOffset = (frameWidth - bi.getWidth()) / 2; | |
int yOffset = (frameHeight - bi.getHeight()) / 2; | |
for (int w = 0; w < bi.getWidth(); w++) { | |
for (int h = 0; h < bi.getHeight(); h++) { | |
int rgb = bi.getRGB(w, h); | |
int r = FastColor.ARGB32.red(rgb); | |
int g = FastColor.ARGB32.green(rgb); | |
int b = FastColor.ARGB32.blue(rgb); | |
int a = FastColor.ARGB32.alpha(rgb); | |
int col = i % cols; | |
int row = (int) Math.floor(i / (double)cols); | |
image.setPixelRGBA( | |
frameWidth * col + w + xOffset, | |
frameHeight * row + h + yOffset, | |
FastColor.ABGR32.color(a, b, g, r) // NativeImage uses ABGR for some reason | |
); | |
} | |
} | |
} | |
// gives the texture to GL for rendering | |
// usually, you create a native image with NativeImage.create, which sets the pixels and | |
// runs this function itself. In this case, we need to do it manually. | |
image.upload(0, 0, 0, false); | |
if (graphics != null) | |
graphics.dispose(); | |
reader.dispose(); | |
return new AnimatedNativeImageBacked(image, frameWidth, frameHeight, frameCount, frameDelays, cols, rows, uniqueLocation); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment