Switch-Toolbox/Switch_Toolbox_Library/Imaging/BitmapExtension.cs

686 lines
22 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Toolbox.Library
{
public class BitmapExtension
{
public BitmapExtension()
{
}
public static Bitmap FillColor(int Width, int Height, Color color)
{
Bitmap Bmp = new Bitmap(Width, Height);
using (Graphics gfx = Graphics.FromImage(Bmp))
using (SolidBrush brush = new SolidBrush(color))
{
gfx.FillRectangle(brush, 0, 0, Width, Height);
}
return Bmp;
}
public static List<byte[]> GenerateMipMaps(Bitmap bitmap)
{
List<byte[]> datas = new List<byte[]>();
datas.Add(ImageToByte(bitmap));
while (bitmap.Width / 2 > 0 && bitmap.Height / 2 > 0)
{
bitmap = Resize(bitmap, bitmap.Width / 2, bitmap.Height / 2);
datas.Add(ImageToByte(bitmap));
}
return datas;
}
public static Bitmap Resize(Image original, Size size)
{
return ResizeImage(original, size.Width, size.Height);
}
public static Bitmap Resize(Image original, int width, int height)
{
return ResizeImage(original, width, height);
}
public static Bitmap ReplaceChannel(Image OriginalImage, Image ChannelImage, STChannelType ChannelType)
{
Bitmap b = new Bitmap(OriginalImage);
Bitmap c = new Bitmap(ChannelImage, new Size(b.Width, b.Height)); //Force to be same size
c = GrayScale(c); //Convert to grayscale
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
BitmapData cmData = c.LockBits(new Rectangle(0, 0, c.Width, c.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int cstride = cmData.Stride;
System.IntPtr cScan0 = cmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
byte* channelPointer = (byte*)(void*)cScan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
if (ChannelType == STChannelType.Red)
{
p[2] = channelPointer[2];
p[1] = green;
p[0] = blue;
p[3] = alpha;
}
else if (ChannelType == STChannelType.Green)
{
p[2] = red;
p[1] = channelPointer[2];
p[0] = blue;
p[3] = alpha;
}
else if (ChannelType == STChannelType.Blue)
{
p[2] = red;
p[1] = green;
p[0] = channelPointer[2];
p[3] = alpha;
}
else if (ChannelType == STChannelType.Alpha)
{
p[2] = red;
p[1] = green;
p[0] = blue;
p[3] = channelPointer[2];
}
p += 4;
channelPointer += 4;
}
p += nOffset;
channelPointer += nOffset;
}
}
b.UnlockBits(bmData);
c.UnlockBits(cmData);
return b;
}
public static Bitmap SwapBlueRedChannels(Image image)
{
Bitmap b = new Bitmap(image);
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
p[0] = red;
p[1] = green;
p[2] = blue;
p[3] = alpha;
p += 4;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return b;
}
public static Bitmap ResizeImage(Image image, int width, int height,
InterpolationMode interpolationMode = InterpolationMode.HighQualityBicubic,
SmoothingMode smoothingMode = SmoothingMode.HighQuality)
{
if (width == 0) width = 1;
if (height == 0) height = 1;
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = interpolationMode;
graphics.SmoothingMode = smoothingMode;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public static Bitmap GetBitmap(byte[] Buffer, int Width, int Height, PixelFormat pixelFormat = PixelFormat.Format32bppArgb)
{
Rectangle Rect = new Rectangle(0, 0, Width, Height);
Bitmap Img = new Bitmap(Width, Height, pixelFormat);
BitmapData ImgData = Img.LockBits(Rect, ImageLockMode.WriteOnly, Img.PixelFormat);
if (Buffer.Length > ImgData.Stride * Img.Height)
throw new Exception($"Invalid Buffer Length ({Buffer.Length})!!!");
Marshal.Copy(Buffer, 0, ImgData.Scan0, Buffer.Length);
Img.UnlockBits(ImgData);
return Img;
}
public static Bitmap SetChannel(Bitmap b,
STChannelType channelR,
STChannelType channelG,
STChannelType channelB,
STChannelType channelA)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
p[2] = SetChannelByte(channelR, red, green, blue, alpha);
p[1] = SetChannelByte(channelG, red, green, blue, alpha);
p[0] = SetChannelByte(channelB, red, green, blue, alpha);
p[3] = SetChannelByte(channelA, red, green, blue, alpha);
p += 4;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return b;
}
private static byte SetChannelByte(STChannelType channel, byte r, byte g, byte b, byte a)
{
switch (channel) {
case STChannelType.Red: return r;
case STChannelType.Green: return g;
case STChannelType.Blue: return b;
case STChannelType.Alpha: return a;
case STChannelType.One: return 255;
case STChannelType.Zero: return 0;
default:
throw new Exception("Unknown channel type! " + channel);
}
}
public static Bitmap ShowChannel(Bitmap b, STChannelType channel)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
if (channel == STChannelType.Red)
{
p[0] = red;
p[1] = red;
p[2] = red;
p[3] = 255;
}
else if (channel == STChannelType.Green)
{
p[0] = green;
p[1] = green;
p[2] = green;
p[3] = 255;
}
else if (channel == STChannelType.Blue)
{
p[0] = blue;
p[1] = blue;
p[2] = blue;
p[3] = 255;
}
else if (channel == STChannelType.Alpha)
{
p[0] = alpha;
p[1] = alpha;
p[2] = alpha;
p[3] = 255;
}
p += 4;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return b;
}
public static bool SetChannels(Bitmap b, bool UseRed, bool UseBlue, bool UseGreen, bool UseAlpha)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
if (!UseRed)
red = 0;
if (!UseGreen)
green = 0;
if (!UseBlue)
blue = 0;
if (!UseAlpha)
alpha = 0;
p[2] = red;
p[1] = green;
p[0] = blue;
p[3] = alpha;
p += 4;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return true;
}
public static Bitmap GrayScale(Image b) { return GrayScale(new Bitmap(b));
}
public static Bitmap GrayScale(Bitmap b)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 4;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
alpha = p[3];
p[0] = p[1] = p[2] = (byte)(.299 * red
+ .587 * green
+ .114 * blue);
p += 4;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return b;
}
public static bool Invert(Bitmap b)
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 3;
int nWidth = b.Width * 3;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
p[0] = (byte)(255 - p[0]);
++p;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
return true;
}
public static Bitmap HueStaturationBrightnessScale(Bitmap image,
bool EditHue, bool EditSaturation, bool EditBrightness,
float HueScale = 255, float SaturationScale = 0.5f, float BrightnessScale = 0.5f)
{
Bitmap b = new Bitmap(image);
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* pointer = (byte*)(void*)Scan0;
int bytesPerPixel = 4;
int nOffset = stride - b.Width * bytesPerPixel;
byte red, green, blue, alpha;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
blue = pointer[0];
green = pointer[1];
red = pointer[2];
alpha = pointer[3];
double hue, sat, val;
ColorToHSV(Color.FromArgb(alpha, red, green, blue), out hue, out sat, out val);
var color = ColorFromHSV(hue * HueScale, sat * SaturationScale, val * BrightnessScale);
pointer[2] = color.R;
pointer[1] = color.G;
pointer[0] = color.B;
pointer[3] = alpha;
pointer += bytesPerPixel;
}
pointer += nOffset;
}
}
b.UnlockBits(bmData);
return b;
}
public static void ColorToHSV(Color color, out double hue, out double saturation, out double value)
{
int max = Math.Max(color.R, Math.Max(color.G, color.B));
int min = Math.Min(color.R, Math.Min(color.G, color.B));
hue = color.GetHue();
saturation = (max == 0) ? 0 : 1d - (1d * min / max);
value = max / 255d;
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromArgb(255, v, t, p);
else if (hi == 1)
return Color.FromArgb(255, q, v, p);
else if (hi == 2)
return Color.FromArgb(255, p, v, t);
else if (hi == 3)
return Color.FromArgb(255, p, q, v);
else if (hi == 4)
return Color.FromArgb(255, t, p, v);
else
return Color.FromArgb(255, v, p, q);
}
public static void RgbToHls(int r, int g, int b,
out double h, out double l, out double s)
{
// Convert RGB to a 0.0 to 1.0 range.
double double_r = r / 255.0;
double double_g = g / 255.0;
double double_b = b / 255.0;
// Get the maximum and minimum RGB components.
double max = double_r;
if (max < double_g) max = double_g;
if (max < double_b) max = double_b;
double min = double_r;
if (min > double_g) min = double_g;
if (min > double_b) min = double_b;
double diff = max - min;
l = (max + min) / 2;
if (Math.Abs(diff) < 0.00001)
{
s = 0;
h = 0; // H is really undefined.
}
else
{
if (l <= 0.5) s = diff / (max + min);
else s = diff / (2 - max - min);
double r_dist = (max - double_r) / diff;
double g_dist = (max - double_g) / diff;
double b_dist = (max - double_b) / diff;
if (double_r == max) h = b_dist - g_dist;
else if (double_g == max) h = 2 + r_dist - b_dist;
else h = 4 + g_dist - r_dist;
h = h * 60;
if (h < 0) h += 360;
}
}
// Convert an HLS value into an RGB value.
public static void HlsToRgb(double h, double l, double s,
out int r, out int g, out int b)
{
double p2;
if (l <= 0.5) p2 = l * (1 + s);
else p2 = l + s - l * s;
double p1 = 2 * l - p2;
double double_r, double_g, double_b;
if (s == 0)
{
double_r = l;
double_g = l;
double_b = l;
}
else
{
double_r = QqhToRgb(p1, p2, h + 120);
double_g = QqhToRgb(p1, p2, h);
double_b = QqhToRgb(p1, p2, h - 120);
}
// Convert RGB to the 0 to 255 range.
r = (int)(double_r * 255.0);
g = (int)(double_g * 255.0);
b = (int)(double_b * 255.0);
}
private static double QqhToRgb(double q1, double q2, double hue)
{
if (hue > 360) hue -= 360;
else if (hue < 0) hue += 360;
if (hue < 60) return q1 + (q2 - q1) * hue / 60;
if (hue < 180) return q2;
if (hue < 240) return q1 + (q2 - q1) * (240 - hue) / 60;
return q1;
}
private static void ConvertBgraToRgba(byte[] bytes)
{
for (int i = 0; i < bytes.Length; i += 4)
{
var temp = bytes[i];
bytes[i] = bytes[i + 2];
bytes[i + 2] = temp;
}
}
public static Bitmap AdjustGamma(Image image, float gamma)
{
ImageAttributes attributes = new ImageAttributes();
attributes.SetGamma(gamma);
Point[] points =
{
new Point(0, 0),
new Point(image.Width, 0),
new Point(0, image.Height),
};
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Bitmap bm = new Bitmap(image.Width, image.Height);
using (Graphics gr = Graphics.FromImage(bm))
{
gr.DrawImage(image, points, rect,
GraphicsUnit.Pixel, attributes);
}
return bm;
}
public static byte[] ImageToByte(Bitmap bitmap)
{
BitmapData bmpdata = null;
try
{
bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
return bytedata;
}
finally
{
if (bmpdata != null)
bitmap.UnlockBits(bmpdata);
}
}
}
}