***************
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' be adapted so that icons 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 format. It's just a question of messing with colour tables etc.
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")