2007-07-13

Convert BMP to ICON - Part 1

it is known that gdi+ does not bring full support to icon files. so, we can't make some simple conversions to create .ico files directly.


but there are some easy tricks that we can use to do that. the main step is to retrieve a icon handle from our bitmap, the "hicon".


the simplest aproach is to use the olecreatepictureindirect api to obtain a ole picture object reference. next step is to send this object to the savepicture() function to save it to disk.


but the problem of this technique is that the icons are generated at a lousy quality, 4bpp ( 4 bits per pixel ). this means that the se icons can have at most 2^4 = 16 colors, that brings really ugly and bad quality results. 


 


below is the code:


 

important
requires vfp9 and gdiplusx to run. 
please make sure that you have the latest version!
http://www.codeplex.com/vfpx/wiki/view.aspx?title=gdiplusx&referringtitle=home

 


* api declarations
declare short destroyicon in user32 integer hicon
declare long olecreatepictureindirect in oleaut32 ;
   string @pictdesc , string @riid , long own , object @obj


* initialize gdiplusx
_screen.addproperty("system", newobject("xfcsystem", locfile("system.vcx")))


local lcpict, lciconfile
lcpict =
getpict("bmp")
lciconfile = "c:\" +
juststem(lcpict) + ".ico"


with _screen.system.drawing
   local lhicon
   local lobmp as xfcbitmap
   lobmp = .
bitmap.fromfile(lcpict)
   lhicon = lobmp.gethicon()
endwith
 
if
lhicon # 0 
   *!* typedef struct tagpictdesc 
   *!* { 
   *!* uint cbsizeofstruct; 
   *!* uint pictype; 
   *!* hicon hicon; 
   *!* } icon; 
   *!* struct 
   *!* { 
   *!* henhmetafile hemf; 
   *!* } emf; 
   *!* } ; 
   *!* } pictdesc;


   #define pictype_icon 3
   #
define guid_icon2 0h0004020000000000c000000000000046 && "{00020400-0000-0000-c000-000000000046}"
   #define guid_icon 0h8109f87b32bf1a108bbb00aa00300cab && "{7bf80981-bf32-101a-8bbb-00aa00300cab}"


   local lcpictdesc, lqguid, loiconobj
   lcpictdesc =
bintoc(16,"4rs") + ; && size of structure
      bintoc(pictype_icon, "4rs") + ; && type of image
      bintoc(lhicon, "4rs") + ; && image handle
      bintoc(0, "4rs")


   lqguid = guid_icon
   loiconobj = 0 

   *!* http://msdn2.microsoft.com/en-us/library/ms694511.aspx
   *!* stdapi olecreatepictureindirect( 
   *!* pictdesc* ppictdesc, //pointer to the structure of parameters for picture
   *!* refiid riid, //reference to the identifier of the interface
   *!* bool fown, //whether the picture is to be destroyed
   *!* if true, the picture object is to destroy its picture when the object is destroyed. if false, the caller is responsible for destroying the picture.
   *!* void** ppvobj //address of output variable that receives the 
   *!* // interface pointer requested in riid
   *!* );


   * create the picture ole object
   olecreatepictureindirect(@lcpictdesc, @lqguid, 1, @loiconobj)


   if vartype(loiconobj) = 'o'
      * save the picture using vfp native function 'savepicture()' 
      if savepicture(loiconobj, lciconfile) 
         messagebox('icon created successfully !', 64) 
      endif 
   else 
   messagebox
('olecreatepictureindirect() error', 16)
endif

* free the memory handle allocated for the hicon
= destroyicon(lhicon)
endif
return



 


















original picture .bmp       icon picture .ico (16 colors)
      
      
      
      



 


you can try it by yourself without running this code: open mspaint, and load one of the four images on the left. then, select to save the image as a 16 color bitmap, and you'll obtain exactly the same result that olecreatepictureindirect generated. it will use only the color palette below to convert the image.



so, as the result isn't as good as desired, why did i post these codes here ?


that's because the original .net "system.drawing" library uses a similar aproach as the shown above to create icons, in the icon.save() method.


in the next post i'll show how we can convert our bitmaps to high quality icons, but using some good and old gdi ( not gdi+) techniques. the code wont be simple and short as the shown here, but the performance will be similar and the quality will be perfect !

4 comments:

  1. I'm looking forward to trying that Cesar. By co-incidence, I was going to ask about that.

    GDI is a fascinating subject.

    ReplyDelete
  2. Este articulo estra traducido al español en PortalFox en:


    -- Convertir BMP a ICONO - Parte 1 --

    http://www.portalfox.com/article.php?sid=2480

    ReplyDelete
  3. Here's a challenge: How would you convert a bitmap to a metafile? Is it possible?
    Hi Tod,
    Yep ! Gdi+ brings some functions to deal with metafiles, but I haven't tried them yet. Anyway, Bo Durban has already tried with success, you can check this here:
    http://blog.moxiedata.com/PermaLink,guid,00a6d6f7-ca4b-4269-9e2d-1093559b3bbe.aspx
    Regards
    Cesar

    ReplyDelete
  4. Thats great! I'll get a chance to meet Bo next month in Atlanta for Fox Forward.


    ReplyDelete