Browse Source

Add PackedPixelConverterHelper Fix #27

Allows the conversion between all the known packed pixel types.
af/merge-core
James Jackson-South 9 years ago
parent
commit
ba13febc78
  1. 32
      README.md
  2. 276
      src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs
  3. 12
      src/ImageSharp/Image/Image.cs
  4. 12
      src/ImageSharp/Image/ImageFrame.cs
  5. 14
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

32
README.md

@ -28,7 +28,7 @@ To clone it locally click the "Clone in Windows" button above or run the followi
git clone https://github.com/JimBobSquarePants/ImageSharp
```
###What works so far/ What is planned?
### What works so far/ What is planned?
- Encoding/decoding of image formats (plugable).
- [x] Jpeg (Includes Subsampling. Progressive writing required)
@ -43,16 +43,34 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Xiaolin Wu
- [x] Palette
- Basic color structs with implicit operators.
- [x] Color - 32bit color in RGBA order.
- [x] BGRA32
- [x] Color - 32bit color in RGBA order (IPackedPixel\<TPacked\>).
- [x] Bgra32
- [x] CIE Lab
- [x] CIE XYZ
- [x] CMYK
- [x] HSV
- [x] HSL
- [x] YCbCr
- Basic shape primitives (Vector backed)
- [x] Rectangle (Doesn't contain all System.Drawing methods)
- IPackedPixel\<TPacked\> representations of color models. Compatible with Microsoft XNA Game Studio and MonoGame.
- [x] Alpha8
- [x] Bgr565
- [x] Bgra444
- [x] Bgra565
- [x] Byte4
- [x] HalfSingle
- [x] HalfVector2
- [x] HalfVector4
- [x] NormalizedByte2
- [x] NormalizedByte4
- [x] NormalizedShort2
- [x] NormalizedShort4
- [x] Rg32
- [x] Rgba1010102
- [x] Rgba64
- [x] Short2
- [x] Short4
- Basic shape primitives.
- [x] Rectangle
- [x] Size
- [x] Point
- [x] Ellipse
@ -131,9 +149,9 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Threshold
- Drawing
- [ ] Path brush (Need help)
- [ ] Pattern brush (Need help)
- [ ] Hatch brush (Need help)
- [ ] Elliptical brush (Need help)
- [ ] Gradient brush (vignette? Need help)
- [ ] Gradient brush (Need help)
- Other stuff I haven't thought of.
### What might never happen

276
src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs

@ -0,0 +1,276 @@
// <copyright file="PackedPixelConverterHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <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>
/// <param name="scaleFunc">The scale function.</param>
/// <typeparam name="TColor">The source pixel format.</typeparam>
/// <typeparam name="TColor2">The target pixel format.</typeparam>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
public static Func<Vector4, Vector4> ComputeScaleFunction<TColor, TColor2>(Func<Vector4, Vector4> scaleFunc)
{
// Custom type with a custom function.
if (scaleFunc != null)
{
return scaleFunc;
}
Type source = typeof(TColor);
Type target = typeof(TColor2);
// Standard to offset
if (IsStandardNormalizedType(source))
{
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 v => (65534 * v) - new Vector4(32767);
}
}
// 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 -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 (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 / 65534) + new Vector4(.5F);
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Compress the range. Two component won't read the last two values.
return vector4 => (vector4 / 32767);
}
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, 0, 1);
};
}
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, -1, 1);
}
if (IsOffsetTwoComponentType(target))
{
// Multiply. Two component won't read the last two values.
return vector4 => (vector4 * 32767);
}
if (IsOffsetType(target))
{
return vector4 =>
{
// Multiply the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 * 32767;
return new Vector4(v.X, v.Y, -32767, 32767);
};
}
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 / 65534) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
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, -32767, 32767);
}
if (IsOffsetNormalizedType(target))
{
return vector4 =>
{
// Divide the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 / 32767;
return new Vector4(v.X, v.Y, -1, 1);
};
}
if (IsOffsetTwoComponentNormalizedType(target))
{
// Divide. Two component won't read the last two values.
return vector4 => (vector4 / 32767);
}
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(Color)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
|| type == typeof(Byte4)
|| type == typeof(HalfSingle)
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
|| type == typeof(Rgba1010102)
|| type == typeof(Rgba64);
}
/// <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);
}
}
}

12
src/ImageSharp/Image/Image.cs

@ -261,13 +261,8 @@ namespace ImageSharp
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// <remarks>
/// Most color formats when converted to vectors have a range of <value>0</value> to <value>1</value>. Some however, <see cref="NormalizedByte4"/>, for example scale between
/// <value>-1</value> to <value>1</value>. This requires additional computation to convert between the formats.
/// For example, if I wanted to convert from <see cref="Color"/> to <see cref="NormalizedByte4"/> the following function would be required. <example>v => (2F * v) - Vector4.One</example>
/// </remarks>
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between color formats.</param>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="Image{TColor2, TPacked2}"/></returns>
@ -275,10 +270,7 @@ namespace ImageSharp
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
if (scaleFunc == null)
{
scaleFunc = v => v;
}
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
Image<TColor2, TPacked2> target = new Image<TColor2, TPacked2>(this.Width, this.Height)
{

12
src/ImageSharp/Image/ImageFrame.cs

@ -42,13 +42,8 @@ namespace ImageSharp
/// <summary>
/// Returns a copy of the image frame in the given pixel format.
/// <remarks>
/// Most color formats when converted to vectors have a range of <value>0</value> to <value>1</value>. Some however, <see cref="NormalizedByte4"/>, for example scale between
/// <value>-1</value> to <value>1</value>. This requires additional computation to convert between the formats.
/// For example, if I wanted to convert from <see cref="Color"/> to <see cref="NormalizedByte4"/> the following function would be required. <example>v => (2F * v) - Vector4.One</example>
/// </remarks>
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between color formats.</param>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="ImageFrame{TColor2, TPacked2}"/></returns>
@ -56,10 +51,7 @@ namespace ImageSharp
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
if (scaleFunc == null)
{
scaleFunc = v => v;
}
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
ImageFrame<TColor2, TPacked2> target = new ImageFrame<TColor2, TPacked2>
{

14
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -64,14 +64,12 @@ namespace ImageSharp.Tests
// Image<Rg32, uint> image = file.CreateImage().To<Rg32, uint>();
// Image<Rgba1010102, uint> image = file.CreateImage().To<Rgba1010102, uint>();
// Image<Rgba64, ulong> image = file.CreateImage().To<Rgba64, ulong>();
// TODO: Conversion between types who's vector ranges are different are not possible without scaling function, Make static version of known ones.
// Image<NormalizedByte2, ushort> image = file.CreateImage().To<NormalizedByte2, ushort>(v => (2F * v) - Vector4.One);
// Image<NormalizedByte4, uint> image = file.CreateImage().To<NormalizedByte4, uint>(v => (2F * v) - Vector4.One);
// Image<NormalizedShort2, uint> image = file.CreateImage().To<NormalizedShort2, uint>(v => (2F * v) - Vector4.One);
// Image<NormalizedShort4, ulong> image = file.CreateImage().To<NormalizedShort4, ulong>(v => (2F * v) - Vector4.One);
// Image<Short2, uint> image = file.CreateImage().To<Short2, uint>(v => (65534 * v) - new Vector4(32767));
// Image<NormalizedByte2, ushort> image = file.CreateImage().To<NormalizedByte2, ushort>();
// Image<NormalizedByte4, uint> image = file.CreateImage().To<NormalizedByte4, uint>();
// Image<NormalizedShort2, uint> image = file.CreateImage().To<NormalizedShort2, uint>();
// Image<NormalizedShort4, ulong> image = file.CreateImage().To<NormalizedShort4, ulong>();
// Image<Short2, uint> image = file.CreateImage().To<Short2, uint>();
// Image<Short4, ulong> image = file.CreateImage().To<Short4, ulong>();
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
image.Save(output);

Loading…
Cancel
Save