Browse Source

fixed PixelArea pooling + added reusing of PixelArea<TColor>-s in JpegEncoderCore

af/merge-core
Anton Firszov 10 years ago
parent
commit
2999ef1573
  1. 166
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  2. 39
      src/ImageSharp/Image/PixelArea{TColor}.cs

166
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -395,45 +395,45 @@ namespace ImageSharp.Formats
/// <param name="yBlock">The luminance block.</param> /// <param name="yBlock">The luminance block.</param>
/// <param name="cbBlock">The red chroma block.</param> /// <param name="cbBlock">The red chroma block.</param>
/// <param name="crBlock">The blue chroma block.</param> /// <param name="crBlock">The blue chroma block.</param>
/// <param name="rgbBytes">Temporal <see cref="PixelArea{TColor}"/> provided by the caller</param>
private static void ToYCbCr<TColor>( private static void ToYCbCr<TColor>(
PixelAccessor<TColor> pixels, PixelAccessor<TColor> pixels,
int x, int x,
int y, int y,
Block8x8F* yBlock, Block8x8F* yBlock,
Block8x8F* cbBlock, Block8x8F* cbBlock,
Block8x8F* crBlock) Block8x8F* crBlock,
PixelArea<TColor> rgbBytes)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
float* yBlockRaw = (float*)yBlock; float* yBlockRaw = (float*)yBlock;
float* cbBlockRaw = (float*)cbBlock; float* cbBlockRaw = (float*)cbBlock;
float* crBlockRaw = (float*)crBlock; float* crBlockRaw = (float*)crBlock;
using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ)) rgbBytes.Reset();
{ pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x);
pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x);
byte* data = (byte*)rgbBytes.DataPointer; byte* data = (byte*)rgbBytes.DataPointer;
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{
int j8 = j * 8;
for (int i = 0; i < 8; i++)
{ {
int j8 = j * 8; Vector3 v = new Vector3(data[0], data[1], data[2]);
for (int i = 0; i < 8; i++)
{
Vector3 v = new Vector3(data[0], data[1], data[2]);
// Convert returned bytes into the YCbCr color space. Assume RGBA // Convert returned bytes into the YCbCr color space. Assume RGBA
float yy = (0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z); float yy = (0.299F * v.X) + (0.587F * v.Y) + (0.114F * v.Z);
float cb = 128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z)); float cb = 128 + ((-0.168736F * v.X) - (0.331264F * v.Y) + (0.5F * v.Z));
float cr = 128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z)); float cr = 128 + ((0.5F * v.X) - (0.418688F * v.Y) - (0.081312F * v.Z));
int index = j8 + i; int index = j8 + i;
yBlockRaw[index] = yy; yBlockRaw[index] = yy;
cbBlockRaw[index] = cb; cbBlockRaw[index] = cb;
crBlockRaw[index] = cr; crBlockRaw[index] = cr;
data += 3; data += 3;
}
} }
} }
} }
@ -844,6 +844,7 @@ namespace ImageSharp.Formats
private void Encode444<TColor>(PixelAccessor<TColor> pixels) private void Encode444<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
// TODO: Need a JpegEncoderCoreCore<TColor> class or struct to hold all this mess:
Block8x8F b = default(Block8x8F); Block8x8F b = default(Block8x8F);
Block8x8F cb = default(Block8x8F); Block8x8F cb = default(Block8x8F);
Block8x8F cr = default(Block8x8F); Block8x8F cr = default(Block8x8F);
@ -859,36 +860,39 @@ namespace ImageSharp.Formats
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
float prevDCY = 0, prevDCCb = 0, prevDCCr = 0; float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
for (int y = 0; y < pixels.Height; y += 8) using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ, true))
{ {
for (int x = 0; x < pixels.Width; x += 8) for (int y = 0; y < pixels.Height; y += 8)
{ {
ToYCbCr(pixels, x, y, &b, &cb, &cr); for (int x = 0; x < pixels.Width; x += 8)
{
prevDCY = this.WriteBlock( ToYCbCr(pixels, x, y, &b, &cb, &cr, rgbBytes);
QuantIndex.Luminance,
prevDCY, prevDCY = this.WriteBlock(
&b, QuantIndex.Luminance,
&temp1, prevDCY,
&temp2, &b,
&onStackLuminanceQuantTable, &temp1,
unzig.Data); &temp2,
prevDCCb = this.WriteBlock( &onStackLuminanceQuantTable,
QuantIndex.Chrominance, unzig.Data);
prevDCCb, prevDCCb = this.WriteBlock(
&cb, QuantIndex.Chrominance,
&temp1, prevDCCb,
&temp2, &cb,
&onStackChrominanceQuantTable, &temp1,
unzig.Data); &temp2,
prevDCCr = this.WriteBlock( &onStackChrominanceQuantTable,
QuantIndex.Chrominance, unzig.Data);
prevDCCr, prevDCCr = this.WriteBlock(
&cr, QuantIndex.Chrominance,
&temp1, prevDCCr,
&temp2, &cr,
&onStackChrominanceQuantTable, &temp1,
unzig.Data); &temp2,
&onStackChrominanceQuantTable,
unzig.Data);
}
} }
} }
} }
@ -912,6 +916,7 @@ namespace ImageSharp.Formats
private void Encode420<TColor>(PixelAccessor<TColor> pixels) private void Encode420<TColor>(PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor> where TColor : struct, IPackedPixel, IEquatable<TColor>
{ {
// TODO: Need a JpegEncoderCoreCore<TColor> class or struct to hold all this mess:
Block8x8F b = default(Block8x8F); Block8x8F b = default(Block8x8F);
BlockQuad cb = default(BlockQuad); BlockQuad cb = default(BlockQuad);
@ -930,45 +935,48 @@ namespace ImageSharp.Formats
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
float prevDCY = 0, prevDCCb = 0, prevDCCr = 0; float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
for (int y = 0; y < pixels.Height; y += 16) using (var rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ, true))
{ {
for (int x = 0; x < pixels.Width; x += 16) for (int y = 0; y < pixels.Height; y += 16)
{ {
for (int i = 0; i < 4; i++) for (int x = 0; x < pixels.Width; x += 16)
{ {
int xOff = (i & 1) * 8; for (int i = 0; i < 4; i++)
int yOff = (i & 2) * 4; {
int xOff = (i & 1) * 8;
ToYCbCr(pixels, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i); int yOff = (i & 2) * 4;
prevDCY = this.WriteBlock( ToYCbCr(pixels, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i, rgbBytes);
QuantIndex.Luminance,
prevDCY, prevDCY = this.WriteBlock(
QuantIndex.Luminance,
prevDCY,
&b,
&temp1,
&temp2,
&onStackLuminanceQuantTable,
unzig.Data);
}
Block8x8F.Scale16X16To8X8(&b, cbPtr);
prevDCCb = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCb,
&b, &b,
&temp1, &temp1,
&temp2, &temp2,
&onStackLuminanceQuantTable, &onStackChrominanceQuantTable,
unzig.Data);
Block8x8F.Scale16X16To8X8(&b, crPtr);
prevDCCr = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCr,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data); unzig.Data);
} }
Block8x8F.Scale16X16To8X8(&b, cbPtr);
prevDCCb = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCb,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
Block8x8F.Scale16X16To8X8(&b, crPtr);
prevDCCr = this.WriteBlock(
QuantIndex.Chrominance,
prevDCCr,
&b,
&temp1,
&temp2,
&onStackChrominanceQuantTable,
unzig.Data);
} }
} }
} }

39
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -28,6 +28,11 @@ namespace ImageSharp
/// </summary> /// </summary>
private IntPtr dataPointer; private IntPtr dataPointer;
/// <summary>
/// True if <see cref="Bytes"/> was rented from <see cref="BytesPool"/> by the constructor
/// </summary>
private bool isBufferRented;
/// <summary> /// <summary>
/// A value indicating whether this instance of the given entity has been disposed. /// A value indicating whether this instance of the given entity has been disposed.
/// </summary> /// </summary>
@ -39,11 +44,6 @@ namespace ImageSharp
/// </remarks> /// </remarks>
private bool isDisposed; private bool isDisposed;
/// <summary>
/// True if <see cref="Bytes"/> was allocated by the constructor (rented from <see cref="BytesPool"/>)
/// </summary>
private bool isBufferOwner;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
/// </summary> /// </summary>
@ -90,8 +90,9 @@ namespace ImageSharp
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param> /// <param name="componentOrder">The component order.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder) /// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
: this(width, height, componentOrder, 0) public PixelArea(int width, int height, ComponentOrder componentOrder, bool usePool = false)
: this(width, height, componentOrder, 0, usePool)
{ {
} }
@ -123,14 +124,27 @@ namespace ImageSharp
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param> /// <param name="componentOrder">The component order.</param>
/// <param name="padding">The number of bytes to pad each row.</param> /// <param name="padding">The number of bytes to pad each row.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder, int padding) /// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
public PixelArea(int width, int height, ComponentOrder componentOrder, int padding, bool usePool = false)
{ {
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.ComponentOrder = componentOrder; this.ComponentOrder = componentOrder;
this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding; this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding;
this.Bytes = BytesPool.Rent(this.RowByteCount * height);
this.isBufferOwner = true; var bufferSize = this.RowByteCount * height;
if (usePool)
{
this.Bytes = BytesPool.Rent(bufferSize);
this.isBufferRented = true;
Array.Clear(this.Bytes, 0, bufferSize);
}
else
{
this.Bytes = new byte[bufferSize];
}
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
// TODO: Why is Resharper warning us about an impure method call? // TODO: Why is Resharper warning us about an impure method call?
@ -184,6 +198,7 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Gets the pool used to rent <see cref="Bytes"/>, when it's not coming from an external source /// Gets the pool used to rent <see cref="Bytes"/>, when it's not coming from an external source
/// </summary> /// </summary>
// ReSharper disable once StaticMemberInGenericType
private static ArrayPool<byte> BytesPool => ArrayPool<byte>.Shared; private static ArrayPool<byte> BytesPool => ArrayPool<byte>.Shared;
/// <summary> /// <summary>
@ -217,7 +232,7 @@ namespace ImageSharp
/// </summary> /// </summary>
internal void Reset() internal void Reset()
{ {
Unsafe.InitBlock(this.PixelBase, 0, (uint)this.Bytes.Length); Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowByteCount * this.Height));
} }
/// <summary> /// <summary>
@ -274,7 +289,7 @@ namespace ImageSharp
this.pixelsHandle.Free(); this.pixelsHandle.Free();
} }
if (disposing && this.isBufferOwner) if (disposing && this.isBufferRented)
{ {
BytesPool.Return(this.Bytes); BytesPool.Return(this.Bytes);
} }

Loading…
Cancel
Save