1. Are you ready for the Galaxy S20? Here is everything we know so far!

Need some help with boot animation creator

Discussion in 'Android Devices' started by BLuFeNiX, Apr 14, 2012.

  1. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    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):

    and here is the same animation NOT working (created with my Java program):

    Here is my code:
    Code (Text):
    1. import java.awt.Graphics;
    2. import java.awt.Graphics2D;
    3. import java.awt.RenderingHints;
    4. import java.awt.image.BufferedImage;
    5. import java.io.*;
    6. import java.util.Iterator;
    7. import javax.imageio.*;
    8. import javax.imageio.metadata.IIOMetadata;
    9. import javax.imageio.metadata.IIOMetadataNode;
    10. import javax.imageio.stream.*;
    12. public class test {
    14.     static BufferedImage background; // background (for optimized GIFs)
    16.     public static void main(String[] args) throws Exception {
    18.         Object input = new File("img.gif");  // or Object input = new FileInputStream("animated.gif");
    19.         ImageInputStream stream = ImageIO.createImageInputStream(input);
    20.         Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
    21.         if (!readers.hasNext())
    22.             throw new RuntimeException("no image reader found");
    23.         ImageReader reader = (ImageReader) readers.next();
    24.         reader.setInput(stream);
    26.         int n = reader.getNumImages(true);
    27.         System.out.println("numImages = " + n);
    29.         for (int i = 0; i < n; i++) {
    31.             // get metadata (in order to detect framerate and top/left positioning for each frame)
    32.             IIOImage frame = reader.readAll(i,null);
    33.             IIOMetadata meta = frame.getMetadata();
    34.             IIOMetadataNode imgRootNode = (IIOMetadataNode) meta.getAsTree("javax_imageio_gif_image_1.0");
    36.             // get framerate for animation
    37.             if (i == 0) {
    38.                 IIOMetadataNode gce = (IIOMetadataNode) imgRootNode.getElementsByTagName("GraphicControlExtension").item(0);
    39.                 int delay = Integer.parseInt(gce.getAttribute("delayTime"));
    40.                 System.out.println("DELAY: " + delay);
    41.             }
    43.             IIOMetadataNode imgDescr = (IIOMetadataNode) imgRootNode.getElementsByTagName("ImageDescriptor").item(0);
    44.             int offsetX = Integer.parseInt(imgDescr.getAttribute("imageLeftPosition"));  // find offsets for each
    45.             int offsetY = Integer.parseInt(imgDescr.getAttribute("imageTopPosition"));   // frame in the animated GIF
    47.             BufferedImage overlay = reader.read(i);
    48.             if (i == 0) { background = reader.read(i); }
    50.             // combine current and previous frame to create complete frame (in case of optimized GIFs)
    51.             BufferedImage combined = new BufferedImage(background.getWidth(), background.getHeight(), BufferedImage.TYPE_INT_ARGB);
    53.             // paint both images, preserving the alpha channels
    54.             Graphics g = combined.getGraphics();
    55.             g.drawImage(background, 0, 0, null);
    56.             g.drawImage(overlay, offsetX, offsetY, null);          
    58.             // create new directory for images
    59.             if (i == 0) {
    60.                 new File("bootanimation/").mkdirs();
    61.             }
    63.             // resize frame to fit screen
    64.             int resizeWidth;
    65.             int resizeHeight;
    66.             if (background.getWidth() > background.getHeight()) {
    67.                 resizeWidth = 480;
    68.                 resizeHeight = 320;
    69.             }
    70.             else {
    71.                 resizeWidth = 320;
    72.                 resizeHeight = 480;
    73.             }
    75.             // Create new (blank) image of required (scaled) size
    76.             BufferedImage scaledImage = new BufferedImage(resizeWidth, resizeHeight, BufferedImage.TYPE_INT_ARGB);
    77.             // Paint scaled version of image to new image
    78.             Graphics2D graphics2D = scaledImage.createGraphics();
    79.             graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    80.             RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    81.             graphics2D.drawImage(combined, 0, 0, resizeWidth, resizeHeight, null);
    82.             graphics2D.dispose();
    84.             // rotate the image if it's wider than it is tall
    85.             if (scaledImage.getWidth() > scaledImage.getHeight()) {
    86.                 ImageIO.write(rotate270(scaledImage), "PNG", new File("bootanimation/img" + String.format("%04d", i) + ".png"));
    87.             }
    88.             else {
    89.                 ImageIO.write(scaledImage, "PNG", new File("bootanimation/img" + String.format("%04d", i) + ".png"));
    90.             }
    91.             background = combined; // save our most recently created frame as the background for the next frame (in case of optimized GIFs)
    92.         }
    93.         stream.close();
    95.     }
    97.     public static BufferedImage rotate270(BufferedImage img) {
    98.         int width = img.getWidth();
    99.         int height = img.getHeight();
    100.         BufferedImage newImage = new BufferedImage(height, width, img.getType());
    102.         for (int i = 0; i < width; i++)
    103.             for (int j = 0; j < height; j++)
    104.                 newImage.setRGB(j, width - 1 - i, img.getRGB(i, j));
    106.         return newImage;
    107.     }
    109. }
    Please help me figure out what the difference is between my working and non working PNGs!

    1. Download the Forums for Android™ app!


  2. Loota

    Loota Android Enthusiast

    Sorry I'm half asleep and not good with code... are the pngs compressed at all? Is the boot animaTion zip compressed?
  3. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    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).
  4. KydDynoMyte

    KydDynoMyte Newbie

    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.
  5. Loota

    Loota Android Enthusiast

    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
  6. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    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.
  7. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    Making them 24bit did not fix it. :(
  8. KydDynoMyte

    KydDynoMyte Newbie

    I think it might be because the one that doesn't work has an alpha channel transparency layer in it.
  9. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    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.


    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.
  10. KydDynoMyte

    KydDynoMyte Newbie

    What fixed it?
  11. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

    I ended up having to try about a dozen different libraries for writing out a PNG file. Finally found one that worked: org.apache.xmlgraphics.image.codec.png.PNGImageEncoder

    You'd be surprised how many other libraries just don't write the correct metadata.
  12. BLuFeNiX

    BLuFeNiX Well-Known Member
    Thread Starter

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

Samsung Transform Ultra Forum

The Samsung Transform Ultra release date was October 2011. Features and Specs include a 3.5" inch screen, 3MP camera, 128GB RAM, Snapdragon S2 processor, and 1500mAh battery.

October 2011
Release Date

Share This Page