Example #1
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.width  = image.Width;
            this.height = image.Height;

            ImageMetadata metadata    = image.Metadata;
            PngMetadata   pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            PngEncoderOptionsHelpers.AdjustOptions(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
            IQuantizedFrame <TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);

            this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized);

            stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);

            this.WriteHeaderChunk(stream);
            this.WritePaletteChunk(stream, quantized);
            this.WriteTransparencyChunk(stream, pngMetadata);
            this.WritePhysicalChunk(stream, metadata);
            this.WriteGammaChunk(stream);
            this.WriteExifChunk(stream, metadata);
            this.WriteTextChunks(stream, pngMetadata);
            this.WriteDataChunks(image.Frames.RootFrame, quantized, stream);
            this.WriteEndChunk(stream);
            stream.Flush();

            quantized?.Dispose();
        }
Example #2
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.configuration = image.GetConfiguration();

            ImageMetadata metadata = image.Metadata;

            this.gifMetadata    = metadata.GetFormatMetadata(GifFormat.Instance);
            this.colorTableMode = this.colorTableMode ?? this.gifMetadata.ColorTableMode;
            bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;

            // Quantize the image returning a palette.
            QuantizedFrame <TPixel> quantized =
                this.quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration()).QuantizeFrame(image.Frames.RootFrame);

            // Get the number of bits.
            this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);

            // Write the header.
            this.WriteHeader(stream);

            // Write the LSD.
            int index = this.GetTransparentIndex(quantized);

            this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream);

            if (useGlobalTable)
            {
                this.WriteColorTable(quantized, stream);
            }

            // Write the comments.
            this.WriteComments(metadata, stream);

            // Write application extension to allow additional frames.
            if (image.Frames.Count > 1)
            {
                this.WriteApplicationExtension(stream, this.gifMetadata.RepeatCount);
            }

            if (useGlobalTable)
            {
                this.EncodeGlobal(image, quantized, index, stream);
            }
            else
            {
                this.EncodeLocal(image, quantized, stream);
            }

            // Clean up.
            quantized?.Dispose();
            quantized = null;

            // TODO: Write extension etc
            stream.WriteByte(GifConstants.EndIntroducer);
        }
Example #3
0
        /// <summary>
        /// Reads the raw image information from the specified stream.
        /// </summary>
        /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
        public IImageInfo Identify(Stream stream)
        {
            var         metadata    = new ImageMetadata();
            PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            this.currentStream = stream;
            this.currentStream.Skip(8);
            try
            {
                while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
                {
                    try
                    {
                        switch (chunk.Type)
                        {
                        case PngChunkType.Header:
                            this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
                            break;

                        case PngChunkType.Physical:
                            this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Gamma:
                            this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Data:
                            this.SkipChunkDataAndCrc(chunk);
                            break;

                        case PngChunkType.Text:
                            this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
                            break;

                        case PngChunkType.End:
                            this.isEndChunkReached = true;
                            break;
                        }
                    }
                    finally
                    {
                        chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
                    }
                }
            }
            finally
            {
                this.scanline?.Dispose();
                this.previousScanline?.Dispose();
            }

            if (this.header.Width == 0 && this.header.Height == 0)
            {
                PngThrowHelper.ThrowNoHeader();
            }

            return(new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata));
        }
Example #4
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.configuration = image.GetConfiguration();
            ImageMetadata metadata    = image.Metadata;
            TgaMetadata   tgaMetadata = metadata.GetFormatMetadata(TgaFormat.Instance);

            this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel;

            TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor;

            if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8)
            {
                imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite;
            }

            // If compression is used, set bit 5 of the image descriptor to indicate an left top origin.
            byte imageDescriptor = (byte)(this.compression is TgaCompression.RunLength ? 32 : 0);

            var fileHeader = new TgaFileHeader(
                idLength: 0,
                colorMapType: 0,
                imageType: imageType,
                cMapStart: 0,
                cMapLength: 0,
                cMapDepth: 0,
                xOffset: 0,
                yOffset: this.compression is TgaCompression.RunLength ? (short)image.Height : (short)0, // When run length encoding is used, the origin should be top left instead of the default bottom left.
                width: (short)image.Width,
                height: (short)image.Height,
                pixelDepth: (byte)this.bitsPerPixel.Value,
                imageDescriptor: imageDescriptor);

#if NETCOREAPP2_1
            Span <byte> buffer = stackalloc byte[TgaFileHeader.Size];
#else
            byte[] buffer = new byte[TgaFileHeader.Size];
#endif
            fileHeader.WriteTo(buffer);

            stream.Write(buffer, 0, TgaFileHeader.Size);

            if (this.compression is TgaCompression.RunLength)
            {
                this.WriteRunLengthEndcodedImage(stream, image.Frames.RootFrame);
            }
            else
            {
                this.WriteImage(stream, image.Frames.RootFrame);
            }

            stream.Flush();
        }
Example #5
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.width  = image.Width;
            this.height = image.Height;

            ImageMetadata metadata = image.Metadata;

            PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            PngEncoderOptionsHelpers.AdjustOptions <TPixel>(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
            Image <TPixel> clonedImage       = null;
            bool           clearTransparency = this.options.TransparentColorMode == PngTransparentColorMode.Clear;

            if (clearTransparency)
            {
                clonedImage = image.Clone();
                ClearTransparentPixels(clonedImage);
            }

            IndexedImageFrame <TPixel> quantized = this.CreateQuantizedImage(image, clonedImage);

            stream.Write(PngConstants.HeaderBytes);

            this.WriteHeaderChunk(stream);
            this.WriteGammaChunk(stream);
            this.WritePaletteChunk(stream, quantized);
            this.WriteTransparencyChunk(stream, pngMetadata);
            this.WritePhysicalChunk(stream, metadata);
            this.WriteExifChunk(stream, metadata);
            this.WriteTextChunks(stream, pngMetadata);
            this.WriteDataChunks(clearTransparency ? clonedImage : image, quantized, stream);
            this.WriteEndChunk(stream);

            stream.Flush();

            quantized?.Dispose();
            clonedImage?.Dispose();
        }
        public void CloneIsDeep()
        {
            var metaData = new ImageMetadata();

            var exifProfile   = new ExifProfile();
            var imageProperty = new ImageProperty("name", "value");

            metaData.ExifProfile          = exifProfile;
            metaData.HorizontalResolution = 4;
            metaData.VerticalResolution   = 2;
            metaData.Properties.Add(imageProperty);

            ImageMetadata clone = metaData.DeepClone();

            clone.HorizontalResolution = 2;
            clone.VerticalResolution   = 4;

            Assert.False(metaData.ExifProfile.Equals(clone.ExifProfile));
            Assert.False(metaData.HorizontalResolution.Equals(clone.HorizontalResolution));
            Assert.False(metaData.VerticalResolution.Equals(clone.VerticalResolution));
            Assert.False(metaData.Properties.Equals(clone.Properties));
            Assert.False(metaData.GetFormatMetadata(GifFormat.Instance).Equals(clone.GetFormatMetadata(GifFormat.Instance)));
        }
Example #7
0
        /// <summary>
        /// Decodes the stream to the image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream containing image data. </param>
        /// <exception cref="ImageFormatException">
        /// Thrown if the stream does not contain and end chunk.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the image is larger than the maximum allowable size.
        /// </exception>
        /// <returns>The decoded image</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            var         metadata    = new ImageMetadata();
            PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            this.currentStream = stream;
            this.currentStream.Skip(8);
            Image <TPixel> image = null;

            try
            {
                while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
                {
                    try
                    {
                        switch (chunk.Type)
                        {
                        case PngChunkType.Header:
                            this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
                            break;

                        case PngChunkType.Physical:
                            this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Gamma:
                            this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Data:
                            if (image is null)
                            {
                                this.InitializeImage(metadata, out image);
                            }

                            using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
                            {
                                deframeStream.AllocateNewBytes(chunk.Length);
                                this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata);
                            }

                            break;

                        case PngChunkType.Palette:
                            byte[] pal = new byte[chunk.Length];
                            Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
                            this.palette = pal;
                            break;

                        case PngChunkType.Transparency:
                            byte[] alpha = new byte[chunk.Length];
                            Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
                            this.paletteAlpha = alpha;
                            this.AssignTransparentMarkers(alpha, pngMetadata);
                            break;

                        case PngChunkType.Text:
                            this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length));
                            break;

                        case PngChunkType.Exif:
                            if (!this.ignoreMetadata)
                            {
                                byte[] exifData = new byte[chunk.Length];
                                Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
                                metadata.ExifProfile = new ExifProfile(exifData);
                            }

                            break;

                        case PngChunkType.End:
                            this.isEndChunkReached = true;
                            break;
                        }
                    }
                    finally
                    {
                        chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
                    }
                }

                if (image is null)
                {
                    throw new ImageFormatException("PNG Image does not contain a data chunk");
                }

                return(image);
            }
            finally
            {
                this.scanline?.Dispose();
                this.previousScanline?.Dispose();
            }
        }
 /// <summary>
 /// Gets the tga format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="TgaMetadata"/>.</returns>
 public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance);
 /// <summary>
 /// Gets the tiff format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="TiffMetadata"/>.</returns>
 public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance);
Example #10
0
 /// <summary>
 /// Gets the gif format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="GifMetadata"/>.</returns>
 public static GifMetadata GetGifMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(GifFormat.Instance);
Example #11
0
        /// <summary>
        /// Decodes the stream to the image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream containing image data. </param>
        /// <exception cref="ImageFormatException">
        /// Thrown if the stream does not contain and end chunk.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if the image is larger than the maximum allowable size.
        /// </exception>
        /// <returns>The decoded image</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            var         metadata    = new ImageMetadata();
            PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            this.currentStream = stream;
            this.currentStream.Skip(8);
            Image <TPixel> image = null;

            try
            {
                while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
                {
                    try
                    {
                        switch (chunk.Type)
                        {
                        case PngChunkType.Header:
                            this.ReadHeaderChunk(pngMetadata, chunk.Data.Array);
                            break;

                        case PngChunkType.Physical:
                            this.ReadPhysicalChunk(metadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Gamma:
                            this.ReadGammaChunk(pngMetadata, chunk.Data.GetSpan());
                            break;

                        case PngChunkType.Data:
                            if (image is null)
                            {
                                this.InitializeImage(metadata, out image);
                            }

                            this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata);

                            break;

                        case PngChunkType.Palette:
                            var pal = new byte[chunk.Length];
                            Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
                            this.palette = pal;
                            break;

                        case PngChunkType.Transparency:
                            var alpha = new byte[chunk.Length];
                            Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
                            this.paletteAlpha = alpha;
                            this.AssignTransparentMarkers(alpha, pngMetadata);
                            break;

                        case PngChunkType.Text:
                            this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
                            break;

                        case PngChunkType.CompressedText:
                            this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
                            break;

                        case PngChunkType.InternationalText:
                            this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
                            break;

                        case PngChunkType.Exif:
                            if (!this.ignoreMetadata)
                            {
                                var exifData = new byte[chunk.Length];
                                Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
                                metadata.ExifProfile = new ExifProfile(exifData);
                            }

                            break;

                        case PngChunkType.End:
                            this.isEndChunkReached = true;
                            break;
                        }
                    }
                    finally
                    {
                        chunk.Data?.Dispose(); // Data is rented in ReadChunkData()
                    }
                }

                if (image is null)
                {
                    PngThrowHelper.ThrowNoData();
                }

                return(image);
            }
            finally
            {
                this.scanline?.Dispose();
                this.previousScanline?.Dispose();
            }
        }
Example #12
0
 /// <summary>
 /// Gets the bmp format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="BmpMetadata"/>.</returns>
 public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance);
Example #13
0
 /// <summary>
 /// Gets the jpeg format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="JpegMetadata"/>.</returns>
 public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance);
Example #14
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.configuration = image.GetConfiguration();
            this.width         = image.Width;
            this.height        = image.Height;

            // Always take the encoder options over the metadata values.
            ImageMetadata metadata    = image.Metadata;
            PngMetadata   pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);

            this.gamma        = this.gamma ?? pngMetadata.Gamma;
            this.writeGamma   = this.gamma > 0;
            this.pngColorType = this.pngColorType ?? pngMetadata.ColorType;
            this.pngBitDepth  = this.pngBitDepth ?? pngMetadata.BitDepth;
            this.use16Bit     = this.pngBitDepth == PngBitDepth.Bit16;

            // Ensure we are not allowing impossible combinations.
            if (!ColorTypes.ContainsKey(this.pngColorType.Value))
            {
                throw new NotSupportedException("Color type is not supported or not valid.");
            }

            stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);

            QuantizedFrame <TPixel> quantized = null;

            if (this.pngColorType == PngColorType.Palette)
            {
                byte bits = (byte)this.pngBitDepth;
                if (!ColorTypes[this.pngColorType.Value].Contains(bits))
                {
                    throw new NotSupportedException("Bit depth is not supported or not valid.");
                }

                // Use the metadata to determine what quantization depth to use if no quantizer has been set.
                if (this.quantizer is null)
                {
                    this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
                }

                // Create quantized frame returning the palette and set the bit depth.
                quantized = this.quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration())
                            .QuantizeFrame(image.Frames.RootFrame);
                byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
                bits = Math.Max(bits, quantizedBits);

                // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
                // We check again for the bit depth as the bit depth of the color palette from a given quantizer might not
                // be within the acceptable range.
                if (bits == 3)
                {
                    bits = 4;
                }
                else if (bits >= 5 && bits <= 7)
                {
                    bits = 8;
                }

                this.bitDepth = bits;
            }
            else
            {
                this.bitDepth = (byte)this.pngBitDepth;
                if (!ColorTypes[this.pngColorType.Value].Contains(this.bitDepth))
                {
                    throw new NotSupportedException("Bit depth is not supported or not valid.");
                }
            }

            this.bytesPerPixel = this.CalculateBytesPerPixel();

            var header = new PngHeader(
                width: image.Width,
                height: image.Height,
                bitDepth: this.bitDepth,
                colorType: this.pngColorType.Value,
                compressionMethod: 0, // None
                filterMethod: 0,
                interlaceMethod: 0);  // TODO: Can't write interlaced yet.

            this.WriteHeaderChunk(stream, header);

            // Collect the indexed pixel data
            if (quantized != null)
            {
                this.WritePaletteChunk(stream, quantized);
            }

            if (pngMetadata.HasTrans)
            {
                this.WriteTransparencyChunk(stream, pngMetadata);
            }

            this.WritePhysicalChunk(stream, metadata);
            this.WriteGammaChunk(stream);
            this.WriteExifChunk(stream, metadata);
            this.WriteDataChunks(image.Frames.RootFrame, quantized, stream);
            this.WriteEndChunk(stream);
            stream.Flush();

            quantized?.Dispose();
        }
Example #15
0
 /// <summary>
 /// Gets the jpeg format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="JpegMetadata"/>.</returns>
 public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(new JpegFormat());
Example #16
0
 /// <summary>
 /// Gets the pbm format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="PbmMetadata"/>.</returns>
 public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PbmFormat.Instance);
Example #17
0
        /// <summary>
        /// Encode writes the image to the jpeg baseline format with the given options.
        /// </summary>
        /// <param name="image">The image to write from.</param>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="cancellationToken">The token to request cancellation.</param>
        public void Encode(Buffer2D <Pixel> image, ImageMetadata metadata, Stream stream, CancellationToken cancellationToken)
        {
            //Guard.NotNull(image, nameof(image));
            //Guard.NotNull(stream, nameof(stream));
            cancellationToken.ThrowIfCancellationRequested();

            const ushort max = JpegConstants.MaxLength;

            if (image.Width >= max || image.Height >= max)
            {
                throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
            }

            this.outputStream = stream;

            // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
            int qlty = Numeric.Clamp(this.quality ?? metadata.GetFormatMetadata(new JpegFormat()).Quality, 1, 100);

            this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420;

            // Convert from a quality rating to a scaling factor.
            int scale;

            if (qlty < 50)
            {
                scale = 5000 / qlty;
            }
            else
            {
                scale = 200 - (qlty * 2);
            }

            // Initialize the quantization tables.
            InitQuantizationTable(0, scale, ref this.luminanceQuantTable);
            InitQuantizationTable(1, scale, ref this.chrominanceQuantTable);

            // Compute number of components based on input image type.
            const int componentCount = 3;

            // Write the Start Of Image marker.
            this.WriteApplicationHeader(metadata);

            // Write Exif, ICC and IPTC profiles
            this.WriteProfiles(metadata);

            // Write the quantization tables.
            this.WriteDefineQuantizationTables();

            // Write the image dimensions.
            this.WriteStartOfFrame(image.Width, image.Height, componentCount);

            // Write the Huffman tables.
            this.WriteDefineHuffmanTables(componentCount);

            // Write the image data.
            this.WriteStartOfScan(image, cancellationToken);

            // Write the End Of Image marker.
            this.buffer[0] = JpegConstants.Markers.XFF;
            this.buffer[1] = JpegConstants.Markers.EOI;
            stream.Write(this.buffer, 0, 2);
            stream.Flush();
        }
Example #18
0
 /// <summary>
 /// Gets the webp format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="WebpMetadata"/>.</returns>
 public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
Example #19
0
        /// <summary>
        /// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
        /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
        public void Encode <TPixel>(Image <TPixel> image, Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.configuration = image.GetConfiguration();
            ImageMetadata metadata    = image.Metadata;
            BmpMetadata   bmpMetadata = metadata.GetFormatMetadata(BmpFormat.Instance);

            this.bitsPerPixel = this.bitsPerPixel ?? bmpMetadata.BitsPerPixel;

            short bpp          = (short)this.bitsPerPixel;
            int   bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);

            this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F));

            // Set Resolution.
            int hResolution = 0;
            int vResolution = 0;

            if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio)
            {
                if (metadata.HorizontalResolution > 0 && metadata.VerticalResolution > 0)
                {
                    switch (metadata.ResolutionUnits)
                    {
                    case PixelResolutionUnit.PixelsPerInch:

                        hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution));
                        vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution));
                        break;

                    case PixelResolutionUnit.PixelsPerCentimeter:

                        hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution));
                        vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution));
                        break;

                    case PixelResolutionUnit.PixelsPerMeter:
                        hResolution = (int)Math.Round(metadata.HorizontalResolution);
                        vResolution = (int)Math.Round(metadata.VerticalResolution);

                        break;
                    }
                }
            }

            int infoHeaderSize = this.writeV4Header ? BmpInfoHeader.SizeV4 : BmpInfoHeader.SizeV3;
            var infoHeader     = new BmpInfoHeader(
                headerSize: infoHeaderSize,
                height: image.Height,
                width: image.Width,
                bitsPerPixel: bpp,
                planes: 1,
                imageSize: image.Height * bytesPerLine,
                clrUsed: 0,
                clrImportant: 0,
                xPelsPerMeter: hResolution,
                yPelsPerMeter: vResolution);

            if (this.writeV4Header && this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
            {
                infoHeader.AlphaMask   = Rgba32AlphaMask;
                infoHeader.RedMask     = Rgba32RedMask;
                infoHeader.GreenMask   = Rgba32GreenMask;
                infoHeader.BlueMask    = Rgba32BlueMask;
                infoHeader.Compression = BmpCompression.BitFields;
            }

            int colorPaletteSize = this.bitsPerPixel == BmpBitsPerPixel.Pixel8 ? ColorPaletteSize8Bit : 0;

            var fileHeader = new BmpFileHeader(
                type: BmpConstants.TypeMarkers.Bitmap,
                fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
                reserved: 0,
                offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize);

#if NETCOREAPP2_1
            Span <byte> buffer = stackalloc byte[infoHeaderSize];
#else
            byte[] buffer = new byte[infoHeaderSize];
#endif
            fileHeader.WriteTo(buffer);

            stream.Write(buffer, 0, BmpFileHeader.Size);

            if (this.writeV4Header)
            {
                infoHeader.WriteV4Header(buffer);
            }
            else
            {
                infoHeader.WriteV3Header(buffer);
            }

            stream.Write(buffer, 0, infoHeaderSize);

            this.WriteImage(stream, image.Frames.RootFrame);

            stream.Flush();
        }
 /// <summary>
 /// Gets the png format specific metadata for the image.
 /// </summary>
 /// <param name="metadata">The metadata this method extends.</param>
 /// <returns>The <see cref="PngMetadata"/>.</returns>
 public static PngMetadata GetPngMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PngFormat.Instance);