39 lines
2.5 KiB
C#
39 lines
2.5 KiB
C#
using SkiaSharp;
|
|
|
|
namespace JobTrackerApi.Services;
|
|
|
|
public static class AvatarImageProcessor
|
|
{
|
|
public static byte[]? Normalize(byte[] source)
|
|
{
|
|
if (source.Length is 0 or > 1_000_000) return null;
|
|
using var data = SKData.CreateCopy(source);
|
|
using var codec = SKCodec.Create(data);
|
|
if (codec is null || codec.EncodedFormat is not (SKEncodedImageFormat.Png or SKEncodedImageFormat.Jpeg or SKEncodedImageFormat.Webp)) return null;
|
|
var info = codec.Info;
|
|
if (info.Width is < 1 or > 2048 || info.Height is < 1 or > 2048 || (long)info.Width * info.Height > 4_000_000 || codec.FrameCount > 1) return null;
|
|
using var pixels = new SKBitmap(info.Width, info.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
|
if (codec.GetPixels(pixels.Info, pixels.GetPixels()) != SKCodecResult.Success) return null;
|
|
var rotated = codec.EncodedOrigin is SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.LeftBottom;
|
|
using var oriented = new SKBitmap(rotated ? info.Height : info.Width, rotated ? info.Width : info.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
|
using (var canvas = new SKCanvas(oriented))
|
|
{
|
|
switch (codec.EncodedOrigin)
|
|
{
|
|
case SKEncodedOrigin.TopRight: canvas.Translate(info.Width, 0); canvas.Scale(-1, 1); break;
|
|
case SKEncodedOrigin.BottomRight: canvas.Translate(info.Width, info.Height); canvas.RotateDegrees(180); break;
|
|
case SKEncodedOrigin.BottomLeft: canvas.Translate(0, info.Height); canvas.Scale(1, -1); break;
|
|
case SKEncodedOrigin.LeftTop: canvas.RotateDegrees(90); canvas.Scale(1, -1); break;
|
|
case SKEncodedOrigin.RightTop: canvas.Translate(info.Height, 0); canvas.RotateDegrees(90); break;
|
|
case SKEncodedOrigin.RightBottom: canvas.Translate(info.Height, info.Width); canvas.RotateDegrees(90); canvas.Scale(-1, 1); break;
|
|
case SKEncodedOrigin.LeftBottom: canvas.Translate(0, info.Width); canvas.RotateDegrees(270); break;
|
|
}
|
|
canvas.DrawBitmap(pixels, 0, 0, new SKSamplingOptions(SKFilterMode.Nearest), null);
|
|
}
|
|
// Re-encoding decoded pixels drops source metadata and trailing payloads.
|
|
using var image = SKImage.FromBitmap(oriented);
|
|
using var encoded = image.Encode(SKEncodedImageFormat.Png, 100);
|
|
return encoded is null || encoded.Size > 1_000_000 ? null : encoded.ToArray();
|
|
}
|
|
}
|