Page 1 of 1

LoadICO Procedure

Posted: Sat Jun 09, 2007 6:00 pm
by hagibaba
This code will load 1/4/8/16/24/32-bit Icon and Cursor files. It is from the FreeImage source but I have new code to deal with the And map as the FreeImage code was just converting to 32-bit. The procedure has 3 optional parameters to specify the page index, background color and inverse color. Being able to specify the color is also new. LoadICO() returns a DIB and LoadICO_() returns a DDB. The 16/32-bit And map code is untested.

Code: Select all

;- Structures

Structure ICONDIR ;Icon directory header
 idReserved.w ;Reserved (must be 0)
 idType.w ;Resource type (1 for icons, 2 for cursors)
 idCount.w ;How many images?
EndStructure

Structure ICONDIRENTRY ;Icon directory entry for each image
 bWidth.b ;Width of the image, in pixels
 bHeight.b ;Height of the image, in pixels (times 2)
 bColorCount.b ;Number of colors in image (0 if >= 8bpp)
 bReserved.b ;Reserved (must be 0)
 wPlanes.w ;Color planes
 wBitCount.w ;Bits per pixel
 dwBytesInRes.l ;How many bytes in this resource?
 dwImageOffset.l ;Where in the file is this image?
EndStructure

Procedure.l LoadICO(filename.s,page.l=0,backmask.l=0,invmask.l=0)
 ;From FreeImage source "PluginICO.cpp"
 ;Loads 1/4/8/16/24/32-bit Windows Icon and Cursor files
 ;page -> icon page index to load, first page = 0
 ;backmask -> background mask, palette index or RGB int color
 ;invmask -> inverse mask, palette index or RGB int color
 
 Protected idh.ICONDIR
 Protected bih.BITMAPINFOHEADER
 Protected *dib.BITMAPINFOHEADER
 Protected *entry.ICONDIRENTRY
 Protected *andbits.BYTE,*bits.BYTE,*wbits.WORD,*pbits.RGBQUAD
 Protected file.l,hIDE.l,width.l,height.l,bitcount.l,pitch.l
 Protected andpitch.l,bhsize.l,ncolors.l,hDIB.l,andmap.l
 Protected ix.l,iy.l,shift.l,bitmask.l,pixel.l,count.l
 
 ;Open file
 file=ReadFile(#PB_Any,filename)
 If file=0
  MessageRequester("LOAD ERROR","File could not be opened")
  ProcedureReturn #False
 EndIf
 
 ;Read the icon directory header
 ReadData(file,idh,SizeOf(ICONDIR))
 
 ;Allocate the icon directory entrys
 hIDE=AllocateMemory(idh\idCount*SizeOf(ICONDIRENTRY))
 If hIDE=0
  CloseFile(file)
  MessageRequester("LOAD ERROR","Memory allocation failed")
  ProcedureReturn #False
 EndIf
 
 ;Read the icon directory entrys
 ReadData(file,hIDE,idh\idCount*SizeOf(ICONDIRENTRY))
 
 If page<0 Or page>idh\idCount-1
  page=idh\idCount-1 ;Make sure page is valid
 EndIf
 
 ;Seek to the icon directory entry specified by page
 *entry=hIDE+(page*SizeOf(ICONDIRENTRY))
 FileSeek(file,*entry\dwImageOffset)
 FreeMemory(hIDE) ;Free the icon directory entrys
 
 ;Read the icon directory entry info header
 ReadData(file,bih,SizeOf(BITMAPINFOHEADER))
 
 ;Calculate some information
 width=bih\biWidth
 height=bih\biHeight/2 ;biHeight = Xor map height + And map height
 bitcount=bih\biBitCount
 pitch=(((width*bitcount)+31)/32)*4 ;DWORD-aligned width
 andpitch=(((width*1)+31)/32)*4 ;DWORD-aligned width
 bhsize=SizeOf(BITMAPINFOHEADER) ;DIB info header size
 If bitcount<16 ;1/4/8-bit
  ncolors=1 << bitcount ;2/16/256 colors
  If backmask<0 Or backmask>ncolors-1
   backmask=ncolors-1 ;Make sure backmask is valid
  EndIf
  If invmask<0 Or invmask>ncolors-1
   invmask=ncolors-1 ;Make sure invmask is valid
  EndIf
 Else ;16/24/32-bit
  ncolors=bih\biClrUsed ;Should be 0
 EndIf
 
 ;Allocate the DIB
 hDIB=AllocateMemory(bhsize+(ncolors*4)+(pitch*height))
 If hDIB=0
  CloseFile(file)
  MessageRequester("LOAD ERROR","Memory allocation failed")
  ProcedureReturn #False
 EndIf
 
 ;Fill in the DIB info header
 *dib=hDIB ;Pointer to DIB
 With *dib
  \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
 
 ;Read the icon palette, if any
 ReadData(file,hDIB+bhsize,ncolors*4)
 
 ;Read the icon bits
 ReadData(file,hDIB+bhsize+(ncolors*4),pitch*height)
 
 ;Allocate the And map
 andmap=AllocateMemory(andpitch*height)
 If andmap=0
  FreeMemory(hDIB)
  CloseFile(file)
  MessageRequester("LOAD ERROR","Memory allocation failed")
  ProcedureReturn #False
 EndIf
 
 ;Read the And map bits
 ReadData(file,andmap,andpitch*height)
 
 ;Generate the background and inverse colors from the And map
 Select bitcount
 
  Case 1 ;1-bit
  
   For iy=0 To height-1
    *andbits=andmap+(iy*andpitch)
    *bits=hDIB+bhsize+(ncolors*4)+(iy*pitch)
    For ix=0 To width-1
     shift=7-(ix % 8)
     bitmask=(*andbits\b & (1 << shift)) >> shift
     pixel=(*bits\b & (1 << shift)) >> shift
     If bitmask And pixel=0 ;Set background mask color
      *bits\b=(*bits\b & (255-(1 << shift))) | (backmask << shift)
     ElseIf bitmask And pixel ;Set inverse mask color
      *bits\b=(*bits\b & (255-(1 << shift))) | (invmask << shift)
     EndIf
     If ix % 8=7 : *andbits+1 : *bits+1 : EndIf
    Next
   Next
   
  Case 4 ;4-bit
  
   For iy=0 To height-1
    *andbits=andmap+(iy*andpitch)
    *bits=hDIB+bhsize+(ncolors*4)+(iy*pitch)
    count=4 ;Get high nibble first
    For ix=0 To width-1
     shift=7-(ix % 8)
     bitmask=(*andbits\b & (1 << shift)) >> shift
     pixel=(*bits\b & (15 << count)) >> count
     If bitmask And pixel=0 ;Set background mask color
      *bits\b=(*bits\b & (15 << (4-count))) | (backmask << count)
     ElseIf bitmask And pixel ;Set inverse mask color
      *bits\b=(*bits\b & (15 << (4-count))) | (invmask << count)
     EndIf
     If ix % 8=7 : *andbits+1 : EndIf
     *bits+(ix & 1) ;Add if odd
     count=~count & 4 ;0 or 4
    Next
   Next
   
  Case 8 ;8-bit
  
   For iy=0 To height-1
    *andbits=andmap+(iy*andpitch)
    *bits=hDIB+bhsize+(ncolors*4)+(iy*pitch)
    For ix=0 To width-1
     shift=7-(ix % 8)
     bitmask=(*andbits\b & (1 << shift)) >> shift
     pixel=*bits\b
     If bitmask And pixel=0 ;Set background mask color
      *bits\b=backmask
     ElseIf bitmask And pixel ;Set inverse mask color
      *bits\b=invmask
     EndIf
     If ix % 8=7 : *andbits+1 : EndIf
     *bits+1
    Next
   Next
   
  Case 16 ;16-bit, rgb=555
  
   For iy=0 To height-1
    *andbits=andmap+(iy*andpitch)
    *wbits=hDIB+bhsize+(ncolors*4)+(iy*pitch)
    For ix=0 To width-1
     shift=7-(ix % 8)
     bitmask=(*andbits\b & (1 << shift)) >> shift
     pixel=*wbits\w
     If bitmask And pixel=0 ;Set background mask color
      *wbits\w=(backmask >> 3) & $001F ;blue
      *wbits\w+((backmask >> 6) & $03E0) ;green
      *wbits\w+((backmask >> 9) & $7C00) ;red
     ElseIf bitmask And pixel ;Set inverse mask color
      *wbits\w=(invmask >> 3) & $001F ;blue
      *wbits\w+((invmask >> 6) & $03E0) ;green
      *wbits\w+((invmask >> 9) & $7C00) ;red
     EndIf
     If ix % 8=7 : *andbits+1 : EndIf
     *wbits+2
    Next
   Next
   
  Case 24,32 ;24/32-bit
  
   For iy=0 To height-1
    *andbits=andmap+(iy*andpitch)
    *pbits=hDIB+bhsize+(ncolors*4)+(iy*pitch)
    For ix=0 To width-1
     shift=7-(ix % 8)
     bitmask=(*andbits\b & (1 << shift)) >> shift
     pixel=*pbits\rgbRed+*pbits\rgbGreen+*pbits\rgbBlue
     If bitmask And pixel=0 ;Set background mask color
      *pbits\rgbBlue=backmask & 255
      *pbits\rgbGreen=(backmask >> 8) & 255
      *pbits\rgbRed=(backmask >> 16) & 255
     ElseIf bitmask And pixel ;Set inverse mask color
      *pbits\rgbBlue=invmask & 255
      *pbits\rgbGreen=(invmask >> 8) & 255
      *pbits\rgbRed=(invmask >> 16) & 255
     EndIf
     If ix % 8=7 : *andbits+1 : EndIf
     *pbits+(bitcount >> 3) ;Add 3 or 4
    Next
   Next
   
 EndSelect
 
 FreeMemory(andmap) ;Free the And map
 CloseFile(file) ;Close the file
 ProcedureReturn hDIB
 
EndProcedure

Procedure.l LoadICO_(filename.s,page.l=0,backmask.l=0,invmask.l=0)

 Protected *dib.BITMAPINFOHEADER
 Protected bits.l,hDC.l,hBitmap.l
 
 *dib=LoadICO(filename,page,backmask,invmask)
 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 ICO",#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|*.ico;*.cur"
     filename.s=OpenFileRequester("Choose An Image File To Open","",Pattern,0)
     If filename
      hBitmap.l=LoadICO_(filename,0,0,0)
      SendMessage_(GadgetID(1),#STM_SETIMAGE,#IMAGE_BITMAP,hBitmap)
     EndIf
   EndSelect
  Case #PB_Event_CloseWindow
   End
 EndSelect
ForEver