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
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


 

The main part of the previous code is below:

&& 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


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

 

 

1 comment:

  1. I have done it as you instructured but the problem is when i tried to change the compression with EncoderValueCompressionRle (packbits), the gdi+ returns invalid parameter error?


    why?

    ReplyDelete