Browse Source

Fixing StyleCop

qoi
LuisAlfredo92 3 years ago
parent
commit
aed9acd6bf
No known key found for this signature in database GPG Key ID: 13A8436905993B8F
  1. 54
      src/ImageSharp/Formats/Qoi/QoiChunkEnum.cs
  2. 8
      src/ImageSharp/Formats/Qoi/QoiConstants.cs
  3. 41
      src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs
  4. 4
      src/ImageSharp/Formats/Qoi/QoiEncoder.cs
  5. 35
      src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs
  6. 13
      src/ImageSharp/Formats/Qoi/QoiFormat.cs
  7. 10
      src/ImageSharp/Formats/Qoi/QoiHeader.cs
  8. 7
      src/ImageSharp/Image{TPixel}.cs
  9. 4
      tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs

54
src/ImageSharp/Formats/Qoi/QoiChunkEnum.cs

@ -3,12 +3,54 @@
namespace SixLabors.ImageSharp.Formats.Qoi;
/// <summary>
/// Enum that contains the operations that encoder and decoder must process, written
/// in binary to be easier to compare them in the reference
/// </summary>
public enum QoiChunkEnum
{
QOI_OP_RGB = 0b11111110,
QOI_OP_RGBA = 0b11111111,
QOI_OP_INDEX = 0b00000000,
QOI_OP_DIFF = 0b01000000,
QOI_OP_LUMA = 0b10000000,
QOI_OP_RUN = 0b11000000
/// <summary>
/// Indicates that the operation is QOI_OP_RGB where the RGB values are written
/// in one byte each one after this marker
/// </summary>
QoiOpRgb = 0b11111110,
/// <summary>
/// Indicates that the operation is QOI_OP_RGBA where the RGBA values are written
/// in one byte each one after this marker
/// </summary>
QoiOpRgba = 0b11111111,
/// <summary>
/// Indicates that the operation is QOI_OP_INDEX where one byte contains a 2-bit
/// marker (0b00) followed by an index on the previously seen pixels array 0..63
/// </summary>
QoiOpIndex = 0b00000000,
/// <summary>
/// Indicates that the operation is QOI_OP_DIFF where one byte contains a 2-bit
/// marker (0b01) followed by 2-bit differences in red, green and blue channel
/// with the previous pixel with a bias of 2 (-2..1)
/// </summary>
QoiOpDiff = 0b01000000,
/// <summary>
/// Indicates that the operation is QOI_OP_LUMA where one byte contains a 2-bit
/// marker (0b01) followed by a 6-bits number that indicates the difference of
/// the green channel with the previous pixel. Then another byte that contains
/// a 4-bit number that indicates the difference of the red channel minus the
/// previous difference, and another 4-bit number that indicates the difference
/// of the blue channel minus the green difference
/// Example: 0b10[6-bits diff green] 0b[6-bits dr-dg][6-bits db-dg]
/// dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
/// db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)
/// </summary>
QoiOpLuma = 0b10000000,
/// <summary>
/// Indicates that the operation is QOI_OP_RUN where one byte contains a 2-bit
/// marker (0b11) followed by a 6-bits number that indicates the times that the
/// previous pixel is repeated
/// </summary>
QoiOpRun = 0b11000000
}

8
src/ImageSharp/Formats/Qoi/QoiConstants.cs

@ -15,13 +15,13 @@ internal static class QoiConstants
public static ReadOnlySpan<byte> Magic => SMagic;
/// <summary>
/// The list of mimetypes that equate to a QOI.
/// Gets the list of mimetypes that equate to a QOI.
/// See https://github.com/phoboslab/qoi/issues/167
/// </summary>
public static readonly string[] MimeTypes = { "image/qoi", "image/x-qoi", "image/vnd.qoi" };
public static string[] MimeTypes { get; } = { "image/qoi", "image/x-qoi", "image/vnd.qoi" };
/// <summary>
/// The list of file extensions that equate to a QOI.
/// Gets the list of file extensions that equate to a QOI.
/// </summary>
public static readonly string[] FileExtensions = { "qoi" };
public static string[] FileExtensions { get; } = { "qoi" };
}

41
src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs

@ -61,7 +61,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
};
Image<TPixel> image = new(this.configuration, (int)this.header.Width, (int)this.header.Height, metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
this.ProcessPixels(stream, pixels);
return image;
@ -145,11 +145,11 @@ internal class QoiDecoderCore : IImageDecoderInternals
where TPixel : unmanaged, IPixel<TPixel>
{
Rgba32[] previouslySeenPixels = new Rgba32[64];
Rgba32 previousPixel = new(0,0,0,255);
Rgba32 previousPixel = new(0, 0, 0, 255);
// We save the pixel to avoid loosing the fully opaque black pixel
// See https://github.com/phoboslab/qoi/issues/258
int pixelArrayPosition = this.GetArrayPosition(previousPixel);
int pixelArrayPosition = GetArrayPosition(previousPixel);
previouslySeenPixels[pixelArrayPosition] = previousPixel;
for (int i = 0; i < this.header.Height; i++)
@ -159,11 +159,11 @@ internal class QoiDecoderCore : IImageDecoderInternals
byte operationByte = (byte)stream.ReadByte();
byte[] pixelBytes;
Rgba32 readPixel;
TPixel pixel = new();
TPixel pixel = default;
switch ((QoiChunkEnum)operationByte)
{
// Reading one pixel with previous alpha intact
case QoiChunkEnum.QOI_OP_RGB:
case QoiChunkEnum.QoiOpRgb:
pixelBytes = new byte[3];
if (stream.Read(pixelBytes) < 3)
{
@ -172,12 +172,12 @@ internal class QoiDecoderCore : IImageDecoderInternals
readPixel = previousPixel with { R = pixelBytes[0], G = pixelBytes[1], B = pixelBytes[2] };
pixel.FromRgba32(readPixel);
pixelArrayPosition = this.GetArrayPosition(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel;
break;
// Reading one pixel with new alpha
case QoiChunkEnum.QOI_OP_RGBA:
case QoiChunkEnum.QoiOpRgba:
pixelBytes = new byte[4];
if (stream.Read(pixelBytes) < 4)
{
@ -186,7 +186,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
readPixel = new Rgba32(pixelBytes[0], pixelBytes[1], pixelBytes[2], pixelBytes[3]);
pixel.FromRgba32(readPixel);
pixelArrayPosition = this.GetArrayPosition(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel;
break;
@ -194,13 +194,13 @@ internal class QoiDecoderCore : IImageDecoderInternals
switch ((QoiChunkEnum)(operationByte & 0b11000000))
{
// Getting one pixel from previously seen pixels
case QoiChunkEnum.QOI_OP_INDEX:
case QoiChunkEnum.QoiOpIndex:
readPixel = previouslySeenPixels[operationByte];
pixel.FromRgba32(readPixel);
break;
// Get one pixel from the difference (-2..1) of the previous pixel
case QoiChunkEnum.QOI_OP_DIFF:
case QoiChunkEnum.QoiOpDiff:
byte redDifference = (byte)((operationByte & 0b00110000) >> 4),
greenDifference = (byte)((operationByte & 0b00001100) >> 2),
blueDifference = (byte)(operationByte & 0b00000011);
@ -211,30 +211,30 @@ internal class QoiDecoderCore : IImageDecoderInternals
B = (byte)((previousPixel.B + (blueDifference - 2)) % 256)
};
pixel.FromRgba32(readPixel);
pixelArrayPosition = this.GetArrayPosition(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel;
break;
// Get green difference in 6 bits and red and blue differences
// depending on the green one
case QoiChunkEnum.QOI_OP_LUMA:
case QoiChunkEnum.QoiOpLuma:
byte diffGreen = (byte)(operationByte & 0b00111111),
currentGreen = (byte)((previousPixel.G + (diffGreen - 32)) % 256),
nextByte = (byte)stream.ReadByte(),
diffRedDG = (byte)(nextByte >> 4),
diffBlueDG = (byte)(nextByte & 0b00001111),
currentRed = (byte)((diffRedDG-8 + (diffGreen - 32) + previousPixel.R)%256),
currentBlue = (byte)((diffBlueDG-8 + (diffGreen - 32) + previousPixel.B)%256);
currentRed = (byte)((diffRedDG - 8 + (diffGreen - 32) + previousPixel.R) % 256),
currentBlue = (byte)((diffBlueDG - 8 + (diffGreen - 32) + previousPixel.B) % 256);
readPixel = previousPixel with { R = currentRed, B = currentBlue, G = currentGreen };
pixel.FromRgba32(readPixel);
pixelArrayPosition = this.GetArrayPosition(readPixel);
pixelArrayPosition = GetArrayPosition(readPixel);
previouslySeenPixels[pixelArrayPosition] = readPixel;
break;
// Repeating the previous pixel 1..63 times
case QoiChunkEnum.QOI_OP_RUN:
case QoiChunkEnum.QoiOpRun:
byte repetitions = (byte)(operationByte & 0b00111111);
if(repetitions is 62 or 63)
if (repetitions is 62 or 63)
{
ThrowInvalidImageContentException();
}
@ -248,7 +248,8 @@ internal class QoiDecoderCore : IImageDecoderInternals
j = 0;
i++;
}
pixels[j,i] = pixel;
pixels[j, i] = pixel;
}
j--;
@ -262,7 +263,7 @@ internal class QoiDecoderCore : IImageDecoderInternals
break;
}
pixels[j,i] = pixel;
pixels[j, i] = pixel;
previousPixel = readPixel;
}
}
@ -282,5 +283,5 @@ internal class QoiDecoderCore : IImageDecoderInternals
}
}
private int GetArrayPosition(Rgba32 pixel) => ((pixel.R * 3) + (pixel.G * 5) + (pixel.B * 7) + (pixel.A * 11)) % 64;
private static int GetArrayPosition(Rgba32 pixel) => ((pixel.R * 3) + (pixel.G * 5) + (pixel.B * 7) + (pixel.A * 11)) % 64;
}

4
src/ImageSharp/Formats/Qoi/QoiEncoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
namespace SixLabors.ImageSharp.Formats.Qoi;
/// <summary>
@ -13,7 +11,7 @@ public class QoiEncoder : ImageEncoder
/// <inheritdoc />
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
QoiEncoderCore encoder = new(image.GetConfiguration(), this);
QoiEncoderCore encoder = new();
encoder.Encode(image, stream, cancellationToken);
}
}

35
src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Runtime.InteropServices.ComTypes;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -13,25 +12,11 @@ namespace SixLabors.ImageSharp.Formats.Qoi;
/// </summary>
public class QoiEncoderCore : IImageEncoderInternals
{
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The encoder with options.
/// </summary>
private readonly QoiEncoder encoder;
/// <summary>
/// Initializes a new instance of the <see cref="QoiEncoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="encoder">The encoder with options.</param>
public QoiEncoderCore(Configuration configuration, QoiEncoder encoder)
public QoiEncoderCore()
{
this.configuration = configuration;
this.encoder = encoder;
}
/// <inheritdoc />
@ -67,7 +52,8 @@ public class QoiEncoderCore : IImageEncoderInternals
stream.WriteByte((byte)qoiColorSpace);
}
private static void WritePixels<TPixel>(Image<TPixel> image, Stream stream) where TPixel : unmanaged, IPixel<TPixel>
private static void WritePixels<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
// Start image encoding
Rgba32[] previouslySeenPixels = new Rgba32[64];
@ -76,7 +62,7 @@ public class QoiEncoderCore : IImageEncoderInternals
previouslySeenPixels[pixelArrayPosition] = previousPixel;
Buffer2D<TPixel> pixels = image.Frames[0].PixelBuffer;
Rgba32 currentRgba32 = new();
Rgba32 currentRgba32 = default;
for (int i = 0; i < pixels.Height; i++)
{
for (int j = 0; j < pixels.Width && i < pixels.Height; j++)
@ -115,10 +101,11 @@ public class QoiEncoderCore : IImageEncoderInternals
currentPixel = pixels[j, i];
currentPixel.ToRgba32(ref currentRgba32);
} while (currentRgba32.Equals(previousPixel) && repetitions < 62);
}
while (currentRgba32.Equals(previousPixel) && repetitions < 62);
j--;
stream.WriteByte((byte)((byte)QoiChunkEnum.QOI_OP_RUN | (repetitions - 1)));
stream.WriteByte((byte)((byte)QoiChunkEnum.QoiOpRun | (repetitions - 1)));
/* If it's a QOI_OP_RUN, we don't overwrite the previous pixel since
* it will be taken and compared on the next iteration
@ -153,7 +140,7 @@ public class QoiEncoderCore : IImageEncoderInternals
byte dr = (byte)(diffRed + 2),
dg = (byte)(diffGreen + 2),
db = (byte)(diffBlue + 2),
valueToWrite = (byte)((byte)QoiChunkEnum.QOI_OP_DIFF | (dr << 4) | (dg << 2) | db);
valueToWrite = (byte)((byte)QoiChunkEnum.QoiOpDiff | (dr << 4) | (dg << 2) | db);
stream.WriteByte(valueToWrite);
}
else
@ -169,7 +156,7 @@ public class QoiEncoderCore : IImageEncoderInternals
{
byte dr_dg = (byte)(diffRedGreen + 8),
db_dg = (byte)(diffBlueGreen + 8),
byteToWrite1 = (byte)((byte)QoiChunkEnum.QOI_OP_LUMA | (diffGreen + 32)),
byteToWrite1 = (byte)((byte)QoiChunkEnum.QoiOpLuma | (diffGreen + 32)),
byteToWrite2 = (byte)((dr_dg << 4) | db_dg);
stream.WriteByte(byteToWrite1);
stream.WriteByte(byteToWrite2);
@ -180,7 +167,7 @@ public class QoiEncoderCore : IImageEncoderInternals
// If so, we do a QOI_OP_RGB
if (currentRgba32.A == previousPixel.A)
{
stream.WriteByte((byte)QoiChunkEnum.QOI_OP_RGB);
stream.WriteByte((byte)QoiChunkEnum.QoiOpRgb);
stream.WriteByte(currentRgba32.R);
stream.WriteByte(currentRgba32.G);
stream.WriteByte(currentRgba32.B);
@ -188,7 +175,7 @@ public class QoiEncoderCore : IImageEncoderInternals
else
{
// else, we do a QOI_OP_RGBA
stream.WriteByte((byte)QoiChunkEnum.QOI_OP_RGBA);
stream.WriteByte((byte)QoiChunkEnum.QoiOpRgba);
stream.WriteByte(currentRgba32.R);
stream.WriteByte(currentRgba32.G);
stream.WriteByte(currentRgba32.B);

13
src/ImageSharp/Formats/Qoi/QoiFormat.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Png;
namespace SixLabors.ImageSharp.Formats.Qoi;
/// <summary>
@ -11,7 +9,8 @@ namespace SixLabors.ImageSharp.Formats.Qoi;
public sealed class QoiFormat : IImageFormat<QoiMetadata>
{
private QoiFormat()
{ }
{
}
/// <summary>
/// Gets the shared instance.
@ -19,17 +18,17 @@ public sealed class QoiFormat : IImageFormat<QoiMetadata>
public static QoiFormat Instance { get; } = new QoiFormat();
/// <inheritdoc/>
public QoiMetadata CreateDefaultFormatMetadata() => new();
public string DefaultMimeType => "image/qoi";
/// <inheritdoc/>
public string Name => "QOI";
/// <inheritdoc/>
public string DefaultMimeType => "image/qoi";
/// <inheritdoc/>
public IEnumerable<string> MimeTypes => QoiConstants.MimeTypes;
/// <inheritdoc/>
public IEnumerable<string> FileExtensions => QoiConstants.FileExtensions;
/// <inheritdoc/>
public QoiMetadata CreateDefaultFormatMetadata() => new();
}

10
src/ImageSharp/Formats/Qoi/QoiHeader.cs

@ -19,27 +19,27 @@ internal readonly struct QoiHeader
}
/// <summary>
/// Magic bytes "qoif"
/// Gets the magic bytes "qoif"
/// </summary>
public byte[] Magic { get; } = Encoding.UTF8.GetBytes("qoif");
/// <summary>
/// Image width in pixels (BE)
/// Gets the image width in pixels (Big Endian)
/// </summary>
public uint Width { get; }
/// <summary>
/// Image height in pixels (BE)
/// Gets the image height in pixels (Big Endian)
/// </summary>
public uint Height { get; }
/// <summary>
/// Color channels of the image. 3 = RGB, 4 = RGBA.
/// Gets the color channels of the image. 3 = RGB, 4 = RGBA.
/// </summary>
public QoiChannels Channels { get; }
/// <summary>
/// Color space of the image. 0 = sRGB with linear alpha, 1 = All channels linear
/// Gets the color space of the image. 0 = sRGB with linear alpha, 1 = All channels linear
/// </summary>
public QoiColorSpace ColorSpace { get; }
}

7
src/ImageSharp/Image{TPixel}.cs

@ -418,12 +418,7 @@ public sealed class Image<TPixel> : Image
{
Guard.NotNull(frames, nameof(frames));
ImageFrame<TPixel>? rootFrame = frames.FirstOrDefault();
if (rootFrame == null)
{
throw new ArgumentException("Must not be empty.", nameof(frames));
}
ImageFrame<TPixel>? rootFrame = frames.FirstOrDefault() ?? throw new ArgumentException("Must not be empty.", nameof(frames));
Size rootSize = rootFrame.Size();

4
tests/ImageSharp.Tests/Formats/Qoi/QoiEncoderTests.cs

@ -20,7 +20,9 @@ public class QoiEncoderTests
[WithFile(TestImages.Qoi.TestCard, PixelTypes.Rgba32)]
[WithFile(TestImages.Qoi.TestCardRGBA, PixelTypes.Rgba32)]
[WithFile(TestImages.Qoi.Wikipedia008, PixelTypes.Rgba32)]
private static void Encode<TPixel>(TestImageProvider<TPixel> provider) where TPixel : unmanaged, IPixel<TPixel>
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Quitar miembros privados no utilizados", Justification = "Function implicitly in tests")]
private static void Encode<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
using MemoryStream stream = new();

Loading…
Cancel
Save