Page 1 of 1

LoadIFF Procedure

Posted: Mon Jun 18, 2007 1:10 am
by hagibaba
This code will load Interchange File Format (iff) files and lbm which is the same format with a different extension. It is similar to FreeImage's IFF code except that it doesn't deal with PBM but does deal with HAM mode. It will not load PBM (planar bitmap) as I couldn't find any information on it, or create PBM type files. It loads the standard ILBM (interleaved bitmap). LoadIFF() returns a DIB and LoadIFF_() returns a DDB.

Last edited on 19 June 2007.

Code: Select all

;- Structures

Structure IFFBMHD
 width.w ;Width of image in pixels
 height.w ;Height of image in pixels
 left.w ;X coordinate of image
 top.w ;Y coordinate of image
 nplanes.b ;Number of bitplanes
 masking.b ;Type of masking used
 compression.b ;Compression method used on image data
 flags.b ;CMAP flags (formerly pad1, unused)
 transparentcolor.w ;Transparent background color
 xaspect.b ;Horizontal pixel size
 yaspect.b ;Vertical pixel size
 pagewidth.w ;Horizontal resolution of display device
 pageheight.w ;Vertical resolution of display device
EndStructure

Procedure SwapLong(*lp.LONG)
 ;Convert a long from big endian to little endian, or vice-versa
 
 Protected b1.l,b2.l,b3.l,b4.l
 b1=(*lp\l >> 24) & $000000FF
 b2=(*lp\l >> 8) & $0000FF00
 b3=(*lp\l << 8) & $00FF0000
 b4=(*lp\l << 24) & $FF000000
 *lp\l=b1 | b2 | b3 | b4
 
EndProcedure

Procedure SwapWord(*wp.WORD)
 ;Convert a word from big endian to little endian, or vice-versa
 
 Protected b1.l,b2.l
 b1=(*wp\w >> 8) & $00FF
 b2=(*wp\w << 8) & $FF00
 *wp\w=b1 | b2
 
EndProcedure

Procedure.l LoadIFF(filename.s)
 ;From "IFF File Format Summary"
 ;Loads ILBM Interchange File Format files
 ;Uses SwapLong() and SwapWord()
 
 Protected ibh.IFFBMHD
 Protected *dib.BITMAPINFOHEADER
 Protected *pal.RGBQUAD,*bits.BYTE,*pbits.RGBQUAD,black.RGBQUAD
 Protected file.l,chunk.l,mainid.s,type.l,typeid.s,pos.l,chunkid.s
 Protected size.l,bitcount.l,width.l,height.l,linewidth.l,pitch.l
 Protected bhsize.l,ncolors.l,nplanes.l,hDIB.l,cmcolors.l,cmap.l
 Protected count.l,camg.l,bsize.l,body.l,length.l,run.l,pixel.l
 Protected ix.l,iy.l,sline.l,pline.l,shift.l,bit.l,plane.l
 
 ;Open the file
 file=ReadFile(#PB_Any,filename)
 If file=0
  MessageRequester("LOAD ERROR","File could not be opened")
  ProcedureReturn #False
 EndIf
 
 chunk=ReadLong(file) ;Main chunk id
 mainid=PeekS(@chunk,4)
 FileSeek(file,8) ;Seek past size
 type=ReadLong(file) ;Main type id
 typeid=PeekS(@type,4)
 pos=Loc(file)
 
 While Not Eof(file)
 
  chunk=ReadLong(file) ;Chunk id
  chunkid=PeekS(@chunk,4)
  size=ReadLong(file) ;Chunk size
  SwapLong(@size)
  
  If chunkid="FORM" Or chunkid="LIST" Or chunkid="PROP"
   mainid=chunkid ;Next chunk id
   type=ReadLong(file) ;Next type id
   typeid=PeekS(@type,4)
   size=4 ;We're not going to skip this chunk
  EndIf
  
  If chunkid="BMHD" ;Bitmap header chunk
  
   ;Read the bitmap header
   ReadData(file,ibh,SizeOf(IFFBMHD))
   SwapWord(@ibh\width)
   SwapWord(@ibh\height)
   SwapWord(@ibh\left)
   SwapWord(@ibh\top)
   SwapWord(@ibh\transparentcolor)
   SwapWord(@ibh\pagewidth)
   SwapWord(@ibh\pageheight)
   
   ;Calculate some information
   If ibh\nplanes<=1
    bitcount=1
   ElseIf ibh\nplanes<=4
    bitcount=4
   ElseIf ibh\nplanes<=8
    bitcount=8
   Else
    bitcount=24
   EndIf
   width=ibh\width & $FFFF
   height=ibh\height & $FFFF
   linewidth=((width+15)/16)*2 ;WORD-aligned plane width
   pitch=(((width*bitcount)+31)/32)*4 ;DWORD-aligned scanline width
   bhsize=SizeOf(BITMAPINFOHEADER) ;DIB info header size
   ncolors=0 ;No DIB palette
   If bitcount<24
    ncolors=1 << bitcount ;2/16/256 colors
   EndIf
   nplanes=ibh\nplanes
   If ibh\masking=1 ;Mask data as an extra bitplane per scanline
    nplanes+1 ;We're ignoring the mask but need to account for it
   EndIf
   
  ElseIf chunkid="CMAP" ;Color map chunk
  
   cmcolors=size/3 ;Color maps are in rgb triples
   If cmcolors>ncolors
    cmcolors=ncolors ;Make sure cmcolors is valid
   EndIf
   
   ;Allocate the color map
   cmap=AllocateMemory((cmcolors*4)+1) ;+1 just in case
   If cmap=0
    MessageRequester("LOAD ERROR","Memory allocation failed")
    Break
   EndIf
   
   ;Read the color map
   *pal=cmap ;Pointer to color map
   For count=0 To cmcolors-1
    *pal\rgbRed=ReadByte(file) ;red
    *pal\rgbGreen=ReadByte(file) ;green
    *pal\rgbBlue=ReadByte(file) ;blue
    *pal+4
   Next
   
  ElseIf chunkid="CAMG" ;Commodore-AMiGa chunk
  
   camg=ReadLong(file) ;Amiga display mode
   SwapLong(@camg)
   camg=(camg >> 8) & $FFFF ;Convert to a word, ignore the upper word
   
  ElseIf chunkid="BODY" ;Body chunk
  
   If camg & $0008 ;HAM display mode, we need 24-bit
    bitcount=24
    ncolors=0 ;No DIB palette
    pitch=(((width*bitcount)+31)/32)*4 ;DWORD-aligned scanline width
   EndIf
   
   ;Allocate the DIB
   hDIB=AllocateMemory(bhsize+(ncolors*4)+(pitch*height))
   If hDIB=0
    MessageRequester("LOAD ERROR","Memory allocation failed")
    Break
   EndIf
   
   *dib=hDIB ;Pointer to DIB
   With *dib ;Fill in the DIB info header
    \biSize=SizeOf(BITMAPINFOHEADER)
    \biWidth=width
    \biHeight=height
    \biPlanes=1
    \biBitCount=bitcount
    \biCompression=#BI_RGB
    \biSizeImage=pitch*height
    \biXPelsPerMeter=0
    \biYPelsPerMeter=0
    \biClrUsed=ncolors
    \biClrImportant=0
   EndWith
   
   ;Fill in the DIB palette
   If cmap ;We found a CMAP
    CopyMemory(cmap,hDIB+bhsize,cmcolors*4)
   Else ;No CMAP
    *pal=hDIB+bhsize ;Pointer to DIB palette
    For count=0 To ncolors-1 ;Build a greyscale palette
     *pal\rgbBlue=(255*count)/(ncolors-1) ;blue
     *pal\rgbGreen=(255*count)/(ncolors-1) ;green
     *pal\rgbRed=(255*count)/(ncolors-1) ;red
     *pal+4
    Next
   EndIf
   
   bsize=linewidth*nplanes*height ;Expected uncompressed size
   If size>bsize
    bsize=size ;Make sure bsize is valid
   EndIf
   
   ;Allocate the body
   body=AllocateMemory(bsize+1) ;+1 just in case
   If body=0
    MessageRequester("LOAD ERROR","Memory allocation failed")
    Break
   EndIf
   
   ;Read the bits
   If ibh\compression=0 ;Uncompressed
   
    ReadData(file,body,bsize)
    
   ElseIf ibh\compression=1 ;RLE compressed
   
    length=size ;Size of compressed data
    *bits=body ;Pointer to bits
    While length>0
     run=ReadByte(file) ;Run count minus 1
     length-1
     If run>=0 ;Raw packet
      run+1
      For count=0 To run-1 ;Non-RLE pixels
       pixel=ReadByte(file)
       length-1
       *bits\b=pixel
       *bits+1
      Next
     ElseIf run<>-128 ;Run packet
      run=-run+1
      pixel=ReadByte(file)
      length-1
      For count=0 To run-1 ;RLE pixels
       *bits\b=pixel
       *bits+1
      Next
     EndIf
    Wend
    
   EndIf
   
   If typeid="ILBM" ;Interleaved bitmap
   
    If camg & $0008 ;HAM display mode, we need 24-bit
    
     *pal=hDIB+bhsize ;Pointer to DIB palette
     For count=0 To cmcolors-1 ;Clear the palette
      *pal\rgbRed=0 : *pal\rgbGreen=0 : *pal\rgbBlue=0 : *pal+4
     Next
     
     For iy=0 To height-1
      sline=hDIB+bhsize+(ncolors*4)+((height-1-iy)*pitch)
      pline=body+(iy*linewidth*nplanes)
      For plane=0 To ibh\nplanes-1 ;Get the pixel index
       For ix=0 To width-1
        shift=7-(ix % 8)
        bit=(PeekB(pline+(ix >> 3)) & (1 << shift)) >> shift
        *pbits=sline+(ix*3)
        *pbits\rgbBlue | (bit << plane) ;Store it in rgbBlue
       Next
       pline+linewidth
      Next
      shift=ibh\nplanes-2 ;6-2=4
      plane=(1 << shift)+(1 << (shift+1)) ;16+32=48
      bit=(1 << ibh\nplanes)-1-plane ;64-1-48=15
      For ix=0 To width-1 ;Translate the pixel index to 24-bit
       *pbits=sline+(ix*3)
       pixel=*pbits\rgbBlue & bit ;Pixel index, 15
       count=*pbits\rgbBlue & plane ;High 2 bits, 48
       If count=0 ;Pixel index to the color map
        *pal=cmap+(pixel << 2)
        *pbits\rgbBlue=*pal\rgbBlue
        *pbits\rgbGreen=*pal\rgbGreen
        *pbits\rgbRed=*pal\rgbRed
       Else ;Hold-and-modify this pixel
        *pal=sline+(ix*3)-3 ;Get the last pixel
        If ix=0 ;If ix = 0 assume the last pixel is black
         *pal=black
        EndIf
        *pbits\rgbBlue=*pal\rgbBlue
        *pbits\rgbGreen=*pal\rgbGreen
        *pbits\rgbRed=*pal\rgbRed
        If count=1 << shift ;Blue level, 16
         *pbits\rgbBlue=(*pal\rgbBlue & bit) | (pixel << shift)
        ElseIf count=1 << (shift+1) ;Red level, 32
         *pbits\rgbRed=(*pal\rgbRed & bit) | (pixel << shift)
        ElseIf count=plane ;Green level, 48
         *pbits\rgbGreen=(*pal\rgbGreen & bit) | (pixel << shift)
        EndIf
       EndIf
      Next
     Next
     
    ElseIf ibh\nplanes<=1 ;1-bit ilbm
    
     For iy=0 To height-1
      sline=hDIB+bhsize+(ncolors*4)+((height-1-iy)*pitch)
      pline=body+(iy*linewidth*nplanes)
      For ix=0 To width-1
       shift=7-(ix % 8)
       bit=(PeekB(pline+(ix >> 3)) & (1 << shift))
       *bits=sline+(ix >> 3)
       *bits\b | bit
      Next
     Next
     
    ElseIf ibh\nplanes<=4 ;4-bit ilbm
    
     For iy=0 To height-1
      sline=hDIB+bhsize+(ncolors*4)+((height-1-iy)*pitch)
      pline=body+(iy*linewidth*nplanes)
      For plane=0 To ibh\nplanes-1
       count=4 ;Get high nibble first
       For ix=0 To width-1
        shift=7-(ix % 8)
        bit=(PeekB(pline+(ix >> 3)) & (1 << shift)) >> shift
        *bits=sline+(ix >> 1)
        *bits\b | (bit << (plane+count))
        count=~count & 4 ;0 or 4
       Next
       pline+linewidth
      Next
     Next
     
    ElseIf ibh\nplanes<=8 ;8-bit ilbm
    
     For iy=0 To height-1
      sline=hDIB+bhsize+(ncolors*4)+((height-1-iy)*pitch)
      pline=body+(iy*linewidth*nplanes)
      For plane=0 To ibh\nplanes-1
       For ix=0 To width-1
        shift=7-(ix % 8)
        bit=(PeekB(pline+(ix >> 3)) & (1 << shift)) >> shift
        *bits=sline+ix
        *bits\b | (bit << plane)
       Next
       pline+linewidth
      Next
     Next
     
    ElseIf ibh\nplanes=21 ;21-bit ilbm, NewTek
    
     ;21-bit ordering: RGB7 RGB6 RGB5 RGB4 RGB3 RGB2 RGB1 RGB0
     MessageRequester("LOAD ERROR","Image type not supported")
     
    ElseIf ibh\nplanes<=24 ;24-bit ilbm, Standard
    
     For iy=0 To height-1
      sline=hDIB+bhsize+(ncolors*4)+((height-1-iy)*pitch)
      pline=body+(iy*linewidth*nplanes)
      For count=0 To 2
       For plane=0 To (ibh\nplanes/3)-1
        For ix=0 To width-1
         shift=7-(ix % 8)
         bit=(PeekB(pline+(ix >> 3)) & (1 << shift)) >> shift
         *bits=sline+(ix*3)+(2-count)
         *bits\b | (bit << plane)
        Next
        pline+linewidth
       Next
      Next
     Next
     
    EndIf
    
   ElseIf typeid="PBM " ;Planar bitmap
   
    MessageRequester("LOAD ERROR","Image type not supported")
    
   EndIf
   
   Break ;We're finished
   
  EndIf
  
  If size & 1 ;Odd-length chunks need to be WORD-aligned
   size+1
  EndIf
  
  pos+size+8 ;Add chunk size to pos
  FileSeek(file,pos) ;Seek to next chunk
  
 Wend
 
 If cmap
  FreeMemory(cmap) ;Free the color map
 EndIf
 
 If body
  FreeMemory(body) ;Free the body
 EndIf
 
 CloseFile(file) ;Close the file
 ProcedureReturn hDIB
 
EndProcedure

Procedure.l LoadIFF_(filename.s)

 Protected *dib.BITMAPINFOHEADER
 Protected bits.l,hDC.l,hBitmap.l
 
 *dib=LoadIFF(filename)
 If *dib=0 ;Avoid errors
  ProcedureReturn #False
 EndIf
 
 bits=*dib+*dib\biSize+(*dib\biClrUsed*4) ;Pointer to bits
 
 ;Create the DDB bitmap
 hDC=GetDC_(#Null)
 hBitmap=CreateDIBitmap_(hDC,*dib,#CBM_INIT,bits,*dib,#DIB_RGB_COLORS)
 
 FreeMemory(*dib) ;Free the DIB
 ProcedureReturn hBitmap
 
EndProcedure

If OpenWindow(0,0,0,640,480,"Load IFF",#PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0))
 ButtonGadget(0,10,10,80,20,"Open File")
 ImageGadget(1,10,50,300,300,0,#PB_Image_Border)
EndIf

Repeat
 Select WaitWindowEvent()
  Case #PB_Event_Gadget
   Select EventGadget()
    Case 0
     Pattern.s="All Supported Formats|*.iff;*.lbm"
     filename.s=OpenFileRequester("Choose An Image File To Open","",Pattern,0)
     If filename
      hBitmap.l=LoadIFF_(filename)
      SendMessage_(GadgetID(1),#STM_SETIMAGE,#IMAGE_BITMAP,hBitmap)
     EndIf
   EndSelect
  Case #PB_Event_CloseWindow
   End
 EndSelect
ForEver

Posted: Tue Jun 19, 2007 4:43 am
by hagibaba
I couldn't sleep. I kept thinking about HAM mode and how I knew I could figure it out. I couldn't find any code on it so I had to go by the description on fileformat.info. It wasn't very clear but I finally realized what way it works and was able to get it working. Yesss! :lol:

Posted: Tue Jun 19, 2007 5:12 am
by Dare
:D

Good on you!

* thumbs up *