//
// 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);
}
}
}
}