개발이라고 하기에 작은 프로젝트였지만 아무튼,
이번 프로젝트에서 처음 배웠는데 가장 많이 사용한 클래스가 두 가지였다.
첫 번째는 StreamReader, StreamWriter 클래스. csv 파일 불러오기와 저장하기에 쓰였다.
깃허브에서 얻어온 포켓몬 스트라이프의 파일명이 모두 영어였다.
영어를 한글로 번역하기 위해 파일명을 불러온 후에 단어 쌍을 csv 파일로 만들고 다시 불러와야했다.
using System.IO;
using System.Collections.Generic;
Dictionary<string, stirng> dict = new Dictionary<stirng, string>();
StreamReader sr = new StreamReader(string path);
string str = string.Empty;
while((str = sr.ReadLine()) != null) // stream의 끝을 확인하고 반복 중단
{
string[] arr = str.Split('-');
dict.Add(arr[0], arr[1]);
}
sr.Close(); // 내부 stream 닫기
이런식으로 불러온 csv 파일을 내부 stream에서 읽어들인다.
인코딩 옵션 문제로 한글이 깨지는 문제가 있었는데 어떻게 해결해야할지 몰라서 메모장으로 복사했다가 다시 저장하니 해결이 되었다.(무슨 원리인지는 모름)
using System.IO;
StreamReader sr = new StreamReader(string path);
StreamWriter sw = new StreamWriter(string path);
string str = string.Empty;
while((str = sr.ReadLine()) != null)
{
sw.WriteLine(str); // stream 에 입력하기
}
sr.Close(); // streamreader 닫기
sw.Close(); // streamwriter 닫기
// 여기서 stream을 닫지 않으면 입력이 끝까지 되지 않음.
// 아마도 내부 stream에 의한 파일 생성보다
// 프로세스가 먼저 끝나버리고 리소스가 모두 해제되기 때문이지 않을까...
// 추측함(정확히 뭔지 모름)
이렇게 csv 파일을 만들어 번역된 파일 명을 만들었다.
이렇게 만든 파일명들은 원래 파일명을 그대로 key값으로 사용해 새로운 파일명을 지정하도록 했다.
내부 stream이란게 정확이 어떤건지, 어떤 원리로 돌아가고 메모리는 어떻게 쓰고 언제 메모리가 해제되는 건지 모르겠다.
그래도 몇번 써보니까 대충 뭔지는 알겠고 내 수준에서 적당히 쓸 수 있을 것 같다.
...
두 번째로 이미지 파일명을 번역한 후에는 이미지 파일의 색상을 단순화시켜야 했다.
2500장이 넘는 이미지를 하나하나 수정하는 것은 시간적으로 불가능하다.
크기가 같은 여러개의 이미지를 배열해 하나로 합치고 한꺼번에 수정을 거친 후에 다시 분할하기로 했다.
여기서 Bitmap 클래스의 GetPixel, SetPixel 메서드로 이미지를 하나의 덩어리에 하나씩 이어붙였다.
// ...
int piece_width = 40; // 1개 이미지의 너비
int piece_height = 30; // 1개 이미지의 높이
int merge_row = 10; // 1개 열에 들어갈 이미지 수
int merge_column = 10; // 1개 행에 들어갈 이미지 수
Bitmap merge = new Bitmap(piece_width * merge_row, piece_height * merge_column);
while (piece_number < image_list.Count)
{
Bitmap piece = new Bitmap(piece_width, piece_height);
// 이미지 조각
try
{
Bitmap load_image = new Bitmap(image_piece_path + $"\\{image_list[piece_number]}");
// 이미지 불러오기
for (int row = 0; row < piece_height; row++)
{
for (int column = 0; column < piece_width; column++)
{
piece.SetPixel(column, row, load_image.GetPixel(column, row));
// 이미지 조각에 불러온 이미지의 픽셀 값을 저장
}
}
piece_number++;
}
catch { continue; }
// 이 과정 없이 불러오면서 바로 큰 이미지의 픽셀값을 지정할 수 있었다.
// 하지만 처음에 파일명 번역이 불완전했기 때문에 중간에 빠지는 이미지를
// 확인하고자 try-catch 문을 사용해서 확인되지 않는 파일들을 건너뛰도록 했다.
for(int row = 0; row < piece_height; row++)
{
for(int column = 0; column < piece_width; column++)
merge.SetPixel(((piece_number - 1) % merge_row) * piece_width + column,
((piece_number - 1) / merge_row) * piece_height + row,
piece.GetPixel(column, row));
// 합쳐진 이미지의 픽셀 색상을 필요한 값으로 바꿔준다.
// 코드 너무 길어서 줄바꿈
}
}
merge.Save(image_piece_path + "\\image.bmp");
// 합쳐진 이미지 저장
// ...
PictureBox에 담을 이미지를 생성할 때에도 이 방법으로 이미지 크기를 확대했다.
저장할 픽셀아트 이미지는 처음에는 출력 시킬 수 있게 만들어보거나 한글 파일로 만들어볼까 했으나
참고할 자료가 없어서 아예 이미지 파일을 크게 만들어버리기로 했다.
칸 그리기, 숫자 그리기, 포켓몬 이미지 넣기, 색상 정보 쓰기 등 몇 가지 모듈을 메서드로 만들어서 구현했다.
포켓몬의 이름과 번호를 하나하나 그려볼까 했다.
조금 생각해보니 한글의 모든 글자 픽셀 좌표를 내가 지정해줘야하는 문제가 있었다.
이미지에 글자를 써넣는 방법을 찾아보다 Graphics 클래스의 DrawString 메서드를 찾았다.
string pokemon_name = "대충 포켓몬 번호 & 이름";
Graphics text = Graphics.FromImage(pixelart);
// text를 이미지에 그려넣는 객체 생성
cursor_x = 1280;
cursor_y = 208;
Brush black = new SolidBrush(Color.Black);
Brush yellow = new SolidBrush(Color.Yellow);
FontFamily _font = new FontFamily("굴림체");
Font myFont = new Font(_font, 15, FontStyle.Regular, GraphicsUnit.Pixel);
// 글자 모양 지정
PointF startPoint = new PointF(cursor_x, cursor_y);
// 좌표 생성
text.DrawString(pokemon_name, myFont, black, startPoint);
// 포켓몬 이름을 지정한 글자 모양과 색깔로 지정한 위치에 넣는다.
if (shiny)
{
startPoint.X -= 1;
startPoint.Y -= 1;
text.DrawString(pokemon_name, myFont, yellow, startPoint);
// 이로치 포켓몬은 노란색으로 강조하도록 했다.
}
색상표를 만들 때는 번호에 색깔을 그대로 옆에 그려줬다.
Pen 클래스를 사용했다.
cursor_x = 1280;
cursor_y = 253;
startPoint.X = cursor_x;
startPoint.Y = cursor_y;
Pen pen = new Pen(Color.Black, 15);
// 펜 객체 생성
for(int i = 1; i < color_count; i++)
{
pen.Color = color_chart_r[i];
text.DrawString(i.ToString(), myFont, black, startPoint);
text.DrawLine(pen, startPoint.X + 20, startPoint.Y + 7, startPoint.X + 50, startPoint.Y + 7);
// 펜 객체로 선 그리기
// Pen 클래스는 Graphics 객체의 내부에서 사용한다.
// Brush 클래스와 똑같다.
if (i >= color_count / 2 && startPoint.X == cursor_x)
{
startPoint.X = cursor_x + 70;
startPoint.Y = cursor_y;
}
else startPoint.Y += 21;
}
처음부터 이걸 알았으면 칸을 하나하나 for문으로 그리지 않았을텐데 조금 아쉽다.