diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs
index 44ed036e3..3d04a0a41 100644
--- a/src/ImageProcessor/Colors/Color.cs
+++ b/src/ImageProcessor/Colors/Color.cs
@@ -11,7 +11,7 @@ namespace ImageProcessor
///
/// Represents a four-component color using red, green, blue, and alpha data.
- /// Each component is stored in premultiplied format multiplied by the alpha component.
+ /// Each component is stored in a linear premultiplied format multiplied by the alpha component.
///
///
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
diff --git a/src/ImageProcessor/Colors/ColorDefinitions.cs b/src/ImageProcessor/Colors/ColorDefinitions.cs
index 061b509de..78ea3fbca 100644
--- a/src/ImageProcessor/Colors/ColorDefinitions.cs
+++ b/src/ImageProcessor/Colors/ColorDefinitions.cs
@@ -7,7 +7,7 @@ namespace ImageProcessor
{
///
/// Represents a four-component color using red, green, blue, and alpha data.
- /// Each component is stored in premultiplied format multiplied by the alpha component.
+ /// Each component is stored in a linear premultiplied format multiplied by the alpha component.
///
///
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
diff --git a/src/ImageProcessor/Colors/ColorTransforms.cs b/src/ImageProcessor/Colors/ColorTransforms.cs
index 43bc03251..9551222fb 100644
--- a/src/ImageProcessor/Colors/ColorTransforms.cs
+++ b/src/ImageProcessor/Colors/ColorTransforms.cs
@@ -9,7 +9,7 @@ namespace ImageProcessor
///
/// Represents a four-component color using red, green, blue, and alpha data.
- /// Each component is stored in premultiplied format multiplied by the alpha component.
+ /// Each component is stored in a linear premultiplied format multiplied by the alpha component.
///
///
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
diff --git a/src/ImageProcessor/Colors/ColorspaceTransforms.cs b/src/ImageProcessor/Colors/ColorspaceTransforms.cs
index 334c967fb..ea08973fe 100644
--- a/src/ImageProcessor/Colors/ColorspaceTransforms.cs
+++ b/src/ImageProcessor/Colors/ColorspaceTransforms.cs
@@ -9,7 +9,7 @@ namespace ImageProcessor
///
/// Represents a four-component color using red, green, blue, and alpha data.
- /// Each component is stored in premultiplied format multiplied by the alpha component.
+ /// Each component is stored in a linear premultiplied format multiplied by the alpha component.
///
///
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
@@ -86,7 +86,7 @@ namespace ImageProcessor
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
- return Color.Compress(new Color(r, g, b));
+ return new Color(r, g, b);
}
///
@@ -226,7 +226,7 @@ namespace ImageProcessor
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
- return Color.Compress(new Color(r, g, b));
+ return new Color(r, g, b);
}
///
diff --git a/src/ImageProcessor/Colors/Colorspaces/CieLab.cs b/src/ImageProcessor/Colors/Colorspaces/CieLab.cs
index d5875da07..c1b64134d 100644
--- a/src/ImageProcessor/Colors/Colorspaces/CieLab.cs
+++ b/src/ImageProcessor/Colors/Colorspaces/CieLab.cs
@@ -79,8 +79,6 @@ namespace ImageProcessor
public static implicit operator CieLab(Color color)
{
// First convert to CIE XYZ
- color = Color.Expand(color);
-
float x = (color.R * 0.4124F) + (color.G * 0.3576F) + (color.B * 0.1805F);
float y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F);
diff --git a/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs b/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs
index ab63c868d..8c609dc0d 100644
--- a/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs
+++ b/src/ImageProcessor/Colors/Colorspaces/CieXyz.cs
@@ -79,8 +79,6 @@ namespace ImageProcessor
///
public static implicit operator CieXyz(Color color)
{
- color = Color.Expand(color);
-
float x = (color.R * 0.4124F) + (color.G * 0.3576F) + (color.B * 0.1805F);
float y = (color.R * 0.2126F) + (color.G * 0.7152F) + (color.B * 0.0722F);
float z = (color.R * 0.0193F) + (color.G * 0.1192F) + (color.B * 0.9505F);
diff --git a/src/ImageProcessor/Filters/Brightness.cs b/src/ImageProcessor/Filters/Brightness.cs
index 6a5ae3985..08165037c 100644
--- a/src/ImageProcessor/Filters/Brightness.cs
+++ b/src/ImageProcessor/Filters/Brightness.cs
@@ -50,12 +50,12 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
- Color color = Color.Expand(source[x, y]);
+ Color color = source[x, y];
Vector3 vector3 = color.ToVector3();
vector3 += new Vector3(brightness);
- target[x, y] = Color.Compress(new Color(vector3, color.A));
+ target[x, y] = new Color(vector3, color.A);
}
}
});
diff --git a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
index d63732751..396d5cda1 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs
@@ -17,7 +17,7 @@ namespace ImageProcessor.Filters
public abstract Matrix4x4 Matrix { get; }
///
- public virtual bool Compand => true;
+ public virtual bool Compand => false;
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
@@ -57,11 +57,11 @@ namespace ImageProcessor.Filters
if (compand)
{
- color = Color.Expand(color);
+ color = Color.Compress(color);
}
Vector3 transformed = Vector3.Transform(color.ToVector3(), matrix);
- return compand ? Color.Compress(new Color(transformed, color.A)) : new Color(transformed, color.A);
+ return compand ? Color.Expand(new Color(transformed, color.A)) : new Color(transformed, color.A);
}
}
}
diff --git a/src/ImageProcessor/Filters/ColorMatrix/Hue.cs b/src/ImageProcessor/Filters/ColorMatrix/Hue.cs
index a77f6ff09..9acf9e3bb 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/Hue.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/Hue.cs
@@ -37,7 +37,7 @@
public override Matrix4x4 Matrix => this.matrix;
///
- public override bool Compand => false;
+ public override bool Compand => true;
///
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
diff --git a/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs b/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
index cab932b25..db5020271 100644
--- a/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
+++ b/src/ImageProcessor/Filters/ColorMatrix/Sepia.cs
@@ -28,6 +28,6 @@ namespace ImageProcessor.Filters
};
///
- public override bool Compand => false;
+ public override bool Compand => true;
}
}
diff --git a/src/ImageProcessor/Filters/Contrast.cs b/src/ImageProcessor/Filters/Contrast.cs
index da55507ff..97216f4cc 100644
--- a/src/ImageProcessor/Filters/Contrast.cs
+++ b/src/ImageProcessor/Filters/Contrast.cs
@@ -51,11 +51,11 @@ namespace ImageProcessor.Filters
{
for (int x = startX; x < endX; x++)
{
- Vector4 color = Color.Expand(source[x, y]).ToVector4();
+ Vector4 color = source[x, y].ToVector4();
color -= shiftVector;
color *= contrastVector;
color += shiftVector;
- target[x, y] = Color.Compress(new Color(color));
+ target[x, y] = new Color(color);
}
}
});
diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
index c11f2d7c7..e058d48f0 100644
--- a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
+ using System.Numerics;
using System.Threading.Tasks;
///
@@ -223,11 +224,17 @@ namespace ImageProcessor.Formats
int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
// We divide by 255 as we will store the colors in our floating point format.
+ // Default colorspace is sRGB TODO: Check if we can detect this.
// Stored in r-> g-> b-> a order.
- imageData[arrayOffset] = colors[colorIndex + 2] / 255f; // r
- imageData[arrayOffset + 1] = colors[colorIndex + 1] / 255f; // g
- imageData[arrayOffset + 2] = colors[colorIndex] / 255f; // b
- imageData[arrayOffset + 3] = 1; // a
+ // Expand from sRGB to linear RGB
+ Color color =
+ Color.Expand(
+ new Color(new Vector3(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex]) / 255f));
+
+ imageData[arrayOffset] = color.R; // r
+ imageData[arrayOffset + 1] = color.G; // g
+ imageData[arrayOffset + 2] = color.B; // b
+ imageData[arrayOffset + 3] = color.A; // a
}
}
});
@@ -270,11 +277,14 @@ namespace ImageProcessor.Formats
int arrayOffset = ((row * width) + x) * 4;
+ // Expand from sRGB to linear RGB
+ Color color = Color.Expand(new Color(r, g, b, 1));
+
// Stored in r-> g-> b-> a order.
- imageData[arrayOffset] = r;
- imageData[arrayOffset + 1] = g;
- imageData[arrayOffset + 2] = b;
- imageData[arrayOffset + 3] = 1;
+ imageData[arrayOffset] = color.R;
+ imageData[arrayOffset + 1] = color.G;
+ imageData[arrayOffset + 2] = color.B;
+ imageData[arrayOffset + 3] = color.A;
}
});
}
@@ -305,12 +315,15 @@ namespace ImageProcessor.Formats
int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x) * 4;
+ // Expand from sRGB to linear RGB
+ Color color = Color.Expand(new Color(new Vector3(data[offset + 2], data[offset + 1], data[offset]) / 255f, 1));
+
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
- imageData[arrayOffset] = data[offset + 2] / 255f;
- imageData[arrayOffset + 1] = data[offset + 1] / 255f;
- imageData[arrayOffset + 2] = data[offset] / 255f;
- imageData[arrayOffset + 3] = 1;
+ imageData[arrayOffset] = color.R;
+ imageData[arrayOffset + 1] = color.G;
+ imageData[arrayOffset + 2] = color.B;
+ imageData[arrayOffset + 3] = color.A;
}
});
}
@@ -343,10 +356,15 @@ namespace ImageProcessor.Formats
// We divide by 255 as we will store the colors in our floating point format.
// Stored in r-> g-> b-> a order.
- imageData[arrayOffset] = data[offset + 2] / 255f;
- imageData[arrayOffset + 1] = data[offset + 1] / 255f;
- imageData[arrayOffset + 2] = data[offset] / 255f;
- imageData[arrayOffset + 3] = 1; // TODO: Can we use our real alpha here?
+
+ // Expand from sRGB to linear RGB
+ // TODO: Can we use our real alpha here?
+ Color color = Color.Expand(new Color(new Vector3(data[offset + 2], data[offset + 1], data[offset]) / 255f, 1));
+
+ imageData[arrayOffset] = color.R;
+ imageData[arrayOffset + 1] = color.G;
+ imageData[arrayOffset + 2] = color.B;
+ imageData[arrayOffset + 3] = color.A;
}
});
}
diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
index ad80bbc76..cd955612c 100644
--- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
+++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
@@ -110,13 +110,13 @@ namespace ImageProcessor.Formats
// Limit the output range and multiply out from our floating point.
// Convert back to b-> g-> r-> a order.
- // Convert to non-premultiplied color.
+ // Convert to non-premultiplied sRGB color.
float r = data[offset];
float g = data[offset + 1];
float b = data[offset + 2];
float a = data[offset + 3];
- Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a));
+ Bgra32 color = Color.ToNonPremultiplied(Color.Compress(new Color(r, g, b, a)));
writer.Write(color.B);
writer.Write(color.G);
diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
index 2f456dfc0..6ffcafe06 100644
--- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
+++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs
@@ -7,6 +7,7 @@ namespace ImageProcessor.Formats
{
using System;
using System.IO;
+ using System.Numerics;
using System.Threading.Tasks;
using BitMiracle.LibJpeg;
@@ -117,10 +118,14 @@ namespace ImageProcessor.Formats
int offset = ((y * pixelWidth) + x) * 4;
- pixels[offset + 0] = sample[0] / 255f;
- pixels[offset + 1] = sample[1] / 255f;
- pixels[offset + 2] = sample[2] / 255f;
- pixels[offset + 3] = 1;
+ // Expand from sRGB to linear RGB
+ Color color =
+ Color.Expand(new Color(new Vector3(sample[0], sample[1], sample[2]) / 255f));
+
+ pixels[offset + 0] = color.R;
+ pixels[offset + 1] = color.G;
+ pixels[offset + 2] = color.B;
+ pixels[offset + 3] = color.A;
}
});
}
@@ -139,10 +144,14 @@ namespace ImageProcessor.Formats
int offset = ((y * pixelWidth) + x) * 4;
- pixels[offset + 0] = sample[0] / 255f;
- pixels[offset + 1] = sample[0] / 255f;
- pixels[offset + 2] = sample[0] / 255f;
- pixels[offset + 3] = 1;
+ // Expand from sRGB to linear RGB
+ Color color =
+ Color.Expand(new Color(new Vector3(sample[0], sample[0], sample[0]) / 255f));
+
+ pixels[offset + 0] = color.R;
+ pixels[offset + 1] = color.G;
+ pixels[offset + 2] = color.B;
+ pixels[offset + 3] = color.A;
}
});
}
diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
index 0517b422c..9958d6e63 100644
--- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
+++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs
@@ -111,7 +111,8 @@ namespace ImageProcessor.Formats
float b = sourcePixels[source + 2];
float a = sourcePixels[source + 3];
- Bgra32 color = Color.ToNonPremultiplied(new Color(r, g, b, a));
+ // Compress back to sRGB
+ Bgra32 color = Color.ToNonPremultiplied(Color.Compress(new Color(r, g, b, a)));
samples[start] = color.R;
samples[start + 1] = color.G;
diff --git a/src/ImageProcessor/Samplers/Resampler.cs b/src/ImageProcessor/Samplers/Resampler.cs
index 4fd105f16..22bc7b57c 100644
--- a/src/ImageProcessor/Samplers/Resampler.cs
+++ b/src/ImageProcessor/Samplers/Resampler.cs
@@ -193,12 +193,11 @@ namespace ImageProcessor.Samplers
foreach (Weight xw in horizontalValues)
{
int originX = xw.Index;
- Color sourceColor = Color.Expand(source[originX, originY]);
+ Color sourceColor = source[originX, originY];
destination += sourceColor * yw.Value * xw.Value;
}
}
- destination = Color.Compress(destination);
target[x, y] = destination;
}
}
@@ -302,13 +301,12 @@ namespace ImageProcessor.Samplers
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
- Color sourceColor = Color.Expand(source[rotated.X, rotated.Y]);
+ Color sourceColor = source[rotated.X, rotated.Y];
destination += sourceColor * yw.Value * xw.Value;
}
}
}
- destination = Color.Compress(destination);
target[x, y] = destination;
}
}