public void TestRgb24() { Assert.Equal(3, Unsafe.SizeOf <TiffRgb24>()); var defaultPixel = default(TiffRgb24); Assert.Equal(0, defaultPixel.R); Assert.Equal(0, defaultPixel.G); Assert.Equal(0, defaultPixel.B); var pixel1 = new TiffRgb24(0x12, 0x34, 0x56); Assert.Equal(0x12, pixel1.R); Assert.Equal(0x34, pixel1.G); Assert.Equal(0x56, pixel1.B); Assert.False(pixel1.Equals(defaultPixel)); Assert.False(defaultPixel.Equals(pixel1)); Assert.False(pixel1 == defaultPixel); Assert.False(defaultPixel == pixel1); Assert.True(pixel1 != defaultPixel); Assert.True(defaultPixel != pixel1); Assert.False(pixel1.GetHashCode() == defaultPixel.GetHashCode()); var pixel2 = new TiffRgb24(0x12, 0x34, 0x56); Assert.True(pixel1.Equals(pixel2)); Assert.True(pixel2.Equals(pixel1)); Assert.True(pixel1 == pixel2); Assert.True(pixel2 == pixel1); Assert.False(pixel1 != pixel2); Assert.False(pixel2 != pixel1); Assert.True(pixel1.GetHashCode() == pixel2.GetHashCode()); }
public async Task TestAsync(string reference, string test) { using var refImage = Image.Load <Rgb24>(reference); await using TiffFileReader tiff = await TiffFileReader.OpenAsync(test); TiffStreamOffset ifdOffset = tiff.FirstImageFileDirectoryOffset; while (!ifdOffset.IsZero) { TiffImageFileDirectory ifd = await tiff.ReadImageFileDirectoryAsync(ifdOffset); TiffImageDecoder decoder = await tiff.CreateImageDecoderAsync(ifd, new TiffImageDecoderOptions { UndoColorPreMultiplying = true }); Assert.Equal(refImage.Width, decoder.Width); Assert.Equal(refImage.Height, decoder.Height); TiffRgb24[] pixels = new TiffRgb24[decoder.Width * decoder.Height]; await decoder.DecodeAsync(TiffPixelBuffer.Wrap(pixels, decoder.Width, decoder.Height)); AssertEqual(refImage, pixels); using (var image = new Image <Rgb24>(decoder.Width, decoder.Height)) { decoder.Decode(image); AssertEqual(refImage, image); } ifdOffset = ifd.NextOffset; } }
public void TestYCbCr8ToRgb24(byte y, byte cb, byte cr) { var converter = TiffYCbCrConverter8.CreateDefault(); TiffRgb24 pixel = converter.ConvertToRgb24(y, cb, cr); converter.ConvertFromRgb24(pixel, out byte oy, out byte ocb, out byte ocr); Assert.Equal(y, oy); Assert.Equal(cb, ocb); Assert.Equal(cr, ocr); Span <byte> ycbcr = stackalloc byte[9]; ycbcr[0] = y; ycbcr[1] = cb; ycbcr[2] = cr; ycbcr[3] = y; ycbcr[4] = cb; ycbcr[5] = cr; ycbcr[6] = y; ycbcr[7] = cb; ycbcr[8] = cr; Span <TiffRgb24> rgb = stackalloc TiffRgb24[3]; Span <byte> ycbcrBack = stackalloc byte[9]; converter.ConvertToRgb24(ycbcr, rgb, 3); converter.ConvertFromRgb24(rgb, ycbcrBack, 3); Assert.True(ycbcr.SequenceEqual(ycbcrBack)); }
public TiffRgb24 ConvertToRgb24(byte y, byte cb, byte cr) { CodingRangeExpander expanderY = _expanderY; CodingRangeExpander expanderCb = _expanderCb; CodingRangeExpander expanderCr = _expanderCr; YCbCrToRgbConverter converter = _converterFrom; float y64 = expanderY.Expand(y); float cb64 = expanderCb.Expand(cb); float cr64 = expanderCr.Expand(cr); TiffRgb24 pixel = default; // TODO: SkipInit converter.Convert(y64, cb64, cr64, ref pixel); return(pixel); }
/// <inheritdoc /> public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } int skippedRowOffset = context.SourceImageSize.Width * context.SourceReadOffset.Y; int planarByteCount = context.SourceImageSize.Width * context.SourceImageSize.Height; ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span; ReadOnlySpan <byte> sourceR = sourceSpan.Slice(0, planarByteCount); ReadOnlySpan <byte> sourceG = sourceSpan.Slice(planarByteCount, planarByteCount); ReadOnlySpan <byte> sourceB = sourceSpan.Slice(2 * planarByteCount, planarByteCount); using TiffPixelBufferWriter <TiffRgb24> writer = context.GetWriter <TiffRgb24>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgb24> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgb24> rowDestinationSpan = pixelSpanHandle.GetSpan(); int rowOffset = skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X; for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + col; rowDestinationSpan[col] = new TiffRgb24(sourceR[componentOffset], sourceG[componentOffset], sourceB[componentOffset]); } } return(next.RunAsync(context)); }
private static void ProcessAssociatedPreservingColorPreMultiplying(TiffImageDecoderContext context) { int bytesPerScanline = 4 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgb24> writer = context.GetWriter <TiffRgb24>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgb24> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <TiffRgba32> rowSourceSpan = MemoryMarshal.Cast <byte, TiffRgba32>(sourceSpan.Slice(4 * context.SourceReadOffset.X, 4 * context.ReadSize.Width)); Span <TiffRgb24> rowDestinationSpan = pixelSpanHandle.GetSpan(); for (int col = 0; col < cols; col++) { TiffRgba32 pixel = rowSourceSpan[col]; TiffRgb24 pixel24; byte a = pixel.A; if (a == 0) { pixel24 = default; } else { pixel24 = new TiffRgb24((byte)(pixel.R * 255 / a), (byte)(pixel.G * 255 / a), (byte)(pixel.B * 255 / a)); } rowDestinationSpan[col] = pixel24; } sourceSpan = sourceSpan.Slice(bytesPerScanline); } }
/// <inheritdoc /> public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } Span <ushort> bitsPerSample = stackalloc ushort[3]; _bitsPerSample.CopyTo(bitsPerSample); bool isHigherOrderBitsFirst = _fillOrder != TiffFillOrder.LowerOrderBitsFirst; bool canDoFastPath = bitsPerSample[0] >= 4 && bitsPerSample[1] >= 4 && bitsPerSample[2] >= 4; int totalBitsPerSample = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; int bytesPerScanline = (context.SourceImageSize.Width * totalBitsPerSample + 7) / 8; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgb24> writer = context.GetWriter <TiffRgb24>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; TiffRgb24 pixel = default; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgb24> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgb24> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * totalBitsPerSample); if (canDoFastPath) { // Fast path for bits >= 8 for (int col = 0; col < cols; col++) { pixel.R = (byte)FastExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 8); pixel.G = (byte)FastExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 8); pixel.B = (byte)FastExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 8); pixelSpan[col] = pixel; } } else { // Slow path for (int col = 0; col < cols; col++) { pixel.R = (byte)ExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 8); pixel.G = (byte)ExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 8); pixel.B = (byte)ExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 8); pixelSpan[col] = pixel; } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }