public static (Image image, ImageMetadata metadata) ExtractCurrentFrame(Tiff tiff) { // read image properties int width = GetTagInt32(TiffTag.IMAGEWIDTH, null); int height = GetTagInt32(TiffTag.IMAGELENGTH, null); int samplesPerPixel = GetTagInt32(TiffTag.SAMPLESPERPIXEL, 1); int bitsPerSample = GetTagInt32(TiffTag.BITSPERSAMPLE, 1); int xres = GetTagInt32(TiffTag.XRESOLUTION, 200); int yres = GetTagInt32(TiffTag.YRESOLUTION, 200); int photometric = GetTagInt32(TiffTag.PHOTOMETRIC, (int)TIFFPhotometricInterpretation.WhiteIsZero); Image image = null; if (photometric == (int)TIFFPhotometricInterpretation.YCbCr) { // special case for YCbCr photometric - read image as rgba image = new Image(width, height, 32, xres, yres); int imageSize = height * width; int[] buffer = new int[imageSize]; // Read the image into the memory buffer if (tiff.ReadRGBAImageOriented(width, height, buffer, Orientation.TOPLEFT)) { unsafe { fixed(int *src = buffer) { fixed(ulong *dst = image.Bits) { Arrays.CopyStrides(image.Height, new IntPtr(src), width * sizeof(int), new IntPtr(dst), image.Stride8); } } } } } else { image = new Image(width, height, samplesPerPixel * bitsPerSample, xres, yres); // read bytes int scanlineSize = tiff.ScanlineSize(); byte[] buffer = new byte[Math.Max(scanlineSize, image.Stride8)]; for (int i = 0, offdst = 0; i < height; i++, offdst += image.Stride) { if (!tiff.ReadScanline(buffer, 0, i, 0)) { throw new InvalidOperationException(Properties.Resources.E_CannotDecodeImage); } if (image.BitsPerPixel < 8) { unsafe { fixed(ulong *dst = &image.Bits[offdst]) { fixed(byte *src = buffer) { Vectors.SwapBits(image.Stride, (ulong *)src, image.BitsPerPixel, dst); } } } } else { Buffer.BlockCopy(buffer, 0, image.Bits, offdst, image.Stride8); } } } // read all set tags ImageMetadata metadata = new ImageMetadata(); foreach (TiffFieldInfo fip in tiff.EnumFieldInfo(TiffType.ANY)) { object value = GetTag(fip); if (value != null) { metadata.SetPropertyItem((int)fip.Tag, value); } } return(image, metadata); int GetTagInt32(TiffTag tag, int?defaultValue) { FieldValue[] values = tiff.GetField(tag); if (values != null && values.Length > 0) { return(values[0].ToInt()); } if (!defaultValue.HasValue) { throw new InvalidOperationException(); } return(defaultValue.Value); } object GetTag(TiffFieldInfo fip) { object convertedValue = null; if (CanLoadTag((int)fip.Tag)) { FieldValue[] value = tiff.GetField(fip.Tag); if (value != null && value.Length > 0) { try { if (value.Length == 1) { convertedValue = ConvertFieldValue(fip.Type, value[0]); } else if (value.Length == 2 && fip.PassCount) { convertedValue = ConvertFieldValue(fip.Type, value[1]); } else { convertedValue = CastFieldValues(fip.Type, value.Select(x => ConvertFieldValue(fip.Type, x)).ToArray()); } } catch { } } } return(convertedValue); bool CanLoadTag(int id) { if (id == (int)TIFFField.RowsPerStrip || id == (int)TIFFField.StripByteCounts || id == (int)TIFFField.StripOffsets || id == (int)TIFFField.TileByteCounts || id == (int)TIFFField.TileOffsets) { return(false); } return(true); } object ConvertFieldValue(TiffType type, FieldValue value) { switch (type) { case TiffType.BYTE: return(value.Value is Array ? (object)value.ToByteArray() : value.ToByte()); case TiffType.SBYTE: return(value.Value is Array ? (object)value.ToSByteArray() : value.ToSByte()); case TiffType.SHORT: return(value.Value is Array ? (object)value.ToUShortArray() : value.ToUShort()); case TiffType.SSHORT: return(value.Value is Array ? (object)value.ToShortArray() : value.ToShort()); case TiffType.LONG: case TiffType.IFD: return(value.Value is Array ? (object)value.ToUIntArray() : value.ToUInt()); case TiffType.SLONG: return(value.Value is Array ? (object)value.ToIntArray() : value.ToInt()); case TiffType.LONG8: case TiffType.IFD8: case TiffType.SLONG8: return(value.Value is Array ? (object)value.TolongArray() : value.ToLong()); case TiffType.RATIONAL: case TiffType.SRATIONAL: case TiffType.FLOAT: return(value.Value is Array ? (object)value.ToFloatArray() : value.ToFloat()); case TiffType.DOUBLE: return(value.Value is Array ? (object)value.ToDoubleArray() : value.ToDouble()); case TiffType.ASCII: if (value.Value is byte[]) { return(Encoding.GetEncoding("Latin1").GetString(value.Value as byte[]).TrimEnd(new[] { '\0' })); } else { return(Convert.ToString(value, CultureInfo.InvariantCulture)); } default: return(value.ToString()); } } object CastFieldValues(TiffType type, object[] values) { switch (type) { case TiffType.BYTE: return(Cast <byte>()); case TiffType.SBYTE: return(Cast <sbyte>()); case TiffType.SHORT: return(Cast <ushort>()); case TiffType.SSHORT: return(Cast <short>()); case TiffType.LONG: case TiffType.IFD: return(Cast <uint>()); case TiffType.SLONG: return(Cast <int>()); case TiffType.LONG8: case TiffType.IFD8: return(Cast <ulong>()); case TiffType.SLONG8: return(Cast <long>()); case TiffType.RATIONAL: case TiffType.SRATIONAL: case TiffType.FLOAT: return(Cast <float>()); case TiffType.DOUBLE: return(Cast <double>()); case TiffType.ASCII: return(Cast <string>()); default: return(values); } object Cast <T>() { return(values.All(x => x is T) ? (object)values.Cast <T>().ToArray() : values); } } } }