// Encode the supplied data and write it to the // specified tile. There must be space for the // data. The function clamps individual writes // to a tile to the tile size, but does not (and // can not) check that multiple writes to the same // tile do not write more than tile size data. // // NB: Image length must be setup before writing; this // interface does not support automatically growing // the image on each write (as TIFFWriteScanline does). public static int TIFFWriteEncodedTile(TIFF tif, uint tile, byte[] data, int cc) { string module="TIFFWriteEncodedTile"; ushort sample; if(!((tif.tif_flags&TIF_FLAGS.TIFF_BEENWRITING)!=0||TIFFWriteCheck(tif, true, module))) return -1; TIFFDirectory td=tif.tif_dir; if(tile>=td.td_nstrips) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Tile {1} out of range, max {2}", tif.tif_name, tile, td.td_nstrips); return -1; } // Handle delayed allocation of data buffer. This // permits it to be sized more intelligently (using // directory information). if(!BUFFERCHECK(tif)) return -1; tif.tif_curtile=tile; tif.tif_rawcc=0; tif.tif_rawcp=0;; if(td.td_stripbytecount[tile]>0) { // if we are writing over existing tiles, zero length. td.td_stripbytecount[tile]=0; // this forces TIFFAppendToStrip() to do a seek. tif.tif_curoff=0; } // Compute tiles per row & per column to compute // current row and column tif.tif_row=(tile%TIFFhowmany(td.td_imagelength, td.td_tilelength))*td.td_tilelength; tif.tif_col=(tile%TIFFhowmany(td.td_imagewidth, td.td_tilewidth))*td.td_tilewidth; if((tif.tif_flags&TIF_FLAGS.TIFF_CODERSETUP)==0) { if(!tif.tif_setupencode(tif)) return -1; tif.tif_flags|=TIF_FLAGS.TIFF_CODERSETUP; } tif.tif_flags&=~TIF_FLAGS.TIFF_POSTENCODE; sample=(ushort)(tile/td.td_stripsperimage); if(!tif.tif_preencode(tif, sample)) return -1; // Clamp write amount to the tile size. This is mostly // done so that callers can pass in some large number // (e.g. -1) and have the tile size used instead. if(cc<1||cc>tif.tif_tilesize) cc=tif.tif_tilesize; // swab if needed - note that source buffer will be altered tif.tif_postdecode(tif, data, 0, cc); if(!tif.tif_encodetile(tif, data, cc, sample)) return 0; if(!tif.tif_postencode(tif)) return -1; if(!isFillOrder(tif, td.td_fillorder)&&(tif.tif_flags&TIF_FLAGS.TIFF_NOBITREV)==0) TIFFReverseBits(tif.tif_rawdata, tif.tif_rawcc); if(tif.tif_rawcc>0&&!TIFFAppendToStrip(tif, tile, tif.tif_rawdata, tif.tif_rawcc)) return -1; tif.tif_rawcc=0; tif.tif_rawcp=0; return cc; }
// Encode the supplied data and write it to the // specified strip. // // NB: Image length must be setup before writing. public static int TIFFWriteEncodedStrip(TIFF tif, uint strip, byte[] data, int cc) { string module="TIFFWriteEncodedStrip"; TIFFDirectory td=tif.tif_dir; ushort sample; if(!((tif.tif_flags&TIF_FLAGS.TIFF_BEENWRITING)!=0||TIFFWriteCheck(tif, false, module))) return -1; // Check strip array to make sure there's space. // We don't support dynamically growing files that // have data organized in separate bitplanes because // it's too painful. In that case we require that // the imagelength be set properly before the first // write (so that the strips array will be fully // allocated above). /// if(strip>=td.td_nstrips) { if(td.td_planarconfig==PLANARCONFIG.SEPARATE) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Can not grow image by strips when using separate planes"); return -1; } if(!TIFFGrowStrips(tif, 1, module)) return -1; td.td_stripsperimage=TIFFhowmany(td.td_imagelength, td.td_rowsperstrip); } // Handle delayed allocation of data buffer. This // permits it to be sized according to the directory // info. if(!BUFFERCHECK(tif)) return -1; tif.tif_curstrip=strip; tif.tif_row=(strip%td.td_stripsperimage)*td.td_rowsperstrip; if((tif.tif_flags&TIF_FLAGS.TIFF_CODERSETUP)==0) { if(!tif.tif_setupencode(tif)) return -1; tif.tif_flags|=TIF_FLAGS.TIFF_CODERSETUP; } tif.tif_rawcc=0; tif.tif_rawcp=0; if(td.td_stripbytecount[strip]>0) { // Force TIFFAppendToStrip() to consider placing data at end of file. tif.tif_curoff=0; } tif.tif_flags&=~TIF_FLAGS.TIFF_POSTENCODE; sample=(ushort)(strip/td.td_stripsperimage); if(!tif.tif_preencode(tif, sample)) return -1; // swab if needed - note that source buffer will be altered tif.tif_postdecode(tif, data, 0, cc); if(!tif.tif_encodestrip(tif, data, cc, sample)) return 0; if(!tif.tif_postencode(tif)) return -1; if(!isFillOrder(tif, td.td_fillorder)&&(tif.tif_flags&TIF_FLAGS.TIFF_NOBITREV)==0) TIFFReverseBits(tif.tif_rawdata, tif.tif_rawcc); if(tif.tif_rawcc>0&&!TIFFAppendToStrip(tif, strip, tif.tif_rawdata, tif.tif_rawcc)) return -1; tif.tif_rawcc=0; tif.tif_rawcp=0; return cc; }
// Encode the supplied data and write it to the // specified tile. There must be space for the // data. The function clamps individual writes // to a tile to the tile size, but does not (and // can not) check that multiple writes to the same // tile do not write more than tile size data. // // NB: Image length must be setup before writing; this // interface does not support automatically growing // the image on each write (as TIFFWriteScanline does). public static int TIFFWriteEncodedTile(TIFF tif, uint tile, byte[] data, int cc) { string module = "TIFFWriteEncodedTile"; ushort sample; if (!((tif.tif_flags & TIF_FLAGS.TIFF_BEENWRITING) != 0 || TIFFWriteCheck(tif, true, module))) { return(-1); } TIFFDirectory td = tif.tif_dir; if (tile >= td.td_nstrips) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Tile {1} out of range, max {2}", tif.tif_name, tile, td.td_nstrips); return(-1); } // Handle delayed allocation of data buffer. This // permits it to be sized more intelligently (using // directory information). if (!BUFFERCHECK(tif)) { return(-1); } tif.tif_curtile = tile; tif.tif_rawcc = 0; tif.tif_rawcp = 0;; if (td.td_stripbytecount[tile] > 0) { // if we are writing over existing tiles, zero length. td.td_stripbytecount[tile] = 0; // this forces TIFFAppendToStrip() to do a seek. tif.tif_curoff = 0; } // Compute tiles per row & per column to compute // current row and column tif.tif_row = (tile % TIFFhowmany(td.td_imagelength, td.td_tilelength)) * td.td_tilelength; tif.tif_col = (tile % TIFFhowmany(td.td_imagewidth, td.td_tilewidth)) * td.td_tilewidth; if ((tif.tif_flags & TIF_FLAGS.TIFF_CODERSETUP) == 0) { if (!tif.tif_setupencode(tif)) { return(-1); } tif.tif_flags |= TIF_FLAGS.TIFF_CODERSETUP; } tif.tif_flags &= ~TIF_FLAGS.TIFF_POSTENCODE; sample = (ushort)(tile / td.td_stripsperimage); if (!tif.tif_preencode(tif, sample)) { return(-1); } // Clamp write amount to the tile size. This is mostly // done so that callers can pass in some large number // (e.g. -1) and have the tile size used instead. if (cc < 1 || cc > tif.tif_tilesize) { cc = tif.tif_tilesize; } // swab if needed - note that source buffer will be altered tif.tif_postdecode(tif, data, 0, cc); if (!tif.tif_encodetile(tif, data, cc, sample)) { return(0); } if (!tif.tif_postencode(tif)) { return(-1); } if (!isFillOrder(tif, td.td_fillorder) && (tif.tif_flags & TIF_FLAGS.TIFF_NOBITREV) == 0) { TIFFReverseBits(tif.tif_rawdata, tif.tif_rawcc); } if (tif.tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile, tif.tif_rawdata, tif.tif_rawcc)) { return(-1); } tif.tif_rawcc = 0; tif.tif_rawcp = 0; return(cc); }
// Write the contents of the current directory // to the specified file. This routine doesn't // handle overwriting a directory with auxiliary // storage that's been changed. static bool TIFFWriteDirectory(TIFF tif, bool done) { if(tif.tif_mode==O.RDONLY) return true; // Clear write state so that subsequent images with // different characteristics get the right buffers // setup for them. if(done) { if((tif.tif_flags&TIF_FLAGS.TIFF_POSTENCODE)!=0) { tif.tif_flags&=~TIF_FLAGS.TIFF_POSTENCODE; if(!tif.tif_postencode(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error post-encoding before directory write"); return false; } } tif.tif_close(tif); // shutdown encoder // Flush any data that might have been written // by the compression close+cleanup routines. if(tif.tif_rawcc>0&&(tif.tif_flags&TIF_FLAGS.TIFF_BEENWRITING)!=0&&!TIFFFlushData1(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error flushing data before directory write"); return false; } tif.tif_rawdata=null; tif.tif_rawcc=0; tif.tif_rawdatasize=0; tif.tif_flags&=~(TIF_FLAGS.TIFF_BEENWRITING|TIF_FLAGS.TIFF_BUFFERSETUP); } TIFFDirectory td=tif.tif_dir; // Some Irfan view versions don't like RowsPerStrip set in tiled tifs if(isTiled(tif)) TIFFClrFieldBit(tif, FIELD.ROWSPERSTRIP); // Size the directory so that we can calculate // offsets for the data items that aren't kept // in-place in each field. uint nfields=0; uint[] fields=new uint[FIELD_SETLONGS]; for(FIELD b=0; b<=(FIELD)FIELD_LAST; b++) { if(TIFFFieldSet(tif, b)&&b!=FIELD.CUSTOM) nfields+=(b<FIELD.SUBFILETYPE?2u:1u); } nfields+=(uint)td.td_customValueCount; int dirsize=(int)nfields*12; // 12: sizeof(TIFFDirEntry); List<TIFFDirEntry> dirs=null; try { dirs=new List<TIFFDirEntry>(); for(int i=0; i<nfields; i++) dirs.Add(new TIFFDirEntry()); } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Cannot write directory, out of space"); return false; } // Directory hasn't been placed yet, put // it at the end of the file and link it // into the existing directory structure. if(tif.tif_diroff==0&&!TIFFLinkDirectory(tif)) return false; //tif.tif_dataoff=(uint)(tif.tif_diroff+sizeof(uint16)+dirsize+sizeof(toff_t)); tif.tif_dataoff=(uint)(tif.tif_diroff+2+dirsize+4); if((tif.tif_dataoff&1)==1) tif.tif_dataoff++; TIFFSeekFile(tif, tif.tif_dataoff, SEEK.SET); tif.tif_curdir++; // Setup external form of directory // entries and write data items. td.td_fieldsset.CopyTo(fields, 0); // Write out ExtraSamples tag only if // extra samples are present in the data. if(FieldSet(fields, FIELD.EXTRASAMPLES)&&td.td_extrasamples==0) { ResetFieldBit(fields, FIELD.EXTRASAMPLES); nfields--; dirsize-=12; // 12: sizeof(TIFFDirEntry); } // XXX int dirsnumber=0; TIFFTAG tag; foreach(TIFFFieldInfo fip in tif.tif_fieldinfo) { TIFFDirEntry dir=(dirsnumber<dirs.Count)?dir=dirs[dirsnumber]:null; // For custom fields, we test to see if the custom field // is set or not. For normal fields, we just use the // FieldSet test. if(fip.field_bit==FIELD.CUSTOM) { bool is_set=false; for(int ci=0; ci<td.td_customValueCount; ci++) is_set|=(td.td_customValues[ci].info==fip); if(!is_set) continue; } else if(!FieldSet(fields, fip.field_bit)) continue; // Handle other fields. switch(fip.field_bit) { case FIELD.STRIPOFFSETS: // We use one field bit for both strip and tile // offsets, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEOFFSETS:TIFFTAG.STRIPOFFSETS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripoffset)) return false; break; case FIELD.STRIPBYTECOUNTS: // We use one field bit for both strip and tile // byte counts, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEBYTECOUNTS:TIFFTAG.STRIPBYTECOUNTS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripbytecount)) return false; break; case FIELD.ROWSPERSTRIP: TIFFSetupShortLong(tif, TIFFTAG.ROWSPERSTRIP, dir, td.td_rowsperstrip); break; case FIELD.COLORMAP: if(!TIFFWriteShortTable(tif, TIFFTAG.COLORMAP, dir, 3, td.td_colormap)) return false; break; case FIELD.IMAGEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.IMAGEWIDTH, dir, td.td_imagewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.IMAGELENGTH, dir, td.td_imagelength); break; case FIELD.TILEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.TILEWIDTH, dir, td.td_tilewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.TILELENGTH, dir, td.td_tilelength); break; case FIELD.COMPRESSION: TIFFSetupShort(tif, TIFFTAG.COMPRESSION, dir, (ushort)td.td_compression); break; case FIELD.PHOTOMETRIC: TIFFSetupShort(tif, TIFFTAG.PHOTOMETRIC, dir, (ushort)td.td_photometric); break; case FIELD.POSITION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XPOSITION, dir, td.td_xposition)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YPOSITION, dir, td.td_yposition)) return false; break; case FIELD.RESOLUTION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XRESOLUTION, dir, td.td_xresolution)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YRESOLUTION, dir, td.td_yresolution)) return false; break; case FIELD.BITSPERSAMPLE: case FIELD.MINSAMPLEVALUE: case FIELD.MAXSAMPLEVALUE: case FIELD.SAMPLEFORMAT: if(!TIFFWritePerSampleShorts(tif, fip.field_tag, dir)) return false; break; case FIELD.SMINSAMPLEVALUE: case FIELD.SMAXSAMPLEVALUE: if(!TIFFWritePerSampleAnys(tif, TIFFSampleToTagType(tif), fip.field_tag, dir)) return false; break; case FIELD.PAGENUMBER: case FIELD.HALFTONEHINTS: case FIELD.YCBCRSUBSAMPLING: if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; break; case FIELD.INKNAMES: if(!TIFFWriteInkNames(tif, dir)) return false; break; case FIELD.TRANSFERFUNCTION: if(!TIFFWriteTransferFunction(tif, dir)) return false; break; case FIELD.SUBIFD: // XXX: Always write this field using LONG type // for backward compatibility. dir.tdir_tag=(ushort)fip.field_tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=(uint)td.td_nsubifd; if(!TIFFWriteLongArray(tif, dir, td.td_subifd)) return false; // Total hack: if this directory includes a SubIFD // tag then force the next <n> directories to be // written as "sub directories" of this one. This // is used to write things like thumbnails and // image masks that one wants to keep out of the // normal directory linkage access mechanism. if(dir.tdir_count>0) { tif.tif_flags|=TIF_FLAGS.TIFF_INSUBIFD; tif.tif_nsubifd=(ushort)dir.tdir_count; if(dir.tdir_count>1) tif.tif_subifdoff=dir.tdir_offset; //was else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+((char*)&dir.tdir_offset-data)); else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+12*dirsnumber-4); } break; default: // XXX: Should be fixed and removed. if(fip.field_tag==TIFFTAG.DOTRANGE) { if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; } else if(!TIFFWriteNormalTag(tif, dir, fip)) return false; break; } dirsnumber++; if(fip.field_bit!=FIELD.CUSTOM) ResetFieldBit(fields, fip.field_bit); } // Write directory. ushort dircount=(ushort)nfields; uint diroff=tif.tif_nextdiroff; if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { // The file's byte order is opposite to the // native machine architecture. We overwrite // the directory information with impunity // because it'll be released below after we // write it to the file. Note that all the // other tag construction routines assume that // we do this byte-swapping; i.e. they only // byte-swap indirect data. foreach(TIFFDirEntry dir in dirs) { TIFFSwab(ref dir.tdir_tag); TIFFSwab(ref dir.tdir_type); TIFFSwab(ref dir.tdir_count); TIFFSwab(ref dir.tdir_offset); } dircount=(ushort)nfields; TIFFSwab(ref dircount); TIFFSwab(ref diroff); } TIFFSeekFile(tif, tif.tif_diroff, SEEK.SET); if(!WriteOK(tif, dircount)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory count"); return false; } if(!WriteOK(tif, dirs, (ushort)dirsize)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory contents"); return false; } if(!WriteOK(tif, diroff)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory link"); return false; } if(done) { TIFFFreeDirectory(tif); tif.tif_flags&=~TIF_FLAGS.TIFF_DIRTYDIRECT; tif.tif_cleanup(tif); // Reset directory-related state for subsequent directories. TIFFCreateDirectory(tif); } return true; }
// Encode the supplied data and write it to the // specified strip. // // NB: Image length must be setup before writing. public static int TIFFWriteEncodedStrip(TIFF tif, uint strip, byte[] data, int cc) { string module = "TIFFWriteEncodedStrip"; TIFFDirectory td = tif.tif_dir; ushort sample; if (!((tif.tif_flags & TIF_FLAGS.TIFF_BEENWRITING) != 0 || TIFFWriteCheck(tif, false, module))) { return(-1); } // Check strip array to make sure there's space. // We don't support dynamically growing files that // have data organized in separate bitplanes because // it's too painful. In that case we require that // the imagelength be set properly before the first // write (so that the strips array will be fully // allocated above). /// if (strip >= td.td_nstrips) { if (td.td_planarconfig == PLANARCONFIG.SEPARATE) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Can not grow image by strips when using separate planes"); return(-1); } if (!TIFFGrowStrips(tif, 1, module)) { return(-1); } td.td_stripsperimage = TIFFhowmany(td.td_imagelength, td.td_rowsperstrip); } // Handle delayed allocation of data buffer. This // permits it to be sized according to the directory // info. if (!BUFFERCHECK(tif)) { return(-1); } tif.tif_curstrip = strip; tif.tif_row = (strip % td.td_stripsperimage) * td.td_rowsperstrip; if ((tif.tif_flags & TIF_FLAGS.TIFF_CODERSETUP) == 0) { if (!tif.tif_setupencode(tif)) { return(-1); } tif.tif_flags |= TIF_FLAGS.TIFF_CODERSETUP; } tif.tif_rawcc = 0; tif.tif_rawcp = 0; if (td.td_stripbytecount[strip] > 0) { // Force TIFFAppendToStrip() to consider placing data at end of file. tif.tif_curoff = 0; } tif.tif_flags &= ~TIF_FLAGS.TIFF_POSTENCODE; sample = (ushort)(strip / td.td_stripsperimage); if (!tif.tif_preencode(tif, sample)) { return(-1); } // swab if needed - note that source buffer will be altered tif.tif_postdecode(tif, data, 0, cc); if (!tif.tif_encodestrip(tif, data, cc, sample)) { return(0); } if (!tif.tif_postencode(tif)) { return(-1); } if (!isFillOrder(tif, td.td_fillorder) && (tif.tif_flags & TIF_FLAGS.TIFF_NOBITREV) == 0) { TIFFReverseBits(tif.tif_rawdata, tif.tif_rawcc); } if (tif.tif_rawcc > 0 && !TIFFAppendToStrip(tif, strip, tif.tif_rawdata, tif.tif_rawcc)) { return(-1); } tif.tif_rawcc = 0; tif.tif_rawcp = 0; return(cc); }