using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace TestProject
{
/// <summary>
/// 비트맵 헬퍼
/// </summary>
public class BitmapHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 워프 연산 - WarpOperation
/// <summary>
/// 워프 연산
/// </summary>
public enum WarpOperation
{
/// <summary>
/// Identity
/// </summary>
Identity,
/// <summary>
/// FishEye
/// </summary>
FishEye,
/// <summary>
/// Twist
/// </summary>
Twist,
/// <summary>
/// Wave
/// </summary>
Wave,
/// <summary>
/// SmallTop
/// </summary>
SmallTop,
/// <summary>
/// Wiggles
/// </summary>
Wiggles,
/// <summary>
/// DoubleWave
/// </summary>
DoubleWave
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Class
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 필터 - Filter
/// <summary>
/// 필터
/// </summary>
public class Filter
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 커널 배열
/// </summary>
public float[,] KernelArray;
/// <summary>
/// 가중치
/// </summary>
public float Weight;
/// <summary>
/// 오프셋
/// </summary>
public float Offset;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 정규화하기 - Normalize()
/// <summary>
/// 정규화하기
/// </summary>
public void Normalize()
{
Weight = 0;
for(int row = 0; row <= KernelArray.GetUpperBound(0); row++)
{
for(int column = 0; column <= KernelArray.GetUpperBound(1); column++)
{
Weight += KernelArray[row, column];
}
}
}
#endregion
#region 총합이 0이 되는 중앙 커널 상관계수 설정하기 - SetCenterKernelCoefficientForZeroTotal()
/// <summary>
/// 총합이 0이 되는 중앙 커널 상관계수 설정하기
/// </summary>
public void SetCenterKernelCoefficientForZeroTotal()
{
float total = 0;
int rowCount = KernelArray.GetUpperBound(0);
int columnCount = KernelArray.GetUpperBound(1);
for(int row = 0; row <= rowCount; row++)
{
for(int column = 0; column <= columnCount; column++)
{
total += KernelArray[row, column];
}
}
int middleRow = (int)(KernelArray.GetUpperBound(0) / 2);
int middleColumn = (int)(KernelArray.GetUpperBound(1) / 2);
total -= KernelArray[middleRow, middleColumn];
KernelArray[middleRow, middleColumn] = -total;
}
#endregion
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 이미지 바이트 배열
/// </summary>
public byte[] ImageByteArray;
/// <summary>
/// 행 크기 바이트 카운트
/// </summary>
public int RowSizeByteCount;
/// <summary>
/// 픽셀 데이터 크기
/// </summary>
public const int PixelDataSize = 32;
/// <summary>
/// 비트맵
/// </summary>
public Bitmap Bitmap;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 잠금 여부
/// </summary>
private bool isLocked = false;
/// <summary>
/// 비트맵 데이터
/// </summary>
private BitmapData bitmapData;
#endregion
#region 너비 - Width
/// <summary>
/// 너비
/// </summary>
public int Width
{
get
{
return Bitmap.Width;
}
}
#endregion
#region 높이 - Height
/// <summary>
/// 높이
/// </summary>
public int Height
{
get
{
return Bitmap.Height;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 엠보싱 필터 - EmbossingFilter
/// <summary>
/// 엠보싱 필터
/// </summary>
public static Filter EmbossingFilter
{
get
{
return new Filter()
{
Weight = 1,
Offset = 127,
KernelArray = new float[,]
{
{ -1, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 1 }
}
};
}
}
#endregion
#region 엠보싱 필터 2 - EmbossingFilter2
/// <summary>
/// 엠보싱 필터 2
/// </summary>
public static Filter EmbossingFilter2
{
get
{
return new Filter()
{
Weight = 1,
Offset = 127,
KernelArray = new float[,]
{
{ 2, 0, 0 },
{ 0, -1, 0 },
{ 0, 0, -1 }
}
};
}
}
#endregion
#region 5X5 가우시안 블러 필터 - BlurFilter5X5Gaussian
/// <summary>
/// 5X5 가우시안 블러 필터
/// </summary>
public static Filter BlurFilter5X5Gaussian
{
get
{
Filter filter = new Filter()
{
Offset = 0,
KernelArray = new float[,]
{
{ 1, 4, 7, 4, 1 },
{ 4, 16, 26, 16, 4 },
{ 7, 26, 41, 26, 7 },
{ 4, 16, 26, 16, 4 },
{ 1, 4, 7, 4, 1 }
}
};
filter.Normalize();
return filter;
}
}
#endregion
#region 5X5 평균 블러 필터 - BlurFilter5X5Mean
/// <summary>
/// 5X5 평균 블러 필터
/// </summary>
public static Filter BlurFilter5X5Mean
{
get
{
Filter filter = new Filter()
{
Offset = 0,
KernelArray = new float[,]
{
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 }
}
};
filter.Normalize();
return filter;
}
}
#endregion
#region 좌상단→우하단 모서리 탐지 필터 - EdgeDetectionFilterULtoLR
/// <summary>
/// 좌상단→우하단 모서리 탐지 필터
/// </summary>
public static Filter EdgeDetectionFilterULtoLR
{
get
{
return new Filter()
{
Weight = 1,
Offset = 0,
KernelArray = new float[,]
{
{ -5, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 5 }
}
};
}
}
#endregion
#region 상단→하단 모서리 탐지 필터 - EdgeDetectionFilterTopToBottom
/// <summary>
/// 상단→하단 모서리 탐지 필터
/// </summary>
public static Filter EdgeDetectionFilterTopToBottom
{
get
{
return new Filter()
{
Weight = 1,
Offset = 0,
KernelArray = new float[,]
{
{ -1, -1, -1 },
{ 0, 0, 0 },
{ 1, 1, 1 }
}
};
}
}
#endregion
#region 좌측→우측 모서리 탐지 필터 - EdgeDetectionFilterLeftToRight
/// <summary>
/// 좌측→우측 모서리 탐지 필터
/// </summary>
public static Filter EdgeDetectionFilterLeftToRight
{
get
{
return new Filter()
{
Weight = 1,
Offset = 0,
KernelArray = new float[,]
{
{ -1, 0, 1 },
{ -1, 0, 1 },
{ -1, 0, 1 }
}
};
}
}
#endregion
#region 3X3 하이 패스 필터 - HighPassFilter3X3
/// <summary>
/// 3X3 하이 패스 필터
/// </summary>
public static Filter HighPassFilter3X3
{
get
{
return new Filter()
{
Weight = 16,
Offset = 127,
KernelArray = new float[,]
{
{ -1, -2, -1 },
{ -2, 12, -2 },
{ -1, -2, -1 }
}
};
}
}
#endregion
#region 5X5 하이 패스 필터 - HighPassFilter5X5
/// <summary>
/// 5X5 하이 패스 필터
/// </summary>
public static Filter HighPassFilter5X5
{
get
{
Filter filter = new Filter()
{
Offset = 127,
KernelArray = new float[,]
{
{ -1, -4, -7, -4, -1 },
{ -4, -16, -26, -16, -4 },
{ -7, -26, -41, -26, -7 },
{ -4, -16, -26, -16, -4 },
{ -1, -4, -7, -4, -1 }
}
};
filter.Normalize();
filter.Weight = -filter.Weight;
filter.SetCenterKernelCoefficientForZeroTotal();
return filter;
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 잠금 여부 - IsLocked
/// <summary>
/// 잠금 여부
/// </summary>
public bool IsLocked
{
get
{
return this.isLocked;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - BitmapHelper(bitmap)
/// <summary>
/// 생성자
/// </summary>
/// <param name="bitmap">비트맵</param>
public BitmapHelper(Bitmap bitmap)
{
Bitmap = bitmap;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 픽셀 매핑하기 - MapPixel(warpOperation, middleX, middleY, maximumR, targetX, targetY, sourceX, sourceY)
/// <summary>
/// 픽셀 매핑하기
/// </summary>
/// <param name="warpOperation">워프 연산</param>
/// <param name="middleX">중간 X</param>
/// <param name="middleY">중간 Y</param>
/// <param name="maximumR">최대 R</param>
/// <param name="targetX">타겟 X</param>
/// <param name="targetY">타겟 Y</param>
/// <param name="sourceX">소스 X</param>
/// <param name="sourceY">소스 Y</param>
/// <remarks>출력 픽셀(targetX, targetY)을 다시 입력 픽셀(sourceX, sourceY)에 매핑한다.</remarks>
private static void MapPixel(WarpOperation warpOperation, double middleX, double middleY, double maximumR, int targetX, int targetY, out double sourceX, out double sourceY)
{
const double PI_OVER_2 = Math.PI / 2.0;
const double K = 100.0;
const double OFFSET = -PI_OVER_2;
double deltaX;
double deltaY;
double r1;
double r2;
switch(warpOperation)
{
case WarpOperation.Identity :
sourceX = targetX;
sourceY = targetY;
break;
case WarpOperation.FishEye :
deltaX = targetX - middleX;
deltaY = targetY - middleY;
r1 = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
if(r1 == 0)
{
sourceX = middleX;
sourceY = middleY;
}
else
{
r2 = maximumR / 2 * (1 / (1 - r1 / maximumR) - 1);
sourceX = deltaX * r2 / r1 + middleX;
sourceY = deltaY * r2 / r1 + middleY;
}
break;
case WarpOperation.Twist :
deltaX = targetX - middleX;
deltaY = targetY - middleY;
r1 = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
if(r1 == 0)
{
sourceX = 0;
sourceY = 0;
}
else
{
double theta = Math.Atan2(deltaX, deltaY) - r1 / K - OFFSET;
sourceX = r1 * Math.Cos(theta);
sourceY = r1 * Math.Sin(theta);
}
sourceX += middleX;
sourceY += middleY;
break;
case WarpOperation.Wave :
sourceX = targetX;
sourceY = targetY - 10 * (Math.Sin(targetX / 50.0 * Math.PI) + 1) + 5;
break;
case WarpOperation.SmallTop :
deltaX = middleX - targetX;
deltaX = deltaX * middleY * 2 / (targetY + 1);
sourceX = middleX - deltaX;
sourceY = targetY;
break;
case WarpOperation.Wiggles :
deltaX = middleX - targetX;
deltaX = deltaX * (Math.Sin(targetY / 50.0 * Math.PI) / 2 + 1.5);
sourceX = middleX - deltaX;
sourceY = targetY;
break;
case WarpOperation.DoubleWave :
sourceX = targetX - 10 * (Math.Sin(targetY / 50.0 * Math.PI) + 1) + 5;
sourceY = targetY - 10 * (Math.Sin(targetX / 50.0 * Math.PI) + 1) + 5;
break;
default : // 수직/수평 반전
sourceX = 2 * middleX - targetX;
sourceY = 2 * middleY - targetY;
break;
}
}
#endregion
#region 비트맵 워프하기 - WarpBitmap(sourceHelper, targetHelper, warpOperation)
/// <summary>
/// 비트맵 워프하기
/// </summary>
/// <param name="sourceHelper">소스 비트맵 헬퍼</param>
/// <param name="targetHelper">타겟 비트맵 헬퍼</param>
/// <param name="warpOperation">워프 연산</param>
private static void WarpBitmap(BitmapHelper sourceHelper, BitmapHelper targetHelper, WarpOperation warpOperation)
{
double middleX = targetHelper.Width / 2.0;
double middleY = targetHelper.Height / 2.0;
double maximumR = targetHelper.Width * 0.75;
int maximumIndexX = sourceHelper.Width - 2;
int maximumIndexY = sourceHelper.Height - 2;
for(int y1 = 0; y1 < targetHelper.Height; y1++)
{
for (int x1 = 0; x1 < targetHelper.Width; x1++)
{
MapPixel(warpOperation, middleX, middleY, maximumR, x1, y1, out double x0, out double y0);
int indexX0 = (int)x0;
int indexY0 = (int)y0;
if((indexX0 < 0) || (indexX0 > maximumIndexX) || (indexY0 < 0) || (indexY0 > maximumIndexY))
{
targetHelper.SetPixel(x1, y1, 255, 255, 255, 255);
}
else
{
double deltaX0 = x0 - indexX0;
double deltaY0 = y0 - indexY0;
double deltaX1 = 1 - deltaX0;
double deltaY1 = 1 - deltaY0;
sourceHelper.GetPixel(indexX0 , indexY0 , out byte r00, out byte g00, out byte b00, out byte a00);
sourceHelper.GetPixel(indexX0 , indexY0 + 1, out byte r01, out byte g01, out byte b01, out byte a01);
sourceHelper.GetPixel(indexX0 + 1, indexY0 , out byte r10, out byte g10, out byte b10, out byte a10);
sourceHelper.GetPixel(indexX0 + 1, indexY0 + 1, out byte r11, out byte g11, out byte b11, out byte a11);
int r = (int)(r00 * deltaX1 * deltaY1 + r01 * deltaX1 * deltaY0 + r10 * deltaX0 * deltaY1 + r11 * deltaX0 * deltaY0);
int g = (int)(g00 * deltaX1 * deltaY1 + g01 * deltaX1 * deltaY0 + g10 * deltaX0 * deltaY1 + g11 * deltaX0 * deltaY0);
int b = (int)(b00 * deltaX1 * deltaY1 + b01 * deltaX1 * deltaY0 + b10 * deltaX0 * deltaY1 + b11 * deltaX0 * deltaY0);
int a = (int)(a00 * deltaX1 * deltaY1 + a01 * deltaX1 * deltaY0 + a10 * deltaX0 * deltaY1 + a11 * deltaX0 * deltaY0);
targetHelper.SetPixel(x1, y1, (byte)r, (byte)g, (byte)b, (byte)a);
}
}
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 비트맵 잠금 설정하기 - LockBitmap()
/// <summary>
/// 비트맵 잠금 설정하기
/// </summary>
public void LockBitmap()
{
if(IsLocked)
{
return;
}
Rectangle boundRectangle = new Rectangle
(
0,
0,
Bitmap.Width,
Bitmap.Height
);
this.bitmapData = Bitmap.LockBits
(
boundRectangle,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb
);
RowSizeByteCount = this.bitmapData.Stride;
int totalSize = this.bitmapData.Stride * this.bitmapData.Height;
ImageByteArray = new byte[totalSize];
Marshal.Copy(this.bitmapData.Scan0, ImageByteArray, 0, totalSize);
this.isLocked = true;
}
#endregion
#region 비트맵 잠금 해제하기 - UnlockBitmap()
/// <summary>
/// 비트맵 잠금 해제하기
/// </summary>
public void UnlockBitmap()
{
if(!IsLocked)
{
return;
}
int totalSize = this.bitmapData.Stride * this.bitmapData.Height;
Marshal.Copy(ImageByteArray, 0, this.bitmapData.Scan0, totalSize);
Bitmap.UnlockBits(this.bitmapData);
ImageByteArray = null;
this.bitmapData = null;
this.isLocked = false;
}
#endregion
#region 픽셀 설정하기 - SetPixel(x, y, red, green, blue, alpha)
/// <summary>
/// 픽셀 설정하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="red">적색 채널</param>
/// <param name="green">녹색 채널</param>
/// <param name="blue">청색 채널</param>
/// <param name="alpha">알파 채널</param>
public void SetPixel(int x, int y, byte red, byte green, byte blue, byte alpha)
{
int i = y * this.bitmapData.Stride + x * 4;
ImageByteArray[i++] = blue;
ImageByteArray[i++] = green;
ImageByteArray[i++] = red;
ImageByteArray[i ] = alpha;
}
#endregion
#region 픽셀 구하기 - GetPixel(x, y, red, green, blue, alpha)
/// <summary>
/// 픽셀 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="red">적색 채널</param>
/// <param name="green">녹색 채널</param>
/// <param name="blue">청색 채널</param>
/// <param name="alpha">알파 채널</param>
public void GetPixel(int x, int y, out byte red, out byte green, out byte blue, out byte alpha)
{
int i = y * this.bitmapData.Stride + x * 4;
blue = ImageByteArray[i++];
green = ImageByteArray[i++];
red = ImageByteArray[i++];
alpha = ImageByteArray[i ];
}
#endregion
#region 알파 채널 설정하기 - SetAlpha(x, y, alpha)
/// <summary>
/// 알파 채널 설정하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="alpha">알파 채널</param>
public void SetAlpha(int x, int y, byte alpha)
{
int i = y * this.bitmapData.Stride + x * 4;
ImageByteArray[i + 3] = alpha;
}
#endregion
#region 알파 채널 구하기 - GetAlpha(x, y)
/// <summary>
/// 알파 채널 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <returns>알파 채널</returns>
public byte GetAlpha(int x, int y)
{
int i = y * this.bitmapData.Stride + x * 4;
return ImageByteArray[i + 3];
}
#endregion
#region 녹색 채널 설정하기 - SetGreen(x, y, green)
/// <summary>
/// 녹색 채널 설정하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="green">녹색 채널</param>
public void SetGreen(int x, int y, byte green)
{
int i = y * this.bitmapData.Stride + x * 4;
ImageByteArray[i + 1] = green;
}
#endregion
#region 녹색 채널 구하기 - GetGreen(x, y)
/// <summary>
/// 녹색 채널 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <returns>녹색 채널</returns>
public byte GetGreen(int x, int y)
{
int i = y * this.bitmapData.Stride + x * 4;
return ImageByteArray[i + 1];
}
#endregion
#region 청색 채널 설정하기 - SetBlue(x, y, blue)
/// <summary>
/// 청색 채널 설정하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="blue">청색 채널</param>
public void SetBlue(int x, int y, byte blue)
{
int i = y * this.bitmapData.Stride + x * 4;
ImageByteArray[i] = blue;
}
#endregion
#region 청색 채널 구하기 - GetBlue(x, y)
/// <summary>
/// 청색 채널 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <returns>청색 채널</returns>
public byte GetBlue(int x, int y)
{
int i = y * this.bitmapData.Stride + x * 4;
return ImageByteArray[i];
}
#endregion
#region 적색 채널 설정하기- SetRed(x, y, red)
/// <summary>
/// 적색 채널 설정하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="red">적색 채널</param>
public void SetRed(int x, int y, byte red)
{
int i = y * this.bitmapData.Stride + x * 4;
ImageByteArray[i + 2] = red;
}
#endregion
#region 적색 채널 구하기 - GetRed(x, y)
/// <summary>
/// 적색 채널 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <returns>적색 채널</returns>
public byte GetRed(int x, int y)
{
int i = y * this.bitmapData.Stride + x * 4;
return ImageByteArray[i + 2];
}
#endregion
#region 적색 채널 지우기 - ClearRed()
/// <summary>
/// 적색 채널 지우기
/// </summary>
public void ClearRed()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
SetRed(x, y, 0);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 녹색 채널 지우기 - ClearGreen()
/// <summary>
/// 녹색 채널 지우기
/// </summary>
public void ClearGreen()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
SetGreen(x, y, 0);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 청색 채널 지우기 - ClearBlue()
/// <summary>
/// 청색 채널 지우기
/// </summary>
public void ClearBlue()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
SetBlue(x, y, 0);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 채널 평균 값으로 회색조 비트맵으로 변환하기 - ConvertToAverageGrayscaleBitmap()
/// <summary>
/// 채널 평균 값으로 회색조 비트맵으로 변환하기
/// </summary>
public void ConvertToAverageGrayscaleBitmap()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
GetPixel(x, y, out byte red, out byte green, out byte blue, out byte alpha);
byte gray = (byte)((red + green + blue) / 3);
SetPixel(x, y, gray, gray, gray, alpha);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 회색조 비트맵으로 변환하기 - ConvertToGrayscaleBitmap()
/// <summary>
/// 회색조 비트맵으로 변환하기
/// </summary>
public void ConvertToGrayscaleBitmap()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
GetPixel(x, y, out byte red, out byte green, out byte blue, out byte alpha);
byte gray = (byte)(0.3 * red + 0.5 * green + 0.2 * blue);
SetPixel(x, y, gray, gray, gray, alpha);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 비트맵 반전하기 - InvertBitmap()
/// <summary>
/// 비트맵 반전하기
/// </summary>
public void InvertBitmap()
{
bool wasLocked = IsLocked;
LockBitmap();
for(int y = 0; y < Height; y++)
{
for(int x = 0; x < Width; x++)
{
byte red = (byte)(255 - GetRed (x, y));
byte green = (byte)(255 - GetGreen(x, y));
byte blue = (byte)(255 - GetBlue (x, y));
byte alpha = GetAlpha(x, y);
SetPixel(x, y, red, green, blue, alpha);
}
}
if(!wasLocked)
{
UnlockBitmap();
}
}
#endregion
#region 복제하기 - Clone()
/// <summary>
/// 복제하기
/// </summary>
/// <returns>비트맵 헬퍼</returns>
public BitmapHelper Clone()
{
bool wasLocked = this.IsLocked;
LockBitmap();
BitmapHelper bitmapHelper = MemberwiseClone() as BitmapHelper;
bitmapHelper.Bitmap = new Bitmap(Bitmap.Width, Bitmap.Height);
bitmapHelper.isLocked = false;
if(!wasLocked)
{
UnlockBitmap();
}
return bitmapHelper;
}
#endregion
#region 필터 적용하기 - ApplyFilter(filter, targetLocked)
/// <summary>
/// 필터 적용하기
/// </summary>
/// <param name="filter">필터</param>
/// <param name="targetLocked">타겟 잠금 여부</param>
/// <returns>비트맵 헬퍼</returns>
public BitmapHelper ApplyFilter(Filter filter, bool targetLocked)
{
BitmapHelper targetHelper = Clone();
bool wasLocked = IsLocked;
LockBitmap();
targetHelper.LockBitmap();
int offsetX = -(int)(filter.KernelArray.GetUpperBound(1) / 2);
int offsetY = -(int)(filter.KernelArray.GetUpperBound(0) / 2);
int minimumX = -offsetX;
int maximumX = Bitmap.Width - filter.KernelArray.GetUpperBound(1);
int minimumY = -offsetY;
int maximumY = Bitmap.Height - filter.KernelArray.GetUpperBound(0);
int maximumRow = filter.KernelArray.GetUpperBound(0);
int maximumColumn = filter.KernelArray.GetUpperBound(1);
for(int x = minimumX; x <= maximumX; x++)
{
for(int y = minimumY; y <= maximumY; y++)
{
bool skipPixel = false;
float red = 0;
float green = 0;
float blue = 0;
for(int row = 0; row <= maximumRow; row++)
{
for(int column = 0; column <= maximumColumn; column++)
{
int indexX = x + column + offsetX;
int indexY = y + row + offsetY;
GetPixel(indexX, indexY, out byte newRed, out byte newGreen, out byte newBlue, out byte newAlpha);
if(newAlpha == 0)
{
skipPixel = true;
break;
}
red += newRed * filter.KernelArray[row, column];
green += newGreen * filter.KernelArray[row, column];
blue += newBlue * filter.KernelArray[row, column];
}
if(skipPixel)
{
break;
}
}
if(!skipPixel)
{
red = filter.Offset + red / filter.Weight;
if(red < 0)
{
red = 0;
}
if(red > 255)
{
red = 255;
}
green = filter.Offset + green / filter.Weight;
if(green < 0)
{
green = 0;
}
if(green > 255)
{
green = 255;
}
blue = filter.Offset + blue / filter.Weight;
if(blue < 0)
{
blue = 0;
}
if(blue > 255)
{
blue = 255;
}
targetHelper.SetPixel(x, y, (byte)red, (byte)green, (byte)blue, GetAlpha(x, y));
}
}
}
if(!targetLocked)
{
targetHelper.UnlockBitmap();
}
if(!wasLocked)
{
UnlockBitmap();
}
return targetHelper;
}
#endregion
#region 워프하기 - Warp(warpOperation, targetLocked)
/// <summary>
/// 워프하기
/// </summary>
/// <param name="warpOperation">워프 연산</param>
/// <param name="targetLocked">타겟 잠금 여부</param>
/// <returns>비트맵 헬퍼</returns>
public BitmapHelper Warp(WarpOperation warpOperation, bool targetLocked)
{
BitmapHelper targetHelper = Clone();
bool wasLocked = IsLocked;
LockBitmap();
targetHelper.LockBitmap();
WarpBitmap(this, targetHelper, warpOperation);
if(!targetLocked)
{
targetHelper.UnlockBitmap();
}
if(!wasLocked)
{
UnlockBitmap();
}
return targetHelper;
}
#endregion
}
}