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