// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Formats.Jpg { using System; using System.Buffers; /// /// Represents an image made up of three color components (luminance, blue chroma, red chroma) /// internal class YCbCrImage : IDisposable { // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P #pragma warning disable SA1401 // FieldsMustBePrivate /// /// Gets the luminance components channel as . /// public JpegPixelArea YChannel; /// /// Gets the blue chroma components channel as . /// public JpegPixelArea CbChannel; /// /// Gets an offseted to the Cr channel /// public JpegPixelArea CrChannel; #pragma warning restore SA1401 /// /// Initializes a new instance of the class. /// /// The width. /// The height. /// The ratio. public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio) { Size cSize = CalculateChrominanceSize(width, height, ratio); this.Ratio = ratio; this.YStride = width; this.CStride = cSize.Width; this.YChannel = JpegPixelArea.CreatePooled(width, height); this.CbChannel = JpegPixelArea.CreatePooled(cSize.Width, cSize.Height); this.CrChannel = JpegPixelArea.CreatePooled(cSize.Width, cSize.Height); } /// /// Provides enumeration of the various available subsample ratios. /// public enum YCbCrSubsampleRatio { /// /// YCbCrSubsampleRatio444 /// YCbCrSubsampleRatio444, /// /// YCbCrSubsampleRatio422 /// YCbCrSubsampleRatio422, /// /// YCbCrSubsampleRatio420 /// YCbCrSubsampleRatio420, /// /// YCbCrSubsampleRatio440 /// YCbCrSubsampleRatio440, /// /// YCbCrSubsampleRatio411 /// YCbCrSubsampleRatio411, /// /// YCbCrSubsampleRatio410 /// YCbCrSubsampleRatio410, } /// /// Gets the Y slice index delta between vertically adjacent pixels. /// public int YStride { get; } /// /// Gets the red and blue chroma slice index delta between vertically adjacent pixels /// that map to separate chroma samples. /// public int CStride { get; } /// /// Gets or sets the subsampling ratio. /// public YCbCrSubsampleRatio Ratio { get; set; } /// /// Disposes the returning rented arrays to the pools. /// public void Dispose() { this.YChannel.ReturnPooled(); this.CbChannel.ReturnPooled(); this.CrChannel.ReturnPooled(); } /// /// 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 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 height and width of the chroma components /// /// The width. /// The height. /// The subsampling ratio. /// The of the chrominance channel internal static Size CalculateChrominanceSize( int width, int height, YCbCrSubsampleRatio ratio) { switch (ratio) { case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: return new Size((width + 1) / 2, height); case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: return new Size((width + 1) / 2, (height + 1) / 2); case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: return new Size(width, (height + 1) / 2); case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: return new Size((width + 3) / 4, height); case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: return new Size((width + 3) / 4, (height + 1) / 2); default: // Default to 4:4:4 subsampling. return new Size(width, height); } } } }