Image Dimensions from InputStream

The following code reads the width, height and type (png/jpeg/etc…) of an image from an InputStream and closes it immediately.


   public enum ImageType
   {
      TGA, BMP, JPG, GIF, PNG;
   }

   public class ImageMetaData
   {
      public final ImageType type;
      public final int       width;
      public final int       height;

      public ImageMetaData(ImageType type, int width, int height)
      {
         this.type = type;
         this.width = width;
         this.height = height;
      }

      public Dimension getDimension()
      {
         return new Dimension(this.width, this.height);
      }

      public String toString()
      {
         return type.name() + ":" + this.width + "x" + this.height;
      }
   }

   public static Dimension getImageSize(InputStream in) throws IOException
   {
      ImageMetaData meta = getImageTypeAndSize(in);
      if(meta == null) return null;
      return new Dimension(meta.width, meta.height);
   }

   public static ImageMetaData getImageTypeAndSize(InputStream in) throws IOException
   {
      DataInputStream dis = new DataInputStream(new BufferedInputStream(in));

      try
      {
         int header = dis.readUnsignedShort();

         if (header == 0x8950)
         {
            // PNG!
            dis.readFully(new byte[(8 - 2) + 4 + 4]); // thanks Abuse

            return new ImageMetaData(ImageType.PNG, dis.readInt(), dis.readInt());
         }

         if (header == 0xffd8)
         {
            // JPG!

            while (true)
            {
               int marker = dis.readUnsignedShort();

               switch (marker)
               {
                  case 0xffd8: // SOI
                  case 0xffd0: // RST0
                  case 0xffd1: // RST1
                  case 0xffd2: // RST2
                  case 0xffd3: // RST3
                  case 0xffd4: // RST4
                  case 0xffd5: // RST5
                  case 0xffd6: // RST6
                  case 0xffd7: // RST7
                  case 0xffd9: // EOI
                     break;

                  case 0xffdd: // DRI
                     dis.readUnsignedShort();
                     break;

                  case 0xffe0: // APP0
                  case 0xffe1: // APP1
                  case 0xffe2: // APP2
                  case 0xffe3: // APP3
                  case 0xffe4: // APP4
                  case 0xffe5: // APP5
                  case 0xffe6: // APP6
                  case 0xffe7: // APP7
                  case 0xffe8: // APP8
                  case 0xffe9: // APP9
                  case 0xffea: // APPa
                  case 0xffeb: // APPb
                  case 0xffec: // APPc
                  case 0xffed: // APPd
                  case 0xffee: // APPe
                  case 0xffef: // APPf
                  case 0xfffe: // COM
                  case 0xffdb: // DQT
                  case 0xffc4: // DHT
                  case 0xffda: // SOS
                     dis.readFully(new byte[dis.readUnsignedShort() - 2]);
                     break;

                  case 0xffc0: // SOF0
                  case 0xffc2: // SOF2
                     dis.readUnsignedShort();
                     dis.readByte();
                     int height = dis.readUnsignedShort();
                     int width = dis.readUnsignedShort();
                     return new ImageMetaData(ImageType.JPG, width, height);

                  default:
                     throw new IllegalStateException("invalid jpg marker: " + Integer.toHexString(marker));
               }
            }
         }
         else if (header == 0x424D)
         {
            // BMP!
            dis.readFully(new byte[16]);

            int w = PrimIO.swap32(dis.readInt());
            int h = PrimIO.swap32(dis.readInt());
            return new ImageMetaData(ImageType.BMP, w, h);
         }
         else if (header == (('G' << 8) | ('I' << 0))) // GIF
         {
            // GIF!
            dis.readFully(new byte[4]);
            int w = PrimIO.swap16(dis.readUnsignedShort());
            int h = PrimIO.swap16(dis.readUnsignedShort());
            return new ImageMetaData(ImageType.GIF, w, h);
         }
         else
         // TGA?
         {
            // TGA doesn't have a magic number, try to parse
            byte[] tgaRemainingHeader = new byte[18 - 2];
            try
            {
               dis.readFully(tgaRemainingHeader);
            }
            catch (EOFException exc)
            {
               return null;
               //throw new IllegalStateException("unsupported image type. (header: " + Integer.toHexString(header) + ")");
            }

            try
            {
               if (tgaRemainingHeader[2 - 2] != 2) // uncompressed
                  throw new IllegalStateException();
               int w = 0, h = 0;
               w |= (tgaRemainingHeader[12 - 2] & 0xFF) << 0;
               w |= (tgaRemainingHeader[13 - 2] & 0xFF) << 8;
               h |= (tgaRemainingHeader[14 - 2] & 0xFF) << 0;
               h |= (tgaRemainingHeader[15 - 2] & 0xFF) << 8;
               if ((w | h) < 0)
                  throw new IllegalStateException();

               boolean alpha;
               if (tgaRemainingHeader[16 - 2] == 24)
                  alpha = false;
               else if (tgaRemainingHeader[16 - 2] == 32)
                  alpha = true;
               else
                  throw new IllegalStateException();
               if (tgaRemainingHeader[17 - 2] != ((alpha ? 8 : 0) & 15))
                  throw new IllegalStateException();

               return new ImageMetaData(ImageType.TGA, w, h);
            }
            catch (IllegalStateException exc)
            {
               return null;
               //throw new IllegalStateException("unsupported image type. (header: " + Integer.toHexString(header) + ")");
            }
         }
      }
      finally
      {
         dis.close();
      }
   }