TIFFCLASS.CPP Code

//---------------------------------------------------------------------------

#pragma hdrstop

#include "classes.hpp"

#include "tiffclass.h"

 

//---------------------------------------------------------------------------

 

#pragma package(smart_init)

 

//---------------------------------------------------------------------------

__fastcall TIFFFile::TIFFFile(void) {

        Image = NULL;

        StripSize = NumberofStrips = 0;

        Width = Length = RowsPerStrip = 0;

        BitsPerPixel = SamplesPerPixel = 0;

        ImageLoaded = false;

        WarningHandler = NULL;

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::Load(AnsiString fname,Graphics::TBitmap *bitmap) {

        FileName = fname;

        TFileStream *fs = new TFileStream(fname,fmOpenRead);

        if (NULL == (Image = TIFFOpenStream(fs,fmOpenRead)))

                return;

        Load(bitmap);

        delete (TFileStream *)fs;

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::Load(TStream *strm,Graphics::TBitmap *bitmap) {

        FileName = "Stream";

        if (NULL == (Image = TIFFOpenStream(strm,fmOpenRead)))

                return;

        Load(bitmap);

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::Load(Graphics::TBitmap *bitmap) {

        unsigned int i;

        int j,x,w,c;

        HPALETTE hpal = 0;

        uint16 (*cmap)[3];

        LOGPALETTE *plp;

        char *p = NULL;

        char *nbuf;

        long r;

        int bpp;

        int size;

        unsigned char ch;

        BITMAPINFO *pbmi;

        bool gray_scale = false;

 

//        Image = TIFFOpen(fname.c_str(),"r");

        TIFFGetField(Image, TIFFTAG_IMAGEWIDTH,&Width);

        TIFFGetField(Image, TIFFTAG_IMAGELENGTH,&Length);

        TIFFGetField(Image, TIFFTAG_BITSPERSAMPLE,&BitsPerPixel);

        TIFFGetField(Image, TIFFTAG_SAMPLESPERPIXEL,&SamplesPerPixel);

        TIFFGetField(Image, TIFFTAG_ROWSPERSTRIP,&RowsPerStrip);

        TIFFGetField(Image,TIFFTAG_PHOTOMETRIC,&Photo);

        TIFFGetField(Image,TIFFTAG_PLANARCONFIG,&PlanarConfig);

        TIFFGetField(Image,TIFFTAG_FILLORDER,&FillOrder);

        if (!FillOrder)

                FillOrder = 1;

        TIFFGetField(Image,TIFFTAG_COMPRESSION,&Compression);

        TIFFGetField(Image,TIFFTAG_XRESOLUTION,XResolution);

        TIFFGetField(Image,TIFFTAG_YRESOLUTION,YResolution);

        TIFFGetField(Image,TIFFTAG_RESOLUTIONUNIT,&ResolutionUnit);

 

        //ImageLoaded does not refer to the actual picture bits, but rather to

        //the validity of the fields.  If ImageLoaded is true, then we are

        //saving a prior loaded, unchanged picture.  Otherwise we will need to

        //create all these fields.

        ImageLoaded = true;

 

        bpp = BitsPerPixel * SamplesPerPixel;

        switch (bpp) {

                default:

                case 1:

                        bitmap->PixelFormat = pf1bit;

                        bitmap->Monochrome = true;

                        bitmap->IgnorePalette = true;

                        break;

                case 8:

                        bitmap->PixelFormat = pf8bit;

                        break;

                case 16:

                        bitmap->PixelFormat = pf16bit;

                        break;

                case 24:

                        bitmap->PixelFormat = pf24bit;

                        break;

                case 32:

                        bitmap->PixelFormat = pf32bit;

                        break;

        }

 

        x = 1 << bpp;

        cmap = new uint16[x][3];

        if (!TIFFGetField(Image,TIFFTAG_COLORMAP,cmap)) {

                if (bpp == 8) {

                        gray_scale = true;

                        p = new char[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];

                        plp = (LOGPALETTE *)p;

                        plp->palVersion = 0x300;

                        plp->palNumEntries = x;

 

                        for (j=0; j<x; j++) {

                                plp->palPalEntry[j].peRed = j;

                                plp->palPalEntry[j].peGreen = j;

                                plp->palPalEntry[j].peBlue = j;

                                plp->palPalEntry[j].peFlags = 0;

                        }

                }

        } else {

                p = new char[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];

                plp = (LOGPALETTE *)p;

                plp->palVersion = 0x300;

                plp->palNumEntries = x;

 

                for (j=0; j<x; j++) {

                        plp->palPalEntry[j].peRed = cmap[j][0];

                        plp->palPalEntry[j].peGreen = cmap[j][1];

                        plp->palPalEntry[j].peBlue = cmap[j][2];

                        plp->palPalEntry[j].peFlags = 0;

                }

        }

        delete[] cmap;

 

        bitmap->Width = Width;

        bitmap->Height = Length;

 

        StripSize = TIFFStripSize(Image);

        NumberofStrips = TIFFNumberOfStrips(Image);

        unsigned char *buf = new char[StripSize*NumberofStrips];

        for (i=0; i<NumberofStrips; i++) {

                r = TIFFReadEncodedStrip(Image,i,buf+i*StripSize,StripSize);

        }

        if (Photo == PHOTOMETRIC_MINISWHITE){

                for(i = 0; i < StripSize*NumberofStrips; i++)

                buf[i] = ~buf[i];

        }

        if (bpp == 24) {

                size = NumberofStrips * StripSize;

                for (i=0; i<size; i+=3) {

                        ch = buf[i];

                        buf[i] = buf[i+2];

                        buf[i+2] = ch;

                }

        } else {

                if (bpp == 32) {

                        size = NumberofStrips * StripSize;

                        for (i=0; i<size; i+=4) {

                                ch = buf[i];

                                buf[i] = buf[i+2];

                                buf[i+2] = ch;

                        }

                }

        }

 

        LineSize = TIFFScanlineSize(Image);

        w = LineSize;

        if (w % 2)

                ++w;

        size = StripSize * NumberofStrips;

        if (w != LineSize) {

                nbuf = new char[w * Length];

                memset(nbuf,w*Length,'\0');

                for (i=0; i<Length; i++)

                        memcpy(nbuf+i*w,buf+i*LineSize,LineSize);

                delete[] buf;

                buf = nbuf;

                size = w * Length;

        }

 

        if (bitmap->PixelFormat == pf8bit) {

                //I don't like messing with the palette.  I convert 8 bit graphics

                //to 24 bit using the palette.  The routine here is not terribly efficient,

                //but these 8 bit images aren't that common these days, and unless they are very

                //large, you probably won't notice the overhead.

                bitmap->PixelFormat = pf24bit;

                for (i=0; i<bitmap->Height; i++)

                        for (j=0; j<bitmap->Width; j++)

                                bitmap->Canvas->Pixels[j][i] = TColor(plp->palPalEntry[buf[i*w+j]].peRed << 16 | plp->palPalEntry[buf[i*w+j]].peGreen << 8 | plp->palPalEntry[buf[i*w+j]].peBlue);

        } else {

                for (i=0; i<bitmap->Height; i++)

                        memcpy(bitmap->ScanLine[i],buf+i*w,LineSize);

                if (hpal)

                        bitmap->Palette = hpal;

        }

 

        TIFFClose(Image);

        Image = NULL;

        delete[] buf;

        if (p)

                delete[] p;

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::ClearWarningHandler(void) {

        WarningHandler = TIFFSetWarningHandler(NULL);

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::RestoreWarningHandler(void) {

        if (WarningHandler)

                TIFFSetWarningHandler(WarningHandler);

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::SaveStream(TStream *strm,Graphics::TBitmap *bitmap) {

        FileName = "Stream";

        if (NULL == (Image = TIFFOpenStream(strm, fmCreate)))

                return;

        Save(bitmap);

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::Save(AnsiString fname,Graphics::TBitmap *bitmap) {

        FileName = fname;

        if (NULL == (Image = TIFFOpen(fname.c_str(), fmCreate)))

                return;

        Save(bitmap);

        delete (TFileStream *)Image->tif_fd;

}

//---------------------------------------------------------------------------

void __fastcall TIFFFile::Save(Graphics::TBitmap *bitmap) {

        char *buf,*nbuf;

        uint32 i,bpp,w,size;

        uint8 ch;

        BITMAP bm;

 

 

        //Set the necessary fields; I take the compression value from the

        //old file and recalculate all other fields.

        //TIFFFile preserves the essential data so you can resave it as is.

        //For example, if you want to save an image again and change only the

        //compression, all required fields are loaded and you just have to write

        //them.  You would only want to do this if it is certain that the image

        //is unchanged from when it was loaded.

        if (ImageLoaded) {

                TIFFSetField(Image, TIFFTAG_COMPRESSION, Compression);

        } else {

                //You could (and I will) much more intelligently choose the

                //type of compression.  That's something for later!

                Compression = tifComp_PackBits;

                TIFFSetField(Image, TIFFTAG_COMPRESSION, tifComp_PackBits);

        }

        Width = bitmap->Width;

        Length = bitmap->Height;

        TIFFSetField(Image, TIFFTAG_IMAGEWIDTH,Width);

        TIFFSetField(Image, TIFFTAG_IMAGELENGTH,Length);

 

        switch (bitmap->PixelFormat) {

                case pf1bit:

                        BitsPerPixel = 1;

                        break;

                case pf8bit:

                        BitsPerPixel = 8;

                        break;

                case pf16bit:

                        //I don't save in 16 bit

                        bitmap->PixelFormat = pf24bit;

                case pf24bit:

                        BitsPerPixel = 24;

                        break;

                case pf32bit:

                        BitsPerPixel = 32;

                        break;

                case pfCustom:

                case pfDevice:

                default:

                        //When you create a bitmap by pasting an image, it will

                        //have pfCustom or pfDevice as its pixel format.  We

                        //access the actual format and set the format accordingly.

                        GetObject(bitmap->Handle,sizeof(BITMAP),&bm);

                        BitsPerPixel = bm.bmBitsPixel;

                        if (BitsPerPixel == 16) {

                                bitmap->PixelFormat = pf24bit;

                                BitsPerPixel = 24;

                        }

                        break;

        }

        TIFFSetField(Image, TIFFTAG_BITSPERSAMPLE,BitsPerPixel);

        SamplesPerPixel = 1;

        TIFFSetField(Image, TIFFTAG_SAMPLESPERPIXEL,SamplesPerPixel);

 

        //I use as close to libtiff defaults as possible

        RowsPerStrip = Length / 16;

        RowsPerStrip = TIFFDefaultStripSize(Image,RowsPerStrip);

        TIFFSetField(Image, TIFFTAG_ROWSPERSTRIP,RowsPerStrip);

        NumberofStrips = TIFFNumberOfStrips(Image);

        StripSize = TIFFStripSize(Image);

 

        Photo = PHOTOMETRIC_MINISBLACK;

        TIFFSetField(Image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);

        FillOrder = 1;

        TIFFSetField(Image, TIFFTAG_FILLORDER, FillOrder);

        PlanarConfig = 1;

        TIFFSetField(Image, TIFFTAG_PLANARCONFIG, PlanarConfig);

 

        //This will not be an accurate resolution, since it is taken from the

        //screen, but since I don't use it, I don't worry about it.  I assume that

        //if you have resolution requirements, you'll know how to calculate and

        //use them.

        int lpx,lpy;

        lpx = GetDeviceCaps(bitmap->Canvas->Handle,LOGPIXELSX);

        lpy = GetDeviceCaps(bitmap->Canvas->Handle,LOGPIXELSY);

        XResolution[0] = lpx;

        XResolution[1] = 1;

        YResolution[0] = lpy;

        YResolution[1] = 1;

        TIFFSetField(Image, TIFFTAG_XRESOLUTION,XResolution);

        TIFFSetField(Image, TIFFTAG_YRESOLUTION,YResolution);

        ResolutionUnit = 2;

        TIFFSetField(Image, TIFFTAG_RESOLUTIONUNIT, ResolutionUnit);

        ImageLoaded = true;

        //This completes the fields required for a compliant tiff.

 

        //Correct the line offsets from bitmap to TIFF format

        LineSize = TIFFScanlineSize(Image);

        w = LineSize;

        if (w % 2)

                ++w;

        //w now represents the line width in bytes in the bitmap; LineSize in the TIFF

 

        //Get the bits we need to save

        bpp = BitsPerPixel * SamplesPerPixel;

        buf = new char[bitmap->Width*bitmap->Height*bpp];

        for (i=0; i<bitmap->Height; i++)

                memcpy(buf+i*LineSize,bitmap->ScanLine[i],w);

 

        //We reinvert our RGB information

        //The RGB order is reversed between the TIFF bits and of bitmap bits

        if (bpp == 24) {

                size = NumberofStrips * StripSize;

                for (i=0; i<size; i+=3) {

                        ch = buf[i];

                        buf[i] = buf[i+2];

                        buf[i+2] = ch;

                }

        } else {

                if (bpp == 32) {

                        size = NumberofStrips * StripSize;

                        for (i=0; i<size; i+=4) {

                                ch = buf[i];

                                buf[i] = buf[i+2];

                                buf[i+2] = ch;

                        }

                }

        }

 

        size = StripSize * NumberofStrips;

 

        //Save the strips; for the moment we only save in strips

        for (i=0; i<NumberofStrips; i++) {

                //Right now, I'm not checking the errors.  You'll get warnings

                //from libtiff based on your setting of the warning handler,

                //and error messages are on.

                TIFFWriteEncodedStrip(Image,i,buf+i*StripSize,StripSize);

        }

 

        delete[] buf;

        TIFFClose(Image);

}

//---------------------------------------------------------------------------

//The following functions are passed to TIFF for file IO.  These very simple

//functions represent the major reason I created this class.  They read from

//a stream rather than an ordinary windows file handle, so you can save images

//in TIFF format in a blob or directly in memory using a stream.

static tsize_t _tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) {

            DWORD dwSizeRead;

            if ((dwSizeRead = ((TStream *)fd)->Read(buf,size)) == 0)

                        return(0);

            return ((tsize_t) dwSizeRead);

}

//---------------------------------------------------------------------------

static tsize_t _tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) {

            DWORD dwSizeWritten;

 

        if ((dwSizeWritten = ((TStream *)fd)->Write(buf,size)) != size)

                        return(0);

            return ((tsize_t)dwSizeWritten);

}

//---------------------------------------------------------------------------

static toff_t _tiffSeekProc(thandle_t fd, toff_t off, int whence) {

            Word wMoveMethod;

        int r;

        uint32 pos;

 

        /* we use this as a special code, so avoid accepting it */

        if( off == 0xFFFFFFFF )

            return 0xFFFFFFFF;

 

            switch(whence)

            {

            case SEEK_SET:

                        wMoveMethod = soFromBeginning;

                        break;

            case SEEK_CUR:

                        wMoveMethod = soFromCurrent;

                        break;

            case SEEK_END:

                        wMoveMethod = soFromEnd;

                        break;

            default:

                        wMoveMethod = soFromBeginning;

                        break;

            }

        r = ((toff_t)((TStream *)fd)->Seek(off,wMoveMethod));

        pos = ((TStream *)fd)->Position;

        //This may look a little clumsy, but it is essential to allow this class

        //to be used with Blobs.  In a file, if you position the file past the end

        //in write mode, the file size is extended up to that point.  In a blob, the

        //next write will produce an error.  This simple code extends the size of the

        //file.  I put it *after* the seek, because in the case of a file handle

        //the fd->Size will not be less than the position and we don't waste time

        //writing ballast to our file, but in case of a blob, we will.

        if (pos > ((TStream *)fd)->Size) {

                ((TStream *)fd)->Seek(0,soFromEnd);

                int sz = pos - ((TStream *)fd)->Size;

                char *b = new char[sz];

                memset(b,'\0',sz);

                ((TStream *)fd)->Write(b,sz);

        }

            return r;

}

//---------------------------------------------------------------------------

static int _tiffCloseProc(thandle_t fd)

{

        //DO NOT DELETE your TStream here.  If you are using TIFFFile with

        //objects such as my eneTiffBitmap, the library routines will be called

        //with an already open stream, which the calling component will then

        //delete.

            return 0;

}

//---------------------------------------------------------------------------

static toff_t _tiffSizeProc(thandle_t fd)

{

            return ((toff_t)((TStream *)fd)->Size);

}

//---------------------------------------------------------------------------

 

//I do not do anything with mapped files.  I use this for plain read and write

 

//which is what is needed to work with TImage and TDBImage as well as the related

 

//controls I have written.  Thus, I use the dummy routines for mapping.  These

 

//are taken from tif_win32.c

 

static void

 

_tiffDummyUnmapProc(thandle_t fd, tdata_t base, toff_t size)

{

}

//---------------------------------------------------------------------------

static int

_tiffDummyMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize)

{

            return (0);

}

//---------------------------------------------------------------------------

//I've left the bmXXXX constants in with comments.  They are identical to their

//file counterparts.

static const char *GetTiffMode(Word mode) {

        char *m = "";

 

        switch (mode) {

                default:

//                case bmRead:

                case fmOpenRead:

                        m = "r";

                        break;

//                case bmWrite:

                case fmCreate:

                case fmOpenWrite:

                        m = "w";

                        break;

//                case bmReadWrite:

                case fmOpenReadWrite:

                        m = "a";

                        break;

        }

        return m;

}

//---------------------------------------------------------------------------

//The following two functions, TIFFOpenStream and TIFFOpen are critical.

//TIFFOpenStream is the most commonly used, as it will be called by the

//eneTIFFBitmap class  TIFFOpen simply provides an option for explicitly opening

//a file.  Note that if you use TIFFOpen, your stream will *NOT* be deleted, and

//thus your file closed automatically.  You need to explicitly delete the TFileStream

//after you're done with the TIFF.

TIFF * __fastcall TIFFFile::TIFFOpenStream(TStream *strm,Word mode) {

        const char *m;

        TIFF *tif;

 

        m = GetTiffMode(mode);

            tif = TIFFClientOpen("Stream", m, (thandle_t)strm,

                _tiffReadProc, _tiffWriteProc,

                _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,

            _tiffDummyMapProc, _tiffDummyUnmapProc);

            if (tif)

                        tif->tif_fd = (int)strm;

        return tif;

}

//---------------------------------------------------------------------------

TIFF * __fastcall TIFFFile::TIFFOpen(AnsiString fname,Word mode) {

            thandle_t fd;

        const char *m;

        TIFF *tif;

 

            fd = (thandle_t)new TFileStream(fname,mode,fmShareExclusive);

        return TIFFOpenStream((TStream *)fd,mode);

}

 

 

 


Links to project files:

Project file and modified tiffio.h/tiffiop.h files for building libtiff with BCB6

Project group, project files and all support for the eneTIFFBitmap object

LIBTIFF Home Page