// Copyright 1997, 1998 Carmen Delessio (carmen@blackdirt.com)
// Black Dirt Software http://www.blackdirt.com/graphics
// Free for non-commercial use


import java.io.*;
import java.util.*;
import java.awt.*;
import java.net.*;
import java.applet.Applet;
import java.lang.Math;
import java.lang.String;
import java.awt.image.*;


class bmpImage extends Canvas{

      private byte[] bytebuffer;
      private int i;
      private int j;

      private int windowLong;
      private short windowInt;


      private DataInputStream d;

      int BMPsize;                               // size of this header in bytes
      int BMPreserved;
      int BMPimageoffset;
      int BMPheadersize;
      int BMPwidth;                              // image width in pixels
      int BMPheight;                             // image height in pixels (if < 0, "top-down")
      short BMPplanes;                   // no. of color planes: always 1
      short BMPbitsPerPixel;             // number of bits per pixel: 1, 4, 8, or 24 (no color map)
      int BMPcompression;                // compression methods used: 0 (none), 1 (8-bit RLE), or 2 (4-bit RLE)
      int BMPsizeOfBitmap;               // size of bitmap in bytes (may be 0: if so, calculate)
      int BMPhorzResolution;             // horizontal resolution, pixels/meter (may be 0)
      int BMPvertResolution;             // vertical resolution, pixels/meter (may be 0)
      int BMPcolorsUsed;                 // no. of colors in palette (if 0, calculate)
      int BMPcolorsImportant;    // no. of important colors (appear first in palette) (0 means all are important)
      Color colorTable[]; //color table

      int pixels[]; // array of pixels

      int colorIndex;
      int bytesPerLine;
      byte scanline[];
      Image BMPimage;

  public bmpImage(InputStream is ) //throws IOException
  {
        try {
          d = new DataInputStream(is);
          parseit();
          BMPimage = createImage (new MemoryImageSource (BMPwidth,BMPheight, pixels, 0, BMPwidth));
        } catch (IOException ex){
              System.out.println("Error Creating Bitmap Image");
        }
  }

  public bmpImage(String bmpString, int typeFlag )
  {
        byte bytePicture[];
        ByteArrayInputStream  byteInput = null;

        // add string error check, length, BM, etc

        bytePicture = new byte[bmpString.length()];
        bmpString.getBytes(0, bmpString.length(),bytePicture, 0);
        byteInput = new ByteArrayInputStream(bytePicture);
        try {
          d = new DataInputStream(byteInput);
          parseit(typeFlag);
          BMPimage = createImage (new MemoryImageSource (BMPwidth,BMPheight, pixels, 0, BMPwidth));
        } catch (IOException ex){
              System.out.println("Error Creating Bitmap Image");
        }
  }

  public bmpImage(String bmpString )
  {
        byte bytePicture[];
        ByteArrayInputStream  byteInput = null;
        bytePicture = new byte[bmpString.length()];
        bmpString.getBytes(0, bmpString.length(),bytePicture, 0);
        byteInput = new ByteArrayInputStream(bytePicture);
        try {
          d = new DataInputStream(byteInput);
          parseit();
          BMPimage = createImage (new MemoryImageSource (BMPwidth,BMPheight, pixels, 0, BMPwidth));
        } catch (IOException ex){
              System.out.println("Error Creating Bitmap Image");
        }
  }


   public Dimension preferredSize(){
      return new Dimension(BMPwidth, BMPheight);
   }

   public Dimension minimumSize(){
      return new Dimension(BMPwidth, BMPheight);
   }

   public void paint (Graphics g){
           g.drawImage(BMPimage, 0, 0, this);
   }

   public void update(Graphics g){
      paint(g);
   }

    public MemoryImageSource getMemoryImageSource(){
     return (new MemoryImageSource (BMPwidth,BMPheight, pixels, 0, BMPwidth));
    }

    public Image getImage(){
      return (BMPimage);
    }






  public synchronized void  parseit() throws IOException
  {
          parseit(0);
  }

  public synchronized void  parseit(int typeFlag) throws IOException
  {

       byte[] f_long =new byte[4];
       byte[] f_int = new byte[2];
       byte[] parmBuffer;
       byte[] tempBuffer;
       short x;
       short y;
       short x2;
       short y2;
       short count = 0;

 

//begin bitmap header

        if (typeFlag == 0) {  // read header - bitmapfile
            tempBuffer = null;
            tempBuffer = new byte[2];  //get BM

            d.read(tempBuffer);
            System.out.println ("tempBuffer " + new String(tempBuffer,0));
            BMPsize = readLong(d); //key  4 bytes
            System.out.println ("size " + BMPsize);

            BMPreserved = readLong(d);
            System.out.println ("reservede " + BMPreserved);

            BMPimageoffset = readLong(d);
            System.out.println ("offset " + BMPimageoffset);
        }

        System.out.println ("in bmpstream" );
        BMPheadersize = readLong(d);
        System.out.println ("BMPheadersize " + BMPheadersize);

        BMPwidth = readLong(d); 
        System.out.println ("BMPwidth " + BMPwidth);

        BMPheight = readLong(d); 
        System.out.println ("BMPheight " + BMPheight);

        BMPplanes = readInt(d); 
        System.out.println ("BMPplanes " + BMPplanes);

        BMPbitsPerPixel = readInt(d); 
        System.out.println ("BMPbitsPerPixel " + BMPbitsPerPixel);


        BMPcompression = readLong(d); 
        System.out.println ("BMPcompression " + BMPcompression);

        BMPsizeOfBitmap = readLong(d); 
        System.out.println ("BMPsizeOfBitmap " + BMPsizeOfBitmap);

        BMPhorzResolution = readLong(d); 
        System.out.println ("BMPhorzResolution " + BMPhorzResolution);

        BMPvertResolution = readLong(d); 
        System.out.println ("BMPvertResolution " + BMPvertResolution);

        BMPcolorsUsed = readLong(d); 
        System.out.println ("BMPcolorsUsed " + BMPcolorsUsed);

        BMPcolorsImportant = readLong(d); 
        System.out.println ("BMPcolorsImportant " + BMPcolorsImportant);


        pixels = new int [BMPwidth * (BMPheight +1)];
        
        if (BMPbitsPerPixel== 1){
          colorTable = new Color[2];
          for (i =0; i < 2; i++){
            colorTable[i] = win2Color(readLong(d));
          }
          bytesPerLine = BMPwidth/8;   //width is # of pixels, twice as many bytes as pixles
                                       // only used to read in scan lines
          if (bytesPerLine * 8 < BMPwidth) {  // if pixel is on odd boundary 
              bytesPerLine++;
          }

          while (bytesPerLine % 4 != 0)
             bytesPerLine++; // get even boundary, DWORD boundary

          scanline = new byte[bytesPerLine]; // declare a buffer sufficient for 1 line

          for (i = BMPheight -1; i >= 0; i--){    // bottom up, start with last line
            d.readFully(scanline,0, bytesPerLine);  // read in a line

            for (j = 0; j < BMPwidth; j+=8){
              colorIndex = (scanline[j/8])>>7 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>6 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +1 ]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>5 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +2]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>4 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +3]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>3 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +4]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>2 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +5]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8])>>1 & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +6]= colorTable[colorIndex].getRGB();

              colorIndex = (scanline[j/8]) & 0x01;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j +7]= colorTable[colorIndex].getRGB();

            }
          }
       } // if bpp = 1




        if (BMPbitsPerPixel== 4){
          colorTable = new Color[16];
          for (i =0; i < 16; i++){
            colorTable[i] = win2Color(readLong(d));
          }
          bytesPerLine = BMPwidth/2;   //width is # of pixels, twice as many bytes as pixles
                                       // only used to read in scan lines
          if (bytesPerLine * 2 < BMPwidth) {  // if pixel is on odd boundary 
              bytesPerLine++;
          }

          while (bytesPerLine % 4 != 0)
             bytesPerLine++; // get even boundary, DWORD boundary

          scanline = new byte[bytesPerLine]; // declare a buffer sufficient for 1 line
          for (i = BMPheight -1; i >= 0; i--){    // bottom up, start with last line
            d.readFully(scanline,0, bytesPerLine);  // read in a line

            for (j = 0; j < BMPwidth; j+=2){
              colorIndex = (scanline[j/2]>>4) & 0x0F;  // 1st 4 bits of byte shifted and masked
              pixels[i*BMPwidth + j]= colorTable[colorIndex].getRGB();
              colorIndex = (scanline[j/2]) & 0x0F;  // 2nd 4 bits masked
              pixels[i*BMPwidth + j + 1]= colorTable[colorIndex].getRGB();
            }
          }
       } // if bpp = 4



        if (BMPbitsPerPixel== 8){
          colorTable = new Color[256];
          for (i =0; i < 256; i++){
            colorTable[i] = win2Color(readLong(d));
          }
          bytesPerLine = BMPwidth;   //width is # of pixels, 1 pixels for each byte
          while (bytesPerLine % 4 != 0)
             bytesPerLine++; // get even boundary
          scanline = new byte[bytesPerLine]; // declare a buffer sufficient for 1 line

        System.out.println ("bytesPerLine " + bytesPerLine );


          for (i = BMPheight -1; i >= 0; i--){    // bottom up, start with last line
            d.readFully(scanline);  // read in a line
            for (j = 0; j < BMPwidth; j++){
              colorIndex = scanline[j] ;
              if (colorIndex <0)
                colorIndex +=256;
              pixels[i*BMPwidth + j]= colorTable[colorIndex].getRGB();
            }
          }
       } // if bpp = 8



        if (BMPbitsPerPixel== 24){
        System.out.println ("in bmpstream bpp =24 ");

          int winBlue;
          int winGreen;
          int winRed;
          bytesPerLine = 3 * BMPwidth;   //width is # of pixels, 3 bytes for each pixel
          while (bytesPerLine % 4 != 0)
             bytesPerLine++; // get even boundary
          scanline = new byte[bytesPerLine+4]; // declare a buffer sufficient for 1 line

          for (i = BMPheight -1; i >= 0; i--){    // bottom up, start with last line
            d.readFully(scanline,0, bytesPerLine);  // read in a line
            for (j = 0; j < bytesPerLine; j+=3){ //work with 3 bytes at a time
              //j
              //j+1
              //j+2
              winBlue =   (int) (scanline[j]) & 0xff;
              winGreen = ((int) (scanline[j+1]) & 0xff) << 8;
              winRed =   ((int) (scanline[j+2]) & 0xff) << 16;
              pixels[i*BMPwidth + j/3]= 0xff000000;
              pixels[i*BMPwidth + j/3] |= winRed;
              pixels[i*BMPwidth + j/3] |= winGreen;
              pixels[i*BMPwidth + j/3] |= winBlue;
            }
         }
       } // if bpp = 24

  }



    public Color win2Color( int colorValue) {

       // windows does it backwards
       int rgbBlue   = 16711680; // ff0000
       int rgbGreen  =    65280; // 00ff00
       int rgbRed    =      255; // 0000ff
      
       int javaBlue ;
       int javaGreen;
       int javaRed  ;

      javaRed = (int)((colorValue & rgbBlue)/ 65536);
      javaGreen = (int)((colorValue & rgbGreen)/ 256);
      javaBlue = (int)((colorValue & rgbRed));

      return( new Color (javaRed , javaGreen, javaBlue));
    }




   public int readLong( DataInputStream d){
       byte[] longBuf =new byte[4];

        try{
            d.readFully(longBuf);
            return flipLong(longBuf);
         }  catch(IOException e){
            System.err.println(e);
            return 99;
         }

   }

   public short readInt( DataInputStream d){
        byte[] intBuf =new byte[2];

        try{
            d.readFully(intBuf);
            return flipInt(intBuf);
         }  catch(IOException e){
            System.err.println(e);
            return 99;
         }

   }

   public int flipLong( byte[] byteFlip){
         DataInputStream dl;
         ByteArrayInputStream  b_in;
         byte[] bytebuffer;
         bytebuffer = new byte[4];
         bytebuffer[0] = byteFlip[3];
         bytebuffer[1] = byteFlip[2];  
         bytebuffer[2] = byteFlip[1];  
         bytebuffer[3] = byteFlip[0];  

         b_in = new ByteArrayInputStream(bytebuffer);
         dl = new DataInputStream(b_in);
         try{
           return dl.readInt();
         }
          catch(IOException e){System.err.println(e);}
            return 0;

  }




   public short flipInt( byte[] byteFlip){
         DataInputStream d;
         ByteArrayOutputStream b_out;
         ByteArrayInputStream  b_in;
         byte[] bytebuffer;

         bytebuffer = new byte[2];
         bytebuffer[0] = byteFlip[1];
         bytebuffer[1] = byteFlip[0];  

         b_in = new ByteArrayInputStream(bytebuffer);
         d = new DataInputStream(b_in);
         try{
           return d.readShort();
         }
         catch(IOException e){ System.err.println(e);}
            return 0;

  }
}





