Run Length Encoding
typedef struct PCX_HEADER {
char manufacturer; // 1
char version; // 2
char encoding; // 3
char bits_per_pixel; // 4
short int xmin,ymin; // 5 , 6, 7, 8
short int xmax,ymax; // 9 10, 11, 12
short int hres; // 13, 14
short int vres; // 15, 16
char palette16[48]; // 17-64
char reserved; // 65
char color_planes; // 66
short int bytes_per_line; // 67, 68
short int palette_type; // 69, 70
char filler[58]; // 71 - 128
} pcx_header;
void CImageProDoc::LoadPCX()
{
int i, x, y;
int run_length;
unsigned char c;
pcx_header pcxhead;
CString fname;
CFile file;
CFileDialog dlg(TRUE);
if(dlg.DoModal()==IDOK) {
fname = dlg.GetFileName(); // 파일 이름 받아오기
file.Open(fname, CFile::modeRead); // 파일 열기
if (strcmp(strchr(fname, '.'), ".PCX") == 0 || // 파일 확장자 확인
strcmp(strchr(fname, '.'), ".pcx") == 0)
{
file.Read(&pcxhead, sizeof(pcx_header)); // 헤더를 읽어 들임
imageWidth = (pcxhead.xmax-pcxhead.xmin) + 1; // 영상의 넓이 계산
imageHeight = (pcxhead.ymax-pcxhead.ymin) + 1; // 영상의 높이 계산
depth = pcxhead.color_planes;
if (depth != 1) return;
// 기억장소 할당
inputImg=(unsigned char **) malloc(imageHeight*sizeof(unsigned char *));
for (i = 0; i < imageHeight; i++) {
inputImg[i] = (unsigned char *) malloc(imageWidth * depth);
}
for (y = 0; y < imageHeight; y++)
for (x = 0; x < imageWidth; )
{
file.Read(&c, 1); // 한 바이트를 읽어 들임
if((c & 0xc0) == 0xc0) // 런 길이를 포함하는 바이트이면
{
run_length = c & 0x3f; // 런 길이 계산
file.Read(&c, 1); // 데이터를 읽어들임
while(run_length--) // 런 길이만큼 저장
inputImg[y][x++] = c;
}
else // 런 길이를 포함하는 바이트가 아니면
{
inputImg[y][x++] = c;
}
}
}
}
}
void CImageProDoc::SavePCX()
{
int x, y;
int run_length;
unsigned char c;
unsigned char cur;
pcx_header pcxhead;
CString fname;
CFile file;
CFileDialog dlg(TRUE);
if(dlg.DoModal()==IDOK) {
fname = dlg.GetFileName(); // 파일 이름 받아오기
file.Open(fname, CFile::modeCreate | CFile::modeWrite); // 파일 열기
if (strcmp(strchr(fname, '.'), ".PCX") == 0 || // 파일 확장자 확인
strcmp(strchr(fname, '.'), ".pcx") == 0)
{
memset(&pcxhead, 0, 128); // 헤더 내용을 0으로 초기화
pcxhead.manufacturer = 10; // 기본 값
pcxhead.version = 5;
pcxhead.encoding = 1; // 런 길이 부호화 사용
pcxhead.bits_per_pixel = 8;
pcxhead.xmin = 0;
pcxhead.ymin = 0;
pcxhead.xmax = imageWidth - 1;
pcxhead.ymax = imageHeight - 1;
pcxhead.hres = 150; // 150 DPI
pcxhead.vres = 150; // 150 DPI
pcxhead.color_planes = 1; // 흑백 영상
pcxhead.bytes_per_line = imageWidth;
pcxhead.palette_type = 1;
file.Write(&pcxhead, 128); // 헤더 출력
for (y = 0; y < imageHeight; y++) { // 각 라인에 대하여
run_length = 0;
for (x = 0; x < imageWidth; x++) { // 각 픽셀에 대하여
cur = inputImg[y][x];
run_length++;
if (x < imageWidth - 1 && cur == inputImg[y][x+1] && run_length < 63) {
// 라인의 끝이 아니고 다음에 같은 픽셀이 반복되고 런길이가 63보다 작으면
// ==> 다음 픽셀로 이동
}
else if (run_length > 1 || (0xC0 & cur) == 0xC0) {
// 런 길이가 1보다 크거나 런 길이가 1이지만 상위 두 비트 값이 1인 경우
// ==> 런 길이 바이트와 데이터 바이트 저장
c = 0xC0 | (unsigned char) run_length;
file.Write(&c, 1);
file.Write(&cur, 1);
run_length = 0;
}
else {
// 런 길이가 1이고 상위 두 비트 값이 1이 아닌 경우
// ==> 데이터 바이트만 저장
file.Write(&cur, 1);
run_length = 0;
}
}
}
}
file.Close();
}
}
void CImageProView::OnDraw(CDC* pDC)
{
CImageProDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (pDoc->inputImg == NULL) return;
if (pDoc->depth == 1) {
for(int y=0; y < pDoc->imageHeight; y++) // 입력 영상 출력
for(int x=0; x < pDoc->imageWidth; x++)
pDC->SetPixel(x, y, RGB(pDoc->inputImg[y][x],
pDoc->inputImg[y][x],
pDoc->inputImg[y][x]));
}
void CImageProView::OnLoadPCX()
{
CImageProDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDoc->LoadPCX();
Invalidate(FALSE);
}
void CImageProView::OnSavePCX()
{
CImageProDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (pDoc->inputImg == NULL) return;
pDoc->SavePCX();
Invalidate(FALSE);
}