125 lines
5.9 KiB
C#
125 lines
5.9 KiB
C#
|
using System;
|
|||
|
|
|||
|
namespace Unity.Screenshots
|
|||
|
{
|
|||
|
public static class Downsampler
|
|||
|
{
|
|||
|
#region Static Methods
|
|||
|
|
|||
|
public static byte[] Downsample(byte[] dataRgba, int stride, int maximumWidth, int maximumHeight, out int downsampledStride)
|
|||
|
{
|
|||
|
// Preconditions
|
|||
|
if (stride == 0)
|
|||
|
{
|
|||
|
throw new ArgumentException("The stride must be greater than 0.");
|
|||
|
}
|
|||
|
if (stride % 4 != 0)
|
|||
|
{
|
|||
|
throw new ArgumentException("The stride must be evenly divisible by 4.");
|
|||
|
}
|
|||
|
if (dataRgba == null)
|
|||
|
{
|
|||
|
throw new ArgumentNullException("dataRgba");
|
|||
|
}
|
|||
|
if (dataRgba.Length == 0)
|
|||
|
{
|
|||
|
throw new ArgumentException("The data length must be greater than 0.");
|
|||
|
}
|
|||
|
if (dataRgba.Length % 4 != 0)
|
|||
|
{
|
|||
|
throw new ArgumentException("The data must be evenly divisible by 4.");
|
|||
|
}
|
|||
|
if (dataRgba.Length % stride != 0)
|
|||
|
{
|
|||
|
throw new ArgumentException("The data must be evenly divisible by the stride.");
|
|||
|
}
|
|||
|
|
|||
|
// Implementation
|
|||
|
int width = stride / 4;
|
|||
|
int height = dataRgba.Length / stride;
|
|||
|
float ratioX = maximumWidth / (float) width;
|
|||
|
float ratioY = maximumHeight / (float) height;
|
|||
|
float ratio = Math.Min(ratioX, ratioY);
|
|||
|
if (ratio < 1)
|
|||
|
{
|
|||
|
int downsampledWidth = (int) Math.Round(width * ratio);
|
|||
|
int downsampledHeight = (int) Math.Round(height * ratio);
|
|||
|
float[] downsampledData = new float[downsampledWidth * downsampledHeight * 4];
|
|||
|
float sampleWidth = width / (float) downsampledWidth;
|
|||
|
float sampleHeight = height / (float) downsampledHeight;
|
|||
|
int kernelWidth = (int) Math.Floor(sampleWidth);
|
|||
|
int kernelHeight = (int) Math.Floor(sampleHeight);
|
|||
|
int kernelSize = kernelWidth * kernelHeight;
|
|||
|
for (int y = 0; y < downsampledHeight; y++)
|
|||
|
{
|
|||
|
for (int x = 0; x < downsampledWidth; x++)
|
|||
|
{
|
|||
|
int destinationIndex = y * downsampledWidth * 4 + x * 4;
|
|||
|
int sampleLowerX = (int) Math.Floor(x * sampleWidth);
|
|||
|
int sampleLowerY = (int) Math.Floor(y * sampleHeight);
|
|||
|
int sampleUpperX = sampleLowerX + kernelWidth;
|
|||
|
int sampleUpperY = sampleLowerY + kernelHeight;
|
|||
|
for (int sampleY = sampleLowerY; sampleY < sampleUpperY; sampleY++)
|
|||
|
{
|
|||
|
if (sampleY >= height)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
for (int sampleX = sampleLowerX; sampleX < sampleUpperX; sampleX++)
|
|||
|
{
|
|||
|
if (sampleX >= width)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
int sourceIndex = sampleY * width * 4 + sampleX * 4;
|
|||
|
downsampledData[destinationIndex] += dataRgba[sourceIndex];
|
|||
|
downsampledData[destinationIndex + 1] += dataRgba[sourceIndex + 1];
|
|||
|
downsampledData[destinationIndex + 2] += dataRgba[sourceIndex + 2];
|
|||
|
downsampledData[destinationIndex + 3] += dataRgba[sourceIndex + 3];
|
|||
|
}
|
|||
|
}
|
|||
|
downsampledData[destinationIndex] /= kernelSize;
|
|||
|
downsampledData[destinationIndex + 1] /= kernelSize;
|
|||
|
downsampledData[destinationIndex + 2] /= kernelSize;
|
|||
|
downsampledData[destinationIndex + 3] /= kernelSize;
|
|||
|
}
|
|||
|
}
|
|||
|
byte[] flippedData = new byte[downsampledWidth * downsampledHeight * 4];
|
|||
|
for (int y = 0; y < downsampledHeight; y++)
|
|||
|
{
|
|||
|
for (int x = 0; x < downsampledWidth; x++)
|
|||
|
{
|
|||
|
int sourceIndex = (downsampledHeight - 1 - y) * downsampledWidth * 4 + x * 4;
|
|||
|
int destinationIndex = y * downsampledWidth * 4 + x * 4;
|
|||
|
flippedData[destinationIndex] += (byte) downsampledData[sourceIndex];
|
|||
|
flippedData[destinationIndex + 1] += (byte) downsampledData[sourceIndex + 1];
|
|||
|
flippedData[destinationIndex + 2] += (byte) downsampledData[sourceIndex + 2];
|
|||
|
flippedData[destinationIndex + 3] += (byte) downsampledData[sourceIndex + 3];
|
|||
|
}
|
|||
|
}
|
|||
|
downsampledStride = downsampledWidth * 4;
|
|||
|
return flippedData;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
byte[] flippedData = new byte[dataRgba.Length];
|
|||
|
for (int y = 0; y < height; y++)
|
|||
|
{
|
|||
|
for (int x = 0; x < width; x++)
|
|||
|
{
|
|||
|
int sourceIndex = (height - 1 - y) * width * 4 + x * 4;
|
|||
|
int destinationIndex = y * width * 4 + x * 4;
|
|||
|
flippedData[destinationIndex] += (byte) dataRgba[sourceIndex];
|
|||
|
flippedData[destinationIndex + 1] += (byte) dataRgba[sourceIndex + 1];
|
|||
|
flippedData[destinationIndex + 2] += (byte) dataRgba[sourceIndex + 2];
|
|||
|
flippedData[destinationIndex + 3] += (byte) dataRgba[sourceIndex + 3];
|
|||
|
}
|
|||
|
}
|
|||
|
downsampledStride = width * 4;
|
|||
|
return flippedData;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|