/// <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> { ImageFrameMetadata imageFrameMetaData = this.ignoreMetadata ? new ImageFrameMetadata() : new ImageFrameMetadata { ExifProfile = tags, XmpProfile = tags.GetValue(ExifTag.XMP)?.Value }; 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; Number[] stripOffsets = tags.GetValue(ExifTag.StripOffsets)?.Value; Number[] stripByteCounts = tags.GetValue(ExifTag.StripByteCounts)?.Value; if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar) { this.DecodeStripsPlanar(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); } else { this.DecodeStripsChunky(frame, rowsPerStrip, stripOffsets, stripByteCounts, cancellationToken); } return(frame); }
/// <summary> /// Returns a new <see cref="TiffFrameMetadata"/> instance parsed from the given Exif profile. /// </summary> /// <param name="profile">The Exif profile containing tiff frame directory tags to parse. /// If null, a new instance is created and parsed instead.</param> /// <returns>The <see cref="TiffFrameMetadata"/>.</returns> internal static TiffFrameMetadata Parse(ExifProfile profile) { var meta = new TiffFrameMetadata(); Parse(meta, profile); return(meta); }
/// <summary> /// Initializes a new instance of the <see cref="TiffFrameMetadata"/> class. /// </summary> /// <param name="other">The other tiff frame metadata.</param> private TiffFrameMetadata(TiffFrameMetadata other) { this.BitsPerPixel = other.BitsPerPixel; this.Compression = other.Compression; this.PhotometricInterpretation = other.PhotometricInterpretation; this.Predictor = other.Predictor; }
/// <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); }
/// <inheritdoc/> public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.inputStream = stream; var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator); IEnumerable <ExifProfile> directories = reader.Read(); ExifProfile rootFrameExifProfile = directories.First(); var rootMetadata = TiffFrameMetadata.Parse(rootFrameExifProfile); ImageMetadata metadata = TiffDecoderMetadataCreator.Create(reader.ByteOrder, reader.IsBigTiff, rootFrameExifProfile); int width = GetImageWidth(rootFrameExifProfile); int height = GetImageHeight(rootFrameExifProfile); return(new ImageInfo(new PixelTypeInfo((int)rootMetadata.BitsPerPixel), width, height, metadata)); }
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!"); } }
/// <summary> /// Parses the given Exif profile to populate the properties of the tiff frame meta data. /// </summary> /// <param name="meta">The tiff frame meta data.</param> /// <param name="profile">The Exif profile containing tiff frame directory tags.</param> internal static void Parse(TiffFrameMetadata meta, ExifProfile profile) { if (profile != null) { if (TiffBitsPerSample.TryParse(profile.GetValue(ExifTag.BitsPerSample)?.Value, out TiffBitsPerSample bitsPerSample)) { meta.BitsPerSample = bitsPerSample; } meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.PhotometricInterpretation); profile.RemoveValue(ExifTag.Predictor); } }
/// <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); }