#include "ET++.ph" #include "Converter.h" #include "Class.h" #include "Data.h" #include "Bitmap.h" #include "DevBitmap.h" #include "String.h" #include "ColorMapper.h" #include "Math.h" #include "TypeMatcher.h" #include "ET_stdio.h" #include "ET_tiff.h" static const Symbol cDocTypeTIFF("TIFF"); //---- TIFFMatcher -------------------------------------------------------------- class TIFFMatcher: public TypeMatcher { public: TIFFMatcher(const Symbol &t) : TypeMatcher(t, 0, 0, FALSE) { } bool MatchContents(Data*, const char *b, int len) { return len >= 2 && (*b == b[1]) && (*b == 'M' || *b == 'I'); } bool MatchExtension(Data*, const char *e) { return StrCmp(e, "tiff", -1, gStdEncoding->UpperCaseMap()) == 0 || StrCmp(e, "tif", -1, gStdEncoding->UpperCaseMap()) == 0; } Bitmap *GetBitmap() { return gImageIcon; } }; static TIFFMatcher tiffmatcher(cDocTypeTIFF); //---- TiffConverter ----------------------------------------------------------- class TiffConverter : public Converter { public: MetaDef(TiffConverter); TiffConverter() { } const char *AsString() { return "TIFF"; } Object *Convert(Data *data, Class *want); bool CanConvert(Data *data, Class *want) { return data->Type() == cDocTypeTIFF && want == Meta(Bitmap); } bool CanConvertTo(Object *op) { return op->IsKindOf(Bitmap); } bool ConvertTo(Data *data, Object *op); }; NewMetaImpl0(TiffConverter,Converter); static int checkcmap(int n, u_short *r, u_short *g, u_short *b) { while (n-- > 0) if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) return 16; return 8; } #define CVT(x,bits) (((x) * 255) / ((1L << (bits))-1)) Object *TiffConverter::Convert(Data *data, Class*) { u_short *redcmap, *greencmap, *bluecmap; int i, row, len; u_long width, height; u_short bitspersample, samplesperpixel, photometric, matteing; TIFF *tif; Bitmap *b; DevBitmap *dbm; u_char *buf= 0; BitmapInfo bi(8, 8, 1); RGB rgb; u_long *raster; tif= TIFFOpen((char*) data->Name(), "r"); if (tif == 0) return 0; if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample)) bitspersample= 1; switch (bitspersample) { case 1: case 2: case 4: case 8: case 16: break; default: TIFFClose(tif); return 0; } len= 1 << bitspersample; if (!TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel)) samplesperpixel= 1; switch (samplesperpixel) { case 1: case 3: case 4: break; default: TIFFClose(tif); return 0; } TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { switch (samplesperpixel) { case 1: photometric= PHOTOMETRIC_MINISBLACK; break; case 3: case 4: photometric= PHOTOMETRIC_RGB; break; default: TIFFClose(tif); return 0; } } if (!TIFFGetField(tif, TIFFTAG_MATTEING, &matteing)) matteing= 0; //fprintf(stderr, "matteing: %d\n", matteing); switch (photometric) { case PHOTOMETRIC_PALETTE: b= new Bitmap(Point((int)width, (int)height), 8); dbm= b->GetDevBitmap(); bi= BitmapInfo(b->Size(), bitspersample, 1); TIFFGetField(tif, TIFFTAG_COLORMAP, &redcmap, &greencmap, &bluecmap); if (checkcmap(len, redcmap, greencmap, bluecmap) == 16) { for (i= len-1; i > 0; i--) { redcmap[i]= (u_short) CVT(redcmap[i], 16); greencmap[i]= (u_short) CVT(greencmap[i], 16); bluecmap[i]= (u_short) CVT(bluecmap[i], 16); } } for (i= 0; i < len; i++) { rgb.red= redcmap[i]; rgb.green= greencmap[i]; rgb.blue= bluecmap[i]; if (i == 0 && matteing > 0) rgb.alpha= 0; else rgb.alpha= 255; b->ChangeRGB(i, &rgb); } buf= new u_char[TIFFScanlineSize(tif)]; for (row= 0; row < height; row++) { if (TIFFReadScanline(tif, buf, row, 0) < 0) break; dbm->SetRow(&bi, row, buf); } break; case PHOTOMETRIC_MINISBLACK: case PHOTOMETRIC_MINISWHITE: b= new Bitmap(Point((int)width, (int)height), 8); dbm= b->GetDevBitmap(); bi= BitmapInfo(b->Size(), bitspersample, 1); for (i= 0; i < len; i++) { if (photometric == PHOTOMETRIC_MINISBLACK) rgb.red= rgb.green= rgb.blue= (short) CVT(i,bitspersample); else rgb.red= rgb.green= rgb.blue= (short) CVT((len-1)-i,bitspersample); b->ChangeRGB(i, &rgb); } buf= new u_char[TIFFScanlineSize(tif)]; for (row= 0; row < height; row++) { if (TIFFReadScanline(tif, buf, row, 0) < 0) break; dbm->SetRow(&bi, row, buf); } break; default: b= new Bitmap(Point((int)width, (int)height), 24); bi= BitmapInfo(b->Size(), 32, 1); raster= new u_long[width * height]; if (TIFFReadRGBAImage(tif, width, height, raster, 0) == 1) { /* b->GetDevBitmap()->SetData(&bi, raster); */ register u_int x, y; register u_long *c, pixel; for (y= 0; y < height; y++) { c= &raster[(height-y)*width]; for (x= 0; x < width; x++) { pixel= *c++; rgb.red= (short) TIFFGetR(pixel); rgb.green= (short) TIFFGetG(pixel); rgb.blue= (short) TIFFGetB(pixel); rgb.alpha= (short) TIFFGetA(pixel); b->SetRGB(x, y, &rgb); } } } else { SafeDelete(b); delete raster; } } SafeDelete(buf); TIFFClose(tif); return b; } bool TiffConverter::ConvertTo(Data *data, Object *op) { Bitmap *b= (Bitmap*) op; DevBitmap *dbm= b->GetDevBitmap(); ColorMapper *cmapper= b->GetColorMapper(); Point e(b->Size()); int depth= b->GetDepth(); TIFF *out; if ((out= TIFFOpen((char*) data->Name(), "w")) == 0) return FALSE; TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (u_long) e.x); TIFFSetField(out, TIFFTAG_IMAGELENGTH, (u_long) e.y); TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); int spp; if (depth > 8) { spp= 3; } else { spp= 1; } if (cmapper->HasMask()) { //spp++; TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, spp); //TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1); TIFFSetField(out, TIFFTAG_MATTEING, 1); } else TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, spp); TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, depth > 8 ? 8 : depth); TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); if (depth > 8) { TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_LZW); } else if (depth > 1) { register int i; int mapsize= 1 << depth; bool grey= FALSE; RGB rgb; if (cmapper->IsGrey() && !cmapper->HasMask() && (depth == 4 || depth == 8)) { grey= TRUE; for (i= 0; i <= cmapper->MaxPixel(); i++) { cmapper->Pixel2RGB(i, &rgb); if (rgb.red != i) { grey= FALSE; break; } } } if (grey) { fprintf(stderr, "IsGrey\n"); TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } else { u_short *red= new u_short[mapsize], *green= new u_short[mapsize], *blue= new u_short[mapsize]; for (i= 0; i <= cmapper->MaxPixel(); i++) { cmapper->Pixel2RGB(i, &rgb); red[i]= (rgb.red*0xFFFF)/255; green[i]= (rgb.green*0xFFFF)/255; blue[i]= (rgb.blue*0xFFFF)/255; } TIFFSetField(out, TIFFTAG_COLORMAP, red, green, blue); TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); delete red; delete green; delete blue; } TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS); } else { TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_LZW); } BitmapInfo bi(e, depth, 1); u_char *buf= new u_char[bi.rowlen]; TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, Math::Max((8*1024)/bi.rowlen, 1)); for (int row= 0; row < e.y; row++) { dbm->GetRow(&bi, row, buf); if (TIFFWriteScanline(out, buf, row, 0) < 0) break; } TIFFClose(out); return TRUE; }