LoadICO Procedure
Posted: Sat Jun 09, 2007 6:00 pm
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