Пример #1
0
        /// <summary>
        /// Writes decoded pixel to buffer at a given position.
        /// </summary>
        /// <param name="buffer">The buffer to write to.</param>
        /// <param name="offset">The position to write to.</param>
        /// <returns>The number of bytes written.</returns>
        public int WriteTo(Span <byte> buffer, int offset)
        {
            if (this.Length == 0)
            {
                return(0);
            }

            if (this.Length == 1)
            {
                buffer[offset] = this.value;
                return(1);
            }

            LzwString e      = this;
            int       endIdx = this.Length - 1;

            if (endIdx >= buffer.Length)
            {
                TiffThrowHelper.ThrowImageFormatException("Error reading lzw compressed stream. Either pixel buffer to write to is to small or code length is invalid!");
            }

            for (int i = endIdx; i >= 0; i--)
            {
                buffer[offset + i] = e.value;
                e = e.previous;
            }

            return(this.Length);
        }
Пример #2
0
        private void AddStringToTable(LzwString lzwString)
        {
            if (this.tableLength > this.table.Length)
            {
                TiffThrowHelper.ThrowImageFormatException($"TIFF LZW with more than {MaxBits} bits per code encountered (table overflow)");
            }

            this.table[this.tableLength++] = lzwString;

            if (this.tableLength > this.maxCode)
            {
                this.bitsPerCode++;

                if (this.bitsPerCode > MaxBits)
                {
                    // Continue reading MaxBits (12 bit) length codes.
                    this.bitsPerCode = MaxBits;
                }

                this.bitMask = BitmaskFor(this.bitsPerCode);
                this.maxCode = this.MaxCode();
            }

            if (lzwString.Length > this.maxString)
            {
                this.maxString = lzwString.Length;
            }
        }
Пример #3
0
        /// <summary>
        /// Decodes and decompresses all pixel indices from the stream.
        /// </summary>
        /// <param name="pixels">The pixel array to decode to.</param>
        public void DecodePixels(Span <byte> pixels)
        {
            // Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
            // See Section 13: "LZW Compression"/"LZW Decoding", page 61+
            int code;
            int offset = 0;

            while ((code = this.GetNextCode()) != EoiCode)
            {
                if (code == ClearCode)
                {
                    this.Init();
                    code = this.GetNextCode();

                    if (code == EoiCode)
                    {
                        break;
                    }

                    if (this.table[code] == null)
                    {
                        TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {code} (table size: {this.tableLength})");
                    }

                    offset += this.table[code].WriteTo(pixels, offset);
                }
                else
                {
                    if (this.table[this.oldCode] == null)
                    {
                        TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {this.oldCode} (table size: {this.tableLength})");
                    }

                    if (this.IsInTable(code))
                    {
                        offset += this.table[code].WriteTo(pixels, offset);

                        this.AddStringToTable(this.table[this.oldCode].Concatenate(this.table[code].FirstChar));
                    }
                    else
                    {
                        LzwString outString = this.table[this.oldCode].Concatenate(this.table[this.oldCode].FirstChar);

                        offset += outString.WriteTo(pixels, offset);
                        this.AddStringToTable(outString);
                    }
                }

                this.oldCode = code;

                if (offset >= pixels.Length)
                {
                    break;
                }
            }
        }
Пример #4
0
        private void LoadNewByte()
        {
            this.Position++;
            this.ResetBitsRead();

            if (this.Position >= (ulong)this.DataLength)
            {
                TiffThrowHelper.ThrowImageFormatException("tiff image has invalid ccitt compressed data");
            }
        }
        /// <inheritdoc/>
        protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span <byte> buffer)
        {
            if (this.compressedDataMemory == null)
            {
                this.compressedDataMemory = this.Allocator.Allocate <byte>(byteCount);
            }
            else if (this.compressedDataMemory.Length() < byteCount)
            {
                this.compressedDataMemory.Dispose();
                this.compressedDataMemory = this.Allocator.Allocate <byte>(byteCount);
            }

            Span <byte> compressedData = this.compressedDataMemory.GetSpan();

            stream.Read(compressedData, 0, byteCount);
            int compressedOffset   = 0;
            int decompressedOffset = 0;

            while (compressedOffset < byteCount)
            {
                byte headerByte = compressedData[compressedOffset];

                if (headerByte <= 127)
                {
                    int literalOffset = compressedOffset + 1;
                    int literalLength = compressedData[compressedOffset] + 1;

                    if ((literalOffset + literalLength) > compressedData.Length)
                    {
                        TiffThrowHelper.ThrowImageFormatException("Tiff packbits compression error: not enough data.");
                    }

                    compressedData.Slice(literalOffset, literalLength).CopyTo(buffer.Slice(decompressedOffset));

                    compressedOffset   += literalLength + 1;
                    decompressedOffset += literalLength;
                }
                else if (headerByte == 0x80)
                {
                    compressedOffset += 1;
                }
                else
                {
                    byte repeatData   = compressedData[compressedOffset + 1];
                    int  repeatLength = 257 - headerByte;

                    ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength);

                    compressedOffset   += 2;
                    decompressedOffset += repeatLength;
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Decompresses image data into the supplied buffer.
        /// </summary>
        /// <param name="stream">The <see cref="Stream" /> to read image data from.</param>
        /// <param name="stripOffset">The strip offset of stream.</param>
        /// <param name="stripByteCount">The number of bytes to read from the input stream.</param>
        /// <param name="stripHeight">The height of the strip.</param>
        /// <param name="buffer">The output buffer for uncompressed data.</param>
        public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span <byte> buffer)
        {
            DebugGuard.MustBeLessThanOrEqualTo(stripOffset, (ulong)long.MaxValue, nameof(stripOffset));
            DebugGuard.MustBeLessThanOrEqualTo(stripByteCount, (ulong)long.MaxValue, nameof(stripByteCount));

            stream.Seek((long)stripOffset, SeekOrigin.Begin);
            this.Decompress(stream, (int)stripByteCount, stripHeight, buffer);

            if ((long)stripOffset + (long)stripByteCount < stream.Position)
            {
                TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip.");
            }
        }
Пример #7
0
        /// <summary>
        /// An EOL is expected before the first data.
        /// </summary>
        protected virtual void ReadEolBeforeFirstData()
        {
            if (this.isFirstScanLine)
            {
                this.Value = this.ReadValue(this.eolPadding ? 16 : 12);

                if (!this.IsEndOfScanLine)
                {
                    TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: expected start of data marker not found");
                }

                this.Reset();
            }
        }
Пример #8
0
        /// <summary>
        /// Decompresses image data into the supplied buffer.
        /// </summary>
        /// <param name="stream">The <see cref="Stream" /> to read image data from.</param>
        /// <param name="stripOffset">The strip offset of stream.</param>
        /// <param name="stripByteCount">The number of bytes to read from the input stream.</param>
        /// <param name="stripHeight">The height of the strip.</param>
        /// <param name="buffer">The output buffer for uncompressed data.</param>
        public void Decompress(BufferedReadStream stream, uint stripOffset, uint stripByteCount, int stripHeight, Span <byte> buffer)
        {
            if (stripByteCount > int.MaxValue)
            {
                TiffThrowHelper.ThrowImageFormatException("The StripByteCount value is too big.");
            }

            stream.Seek(stripOffset, SeekOrigin.Begin);
            this.Decompress(stream, (int)stripByteCount, stripHeight, buffer);

            if (stripOffset + stripByteCount < stream.Position)
            {
                TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip.");
            }
        }
Пример #9
0
        /// <inheritdoc/>
        protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span <byte> buffer)
        {
            using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator);

            buffer.Clear();
            uint bitsWritten   = 0;
            uint pixelsWritten = 0;

            while (bitReader.HasMoreData)
            {
                bitReader.ReadNextRun();

                if (bitReader.RunLength > 0)
                {
                    if (bitReader.IsWhiteRun)
                    {
                        BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue);
                    }
                    else
                    {
                        BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue);
                    }

                    bitsWritten   += bitReader.RunLength;
                    pixelsWritten += bitReader.RunLength;
                }

                if (pixelsWritten == this.Width)
                {
                    bitReader.StartNewRow();
                    pixelsWritten = 0;

                    // Write padding bits, if necessary.
                    uint pad = 8 - (bitsWritten % 8);
                    if (pad != 8)
                    {
                        BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0);
                        bitsWritten += pad;
                    }
                }

                if (pixelsWritten > this.Width)
                {
                    TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width");
                }
            }
        }
Пример #10
0
        public YCbCrConverter(Rational[] referenceBlackAndWhite, Rational[] coefficients)
        {
            referenceBlackAndWhite ??= DefaultReferenceBlackWhite;
            coefficients ??= DefaultLuma;

            if (referenceBlackAndWhite.Length != 6)
            {
                TiffThrowHelper.ThrowImageFormatException("reference black and white array should have 6 entry's");
            }

            if (coefficients.Length != 3)
            {
                TiffThrowHelper.ThrowImageFormatException("luma coefficients array should have 6 entry's");
            }

            this.yExpander  = new CodingRangeExpander(referenceBlackAndWhite[0], referenceBlackAndWhite[1], 255);
            this.cbExpander = new CodingRangeExpander(referenceBlackAndWhite[2], referenceBlackAndWhite[3], 127);
            this.crExpander = new CodingRangeExpander(referenceBlackAndWhite[4], referenceBlackAndWhite[5], 127);
            this.converter  = new YCbCrToRgbConverter(coefficients[0], coefficients[1], coefficients[2]);
        }
Пример #11
0
        private static void Decode2DScanline(T6BitReader bitReader, bool whiteIsZero, CcittReferenceScanline referenceScanline, Span <byte> scanline)
        {
            int width = scanline.Length;

            bitReader.StartNewRow();

            // 2D Encoding variables.
            int  a0       = -1;
            byte fillByte = whiteIsZero ? (byte)0 : (byte)255;

            // Process every code word in this scanline.
            int unpacked = 0;

            while (true)
            {
                // Read next code word and advance pass it.
                bool isEol = bitReader.ReadNextCodeWord();

                // Special case handling for EOL.
                if (isEol)
                {
                    // If a TIFF reader encounters EOFB before the expected number of lines has been extracted,
                    // it is appropriate to assume that the missing rows consist entirely of white pixels.
                    scanline.Fill(whiteIsZero ? (byte)0 : (byte)255);
                    break;
                }

                // Update 2D Encoding variables.
                int b1 = referenceScanline.FindB1(a0, fillByte);

                // Switch on the code word.
                int a1;
                switch (bitReader.Code.Type)
                {
                case CcittTwoDimensionalCodeType.None:
                    TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, could not read a valid code word.");
                    break;

                case CcittTwoDimensionalCodeType.Pass:
                    int b2 = referenceScanline.FindB2(b1);
                    scanline.Slice(unpacked, b2 - unpacked).Fill(fillByte);
                    unpacked = b2;
                    a0       = b2;
                    break;

                case CcittTwoDimensionalCodeType.Horizontal:
                    // Decode M(a0a1)
                    bitReader.ReadNextRun();
                    int runLength = (int)bitReader.RunLength;
                    if (runLength > (uint)(scanline.Length - unpacked))
                    {
                        TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error");
                    }

                    scanline.Slice(unpacked, runLength).Fill(fillByte);
                    unpacked += runLength;
                    fillByte  = (byte)~fillByte;

                    // Decode M(a1a2)
                    bitReader.ReadNextRun();
                    runLength = (int)bitReader.RunLength;
                    if (runLength > (uint)(scanline.Length - unpacked))
                    {
                        TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error");
                    }

                    scanline.Slice(unpacked, runLength).Fill(fillByte);
                    unpacked += runLength;
                    fillByte  = (byte)~fillByte;

                    // Prepare next a0
                    a0 = unpacked;
                    break;

                case CcittTwoDimensionalCodeType.Vertical0:
                    a1 = b1;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalR1:
                    a1 = b1 + 1;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalR2:
                    a1 = b1 + 2;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalR3:
                    a1 = b1 + 3;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalL1:
                    a1 = b1 - 1;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalL2:
                    a1 = b1 - 2;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                case CcittTwoDimensionalCodeType.VerticalL3:
                    a1 = b1 - 3;
                    scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte);
                    unpacked = a1;
                    a0       = a1;
                    fillByte = (byte)~fillByte;
                    bitReader.SwapColor();
                    break;

                default:
                    throw new NotSupportedException("ccitt extensions are not supported.");
                }

                // This line is fully unpacked. Should exit and process next line.
                if (unpacked == width)
                {
                    break;
                }

                if (unpacked > width)
                {
                    TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, unpacked data > width");
                }
            }
        }
Пример #12
0
        /// <summary>
        /// Read the next run of pixels.
        /// </summary>
        public void ReadNextRun()
        {
            if (this.terminationCodeFound)
            {
                this.IsWhiteRun           = !this.IsWhiteRun;
                this.terminationCodeFound = false;
            }

            // Initialize for next run.
            this.Reset();

            // We expect an EOL before the first data.
            this.ReadEolBeforeFirstData();

            // A code word must have at least 2 bits.
            this.Value = this.ReadValue(MinCodeLength);

            do
            {
                if (this.CurValueBitsRead > this.maxCodeLength)
                {
                    TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read");
                }

                bool isMakeupCode = this.IsMakeupCode();
                if (isMakeupCode)
                {
                    if (this.IsWhiteRun)
                    {
                        this.RunLength += this.WhiteMakeupCodeRunLength();
                    }
                    else
                    {
                        this.RunLength += this.BlackMakeupCodeRunLength();
                    }

                    this.isStartOfRow = false;
                    this.Reset(resetRunLength: false);
                    continue;
                }

                bool isTerminatingCode = this.IsTerminatingCode();
                if (isTerminatingCode)
                {
                    // Each line starts with a white run. If the image starts with black, a white run with length zero is written.
                    if (this.isStartOfRow && this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0)
                    {
                        this.Reset();
                        this.isStartOfRow         = false;
                        this.terminationCodeFound = true;
                        this.RunLength            = 0;
                        break;
                    }

                    if (this.IsWhiteRun)
                    {
                        this.RunLength += this.WhiteTerminatingCodeRunLength();
                    }
                    else
                    {
                        this.RunLength += this.BlackTerminatingCodeRunLength();
                    }

                    this.terminationCodeFound = true;
                    this.isStartOfRow         = false;
                    break;
                }

                uint currBit = this.ReadValue(1);
                this.Value = (this.Value << 1) | currBit;

                if (this.IsEndOfScanLine)
                {
                    this.StartNewRow();
                }
            }while (!this.IsEndOfScanLine);

            this.isFirstScanLine = false;
        }