Browse Source

Changed PixelRow into a PixelArea.

af/merge-core
Dirk Lemstra 10 years ago
parent
commit
e23d8dfe2c
  1. 6
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 4
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  3. 4
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  4. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  5. 243
      src/ImageSharp/Image/PixelAccessor.cs
  6. 89
      src/ImageSharp/Image/PixelArea.cs
  7. 122
      src/ImageSharp/PixelAccessor.cs
  8. 8
      tests/ImageSharp.Tests/Image/PixelAccessorTests.cs

6
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -281,7 +281,7 @@ namespace ImageSharp.Formats
const int ComponentCount = 2; const int ComponentCount = 2;
TColor color = default(TColor); TColor color = default(TColor);
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(width, ComponentOrder.XYZ)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(width, ComponentOrder.XYZ))
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -320,7 +320,7 @@ namespace ImageSharp.Formats
where TPacked : struct where TPacked : struct
{ {
int padding = CalculatePadding(width, 3); int padding = CalculatePadding(width, 3);
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(width, ComponentOrder.ZYX, padding)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(width, ComponentOrder.ZYX, padding))
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -346,7 +346,7 @@ namespace ImageSharp.Formats
where TPacked : struct where TPacked : struct
{ {
int padding = CalculatePadding(width, 4); int padding = CalculatePadding(width, 4);
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(width, ComponentOrder.ZYXW, padding)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(width, ComponentOrder.ZYXW, padding))
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {

4
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -155,7 +155,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(pixels.Width, ComponentOrder.ZYXW, this.padding)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(pixels.Width, ComponentOrder.ZYXW, this.padding))
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {
@ -176,7 +176,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(pixels.Width, ComponentOrder.ZYX, this.padding)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(pixels.Width, ComponentOrder.ZYX, this.padding))
{ {
for (int y = pixels.Height - 1; y >= 0; y--) for (int y = pixels.Height - 1; y >= 0; y--)
{ {

4
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -339,7 +339,7 @@ namespace ImageSharp.Formats
using (PixelAccessor<TColor, TPacked> pixelAccessor = image.Lock()) using (PixelAccessor<TColor, TPacked> pixelAccessor = image.Lock())
{ {
using (PixelRow<TColor, TPacked> pixelRow = new PixelRow<TColor, TPacked>(imageWidth, ComponentOrder.XYZW)) using (PixelArea<TColor, TPacked> pixelRow = new PixelArea<TColor, TPacked>(imageWidth, ComponentOrder.XYZW))
{ {
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
{ {
@ -445,7 +445,7 @@ namespace ImageSharp.Formats
} }
else else
{ {
using (PixelRow<TColor, TPacked> emptyRow = new PixelRow<TColor, TPacked>(this.restoreArea.Value.Width, ComponentOrder.XYZW)) using (PixelArea<TColor, TPacked> emptyRow = new PixelArea<TColor, TPacked>(this.restoreArea.Value.Width, ComponentOrder.XYZW))
{ {
using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock()) using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock())
{ {

2
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -322,7 +322,7 @@ namespace ImageSharp.Formats
where TPacked : struct where TPacked : struct
{ {
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory. // We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
using (PixelRow<TColor, TPacked> pixelRow = new PixelRow<TColor, TPacked>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ)) using (PixelArea<TColor, TPacked> pixelRow = new PixelArea<TColor, TPacked>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ))
{ {
pixels.CopyTo(pixelRow, row); pixels.CopyTo(pixelRow, row);
} }

243
src/ImageSharp/Image/PixelAccessor.cs

@ -177,27 +177,32 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Copied a row of pixels from the image. /// Copied a row of pixels from the image.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param> /// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param> /// <param name="targetX">The target column index.</param>
/// <exception cref="NotSupportedException"> /// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed. /// Thrown when an unsupported component order value is passed.
/// </exception> /// </exception>
public void CopyFrom(PixelRow<TColor, TPacked> row, int targetY, int targetX = 0) public void CopyFrom(PixelArea<TColor, TPacked> area, int targetY, int targetX = 0)
{ {
switch (row.ComponentOrder) int width = Math.Min(area.Width, this.Width - targetX);
int height = Math.Min(area.Height, this.Height - targetY);
CheckDimensions(width, height);
switch (area.ComponentOrder)
{ {
case ComponentOrder.ZYX: case ComponentOrder.ZYX:
this.CopyFromZYX(row, targetY, targetX, Math.Min(row.Width, this.Width)); this.CopyFromZYX(area, targetY, targetX, width, height);
break; break;
case ComponentOrder.ZYXW: case ComponentOrder.ZYXW:
this.CopyFromZYXW(row, targetY, targetX, Math.Min(row.Width, this.Width)); this.CopyFromZYXW(area, targetY, targetX, width, height);
break; break;
case ComponentOrder.XYZ: case ComponentOrder.XYZ:
this.CopyFromXYZ(row, targetY, targetX, Math.Min(row.Width, this.Width)); this.CopyFromXYZ(area, targetY, targetX, width, height);
break; break;
case ComponentOrder.XYZW: case ComponentOrder.XYZW:
this.CopyFromXYZW(row, targetY, targetX, Math.Min(row.Width, this.Width)); this.CopyFromXYZW(area, targetY, targetX, width, height);
break; break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
@ -205,28 +210,34 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Copied a row of pixels to the image. /// Copied an area of pixels to the image.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="sourceY">The source row index.</param> /// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <exception cref="NotSupportedException"> /// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed. /// Thrown when an unsupported component order value is passed.
/// </exception> /// </exception>
public void CopyTo(PixelRow<TColor, TPacked> row, int sourceY) public void CopyTo(PixelArea<TColor, TPacked> area, int sourceY, int sourceX = 0)
{ {
switch (row.ComponentOrder) int width = Math.Min(area.Width, this.Width - sourceX);
int height = Math.Min(area.Height, this.Height - sourceY);
CheckDimensions(width, height);
switch (area.ComponentOrder)
{ {
case ComponentOrder.ZYX: case ComponentOrder.ZYX:
this.CopyToZYX(row, sourceY, Math.Min(row.Width, this.Width)); this.CopyToZYX(area, sourceY, sourceX, width, height);
break; break;
case ComponentOrder.ZYXW: case ComponentOrder.ZYXW:
this.CopyToZYXW(row, sourceY, Math.Min(row.Width, this.Width)); this.CopyToZYXW(area, sourceY, sourceX, width, height);
break; break;
case ComponentOrder.XYZ: case ComponentOrder.XYZ:
this.CopyToXYZ(row, sourceY, Math.Min(row.Width, this.Width)); this.CopyToXYZ(area, sourceY, sourceX, width, height);
break; break;
case ComponentOrder.XYZW: case ComponentOrder.XYZW:
this.CopyToXYZW(row, sourceY, Math.Min(row.Width, this.Width)); this.CopyToXYZW(area, sourceY, sourceX, width, height);
break; break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
@ -271,166 +282,202 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Copies from a row in <see cref="ComponentOrder.ZYX"/> format. /// Copies from an area in <see cref="ComponentOrder.ZYX"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param> /// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param> /// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyFromZYX(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width) /// <param name="height">The height.</param>
protected virtual void CopyFromZYX(PixelArea<TColor, TPacked> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor); TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>(); int size = Unsafe.SizeOf<TColor>();
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++)
{ {
packed.PackFromBytes(*(source + 2), *(source + 1), *source, 255); byte* source = area.PixelBase + (y * area.RowByteCount);
Unsafe.Write(destination, packed); byte* destination = this.GetRowPointer(targetY + y) + targetX;
for (int x = 0; x < width; x++)
{
packed.PackFromBytes(*(source + 2), *(source + 1), *source, 255);
Unsafe.Write(destination, packed);
source += 3; source += 3;
destination += size; destination += size;
}
} }
} }
/// <summary> /// <summary>
/// Copies from a row in <see cref="ComponentOrder.ZYXW"/> format. /// Copies from an area in <see cref="ComponentOrder.ZYXW"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param> /// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param> /// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyFromZYXW(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width) /// <param name="height">The height.</param>
protected virtual void CopyFromZYXW(PixelArea<TColor, TPacked> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor); TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>(); int size = Unsafe.SizeOf<TColor>();
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++)
{ {
packed.PackFromBytes(*(source + 2), *(source + 1), *source, *(source + 3)); byte* source = area.PixelBase + (y * area.RowByteCount);
Unsafe.Write(destination, packed); byte* destination = this.GetRowPointer(targetY + y) + targetX;
source += 4; for (int x = 0; x < width; x++)
destination += size; {
packed.PackFromBytes(*(source + 2), *(source + 1), *source, *(source + 3));
Unsafe.Write(destination, packed);
source += 4;
destination += size;
}
} }
} }
/// <summary> /// <summary>
/// Copies from a row in <see cref="ComponentOrder.XYZ"/> format. /// Copies from an area in <see cref="ComponentOrder.XYZ"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param> /// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param> /// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyFromXYZ(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width) /// <param name="height">The height.</param>
protected virtual void CopyFromXYZ(PixelArea<TColor, TPacked> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor); TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>(); int size = Unsafe.SizeOf<TColor>();
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++)
{ {
packed.PackFromBytes(*source, *(source + 1), *(source + 2), 255); byte* source = area.PixelBase + (y * area.RowByteCount);
Unsafe.Write(destination, packed); byte* destination = this.GetRowPointer(targetY + y) + targetX;
for (int x = 0; x < width; x++)
{
packed.PackFromBytes(*source, *(source + 1), *(source + 2), 255);
Unsafe.Write(destination, packed);
source += 3; source += 3;
destination += size; destination += size;
}
} }
} }
/// <summary> /// <summary>
/// Copies from a row in <see cref="ComponentOrder.XYZW"/> format. /// Copies from an area in <see cref="ComponentOrder.XYZW"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param> /// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param> /// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyFromXYZW(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width) /// <param name="height">The height.</param>
protected virtual void CopyFromXYZW(PixelArea<TColor, TPacked> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor); TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>(); int size = Unsafe.SizeOf<TColor>();
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++)
{ {
packed.PackFromBytes(*source, *(source + 1), *(source + 2), *(source + 3)); byte* source = area.PixelBase + (y * area.RowByteCount);
Unsafe.Write(destination, packed); byte* destination = this.GetRowPointer(targetY + y) + targetX;
for (int x = 0; x < width; x++)
{
packed.PackFromBytes(*source, *(source + 1), *(source + 2), *(source + 3));
Unsafe.Write(destination, packed);
source += 4; source += 4;
destination += size; destination += size;
}
} }
} }
/// <summary> /// <summary>
/// Copies to a row in <see cref="ComponentOrder.ZYX"/> format. /// Copies to an area in <see cref="ComponentOrder.ZYX"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The row.</param>
/// <param name="sourceY">The target row index.</param> /// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyToZYX(PixelRow<TColor, TPacked> row, int sourceY, int width) /// <param name="height">The height.</param>
protected virtual void CopyToZYX(PixelArea<TColor, TPacked> area, int sourceY, int sourceX, int width, int height)
{ {
int offset = 0; for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.ZYX); int offset = y * area.RowByteCount;
offset += 3; for (int x = 0; x < width; x++)
{
this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.ZYX);
offset += 3;
}
} }
} }
/// <summary> /// <summary>
/// Copies to a row in <see cref="ComponentOrder.ZYXW"/> format. /// Copies to an area in <see cref="ComponentOrder.ZYXW"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The row.</param>
/// <param name="sourceY">The target row index.</param> /// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyToZYXW(PixelRow<TColor, TPacked> row, int sourceY, int width) /// <param name="height">The height.</param>
protected virtual void CopyToZYXW(PixelArea<TColor, TPacked> area, int sourceY, int sourceX, int width, int height)
{ {
int offset = 0; for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.ZYXW); int offset = y * area.RowByteCount;
offset += 4; for (int x = 0; x < width; x++)
{
this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.ZYXW);
offset += 4;
}
} }
} }
/// <summary> /// <summary>
/// Copies to a row in <see cref="ComponentOrder.XYZ"/> format. /// Copies to an area in <see cref="ComponentOrder.XYZ"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The row.</param>
/// <param name="sourceY">The target row index.</param> /// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyToXYZ(PixelRow<TColor, TPacked> row, int sourceY, int width) /// <param name="height">The height.</param>
protected virtual void CopyToXYZ(PixelArea<TColor, TPacked> area, int sourceY, int sourceX, int width, int height)
{ {
int offset = 0; for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.XYZ); int offset = y * area.RowByteCount;
offset += 3; for (int x = 0; x < width; x++)
{
this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.XYZ);
offset += 3;
}
} }
} }
/// <summary> /// <summary>
/// Copies to a row in <see cref="ComponentOrder.XYZW"/> format. /// Copies to an area in <see cref="ComponentOrder.XYZW"/> format.
/// </summary> /// </summary>
/// <param name="row">The row.</param> /// <param name="area">The row.</param>
/// <param name="sourceY">The target row index.</param> /// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
protected virtual void CopyToXYZW(PixelRow<TColor, TPacked> row, int sourceY, int width) /// <param name="height">The height.</param>
protected virtual void CopyToXYZW(PixelArea<TColor, TPacked> area, int sourceY, int sourceX, int width, int height)
{ {
int offset = 0; for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
this[x, sourceY].ToBytes(row.Bytes, offset, ComponentOrder.XYZW); int offset = y * area.RowByteCount;
offset += 4; for (int x = 0; x < width; x++)
{
this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.XYZW);
offset += 4;
}
} }
} }
@ -446,6 +493,20 @@ namespace ImageSharp
return this.pixelsBase + ((targetY * this.Width) * Unsafe.SizeOf<TColor>()); return this.pixelsBase + ((targetY * this.Width) * Unsafe.SizeOf<TColor>());
} }
[Conditional("DEBUG")]
private void CheckDimensions(int width, int height)
{
if (width < 1)
{
throw new ArgumentOutOfRangeException(nameof(width), width, $"Invalid area size specified.");
}
if (height < 1)
{
throw new ArgumentOutOfRangeException(nameof(height), height, $"Invalid area size specified.");
}
}
/// <summary> /// <summary>
/// Checks the coordinates to ensure they are within bounds. /// Checks the coordinates to ensure they are within bounds.
/// </summary> /// </summary>

89
src/ImageSharp/Image/PixelRow.cs → src/ImageSharp/Image/PixelArea.cs

@ -1,4 +1,4 @@
// <copyright file="PixelRow.cs" company="James Jackson-South"> // <copyright file="PixelArea.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -6,16 +6,17 @@
namespace ImageSharp namespace ImageSharp
{ {
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Represents a row of generic <see cref="Image{TColor,TPacked}"/> pixels. /// Represents an area of generic <see cref="Image{TColor,TPacked}"/> pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public sealed unsafe class PixelRow<TColor, TPacked> : IDisposable public sealed unsafe class PixelArea<TColor, TPacked> : IDisposable
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
@ -41,7 +42,7 @@ namespace ImageSharp
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
/// <param name="bytes">The bytes.</param> /// <param name="bytes">The bytes.</param>
@ -49,15 +50,29 @@ namespace ImageSharp
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="bytes"></paramref> is the incorrect length. /// Thrown if <paramref name="bytes"></paramref> is the incorrect length.
/// </exception> /// </exception>
public PixelRow(int width, byte[] bytes, ComponentOrder componentOrder) public PixelArea(int width, byte[] bytes, ComponentOrder componentOrder)
: this(width, 1, bytes, componentOrder)
{ {
if (bytes.Length != width * GetComponentCount(componentOrder)) }
{
throw new ArgumentOutOfRangeException($"Invalid byte array length. Length {bytes.Length}; Should be {width * GetComponentCount(componentOrder)}."); /// <summary>
} /// Initializes a new instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="bytes">The bytes.</param>
/// <param name="componentOrder">The component order.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="bytes"></paramref> is the incorrect length.
/// </exception>
public PixelArea(int width, int height, byte[] bytes, ComponentOrder componentOrder)
{
this.CheckBytesLength(width, height, bytes, componentOrder);
this.Width = width; this.Width = width;
this.Height = height;
this.ComponentOrder = componentOrder; this.ComponentOrder = componentOrder;
this.RowByteCount = width * GetComponentCount(componentOrder);
this.Bytes = bytes; this.Bytes = bytes;
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
@ -67,26 +82,40 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="width">The width. </param> /// <param name="width">The width.</param>
/// <param name="componentOrder">The component order.</param> /// <param name="componentOrder">The component order.</param>
public PixelRow(int width, ComponentOrder componentOrder) public PixelArea(int width, ComponentOrder componentOrder)
: this(width, componentOrder, 0) : this(width, 1, componentOrder, 0)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="width">The width. </param> /// <param name="width">The width. </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 PixelRow(int width, ComponentOrder componentOrder, int padding) public PixelArea(int width, ComponentOrder componentOrder, int padding)
: this(width, 1, componentOrder, padding)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
/// <param name="padding">The number of bytes to pad each row.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder, int padding)
{ {
this.Width = width; this.Width = width;
this.Height = height;
this.ComponentOrder = componentOrder; this.ComponentOrder = componentOrder;
this.Bytes = new byte[(width * GetComponentCount(componentOrder)) + padding]; this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding;
this.Bytes = new byte[this.RowByteCount * height];
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?
@ -95,9 +124,9 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="PixelRow{TColor,TPacked}"/> class. /// Finalizes an instance of the <see cref="PixelArea{TColor,TPacked}"/> class.
/// </summary> /// </summary>
~PixelRow() ~PixelArea()
{ {
this.Dispose(); this.Dispose();
} }
@ -128,7 +157,17 @@ namespace ImageSharp
public int Width { get; } public int Width { get; }
/// <summary> /// <summary>
/// Reads the stream to the row. /// Gets the height.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets number of bytes in a row.
/// </summary>
public int RowByteCount { get; }
/// <summary>
/// Reads the stream to the area.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
public void Read(Stream stream) public void Read(Stream stream)
@ -137,7 +176,7 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Writes the row to the stream. /// Writes the area to the stream.
/// </summary> /// </summary>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
public void Write(Stream stream) public void Write(Stream stream)
@ -210,5 +249,15 @@ namespace ImageSharp
throw new NotSupportedException(); throw new NotSupportedException();
} }
[Conditional("DEBUG")]
private void CheckBytesLength(int width, int height, byte[] bytes, ComponentOrder componentOrder)
{
int requiredLength = (width * GetComponentCount(componentOrder)) * height;
if (bytes.Length != requiredLength)
{
throw new ArgumentOutOfRangeException(nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}.");
}
}
} }
} }

122
src/ImageSharp/PixelAccessor.cs

@ -22,91 +22,111 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyFromXYZW(PixelRow<Color, uint> row, int targetY, int targetX, int width) protected override void CopyFromXYZW(PixelArea<Color, uint> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase; uint byteCount = (uint)width * 4;
byte* destination = this.GetRowPointer(targetY) + targetX;
Unsafe.CopyBlock(destination, source, (uint)width * 4); for (int y = 0; y < height; y++)
{
byte* source = area.PixelBase + (y * area.RowByteCount);
byte* destination = this.GetRowPointer(targetY + y) + targetX;
Unsafe.CopyBlock(destination, source, byteCount);
}
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyFromXYZ(PixelRow<Color, uint> row, int targetY, int targetX, int width) protected override void CopyFromXYZ(PixelArea<Color, uint> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase; for (int y = 0; y < height; y++)
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{ {
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); byte* source = area.PixelBase + (y * area.RowByteCount);
byte* destination = this.GetRowPointer(targetY + y) + targetX;
source += 3; for (int x = 0; x < width; x++)
destination += 4; {
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24));
source += 3;
destination += 4;
}
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyFromZYX(PixelRow<Color, uint> row, int targetY, int targetX, int width) protected override void CopyFromZYX(PixelArea<Color, uint> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase; for (int y = 0; y < height; y++)
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{ {
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); byte* source = area.PixelBase + (y * area.RowByteCount);
byte* destination = this.GetRowPointer(targetY + y) + targetX;
for (int x = 0; x < width; x++)
{
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24));
source += 3; source += 3;
destination += 4; destination += 4;
}
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyFromZYXW(PixelRow<Color, uint> row, int targetY, int targetX, int width) protected override void CopyFromZYXW(PixelArea<Color, uint> area, int targetY, int targetX, int width, int height)
{ {
byte* source = row.PixelBase; for (int y = 0; y < height; y++)
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{ {
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); byte* source = area.PixelBase + (y * area.RowByteCount);
byte* destination = this.GetRowPointer(targetY + y) + targetX;
for (int x = 0; x < width; x++)
{
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24));
source += 4; source += 4;
destination += 4; destination += 4;
}
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyToZYX(PixelRow<Color, uint> row, int sourceY, int width) protected override void CopyToZYX(PixelArea<Color, uint> area, int sourceY, int sourceX, int width, int height)
{ {
byte* source = this.GetRowPointer(sourceY); for (int y = 0; y < height; y++)
byte* destination = row.PixelBase;
for (int x = 0; x < width; x++)
{ {
*destination = *(source + 2); byte* source = this.GetRowPointer(sourceY + y) + sourceX;
*(destination + 1) = *(source + 1); byte* destination = area.PixelBase + (y * area.RowByteCount);
*(destination + 2) = *(source + 0);
for (int x = 0; x < width; x++)
source += 4; {
destination += 3; *destination = *(source + 2);
*(destination + 1) = *(source + 1);
*(destination + 2) = *(source + 0);
source += 4;
destination += 3;
}
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void CopyToZYXW(PixelRow<Color, uint> row, int sourceY, int width) protected override void CopyToZYXW(PixelArea<Color, uint> area, int sourceY, int sourceX, int width, int height)
{ {
byte* source = this.GetRowPointer(sourceY); for (int y = 0; y < height; y++)
byte* destination = row.PixelBase;
for (int x = 0; x < width; x++)
{ {
*destination = *(source + 2); byte* source = this.GetRowPointer(sourceY + y) + sourceX;
*(destination + 1) = *(source + 1); byte* destination = area.PixelBase + (y * area.RowByteCount);
*(destination + 2) = *(source + 0);
*(destination + 3) = *(source + 3); for (int x = 0; x < width; x++)
{
source += 4; *destination = *(source + 2);
destination += 4; *(destination + 1) = *(source + 1);
*(destination + 2) = *(source + 0);
*(destination + 3) = *(source + 3);
source += 4;
destination += 4;
}
} }
} }
} }

8
tests/ImageSharp.Tests/Image/PixelAccessorTests.cs

@ -71,7 +71,7 @@ namespace ImageSharp.Tests
byte blue = 3; byte blue = 3;
byte alpha = 255; byte alpha = 255;
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(1, ComponentOrder.ZYX)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(1, ComponentOrder.ZYX))
{ {
row.Bytes[0] = blue; row.Bytes[0] = blue;
row.Bytes[1] = green; row.Bytes[1] = green;
@ -99,7 +99,7 @@ namespace ImageSharp.Tests
byte blue = 3; byte blue = 3;
byte alpha = 4; byte alpha = 4;
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(1, ComponentOrder.ZYXW)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(1, ComponentOrder.ZYXW))
{ {
row.Bytes[0] = blue; row.Bytes[0] = blue;
row.Bytes[1] = green; row.Bytes[1] = green;
@ -127,7 +127,7 @@ namespace ImageSharp.Tests
byte green = 2; byte green = 2;
byte blue = 3; byte blue = 3;
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(1, ComponentOrder.ZYX)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(1, ComponentOrder.ZYX))
{ {
pixels[0, 0] = (TColor) (object) new Color(red, green, blue); pixels[0, 0] = (TColor) (object) new Color(red, green, blue);
@ -151,7 +151,7 @@ namespace ImageSharp.Tests
byte blue = 3; byte blue = 3;
byte alpha = 4; byte alpha = 4;
using (PixelRow<TColor, TPacked> row = new PixelRow<TColor, TPacked>(1, ComponentOrder.ZYXW)) using (PixelArea<TColor, TPacked> row = new PixelArea<TColor, TPacked>(1, ComponentOrder.ZYXW))
{ {
pixels[0, 0] = (TColor) (object) new Color(red, green, blue, alpha); pixels[0, 0] = (TColor) (object) new Color(red, green, blue, alpha);

Loading…
Cancel
Save