Files
OpenMaidEngine/engine/Age.Engine/Sys4/RgbaSurfaceOps.cs
2026-08-03 10:46:28 -04:00

85 lines
3.7 KiB
C#

using Age.Engine.Model;
namespace Age.Engine.Sys4;
/// <summary>Platform-neutral mutation helpers for AGE's software-modeled RGBA surfaces.</summary>
public static class RgbaSurfaceOps
{
public static bool FillRect(RgbaImage destination, int x, int y, int width, int height,
byte alpha, long rgb)
{
long left = x, top = y, right = left + width, bottom = top + height;
if (width <= 0 || height <= 0) return false;
left = System.Math.Max(0, left);
top = System.Math.Max(0, top);
right = System.Math.Min(destination.Width, right);
bottom = System.Math.Min(destination.Height, bottom);
if (right <= left || bottom <= top) return false;
byte red = (byte)((rgb >> 16) & 0xff);
byte green = (byte)((rgb >> 8) & 0xff);
byte blue = (byte)(rgb & 0xff);
for (int row = (int)top; row < (int)bottom; row++)
{
int offset = checked((row * destination.Width + (int)left) * 4);
for (int column = (int)left; column < (int)right; column++, offset += 4)
{
destination.Pixels[offset] = red;
destination.Pixels[offset + 1] = green;
destination.Pixels[offset + 2] = blue;
destination.Pixels[offset + 3] = alpha;
}
}
return true;
}
public static RgbaImage WithColorKey(RgbaImage source, long colorKey)
{
if (!Age.Engine.Model.BlendMath.HasColorKey(colorKey)) return source;
byte[] pixels = (byte[])source.Pixels.Clone();
for (int index = 0; index < pixels.Length; index += 4)
if (Age.Engine.Model.BlendMath.ColorKeyMatches(
pixels[index], pixels[index + 1], pixels[index + 2], colorKey))
pixels[index + 3] = 0;
return new RgbaImage(source.Width, source.Height, pixels);
}
/// <summary>Copy a rectangle while clipping source and destination together. A temporary buffer
/// preserves native blit behavior when the two rectangles overlap in the same surface.</summary>
public static bool CopyRect(RgbaImage source, RgbaImage destination,
int sourceX, int sourceY, int width, int height,
int destinationX, int destinationY)
{
long sx = sourceX, sy = sourceY, dx = destinationX, dy = destinationY;
long w = width, h = height;
if (w <= 0 || h <= 0) return false;
if (sx < 0) { long n = -sx; sx = 0; dx += n; w -= n; }
if (sy < 0) { long n = -sy; sy = 0; dy += n; h -= n; }
if (dx < 0) { long n = -dx; dx = 0; sx += n; w -= n; }
if (dy < 0) { long n = -dy; dy = 0; sy += n; h -= n; }
w = System.Math.Min(w, source.Width - sx);
h = System.Math.Min(h, source.Height - sy);
w = System.Math.Min(w, destination.Width - dx);
h = System.Math.Min(h, destination.Height - dy);
if (w <= 0 || h <= 0) return false;
int clippedWidth = checked((int)w);
int clippedHeight = checked((int)h);
int rowBytes = checked(clippedWidth * 4);
byte[] pixels = new byte[checked(rowBytes * clippedHeight)];
for (int row = 0; row < clippedHeight; row++)
{
int sourceOffset = checked(((int)sy + row) * source.Width * 4 + (int)sx * 4);
source.Pixels.AsSpan(sourceOffset, rowBytes).CopyTo(pixels.AsSpan(row * rowBytes, rowBytes));
}
for (int row = 0; row < clippedHeight; row++)
{
int destinationOffset = checked(((int)dy + row) * destination.Width * 4 + (int)dx * 4);
pixels.AsSpan(row * rowBytes, rowBytes)
.CopyTo(destination.Pixels.AsSpan(destinationOffset, rowBytes));
}
return true;
}
}