/// <summary>
        /// Decodes the image data from a specified IFD.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="tags">The IFD tags.</param>
        /// <param name="cancellationToken">The token to monitor cancellation.</param>
        /// <returns> The tiff frame. </returns>
        private ImageFrame <TPixel> DecodeFrame <TPixel>(ExifProfile tags, CancellationToken cancellationToken)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            var imageFrameMetaData = new ImageFrameMetadata();

            if (!this.ignoreMetadata)
            {
                imageFrameMetaData.ExifProfile = tags;
            }

            TiffFrameMetadata tiffFrameMetaData = imageFrameMetaData.GetTiffMetadata();

            TiffFrameMetadata.Parse(tiffFrameMetaData, tags);

            this.VerifyAndParse(tags, tiffFrameMetaData);

            int width  = GetImageWidth(tags);
            int height = GetImageHeight(tags);
            var frame  = new ImageFrame <TPixel>(this.Configuration, width, height, imageFrameMetaData);

            int rowsPerStrip = tags.GetValue(ExifTag.RowsPerStrip) != null ? (int)tags.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity;

            var stripOffsetsArray    = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue();
            var stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue();

            IMemoryOwner <ulong> stripOffsetsMemory    = this.ConvertNumbers(stripOffsetsArray, out Span <ulong> stripOffsets);
            IMemoryOwner <ulong> stripByteCountsMemory = this.ConvertNumbers(stripByteCountsArray, out Span <ulong> stripByteCounts);

            if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar)
            {
                this.DecodeStripsPlanar(
                    frame,
                    rowsPerStrip,
                    stripOffsets,
                    stripByteCounts,
                    cancellationToken);
            }
            else
            {
                this.DecodeStripsChunky(
                    frame,
                    rowsPerStrip,
                    stripOffsets,
                    stripByteCounts,
                    cancellationToken);
            }

            stripOffsetsMemory?.Dispose();
            stripByteCountsMemory?.Dispose();
            return(frame);
        }
        private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
        {
            if (exifProfile.GetValueInternal(ExifTag.StripOffsets) is null)
            {
                TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!");
            }

            if (exifProfile.GetValueInternal(ExifTag.StripByteCounts) is null)
            {
                TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!");
            }

            if (frameMetadata.BitsPerPixel == null)
            {
                TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!");
            }
        }
        public void DamagedFiles <TPixel>(TestImageProvider <TPixel> provider)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Assert.Throws <ImageDifferenceIsOverThresholdException>(() => TestTiffDecoder(provider));

            using Image <TPixel> image = provider.GetImage(TiffDecoder);
            ExifProfile exif           = image.Frames.RootFrame.Metadata.ExifProfile;

            // PhotometricInterpretation is required tag: https://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
            Assert.Null(exif.GetValueInternal(ExifTag.PhotometricInterpretation));
        }
Exemple #4
0
        public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
        {
            // Arrange
            var image = new Image <Rgba32>(1, 1);

            image.Metadata.ExifProfile = CreateExifProfile();

            // Act
            Image <Rgba32> reloadedImage = WriteAndRead(image, imageFormat);

            // Assert
            ExifProfile actual = reloadedImage.Metadata.ExifProfile;

            Assert.NotNull(actual);
            foreach (KeyValuePair <ExifTag, object> expectedProfileValue in TestProfileValues)
            {
                IExifValue actualProfileValue = actual.GetValueInternal(expectedProfileValue.Key);
                Assert.NotNull(actualProfileValue);
                Assert.Equal(expectedProfileValue.Value, actualProfileValue.GetValue());
            }
        }
Exemple #5
0
        public void ProfileToByteArray()
        {
            // Arrange
            byte[]      exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
            ExifProfile expectedProfile          = CreateExifProfile();
            var         expectedProfileTags      = expectedProfile.Values.Select(x => x.Tag).ToList();

            // Act
            byte[] actualBytes   = expectedProfile.ToByteArray();
            var    actualProfile = new ExifProfile(actualBytes);

            // Assert
            Assert.NotNull(actualBytes);
            Assert.NotEmpty(actualBytes);
            Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
            foreach (ExifTag expectedProfileTag in expectedProfileTags)
            {
                IExifValue actualProfileValue   = actualProfile.GetValueInternal(expectedProfileTag);
                IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
                Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
            }
        }
Exemple #6
0
        public void ReadWriteLargeProfileJpg()
        {
            ExifTag <string>[] tags = new[] { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription };
            foreach (ExifTag <string> tag in tags)
            {
                // Arrange
                var junk = new StringBuilder();
                for (int i = 0; i < 65600; i++)
                {
                    junk.Append("a");
                }

                var         image               = new Image <Rgba32>(100, 100);
                ExifProfile expectedProfile     = CreateExifProfile();
                var         expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
                expectedProfile.SetValue(tag, junk.ToString());
                image.Metadata.ExifProfile = expectedProfile;

                // Act
                Image <Rgba32> reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg);

                // Assert
                ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile;
                Assert.NotNull(actualProfile);

                foreach (ExifTag expectedProfileTag in expectedProfileTags)
                {
                    IExifValue actualProfileValue   = actualProfile.GetValueInternal(expectedProfileTag);
                    IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
                    Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
                }

                IExifValue <string> expected = expectedProfile.GetValue(tag);
                IExifValue <string> actual   = actualProfile.GetValue(tag);
                Assert.Equal(expected, actual);
            }
        }
        /// <summary>
        /// Determines the TIFF compression and color types, and reads any associated parameters.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="exifProfile">The exif profile of the frame to decode.</param>
        /// <param name="frameMetadata">The IFD entries container to read the image format information for current frame.</param>
        public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
        {
            if (exifProfile.GetValueInternal(ExifTag.TileOffsets) is not null || exifProfile.GetValueInternal(ExifTag.TileByteCounts) is not null)
            {
                TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
            }

            if (exifProfile.GetValueInternal(ExifTag.ExtraSamples) is not null)
            {
                TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported.");
            }

            TiffFillOrder fillOrder = (TiffFillOrder?)exifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst;

            if (fillOrder == TiffFillOrder.LeastSignificantBitFirst && frameMetadata.BitsPerPixel != TiffBitsPerPixel.Bit1)
            {
                TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is only supported in combination with 1bit per pixel bicolor tiff's.");
            }

            if (frameMetadata.Predictor == TiffPredictor.FloatingPoint)
            {
                TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported.");
            }

            TiffSampleFormat[] sampleFormats = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
            TiffSampleFormat?  sampleFormat  = null;

            if (sampleFormats != null)
            {
                sampleFormat = sampleFormats[0];
                foreach (TiffSampleFormat format in sampleFormats)
                {
                    if (format != TiffSampleFormat.UnsignedInteger && format != TiffSampleFormat.Float)
                    {
                        TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger and Float SampleFormat.");
                    }
                }
            }

            ushort[] ycbcrSubSampling = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value;
            if (ycbcrSubSampling != null && ycbcrSubSampling.Length != 2)
            {
                TiffThrowHelper.ThrowImageFormatException("Invalid YCbCrSubsampling, expected 2 values.");
            }

            if (ycbcrSubSampling != null && ycbcrSubSampling[1] > ycbcrSubSampling[0])
            {
                TiffThrowHelper.ThrowImageFormatException("ChromaSubsampleVert shall always be less than or equal to ChromaSubsampleHoriz.");
            }

            if (exifProfile.GetValue(ExifTag.StripRowCounts)?.Value != null)
            {
                TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported.");
            }

            VerifyRequiredFieldsArePresent(exifProfile, frameMetadata);

            options.PlanarConfiguration       = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
            options.Predictor                 = frameMetadata.Predictor ?? TiffPredictor.None;
            options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb;
            options.SampleFormat              = sampleFormat ?? TiffSampleFormat.UnsignedInteger;
            options.BitsPerPixel              = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
            options.BitsPerSample             = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0);
            options.ReferenceBlackAndWhite    = exifProfile.GetValue(ExifTag.ReferenceBlackWhite)?.Value;
            options.YcbcrCoefficients         = exifProfile.GetValue(ExifTag.YCbCrCoefficients)?.Value;
            options.YcbcrSubSampling          = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value;
            options.FillOrder                 = fillOrder;
            options.JpegTables                = exifProfile.GetValue(ExifTag.JPEGTables)?.Value;

            options.ParseColorType(exifProfile);
            options.ParseCompression(frameMetadata.Compression, exifProfile);
        }