Browse Source

Cleanup plus perf

Former-commit-id: 63f72c2f2fe3fcc93bc33dc34d53fd510c8b303f
Former-commit-id: 62ee31e766620df396d7155a7b30bf09ce7ba4c3
Former-commit-id: ba93e7236c32f9b633c2d754781a641da27cc3c0
pull/17/head
James Jackson-South 10 years ago
parent
commit
e7bb84d597
  1. 218
      src/ImageProcessor/ApiPortabilityAnalysis.htm
  2. 19
      src/ImageProcessor/Colors/Bgra.cs
  3. 132
      src/ImageProcessor/Colors/ColorVector.cs
  4. 88
      src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs
  5. 26
      src/ImageProcessor/Filters/Alpha.cs
  6. 43
      src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
  7. 66
      src/ImageProcessor/Filters/Contrast.cs
  8. 29
      src/ImageProcessor/Filters/Invert.cs
  9. 46
      src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
  10. 1
      src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
  11. 66
      src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
  12. 36
      src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
  13. 66
      src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs
  14. 10
      src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs
  15. 69
      src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs
  16. 2
      src/ImageProcessor/ImageProcessor.csproj
  17. 232
      src/ImageProcessor/Samplers/Resize - Copy.cs
  18. 132
      src/ImageProcessor/Samplers/Resize.cs
  19. 8
      tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj
  20. 16
      tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs
  21. 1
      tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
  22. 5
      tests/ImageProcessor.Tests/packages.config

218
src/ImageProcessor/ApiPortabilityAnalysis.htm

@ -0,0 +1,218 @@
<!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>

19
src/ImageProcessor/Colors/Bgra.cs

@ -12,6 +12,10 @@ 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>
{
@ -178,6 +182,21 @@ namespace ImageProcessor
[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"/>.
/// </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 Bgra(ColorVector color)
{
return new Bgra((255 * color.B).ToByte(), (255 * color.G).ToByte(), (255 * color.R).ToByte(), (255 * color.A).ToByte());
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Bgra"/>.

132
src/ImageProcessor/Colors/ColorVector.cs

@ -0,0 +1,132 @@
// <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);
}
}
}

88
src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs

@ -0,0 +1,88 @@
// <copyright file="EnumerableExtensions.cs" company="James South">
// Copyright (c) James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
using System.Collections.Generic;
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable"/> interface.
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toExclusive">
/// The end index, exclusive.
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, int toExclusive, int step)
{
// Borrowed from Enumerable.Range
long num = (fromInclusive + toExclusive) - 1L;
if ((toExclusive < 0) || (num > 0x7fffffffL))
{
throw new ArgumentOutOfRangeException(nameof(toExclusive));
}
return RangeIterator(fromInclusive, i => i < toExclusive, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, Func<int, bool> toDelegate, int step)
{
return RangeIterator(fromInclusive, toDelegate, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
private static IEnumerable<int> RangeIterator(int fromInclusive, Func<int, bool> toDelegate, int step)
{
int i = fromInclusive;
while (toDelegate(i))
{
yield return i;
i += step;
}
}
}
}

26
src/ImageProcessor/Filters/Alpha.cs

@ -6,6 +6,7 @@
namespace ImageProcessor.Filters
{
using System;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
@ -39,18 +40,21 @@ namespace ImageProcessor.Filters
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
for (int y = startY; y < endY; y++)
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
y =>
{
Bgra color = source[x, y];
double a = color.A * alpha;
target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte());
}
}
}
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Bgra color = source[x, y];
double a = color.A * alpha;
target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte());
}
}
});
}
}
}

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

@ -5,6 +5,8 @@
namespace ImageProcessor.Filters
{
using System.Threading.Tasks;
/// <summary>
/// The color matrix filter.
/// </summary>
@ -42,29 +44,32 @@ namespace ImageProcessor.Filters
Bgra previousColor = source[0, 0];
Bgra pixelValue = this.ApplyMatrix(previousColor, matrix);
for (int y = startY; y < endY; y++)
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
y =>
{
Bgra 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.
if (sourceColor != previousColor)
if (y >= sourceY && y < sourceBottom)
{
// Perform the operation on the pixel.
pixelValue = this.ApplyMatrix(sourceColor, matrix);
for (int x = startX; x < endX; x++)
{
Bgra sourceColor = source[x, y];
// And setup the previous pointer
previousColor = sourceColor;
}
// 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.
if (sourceColor != previousColor)
{
// Perform the operation on the pixel.
pixelValue = this.ApplyMatrix(sourceColor, matrix);
target[x, y] = pixelValue;
}
}
}
// And setup the previous pointer
previousColor = sourceColor;
}
target[x, y] = pixelValue;
}
}
});
}
/// <summary>

66
src/ImageProcessor/Filters/Contrast.cs

@ -6,6 +6,7 @@
namespace ImageProcessor.Filters
{
using System;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
@ -39,42 +40,45 @@ namespace ImageProcessor.Filters
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
for (int y = startY; y < endY; y++)
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
y =>
{
Bgra sourceColor = source[x, y];
sourceColor = PixelOperations.ToLinear(sourceColor);
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Bgra sourceColor = source[x, y];
sourceColor = PixelOperations.ToLinear(sourceColor);
double r = sourceColor.R / 255.0;
r -= 0.5;
r *= contrast;
r += 0.5;
r *= 255;
r = r.ToByte();
double r = sourceColor.R / 255.0;
r -= 0.5;
r *= contrast;
r += 0.5;
r *= 255;
r = r.ToByte();
double g = sourceColor.G / 255.0;
g -= 0.5;
g *= contrast;
g += 0.5;
g *= 255;
g = g.ToByte();
double g = sourceColor.G / 255.0;
g -= 0.5;
g *= contrast;
g += 0.5;
g *= 255;
g = g.ToByte();
double b = sourceColor.B / 255.0;
b -= 0.5;
b *= contrast;
b += 0.5;
b *= 255;
b = b.ToByte();
double b = sourceColor.B / 255.0;
b -= 0.5;
b *= contrast;
b += 0.5;
b *= 255;
b = b.ToByte();
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}
}
}
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A);
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}
}
});
}
}
}

29
src/ImageProcessor/Filters/Invert.cs

@ -5,6 +5,8 @@
namespace ImageProcessor.Filters
{
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
@ -18,19 +20,22 @@ namespace ImageProcessor.Filters
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
for (int y = startY; y < endY; y++)
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
y =>
{
// 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);
target[x, y] = targetColor;
}
}
}
if (y >= sourceY && y < sourceBottom)
{
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);
target[x, y] = targetColor;
}
}
});
}
}
}

46
src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs

@ -12,6 +12,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
/// <summary>
/// Performs the bmp decoding operation.
@ -194,7 +195,7 @@ namespace ImageProcessor.Formats
// Bit mask
int mask = 0xFF >> (8 - bits);
byte[] data = new byte[(arrayWidth * height)];
byte[] data = new byte[arrayWidth * height];
this.currentStream.Read(data, 0, data.Length);
@ -205,31 +206,34 @@ namespace ImageProcessor.Formats
alignment = 4 - alignment;
}
for (int y = 0; y < height; y++)
{
int rowOffset = y * (arrayWidth + alignment);
Parallel.For(
0,
height,
y =>
{
int rowOffset = y * (arrayWidth + alignment);
for (int x = 0; x < arrayWidth; x++)
{
int offset = rowOffset + x;
for (int x = 0; x < arrayWidth; x++)
{
int offset = rowOffset + x;
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
int colOffset = x * ppb;
int colOffset = x * ppb;
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{
int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask;
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{
int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask;
int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
imageData[arrayOffset + 0] = colors[colorIndex * 4];
imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1];
imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2];
imageData[arrayOffset + 3] = 255;
}
}
}
int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
imageData[arrayOffset + 0] = colors[colorIndex * 4];
imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1];
imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2];
imageData[arrayOffset + 3] = 255;
}
}
});
}
/// <summary>

1
src/ImageProcessor/Formats/Bmp/BmpEncoder.cs

@ -7,6 +7,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
/// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap.

66
src/ImageProcessor/Formats/Jpg/JpegDecoder.cs

@ -7,6 +7,8 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
using BitMiracle.LibJpeg;
/// <summary>
@ -104,43 +106,34 @@ namespace ImageProcessor.Formats
throw new NotSupportedException("JpegDecoder only support RGB color space.");
}
for (int y = 0; y < pixelHeight; y++)
{
SampleRow row = jpg.GetRow(y);
Parallel.For(
0,
pixelHeight,
y =>
{
SampleRow row = jpg.GetRow(y);
for (int x = 0; x < pixelWidth; x++)
{
Sample sample = row.GetAt(x);
for (int x = 0; x < pixelWidth; x++)
{
Sample sample = row.GetAt(x);
int offset = ((y * pixelWidth) + x) * 4;
int offset = ((y * pixelWidth) + x) * 4;
pixels[offset + 0] = (byte)sample[2];
pixels[offset + 1] = (byte)sample[1];
pixels[offset + 2] = (byte)sample[0];
pixels[offset + 3] = 255;
}
}
pixels[offset + 0] = (byte)sample[2];
pixels[offset + 1] = (byte)sample[1];
pixels[offset + 2] = (byte)sample[0];
pixels[offset + 3] = 255;
}
});
image.SetPixels(pixelWidth, pixelHeight, pixels);
}
/// <summary>
///
/// Returns a value indicating whether the given bytes identify Jpeg data.
/// </summary>
/// <param name="header"></param>
/// <returns></returns>
private bool IsExif(byte[] header)
{
bool isExif =
header[6] == 0x45 && // E
header[7] == 0x78 && // x
header[8] == 0x69 && // i
header[9] == 0x66 && // f
header[10] == 0x00;
return isExif;
}
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsJpeg(byte[] header)
{
bool isJpg =
@ -152,5 +145,22 @@ namespace ImageProcessor.Formats
return isJpg;
}
/// <summary>
/// Returns a value indicating whether the given bytes identify EXIF data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsExif(byte[] header)
{
bool isExif =
header[6] == 0x45 && // E
header[7] == 0x78 && // x
header[8] == 0x69 && // i
header[9] == 0x66 && // f
header[10] == 0x00;
return isExif;
}
}
}

36
src/ImageProcessor/Formats/Jpg/JpegEncoder.cs

@ -7,6 +7,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
using System.Threading.Tasks;
using BitMiracle.LibJpeg;
@ -92,22 +93,25 @@ namespace ImageProcessor.Formats
SampleRow[] rows = new SampleRow[pixelHeight];
for (int y = 0; y < pixelHeight; y++)
{
byte[] samples = new byte[pixelWidth * 3];
for (int x = 0; x < pixelWidth; x++)
{
int start = x * 3;
int source = ((y * pixelWidth) + x) * 4;
samples[start] = sourcePixels[source + 2];
samples[start + 1] = sourcePixels[source + 1];
samples[start + 2] = sourcePixels[source];
}
rows[y] = new SampleRow(samples, pixelWidth, 8, 3);
}
Parallel.For(
0,
pixelHeight,
y =>
{
byte[] samples = new byte[pixelWidth * 3];
for (int x = 0; x < pixelWidth; x++)
{
int start = x * 3;
int source = ((y * pixelWidth) + x) * 4;
samples[start] = sourcePixels[source + 2];
samples[start + 1] = sourcePixels[source + 1];
samples[start + 2] = sourcePixels[source];
}
rows[y] = new SampleRow(samples, pixelWidth, 8, 3);
});
JpegImage jpg = new JpegImage(rows, Colorspace.RGB);
jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality });

66
src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs

@ -3,9 +3,6 @@
using System;
using System.IO;
//using ICSharpCode.SharpZipLib.Zip;
//using ICSharpCode.SharpZipLib.Zip.Compression;
/// <summary>
/// An input buffer customised for use by <see cref="InflaterInputStream"/>
/// </summary>
@ -14,7 +11,6 @@
/// </remarks>
public class InflaterInputBuffer
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="InflaterInputBuffer"/> with a default buffer size
/// </summary>
@ -39,7 +35,6 @@
rawData = new byte[bufferSize];
clearText = rawData;
}
#endregion
/// <summary>
/// Get the length of bytes bytes in the <see cref="RawData"/>
@ -127,17 +122,7 @@
toRead -= count;
}
#if !NETCF_1_0 && !NOCRYPTO
if (cryptoTransform != null)
{
clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
}
else
#endif
{
clearTextLength = rawLength;
}
clearTextLength = rawLength;
available = clearTextLength;
}
@ -178,12 +163,14 @@
return 0;
}
}
int toCopy = Math.Min(currentLength, available);
System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
currentOffset += toCopy;
currentLength -= toCopy;
available -= toCopy;
}
return length;
}
@ -270,57 +257,12 @@
return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
}
#if !NETCF_1_0 && !NOCRYPTO
/// <summary>
/// Get/set the <see cref="ICryptoTransform"/> to apply to any data.
/// </summary>
/// <remarks>Set this value to null to have no transform applied.</remarks>
public ICryptoTransform CryptoTransform
{
set
{
cryptoTransform = value;
if (cryptoTransform != null)
{
if (rawData == clearText)
{
if (internalClearText == null)
{
internalClearText = new byte[rawData.Length];
}
clearText = internalClearText;
}
clearTextLength = rawLength;
if (available > 0)
{
cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
}
}
else
{
clearText = rawData;
clearTextLength = rawLength;
}
}
}
#endif
#region Instance Fields
int rawLength;
byte[] rawData;
int clearTextLength;
byte[] clearText;
#if !NETCF_1_0 && !NOCRYPTO
byte[] internalClearText;
#endif
int available;
#if !NETCF_1_0 && !NOCRYPTO
ICryptoTransform cryptoTransform;
#endif
Stream inputStream;
#endregion
}
}

10
src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs

@ -149,16 +149,6 @@
}
}
/// <summary>
/// Clear any cryptographic state.
/// </summary>
protected void StopDecrypting()
{
#if !NETCF_1_0 && !NOCRYPTO
inputBuffer.CryptoTransform = null;
#endif
}
/// <summary>
/// Returns 0 once the end of the stream (EOF) has been reached.
/// Otherwise returns 1.

69
src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs

@ -7,7 +7,6 @@
/// </summary>
public static class ZipConstants
{
#region Versions
/// <summary>
/// The version made by field for entries in the central header when created by this library
/// </summary>
@ -30,9 +29,7 @@
/// The version required for Zip64 extensions (4.5 or higher)
/// </summary>
public const int VersionZip64 = 45;
#endregion
#region Header Sizes
/// <summary>
/// Size of local entry header (excluding variable length fields at end)
/// </summary>
@ -62,9 +59,7 @@
/// Size of 'classic' cryptographic header stored before any entry data
/// </summary>
public const int CryptoHeaderSize = 12;
#endregion
#region Header Signatures
/// <summary>
/// Signature for local entry header
@ -121,52 +116,8 @@
/// End of central directory record signature
/// </summary>
public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24);
#endregion
#if NETCF_1_0 || NETCF_2_0
// This isnt so great but is better than nothing.
// Trying to work out an appropriate OEM code page would be good.
// 850 is a good default for english speakers particularly in Europe.
static int defaultCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
#elif PCL
static Encoding defaultEncoding = Encoding.UTF8;
#else
/// <remarks>
/// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc.
/// But sometimes it yields the special value of 1 which is nicknamed <c>CodePageNoOEM</c> in <see cref="Encoding"/> sources (might also mean <c>CP_OEMCP</c>, but Encoding puts it so).
/// This was observed on Ukranian and Hindu systems.
/// Given this value, <see cref="Encoding.GetEncoding(int)"/> throws an <see cref="ArgumentException"/>.
/// So replace it with some fallback, e.g. 437 which is the default cpcp in a console in a default Windows installation.
/// </remarks>
static int defaultCodePage =
// these values cause ArgumentException in subsequent calls to Encoding::GetEncoding()
((Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 1) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 2) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 3) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 42))
? 437 // The default OEM encoding in a console in a default Windows installation, as a fallback.
: Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage;
#endif
#if !PCL
/// <summary>
/// Default encoding used for string conversion. 0 gives the default system OEM code page.
/// Dont use unicode encodings if you want to be Zip compatible!
/// Using the default code page isnt the full solution neccessarily
/// there are many variable factors, codepage 850 is often a good choice for
/// European users, however be careful about compatability.
/// </summary>
public static int DefaultCodePage {
get {
return defaultCodePage;
}
set {
if ((value < 0) || (value > 65535) ||
(value == 1) || (value == 2) || (value == 3) || (value == 42)) {
throw new ArgumentOutOfRangeException("value");
}
defaultCodePage = value;
}
}
#else
/// <summary>
/// PCL don't support CodePage so we used Encoding instead of
/// </summary>
@ -176,12 +127,12 @@
{
return defaultEncoding;
}
set
{
defaultEncoding = value;
}
}
#endif
/// <summary>
/// Convert a portion of a byte array to a string.
@ -201,11 +152,8 @@
{
return string.Empty;
}
#if !PCL
return Encoding.GetEncoding(DefaultCodePage).GetString(data, 0, count);
#else
return DefaultEncoding.GetString(data, 0, count);
#endif
}
/// <summary>
@ -294,11 +242,8 @@
{
return new byte[0];
}
#if !PCL
return Encoding.GetEncoding(DefaultCodePage).GetBytes(str);
#else
return DefaultEncoding.GetBytes(str);
#endif
}
/// <summary>
@ -320,10 +265,8 @@
{
return Encoding.UTF8.GetBytes(str);
}
else
{
return ConvertToArray(str);
}
return ConvertToArray(str);
}
}
}

2
src/ImageProcessor/ImageProcessor.csproj

@ -42,6 +42,8 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Colors\Cmyk.cs" />
<Compile Include="Colors\ColorVector.cs" />
<Compile Include="Common\Extensions\EnumerableExtensions.cs" />
<Compile Include="Common\Helpers\ImageMaths.cs" />
<Compile Include="Common\Helpers\PixelOperations.cs" />
<Compile Include="Filters\Invert.cs" />

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

@ -0,0 +1,232 @@
// <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; }
}
}
}

132
src/ImageProcessor/Samplers/Resize.cs

@ -7,6 +7,7 @@ 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.
@ -61,57 +62,61 @@ namespace ImageProcessor.Samplers
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
for (int y = startY; y < endY; y++)
{
if (y >= targetY && y < targetBottom)
Parallel.For(
startY,
endY,
y =>
{
List<Weight> verticalValues = this.verticalWeights[y].Values;
double verticalSum = this.verticalWeights[y].Sum;
for (int x = startX; x < endX; x++)
if (y >= targetY && y < targetBottom)
{
List<Weight> horizontalValues = this.horizontalWeights[x].Values;
double horizontalSum = this.horizontalWeights[x].Sum;
List<Weight> verticalValues = this.verticalWeights[y].Values;
double verticalSum = this.verticalWeights[y].Sum;
// Destination color components
double r = 0;
double g = 0;
double b = 0;
double a = 0;
foreach (Weight yw in verticalValues)
for (int x = startX; x < endX; x++)
{
if (Math.Abs(yw.Value) < Epsilon)
{
continue;
}
List<Weight> horizontalValues = this.horizontalWeights[x].Values;
double horizontalSum = this.horizontalWeights[x].Sum;
int originY = yw.Index;
// Destination color components
double r = 0;
double g = 0;
double b = 0;
double a = 0;
foreach (Weight xw in horizontalValues)
foreach (Weight yw in verticalValues)
{
if (Math.Abs(xw.Value) < Epsilon)
if (Math.Abs(yw.Value) < Epsilon)
{
continue;
}
int originX = xw.Index;
Bgra sourceColor = source[originX, originY];
sourceColor = PixelOperations.ToLinear(sourceColor);
int originY = yw.Index;
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);
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);
}
}
}
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
// 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>
@ -136,39 +141,42 @@ namespace ImageProcessor.Samplers
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);
Parallel.For(
0,
destinationSize,
i =>
{
double fu = ((i + .5) * du) - 0.5;
int startU = (int)Math.Ceiling(fu - ru);
if (startU < 0)
{
startU = 0;
}
if (startU < 0)
{
startU = 0;
}
int endU = (int)Math.Floor(fu + ru);
int endU = (int)Math.Floor(fu + ru);
if (endU > sourceSize - 1)
{
endU = sourceSize - 1;
}
if (endU > sourceSize - 1)
{
endU = sourceSize - 1;
}
double sum = 0;
result[i] = new Weights();
double sum = 0;
result[i] = new Weights();
for (int a = startU; a <= endU; a++)
{
double w = 255 * sampler.GetValue((a - fu) / scale);
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));
}
}
if (Math.Abs(w) > Epsilon)
{
sum += w;
result[i].Values.Add(new Weight(a, w));
}
}
result[i].Sum = sum;
}
result[i].Sum = sum;
});
return result;
}

8
tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj

@ -10,13 +10,14 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageProcessor.Tests</RootNamespace>
<AssemblyName>ImageProcessor.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -37,6 +38,11 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Numerics.Vectors.4.1.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>

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

@ -19,15 +19,15 @@ namespace ImageProcessor.Tests
/// </summary>
public static readonly List<string> Files = new List<string>
{
//"../../TestImages/Formats/Jpg/Backdrop.jpg",
//"../../TestImages/Formats/Jpg/Calliphora.jpg",
//"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg",
"../../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/Bmp/Car.bmp",
//"../../TestImages/Formats/Png/cmyk.png",
//"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png",
//"../../TestImages/Formats/Gif/leaf.gif",
//"../../TestImages/Formats/Gif/rings.gif"
"../../TestImages/Formats/Bmp/Car.bmp",
"../../TestImages/Formats/Png/cmyk.png",
"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png",
"../../TestImages/Formats/Gif/leaf.gif",
"../../TestImages/Formats/Gif/rings.gif"
// { "../../TestImages/Formats/Gif/ani.gif" },
// { "../../TestImages/Formats/Gif/ani2.gif" },

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

@ -1,6 +1,7 @@

namespace ImageProcessor.Tests
{
using System;
using System.Diagnostics;
using System.IO;

5
tests/ImageProcessor.Tests/packages.config

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Globalization" version="4.0.10" targetFramework="net46" />
<package id="System.Numerics.Vectors" version="4.1.0" targetFramework="net46" />
<package id="System.Resources.ResourceManager" version="4.0.0" targetFramework="net46" />
<package id="System.Runtime" version="4.0.20" targetFramework="net46" />
<package id="System.Runtime.Extensions" version="4.0.10" targetFramework="net46" />
<package id="xunit" version="2.0.0" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
<package id="xunit.assert" version="2.0.0" targetFramework="net45" />

Loading…
Cancel
Save