/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkTIFFReader.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkTIFFReader.h" #include "vtkImageData.h" #include "vtkObjectFactory.h" #include "vtkToolkits.h" #include extern "C" { #include "vtk_tiff.h" } //------------------------------------------------------------------------- vtkStandardNewMacro(vtkTIFFReader); vtkCxxRevisionMacro(vtkTIFFReader, "$Revision: 1.51 $"); class vtkTIFFReaderInternal { public: vtkTIFFReaderInternal(); int Initialize(); void Clean(); int CanRead(); int Open( const char *filename ); TIFF *Image; unsigned int Width; unsigned int Height; unsigned short SamplesPerPixel; unsigned short Compression; unsigned short BitsPerSample; unsigned short Photometrics; unsigned short PlanarConfig; unsigned long int TileDepth; static void ErrorHandler(const char* module, const char* fmt, va_list ap); }; extern "C" { void vtkTIFFReaderInternalErrorHandler(const char* vtkNotUsed(module), const char* vtkNotUsed(fmt), va_list vtkNotUsed(ap)) { // Do nothing // Ignore errors } } //------------------------------------------------------------------------- int vtkTIFFReaderInternal::Open( const char *filename ) { this->Clean(); struct stat fs; if ( stat(filename, &fs) ) { return 0; } this->Image = TIFFOpen(filename, "r"); if ( !this->Image) { this->Clean(); return 0; } if ( !this->Initialize() ) { this->Clean(); return 0; } return 1; } //------------------------------------------------------------------------- void vtkTIFFReaderInternal::Clean() { if ( this->Image ) { TIFFClose(this->Image); } this->Image=NULL; this->Width = 0; this->Height = 0; this->SamplesPerPixel = 0; this->Compression = 0; this->BitsPerSample = 0; this->Photometrics = 0; this->PlanarConfig = 0; this->TileDepth = 0; } //------------------------------------------------------------------------- vtkTIFFReaderInternal::vtkTIFFReaderInternal() { this->Image = NULL; TIFFSetErrorHandler(&vtkTIFFReaderInternalErrorHandler); TIFFSetWarningHandler(&vtkTIFFReaderInternalErrorHandler); this->Clean(); } //------------------------------------------------------------------------- int vtkTIFFReaderInternal::Initialize() { if ( this->Image ) { if ( !TIFFGetField(this->Image, TIFFTAG_IMAGEWIDTH, &this->Width) || !TIFFGetField(this->Image, TIFFTAG_IMAGELENGTH, &this->Height) ) { return 0; } TIFFGetField(this->Image, TIFFTAG_SAMPLESPERPIXEL, &this->SamplesPerPixel); TIFFGetField(this->Image, TIFFTAG_COMPRESSION, &this->Compression); TIFFGetField(this->Image, TIFFTAG_BITSPERSAMPLE, &this->BitsPerSample); TIFFGetField(this->Image, TIFFTAG_PHOTOMETRIC, &this->Photometrics); TIFFGetField(this->Image, TIFFTAG_PLANARCONFIG, &this->PlanarConfig); if ( !TIFFGetField(this->Image, TIFFTAG_TILEDEPTH, &this->TileDepth) ) { this->TileDepth = 0; } } return 1; } //------------------------------------------------------------------------- int vtkTIFFReaderInternal::CanRead() { return ( this->Image && ( this->Width > 0 ) && ( this->Height > 0 ) && ( this->SamplesPerPixel > 0 ) && ( this->Compression == COMPRESSION_NONE ) && ( this->Photometrics == PHOTOMETRIC_RGB || this->Photometrics == PHOTOMETRIC_MINISWHITE || this->Photometrics == PHOTOMETRIC_MINISBLACK || this->Photometrics == PHOTOMETRIC_PALETTE ) && this->PlanarConfig == PLANARCONFIG_CONTIG && ( !this->TileDepth ) && ( this->BitsPerSample == 8 ) || this->BitsPerSample == 32 ); } //------------------------------------------------------------------------- vtkTIFFReader::vtkTIFFReader() { this->InitializeColors(); this->InternalImage = new vtkTIFFReaderInternal; this->InternalExtents = 0; } //------------------------------------------------------------------------- vtkTIFFReader::~vtkTIFFReader() { delete this->InternalImage; } //------------------------------------------------------------------------- void vtkTIFFReader::ExecuteInformation() { this->InitializeColors(); this->ComputeInternalFileName(this->DataExtent[4]); if (this->InternalFileName == NULL) { return; } if ( !this->InternalImage->Open(this->InternalFileName) ) { vtkErrorMacro("Unable to open file " << this->InternalFileName ); this->DataExtent[0] = 0; this->DataExtent[1] = 0; this->DataExtent[2] = 0; this->DataExtent[3] = 0; this->DataExtent[4] = 0; this->DataExtent[5] = 0; this->SetNumberOfScalarComponents(1); this->vtkImageReader2::ExecuteInformation(); return; } // pull out the width/height, etc. this->DataExtent[0] = 0; this->DataExtent[1] = this->GetInternalImage()->Width - 1; this->DataExtent[2] = 0; this->DataExtent[3] = this->GetInternalImage()->Height - 1; switch (this->GetInternalImage()->BitsPerSample) { case 32: this->SetDataScalarTypeToFloat(); break; default: this->SetDataScalarTypeToUnsignedChar(); } switch ( this->GetFormat() ) { case vtkTIFFReader::GRAYSCALE: case vtkTIFFReader::PALETTE_GRAYSCALE: this->SetNumberOfScalarComponents( 1 ); break; case vtkTIFFReader::RGB: this->SetNumberOfScalarComponents( this->GetInternalImage()->SamplesPerPixel ); break; case vtkTIFFReader::PALETTE_RGB: this->SetNumberOfScalarComponents( 3 ); break; default: this->SetNumberOfScalarComponents( 4 ); } if ( !this->GetInternalImage()->CanRead() ) { this->SetNumberOfScalarComponents( 4 ); } this->vtkImageReader2::ExecuteInformation(); // close the file this->GetInternalImage()->Clean(); } //------------------------------------------------------------------------- template void vtkTIFFReaderUpdate2(vtkTIFFReader *self, OT *outPtr, int *outExt, vtkIdType* vtkNotUsed(outInc), long) { if ( !self->GetInternalImage()->Open(self->GetInternalFileName()) ) { return; } self->InitializeColors(); self->ReadImageInternal(self->GetInternalImage()->Image, outPtr, outExt, sizeof(OT) ); // close the file self->GetInternalImage()->Clean(); } //---------------------------------------------------------------------------- // This function reads in one data of data. // templated to handle different data types. template void vtkTIFFReaderUpdate(vtkTIFFReader *self, vtkImageData *data, OT *outPtr) { vtkIdType outIncr[3]; int outExtent[6]; OT *outPtr2; data->GetExtent(outExtent); data->GetIncrements(outIncr); long pixSize = data->GetNumberOfScalarComponents()*sizeof(OT); outPtr2 = outPtr; int idx2; for (idx2 = outExtent[4]; idx2 <= outExtent[5]; ++idx2) { self->ComputeInternalFileName(idx2); // read in a TIFF file vtkTIFFReaderUpdate2(self, outPtr2, outExtent, outIncr, pixSize); self->UpdateProgress((idx2 - outExtent[4])/ (outExtent[5] - outExtent[4] + 1.0)); outPtr2 += outIncr[2]; } } //---------------------------------------------------------------------------- // This function reads a data from a file. The datas extent/axes // are assumed to be the same as the file extent/order. void vtkTIFFReader::ExecuteData(vtkDataObject *output) { vtkImageData *data = this->AllocateOutputData(output); if (this->InternalFileName == NULL) { vtkErrorMacro("Either a FileName or FilePrefix must be specified."); return; } this->ComputeDataIncrements(); // Call the correct templated function for the output void *outPtr; // Call the correct templated function for the input outPtr = data->GetScalarPointer(); switch (data->GetScalarType()) { vtkTemplateMacro(vtkTIFFReaderUpdate(this, data, (VTK_TT *)(outPtr))); default: vtkErrorMacro("UpdateFromFile: Unknown data type"); } } //---------------------------------------------------------------------------- unsigned int vtkTIFFReader::GetFormat() { unsigned int cc; if ( this->ImageFormat != vtkTIFFReader::NOFORMAT ) { return this->ImageFormat; } switch ( this->GetInternalImage()->Photometrics ) { case PHOTOMETRIC_RGB: case PHOTOMETRIC_YCBCR: this->ImageFormat = vtkTIFFReader::RGB; return this->ImageFormat; case PHOTOMETRIC_MINISWHITE: case PHOTOMETRIC_MINISBLACK: this->ImageFormat = vtkTIFFReader::GRAYSCALE; return this->ImageFormat; case PHOTOMETRIC_PALETTE: for( cc=0; cc<256; cc++ ) { unsigned short red, green, blue; this->GetColor( cc, &red, &green, &blue ); if ( red != green || red != blue ) { this->ImageFormat = vtkTIFFReader::PALETTE_RGB; return this->ImageFormat; } } this->ImageFormat = vtkTIFFReader::PALETTE_GRAYSCALE; return this->ImageFormat; } this->ImageFormat = vtkTIFFReader::OTHER; return this->ImageFormat; } //---------------------------------------------------------------------------- void vtkTIFFReader::GetColor( int index, unsigned short *red, unsigned short *green, unsigned short *blue ) { *red = 0; *green = 0; *blue = 0; if ( index < 0 ) { vtkErrorMacro("Color index has to be greater than 0"); return; } if ( this->TotalColors > 0 && this->ColorRed && this->ColorGreen && this->ColorBlue ) { if ( index >= this->TotalColors ) { vtkErrorMacro("Color index has to be less than number of colors (" << this->TotalColors << ")"); return; } *red = *(this->ColorRed + index); *green = *(this->ColorGreen + index); *blue = *(this->ColorBlue + index); return; } unsigned short photometric; if (!TIFFGetField(this->GetInternalImage()->Image, TIFFTAG_PHOTOMETRIC, &photometric)) { if ( this->GetInternalImage()->Photometrics != PHOTOMETRIC_PALETTE ) { vtkErrorMacro("You can only access colors for palette images"); return; } } unsigned short *red_orig, *green_orig, *blue_orig; switch (this->GetInternalImage()->BitsPerSample) { case 1: case 2: case 4: case 8: case 16: break; default: vtkErrorMacro( "Sorry, can not image with " << this->GetInternalImage()->BitsPerSample << "-bit samples" ); return; } if (!TIFFGetField(this->GetInternalImage()->Image, TIFFTAG_COLORMAP, &red_orig, &green_orig, &blue_orig)) { vtkErrorMacro("Missing required \"Colormap\" tag"); return; } this->TotalColors = (1L << this->GetInternalImage()->BitsPerSample); if ( index >= this->TotalColors ) { vtkErrorMacro("Color index has to be less than number of colors (" << this->TotalColors << ")"); return; } this->ColorRed = red_orig; this->ColorGreen = green_orig; this->ColorBlue = blue_orig; *red = *(red_orig + index); *green = *(green_orig + index); *blue = *(blue_orig + index); } //------------------------------------------------------------------------- void vtkTIFFReader::InitializeColors() { this->ColorRed = 0; this->ColorGreen = 0; this->ColorBlue = 0; this->TotalColors = -1; this->ImageFormat = vtkTIFFReader::NOFORMAT; } //------------------------------------------------------------------------- template void ReadTiledImage(vtkTIFFReader *self, void *out, unsigned int width, unsigned int height, unsigned int vtkNotUsed(size), int *internalExtents) { TIFF *tiff; uint32 tileWidth, tileLength, x, y, yi, rows, cols, tileSize; int xx,yy; int pixelDepth = self->GetInternalImage()->SamplesPerPixel; T *image; uint32 imagepos; image = (T*)out; tiff = self->GetInternalImage()->Image; TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth); TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileLength); tileSize = TIFFTileSize(tiff); tdata_t buffer = _TIFFmalloc(tileSize); for(yi=0;yi width - x) { cols = width-x; } else { cols = tileWidth; } if(tileLength > height - y) { rows = height - y; } else { rows = tileLength; } for(uint32 j = 0;j< rows;j++) { for(uint32 i=0;i= internalExtents[0] && xx <= internalExtents[1] && yy >= internalExtents[2] && yy <= internalExtents[3] ) { imagepos = (xx + width*yy)*pixelDepth; self->EvaluateImageAt(image+imagepos, static_cast(buffer)+tilepos); } } } } } _TIFFfree(buffer); } //------------------------------------------------------------------------- template void ReadScanlineImage(vtkTIFFReader *self, void *out, unsigned int vtkNotUsed(width), unsigned height, unsigned int vtkNotUsed(size), int *internalExtents) { unsigned int isize = TIFFScanlineSize(self->GetInternalImage()->Image); unsigned int cc; int row, inc; int xx=0, yy=0; tdata_t buf = _TIFFmalloc(isize); T *image = (T *)out; unsigned int c_inc = self->GetInternalImage()->SamplesPerPixel; // * self->GetInternalImage()->BitsPerSample; if ( self->GetInternalImage()->PlanarConfig == PLANARCONFIG_CONTIG ) { for ( row = height-1; row >= 0; row -- ) { if (TIFFReadScanline(self->GetInternalImage()->Image, buf, row, 0) <= 0) { cout << "Problem reading the row: " << row << endl; break; } for (cc = 0; cc < isize; cc += c_inc ) { if ( xx >= internalExtents[0] && xx <= internalExtents[1] && yy >= internalExtents[2] && yy <= internalExtents[3] ) { //unsigned char *c = static_cast(buf)+cc; inc = self->EvaluateImageAt( image, static_cast(buf) + cc ); image += inc; } xx++; } xx=0; yy++; } } else { cout << "This reader can only do PLANARCONFIG_CONTIG" << endl; } _TIFFfree(buf); } //------------------------------------------------------------------------- void vtkTIFFReader::ReadImageInternal( void* vtkNotUsed(in), void* outPtr, int* outExt, unsigned int size ) { if ( this->GetInternalImage()->Compression == COMPRESSION_OJPEG ) { vtkErrorMacro("This reader cannot read old JPEG compression"); return; } int width = this->GetInternalImage()->Width; int height = this->GetInternalImage()->Height; this->InternalExtents = outExt; if ( !this->GetInternalImage()->CanRead() ) { uint32 *tempImage = static_cast( outPtr ); if ( this->InternalExtents[0] != 0 || this->InternalExtents[1] != width -1 || this->InternalExtents[2] != 0 || this->InternalExtents[3] != height-1 ) { tempImage = new uint32[ width * height ]; } if ( !TIFFReadRGBAImage(this->GetInternalImage()->Image, width, height, tempImage, 0 ) ) { vtkErrorMacro("Problem reading RGB image"); if ( tempImage != outPtr ) { delete [] tempImage; } return; } int xx, yy; uint32* ssimage = tempImage; unsigned char *fimage = (unsigned char *)outPtr; for ( yy = 0; yy < height; yy ++ ) { for ( xx = 0; xx < width; xx++ ) { if ( xx >= this->InternalExtents[0] && xx <= this->InternalExtents[1] && yy >= this->InternalExtents[2] && yy <= this->InternalExtents[3] ) { unsigned char red = static_cast(TIFFGetR(*ssimage)); unsigned char green = static_cast(TIFFGetG(*ssimage)); unsigned char blue = static_cast(TIFFGetB(*ssimage)); unsigned char alpha = static_cast(TIFFGetA(*ssimage)); *(fimage ) = red;//red; *(fimage+1) = green;//green; *(fimage+2) = blue;//blue; *(fimage+3) = alpha;//alpha; fimage += 4; } ssimage ++; } } if ( tempImage != 0 && tempImage != outPtr ) { delete [] tempImage; } return; } unsigned int format = this->GetFormat(); if ( this->GetInternalImage()->Compression == COMPRESSION_PACKBITS ) { height /= this->GetInternalImage()->BitsPerSample; } switch ( format ) { case vtkTIFFReader::GRAYSCALE: case vtkTIFFReader::RGB: case vtkTIFFReader::PALETTE_RGB: case vtkTIFFReader::PALETTE_GRAYSCALE: if (TIFFIsTiled(this->GetInternalImage()->Image)) { switch(this->GetInternalImage()->BitsPerSample) { case 32: ReadTiledImage(this, outPtr, width, height, size, this->InternalExtents); break; default: ReadTiledImage(this, outPtr, width, height, size, this->InternalExtents); } } else { switch(this->GetInternalImage()->BitsPerSample) { case 32: ReadScanlineImage(this, outPtr, width, height, size, this->InternalExtents); break; default: ReadScanlineImage(this, outPtr, width, height, size, this->InternalExtents); } } break; default: return; } } //------------------------------------------------------------------------- int vtkTIFFReader::EvaluateImageAt( void* out, void* in ) { unsigned char *image = (unsigned char *)out; unsigned char *source = (unsigned char *)in; int increment; unsigned short red, green, blue, alpha; switch ( this->GetFormat() ) { case vtkTIFFReader::GRAYSCALE: if ( this->GetInternalImage()->Photometrics == PHOTOMETRIC_MINISBLACK ) { *image = *source; } else { *image = ~( *source ); } increment = 1; break; case vtkTIFFReader::PALETTE_GRAYSCALE: this->GetColor(*source, &red, &green, &blue); *image = static_cast(red >> 8); increment = 1; break; case vtkTIFFReader::RGB: red = *(source); green = *(source+1); blue = *(source+2); *(image) = red; *(image+1) = green; *(image+2) = blue; if ( this->GetInternalImage()->SamplesPerPixel == 4 ) { alpha = *(source+3); *(image+3) = 255-alpha; } increment = this->GetInternalImage()->SamplesPerPixel; break; case vtkTIFFReader::PALETTE_RGB: this->GetColor(*source, &red, &green, &blue); *(image) = static_cast(red >> 8); *(image+1) = static_cast(green >> 8); *(image+2) = static_cast(blue >> 8); increment = 3; break; default: return 0; } return increment; } //------------------------------------------------------------------------- int vtkTIFFReader::CanReadFile(const char* fname) { vtkTIFFReaderInternal tf; int res = tf.Open(fname); tf.Clean(); if (res) { return 3; } return 0; } //---------------------------------------------------------------------------- void vtkTIFFReader::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); }