/// <inheritdoc />
        public void Compress(TiffCompressionContext context, ReadOnlyMemory <byte> input, IBufferWriter <byte> outputWriter)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("Modified Huffman compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 8)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            context.BitsPerSample = TiffValueCollection.Single <ushort>(1);

            ReadOnlySpan <byte> inputSpan = input.Span;

            int width     = context.ImageSize.Width;
            int height    = context.ImageSize.Height;
            var bitWriter = new BitWriter2(outputWriter, 4096);

            ReferenceScanline referenceScanline = new ReferenceScanline(whiteIsZero: true, width);

            // Process every scanline
            for (int row = 0; row < height; row++)
            {
                ReadOnlySpan <byte> scanline = inputSpan.Slice(0, width);
                inputSpan = inputSpan.Slice(width);

                Encode2DScanline(ref bitWriter, referenceScanline, scanline);

                referenceScanline = new ReferenceScanline(whiteIsZero: true, scanline);
            }

            bitWriter.Flush();
        }
Beispiel #2
0
        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);
                }
            }
        }
Beispiel #3
0
        public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next)
        {
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;
            TiffSize          imageSize  = context.ImageSize;

            using (IMemoryOwner <byte> pixelData = memoryPool.Rent(imageSize.Width * imageSize.Height))
            {
                Memory <byte> pixelDataMemory = pixelData.Memory;
                using (var writer = new TiffMemoryPixelBufferWriter <TiffMask>(memoryPool, pixelDataMemory, imageSize.Width, imageSize.Height))
                    using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter()))
                    {
                        await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false);
                    }

                int count = PackBytesIntoBits(pixelDataMemory.Span, imageSize, _threshold);

                context.PhotometricInterpretation = TiffPhotometricInterpretation.TransparencyMask;
                context.BitsPerSample             = TiffValueCollection.Single <ushort>(1);
                context.UncompressedData          = pixelData.Memory.Slice(0, count);

                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.FillOrder, TiffValueCollection.Single((ushort)TiffFillOrder.HigherOrderBitsFirst)).ConfigureAwait(false);
                }
            }
        }
        public void TestSingle()
        {
            var singleVal = new TiffValueCollection <int>(42);

            Assert.Single(singleVal);
            Assert.False(singleVal.IsEmpty);
            Assert.True(singleVal.Count == 1);
            Assert.Equal(42, singleVal[0]);
            Assert.Throws <IndexOutOfRangeException>(() => singleVal[-1]);
            Assert.Throws <IndexOutOfRangeException>(() => singleVal[2]);

            var singleValZero = new TiffValueCollection <int>(0);

            Assert.Single(singleVal);
            Assert.False(singleVal.IsEmpty);
            Assert.True(singleVal.Count == 1);
            Assert.Equal(0, singleValZero[0]);
            Assert.Throws <IndexOutOfRangeException>(() => singleValZero[-1]);
            Assert.Throws <IndexOutOfRangeException>(() => singleValZero[2]);

            var singleRef = new TiffValueCollection <string>("hello world.");

            Assert.Single(singleRef);
            Assert.False(singleRef.IsEmpty);
            Assert.True(singleRef.Count == 1);
            Assert.Equal("hello world.", singleRef[0]);
            Assert.Throws <IndexOutOfRangeException>(() => singleValZero[-1]);
            Assert.Throws <IndexOutOfRangeException>(() => singleValZero[2]);

            var singleRefNull = new TiffValueCollection <string>((string)null);

            Assert.Single(singleRefNull);
            Assert.False(singleRefNull.IsEmpty);
            Assert.True(singleRefNull.Count == 1);
            Assert.Null(singleRefNull[0]);
            Assert.Throws <IndexOutOfRangeException>(() => singleRefNull[-1]);
            Assert.Throws <IndexOutOfRangeException>(() => singleRefNull[2]);
        }
Beispiel #5
0
 private static void DumpValueCollecionSimple <T>(TiffValueCollection <T> values)
 {
     Console.Write("[");
     if (values.IsEmpty)
     {
         // Do nothing
     }
     else if (values.Count == 1)
     {
         Console.Write(values.GetFirstOrDefault());
     }
     else
     {
         for (int i = 0; i < values.Count; i++)
         {
             Console.Write(values[i]);
             if (i != values.Count - 1)
             {
                 Console.Write(", ");
             }
         }
     }
     Console.Write("]");
 }
Beispiel #6
0
        /// <inheritdoc />
        public void Compress(TiffCompressionContext context, ReadOnlyMemory <byte> input, IBufferWriter <byte> outputWriter)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.PhotometricInterpretation != TiffPhotometricInterpretation.WhiteIsZero && context.PhotometricInterpretation != TiffPhotometricInterpretation.BlackIsZero)
            {
                throw new NotSupportedException("Modified Huffman compression does not support this photometric interpretation.");
            }

            if (context.BitsPerSample.Count != 1 || context.BitsPerSample[0] != 8)
            {
                throw new NotSupportedException("Unsupported bits per sample.");
            }

            context.BitsPerSample = TiffValueCollection.Single <ushort>(1);

            ReadOnlySpan <byte> inputSpan = input.Span;

            int width     = context.ImageSize.Width;
            int height    = context.ImageSize.Height;
            var bitWriter = new BitWriter2(outputWriter, 4096);

            // Process every scanline
            for (int row = 0; row < height; row++)
            {
                ReadOnlySpan <byte> rowSpan = inputSpan.Slice(0, width);
                inputSpan = inputSpan.Slice(width);

                CcittEncodingTable currentTable = CcittEncodingTable.WhiteInstance;
                CcittEncodingTable otherTable   = CcittEncodingTable.BlackInstance;

                // EOL code
                bitWriter.Write(0b000000000001, 12);

                // ModifiedHuffman compression assumes WhiteIsZero photometric interpretation is used.
                // Since the first run is white run, we look for black pixel in the first iteration.
                byte nextRunPixel = 255;

                while (!rowSpan.IsEmpty)
                {
                    // Get the length of the current run
                    int runLength = rowSpan.IndexOf(nextRunPixel);
                    if (runLength < 0)
                    {
                        runLength = rowSpan.Length;
                    }
                    currentTable.EncodeRun(ref bitWriter, runLength);
                    rowSpan = rowSpan.Slice(runLength);

                    // Switch to the other color
                    CcittHelper.SwapTable(ref currentTable, ref otherTable);
                    nextRunPixel = (byte)~nextRunPixel;
                }
            }

            // RTC
            bitWriter.Write(0b000000000001, 12);
            bitWriter.Write(0b000000000001, 12);
            bitWriter.Write(0b000000000001, 12);
            bitWriter.Write(0b000000000001, 12);
            bitWriter.Write(0b000000000001, 12);
            bitWriter.Write(0b000000000001, 12);

            bitWriter.AdvanceAlignByte();
            bitWriter.Flush();
        }
Beispiel #7
0
        private static async Task TestValidConversionAsync <T>(TiffFieldReader fieldReader, TiffImageFileDirectoryEntry entry, string methodName, T[] refData) where T : unmanaged
        {
            MethodInfo method1 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(bool), typeof(CancellationToken) });
            MethodInfo method2 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(int), typeof(bool), typeof(CancellationToken) });
            MethodInfo method3 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(int), typeof(Memory <T>), typeof(bool), typeof(CancellationToken) });

            Assert.NotNull(method1);
            Assert.NotNull(method2);
            Assert.NotNull(method3);
            var delegate1 = (Func <TiffImageFileDirectoryEntry, bool, CancellationToken, ValueTask <TiffValueCollection <T> > >)method1.CreateDelegate(typeof(Func <TiffImageFileDirectoryEntry, bool, CancellationToken, ValueTask <TiffValueCollection <T> > >), fieldReader);
            var delegate2 = (Func <TiffImageFileDirectoryEntry, int, bool, CancellationToken, ValueTask <TiffValueCollection <T> > >)method2.CreateDelegate(typeof(Func <TiffImageFileDirectoryEntry, int, bool, CancellationToken, ValueTask <TiffValueCollection <T> > >), fieldReader);
            var delegate3 = (Func <TiffImageFileDirectoryEntry, int, Memory <T>, bool, CancellationToken, ValueTask>)method3.CreateDelegate(typeof(Func <TiffImageFileDirectoryEntry, int, Memory <T>, bool, CancellationToken, ValueTask>), fieldReader);

            // Test basic overload
            TiffValueCollection <T> testData = await delegate1(entry, false, default);

            Assert.True(MemoryMarshal.AsBytes(refData.AsSpan()).SequenceEqual(MemoryMarshal.AsBytes(testData.ToArray().AsSpan())));

            // Test overload with sizeLimit argument
            int sizeLimit = refData.Length / 2;

            testData = await delegate2(entry, sizeLimit, false, default);

            Assert.Equal(sizeLimit, testData.Count);
            Assert.True(MemoryMarshal.AsBytes(refData.AsSpan(0, sizeLimit)).SequenceEqual(MemoryMarshal.AsBytes(testData.ToArray().AsSpan())));

            // Test overload with external buffer
            int offset = refData.Length / 4;

            T[] testArray = new T[sizeLimit];
            await delegate3(entry, offset, testArray, false, default);

            Assert.True(MemoryMarshal.AsBytes(refData.AsSpan(offset, sizeLimit)).SequenceEqual(MemoryMarshal.AsBytes(testArray.AsSpan())));

            // Test invalid parameter
            Assert.Throws <ArgumentOutOfRangeException>("offset", () => delegate3(entry, -1, new T[entry.ValueCount], false, default));
            Assert.Throws <ArgumentOutOfRangeException>("offset", () => delegate3(entry, (int)(entry.ValueCount + 1), new T[1], false, default));
            Assert.Throws <ArgumentOutOfRangeException>("destination", () => delegate3(entry, 0, new T[entry.ValueCount + 1], false, default));

            // Now do it again with the sync version
            if (methodName.EndsWith("Async", StringComparison.Ordinal))
            {
                methodName = methodName.Substring(0, methodName.Length - 5);

                MethodInfo method4 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(bool) });
                MethodInfo method5 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(int), typeof(bool) });
                MethodInfo method6 = typeof(TiffFieldReader).GetMethod(methodName, new Type[] { typeof(TiffImageFileDirectoryEntry), typeof(int), typeof(Memory <T>), typeof(bool) });
                Assert.NotNull(method4);
                Assert.NotNull(method5);
                Assert.NotNull(method6);
                var delegate4 = (Func <TiffImageFileDirectoryEntry, bool, TiffValueCollection <T> >)method4.CreateDelegate(typeof(Func <TiffImageFileDirectoryEntry, bool, TiffValueCollection <T> >), fieldReader);
                var delegate5 = (Func <TiffImageFileDirectoryEntry, int, bool, TiffValueCollection <T> >)method5.CreateDelegate(typeof(Func <TiffImageFileDirectoryEntry, int, bool, TiffValueCollection <T> >), fieldReader);
                var delegate6 = (Action <TiffImageFileDirectoryEntry, int, Memory <T>, bool>)method6.CreateDelegate(typeof(Action <TiffImageFileDirectoryEntry, int, Memory <T>, bool>), fieldReader);

                // Test basic overload
                testData = delegate4(entry, false);
                Assert.True(MemoryMarshal.AsBytes(refData.AsSpan()).SequenceEqual(MemoryMarshal.AsBytes(testData.ToArray().AsSpan())));

                // Test overload with sizeLimit argument
                sizeLimit = refData.Length / 2;
                testData  = delegate5(entry, sizeLimit, false);
                Assert.Equal(sizeLimit, testData.Count);
                Assert.True(MemoryMarshal.AsBytes(refData.AsSpan(0, sizeLimit)).SequenceEqual(MemoryMarshal.AsBytes(testData.ToArray().AsSpan())));

                // Test overload with external buffer
                offset    = refData.Length / 4;
                testArray = new T[sizeLimit];
                delegate6(entry, offset, testArray, false);
                Assert.True(MemoryMarshal.AsBytes(refData.AsSpan(offset, sizeLimit)).SequenceEqual(MemoryMarshal.AsBytes(testArray.AsSpan())));

                // Test invalid parameter
                Assert.Throws <ArgumentOutOfRangeException>("offset", () => delegate6(entry, -1, new T[entry.ValueCount], false));
                Assert.Throws <ArgumentOutOfRangeException>("offset", () => delegate6(entry, (int)(entry.ValueCount + 1), new T[1], false));
                Assert.Throws <ArgumentOutOfRangeException>("destination", () => delegate6(entry, 0, new T[entry.ValueCount + 1], false));
            }
        }
        private static void UndoHorizontalDifferencingForScanline(Span <byte> scanline, TiffValueCollection <ushort> bitsPerSample, int width)
        {
            if (width <= 1)
            {
                return;
            }
            int sampleCount = bitsPerSample.Count;

            if (sampleCount > 8)
            {
                throw new NotSupportedException("Too many samples.");
            }

            Span <ushort> bitsPerSampleSpan    = stackalloc ushort[8];
            ref ushort    bitsPerSampleSpanRef = ref MemoryMarshal.GetReference(bitsPerSampleSpan);
 /// <summary>
 /// Initialize the middleware.
 /// </summary>
 /// <param name="coefficients">The YCbCrCoefficients tag.</param>
 /// <param name="referenceBlackWhite">The ReferenceBlackWhite tag.</param>
 public TiffChunkyYCbCr161616Interpreter(TiffValueCollection <TiffRational> coefficients, TiffValueCollection <TiffRational> referenceBlackWhite)
 {
     if (!coefficients.IsEmpty && coefficients.Count != 3)
     {
         throw new ArgumentException("coefficient should have 3 none-zero elements.");
     }
     if (!referenceBlackWhite.IsEmpty && referenceBlackWhite.Count != 6)
     {
         throw new ArgumentException("referenceWhiteBlack should have 6 elements.");
     }
     _converter = TiffYCbCrConverter16.Create(coefficients.GetOrCreateArray(), referenceBlackWhite.GetOrCreateArray());
 }
Beispiel #10
0
        private static async Task DumpIfdEntryAsync(int index, TiffFieldReader fieldReader, TiffImageFileDirectoryEntry entry, CancellationToken cancellationToken)
        {
            string tagName  = Enum.IsDefined(typeof(TiffTag), entry.Tag) ? $"{entry.Tag} ({(int)entry.Tag})" : ((int)entry.Tag).ToString();
            string typeName = Enum.IsDefined(typeof(TiffFieldType), entry.Type) ? entry.Type.ToString() : "Unknown";

            Console.Write($"    Tag #{index}: {tagName}, {typeName}[{entry.ValueCount}].");

            switch (entry.Type)
            {
            case TiffFieldType.Byte:
                Console.Write(" Binary data not shown.");
                break;

            case TiffFieldType.ASCII:
                TiffValueCollection <string> valuesAscii = await fieldReader.ReadASCIIFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                if (valuesAscii.IsEmpty)
                {
                    // Do nothing
                }
                else if (valuesAscii.Count == 1)
                {
                    Console.Write(" Value = " + valuesAscii.GetFirstOrDefault());
                }
                else
                {
                    Console.WriteLine();
                    for (int i = 0; i < valuesAscii.Count; i++)
                    {
                        Console.Write($"      [{i}] = {valuesAscii[i]}");
                    }
                }
                break;

            case TiffFieldType.Short:
                TiffValueCollection <ushort> valuesShort = await fieldReader.ReadShortFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesShort);
                break;

            case TiffFieldType.Long:
                TiffValueCollection <uint> valuesLong = await fieldReader.ReadLongFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesLong);
                break;

            case TiffFieldType.Rational:
                TiffValueCollection <TiffRational> valuesRational = await fieldReader.ReadRationalFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesRational);
                break;

            case TiffFieldType.SByte:
                Console.Write(" Binary data not shown.");
                break;

            case TiffFieldType.Undefined:
                Console.Write(" Binary data not shown.");
                break;

            case TiffFieldType.SShort:
                TiffValueCollection <short> valuesSShort = await fieldReader.ReadSShortFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesSShort);
                break;

            case TiffFieldType.SLong:
                TiffValueCollection <int> valuesSLong = await fieldReader.ReadSLongFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesSLong);
                break;

            case TiffFieldType.SRational:
                TiffValueCollection <TiffSRational> valuesSRational = await fieldReader.ReadSRationalFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesSRational);
                break;

            case TiffFieldType.Float:
                TiffValueCollection <float> valuesFloat = await fieldReader.ReadFloatFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesFloat);
                break;

            case TiffFieldType.Double:
                TiffValueCollection <double> valuesDouble = await fieldReader.ReadDoubleFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesDouble);
                break;

            case TiffFieldType.IFD:
                TiffValueCollection <TiffStreamOffset> valuesIfd = await fieldReader.ReadIFDFieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesIfd);
                break;

            case TiffFieldType.Long8:
                TiffValueCollection <ulong> valuesLong8 = await fieldReader.ReadLong8FieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesLong8);
                break;

            case TiffFieldType.SLong8:
                TiffValueCollection <long> valuesSLong8 = await fieldReader.ReadSLong8FieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesSLong8);
                break;

            case TiffFieldType.IFD8:
                TiffValueCollection <TiffStreamOffset> valuesIfd8 = await fieldReader.ReadIFD8FieldAsync(entry, skipTypeValidation : true, cancellationToken : cancellationToken);

                DumpValueCollecion(valuesIfd8);
                break;

            default:
                Console.Write(" Unsupported field type.");
                break;
            }

            Console.WriteLine();
        }
Beispiel #11
0
            static async Task <string?> TransformValueTaskAsync(ValueTask <TiffValueCollection <string> > valueTask)
            {
                TiffValueCollection <string> result = await valueTask.ConfigureAwait(false);

                return(result.GetFirstOrDefault());
            }
Beispiel #12
0
        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>
 /// Initialize the middleware with the default YCbCrCoefficients and ReferenceBlackWhite tags.
 /// </summary>
 public TiffPlanarYCbCr888Interpreter() : this(TiffValueCollection.Empty <TiffRational>(), TiffValueCollection.Empty <TiffRational>())
 {
 }
Beispiel #14
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);
                }
            }
        }
        /// <summary>
        /// Initialize the middleware.
        /// </summary>
        /// <param name="photometricInterpretation">The photometric interpretation of the image.</param>
        /// <param name="bitsPerSample">Bits per sample.</param>
        /// <param name="bytesPerScanlines">Byte count per scanline.</param>
        /// <param name="decompressionAlgorithm">The decompression algorithm.</param>
        public TiffImageDecompressionMiddleware(TiffPhotometricInterpretation photometricInterpretation, TiffValueCollection <ushort> bitsPerSample, TiffValueCollection <int> bytesPerScanlines, ITiffDecompressionAlgorithm decompressionAlgorithm)
        {
            if (bytesPerScanlines.IsEmpty)
            {
                throw new ArgumentException("BytesPerScanlines not specified.");
            }

            _photometricInterpretation = photometricInterpretation;
            _bitsPerSample             = bitsPerSample;
            _bytesPerScanlines         = bytesPerScanlines;
            _decompressionAlgorithm    = decompressionAlgorithm;
        }
 /// <summary>
 /// Initialize the middleware with the default YCbCrCoefficients and ReferenceBlackWhite tags.
 /// </summary>
 public TiffChunkyYCbCr161616Interpreter() : this(TiffValueCollection.Empty <TiffRational>(), TiffValueCollection.Empty <TiffRational>())
 {
 }
Beispiel #17
0
        /// <summary>
        /// Initialize the middleware.
        /// </summary>
        /// <param name="tileWidth">The TileWidth tag.</param>
        /// <param name="tileHeight">The TileLength tag.</param>
        /// <param name="tileOffsets">The TileOffsets tag.</param>
        /// <param name="tileByteCounts">The TileByteCounts tag.</param>
        /// <param name="planeCount">The plane count.</param>
        public TiffTiledImageDecoderEnumeratorMiddleware(int tileWidth, int tileHeight, TiffValueCollection <ulong> tileOffsets, TiffValueCollection <ulong> tileByteCounts, int planeCount)
        {
            _tileWidth  = tileWidth;
            _tileHeight = tileHeight;
            _planaCount = planeCount;
            _lazyLoad   = false;

            _tileOffsets    = tileOffsets;
            _tileByteCounts = tileByteCounts;

            if (tileOffsets.Count != tileByteCounts.Count)
            {
                throw new ArgumentException("tileOffsets does not have the same element count as tileByteCounts.", nameof(tileByteCounts));
            }
        }
 /// <summary>
 /// Initialize the middleware.
 /// </summary>
 /// <param name="bytesPerScanlines">Byte count per scanline.</param>
 /// <param name="bitsPerSample">Bits per sample.</param>
 /// <param name="predictor">The predictor tag.</param>
 public TiffReversePredictorMiddleware(TiffValueCollection <int> bytesPerScanlines, TiffValueCollection <ushort> bitsPerSample, TiffPredictor predictor)
 {
     _bytesPerScanlines = bytesPerScanlines;
     _bitsPerSample     = bitsPerSample;
     _predictor         = predictor;
 }
Beispiel #19
0
        /// <summary>
        /// Apply compression to <see cref="TiffImageEncoderContext{TPixel}.UncompressedData"/> and writes the compressed image to <see cref="TiffImageEncoderContext{TPixel}.FileWriter"/>. Writes <see cref="TiffTag.Compression"/> to thhe IFD writer and runs the next middleware.
        /// </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));
            }
            if (context.FileWriter is null)
            {
                throw new InvalidOperationException("Failed to acquire FileWriter");
            }

            TiffValueCollection <ushort> bitsPerSample = context.BitsPerSample;
            int totalBits = 0;

            for (int i = 0; i < bitsPerSample.Count; i++)
            {
                totalBits += bitsPerSample[i];
            }

            int width             = context.ImageSize.Width;
            int bytesPerScanlines = (totalBits * width + 7) / 8;

            var compressionContext = new TiffCompressionContext
            {
                MemoryPool = context.MemoryPool,
                PhotometricInterpretation = context.PhotometricInterpretation,
                ImageSize        = context.ImageSize,
                BitsPerSample    = context.BitsPerSample,
                BytesPerScanline = bytesPerScanlines
            };

            using (var bufferWriter = new MemoryPoolBufferWriter(context.MemoryPool))
            {
                _compressionAlgorithm.Compress(compressionContext, context.UncompressedData, bufferWriter);
                int length = bufferWriter.Length;

                using (await context.LockAsync().ConfigureAwait(false))
                {
                    TiffStreamOffset offset = await context.FileWriter !.WriteAlignedBytesAsync(bufferWriter.GetReadOnlySequence(), context.CancellationToken).ConfigureAwait(false);
                    context.BitsPerSample = compressionContext.BitsPerSample;
                    context.OutputRegion  = new TiffStreamRegion(offset, length);
                }
            }

            TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter;

            if (!(ifdWriter is null))
            {
                using (await context.LockAsync().ConfigureAwait(false))
                {
                    CancellationToken cancellationToken = context.CancellationToken;
                    await ifdWriter.WriteTagAsync(TiffTag.Compression, TiffValueCollection.Single((ushort)_compression), cancellationToken).ConfigureAwait(false);
                }
            }

            await next.RunAsync(context).ConfigureAwait(false);
        }
 /// <summary>
 /// Initialize the middleware.
 /// </summary>
 /// <param name="isAlphaAssociated">Whether the alpha channel is associated.</param>
 /// <param name="undoColorPreMultiplying">Whether to undo color pre-multiplying.</param>
 /// <param name="bitsPerSample">The BitsPerSample flags.</param>
 /// <param name="fillOrder">The FillOrder tag.</param>
 public TiffChunkyRgbaAny8888Interpreter(bool isAlphaAssociated, bool undoColorPreMultiplying, TiffValueCollection <ushort> bitsPerSample, TiffFillOrder fillOrder = TiffFillOrder.HigherOrderBitsFirst)
 {
     _isAlphaAssociated       = isAlphaAssociated;
     _undoColorPreMultiplying = undoColorPreMultiplying;
     if (bitsPerSample.Count != 4)
     {
         throw new ArgumentOutOfRangeException(nameof(bitsPerSample));
     }
     if ((uint)bitsPerSample[0] > 8 || (uint)bitsPerSample[1] > 8 || (uint)bitsPerSample[2] > 8 || (uint)bitsPerSample[3] > 8)
     {
         throw new ArgumentOutOfRangeException(nameof(bitsPerSample));
     }
     if (fillOrder == 0)
     {
         fillOrder = TiffFillOrder.HigherOrderBitsFirst;
     }
     _bitsPerSample = bitsPerSample;
     _fillOrder     = fillOrder;
 }
        /// <summary>
        /// Initialize the middleware.
        /// </summary>
        /// <param name="rowsPerStrip">Rows per strip.</param>
        /// <param name="stripOffsets">The StripOffsets tag.</param>
        /// <param name="stripsByteCount">The StripsByteCount tag.</param>
        /// <param name="planeCount">The number of planes.</param>
        public TiffStrippedImageDecoderEnumeratorMiddleware(int rowsPerStrip, TiffValueCollection <ulong> stripOffsets, TiffValueCollection <ulong> stripsByteCount, int planeCount)
        {
            _rowsPerStrip = rowsPerStrip;
            _planeCount   = planeCount;
            _lazyLoad     = false;

            _stripOffsets    = stripOffsets;
            _stripsByteCount = stripsByteCount;

            if (stripOffsets.Count != stripsByteCount.Count)
            {
                throw new ArgumentException("stripsByteCount does not have the same element count as stripsOffsets.", nameof(stripsByteCount));
            }
            _stripCount = stripOffsets.Count;
        }
Beispiel #22
0
        /// <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 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);
        }