***************
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")


