using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CSharpCodeAnalyst.TreeMap.Bitmap;
internal static class BitmapManipulation
{
///
/// Trims a bitmap by removing transparent pixels from the edges.
/// WPF version of the original Insight GDI+ implementation.
///
public static BitmapSource TrimBitmap(BitmapSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
// Convert to Format32bppArgb if necessary
var formattedSource = source.Format != PixelFormats.Bgra32
? new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0)
: source;
var width = formattedSource.PixelWidth;
var height = formattedSource.PixelHeight;
var stride = (width * formattedSource.Format.BitsPerPixel + 7) / 8;
var buffer = new byte[height * stride];
formattedSource.CopyPixels(buffer, stride, 0);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
var foundPixel = false;
// Find xMin left to right
for (var x = 0; x < width; x++)
{
var stop = false;
for (var y = 0; y < height; y++)
{
var alpha = buffer[y * stride + 4 * x + 3];
if (alpha != 0)
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
{
break;
}
}
// Image is empty.
if (!foundPixel)
{
throw new InvalidOperationException("Image is empty");
}
// Find yMin top to bottom
for (var y = 0; y < height; y++)
{
var stop = false;
for (var x = xMin; x < width; x++)
{
var alpha = buffer[y * stride + 4 * x + 3];
if (alpha != 0)
{
yMin = y;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
// Find xMax right to left
for (var x = width - 1; x >= xMin; x--)
{
var stop = false;
for (var y = yMin; y < height; y++)
{
var alpha = buffer[y * stride + 4 * x + 3];
if (alpha != 0)
{
xMax = x;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
// Find yMax bottom to top
for (var y = height - 1; y >= yMin; y--)
{
var stop = false;
for (var x = xMin; x <= xMax; x++)
{
var alpha = buffer[y * stride + 4 * x + 3];
if (alpha != 0)
{
yMax = y;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
var srcRect = new Int32Rect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
return new CroppedBitmap(formattedSource, srcRect);
}
///
/// Alternative: Trims a Bitmap based on a background color.
///
public static BitmapSource TrimBitmap(BitmapSource source, Color backgroundColor)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var formattedSource = source.Format != PixelFormats.Bgra32
? new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0)
: source;
var width = formattedSource.PixelWidth;
var height = formattedSource.PixelHeight;
var stride = (width * formattedSource.Format.BitsPerPixel + 7) / 8;
var buffer = new byte[height * stride];
formattedSource.CopyPixels(buffer, stride, 0);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
var foundPixel = false;
var bgR = backgroundColor.R;
var bgG = backgroundColor.G;
var bgB = backgroundColor.B;
var bgA = backgroundColor.A;
// Find xMin
for (var x = 0; x < width; x++)
{
var stop = false;
for (var y = 0; y < height; y++)
{
var index = y * stride + 4 * x;
var alpha = buffer[index + 3];
// Prüfe ob Pixel nicht der Hintergrundfarbe entspricht
if (alpha != 0 &&
(buffer[index] != bgB || buffer[index + 1] != bgG ||
buffer[index + 2] != bgR || buffer[index + 3] != bgA))
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
{
break;
}
}
if (!foundPixel)
{
throw new InvalidOperationException("Image is empty");
}
// Find yMin
for (var y = 0; y < height; y++)
{
var stop = false;
for (var x = xMin; x < width; x++)
{
var index = y * stride + 4 * x;
var alpha = buffer[index + 3];
if (alpha != 0 &&
(buffer[index] != bgB || buffer[index + 1] != bgG ||
buffer[index + 2] != bgR || buffer[index + 3] != bgA))
{
yMin = y;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
// Find xMax
for (var x = width - 1; x >= xMin; x--)
{
var stop = false;
for (var y = yMin; y < height; y++)
{
var index = y * stride + 4 * x;
var alpha = buffer[index + 3];
if (alpha != 0 &&
(buffer[index] != bgB || buffer[index + 1] != bgG ||
buffer[index + 2] != bgR || buffer[index + 3] != bgA))
{
xMax = x;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
// Find yMax
for (var y = height - 1; y >= yMin; y--)
{
var stop = false;
for (var x = xMin; x <= xMax; x++)
{
var index = y * stride + 4 * x;
var alpha = buffer[index + 3];
if (alpha != 0 &&
(buffer[index] != bgB || buffer[index + 1] != bgG ||
buffer[index + 2] != bgR || buffer[index + 3] != bgA))
{
yMax = y;
stop = true;
break;
}
}
if (stop)
{
break;
}
}
var srcRect = new Int32Rect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
return new CroppedBitmap(formattedSource, srcRect);
}
}