Browse Source

Reshuffle colors, improve accuracy of cspace conversion

Former-commit-id: 3c378bc93b0edd73d1c8f07fab33b1943b9d4411
Former-commit-id: 2fa968ca7c3535ab92251d55afba20e64c574089
Former-commit-id: 12065bfba7ce14e175d99d15d2f0a937665fa395
af/merge-core
James Jackson-South 10 years ago
parent
commit
c8b3355282
  1. 218
      src/ImageProcessor/ApiPortabilityAnalysis.htm
  2. 249
      src/ImageProcessor/Colors/Color.cs
  3. 132
      src/ImageProcessor/Colors/ColorVector.cs
  4. 125
      src/ImageProcessor/Colors/Formats/Bgra32.cs
  5. 6
      src/ImageProcessor/Colors/Formats/Cmyk.cs
  6. 6
      src/ImageProcessor/Colors/Formats/Hsv.cs
  7. 6
      src/ImageProcessor/Colors/Formats/YCbCr.cs
  8. 32
      src/ImageProcessor/Common/Helpers/ImageMaths.cs
  9. 193
      src/ImageProcessor/Common/Helpers/PixelOperations - Copy.cs
  10. 145
      src/ImageProcessor/Common/Helpers/PixelOperations.cs
  11. 4
      src/ImageProcessor/Filters/Alpha.cs
  12. 13
      src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
  13. 4
      src/ImageProcessor/Filters/Contrast.cs
  14. 4
      src/ImageProcessor/Filters/Invert.cs
  15. 4
      src/ImageProcessor/Formats/Gif/GifEncoder.cs
  16. 34
      src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs
  17. 6
      src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs
  18. 12
      src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs
  19. 4
      src/ImageProcessor/IImageBase.cs
  20. 6
      src/ImageProcessor/ImageBase.cs
  21. 12
      src/ImageProcessor/ImageProcessor.csproj
  22. 1
      src/ImageProcessor/ImageProcessor.csproj.DotSettings
  23. 12
      src/ImageProcessor/Samplers/Resamplers/BicubicResampler.cs
  24. 4
      src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs
  25. 8
      src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs
  26. 8
      src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs
  27. 15
      src/ImageProcessor/Samplers/Resamplers/IResampler.cs
  28. 4
      src/ImageProcessor/Samplers/Resamplers/Lanczos3Resampler.cs
  29. 4
      src/ImageProcessor/Samplers/Resamplers/Lanczos5Resampler.cs
  30. 4
      src/ImageProcessor/Samplers/Resamplers/Lanczos8Resampler.cs
  31. 8
      src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs
  32. 8
      src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs
  33. 8
      src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs
  34. 8
      src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs
  35. 8
      src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs
  36. 4
      src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs
  37. 6
      src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs
  38. 232
      src/ImageProcessor/Samplers/Resize - Copy.cs
  39. 48
      src/ImageProcessor/Samplers/Resize.cs
  40. 72
      tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs
  41. 36
      tests/ImageProcessor.Tests/Colors/ColorTests.cs
  42. 4
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs
  43. 30
      tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

218
src/ImageProcessor/ApiPortabilityAnalysis.htm

@ -1,218 +0,0 @@
<!DOCTYPE html><html xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<head>
<meta content="en-us" http-equiv="Content-Language" /><meta content="text/html; charset=utf-16" http-equiv="Content-Type" /><title _locID="PortabilityAnalysis0">
.NET Portability Report
</title><style>
/* Body style, for the entire document */
body
{
background: #F3F3F4;
color: #1E1E1F;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
padding: 0;
margin: 0;
}
/* Header1 style, used for the main title */
h1
{
padding: 10px 0px 10px 10px;
font-size: 21pt;
background-color: #E2E2E2;
border-bottom: 1px #C1C1C2 solid;
color: #201F20;
margin: 0;
font-weight: normal;
}
/* Header2 style, used for "Overview" and other sections */
h2
{
font-size: 18pt;
font-weight: normal;
padding: 15px 0 5px 0;
margin: 0;
}
/* Header3 style, used for sub-sections, such as project name */
h3
{
font-weight: normal;
font-size: 15pt;
margin: 0;
padding: 15px 0 5px 0;
background-color: transparent;
}
/* Color all hyperlinks one color */
a
{
color: #1382CE;
}
/* Table styles */
table
{
border-spacing: 0 0;
border-collapse: collapse;
font-size: 10pt;
}
table th
{
background: #E7E7E8;
text-align: left;
text-decoration: none;
font-weight: normal;
padding: 3px 6px 3px 6px;
}
table td
{
vertical-align: top;
padding: 3px 6px 5px 5px;
margin: 0px;
border: 1px solid #E7E7E8;
background: #F7F7F8;
}
/* Local link is a style for hyperlinks that link to file:/// content, there are lots so color them as 'normal' text until the user mouse overs */
.localLink
{
color: #1E1E1F;
background: #EEEEED;
text-decoration: none;
}
.localLink:hover
{
color: #1382CE;
background: #FFFF99;
text-decoration: none;
}
/* Center text, used in the over views cells that contain message level counts */
.textCentered
{
text-align: center;
}
/* The message cells in message tables should take up all avaliable space */
.messageCell
{
width: 100%;
}
/* Padding around the content after the h1 */
#content
{
padding: 0px 12px 12px 12px;
}
/* The overview table expands to width, with a max width of 97% */
#overview table
{
width: auto;
max-width: 75%;
}
/* The messages tables are always 97% width */
#messages table
{
width: 97%;
}
/* All Icons */
.IconSuccessEncoded, .IconInfoEncoded, .IconWarningEncoded, .IconErrorEncoded
{
min-width:18px;
min-height:18px;
background-repeat:no-repeat;
background-position:center;
}
/* Success icon encoded */
.IconSuccessEncoded
{
/* Note: Do not delete the comment below. It is used to verify the correctness of the encoded image resource below before the product is released */
/* [---XsltValidateInternal-Base64EncodedImage:IconSuccess#Begin#background-image: url(data:image/png;base64,#Separator#);#End#] */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABPElEQVR4Xp1Tv0vDUBi8FqeA4NpBcBLcWnQSApncOnTo4FSnjP0DsnXpH5CxiwbHDg4Zuj4oOEXiJgiC4FDcCkLWmIMc1Pfw+eMgQ77v3Xf3Pe51YKGqqisAEwCR1TIAsiAIblSo6xrdHeJR85Xle3mdmCQKb0PsfqyxxzM8K15HZADl/H5+sHpZwYfxyRjTs+kWwKBx8yoHd2mRiuzF8mkJniWH/13u3Fjrs/EdhsdDFHGB/DLXEJBDLh1MWPAhPo1BLB4WX5yQywHR+m3tVe/t97D52CB/ziG0nIgD/qDuYg8WuCcVZ2YGwlJ3YDugkpR/VNcAEx6GEKhERSr71FuO4YCM4XBdwKvecjIlkSnsO0Hyp/GxSeJAdzBKzpOtnPwyyiPdAZhpZptT04tU+zk7s8czeges//s5C5+CwqrR4/gw+AAAAABJRU5ErkJggg==);
}
/* Information icon encoded */
.IconInfoEncoded
{
/* Note: Do not delete the comment below. It is used to verify the correctness of the encoded image resource below before the product is released */
/* [---XsltValidateInternal-Base64EncodedImage:IconInformation#Begin#background-image: url(data:image/png;base64,#Separator#);#End#] */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHElEQVR4Xs2TsUoDQRRF7wwoziokjZUKadInhdhukR9YP8DMX1hYW+QvdsXa/QHBbcXC7W0CamWTQnclFutceIQJwwaWNLlwm5k5d94M76mmaeCrrmsLYOocY12FcxZFUeozCqKqqgYA8uevv1H6VuPxcwlfk5N92KHBxfFeCSAxxswlYAW/Xr989x/mv9gkhtyMDhcAxgzRsp7flj8B/HF1RsMXq+NZMkopaHe7lbKxQUEIGbKsYNoGn969060hZBkQex/W8oRQwsQaW2o3Ago2SVcJUzAgY3N0lTCZZm+zPS8HB51gMmS1DEYyOz9acKO1D8JWTlafKIMxdhvlfdyT94Vv5h7P8Ky7nQzACmhvKq3zk3PjW9asz9D/1oigecsioooAAAAASUVORK5CYII=);
}
/* Warning icon encoded */
.IconWarningEncoded
{
/* Note: Do not delete the comment below. It is used to verify the correctness of the encoded image resource below before the product is released */
/* [---XsltValidateInternal-Base64EncodedImage:IconWarning#Begin#background-image: url(data:image/png;base64,#Separator#);#End#] */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAx0lEQVR4XpWSMQ7CMAxFf4xAyBMLCxMrO8dhaBcuwdCJS3RJBw7SA/QGTCxdWJgiQYWKXJWKIXHIlyw5lqr34tQgEOdcBsCOx5yZK3hCCKdYXneQkh4pEfqzLfu+wVDSyyzFoJjfz9NB+pAF+eizx2Vruts0k15mPgvS6GYvpVtQhB61IB/dk6AF6fS4Ben0uIX5odtFe8Q/eW1KvFeH4e8khT6+gm5B+t3juyDt7n0jpe+CANTd+oTUjN/U3yVaABnSUjFz/gFq44JaVSCXeQAAAABJRU5ErkJggg==);
}
/* Error icon encoded */
.IconErrorEncoded
{
/* Note: Do not delete the comment below. It is used to verify the correctness of the encoded image resource below before the product is released */
/* [---XsltValidateInternal-Base64EncodedImage:IconError#Begin#background-image: url(data:image/png;base64,#Separator#);#End#] */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABQElEQVR4XqWTvUoEQRCE6wYPZUA80AfwAQz23uCMjA7MDRQEIzPBVEyNTQUFIw00vcQTTMzuAh/AxEQQT8HF/3G/oGGnEUGuoNnd6qoZuqltyKEsyzVJq5I6rnUp6SjGeGhESikzzlc1eL7opfuVbrqbU1Zw9NCgtQMaZpY0eNnaaL2fHusvTK5vKu7sjSS1Y4y3QUA6K3e3Mau5UFDyMP7tYF9o8cAHZv68vipoIJg971PZIZ5HiwdvYGGvFVFHmGmZ2MxwmQYPXubPl9Up0tfoMQGetXd6mRbvhBw+boZ6WF7Mbv1+GsHRk0fQmPAH1GfmZirbCfDJ61tw3Px8/8pZsPAG4jlVhcPgZ7adwNWBB68lkRQWFiTgFlbnLY3DGGM7izIJIyT/jjIvEJw6fdJTc6krDzh6aMwMP9bvDH4ADSsa9uSWVJkAAAAASUVORK5CYII=);
}
</style>
</head><body>
<h1 _locid="PortabilityReport">
.NET Portability Report
</h1><div id="content">
<h2 _locid="SummaryTitle">
<a name="Summary"></a>Summary
</h2><div id="summary">
<table>
<tbody>
<tr>
<th>Assembly</th><th>.NET Core 5.0</th><th>.NET Framework 4.6.1</th><th>.NET Native 1.0</th><th>ASP.NET 5 1.0</th><th>Mono 3.3.0.0</th><th>Windows 8.1</th><th>Windows Phone 8.1</th>
</tr><tr>
<td><strong><a href="#ImageProcessor">ImageProcessor</a></strong></td><td class="textCentered">100%</td><td class="textCentered">99.7%</td><td class="textCentered">100%</td><td class="textCentered">100%</td><td class="textCentered">97.6%</td><td class="textCentered">97.9%</td><td class="textCentered">97.9%</td>
</tr>
</tbody>
</table>
</div><div id="details">
<a name="ImageProcessor"></a><h3>
ImageProcessor
</h3><table>
<tbody>
<tr>
<th>Target type</th><th>.NET Core 5.0</th><th>.NET Framework 4.6.1</th><th>.NET Native 1.0</th><th>ASP.NET 5 1.0</th><th>Mono 3.3.0.0</th><th>Windows 8.1</th><th>Windows Phone 8.1</th><th>Recommended changes</th>
</tr><tr>
<td>System.Reflection.TypeInfo</td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">get_Assembly</td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td></td>
</tr><tr>
<td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td>
</tr><tr>
<td>System.Numerics.Vector4</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">X</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">Z</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">Y</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">W</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">#ctor(System.Single,System.Single,System.Single,System.Single)</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td>
</tr><tr>
<td>System.Array</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td style="padding-left:2em">Empty``1</td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconSuccessEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td class="IconErrorEncoded"></td><td></td>
</tr><tr>
<td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td><td>&nbsp</td>
</tr>
</tbody>
</table><a href="#Summary">Back to summary</a>
</div>

249
src/ImageProcessor/Colors/Color.cs

@ -0,0 +1,249 @@
// <copyright file="Color.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System.Numerics;
/// <summary>
/// Represents a four-component color using red, green, blue, and alpha data.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public struct Color
{
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="r">
/// The red component of this <see cref="Color"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="Color"/>.
/// </param>
/// <param name="b">
/// The blue component of this <see cref="Color"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="Color"/>.
/// </param>
public Color(float r, float g, float b, float a)
: this()
{
this.backingVector.X = r;
this.backingVector.Y = g;
this.backingVector.Z = b;
this.backingVector.W = a;
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector.
/// </param>
private Color(Vector4 vector)
{
this.backingVector = vector;
}
/// <summary>
/// Gets or sets the blue component of the color.
/// </summary>
public float B
{
get
{
return this.backingVector.X;
}
set
{
this.backingVector.X = value;
}
}
/// <summary>
/// Gets or sets the green component of the color.
/// </summary>
public float G
{
get
{
return this.backingVector.Y;
}
set
{
this.backingVector.Y = value;
}
}
/// <summary>
/// Gets or sets the red component of the color.
/// </summary>
public float R
{
get
{
return this.backingVector.Z;
}
set
{
this.backingVector.Z = value;
}
}
/// <summary>
/// Gets or sets the alpha component of the color.
/// </summary>
public float A
{
get
{
return this.backingVector.W;
}
set
{
this.backingVector.W = value;
}
}
/// <summary>
/// Gets this color with the component values clamped from 0 to 1.
/// </summary>
public Color Limited
{
get
{
float r = this.R.Clamp(0, 1);
float g = this.G.Clamp(0, 1);
float b = this.B.Clamp(0, 1);
float a = this.A.Clamp(0, 1);
return new Color(r, g, b, a);
}
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Color(Bgra32 color)
{
return new Color(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
}
/// <summary>
/// Computes the product of multiplying a color by a given factor.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="factor">The multiplication factor.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(Color color, float factor)
{
return new Color(color.backingVector * factor);
}
/// <summary>
/// Computes the product of multiplying a color by a given factor.
/// </summary>
/// <param name="factor">The multiplication factor.</param>
/// <param name="color">The color.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(float factor, Color color)
{
return new Color(color.backingVector * factor);
}
/// <summary>
/// Computes the product of multiplying two colors.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator *(Color left, Color right)
{
return new Color(left.backingVector * right.backingVector);
}
/// <summary>
/// Computes the sum of adding two colors.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator +(Color left, Color right)
{
return new Color(left.R + right.R, left.G + right.G, left.B + right.B, left.A + right.A);
}
/// <summary>
/// Computes the difference left by subtracting one color from another.
/// </summary>
/// <param name="left">The color on the left hand of the operand.</param>
/// <param name="right">The color on the right hand of the operand.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color operator -(Color left, Color right)
{
return new Color(left.R - right.R, left.G - right.G, left.B - right.B, left.A - right.A);
}
/// <summary>
/// Returns a new color whose components are the average of the components of first and second.
/// </summary>
/// <param name="first">The first color.</param>
/// <param name="second">The second color.</param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color Average(Color first, Color second)
{
return new Color((first.backingVector + second.backingVector) * .5f);
}
/// <summary>
/// Linearly interpolates from one color to another based on the given amount.
/// </summary>
/// <param name="from">The first color value.</param>
/// <param name="to">The second color value.</param>
/// <param name="amount">
/// The weight value. At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color Lerp(Color from, Color to, float amount)
{
amount = amount.Clamp(0f, 1f);
return (from * (1 - amount)) + (to * amount);
}
}
}

132
src/ImageProcessor/Colors/ColorVector.cs

@ -1,132 +0,0 @@
// <copyright file="ColorVector.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System.Numerics;
public struct ColorVector
{
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="ColorVector"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="ColorVector"/>.
/// </param>
public ColorVector(double b, double g, double r, double a)
: this((float)b, (float)g, (float)r, (float)a)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ColorVector"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="ColorVector"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="ColorVector"/>.
/// </param>
public ColorVector(float b, float g, float r, float a)
: this()
{
this.backingVector.X = b;
this.backingVector.Y = g;
this.backingVector.Z = r;
this.backingVector.W = a;
}
/// <summary> The color's blue component, between 0.0 and 1.0 </summary>
public float B
{
get
{
return this.backingVector.X;
}
set
{
this.backingVector.X = value;
}
}
/// <summary> The color's green component, between 0.0 and 1.0 </summary>
public float G
{
get
{
return this.backingVector.Y;
}
set
{
this.backingVector.Y = value;
}
}
/// <summary> The color's red component, between 0.0 and 1.0 </summary>
public float R
{
get
{
return this.backingVector.Z;
}
set
{
this.backingVector.Z = value;
}
}
/// <summary> The color's alpha component, between 0.0 and 1.0 </summary>
public float A
{
get
{
return this.backingVector.W;
}
set
{
this.backingVector.W = value;
}
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="ColorVector"/> to a
/// <see cref="Bgra"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="ColorVector"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// </returns>
public static implicit operator ColorVector(Bgra color)
{
return new ColorVector(color.B / 255f, color.G / 255f, color.R / 255f, color.A / 255f);
}
}
}

125
src/ImageProcessor/Colors/Bgra.cs → src/ImageProcessor/Colors/Formats/Bgra32.cs

@ -12,32 +12,28 @@ namespace ImageProcessor
/// <summary>
/// Represents an BGRA (blue, green, red, alpha) color.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
public struct Bgra : IEquatable<Bgra>
public struct Bgra32 : IEquatable<Bgra32>
{
/// <summary>
/// Represents a <see cref="Bgra"/> that has B, G, R, and A values set to zero.
/// Represents a <see cref="Bgra32"/> that has B, G, R, and A values set to zero.
/// </summary>
public static readonly Bgra Empty = default(Bgra);
public static readonly Bgra32 Empty = default(Bgra32);
/// <summary>
/// Represents a transparent <see cref="Bgra"/> that has B, G, R, and A values set to 255, 255, 255, 0.
/// Represents a transparent <see cref="Bgra32"/> that has B, G, R, and A values set to 255, 255, 255, 0.
/// </summary>
public static readonly Bgra Transparent = new Bgra(255, 255, 255, 0);
public static readonly Bgra32 Transparent = new Bgra32(255, 255, 255, 0);
/// <summary>
/// Represents a black <see cref="Bgra"/> that has B, G, R, and A values set to 0, 0, 0, 0.
/// Represents a black <see cref="Bgra32"/> that has B, G, R, and A values set to 0, 0, 0, 0.
/// </summary>
public static readonly Bgra Black = new Bgra(0, 0, 0, 255);
public static readonly Bgra32 Black = new Bgra32(0, 0, 0, 255);
/// <summary>
/// Represents a white <see cref="Bgra"/> that has B, G, R, and A values set to 255, 255, 255, 255.
/// Represents a white <see cref="Bgra32"/> that has B, G, R, and A values set to 255, 255, 255, 255.
/// </summary>
public static readonly Bgra White = new Bgra(255, 255, 255, 255);
public static readonly Bgra32 White = new Bgra32(255, 255, 255, 255);
/// <summary>
/// Holds the blue component of the color
@ -64,7 +60,7 @@ namespace ImageProcessor
public readonly byte A;
/// <summary>
/// Permits the <see cref="Bgra"/> to be treated as a 32 bit integer.
/// Permits the <see cref="Bgra32"/> to be treated as a 32 bit integer.
/// </summary>
[FieldOffset(0)]
public readonly int BGRA;
@ -75,18 +71,18 @@ namespace ImageProcessor
private const float Epsilon = 0.0001f;
/// <summary>
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="Bgra"/>.
/// The blue component of this <see cref="Bgra32"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="Bgra"/>.
/// The green component of this <see cref="Bgra32"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="Bgra"/>.
/// The red component of this <see cref="Bgra32"/>.
/// </param>
public Bgra(byte b, byte g, byte r)
public Bgra32(byte b, byte g, byte r)
: this()
{
this.B = b;
@ -96,21 +92,21 @@ namespace ImageProcessor
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="b">
/// The blue component of this <see cref="Bgra"/>.
/// The blue component of this <see cref="Bgra32"/>.
/// </param>
/// <param name="g">
/// The green component of this <see cref="Bgra"/>.
/// The green component of this <see cref="Bgra32"/>.
/// </param>
/// <param name="r">
/// The red component of this <see cref="Bgra"/>.
/// The red component of this <see cref="Bgra32"/>.
/// </param>
/// <param name="a">
/// The alpha component of this <see cref="Bgra"/>.
/// The alpha component of this <see cref="Bgra32"/>.
/// </param>
public Bgra(byte b, byte g, byte r, byte a)
public Bgra32(byte b, byte g, byte r, byte a)
: this()
{
this.B = b;
@ -120,25 +116,25 @@ namespace ImageProcessor
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="bgra">
/// The combined color components.
/// </param>
public Bgra(int bgra)
public Bgra32(int bgra)
: this()
{
this.BGRA = bgra;
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra"/> struct.
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="hex">
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rrggbb, or aarrggbb format to match web syntax.
/// </param>
public Bgra(string hex)
public Bgra32(string hex)
: this()
{
// Hexadecimal representations are layed out AARRGGBB to we need to do some reordering.
@ -177,37 +173,38 @@ namespace ImageProcessor
}
/// <summary>
/// Gets a value indicating whether this <see cref="Bgra"/> is empty.
/// Gets a value indicating whether this <see cref="Bgra32"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.B == 0 && this.G == 0 && this.R == 0 && this.A == 0;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="ColorVector"/> to a
/// <see cref="Bgra"/>.
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="ColorVector"/> to convert.
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Bgra(ColorVector color)
public static implicit operator Bgra32(Color color)
{
return new Bgra((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte());
color = color.Limited;
return new Bgra32((255f * color.B).ToByte(), (255f * color.G).ToByte(), (255f * color.R).ToByte(), (255f * color.A).ToByte());
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Bgra"/>.
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Hsv"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Bgra(Hsv color)
public static implicit operator Bgra32(Hsv color)
{
float s = color.S / 100;
float v = color.V / 100;
@ -215,7 +212,7 @@ namespace ImageProcessor
if (Math.Abs(s) < Epsilon)
{
byte component = (byte)(v * 255);
return new Bgra(component, component, component, 255);
return new Bgra32(component, component, component, 255);
}
float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
@ -266,20 +263,20 @@ namespace ImageProcessor
break;
}
return new Bgra((byte)Math.Round(b * 255), (byte)Math.Round(g * 255), (byte)Math.Round(r * 255));
return new Bgra32((byte)Math.Round(b * 255), (byte)Math.Round(g * 255), (byte)Math.Round(r * 255));
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
/// <see cref="Bgra"/>.
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="YCbCr"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Bgra(YCbCr color)
public static implicit operator Bgra32(YCbCr color)
{
float y = color.Y;
float cb = color.Cb - 128;
@ -289,61 +286,61 @@ namespace ImageProcessor
byte g = (y - (0.34414 * cb) - (0.71414 * cr)).ToByte();
byte r = (y + (1.402 * cr)).ToByte();
return new Bgra(b, g, r, 255);
return new Bgra32(b, g, r, 255);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
/// <see cref="Bgra"/>.
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="cmykColor">
/// The instance of <see cref="Cmyk"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra"/>.
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Bgra(Cmyk cmykColor)
public static implicit operator Bgra32(Cmyk cmykColor)
{
int red = Convert.ToInt32((1 - (cmykColor.C / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
int green = Convert.ToInt32((1 - (cmykColor.M / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
int blue = Convert.ToInt32((1 - (cmykColor.Y / 100)) * (1 - (cmykColor.K / 100)) * 255.0);
return new Bgra(blue.ToByte(), green.ToByte(), red.ToByte());
return new Bgra32(blue.ToByte(), green.ToByte(), red.ToByte());
}
/// <summary>
/// Compares two <see cref="Bgra"/> objects. The result specifies whether the values
/// of the <see cref="Bgra.B"/>, <see cref="Bgra.G"/>, <see cref="Bgra.R"/>, and <see cref="Bgra.A"/>
/// properties of the two <see cref="Bgra"/> objects are equal.
/// Compares two <see cref="Bgra32"/> objects. The result specifies whether the values
/// of the <see cref="Bgra32.B"/>, <see cref="Bgra32.G"/>, <see cref="Bgra32.R"/>, and <see cref="Bgra32.A"/>
/// properties of the two <see cref="Bgra32"/> objects are equal.
/// </summary>
/// <param name="left">
/// The <see cref="Bgra"/> on the left side of the operand.
/// The <see cref="Bgra32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Bgra"/> on the right side of the operand.
/// The <see cref="Bgra32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra left, Bgra right)
public static bool operator ==(Bgra32 left, Bgra32 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Bgra"/> objects. The result specifies whether the values
/// of the <see cref="Bgra.B"/>, <see cref="Bgra.G"/>, <see cref="Bgra.R"/>, and <see cref="Bgra.A"/>
/// properties of the two <see cref="Bgra"/> objects are unequal.
/// Compares two <see cref="Bgra32"/> objects. The result specifies whether the values
/// of the <see cref="Bgra32.B"/>, <see cref="Bgra32.G"/>, <see cref="Bgra32.R"/>, and <see cref="Bgra32.A"/>
/// properties of the two <see cref="Bgra32"/> objects are unequal.
/// </summary>
/// <param name="left">
/// The <see cref="Bgra"/> on the left side of the operand.
/// The <see cref="Bgra32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Bgra"/> on the right side of the operand.
/// The <see cref="Bgra32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra left, Bgra right)
public static bool operator !=(Bgra32 left, Bgra32 right)
{
return !left.Equals(right);
}
@ -357,9 +354,9 @@ namespace ImageProcessor
/// <param name="obj">Another object to compare to. </param>
public override bool Equals(object obj)
{
if (obj is Bgra)
if (obj is Bgra32)
{
Bgra color = (Bgra)obj;
Bgra32 color = (Bgra32)obj;
return this.BGRA == color.BGRA;
}
@ -408,7 +405,7 @@ namespace ImageProcessor
/// True if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(Bgra other)
public bool Equals(Bgra32 other)
{
return this.BGRA == other.BGRA;
}

6
src/ImageProcessor/Colors/Cmyk.cs → src/ImageProcessor/Colors/Formats/Cmyk.cs

@ -72,16 +72,16 @@ namespace ImageProcessor
&& Math.Abs(this.K) < Epsilon;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Bgra"/> to a
/// Allows the implicit conversion of an instance of <see cref="Bgra32"/> to a
/// <see cref="Cmyk"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra"/> to convert.
/// The instance of <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Cmyk"/>.
/// </returns>
public static implicit operator Cmyk(Bgra color)
public static implicit operator Cmyk(Bgra32 color)
{
float c = (255f - color.R) / 255;
float m = (255f - color.G) / 255;

6
src/ImageProcessor/Colors/Hsv.cs → src/ImageProcessor/Colors/Formats/Hsv.cs

@ -63,16 +63,16 @@ namespace ImageProcessor
&& Math.Abs(this.V) < Epsilon;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Bgra"/> to a
/// Allows the implicit conversion of an instance of <see cref="Bgra32"/> to a
/// <see cref="Hsv"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra"/> to convert.
/// The instance of <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Hsv"/>.
/// </returns>
public static implicit operator Hsv(Bgra color)
public static implicit operator Hsv(Bgra32 color)
{
float r = color.R / 255f;
float g = color.G / 255f;

6
src/ImageProcessor/Colors/YCbCr.cs → src/ImageProcessor/Colors/Formats/YCbCr.cs

@ -65,16 +65,16 @@ namespace ImageProcessor
&& Math.Abs(this.Cr) < Epsilon;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Bgra"/> to a
/// Allows the implicit conversion of an instance of <see cref="Bgra32"/> to a
/// <see cref="YCbCr"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra"/> to convert.
/// The instance of <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCr"/>.
/// </returns>
public static implicit operator YCbCr(Bgra color)
public static implicit operator YCbCr(Bgra32 color)
{
byte b = color.B;
byte g = color.G;

32
src/ImageProcessor/Common/Helpers/ImageMaths.cs

@ -12,6 +12,12 @@ namespace ImageProcessor
/// </summary>
internal static class ImageMaths
{
/// <summary>
/// Represents PI, the ratio of a circle's circumference to its diameter.
/// </summary>
// ReSharper disable once InconsistentNaming
public const float PI = 3.1415926535897931f;
/// <summary>
/// Returns the result of a B-C filter against the given value.
/// <see href="http://www.imagemagick.org/Usage/filter/#cubic_bc"/>
@ -20,11 +26,11 @@ namespace ImageProcessor
/// <param name="b">The B-Spline curve variable.</param>
/// <param name="c">The Cardinal curve variable.</param>
/// <returns>
/// The <see cref="double"/>.
/// The <see cref="float"/>.
/// </returns>
public static double GetBcValue(double x, double b, double c)
public static float GetBcValue(float x, float b, float c)
{
double temp;
float temp;
if (x < 0)
{
@ -54,19 +60,19 @@ namespace ImageProcessor
/// The value to calculate the result for.
/// </param>
/// <returns>
/// The <see cref="double"/>.
/// The <see cref="float"/>.
/// </returns>
public static double SinC(double x)
public static float SinC(float x)
{
const double Epsilon = .0001;
const float Epsilon = .00001f;
if (Math.Abs(x) > Epsilon)
{
x *= Math.PI;
return Clean(Math.Sin(x) / x);
x *= PI;
return Clean((float)Math.Sin(x) / x);
}
return 1.0;
return 1.0f;
}
/// <summary>
@ -74,15 +80,15 @@ namespace ImageProcessor
/// </summary>
/// <param name="x">The value to clean.</param>
/// <returns>
/// The <see cref="double"/>
/// The <see cref="float"/>
/// </returns>.
private static double Clean(double x)
private static float Clean(float x)
{
const double Epsilon = .0001;
const float Epsilon = .00001f;
if (Math.Abs(x) < Epsilon)
{
return 0.0;
return 0f;
}
return x;

193
src/ImageProcessor/Common/Helpers/PixelOperations - Copy.cs

@ -1,193 +0,0 @@
// <copyright file="PixelOperations.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
/// <summary>
/// Performs per-pixel operations.
/// </summary>
public static class PixelOperations
{
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from sRGB to the linear color space.
/// </summary>
private static readonly Lazy<byte[]> LinearBytes = new Lazy<byte[]>(GetLinearBytes);
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from linear to the sRGB color space.
/// </summary>
private static readonly Lazy<byte[]> SrgbBytes = new Lazy<byte[]>(GetSrgbBytes);
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from gamma to the linear color space.
/// </summary>
private static readonly Lazy<byte[]> LinearGammaBytes = new Lazy<byte[]>(GetLinearGammaBytes);
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from linear to the gamma color space.
/// </summary>
private static readonly Lazy<byte[]> GammaLinearBytes = new Lazy<byte[]>(GetGammaLinearBytes);
/// <summary>
/// Converts an pixel from an sRGB color-space to the equivalent linear color-space.
/// </summary>
/// <param name="composite">
/// The <see cref="Bgra"/> to convert.
/// </param>
/// <returns>
/// The <see cref="Bgra"/>.
/// </returns>
public static Bgra ToLinear(Bgra composite)
{
// Create only once and lazily.
// byte[] ramp = LinearGammaBytes.Value;
byte[] ramp = LinearBytes.Value;
return new Bgra(ramp[composite.B], ramp[composite.G], ramp[composite.R], composite.A);
}
/// <summary>
/// Converts a pixel from a linear color-space to the equivalent sRGB color-space.
/// </summary>
/// <param name="linear">
/// The <see cref="Bgra"/> to convert.
/// </param>
/// <returns>
/// The <see cref="Bgra"/>.
/// </returns>
public static Bgra ToSrgb(Bgra linear)
{
// Create only once and lazily.
// byte[] ramp = GammaLinearBytes.Value;
byte[] ramp = SrgbBytes.Value;
return new Bgra(ramp[linear.B], ramp[linear.G], ramp[linear.R], linear.A);
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from sRGB to the linear color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetLinearBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * SrgbToLinear(x / 255f)).ToByte();
ramp[x] = val;
}
return ramp;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from linear to the sRGB color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetSrgbBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * LinearToSrgb(x / 255f)).ToByte();
ramp[x] = val;
}
return ramp;
}
/// <summary>
/// Gets the correct linear value from an sRGB signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to convert.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float SrgbToLinear(float signal)
{
float a = 0.055f;
if (signal <= 0.04045)
{
return signal / 12.92f;
}
return (float)Math.Pow((signal + a) / (1 + a), 2.4);
}
/// <summary>
/// Gets the correct sRGB value from an linear signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to convert.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float LinearToSrgb(float signal)
{
float a = 0.055f;
if (signal <= 0.0031308)
{
return signal * 12.92f;
}
return ((float)((1 + a) * Math.Pow(signal, 1 / 2.4f))) - a;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from gamma to the linear color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetLinearGammaBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * Math.Pow(x / 255f, 2.2)).ToByte();
ramp[x] = val;
}
return ramp;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from linear to the gamma color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetGammaLinearBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * Math.Pow(x / 255f, 1 / 2.2)).ToByte();
ramp[x] = val;
}
return ramp;
}
}
}

145
src/ImageProcessor/Common/Helpers/PixelOperations.cs

@ -6,110 +6,49 @@
namespace ImageProcessor
{
using System;
using System.Collections.Concurrent;
/// <summary>
/// Performs per-pixel operations.
/// </summary>
public static class PixelOperations
{
/// <summary>
/// The array of values representing each possible value of color component
/// converted from sRGB to the linear color space.
/// </summary>
private static readonly Lazy<float[]> LinearLut = new Lazy<float[]>(GetLinearBytes);
/// <summary>
/// The array of values representing each possible value of color component
/// converted from linear to the sRGB color space.
/// </summary>
private static readonly Lazy<float[]> SrgbLut = new Lazy<float[]>(GetSrgbBytes);
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from gamma to the linear color space.
/// </summary>
private static readonly Lazy<byte[]> LinearGammaBytes = new Lazy<byte[]>(GetLinearGammaBytes);
/// <summary>
/// The array of bytes representing each possible value of color component
/// converted from linear to the gamma color space.
/// </summary>
private static readonly Lazy<byte[]> GammaLinearBytes = new Lazy<byte[]>(GetGammaLinearBytes);
/// <summary>
/// Converts an pixel from an sRGB color-space to the equivalent linear color-space.
/// </summary>
/// <param name="composite">
/// The <see cref="Bgra"/> to convert.
/// The <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// The <see cref="Bgra"/>.
/// The <see cref="Bgra32"/>.
/// </returns>
public static Bgra ToLinear(ColorVector composite)
public static Color ToLinear(Color composite)
{
// Create only once and lazily.
// byte[] ramp = LinearGammaBytes.Value;
float[] ramp = LinearLut.Value;
// TODO: Figure out a way to either cache these values quickly or perform the calcuations together.
composite.R = SrgbToLinear(composite.R);
composite.G = SrgbToLinear(composite.G);
composite.B = SrgbToLinear(composite.B);
// TODO: This just doesn't seem right to me.
return new ColorVector(ramp[(composite.B * 255).ToByte()], ramp[(composite.G * 255).ToByte()], ramp[(composite.R * 255).ToByte()], composite.A);
return composite;
}
/// <summary>
/// Converts a pixel from a linear color-space to the equivalent sRGB color-space.
/// </summary>
/// <param name="linear">
/// The <see cref="Bgra"/> to convert.
/// The <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// The <see cref="Bgra"/>.
/// </returns>
public static Bgra ToSrgb(ColorVector linear)
{
// Create only once and lazily.
// byte[] ramp = GammaLinearBytes.Value;
float[] ramp = SrgbLut.Value;
// TODO: This just doesn't seem right to me.
return new ColorVector(ramp[(linear.B * 255).ToByte()], ramp[(linear.G * 255).ToByte()], (linear.R * 255).ToByte(), linear.A);
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from sRGB to the linear color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static float[] GetLinearBytes()
{
float[] ramp = new float[256];
for (int x = 0; x < 256; ++x)
{
float val = SrgbToLinear(x / 255f);
ramp[x] = val;
}
return ramp;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from linear to the sRGB color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// The <see cref="Bgra32"/>.
/// </returns>
private static float[] GetSrgbBytes()
public static Color ToSrgb(Color linear)
{
float[] ramp = new float[256];
for (int x = 0; x < 256; ++x)
{
float val = LinearToSrgb(x / 255f);
ramp[x] = val;
}
// TODO: Figure out a way to either cache these values quickly or perform the calcuations together.
linear.R = LinearToSrgb(linear.R);
linear.G = LinearToSrgb(linear.G);
linear.B = LinearToSrgb(linear.B);
return ramp;
return linear;
}
/// <summary>
@ -123,14 +62,12 @@ namespace ImageProcessor
/// </returns>
private static float SrgbToLinear(float signal)
{
float a = 0.055f;
if (signal <= 0.04045)
if (signal <= 0.04045f)
{
return signal / 12.92f;
}
return (float)Math.Pow((signal + a) / (1 + a), 2.4);
return (float)Math.Pow((signal + 0.055f) / 1.055f, 2.4f);
}
/// <summary>
@ -144,52 +81,12 @@ namespace ImageProcessor
/// </returns>
private static float LinearToSrgb(float signal)
{
float a = 0.055f;
if (signal <= 0.0031308)
if (signal <= 0.0031308f)
{
return signal * 12.92f;
}
return ((float)((1 + a) * Math.Pow(signal, 1 / 2.4f))) - a;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from gamma to the linear color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetLinearGammaBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * Math.Pow(x / 255f, 2.2)).ToByte();
ramp[x] = val;
}
return ramp;
}
/// <summary>
/// Gets an array of bytes representing each possible value of color component
/// converted from linear to the gamma color space.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private static byte[] GetGammaLinearBytes()
{
byte[] ramp = new byte[256];
for (int x = 0; x < 256; ++x)
{
byte val = (255f * Math.Pow(x / 255f, 1 / 2.2)).ToByte();
ramp[x] = val;
}
return ramp;
return (1.055f * (float)Math.Pow(signal, 0.41666666f)) - 0.055f;
}
}
}

4
src/ImageProcessor/Filters/Alpha.cs

@ -49,9 +49,9 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra color = source[x, y];
Bgra32 color = source[x, y];
double a = color.A * alpha;
target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte());
target[x, y] = new Bgra32(color.B, color.G, color.R, a.ToByte());
}
}
});

13
src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs

@ -41,8 +41,8 @@ namespace ImageProcessor.Filters
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
ColorMatrix matrix = this.Value;
Bgra previousColor = source[0, 0];
Bgra pixelValue = this.ApplyMatrix(previousColor, matrix);
Bgra32 previousColor = source[0, 0];
Bgra32 pixelValue = this.ApplyMatrix(previousColor, matrix);
Parallel.For(
startY,
@ -53,7 +53,7 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra sourceColor = source[x, y];
Bgra32 sourceColor = source[x, y];
// Check if this is the same as the last pixel. If so use that value
// rather than calculating it again. This is an inexpensive optimization.
@ -78,9 +78,9 @@ namespace ImageProcessor.Filters
/// <param name="sourceColor">The source color.</param>
/// <param name="matrix">The matrix.</param>
/// <returns>
/// The <see cref="Bgra"/>.
/// The <see cref="Bgra32"/>.
/// </returns>
private Bgra ApplyMatrix(Bgra sourceColor, ColorMatrix matrix)
private Bgra32 ApplyMatrix(Bgra32 sourceColor, ColorMatrix matrix)
{
bool gamma = this.GammaAdjust;
@ -100,7 +100,8 @@ namespace ImageProcessor.Filters
byte b = ((sr * matrix.Matrix02) + (sg * matrix.Matrix12) + (sb * matrix.Matrix22) + (sa * matrix.Matrix32) + (255f * matrix.Matrix42)).ToByte();
byte a = ((sr * matrix.Matrix03) + (sg * matrix.Matrix13) + (sb * matrix.Matrix23) + (sa * matrix.Matrix33) + (255f * matrix.Matrix43)).ToByte();
return gamma ? PixelOperations.ToSrgb(new Bgra(b, g, r, a)) : new Bgra(b, g, r, a);
// TODO: Fix this.
return gamma ? (Bgra32)PixelOperations.ToSrgb(new Bgra32(b, g, r, a)) : new Bgra32(b, g, r, a);
}
}
}

4
src/ImageProcessor/Filters/Contrast.cs

@ -49,7 +49,7 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
Bgra sourceColor = source[x, y];
Bgra32 sourceColor = source[x, y];
sourceColor = PixelOperations.ToLinear(sourceColor);
double r = sourceColor.R / 255.0;
@ -73,7 +73,7 @@ namespace ImageProcessor.Filters
b *= 255;
b = b.ToByte();
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
Bgra32 destinationColor = new Bgra32(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}

4
src/ImageProcessor/Filters/Invert.cs

@ -30,8 +30,8 @@ namespace ImageProcessor.Filters
for (int x = startX; x < endX; x++)
{
// TODO: This doesn't work for gamma test images.
Bgra color = source[x, y];
Bgra targetColor = new Bgra((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A);
Bgra32 color = source[x, y];
Bgra32 targetColor = new Bgra32((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A);
target[x, y] = targetColor;
}
}

4
src/ImageProcessor/Formats/Gif/GifEncoder.cs

@ -127,7 +127,7 @@ namespace ImageProcessor.Formats
QuantizedImage quantizedImage = quantizer.Quantize(image);
// Grab the pallete and write it to the stream.
Bgra[] pallete = quantizedImage.Palette;
Bgra32[] pallete = quantizedImage.Palette;
int pixelCount = pallete.Length;
// Get max colors for bit depth.
@ -137,7 +137,7 @@ namespace ImageProcessor.Formats
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
Bgra color = pallete[i];
Bgra32 color = pallete[i];
colorTable[offset + 2] = color.B;
colorTable[offset + 1] = color.G;
colorTable[offset + 0] = color.R;

34
src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs

@ -70,7 +70,7 @@ namespace ImageProcessor.Formats
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected override void InitialQuantizePixel(Bgra pixel)
protected override void InitialQuantizePixel(Bgra32 pixel)
{
// Add the color to the Octree
this.octree.AddColor(pixel);
@ -85,7 +85,7 @@ namespace ImageProcessor.Formats
/// <returns>
/// The quantized value
/// </returns>
protected override byte QuantizePixel(Bgra pixel)
protected override byte QuantizePixel(Bgra32 pixel)
{
// The color at [maxColors] is set to transparent
byte paletteIndex = (byte)this.maxColors;
@ -105,13 +105,13 @@ namespace ImageProcessor.Formats
/// <returns>
/// The new color palette
/// </returns>
protected override List<Bgra> GetPalette()
protected override List<Bgra32> GetPalette()
{
// First off convert the Octree to maxColors colors
List<Bgra> palette = this.octree.Palletize(Math.Max(this.maxColors - 1, 1));
List<Bgra32> palette = this.octree.Palletize(Math.Max(this.maxColors - 1, 1));
// Add empty color for transparency
palette.Add(Bgra.Empty);
palette.Add(Bgra32.Empty);
return palette;
}
@ -190,9 +190,9 @@ namespace ImageProcessor.Formats
/// Add a given color value to the Octree
/// </summary>
/// <param name="pixel">
/// The <see cref="Bgra"/>containing color information to add.
/// The <see cref="Bgra32"/>containing color information to add.
/// </param>
public void AddColor(Bgra pixel)
public void AddColor(Bgra32 pixel)
{
// Check if this request is for the same color as the last
if (this.previousColor == pixel.BGRA)
@ -226,7 +226,7 @@ namespace ImageProcessor.Formats
/// <returns>
/// An <see cref="List{Bgra}"/> with the palletized colors
/// </returns>
public List<Bgra> Palletize(int colorCount)
public List<Bgra32> Palletize(int colorCount)
{
while (this.Leaves > colorCount)
{
@ -234,7 +234,7 @@ namespace ImageProcessor.Formats
}
// Now palletize the nodes
List<Bgra> palette = new List<Bgra>(this.Leaves);
List<Bgra32> palette = new List<Bgra32>(this.Leaves);
int paletteIndex = 0;
this.root.ConstructPalette(palette, ref paletteIndex);
@ -246,12 +246,12 @@ namespace ImageProcessor.Formats
/// Get the palette index for the passed color
/// </summary>
/// <param name="pixel">
/// The <see cref="Bgra"/> containing the pixel data.
/// The <see cref="Bgra32"/> containing the pixel data.
/// </param>
/// <returns>
/// The index of the given structure.
/// </returns>
public int GetPaletteIndex(Bgra pixel)
public int GetPaletteIndex(Bgra32 pixel)
{
return this.root.GetPaletteIndex(pixel, 0);
}
@ -387,7 +387,7 @@ namespace ImageProcessor.Formats
/// <param name="octree">
/// The tree to which this node belongs
/// </param>
public void AddColor(Bgra pixel, int colorBits, int level, Octree octree)
public void AddColor(Bgra32 pixel, int colorBits, int level, Octree octree)
{
// Update the color information if this is a leaf
if (this.leaf)
@ -458,7 +458,7 @@ namespace ImageProcessor.Formats
/// <param name="index">
/// The current palette index
/// </param>
public void ConstructPalette(List<Bgra> palette, ref int index)
public void ConstructPalette(List<Bgra32> palette, ref int index)
{
if (this.leaf)
{
@ -470,7 +470,7 @@ namespace ImageProcessor.Formats
byte b = (this.blue / this.pixelCount).ToByte();
// And set the color of the palette entry
palette.Add(new Bgra(b, g, r));
palette.Add(new Bgra32(b, g, r));
}
else
{
@ -489,7 +489,7 @@ namespace ImageProcessor.Formats
/// Return the palette index for the passed color
/// </summary>
/// <param name="pixel">
/// The <see cref="Bgra"/> representing the pixel.
/// The <see cref="Bgra32"/> representing the pixel.
/// </param>
/// <param name="level">
/// The level.
@ -497,7 +497,7 @@ namespace ImageProcessor.Formats
/// <returns>
/// The <see cref="int"/> representing the index of the pixel in the palette.
/// </returns>
public int GetPaletteIndex(Bgra pixel, int level)
public int GetPaletteIndex(Bgra32 pixel, int level)
{
int index = this.paletteIndex;
@ -527,7 +527,7 @@ namespace ImageProcessor.Formats
/// <param name="pixel">
/// The pixel to add.
/// </param>
public void Increment(Bgra pixel)
public void Increment(Bgra32 pixel)
{
this.pixelCount++;
this.red += pixel.R;

6
src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs

@ -19,7 +19,7 @@ namespace ImageProcessor.Formats
/// <param name="height">The image height.</param>
/// <param name="palette">The color palette.</param>
/// <param name="pixels">The quantized pixels.</param>
public QuantizedImage(int width, int height, Bgra[] palette, byte[] pixels)
public QuantizedImage(int width, int height, Bgra32[] palette, byte[] pixels)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
@ -51,7 +51,7 @@ namespace ImageProcessor.Formats
/// <summary>
/// Gets the color palette of this <see cref="T:QuantizedImage"/>.
/// </summary>
public Bgra[] Palette { get; }
public Bgra32[] Palette { get; }
/// <summary>
/// Gets the pixels of this <see cref="T:QuantizedImage"/>.
@ -74,7 +74,7 @@ namespace ImageProcessor.Formats
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 4;
Bgra color = this.Palette[this.Pixels[i]];
Bgra32 color = this.Palette[this.Pixels[i]];
bgraPixels[offset + 0] = color.B;
bgraPixels[offset + 1] = color.G;
bgraPixels[offset + 2] = color.R;

12
src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs

@ -56,7 +56,7 @@ namespace ImageProcessor.Formats
byte[] quantizedPixels = new byte[width * height];
List<Bgra> palette = this.GetPalette();
List<Bgra32> palette = this.GetPalette();
this.SecondPass(imageBase, quantizedPixels, width, height);
@ -95,7 +95,7 @@ namespace ImageProcessor.Formats
int i = 0;
// Convert the first pixel, so that I have values going into the loop
Bgra previousPixel = source[0, 0];
Bgra32 previousPixel = source[0, 0];
byte pixelValue = this.QuantizePixel(previousPixel);
output[0] = pixelValue;
@ -104,7 +104,7 @@ namespace ImageProcessor.Formats
{
for (int x = 0; x < width; x++)
{
Bgra sourcePixel = source[x, y];
Bgra32 sourcePixel = source[x, y];
// Check if this is the same as the last pixel. If so use that value
// rather than calculating it again. This is an inexpensive optimization.
@ -132,7 +132,7 @@ namespace ImageProcessor.Formats
/// This function need only be overridden if your quantize algorithm needs two passes,
/// such as an Octree quantizer.
/// </remarks>
protected virtual void InitialQuantizePixel(Bgra pixel)
protected virtual void InitialQuantizePixel(Bgra32 pixel)
{
}
@ -145,7 +145,7 @@ namespace ImageProcessor.Formats
/// <returns>
/// The quantized value
/// </returns>
protected abstract byte QuantizePixel(Bgra pixel);
protected abstract byte QuantizePixel(Bgra32 pixel);
/// <summary>
/// Retrieve the palette for the quantized image
@ -153,6 +153,6 @@ namespace ImageProcessor.Formats
/// <returns>
/// The new color palette
/// </returns>
protected abstract List<Bgra> GetPalette();
protected abstract List<Bgra32> GetPalette();
}
}

4
src/ImageProcessor/IImageBase.cs

@ -66,8 +66,8 @@ namespace ImageProcessor
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Bgra"/> at the specified position.</returns>
Bgra this[int x, int y] { get; set; }
/// <returns>The <see cref="Bgra32"/> at the specified position.</returns>
Bgra32 this[int x, int y] { get; set; }
/// <summary>
/// Sets the pixel array of the image.

6
src/ImageProcessor/ImageBase.cs

@ -130,8 +130,8 @@ namespace ImageProcessor
/// The y-coordinate of the pixel. Must be greater
/// than zero and smaller than the width of the pixel.
/// </param>
/// <returns>The <see cref="Bgra"/> at the specified position.</returns>
public Bgra this[int x, int y]
/// <returns>The <see cref="Bgra32"/> at the specified position.</returns>
public Bgra32 this[int x, int y]
{
get
{
@ -148,7 +148,7 @@ namespace ImageProcessor
#endif
int start = ((y * this.Width) + x) * 4;
return new Bgra(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
return new Bgra32(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
}
set

12
src/ImageProcessor/ImageProcessor.csproj

@ -41,8 +41,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Colors\Cmyk.cs" />
<Compile Include="Colors\ColorVector.cs" />
<Compile Include="Colors\Formats\Cmyk.cs" />
<Compile Include="Colors\Color.cs" />
<Compile Include="Common\Extensions\EnumerableExtensions.cs" />
<Compile Include="Common\Helpers\ImageMaths.cs" />
<Compile Include="Common\Helpers\PixelOperations.cs" />
@ -82,8 +82,8 @@
<Compile Include="Numerics\Rectangle.cs" />
<Compile Include="ParallelImageProcessor.cs" />
<Compile Include="IImageProcessor.cs" />
<Compile Include="Colors\Hsv.cs" />
<Compile Include="Colors\YCbCr.cs" />
<Compile Include="Colors\Formats\Hsv.cs" />
<Compile Include="Colors\Formats\YCbCr.cs" />
<Compile Include="Common\Extensions\ByteExtensions.cs" />
<Compile Include="Common\Extensions\ComparableExtensions.cs" />
<Compile Include="Formats\Bmp\BmpCompression.cs" />
@ -198,7 +198,7 @@
<Compile Include="IImageBase.cs" />
<Compile Include="ImageProperty.cs" />
<Compile Include="ImageFrame.cs" />
<Compile Include="Colors\Bgra.cs" />
<Compile Include="Colors\Formats\Bgra32.cs" />
<Compile Include="Common\Exceptions\ImageFormatException.cs" />
<Compile Include="Common\Helpers\Guard.cs" />
<Compile Include="Formats\Gif\GifDecoder.cs" />
@ -237,7 +237,7 @@
<None Include="Formats\Jpg\README.md" />
<None Include="Formats\Png\Zlib\README.md" />
<None Include="project.json" />
<None Include="stylecop.json" />
<AdditionalFiles Include="stylecop.json" />
</ItemGroup>
<ItemGroup>
<None Include="Formats\Bmp\README.md" />

1
src/ImageProcessor/ImageProcessor.csproj.DotSettings

@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors_005Cformats/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cextensions/@EntryIndexedValue">True</s:Boolean>

12
src/ImageProcessor/Samplers/Resamplers/BicubicResampler.cs

@ -12,28 +12,28 @@ namespace ImageProcessor.Samplers
public class BicubicResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
// The coefficient.
double a = -0.5;
float a = -0.5f;
if (x < 0)
{
x = -x;
}
double result = 0;
float result = 0;
if (x <= 1)
{
result = (((1.5 * x) - 2.5) * x * x) + 1;
result = (((1.5f * x) - 2.5f) * x * x) + 1;
}
else if (x < 2)
{
result = (((((a * x) + 2.5) * x) - 4) * x) + 2;
result = (((((a * x) + 2.5f) * x) - 4) * x) + 2;
}
return result;

4
src/ImageProcessor/Samplers/Resamplers/BoxResampler.cs

@ -11,10 +11,10 @@ namespace ImageProcessor.Samplers
public class BoxResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 0.5;
public float Radius => 0.5f;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{

8
src/ImageProcessor/Samplers/Resamplers/CatmullRomResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class CatmullRomResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 0;
const double C = 1 / 2d;
const float B = 0;
const float C = 1 / 2f;
return ImageMaths.GetBcValue(x, B, C);
}

8
src/ImageProcessor/Samplers/Resamplers/HermiteResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class HermiteResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 0;
const double C = 0;
const float B = 0;
const float C = 0;
return ImageMaths.GetBcValue(x, B, C);
}

15
src/ImageProcessor/Samplers/Resamplers/IResampler.cs

@ -1,22 +1,27 @@
namespace ImageProcessor.Samplers
// <copyright file="IResampler.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Samplers
{
/// <summary>
/// Encasulates an interpolation algorithm for resampling images.
/// Encapsulates an interpolation algorithm for resampling images.
/// </summary>
public interface IResampler
{
/// <summary>
/// Gets the radius in which to sample pixels.
/// </summary>
double Radius { get; }
float Radius { get; }
/// <summary>
/// Gets the result of the interpolation algorithm.
/// </summary>
/// <param name="x">The value to process.</param>
/// <returns>
/// The <see cref="double"/>
/// The <see cref="float"/>
/// </returns>
double GetValue(double x);
float GetValue(float x);
}
}

4
src/ImageProcessor/Samplers/Resamplers/Lanczos3Resampler.cs

@ -12,10 +12,10 @@ namespace ImageProcessor.Samplers
public class Lanczos3Resampler : IResampler
{
/// <inheritdoc/>
public double Radius => 3;
public float Radius => 3;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{

4
src/ImageProcessor/Samplers/Resamplers/Lanczos5Resampler.cs

@ -12,10 +12,10 @@ namespace ImageProcessor.Samplers
public class Lanczos5Resampler : IResampler
{
/// <inheritdoc/>
public double Radius => 5;
public float Radius => 5;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{

4
src/ImageProcessor/Samplers/Resamplers/Lanczos8Resampler.cs

@ -12,10 +12,10 @@ namespace ImageProcessor.Samplers
public class Lanczos8Resampler : IResampler
{
/// <inheritdoc/>
public double Radius => 8;
public float Radius => 8;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{

8
src/ImageProcessor/Samplers/Resamplers/MitchellNetravaliResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class MitchellNetravaliResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 1 / 3d;
const double C = 1 / 3d;
const float B = 1 / 3f;
const float C = 1 / 3f;
return ImageMaths.GetBcValue(x, B, C);
}

8
src/ImageProcessor/Samplers/Resamplers/RobidouxResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class RobidouxResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 0.3782;
const double C = 0.3109;
const float B = 0.3782f;
const float C = 0.3109f;
return ImageMaths.GetBcValue(x, B, C);
}

8
src/ImageProcessor/Samplers/Resamplers/RobidouxSharpResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class RobidouxSharpResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 0.2620;
const double C = 0.3690;
const float B = 0.2620f;
const float C = 0.3690f;
return ImageMaths.GetBcValue(x, B, C);
}

8
src/ImageProcessor/Samplers/Resamplers/RobidouxSoftResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class RobidouxSoftResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 0.6796;
const double C = 0.1602;
const float B = 0.6796f;
const float C = 0.1602f;
return ImageMaths.GetBcValue(x, B, C);
}

8
src/ImageProcessor/Samplers/Resamplers/SplineResampler.cs

@ -12,13 +12,13 @@ namespace ImageProcessor.Samplers
public class SplineResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 2;
public float Radius => 2;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
const double B = 1;
const double C = 0;
const float B = 1;
const float C = 0;
return ImageMaths.GetBcValue(x, B, C);
}

4
src/ImageProcessor/Samplers/Resamplers/TriangleResampler.cs

@ -11,10 +11,10 @@ namespace ImageProcessor.Samplers
public class TriangleResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 1;
public float Radius => 1;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{

6
src/ImageProcessor/Samplers/Resamplers/WelchResampler.cs

@ -12,10 +12,10 @@ namespace ImageProcessor.Samplers
public class WelchResampler : IResampler
{
/// <inheritdoc/>
public double Radius => 3;
public float Radius => 3;
/// <inheritdoc/>
public double GetValue(double x)
public float GetValue(float x)
{
if (x < 0)
{
@ -24,7 +24,7 @@ namespace ImageProcessor.Samplers
if (x < 3)
{
return ImageMaths.SinC(x) * (1.0 - (x * x / 9.0));
return ImageMaths.SinC(x) * (1.0f - (x * x / 9.0f));
}
return 0;

232
src/ImageProcessor/Samplers/Resize - Copy.cs

@ -1,232 +0,0 @@
// <copyright file="Resize.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Samplers
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the resizing of images using various resampling algorithms.
/// </summary>
public class Resize : ParallelImageProcessor
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.0001f;
/// <summary>
/// The horizontal weights.
/// </summary>
private Weights[] horizontalWeights;
/// <summary>
/// The vertical weights.
/// </summary>
private Weights[] verticalWeights;
/// <summary>
/// Initializes a new instance of the <see cref="Resize"/> class.
/// </summary>
/// <param name="sampler">
/// The sampler to perform the resize operation.
/// </param>
public Resize(IResampler sampler)
{
Guard.NotNull(sampler, nameof(sampler));
this.Sampler = sampler;
}
/// <summary>
/// Gets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler { get; }
/// <inheritdoc/>
protected override void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle)
{
this.horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width);
this.verticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height);
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int targetY = targetRectangle.Y;
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
Parallel.For(
startY,
endY,
y =>
{
if (y >= targetY && y < targetBottom)
{
List<Weight> verticalValues = this.verticalWeights[y].Values;
double verticalSum = this.verticalWeights[y].Sum;
for (int x = startX; x < endX; x++)
{
List<Weight> horizontalValues = this.horizontalWeights[x].Values;
double horizontalSum = this.horizontalWeights[x].Sum;
// Destination color components
double r = 0;
double g = 0;
double b = 0;
double a = 0;
foreach (Weight yw in verticalValues)
{
if (Math.Abs(yw.Value) < Epsilon)
{
continue;
}
int originY = yw.Index;
foreach (Weight xw in horizontalValues)
{
if (Math.Abs(xw.Value) < Epsilon)
{
continue;
}
int originX = xw.Index;
Bgra sourceColor = source[originX, originY];
//ColorVector sourceColor = PixelOperations.ToLinear(source[originX, originY]);
r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
}
}
// TODO: Double cast.
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
//Bgra destinationColor = PixelOperations.ToSrgb(new ColorVector(b, g, r, a));
target[x, y] = destinationColor;
}
}
});
}
/// <summary>
/// Computes the weights to apply at each pixel when resizing.
/// </summary>
/// <param name="destinationSize">The destination section size.</param>
/// <param name="sourceSize">The source section size.</param>
/// <returns>
/// The <see cref="T:Weights[]"/>.
/// </returns>
private Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
IResampler sampler = this.Sampler;
double du = sourceSize / (double)destinationSize;
double scale = du;
if (scale < 1)
{
scale = 1;
}
double ru = Math.Ceiling(scale * sampler.Radius);
Weights[] result = new Weights[destinationSize];
for (int i = 0; i < destinationSize; i++)
{
double fu = ((i + .5) * du) - 0.5;
int startU = (int)Math.Ceiling(fu - ru);
if (startU < 0)
{
startU = 0;
}
int endU = (int)Math.Floor(fu + ru);
if (endU > sourceSize - 1)
{
endU = sourceSize - 1;
}
double sum = 0;
result[i] = new Weights();
for (int a = startU; a <= endU; a++)
{
double w = 255 * sampler.GetValue((a - fu) / scale);
if (Math.Abs(w) > Epsilon)
{
sum += w;
result[i].Values.Add(new Weight(a, w));
}
}
result[i].Sum = sum;
}
return result;
}
/// <summary>
/// Represents the weight to be added to a scaled pixel.
/// </summary>
protected struct Weight
{
/// <summary>
/// The pixel index.
/// </summary>
public readonly int Index;
/// <summary>
/// The result of the interpolation algorithm.
/// </summary>
public readonly double Value;
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> struct.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
public Weight(int index, double value)
{
this.Index = index;
this.Value = value;
}
}
/// <summary>
/// Represents a collection of weights and their sum.
/// </summary>
protected class Weights
{
/// <summary>
/// Initializes a new instance of the <see cref="Weights"/> class.
/// </summary>
public Weights()
{
this.Values = new List<Weight>();
}
/// <summary>
/// Gets or sets the values.
/// </summary>
public List<Weight> Values { get; set; }
/// <summary>
/// Gets or sets the sum.
/// </summary>
public double Sum { get; set; }
}
}
}

48
src/ImageProcessor/Samplers/Resize.cs

@ -62,7 +62,6 @@ namespace ImageProcessor.Samplers
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
//Vector<int> endVX = new Vector<int>(targetRectangle.Right);
Parallel.For(
startY,
@ -72,18 +71,15 @@ namespace ImageProcessor.Samplers
if (y >= targetY && y < targetBottom)
{
List<Weight> verticalValues = this.verticalWeights[y].Values;
double verticalSum = this.verticalWeights[y].Sum;
float verticalSum = this.verticalWeights[y].Sum;
for (int x = startX; x < endX; x++)
{
List<Weight> horizontalValues = this.horizontalWeights[x].Values;
double horizontalSum = this.horizontalWeights[x].Sum;
float horizontalSum = this.horizontalWeights[x].Sum;
// Destination color components
double r = 0;
double g = 0;
double b = 0;
double a = 0;
Color destination = new Color(0, 0, 0, 0);
foreach (Weight yw in verticalValues)
{
@ -102,19 +98,20 @@ namespace ImageProcessor.Samplers
}
int originX = xw.Index;
ColorVector sourceColor = source[originX, originY];
//sourceColor = PixelOperations.ToLinear(sourceColor);
Color sourceColor = source[originX, originY];
sourceColor = PixelOperations.ToLinear(sourceColor);
r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
float weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
destination.R += sourceColor.R * weight;
destination.G += sourceColor.G * weight;
destination.B += sourceColor.B * weight;
destination.A += sourceColor.A * weight;
}
}
// TODO: Double cast?
Bgra destinationColor = new ColorVector(b, g, r, a);//PixelOperations.ToSrgb(new ColorVector(b, g, r, a));
target[x, y] = destinationColor;
destination = PixelOperations.ToSrgb(destination);
target[x, y] = destination;
}
}
});
@ -131,15 +128,15 @@ namespace ImageProcessor.Samplers
private Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
IResampler sampler = this.Sampler;
double du = sourceSize / (double)destinationSize;
double scale = du;
float du = sourceSize / (float)destinationSize;
float scale = du;
if (scale < 1)
{
scale = 1;
}
double ru = Math.Ceiling(scale * sampler.Radius);
float ru = (float)Math.Ceiling(scale * sampler.Radius);
Weights[] result = new Weights[destinationSize];
Parallel.For(
@ -147,7 +144,7 @@ namespace ImageProcessor.Samplers
destinationSize,
i =>
{
double fu = ((i + .5) * du) - 0.5;
float fu = ((i + .5f) * du) - 0.5f;
int startU = (int)Math.Ceiling(fu - ru);
if (startU < 0)
@ -162,12 +159,13 @@ namespace ImageProcessor.Samplers
endU = sourceSize - 1;
}
double sum = 0;
float sum = 0;
result[i] = new Weights();
for (int a = startU; a <= endU; a++)
{
double w = 255 * sampler.GetValue((a - fu) / scale);
// TODO: CHeck multiplier here
float w = 255f * sampler.GetValue((a - fu) / scale);
if (Math.Abs(w) > Epsilon)
{
@ -195,14 +193,14 @@ namespace ImageProcessor.Samplers
/// <summary>
/// The result of the interpolation algorithm.
/// </summary>
public readonly double Value;
public readonly float Value;
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> struct.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
public Weight(int index, double value)
public Weight(int index, float value)
{
this.Index = index;
this.Value = value;
@ -230,7 +228,7 @@ namespace ImageProcessor.Samplers
/// <summary>
/// Gets or sets the sum.
/// </summary>
public double Sum { get; set; }
public float Sum { get; set; }
}
}
}

72
tests/ImageProcessor.Tests/Colors/ColorConversionTests.cs

@ -21,7 +21,7 @@ namespace ImageProcessor.Tests
public class ColorConversionTests
{
/// <summary>
/// Tests the implicit conversion from <see cref="Bgra"/> to <see cref="YCbCr"/>.
/// Tests the implicit conversion from <see cref="Bgra32"/> to <see cref="YCbCr"/>.
/// </summary>
[Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
@ -29,7 +29,7 @@ namespace ImageProcessor.Tests
public void BgrToYCbCr()
{
// White
Bgra color = new Bgra(255, 255, 255, 255);
Bgra32 color = new Bgra32(255, 255, 255, 255);
YCbCr yCbCr = color;
Assert.Equal(255, yCbCr.Y);
@ -37,14 +37,14 @@ namespace ImageProcessor.Tests
Assert.Equal(128, yCbCr.Cr);
// Black
Bgra color2 = new Bgra(0, 0, 0, 255);
Bgra32 color2 = new Bgra32(0, 0, 0, 255);
YCbCr yCbCr2 = color2;
Assert.Equal(0, yCbCr2.Y);
Assert.Equal(128, yCbCr2.Cb);
Assert.Equal(128, yCbCr2.Cr);
// Grey
Bgra color3 = new Bgra(128, 128, 128, 255);
Bgra32 color3 = new Bgra32(128, 128, 128, 255);
YCbCr yCbCr3 = color3;
Assert.Equal(128, yCbCr3.Y);
Assert.Equal(128, yCbCr3.Cb);
@ -52,7 +52,7 @@ namespace ImageProcessor.Tests
}
/// <summary>
/// Tests the implicit conversion from <see cref="YCbCr"/> to <see cref="Bgra"/>.
/// Tests the implicit conversion from <see cref="YCbCr"/> to <see cref="Bgra32"/>.
/// </summary>
[Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
@ -61,7 +61,7 @@ namespace ImageProcessor.Tests
{
// White
YCbCr yCbCr = new YCbCr(255, 128, 128);
Bgra color = yCbCr;
Bgra32 color = yCbCr;
Assert.Equal(255, color.B);
Assert.Equal(255, color.G);
@ -70,7 +70,7 @@ namespace ImageProcessor.Tests
// Black
YCbCr yCbCr2 = new YCbCr(0, 128, 128);
Bgra color2 = yCbCr2;
Bgra32 color2 = yCbCr2;
Assert.Equal(0, color2.B);
Assert.Equal(0, color2.G);
@ -79,7 +79,7 @@ namespace ImageProcessor.Tests
// Grey
YCbCr yCbCr3 = new YCbCr(128, 128, 128);
Bgra color3 = yCbCr3;
Bgra32 color3 = yCbCr3;
Assert.Equal(128, color3.B);
Assert.Equal(128, color3.G);
@ -88,7 +88,7 @@ namespace ImageProcessor.Tests
}
/// <summary>
/// Tests the implicit conversion from <see cref="Bgra"/> to <see cref="Hsv"/>.
/// Tests the implicit conversion from <see cref="Bgra32"/> to <see cref="Hsv"/>.
/// </summary>
[Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
@ -96,7 +96,7 @@ namespace ImageProcessor.Tests
public void BgrToHsv()
{
// Black
Bgra b = new Bgra(0, 0, 0, 255);
Bgra32 b = new Bgra32(0, 0, 0, 255);
Hsv h = b;
Assert.Equal(0, h.H);
@ -104,7 +104,7 @@ namespace ImageProcessor.Tests
Assert.Equal(0, h.V);
// White
Bgra color = new Bgra(255, 255, 255, 255);
Bgra32 color = new Bgra32(255, 255, 255, 255);
Hsv hsv = color;
Assert.Equal(0, hsv.H);
@ -112,7 +112,7 @@ namespace ImageProcessor.Tests
Assert.Equal(100, hsv.V);
// Dark moderate pink.
Bgra color2 = new Bgra(106, 64, 128, 255);
Bgra32 color2 = new Bgra32(106, 64, 128, 255);
Hsv hsv2 = color2;
Assert.Equal(320.6, hsv2.H, 1);
@ -120,7 +120,7 @@ namespace ImageProcessor.Tests
Assert.Equal(50.2, hsv2.V, 1);
// Ochre.
Bgra color3 = new Bgra(34, 119, 204, 255);
Bgra32 color3 = new Bgra32(34, 119, 204, 255);
Hsv hsv3 = color3;
Assert.Equal(30, hsv3.H, 1);
@ -129,22 +129,22 @@ namespace ImageProcessor.Tests
}
/// <summary>
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Bgra"/>.
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Bgra32"/>.
/// </summary>
[Fact]
public void HsvToBgr()
{
// Dark moderate pink.
Hsv hsv = new Hsv(320.6f, 50, 50.2f);
Bgra bgra = hsv;
Bgra32 bgra32 = hsv;
Assert.Equal(bgra.B, 106);
Assert.Equal(bgra.G, 64);
Assert.Equal(bgra.R, 128);
Assert.Equal(bgra32.B, 106);
Assert.Equal(bgra32.G, 64);
Assert.Equal(bgra32.R, 128);
// Ochre
Hsv hsv2 = new Hsv(30, 83.3f, 80);
Bgra bgra2 = hsv2;
Bgra32 bgra2 = hsv2;
Assert.Equal(bgra2.B, 34);
Assert.Equal(bgra2.G, 119);
@ -152,7 +152,7 @@ namespace ImageProcessor.Tests
// White
Hsv hsv3 = new Hsv(0, 0, 100);
Bgra bgra3 = hsv3;
Bgra32 bgra3 = hsv3;
Assert.Equal(bgra3.B, 255);
Assert.Equal(bgra3.G, 255);
@ -162,14 +162,14 @@ namespace ImageProcessor.Tests
Random random = new Random(0);
for (int i = 0; i < 1000; i++)
{
Bgra bgra4 = new Bgra((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
Bgra32 bgra4 = new Bgra32((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
Hsv hsb4 = bgra4;
Assert.Equal(bgra4, (Bgra)hsb4);
Assert.Equal(bgra4, (Bgra32)hsb4);
}
}
/// <summary>
/// Tests the implicit conversion from <see cref="Bgra"/> to <see cref="Cmyk"/>.
/// Tests the implicit conversion from <see cref="Bgra32"/> to <see cref="Cmyk"/>.
/// </summary>
[Fact]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation",
@ -177,7 +177,7 @@ namespace ImageProcessor.Tests
public void BgrToCmyk()
{
// White
Bgra color = new Bgra(255, 255, 255, 255);
Bgra32 color = new Bgra32(255, 255, 255, 255);
Cmyk cmyk = color;
Assert.Equal(0, cmyk.C);
@ -186,7 +186,7 @@ namespace ImageProcessor.Tests
Assert.Equal(0, cmyk.K);
// Black
Bgra color2 = new Bgra(0, 0, 0, 255);
Bgra32 color2 = new Bgra32(0, 0, 0, 255);
Cmyk cmyk2 = color2;
Assert.Equal(0, cmyk2.C);
Assert.Equal(0, cmyk2.M);
@ -194,7 +194,7 @@ namespace ImageProcessor.Tests
Assert.Equal(100, cmyk2.K);
// Grey
Bgra color3 = new Bgra(128, 128, 128, 255);
Bgra32 color3 = new Bgra32(128, 128, 128, 255);
Cmyk cmyk3 = color3;
Assert.Equal(0, cmyk3.C);
Assert.Equal(0, cmyk3.M);
@ -202,7 +202,7 @@ namespace ImageProcessor.Tests
Assert.Equal(49.8, cmyk3.K, 1); // Checked with other tools.
// Cyan
Bgra color4 = new Bgra(255, 255, 0, 255);
Bgra32 color4 = new Bgra32(255, 255, 0, 255);
Cmyk cmyk4 = color4;
Assert.Equal(100, cmyk4.C);
Assert.Equal(0, cmyk4.M);
@ -211,22 +211,22 @@ namespace ImageProcessor.Tests
}
/// <summary>
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Bgra"/>.
/// Tests the implicit conversion from <see cref="Hsv"/> to <see cref="Bgra32"/>.
/// </summary>
[Fact]
public void CmykToBgr()
{
// Dark moderate pink.
Cmyk cmyk = new Cmyk(49.8f, 74.9f, 58.4f, 0);
Bgra bgra = cmyk;
Bgra32 bgra32 = cmyk;
Assert.Equal(bgra.B, 106);
Assert.Equal(bgra.G, 64);
Assert.Equal(bgra.R, 128);
Assert.Equal(bgra32.B, 106);
Assert.Equal(bgra32.G, 64);
Assert.Equal(bgra32.R, 128);
// Ochre
Cmyk cmyk2 = new Cmyk(20, 53.3f, 86.7f, 0);
Bgra bgra2 = cmyk2;
Bgra32 bgra2 = cmyk2;
Assert.Equal(bgra2.B, 34);
Assert.Equal(bgra2.G, 119);
@ -234,7 +234,7 @@ namespace ImageProcessor.Tests
// White
Cmyk cmyk3 = new Cmyk(0, 0, 0, 0);
Bgra bgra3 = cmyk3;
Bgra32 bgra3 = cmyk3;
Assert.Equal(bgra3.B, 255);
Assert.Equal(bgra3.G, 255);
@ -244,9 +244,9 @@ namespace ImageProcessor.Tests
Random random = new Random(0);
for (int i = 0; i < 1000; i++)
{
Bgra bgra4 = new Bgra((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
Bgra32 bgra4 = new Bgra32((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
Cmyk cmyk4 = bgra4;
Assert.Equal(bgra4, (Bgra)cmyk4);
Assert.Equal(bgra4, (Bgra32)cmyk4);
}
}
}

36
tests/ImageProcessor.Tests/Colors/ColorTests.cs

@ -13,7 +13,7 @@ namespace ImageProcessor.Tests
using Xunit;
/// <summary>
/// Tests the <see cref="Bgra"/> struct.
/// Tests the <see cref="Bgra32"/> struct.
/// </summary>
public class ColorTests
{
@ -23,12 +23,12 @@ namespace ImageProcessor.Tests
[Fact]
public void AreEqual()
{
Bgra color1 = new Bgra(0, 0, 0, 255);
Bgra color2 = new Bgra(0, 0, 0, 255);
Bgra color3 = new Bgra("#000");
Bgra color4 = new Bgra("#000000");
Bgra color5 = new Bgra("#FF000000");
Bgra color6 = new Bgra(-16777216);
Bgra32 color1 = new Bgra32(0, 0, 0, 255);
Bgra32 color2 = new Bgra32(0, 0, 0, 255);
Bgra32 color3 = new Bgra32("#000");
Bgra32 color4 = new Bgra32("#000000");
Bgra32 color5 = new Bgra32("#FF000000");
Bgra32 color6 = new Bgra32(-16777216);
Assert.Equal(color1, color2);
Assert.Equal(color1, color3);
@ -43,12 +43,12 @@ namespace ImageProcessor.Tests
[Fact]
public void AreNotEqual()
{
Bgra color1 = new Bgra(255, 0, 0, 255);
Bgra color2 = new Bgra(0, 0, 0, 255);
Bgra color3 = new Bgra("#000");
Bgra color4 = new Bgra("#000000");
Bgra color5 = new Bgra("#FF000000");
Bgra color6 = new Bgra(-16777216);
Bgra32 color1 = new Bgra32(255, 0, 0, 255);
Bgra32 color2 = new Bgra32(0, 0, 0, 255);
Bgra32 color3 = new Bgra32("#000");
Bgra32 color4 = new Bgra32("#000000");
Bgra32 color5 = new Bgra32("#FF000000");
Bgra32 color6 = new Bgra32(-16777216);
Assert.NotEqual(color1, color2);
Assert.NotEqual(color1, color3);
@ -63,25 +63,25 @@ namespace ImageProcessor.Tests
[Fact]
public void ConstructorAssignsProperties()
{
Bgra color1 = new Bgra(255, 10, 34, 220);
Bgra32 color1 = new Bgra32(255, 10, 34, 220);
Assert.Equal(255, color1.B);
Assert.Equal(10, color1.G);
Assert.Equal(34, color1.R);
Assert.Equal(220, color1.A);
Bgra color2 = new Bgra(255, 10, 34);
Bgra32 color2 = new Bgra32(255, 10, 34);
Assert.Equal(255, color2.B);
Assert.Equal(10, color2.G);
Assert.Equal(34, color2.R);
Assert.Equal(255, color2.A);
Bgra color3 = new Bgra(-1);
Bgra32 color3 = new Bgra32(-1);
Assert.Equal(255, color3.B);
Assert.Equal(255, color3.G);
Assert.Equal(255, color3.R);
Assert.Equal(255, color3.A);
Bgra color4 = new Bgra("#FF0000");
Bgra32 color4 = new Bgra32("#FF0000");
Assert.Equal(0, color4.B);
Assert.Equal(0, color4.G);
Assert.Equal(255, color4.R);
@ -95,7 +95,7 @@ namespace ImageProcessor.Tests
public void ConvertHex()
{
const string First = "FF000000";
string second = new Bgra(0, 0, 0, 255).BGRA.ToString("X");
string second = new Bgra32(0, 0, 0, 255).BGRA.ToString("X");
Assert.Equal(First, second);
}
}

4
tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs

@ -21,8 +21,8 @@ namespace ImageProcessor.Tests
{
"../../TestImages/Formats/Jpg/Backdrop.jpg",
//"../../TestImages/Formats/Jpg/Calliphora.jpg",
//"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg",
//"../../TestImages/Formats/Jpg/greyscale.jpg",
"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg",
"../../TestImages/Formats/Jpg/greyscale.jpg",
//"../../TestImages/Formats/Bmp/Car.bmp",
//"../../TestImages/Formats/Png/cmyk.png",
//"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png",

30
tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

@ -15,18 +15,18 @@ namespace ImageProcessor.Tests
new TheoryData<string, IResampler>
{
{ "Bicubic", new BicubicResampler() },
{ "Triangle", new TriangleResampler() },
{ "Box", new BoxResampler() },
{ "Lanczos3", new Lanczos3Resampler() },
{ "Lanczos5", new Lanczos5Resampler() },
{ "Lanczos8", new Lanczos8Resampler() },
{ "MitchellNetravali", new MitchellNetravaliResampler() },
{ "Hermite", new HermiteResampler() },
{ "Spline", new SplineResampler() },
{ "Robidoux", new RobidouxResampler() },
{ "RobidouxSharp", new RobidouxSharpResampler() },
{ "RobidouxSoft", new RobidouxSoftResampler() },
{ "Welch", new WelchResampler() }
//{ "Triangle", new TriangleResampler() },
//{ "Box", new BoxResampler() },
//{ "Lanczos3", new Lanczos3Resampler() },
//{ "Lanczos5", new Lanczos5Resampler() },
//{ "Lanczos8", new Lanczos8Resampler() },
//{ "MitchellNetravali", new MitchellNetravaliResampler() },
//{ "Hermite", new HermiteResampler() },
//{ "Spline", new SplineResampler() },
//{ "Robidoux", new RobidouxResampler() },
//{ "RobidouxSharp", new RobidouxSharpResampler() },
//{ "RobidouxSoft", new RobidouxSoftResampler() },
//{ "Welch", new WelchResampler() }
};
[Theory]
@ -48,7 +48,7 @@ namespace ImageProcessor.Tests
using (FileStream output = File.OpenWrite($"Resized/{filename}"))
{
//image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output);
image.Resize(500, 750, sampler).Save(output);
image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output);
}
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@ -63,10 +63,10 @@ namespace ImageProcessor.Tests
[InlineData(1, 0)]
[InlineData(2, 0)]
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(double x, double expected)
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
Lanczos3Resampler sampler = new Lanczos3Resampler();
double result = sampler.GetValue(x);
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
}

Loading…
Cancel
Save