• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Root Need some help with boot animation creator

BLuFeNiX

Well-Known Member
Feb 16, 2012
209
103
Hi guys,

You may have noticed that I'm working on a program to convert an animated GIF to a bootanimation.zip.

I have almost everything working, but I realized something today: My converted PNGs don't work!

I can take an animated GIF and convert it into PNGs via a command line tool, and put them in a zip, and everything works. When I do the same with my tool it creates an almost identical image (slight differences in resize algorithm), but when I package them into a bootanimation.zip and put it on my phone it just shows a black screen on boot.

Here is an example of a working boot animation I made (created with command line utilities):
http://dl.dropbox.com/u/68531860/bootanimation.zip

and here is the same animation NOT working (created with my Java program):
http://dl.dropbox.com/u/68531860/bootanimationJ.zip

Here is my code:
Code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.*;

public class test {

	static BufferedImage background; // background (for optimized GIFs)

	public static void main(String[] args) throws Exception {

		Object input = new File("img.gif");  // or Object input = new FileInputStream("animated.gif");
		ImageInputStream stream = ImageIO.createImageInputStream(input);
		Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
		if (!readers.hasNext())
			throw new RuntimeException("no image reader found");
		ImageReader reader = (ImageReader) readers.next();
		reader.setInput(stream);
		
		int n = reader.getNumImages(true);
		System.out.println("numImages = " + n);

		for (int i = 0; i < n; i++) {

			// get metadata (in order to detect framerate and top/left positioning for each frame)
			IIOImage frame = reader.readAll(i,null);
			IIOMetadata meta = frame.getMetadata();
			IIOMetadataNode imgRootNode = (IIOMetadataNode) meta.getAsTree("javax_imageio_gif_image_1.0");
			
			// get framerate for animation
			if (i == 0) {
				IIOMetadataNode gce = (IIOMetadataNode) imgRootNode.getElementsByTagName("GraphicControlExtension").item(0);
				int delay = Integer.parseInt(gce.getAttribute("delayTime"));
				System.out.println("DELAY: " + delay);
			}

			IIOMetadataNode imgDescr = (IIOMetadataNode) imgRootNode.getElementsByTagName("ImageDescriptor").item(0);
			int offsetX = Integer.parseInt(imgDescr.getAttribute("imageLeftPosition"));  // find offsets for each
			int offsetY = Integer.parseInt(imgDescr.getAttribute("imageTopPosition"));   // frame in the animated GIF

			BufferedImage overlay = reader.read(i);
			if (i == 0) { background = reader.read(i); }

			// combine current and previous frame to create complete frame (in case of optimized GIFs)
			BufferedImage combined = new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_INT_ARGB);

			// paint both images, preserving the alpha channels
			Graphics g = combined.getGraphics();
			g.drawImage(background, 0, 0, null);
			g.drawImage(overlay, offsetX, offsetY, null);			

			// create new directory for images
			if (i == 0) {
				new File("bootanimation/").mkdirs();
			}
			
			// resize frame to fit screen
			int resizeWidth;
			int resizeHeight;
			if (background.getWidth() > background.getHeight()) {
				resizeWidth = 480;
				resizeHeight = 320;	
			}
			else {
				resizeWidth = 320;
				resizeHeight = 480;
			}
			
			// Create new (blank) image of required (scaled) size
			BufferedImage scaledImage = new BufferedImage(resizeWidth, resizeHeight, BufferedImage.TYPE_INT_ARGB);
			// Paint scaled version of image to new image
			Graphics2D graphics2D = scaledImage.createGraphics();
			graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
			RenderingHints.VALUE_INTERPOLATION_BILINEAR);
			graphics2D.drawImage(combined, 0, 0, resizeWidth, resizeHeight, null);
			graphics2D.dispose();
			
			// rotate the image if it's wider than it is tall
			if (scaledImage.getWidth() > scaledImage.getHeight()) {
				ImageIO.write(rotate270(scaledImage), "PNG", new File("bootanimation/img" + String.format("%04d", i) + ".png"));
			}
			else {
				ImageIO.write(scaledImage, "PNG", new File("bootanimation/img" + String.format("%04d", i) + ".png"));
			}
			background = combined; // save our most recently created frame as the background for the next frame (in case of optimized GIFs)
		}
		stream.close();

	}
	
	public static BufferedImage rotate270(BufferedImage img) {
		int width = img.getWidth();
		int height = img.getHeight();
		BufferedImage newImage = new BufferedImage(height, width, img.getType());

		for (int i = 0; i < width; i++)
			for (int j = 0; j < height; j++)
				newImage.setRGB(j, width - 1 - i, img.getRGB(i, j));

		return newImage;
	}
	
}

Please help me figure out what the difference is between my working and non working PNGs!
 
Sorry I'm half asleep and not good with code... are the pngs compressed at all? Is the boot animaTion zip compressed?

I'm not aware of any PNG compression, but I guess it's possible that the ImageIO.write() method causes this in some way. The zip file is not compressed at all (I do the zipping part myself for testing, and zipped it the same way as the working animation).
 
Upvote 0
The bit depth on the non working one is 32. Needs to be 24?

Another difference I see is file creation and modified dates embedded at the end of you working images, but not in your non working or other known working ones I looked at so that must not be it. Unless I switched yours around.

Only other thing I can think of is check the permissions of the zip file.
 
Upvote 0
The bit depth on the non working one is 32. Needs to be 24?

That could be it... whenever I had a prob loading a image for a bootani I would just copy and paste onto a working boot ani image and resave it.. not that you should have to do that fenix but bit depth could be why it wasn't working and was solved once I copied an image onto a working img
 
Upvote 0
The bit depth on the non working one is 32. Needs to be 24?

Another difference I see is file creation and modified dates embedded at the end of you working images, but not in your non working or other known working ones I looked at so that must not be it. Unless I switched yours around.

Only other thing I can think of is check the permissions of the zip file.

That could be it... whenever I had a prob loading a image for a bootani I would just copy and paste onto a working boot ani image and resave it.. not that you should have to do that fenix but bit depth could be why it wasn't working and was solved once I copied an image onto a working img

I read somewhere that 32bit images are supposed to work, but I will try 24. I know the permissions are okay because I've done numerous "sanity checks" with working animations. Thank you both for the input.
 
Upvote 0
I think it might be because the one that doesn't work has an alpha channel transparency layer in it.

32bit means it has an alpha channel, 24bit means no alpha. (8 bits for each color, Red, Green, Blue, and optionally, Alpha) So, removing the alpha did not fix it.

However....

I did get it working! I'll be posting an alpha release either today or tomorrow. All I have left to do is clean up the code, and add the piece that zips the files.
Can't decide if alpha release will have GUI, might wait for beta.

Thank you all for your input.
 
Upvote 0
32bit means it has an alpha channel, 24bit means no alpha. (8 bits for each color, Red, Green, Blue, and optionally, Alpha) So, removing the alpha did not fix it.

However....

I did get it working! I'll be posting an alpha release either today or tomorrow. All I have left to do is clean up the code, and add the piece that zips the files.
Can't decide if alpha release will have GUI, might wait for beta.

Thank you all for your input.

What fixed it?
 
Upvote 0
32bit means it has an alpha channel, 24bit means no alpha. (8 bits for each color, Red, Green, Blue, and optionally, Alpha) So, removing the alpha did not fix it.

However....

I did get it working! I'll be posting an alpha release either today or tomorrow. All I have left to do is clean up the code, and add the piece that zips the files.
Can't decide if alpha release will have GUI, might wait for beta.

Thank you all for your input.

First version released! http://androidforums.com/samsung-transform-ultra/533275-gif2boot-convert-animated-gifs-boot-animations-v0-1a.html
 
Upvote 0

BEST TECH IN 2023

We've been tracking upcoming products and ranking the best tech since 2007. Thanks for trusting our opinion: we get rewarded through affiliate links that earn us a commission and we invite you to learn more about us.

Smartphones