2006-11-27

FUNCTION TO CONVERT COLORS

The short function below converts any color to its darker or brighter version, just as the original colorpicker slidebar.

parameters:

      rgb - original rgb value to convert

      tnlevel - ranges from -100 to + 100. zero means no changes; +100 = white; -100 = black; positive values will return brighter images, while negative will bring darker.

 

lparameters tnRgb, tnLevel

if tnLevel = 0
   return tnrgb
endif


tnLevel = tnLevel / 100

local lnRed, lnGreen, lnBlue
lnRed   = bitand(tnRgb, 0x000000ff)
lnGreen = bitrshift(bitand(tnRgb, 0x0000ff00), 8 )
lnBlue  = bitrshift(bitand(tnRgb, 0x00ff0000), 16)


if tnLevel > 0
   return rgb( ;
      lnRed   + ((255 - lnRed)   * tnLevel) , ;
      lnGreen + ((255 - lnGreen) * tnLevel) , ;
      lnBlue  + ((255 - lnBlue)  * tnLevel) )
else
   return rgb( ;
      lnRed   + (lnRed   * tnLevel) , ;
      lnGreen + (lnGreen * tnLevel) , ;
      lnBlue  + (lnBlue  * tnLevel) )
endif


To better understand how it works, you can download the attached file, that contains a simple example. Click on "select color" to pick a color then play with the spinner to see the darker and brighter resulting colors.



2006-11-23

Updated version of the My namespace

Doug Hennig very kindly published a new version of the my namespace on the White papers and source code page of the stonefield web site.

It's a 240 k download. This version contains some fixes, a small project and a simple form showing how to use some functions.

In my tests, I installed "MY" with no errors. It works fine even without having a previous version installed, and does not need the sedna ctp to run, just vfp9. Intellisense worked perfectly, showing only the properties and methods of interest, and inside a form. The help file is nice, and contains good documentation about the more than 60 functions included.

As always, another great job from Doug.

2006-09-18

HOWTO: CHANGE BITMAP RESOLUTION

The bitmap resolution can be also very easily changed with gdi+.

The sample below uses the new GdiPlus-X classes from VFPX projects that can be downloaded from here: http://www.codeplex.com/wiki/view.aspx?projectname=vfpx&title=gdiplusx

*!* HOW TO CHNGE THE BITMAP RESOLUTION
* the code loads an image, changes its resolution to 200x200
* and saves it again as a bmp
*
http://msdn2.microsoft.com/en-us/library/system.drawing.bitmap.setresolution.aspx

do locfile("System.app")

with _screen.system.drawing
   * load image to gdi+
   local loBmp as xfcBitmap
   loBmp = .Bitmap.New(getpict())

   * change Bitmap Resolution
   loBmp.SetResolution(200,200)

   * save the bitmap
   loBmp.Save("c:\newresolution.bmp", .imaging.imageformat.bmp)

endwith
return

2006-09-17

Using TIFFs with the new GDI+ classes - UPDATE

As a continuation of my article from may 2006 published in UTMAG, entitled "Multiframe images with Gdi+" i wrote a New article published in the september 2006 issue of utmag called "Using TIFFs with the New Gdi+ classes" showing how to manipulate TIFF images using the New gdiplus-x library from VFP-X project. It also shows how to create TIFFs using all Compressions supported.

But I left one point not very clear. This came to me after I saw a question from Jennifer Slusher in the universal thread forums (thread id #1153672), asking how to create multiPage TIFFs in Compression 4 (ccitt4). in that article, I showed how to create common Multiframe TIFFs and also how to create a single frame TIFF using Compression, but forgot to show or comment about creating Multiframe TIFFs with Compression.

Multiframe TIFFs with Compression

Gdi+ by default uses the 'lzw lossless' Compression whenever asked to save as TIFF. According to Wayne Fulton, from www.scantips.com, "lossless means there is no quality loss due to Compression. lossless guarantees that you can always read back exactly what you thought you saved, bit-for-bit identical, without data corruption". so, unless we specify, gdi+ will always use lzw Compression on TIFFs.

To create a Multiframe TIFF with Compression different from lzw, we need to send some additional Encoder Parameters to gdi+, even if the original picture is already a TIFF with another Compression. The methods "save" and "saveadd" permit us to send more than one Encoder Parameter at a time. Then, all we need to do is to create another Encoder object based on the guid for the Compression Parameter category, and set its EncoderValue to the desired Compression, eg Compressionccitt4.

The sample below will ask for any three images. To ensure that it will be able to save using Compression ccitt4, it will first convert all the selected images to monochrome. Then it will create a Multiframe TIFF image file containing all selected images in the Compression ccitt4.



** The following example loads three Bitmap objects
** converts all Bitmaps to monochrome to be compatible with rle, ccitt3/4 Compression
** the code saves all three images in a single, multiple-frame TIFF
** file, using Compression format CCITT4 
DO LOCFILE("System.app")

WITH _SCREEN.SYSTEM.Drawing
    LOCAL loBmp AS xfcBitmap
    LOCAL loMultif AS xfcBitmap
    LOCAL loPage2 AS xfcBitmap
    LOCAL loPage3 AS xfcBitmap
    LOCAL myEncoder AS xfcEncoder
    LOCAL myCompEncoder AS xfcEncoder
    LOCAL myEncoderParameter AS xfcEncoderParameter
    LOCAL myCompEncoderParameter AS xfcEncoderParameter
    LOCAL myEncoderParameters AS xfcEncoderParameters

    *!* create three Bitmap objects.
    *!* 1st we load the original Bitmap,
    *!* and then get its monochrome version
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loMultif = m.loBmp.getMonochrome()
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loPage2  = m.loBmp.getMonochrome()
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loPage3  = m.loBmp.getMonochrome()

    *!* release the original Bitmap because we'll not use it any more
    m.loBmp = NULL

    *!* create Encoder object based on the guid for the saveflag Parameter category.
    m.myEncoder = .Imaging.Encoder.saveflag

    *!* create Encoder object based on the guid for the Compression Parameter category.
    m.mycompEncoder = .Imaging.Encoder.Compression

    && create an EncoderParameters object.
    && EncoderParameters object has an array of EncoderParameter objects
    && in this case, there'll be 2 EncoderParameter objects in the array.
    m.myEncoderParameters = .Imaging.EncoderParameters.New(2)

    *!* save the first Page (frame).
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.Multiframe)
    m.mycompEncoderParameter = .Imaging.EncoderParameter.New(m.mycompEncoder, ;
          .Imaging.EncoderValue.Compressionccitt4)

    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.myEncoderParameters.PARAM[2] = m.mycompEncoderParameter
    m.loMultif.SAVE("c:\NewMultiframeCompress.tif", ;
          .Imaging.imageformat.TIFF, m.myEncoderParameters)

    *!* save the second Page (frame).
    *!* this time we will only change the 1st Parameter to "framedimensionPage"
    *!* the 2nd Parameter that sets the Compression will remain the same
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.framedimensionPage)
    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.loMultif.saveadd(m.loPage2, m.myEncoderParameters)

    *!* save the third Page (frame).
    *!* this time we don't need to make any change to the Encoder paramenters
    *!* we'll keep using the same Parameters used when we added the 2nd frame
    m.loMultif.saveadd(m.loPage3, m.myEncoderParameters)

    *!* close the multiple-frame file.
    *!* this time we call the "flush" Parameter to close the file.
    *!* we don't need the 2nd Parameter any more, so change it to null
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.FLUSH)
    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.myEncoderParameters.PARAM[2] = NULL
    m.loMultif.saveadd(m.myEncoderParameters)
ENDWITH
RETURN



Notice that the object myEncoderParameters contains a property "params" that is an array of Parameters.


Other related links:
TIFF, tag image file format
A few scanning tips

 

 

2006-09-12

Helper code to create image on the fly for "Using the Alpha Channel in Visual Foxpro Images" from Bernard Bout

Here's another very simple code that attends Bernard Bout, creating an image on the fly, based on his post entitled "Using the alpha channel in visual foxpro images". Again in this sample, I'm using the new Gdiplus-X classes from VFPX project that can be downloaded from GitHub



* Helper code to create image on the fly for
* Bernard Bout "Using the alpha channel in VFP images"
* http://weblogs.foxite.com/bernardbout/archive/2006/09/11/2436.aspx
*    image dimensions 300 x 270 pixels
*    make all image totally transparent
*    draw a light yellow - rgb(254,254,228) rectangle
*         centered in the main image.
*    save as PNG, to preserve the transparencies
DO LOCFILE("System.app")

WITH _SCREEN.SYSTEM.Drawing
    * create an empty bitmap
    LOCAL loBitmap AS xfcBitmap
    m.loBitmap = .BITMAP.New(300,270)

    * initialize the graphics object
    LOCAL loGfx AS xfcGraphics
    m.loGfx = .Graphics.FromImage(m.loBitmap)

    * make all image transparent
    m.loGfx.CLEAR(.COLOR.FromARGB(0,0,0,0))

    * draw the yellow rectangle
    m.loGfx.FillRectangle(.SolidBrush.New(.COLOR.FromRGB(254,254,228)), 10,9,278,249)

    * save as png to keep transparencies
    m.loBitmap.SAVE("c:\bernardboutagain.png", .Imaging.ImageFormat.Png)
ENDWITH
RETURN


2006-08-13

VFP-X GDI+ code samples for "Recreating One Note Tabs in VFP9" from Bernard

2007-oct-23 - Updated, based on "BMPs with transparent backgrounds"

Now Bernard Bout is owing me 2 blog posts :-))
Below are some pictures from Bernard Bout, showing some very cool forms that he created. To ease his job, I'll show how to create on the fly the image files that he uses to create these examples, using the new classes from the gdiplus-x project, available for download at codeplex.

More information can be obtained in Bernard's blog post:"Recreating One Note Tabs in VFP9"





sample 1: on_bigtab

target image enlarged 8 times :



target image in original size :



** creates an image on_bigtab.png to be used in the "one note tabs"
** example from Bernard bout
** - a 112x20 pixel image, with an irregular polygon with a blue
** border and filled with a gradient orange color

do locfile("system.app")


* define the colors to be used
local lnrgbstartgradclr, lnrgbendgradclr
lnrgbstartgradclr = rgb(253,233,218) && light orange
lnrgbendgradclr = rgb(247,182,131) && orange
lnrgbborderclr = rgb(59,97,156) && dark blue


with _screen.system.drawing
local lobitmap as xfcbitmap
local logfx as xfcgraphics
local logradbrush as xfclineargradientbrush
local lopen as xfcpen
local lorect as xfcrectangle


* create a new 112x20 bitmap in the default pixelformat - 32bppargb
lobitmap = .bitmap.new(112,20, 0, .imaging.pixelformat.format24bpprgb)


* create a graphics object to be able to draw in the bitmap
logfx = .graphics.fromimage(lobitmap)
logfx.clear(.color.white) && white


* create a rectangle for the linear gradient brush
lorect = .rectangle.new(0,0,112,19) && size of bitmap

* create a linear gradient brush
logradbrush = .drawing2d.lineargradientbrush.new(lorect,;
.color.fromrgb(lnrgbstartgradclr), .color.fromrgb(lnrgbendgradclr),1)


dimension lapoints(6)
lapoints(1) = .point.new(0,20)
lapoints(2) = .point.new(17,4)
lapoints(3) = .point.new(21,2)
lapoints(4) = .point.new(109,2)
lapoints(5) = .point.new(110,4)
lapoints(6) = .point.new(110,20)


* fill the polygon with gradient brush
logfx.fillpolygon(logradbrush, @lapoints)

* create a blue pen object to draw border
lopen = .pen.new(.color.fromrgb(lnrgbborderclr),1)


* draws the blue border
lapoints(1) = .point.new(0,19)
lapoints(2) = .point.new(17,2)
lapoints(3) = .point.new(22,0)
lapoints(4) = .point.new(109,0)
lapoints(5) = .point.new(111,2)
lapoints(6) = .point.new(111,18)


logfx.drawlines(lopen, @lapoints)

* draw bottom-right pixels
lobitmap.setpixel(111,19,.color.fromrgb(lnrgbendgradclr))
lobitmap.setpixel(110,19,.color.fromrgb(lnrgbendgradclr))


* save the image
lobitmap.save("c:\on_bigtab.png", .imaging.imageformat.png)
endwith


 

 

sample 2: on_smalltab

target image enlarged 8 times :



target image in original size :



 

** creates an image on_smalltab.png to be used in the "one note tabs"
** example from Bernard bout
** - a 16x8 pixel image, with an irregular polygon with a blue border
** and filled with a gradient orange color


do locfile("system.app")


* define the colors to be used
local lnrgbstartgradclr, lnrgbendgradclr
lnrgbstartgradclr = rgb(255,232,197)
&& light orange
lnrgbendgradclr = rgb(255,179,15)
&& orange
lnrgbborderclr = rgb(26,57,86) && dark blue


with _screen.system.drawing
local lobitmap as xfcbitmap
local logfx as xfcgraphics
local logradbrush as xfclineargradientbrush
local lopen as xfcpen
local lorect as xfcrectangle


* create a new 16x8 bitmap in the default pixelformat - 32bppargb
lobitmap = .bitmap.new(16,8,0, .imaging.pixelformat.format24bpprgb)


* create a graphics object to be able to draw in the bitmap
logfx = .graphics.fromimage(lobitmap)
logfx.clear(.color.fromrgb(rgb(255,255,255))) && white


* create a rectangle for the linear gradient brush
lorect = .rectangle.new(0,0,16,8) && size of bitmap

* create a linear gradient brush
logradbrush = .drawing2d.lineargradientbrush.new(lorect,;
.color.fromrgb(lnrgbstartgradclr), .color.fromrgb(lnrgbendgradclr),1)


dimension lapoints(5)
lapoints(1) = .point.new(0,7)
lapoints(2) = .point.new(0,2)
lapoints(3) = .point.new(2,0)
lapoints(4) = .point.new(8,0)
lapoints(5) = .point.new(15,7)


* fill the polygon with gradient brush
logfx.fillpolygon(logradbrush, @lapoints)


* create a blue pen object to draw border
lopen = .pen.new(.color.fromrgb(lnrgbborderclr),1)


* draws the polygon
logfx.drawpolygon(lopen, @lapoints)


* save the image
lobitmap.save("c:\on_smalltab.png", .imaging.imageformat.png)
endwith


 

 

sample 3: on_smallbutton

original image enlarged 8 times: target image enlarged 8 times :



original image in normal size: target image in original size :



 

** loads an image and draws a customized rectangle using a
** lineargradientbrush; creates image file on_smallbutton.png
** to be used in the "one note tabs" example from Bernard bout
** - a 112x20 pixel image, with an irregular polygon with a blue
** border and filled with a gradient orange color

do locfile("system.app")


* define the colors to be used
local lnrgbstartgradclr, lnrgbendgradclr
lnrgbstartgradclr = rgb(253,232,197) && light orange
lnrgbendgradclr = rgb(255,179,15) && orange


with _screen.system.drawing
local lobitmap as xfcbitmap
local logfx as xfcgraphics
local logradbrush as xfclineargradientbrush
local lorect as xfcrectangle


* load the image file
lobitmap = .image.fromfile(getpict())


* create a graphics object to be able to draw in the bitmap
logfx = .graphics.fromimage(lobitmap)


* create a rectangle for the linear gradient brush
lorect = .rectangle.new(0,0,12,18)

* create a linear gradient brush
logradbrush = .drawing2d.lineargradientbrush.new(lorect,;
.color.fromrgb(lnrgbstartgradclr), .color.fromrgb(lnrgbendgradclr),1)


* fill the polygon with gradient brush
logfx.fillrectangle(logradbrush, 3, 1, 12, 17)


* save the image
lobitmap.save("c:\on_smallbutton.png", .imaging.imageformat.png)
endwith


 

Again, let's wait for Bernard's next appearances !

2006-08-10

New VFP-X GDI+ classes

In the last weeks I've been doing some extensive tests and creating some examples using the new VFP-X GdiplusX library. These classes are terrific, completely insane. this library is already making my life with gdi+ a lot easier.

The more I use it, the more I like it. I'm really having fun.

As Craig Boyd said in one of his blog posts: "the GdiplusX library is a pure Visual FoxPro reproduction of the drawing related namespaces in .NET. We've coded well over 40,000 lines of vfp code and the library consists of 80+ classes now. at nearly 95% complete. It is safe to say that no other library on the planet gives visual foxpro developers the functionality and power that this one does when working with gdi+. " - That's totally true.

 

If you're planning to start a new project and will need to use GDI+, or are interested to help in the project, coding, testing making suggestions, I strongly recommend to enter the codeplex vfp-x page, select the "releases" tab, and download the most recent stable version available.

Start running the file demo.prg in the samples folder, and you'll have a good idea of the power of the class.

By the way, the VFP-X project now has a new logo too !

From now on, I'll use this space to show some examples using these classes. You may use the comments space of this blog to make this kind of requests.


Code samples

My first sample comes to attend Bernard Bout, to create an image to be used in his "blueglass" example. You can obtain more information at this link: http://weblogs.foxite.com/bernardbout/archive/2006/06/15/1838.aspx

Create a 2x2 pixel image, with a blue background and a magenta pixel at (0,0)

Here's the code:

** creates an image to be used in the "blueglass" example 
** from Bernard Bout 
** 2x2 pixel image, with blue background and a magenta pixel at (0,0)        

LOCAL lnRgbBackgClr, lnRgbPointClr
DO LOCFILE("System.app")

* define the colors to be used 
m.lnRgbPointClr = RGB(0,0,255) && blue
m.lnRgbBackgClr = RGB(255,0,255) && magenta

WITH _SCREEN.SYSTEM.Drawing
    LOCAL loBitmap AS xfcBitmap
    LOCAL loColor AS xfcColor
    LOCAL loGfx AS xfcGraphics

    * create a new 2x2 bitmap in the default pixelformat - 32bppargb 
    m.loBitmap = .BITMAP.New(2,2)

    * create a graphics object to be able to use the clear function, 
    * that will fill all the bitmap with the desired color. 
    * for this case that is not fundamental, because the bitmap is 
    * small, so we could calls "setpixel" 4 times to cover the bitmap. 
    m.loGfx = .Graphics.FromImage(m.loBitmap)
    m.loGfx.CLEAR(.COLOR.fromrgb(m.lnRgbBackgClr))

    * draw the pixel 
    m.loBitmap.setpixel(0,0, .COLOR.fromrgb(m.lnRgbPointClr))

    * save the image as png 
    m.loBitmap.SAVE("c:\blueglass.png", .Imaging.ImageFormat.Png)
ENDWITH


And here's the image created in a bigger size.