From a421c292e58ef978be42e58b517d11b8d8afb6a3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 25 Nov 2020 18:27:40 +0100 Subject: [PATCH] Add support for writing 8bit gray tiff images --- .../Formats/Tiff/ITiffEncoderOptions.cs | 6 +++- .../Formats/Tiff/TiffBitsPerPixel.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffEncoder.cs | 5 +++ .../Formats/Tiff/TiffEncoderCore.cs | 33 ++++++++++++++----- .../Formats/Tiff/Utils/TiffWriter.cs | 22 +++++++++++++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 98efa52cdc..dcb8a5c440 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Tiff @@ -8,5 +8,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public interface ITiffEncoderOptions { + /// + /// Gets the number of bits per pixel. + /// + TiffBitsPerPixel? BitsPerPixel { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs index 57a6eda5f3..502c2e425c 100644 --- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff public enum TiffBitsPerPixel { /// - /// 8 bits per pixel. Each pixel consists of 1 byte. + /// 8 bits per pixel, grayscale image. Each pixel consists of 1 byte. /// Pixel8 = 8, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 17ed52182a..881b3cc4ee 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -14,6 +14,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { + /// + /// Gets or sets the number of bits per pixel. 8 bit implies a grayscale image. + /// + public TiffBitsPerPixel? BitsPerPixel { get; set; } + /// public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 10d22b25b8..d81acf6c6a 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -47,12 +47,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; + + if (options.BitsPerPixel == TiffBitsPerPixel.Pixel8) + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero; + } + else + { + this.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + } } /// - /// Gets or sets the photometric interpretation implementation to use when encoding the image. + /// Gets the photometric interpretation implementation to use when encoding the image. /// - public TiffColorType ColorType { get; set; } + private TiffPhotometricInterpretation PhotometricInterpretation { get; } /// /// Gets or sets the compression implementation to use when encoding the image. @@ -123,14 +132,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - int imageData = writer.WriteRgbImageData(image, this.padding); + int imageDataBytes; + if (this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb) + { + imageDataBytes = writer.WriteRgbImageData(image, this.padding); + } + else + { + imageDataBytes = writer.WriteGrayImageData(image, this.padding); + } // Write info's about the image to the stream. - this.AddImageFormat(image, ifdEntries, imageDataStart, imageData); + this.AddImageFormat(image, ifdEntries, imageDataStart, imageDataBytes); writer.WriteMarker(ifdOffset, (uint)writer.Position); long nextIfdMarker = this.WriteIfd(writer, ifdEntries); - return nextIfdMarker + imageData; + return nextIfdMarker + imageDataBytes; } /// @@ -211,9 +228,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff Value = (uint)image.Height }; + ushort[] bitsPerSampleValue = this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb ? new ushort[] { 8, 8, 8 } : new ushort[] { 8 }; var bitPerSample = new ExifShortArray(ExifTagValue.BitsPerSample) { - Value = new ushort[] { 8, 8, 8 } + Value = bitsPerSampleValue }; var compression = new ExifShort(ExifTagValue.Compression) @@ -224,8 +242,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) { - // TODO: only rgb for now. - Value = (ushort)TiffPhotometricInterpretation.Rgb + Value = (ushort)this.PhotometricInterpretation }; var stripOffsets = new ExifLongArray(ExifTagValue.StripOffsets) diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index 1908d38ae8..7578c82135 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -145,6 +145,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff return image.Width * image.Height * 3; } + /// + /// Writes the image data as 8 bit gray to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The padding bytes for each row. + /// The number of bytes written + public int WriteGrayImageData(Image image, int padding) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); + Span rowSpan = row.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); + this.output.Write(rowSpan); + } + + return image.Width * image.Height; + } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); ///