diff --git a/src/ImageProcessorCore/Formats/Png/Filters/AverageFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/AverageFilter.cs
index c7f3f537a..087c99514 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/AverageFilter.cs
@@ -1,11 +1,31 @@
-namespace ImageProcessorCore.Formats
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
{
using System;
+ ///
+ /// The Average filter uses the average of the two neighboring pixels (left and above) to predict
+ /// the value of a pixel.
+ ///
+ ///
internal static class AverageFilter
{
+ ///
+ /// Decodes the scanline
+ ///
+ /// The scanline to decode
+ /// The previous scanline.
+ /// The bytes per pixel.
+ ///
+ /// The
+ ///
public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
{
+ // Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
byte[] result = new byte[scanline.Length];
for (int x = 1; x < scanline.Length; x++)
@@ -19,9 +39,17 @@
return result;
}
+ ///
+ /// Encodes the scanline
+ ///
+ /// The scanline to encode
+ /// The previous scanline.
+ /// The bytes per pixel.
+ /// The
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
{
- var encodedScanline = new byte[scanline.Length + 1];
+ // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
+ byte[] encodedScanline = new byte[scanline.Length + 1];
encodedScanline[0] = (byte)FilterType.Average;
@@ -36,9 +64,15 @@
return encodedScanline;
}
+ ///
+ /// Calculates the average value of two bytes
+ ///
+ /// The left byte
+ /// The above byte
+ /// The
private static int Average(byte left, byte above)
{
- return Convert.ToInt32(Math.Floor((left + above) / 2.0));
+ return Convert.ToInt32(Math.Floor((left + above) / 2.0D));
}
}
}
diff --git a/src/ImageProcessorCore/Formats/Png/Filters/FilterType.cs b/src/ImageProcessorCore/Formats/Png/Filters/FilterType.cs
index 0a8a519a3..6f1f29c21 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/FilterType.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/FilterType.cs
@@ -7,6 +7,7 @@ namespace ImageProcessorCore.Formats
{
///
/// Provides enumeration of the various png filter types.
+ ///
///
internal enum FilterType
{
diff --git a/src/ImageProcessorCore/Formats/Png/Filters/NoneFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/NoneFilter.cs
index a92a53a90..f4551000b 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/NoneFilter.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/NoneFilter.cs
@@ -8,6 +8,7 @@ namespace ImageProcessorCore.Formats
///
/// The None filter, the scanline is transmitted unmodified; it is only necessary to
/// insert a filter type byte before the data.
+ ///
///
internal static class NoneFilter
{
diff --git a/src/ImageProcessorCore/Formats/Png/Filters/PaethFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/PaethFilter.cs
index bb90ce06f..94117b9a8 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/PaethFilter.cs
@@ -1,11 +1,30 @@
-namespace ImageProcessorCore.Formats
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
{
using System;
+ ///
+ /// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left),
+ /// then chooses as predictor the neighboring pixel closest to the computed value.
+ /// This technique is due to Alan W. Paeth.
+ ///
+ ///
internal static class PaethFilter
{
+ ///
+ /// Decodes the scanline
+ ///
+ /// The scanline to decode
+ /// The previous scanline.
+ /// The bytes per pixel.
+ /// The
public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
{
+ // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
byte[] result = new byte[scanline.Length];
for (int x = 1; x < scanline.Length; x++)
@@ -14,16 +33,23 @@
byte above = previousScanline[x];
byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : previousScanline[x - bytesPerPixel];
- result[x] = (byte)((scanline[x] + PaethPredictor(left, above, upperLeft)) % 256);
+ result[x] = (byte)((scanline[x] + PaethPredicator(left, above, upperLeft)) % 256);
}
return result;
}
+ ///
+ /// Encodes the scanline
+ ///
+ /// The scanline to encode
+ /// The previous scanline.
+ /// The bytes per pixel.
+ /// The
public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
{
- var encodedScanline = new byte[scanline.Length + 1];
-
+ // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
+ byte[] encodedScanline = new byte[scanline.Length + 1];
encodedScanline[0] = (byte)FilterType.Paeth;
for (int x = 0; x < scanline.Length; x++)
@@ -32,35 +58,40 @@
byte above = previousScanline[x];
byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : previousScanline[x - bytesPerPixel];
- encodedScanline[x + 1] = (byte)((scanline[x] - PaethPredictor(left, above, upperLeft)) % 256);
+ encodedScanline[x + 1] = (byte)((scanline[x] - PaethPredicator(left, above, upperLeft)) % 256);
}
return encodedScanline;
}
- private static int PaethPredictor(int a, int b, int c)
+ ///
+ /// Computes a simple linear function of the three neighboring pixels (left, above, upper left), then chooses
+ /// as predictor the neighboring pixel closest to the computed value.
+ ///
+ /// The left neighbor pixel.
+ /// The above neighbor pixel.
+ /// The upper left neighbor pixel.
+ ///
+ /// The .
+ ///
+ private static byte PaethPredicator(byte left, byte above, byte upperLeft)
{
- int p = a + b - c;
- int pa = Math.Abs(p - a);
- int pb = Math.Abs(p - b);
- int pc = Math.Abs(p - c);
+ int p = left + above - upperLeft;
+ int pa = Math.Abs(p - left);
+ int pb = Math.Abs(p - above);
+ int pc = Math.Abs(p - upperLeft);
- if ((pa <= pb) && (pa <= pc))
+ if (pa <= pb && pa <= pc)
{
- return a;
+ return left;
}
- else
+
+ if (pb <= pc)
{
- if (pb <= pc)
- {
- return b;
- }
- else
- {
- return c;
- }
+ return above;
}
+
+ return upperLeft;
}
}
-
}
diff --git a/src/ImageProcessorCore/Formats/Png/Filters/SubFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/SubFilter.cs
index 9734af654..6fb559f5e 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/SubFilter.cs
@@ -1,9 +1,26 @@
-namespace ImageProcessorCore.Formats
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
{
+ ///
+ /// The Sub filter transmits the difference between each byte and the value of the corresponding byte
+ /// of the prior pixel.
+ ///
+ ///
internal static class SubFilter
{
+ ///
+ /// Decodes the scanline
+ ///
+ /// The scanline to decode
+ /// The bytes per pixel.
+ /// The
public static byte[] Decode(byte[] scanline, int bytesPerPixel)
{
+ // Sub(x) + Raw(x-bpp)
byte[] result = new byte[scanline.Length];
for (int x = 1; x < scanline.Length; x++)
@@ -16,10 +33,16 @@
return result;
}
+ ///
+ /// Encodes the scanline
+ ///
+ /// The scanline to encode
+ /// The bytes per pixel.
+ /// The
public static byte[] Encode(byte[] scanline, int bytesPerPixel)
{
- var encodedScanline = new byte[scanline.Length + 1];
-
+ // Sub(x) = Raw(x) - Raw(x-bpp)
+ byte[] encodedScanline = new byte[scanline.Length + 1];
encodedScanline[0] = (byte)FilterType.Sub;
for (int x = 0; x < scanline.Length; x++)
diff --git a/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs
index a2ca0ed15..8f87ec1ca 100644
--- a/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs
@@ -1,9 +1,26 @@
-namespace ImageProcessorCore.Formats
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Formats
{
+ ///
+ /// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel,
+ /// rather than just to its left, is used as the predictor.
+ ///
+ ///
internal static class UpFilter
{
+ ///
+ /// Decodes the scanline
+ ///
+ /// The scanline to decode
+ /// The previous scanline.
+ /// The
public static byte[] Decode(byte[] scanline, byte[] previousScanline)
{
+ // Up(x) + Prior(x)
byte[] result = new byte[scanline.Length];
for (int x = 1; x < scanline.Length; x++)
@@ -16,10 +33,16 @@
return result;
}
+ ///
+ /// Encodes the scanline
+ ///
+ /// The scanline to encode
+ /// The previous scanline.
+ /// The
public static byte[] Encode(byte[] scanline, byte[] previousScanline)
{
- var encodedScanline = new byte[scanline.Length + 1];
-
+ // Up(x) = Raw(x) - Prior(x)
+ byte[] encodedScanline = new byte[scanline.Length + 1];
encodedScanline[0] = (byte)FilterType.Up;
for (int x = 0; x < scanline.Length; x++)
diff --git a/src/ImageProcessorCore/Formats/Png/PngColorType.cs b/src/ImageProcessorCore/Formats/Png/PngColorType.cs
index 574ed2a09..8256a30a5 100644
--- a/src/ImageProcessorCore/Formats/Png/PngColorType.cs
+++ b/src/ImageProcessorCore/Formats/Png/PngColorType.cs
@@ -6,9 +6,9 @@
namespace ImageProcessorCore.Formats
{
///
- /// Provides enumeration of available png color types.
+ /// Provides enumeration of available PNG color types.
///
- public enum PngColorType
+ public enum PngColorType : byte
{
///
/// Each pixel is a grayscale sample.
diff --git a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
index 71b9b6542..2381ae44c 100644
--- a/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
@@ -43,6 +43,11 @@ namespace ImageProcessorCore.Formats
///
private byte bitDepth;
+ ///
+ /// The number of bytes per pixel.
+ ///
+ private int bytesPerPixel;
+
///
/// Gets or sets the quality of output for images.
///
@@ -120,7 +125,7 @@ namespace ImageProcessorCore.Formats
int quality = this.Quality > 0 ? this.Quality : image.Quality;
this.Quality = quality > 0 ? quality.Clamp(1, int.MaxValue) : int.MaxValue;
- // Set correct color type.
+ // Set correct color type if the color count is 256 or less.
if (Quality <= 256)
{
this.PngColorType = PngColorType.Palette;
@@ -141,12 +146,13 @@ namespace ImageProcessorCore.Formats
this.bitDepth = 8;
}
- // TODO: Add more color options here.
+ this.bytesPerPixel = CalculateBytesPerPixel();
+
PngHeader header = new PngHeader
{
Width = image.Width,
Height = image.Height,
- ColorType = (byte)(this.Quality <= 256 ? 3 : 6), // 3 = indexed, 6= Each pixel is an R,G,B triple, followed by an alpha sample.
+ ColorType = (byte)this.PngColorType,
BitDepth = this.bitDepth,
FilterMethod = 0, // None
CompressionMethod = 0,
@@ -155,61 +161,139 @@ namespace ImageProcessorCore.Formats
this.WriteHeaderChunk(stream, header);
- if (this.Quality <= 256)
+ // Collect the pixel data
+ if (this.PngColorType == PngColorType.Palette)
{
- // Quatize the image and get the pixels
- QuantizedImage quantized = this.WritePaletteChunk(stream, header, image);
- pixelData = quantized.Pixels;
+ this.CollectIndexedBytes(image, stream, header);
+ }
+ else if (this.PngColorType == PngColorType.Grayscale || this.PngColorType == PngColorType.GrayscaleWithAlpha)
+ {
+ this.CollectGrayscaleBytes(image);
}
else
{
- // Copy the pixels across from the image.
- // TODO: This should vary by bytes per pixel.
- this.pixelData = new byte[this.width * this.height * 4];
- int stride = this.width * 4;
- using (IPixelAccessor pixels = image.Lock())
- {
- for (int y = 0; y < this.height; y++)
- {
- for (int x = 0; x < this.width; x++)
- {
- int dataOffset = (y * stride) + (x * 4);
- byte[] source = pixels[x, y].ToBytes();
-
- // r -> g -> b -> a
- this.pixelData[dataOffset] = source[0];
- this.pixelData[dataOffset + 1] = source[1];
- this.pixelData[dataOffset + 2] = source[2];
- this.pixelData[dataOffset + 3] = source[3];
- }
- }
- }
+ this.CollectColorBytes(image);
}
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
-
- //using (IPixelAccessor pixels = image.Lock())
- //{
- // this.WriteDataChunks(stream, pixels, quantized);
- //}
this.WriteDataChunks(stream);
-
this.WriteEndChunk(stream);
stream.Flush();
}
+ ///
+ /// Collects the indexed pixel data.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image to encode.
+ /// The containing image data.
+ /// The .
+ private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header)
+ where T : IPackedVector
+ where TP : struct
+ {
+ // Quatize the image and get the pixels
+ QuantizedImage quantized = this.WritePaletteChunk(stream, header, image);
+ pixelData = quantized.Pixels;
+ }
+
+ ///
+ /// Collects the grayscale pixel data.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image to encode.
+ private void CollectGrayscaleBytes(ImageBase image)
+ where T : IPackedVector
+ where TP : struct
+ {
+ // Copy the pixels across from the image.
+ this.pixelData = new byte[this.width * this.height * this.bytesPerPixel];
+ int stride = this.width * this.bytesPerPixel;
+ using (IPixelAccessor pixels = image.Lock())
+ {
+ Parallel.For(
+ 0,
+ this.height,
+ Bootstrapper.Instance.ParallelOptions,
+ y =>
+ {
+ for (int x = 0; x < this.width; x++)
+ {
+ // Convert the color to YCbCr and store the luminance
+ // Optionally store the original color alpha.
+ int dataOffset = (y * stride) + (x * this.bytesPerPixel);
+ Color source = new Color(pixels[x, y].ToVector4());
+ YCbCr luminance = source;
+ for (int i = 0; i < this.bytesPerPixel; i++)
+ {
+ if (i == 0)
+ {
+ this.pixelData[dataOffset] = ((byte)luminance.Y).Clamp(0, 255);
+ }
+ else
+ {
+ this.pixelData[dataOffset + i] = source.A;
+ }
+ }
+ }
+ });
+ }
+ }
+
+ ///
+ /// Collects the true color pixel data.
+ ///
+ /// The pixel format.
+ /// The packed format. long, float.
+ /// The image to encode.
+ private void CollectColorBytes(ImageBase image)
+ where T : IPackedVector
+ where TP : struct
+ {
+ // Copy the pixels across from the image.
+ this.pixelData = new byte[this.width * this.height * this.bytesPerPixel];
+ int stride = this.width * this.bytesPerPixel;
+ using (IPixelAccessor pixels = image.Lock())
+ {
+ Parallel.For(
+ 0,
+ this.height,
+ Bootstrapper.Instance.ParallelOptions,
+ y =>
+ {
+ // Color data is stored in r -> g -> b -> a order
+ for (int x = 0; x < this.width; x++)
+ {
+ int dataOffset = (y * stride) + (x * this.bytesPerPixel);
+ byte[] source = pixels[x, y].ToBytes();
+
+ for (int i = 0; i < this.bytesPerPixel; i++)
+ {
+ this.pixelData[dataOffset + i] = source[i];
+ }
+ }
+ });
+ }
+ }
+
+ ///
+ /// Encodes the pixel data line by line.
+ /// Each scanline is encoded in the most optimal manner to improve compression.
+ ///
+ /// The
private byte[] EncodePixelData()
{
List filteredScanlines = new List();
- int bytesPerPixel = CalculateBytesPerPixel();
- byte[] previousScanline = new byte[width * bytesPerPixel];
+ byte[] previousScanline = new byte[width * this.bytesPerPixel];
for (int y = 0; y < height; y++)
{
byte[] rawScanline = GetRawScanline(y);
- byte[] filteredScanline = GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerPixel);
+ byte[] filteredScanline = GetOptimalFilteredScanline(rawScanline, previousScanline, this.bytesPerPixel);
filteredScanlines.Add(filteredScanline);
@@ -230,24 +314,24 @@ namespace ImageProcessorCore.Formats
/// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
/// to be most compressible, using lowest total variation as proxy for compressibility.
///
- ///
- ///
- ///
+ /// The raw scanline
+ /// The previous scanline
+ /// The number of bytes per pixel
///
- private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerPixel)
+ private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int byteCount)
{
List> candidates = new List>();
- byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel);
+ byte[] sub = SubFilter.Encode(rawScanline, byteCount);
candidates.Add(new Tuple(sub, CalculateTotalVariation(sub)));
byte[] up = UpFilter.Encode(rawScanline, previousScanline);
candidates.Add(new Tuple(up, CalculateTotalVariation(up)));
- byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel);
+ byte[] average = AverageFilter.Encode(rawScanline, previousScanline, byteCount);
candidates.Add(new Tuple(average, CalculateTotalVariation(average)));
- byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel);
+ byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, byteCount);
candidates.Add(new Tuple(paeth, CalculateTotalVariation(paeth)));
int lowestTotalVariation = int.MaxValue;
@@ -266,11 +350,11 @@ namespace ImageProcessorCore.Formats
}
///
- /// Calculates the total variation of given byte array. Total variation is the sum of the absolute values of
+ /// Calculates the total variation of given byte array. Total variation is the sum of the absolute values of
/// neighbour differences.
///
- ///
- ///
+ /// The scanline bytes
+ /// The
private int CalculateTotalVariation(byte[] input)
{
int totalVariation = 0;
@@ -283,15 +367,23 @@ namespace ImageProcessorCore.Formats
return totalVariation;
}
+ ///
+ /// Get the raw scanline data from the pixel data
+ ///
+ /// The row number
+ /// The
private byte[] GetRawScanline(int y)
{
- // TODO: This should vary by bytes per pixel.
- int stride = (this.PngColorType == PngColorType.Palette ? 1 : 4) * this.width;
+ int stride = this.bytesPerPixel * this.width;
byte[] rawScanline = new byte[stride];
Array.Copy(this.pixelData, y * stride, rawScanline, 0, stride);
return rawScanline;
}
+ ///
+ /// Calculates the correct number of bytes per pixel for the given color type.
+ ///
+ /// The
private int CalculateBytesPerPixel()
{
switch (this.PngColorType)
@@ -309,6 +401,8 @@ namespace ImageProcessorCore.Formats
return 3;
// PngColorType.RgbWithAlpha
+ // TODO: Maybe figure out a way to detect if there are any transparent
+ // pixels and encode RGB if none.
default:
return 4;
}
@@ -536,139 +630,6 @@ namespace ImageProcessorCore.Formats
}
}
- /////
- ///// Writes the pixel information to the stream.
- /////
- ///// The pixel format.
- ///// The packed format. long, float.
- ///// The containing image data.
- ///// The image pixels.
- ///// The quantized image.
- //private void WriteDataChunks(Stream stream, IPixelAccessor pixels, QuantizedImage quantized)
- // where T : IPackedVector
- // where TP : struct
- //{
- // byte[] data;
- // int imageWidth = pixels.Width;
- // int imageHeight = pixels.Height;
-
- // // Indexed image.
- // if (this.Quality <= 256)
- // {
- // int rowLength = imageWidth + 1;
- // data = new byte[rowLength * imageHeight];
-
- // Parallel.For(
- // 0,
- // imageHeight,
- // Bootstrapper.Instance.ParallelOptions,
- // y =>
- // {
- // int dataOffset = (y * rowLength);
- // byte compression = 0;
- // if (y > 0)
- // {
- // compression = 2;
- // }
- // data[dataOffset++] = compression;
- // for (int x = 0; x < imageWidth; x++)
- // {
- // data[dataOffset++] = quantized.Pixels[(y * imageWidth) + x];
- // if (y > 0)
- // {
- // data[dataOffset - 1] -= quantized.Pixels[((y - 1) * imageWidth) + x];
- // }
- // }
- // });
- // }
- // else
- // {
- // // TrueColor image.
- // data = new byte[(imageWidth * imageHeight * 4) + pixels.Height];
-
- // int rowLength = (imageWidth * 4) + 1;
-
- // Parallel.For(
- // 0,
- // imageHeight,
- // Bootstrapper.Instance.ParallelOptions,
- // y =>
- // {
- // byte compression = 0;
- // if (y > 0)
- // {
- // compression = 2;
- // }
-
- // data[y * rowLength] = compression;
-
- // for (int x = 0; x < imageWidth; x++)
- // {
- // byte[] color = pixels[x, y].ToBytes();
-
- // // Calculate the offset for the new array.
- // int dataOffset = (y * rowLength) + (x * 4) + 1;
-
- // // Expected format
- // data[dataOffset] = color[0];
- // data[dataOffset + 1] = color[1];
- // data[dataOffset + 2] = color[2];
- // data[dataOffset + 3] = color[3];
-
- // if (y > 0)
- // {
- // color = pixels[x, y - 1].ToBytes();
-
- // data[dataOffset] -= color[0];
- // data[dataOffset + 1] -= color[1];
- // data[dataOffset + 2] -= color[2];
- // data[dataOffset + 3] -= color[3];
- // }
- // }
- // });
- // }
-
- // byte[] buffer;
- // int bufferLength;
-
- // MemoryStream memoryStream = null;
- // try
- // {
- // memoryStream = new MemoryStream();
-
- // using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.CompressionLevel))
- // {
- // deflateStream.Write(data, 0, data.Length);
- // }
-
- // bufferLength = (int)memoryStream.Length;
- // buffer = memoryStream.ToArray();
- // }
- // finally
- // {
- // memoryStream?.Dispose();
- // }
-
- // int numChunks = bufferLength / MaxBlockSize;
-
- // if (bufferLength % MaxBlockSize != 0)
- // {
- // numChunks++;
- // }
-
- // for (int i = 0; i < numChunks; i++)
- // {
- // int length = bufferLength - (i * MaxBlockSize);
-
- // if (length > MaxBlockSize)
- // {
- // length = MaxBlockSize;
- // }
-
- // this.WriteChunk(stream, PngChunkTypes.Data, buffer, i * MaxBlockSize, length);
- // }
- //}
-
///
/// Writes the chunk end to the stream.
///