From ce5f235e5f2b4cba097e3103d63ea36ec9ec2fea Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 31 Oct 2015 13:04:31 +1100 Subject: [PATCH] Switch over to new Color struct as default. Former-commit-id: 1cb78f9723ddcdd4bcf45c27d7571423629ad0c6 Former-commit-id: 727c1b5eeaec657fa541ef64e118006c2b74911f Former-commit-id: 7c9b65f6d71f10e9c3ce8193ae797f30b8c291f2 --- src/ImageProcessor/Colors/Color.cs | 16 +--- .../Formats/Bmp/BmpDecoderCore.cs | 83 ++++++++++--------- src/ImageProcessor/Formats/Bmp/BmpEncoder.cs | 10 ++- .../Formats/Gif/GifDecoderCore.cs | 21 +++-- .../Formats/Gif/Quantizer/OctreeQuantizer.cs | 12 +-- .../Formats/Gif/Quantizer/QuantizedImage.cs | 2 +- .../Formats/Gif/Quantizer/Quantizer.cs | 7 +- src/ImageProcessor/Formats/Jpg/JpegDecoder.cs | 10 +-- src/ImageProcessor/Formats/Jpg/JpegEncoder.cs | 8 +- .../Formats/Png/GrayscaleReader.cs | 41 ++++----- .../Formats/Png/IColorReader.cs | 16 ++-- .../Formats/Png/PaletteIndexReader.cs | 41 ++++----- .../Formats/Png/PngDecoderCore.cs | 9 +- src/ImageProcessor/Formats/Png/PngEncoder.cs | 25 +++--- .../Formats/Png/TrueColorReader.cs | 39 ++++----- src/ImageProcessor/IImageBase.cs | 8 +- src/ImageProcessor/ImageBase.cs | 52 +++--------- src/ImageProcessor/ParallelImageProcessor.cs | 2 +- .../Processors/ProcessorTestBase.cs | 21 ++--- .../Formats/Png/splash.png.REMOVED.git-id | 1 + 20 files changed, 179 insertions(+), 245 deletions(-) create mode 100644 tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id diff --git a/src/ImageProcessor/Colors/Color.cs b/src/ImageProcessor/Colors/Color.cs index e2bff3e03..58bfd4040 100644 --- a/src/ImageProcessor/Colors/Color.cs +++ b/src/ImageProcessor/Colors/Color.cs @@ -31,18 +31,10 @@ namespace ImageProcessor /// /// Initializes a new instance of the struct. /// - /// - /// The red component of this . - /// - /// - /// The green component of this . - /// - /// - /// The blue component of this . - /// - /// - /// The alpha component of this . - /// + /// The red component of this . + /// The green component of this . + /// The blue component of this . + /// The alpha component of this . public Color(float r, float g, float b, float a) : this() { diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs index fe4a7c889..fa434da84 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Performs the bmp decoding operation. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -109,7 +104,7 @@ namespace ImageProcessor.Formats + $"bigger then the max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); } - byte[] imageData = new byte[this.infoHeader.Width * this.infoHeader.Height * 4]; + float[] imageData = new float[this.infoHeader.Width * this.infoHeader.Height * 4]; switch (this.infoHeader.Compression) { @@ -180,12 +175,12 @@ namespace ImageProcessor.Formats /// /// Reads the color palette from the stream. /// - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. /// The number of bits per pixel. - private void ReadRgbPalette(byte[] imageData, byte[] colors, int width, int height, int bits) + private void ReadRgbPalette(float[] imageData, byte[] colors, int width, int height, int bits) { // Pixels per byte (bits per pixel) int ppb = 8 / bits; @@ -224,13 +219,15 @@ namespace ImageProcessor.Formats for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) { - int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask; - + int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4; 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; + + // We divide by 255 as we will store the colors in our floating point format. + // 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 } } }); @@ -239,13 +236,14 @@ namespace ImageProcessor.Formats /// /// Reads the 16 bit color palette from the stream /// - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. - private void ReadRgb16(byte[] imageData, int width, int height) + private void ReadRgb16(float[] imageData, int width, int height) { - const int ScaleR = 256 / 32; - const int ScaleG = 256 / 64; + // We divide here as we will store the colors in our floating point format. + const int ScaleR = (256 / 32) / 32; + const int ScaleG = (256 / 64) / 64; int alignment; byte[] data = this.GetImageArray(width, height, 2, out alignment); @@ -266,16 +264,17 @@ namespace ImageProcessor.Formats short temp = BitConverter.ToInt16(data, offset); - byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); - byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); - byte b = (byte)((temp & Rgb16BMask) * ScaleR); + float r = ((temp & Rgb16RMask) >> 11) * ScaleR; + float g = ((temp & Rgb16GMask) >> 5) * ScaleG; + float b = (temp & Rgb16BMask) * ScaleR; int arrayOffset = ((row * width) + x) * 4; - imageData[arrayOffset + 0] = b; + // Stored in r-> g-> b-> a order. + imageData[arrayOffset] = r; imageData[arrayOffset + 1] = g; - imageData[arrayOffset + 2] = r; - imageData[arrayOffset + 3] = 255; + imageData[arrayOffset + 2] = b; + imageData[arrayOffset + 3] = 1; } }); } @@ -283,10 +282,10 @@ namespace ImageProcessor.Formats /// /// Reads the 24 bit color palette from the stream /// - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. - private void ReadRgb24(byte[] imageData, int width, int height) + private void ReadRgb24(float[] imageData, int width, int height) { int alignment; byte[] data = this.GetImageArray(width, height, 3, out alignment); @@ -306,10 +305,12 @@ namespace ImageProcessor.Formats int offset = rowOffset + (x * 3); int arrayOffset = ((row * width) + x) * 4; - imageData[arrayOffset + 0] = data[offset + 0]; - imageData[arrayOffset + 1] = data[offset + 1]; - imageData[arrayOffset + 2] = data[offset + 2]; - imageData[arrayOffset + 3] = 255; + // 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; } }); } @@ -317,10 +318,10 @@ namespace ImageProcessor.Formats /// /// Reads the 32 bit color palette from the stream /// - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. - private void ReadRgb32(byte[] imageData, int width, int height) + private void ReadRgb32(float[] imageData, int width, int height) { int alignment; byte[] data = this.GetImageArray(width, height, 4, out alignment); @@ -338,12 +339,14 @@ namespace ImageProcessor.Formats for (int x = 0; x < width; x++) { int offset = rowOffset + (x * 4); + int arrayOffset = ((row * width) + x) * 4; - var arrayOffset = ((row * width) + x) * 4; - imageData[arrayOffset + 0] = data[offset + 0]; - imageData[arrayOffset + 1] = data[offset + 1]; - imageData[arrayOffset + 2] = data[offset + 2]; - imageData[arrayOffset + 3] = 255; // Can we get alpha here? + // 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? } }); } diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs index ed833bf62..c37809635 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -100,7 +100,7 @@ namespace ImageProcessor.Formats amount = 4 - amount; } - byte[] data = image.Pixels; + float[] data = image.Pixels; for (int y = image.Height - 1; y >= 0; y--) { @@ -108,9 +108,11 @@ namespace ImageProcessor.Formats { int offset = ((y * image.Width) + x) * 4; - writer.Write(data[offset + 0]); - writer.Write(data[offset + 1]); - writer.Write(data[offset + 2]); + // Limit the output range and multiply out from our floating point. + // Convert back to b-> g-> r-> a order. + writer.Write((byte)(data[offset + 2].Clamp(0, 1) * 255)); + writer.Write((byte)(data[offset + 1].Clamp(0, 1) * 255)); + writer.Write((byte)(data[offset].Clamp(0, 1) * 255)); } for (int i = 0; i < amount; i++) diff --git a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs index ae91b3d03..8675cbc7a 100644 --- a/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageProcessor/Formats/Gif/GifDecoderCore.cs @@ -31,7 +31,7 @@ namespace ImageProcessor.Formats /// /// The current frame. /// - private byte[] currentFrame; + private float[] currentFrame; /// /// The logical screen descriptor. @@ -288,15 +288,15 @@ namespace ImageProcessor.Formats if (this.currentFrame == null) { - this.currentFrame = new byte[imageWidth * imageHeight * 4]; + this.currentFrame = new float[imageWidth * imageHeight * 4]; } - byte[] lastFrame = null; + float[] lastFrame = null; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { - lastFrame = new byte[imageWidth * imageHeight * 4]; + lastFrame = new float[imageWidth * imageHeight * 4]; Array.Copy(this.currentFrame, lastFrame, lastFrame.Length); } @@ -352,18 +352,20 @@ namespace ImageProcessor.Formats this.graphicsControlExtension.TransparencyFlag == false || this.graphicsControlExtension.TransparencyIndex != index) { + // We divide by 255 as we will store the colors in our floating point format. + // Stored in r-> g-> b-> a order. int indexOffset = index * 3; - this.currentFrame[offset + 0] = colorTable[indexOffset + 2]; - this.currentFrame[offset + 1] = colorTable[indexOffset + 1]; - this.currentFrame[offset + 2] = colorTable[indexOffset + 0]; - this.currentFrame[offset + 3] = 255; + this.currentFrame[offset + 0] = colorTable[indexOffset] / 255f; // r + this.currentFrame[offset + 1] = colorTable[indexOffset + 1] / 255f; // g + this.currentFrame[offset + 2] = colorTable[indexOffset + 2] / 255f; // b + this.currentFrame[offset + 3] = 1; // a } i++; } } - byte[] pixels = new byte[imageWidth * imageHeight * 4]; + float[] pixels = new float[imageWidth * imageHeight * 4]; Array.Copy(this.currentFrame, pixels, pixels.Length); @@ -406,6 +408,7 @@ namespace ImageProcessor.Formats { offset = ((y * imageWidth) + x) * 4; + // Stored in r-> g-> b-> a order. this.currentFrame[offset + 0] = 0; this.currentFrame[offset + 1] = 0; this.currentFrame[offset + 2] = 0; diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs index 1b08f16d7..6efdae1be 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/OctreeQuantizer.cs @@ -199,7 +199,7 @@ namespace ImageProcessor.Formats { // If so, check if I have a previous node setup. This will only occur if the first color in the image // happens to be black, with an alpha component of zero. - if (null == this.previousNode) + if (this.previousNode == null) { this.previousColor = pixel.BGRA; this.root.AddColor(pixel, this.maxColorBits, 0, this); @@ -274,7 +274,7 @@ namespace ImageProcessor.Formats { // Find the deepest level containing at least one reducible node int index = this.maxColorBits - 1; - while ((index > 0) && (null == this.reducibleNodes[index])) + while ((index > 0) && (this.reducibleNodes[index] == null)) { index--; } @@ -407,7 +407,7 @@ namespace ImageProcessor.Formats OctreeNode child = this.children[index]; - if (null == child) + if (child == null) { // Create a new child node and store it in the array child = new OctreeNode(level + 1, colorBits, octree); @@ -431,7 +431,7 @@ namespace ImageProcessor.Formats // Loop through all children and add their information to this node for (int index = 0; index < 8; index++) { - if (null != this.children[index]) + if (this.children[index] != null) { this.red += this.children[index].red; this.green += this.children[index].green; @@ -477,7 +477,7 @@ namespace ImageProcessor.Formats // Loop through children looking for leaves for (int i = 0; i < 8; i++) { - if (null != this.children[i]) + if (this.children[i] != null) { this.children[i].ConstructPalette(palette, ref index); } @@ -508,7 +508,7 @@ namespace ImageProcessor.Formats ((pixel.G & Mask[level]) >> (shift - 1)) | ((pixel.B & Mask[level]) >> shift); - if (null != this.children[pixelIndex]) + if (this.children[pixelIndex] != null) { index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1); } diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs index da55ff020..5bb8f9ec6 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/QuantizedImage.cs @@ -69,7 +69,7 @@ namespace ImageProcessor.Formats Image image = new Image(); int pixelCount = this.Pixels.Length; - byte[] bgraPixels = new byte[pixelCount * 4]; + float[] bgraPixels = new float[pixelCount * 4]; for (int i = 0; i < pixelCount; i++) { diff --git a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs index a374ae3b5..c35a9f7f4 100644 --- a/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs +++ b/src/ImageProcessor/Formats/Gif/Quantizer/Quantizer.cs @@ -18,7 +18,7 @@ namespace ImageProcessor.Formats private readonly bool singlePass; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// If true, the quantization only needs to loop through the source pixels once @@ -56,6 +56,7 @@ namespace ImageProcessor.Formats byte[] quantizedPixels = new byte[width * height]; + // Get the pallete List palette = this.GetPalette(); this.SecondPass(imageBase, quantizedPixels, width, height); @@ -94,7 +95,8 @@ namespace ImageProcessor.Formats { int i = 0; - // Convert the first pixel, so that I have values going into the loop + // Convert the first pixel, so that I have values going into the loop. + // Implicit cast here from Color. Bgra32 previousPixel = source[0, 0]; byte pixelValue = this.QuantizePixel(previousPixel); @@ -104,6 +106,7 @@ namespace ImageProcessor.Formats { for (int x = 0; x < width; x++) { + // Implicit cast here from Color. Bgra32 sourcePixel = source[x, y]; // Check if this is the same as the last pixel. If so use that value diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs index 587a1bf18..541400a4e 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs @@ -99,7 +99,7 @@ namespace ImageProcessor.Formats int pixelWidth = jpg.Width; int pixelHeight = jpg.Height; - byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; + float[] pixels = new float[pixelWidth * pixelHeight * 4]; if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8)) { @@ -119,10 +119,10 @@ namespace ImageProcessor.Formats 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] = sample[0] / 255f; + pixels[offset + 1] = sample[1] / 255f; + pixels[offset + 2] = sample[2] / 255f; + pixels[offset + 3] = 1; } }); diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs index 60971697c..84ce9c423 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs @@ -89,7 +89,7 @@ namespace ImageProcessor.Formats int pixelWidth = image.Width; int pixelHeight = image.Height; - byte[] sourcePixels = image.Pixels; + float[] sourcePixels = image.Pixels; SampleRow[] rows = new SampleRow[pixelHeight]; @@ -105,9 +105,9 @@ namespace ImageProcessor.Formats 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]; + samples[start] = (byte)(sourcePixels[source].Clamp(0, 1) * 255); + samples[start + 1] = (byte)(sourcePixels[source + 1].Clamp(0, 1) * 255); + samples[start + 2] = (byte)(sourcePixels[source + 2].Clamp(0, 1) * 255); } rows[y] = new SampleRow(samples, pixelWidth, 8, 3); diff --git a/src/ImageProcessor/Formats/Png/GrayscaleReader.cs b/src/ImageProcessor/Formats/Png/GrayscaleReader.cs index 552776a67..244c0e8c0 100644 --- a/src/ImageProcessor/Formats/Png/GrayscaleReader.cs +++ b/src/ImageProcessor/Formats/Png/GrayscaleReader.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Color reader for reading grayscale colors from a png file. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -37,31 +32,25 @@ namespace ImageProcessor.Formats this.useAlpha = useAlpha; } - /// - /// Reads the specified scanline. - /// - /// The scanline. - /// The pixels, where the colors should be stored in BGRA format. - /// - /// The header, which contains information about the png file, like - /// the width of the image and the height. - /// - public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header) + /// + public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header) { int offset; byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth); + // We divide by 255 as we will store the colors in our floating point format. + // Stored in r-> g-> b-> a order. if (this.useAlpha) { for (int x = 0; x < header.Width / 2; x++) { offset = ((this.row * header.Width) + x) * 4; - pixels[offset + 0] = newScanline[x * 2]; - pixels[offset + 1] = newScanline[x * 2]; - pixels[offset + 2] = newScanline[x * 2]; - pixels[offset + 3] = newScanline[(x * 2) + 1]; + pixels[offset] = newScanline[x * 2] / 255f; + pixels[offset + 1] = newScanline[x * 2] / 255f; + pixels[offset + 2] = newScanline[x * 2] / 255f; + pixels[offset + 3] = newScanline[(x * 2) + 1] / 255f; } } else @@ -70,10 +59,10 @@ namespace ImageProcessor.Formats { offset = ((this.row * header.Width) + x) * 4; - pixels[offset + 0] = newScanline[x]; - pixels[offset + 1] = newScanline[x]; - pixels[offset + 2] = newScanline[x]; - pixels[offset + 3] = 255; + pixels[offset] = newScanline[x] / 255f; + pixels[offset + 1] = newScanline[x] / 255f; + pixels[offset + 2] = newScanline[x] / 255f; + pixels[offset + 3] = 1; } } diff --git a/src/ImageProcessor/Formats/Png/IColorReader.cs b/src/ImageProcessor/Formats/Png/IColorReader.cs index 40c14d6ec..0d7ff27e3 100644 --- a/src/ImageProcessor/Formats/Png/IColorReader.cs +++ b/src/ImageProcessor/Formats/Png/IColorReader.cs @@ -1,18 +1,12 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Encapsulates methods for color readers, which are responsible for reading -// different color formats from a png file. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { /// - /// Encapsulates methods for color readers, which are responsible for reading + /// Encapsulates methods for color readers, which are responsible for reading /// different color formats from a png file. /// public interface IColorReader @@ -26,6 +20,6 @@ namespace ImageProcessor.Formats /// The header, which contains information about the png file, like /// the width of the image and the height. /// - void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header); + void ReadScanline(byte[] scanline, float[] pixels, PngHeader header); } } diff --git a/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs b/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs index a44d34c29..550ac358b 100644 --- a/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs +++ b/src/ImageProcessor/Formats/Png/PaletteIndexReader.cs @@ -1,12 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// A color reader for reading palette indices from the png file. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -43,14 +38,8 @@ namespace ImageProcessor.Formats this.paletteAlpha = paletteAlpha; } - /// - /// Reads the specified scanline. - /// - /// The scanline. - /// The pixels, where the colors should be stored in BGRA format. - /// The header, which contains information about the png file, like - /// the width of the image and the height. - public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header) + /// + public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header) { byte[] newScanline = scanline.ToArrayByBitsLength(header.BitDepth); int offset, index; @@ -65,13 +54,14 @@ namespace ImageProcessor.Formats index = newScanline[i]; offset = ((this.row * header.Width) + i) * 4; + int pixelOffset = index * 3; - pixels[offset + 0] = this.palette[(index * 3) + 2]; - pixels[offset + 1] = this.palette[(index * 3) + 1]; - pixels[offset + 2] = this.palette[(index * 3) + 0]; + pixels[offset] = this.palette[pixelOffset] / 255f; + pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f; + pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f; pixels[offset + 3] = this.paletteAlpha.Length > index - ? this.paletteAlpha[index] - : (byte)255; + ? this.paletteAlpha[index] / 255f + : 1; } } else @@ -81,11 +71,12 @@ namespace ImageProcessor.Formats index = newScanline[i]; offset = ((this.row * header.Width) + i) * 4; + int pixelOffset = index * 3; - pixels[offset + 0] = this.palette[(index * 3) + 2]; - pixels[offset + 1] = this.palette[(index * 3) + 1]; - pixels[offset + 2] = this.palette[(index * 3) + 0]; - pixels[offset + 3] = 255; + pixels[offset] = this.palette[pixelOffset] / 255f; + pixels[offset + 1] = this.palette[pixelOffset + 1] / 255f; + pixels[offset + 2] = this.palette[pixelOffset + 2] / 255f; + pixels[offset + 3] = 1; } } diff --git a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs index 9f837a6bf..1bf8e7740 100644 --- a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs +++ b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs @@ -16,9 +16,6 @@ namespace ImageProcessor.Formats using System.Linq; using System.Text; - //using ICSharpCode.SharpZipLib.Checksums; - //using ICSharpCode.SharpZipLib.Zip.Compression.Streams; - /// /// Performs the png decoding operation. /// @@ -145,7 +142,7 @@ namespace ImageProcessor.Formats + $"max allowed size '{ImageBase.MaxWidth}x{ImageBase.MaxHeight}'"); } - byte[] pixels = new byte[this.header.Width * this.header.Height * 4]; + float[] pixels = new float[this.header.Width * this.header.Height * 4]; PngColorTypeInformation colorTypeInformation = ColorTypes[this.header.ColorType]; @@ -248,10 +245,10 @@ namespace ImageProcessor.Formats /// /// The containing data. /// - /// The containing pixel data. + /// The containing pixel data. /// The color reader. /// The color type information. - private void ReadScanlines(MemoryStream dataStream, byte[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation) + private void ReadScanlines(MemoryStream dataStream, float[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation) { dataStream.Position = 0; diff --git a/src/ImageProcessor/Formats/Png/PngEncoder.cs b/src/ImageProcessor/Formats/Png/PngEncoder.cs index 14a312220..fd3c531d0 100644 --- a/src/ImageProcessor/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessor/Formats/Png/PngEncoder.cs @@ -8,9 +8,6 @@ namespace ImageProcessor.Formats using System; using System.IO; - //using ICSharpCode.SharpZipLib.Checksums; - //using ICSharpCode.SharpZipLib.Zip.Compression.Streams; - /// /// Image encoder for writing image data to a stream in png format. /// @@ -36,7 +33,7 @@ namespace ImageProcessor.Formats public int Quality { get; set; } /// - public string MimeType => "image/jpepngg"; + public string MimeType => "image/png"; /// public string Extension => "png"; @@ -225,7 +222,7 @@ namespace ImageProcessor.Formats /// The image base. private void WriteDataChunksFast(Stream stream, ImageBase imageBase) { - byte[] pixels = imageBase.Pixels; + float[] pixels = imageBase.Pixels; // Convert the pixel array to a new array for adding // the filter byte. @@ -297,7 +294,7 @@ namespace ImageProcessor.Formats /// The image base. private void WriteDataChunks(Stream stream, ImageBase imageBase) { - byte[] pixels = imageBase.Pixels; + float[] pixels = imageBase.Pixels; byte[] data = new byte[(imageBase.Width * imageBase.Height * 4) + imageBase.Height]; @@ -321,19 +318,19 @@ namespace ImageProcessor.Formats // Calculate the offset for the original pixel array. int pixelOffset = ((y * imageBase.Width) + x) * 4; - data[dataOffset + 0] = pixels[pixelOffset + 2]; - data[dataOffset + 1] = pixels[pixelOffset + 1]; - data[dataOffset + 2] = pixels[pixelOffset + 0]; - data[dataOffset + 3] = pixels[pixelOffset + 3]; + data[dataOffset] = (byte)(pixels[pixelOffset].Clamp(0, 1) * 255); + data[dataOffset + 1] = (byte)(pixels[pixelOffset + 1].Clamp(0, 1) * 255); + data[dataOffset + 2] = (byte)(pixels[pixelOffset + 2].Clamp(0, 1) * 255); + data[dataOffset + 3] = (byte)(pixels[pixelOffset + 3].Clamp(0, 1) * 255); if (y > 0) { int lastOffset = (((y - 1) * imageBase.Width) + x) * 4; - data[dataOffset + 0] -= pixels[lastOffset + 2]; - data[dataOffset + 1] -= pixels[lastOffset + 1]; - data[dataOffset + 2] -= pixels[lastOffset + 0]; - data[dataOffset + 3] -= pixels[lastOffset + 3]; + data[dataOffset] -= (byte)(pixels[lastOffset].Clamp(0, 1) * 255); + data[dataOffset + 1] -= (byte)(pixels[lastOffset + 1].Clamp(0, 1) * 255); + data[dataOffset + 2] -= (byte)(pixels[lastOffset + 2].Clamp(0, 1) * 255); + data[dataOffset + 3] -= (byte)(pixels[lastOffset + 3].Clamp(0, 1) * 255); } } } diff --git a/src/ImageProcessor/Formats/Png/TrueColorReader.cs b/src/ImageProcessor/Formats/Png/TrueColorReader.cs index c60e68306..0ce755a3f 100644 --- a/src/ImageProcessor/Formats/Png/TrueColorReader.cs +++ b/src/ImageProcessor/Formats/Png/TrueColorReader.cs @@ -1,13 +1,7 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Color reader for reading true colors from a png file. Only colors -// with 24 or 32 bit (3 or 4 bytes) per pixel are supported at the moment. -// -// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Formats { @@ -37,14 +31,8 @@ namespace ImageProcessor.Formats this.useAlpha = useAlpha; } - /// - /// Reads the specified scanline. - /// - /// The scanline. - /// The pixels, where the colors should be stored in BGRA format. - /// The header, which contains information about the png file, like - /// the width of the image and the height. - public void ReadScanline(byte[] scanline, byte[] pixels, PngHeader header) + /// + public void ReadScanline(byte[] scanline, float[] pixels, PngHeader header) { int offset; @@ -56,10 +44,10 @@ namespace ImageProcessor.Formats { offset = ((this.row * header.Width) + (x >> 2)) * 4; - pixels[offset + 0] = newScanline[x + 2]; - pixels[offset + 1] = newScanline[x + 1]; - pixels[offset + 2] = newScanline[x + 0]; - pixels[offset + 3] = newScanline[x + 3]; + pixels[offset + 0] = newScanline[x] / 255f; + pixels[offset + 1] = newScanline[x + 1] / 255f; + pixels[offset + 2] = newScanline[x + 2] / 255f; + pixels[offset + 3] = newScanline[x + 3] / 255f; } } else @@ -67,11 +55,12 @@ namespace ImageProcessor.Formats for (int x = 0; x < newScanline.Length / 3; x++) { offset = ((this.row * header.Width) + x) * 4; + int pixelOffset = x * 3; - pixels[offset + 0] = newScanline[(x * 3) + 2]; - pixels[offset + 1] = newScanline[(x * 3) + 1]; - pixels[offset + 2] = newScanline[(x * 3) + 0]; - pixels[offset + 3] = 255; + pixels[offset + 0] = newScanline[pixelOffset] / 255f; + pixels[offset + 1] = newScanline[pixelOffset + 1] / 255f; + pixels[offset + 2] = newScanline[pixelOffset + 2] / 255f; + pixels[offset + 3] = 1; } } diff --git a/src/ImageProcessor/IImageBase.cs b/src/ImageProcessor/IImageBase.cs index a4ee9e04f..cdbe2104a 100644 --- a/src/ImageProcessor/IImageBase.cs +++ b/src/ImageProcessor/IImageBase.cs @@ -20,7 +20,7 @@ namespace ImageProcessor /// and stores the blue, the green, the red and the alpha value for /// each pixel in this order. /// - byte[] Pixels { get; } + float[] Pixels { get; } /// /// Gets the width in pixels. @@ -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. /// - /// The at the specified position. - Bgra32 this[int x, int y] { get; set; } + /// The at the specified position. + Color this[int x, int y] { get; set; } /// /// Sets the pixel array of the image. @@ -85,6 +85,6 @@ namespace ImageProcessor /// /// Thrown if the length is not equal to Width * Height * 4. /// - void SetPixels(int width, int height, byte[] pixels); + void SetPixels(int width, int height, float[] pixels); } } diff --git a/src/ImageProcessor/ImageBase.cs b/src/ImageProcessor/ImageBase.cs index f7c410b82..805ee76d0 100644 --- a/src/ImageProcessor/ImageBase.cs +++ b/src/ImageProcessor/ImageBase.cs @@ -40,7 +40,7 @@ namespace ImageProcessor this.Width = width; this.Height = height; - this.Pixels = new byte[width * height * 4]; + this.Pixels = new float[width * height * 4]; } /// @@ -56,13 +56,13 @@ namespace ImageProcessor { Guard.NotNull(other, nameof(other), "Other image cannot be null."); - byte[] pixels = other.Pixels; + float[] pixels = other.Pixels; this.Width = other.Width; this.Height = other.Height; this.Quality = other.Quality; this.FrameDelay = other.FrameDelay; - this.Pixels = new byte[pixels.Length]; + this.Pixels = new float[pixels.Length]; Array.Copy(pixels, this.Pixels, pixels.Length); } @@ -84,7 +84,7 @@ namespace ImageProcessor /// and stores the blue, the green, the red and the alpha value for /// each pixel in this order. /// - public byte[] Pixels { get; private set; } + public float[] Pixels { get; private set; } /// /// Gets the width in pixels. @@ -106,9 +106,7 @@ namespace ImageProcessor /// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - /// - /// Gets or sets th quality of the image. This affects the output quality of lossy image formats. - /// + /// public int Quality { get; set; } /// @@ -119,19 +117,8 @@ namespace ImageProcessor /// public int FrameDelay { get; set; } - /// - /// Gets or sets the color of a pixel at the specified position. - /// - /// - /// The x-coordinate of the pixel. Must be greater - /// than zero and smaller than the width of the pixel. - /// - /// - /// The y-coordinate of the pixel. Must be greater - /// than zero and smaller than the width of the pixel. - /// - /// The at the specified position. - public Bgra32 this[int x, int y] + /// + public Color this[int x, int y] { get { @@ -148,7 +135,7 @@ namespace ImageProcessor #endif int start = ((y * this.Width) + x) * 4; - return new Bgra32(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]); + return new Color(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]); } set @@ -166,30 +153,15 @@ namespace ImageProcessor #endif int start = ((y * this.Width) + x) * 4; - this.Pixels[start + 0] = value.B; + this.Pixels[start + 0] = value.R; this.Pixels[start + 1] = value.G; - this.Pixels[start + 2] = value.R; + this.Pixels[start + 2] = value.B; this.Pixels[start + 3] = value.A; } } - /// - /// Sets the pixel array of the image. - /// - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// - /// The array with colors. Must be a multiple - /// of four, width and height. - /// - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height * 4. - /// - public void SetPixels(int width, int height, byte[] pixels) + /// + public void SetPixels(int width, int height, float[] pixels) { if (width <= 0) { diff --git a/src/ImageProcessor/ParallelImageProcessor.cs b/src/ImageProcessor/ParallelImageProcessor.cs index d84b3cdaf..843f40b9e 100644 --- a/src/ImageProcessor/ParallelImageProcessor.cs +++ b/src/ImageProcessor/ParallelImageProcessor.cs @@ -53,7 +53,7 @@ namespace ImageProcessor /// public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle)) { - byte[] pixels = new byte[width * height * 4]; + float[] pixels = new float[width * height * 4]; target.SetPixels(width, height, pixels); if (targetRectangle == Rectangle.Empty) diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index 15c073b19..d7e0c58db 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs @@ -19,16 +19,17 @@ namespace ImageProcessor.Tests /// public static readonly List Files = new List { - //"../../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/Gif/ani2.gif" , + "../../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/Png/splash.png", + "../../TestImages/Formats/Gif/leaf.gif", + "../../TestImages/Formats/Gif/rings.gif", + "../../TestImages/Formats/Gif/ani2.gif" , "../../TestImages/Formats/Gif/giphy.gif" }; } diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id b/tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id new file mode 100644 index 000000000..73f27bfe6 --- /dev/null +++ b/tests/ImageProcessor.Tests/TestImages/Formats/Png/splash.png.REMOVED.git-id @@ -0,0 +1 @@ +e37d569208ff9490b77b4131330feb323e367fd3 \ No newline at end of file