Reputation: 27
some weird image error I'm trying to split a gif into png images but only first image is good rest got some color error...
(I'm not using any libraries only java 1.8)
public class GifSplitter {
public static void main(String[] args) {
try {
splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void splitGif(File file) throws IOException {
ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
BufferedImage lastImage = reader.read(0);
ImageIO.write(lastImage, "PNG", new File(0 + ".png"));
for (int i = 1; i < reader.getNumImages(true); i++) {
BufferedImage readImage = reader.read(i);
BufferedImage image = new BufferedImage(readImage.getWidth(), readImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
ImageIO.write(image, "PNG", new File(i + ".png"));
}
}
}
Upvotes: 0
Views: 451
Reputation: 1068
You Can use glide to break gif to images. Try this out:
final ArrayList<Bitmap> bitmaps = new ArrayList<>();
Glide.with(MainActivity.this)
.asGif()
.load("url")
.into(new SimpleTarget<GifDrawable>() {
@Override
public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {
try {
resource.start();
resource.setLoopCount(100);
Object GifState = resource.getConstantState();
assert GifState != null;
Field frameLoader = GifState.getClass().getDeclaredField("frameLoader");
frameLoader.setAccessible(true);
Object gifFrameLoader = frameLoader.get(GifState);
assert gifFrameLoader != null;
Field gifDecoder = gifFrameLoader.getClass().getDeclaredField("gifDecoder");
gifDecoder.setAccessible(true);
StandardGifDecoder standardGifDecoder = (StandardGifDecoder) gifDecoder.get(gifFrameLoader);
for (int i = 0; i < Objects.requireNonNull(standardGifDecoder).getFrameCount(); i++) {
standardGifDecoder.advance();
bitmaps.add(standardGifDecoder.getNextFrame());
Log.e("bitmapcheck", String.valueOf(bitmaps.size()));
}
} catch (Exception ex) {
Log.e("bitmapcheck", ex.getMessage());
}
}
});
Dependencies
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
Upvotes: 0
Reputation: 1341
That is not an error, its most likely a GIF image with transparency enabled, an optimization feature which helps reduce the size of the entire image file.
The first GIF frame is always the full image, so you will have to perform some additional post-processing on the succeeding frames to get the actual output.
So basically, when you read each frame with reader.read(i)
, it will initially appear like this:
After processing, it should look like this:
Then you can convert the fully transformed frame to PNG.
Read more about GIF's "disposal method" here (page 4): https://cs.nyu.edu/courses/fall10/V22.0004-002/animatedGifs.pdf
Example code (post processing part):
public static final String DISPOSAL_PREVIOUS = "restoreToPrevious";
public static final String DISPOSAL_BACKGROUND = "restoreToBackgroundColor";
public static final String DISPOSAL_NONE = "none";
public static class ImageFrame {
private final int delay;
private final BufferedImage image;
private final String disposal;
private final int width, height;
public ImageFrame(BufferedImage image, int delay, String disposal) {
this.image = image;
this.delay = delay;
this.disposal = disposal;
this.width = -1;
this.height = -1;
}
public ImageFrame(BufferedImage image, int delay, String disposal, int width, int height) {
this.image = image;
this.delay = delay;
this.disposal = disposal;
this.width = width;
this.height = height;
}
public ImageFrame(BufferedImage image) {
this.image = image;
this.delay = -1;
this.disposal = null;
this.width = -1;
this.height = -1;
}
public BufferedImage getImage() {
return image;
}
public int getDelay() {
return delay;
}
public String getDisposal() {
return disposal;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
private static class ImageMetaData {
int index;
int delay;
String disposal;
int x;
int y;
private ImageMetaData(int index, int delay, String disposal, int x, int y) {
this.index = index;
this.delay = delay;
this.disposal = disposal;
this.x = x;
this.y = y;
}
}
private static ImageMetaData extractImageMetaData(ImageReader reader, int frameIndex) {
try {
var metadata = reader.getImageMetadata(frameIndex);
var root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_gif_image_1.0");
var gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0);
int delay = Integer.parseInt(gce.getAttribute("delayTime"));
String disposal = gce.getAttribute("disposalMethod");
int x = 0, y = 0;
var children = root.getChildNodes();
for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) {
Node nodeItem = children.item(nodeIndex);
if ("ImageDescriptor".equalsIgnoreCase(nodeItem.getNodeName())) {
NamedNodeMap map = nodeItem.getAttributes();
x = Integer.parseInt(map.getNamedItem("imageLeftPosition").getNodeValue());
y = Integer.parseInt(map.getNamedItem("imageTopPosition").getNodeValue());
}
}
return new ImageMetaData(frameIndex, delay, disposal, x, y);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static Integer[] extractLogicalScreenSize(ImageReader reader) {
Integer[] size = null;
try {
var metadata = reader.getStreamMetadata();
if (metadata == null)
return null;
var globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
var globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor");
if (globalScreenDescriptor != null && globalScreenDescriptor.getLength() > 0) {
IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0);
if (screenDescriptor != null) {
size = new Integer[2];
size[0] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth"));
size[1] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight"));
}
}
} catch (IOException e) {
e.printStackTrace();
}
return size;
}
private static List<ImageFrame> readGifFrames(File file) throws IOException {
var frames = new ArrayList<ImageFrame>(2);
try (var is = ImageIO.createImageInputStream(file)) {
var it = ImageIO.getImageReadersBySuffix("gif");
if (!it.hasNext())
throw new IOException("No supported reader for this format");
final var reader = it.next();
reader.setInput(is);
try {
final var size = extractLogicalScreenSize(reader);
int width = size != null ? size[0] : -1;
int height = size != null ? size[1] : -1;
BufferedImage master = null;
Graphics2D baseImage = null;
int numFrames = reader.getNumImages(true);
for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
BufferedImage img;
try {
img = reader.read(frameIndex);
} catch (IndexOutOfBoundsException e) {
break;
}
if (width == -1 || height == -1) {
width = img.getWidth();
height = img.getHeight();
}
ImageMetaData imageMetadata = extractImageMetaData(reader, frameIndex);
int x = imageMetadata.x;
int y = imageMetadata.y;
if (master == null) {
master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
baseImage = master.createGraphics();
baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
}
baseImage.drawImage(img, x, y, null);
//copy master image to image frame
var copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null);
var imageFrame = new ImageFrame(copy, imageMetadata.delay, imageMetadata.disposal, width, height);
frames.add(imageFrame);
//Process disposal
if (DISPOSAL_PREVIOUS.equals(imageMetadata.disposal)) {
BufferedImage from = null;
//scan frames backwards (from current to first), search for last processed undisposed frame
for (int i = frameIndex - 1; i >= 0; i--) {
var frame = frames.get(i);
//scan previous undisposed frame
if (!DISPOSAL_PREVIOUS.equals(frame.getDisposal()) || frameIndex == 0) {
from = frame.getImage();
break;
}
}
if (from != null) {
master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null);
baseImage = master.createGraphics();
//clear everything with transparent pixels
baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
}
} else if (DISPOSAL_BACKGROUND.equals(imageMetadata.disposal)) {
baseImage.clearRect(x, y, img.getWidth(), img.getHeight());
}
} //end for
return frames;
} finally {
reader.dispose();
}
}
}
public static void main(String[] args) throws Exception {
File testGif = new File("/home/test/test.gif");
List<ImageFrame> frames = readGifFrames(testGif);
for (ImageFrame frame : frames) {
var frameImage = frame.getImage();
//convert frameImage to PNG
}
}
Upvotes: 2
Reputation: 1188
Looks like you're creating an empty image in your loop and writing that out to file. Try this minor change to your code:
public class GifSplitter {
public static void main(String[] args) {
try {
splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void splitGif(File file) throws IOException {
ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
BufferedImage lastImage = reader.read(0);
ImageIO.write(lastImage, "PNG", new File(0 + ".png"));
for (int i = 1; i < reader.getNumImages(true); i++) {
BufferedImage readImage = reader.read(i);
ImageIO.write(readImage, "PNG", new File(i + ".png"));
}
}
}
Upvotes: 2