From 63d9bd40dd6c12ce1ffc69ee48735e39158cb1fc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 13 Sep 2016 23:02:11 +1000 Subject: [PATCH] Extract jpeg component classes [skip ci] Former-commit-id: 89550ed1243331a85c00f315dc8cd4c06b14ae9c Former-commit-id: 8d15dd77d84254cb59154322736904f05a434875 Former-commit-id: 219c0fd3b6925bde6cac1323b17bad9a857ec56d --- .../Colors/Colorspaces/YCbCr.cs | 2 +- .../Formats/Jpg/Components/Bits.cs | 31 +++ .../Formats/Jpg/{ => Components}/Block.cs | 0 .../Formats/Jpg/Components/Bytes.cs | 43 ++++ .../Formats/Jpg/Components/Component.cs | 33 +++ .../Formats/Jpg/{ => Components}/FDCT.cs | 0 .../Formats/Jpg/Components/GrayImage.cs | 101 ++++++++ .../Formats/Jpg/Components/Huffman.cs | 64 +++++ .../Formats/Jpg/{ => Components}/IDCT.cs | 0 .../Formats/Jpg/Components/YCbCrImage.cs | 228 ++++++++++++++++++ .../Jpg/JpegDecoderCore.cs.REMOVED.git-id | 2 +- 11 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/Bits.cs rename src/ImageProcessorCore/Formats/Jpg/{ => Components}/Block.cs (100%) create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/Bytes.cs create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/Component.cs rename src/ImageProcessorCore/Formats/Jpg/{ => Components}/FDCT.cs (100%) create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/GrayImage.cs create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/Huffman.cs rename src/ImageProcessorCore/Formats/Jpg/{ => Components}/IDCT.cs (100%) create mode 100644 src/ImageProcessorCore/Formats/Jpg/Components/YCbCrImage.cs diff --git a/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs b/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs index 64560d7d0..a801582f7 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/YCbCr.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore using System.Numerics; /// - /// Represents an YCbCr (luminance, chroma, chroma) color conforming to the + /// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the /// Full range standard used in digital imaging systems. /// /// diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/Bits.cs b/src/ImageProcessorCore/Formats/Jpg/Components/Bits.cs new file mode 100644 index 000000000..615a8f897 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/Bits.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Holds the unprocessed bits that have been taken from the byte-stream. + /// The n least significant bits of a form the unread bits, to be read in MSB to + /// LSB order. + /// + internal class Bits + { + /// + /// Gets or sets the accumulator. + /// + public uint Accumulator { get; set; } + + /// + /// Gets or sets the mask. + /// 0, with mask==0 when unreadbits==0.]]> + /// + public uint Mask { get; set; } + + /// + /// Gets or sets the number of unread bits in the accumulator. + /// + public int UnreadBits { get; set; } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/Block.cs b/src/ImageProcessorCore/Formats/Jpg/Components/Block.cs similarity index 100% rename from src/ImageProcessorCore/Formats/Jpg/Block.cs rename to src/ImageProcessorCore/Formats/Jpg/Components/Block.cs diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/Bytes.cs b/src/ImageProcessorCore/Formats/Jpg/Components/Bytes.cs new file mode 100644 index 000000000..8467d3b9f --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/Bytes.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Bytes is a byte buffer, similar to a stream, except that it + /// has to be able to unread more than 1 byte, due to byte stuffing. + /// Byte stuffing is specified in section F.1.2.3. + /// + internal class Bytes + { + /// + /// Initializes a new instance of the class. + /// + public Bytes() + { + this.Buffer = new byte[4096]; + this.I = 0; + this.J = 0; + this.UnreadableBytes = 0; + } + + /// + /// Gets or sets the buffer. + /// buffer[i:j] are the buffered bytes read from the underlying + /// stream that haven't yet been passed further on. + /// + public byte[] Buffer { get; set; } + + public int I { get; set; } + + public int J { get; set; } + + /// + /// Gets or sets the unreadable bytes. The number of bytes to back up i after + /// overshooting. It can be 0, 1 or 2. + /// + public int UnreadableBytes { get; set; } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/Component.cs b/src/ImageProcessorCore/Formats/Jpg/Components/Component.cs new file mode 100644 index 000000000..a4131af6e --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/Component.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Represents a single color component + /// + internal class Component + { + /// + /// Gets or sets the horizontal sampling factor. + /// + public int HorizontalFactor { get; set; } + + /// + /// Gets or sets the vertical sampling factor. + /// + public int VerticalFactor { get; set; } + + /// + /// Gets or sets the identifier + /// + public byte Identifier { get; set; } + + /// + /// Gets or sets the quantization table destination selector. + /// + public byte Selector { get; set; } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/FDCT.cs b/src/ImageProcessorCore/Formats/Jpg/Components/FDCT.cs similarity index 100% rename from src/ImageProcessorCore/Formats/Jpg/FDCT.cs rename to src/ImageProcessorCore/Formats/Jpg/Components/FDCT.cs diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/GrayImage.cs b/src/ImageProcessorCore/Formats/Jpg/Components/GrayImage.cs new file mode 100644 index 000000000..b511b2694 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/GrayImage.cs @@ -0,0 +1,101 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Represents a grayscale image + /// + internal class GrayImage + { + /// + /// Initializes a new instance of the class. + /// + /// The width. + /// The height. + public GrayImage(int width, int height) + { + this.Width = width; + this.Height = height; + this.Pixels = new byte[width * height]; + this.Stride = width; + this.Offset = 0; + } + + /// + /// Prevents a default instance of the class from being created. + /// + private GrayImage() + { + } + + /// + /// Gets or sets the pixels. + /// + public byte[] Pixels { get; set; } + + /// + /// Gets or sets the stride. + /// + public int Stride { get; set; } + + /// + /// Gets or sets the horizontal position. + /// + public int X { get; set; } + + /// + /// Gets or sets the vertical position. + /// + public int Y { get; set; } + + /// + /// Gets or sets the width. + /// + public int Width { get; set; } + + /// + /// Gets or sets the height. + /// + public int Height { get; set; } + + /// + /// Gets or sets the offset + /// + public int Offset { get; set; } + + /// + /// Gets an image made up of a subset of the originals pixels. + /// + /// The x-coordinate of the image. + /// The y-coordinate of the image. + /// The width. + /// The height. + /// + /// The . + /// + public GrayImage Subimage(int x, int y, int width, int height) + { + return new GrayImage + { + Width = width, + Height = height, + Pixels = this.Pixels, + Stride = this.Stride, + Offset = (y * this.Stride) + x + }; + } + + /// + /// Gets the row offset at the given position + /// + /// The y-coordinate of the image. + /// The + public int GetRowOffset(int y) + { + return this.Offset + (y * this.Stride); + } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/Huffman.cs b/src/ImageProcessorCore/Formats/Jpg/Components/Huffman.cs new file mode 100644 index 000000000..7a7083202 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/Huffman.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Represents a Huffman tree + /// + internal class Huffman + { + /// + /// Initializes a new instance of the class. + /// + /// The log-2 size of the Huffman decoder's look-up table. + /// The maximum (inclusive) number of codes in a Huffman tree. + /// The maximum (inclusive) number of bits in a Huffman code. + public Huffman(int lutSize, int maxNCodes, int maxCodeLength) + { + this.Lut = new ushort[1 << lutSize]; + this.Values = new byte[maxNCodes]; + this.MinCodes = new int[maxCodeLength]; + this.MaxCodes = new int[maxCodeLength]; + this.Indices = new int[maxCodeLength]; + this.Length = 0; + } + + /// + /// Gets or sets the number of codes in the tree. + /// + public int Length { get; set; } + + /// + /// Gets the look-up table for the next LutSize bits in the bit-stream. + /// The high 8 bits of the uint16 are the encoded value. The low 8 bits + /// are 1 plus the code length, or 0 if the value is too large to fit in + /// lutSize bits. + /// + public ushort[] Lut { get; } + + /// + /// Gets the the decoded values, sorted by their encoding. + /// + public byte[] Values { get; } + + /// + /// Gets the array of minimum codes. + /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. + /// + public int[] MinCodes { get; } + + /// + /// Gets the array of maximum codes. + /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. + /// + public int[] MaxCodes { get; } + + /// + /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. + /// + public int[] Indices { get; } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/IDCT.cs b/src/ImageProcessorCore/Formats/Jpg/Components/IDCT.cs similarity index 100% rename from src/ImageProcessorCore/Formats/Jpg/IDCT.cs rename to src/ImageProcessorCore/Formats/Jpg/Components/IDCT.cs diff --git a/src/ImageProcessorCore/Formats/Jpg/Components/YCbCrImage.cs b/src/ImageProcessorCore/Formats/Jpg/Components/YCbCrImage.cs new file mode 100644 index 000000000..ea7ab15f7 --- /dev/null +++ b/src/ImageProcessorCore/Formats/Jpg/Components/YCbCrImage.cs @@ -0,0 +1,228 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Formats +{ + /// + /// Represents an image made up of three color components (luminance, blue chroma, red chroma) + /// + internal class YCbCrImage + { + /// + /// Initializes a new instance of the class. + /// + /// The width. + /// The height. + /// The ratio. + public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio) + { + int cw, ch; + YCbCrSize(width, height, ratio, out cw, out ch); + this.YChannel = new byte[width * height]; + this.CbChannel = new byte[cw * ch]; + this.CrChannel = new byte[cw * ch]; + this.Ratio = ratio; + this.YStride = width; + this.CStride = cw; + this.X = 0; + this.Y = 0; + this.Width = width; + this.Height = height; + } + + /// + /// Prevents a default instance of the class from being created. + /// + private YCbCrImage() + { + } + + /// + /// Provides enumeration of the various available subsample ratios. + /// + public enum YCbCrSubsampleRatio + { + YCbCrSubsampleRatio444, + + YCbCrSubsampleRatio422, + + YCbCrSubsampleRatio420, + + YCbCrSubsampleRatio440, + + YCbCrSubsampleRatio411, + + YCbCrSubsampleRatio410, + } + + /// + /// Gets or sets the luminance components channel. + /// + public byte[] YChannel { get; set; } + + /// + /// Gets or sets the blue chroma components channel. + /// + public byte[] CbChannel { get; set; } + + /// + /// Gets or sets the red chroma components channel. + /// + public byte[] CrChannel { get; set; } + + /// + /// Gets or sets the Y slice index delta between vertically adjacent pixels. + /// + public int YStride { get; set; } + + /// + /// Gets or sets the red and blue chroma slice index delta between vertically adjacent pixels + /// that map to separate chroma samples. + /// + public int CStride { get; set; } + + /// + /// Gets or sets the index of the first luminance element. + /// + public int YOffset { get; set; } + + /// + /// Gets or sets the index of the first element of red or blue chroma. + /// + public int COffset { get; set; } + + /// + /// Gets or sets the horizontal position. + /// + public int X { get; set; } + + /// + /// Gets or sets the vertical position. + /// + public int Y { get; set; } + + /// + /// Gets or sets the width. + /// + public int Width { get; set; } + + /// + /// Gets or sets the height. + /// + public int Height { get; set; } + + /// + /// Gets or sets the subsampling ratio. + /// + public YCbCrSubsampleRatio Ratio { get; set; } + + /// + /// Gets an image made up of a subset of the originals pixels. + /// + /// The x-coordinate of the image. + /// The y-coordinate of the image. + /// The width. + /// The height. + /// + /// The . + /// + public YCbCrImage Subimage(int x, int y, int width, int height) + { + YCbCrImage ret = new YCbCrImage + { + Width = width, + Height = height, + YChannel = this.YChannel, + CbChannel = this.CbChannel, + CrChannel = this.CrChannel, + Ratio = this.Ratio, + YStride = this.YStride, + CStride = this.CStride, + YOffset = (y * this.YStride) + x, + COffset = (y * this.CStride) + x + }; + return ret; + } + + /// + /// Returns the offset of the first luminance component at the given row + /// + /// The row number. + /// + /// The . + /// + public int GetRowYOffset(int y) + { + return y * this.YStride; + } + + /// + /// Returns the offset of the first chroma component at the given row + /// + /// The row number. + /// + /// The . + /// + public int GetRowCOffset(int y) + { + switch (this.Ratio) + { + case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: + return y * this.CStride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: + return (y / 2) * this.CStride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: + return (y / 2) * this.CStride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: + return y * this.CStride; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: + return (y / 2) * this.CStride; + default: + return y * this.CStride; + } + } + + /// + /// Returns the height and width of the chroma components + /// + /// The width. + /// The height. + /// The subsampling ratio. + /// The chroma width. + /// The chroma height. + private static void YCbCrSize(int width, int height, YCbCrSubsampleRatio ratio, out int chromaWidth, out int chromaHeight) + { + switch (ratio) + { + case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: + chromaWidth = (width + 1) / 2; + chromaHeight = height; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: + chromaWidth = (width + 1) / 2; + chromaHeight = (height + 1) / 2; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: + chromaWidth = width; + chromaHeight = (height + 1) / 2; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: + chromaWidth = (width + 3) / 4; + chromaHeight = height; + break; + case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: + chromaWidth = (width + 3) / 4; + chromaHeight = (height + 1) / 2; + break; + default: + + // Default to 4:4:4 subsampling. + chromaWidth = width; + chromaHeight = height; + break; + } + } + } +} diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id index 922e9cccc..f10210ac0 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id +++ b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id @@ -1 +1 @@ -efdc36c9d84d1fd42bf3b1e722946bccef95db1c \ No newline at end of file +8ce31914f581080b90c50b6a6529db20a29e7551 \ No newline at end of file