lowpoly-walking-simulator/directx11_hellovr/DirectXTK/MakeSpriteFont/BitmapUtils.cs
2024-11-14 20:54:38 +09:00

245 lines
8.5 KiB
C#

// DirectXTK MakeSpriteFont tool
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/?LinkId=248929
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace MakeSpriteFont
{
// Assorted helpers for doing useful things with bitmaps.
public static class BitmapUtils
{
// Copies a rectangular area from one bitmap to another.
public static void CopyRect(Bitmap source, Rectangle sourceRegion, Bitmap output, Rectangle outputRegion)
{
if (sourceRegion.Width != outputRegion.Width ||
sourceRegion.Height != outputRegion.Height)
{
throw new ArgumentException();
}
using (var sourceData = new PixelAccessor(source, ImageLockMode.ReadOnly, sourceRegion))
using (var outputData = new PixelAccessor(output, ImageLockMode.WriteOnly, outputRegion))
{
for (int y = 0; y < sourceRegion.Height; y++)
{
for (int x = 0; x < sourceRegion.Width; x++)
{
outputData[x, y] = sourceData[x, y];
}
}
}
}
// Checks whether an area of a bitmap contains entirely the specified alpha value.
public static bool IsAlphaEntirely(byte expectedAlpha, Bitmap bitmap, Rectangle? region = null)
{
using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadOnly, region))
{
for (int y = 0; y < bitmapData.Region.Height; y++)
{
for (int x = 0; x < bitmapData.Region.Width; x++)
{
byte alpha = bitmapData[x, y].A;
if (alpha != expectedAlpha)
return false;
}
}
}
return true;
}
// Checks whether a bitmap contains entirely the specified RGB value.
public static bool IsRgbEntirely(Color expectedRgb, Bitmap bitmap)
{
using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadOnly))
{
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color color = bitmapData[x, y];
if (color.A == 0)
continue;
if ((color.R != expectedRgb.R) ||
(color.G != expectedRgb.G) ||
(color.B != expectedRgb.B))
{
return false;
}
}
}
}
return true;
}
// Converts greyscale luminosity to alpha data.
public static void ConvertGreyToAlpha(Bitmap bitmap)
{
using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite))
{
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color color = bitmapData[x, y];
// Average the red, green and blue values to compute brightness.
int alpha = (color.R + color.G + color.B) / 3;
bitmapData[x, y] = Color.FromArgb(alpha, 255, 255, 255);
}
}
}
}
// Converts a bitmap to premultiplied alpha format.
public static void PremultiplyAlpha(Bitmap bitmap)
{
using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite))
{
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
Color color = bitmapData[x, y];
int a = color.A;
int r = color.R * a / 255;
int g = color.G * a / 255;
int b = color.B * a / 255;
bitmapData[x, y] = Color.FromArgb(a, r, g, b);
}
}
}
}
// To avoid filtering artifacts when scaling or rotating fonts that do not use premultiplied alpha,
// make sure the one pixel border around each glyph contains the same RGB values as the edge of the
// glyph itself, but with zero alpha. This processing is an elaborate no-op when using premultiplied
// alpha, because the premultiply conversion will change the RGB of all such zero alpha pixels to black.
public static void PadBorderPixels(Bitmap bitmap, Rectangle region)
{
using (var bitmapData = new PixelAccessor(bitmap, ImageLockMode.ReadWrite))
{
// Pad the top and bottom.
for (int x = region.Left; x < region.Right; x++)
{
CopyBorderPixel(bitmapData, x, region.Top, x, region.Top - 1);
CopyBorderPixel(bitmapData, x, region.Bottom - 1, x, region.Bottom);
}
// Pad the left and right.
for (int y = region.Top; y < region.Bottom; y++)
{
CopyBorderPixel(bitmapData, region.Left, y, region.Left - 1, y);
CopyBorderPixel(bitmapData, region.Right - 1, y, region.Right, y);
}
// Pad the four corners.
CopyBorderPixel(bitmapData, region.Left, region.Top, region.Left - 1, region.Top - 1);
CopyBorderPixel(bitmapData, region.Right - 1, region.Top, region.Right, region.Top - 1);
CopyBorderPixel(bitmapData, region.Left, region.Bottom - 1, region.Left - 1, region.Bottom);
CopyBorderPixel(bitmapData, region.Right - 1, region.Bottom - 1, region.Right, region.Bottom);
}
}
// Copies a single pixel within a bitmap, preserving RGB but forcing alpha to zero.
static void CopyBorderPixel(PixelAccessor bitmapData, int sourceX, int sourceY, int destX, int destY)
{
Color color = bitmapData[sourceX, sourceY];
bitmapData[destX, destY] = Color.FromArgb(0, color);
}
// Converts a bitmap to the specified pixel format.
public static Bitmap ChangePixelFormat(Bitmap bitmap, PixelFormat format)
{
Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
return bitmap.Clone(bounds, format);
}
// Helper for locking a bitmap and efficiently reading or writing its pixels.
public sealed class PixelAccessor : IDisposable
{
// Constructor locks the bitmap.
public PixelAccessor(Bitmap bitmap, ImageLockMode mode, Rectangle? region = null)
{
this.bitmap = bitmap;
this.Region = region.GetValueOrDefault(new Rectangle(0, 0, bitmap.Width, bitmap.Height));
this.data = bitmap.LockBits(Region, mode, PixelFormat.Format32bppArgb);
}
// Dispose unlocks the bitmap.
public void Dispose()
{
if (data != null)
{
bitmap.UnlockBits(data);
data = null;
}
}
// Query what part of the bitmap is locked.
public Rectangle Region { get; private set; }
// Get or set a pixel value.
public Color this[int x, int y]
{
get
{
return Color.FromArgb(Marshal.ReadInt32(PixelAddress(x, y)));
}
set
{
Marshal.WriteInt32(PixelAddress(x, y), value.ToArgb());
}
}
// Helper computes the address of the specified pixel.
IntPtr PixelAddress(int x, int y)
{
return data.Scan0 + (y * data.Stride) + (x * sizeof(int));
}
// Fields.
Bitmap bitmap;
BitmapData data;
}
}
}