IMPORTANT:
All samples shown in this article use the new GDIPlus-X library, that is still in ALPHA version, but is already stable and reliable to do the majority of GDI+ tasks. So, before you start, please download the latest stable release from Codeplex: http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX
Creating multiframe TIFFS
The steps are exactly the same that were presented in the previous article:
* Converted to GDIPlusX for VFP from .NET help: * http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.encoder.saveflag.aspx * The code saves three images in a single, multiple-frame TIFF file. _SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) WITH _SCREEN.System.Drawing LOCAL multif AS xfcBitmap LOCAL page2 AS xfcBitmap LOCAL page3 AS xfcBitmap LOCAL myImageCodecInfo AS xfcImageCodecInfo LOCAL myEncoder AS xfcEncoder LOCAL myEncoderParameter AS xfcEncoderParameter LOCAL myEncoderParameters AS xfcEncoderParameters && Create three Bitmap objects. multif = .Bitmap.New(GETPICT()) page2 = .Bitmap.New(GETPICT()) page3 = .Bitmap.New(GETPICT()) && Get an ImageCodecInfo object that represents the TIFF codec. myImageCodecInfo = GetEncoderInfo("image/tiff") && Create an Encoder object based on the GUID && for the SaveFlag parameter category. myEncoder = .Imaging.Encoder.SaveFlag && Create an EncoderParameters object. && An EncoderParameters object has an array of EncoderParameter && objects. In this case, there is only one && EncoderParameter object in the array. myEncoderParameters = .Imaging.EncoderParameters.New(1); *!* Save the first page (frame). myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.MultiFrame) myEncoderParameters.Param[1] = myEncoderParameter multif.Save("c:\Multiframe.tif", myImageCodecInfo, myEncoderParameters) *!* Save the second page (frame). myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.FrameDimensionPage) myEncoderParameters.Param[1] = myEncoderParameter multif.SaveAdd(page2, myEncoderParameters) *!* Save the third page (frame). myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.FrameDimensionPage) myEncoderParameters.Param[1] = myEncoderParameter multif.SaveAdd(page3, myEncoderParameters) *!* Close the multiple-frame file. myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.Flush) myEncoderParameters.Param[1] = myEncoderParameter multif.SaveAdd(myEncoderParameters) ENDWITH RETURN FUNCTION GetEncoderInfo(mimeType AS String) AS xfcImageCodecInfo LOCAL j LOCAL encoders AS Collection encoders = _SCREEN.System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders() FOR j = 1 TO encoders.Count IF encoders[j].MimeType = mimeType RETURN encoders[j] ENDIF ENDFOR ENDFUNC
We can obtain the same result, erasing the "GetEncoderInfo" procedure, and substituting
myImageCodecInfo = GetEncoderInfo("image/tiff")
myImageCodecInfo = .Imaging.ImageFormat.Tiff
Extracting frames from TIFFS
This is also very simple.
** HOWTO: Extract frames from a Multiframe TIFF _SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) WITH _SCREEN.System.Drawing * Load the multiframe TIFF to GDI+ LOCAL loMultif AS xfcBitmap loMultif = .Bitmap.New(GETPICT("TIF")) LOCAL lnFrames, n, lcFrameFileName lnFrames = loMultif.GetFrameCount() IF lnFrames > 1 FOR n = 0 TO lnFrames - 1 loMultif.SelectActiveFrame(.Imaging.FrameDimension.Page, n) lcFrameFileName = "c:\Frame" + TRANSFORM(n + 1) + ".Tif" loMultif.Save(lcFrameFileName, .Imaging.ImageFormat.Bmp) ENDFOR ELSE MESSAGEBOX("The selected file is not a Multiframe TIFF") ENDIF ENDWITH RETURN
Setting the TIFF compression
MSDN (http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.encodervalue.aspx) describes the various compression types supported by GDI+:
Convert to monochrome
CCITT3, CCITT4 and RLE compression types are normally used for faxing purposes. But this compression will only work for monochromatic (one bit-per-pixel) images. So, before attempting to apply this kind of conversion we need to successfully convert the original image to one bit per pixel.
Bob Powell shows in his "GDI+ Faqs" an interesting but slow routine to convert images to 1bpp: http://www.bobpowell.net/onebit.htm
But GDI (the good and old one) provides some functions (CopyImage and LoadImage) to do that in a flash. Many thanks to Mike Gagnon, who showed the right path to accomplish that. So, the developers team of GDIPlus-X agreed to add this important feature to the library, as a new Method added to the Image and Bitmap classes: GetMonochrome().
Steps to get the monochrome 1bpp version of any image using the GDI+X classes
** HOWTO: Get a Monochromatic 1 bit per pixel image _SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) WITH _SCREEN.System.Drawing * Create a Bitmap object based on a BMP file. LOCAL loOriginalBmp AS xfcBitmap loOriginalBmp = .Bitmap.New(GETPICT()) LOCAL loMonoChrBmp as xfcBitmap loMonoChrBmp = loOriginalBmp.GetMonochrome() * Dispose the original Bitmap because we don't need it any more loOriginalBmp = NULL * Save the created Monochromatic Bmp loMonochrBmp.Save("c:\Monochromatic.bmp", .Imaging.ImageFormat.Bmp) ENDWITH RETURN
Now we can use the routine below to save any image in all kinds of compression. For compression types NONE and LZW the original the image is used. For RLE, CCITT3/4, a monochromatic copy of the selected image is compressed.
** HOWTO: Set Tiff Compression ** http://msdn2.microsoft.com/en-us/library/system.drawing.imaging.encoder.compression.aspx ** The following example creates a Bitmap object from a BMP file. ** The code saves the image as a TIFF file with LZW compression. _SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) WITH _SCREEN.System.Drawing LOCAL loMyBitmap AS xfcBitmap LOCAL myEncoder AS xfcEncoder LOCAL myEncoderParameters AS xfcEncoderParameters * Create a Bitmap object based on a BMP file. loMyBitmap = .Bitmap.New(GETPICT()) * Create an Encoder object based on the GUID * for the Compression parameter category. myEncoder = .Imaging.Encoder.Compression * Create an EncoderParameters object. * An EncoderParameters object has an array of EncoderParameter objects. * In this case, there is only one EncoderParameter object in the array. myEncoderParameters = .Imaging.EncoderParameters.New(1) * Save the bitmap as a TIFF file with LZW compression. myEncoderParameters.Param(1) = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.CompressionLZW) loMyBitmap.Save("c:\ImageLZW.tif", .Imaging.ImageFormat.Tiff, myEncoderParameters) * Save the bitmap as a TIFF file with NONE compression. myEncoderParameters.Param(1) = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.CompressionNone) loMyBitmap.Save("c:\ImageNONE.tif", .Imaging.ImageFormat.Tiff, myEncoderParameters) * Convert the original Bitmap to Monochrome LOCAL loMonoChrBmp as xfcBitmap loMonoChrBmp = loMyBitmap.GetMonochrome() * Dispose the original Bitmap because we don't need it any more loMyBitmap = NULL * Go on with the compressions using the monochromatic version of the selected image * Save the bitmap as a TIFF file with RLE compression. myEncoderParameters.Param(1) = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.CompressionRle) loMonoChrBmp.Save("c:\ImageRLE.tif", .Imaging.ImageFormat.Tiff, myEncoderParameters) * Save the bitmap as a TIFF file with CCITT3 compression. myEncoderParameters.Param(1) = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.CompressionCCITT3) loMonoChrBmp.Save("c:\ImageCCITT3.tif", .Imaging.ImageFormat.Tiff, myEncoderParameters) * Save the bitmap as a TIFF file with CCITT4 compression. myEncoderParameters.Param(1) = .Imaging.EncoderParameter.New(myEncoder,; .Imaging.EncoderValue.CompressionCCITT4) loMonoChrBmp.Save("c:\ImageCCITT4.tif", .Imaging.ImageFormat.Tiff, myEncoderParameters) ENDWITH RETURN
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 _SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) 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 loBmp = .Bitmap.New(GETPICT()) loMultif = loBmp.GetMonochrome() loBmp = .Bitmap.New(GETPICT()) loPage2 = loBmp.GetMonochrome() loBmp = .Bitmap.New(GETPICT()) loPage3 = loBmp.GetMonochrome() *!* Release the original Bitmap because we'll not use it any more loBmp = NULL && Create Encoder object based on the GUID for the SaveFlag parameter category. myEncoder = .Imaging.Encoder.SaveFlag && Create Encoder object based on the GUID for the Compression parameter category. 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. myEncoderParameters = .Imaging.EncoderParameters.New(2) *!* Save the first page (frame). myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder, ; .Imaging.EncoderValue.MultiFrame) myCompEncoderParameter =.Imaging.EncoderParameter.New(myCompEncoder, ; .Imaging.EncoderValue.CompressionCCITT4) myEncoderParameters.Param[1] = myEncoderParameter myEncoderParameters.Param[2] = myCompEncoderParameter loMultif.Save("c:\NewMultiframeCompress.tif", ; .Imaging.ImageFormat.Tiff, 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 myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder, ; .Imaging.EncoderValue.FrameDimensionPage) myEncoderParameters.Param[1] = myEncoderParameter loMultif.SaveAdd(loPage2, 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 loMultif.SaveAdd(loPage3, 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 myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder, ; .Imaging.EncoderValue.Flush) myEncoderParameters.Param[1] = myEncoderParameter myEncoderParameters.Param[2] = NULL loMultif.SaveAdd(myEncoderParameters) ENDWITH RETURN
&& Create Encoder object based on the GUID for the SaveFlag parameter category. myEncoder = .Imaging.Encoder.SaveFlag && Create Encoder object based on the GUID for the Compression parameter category. 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. myEncoderParameters = .Imaging.EncoderParameters.New(2) *!* Save the first page (frame). myEncoderParameter = .Imaging.EncoderParameter.New(myEncoder, ; .Imaging.EncoderValue.MultiFrame) myCompEncoderParameter =.Imaging.EncoderParameter.New(myCompEncoder, ; .Imaging.EncoderValue.CompressionCCITT4) myEncoderParameters.Param[1] = myEncoderParameter myEncoderParameters.Param[2] = myCompEncoderParameter
Conclusion
GdiPlus-X project is still in ALPHA version. If you are interested to help in the project, coding, testing making suggestions, in Codeplex you will find the Discussions and Issue Tracker sections. There's still a lot to be tested, as Bo said: "We welcome any feedback. Please let us know if you find any bugs in the classes that need to be addressed. If you are interested in helping with the development of this library, documentation or being an official tester, please contact the project manager Bo Durban at gdiplusx@moxiedata.com."
The reference manual for these libraries is still under development. To help you get started, in the "readme.htm" file you'll find a trick using intellisense to display a list of classes and members with their descriptions while writing code. Bo also included a preliminary reference document that list the classes included with the library and their current status. It also includes a brief description of each class with links to the equivalent .NET class in the MSDN library.
If you find any difficulty to work with the classes, or want to see GDI+ doing some specific task, feel free to ask at the codeplex message boards.
Related links
GdiPlus-X at CodePlex http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX
How to convert a bitmap file to monochrome format http://www.news2news.com/vfp/?example=493&function=123
Bob Powell GDI+ FAQS http://www.bobpowell.net/faqmain.htm
Source code