Browse Source

Simplify CloneAs()

pull/511/head
James Jackson-South 8 years ago
parent
commit
f4a528f413
  1. 32
      src/ImageSharp/ImageFrame{TPixel}.cs
  2. 365
      src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
  3. 88
      tests/ImageSharp.Tests/Image/ImageCloneTests.cs

32
src/ImageSharp/ImageFrame{TPixel}.cs

@ -246,27 +246,23 @@ namespace SixLabors.ImageSharp
return this.Clone() as ImageFrame<TPixel2>;
}
Func<Vector4, Vector4> scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TPixel, TPixel2>();
var target = new ImageFrame<TPixel2>(this.MemoryManager, this.Width, this.Height, this.MetaData.Clone());
using (PixelAccessor<TPixel> pixels = this.Lock())
using (PixelAccessor<TPixel2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Configuration.Default.ParallelOptions,
y =>
Parallel.For(
0,
target.Height,
Configuration.Default.ParallelOptions,
y =>
{
Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y);
for (int x = 0; x < target.Width; x++)
{
for (int x = 0; x < target.Width; x++)
{
var color = default(TPixel2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
ref var pixel = ref targetRow[x];
pixel.PackFromScaledVector4(sourceRow[x].ToScaledVector4());
}
});
return target;
}

365
src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs

@ -1,365 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Assists with the conversion of known packed pixel formats from one to another.
/// </summary>
internal static class PackedPixelConverterHelper
{
/// <summary>
/// A non operative function. Simply returns the original vector.
/// </summary>
private static readonly Func<Vector4, Vector4> Noop = vector4 => vector4;
/// <summary>
/// Returns the correct scaling function for the given types The compute scale function.
/// </summary>
/// <typeparam name="TPixel">The source pixel format.</typeparam>
/// <typeparam name="TPixel2">The target pixel format.</typeparam>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
public static Func<Vector4, Vector4> ComputeScaleFunction<TPixel, TPixel2>()
{
Type source = typeof(TPixel);
Type target = typeof(TPixel2);
// Normalized standard
if (IsStandardNormalizedType(source))
{
return FromStandardNormalizedType(target);
}
// Standard
if (IsStandardType(source))
{
return FromStandardType(target);
}
// Normalized offsets. All four components.
if (IsOffsetNormalizedType(source))
{
return FromOffsetNormalizedType(target);
}
// Offset. All four components.
if (IsOffsetType(source))
{
return FromOffsetType(target);
}
// Normalized offsets. First component pair only.
if (IsOffsetTwoComponentNormalizedType(source))
{
return FromOffsetTwoComponentNormalizedType(target);
}
// Offsets. First component pair only.
if (IsOffsetTwoComponentType(source))
{
return FromOffsetTwoComponentType(target);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardNormalizedType(Type target)
{
if (IsStandardType(target))
{
return vector4 => 255F * vector4;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range then offset the center down.
return vector4 => (2F * vector4) - Vector4.One;
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * vector4) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 255.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardType(Type target)
{
// Scale down
if (IsStandardNormalizedType(target))
{
return vector4 => vector4 / 255F;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range, divide, then offset the center down.
return vector4 => ((2F * (vector4 / 255F)) - Vector4.One);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * (vector4 / 255F)) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -1 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 2F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 2F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
// Multiply out the range, two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -32767 to 32767.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 65534F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Compress the range. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
// Compress the range then offset the center up for first pair.
Vector4 v = (vector4 / 2F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0F, 1F);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
// Compress the range, multiply, then offset the center up for first pair.
Vector4 v = ((vector4 / 2F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0F, 255F);
};
}
if (IsOffsetNormalizedType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -1F, 1F);
}
if (IsOffsetTwoComponentType(target))
{
// Multiply. Two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
if (IsOffsetType(target))
{
return vector4 =>
{
// Multiply the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 * 32767F;
return new Vector4(v.X, v.Y, -32767F, 32767F);
};
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
Vector4 v = (vector4 / 65534F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
Vector4 v = ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0, 255F);
};
}
if (IsOffsetType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -32767F, 32767F);
}
if (IsOffsetNormalizedType(target))
{
return vector4 =>
{
// Divide the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 / 32767F;
return new Vector4(v.X, v.Y, -1F, 1F);
};
}
if (IsOffsetTwoComponentNormalizedType(target))
{
// Divide. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardNormalizedType(Type type)
{
return
type == typeof(Alpha8)
|| type == typeof(Argb32)
|| type == typeof(Bgr24)
|| type == typeof(Bgra32)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
|| type == typeof(HalfSingle)
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
|| type == typeof(Rgb24)
|| type == typeof(Rgba32)
|| type == typeof(Rgba64)
|| type == typeof(Rgba1010102);
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 255.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardType(Type type)
{
return type == typeof(Byte4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentNormalizedType(Type type)
{
return type == typeof(NormalizedByte2)
|| type == typeof(NormalizedShort2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -1 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetNormalizedType(Type type)
{
return type == typeof(NormalizedByte4)
|| type == typeof(NormalizedShort4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentType(Type type)
{
return type == typeof(Short2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -32767 to 32767.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetType(Type type)
{
return type == typeof(Short4);
}
}
}

88
tests/ImageSharp.Tests/Image/ImageCloneTests.cs

@ -21,13 +21,89 @@ namespace SixLabors.ImageSharp.Tests
for (int x = 0; x < image.Width; x++)
{
Rgba32 rgba = row[x];
Bgra32 bgra = rowClone[x];
Rgba32 expected = row[x];
Bgra32 actual = rowClone[x];
Assert.Equal(rgba.R, bgra.R);
Assert.Equal(rgba.G, bgra.G);
Assert.Equal(rgba.B, bgra.B);
Assert.Equal(rgba.A, bgra.A);
Assert.Equal(expected.R, actual.R);
Assert.Equal(expected.G, actual.G);
Assert.Equal(expected.B, actual.B);
Assert.Equal(expected.A, actual.A);
}
}
}
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToBgr24(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Bgr24> clone = image.CloneAs<Bgr24>())
{
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> row = image.GetPixelRowSpan(y);
Span<Bgr24> rowClone = clone.GetPixelRowSpan(y);
for (int x = 0; x < image.Width; x++)
{
Rgba32 expected = row[x];
Bgr24 actual = rowClone[x];
Assert.Equal(expected.R, actual.R);
Assert.Equal(expected.G, actual.G);
Assert.Equal(expected.B, actual.B);
}
}
}
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToArgb32(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Argb32> clone = image.CloneAs<Argb32>())
{
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> row = image.GetPixelRowSpan(y);
Span<Argb32> rowClone = clone.GetPixelRowSpan(y);
for (int x = 0; x < image.Width; x++)
{
Rgba32 expected = row[x];
Argb32 actual = rowClone[x];
Assert.Equal(expected.R, actual.R);
Assert.Equal(expected.G, actual.G);
Assert.Equal(expected.B, actual.B);
Assert.Equal(expected.A, actual.A);
}
}
}
}
[Theory]
[WithTestPatternImages(9, 9, PixelTypes.Rgba32)]
public void CloneAs_ToRgb24(TestImageProvider<Rgba32> provider)
{
using (Image<Rgba32> image = provider.GetImage())
using (Image<Rgb24> clone = image.CloneAs<Rgb24>())
{
for (int y = 0; y < image.Height; y++)
{
Span<Rgba32> row = image.GetPixelRowSpan(y);
Span<Rgb24> rowClone = clone.GetPixelRowSpan(y);
for (int x = 0; x < image.Width; x++)
{
Rgba32 expected = row[x];
Rgb24 actual = rowClone[x];
Assert.Equal(expected.R, actual.R);
Assert.Equal(expected.G, actual.G);
Assert.Equal(expected.B, actual.B);
}
}
}

Loading…
Cancel
Save