2008-12-15

Convert your buttons to BMPs keeping transparency with GdiPlusX - REVISITED

this is just an update for an old post with the same title. erik gomez and russel campbell had some problems on the conversions of some specific png images, so i recoded this function, this time using a safer code.


new update:


thanks to bernard bout and craig boyd, i've updated the code below. in fact it contained a small bug, that was not adjusting the white colors. now it seems to be working nice. thanks !


 


 


this runs a little bit slower than the other aproach, but in my tests, the success was 100%.


 


the function below converts any button image to a bmp to be used in vfp forms.


there are lots of cool and free icons available on the web, but the vast majority are in .ico, gif or png image formats, that are not very familiar and reliable to be used in vfp. for us, the best image format, for a lot of reasons, is the bmp format.


some transformations are needed to make this bmp to show exactly how we desire, specially when converting source images in a png, gif or ico formats.


vfp shows the pure white - rgb(255,255,255) as transparent in our buttons and image objects. the code below first converts the original whites to rgb(254,254,254) that is visually the same, but does not become transparent, and eliminates the need to create a mask image (.msk) and next, converts the background color of the original bitmap to pure white, that will show transparent in vfp forms.


for more details, please check these prior posts:


bmps with transparent backgrounds


how to put one image over another in a form


 


important
requires vfp9 and gdiplusx to run. 
please make sure that you have the latest version, because this sample may be using some functions that were added or fixed recently.
http://www.codeplex.com/vfpx/wiki/view.aspx?title=gdiplusx&referringtitle=home


 


save the program below as button2bmp.prg, and call it this way:


button2bmp(getpict(), "c:\newicon.bmp")


when you compile this program in your executable, please don't forget to remove the locfile() command, and just use a do system.app instead


 


lparameters tcsourcefile, tcdestfile

* tcsourcefile = getpict()
* tcdestfile = forceext(tcsourcefile, "bmp")


do locfile("system.app")

local lobmp as xfcbitmap
local logfx as xfcgraphics
local loborderclr as xfccolor
local lorect as xfcrectangle
local loattr as xfcimageattributes
local locolormap as xfccolormap
local lodestbmp as xfcbitmap

with _screen.system.drawing
locolormap = .imaging.colormap.new()
loattr = .imaging.imageattributes.new()
lobmp = .bitmap.fromfile(tcsourcefile)
logfx = .graphics.fromimage(lobmp)

lodestbmp = .bitmap.new(lobmp.width, lobmp.height, .imaging.pixelformat.format24bpprgb)
lodestgfx = .graphics.fromimage(lodestbmp)

* clear the new bitmap
lodestgfx.clear(.color.white)

* by craig boyd - for enhancing the smoothless and quality
lodestgfx.smoothingmode = .drawing2d.smoothingmode.highquality
lodestgfx.interpolationmode = .drawing2d.interpolationmode.highqualitybicubic
lodestgfx.pixeloffsetmode = .drawing2d.pixeloffsetmode.highquality

lorect = lobmp.getbounds()

* get the top left pixel color, presuming this color is the background color to become transparent
* for our bmp case, this will become pure white - rgb(255,255,255)
* that becomes transparent when used in vfp objects
loborderclr = lobmp.getpixel(0,0)

* convert original whites rgb(255,255,255) to off white - rgb(254,254,254)
* this way, the whites will remain without the need of a mask
locolormap.oldcolor = .color.white
locolormap.newcolor = .color.fromargb(255,254,254,254)

loattr.setremaptable(locolormap)
lodestgfx.drawimage(lobmp, lorect, lorect, .graphicsunit.pixel, loattr)

* next step, convert the borders to pure white, rgb(255,255,255) that will become transparent in buttons
locolormap.oldcolor = loborderclr
locolormap.newcolor = .color.white
loattr.setremaptable(locolormap)

lodestgfx.drawimage(lodestbmp, lorect, lorect, .graphicsunit.pixel, loattr)

lodestbmp.save(tcdestfile, .imaging.imageformat.bmp)
endwith

 


 

5 comments:

  1. Hi Cesar. This is great.
    Hi Bernard, Thanks for your comments

    Some comments:

    1. Why call it with Button2Bmp(GETPICT(), "c:\NewIcon.bmp"). I mean why  GETPICT() when the first line of the PROCEDURE also has a GETPICT() which is called even if you pass in a file name to process?
    hehe, didatic purposes :-)

    2. loColorMap.NewColor = .Color.FromARGB(255,254,254,254)

    Is there any need for .FromARGB when the A = 255? Isn't it simpler to use .FromRGB(254,254,254) especially since the new BMP will not be transparent anyway?
    Nope ! That's just a preference of usage. Both ways work at the same speed. All color objects must be converted to Integer ARGB values before being passed to GdiPlus.dll

    Otherwise this is great once I wrapped it in a batch process to handle a complete directory of PNG's. Doing them 1 by 1 is a pain.

    Also it would be useful if the code opened <b>any</b> VFP common image type - BMP, JPG, GIF processed and then saved them as BMP's. Especially for BMP's this would mean that once converted I could get rid of all the .MSK files that I used with my BMP's as they would not be needed any more.

    If you don't make this change then I will have to make it myself.  :)

    Well, I presume that this works nice with BMPs the way it is now. For GIFs, you should first get the transparent color. Fortunately, you'll find 2 samples at the GdiPlusX Demo project.
    For JPEGs it's completely different. As you probably know, White colors are never pure whites in JPEGs. Its encoder uses some similar colors during the compression process, so it's much more difficult to identify which of the whites should become transparent and which not; IMO, JPGs are not to be used for Buttons.
    Anyway, if you insist, in the GdiPlusX samples there's a sample that changes some colors in a JPG. GDI+ allows you to select a kind of range of colors to be transformed. It uses the ImageAttributes class, with the method "SetColorKey()" - http://msdn.microsoft.com/en-us/library/e7755txx.aspx .

    ReplyDelete
  2. Cesar

    This code will also not work if the top left pixel is WHITE. It only will work if the TOP LEFT is non white.

    In that case it cannot be used with a BMP/JPG/GIF to convert them as it will leave the whites as "holes".

    This will also only work on transparent background PNG's and then too, only if there is no WHITE in the PNG. Otherwise it will make holes.
    Hi Bernard,
    I've just updated the codes in order to fix the issues you reported. Thanks !
     

    ReplyDelete
  3. Great job Cesar. Thank you for posting this updated code. Works like a charm at this end.

    ReplyDelete
  4. Hi Cesar. Thanks for taking time to help. The code really works like a charm (as Craig has put it). It has never failed me so far. I have converted around 1000+ small PNG's to pure white background BMP's also using several matrices. It does it's job without a hitch. Thanks again and more power. Keep up the good work.
    Hi Erik,
    Thanks a lot for your comments and for testing the function before I published.
    Best regards
    Cesar

    ReplyDelete
  5. occur error :

    when source file is :  home(4) + [Bitmaps\Offctlbr\Small\B&w\new.bmp]


    error message:


    Program Error

    Cancel

    Suspend

    Ignore

    Help

    LOGFX is not an object.



    ReplyDelete