private bool writeDirectory(bool done)
{
if (m_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 ((m_flags & TiffFlags.POSTENCODE) == TiffFlags.POSTENCODE)
{
m_flags &= ~TiffFlags.POSTENCODE;
if (!m_currentCodec.PostEncode())
{
ErrorExt(this, m_clientdata, m_name, "Error post-encoding before directory write");
return false;
}
}
// shutdown encoder
m_currentCodec.Close();
// Flush any data that might have been written by the
// compression close+cleanup routines.
if (m_rawcc > 0 && (m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING && !flushData1())
{
ErrorExt(this, m_clientdata, m_name, "Error flushing data before directory write");
return false;
}
if ((m_flags & TiffFlags.MYBUFFER) == TiffFlags.MYBUFFER && m_rawdata != null)
{
m_rawdata = null;
m_rawcc = 0;
m_rawdatasize = 0;
}
m_flags &= ~(TiffFlags.BEENWRITING | TiffFlags.BUFFERSETUP);
}
// Size the directory so that we can calculate offsets for the data
// items that aren't kept in-place in each field.
int nfields = 0;
for (int b = 0; b <= FieldBit.Last; b++)
{
if (fieldSet(b) && b != FieldBit.Custom)
nfields += (b < FieldBit.SubFileType ? 2 : 1);
}
nfields += m_dir.td_customValueCount;
int dirsize = nfields * TiffDirEntry.SizeInBytes;
TiffDirEntry[] data = new TiffDirEntry [nfields];
for (int i = 0; i < nfields; i++)
data[i] = new TiffDirEntry();
// Directory hasn't been placed yet, put it at the end of the file
// and link it into the existing directory structure.
if (m_diroff == 0 && !linkDirectory())
return false;
m_dataoff = m_diroff + sizeof(short) + (uint)dirsize + sizeof(int);
if ((m_dataoff & 1) != 0)
m_dataoff++;
seekFile(m_dataoff, SeekOrigin.Begin);
m_curdir++;
int dir = 0;
// Setup external form of directory entries and write data items.
int[] fields = new int[FieldBit.SetLongs];
Buffer.BlockCopy(m_dir.td_fieldsset, 0, fields, 0, FieldBit.SetLongs * sizeof(int));
// Write out ExtraSamples tag only if extra samples are present in the data.
if (fieldSet(fields, FieldBit.ExtraSamples) && m_dir.td_extrasamples == 0)
{
resetFieldBit(fields, FieldBit.ExtraSamples);
nfields--;
dirsize -= TiffDirEntry.SizeInBytes;
} // XXX
for (int fi = 0, nfi = m_nfields; nfi > 0; nfi--, fi++)
{
TiffFieldInfo fip = m_fieldinfo[fi];
// 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.Bit == FieldBit.Custom)
{
bool is_set = false;
for (int ci = 0; ci < m_dir.td_customValueCount; ci++)
is_set |= (m_dir.td_customValues[ci].info == fip);
if (!is_set)
continue;
}
else if (!fieldSet(fields, fip.Bit))
{
continue;
}
// Handle other fields.
TiffTag tag = (TiffTag)FieldBit.Ignore;
switch (fip.Bit)
{
case FieldBit.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() ? TiffTag.TILEOFFSETS : TiffTag.STRIPOFFSETS;
if (tag != fip.Tag)
continue;
data[dir].tdir_tag = tag;
data[dir].tdir_type = TiffType.LONG;
data[dir].tdir_count = m_dir.td_nstrips;
if (!writeLongArray(ref data[dir], m_dir.td_stripoffset))
return false;
break;
case FieldBit.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() ? TiffTag.TILEBYTECOUNTS: TiffTag.STRIPBYTECOUNTS;
if (tag != fip.Tag)
continue;
data[dir].tdir_tag = tag;
data[dir].tdir_type = TiffType.LONG;
data[dir].tdir_count = m_dir.td_nstrips;
if (!writeLongArray(ref data[dir], m_dir.td_stripbytecount))
return false;
break;
case FieldBit.RowsPerStrip:
setupShortLong(TiffTag.ROWSPERSTRIP, ref data[dir], m_dir.td_rowsperstrip);
break;
case FieldBit.ColorMap:
if (!writeShortTable(TiffTag.COLORMAP, ref data[dir], 3, m_dir.td_colormap))
return false;
break;
case FieldBit.ImageDimensions:
setupShortLong(TiffTag.IMAGEWIDTH, ref data[dir++], m_dir.td_imagewidth);
setupShortLong(TiffTag.IMAGELENGTH, ref data[dir], m_dir.td_imagelength);
break;
case FieldBit.TileDimensions:
setupShortLong(TiffTag.TILEWIDTH, ref data[dir++], m_dir.td_tilewidth);
setupShortLong(TiffTag.TILELENGTH, ref data[dir], m_dir.td_tilelength);
break;
case FieldBit.Compression:
setupShort(TiffTag.COMPRESSION, ref data[dir], (short)m_dir.td_compression);
break;
case FieldBit.Photometric:
setupShort(TiffTag.PHOTOMETRIC, ref data[dir], (short)m_dir.td_photometric);
break;
case FieldBit.Position:
if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XPOSITION, m_dir.td_xposition, TiffTag.YPOSITION, m_dir.td_yposition))
return false;
dir++;
break;
case FieldBit.Resolution:
if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XRESOLUTION, m_dir.td_xresolution, TiffTag.YRESOLUTION, m_dir.td_yresolution))
return false;
dir++;
break;
case FieldBit.BitsPerSample:
case FieldBit.MinSampleValue:
case FieldBit.MaxSampleValue:
case FieldBit.SampleFormat:
if (!writePerSampleShorts(fip.Tag, ref data[dir]))
return false;
break;
case FieldBit.SMinSampleValue:
case FieldBit.SMaxSampleValue:
if (!writePerSampleAnys(sampleToTagType(), fip.Tag, ref data[dir]))
return false;
break;
case FieldBit.PageNumber:
case FieldBit.HalftoneHints:
case FieldBit.YCbCrSubsampling:
if (!setupShortPair(fip.Tag, ref data[dir]))
return false;
break;
case FieldBit.InkNames:
if (!writeInkNames(ref data[dir]))
return false;
break;
case FieldBit.TransferFunction:
if (!writeTransferFunction(ref data[dir]))
return false;
break;
case FieldBit.SubIFD:
// XXX: Always write this field using LONG type
// for backward compatibility.
data[dir].tdir_tag = fip.Tag;
data[dir].tdir_type = TiffType.LONG;
data[dir].tdir_count = m_dir.td_nsubifd;
if (!writeLongArray(ref data[dir], m_dir.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 (data[dir].tdir_count > 0)
{
m_flags |= TiffFlags.INSUBIFD;
m_nsubifd = (short)data[dir].tdir_count;
if (data[dir].tdir_count > 1)
{
m_subifdoff = data[dir].tdir_offset;
}
else
{
m_subifdoff = m_diroff + sizeof(short) +
(uint)dir * TiffDirEntry.SizeInBytes +
sizeof(short) * 2 + sizeof(int);
}
}
break;
default:
// XXX: Should be fixed and removed.
if (fip.Tag == TiffTag.DOTRANGE)
{
if (!setupShortPair(fip.Tag, ref data[dir]))
return false;
}
else if (!writeNormalTag(ref data[dir], fip))
return false;
break;
}
dir++;
if (fip.Bit != FieldBit.Custom)
resetFieldBit(fields, fip.Bit);
}
// Write directory.
short dircount = (short)nfields;
uint diroff = m_nextdiroff;
if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB)
{
// 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.
for (dir = 0; dircount != 0; dir++, dircount--)
{
short temp = (short)data[dir].tdir_tag;
SwabShort(ref temp);
data[dir].tdir_tag = (TiffTag)(ushort)temp;
temp = (short)data[dir].tdir_type;
SwabShort(ref temp);
data[dir].tdir_type = (TiffType)temp;
SwabLong(ref data[dir].tdir_count);
SwabUInt(ref data[dir].tdir_offset);
}
dircount = (short)nfields;
SwabShort(ref dircount);
SwabUInt(ref diroff);
}
seekFile(m_diroff, SeekOrigin.Begin);
if (!writeShortOK(dircount))
{
ErrorExt(this, m_clientdata, m_name, "Error writing directory count");
return false;
}
if (!writeDirEntryOK(data, dirsize / TiffDirEntry.SizeInBytes))
{
ErrorExt(this, m_clientdata, m_name, "Error writing directory contents");
return false;
}
if (!writeIntOK((int)diroff))
{
ErrorExt(this, m_clientdata, m_name, "Error writing directory link");
return false;
}
if (done)
{
FreeDirectory();
m_flags &= ~TiffFlags.DIRTYDIRECT;
m_currentCodec.Cleanup();
// Reset directory-related state for subsequent directories.
CreateDirectory();
}
return true;
}