Foxite.COM Community Weblog

Foxite.COM Community Weblog - free weblog service for the Visual FoxPro Community.
Welcome to Foxite.COM Community Weblog Sign in | Join | Help
in
Home Blogs Forum Photos Forum Archives

VFP IMAGING



  • Crop Images with GdiPlusX

    Cropping an image is super simple using GdiPlusX

    Prerequisites
    Visual FoxPro 9 and the GdiPlusX library from VFPX 

    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

     

    The cropping is done by the fuction "Clone()" from the Bitmap class. All we need is to pass a Rectangle object containing the X, Y, Width and Height of the desired image to be cropped.

    Original Image 

    Top Left  

                                    Center 

                                                       Bottom Right 

    Run the code below, selecting any image, and you will see the image cropped in three ways: the top-left part of the image, the bottom-right part, and the center.


    LOCAL lcSource, lnWidth, lnHeight
    lcSource =
    GETPICT
    ()
    IF EMPTY
    (lcSource)
       RETURN
    ENDIF

    DO LOCFILE
    ("System.App")

    WITH _SCREEN.System.Drawing
     
    * Load Image to GdiplusX
    LOCAL loBmp AS
    xfcBitmap
    loBmp = .
    Bitmap
    .FromFile(lcSource)
    lnWidth = loBmp.
    Width
    lnHeight = loBmp.Height

    * Crop Image
    LOCAL loCropped AS xfcBitmap

    * Crop Top-Left
    LOCAL loRect as xfcRectangle
    loRect = .Rectangle.New(0, 0, lnWidth / 2, lnHeight /2)
    loCropped = loBmp.Clone(loRect)
    loCropped.
    Save("c:\Crop-TopLeft.png", .Imaging.ImageFormat.Png)
    RUN /N explorer.EXE c:\Crop-TopLeft.png
     
    * Crop Bottom-Right
    * Now, the Rectangle region will be created inside the Clone function
    loCropped = loBmp.Clone(.Rectangle.New(lnWidth / 2, lnHeight /2, lnWidth /2, lnHeight /2))
    loCropped.
    Save("c:\Crop-BottomRight.png", .Imaging.ImageFormat.Png)
    RUN /N explorer.EXE c:\Crop-bottomright.png
     
    * Crop Center
    loCropped = loBmp.Clone(.Rectangle.New(lnWidth / 4, lnHeight /4, lnWidth /2, lnHeight /2))
    loCropped.
    Save("c:\Crop-Center.png", .Imaging.ImageFormat.Png)
    RUN /N explorer.EXE c:\Crop-Center.png
     
    ENDWITH
    RETURN
  • FIX: The toolbar on an SDI form is disabled in Visual FoxPro 9.0 Service Pack 2

    One fix for VFP9 SP2 has just been released:

    FIX: The toolbar on an SDI form is disabled in Visual FoxPro 9.0 Service Pack 2

    http://support.microsoft.com/kb/948528/en-us

     

    Good news is when they say: "This hotfix may receive additional testing. Therefore, if you are not severely affected by this problem, we recommend that you wait for the next Visual FoxPro 9.0 service pack that contains this hotfix."

  • FoxCharts 0.14 ALPHA released !

    I'm happy to announce that FoxCharts has recently been accepted as a VFPX project.

    Today I've just uploaded to VFPX at CodePlex a new version of FoxCharts. This is version 0.14 - still ALPHA - NOT FOR PRODUCTION !

    Prerequisites:
    Visual FoxPro 9 and the GdiPlusX library from VFPX 

    First of all I have to thank you all for your kind comments and feedback you provided in my last post. I had never expected to receive so many comments, in this blog, at the other VFP forums and by email. I really apreciated that. Be sure that your feedback resulted in a superpowered motivation to improve it.

    It is also important to tell everybody of the importance that both Bo Durban and Craig Boyd have in this project. They are the menthors and fathers of GdiPlusX. Without GdiPlusX this project would become almost impossible. If I had to use any other of the Gdi+ classes available for VFP, for sure it would have taken much more time - Believe-me, I've tried them all.

    Using GdiPlusX I can be indeed much more productive. Once you understand how it works, things become really easy and simple.

    At first sight, when I saw one of the first versions that Bo and Craig designed for GdiPlusX I was really amazed, and I said to myself - I have to go deeper into this.

    Bo Durban has also provided important help, providing important suggestions, support and testing. Thanks also to EmersonReed, Luis Maria Guayan and Leandro Walfrans for your tips and support.

    This version also distributes the two main files from GdiPlusX - System.app and GdiPlusX.vcx.
    If you are already a GdiPlusX user, you can use your own GdiPlusX version.
    Just make sure to be using the latest version.

    This new release contains lots of enhancements:

    Chart Types
    - Bar charts
    - Multiple bars
    - Stacked bars

    - Pie
    - Doughnut

    - Lines
    - Area
    - Points and Shapes


    Color variations:
    - Basic colors
    - Custom colors
    - Gradient colors
    - Random colors
    - Monochrome
    - Gradient or solid colors


    Legends in many places: Axys, shapes, side legends

    Scales - automatic or customized

    Customize:
    - Titles
    - Subtitles
    - Backgrounds (solid or gradient)
    - Fonts
    - Colors

    The sample form shows most of the features that are available. Run the form "ChartsSample.scx" and play changing the various properties, generating some cool charts like the ones below.

    All the relevant codes are in the INIT() event of the sample form. With very few lines of code you can create super cool and beautiful charts.

    This sample also includes a report sample. Check the codes in the command button that generates a report to see one of the various ways we have to print our charts.

     

    Important to remember that this is still an ALPHA version, and is not recommended to be used in production although it works nice.

    Some important improvements still need to be applied, specially in the class initialization. The codes also need to be optimized. My priority was in obtaining the results, and in many places the performance can be improved.

    If you have any tips, or can provide suggestions, or fixes, you are most welcome !

    Enjoy !

    Download the latest version directly from the FoxCharts page on VFPX:

    http://www.codeplex.com/VFPX/Wiki/View.aspx?title=FoxCharts&referringTitle=Home

    The page for the latest release 0.14 Alpha:

    http://www.codeplex.com/VFPX/Release/ProjectReleases.aspx?ReleaseId=12847

     

















     




     


     


     


     
      

     
     




     

    Here are the properties that are currently provided in FoxCharts, that you can use to improve your charts:

    SourceAlias Character, the name of the alias that contains the needed fields that will create the chart
    FieldValue1 Character, the field name of the cursor that contains the numeric values that contain the values that will create the chart.
    FieldColor Character, the field name of the cursor that contains the RGB values of the custom colors for the chart
    FieldDetachSlice Character, the field name of the cursor that contains the logical values that tell if the slice of the Pie or Donut chart will be detached or not
    FieldHideSlice Character, the field name of the cursor that contains the logical values that tell if the slice of the Pie or Donut chart will be hidden or not
    FieldLegend Character, the field name of the cursor that contains the character values that contain the main legends of the Pie or Donut charts
    FieldLegend2 Character, the field name of the cursor that contains the character values that contain the secondary legends of the chart that will be drawn inside the slice, or point or bar.
    FieldXAxys Character, the name of the field that contains the text to be drawn in the X axys
    Color1 Numeric, the RGB value of the color from the first chart
    Legend1 Character, the Legend text for the first sequence of values
    ChartType Numeric, type of chart: 1 = Pie ; 2 = Donut ; 3 = Unspecified ; 4 = Point ; 5 = Line ;  6 = Area ; 7 = Simple Bar ; 8 = Multiple Bars ; 9 = Stacked Bars
    ChartsCount Numeric, the number of Value sources
    FontName Character, the name of the font to be used to display text
    TitleFontSize Numeric, the font size for the main title
    TitleCaption Character, the title caption
    TitleForeColor Numeric, the title and subtitle forecolor RGB value
    SubTitleCaption Character, the subtitle caption
    SubTitleFontSize Numeric, the subtitle font size
    ShowLegend Logical, Shows the side legends
    LegendFontSize Numeric, the fontsize for the side legends
    LegendForeColor Numeric, the legend fore color RGB value
    ShowValuesonShapes Logical, determines if the values will be drawn inside the shapes of the chart
    LegendOnShapeColor Numeric, the RGB value for the shape color
    LegendOnShapeFontSize Numeric, the font size for legends on shape
    ShowAxys Logical, for Line, Area or Point charts - determines if the X and Y axys will be drawn
    AxysXCaption Character, the main text for the X axys
    AxysYCaption Character, the main text for the Y axys
    AxysColor Numeric, the RGB value for the Axys main color
    BackColor Numeric, RGB value of the main backcolor
    BackColor2 Numeric, RGB value of the secondary (destination) backcolor. This is used to create gradients, together with "BackColor"
    BackGradientMode Numeric, if gradient background (having BackColor2 specified) - 0 - horizontal; 1 - vertical; 2 - diagonal1 ; 3 - diagonal 2
    ShowScale Logical, determines if the scale in the Y axys will be shown
    Scale Numeric, the scale value for the Y axys; 0 = Automatic scale
    ScaleBackType Numeric, the background scale type; 0 = none; 1 = lines; 2 = rectangle
    ColorType Numeric, the type of colors of the chart: 0 = Basic Colors   1 = Custom (default)   2 = Random   3 = Scale of Gradients
    BrushType Numeric, type of brush used to fill the chart: 1 - Solid Colors; 2 - Gradient Colors; 3 - Monochrome Hatch brush
    GradientLevel Numeric, for gradient brush mode (-10 = destination black; 0 = solid color; +10 destination white)
    AlphaChannel Numeric, 0-255 Determines the level of the transparency level; 255 = Opaque; 0 = transparent
    _3D Numeric, the quantity of pixels that create the 3D effect (0 = plain)
    Margin Numeric, specifies the margin width created in the text portion of the control.
    DonutRatio Numeric, For Donut chart - the width of the donut related to its size ( 0.01 = full slice ; 0.99 = thin)
    LineCaps Logical, for the case of plain line chart, shows rounded caps in each point.
    LineShape
    PointsShape Numeric, the shape to be used in a points chart: 0 = Round; 1 = Square; 2 = Triangle; 3 = Cross; 4 = Star; 5 = Man; 98 = GDI+X Path object; 99 = Custom Image
    PointsShapeWidth Numeric, for Point chart, determines the width of the pen that will draw the shapes
    PieDetachPixels Numeric, for Pie and Donut charts, the quantity of pixels to detach from center
    BarsSpaceBetween Numeric, for bars chart - the distance in pixels between bars
    Area3DTop Logical, when true, a line will be drawn on the top of the 3D Area chat
    MultiChart Logical, determines if more than one kind of chart will run at the same time

     

     

  • How to disable print button in report preview

    Till now I had seen many samples showing how we can make the Print button invisible from the Report Preview toolbar, but I had never seen the possibility to show it disabled.
    Here's a sample, totally based in Emerson Reed's, from an old post in his great blog: A sample on how to add features to Report Listener - http://weblogs.foxite.com/emersonreed/archive/2006/09/13/A_sample_on_how_to_add_features_to_Report_Listener.aspx

    It brings the possibility to hide completely the Print button or to disable it - just set the properties "PrintButtonVisible" and "PrintButtonEnabled". Apart from this, it also allows to change the tooltips from the buttons. For my case this is really helpful, in order to have the translated tooltips to my native language.

    I've only added 7 or 8 lines of code all the sample comes from Emerson.
    It was tricky to set the "enabled" property of the print button, because there's no property that allows us to set this, differently from the "Visible", that was allowed, using the property "AllowPrintfromPreview".
    Even setting the property "Enabled" directly in the ExtensionHandler class below, the button still appeared enabled.

    .cmdPrint.Enabled = .F. && Did not work

    My solution was to use BINDEVENT, in order to control the behavior whenever the report previewer tried to change the "Enabled" property:

    BINDEVENT(THIS.PreviewForm.Toolbar.CmdPrint,"Enabled",This,"Disabled",1)
     
    Then, I added a custom function "Disabled", that forces the Print button to appear disabled:
     
    PROCEDURE Disabled
       THIS
    .PreviewForm.Toolbar.cmdPrint.Enabled = .F.
    ENDPROC

     

     
    * See "Leveraging the default preview container" topic in HELP for more info
    * Report Listener based on Emerson Reed's sample from
    *
    http://weblogs.foxite.com/emersonreed/archive/2006/09/13/A_sample_on_how_to_add_features_to_Report_Listener.aspx

    * Create a Report Listener object
    Local loReportListener
    loReportListener =
    Newobject("MyReportListener")
    With loReportListener
       .
    ListenerType = 1 && Preview
       .PrintButtonVisible = .T.
       .PrintButtonEnabled = .F.
    Endwith
     
    * Run a report from the samples of VFP using the new report engine (object-assisted output)
    REPORT FORM ;
       HOME() + 'Samples\Solution\Reports\colors.frx' ;
       OBJECT loReportListener
    RETURN
     
     
     
    * Custom Report Listener that adds some features
    Define Class MyReportListener As FXLISTENER Of Addbs(Home()) + "FFC\_ReportListener.VCX"

    * Public properties
    PrintButtonVisible = .T.
    PrintButtonEnabled = .T.
    *
    Procedure LoadReport
       DoDefault
    ()
       With This
          If
    .ListenerType==1 And Not Vartype(.PreviewContainer)=="O"
             .ExtendPreviewContainer()
          Endif
       Endwith
    Endproc
    *
    Function ExtendPreviewContainer
       Local loPreviewContainer
       loPreviewContainer =
    Null
       Do
    (_ReportPreview) With loPreviewContainer
       loPreviewContainer.AllowPrintfromPreview =
    This.PrintButtonVisible

       LOCAL loExtHandler
       loExtHandler =
    CREATEOBJECT("MyExtensionHandler")
       loExtHandler.DisablePrintButton = NOT
    This.PrintButtonEnabled

       loPreviewContainer.SetExtensionHandler(loExtHandler)
       This.PreviewContainer = loPreviewContainer
    Endfunc
    Enddefine


    * Create a class that will extend Report Preview
    Define Class MyExtensionHandler As Custom
       DisablePrintButton = .F.

    Procedure Show(iStyle)
    With This.PreviewForm
       With .Toolbar
       * Translate toolbar buttons ToolTips to Brazilian Portuguese language
       .cboZoom.ToolTipText = "Zoom"
       .cmdClose.
    ToolTipText = "Fechar a visualização"
       .cmdGoToPage.
    ToolTipText = "Ir para a página"
       .cmdPrint.
    ToolTipText = "Imprimir"
     
       IF This.DisablePrintButton
          * BINDEVENT(THIS.PreviewForm.Toolbar.CmdPrint, "Visible", This, "Invisible", 1)

          * Here we control the ENABLED property
          BINDEVENT(THIS.PreviewForm.Toolbar.CmdPrint, "Enabled", This, "Disabled", 1)
       ENDIF

       With
    .cntNext
          .cmdBottom.
    ToolTipText = "Última página"
          .cmdForward.
    ToolTipText = "Próxima página"
       Endwith

       With .cntPrev
          .cmdBack.
    ToolTipText = "Página anterior"
          .cmdTop.
    ToolTipText = "Primeira página"
       Endwith

       With .opgPageCount
          .opt1.
    ToolTipText = "Uma página"
          .opt2.
    ToolTipText = "Duas páginas"
          .opt3.
    ToolTipText = "Quatro páginas"
       Endwith
    *
       Endwith
       .WindowState = 2 && Maximize report preview
       Endwith
       DoDefault
    (iStyle)

    ENDPROC

    * Here is the relevant code to disable the button
    PROCEDURE Disabled
       THIS
    .PreviewForm.Toolbar.cmdPrint.Enabled = .F.
    ENDPROC
    *
    Enddefine

  • FoxCharts !!!

    Wanna create some cool charts in VFP ?


    With no ActiveX controls, dlls, or 3rd party products ?


    What do you think of these ?


     

     

     

    Recently, I’ve seen many discussions about people asking for charts components.
    Since GdiPlusX brings us all these possibilities, I thought it would  be worth to start a project regarding this.


     

    FoxCharts is a subclass of the ImageCanvas class from GdiPlusX, that allows us to direct draw in an image object, among many other cool and useful features, that are not in the scope of this post.


    Goals of FoxCharts:
    - Create good looking and modern charts in pure VFP
    - NO ActiveX components
    - Easy to setup
    - Easy to customize.
    - Easy to save to disk or print
    - Open Source
    - Benefit from all the GdiPlusX drawing capabilities, allowing users to modify the charts the way they like.
    - Save as EMF, resulting in perfect charts when printed in VFP reports


    Prerequisites:
    Visual FoxPro 9 and the GdiPlusX library from VFPX 


    In the source code you'll find a sample form, “NewChart.Scx” that allows to create different kinds of charts using this class.

    Please note that as it is still in Alpha version, the codes that create the charts are still in the sample form. GpCharts.vcx is (at this moment) just a holder that contains the PEMs needed to draw. In development mode, double click the ImageCanvas object to see the source code that generates these charts.

    The codes that will instantiate GpCharts reside in the INIT() event of the sample form, only there.

     

    Currently available:
    Bars, Lines, Area, Pie and Donut charts, in various color variations, using gradients, custom colors, basic and random colors.
    Titles, subtitles and legends

     

    To do:
    Enhance the data filling, create a builder, allow different kinds of charts to appear together (eg. Lines and bars). Legends in the Y and X axys.

    And obviously:
    Fix some bugs !


    If you are interested in developing and enhancing FoxCharts, feel free to post a comment here.
    Your suggestions, tips, critics, testing and bugs hunting will be most appreciated !

     

    Download directly from the FoxCharts page in VFPX

    http://www.codeplex.com/VFPX/Wiki/View.aspx?title=FoxCharts&referringTitle=Home



     

     


     













  • What is the best image format for VFP

    Carlos alloatti has just published a new and very interesting article about image formats, I strongly recommend people to have a look at this new great work from Carlos.

    http://www.ctl32.com.ar/articles/what_is_the_best_image_format_for_VFP.asp

     

    Also, don't miss to visit his CTL32 site, full of really cool and useful classes:

    http://www.ctl32.com.ar

     

     

  • A great Color Picker

    Recently MVP Cetin Basoz posted a great script that generates an excellent color picker. This is so good and useful, that I added it to my "Tools" menu during development.

    Thanks Cetin !

    If you're interested, you can download it from here:

    http://www.foxite.com/downloads/default.aspx?id=179

     

  • Convert BMP to ICO - Part 3

    Below are 4 simple ways to convert a BMP to ICON, using GdiPlusX.
    Those 2 blog posts were before we updated the library, adding the support for saving Icons with good quality.

    This sample uses 4 techniques, and creates 4 versions of ICONS from the same image file.
    Before the conversion, it resizes the source image to the size of 16x16. This means that with this sample you can convert any image to ICO file.

    In the next release, we hope to deliver a complete solution for .ICO files too, with very simplified code, offering great ICO files support, that is not present in the .NET version, thanks to Carlos Alloatti.

     

    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

     

    Do Locfile("system.prg")

    With _Screen.System.Drawing As xfcDrawing * Convert the original bitmap to ensure better quality and compatibility
       loResized = .Bitmap.New(.Bitmap.FromFile(Getpict()), 16,16)

    * Create Icon Object
        Local loIcon As xfcIcon
        loIcon = .Icon.FromHandle(loResized.GetHicon())

    *** LOW QUALITY ICONS

    * Save sending FileName
        loIcon.Save("c:\Icon_Save_FileName_LowQual.ico")

    * Save Using Stream
        Local loStream As xfcMemoryStream
        loStream = _Screen.System.IO.MemoryStream.New()

        loIcon.Save(loStream)
        Strtofile(loStream.GetBuffer(), "c:\Icon_Save_Stream_LowQual.Ico")


    *** HIGH QUALITY ICONS
    *** Setting the tlQuality flag to .T.

    * Save sending FileName
        loIcon.Save("c:\Icon_Save_FileName_HighQual.ico", .T.)

    * Save Using Stream
        Local loStream2 As xfcMemoryStream
        loStream2 = _Screen.System.IO.MemoryStream.New()

        loIcon.Save(loStream2, .T.)
        Strtofile(loStream2.GetBuffer(), "c:\Icon_Save_Stream_HighQual.Ico")

    Endwith

     

  • How to create text as image file with GdiPlusX

    UPDATED: Fixed Image Size, Thanks to Christof Wollenhaupt

     

    More than once I've seen people asking to create images containing some text. The sample below is really very simple.

    • Creates a font
    • Measures the space that the text will need
    • Creates an image with the needed size
    • Draws the string
    • Saves to disk

    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

     

    DO LOCFILE("System.prg")

    WITH _SCREEN.System.Drawing
       LOCAL lcText
       lcText = "GdiPlusX is Cool !!!"

       * Create a Font
       LOCAL loFont as xfcFont
       loFont =
    _screen.system.Drawing.Font.New("Verdana", 32, .FontStyle.BoldItalic)

       LOCAL loTmpBmp as xfcBitmap
       loTmpBmp = .
    Bitmap.New(1,1)

       * Retrieve the graphics object.
       LOCAL loTmpGfx AS xfcGraphics
       loTmpGfx = .Graphics.FromImage(loTmpBmp)

       * Measure the String
       * Get the size required for our text
       LOCAL loSize as xfcSize
       loSize = loTmpGfx.MeasureString(lcText, loFont)

       LOCAL loNewBmp as xfcBitmap
       loNewBmp = .
    Bitmap.New(loSize.Ceiling)

       LOCAL loNewGfx as xfcGraphics
       loNewGfx = .Graphics.FromImage(loNewBmp)

       * Clear the background to Yellow
       loNewGfx.Clear(.Color.Yellow)

       * Create a solid brush
       LOCAL loBrush as xfcSolidBrush
       loBrush = .SolidBrush.New(.
    Color.FromRGB(255,0,0)) && Red

       * Create an StringFormat object in order to draw the sting centered in the image
       LOCAL loStringFmt as xfcStringFormat
       loStringFmt = .StringFormat.New()
       loStringFmt.
    Alignment = .StringAlignment.Center

       * Create a Rectangle with the measures of the Bitmap
       LOCAL loRect as xfcRectangleF
       loRect = loNewBmp.GetBounds()

       * Draw the String
       loNewGfx.DrawString(lcText, loFont, loBrush, loRect, loStringFmt)

       * Finally save the image
       loNewBmp.Save("c:\MyText.Png", .Imaging.ImageFormat.Png)

       * Show the image
       RUN /N Explorer.exe c:\Mytext.Png
    ENDWITH
     

  • Convert your buttons to BMPs keeping transparency with GdiPlusX

    UPDATED CODE - FIXED BUG REPORTED BY JEAN PIERRE SENET IN THE GRAPHICS INITIALIZATION. Merci beaucup Jean !

     

    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 dorget to remove the LOCFILE() command, and just use a DO System.prg instead

     

    LPARAMETERS tcSourceFile, tcDestFile
    
    DO LOCFILE("System.prg")
    
    LOCAL loBmp AS xfcBitmap
    LOCAL loGfx AS xfcGraphics
    LOCAL loBorderClr AS xfcColor
    LOCAL loRect AS xfcRectangle
    LOCAL loAttr AS xfcImageAttributes
    LOCAL loColorMap AS xfcColorMap
    
    WITH _SCREEN.SYSTEM.Drawing
       loColorMap = .Imaging.ColorMap.New()
       loAttr = .Imaging.ImageAttributes.New()
       loBmp = .Bitmap.FromFile(tcSourceFile)
       loGfx = .Graphics.FromImage(loBmp)
       loRect = loBmp.GetBounds()
    
       * Get the top left pixel color, presuming this color is the BackGround colro 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)
       loGfx.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)
       loGfx.DrawImage(loBmp, m.loRect, m.loRect, .GraphicsUnit.Pixel, loAttr)
    
       loBmp.Save(tcDestFile, .Imaging.ImageFormat.Bmp)
    ENDWITH
  • Extract Icons from resources in a DLL with GdiPlusX

    This week Calvin Hsia came with a very interesting post, Extract TreeView or ListView ImageList icons from a DLL .

    That Calvin's post is dedicated to Visual Studio developers.

    As in the comments there were some people asking the code for VFP, below I'm putting and adapted version for VFP users, using, of course, GdiPlusX to help us extracting the icons from the resource.

     

    There's still more to say about this, but in this first moment, I'm putting just the code to retrieve the embedded bitmap from the Visual Studio DLL that Calvin refers to. Below is the most relevant part of the original code in VB .NET from Calvin, very easilly converted to VFP, using the GdiPlusX library.

    Picture 1: Relevant Source code in VB .NET

     

    And here is the VFP code. Very simple, isn't it ?

    IMPORTANT
    Requires VFP9 and GdiPlusX to run. 
    Please make sure that you have the latest version, because VFPPaint uses some functions that were added recently.
    http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home

     

    * Init GdiPlusX
    DO LOCFILE
    ("System.prg")

    #DEFINE LOAD_LIBRARY_AS_IMAGE_RESOURCE 0X20
    #
    DEFINE
    LOAD_LIBRARY_AS_DATAFILE 2

    DECLARE INTEGER LoadLibraryEx IN kernel32;
       STRING lpFileName, INTEGER hModule, INTEGER
    dwFlags
    DECLARE INTEGER LoadBitmap IN
    user32;
       INTEGER hModule, INTEGER
    hResInfo
    DECLARE INTEGER FreeLibrary IN Kernel32 LONG
    hModule
    DECLARE INTEGER DeleteObject IN WIN32API INTEGER
    hObject

    * Other files that can be used for this test
    * C:\Windows\System32\shdocvw.dll Resource Pointer 328
    * C:\Windows\System32\browseui.dll Resource Pointer 279

    LOCAL lhModule, lhRes
    lhModule = LoadLibraryEx("C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\msenv.dll", ;
       0, LOAD_LIBRARY_AS_IMAGE_RESOURCE + LOAD_LIBRARY_AS_DATAFILE)
    lhRes = LoadBitmap(lhModule, 0x4f4)

    LOCAL loBmp AS
    xfcBitmap
    WITH _SCREEN.SYSTEM
    .Drawing
       loBmp = .
    BITMAP
    .FromHbitmap(lhRes)
       loBmp.
    Save
    ("c:ResourceImage.Bmp", .Imaging.ImageFormat.Bmp)
    ENDWITH

     

    Picture 2: The extracted image in the original size

     

    Picture 3: A part of the extracted image expanded 8 times

     

    Cool ! But still not ready to be used in our VFP forms.

    As that bitmap contains many icons in the same file, I'll use some GDI+ stuff to separate each of them, saving in a different file. We have to know the dimensions of each image, so I'll take the smallest value between the width and height, and use a loop to crop each icon image separately. The backgrounds also need to be treated, as I'll comment below.

    For VS users, Calvin used a totally different technique to separate the images, using the ListView and the TreeView controls from VS. If someone could show me how to chieve this in VFP using his technique... it would be great !

     

    Each image is saved in two different formats:

    1 - BMP of 24 bits, with white background, to be used in VFP buttons. The white background is RGB(255,255,255). For safety purposes, I also changed all original whites from the icons to RGB(254,254,254). This way, VFP will not make these parts of the images as transparent.

    2 - PNG of 32bits, with the background color set as transparent

     

    There is some more complex code that I used to crop the images that I've never showed before in my blog. It's all full of comments, and I hope, it's easy to understand. Feel free to ask for more info about this, if you need.


    ************************************************************************************************************
    * PROGRAM: ExtractIconsFromResource2.Prg
    * AUTHOR : Cesar Chalom
    *
    * Extracts all 16x16 icons stored in DLLs
    * Extracts the icons from the big collection pictures obtained from the resource
    * Aplies effects to the image to keep transparencies
    *
    * Sample totally based on Calvin Hsia's article from his blog
    * "Extract TreeView or ListView ImageList icons from a DLL"
    *
    http://blogs.msdn.com/calvin_hsia/archive/2007/11/06/5948486.aspx?CommentPosted=true#commentmessage
    *
    * Prerequisites:
    * VFP9 and GdiPlusX library -
    http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home
    ************************************************************************************************************
     
    DO LOCFILE("System.prg")

    #DEFINE LOAD_LIBRARY_AS_IMAGE_RESOURCE 0X20
    #
    DEFINE LOAD_LIBRARY_AS_DATAFILE 2

    DECLARE INTEGER LoadLibraryEx IN kernel32;
       STRING lpFileName,;
       INTEGER hFile,;
       INTEGER dwFlags
    DECLARE LONG FreeLibrary IN Kernel32 LONG hModule
    DECLARE INTEGER LoadBitmap IN user32;
       INTEGER hInstance,;
       INTEGER lpBitmapName
    DECLARE INTEGER DeleteObject IN WIN32API INTEGER hObject
     
    WITH _SCREEN.SYSTEM.Drawing
       LOCAL lcFile, lcShortName, lcIconFile
       LOCAL hModule, hRes
       LOCAL N, lnWidth, lnHeight
       LOCAL loIcon AS xfcBitmap
       LOCAL loBmp AS xfcBitmap
       LOCAL loBorderClr AS xfcColor
     
       * These objects will be used only if the output is BMP
       LOCAL loCloned AS xfcBitmap
       LOCAL loColorMap AS xfcColorMap
       loColorMap = .Imaging.ColorMap.New()
       LOCAL loAttr AS xfcImageAttributes
       m.loAttr = .Imaging.ImageAttributes.New()
     
       lcFile = "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\msenv.dll" +
    CHR(0)
       lcShortName = "C:\" +
    JUSTFNAME(lcFile)
       hModule = LoadLibraryEx(lcFile, 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE + LOAD_LIBRARY_AS_DATAFILE)
       hRes = LoadBitmap(hModule, 0x4f4)

       IF hRes = 0
          LOOP
       ENDIF

       loBmp = .BITMAP.FromHbitmap(hRes)
       loBmp.
    SAVE(lcShortName + "_AllIcons.Png", .Imaging.ImageFormat.Png)
       DeleteObject(hRes)
       lnHeight = loBmp.
    HEIGHT
       lnWidth = lnHeight

       IF lnHeight > 16 && This is a big image, so it's not the icon we're interested in
          LOOP
       ENDIF
     
       * Get the quantity of Bits per Pixels of the image.
       * If we have an indexed format (< 24), we wont create the transparencies manually.
       LOCAL lnPixFmtSize
       lnPixFmtSize = loBmp.GetPixelFormatSize(loBmp.PixelFormat)

       * Get the top left pixel color, presuming this color will be converted
       * to transparent
       loBorderClr = loBmp.GetPixel(0,0)

       * Make a copy of the bitmap for further processing as BMP
    loCloned = loBmp.CLONE()
     
     
       *-* BEGINNING OF THE PNG CROPPING
       * Save the Icons as PNGs with transparent background
       IF lnPixFmtSize >= 24 && The background is not White, so
          loBmp.MakeTransparent(loBorderClr)
       ENDIF

       FOR N = 0 TO (loBmp.WIDTH / lnWidth) - 1
          lcIconFile = lcShortName + "_Icon_" +
    TRANSFORM(N) + ".png"
          loIcon = loBmp.CLONE(.Rectangle.New(N * lnWidth, 0, lnWidth, lnHeight))
          IF VARTYPE(loIcon) = "O"
             loIcon.
    SAVE(lcIconFile, .Imaging.ImageFormat.Png)
          ENDIF
       ENDFOR

       * Restore the original Bitmap object for the BMP transformations
       loBmp = loCloned.CLONE()
       *-* END OF PNG CROPPING
     
     
       *-* BEGINNING OF THE BMP CROPPING
       IF loBorderClr <> .COLOR.White AND ;
          lnPixFmtSize >= 24
    && The background is not White, so
                && let's make some changes to adapt the icon to have a white
                && background, that will appear transparent in buttons

          * 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)
          loGfx = .Graphics.FromImage(loBmp)
          loRect = loBmp.GetBounds()
          loGfx.DrawImage(loBmp, m.loRect, m.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)
          loGfx.DrawImage(loBmp, m.loRect, m.loRect, .GraphicsUnit.
    PIXEL, loAttr)
       ENDIF
     
       * Continue, cropping each icon from the big image
       FOR N = 0 TO (loBmp.WIDTH / lnWidth) - 1
          lcIconFile = lcShortName + "_Icon_" +
    TRANSFORM(N) + ".bmp"
          loIcon = loBmp.CLONE(.Rectangle.New(N * lnWidth, 0, lnWidth, lnHeight), .Imaging.PixelFormat.Format24bppRGB)
          IF VARTYPE(loIcon) = "O"
             loIcon.
    SAVE(lcIconFile, .Imaging.ImageFormat.Bmp)
          ENDIF
       ENDFOR
       *-* END OF THE BMP CROPPING

       FreeLibrary(hModule)
    ENDWITH

     

    Picture 4: Thumbnails of some of the images cropped and transformed from the resource file

     

    But there's still one important problem that I wasn't able to solve yet: To list all available resources. So, for the case I wanted to extract the biggest quantity of available icons in all resource files of my computer, the provisory solution I've found was to create a loop, from 0 (zero) to 65535, trying to retrieve the image resource.

    I've just based myself in another old post of mine, Extract icons from EXE, DLL and ICO files with GdiPlusX , and created a new script for extracting all available 16x16 icons in all resources from a specific folder, checking also all the subdirectories. The final result is TONS of icons, all stored in just one folder, "C:\Resource Icons", in two versions, one in PNG with the transparent background, and another version stored in the format of BMP of 24 bits, with white background, that is the format best supported by VFP for our command buttons.

    When you run this last sample, depending on the folder that you choose, and the remaining subfolders, the resulting process may be really long, so I ask you to be patient, but I ensure you it's worth. When finished, open the folder "C:\Resource Icons". My search in Drive C of my computer, resulted in more than 25000 different icons !

    Enjoy !


    ************************************************************************************************************
    * PROGRAM: ExtractIconsFromResource3.Prg
    * AUTHOR : Cesar Chalom
    *
    * Extracts all 16x16 icons stored in DLLs or EXE files
    * Explores recursively in all the directory and subdirectories selected
    * Extracts the icons from the big collection pictures obtained from the resource
    *
    * Sample totally based on Calvin Hsia's article from his blog
    * "Extract TreeView or ListView ImageList icons from a DLL"
    *
    http://blogs.msdn.com/calvin_hsia/archive/2007/11/06/5948486.aspx?CommentPosted=true#commentmessage
    *
    * Prerequisites:
    * VFP9 and GdiPlusX library -
    http://www.codeplex.com/VFPX/Wiki/View.aspx?title=GDIPlusX&referringTitle=Home
    ************************************************************************************************************
     
    DO LOCFILE("System.prg")

    #DEFINE LOAD_LIBRARY_AS_IMAGE_RESOURCE 0X20
    #
    DEFINE LOAD_LIBRARY_AS_DATAFILE 2

    DECLARE INTEGER LoadLibraryEx IN kernel32;
       STRING lpFileName,;
       INTEGER hFile,;
       INTEGER dwFlags
    DECLARE LONG FreeLibrary IN Kernel32 LONG hModule
    DECLARE INTEGER LoadBitmap IN user32;
       INTEGER hInstance,;
       INTEGER lpBitmapName
    DECLARE INTEGER DeleteObject IN WIN32API INTEGER hObject
     
    * Create destination directory to receive the extracted icons
    lcDir = "C:\ResourceIcons"
    IF NOT DIRECTORY(lcDir)
       MKDIR (lcDir)
    ENDIF
     
    * Create the cursor that will store the valid file locations
    CREATE CURSOR Recursive (cFile c(250))
    LOCAL lcSelectedDir
    lcSelectedDir =
    GETDIR()
    IF EMPTY(lcSelectedDir)
       WAIT WINDOW "Invalid Directory"
       RETURN .F.
    ENDIF
    WAIT WINDOW
    "Retrieving folders information" NOWAIT

    * Scan through the selected and the subfolders
    =Recurse(lcSelectedDir)
     
    WITH _SCREEN.SYSTEM.Drawing

    LOCAL lcFile, lcShortName, lcIconFile, lnTotRec
    LOCAL lnOldPercentage, lnPercentage
    LOCAL hModule, hRes
    LOCAL N, lnWidth, lnHeight
    LOCAL loIcon AS xfcBitmap
    LOCAL loBmp AS xfcBitmap
    LOCAL loBorderClr AS xfcColor
     
    * These objects will be used only if the output is BMP
    LOCAL loCloned AS xfcBitmap
    LOCAL loColorMap AS xfcColorMap
    loColorMap = .Imaging.ColorMap.New()
    LOCAL loAttr AS xfcImageAttributes
    m.loAttr = .Imaging.ImageAttributes.New()
     
    STORE 0 TO lnPercentage, lnOldPercentage
    lnTotRec =
    RECCOUNT()

    SCAN
       lcFile = ALLTRIM(Recursive.cFile) + CHR(0)
       lcShortName =
    JUSTFNAME(lcFile)
       hModule = LoadLibraryEx(lcFile, 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE + LOAD_LIBRARY_AS_DATAFILE)
       FOR x = 0 TO 0xffff
          hRes = LoadBitmap(hModule, x)
    && 0x4f4)
          IF hRes = 0
             LOOP
          ENDIF
          lcNewFile = ADDBS(lcDir) + lcShortName
          loBmp = .
    BITMAP.FromHbitmap(hRes)
          loBmp.
    Save(lcNewFile + "_AllIcons_r" + TRANSFORM(x) + ".Png", .Imaging.ImageFormat.Png)
          DeleteObject(hRes)
          lnHeight = loBmp.
    HEIGHT
          lnWidth = lnHeight
          IF lnHeight > 16 && This is a big image, so it's not the icon we're interested in
             LOOP
          ENDIF
     
          * Get the quantity of Bits per Pixels of the image.
          * If we have an indexed format (< 24), we wont create the transparencies manually.
          LOCAL lnPixFmtSize
          lnPixFmtSize = loBmp.GetPixelFormatSize(loBmp.PixelFormat)

          * Get the top left pixel color, presuming this color will be converted
          * to transparent
          loBorderClr = loBmp.GetPixel(0,0)

          * Make a copy of the bitmap for further processing as BMP
          loCloned = loBmp.Clone()
     
     
     
          *-* BEGINNING OF THE PNG CROPPING
          * Save the Icons as PNGs with transparent background
          IF lnPixFmtSize >= 24 && The background is not White, so
             loBmp.MakeTransparent(loBorderClr)
          ENDIF

          FOR N = 0 TO (loBmp.WIDTH / lnWidth) - 1
              lcIconFile = lcNewFile +
    TRANSFORM(x) + "_Icon_" + TRANSFORM (N) + ".png"
              loIcon = loBmp.CLONE(.Rectangle.New(N * lnWidth, 0, lnWidth, lnHeight))
              IF VARTYPE(loIcon) = "O"
                 loIcon.
    SAVE(lcIconFile, .Imaging.ImageFormat.Png)
              ENDIF
          ENDFOR
          * Restore the original Bitmap object for the BMP transformations
          loBmp = loCloned.Clone()
          *-* END OF PNG CROPPING


          *-* BEGINNING OF THE BMP CROPPING
          IF loBorderClr <> .Color.White AND ;
             lnPixFmtSize >= 24
    && The background is not White, so
             && let's make some changes to adapt the icon to have a white
             && background, that will appear transparent in buttons
             * 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)
             loGfx = .Graphics.FromImage(loBmp)

             loRect = loBmp.GetBounds()
             loGfx.DrawImage(loBmp, m.loRect, m.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)
             loGfx.DrawImage(loBmp, m.loRect, m.loRect, .GraphicsUnit.
    Pixel, loAttr)
          ENDIF


          * Continue, cropping each icon from the big image
          FOR N = 0 TO (loBmp.WIDTH / lnWidth) - 1
             lcIconFile = lcNewFile +
    TRANSFORM(x) + "_Icon_" + TRANSFORM(N) + ".bmp"
             loIcon = loBmp.CLONE(.Rectangle.New(N * lnWidth, 0, lnWidth, lnHeight), .Imaging.PixelFormat.Format24bppRGB)
             IF VARTYPE(loIcon) = "O"
                loIcon.
    SAVE(lcIconFile, .Imaging.ImageFormat.Bmp)
             ENDIF
          ENDFOR
          *-* END OF THE BMP CROPPING
     
       ENDFOR

       FreeLibrary(hModule)

       * Show progression
       lnRec = RECNO()
       lnPercentage =
    ROUND((lnRec / lnTotRec),2)
       IF lnPercentage > lnOldPercentage
          WAIT WINDOW "Extracting icons from files..." + CHR(13) + ;
             "Elapsed: " +
    TRANSFORM(lnPercentage * 100) + " %" NOWAIT
          lnOldPercentage = lnPercentage
       ENDIF
    ENDSCAN
    ENDWITH
    RETURN
     
     
    *****************************************************************************************
    * FUNCTION : RECURSE
    * AUTHOR : MICHAEL REYNOLDS
    * DESCRIPTION : Good for performing file processing throughout an entire directory tree.
    * The function, RECURSE(), is called with the full path of a directory.
    * RECURSE() will then read all files and directories in the path.
    * A function can be called to process files that it finds.
    * Plus, the function calls itself to process any sub-directories.
    *
    http://fox.wikis.com/wc.dll?Wiki~RecursiveDirectoryProcessing~VFP
    *****************************************************************************************
    FUNCTION Recurse(pcDir)
    LOCAL lnPtr, lnFileCount, laFileList, lcDir, lcFile
    CHDIR (pcDir)
    DIMENSION laFileList[1]
    *--- Read the chosen directory.
    lnFileCount = ADIR(laFileList, '*.*', 'D')
    FOR lnPtr = 1 TO lnFileCount

       IF 'D' $ laFileList[lnPtr, 5]
          *--- Get directory name.
          lcDir = laFileList[lnPtr, 1]
          *--- Ignore current and parent directory pointers.
          IF lcDir != '.' AND lcDir != '..'
             *--- Call this routine again.
         &nb