본문 바로가기

C++ in Windows/Challenges

ICPC 대비 문제 5. 그래픽 편집기(Graphical Editor)


문제 5. 그래픽 편집기(Graphical Editor)


PC/UVa ID:110105/10267, 인기도:B, 성공률: 낮음, 레벨: 1


포토샵 같은 그래픽 편집기를 이용하면 텐스트 편집기에서 문서를 수정하는 것처럼 비트맵 이미지를

수정할 수 있다. 이미지는 픽셀로 이루어진 M X N 배열로 표현되며 각 픽셀마다 색이 주어진다.

간단한 대화형 그래픽 편집기를 흉내를 낼 수 있는 프로그램을 만들어보자.


>> 입력

입력은 한줄에 하나씩의 편집기 명령으로 구성된다. 각 명령은 줄 맨 앞에 있는 대문자 한 개로 표현된

다. 매개변수가 필요한 경우에는 그 명령과 같은 줄에 스페이스로 분리되어 매개변수가 입력된다.

픽셀 좌표는 1이상 M이하의 열번호와 1 이상 N 이하의 행 번호, 이렇게 두 개의 정수로 표현되며 이때

1 <= M, N <= 250라는 조건이 만족된다. 표의 왼쪽 위 꼭지점을 원점으로 삼는다. 색은 대문자로 지정된다.

편집기에서 받아들이는 명령은 다음과 같다.


I M N 모든 픽셀이 흰색(O)으로 칠해진 M X N 이미지를 새로 만든다.

C 모든 픽셀을 흰색(O)으로 칠해서 표를 지운다. 이미지의 크기는 바뀌지 않는다.

L X Y C (X,Y) 픽셀을 주어진 색으로 칠한다.

V X Y1 Y2 C X열에 Y1행과 Y2행(Y1, Y2 포함) 사이에 주어진 색(C)으로 수직방향 직선을 긋는다.

H X1 X2 Y C Y행에 X1열과 X2열(X1, X2 포함) 사이에 주어진 색(C)으로 수평방향 직선을 긋는다.

K X1 Y1 X2 Y2 C 주어진 색(C)으로 채워진 직사각형을 그린다. (X1, Y1)은 왼쪽 위 끝점, (X2, Y2)는

오른쪽 아래 끝 점을 의미한다.

F X Y C R 영역을 주어진 색(C)으로 채우는데, 영역 R은 다음과 같이 정의된다. (X,Y) 픽셀은

R에 속한다. (X,Y) 픽셀과 색이 같고 R에 포함된 픽셀과 맞닿은 부분이 하나라도 있다면

그 픽셀도 R영역에 포함된다.

S Name 파일명을 MSDOS 8.3 형식으로 출력하고 그 뒤에 현재 이미지의 내용을 출력한다.

X 세션을 종료한다.



>> 출력

S NAME이라는 명령이 있을 때마다 NAME으로 주어진 파일명을 출력하고 현재 이미지의 내용을 출력한다.

각 행은 각 픽셀의 색을 나타내는 문자로 표시된다. 출력 예를 참고하자.

I,C,L,V,H,K,F,S,X를 제외한 문자로 정의된 명령이 있으면 그 줄 전체를 무시하고 다음 명령으

넘어간다. 다른 오류에 대해서는 프로그램의 행동을 예측할 수 없다.


>> 입력예

I 5 6

L 2 3 A

S one.bmp

G 2 3 J

F 3 3 J

V 2 3 4 W

H 3 4 2 Z

s two.bmp

x


>> 출력예

one.bmp

OOOOO

OOOOO

OAOOO

OOOOO

OOOOO

OOOOO

two.bmp

JJJJJ

JJZZJ

JWJJJ

JWJJJ

JJJJJ

JJJJJ


main.cpp
#include "Command.h"

#define LINE_LENGTH 255

void main(void)
{
	Command cmd;
	bool keep = true;

	while( keep )
	{
		char buff[LINE_LENGTH];
		string strcmd;

		// stream에서 문자열 한줄을 입력받는다. 
		cin.getline(buff, LINE_LENGTH);

		// char* -> string
		strcmd = buff; 

		// 명령어 해석기에 전달. 
		// return값으로 프로그램을 계속 진행할지 판단.
		keep = cmd.Parser(strcmd);
	}
}


Command.h
#pragma once 

#ifndef _Command_h_
#define _Command_h_

#include "EditControl.h"

#include <string>
#include <sstream>

class Command
{
public:
	Command();
	~Command();

	bool Parser(string const cmd);
private:
	EditControl mControl;
};

#endif 


Command.cpp
#include "Command.h"

Command::Command()
{

}

Command::~Command()
{

}

bool Command::Parser(string const cmd)
{
	// 기본적인 return값은 true
	bool ret = true;

	string buf;				// 임시 버퍼
	stringstream ss(cmd);

	vector<string> tokens;

	// 명령어를 공백으로 나눠 vector에 저장.
	while ( ss >> buf )
		tokens.push_back(buf);

	if ( tokens.size() > 0 )		// 명령이 한단어 이상이고
	if ( tokens[0].length() == 1 )	// 명령어가 한글자일때만 해석
	{	
		char sw;

		memcpy(&sw, tokens[0].c_str(), sizeof(char));

		// 명령에 맞게 인수를 생성하여 Control에 전달.
		switch(sw)
		{
			case 'I':
				if ( tokens.size() == 3 )
				{
					int m = atoi(tokens[1].c_str());
					int n = atoi(tokens[2].c_str());

					this->mControl.New(m, n);
				}
				break;

			case 'C':
				if ( tokens.size() == 1 )
				{
					this->mControl.Clear();
				}
				break;

			case 'L':
				if ( tokens.size() == 4 )
				{
					if ( tokens[3].length() == 1)
					{
						int x = atoi(tokens[1].c_str());
						int y = atoi(tokens[2].c_str());
						
						char c;

						memcpy(&c, tokens[3].c_str(), sizeof(char));

						this->mControl.Point( x, y, c );
					}
				}
				break;
			case 'V':
				if ( tokens.size() == 5 )
				{
					if ( tokens[3].length() == 1)
					{
						int x = atoi(tokens[1].c_str());
						int y1 = atoi(tokens[2].c_str());
						int y2 = atoi(tokens[3].c_str());

						char c;
						memcpy(&c, tokens[4].c_str(), sizeof(char));

						this->mControl.VLine(x, y1, y2, c);
					}
				}	
				break;
			
			case 'H':
				if ( tokens.size() == 5 )
				{
					if ( tokens[3].length() == 1 )
					{
						int x1 = atoi(tokens[1].c_str());
						int x2 = atoi(tokens[2].c_str());
						int y = atoi(tokens[3].c_str());

						char c;
						memcpy(&c, tokens[4].c_str(), sizeof(char));

						this->mControl.HLine(x1, x2, y, c);
					}
				}
				break;

			case 'K':
				if ( tokens.size() == 6)
				{
					if ( tokens[5].length() == 1 )
					{
						int x1 = atoi(tokens[1].c_str());
						int y1 = atoi(tokens[2].c_str());
						int x2 = atoi(tokens[3].c_str());
						int y2 = atoi(tokens[4].c_str());

						char c;
						memcpy(&c, tokens[5].c_str(), sizeof(char));

						this->mControl.Box(x1, y1, x2, y2, c);
					}
				}
				break;

			case 'F':
				if ( tokens.size() == 4 )
				{
					if ( tokens[3].length() == 1 )
					{
						int x = atoi(tokens[1].c_str());
						int y = atoi(tokens[2].c_str());

						char c;
						memcpy(&c, tokens[3].c_str(), sizeof(char));

						this->mControl.MagicPoint(x, y, c);
					}
				}
				break;

			case 'S':
				if ( tokens.size() == 2 )
				{
					string fileName = tokens[1].c_str();

					this->mControl.SaveFile(fileName);
				}
				break;

			case 'X':
				if ( tokens.size() == 1 )
				{
					this->mControl.End();
				}
				// 세션 종료를 위한 return값 false
				ret = false;
				break;
		}
	}

	return ret;
}


EditControl.h
#pragma once 

#ifndef _EditControl_h_
#define _EditControl_h_

#include <iostream>
#include <vector>

using namespace std;

class EditControl
{
public:
	EditControl();
	~EditControl();

private:
	int n, m;
	bool session;
	char **bitmap;
	
public:
	void New(int const m, int const n);
	void Clear(void);
	void Point(int const x, int const y, char const color);
	void VLine(int const x, int const y1, int const y2, char const color);
	void HLine(int const x1, int const x2, int const y, char const color);
	void Box(int const x1, int const y1, int const x2, int const y2, char const color);
	void MagicPoint(int const x, int const y, char const color);
	void SaveFile(string const fileName);
	void End(void);

private:
	void newBitmap(int const m, int const n);
	void deleteBitmap(void);

	bool maxCheck(int x, int y);
	bool maxCheckX(int x);
	bool maxCheckY(int y);

	void Fill(int x, int y, char org_color, char new_color);

};


#endif


EditControl.cpp
#include "EditControl.h"

EditControl::EditControl()
{
	this->session = false;
	this->m = this->n = 0;
	this->bitmap = NULL;
}

EditControl::~EditControl()
{
	
}

void EditControl::New(int const m, int const n)
{
	if( m > 0 && m <= 250 && n > 0 && n <= 250 )
	{
		// 유효 크기 안에서 bitmap 생성.
		this->session = true;
		this->newBitmap(m, n);
	}
}

void EditControl::Clear(void)
{
	// 세션 예외처리
	if ( this->session == false ) return;

	for ( int y = 0; y < this->n; y++)
	{
		// bitmap을 'O'문자로 채움.
		for ( int x = 0; x < this->m; x++)
		{
			this->bitmap[y][x] = 'O';
		}

		this->bitmap[y][this->m] = NULL;
	}
}

void EditControl::Point(int const x, int const y, char const color)
{
	if ( this->session == false ) return;

	// 범위 확인뒤 적용. 배열 특성상 -1 처리.
	if ( this->maxCheck(x, y) )
		this->bitmap[y-1][x-1] = color;
}

void EditControl::VLine(int const x, int const y1, int const y2, char const color)
{
	if ( this->session == false ) return;

	if ( this->maxCheck(x, y1) && this->maxCheckY(y2) )
	{
		int temp1 = y1 - 1, temp2 = y2 - 1;
		int tempX = x - 1;

		if ( temp1 > temp2 )
		{
			// y1이 y2보다 클경우 swap
			temp1 += temp2;
			temp2 = temp1 - temp2;
			temp1 = temp1 - temp2;
		}

		// 라인출력.
		for (int i = temp1; i <= temp2; i++)
			this->bitmap[i][tempX] = color;
	}
}

void EditControl::HLine(int const x1, int const x2, int const y, char const color)
{
	if ( this->session == false ) return;

	if ( this->maxCheck(x1, y) && this->maxCheckX(x2) )
	{
		int temp1 = x1 - 1, temp2 = x2 - 1;
		int tempY = y - 1;

		if ( temp1 > temp2 )
		{
			temp1 += temp2;
			temp2 = temp1 - temp2;
			temp1 = temp1 - temp2;
		}

		for (int i = temp1; i <= temp2; i++)
			this->bitmap[tempY][i] = color;
	}
}

void EditControl::Box(int const x1, int const y1, int const x2, 
                               int const y2, char const color)
{
	if ( this->session == false ) return;

	if ( this->maxCheck(x1, y1) && this->maxCheck(x2, y2) )
	{
		int tempX1 = x1 - 1, tempX2 = x2 - 1;
		int tempY1 = y1 - 1, tempY2 = y2 - 1;

		if ( tempX1 > tempX2 )
		{
			tempX1 += tempX2;
			tempX2 = tempX1 - tempX2;
			tempX1 = tempX1 - tempX2;
		}

		if ( tempY1 > tempY2 )
		{
			tempY1 += tempY2;
			tempY2 = tempY1 - tempY2;
			tempY1 = tempY1 - tempY2;
		}

		for(int y = tempY1; y <= tempY2; y++)
			for(int x = tempX1; x <= tempX2; x++)
				this->bitmap[y][x] = color;
	}
}

void EditControl::MagicPoint(int const x, int const y, char const color)
{
	if ( this->session == false ) return;

	// 유효 범위 확인뒤 Fill 재귀함수 호출.
	if ( this->maxCheck(x, y) )
		this->Fill(x, y, this->bitmap[x-1][y-1], color);
}

void EditControl::Fill(int x, int y, char org_color, char new_color)
{
	this->bitmap[y][x] = new_color;
	
	// 일단은 기준점으로 4방향만.

	// 기준점의 왼쪽
	if ( x > 0 && this->bitmap[y][x-1] == org_color )
		this->Fill( x - 1, y, org_color, new_color);

	// 기준점의 위쪽
	if ( y > 0 && this->bitmap[y-1][x] == org_color )
		this->Fill( x, y - 1, org_color, new_color);

	// 기준점의 오른쪽
	if ( x < this->m - 1 && this->bitmap[y][x+1] == org_color )
		this->Fill( x + 1, y, org_color, new_color);

	// 기준점의 아래쪽
	if ( y < this->n - 1 && this->bitmap[y+1][x] == org_color )
		this->Fill( x, y + 1, org_color, new_color);
}


void EditControl::SaveFile(string const fileName)
{
	if ( this->session == false ) return;

	// 파일명 출력.
	cout << fileName.c_str() << "\n";

	// 이미지 출력;
	for ( int i = 0; i < this->n; i++)
		cout << this->bitmap[i] << "\n";
}

void EditControl::End(void)
{
	if ( this->session == false ) return;

	// 세션종료시 메모리 해제, 초기값 셋팅.
	this->deleteBitmap();
	this->session = false;
	this->m = this->n = 0;

}

void EditControl::newBitmap(int const m, int const n)
{
	// 비트맵 생성.
	this->bitmap = new char*[n];
	
	for ( int i = 0; i < n; i++)
	{
		this->bitmap[i] = new char[m+1];
	}
	
	this->m = m;
	this->n = n;

	// 이후 비트맵 청소.
	this->Clear();
}

void EditControl::deleteBitmap(void)
{
	// 매모리 해제.
	for( int i = 0; i < this->n; i++)
	{
		delete this->bitmap[i];
	}

	delete []this->bitmap;

	this->bitmap = NULL;
}


bool EditControl::maxCheck(int x, int y)
{
	bool ret = false;

	// x, y의 유효범위 확인뒤 결과값 반환. and 연산.
	ret = this->maxCheckX(x) & this->maxCheckY(y);

	return ret;
}

bool EditControl::maxCheckX(int x)
{
	if ( this->session == false ) return false;
	
	bool ret = false;

	if( x <= this->m && x > 0)
		ret = true;

	return ret;
}

bool EditControl::maxCheckY(int y)
{
	if ( this->session == false ) return false;

	bool ret = false;

	if( y <= this->n && y > 0)
		ret = true;

	return ret;
}




나름 예외처리를 하다 보니까. 소스가 길어진거 같다.