here's a code that converts a gdi+ bitmap or image object to an icon .ico file, keeping the same quality as the source image.
in a previous post, i showed the simplest way to convert a bitmap to icon, using the olecreatepictureindirect api function. with very few code we could get .ico files, but unfortunately, the results were always 4bpp (bits per pixel) images, with 16 colors. i suspect that windows didn't provide support for more than 16 colors. in a short research on the web, i found the following tip in foxyclasses tips from cetin basoz, that gives a possible reason for the 16 color icon: "here's the trick when you make your own icons: the images used must be 16 colors only. if you use 256 colors, the fox logo icon will be shown instead of your own! you must also include the correct size images in the icon". this seems to be an old tip, because in my tests vfp forms work nice with icons of more than 16 colors.
fortunately, vfp allows us to use up to 32bpp colors, so we can convert any image to an icon. the limitation is that vfp supports only 16x16 or 32x32 pixel images.
special thanks to sergey karimov
my starting point was a code from sergey karimov, that he posted in ut last year. his original code dealt with physical bmps, converting them to .ico files. but my real need was to do this without additional disk access.
the main source of .ico information that i found on the web was a great article from john hornick, in msdn, "icons in win32". to create my icons, all i had to do was to follow carefully all the instructions provided there, obviously with a great help from anatolyi mogylevetz, in some of his great articles: "retrieving information about the specified icon" and "converting image file to .ico file".
below is my bmp2ico function, that creates high quality .ico physical files. in the first part of the code below you'll find the caller program, that also resizes the image to 32x32 pixels. the caller code is very easy to understand, and you can adapt it to fit your needs. but the main part is much more complicated, because it needs to create many c structures following the instructions from "icons in win32".
local lcpict, lciconfile
lcpict = getpict("bmp")
if empty(lcpict)
return
endif
lciconfile = "c:\_" + juststem(justfname(lcpict)) + ".ico"
* make sure we have initialized the gdiplusx library
* http://www.codeplex.com/vfpx/wiki/view.aspx?title=gdiplusx
if vartype(_screen.system) <> "o"
do locfile("system.app")
endif
local lobmp as xfcbitmap
lobmp = _screen.system.drawing.bitmap.fromfile(lcpict)
local
loresized as xfcbitmap
loresized = lobmp.getthumbnailimage(32,32)
=bmp2ico(loresized, lciconfile, .t.)
return
* function: bmp2ico
* parameters: tobmp (xfcimage), tcfilename
* related articles:
* icons in win32 - by john hornick http://msdn2.microsoft.com/en-us/library/ms997538.aspx
* retrieving information about the specified icon http://www.news2news.com/vfp/?example=206&function=331
* converting image file to .ico file http://www.news2news.com/vfp/?example=503&function=331
* special thanks:
* sergey karimov, ontario, canada
* anatolyi mogylevetzfunction bmp2ico(tobmp as xfcbitmap, tcfilename as character, tlchangepixformat as boolean)
* make sure we have initialized the gdiplusx library
* http://www.codeplex.com/vfpx/wiki/view.aspx?title=gdiplusx
if vartype(_screen.system) <> "o"
do locfile("system.app")
endif
local hicon, lcbuffer, lnwidth, lnheight, lnbitsperpixel
local lhcolorbitmap, lhmaskbitmap
local lobmp as xfcbitmap
lnwidth = tobmp.width
lnheight = tobmp.height
if tlchangepixformat
* convert the original bitmap to ensure better quality and compatibility
lobmp = _screen.system.drawing.bitmap.new(tobmp, lnwidth, lnheight) && this is to transform the bitmap to 32bppargb
lnbitsperpixel = 32 && 32bpp argb
else
lobmp = tobmp
lnbitsperpixel = loresized.getpixelformatsize(loresized.pixelformat)
endif
* obtain hicon from bitmap
hicon = lobmp.gethicon()
* api declarations needed
declare short destroyicon in user32 integer hicon
declare integer geticoninfo in user32 integer hicon, string @piconinfo
declare integer getdibits in gdi32;
integer hdc, integer hbmp, integer ustartscan,;
integer cscanlines, integer lpvbits, string @lpbi, integer uusage
declare integer createcompatibledc in gdi32 integer hdc
declare integer deletedc in gdi32 integer hdc
declare integer releasedc in user32 integer hwnd, integer hdc
declare integer getwindowdc in user32 integer hwnd
declare rtlzeromemory in kernel32 as zeromemory integer dest, integer numbytes
declare integer selectobject in gdi32 integer hdc, integer hobject
declare integer deleteobject in gdi32 integer hobject
declare integer globalfree in kernel32 integer hmem
declare integer globalalloc in kernel32 integer wflags, integer dwbytes
* iconinfo structure
*| typedef struct _iconinfo {
*| bool ficon; 0:4
*| dword xhotspot; 4:4
*| dword yhotspot; 8:4
*| hbitmap hbmmask; 12:4
*| hbitmap hbmcolor; 16:4
*| } iconinfo; total bytes = 20
#define iconinfo_size 20
lcbuffer = replicate(chr(0), iconinfo_size)
= geticoninfo(hicon, @lcbuffer)
lhcolorbitmap = ctobin(substr(lcbuffer,17,4),"4rs")
lhmaskbitmap = ctobin(substr(lcbuffer,13,4),"4rs")
= destroyicon(hicon)
* dib bitmapinfoheader.
* only the following members are used: bisize, biwidth, biheight, biplanes, bibitcount, bisizeimage
*!* typedef struct tagbitmapinfoheader{
*!* dword bisize;
*!* long biwidth;
*!* long biheight;
*!* word biplanes;
*!* word bibitcount;
*!* dword bicompression;
*!* dword bisizeimage;
*!* long bixpelspermeter;
*!* long biypelspermeter;
*!* dword biclrused;
*!* dword biclrimportant;
*!* } bitmapinfoheader, *pbitmapinfoheader
#define dib_rgb_colors 0
#define rgbquad_size 4
#define bhdr_size 40
#define gmem_fixed 0
#define bi_rgb 0
local lcbihdr, lcbinfo, lcrgbquad, lnrgbquadsize, lpbitsarray, lnbitssize1, lnbitssize2
local lnbytesperscan
* obtain the xor bitmap
m.lnbytesperscan = int((m.lnwidth * m.lnbitsperpixel)/8)
if mod(m.lnbytesperscan, 4) # 0
m.lnbytesperscan = m.lnbytesperscan + 4 - mod(m.lnbytesperscan, 4)
endif
m.lnbitssize1 = m.lnheight * m.lnbytesperscan
m.lcbihdr = bintoc(bhdr_size ,"4rs") + ; && bisize
bintoc(m.lnwidth,"4rs") + ; && biwidth
bintoc(m.lnheight, "4rs") + ; && biheight
bintoc(1, "2rs") + ; && biplanes
bintoc(lnbitsperpixel, "2rs") + ; && bibitcount
bintoc(bi_rgb, "4rs") + ; && bicompression
bintoc(lnbitssize1, "4rs") + ; && bisizeimage
replicate(chr(0), 16)
if m.lnbitsperpixel <= 8
m.lnrgbquadsize = (2^m.lnbitsperpixel) * rgbquad_size
m.lcrgbquad = replicate(chr(0), m.lnrgbquadsize)
else
m.lcrgbquad = ""
endif
m.lcbinfo = m.lcbihdr + m.lcrgbquad
m.lpbitsarray = globalalloc (gmem_fixed, m.lnbitssize1)
= zeromemory (m.lpbitsarray, m.lnbitssize1)
local lhdc, lhmemdc
m.lhdc = getwindowdc(_screen.hwnd)
m.lhmemdc = createcompatibledc(m.lhdc)
= releasedc (_screen.hwnd, m.lhdc)
= selectobject(lhmemdc, lhcolorbitmap)
= getdibits (m.lhmemdc, m.lhcolorbitmap, 0, m.lnheight, m.lpbitsarray , @lcbinfo, dib_rgb_colors)
local lqcolorbinary, lqmaskbinary
m.lqcolorbinary = sys(2600, m.lpbitsarray, m.lnbitssize1)
= deleteobject (m.lhcolorbitmap)
= globalfree(m.lpbitsarray)
* obtain the and mask
* the icand member contains the bits for the monochrome and mask.
* the number of bytes in this array is determined by examining the icheader member, and assuming 1bpp.
* the dimensions of this bitmap must be the same as the dimensions of the xor mask.
* the and mask is applied to the destination using the and operation, to preserve or remove destination pixels before applying the xor mask.
local lpbitsarray2, lcbinfo2
m.lnbitssize2 = m.lnheight * int((m.lnwidth * 1)/8) && 1bpp
m.lpbitsarray2 = globalalloc (gmem_fixed, m.lnbitssize2)
= zeromemory (m.lpbitsarray2, m.lnbitssize2)
= selectobject(lhmemdc, lhmaskbitmap)
m.lcbinfo2 = bintoc(bhdr_size ,"4rs") + ; && bisize
bintoc(m.lnwidth,"4rs") + ; && biwidth
bintoc(m.lnheight, "4rs") + ; && biheight
bintoc(1, "2rs") + ; && biplanes
bintoc(1, "2rs") + ; && chr(int(1/256))) + && bibitcount
bintoc(bi_rgb, "4rs") + ; && bicompression
bintoc(lnbitssize2, "4rs") + ; && bisizeimage
replicate(chr(0), 16)
= getdibits (m.lhmemdc, m.lhmaskbitmap, 0, m.lnheight, m.lpbitsarray2 , @lcbinfo2, dib_rgb_colors)
m.lqmaskbinary = sys(2600, m.lpbitsarray2, m.lnbitssize2)
= deleteobject (m.lhmaskbitmap)
= globalfree(m.lpbitsarray2)
= deletedc (m.lhmemdc)
local lcicondir, lnoffset
*!* typedef struct
*!* {
*!* word idreserved; // reserved (must be 0)
*!* word idtype; // resource type (1 for icons)
*!* word idcount; // how many images?
*!* icondirentry identries[1]; // an entry for each image (idcount of 'em)
*!* } icondir, *lpicondir
lcicondir = bintoc(0, "2rs") + ; && 0 reserved
bintoc(1, "2rs") + ; && 2 type
bintoc(1, "2rs") && 4 number of icons in this file
lnoffset = len(lcicondir)+16
*!* typedef struct
*!* {
*!* byte bwidth; // width, in pixels, of the image
*!* byte bheight; // height, in pixels, of the image
*!* byte bcolorcount; // number of colors in image (0 if >=8bpp)
*!* byte breserved; // reserved ( must be 0)
*!* word wplanes; // color planes
*!* word wbitcount; // bits per pixel
*!* dword dwbytesinres; // how many bytes in this resource?
*!* dword dwimageoffset; // where in the file is this image?
*!* } icondirentry, *lpicondirentry
local lncolors, lcicondirentry
lncolors = iif(lnbitsperpixel > 8, 0, 4*2^lnbitsperpixel)
lcicondirentry = chr(lnwidth) + ; && 0 width of the image, in pixels
chr(lnheight) + ; && 1 height of image, in pixels (or & and bmps)
chr(lncolors) + ; && 2 number of colors in image (0 if >=8bpp)
chr(0) + ; && 3 reserved
bintoc(1,"2rs") + ; && 4 number of planes
bintoc(lnbitsperpixel,"2rs") && 6 bits per pixel
lcbinfo = stuff(lcbinfo,9,1,chr(lnheight*2)) && height of img, in pixels (or & and bmps)
&& the biheight member of the icheader structure represents the combined height of the xor and and masks
lcbinfo = stuff(lcbinfo,21,4, bintoc((lnbitssize1 + lnbitssize2), "4rs")) && size of image (or & and bitmaps)
*!* typdef struct
*!* {
*!* bitmapinfoheader icheader; // dib header
*!* rgbquad iccolors[1]; // color table
*!* byte icxor[1]; // dib bits for xor mask
*!* byte icand[1]; // dib bits for and mask
*!* } iconimage, *lpiconimage
lqbinary = lcbinfo + lqcolorbinary + lqmaskbinary
lcicondirentry = lcicondirentry + bintoc(len(lqbinary),"4rs") + ; && 8 size of img area
bintoc(lnoffset,"4rs") && 12 offset to image area
* finally, save the icon to the disk
* this still needs some error checking !
local lhfile
lhfile= fcreate(tcfilename)
if lhfile<1
=messagebox("cannot create file " + tcfilename + " !")
return .f.
endif
=fwrite(lhfile, (lcicondir + lcicondirentry))
=fwrite(lhfile, lqbinary)
=fclose(lhfile)
return