private static async Task CopyStrippedImageAsync(TiffFileContentReader contentReader, TiffTagReader tagReader, TiffImageFileDirectoryWriter dest, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); TiffValueCollection <ulong> offsets = await tagReader.ReadStripOffsetsAsync(cancellationToken); TiffValueCollection <ulong> byteCounts = await tagReader.ReadStripByteCountsAsync(cancellationToken); if (offsets.Count != byteCounts.Count) { throw new InvalidDataException("Failed to copy stripped image data. StripOffsets and StripByteCounts don't have the same amount of elements."); } uint[] offsets32 = new uint[offsets.Count]; uint[] byteCounts32 = new uint[offsets.Count]; byte[]? buffer = null; try { for (int i = 0; i < offsets.Count; i++) { int offset = checked ((int)offsets[i]); int byteCount = checked ((int)byteCounts[i]); byteCounts32[i] = checked ((uint)byteCount); if (buffer is null || byteCount > buffer.Length) { if (!(buffer is null)) { ArrayPool <byte> .Shared.Return(buffer); } buffer = ArrayPool <byte> .Shared.Rent(byteCount); } if (await contentReader.ReadAsync(offset, new ArraySegment <byte>(buffer, 0, byteCount), cancellationToken) != byteCount) { throw new InvalidDataException("Invalid ByteCount field."); } TiffStreamOffset region = await dest.FileWriter.WriteAlignedBytesAsync(buffer, 0, byteCount); offsets32[i] = checked ((uint)region.Offset); } } finally { if (!(buffer is null)) { ArrayPool <byte> .Shared.Return(buffer); } } await dest.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(offsets32)); await dest.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(byteCounts32)); }
public async Task TestDoubleCompatibility(int length, bool bigTiff) { double[] refData = new double[length]; var rand = new Random(); for (int i = 0; i < refData.Length; i++) { refData[i] = rand.NextDouble(); } using Stream stream = await GenerateTiffAsync(bigTiff, async ifd => { await ifd.WriteTagAsync((TiffTag) 0x1234, TiffValueCollection.UnsafeWrap(refData)); }); await using (TiffFileReader reader = await TiffFileReader.OpenAsync(stream, leaveOpen: true)) { TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync(); TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync(); TiffImageFileDirectoryEntry entry = ifd.FindEntry((TiffTag)0x1234); Assert.Equal((TiffTag)0x1234, entry.Tag); // Byte await TestInvalidConversionAsync <byte>(fieldReader, entry, nameof(fieldReader.ReadByteFieldAsync), refData.Length); // SSbyte await TestInvalidConversionAsync <sbyte>(fieldReader, entry, nameof(fieldReader.ReadSByteFieldAsync), refData.Length); // Short await TestInvalidConversionAsync <ushort>(fieldReader, entry, nameof(fieldReader.ReadShortFieldAsync), refData.Length); // SShort await TestInvalidConversionAsync <short>(fieldReader, entry, nameof(fieldReader.ReadSShortFieldAsync), refData.Length); // Long await TestInvalidConversionAsync <uint>(fieldReader, entry, nameof(fieldReader.ReadLongFieldAsync), refData.Length); // SLong await TestInvalidConversionAsync <int>(fieldReader, entry, nameof(fieldReader.ReadSLongFieldAsync), refData.Length); // Float await TestInvalidConversionAsync <float>(fieldReader, entry, nameof(fieldReader.ReadFloatFieldAsync), refData.Length); // Double await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadDoubleFieldAsync), refData); // Rational await TestInvalidConversionAsync <TiffRational>(fieldReader, entry, nameof(fieldReader.ReadRationalFieldAsync), refData.Length); // SRational await TestInvalidConversionAsync <TiffSRational>(fieldReader, entry, nameof(fieldReader.ReadSRationalFieldAsync), refData.Length); } }
public async Task TestSLongCompatibility(int length, bool bigTiff) { int[] refData = new int[length]; new Random(42).NextBytes(MemoryMarshal.AsBytes(refData.AsSpan())); using Stream stream = await GenerateTiffAsync(bigTiff, async ifd => { await ifd.WriteTagAsync((TiffTag) 0x1234, TiffValueCollection.UnsafeWrap(refData)); }); await using (TiffFileReader reader = await TiffFileReader.OpenAsync(stream, leaveOpen: true)) { TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync(); TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync(); TiffImageFileDirectoryEntry entry = ifd.FindEntry((TiffTag)0x1234); Assert.Equal((TiffTag)0x1234, entry.Tag); // Byte await TestInvalidConversionAsync <byte>(fieldReader, entry, nameof(fieldReader.ReadByteFieldAsync), refData.Length); // SSbyte await TestInvalidConversionAsync <sbyte>(fieldReader, entry, nameof(fieldReader.ReadSByteFieldAsync), refData.Length); // Short await TestInvalidConversionAsync <ushort>(fieldReader, entry, nameof(fieldReader.ReadShortFieldAsync), refData.Length); // SShort await TestInvalidConversionAsync <short>(fieldReader, entry, nameof(fieldReader.ReadSShortFieldAsync), refData.Length); // Long await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadLongFieldAsync), Array.ConvertAll(refData, v => (uint)v)); // SLong await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadSLongFieldAsync), refData); // Float await TestInvalidConversionAsync <float>(fieldReader, entry, nameof(fieldReader.ReadFloatFieldAsync), refData.Length); // Double await TestInvalidConversionAsync <double>(fieldReader, entry, nameof(fieldReader.ReadDoubleFieldAsync), refData.Length); // Rational await TestInvalidConversionAsync <TiffRational>(fieldReader, entry, nameof(fieldReader.ReadRationalFieldAsync), refData.Length); // SRational await TestValidConversionAsync(fieldReader, entry, nameof(fieldReader.ReadSRationalFieldAsync), Array.ConvertAll(refData, v => new TiffSRational(v, 1))); } }
public async Task TestChunkyData(int width, int height, ushort horizontalSubsampling, ushort verticalSubsampling, byte[] subsampled, byte[] original) { var middleware = new TiffApplyChromaSubsamplingMiddleware <byte>(horizontalSubsampling, verticalSubsampling); byte[] buffer = new byte[original.Length]; original.AsSpan().CopyTo(buffer); var context = new TestEncoderContext <byte> { MemoryPool = MemoryPool <byte> .Shared, BitsPerSample = TiffValueCollection.UnsafeWrap(new ushort[] { 8, 8, 8 }), UncompressedData = buffer, ImageSize = new TiffSize(width, height) }; await middleware.InvokeAsync(context, new ValidationPipelineNode <byte>(subsampled)); }
public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared; TiffSize imageSize = context.ImageSize; int arraySize = 3 * imageSize.Width * imageSize.Height; using (IMemoryOwner <byte> pixelData = memoryPool.Rent(arraySize)) { Memory <byte> pixelDataMemory = pixelData.Memory; using (var writer = new TiffMemoryPixelBufferWriter <TiffRgb24>(memoryPool, pixelDataMemory, imageSize.Width, imageSize.Height)) using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter())) { await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false); } TiffYCbCrConverter8.CreateDefault().ConvertFromRgb24(MemoryMarshal.Cast <byte, TiffRgb24>(pixelDataMemory.Span), pixelDataMemory.Span, imageSize.Width * imageSize.Height); context.PhotometricInterpretation = TiffPhotometricInterpretation.YCbCr; context.BitsPerSample = TiffValueCollection.UnsafeWrap(s_bitsPerSample); context.UncompressedData = pixelDataMemory.Slice(0, arraySize); await next.RunAsync(context).ConfigureAwait(false); context.UncompressedData = default; } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { using (await context.LockAsync().ConfigureAwait(false)) { await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)context.PhotometricInterpretation)).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, context.BitsPerSample).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, TiffValueCollection.Single <ushort>(3)).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.YCbCrCoefficients, TiffYCbCrConverter8.DefaultLuma).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ReferenceBlackWhite, TiffYCbCrConverter8.DefaultReferenceBlackWhite).ConfigureAwait(false); } } }
public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared; TiffSize imageSize = context.ImageSize; int arraySize = 4 * imageSize.Width * imageSize.Height; using (IMemoryOwner <byte> pixelData = memoryPool.Rent(arraySize)) { using (var writer = new TiffMemoryPixelBufferWriter <TiffRgba32>(memoryPool, pixelData.Memory, imageSize.Width, imageSize.Height)) using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter())) { await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false); } context.PhotometricInterpretation = TiffPhotometricInterpretation.RGB; context.BitsPerSample = TiffValueCollection.UnsafeWrap(s_bitsPerSample); context.UncompressedData = pixelData.Memory.Slice(0, arraySize); await next.RunAsync(context).ConfigureAwait(false); context.UncompressedData = default; } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { using (await context.LockAsync().ConfigureAwait(false)) { await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)context.PhotometricInterpretation)).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, context.BitsPerSample).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, TiffValueCollection.Single <ushort>(4)).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ExtraSamples, TiffValueCollection.Single((ushort)TiffExtraSample.UnassociatedAlphaData)).ConfigureAwait(false); } } }
/// <summary> /// Crops the input image into tiles and runs the next middleware for each tile. /// </summary> /// <param name="context">The encoder context.</param> /// <param name="next">The next middleware.</param> /// <returns>A <see cref="Task"/> that completes when the image has been encoded.</returns> public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } var state = context.GetService(typeof(TiffParallelEncodingState)) as TiffParallelEncodingState; TiffCroppedImageEncoderContext <TPixel>?wrappedContext = null; int width = context.ImageSize.Width, height = context.ImageSize.Height; int tileWidth = _tileSize.Width, tileHeight = _tileSize.Height; int tileAcross = (width + tileWidth - 1) / tileWidth; int tileDown = (height + tileHeight - 1) / tileHeight; int tileCount = tileAcross * tileDown; ulong[] tileOffsets = new ulong[tileCount]; ulong[] tileByteCounts = new ulong[tileCount]; int index = 0; state?.LockTaskCompletion(); for (int row = 0; row < tileDown; row++) { int offsetY = row * tileHeight; int imageHeight = Math.Min(height - offsetY, tileHeight); for (int col = 0; col < tileAcross; col++) { int offsetX = col * tileWidth; int imageWidth = Math.Min(width - offsetX, tileWidth); wrappedContext ??= new TiffCroppedImageEncoderContext <TPixel>(context); wrappedContext.ExposeIfdWriter = row == 0 && col == 0; wrappedContext.OutputRegion = default; wrappedContext.Crop(new TiffPoint(offsetX, offsetY), new TiffSize(imageWidth, imageHeight)); if (state is null) { await next.RunAsync(wrappedContext).ConfigureAwait(false); tileOffsets[index] = (ulong)(long)wrappedContext.OutputRegion.Offset; tileByteCounts[index] = (ulong)wrappedContext.OutputRegion.Length; } else { TiffCroppedImageEncoderContext <TPixel>?wContext = wrappedContext; wrappedContext = null; int currentIndex = index; await state.DispatchAsync(async() => { await next.RunAsync(wContext).ConfigureAwait(false); tileOffsets[currentIndex] = (ulong)(long)wContext.OutputRegion.Offset; tileByteCounts[currentIndex] = (ulong)wContext.OutputRegion.Length; }, context.CancellationToken).ConfigureAwait(false); } index++; } } // Wait until all tiles are written. if (!(state is null)) { state.ReleaseTaskCompletion(); await state.Complete.Task.ConfigureAwait(false); } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { using (await context.LockAsync().ConfigureAwait(false)) { CancellationToken cancellationToken = context.CancellationToken; await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, TiffValueCollection.Single((uint)width), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ImageLength, TiffValueCollection.Single((uint)height), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileWidth, TiffValueCollection.Single((ushort)tileWidth), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileLength, TiffValueCollection.Single((ushort)tileHeight), cancellationToken).ConfigureAwait(false); if (context.FileWriter?.UseBigTiff ?? false) { await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, TiffValueCollection.UnsafeWrap(tileOffsets), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, TiffValueCollection.UnsafeWrap(tileByteCounts), cancellationToken).ConfigureAwait(false); } else { uint[] tileOffsets32 = new uint[tileCount]; uint[] tileByteCounts32 = new uint[tileCount]; for (int i = 0; i < tileCount; i++) { tileOffsets32[i] = (uint)tileOffsets[i]; tileByteCounts32[i] = (uint)tileByteCounts[i]; } await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, TiffValueCollection.UnsafeWrap(tileOffsets32), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, TiffValueCollection.UnsafeWrap(tileByteCounts32), cancellationToken).ConfigureAwait(false); } } } }
public static async Task <int> Wrap(FileInfo source, FileInfo output, CancellationToken cancellationToken) { if (source is null || !source.Exists) { Console.WriteLine(source is null ? "Input JPEG image is not specified." : "File not found: " + source.FullName); return(1); } if (output is null) { Console.WriteLine("Output TIFF file is not specified."); return(1); } byte[] jpegFile = await File.ReadAllBytesAsync(source.FullName, cancellationToken); var decoder = new JpegDecoder(); decoder.SetInput(jpegFile); decoder.Identify(loadQuantizationTables: false); ushort[] bitsPerSample = new ushort[decoder.NumberOfComponents]; ushort bits = (ushort)decoder.Precision; for (int i = 0; i < bitsPerSample.Length; i++) { bitsPerSample[i] = bits; } TiffPhotometricInterpretation photometricInterpretation = bitsPerSample.Length == 1 ? TiffPhotometricInterpretation.BlackIsZero : bitsPerSample.Length == 3 ? TiffPhotometricInterpretation.YCbCr : throw new InvalidDataException("Photometric interpretation not supported."); using (TiffFileWriter writer = await TiffFileWriter.OpenAsync(output.FullName, useBigTiff: false)) { TiffStreamOffset imageOffset = await writer.WriteAlignedBytesAsync(jpegFile); TiffStreamOffset ifdOffset; using (TiffImageFileDirectoryWriter ifdWriter = writer.CreateImageFileDirectory()) { await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, TiffValueCollection.Single((ushort)decoder.Width)); await ifdWriter.WriteTagAsync(TiffTag.ImageLength, TiffValueCollection.Single((ushort)decoder.Height)); await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, TiffValueCollection.UnsafeWrap(bitsPerSample)); await ifdWriter.WriteTagAsync(TiffTag.Compression, TiffValueCollection.Single((ushort)TiffCompression.Jpeg)); await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)photometricInterpretation)); await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, TiffValueCollection.Single((ushort)bitsPerSample.Length)); await ifdWriter.WriteTagAsync(TiffTag.PlanarConfiguration, TiffValueCollection.Single((ushort)TiffPlanarConfiguration.Chunky)); await ifdWriter.WriteTagAsync(TiffTag.RowsPerStrip, TiffValueCollection.Single((ushort)decoder.Height)); await ifdWriter.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.Single((uint)imageOffset.Offset)); await ifdWriter.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.Single((uint)jpegFile.Length)); if (photometricInterpretation == TiffPhotometricInterpretation.YCbCr) { int maxHorizontalSampling = decoder.GetMaximumHorizontalSampling(); int maxVerticalSampling = decoder.GetMaximumVerticalSampling(); int yHorizontalSubSampling = maxHorizontalSampling / decoder.GetHorizontalSampling(0); int yVerticalSubSampling = maxVerticalSampling / decoder.GetVerticalSampling(0); int cbHorizontalSubSampling = maxHorizontalSampling / decoder.GetHorizontalSampling(1) / yHorizontalSubSampling; int cbVerticalSubSampling = maxVerticalSampling / decoder.GetVerticalSampling(1) / yVerticalSubSampling; int crHorizontalSubSampling = maxHorizontalSampling / decoder.GetHorizontalSampling(2) / yHorizontalSubSampling; int crVerticalSubSampling = maxVerticalSampling / decoder.GetVerticalSampling(2) / yVerticalSubSampling; if (cbHorizontalSubSampling != crHorizontalSubSampling || cbVerticalSubSampling != crVerticalSubSampling) { throw new InvalidDataException("Unsupported JPEG image."); } await ifdWriter.WriteTagAsync(TiffTag.YCbCrSubSampling, TiffValueCollection.UnsafeWrap(new ushort[] { (ushort)cbHorizontalSubSampling, (ushort)cbVerticalSubSampling })); } // Write other properties here (eg, XResolution, YResolution) await ifdWriter.WriteTagAsync(TiffTag.XResolution, TiffValueCollection.Single(new TiffRational(96, 1))); await ifdWriter.WriteTagAsync(TiffTag.YResolution, TiffValueCollection.Single(new TiffRational(96, 1))); await ifdWriter.WriteTagAsync(TiffTag.ResolutionUnit, TiffValueCollection.Single((ushort)TiffResolutionUnit.Inch)); ifdOffset = await ifdWriter.FlushAsync(); } writer.SetFirstImageFileDirectoryOffset(ifdOffset); await writer.FlushAsync(); } return(0); }
/// <summary> /// Crops the input image into multiple strips and runs the next middleware for each strip. /// </summary> /// <param name="context">The encoder context.</param> /// <param name="next">The next middleware.</param> /// <returns>A <see cref="Task"/> that completes when the image has been encoded.</returns> public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } var state = context.GetService(typeof(TiffParallelEncodingState)) as TiffParallelEncodingState; TiffCroppedImageEncoderContext <TPixel>?wrappedContext = null; int width = context.ImageSize.Width, height = context.ImageSize.Height; int rowsPerStrip = _rowsPerStrip <= 0 ? height : _rowsPerStrip; int stripCount = (height + rowsPerStrip - 1) / rowsPerStrip; ulong[] stripOffsets = new ulong[stripCount]; ulong[] stripByteCounts = new ulong[stripCount]; state?.LockTaskCompletion(); for (int i = 0; i < stripCount; i++) { int offsetY = i * rowsPerStrip; int stripHeight = Math.Min(height - offsetY, rowsPerStrip); wrappedContext ??= new TiffCroppedImageEncoderContext <TPixel>(context); wrappedContext.ExposeIfdWriter = i == 0; wrappedContext.OutputRegion = default; wrappedContext.Crop(new TiffPoint(0, offsetY), new TiffSize(width, stripHeight)); if (state is null) { await next.RunAsync(wrappedContext).ConfigureAwait(false); stripOffsets[i] = (ulong)(long)wrappedContext.OutputRegion.Offset; stripByteCounts[i] = (ulong)wrappedContext.OutputRegion.Length; } else { TiffCroppedImageEncoderContext <TPixel>?wContext = wrappedContext; wrappedContext = null; int currentIndex = i; await state.DispatchAsync(async() => { await next.RunAsync(wContext).ConfigureAwait(false); stripOffsets[currentIndex] = (ulong)(long)wContext.OutputRegion.Offset; stripByteCounts[currentIndex] = (ulong)wContext.OutputRegion.Length; }, context.CancellationToken).ConfigureAwait(false); } } // Wait until all strips are written. if (!(state is null)) { state.ReleaseTaskCompletion(); await state.Complete.Task.ConfigureAwait(false); } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { CancellationToken cancellationToken = context.CancellationToken; await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, TiffValueCollection.Single((uint)width), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ImageLength, TiffValueCollection.Single((uint)height), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.RowsPerStrip, TiffValueCollection.Single((ushort)rowsPerStrip), cancellationToken).ConfigureAwait(false); if (context.FileWriter?.UseBigTiff ?? false) { await ifdWriter.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(stripOffsets), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(stripByteCounts), cancellationToken).ConfigureAwait(false); } else { uint[] stripOffsets32 = new uint[stripCount]; uint[] stripByteCounts32 = new uint[stripCount]; for (int i = 0; i < stripCount; i++) { stripOffsets32[i] = (uint)stripOffsets[i]; stripByteCounts32[i] = (uint)stripByteCounts[i]; } await ifdWriter.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(stripOffsets32), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(stripByteCounts32), cancellationToken).ConfigureAwait(false); } } }
public void TestMultiValue() { int[] values = new int[] { 42, 88, 100 }; var multiVal = TiffValueCollection.UnsafeWrap(values); Assert.Equal(3, multiVal.Count); Assert.False(multiVal.IsEmpty); Assert.Equal(42, multiVal[0]); Assert.Equal(88, multiVal[1]); Assert.Equal(100, multiVal[2]); Assert.Throws <IndexOutOfRangeException>(() => multiVal[-1]); Assert.Throws <IndexOutOfRangeException>(() => multiVal[3]); int count = 0; foreach (int v in multiVal) { switch (count) { case 0: Assert.Equal(42, v); break; case 1: Assert.Equal(88, v); break; case 2: Assert.Equal(100, v); break; } count++; } Assert.Equal(3, count); // TiffValueCollection contains the reference to the original array. values[1] = 128; Assert.Equal(128, multiVal[1]); // When using the ReadOnlySpan<T> constructor, TiffValueCollection creates an array internally and copys all the values from the span to the array. var multiVal2 = new TiffValueCollection <int>(values.AsSpan()); Assert.Equal(3, multiVal2.Count); Assert.False(multiVal2.IsEmpty); Assert.Equal(42, multiVal2[0]); Assert.Equal(128, multiVal2[1]); Assert.Equal(100, multiVal2[2]); Assert.Throws <IndexOutOfRangeException>(() => multiVal2[-1]); Assert.Throws <IndexOutOfRangeException>(() => multiVal2[3]); values[1] = 256; Assert.Equal(128, multiVal2[1]); count = 0; foreach (int v in multiVal2) { switch (count) { case 0: Assert.Equal(42, v); break; case 1: Assert.Equal(128, v); break; case 2: Assert.Equal(100, v); break; } count++; } Assert.Equal(3, count); string[] strings = new string[] { "hello", "world", "." }; var multiRef = new TiffValueCollection <string>(strings); Assert.Equal(3, multiRef.Count); Assert.False(multiRef.IsEmpty); Assert.Equal("hello", multiRef[0]); Assert.Equal("world", multiRef[1]); Assert.Equal(".", multiRef[2]); Assert.Throws <IndexOutOfRangeException>(() => multiRef[-1]); Assert.Throws <IndexOutOfRangeException>(() => multiRef[3]); count = 0; foreach (string v in multiRef) { switch (count) { case 0: Assert.Equal("hello", v); break; case 1: Assert.Equal("world", v); break; case 2: Assert.Equal(".", v); break; } count++; } Assert.Equal(3, count); }