It is currently Mon Aug 03, 2020 9:09 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Saving icons in .ico files (updated to include cursors)
PostPosted: Sat Aug 26, 2006 12:11 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
**UPDATE** Now saves cursors in .cur files as well. Just pass a handle to either an icon or a cursor along with the required filename and the code does the rest. You don't need to specify icon or cursor, it's done automatically.
***************

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:
;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


Example:
Code:
;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")


Regards.

_________________
I may look like a mule, but I'm not a complete ass.


Last edited by srod on Tue Apr 27, 2010 8:56 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 12:38 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 8140
Location: Fort Nelson, BC, Canada
Hmm. Seems to work for some icons and not others. Here's two that appear to work, but the resulting icon is just white:

http://www.networkmaestro.com/icons.zip

_________________
Veni, vidi, vici.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 12:44 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
They both work okay here!

The resulting icon for 'setup.ico' is changed to 16x16, but this will be because the original ico file contains multiple icons.

Can't explain why they work here but not on your system!

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 12:48 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 8140
Location: Fort Nelson, BC, Canada
OOPS - Sorry I seem to have spoken too soon, they show just white in my Nero PhotoSnap Viewer, where most icons show normally, but they work perfectly embedded in exe's and they show with the Windows Picture and Fax viewer. So probably no problem.

_________________
Veni, vidi, vici.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 12:58 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
I've been working from a rather old document detailing the .ico file format (11 years old!) Maybe something has changed and Nero hasn't spotted that it might be a slightly out of date format?

Mind you I found the same specification from another source as well!

I'll keep an eye out for more recent specs.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 1:21 am 
Offline
Addict
Addict

Joined: Mon May 29, 2006 1:01 am
Posts: 1965
Location: Outback
May or may not be of interest:

http://www.axialis.com/tutorials/tutori ... icons.html

(Vista icons).

Thanks for the code, srod.

_________________
Dare2 cut down to size


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 1:30 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
See the size of those icon files? 400 kb!!! Jeepers! :shock:

They look good though, and backward compatible in that our current .ico files will work no problem.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Aug 26, 2006 11:48 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
Code updated to include cursors.

See the first post.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 27, 2006 12:45 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 8140
Location: Fort Nelson, BC, Canada
Done some testing with it - so far it's working very well! Another homerun from srod :!: gotta think of a new killer lib to compete.. he's pulling ahead...

_________________
Veni, vidi, vici.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 27, 2006 12:19 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
netmaestro wrote:
Done some testing with it - so far it's working very well! Another homerun from srod :!: gotta think of a new killer lib to compete.. he's pulling ahead...


I've got to get back to my projects and stop getting distracted!!! :) Thinking of emigrating to Alaska so I can get some work done! Mind you the Bahamas sounds more attractive... not sure how much work I'd get done though? :D

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Saving icons in .ico files (updated to include cursors)
PostPosted: Sun Nov 01, 2009 5:25 pm 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 8140
Location: Fort Nelson, BC, Canada
Quote:
Warning: I have only tested with 32-bit and 1-bit monochrome bitmaps so far.

I found a boo-boo:
Code:
;Get colour table.
      temp=Pow(2,cbitsperpixel)
      bytesinrow = SizeOf(rgbquad)*temp
      mem=AllocateMemory(bytesinrow)
      GetDIBColorTable_(hdc, 0, temp, mem)     
      WriteData(file, mem, bytesinrow) ;Write color table.
      FreeMemory(mem)
      imagebytecount+bytesinrow
    EndIf

It is my opinion, after much testing, that GetIconInfo is always returning a 32bit color bitmap regardless of the image depth in the hIcon. (doesn't apply to 1-bit as there's no color bitmap at all in those) It isn't documented as such, but as I say, after much testing I found that's what it's doing. So when you save the icon, it's always saved with a 32bit color bitmap. To test my version of this, I had to forcefeed the Save procedure with an 8bit image. When I did the same test with yours, I found that it didn't work because you made a boo-boo: in order for GetDibColorTable_() to work, the bitmap has to be selected into the hdc. You aren't doing that, and so actual saving of an 8bit icon won't work. It will seem to work however, because of GetIconInfo never supplying you with an 8bit color bitmap. Thought you might be interested in knowing this bit of (hard-won) information.

_________________
Veni, vidi, vici.


Top
 Profile  
Reply with quote  
 Post subject: Re: Saving icons in .ico files (updated to include cursors)
PostPosted: Mon Nov 02, 2009 12:16 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
Then you must be using an old/corrupted version of the code netmaestro because I am indeed selecting the color bitmap into the hdc. I do not recall adding this following some bug report, though I am not ruling that out completely! :)

Code:
;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


As for the 32-bit images being returned by GetIconInfo_(). Yes I noticed that when I first created this code, but decided at the time that it was probably more to do with the icons I was feeding to the routine. If you are saying that this is a Windows peculiarity then, well, kind of makes sense that Windows would do this I guess. Does make a lot of my code pretty much redundant then! :)

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re: Saving icons in .ico files (updated to include cursors)
PostPosted: Mon Nov 02, 2009 12:57 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
My mistake, looking at the code I initially posted in this thread; I did indeed miss out the SelectObject_() etc. I must have realised the mistake some time ago, corrected the code, but not updated the code in this thread. Must be going senile in my old age! :)

Still, I haven't fixed that GetDIBits_() issue anyhow (color table being appended) and so I think I'll just remove the code. I'll repost when I get around to correcting the code.

Thanks for the heads up.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject: Re:
PostPosted: Tue Apr 27, 2010 3:05 pm 
Offline
User
User

Joined: Tue Apr 13, 2010 8:02 am
Posts: 54
srod, any chance of fixing your code and uploading it here again? I found a bug with it in 4.50 as shown here: viewtopic.php?f=13&t=42049


Top
 Profile  
Reply with quote  
 Post subject: Re: Re:
PostPosted: Tue Apr 27, 2010 8:58 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10588
Location: Beyond the pale...
Mr Coder wrote:
srod, any chance of fixing your code and uploading it here again? I found a bug with it in 4.50 as shown here: viewtopic.php?f=13&t=42049


Code updated in first post. A long standing bug has been fixed which I hope was the cause of your problem. :) If it still crashes then you will need to send me the icon you are trying to save as well as any additional code you are using etc.

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 10 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye