[WinForm] 픽셀 이동 툴 개발기타 개발 관련/윈폼2023. 11. 10. 00:31
Table of Contents
🚩개요
회사에서 만드는 픽셀 게임 프로젝트가 있다.
게임에 쓰이는 리소스를 외주를 맡겨서 추가로 받았는데,
받고 보니 이미지의 픽셀위치를 좀 이동시켜야했다.
한두장이면 이미지 툴에서 작업하겠지만 양이 상당했기때문에
코드로 한번에 옮겨버리는게 낫지않을까 싶어서 작업하게됐다.
처음에는 대충 Unity로 개발할까 싶었지만
C#의 이미지 관련 API를 쓸려면 프레임워크를 Unity프로젝트에 받아야해서 번거롭고
굳이 무거운 Unity 프로젝트를 새로 만들바에야 그냥 윈폼으로 작업했다.
🔥본문
결과물을 먼저 보자면 이렇다.
📝기능 설명
1.이미지 불러오기
- 파일 선택 창이 열리며 이미지 파일을 다중으로 불러올 수 있다.
2.불러온 이미지 리스트
- 이미지를 불러오면 경로와 함께 작은 미리보기 이미지를 붙여뒀다.
3.이미지 미리보기
- 2번 리스트에서 항목을 클릭하면 이쪽에 크게 미리보기를 붙여뒀다.
4.저장 경로 선택
- 변환된 파일들을 저장할 폴더를 선택하는 창이 열린다.
- 경로를 선택하면 저장 경로가 업데이트된다.
5.움직일 픽셀 좌표
- X,Y축으로 얼만큼 움직일지를 설정한다.
- 이미지 범위를 벗어나게되면 변환 시 에러창을 띄웠다.
6.픽셀 이동
- 픽셀을 좌표만큼 이동시키고 설정된 저장경로에 저장한다;
- 이게 생각해보니 직관적이지 않은게 2번 리스트에서 선택한 것만 변환이 아니라, 불러온 이미지는 전부 변환해준다.
🏃♂️실행 결과
만약 다음과 같은 64x64의 코인 이미지를
X좌표를 10만큼 설정하고 픽셀을 이동시키면
이렇게 오른쪽으로 10픽셀만큼 움직인 리소스를 저장한다.
🧑💻소스 코드
public partial class Form1 : Form
{
private List<string> selectedFileNames;
private string savePath;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// 초기 저장 위치는 바탕화면 path
savePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
savePathBox.Text = savePath;
}
/// <summary>
/// 이미지 불러오기
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnLoadImageClicked(object sender, EventArgs e)
{
// 파일 선택창을 띄움
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.Multiselect = true;
dialog.IsFolderPicker = false;
// 파일선택 완료 시 다음 함수로 전달
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
OnFilesSelectComplete(dialog);
}
}
/// <summary>
/// 파일 선택 완료시 호출
/// </summary>
/// <param name="dialog"></param>
private void OnFilesSelectComplete(CommonOpenFileDialog dialog)
{
Image img = null;
imagePathListView.Clear();
imagePathListView.View = View.List;
try
{
selectedFileNames = dialog.FileNames.ToList();
if (selectedFileNames == null || selectedFileNames.Count <= 0)
{
MessageBox.Show("선택된 파일이 없습니다.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
for (int i = 0; i < selectedFileNames.Count; i++)
{
imageList1.Images.Add(Image.FromFile(selectedFileNames[i]));
}
// 첫번째 이미지만 불러오기
img = Image.FromFile(selectedFileNames[0]);
// 불러온 첫번째 이미지 객체를 복사해서 크게보기 PictureBox에 넣음
ImagePictureBox.Image = (Image)img.Clone();
for (int i = 0; i < selectedFileNames.Count; i++)
{
ListViewItem item = new ListViewItem(selectedFileNames[i], i);
imagePathListView.Items.Add(item);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
img.Dispose();
}
}
/// <summary>
/// 이미지 주소 리스트에서 새로운 이미지가 선택됐을 때
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnImagePathListItemSelected(object sender, EventArgs e)
{
if (imagePathListView.SelectedItems.Count <= 0)
return;
string path = imagePathListView.SelectedItems[0].Text;
Image img = null;
try
{
img = Image.FromFile(path);
// 이미지 객체를 복사해서 크게보기 PictureBox에 넣음
ImagePictureBox.Image = (Image)img.Clone();
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (img != null)
img.Dispose();
}
}
/// <summary>
/// 픽셀 이동
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnMovePixelButtonClicked(object sender, EventArgs e)
{
if(selectedFileNames == null || selectedFileNames.Count <= 0)
{
MessageBox.Show("이미지를 먼저 불러와주세요", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int moveX = (int)numericUpDown_movePixel_X.Value;
// y축은 확인해보니 양수면 아래로 보내고 음수면 위로 보냄 따라서 반전시켜줌
int moveY = (int)numericUpDown_movePixel_Y.Value * -1;
for (int i = 0; i < selectedFileNames.Count; i++)
{
string path = selectedFileNames[i];
Bitmap originBmp = new Bitmap(path);
Bitmap newBmp = new Bitmap(path);
// 모든 픽셀을 돌면서 이동시킴
for (int y = 0; y < originBmp.Height; y++)
{
for (int x = 0; x < originBmp.Width; x++)
{
// 범위를 초과한 경우 continue
if ((y + moveY) >= originBmp.Height)
continue;
if ((y + moveY) < 0)
continue;
if ((x + moveX) >= originBmp.Width)
continue;
if ((x + moveX) < 0)
continue;
newBmp.SetPixel(x + moveX, y + moveY, originBmp.GetPixel(x, y));
}
}
string fileName = Path.GetFileNameWithoutExtension(path);
newBmp.Save(Path.Combine(savePathBox.Text, fileName) + ".png", ImageFormat.Png);
}
}
/// <summary>
/// 저장 경로 선택
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SavePathButtonClicked(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
// 파일선택 완료 시 다음 함수로 전달
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
savePath = dialog.FileName;
savePathBox.Text = savePath;
}
}
}
💽깃허브 저장소
🧠 마치며
요즘엔 이런 툴을 만들때가 제일 재밌는것같다
뭐랄까…
내가 만든 프로그램을 사용하고 고맙다는 인사를 들을때도 뿌듯하고
이런거까지 할수있구나 라는걸 알게됐을 때도 신이난다
사실 윈폼으로 뭘 만들어본건 처음인데 생각보다 굉장히 쉽게돼있어서 금방 만들 수 있었다.
다음에도 뭔가 만들 기회가 있었으면 좋겠다.
🔗 참고한 곳들
이 글은 옵시디언에서 마크다운으로 작성되었습니다.