mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
431 changed files with 5379 additions and 3624 deletions
@ -0,0 +1,105 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for <see cref="Buffer2D{T}"/>.
|
|||
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
|
|||
/// </summary>
|
|||
internal static class Buffer2DUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="kernel">The 1D convolution kernel.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRow">The target row.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
public static void Convolve4<TPixel>( |
|||
Span<Complex64> kernel, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
Span<ComplexVector4> targetRow, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
ComplexVector4 vector = default; |
|||
int kernelLength = kernel.Length; |
|||
int radiusY = kernelLength >> 1; |
|||
int sourceOffsetColumnBase = column + minColumn; |
|||
ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); |
|||
|
|||
for (int i = 0; i < kernelLength; i++) |
|||
{ |
|||
int offsetY = (row + i - radiusY).Clamp(minRow, maxRow); |
|||
int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn); |
|||
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
|||
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
|||
|
|||
vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor); |
|||
} |
|||
|
|||
targetRow[column] = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
|
|||
/// </summary>
|
|||
/// <param name="kernel">The 1D convolution kernel.</param>
|
|||
/// <param name="sourceValues">The source frame.</param>
|
|||
/// <param name="targetRow">The target row.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
public static void Convolve4( |
|||
Span<Complex64> kernel, |
|||
Buffer2D<ComplexVector4> sourceValues, |
|||
Span<ComplexVector4> targetRow, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
{ |
|||
ComplexVector4 vector = default; |
|||
int kernelLength = kernel.Length; |
|||
int radiusX = kernelLength >> 1; |
|||
int sourceOffsetColumnBase = column + minColumn; |
|||
|
|||
int offsetY = row.Clamp(minRow, maxRow); |
|||
ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY)); |
|||
ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel); |
|||
|
|||
for (int x = 0; x < kernelLength; x++) |
|||
{ |
|||
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn); |
|||
vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX)); |
|||
} |
|||
|
|||
targetRow[column] = vector; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,57 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Png |
|||
{ |
|||
/// <summary>
|
|||
/// The helper methods for <see cref="PngEncoderCore"/> class.
|
|||
/// </summary>
|
|||
internal static class PngEncoderHelpers |
|||
{ |
|||
/// <summary>
|
|||
/// Packs the given 8 bit array into and array of <paramref name="bits"/> depths.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span in 8 bits.</param>
|
|||
/// <param name="result">The resultant span in <paramref name="bits"/>.</param>
|
|||
/// <param name="bits">The bit depth.</param>
|
|||
/// <param name="scale">The scaling factor.</param>
|
|||
public static void ScaleDownFrom8BitArray(ReadOnlySpan<byte> source, Span<byte> result, int bits, float scale = 1) |
|||
{ |
|||
ref byte sourceRef = ref MemoryMarshal.GetReference(source); |
|||
ref byte resultRef = ref MemoryMarshal.GetReference(result); |
|||
|
|||
int shift = 8 - bits; |
|||
byte mask = (byte)(0xFF >> shift); |
|||
byte shift0 = (byte)shift; |
|||
int v = 0; |
|||
int resultOffset = 0; |
|||
|
|||
for (int i = 0; i < source.Length; i++) |
|||
{ |
|||
int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, i) / scale)) & mask; |
|||
v |= value << shift; |
|||
|
|||
if (shift == 0) |
|||
{ |
|||
shift = shift0; |
|||
Unsafe.Add(ref resultRef, resultOffset) = (byte)v; |
|||
resultOffset++; |
|||
v = 0; |
|||
} |
|||
else |
|||
{ |
|||
shift -= bits; |
|||
} |
|||
} |
|||
|
|||
if (shift != shift0) |
|||
{ |
|||
Unsafe.Add(ref resultRef, resultOffset) = (byte)v; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Processing.Processors.Quantization; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Png |
|||
{ |
|||
/// <summary>
|
|||
/// The options structure for the <see cref="PngEncoderCore"/>.
|
|||
/// </summary>
|
|||
internal class PngEncoderOptions : IPngEncoderOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
|
|||
/// </summary>
|
|||
/// <param name="source">The source.</param>
|
|||
public PngEncoderOptions(IPngEncoderOptions source) |
|||
{ |
|||
this.BitDepth = source.BitDepth; |
|||
this.ColorType = source.ColorType; |
|||
|
|||
// Specification recommends default filter method None for paletted images and Paeth for others.
|
|||
this.FilterMethod = source.FilterMethod ?? (source.ColorType == PngColorType.Palette |
|||
? PngFilterMethod.None |
|||
: PngFilterMethod.Paeth); |
|||
this.CompressionLevel = source.CompressionLevel; |
|||
this.TextCompressionThreshold = source.TextCompressionThreshold; |
|||
this.Gamma = source.Gamma; |
|||
this.Quantizer = source.Quantizer; |
|||
this.Threshold = source.Threshold; |
|||
this.InterlaceMethod = source.InterlaceMethod; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of bits per sample or per palette index (not per pixel).
|
|||
/// Not all values are allowed for all <see cref="P:SixLabors.ImageSharp.Formats.Png.IPngEncoderOptions.ColorType" /> values.
|
|||
/// </summary>
|
|||
public PngBitDepth? BitDepth { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the color type.
|
|||
/// </summary>
|
|||
public PngColorType? ColorType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the filter method.
|
|||
/// </summary>
|
|||
public PngFilterMethod? FilterMethod { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the compression level 1-9.
|
|||
/// <remarks>Defaults to 6.</remarks>
|
|||
/// </summary>
|
|||
public int CompressionLevel { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public int TextCompressionThreshold { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the gamma value, that will be written the image.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The gamma value of the image.
|
|||
/// </value>
|
|||
public float? Gamma { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the quantizer for reducing the color count.
|
|||
/// </summary>
|
|||
public IQuantizer Quantizer { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the transparency threshold.
|
|||
/// </summary>
|
|||
public byte Threshold { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image.
|
|||
/// </summary>
|
|||
public PngInterlaceMode? InterlaceMethod { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing.Processors.Quantization; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Png |
|||
{ |
|||
/// <summary>
|
|||
/// The helper methods for the PNG encoder options.
|
|||
/// </summary>
|
|||
internal static class PngEncoderOptionsHelpers |
|||
{ |
|||
/// <summary>
|
|||
/// Adjusts the options.
|
|||
/// </summary>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <param name="pngMetadata">The PNG metadata.</param>
|
|||
/// <param name="use16Bit">if set to <c>true</c> [use16 bit].</param>
|
|||
/// <param name="bytesPerPixel">The bytes per pixel.</param>
|
|||
public static void AdjustOptions( |
|||
PngEncoderOptions options, |
|||
PngMetadata pngMetadata, |
|||
out bool use16Bit, |
|||
out int bytesPerPixel) |
|||
{ |
|||
// Always take the encoder options over the metadata values.
|
|||
options.Gamma = options.Gamma ?? pngMetadata.Gamma; |
|||
options.ColorType = options.ColorType ?? pngMetadata.ColorType; |
|||
options.BitDepth = options.BitDepth ?? pngMetadata.BitDepth; |
|||
options.InterlaceMethod = options.InterlaceMethod ?? pngMetadata.InterlaceMethod; |
|||
|
|||
use16Bit = options.BitDepth == PngBitDepth.Bit16; |
|||
bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit); |
|||
|
|||
// Ensure we are not allowing impossible combinations.
|
|||
if (!PngConstants.ColorTypes.ContainsKey(options.ColorType.Value)) |
|||
{ |
|||
throw new NotSupportedException("Color type is not supported or not valid."); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the quantized frame.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <param name="image">The image.</param>
|
|||
public static IQuantizedFrame<TPixel> CreateQuantizedFrame<TPixel>( |
|||
PngEncoderOptions options, |
|||
Image<TPixel> image) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (options.ColorType != PngColorType.Palette) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
byte bits = (byte)options.BitDepth; |
|||
if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bits) == -1) |
|||
{ |
|||
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 (options.Quantizer is null) |
|||
{ |
|||
options.Quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits)); |
|||
} |
|||
|
|||
// Create quantized frame returning the palette and set the bit depth.
|
|||
using (IFrameQuantizer<TPixel> frameQuantizer = options.Quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration())) |
|||
{ |
|||
return frameQuantizer.QuantizeFrame(image.Frames.RootFrame); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the bit depth value.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
/// <param name="options">The options.</param>
|
|||
/// <param name="image">The image.</param>
|
|||
/// <param name="quantizedFrame">The quantized frame.</param>
|
|||
public static byte CalculateBitDepth<TPixel>( |
|||
PngEncoderOptions options, |
|||
Image<TPixel> image, |
|||
IQuantizedFrame<TPixel> quantizedFrame) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
byte bitDepth; |
|||
if (options.ColorType == PngColorType.Palette) |
|||
{ |
|||
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantizedFrame.Palette.Length).Clamp(1, 8); |
|||
byte bits = Math.Max((byte)options.BitDepth, 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; |
|||
} |
|||
|
|||
bitDepth = bits; |
|||
} |
|||
else |
|||
{ |
|||
bitDepth = (byte)options.BitDepth; |
|||
} |
|||
|
|||
if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bitDepth) == -1) |
|||
{ |
|||
throw new NotSupportedException("Bit depth is not supported or not valid."); |
|||
} |
|||
|
|||
return bitDepth; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the correct number of bytes per pixel for the given color type.
|
|||
/// </summary>
|
|||
/// <returns>Bytes per pixel.</returns>
|
|||
private static int CalculateBytesPerPixel(PngColorType? pngColorType, bool use16Bit) |
|||
{ |
|||
switch (pngColorType) |
|||
{ |
|||
case PngColorType.Grayscale: |
|||
return use16Bit ? 2 : 1; |
|||
|
|||
case PngColorType.GrayscaleWithAlpha: |
|||
return use16Bit ? 4 : 2; |
|||
|
|||
case PngColorType.Palette: |
|||
return 1; |
|||
|
|||
case PngColorType.Rgb: |
|||
return use16Bit ? 6 : 3; |
|||
|
|||
// PngColorType.RgbWithAlpha
|
|||
default: |
|||
return use16Bit ? 8 : 4; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,143 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Png |
|||
{ |
|||
/// <summary>
|
|||
/// Stores text data contained in the iTXt, tEXt, and zTXt chunks.
|
|||
/// Used for conveying textual information associated with the image, like the name of the author,
|
|||
/// the copyright information, the date, where the image was created, or some other information.
|
|||
/// </summary>
|
|||
public readonly struct PngTextData : IEquatable<PngTextData> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PngTextData"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="keyword">The keyword of the property.</param>
|
|||
/// <param name="value">The value of the property.</param>
|
|||
/// <param name="languageTag">An optional language tag.</param>
|
|||
/// <param name="translatedKeyword">A optional translated keyword.</param>
|
|||
public PngTextData(string keyword, string value, string languageTag, string translatedKeyword) |
|||
{ |
|||
Guard.NotNullOrWhiteSpace(keyword, nameof(keyword)); |
|||
|
|||
// No leading or trailing whitespace is allowed in keywords.
|
|||
this.Keyword = keyword.Trim(); |
|||
this.Value = value; |
|||
this.LanguageTag = languageTag; |
|||
this.TranslatedKeyword = translatedKeyword; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the keyword of this <see cref="PngTextData"/> which indicates
|
|||
/// the type of information represented by the text string as described in https://www.w3.org/TR/PNG/#11keywords.
|
|||
/// </summary>
|
|||
/// <example>
|
|||
/// Typical properties are the author, copyright information or other meta information.
|
|||
/// </example>
|
|||
public string Keyword { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of this <see cref="PngTextData"/>.
|
|||
/// </summary>
|
|||
public string Value { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets an optional language tag defined in https://www.w3.org/TR/PNG/#2-RFC-3066 indicates the human language used by the translated keyword and the text.
|
|||
/// If the first word is two or three letters long, it is an ISO language code https://www.w3.org/TR/PNG/#2-ISO-639.
|
|||
/// </summary>
|
|||
/// <example>
|
|||
/// Examples: cn, en-uk, no-bok, x-klingon, x-KlInGoN.
|
|||
/// </example>
|
|||
public string LanguageTag { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets an optional translated keyword, should contain a translation of the keyword into the language indicated by the language tag.
|
|||
/// </summary>
|
|||
public string TranslatedKeyword { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values
|
|||
/// of the properties of the two <see cref="PngTextData"/> objects are equal.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="PngTextData"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="PngTextData"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator ==(PngTextData left, PngTextData right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values
|
|||
/// of the properties of the two <see cref="PngTextData"/> objects are unequal.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="PngTextData"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="PngTextData"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
public static bool operator !=(PngTextData left, PngTextData right) |
|||
{ |
|||
return !(left == right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether this instance and a specified object are equal.
|
|||
/// </summary>
|
|||
/// <param name="obj">
|
|||
/// The object to compare with the current instance.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// true if <paramref name="obj"/> and this instance are the same type and represent the
|
|||
/// same value; otherwise, false.
|
|||
/// </returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return obj is PngTextData other && this.Equals(other); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the hash code for this instance.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A 32-bit signed integer that is the hash code for this instance.
|
|||
/// </returns>
|
|||
public override int GetHashCode() => HashCode.Combine(this.Keyword, this.Value, this.LanguageTag, this.TranslatedKeyword); |
|||
|
|||
/// <summary>
|
|||
/// Returns the fully qualified type name of this instance.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A <see cref="T:System.String"/> containing a fully qualified type name.
|
|||
/// </returns>
|
|||
public override string ToString() => $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]"; |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether the current object is equal to another object of the same type.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// True if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
/// <param name="other">An object to compare with this object.</param>
|
|||
public bool Equals(PngTextData other) |
|||
{ |
|||
return this.Keyword.Equals(other.Keyword) |
|||
&& this.Value.Equals(other.Value) |
|||
&& this.LanguageTag.Equals(other.LanguageTag) |
|||
&& this.TranslatedKeyword.Equals(other.TranslatedKeyword); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue