ex) 최종 MyImage.h 파일(LoadImage, SaveImage는 다음에 배움)
#pragma once
#include <windows.h>
#include <assert.h>
#define CLIP(x) (x < 0)? 0 : x > 255 ? 255 : x
template <typename T>
class CMyImage
{
public:
CMyImage(void)
: m_nChannels(0)
, m_nHeight(0)
, m_nWidth(0)
, m_nWStep(0)
, m_pImageData(NULL)
{}
CMyImage(int nWidth, int nHeight, int nChannels = 1)
: m_nChannels(nChannels)
, m_nHeight(nHeight)
, m_nWidth(nWidth)
, m_nWStep(((nWidth*nChannels * sizeof(T) + 3)&~3) / sizeof(T))
{
m_pImageData = new T[m_nHeight*m_nWStep];
}
CMyImage(const CMyImage& myImage)
{
m_nChannels = myImage.m_nChannels;
m_nHeight = myImage.m_nHeight;
m_nWidth = myImage.m_nWidth;
m_nWStep = myImage.m_nWStep;
m_pImageData = new T[m_nHeight*m_nWStep];
memcpy(m_pImageData, myImage.m_pImageData, m_nHeight*m_nWStep * sizeof(T));
}
CMyImage& operator=(const CMyImage& myImage)
{
if (this == &myImage)
return *this;
m_nChannels = myImage.m_nChannels;
m_nHeight = myImage.m_nHeight;
m_nWidth = myImage.m_nWidth;
m_nWStep = myImage.m_nWStep;
if (m_pImageData)
delete[] m_pImageData;
if (myImage.m_pImageData != NULL)
{
m_pImageData = new T[m_nHeight*m_nWStep];
memcpy(m_pImageData, myImage.m_pImageData, m_nHeight*m_nWStep * sizeof(T));
}
return *this;
}
template <typename From>
CMyImage(const CMyImage<From>& myImage)
{
m_nChannels = myImage.GetChannel();
m_nHeight = myImage.GetHeight();
m_nWidth = myImage.GetWidth();
m_nWStep = ((m_nWidth*m_nChannels * sizeof(T) + 3)&~3) / sizeof(T);
m_pImageData = new T[m_nHeight*m_nWStep];
int nWStep = myImage.GetWStep();
if (sizeof(T) == 1)
{
for (int r = 0; r<m_nHeight; r++)
{
T* pDst = GetPtr(r);
From* pSrc = myImage.GetPtr(r);
for (int c = 0; c<nWStep; c++)
{
pDst[c] = (T)CLIP(pSrc[c]);
}
}
}
else
{
for (int r = 0; r<m_nHeight; r++)
{
T* pDst = GetPtr(r);
From* pSrc = myImage.GetPtr(r);
for (int c = 0; c<nWStep; c++)
{
pDst[c] = (T)pSrc[c];
}
}
}
}
void SetConstValue(T val)
{
if (val == 0) // 조건 (1)
{
memset(m_pImageData, 0, m_nWStep*m_nHeight * sizeof(T));
return;
}
if (sizeof(T) == 1) // 조건 (2)
{
memset(m_pImageData, val, m_nWStep*m_nHeight);
}
else // 나머지 경우들
{
T* pData = m_pImageData;
for (int r = 0; r<m_nHeight; r++)
{
for (int c = 0; c<m_nWidth; c++)
{
pData[c] = val;
}
pData += m_nWStep;
}
}
}
bool IsEmpty() const
{
return m_pImageData ? false : true;
}
~CMyImage(void)
{
if (m_pImageData)
{
delete[] m_pImageData;
}
}
bool LoadImage(const char* filename)
{
assert(sizeof(T) == 1); // BYTE형의 경우만 가능
if (strcmp(".BMP", &filename[strlen(filename) - 4]))
{
FILE* pFile = NULL;
fopen_s(&pFile, filename, "rb"); // 바이너리 읽기 모드
if (!pFile)
return false;
BITMAPFILEHEADER fileHeader;
if (!fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, pFile))
{
fclose(pFile);
return false;
}
if (fileHeader.bfType != 0x4D42) // 'BM' 문자 검사
{
fclose(pFile);
return false;
}
BITMAPINFOHEADER infoHeader;
if (!fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, pFile))
{
fclose(pFile);
return false;
}
if (infoHeader.biBitCount != 8 && infoHeader.biBitCount != 24)
{
fclose(pFile);
return false;
}
if (m_nWidth != infoHeader.biWidth && m_nHeight != infoHeader.biHeight
&& m_nChannels != infoHeader.biBitCount / 8)
{
if (m_pImageData)
delete[] m_pImageData;
m_nChannels = infoHeader.biBitCount / 8;
m_nHeight = infoHeader.biHeight;
m_nWidth = infoHeader.biWidth;
m_nWStep = (m_nWidth*m_nChannels * sizeof(T) + 3)&~3;
m_pImageData = new T[m_nHeight*m_nWStep];
}
fseek(pFile, fileHeader.bfOffBits, SEEK_SET);
int r;
for (r = m_nHeight - 1; r >= 0; r--)
{
if (!fread(&m_pImageData[r*m_nWStep], sizeof(BYTE), m_nWStep, pFile))
{
fclose(pFile);
return false;
}
}
fclose(pFile);
return true;
}
else
{
return false;
}
}
bool SaveImage(const char* filename)
{
assert(sizeof(T) == 1); // BYTE형의 경우만 가능
if (strcmp(".BMP", &filename[strlen(filename) - 4]))
{
FILE* pFile = NULL;
fopen_s(&pFile, filename, "wb");
if (!pFile)
return false;
BITMAPFILEHEADER fileHeader;
fileHeader.bfType = 0x4D42; // 'BM'
fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nWStep*m_nHeight + (m_nChannels == 1) * 1024;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (m_nChannels == 1) * 256 * sizeof(RGBQUAD);
fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, pFile);
BITMAPINFOHEADER infoHeader;
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
infoHeader.biWidth = m_nWidth;
infoHeader.biHeight = m_nHeight;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = m_nChannels * 8;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = m_nWStep*m_nHeight;
infoHeader.biClrImportant = 0;
infoHeader.biClrUsed = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
if (m_nChannels == 1)
{
for (int l = 0; l<256; l++)
{
RGBQUAD GrayPalette = { l, l, l, 0 };
fwrite(&GrayPalette, sizeof(RGBQUAD), 1, pFile);
}
}
int r;
for (r = m_nHeight - 1; r >= 0; r--)
{
fwrite(&m_pImageData[r*m_nWStep], sizeof(BYTE), m_nWStep, pFile);
}
fclose(pFile);
return true;
}
else
{
return false;
}
}
inline T& GetAt(int x, int y, int c = 0) const
{
assert(x >= 0 && x < m_nWidth && y >= 0 && y < m_nHeight);
return m_pImageData[m_nWStep*y + m_nChannels*x + c];
}
int GetChannel() const { return m_nChannels; }
int GetHeight() const { return m_nHeight; }
int GetWidth() const { return m_nWidth; }
int GetWStep() const { return m_nWStep; }
T* GetPtr(int r = 0, int c = 0) const { return m_pImageData + r*m_nWStep + c; }
protected:
int m_nChannels; // 채널 수
int m_nHeight; // 세로 픽셀 수
int m_nWidth; // 가로 픽셀 수
int m_nWStep; // 행당 데이터 원소 수
T* m_pImageData; // 픽셀 배열 포인터
};
typedef CMyImage<BYTE> CByteImage;
typedef CMyImage<int> CInteImage;
typedef CMyImage<float> CFloatImage;
typedef CMyImage<double> CDoubleImage;
<Main>
// TestImage.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//
#include "stdafx.h"
#include "CMyImage\MyImage.h"
int main()
{
CByteImage image1(640, 480); // 생성자 호출
image1.SetConstValue(0); // 0으로 초기화(운영체제 시스템에 따라서 이상한 값이 들어갈 수 있다)
CByteImage image2(image1); // 복사 생성자 호출
CByteImage image3; // 기본 생성자 호출
image3 = image1; // 대입 연산자 호출
int nWidth = image1.GetWidth(); // 영상의 너비
int nHeight = image1.GetHeight(); // 영상의 높이
int nChannel = image1.GetChannel(); // 영상의 채널 수
double incX = 255.0 / nWidth; // 255를 나눈 이유는 밑에 있는 그라디에션에서 BYTE형이기 때문에 255를 넘지 않기 위해서
double incY = 255.0 / nHeight;
int r, c;
for (r = 0; r<nHeight; r++) // 행 단위 이동
{
for (c = 0; c<nWidth; c++) // 열 단위 이동
{
image2.GetAt(c, r) = (BYTE)(c*incX); // 가로 그라데이션
image3.GetAt(c, r) = (BYTE)(r*incY); // 세로 그라데이션
}
}
// 결과 영상 저장
image1.SaveImage("Black.bmp");
image2.SaveImage("GradationX.bmp");
image3.SaveImage("GradationY.bmp");
return 0;
}
<결과>
왼쪽은 image2이고 오른쪽은 image3이다.