From ba13febc78fc3ec60991f78f815649e6ee228dbb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Dec 2016 00:53:31 +1100 Subject: [PATCH] Add PackedPixelConverterHelper Fix #27 Allows the conversion between all the known packed pixel types. --- README.md | 32 +- .../PackedPixel/PackedPixelConverterHelper.cs | 276 ++++++++++++++++++ src/ImageSharp/Image/Image.cs | 12 +- src/ImageSharp/Image/ImageFrame.cs | 12 +- .../Formats/GeneralFormatTests.cs | 14 +- 5 files changed, 311 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs diff --git a/README.md b/README.md index 5c0ca8298..de9d40fba 100644 --- a/README.md +++ b/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\). + - [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\ 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 diff --git a/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs b/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs new file mode 100644 index 000000000..84f89a33b --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs @@ -0,0 +1,276 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + + /// + /// Assists with the conversion of known packed pixel formats from one to another. + /// + internal static class PackedPixelConverterHelper + { + /// + /// A non operative function. Simply returns the original vector. + /// + private static readonly Func Noop = vector4 => vector4; + + /// + /// Returns the correct scaling function for the given types The compute scale function. + /// + /// The scale function. + /// The source pixel format. + /// The target pixel format. + /// The + public static Func ComputeScaleFunction(Func 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; + } + + /// + /// Returns the correct conversion function to convert from types having vector values representing all four components + /// ranging from -1 to 1. + /// + /// The target type + /// The + private static Func 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; + } + + /// + /// Returns the correct conversion function to convert from types having vector values representing all four components + /// ranging from -32767 to 32767. + /// + /// The target type + /// The + private static Func 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; + } + + /// + /// 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. + /// + /// The target type + /// The + private static Func 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; + } + + /// + /// 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. + /// + /// The target type + /// The + private static Func 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; + } + + /// + /// Identifies the type as having vector component values ranging from 0 to 1. + /// + /// The type to test. + /// The + 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); + } + + /// + /// 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. + /// + /// The type to test. + /// The + private static bool IsOffsetTwoComponentNormalizedType(Type type) + { + return type == typeof(NormalizedByte2) + || type == typeof(NormalizedShort2); + } + + /// + /// Identifies the type as having vector values representing all four components ranging from -1 to 1. + /// + /// The type to test. + /// The + private static bool IsOffsetNormalizedType(Type type) + { + return type == typeof(NormalizedByte4) + || type == typeof(NormalizedShort4); + } + + /// + /// 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. + /// + /// The type to test. + /// The + private static bool IsOffsetTwoComponentType(Type type) + { + return type == typeof(Short2); + } + + /// + /// Identifies the type as having vector values representing all four components ranging from -32767 to 32767. + /// + /// The type to test. + /// The + private static bool IsOffsetType(Type type) + { + return type == typeof(Short4); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.cs b/src/ImageSharp/Image/Image.cs index fcb72ced8..2884eddbd 100644 --- a/src/ImageSharp/Image/Image.cs +++ b/src/ImageSharp/Image/Image.cs @@ -261,13 +261,8 @@ namespace ImageSharp /// /// Returns a copy of the image in the given pixel format. - /// - /// Most color formats when converted to vectors have a range of 0 to 1. Some however, , for example scale between - /// -1 to 1. This requires additional computation to convert between the formats. - /// For example, if I wanted to convert from to the following function would be required. v => (2F * v) - Vector4.One - /// /// - /// A function that allows for the correction of vector scaling between color formats. + /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The packed format. uint, long, float. /// The @@ -275,10 +270,7 @@ namespace ImageSharp where TColor2 : struct, IPackedPixel where TPacked2 : struct { - if (scaleFunc == null) - { - scaleFunc = v => v; - } + scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); Image target = new Image(this.Width, this.Height) { diff --git a/src/ImageSharp/Image/ImageFrame.cs b/src/ImageSharp/Image/ImageFrame.cs index fd4d7fa52..c0e5b3f16 100644 --- a/src/ImageSharp/Image/ImageFrame.cs +++ b/src/ImageSharp/Image/ImageFrame.cs @@ -42,13 +42,8 @@ namespace ImageSharp /// /// Returns a copy of the image frame in the given pixel format. - /// - /// Most color formats when converted to vectors have a range of 0 to 1. Some however, , for example scale between - /// -1 to 1. This requires additional computation to convert between the formats. - /// For example, if I wanted to convert from to the following function would be required. v => (2F * v) - Vector4.One - /// /// - /// A function that allows for the correction of vector scaling between color formats. + /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The packed format. uint, long, float. /// The @@ -56,10 +51,7 @@ namespace ImageSharp where TColor2 : struct, IPackedPixel where TPacked2 : struct { - if (scaleFunc == null) - { - scaleFunc = v => v; - } + scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); ImageFrame target = new ImageFrame { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 6196eed43..575fcf091 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -64,14 +64,12 @@ namespace ImageSharp.Tests // Image image = file.CreateImage().To(); // Image image = file.CreateImage().To(); // Image image = file.CreateImage().To(); - - // TODO: Conversion between types who's vector ranges are different are not possible without scaling function, Make static version of known ones. - // Image image = file.CreateImage().To(v => (2F * v) - Vector4.One); - // Image image = file.CreateImage().To(v => (2F * v) - Vector4.One); - // Image image = file.CreateImage().To(v => (2F * v) - Vector4.One); - // Image image = file.CreateImage().To(v => (2F * v) - Vector4.One); - // Image image = file.CreateImage().To(v => (65534 * v) - new Vector4(32767)); - + // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Save(output);