diff options
Diffstat (limited to 'conversion/tiff.cc')
-rw-r--r-- | conversion/tiff.cc | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/conversion/tiff.cc b/conversion/tiff.cc new file mode 100644 index 0000000..463f858 --- /dev/null +++ b/conversion/tiff.cc @@ -0,0 +1,653 @@ +/* +* 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 <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: tiff.cc + * + * MODULE: conversion + * + * CLASSES: r_Conv_TIFF + * + * COMMENTS: + * + * Provides functions to convert data to TIFF and back. + * +*/ + +#include <iostream> +#include <string.h> + +#ifdef AIX +#include <strings.h> +#endif + +#include "conversion/tiff.hh" +#include "conversion/memfs.hh" +#include "raslib/error.hh" +#include "raslib/rminit.hh" +#include "raslib/parseparams.hh" + +const int r_Conv_TIFF::defaultRPS = 32; + +const char r_Conv_TIFF::dummyFileFmt[] = "/tmp/%p.tif"; + +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) +{ + 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) + RMInit::logOut << "r_Conv_TIFF::get_compression_from_name(): unsupported compression type " << strComp << endl; + } + + return tiffComp; +} + +// Translate string resolution unit type to libtiff resolution unit type +int r_Conv_TIFF::get_resunit_from_name(const char* strResUnit) +{ + 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) + RMInit::logOut << "r_Conv_TIFF::get_resunit_from_name(): unsupported resolution unit type " << strResUnit << endl; + } + + return tiffResUnit; +} + +void r_Conv_TIFF::initTIFF( void ) +{ + compType = NULL; + quality = 80; + 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); +} + + +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) +{ + initTIFF(); +} + + +r_Conv_TIFF::r_Conv_TIFF(const char *src, const r_Minterval &interv, int type) throw(r_Error) +: r_Convert_Memory(src, interv, type) +{ + initTIFF(); +} + + +r_Conv_TIFF::~r_Conv_TIFF(void) +{ + if (compType != NULL) + { + delete [] compType; + compType = NULL; + } +} + + +// 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) +{ + if (options != NULL) + printf("tiff convert option = %s\n", options); + else + printf("tiff options are null = %s\n"); + 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; + default: RMInit::logOut << "r_Conv_TIFF::convertTo(): unknown 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, "Image"); + 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); + } + 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; + } + if (TIFFWriteScanline(tif, (tdata_t)tbuff, row, 0) < 0) break; + } + + delete [] tbuff; tbuff = NULL; + } + + if (row < height) // error + { + RMInit::logOut << "r_Conv_TIFF::convertTo(): error writing data!" << endl; + TIFFClose(tif); remove(dummyFile); + throw r_Error(r_Error::r_Error_General); + } + + TIFFClose(tif); + // Now delete the dummy file + remove(dummyFile); + + r_Long tifSize = memfs_size(handle); + + // Allocate an array of just the right size and "load" object there + if ((desc.dest = (char*)mystore.storage_alloc(sizeof(char) * tifSize)) == NULL) + { + RMInit::logOut << "r_Conv_TIFF::convertTo(): out of memory" << endl; + throw r_Error(MEMMORYALLOCATIONERROR); + } + memfs_seek(handle, 0, SEEK_SET); + memfs_read(handle, desc.dest, tifSize); + + // Set up destination interval + desc.destInterv = r_Minterval(1); + desc.destInterv << r_Sinterval(r_Range(0), r_Range(tifSize - 1)); + + // define the base type as char for now + desc.destType = r_Type::get_any_type("char"); + + return desc; +} + + + +r_convDesc &r_Conv_TIFF::convertFrom(const char *options) throw(r_Error) +{ + if (options != NULL) + printf("tiff convert option = %s\n", options); + else + printf("tiff options are null = %s\n"); + params->process(options); + 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)); + + desc.dest = NULL; + + // Create dummy file for use in the TIFF open function + sprintf(dummyFile, dummyFileFmt, (void*)handle); + fclose(fopen(dummyFile, "wb")); + //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)) + { + RMInit::logOut << "r_Conv_TIFF::convertFrom(): bad number of bits per sample: " << bps << " (must be 1 or 8)" << endl; + if (override_bps) + bps = override_bps; + else + isOK = 0; + } + if ((bpp != 1) && (bpp != 8) && (bpp != 24)) + { + RMInit::logOut << "r_Conv_TIFF::convertFrom(): bad number of bits per pixel: " << bpp << " (must be 1, 8 or 24)" << endl; + if (override_bpp) + bpp = override_bpp; + else + isOK = 0; + } + if (planar != PLANARCONFIG_CONTIG) + { + RMInit::logOut << "r_Conv_TIFF::convertFrom(): can't handle bitplanes!" << endl; + isOK = 0; + } + + if (isOK) + { + //cout << "r_Conv_TIFF: Image OK: bps = " << bps << ", spp = " << spp << ", width = " << width << ", height = " << height << endl; + + if (bpp == 24) + { + pixelAdd = 3*height; lineAdd = 3; typeSize = 3; + desc.baseType = ctype_rgb; + } + 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; + } + } + } + else + { + pixelAdd = height; lineAdd = 1; typeSize = 1; + desc.baseType = (bpp == 1) ? ctype_bool : ctype_char; + } + } + + 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 + { + //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: + { + 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: + { + 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: + { + 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; + } + } + delete [] tbuff; tbuff = NULL; + } + + if (row < height) + { + RMInit::logOut << "r_Conv_TIFF::convertFrom(): error reading data!" << endl; + TIFFClose(tif); remove(dummyFile); + throw r_Error(r_Error::r_Error_General); + } + } + } + + 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); + + 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); +} |