/* * This file is part of rasdaman community. * * Rasdaman community is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Rasdaman community is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rasdaman community. If not, see . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . */ #include #include #ifdef AIX #include #endif #include "conversion/tiff.hh" #include "conversion/memfs.hh" #include "raslib/error.hh" #include "raslib/rminit.hh" #include "raslib/parseparams.hh" #include "debug/debug.hh" const int r_Conv_TIFF::defaultRPS = 32; const char r_Conv_TIFF::dummyFileFmt[] = "/tmp/%p.tif"; const unsigned int r_Conv_TIFF::TIFF_DEFAULT_QUALITY = 80; const struct r_Convertor::convert_string_s r_Conv_TIFF::compNames[] = { {"none", COMPRESSION_NONE}, {"ccittrle", COMPRESSION_CCITTRLE}, {"ccittfax3", COMPRESSION_CCITTFAX3}, {"ccittfax4", COMPRESSION_CCITTFAX4}, {"lzw", COMPRESSION_LZW}, {"ojpeg", COMPRESSION_OJPEG}, {"jpeg", COMPRESSION_JPEG}, {"next", COMPRESSION_NEXT}, {"ccittrlew", COMPRESSION_CCITTRLEW}, {"packbits", COMPRESSION_PACKBITS}, {"thunderscan", COMPRESSION_THUNDERSCAN}, {"pixarfilm", COMPRESSION_PIXARFILM}, {"pixarlog", COMPRESSION_PIXARLOG}, {"deflate", COMPRESSION_DEFLATE}, {"dcs", COMPRESSION_DCS}, {"jbig", COMPRESSION_JBIG}, #ifndef LINUX {"sgilog", COMPRESSION_SGILOG}, {"sgilog24", COMPRESSION_SGILOG24}, {"it8ctpad", COMPRESSION_IT8CTPAD}, {"it8lw", COMPRESSION_IT8LW}, {"it8mp", COMPRESSION_IT8MP}, {"it8bl", COMPRESSION_IT8BL}, #endif {NULL, COMPRESSION_NONE} }; const struct r_Convertor::convert_string_s r_Conv_TIFF::resunitNames[] = { {"none", RESUNIT_NONE}, {"inch", RESUNIT_INCH}, {"centimeter", RESUNIT_CENTIMETER}, {NULL, RESUNIT_NONE} }; // Change these according to the platform! // Fill order of bits in bitmap mode. Define 0 for LSB, otherwise MSB #define _R_TIFF_BITFILLORDER 1 // Setup internal macros according to the fill-order #if (_R_TIFF_BITFILLORDER == 0) #define _R_TIFF_MASK_VALUE 1 #define _R_TIFF_MASK_SHIFT(x) (x) <<= 1; #else #define _R_TIFF_MASK_VALUE (1<<7) #define _R_TIFF_MASK_SHIFT(x) (x) >>= 1; #endif // TIFF class functions /// Translate string compression type to libtiff compression type int r_Conv_TIFF::get_compression_from_name(const char* strComp) { ENTER( "r_Conv_TIFF::get_compression_from_name( " << (strComp?strComp:"(null)") << " )" ); unsigned short i=0; int tiffComp=COMPRESSION_NONE; if(strComp != NULL) { for (i=0; compNames[i].key != NULL; i++) { if (strcasecmp(compNames[i].key, strComp) == 0) { tiffComp = compNames[i].id; break; } } if (compNames[i].key == NULL) { TALK( "r_Conv_TIFF::get_compression_from_name(): error: unsupported compression type " << strComp << "." ); RMInit::logOut << "Error: unsupported compression type " << strComp << "." << endl; } } LEAVE( "r_Conv_TIFF::get_compression_from_name() -> " << tiffComp ); return tiffComp; } /// Translate string resolution unit type to libtiff resolution unit type int r_Conv_TIFF::get_resunit_from_name(const char* strResUnit) { ENTER( "r_Conv_TIFF::get_resunit_from_name( " << (strResUnit?strResUnit:"(null)") << " )" ); unsigned short i=0; int tiffResUnit=RESUNIT_NONE; if(strResUnit != NULL) { for (i=0; resunitNames[i].key != NULL; i++) { if (strcasecmp(resunitNames[i].key, strResUnit) == 0) { tiffResUnit = resunitNames[i].id; break; } } if (resunitNames[i].key == NULL) { TALK( "r_Conv_TIFF::get_resunit_from_name(): error: unsupported resolution unit type " << strResUnit << "." ); RMInit::logOut << "Error: unsupported resolution unit type " << strResUnit << "." << endl; } } LEAVE( "r_Conv_TIFF::get_resunit_from_name() -> " << tiffResUnit ); return tiffResUnit; } /// internal initialization, common to all constructors void r_Conv_TIFF::initTIFF( void ) { ENTER( "r_Conv_TIFF::initTIFF()" ); compType = NULL; quality = r_Conv_TIFF::TIFF_DEFAULT_QUALITY; override_bpp = 0; override_bps = 0; override_depth = 0; if (params == NULL) params = new r_Parse_Params(); params->add("comptype", &compType, r_Parse_Params::param_type_string); params->add("quality", &quality, r_Parse_Params::param_type_int); params->add("bpp", &override_bpp, r_Parse_Params::param_type_int); params->add("bps", &override_bps, r_Parse_Params::param_type_int); params->add("depth", &override_depth, r_Parse_Params::param_type_int); LEAVE( "r_Conv_TIFF::initTIFF()" ); } /// constructor using type structure r_Conv_TIFF::r_Conv_TIFF(const char *src, const r_Minterval &interv, const r_Type *tp) throw(r_Error) : r_Convert_Memory(src, interv, tp, true) { ENTER( "r_Conv_TIFF::r_Conv_TIFF( " << (src?src:"(null)") << ", (minterval), (type ptr) )" ); initTIFF(); LEAVE( "r_Conv_TIFF::r_Conv_TIFF()" ); } /// constructor using int type indicator r_Conv_TIFF::r_Conv_TIFF(const char *src, const r_Minterval &interv, int type) throw(r_Error) : r_Convert_Memory(src, interv, type) { ENTER( "r_Conv_TIFF::r_Conv_TIFF( " << (src?src:"(null)") << ", (minterval), " << type << " )" ); initTIFF(); LEAVE( "r_Conv_TIFF::r_Conv_TIFF()" ); } /// destructor r_Conv_TIFF::~r_Conv_TIFF(void) { ENTER( "r_Conv_TIFF::~r_Conv_TIFF()" ); if (compType != NULL) { delete [] compType; compType = NULL; } LEAVE( "r_Conv_TIFF::~r_Conv_TIFF()" ); } /// convert array to TIFF stream // Compression modes recommended: // Bitmap, Greyscales: COMPRESSION_LZW, COMPRESSION_DEFLATE // RGB: COMPRESSION_JPEG, COMPRESSION_SGILOG24 r_convDesc &r_Conv_TIFF::convertTo( const char *options ) throw(r_Error) { ENTER( "r_Conv_TIFF::convertTo( " << (options?options:"(null)") << " )" ); if (options != NULL) printf("tiff convert option = %s\n", options); else printf("tiff options are null = %s\n", options); TIFF *tif=NULL; char dummyFile[256]; uint16 cmap[256]; // Colour map (for greyscale images) uint32 pixelAdd=0, lineAdd=0; // number of _bytes_ to add to a pointer // to the source data to get the address // of the pixel to the right / downwards. uint16 bps=0, bpp=0; uint32 width=0, height=0, i=0; int tiffcomp = COMPRESSION_NONE; params->process(options); // translate string compression type to libtiff compression type if (compType != NULL) tiffcomp=get_compression_from_name(compType); // Set dimensions width = (uint32)(desc.srcInterv[0].high() - desc.srcInterv[0].low() + 1); height = (uint32)(desc.srcInterv[1].high() - desc.srcInterv[1].low() + 1); switch (desc.baseType) { // MDD arrays are transposed compared to the format needed for images. // Therefore the pixelAdd and lineAdd values change places. case ctype_bool: bps = 1; bpp = 1; pixelAdd = height; lineAdd = 1; break; case ctype_char: bps = 8; bpp = 8; pixelAdd = height; lineAdd = 1; break; case ctype_rgb: bps = 8; bpp = 24; pixelAdd = 3*height; lineAdd = 3; break; case ctype_uint16: bps = 16; bpp = 16; pixelAdd = 2*height; lineAdd = 2; break; case ctype_float32: bps = 32; bpp = 32; pixelAdd = 4*height; lineAdd = 4; break; default: TALK( "r_Conv_TIFF::convertTo(): error: unsupported base type " << desc.baseType << "." ); RMInit::logOut << "Error: encountered unsupported TIFF base type." << endl; throw r_Error(BASETYPENOTSUPPORTEDBYOPERATION); } // Just to make sure nothing serious goes wrong if this conversion // function is called more than once. memfs_newfile(handle); // Open a dummy output file (all operations will be redirected to // Memory). Make dummy file unique for each object by using the // address of its memFSContext (kind of a hack, I know...). That // should ensure re-entrancy. sprintf(dummyFile, dummyFileFmt, (void*)handle); tif = TIFFClientOpen(dummyFile, "w", handle, memfs_read, memfs_write, memfs_seek, memfs_close, memfs_size, memfs_map, memfs_unmap); if (tif == NULL) { RMInit::logOut << "r_Conv_TIFF::convertTo(): couldn't open file " << dummyFile << endl; throw r_Error(r_Error::r_Error_General); } TIFFSetField(tif, TIFFTAG_ARTIST, "rasdaman"); TIFFSetField(tif, TIFFTAG_DOCUMENTNAME, "exported from rasdaman database"); TIFFSetField(tif, TIFFTAG_SOFTWARE, "rasdaman"); //TIFFSetField(tif, TIFFTAG_SUBFILETYPE, (uint32)0); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); // UNIX doesn't mind which fill-order. NT only understands this one. TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16)tiffcomp); TIFFSetField(tif, TIFFTAG_ORIENTATION, (uint16)ORIENTATION_TOPLEFT); // Format-dependent tags if (desc.baseType == ctype_rgb) { TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16)PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16)3); } else { if (desc.baseType == ctype_char) { TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16)PHOTOMETRIC_PALETTE); } else { TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16)PHOTOMETRIC_MINISBLACK); } // FIXME: STILL HAVE TO ADD FOR other CASES eg ctype_uint16 and ctype_float32 ??? TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16)1); } TIFFSetField(tif, TIFFTAG_PLANARCONFIG, (uint16)PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, (uint32)-1)); //TIFFSetField(tif, TIFFTAG_MINSAMPLEVALUE, (uint16)0); //TIFFSetField(tif, TIFFTAG_MAXSAMPLEVALUE, (uint16)255); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)RESUNIT_INCH); TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)90.0); TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)90.0); TIFFSetField(tif, TIFFTAG_XPOSITION, (float)0.0); TIFFSetField(tif, TIFFTAG_YPOSITION, (float)0.0); if ((tiffcomp == COMPRESSION_JPEG) || (tiffcomp == COMPRESSION_OJPEG)) { if (quality == 100) { TIFFSetField(tif, TIFFTAG_JPEGPROC, JPEGPROC_LOSSLESS); } else { TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality); } } // build the colour-map (greyscale, i.e. all 3 components identical) // TIFF needs 16 bit values for this (--> tools/tiffdither.c) for (i=0; i<256; i++) cmap[i] = (uint16)(i*((1L << 16) - 1)/255); TIFFSetField(tif, TIFFTAG_COLORMAP, cmap, cmap, cmap); // Be VERY, VERY careful about the order and the items you write // out. TIFFWriteDirectory, e.g., has very ugly side-effects. uint32 *tbuff=NULL; const char *l=NULL, *line = desc.src; uint8 *normal=NULL; // normalised source data uint32 row=0; // cout << "r_Conv_TIFF: Main Loop:" << endl; if ((tbuff = new uint32[((width * bpp + 31) >> 5)]) != NULL) { // now go line by line for (row = 0; row < height; row++, line += lineAdd) { normal = (uint8 *)tbuff; l = line; // copy data in the correct format to the buffer switch (desc.baseType) { case ctype_bool: { uint8 val = 0, mask = _R_TIFF_MASK_VALUE; // convert 8bpp bitmap to 1bpp bitmap for (i=0; i < width; i++, l += pixelAdd) { // fill bits in lsb order if (*l != 0) val |= mask; _R_TIFF_MASK_SHIFT(mask); if (mask == 0) { *normal++ = val; val = 0; mask = _R_TIFF_MASK_VALUE; } } if (mask != _R_TIFF_MASK_VALUE) *normal++ = val; } break; case ctype_char: { // copy data (and transpose) for (i=0; i < width; i++, l += pixelAdd) { *normal++ = *l; } } break; case ctype_rgb: { // copy data (and transpose) for (i=0; i < width; i++, l += pixelAdd) { *normal++ = l[0]; *normal++ = l[1]; *normal++ = l[2]; } } break; // ADDED FOR ctype_uint16 case case ctype_uint16: { for (i =0; iprocess(options); //==> CHECK THIS "IMP" TIFF *tif=NULL; char dummyFile[256]; int isOK=0, typeSize=0; uint16 bps=0, bpp=0, spp=0, planar=0, photometric=0; uint32 width=0, height=0, pixelAdd=0, lineAdd=0, i=0; uint16 *reds=NULL, *greens=NULL, *blues=NULL; // Init simple (chunky) memFS memfs_chunk_initfs(handle, (char*)desc.src, (r_Long)(desc.srcInterv[0].high()-desc.srcInterv[0].low()+1)); //==> CHECK THIS desc.dest = NULL; // Create dummy file for use in the TIFF open function sprintf(dummyFile, dummyFileFmt, (void*)handle); fclose(fopen(dummyFile, "wb")); std::cout << "r_Conv_TIFF: Dummy created OK" << endl; // Open and force memory mapping mode tif = TIFFClientOpen(dummyFile, "rM", handle, memfs_chunk_read, memfs_chunk_read, memfs_chunk_seek, memfs_chunk_close, memfs_chunk_size, memfs_chunk_map, memfs_chunk_unmap); if (tif == NULL) { RMInit::logOut << "r_Conv_TIFF::convertFrom(): unable to open file!" << endl; throw r_Error(r_Error::r_Error_General); } //cout << "r_Conv_TIFF: Opened OK" << endl; //TIFFPrintDirectory(tif, stdout, 0); TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps); TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); bpp = spp * bps; TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar); TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric); // Filter out the kind of image we understand. isOK = -1; if ((bps != 1) && (bps != 8) && (bps != 32) && (bps != 16)) /* ADDED (bps != 32) since this is float data; also added (bps != 16)*/ { RMInit::logOut << "r_Conv_TIFF::convertFrom(): bad number of bits per sample: " << bps << " (must be 1 or 8 or 32 or 16)" << endl; /* ADDED 'or 32' */ if (override_bps) bps = override_bps; else isOK = 0; } if ((bpp != 1) && (bpp != 8) && (bpp != 24) && (bpp != 32) && (bpp != 16)) /* ADDED bps = 32 and samples/pixel = 1 so bpp = 32; Also added bpp = 16 */ { RMInit::logOut << "r_Conv_TIFF::convertFrom(): bad number of bits per pixel: " << bpp << " (must be 1, 8 or 24 or 32 or 16)" << endl; /* ADDED 'or 32' */ if (override_bpp) bpp = override_bpp; else isOK = 0; } if (planar != PLANARCONFIG_CONTIG)// must be contiguous for our case to handle, other cases not dealt yet { RMInit::logOut << "r_Conv_TIFF::convertFrom(): can't handle bitplanes!" << endl; isOK = 0; } if (isOK) // isOK = -1 { std::cout << "r_Conv_TIFF: Image OK: bps = " << bps << ", spp = " << spp << ", width = " << width << ", height = " << height << endl; if (bpp == 32){ desc.baseType = ctype_float32; pixelAdd = 4*height; lineAdd = 4; typeSize = 4; } else if (bpp == 24) { pixelAdd = 3*height; lineAdd = 3; typeSize = 3; desc.baseType = ctype_rgb; } else if (bpp == 16) { desc.baseType = ctype_uint16; pixelAdd = 2*height; lineAdd = 2; typeSize = 2; } else { if ((photometric == PHOTOMETRIC_PALETTE) && (override_depth != 1)) { TIFFGetField(tif, TIFFTAG_COLORMAP, &reds, &greens, &blues); for (i=0; i<256; i++) { if ((reds[i] != greens[i]) || (greens[i] != blues[i])) break; } if (i < 256) { pixelAdd = 3*height; lineAdd = 3; typeSize = 3; desc.baseType = ctype_rgb; } else { pixelAdd = height; lineAdd = 1; typeSize = 1; desc.baseType = ctype_char; } if (override_depth) { switch (override_depth) { case 1: pixelAdd = height; lineAdd = 1; typeSize = 1; desc.baseType = ctype_bool; break; case 8: pixelAdd = height; lineAdd = 1; typeSize = 1; desc.baseType = ctype_char; break; case 24: pixelAdd = 3*height; lineAdd = 3; typeSize = 3; desc.baseType = ctype_rgb; break; /* // MUST add here for case 32 */ case 32: pixelAdd = 4*height; lineAdd = 4; typeSize = 4; desc.baseType = ctype_float32; break; // add for 16 bit unsigned and signed case case 16: pixelAdd = 2*height; lineAdd = 2; typeSize = 2; desc.baseType = ctype_uint16; break; } } } // if ((photometric == PHOTOMETRIC_PALETTE) && (override_depth != 1)) CLOSED else { pixelAdd = height; lineAdd = 1; typeSize = 1; desc.baseType = (bpp == 1) ? ctype_bool : ctype_char; } } // if sample is not RGB CLOSED if ((desc.dest = (char*)mystore.storage_alloc(width*height*typeSize*sizeof(char))) == NULL) { RMInit::logOut << "r_Conv_TIFF::convertFrom(): out of memory error!" << endl; } else { std::cout << "r_Conv_TIFF: baseType = " << desc.baseType << ", size = " << typeSize << ", pixelAdd = " << pixelAdd << ", lineAdd = " << lineAdd << endl; uint32 *tbuff=NULL; char *l=NULL, *line = desc.dest; uint8 *normal=NULL; uint32 row = 0; if ((tbuff = new uint32[(width * bpp + 31) >> 5]) != NULL) { for (row = 0; row < height; row++, line += lineAdd) { if (TIFFReadScanline(tif, (tdata_t)tbuff, row, 0) < 0) break; normal = (uint8 *)tbuff; l = line; switch (desc.baseType) { case ctype_bool: // when cytpe is bool { uint8 mask = _R_TIFF_MASK_VALUE; for (i=0; i < width; i++, l += pixelAdd) { *l = (((*normal) & mask) == 0) ? 0 : 1; _R_TIFF_MASK_SHIFT(mask); if (mask == 0) {normal++; mask = _R_TIFF_MASK_VALUE;} } } break; case ctype_char: // when ctype is char { if (reds != NULL) { for (i=0; i < width; i++, l += pixelAdd) { *l = (reds[*normal++]) >> 8; } } else { for (i=0; i < width; i++, l += pixelAdd) { *l = *normal++; } } } break; case ctype_rgb: // when cytpe is rgb { if (reds != NULL) { for (i=0; i < width; i++, l += pixelAdd) { uint8 val = *normal++; l[0] = (reds[val]) >> 8; l[1] = (greens[val]) >> 8; l[2] = (blues[val]) >> 8; } } else { for (i=0; i < width; i++, l += pixelAdd) { l[0] = *normal++; l[1] = *normal++; l[2] = *normal++; } } } break; /* FOR NEW float type */ case ctype_float32: { for (i=0; i < width; i++) { *((float*)l) = *((float*)normal); l += pixelAdd; normal += 4; } } break; /* FOR NEW UNSIGNED INT 16 BIT TYPE */ case ctype_uint16: { for(i=0; i < width; i++) { *((uint16*)l) = *((uint16*)normal); l += pixelAdd; normal += 2; } } break; } // switch CLOSED } // for loop CLOSED delete [] tbuff; tbuff = NULL; } // if ((tbuff = new uint32[(width * bpp + 31) >> 5]) != NULL) CLOSED if (row < height) { TALK( "r_Conv_TIFF::convertFrom(): error reading data: got only " << row << " rows out of " << height << "." ); RMInit::logOut << "Error: cannot read all data." << endl; TIFFClose(tif); remove(dummyFile); throw r_Error(r_Error::r_Error_General); } } } // is OK CLOSED TIFFClose(tif); remove(dummyFile); // Build destination interval desc.destInterv = r_Minterval(2); desc.destInterv << r_Sinterval(r_Range(0), r_Range(width - 1)) << r_Sinterval( r_Range(0), r_Range(height - 1)); desc.destType = get_external_type(desc.baseType); LEAVE( "r_Conv_TIFF::convertFrom()" ); return desc; } const char *r_Conv_TIFF::get_name( void ) const { return format_name_tiff; } r_Data_Format r_Conv_TIFF::get_data_format( void ) const { return r_TIFF; } r_Convertor *r_Conv_TIFF::clone( void ) const { return new r_Conv_TIFF(desc.src, desc.srcInterv, desc.baseType); }