***************
Hi,
the following small include file allows you to save an icon in a .ico file, something which Windows will not do on our behalf!
SYNTAX: SaveIcon(hIcon, filename$)
where 'hIcon' is a handle of an icon either loaded from a resource file or with LoadImage_() etc.
This library can only be used with icons which are already stored in memory (either loaded from disk, or created with CreateIconIndirect_() etc.) and saves the icon in the same format (depth etc.) as stored internally by Windows.
However, this code can quite 'easily'

Warning: I have only tested with 32-bit and 1-bit monochrome bitmaps so far.
Include file:
Code: Select all
;SaveIcon.
;By Stephen Rodriguez 2006 (Updated 2010).
;Purebasic 4.5
;This small 'include' file allows for the saving of a single icon/cursor in a .ico/.cur file.
;Extending to include multiple icons/cursors in one file would be straight forward.
;SYNTAX: SaveIcon(hIcon, filename$)
; where 'hIcon' is a handle of an icon/cursor either loaded from a resource file or with LoadImage_() etc.
;NOTES.
; i) This library can only be used with icons/cursors which are already stored in memory
; (either loaded from disc, or created with CreateIconIndirect_() etc.) and
; saves the icon/cursor in the same format (depth etc.) as stored internally by Windows.
; However, this code can quite 'easily' be adapted so that icons/cursors can be created from
; scratch using native Purebasic commands (where images are stored in any format)
; and then, using the code adapted from this library, saved in .ico/.cur format. It's
; just a question of messing with colour tables etc.
; ii) If using this library to save an icon/cursor loaded With LoadImage_(), then it may appear that
; the new icon/cursor is of a different size. This will be because the original .ico/.cur file has multiple
; icons in it of different sizes. Windows explorer may report one size whilst you loaded another etc.
;TODO (maybe!)
; -Extend the code to save multiple icons/cursors in the same .ico file.
; -Extend to cursor .cur files (DONE!)
;**************************************************************************************************
;Result = non-zero if no error.
Procedure.i SaveIcon(hIcon, filename$)
Protected result, iconinfo.ICONINFO, hbmMask, hbmColor
Protected cbitmap.BITMAP, cwidth, cheight, cbitsperpixel, colorcount, colorplanes
Protected mbitmap.BITMAP, mwidth, mheight, fIcon, xHotspot, yHotspot
Protected file, imagebytecount, hdc, oldbitmap, mem, bytesinrow, temp
Protected *bitmapinfo.BITMAPINFO
;Get information regarding the icon.
If Not(GetIconInfo_(hIcon, iconinfo)) : ProcedureReturn 0 : EndIf ;Not a valid icon handle.
fIcon=2-iconinfo\fIcon ;icon = 1, cursor = 2,
If fIcon=2 ;Cursor.
xHotspot=iconinfo\xHotspot
yHotspot=iconinfo\yHotspot
EndIf
;Allocate memory for a BITMAPINFO structure + a color table with 256 entries.
*bitmapinfo = AllocateMemory(SizeOf(BITMAPINFO) + SizeOf(RGBQUAD)<<8)
If *bitmapinfo = 0 : ProcedureReturn 0 :EndIf
;Get the mask (AND) bitmap, which, if the icon is B/W monochrome, contains the colour bitmap.
hbmMask=iconinfo\hbmMask
GetObject_(hbmMask, SizeOf(BITMAP),mbitmap)
mwidth= mbitmap\bmWidth
mheight= mbitmap\bmHeight
;Get the colour (XOR) bitmap.
hbmColor=iconinfo\hbmColor
If hbmColor
GetObject_(hbmColor, SizeOf(BITMAP),cbitmap)
cwidth= cbitmap\bmWidth
cheight= cbitmap\bmHeight
cbitsperpixel = cbitmap\bmBitsPixel
If cbitsperpixel = 0 : cbitsperpixel = 1 : EndIf
If cbitsperpixel < 8
colorcount=Pow(2,cbitsperpixel) ;colorcount = 0 if 8 or more bpp.
EndIf
colorplanes=cbitmap\bmplanes
Else ;Monochrome icon.
cwidth= mwidth
cheight= mheight/2
cbitsperpixel = 1
colorcount=2
colorplanes=1
mheight=cheight
EndIf
;Ready to start creating the file.
file=CreateFile(#PB_Any,filename$)
If file
;Write the data.
;word = 0
WriteWord(file,0)
;word = 1 for icon, 2 for cursor.
WriteWord(file,ficon) ;1 for icon, 2 for cursor.
;word = number of icons in file.
WriteWord(file,1) ;***CHANGE IF EXTENDING CODE TO MORE THAN ONE ICON***
;16 byte ICONDIRENTRY structure, one for each icon.
WriteByte(file, cwidth)
WriteByte(file, cheight)
WriteByte(file, colorcount)
WriteByte(file, 0) ;Reserved.
If ficon=1 ;Icon.
WriteWord(file, colorplanes) ;Should equal 1, -but just in case!
WriteWord(file, cbitsperpixel)
Else ;Cursor.
WriteWord(file, xhotspot)
WriteWord(file, yhotspot)
EndIf
WriteLong(file,0) ;TEMPORARY! WE NEED TO RETURN WHEN WE KNOW THE EXACT QUANTITY.
; Size of (InfoHeader + ANDbitmap + XORbitmap)
WriteLong(file,Loc(file)+4) ;FilePos, where InfoHeader starts
;Now the image data in the form BITMAPINFOHEADER (40 bytes) + colour map for the colour bitmap
;+ bits of colour bitmap + bits of mask bitmap. Gulp! One for each icon.
;40 byte BITMAPINFOHEADER structure.
imagebytecount=SizeOf(BITMAPINFOHEADER)
WriteLong(file, imagebytecount) ;Should be 40.
WriteLong(file, cwidth)
WriteLong(file, cheight+mheight) ;Combined heights of colour + mask images.
WriteWord(file, colorplanes) ;Should equal 1, -but just in case!
WriteWord(file, cbitsperpixel)
WriteLong(file, 0) ;Compression.
WriteLong(file, 0) ;Image size. Valid to set to zero if there's no compression.
WriteLong(file, 0) ;Unused.
WriteLong(file, 0) ;Unused.
WriteLong(file, 0) ;Unused.
WriteLong(file, 0) ;Unused.
;Colour map. Only applies for <= 8 bpp.
hdc=CreateCompatibleDC_(0) ;Needed in order to get the colour table.
If hbmColor = 0 ;Monochrome icon.
WriteLong(file, #Black)
WriteLong(file, #White)
imagebytecount+SizeOf(rgbquad)*2
ElseIf cbitsperpixel<=8 ;Includes 1 bit non-monochrome icons.
;Get colour table.
temp=Pow(2,cbitsperpixel)
bytesinrow = SizeOf(rgbquad)*temp
mem=AllocateMemory(bytesinrow)
oldbitmap=SelectObject_(hdc, hbmColor)
GetDIBColorTable_(hdc, 0, temp, mem)
WriteData(file, mem, bytesinrow) ;Write color table.
FreeMemory(mem)
SelectObject_(hdc, oldbitmap)
imagebytecount+bytesinrow
EndIf
;Now the colour image bits. We use GetDiBits_() for this.
bytesinrow = (cwidth*cbitsperpixel+31)/32*4 ;Aligned to a 4-byte boundary.
bytesinrow * cheight
mem=AllocateMemory(bytesinrow)
*bitmapinfo\bmiHeader\biSize=SizeOf(BITMAPINFOHEADER)
*bitmapinfo\bmiHeader\biWidth=cwidth
*bitmapinfo\bmiHeader\biPlanes=colorplanes
*bitmapinfo\bmiHeader\biBitCount=cbitsperpixel
If hbmColor
*bitmapinfo\bmiHeader\biHeight=cheight
GetDIBits_(hdc,hbmColor,0,cheight,mem,*bitmapinfo,#DIB_RGB_COLORS)
Else ;Monochrome color image is the bottom half of the mask image.
*bitmapinfo\bmiHeader\biHeight=2*cheight
GetDIBits_(hdc,hbmMask,0,cheight,mem,*bitmapinfo,#DIB_RGB_COLORS)
EndIf
WriteData(file, mem, bytesinrow)
FreeMemory(mem)
imagebytecount+bytesinrow
;Now the mask image bits. We use GetDiBits_() for this.
bytesinrow = (mwidth+31)/32*4 ;Aligned to a 4-byte boundary.
bytesinrow * mheight
mem=AllocateMemory(bytesinrow)
*bitmapinfo\bmiHeader\biWidth=mwidth
*bitmapinfo\bmiHeader\biPlanes=1
*bitmapinfo\bmiHeader\biBitCount=1
If hbmColor
*bitmapinfo\bmiHeader\biHeight=mheight
GetDIBits_(hdc,hbmMask,0,mheight,mem,*bitmapinfo,#DIB_RGB_COLORS)
Else
*bitmapinfo\bmiHeader\biHeight=2*mheight
GetDIBits_(hdc,hbmMask,mheight,mheight,mem,*bitmapinfo,#DIB_RGB_COLORS)
EndIf
WriteData(file, mem, bytesinrow)
FreeMemory(mem)
imagebytecount+bytesinrow
DeleteDC_(hdc)
;Finally, return to the field we missed out.
FileSeek(file, 14)
WriteLong(file, imagebytecount)
CloseFile(file)
result= 1 ;Signal everything is fine.
Else
result= 0
EndIf
DeleteObject_(hbmMask) ;These are copies created as a result of GetIconInfo_() and so require deleting.
DeleteObject_(hbmColor)
FreeMemory(*bitmapinfo)
ProcedureReturn result
EndProcedure
Code: Select all
;Test file for SaveIcon.
XIncludeFile "SaveIcon.pbi"
;Load an icon or cursor - either from file or use one of Windows default icons.
;UNCOMMENT AS APPROPRIATE.
; Define icon=LoadImage_(GetModuleHandle_(0),@"setup.ico",#IMAGE_ICON,0,0,#LR_LOADFROMFILE)
; Define cursor=LoadImage_(GetModuleHandle_(0),@"test1.cur",#IMAGE_CURSOR,0,0,#LR_LOADFROMFILE)
; Define icon=LoadIcon_(0,#IDI_ASTERISK)
; Define cursor=LoadCursor_(0,#IDC_ARROW)
;Now call the SaveIcon procedure.
;UNCOMMENT AS APPROPRIATE.
; SaveIcon(icon, "test.ico")
; SaveIcon(cursor, "test.cur")