Beispiel #1
0
        internal bool setCompressionScheme(Compression scheme)
        {
            TiffCodec c = FindCodec(scheme);

            if (c == null)
            {
                /*
                 * Don't treat an unknown compression scheme as an error.
                 * This permits applications to open files with data that
                 * the library does not have builtin support for, but which
                 * may still be meaningful.
                 */
                c = m_builtInCodecs[0];
            }

            m_decodestatus = c.CanDecode;
            m_flags       &= ~(TiffFlags.NOBITREV | TiffFlags.NOREADRAW);

            m_currentCodec = c;
            return(c.Init());
        }
Beispiel #2
0
        /// <summary>
        /// Encodes and writes a scanline of data to an open TIFF file/stream.
        /// </summary>
        /// <param name="buffer">The buffer with image data to be encoded and written.</param>
        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which
        /// to begin reading bytes.</param>
        /// <param name="row">The zero-based index of scanline (row) to place encoded data at.</param>
        /// <param name="plane">The zero-based index of the sample plane.</param>
        /// <returns>
        /// 	<c>true</c> if image data were encoded and written successfully; otherwise, <c>false</c>
        /// </returns>
        /// <remarks>
        /// <para>
        /// <b>WriteScanline</b> encodes and writes to a file at the specified
        /// <paramref name="row"/> and specified sample plane <paramref name="plane"/>.
        /// Applications may use <see cref="WriteScanline(byte[], int)"/> or specify 0 for
        /// <paramref name="plane"/> parameter if image data in a file/stream is contiguous (i.e
        /// not organized in separate planes,
        /// <see cref="TiffTag.PlanarConfig"/> = <see cref="PlanarConfig"/>.Contig).
        /// </para><para>
        /// The data are assumed to be uncompressed and in the native bit- and byte-order of the
        /// host machine. The data written to the file is compressed according to the compression
        /// scheme of the current TIFF directory (see further below). If the current scanline is
        /// past the end of the current subfile, the value of <see cref="TiffTag.ImageLength"/>
        /// tag is automatically increased to include the scanline (except for
        /// <see cref="TiffTag.PlanarConfig"/> = <see cref="PlanarConfig"/>.Contig, where the
        /// <see cref="TiffTag.ImageLength"/> tag cannot be changed once the first data are
        /// written). If the <see cref="TiffTag.ImageLength"/> is increased, the values of
        /// <see cref="TiffTag.StripOffsets"/> and <see cref="TiffTag.StripByteCounts"/> tags are
        /// similarly enlarged to reflect data written past the previous end of image.
        /// </para><para>
        /// The library writes encoded data using the native machine byte order. Correctly
        /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly
        /// process image data with value of <see cref="TiffTag.BitsPerSample"/> tag greater
        /// than 8. The library attempts to hide bit-ordering differences between the image and
        /// the native machine by converting data from the native machine order.
        /// </para><para>
        /// Once data are written to a file/stream for the current directory, the values of
        /// certain tags may not be altered; see 
        /// <a href = "54cbd23d-dc55-44b9-921f-3a06efc2f6ce.htm">"Well-known tags and their
        /// value(s) data types"</a> for more information.
        /// </para><para>
        /// It is not possible to write scanlines to a file/stream that uses a tiled organization.
        /// The <see cref="IsTiled"/> can be used to determine if the file/stream is organized as
        /// tiles or strips.
        /// </para></remarks>
        public bool WriteScanline(byte[] buffer, int offset, int row, short plane)
        {
            const string module = "WriteScanline";

            if (!writeCheckStrips(module))
                return false;

            // Handle delayed allocation of data buffer. This permits it to be
            // sized more intelligently (using directory information).
            bufferCheck();

            // Extend image length if needed (but only for PlanarConfig.Contig).
            bool imagegrew = false;
            if (row >= m_dir.td_imagelength)
            {
                // extend image
                if (m_dir.td_planarconfig == PlanarConfig.Separate)
                {
                    ErrorExt(this, m_clientdata, m_name, "Can not change \"ImageLength\" when using separate planes");
                    return false;
                }

                m_dir.td_imagelength = row + 1;
                imagegrew = true;
            }

            // Calculate strip and check for crossings.
            int strip;
            if (m_dir.td_planarconfig == PlanarConfig.Separate)
            {
                if (plane >= m_dir.td_samplesperpixel)
                {
                    ErrorExt(this, m_clientdata, m_name,
                        "{0}: Sample out of range, max {1}", plane, m_dir.td_samplesperpixel);
                    return false;
                }

                if (m_dir.td_rowsperstrip != -1)
                    strip = plane * m_dir.td_stripsperimage + row / m_dir.td_rowsperstrip;
                else
                    strip = 0;
            }
            else
            {
                if (m_dir.td_rowsperstrip != -1)
                    strip = row / m_dir.td_rowsperstrip;
                else
                    strip = 0;
            }

            // 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 >= m_dir.td_nstrips && !growStrips(1))
                return false;

            if (strip != m_curstrip)
            {
                // Changing strips - flush any data present.
                if (!FlushData())
                    return false;

                m_curstrip = (int)strip;

                // Watch out for a growing image. The value of strips/image 
                // will initially be 1 (since it can't be deduced until the
                // imagelength is known).
                if (strip >= m_dir.td_stripsperimage && imagegrew)
                    m_dir.td_stripsperimage = howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip);

                m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip;
                if ((m_flags & TiffFlags.CoderSetup) != TiffFlags.CoderSetup)
                {
                    if (!m_currentCodec.SetupEncode())
                        return false;

                    m_flags |= TiffFlags.CoderSetup;
                }

                m_rawcc = 0;
                m_rawcp = 0;

                if (m_dir.td_stripbytecount[strip] > 0)
                {
                    // if we are writing over existing tiles, zero length
                    m_dir.td_stripbytecount[strip] = 0;

                    // this forces appendToStrip() to do a seek
                    m_curoff = 0;
                }

                if (!m_currentCodec.PreEncode(plane))
                    return false;

                m_flags |= TiffFlags.PostEncode;
            }

            // Ensure the write is either sequential or at the beginning of a
            // strip (or that we can randomly access the data - i.e. no encoding).
            if (row != m_row)
            {
                if (row < m_row)
                {
                    // Moving backwards within the same strip:
                    // backup to the start and then decode forward (below).
                    m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip;
                    m_rawcp = 0;
                }

                // Seek forward to the desired row.
                if (!m_currentCodec.Seek(row - m_row))
                    return false;

                m_row = row;
            }

            // swab if needed - note that source buffer will be altered
            postDecode(buffer, offset, m_scanlinesize);

            bool status = m_currentCodec.EncodeRow(buffer, offset, m_scanlinesize, plane);

            // we are now poised at the beginning of the next row
            m_row = row + 1;
            return status;
        }
Beispiel #3
0
        /// <summary>
        /// Unlinks the specified directory from the directory chain.
        /// </summary>
        /// <param name="number">The zero-based number of the directory to unlink.</param>
        /// <returns><c>true</c> if directory was unlinked successfully; otherwise, <c>false</c>.</returns>
        /// <remarks><b>UnlinkDirectory</b> does not removes directory bytes from the file/stream.
        /// It only makes them unused.</remarks>
        public bool UnlinkDirectory(short number)
        {
            const string module = "UnlinkDirectory";

            if (m_mode == O_RDONLY)
            {
                ErrorExt(this, m_clientdata, module, "Can not unlink directory in read-only file");
                return false;
            }

            // Go to the directory before the one we want
            // to unlink and nab the offset of the link
            // field we'll need to patch.
            uint nextdir = m_header.tiff_diroff;
            long off = sizeof(short) + sizeof(short);
            for (int n = number - 1; n > 0; n--)
            {
                if (nextdir == 0)
                {
                    ErrorExt(this, m_clientdata, module,
                        "Directory {0} does not exist", number);
                    return false;
                }

                if (!advanceDirectory(ref nextdir, out off))
                    return false;
            }

            // Advance to the directory to be unlinked and fetch the offset of the directory
            // that follows.
            long dummyOff;
            if (!advanceDirectory(ref nextdir, out dummyOff))
                return false;

            // Go back and patch the link field of the preceding directory to point to the
            // offset of the directory that follows.
            seekFile(off, SeekOrigin.Begin);
            if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab)
                SwabUInt(ref nextdir);

            if (!writeIntOK((int)nextdir))
            {
                ErrorExt(this, m_clientdata, module, "Error writing directory link");
                return false;
            }

            // Leave directory state setup safely. We don't have facilities for doing inserting
            // and removing directories, so it's safest to just invalidate everything. This means
            // that the caller can only append to the directory chain.
            m_currentCodec.Cleanup();
            if ((m_flags & TiffFlags.MyBuffer) == TiffFlags.MyBuffer && m_rawdata != null)
            {
                m_rawdata = null;
                m_rawcc = 0;
            }

            m_flags &= ~(TiffFlags.BeenWriting | TiffFlags.BufferSetup | TiffFlags.PostEncode);
            FreeDirectory();
            setupDefaultDirectory();
            m_diroff = 0; // force link on next write
            m_nextdiroff = 0; // next write must be at end
            m_curoff = 0;
            m_row = -1;
            m_curstrip = -1;
            return true;
        }
Beispiel #4
0
        /// <summary>
        /// Verifies that file/stream is writable and that the directory information is
        /// setup properly.
        /// </summary>
        /// <param name="tiles">If set to <c>true</c> then ability to write tiles will be verified;
        /// otherwise, ability to write strips will be verified.</param>
        /// <param name="method">The name of the calling method.</param>
        /// <returns><c>true</c> if file/stream is writeable and the directory information is
        /// setup properly; otherwise, <c>false</c></returns>
        public bool WriteCheck(bool tiles, string method)
        {
            if (m_mode == O_RDONLY)
            {
                ErrorExt(this, m_clientdata, method, "{0}: File not open for writing", m_name);
                return false;
            }

            if (tiles ^ IsTiled())
            {
                ErrorExt(this, m_clientdata, m_name,
                    tiles ? "Can not write tiles to a stripped image" : "Can not write scanlines to a tiled image");

                return false;
            }

            // On the first write verify all the required information has been setup and
            // initialize any data structures that had to wait until directory information was set.
            // Note that a lot of our work is assumed to remain valid because we disallow any of
            // the important parameters from changing after we start writing (i.e. once
            // BeenWriting is set, SetField will only allow the image's length to be changed).
            if (!fieldSet(FieldBit.ImageDimensions))
            {
                ErrorExt(this, m_clientdata, method, "{0}: Must set \"ImageWidth\" before writing data", m_name);
                return false;
            }

            if (m_dir.td_samplesperpixel == 1)
            {
                // PlanarConfiguration is irrelevant in case of single band images and need not
                // be included. We will set it anyway, because this field is used in other parts
                // of library even in the single band case.
                if (!fieldSet(FieldBit.PlanarConfig))
                    m_dir.td_planarconfig = PlanarConfig.Contig;
            }
            else
            {
                if (!fieldSet(FieldBit.PlanarConfig))
                {
                    ErrorExt(this, m_clientdata, method,
                        "{0}: Must set \"PlanarConfiguration\" before writing data", m_name);

                    return false;
                }
            }

            if (m_dir.td_stripoffset == null && !SetupStrips())
            {
                m_dir.td_nstrips = 0;
                ErrorExt(this, m_clientdata, method,
                    "{0}: No space for {1} arrays", m_name, IsTiled() ? "tile" : "strip");

                return false;
            }

            m_tilesize = IsTiled() ? TileSize() : -1;
            m_scanlinesize = ScanlineSize();
            m_flags |= TiffFlags.BeenWriting;
            return true;
        }
Beispiel #5
0
        /// <summary>
        /// Sets up the data buffer used to write raw (encoded) data to a file.
        /// </summary>
        /// <param name="buffer">The data buffer.</param>
        /// <param name="size">The buffer size.</param>
        /// <remarks>
        /// <para>
        /// This method is provided for client-control of the I/O buffers used by the library.
        /// Applications need never use this method; it's provided only for "intelligent clients"
        /// that wish to optimize memory usage and/or eliminate potential copy operations that can
        /// occur when working with images that have data stored without compression.
        /// </para>
        /// <para>
        /// If the <paramref name="size"/> is -1 then the buffer size is selected to hold a
        /// complete tile or strip, or at least 8 kilobytes, whichever is greater. If the
        /// <paramref name="buffer"/> is <c>null</c>, then a buffer of appropriate size is
        /// allocated by the library.
        /// </para>
        /// </remarks>
        public void WriteBufferSetup(byte[] buffer, int size)
        {
            if (m_rawdata != null)
            {
                if ((m_flags & TiffFlags.MyBuffer) == TiffFlags.MyBuffer)
                    m_flags &= ~TiffFlags.MyBuffer;

                m_rawdata = null;
            }

            if (size == -1)
            {
                size = (IsTiled() ? m_tilesize : StripSize());

                // Make raw data buffer at least 8K
                if (size < 8 * 1024)
                    size = 8 * 1024;

                // force allocation
                buffer = null;
            }

            if (buffer == null)
            {
                buffer = new byte[size];
                m_flags |= TiffFlags.MyBuffer;
            }
            else
            {
                m_flags &= ~TiffFlags.MyBuffer;
            }

            m_rawdata = buffer;
            m_rawdatasize = size;
            m_rawcc = 0;
            m_rawcp = 0;
            m_flags |= TiffFlags.BufferSetup;
        }
Beispiel #6
0
        /// <summary>
        /// Sets up the data buffer used to read raw (encoded) data from a file.
        /// </summary>
        /// <param name="buffer">The data buffer.</param>
        /// <param name="size">The buffer size.</param>
        /// <remarks>
        /// <para>
        /// This method is provided for client-control of the I/O buffers used by the library.
        /// Applications need never use this method; it's provided only for "intelligent clients"
        /// that wish to optimize memory usage and/or eliminate potential copy operations that can
        /// occur when working with images that have data stored without compression.
        /// </para>
        /// <para>
        /// If the <paramref name="buffer"/> is <c>null</c>, then a buffer of appropriate size is
        /// allocated by the library. Otherwise, the caller must guarantee that the buffer is
        /// large enough to hold any individual strip of raw data.
        /// </para>
        /// </remarks>
        public void ReadBufferSetup(byte[] buffer, int size)
        {
            Debug.Assert((m_flags & TiffFlags.NoReadRaw) != TiffFlags.NoReadRaw);
            m_rawdata = null;

            if (buffer != null)
            {
                m_rawdatasize = size;
                m_rawdata = buffer;
                m_flags &= ~TiffFlags.MyBuffer;
            }
            else
            {
                m_rawdatasize = roundUp(size, 1024);
                if (m_rawdatasize > 0)
                {
                    m_rawdata = new byte[m_rawdatasize];
                }
                else
                {
                    Tiff.ErrorExt(this, m_clientdata,
                        "ReadBufferSetup", "{0}: No space for data buffer at scanline {1}", m_name, m_row);
                    m_rawdatasize = 0;
                }

                m_flags |= TiffFlags.MyBuffer;
            }
        }
Beispiel #7
0
        /// <summary>
        /// Reads the contents of the next TIFF directory in an open TIFF file/stream and makes
        /// it the current directory.
        /// </summary>
        /// <returns><c>true</c> if directory was successfully read; otherwise, <c>false</c> if an
        /// error was encountered, or if there are no more directories to be read.</returns>
        /// <remarks><para>Directories are read sequentially.</para>
        /// <para>Applications only need to call <see cref="ReadDirectory"/> to read multiple
        /// subfiles in a single TIFF file/stream - the first directory in a file/stream is
        /// automatically read when <see cref="Open"/> or <see cref="ClientOpen"/> is called.
        /// </para><para>
        /// The images that have a single uncompressed strip or tile of data are automatically
        /// treated as if they were made up of multiple strips or tiles of approximately 8
        /// kilobytes each. This operation is done only in-memory; it does not alter the contents
        /// of the file/stream. However, the construction of the "chopped strips" is visible to
        /// the application through the number of strips returned by <see cref="NumberOfStrips"/>
        /// or the number of tiles returned by <see cref="NumberOfTiles"/>.</para>
        /// </remarks>
        public bool ReadDirectory()
        {
            const string module = "ReadDirectory";

            m_diroff = m_nextdiroff;
            if (m_diroff == 0)
            {
                // no more directories
                return false;
            }

            // Check whether we have the last offset or bad offset (IFD looping).
            if (!checkDirOffset(m_nextdiroff))
                return false;

            // Cleanup any previous compression state.
            m_currentCodec.Cleanup();
            m_curdir++;
            TiffDirEntry[] dir;
            short dircount = fetchDirectory(m_nextdiroff, out dir, out m_nextdiroff);
            if (dircount == 0)
            {
                ErrorExt(this, m_clientdata, module, "{0}: Failed to read directory at offset {1}", m_name, m_nextdiroff);
                return false;
            }

            // reset before new dir
            m_flags &= ~TiffFlags.BeenWriting;

            // Setup default value and then make a pass over the fields to check type and tag
            // information, and to extract info required to size data structures. A second pass is
            // made afterwards to read in everthing not taken in the first pass.

            // free any old stuff and reinit
            FreeDirectory();
            setupDefaultDirectory();

            // Electronic Arts writes gray-scale TIFF files without a PlanarConfiguration
            // directory entry. Thus we setup a default value here, even though the TIFF spec says
            // there is no default value.
            SetField(TiffTag.PlanarConfig, PlanarConfig.Contig);

            // Sigh, we must make a separate pass through the directory for the following reason:
            // 
            // We must process the Compression tag in the first pass in order to merge in
            // codec-private tag definitions (otherwise we may get complaints about unknown tags).
            // However, the Compression tag may be dependent on the SamplesPerPixel tag value
            // because older TIFF specs permited Compression to be written as a
            // SamplesPerPixel-count tag entry. Thus if we don't first figure out the correct
            // SamplesPerPixel tag value then we may end up ignoring the Compression tag value
            // because it has an incorrect count value (if the true value of SamplesPerPixel is not 1).
            //
            // It sure would have been nice if Aldus had really thought this stuff through carefully.

            for (int i = 0; i < dircount; i++)
            {
                TiffDirEntry dp = dir[i];
                if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab)
                {
                    short temp = (short)dp.tdir_tag;
                    SwabShort(ref temp);
                    dp.tdir_tag = (TiffTag)(ushort)temp;

                    temp = (short)dp.tdir_type;
                    SwabShort(ref temp);
                    dp.tdir_type = (TiffType)temp;

                    SwabLong(ref dp.tdir_count);
                    SwabUInt(ref dp.tdir_offset);
                }

                if (dp.tdir_tag == TiffTag.SamplesPerPixel)
                {
                    if (!fetchNormalTag(dir[i]))
                        return false;

                    dp.tdir_tag = TiffTag.Ignore;
                }
            }

            // First real pass over the directory.
            int fix = 0;
            bool diroutoforderwarning = false;
            bool haveunknowntags = false;
            for (int i = 0; i < dircount; i++)
            {
                if (dir[i].tdir_tag == TiffTag.Ignore)
                    continue;

                if (fix >= m_nfields)
                    fix = 0;

                // Silicon Beach (at least) writes unordered directory tags (violating the spec).
                // Handle it here, but be obnoxious (maybe they'll fix it?).
                if (dir[i].tdir_tag < m_fieldinfo[fix].Tag)
                {
                    if (!diroutoforderwarning)
                    {
                        WarningExt(this, m_clientdata, module,
                            "{0}: invalid TIFF directory; tags are not sorted in ascending order", m_name);
                        diroutoforderwarning = true;
                    }

                    fix = 0; // O(n^2)
                }

                while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag)
                    fix++;

                if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag)
                {
                    // Unknown tag ... we'll deal with it below
                    haveunknowntags = true;
                    continue;
                }

                // null out old tags that we ignore.
                if (m_fieldinfo[fix].Bit == FieldBit.Ignore)
                {
                    dir[i].tdir_tag = TiffTag.Ignore;
                    continue;
                }

                // Check data type.
                TiffFieldInfo fip = m_fieldinfo[fix];
                while (dir[i].tdir_type != fip.Type && fix < m_nfields)
                {
                    if (fip.Type == TiffType.Any)
                    {
                        // wildcard
                        break;
                    }

                    fip = m_fieldinfo[++fix];
                    if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag)
                    {
                        WarningExt(this, m_clientdata, module,
                            "{0}: wrong data type {1} for \"{2}\"; tag ignored",
                            m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name);

                        dir[i].tdir_tag = TiffTag.Ignore;
                        continue;
                    }
                }

                // Check count if known in advance.
                if (fip.ReadCount != TiffFieldInfo.Variable &&
                    fip.ReadCount != TiffFieldInfo.Variable2)
                {
                    int expected = fip.ReadCount;
                    if (fip.ReadCount == TiffFieldInfo.Spp)
                        expected = m_dir.td_samplesperpixel;

                    if (!checkDirCount(dir[i], expected))
                    {
                        dir[i].tdir_tag = TiffTag.Ignore;
                        continue;
                    }
                }

                switch (dir[i].tdir_tag)
                {
                    case TiffTag.Compression:
                        // The 5.0 spec says the Compression tag has one value,
                        // while earlier specs say it has one value per sample.
                        // Because of this, we accept the tag if one value is supplied.
                        if (dir[i].tdir_count == 1)
                        {
                            int v = extractData(dir[i]);
                            if (!SetField(dir[i].tdir_tag, v))
                                return false;

                            break;
                            // XXX: workaround for broken TIFFs
                        }
                        else if (dir[i].tdir_type == TiffType.Long)
                        {
                            int v;
                            if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v))
                                return false;
                        }
                        else
                        {
                            short iv;
                            if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv))
                                return false;
                        }
                        dir[i].tdir_tag = TiffTag.Ignore;
                        break;
                    case TiffTag.StripOffsets:
                    case TiffTag.StripByteCounts:
                    case TiffTag.TileOffsets:
                    case TiffTag.TileByteCounts:
                        setFieldBit(fip.Bit);
                        break;
                    case TiffTag.ImageWidth:
                    case TiffTag.ImageLength:
                    case TiffTag.IMAGEDEPTH:
                    case TiffTag.TileLength:
                    case TiffTag.TileWidth:
                    case TiffTag.TILEDEPTH:
                    case TiffTag.PlanarConfig:
                    case TiffTag.RowsPerStrip:
                    case TiffTag.ExtraSamples:
                        if (!fetchNormalTag(dir[i]))
                            return false;
                        dir[i].tdir_tag = TiffTag.Ignore;
                        break;
                }
            }

            // If we saw any unknown tags, make an extra pass over the directory to deal with
            // them. This must be done separately because the tags could have become known when we
            // registered a codec after finding the Compression tag. In a correctly-sorted
            // directory there's no problem because Compression will come before any codec-private
            // tags, but if the sorting is wrong that might not hold.
            if (haveunknowntags)
            {
                fix = 0;
                for (int i = 0; i < dircount; i++)
                {
                    if (dir[i].tdir_tag == TiffTag.Ignore)
                        continue;

                    if (fix >= m_nfields || dir[i].tdir_tag < m_fieldinfo[fix].Tag)
                    {
                        // O(n^2)
                        fix = 0;
                    }

                    while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag)
                        fix++;

                    if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag)
                    {
                        Tiff.WarningExt(this, m_clientdata, module,
                            "{0}: unknown field with tag {1} (0x{2:x}) encountered",
                            m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag);

                        TiffFieldInfo[] arr = new TiffFieldInfo[1];
                        arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type);
                        MergeFieldInfo(arr, 1);

                        fix = 0;
                        while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag)
                            fix++;
                    }

                    // Check data type.
                    TiffFieldInfo fip = m_fieldinfo[fix];
                    while (dir[i].tdir_type != fip.Type && fix < m_nfields)
                    {
                        if (fip.Type == TiffType.Any)
                        {
                            // wildcard
                            break;
                        }

                        fip = m_fieldinfo[++fix];
                        if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag)
                        {
                            Tiff.WarningExt(this, m_clientdata, module,
                                "{0}: wrong data type {1} for \"{2}\"; tag ignored",
                                m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name);

                            dir[i].tdir_tag = TiffTag.Ignore;
                            break;
                        }
                    }
                }
            }

            // XXX: OJPEG hack.
            // If a) compression is OJPEG, b) planarconfig tag says it's separate, c) strip
            // offsets/bytecounts tag are both present and d) both contain exactly one value, then
            // we consistently find that the buggy implementation of the buggy compression scheme
            // matches contig planarconfig best. So we 'fix-up' the tag here
            if ((m_dir.td_compression == Compression.OJPEG) && (m_dir.td_planarconfig == PlanarConfig.Separate))
            {
                int dpIndex = readDirectoryFind(dir, dircount, TiffTag.StripOffsets);
                if (dpIndex != -1 && dir[dpIndex].tdir_count == 1)
                {
                    dpIndex = readDirectoryFind(dir, dircount, TiffTag.StripByteCounts);
                    if (dpIndex != -1 && dir[dpIndex].tdir_count == 1)
                    {
                        m_dir.td_planarconfig = PlanarConfig.Contig;
                        WarningExt(this, m_clientdata, "ReadDirectory",
                            "Planarconfig tag value assumed incorrect, assuming data is contig instead of chunky");
                    }
                }
            }

            // Allocate directory structure and setup defaults.
            if (!fieldSet(FieldBit.ImageDimensions))
            {
                missingRequired("ImageLength");
                return false;
            }

            // Setup appropriate structures (by strip or by tile)
            if (!fieldSet(FieldBit.TileDimensions))
            {
                m_dir.td_nstrips = NumberOfStrips();
                m_dir.td_tilewidth = m_dir.td_imagewidth;
                m_dir.td_tilelength = m_dir.td_rowsperstrip;
                m_dir.td_tiledepth = m_dir.td_imagedepth;
                m_flags &= ~TiffFlags.IsTiled;
            }
            else
            {
                m_dir.td_nstrips = NumberOfTiles();
                m_flags |= TiffFlags.IsTiled;
            }

            if (m_dir.td_nstrips == 0)
            {
                ErrorExt(this, m_clientdata, module,
                    "{0}: cannot handle zero number of {1}", m_name, IsTiled() ? "tiles" : "strips");
                return false;
            }

            m_dir.td_stripsperimage = m_dir.td_nstrips;
            if (m_dir.td_planarconfig == PlanarConfig.Separate)
                m_dir.td_stripsperimage /= m_dir.td_samplesperpixel;

            if (!fieldSet(FieldBit.StripOffsets))
            {
                if ((m_dir.td_compression == Compression.OJPEG) && !IsTiled() && (m_dir.td_nstrips == 1))
                {
                    // XXX: OJPEG hack.
                    // If a) compression is OJPEG, b) it's not a tiled TIFF, and c) the number of
                    // strips is 1, then we tolerate the absence of stripoffsets tag, because,
                    // presumably, all required data is in the JpegInterchangeFormat stream.
                    setFieldBit(FieldBit.StripOffsets);
                }
                else
                {
                    missingRequired(IsTiled() ? "TileOffsets" : "StripOffsets");
                    return false;
                }
            }

            // Second pass: extract other information.
            for (int i = 0; i < dircount; i++)
            {
                if (dir[i].tdir_tag == TiffTag.Ignore)
                    continue;

                switch (dir[i].tdir_tag)
                {
                    case TiffTag.MinSampleValue:
                    case TiffTag.MaxSampleValue:
                    case TiffTag.BitsPerSample:
                    case TiffTag.DATATYPE:
                    case TiffTag.SampleFormat:
                        // The 5.0 spec says the Compression tag has one value, while earlier
                        // specs say it has one value per sample. Because of this, we accept the
                        // tag if one value is supplied.
                        //
                        // The MinSampleValue, MaxSampleValue, BitsPerSample DataType and
                        // SampleFormat tags are supposed to be written as one value/sample, but
                        // some vendors incorrectly write one value only - so we accept that as
                        // well (yech). Other vendors write correct value for NumberOfSamples, but
                        // incorrect one for BitsPerSample and friends, and we will read this too.
                        if (dir[i].tdir_count == 1)
                        {
                            int v = extractData(dir[i]);
                            if (!SetField(dir[i].tdir_tag, v))
                                return false;
                            // XXX: workaround for broken TIFFs
                        }
                        else if (dir[i].tdir_tag == TiffTag.BitsPerSample && dir[i].tdir_type == TiffType.Long)
                        {
                            int v;
                            if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v))
                                return false;
                        }
                        else
                        {
                            short iv;
                            if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv))
                                return false;
                        }
                        break;
                    case TiffTag.SMinSampleValue:
                    case TiffTag.SMaxSampleValue:
                        double dv;
                        if (!fetchPerSampleAnys(dir[i], out dv) || !SetField(dir[i].tdir_tag, dv))
                            return false;
                        break;
                    case TiffTag.StripOffsets:
                    case TiffTag.TileOffsets:
                        if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripoffset))
                            return false;
                        break;
                    case TiffTag.StripByteCounts:
                    case TiffTag.TileByteCounts:
                        if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripbytecount))
                            return false;
                        break;
                    case TiffTag.Colormap:
                    case TiffTag.TransferFunction:
                        {
                            // TransferFunction can have either 1x or 3x data values;
                            // Colormap can have only 3x items.
                            int v = 1 << m_dir.td_bitspersample;
                            if (dir[i].tdir_tag == TiffTag.Colormap || dir[i].tdir_count != v)
                            {
                                if (!checkDirCount(dir[i], 3 * v))
                                    break;
                            }

                            byte[] cp = new byte[dir[i].tdir_count * sizeof(short)];
                            if (fetchData(dir[i], cp) != 0)
                            {
                                int c = 1 << m_dir.td_bitspersample;
                                if (dir[i].tdir_count == c)
                                {
                                    // This deals with there being only one array to apply to all samples.
                                    short[] u = ByteArrayToShorts(cp, 0, dir[i].tdir_count * sizeof(short));
                                    SetField(dir[i].tdir_tag, u, u, u);
                                }
                                else
                                {
                                    v *= sizeof(short);
                                    short[] u0 = ByteArrayToShorts(cp, 0, v);
                                    short[] u1 = ByteArrayToShorts(cp, v, v);
                                    short[] u2 = ByteArrayToShorts(cp, 2 * v, v);
                                    SetField(dir[i].tdir_tag, u0, u1, u2);
                                }
                            }
                            break;
                        }
                    case TiffTag.PageNumber:
                    case TiffTag.HalfToneHints:
                    case TiffTag.YCBCRSUBSAMPLING:
                    case TiffTag.DotRange:
                        fetchShortPair(dir[i]);
                        break;
                    case TiffTag.REFERENCEBLACKWHITE:
                        fetchRefBlackWhite(dir[i]);
                        break;
                    // BEGIN REV 4.0 COMPATIBILITY
                    case TiffTag.OSubFileType:
                        FileType ft = 0;
                        switch ((SubFileType)extractData(dir[i]))
                        {
                            case SubFileType.ReducedSizeImage:
                                ft = FileType.ReducedResImage;
                                break;
                            case SubFileType.Page:
                                ft = FileType.Page;
                                break;
                        }

                        if (ft != 0)
                            SetField(TiffTag.SubFileType, ft);

                        break;
                    // END REV 4.0 COMPATIBILITY
                    default:
                        fetchNormalTag(dir[i]);
                        break;
                }
            }

            // OJPEG hack:
            // - If a) compression is OJPEG, and b) photometric tag is missing, then we
            // consistently find that photometric should be YCbCr
            // - If a) compression is OJPEG, and b) photometric tag says it's RGB, then we
            // consistently find that the buggy implementation of the buggy compression scheme
            // matches photometric YCbCr instead.
            // - If a) compression is OJPEG, and b) bitspersample tag is missing, then we
            // consistently find bitspersample should be 8.
            // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric
            // is RGB or YCbCr, then we consistently find samplesperpixel should be 3
            // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric
            // is MinIsWhite or MinIsBlack, then we consistently find samplesperpixel should be 3
            if (m_dir.td_compression == Compression.OJPEG)
            {
                if (!fieldSet(FieldBit.Photometric))
                {
                    WarningExt(this, m_clientdata,
                        "ReadDirectory", "Photometric tag is missing, assuming data is YCbCr");

                    if (!SetField(TiffTag.Photometric, Photometric.YCBCR))
                        return false;
                }
                else if (m_dir.td_photometric == Photometric.RGB)
                {
                    m_dir.td_photometric = Photometric.YCBCR;
                    WarningExt(this, m_clientdata, "ReadDirectory",
                        "Photometric tag value assumed incorrect, assuming data is YCbCr instead of RGB");
                }

                if (!fieldSet(FieldBit.BitsPerSample))
                {
                    WarningExt(this, m_clientdata, "ReadDirectory",
                        "BitsPerSample tag is missing, assuming 8 bits per sample");

                    if (!SetField(TiffTag.BitsPerSample, 8))
                        return false;
                }

                if (!fieldSet(FieldBit.SamplesPerPixel))
                {
                    if ((m_dir.td_photometric == Photometric.RGB) ||
                        (m_dir.td_photometric == Photometric.YCBCR))
                    {
                        WarningExt(this, m_clientdata, "ReadDirectory",
                            "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 3");

                        if (!SetField(TiffTag.SamplesPerPixel, 3))
                            return false;
                    }
                    else if ((m_dir.td_photometric == Photometric.MinIsWhite) ||
                        (m_dir.td_photometric == Photometric.MinIsBlack))
                    {
                        WarningExt(this, m_clientdata, "ReadDirectory",
                            "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 1");
                        if (!SetField(TiffTag.SamplesPerPixel, 1))
                            return false;
                    }
                }
            }

            // Verify Palette image has a Colormap.
            if (m_dir.td_photometric == Photometric.Palette && !fieldSet(FieldBit.ColorMap))
            {
                missingRequired("Colormap");
                return false;
            }

            // OJPEG hack:
            // We do no further messing with strip/tile offsets/bytecounts in OJPEG TIFFs
            if (m_dir.td_compression != Compression.OJPEG)
            {
                // Attempt to deal with a missing StripByteCounts tag.
                if (!fieldSet(FieldBit.StripByteCounts))
                {
                    // Some manufacturers violate the spec by not giving the size of the strips.
                    // In this case, assume there is one uncompressed strip of data.
                    if ((m_dir.td_planarconfig == PlanarConfig.Contig && m_dir.td_nstrips > 1) ||
                        (m_dir.td_planarconfig == PlanarConfig.Separate && m_dir.td_nstrips != m_dir.td_samplesperpixel))
                    {
                        missingRequired("StripByteCounts");
                        return false;
                    }

                    WarningExt(this, m_clientdata, module,
                        "{0}: TIFF directory is missing required \"{1}\" field, calculating from imagelength",
                        m_name, FieldWithTag(TiffTag.StripByteCounts).Name);

                    if (!estimateStripByteCounts(dir, dircount))
                        return false;
                }
                else if (m_dir.td_nstrips == 1 && m_dir.td_stripoffset[0] != 0 && byteCountLooksBad(m_dir))
                {
                    // XXX: Plexus (and others) sometimes give a value of zero for a tag when
                    // they don't know what the correct value is! Try and handle the simple case
                    // of estimating the size of a one strip image.
                    WarningExt(this, m_clientdata, module,
                        "{0}: Bogus \"{1}\" field, ignoring and calculating from imagelength",
                        m_name, FieldWithTag(TiffTag.StripByteCounts).Name);

                    if (!estimateStripByteCounts(dir, dircount))
                        return false;
                }
                else if (m_dir.td_planarconfig == PlanarConfig.Contig && m_dir.td_nstrips > 2 &&
                    m_dir.td_compression == Compression.None &&
                    m_dir.td_stripbytecount[0] != m_dir.td_stripbytecount[1])
                {
                    // XXX: Some vendors fill StripByteCount array with absolutely wrong values
                    // (it can be equal to StripOffset array, for example). Catch this case here.
                    WarningExt(this, m_clientdata, module,
                        "{0}: Wrong \"{1}\" field, ignoring and calculating from imagelength",
                        m_name, FieldWithTag(TiffTag.StripByteCounts).Name);

                    if (!estimateStripByteCounts(dir, dircount))
                        return false;
                }
            }

            dir = null;

            if (!fieldSet(FieldBit.MaxSampleValue))
                m_dir.td_maxsamplevalue = (short)((1 << m_dir.td_bitspersample) - 1);

            // Setup default compression scheme.

            // XXX: We can optimize checking for the strip bounds using the sorted bytecounts
            // array. See also comments for appendToStrip() function.
            if (m_dir.td_nstrips > 1)
            {
                m_dir.td_stripbytecountsorted = true;
                for (int strip = 1; strip < m_dir.td_nstrips; strip++)
                {
                    if (m_dir.td_stripoffset[strip - 1] > m_dir.td_stripoffset[strip])
                    {
                        m_dir.td_stripbytecountsorted = false;
                        break;
                    }
                }
            }

            if (!fieldSet(FieldBit.Compression))
                SetField(TiffTag.Compression, Compression.None);

            // Some manufacturers make life difficult by writing large amounts of uncompressed
            // data as a single strip. This is contrary to the recommendations of the spec. The
            // following makes an attempt at breaking such images into strips closer to the
            // recommended 8k bytes. A side effect, however, is that the RowsPerStrip tag value
            // may be changed.
            if (m_dir.td_nstrips == 1 && m_dir.td_compression == Compression.None &&
                (m_flags & TiffFlags.StripChop) == TiffFlags.StripChop &&
                (m_flags & TiffFlags.IsTiled) != TiffFlags.IsTiled)
            {
                chopUpSingleUncompressedStrip();
            }

            // Reinitialize i/o since we are starting on a new directory.
            m_row = -1;
            m_curstrip = -1;
            m_col = -1;
            m_curtile = -1;
            m_tilesize = -1;

            m_scanlinesize = ScanlineSize();
            if (m_scanlinesize == 0)
            {
                ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero scanline size", m_name);
                return false;
            }

            if (IsTiled())
            {
                m_tilesize = TileSize();
                if (m_tilesize == 0)
                {
                    ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero tile size", m_name);
                    return false;
                }
            }
            else
            {
                if (StripSize() == 0)
                {
                    ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero strip size", m_name);
                    return false;
                }
            }

            return true;
        }
Beispiel #8
0
        /// <summary>
        /// Flushes any pending image data for the specified file to be written out.
        /// </summary>
        /// <returns><c>true</c> if succeeded; otherwise, <c>false</c></returns>
        /// <remarks><see cref="FlushData"/> flushes any pending image data for the specified file
        /// to be written out; directory-related data are not flushed. In normal operation this
        /// call is never needed − the library automatically does any flushing required.
        /// </remarks>
        /// <seealso cref="Flush"/>
        public bool FlushData()
        {
            if ((m_flags & TiffFlags.BeenWriting) != TiffFlags.BeenWriting)
                return false;

            if ((m_flags & TiffFlags.PostEncode) == TiffFlags.PostEncode)
            {
                m_flags &= ~TiffFlags.PostEncode;
                if (!m_currentCodec.PostEncode())
                    return false;
            }

            return flushData1();
        }
Beispiel #9
0
        /*
        * Setup a default directory structure.
        */
        private void setupDefaultDirectory()
        {
            int tiffFieldInfoCount;
            TiffFieldInfo[] tiffFieldInfo = getFieldInfo(out tiffFieldInfoCount);
            setupFieldInfo(tiffFieldInfo, tiffFieldInfoCount);

            m_dir = new TiffDirectory();
            m_postDecodeMethod = PostDecodeMethodType.pdmNone;
            m_foundfield = null;

            m_tagmethods = m_defaultTagMethods;

            /*
             *  Give client code a chance to install their own
             *  tag extensions & methods, prior to compression overloads.
             */
            if (m_extender != null)
                m_extender(this);

            SetField(TiffTag.Compression, Compression.None);

            /*
             * NB: The directory is marked dirty as a result of setting
             * up the default compression scheme.  However, this really
             * isn't correct -- we want DirtyDirect to be set only
             * if the user does something.  We could just do the setup
             * by hand, but it seems better to use the normal mechanism
             * (i.e. SetField).
             */
            m_flags &= ~TiffFlags.DirtyDirect;

            /*
             * we clear the IsTiled flag when setting up a new directory.
             * Should we also be clearing stuff like InSubIFD?
             */
            m_flags &= ~TiffFlags.IsTiled;

            /*
             * Clear other directory-specific fields.
             */
            m_tilesize = -1;
            m_scanlinesize = -1;
        }
Beispiel #10
0
        /// <summary>
        /// Link the current directory into the directory chain for the file.
        /// </summary>
        private bool linkDirectory()
        {
            const string module = "linkDirectory";

            m_diroff = (uint)((seekFile(0, SeekOrigin.End) + 1) & ~1);
            uint diroff = m_diroff;
            if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab)
                SwabUInt(ref diroff);

            // Handle SubIFDs

            if ((m_flags & TiffFlags.InSubIFD) == TiffFlags.InSubIFD)
            {
                seekFile(m_subifdoff, SeekOrigin.Begin);
                if (!writeIntOK((int)diroff))
                {
                    ErrorExt(this, m_clientdata, module,
                        "{0}: Error writing SubIFD directory link", m_name);
                    return false;
                }

                // Advance to the next SubIFD or, if this is the last one
                // configured, revert back to the normal directory linkage.
                --m_nsubifd;

                if (m_nsubifd != 0)
                    m_subifdoff += sizeof(int);
                else
                    m_flags &= ~TiffFlags.InSubIFD;

                return true;
            }

            if (m_header.tiff_diroff == 0)
            {
                // First directory, overwrite offset in header.

                m_header.tiff_diroff = m_diroff;
                seekFile(TiffHeader.TIFF_MAGIC_SIZE + TiffHeader.TIFF_VERSION_SIZE, SeekOrigin.Begin);
                if (!writeIntOK((int)diroff))
                {
                    ErrorExt(this, m_clientdata, m_name, "Error writing TIFF header");
                    return false;
                }

                return true;
            }

            // Not the first directory, search to the last and append.

            uint nextdir = m_header.tiff_diroff;
            do
            {
                short dircount;
                if (!seekOK(nextdir) || !readShortOK(out dircount))
                {
                    ErrorExt(this, m_clientdata, module, "Error fetching directory count");
                    return false;
                }

                if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab)
                    SwabShort(ref dircount);

                seekFile(dircount * TiffDirEntry.SizeInBytes, SeekOrigin.Current);
                if (!readUIntOK(out nextdir))
                {
                    ErrorExt(this, m_clientdata, module, "Error fetching directory link");
                    return false;
                }

                if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab)
                    SwabUInt(ref nextdir);
            }
            while (nextdir != 0);

            // get current offset
            long off = seekFile(0, SeekOrigin.Current);
            seekFile(off - sizeof(int), SeekOrigin.Begin);

            if (!writeIntOK((int)diroff))
            {
                ErrorExt(this, m_clientdata, module, "Error writing directory link");
                return false;
            }

            return true;
        }
Beispiel #11
0
        /// <summary>
        /// Writes the contents of the current directory to the specified file.
        /// </summary>
        /// <remarks>This routine doesn't handle overwriting a directory with
        /// auxiliary storage that's been changed.</remarks>
        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;
        }
Beispiel #12
0
        internal bool setCompressionScheme(Compression scheme)
        {
            TiffCodec c = FindCodec(scheme);
            if (c == null)
            {
                /*
                 * Don't treat an unknown compression scheme as an error.
                 * This permits applications to open files with data that
                 * the library does not have builtin support for, but which
                 * may still be meaningful.
                 */
                c = m_builtInCodecs[0];
            }

            m_decodestatus = c.CanDecode;
            m_flags &= ~(TiffFlags.NoBitRev | TiffFlags.NoReadRaw);

            m_currentCodec = c;
            return c.Init();
        }
Beispiel #13
0
        /// <summary>
        /// Encodes and writes a tile of data to an open TIFF file/stream.
        /// </summary>
        /// <param name="tile">The zero-based index of the tile to write.</param>
        /// <param name="buffer">The buffer with image data to be encoded and written.</param>
        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which
        /// to begin reading bytes to be encoded and written.</param>
        /// <param name="count">The maximum number of tile bytes to be read from
        /// <paramref name="buffer"/>.</param>
        /// <returns>The number of encoded and written bytes or <c>-1</c> if an error occurred.</returns>
        /// <remarks>
        /// <para>
        /// <b>WriteEncodedTile</b> encodes <paramref name="count"/> bytes of raw data from
        /// <paramref name="buffer"/> and append the result to the end of the specified tile. Note
        /// that the value of <paramref name="tile"/> is a "raw tile number". That is, the caller
        /// must take into account whether or not the data are organized in separate planes
        /// (<see cref="TiffTag.PlanarConfig"/> = <see cref="PlanarConfig"/>.Separate).
        /// <see cref="ComputeTile"/> automatically does this when converting an (x, y, z, plane)
        /// coordinate quadruple to a tile number.
        /// </para><para>
        /// 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
        /// were performed.
        /// </para><para>
        /// A correct value for the <see cref="TiffTag.ImageLength"/> tag must be setup before
        /// writing; <b>WriteEncodedTile</b> does not support automatically growing the image on
        /// each write (as <see cref="O:BitMiracle.LibTiff.Classic.Tiff.WriteScanline"/> does).
        /// </para><para>
        /// The library writes encoded data using the native machine byte order. Correctly
        /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly
        /// process image data with value of <see cref="TiffTag.BitsPerSample"/> tag greater
        /// than 8.
        /// </para></remarks>
        public int WriteEncodedTile(int tile, byte[] buffer, int offset, int count)
        {
            const string module = "WriteEncodedTile";

            if (!writeCheckTiles(module))
                return -1;

            if (tile >= m_dir.td_nstrips)
            {
                ErrorExt(this, m_clientdata, module, "{0}: Tile {1} out of range, max {2}", m_name, tile, m_dir.td_nstrips);
                return -1;
            }

            // Handle delayed allocation of data buffer. This permits it to be sized more
            // intelligently (using directory information).
            bufferCheck();

            m_curtile = tile;

            m_rawcc = 0;
            m_rawcp = 0;

            if (m_dir.td_stripbytecount[tile] > 0)
            {
                // this forces appendToStrip() to do a seek
                m_curoff = 0;
            }

            // Compute tiles per row & per column to compute current row and column
            m_row = (tile % howMany(m_dir.td_imagelength, m_dir.td_tilelength)) * m_dir.td_tilelength;
            m_col = (tile % howMany(m_dir.td_imagewidth, m_dir.td_tilewidth)) * m_dir.td_tilewidth;

            if ((m_flags & TiffFlags.CoderSetup) != TiffFlags.CoderSetup)
            {
                if (!m_currentCodec.SetupEncode())
                    return -1;

                m_flags |= TiffFlags.CoderSetup;
            }

            m_flags &= ~TiffFlags.PostEncode;
            short sample = (short)(tile / m_dir.td_stripsperimage);
            if (!m_currentCodec.PreEncode(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 (count < 1 || count > m_tilesize)
                count = m_tilesize;

            // swab if needed - note that source buffer will be altered
            postDecode(buffer, offset, count);

            if (!m_currentCodec.EncodeTile(buffer, offset, count, sample))
                return 0;

            if (!m_currentCodec.PostEncode())
                return -1;

            if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NoBitRev) != TiffFlags.NoBitRev)
                ReverseBits(m_rawdata, m_rawcc);

            if (m_rawcc > 0 && !appendToStrip(tile, m_rawdata, 0, m_rawcc))
                return -1;

            m_rawcc = 0;
            m_rawcp = 0;
            return count;
        }
Beispiel #14
0
        /// <summary>
        /// Encodes and writes a strip of data to an open TIFF file/stream.
        /// </summary>
        /// <param name="strip">The zero-based index of the strip to write.</param>
        /// <param name="buffer">The buffer with image data to be encoded and written.</param>
        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which
        /// to begin reading bytes to be encoded and written.</param>
        /// <param name="count">The maximum number of strip bytes to be read from
        /// <paramref name="buffer"/>.</param>
        /// <returns>The number of encoded and written bytes or <c>-1</c> if an error occurred.</returns>
        /// <remarks>
        /// <para>
        /// <b>WriteEncodedStrip</b> encodes <paramref name="count"/> bytes of raw data from
        /// <paramref name="buffer"/> and append the result to the specified strip; replacing any
        /// previously written data. Note that the value of <paramref name="strip"/> is a "raw
        /// strip number". That is, the caller must take into account whether or not the data are
        /// organized in separate planes
        /// (<see cref="TiffTag.PlanarConfig"/> = <see cref="PlanarConfig"/>.Separate).
        /// <see cref="ComputeStrip"/> automatically does this when converting an (row, plane) to
        /// a strip index.
        /// </para><para>
        /// If there is no space for the strip, the value of <see cref="TiffTag.ImageLength"/>
        /// tag is automatically increased to include the strip (except for
        /// <see cref="TiffTag.PlanarConfig"/> = <see cref="PlanarConfig"/>.Separate, where the
        /// <see cref="TiffTag.ImageLength"/> tag cannot be changed once the first data are
        /// written). If the <see cref="TiffTag.ImageLength"/> is increased, the values of
        /// <see cref="TiffTag.StripOffsets"/> and <see cref="TiffTag.StripByteCounts"/> tags are
        /// similarly enlarged to reflect data written past the previous end of image.
        /// </para><para>
        /// The library writes encoded data using the native machine byte order. Correctly
        /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly
        /// process image data with value of <see cref="TiffTag.BitsPerSample"/> tag greater
        /// than 8.
        /// </para></remarks>
        public int WriteEncodedStrip(int strip, byte[] buffer, int offset, int count)
        {
            const string module = "WriteEncodedStrip";

            if (!writeCheckStrips(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 >= m_dir.td_nstrips)
            {
                if (m_dir.td_planarconfig == PlanarConfig.Separate)
                {
                    ErrorExt(this, m_clientdata, m_name, "Can not grow image by strips when using separate planes");
                    return -1;
                }

                if (!growStrips(1))
                    return -1;

                m_dir.td_stripsperimage = howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip);
            }

            // Handle delayed allocation of data buffer. This permits it to be sized according to
            // the directory info.
            bufferCheck();

            m_curstrip = strip;
            m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip;
            if ((m_flags & TiffFlags.CoderSetup) != TiffFlags.CoderSetup)
            {
                if (!m_currentCodec.SetupEncode())
                    return -1;

                m_flags |= TiffFlags.CoderSetup;
            }

            m_rawcc = 0;
            m_rawcp = 0;

            if (m_dir.td_stripbytecount[strip] > 0)
            {
                // this forces appendToStrip() to do a seek
                m_curoff = 0;
            }

            m_flags &= ~TiffFlags.PostEncode;
            short sample = (short)(strip / m_dir.td_stripsperimage);
            if (!m_currentCodec.PreEncode(sample))
                return -1;

            // swab if needed - note that source buffer will be altered
            postDecode(buffer, offset, count);

            if (!m_currentCodec.EncodeStrip(buffer, offset, count, sample))
                return 0;

            if (!m_currentCodec.PostEncode())
                return -1;

            if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NoBitRev) != TiffFlags.NoBitRev)
                ReverseBits(m_rawdata, m_rawcc);

            if (m_rawcc > 0 && !appendToStrip(strip, m_rawdata, 0, m_rawcc))
                return -1;

            m_rawcc = 0;
            m_rawcp = 0;
            return count;
        }
Beispiel #15
0
        private bool isFillOrder(FillOrder o)
        {
            TiffFlags order = (TiffFlags)o;

            return((m_flags & order) == order);
        }