728x90
728x170
▶ ColorShiftType.cs
namespace TestProject
{
/// <summary>
/// 색상 시프트 타입
/// </summary>
public enum ColorShiftType
{
/// <summary>
/// 해당 무
/// </summary>
None,
/// <summary>
/// 왼쪽 시프트
/// </summary>
ShiftLeft,
/// <summary>
/// 오른쪽 시프트
/// </summary>
ShiftRight
}
}
728x90
▶ EdgeTracingType.cs
namespace TestProject
{
/// <summary>
/// 가장자리 추적 타입
/// </summary>
public enum EdgeTracingType
{
/// <summary>
/// 검정색
/// </summary>
Black,
/// <summary>
/// 흰색
/// </summary>
White,
/// <summary>
/// 1/2 강도
/// </summary>
HalfIntensity,
/// <summary>
/// 2배 강도
/// </summary>
DoubleIntensity,
/// <summary>
/// 색상 반전
/// </summary>
ColorInversion
}
}
300x250
▶ BitmapHelper.cs
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace TestProject
{
/// <summary>
/// 비트맵 헬퍼
/// </summary>
public static class BitmapHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 복사하기 - Copy(sourceBitmap, squareSize)
/// <summary>
/// 복사하기
/// </summary>
/// <param name="sourceBitmap">소스 비트맵</param>
/// <param name="squareSize">정사각형 크기</param>
/// <returns>비트맵</returns>
public static Bitmap Copy(Bitmap sourceBitmap, int squareSize)
{
float ratio = 1.0f;
int maximumSide = sourceBitmap.Width > sourceBitmap.Height ? sourceBitmap.Width : sourceBitmap.Height;
ratio = (float)maximumSide / (float)squareSize;
Bitmap targetBitmap;
if(sourceBitmap.Width > sourceBitmap.Height)
{
targetBitmap = new Bitmap(squareSize, (int)(sourceBitmap.Height / ratio));
}
else
{
targetBitmap = new Bitmap((int)(sourceBitmap.Width / ratio), squareSize);
}
using(Graphics targetGraphics = Graphics.FromImage(targetBitmap))
{
targetGraphics.CompositingQuality = CompositingQuality.HighQuality;
targetGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
targetGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
targetGraphics.DrawImage
(
sourceBitmap,
new Rectangle(0, 0, targetBitmap.Width, targetBitmap.Height),
new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
GraphicsUnit.Pixel
);
targetGraphics.Flush();
}
return targetBitmap;
}
#endregion
#region 바이트 배열 구하기 - GetByteArray(sourceBitmap)
/// <summary>
/// 바이트 배열 구하기
/// </summary>
/// <param name="sourceBitmap">소스 비트맵</param>
/// <returns>바이트 배열</returns>
public static byte[] GetByteArray(Bitmap sourceBitmap)
{
BitmapData sourceBitmapData = sourceBitmap.LockBits
(
new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb
);
byte[] targetByteArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];
Marshal.Copy(sourceBitmapData.Scan0, targetByteArray, 0, targetByteArray.Length);
sourceBitmap.UnlockBits(sourceBitmapData);
return targetByteArray;
}
#endregion
#region 평균 색상 필터 적용하기 - ApplyAverageColorFilter(sourceBitmap, matrixSize, applyBlue, applyGreen, applyRed, colorShiftType)
/// <summary>
/// 평균 색상 필터 적용하기
/// </summary>
/// <param name="sourceBitmap">소스 비트맵</param>
/// <param name="matrixSize">매트릭스 크기</param>
/// <param name="applyBlue">청색 채널 적용 여부</param>
/// <param name="applyGreen">녹색 채널 적용 여부</param>
/// <param name="applyRed">적색 채널 적용 여부</param>
/// <param name="colorShiftType">색상 시프트 타입</param>
/// <returns>비트맵</returns>
public static Bitmap ApplyAverageColorFilter
(
Bitmap sourceBitmap,
int matrixSize,
bool applyBlue = true,
bool applyGreen = true,
bool applyRed = true,
ColorShiftType colorShiftType = ColorShiftType.None
)
{
byte[] sourceByteArray = GetByteArray(sourceBitmap);
byte[] targetByteArray = new byte[sourceByteArray.Length];
int sourceOffset = 0;
int targetOffset = 0;
int blue = 0;
int green = 0;
int red = 0;
int filterOffset = (matrixSize - 1) / 2;
for(int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
for(int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++)
{
targetOffset = offsetY * sourceBitmap.Width * 4 + offsetX * 4;
blue = 0;
green = 0;
red = 0;
for(int filterY = -filterOffset; filterY <= filterOffset; filterY++)
{
for(int filterX = -filterOffset; filterX <= filterOffset; filterX++)
{
sourceOffset = targetOffset + (filterX * 4) + (filterY * sourceBitmap.Width * 4);
blue += sourceByteArray[sourceOffset ];
green += sourceByteArray[sourceOffset + 1];
red += sourceByteArray[sourceOffset + 2];
}
}
blue = blue / matrixSize;
green = green / matrixSize;
red = red / matrixSize;
if(applyBlue == false)
{
blue = sourceByteArray[targetOffset];
}
if(applyGreen == false)
{
green = sourceByteArray[targetOffset + 1];
}
if(applyRed == false)
{
red = sourceByteArray[targetOffset + 2];
}
if(colorShiftType == ColorShiftType.None)
{
targetByteArray[targetOffset ] = (byte)blue;
targetByteArray[targetOffset + 1] = (byte)green;
targetByteArray[targetOffset + 2] = (byte)red;
targetByteArray[targetOffset + 3] = 255;
}
else if(colorShiftType == ColorShiftType.ShiftLeft)
{
targetByteArray[targetOffset ] = (byte)green;
targetByteArray[targetOffset + 1] = (byte)red;
targetByteArray[targetOffset + 2] = (byte)blue;
targetByteArray[targetOffset + 3] = 255;
}
else if(colorShiftType == ColorShiftType.ShiftRight)
{
targetByteArray[targetOffset ] = (byte)red;
targetByteArray[targetOffset + 1] = (byte)blue;
targetByteArray[targetOffset + 2] = (byte)green;
targetByteArray[targetOffset + 3] = 255;
}
}
}
Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData targetBitmapData = targetBitmap.LockBits
(
new Rectangle(0, 0, targetBitmap.Width, targetBitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb
);
Marshal.Copy(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);
targetBitmap.UnlockBits(targetBitmapData);
return targetBitmap;
}
#endregion
#region 이미지 추상 색상 필터 적용하기 - ApplyImageAbstractColorFilter(sourceBitmap, matrixSize, edgeThreshold,
applyBlue, applyGreen, applyRed, edgeTracingType, colorShiftType)
/// <summary>
/// 이미지 추상 색상 필터 적용하기
/// </summary>
/// <param name="sourceBitmap">소스 비트맵</param>
/// <param name="matrixSize">매트릭스 크기</param>
/// <param name="edgeThreshold">가장자리 임계치</param>
/// <param name="applyBlue">청색 채널 적용 여부</param>
/// <param name="applyGreen">녹색 채널 적용 여부</param>
/// <param name="applyRed">적색 채널 적용 여부</param>
/// <param name="edgeTracingType">가장자리 추적 타입</param>
/// <param name="colorShiftType">색상 시프트 타입</param>
/// <returns>비트맵</returns>
public static Bitmap ApplyImageAbstractColorFilter
(
Bitmap sourceBitmap,
int matrixSize,
byte edgeThreshold,
bool applyBlue = true,
bool applyGreen = true,
bool applyRed = true,
EdgeTracingType edgeTracingType = EdgeTracingType.Black,
ColorShiftType colorShiftType = ColorShiftType.None
)
{
Bitmap edgeBitmap = ApplyGradientBasedEdgeDetectionFilter(sourceBitmap, edgeThreshold);
Bitmap colorBitmap = ApplyAverageColorFilter
(
sourceBitmap,
matrixSize,
applyBlue,
applyGreen,
applyRed,
colorShiftType
);
byte[] edgeByteArray = GetByteArray(edgeBitmap);
byte[] colorByteArray = GetByteArray(colorBitmap);
byte[] targetByteArray = GetByteArray(colorBitmap);
for(int k = 0; k + 4 < edgeByteArray.Length; k += 4)
{
if(edgeByteArray[k] == 255)
{
switch(edgeTracingType)
{
case EdgeTracingType.Black:
targetByteArray[k ] = 0;
targetByteArray[k + 1] = 0;
targetByteArray[k + 2] = 0;
break;
case EdgeTracingType.White :
targetByteArray[k ] = 255;
targetByteArray[k + 1] = 255;
targetByteArray[k + 2] = 255;
break;
case EdgeTracingType.HalfIntensity :
targetByteArray[k ] = ClipByte(targetByteArray[k ] * 0.5);
targetByteArray[k + 1] = ClipByte(targetByteArray[k + 1] * 0.5);
targetByteArray[k + 2] = ClipByte(targetByteArray[k + 2] * 0.5);
break;
case EdgeTracingType.DoubleIntensity :
targetByteArray[k ] = ClipByte(targetByteArray[k ] * 2);
targetByteArray[k + 1] = ClipByte(targetByteArray[k + 1] * 2);
targetByteArray[k + 2] = ClipByte(targetByteArray[k + 2] * 2);
break;
case EdgeTracingType.ColorInversion :
targetByteArray[k ] = ClipByte(255 - targetByteArray[k ]);
targetByteArray[k + 1] = ClipByte(255 - targetByteArray[k + 1]);
targetByteArray[k + 2] = ClipByte(255 - targetByteArray[k + 2]);
break;
}
}
}
Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData targetBitmapData = targetBitmap.LockBits
(
new Rectangle(0, 0,targetBitmap.Width, targetBitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb
);
Marshal.Copy(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);
targetBitmap.UnlockBits(targetBitmapData);
return targetBitmap;
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 임계치 체크하기 - CheckThreshold(sourceByteArray, offset1, offset2, gradientValue, threshold, divideBy)
/// <summary>
/// 임계치 체크하기
/// </summary>
/// <param name="sourceByteArray">소스 바이트 배열</param>
/// <param name="offset1">오프셋 1</param>
/// <param name="offset2">오프셋 2</param>
/// <param name="gradientValue">그라디언트 값</param>
/// <param name="threshold">임계치</param>
/// <param name="divideBy">젯수</param>
/// <returns>임계치 체크 결과</returns>
private static bool CheckThreshold
(
byte[] sourceByteArray,
int offset1,
int offset2,
ref int gradientValue,
byte threshold,
int divideBy = 1
)
{
gradientValue += Math.Abs(sourceByteArray[offset1] - sourceByteArray[offset2]) / divideBy;
gradientValue += Math.Abs(sourceByteArray[offset1 + 1] - sourceByteArray[offset2 + 1]) / divideBy;
gradientValue += Math.Abs(sourceByteArray[offset1 + 2] - sourceByteArray[offset2 + 2]) / divideBy;
return (gradientValue >= threshold);
}
#endregion
#region 그라디언트 기반 탐지 필터 적용하기 - ApplyGradientBasedEdgeDetectionFilter(sourceBitmap, threshold)
/// <summary>
/// 그라디언트 기반 탐지 필터 적용하기
/// </summary>
/// <param name="sourceBitmap">소스 비트맵</param>
/// <param name="threshold">임계치</param>
/// <returns>비트맵</returns>
public static Bitmap ApplyGradientBasedEdgeDetectionFilter(Bitmap sourceBitmap, byte threshold = 0)
{
BitmapData sourceBitmapData = sourceBitmap.LockBits
(
new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb
);
byte[] sourceByteArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];
byte[] targetByteArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];
Marshal.Copy(sourceBitmapData.Scan0, sourceByteArray, 0, sourceByteArray.Length);
sourceBitmap.UnlockBits(sourceBitmapData);
int sourceOffset = 0;
int gradientValue = 0;
bool exceedThreshold = false;
for(int offsetY = 1; offsetY < sourceBitmap.Height - 1; offsetY++)
{
for(int offsetX = 1; offsetX < sourceBitmap.Width - 1; offsetX++)
{
sourceOffset = offsetY * sourceBitmapData.Stride + offsetX * 4;
gradientValue = 0;
exceedThreshold = true;
CheckThreshold
(
sourceByteArray,
sourceOffset - 4,
sourceOffset + 4,
ref gradientValue,
threshold,
2
);
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - sourceBitmapData.Stride,
sourceOffset + sourceBitmapData.Stride,
ref gradientValue,
threshold,
2
);
if(exceedThreshold == false)
{
gradientValue = 0;
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - 4,
sourceOffset + 4,
ref gradientValue,
threshold
);
if(exceedThreshold == false)
{
gradientValue = 0;
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - sourceBitmapData.Stride,
sourceOffset + sourceBitmapData.Stride,
ref gradientValue,
threshold
);
if(exceedThreshold == false)
{
gradientValue = 0;
CheckThreshold
(
sourceByteArray,
sourceOffset - 4 - sourceBitmapData.Stride,
sourceOffset + 4 + sourceBitmapData.Stride,
ref gradientValue,
threshold,
2
);
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - sourceBitmapData.Stride + 4,
sourceOffset - 4 + sourceBitmapData.Stride,
ref gradientValue,
threshold,
2
);
if(exceedThreshold == false)
{
gradientValue = 0;
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - 4 - sourceBitmapData.Stride,
sourceOffset + 4 + sourceBitmapData.Stride,
ref gradientValue,
threshold
);
if(exceedThreshold == false)
{
gradientValue = 0;
exceedThreshold = CheckThreshold
(
sourceByteArray,
sourceOffset - sourceBitmapData.Stride + 4,
sourceOffset + sourceBitmapData.Stride - 4,
ref gradientValue,
threshold
);
}
}
}
}
}
targetByteArray[sourceOffset ] = (byte)(exceedThreshold ? 255 : 0);
targetByteArray[sourceOffset + 1] = targetByteArray[sourceOffset];
targetByteArray[sourceOffset + 2] = targetByteArray[sourceOffset];
targetByteArray[sourceOffset + 3] = 255;
}
}
Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData targetBitmapData = targetBitmap.LockBits
(
new Rectangle(0, 0, targetBitmap.Width, targetBitmap.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb
);
Marshal.Copy(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);
targetBitmap.UnlockBits(targetBitmapData);
return targetBitmap;
}
#endregion
#region 바이트 자르기 - ClipByte(value)
/// <summary>
/// 바이트 자르기
/// </summary>
/// <param name="value">값</param>
/// <returns>바이트</returns>
private static byte ClipByte(double value)
{
return (byte)(value > 255 ? 255 : (value < 0 ? 0 : value));
}
#endregion
}
}
▶ MainForm.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
namespace TestProject
{
/// <summary>
/// 메인 폼
/// </summary>
public partial class MainForm : Form
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 소스 비트맵
/// </summary>
private Bitmap sourceBitmap = null;
/// <summary>
/// 미리보기 비트맵
/// </summary>
private Bitmap previewBitmap = null;
/// <summary>
/// 타겟 비트맵
/// </summary>
private Bitmap targetBitmap = null;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
this.filterSizeComboBox.SelectedIndex = 0;
this.colorShiftTypeComboBox.Items.Add(ColorShiftType.None );
this.colorShiftTypeComboBox.Items.Add(ColorShiftType.ShiftLeft );
this.colorShiftTypeComboBox.Items.Add(ColorShiftType.ShiftRight);
this.colorShiftTypeComboBox.SelectedIndex = 0;
this.edgeTracingTypeComboBox.Items.Add(EdgeTracingType.Black );
this.edgeTracingTypeComboBox.Items.Add(EdgeTracingType.DoubleIntensity);
this.edgeTracingTypeComboBox.Items.Add(EdgeTracingType.HalfIntensity );
this.edgeTracingTypeComboBox.Items.Add(EdgeTracingType.ColorInversion );
this.edgeTracingTypeComboBox.Items.Add(EdgeTracingType.White );
this.edgeTracingTypeComboBox.SelectedIndex = 0;
this.applyRedCheckBox.CheckedChanged += filterValueChanged;
this.applyGreenCheckBox.CheckedChanged += filterValueChanged;
this.applyBlueCheckBox.CheckedChanged += filterValueChanged;
this.filterSizeComboBox.SelectedIndexChanged += filterValueChanged;
this.colorShiftTypeComboBox.SelectedIndexChanged += filterValueChanged;
this.edgeTracingTypeComboBox.SelectedIndexChanged += filterValueChanged;
this.edgeThresholdNumericUpDown.ValueChanged += filterValueChanged;
this.loadImageButton.Click += loadImageButton_Click;
this.saveImageButton.Click += saveImageButton_Click;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 필터 값 변경시 처리하기 - filterValueChanged(sender, e)
/// <summary>
/// 필터 값 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void filterValueChanged(object sender, EventArgs e)
{
ApplyFilter(true);
}
#endregion
#region 이미지 로드 버튼 클릭시 처리하기 - loadImageButton_Click(sender, e)
/// <summary>
/// 이미지 로드 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void loadImageButton_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Title = "이미지 파일 로드";
openFileDialog.Filter = "PNG 이미지(*.png)|*.png|JPEG 이미지(*.jpg)|*.jpg|비트맵 이미지(*.bmp)|*.bmp";
if(openFileDialog.ShowDialog() == DialogResult.OK)
{
StreamReader reader = new StreamReader(openFileDialog.FileName);
this.sourceBitmap = Bitmap.FromStream(reader.BaseStream) as Bitmap;
reader.Close();
this.previewBitmap = BitmapHelper.Copy(this.sourceBitmap, this.pictureBox.Width);
this.pictureBox.Image = this.previewBitmap;
ApplyFilter(true);
}
}
#endregion
#region 이미지 저장 버튼 클릭시 처리하기 - saveImageButton_Click(sender, e)
/// <summary>
/// 이미지 저장 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void saveImageButton_Click(object sender, EventArgs e)
{
ApplyFilter(false);
if(this.targetBitmap != null)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Title = "이미지 파일 저장";
saveFileDialog.Filter = "PNG 이미지(*.png)|*.png|JPEG 이미지(*.jpg)|*.jpg|비트맵 이미지(*.bmp)|*.bmp";
if(saveFileDialog.ShowDialog() == DialogResult.OK)
{
string fileExtension = Path.GetExtension(saveFileDialog.FileName).ToUpper();
ImageFormat imageFormat = ImageFormat.Png;
if(fileExtension == "BMP")
{
imageFormat = ImageFormat.Bmp;
}
else if(fileExtension == "JPG")
{
imageFormat = ImageFormat.Jpeg;
}
StreamWriter writer = new StreamWriter(saveFileDialog.FileName, false);
this.targetBitmap.Save(writer.BaseStream, imageFormat);
writer.Flush();
writer.Close();
this.targetBitmap = null;
}
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 필터 적용하기 - ApplyFilter(preview)
/// <summary>
/// 필터 적용하기
/// </summary>
/// <param name="preview">미리보기 여부</param>
private void ApplyFilter(bool preview)
{
if(this.previewBitmap == null || this.filterSizeComboBox.SelectedIndex == -1)
{
return;
}
Bitmap sourceBitmap = null;
Bitmap targetBitmap = null;
if(preview == true)
{
sourceBitmap = this.previewBitmap;
}
else
{
sourceBitmap = this.sourceBitmap;
}
if(sourceBitmap != null)
{
if(this.filterSizeComboBox.SelectedItem.ToString() == "None")
{
targetBitmap = sourceBitmap;
}
else
{
int filterSize = 0;
ColorShiftType colorShiftType = (ColorShiftType )this.colorShiftTypeComboBox.SelectedItem;
EdgeTracingType edgeTracingType = (EdgeTracingType)this.edgeTracingTypeComboBox.SelectedItem;
if(int.TryParse(this.filterSizeComboBox.SelectedItem.ToString(), out filterSize))
{
targetBitmap = BitmapHelper.ApplyImageAbstractColorFilter
(
sourceBitmap,
filterSize,
(byte)this.edgeThresholdNumericUpDown.Value,
applyBlueCheckBox.Checked,
applyGreenCheckBox.Checked,
applyRedCheckBox.Checked,
edgeTracingType,
colorShiftType
);
}
}
}
if(targetBitmap != null)
{
if(preview == true)
{
this.pictureBox.Image = targetBitmap;
}
else
{
this.targetBitmap = targetBitmap;
}
}
}
#endregion
}
}
728x90
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] Bitmap 클래스 : 컴파스 가장자리 탐지 필터(Compass Edge Detection Filter) 사용하기 (0) | 2021.01.04 |
---|---|
[C#/WINFORM] Bitmap 클래스 : 스테인 글라스 필터(Stained Glass Filter) 사용하기 (0) | 2021.01.04 |
[C#/WINFORM] Bitmap 클래스 : 경계 추출 필터(Boundary Extraction Filter) 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 카툰 필터(Cartoon Filter) 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 오일 페인트 필터(Oil Paint Filter) 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 바이트 배열 구하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 정사각형 크기 비트맵에 복사하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 비트맵 일부 오버레이 표시하기 (0) | 2020.12.27 |
[C#/WINFORM] Bitmap 클래스 : 크기 변경 비트맵 구하기 (0) | 2020.12.27 |
[C#/WINFORM] Bitmap 클래스 : 비트맵 파일 로드하기 (0) | 2020.12.27 |