В проекте MyBMP можно открыть и пересохранить bmp-файл. Сохранение осуществляется не в файл формата .bmp, а в файл с придуманным расширением .tss.
Целью данного проекта было изучение работы с оперативной памятью, хранением и представлением bmp-файла в ней, также сохранением текущего состояния объекта на диск (сериализация или дамп) и восстановление образа с диска в оперативную память.
Программа реализована через интерфейс с дочерними окнами. Загружаем Файл через стандартное меню «Открыть»


За обработку bmp-файла отвечает BMP.cpp. Здесь размещен весь функционал. Загрузить .BMP-изображение из памяти (строка bitmap_is_string является оперативным образом .BMP-файла).
BOOL CBmp::LoadBmpFromMemory(const char *bitmap_is_string,size_t size)
{
if (m_bIsLoad) ReleaseBmp();
if (bitmap_is_string == NULL
||
!bitmap_is_string[0]) return FALSE;
memcpy(&m_BmFileHeader, bitmap_is_string, sizeof(m_BmFileHeader));
if (m_BmFileHeader.bfType != 0x4d42)
{
AfxMessageBox(_T("Matiss, class CBmp, function LoadBitmap: wrong .BMP-file (loaded from memory)"),MB_ICONSTOP);
return FALSE;
}
m_dwbsize = size - sizeof(m_BmFileHeader);
m_pBmp = (BYTE *)malloc(m_dwbsize);
if (!m_pBmp)
{
AfxMessageBox(_T("Matiss, class CBmp, function LoadBitmapFromMemory: no room for arrays."),MB_ICONSTOP);
return FALSE;
}
memcpy(m_pBmp, bitmap_is_string+sizeof(m_BmFileHeader), m_dwbsize);
m_pInfo = (LPBITMAPINFOHEADER)m_pBmp;
m_pData = m_pBmp + m_BmFileHeader.bfOffBits - sizeof(m_BmFileHeader);
if (m_pInfo->biCompression)
{
CString s;
s = _T("Matiss, class CBmp, function LoadBitmapFromMemory:\n");
s += _T("file in memory is initially compressed. It is an error for this class.");
AfxMessageBox(s,MB_ICONSTOP);
return FALSE;
}
m_bIsLoad = TRUE;
return TRUE;
}
Загрузка .BMP-файла в ОЗУ (если к моменту обращения какой-либо файл уже был ранее загружен в ОЗУ, его образ удаляется):
BOOL CBmp::LoadBitmap(const TCHAR *filename)
{
if (m_bIsLoad) this->~CBmp(); // Явное обращение к деструктору!
FILE *f = _tfopen(filename, _T("rb"));
if (f == NULL)
{
CString s;
s = _T("Matiss, class CBmp, function LoadBitmap: error during opening file \"");
s += filename;
s += _T("\"");
AfxMessageBox(s,MB_ICONSTOP);
return FALSE;
}
if (!fread(&m_BmFileHeader, sizeof(m_BmFileHeader), 1, f))
{
CString s;
s = _T("Matiss, class CBmp, function LoadBitmap: error reading BMP-file ");
s += filename;
s += _T("\"");
AfxMessageBox(s,MB_ICONSTOP);
return FALSE;
}
if (m_BmFileHeader.bfType != 0x4d42)
{
AfxMessageBox(_T("Matiss, class CBmp, function LoadBitmap: wrong .BMP-file"),MB_ICONSTOP);
return FALSE;
}
m_dwbsize = filelength(fileno(f)) - sizeof(m_BmFileHeader);
m_pBmp = (BYTE *)malloc(m_dwbsize);
if (!m_pBmp)
{
AfxMessageBox(_T("Matiss, class CBmp, function LoadBitmap: no room for arrays."),MB_ICONSTOP);
return FALSE;
}
if (!fread(m_pBmp,m_dwbsize,1,f))
{
CString s;
s = _T("Matiss, class CBmp, function LoadBitmap: error reading data in BMP-file \"");
s += filename;
s += _T("\"");
AfxMessageBox(s,MB_ICONSTOP);
return FALSE;
}
fclose(f);
m_pInfo = (LPBITMAPINFOHEADER)m_pBmp;
m_pData = m_pBmp + m_BmFileHeader.bfOffBits - sizeof(m_BmFileHeader);
if (m_pInfo->biCompression)
{
CString s;
s = _T("Matiss, class CBmp, function LoadBitmap: ");
s += _T("file \"");
s += filename;
s += _T("\" is initially compressed. It is an error for this class.");
AfxMessageBox(s,MB_ICONSTOP);
return FALSE;
}
m_bIsLoad = TRUE;
return TRUE;
}
Вывести .BMP-файл на экран:
void CBmp::DrawBmp(
CDC *pDC,
int left_dc, int top_dc,
int width_dc, int height_dc,
int left_bmp, int top_bmp,
int width_bmp, int height_bmp,
DWORD dwRop
)
{
if (!m_bIsLoad) return;
if (left_dc == -1) left_dc = 0;
if (top_dc == -1) top_dc = 0;
if (left_bmp == -1) left_bmp = 0;
if (top_bmp == -1) top_bmp = 0;
if (width_dc == -1) width_dc = m_pInfo->biWidth;
if (height_dc == -1) height_dc = m_pInfo->biHeight;
if (width_bmp == -1) width_bmp = m_pInfo->biWidth;
if (height_bmp == -1) height_bmp = m_pInfo->biHeight;
::StretchDIBits(
pDC->m_hDC,
left_dc, top_dc,
width_dc, height_dc,
left_bmp, top_bmp,
width_bmp, height_bmp,
m_pData,
(LPBITMAPINFO)m_pBmp,
DIB_RGB_COLORS,
dwRop
);
}
Взять цвет пиксела из поля изображения .BMP-файла.
(0,0)
--------
| |
| |
| |
| |
| |
-------- (m_pInfo->biWidth-1,m_pInfo->biHeight-1)
*/
COLORREF CBmp::GetPixel(int x, int y)
{
BYTE r,g,b;
UINT i;
UINT l;
// Файл загружен ?
if (!m_bIsLoad) return 0xFFFFFFFF;
// Координаты выходят за допустимые пределы ?
// - возвращается несуществующий цвет.
if (x < 0 || x >= m_pInfo->biWidth) return 0xFFFFFFFF;
if (y < 0 || y >= m_pInfo->biHeight) return 0xFFFFFFFF;
// Определяем истинную длину строки двумерного массива,
// в котором хранится изображение.
// Сначала надо вычислить количество троек байт:
l = 3*m_pInfo->biWidth;
// Если оно делится нацело на 4, то это и есть искомая длина.
// Иначе добавляем необходимое количество байт, так, чтобы
// длина l делилась-бы на 4 нацело:
if (l % 4) while (l % 4) l++;
// Теперь надо скорректировать координаты x,y.
// Изначально они заданы из условий, что левый верхний
// угол изображения имеет координаты (0,0).
// Но в памяти ЭВМ .BMP-файл хранится иначе.
// Строки пикселов нумеруются как-бы в математической
// системе координат: самая нижняя строка - самая первая,
// самая верхняя - последняя в массиве данных.
y = m_pInfo->biHeight - 1 - y;
// Теперь можно формировать индекс начала строки:
i = y * l;
// Добавляем смещение:
i += 3*x;
// Берем цвет:
b = m_pData[i];
g = m_pData[i+1];
r = m_pData[i+2];
return RGB(r,g,b);
}
BMP-файл загружен ?
*/
BOOL CBmp::IsLoad()
{
return m_bIsLoad;
}
Оператор =, переопределенный так, чтобы можно было присвоить один объект класса CBmp другому.
CBmp& CBmp::operator = (CBmp &bmp)
{
if (m_bIsLoad) this->~CBmp();
if (bmp.m_bIsLoad)
{
m_BmFileHeader.bfType = bmp.m_BmFileHeader.bfType;
m_BmFileHeader.bfSize = bmp.m_BmFileHeader.bfSize;
m_BmFileHeader.bfReserved1 = bmp.m_BmFileHeader.bfReserved1;
m_BmFileHeader.bfReserved2 = bmp.m_BmFileHeader.bfReserved2;
m_BmFileHeader.bfOffBits = bmp.m_BmFileHeader.bfOffBits;
m_pBmp = new BYTE[bmp.m_dwbsize];
if (!m_pBmp)
{
AfxMessageBox(_T("Matiss, class CBmp, operator = : no room for arrays."),MB_ICONSTOP);
return *this;
}
memcpy(m_pBmp, bmp.m_pBmp, bmp.m_dwbsize);
m_dwbsize = bmp.m_dwbsize;
m_pInfo = (LPBITMAPINFOHEADER)m_pBmp;
m_pData = m_pBmp + m_BmFileHeader.bfOffBits -
sizeof(m_BmFileHeader);
m_bIsLoad = bmp.m_bIsLoad;
}
return *this;
}
Функция отвечает на вопрос, чему равна ширина .BMP-изображения
int CBmp::GetWidth() const
{
if (m_bIsLoad) return m_pInfo->biWidth;
return 0;
}
Функция отвечает на вопрос, чему равна высота .BMP-изображения
int CBmp::GetHeight() const
{
if (m_bIsLoad) return m_pInfo->biHeight;
return 0;
}
Функция отвечает на вопрос, какой объем памяти для .BMP-файла выделяется в оперативной памяти.
size_t CBmp::GetSizeData()
{
if (m_bIsLoad)
{
size_t r1 = m_BmFileHeader.bfOffBits;
size_t r2 = sizeof(m_BmFileHeader);
return m_dwbsize - r1 + r2;
}
return 0;
}
Функция отвечает на вопрос, каков размер изображения, если оно представлено как BMP-файл.
size_t CBmp::GetFileSize()
{
if (m_bIsLoad)
{
size_t r1 = m_BmFileHeader.bfOffBits;
size_t r2 = sizeof(m_BmFileHeader);
return m_dwbsize + r2;
}
return 0;
}
Функция возвращает оперативный образ BMP-файла. Объем памяти под массив d равен значению, возвращаемому функцией GetFileSize().
int CBmp::GetDump(BYTE **d)
{
// file.Write((void *) &m_BmFileHeader,sizeof(m_BmFileHeader));
if(!m_bIsLoad) return -1;
size_t L = GetFileSize();
*d = new BYTE [L];
memcpy(*d, &m_BmFileHeader, sizeof(m_BmFileHeader));
memcpy(*d + sizeof(m_BmFileHeader), m_pBmp, m_dwbsize);
return 0;
}
Функция возвращает указатель на массив байт с изображением .BMP-файла.
BYTE * CBmp::GetData()
{
if(m_bIsLoad) return m_pData;
return NULL;
}
Функция позволяет сохранить ранее загруженный .BMP-файл на диск. Имя файла для этого указано в параметре filename.
void CBmp::SaveBmp(const TCHAR *filename)
{
if (m_bIsLoad)
{
CFile file;
TRY
{
file.Open(filename, CFile::modeWrite | CFile::modeCreate |
CFile::typeBinary | CFile::shareDenyWrite);
}
CATCH(CFileException, e)
{
CString s;
TCHAR buffer[100];
_stprintf(buffer, _T(" N %i"), e->m_cause);
s = _T("Matiss, class CBmp, function SaveBitmap:\n");
s += _T("file \"");
s += _T("\"");
s += _T(" can't be created on disk");
s += _T("\"\nNumber of error is ");
s += buffer;
AfxMessageBox(s,MB_ICONSTOP);
return;
}
END_CATCH
TRY
{
file.Write((void *) &m_BmFileHeader,sizeof(m_BmFileHeader));
# if (_MSC_VER < 1310)
file.WriteHuge((void *)m_pBmp, m_dwbsize);
# else
file.Write((void *)m_pBmp, (UINT)m_dwbsize);
# endif
}
CATCH(CFileException, e)
{
CString s;
TCHAR buffer[100];
_stprintf(buffer, _T(" N %i"), e->m_cause);
s = _T("Matiss, class CBmp, function SaveBitmap:\n");
s += _T("error writing new file \"");
s += filename;
s += _T("\". Error # ");
s += buffer;
AfxMessageBox(s,MB_ICONSTOP);
return;
}
END_CATCH
file.Close();
}
}
Внутренний разрушитель объекта:
void CBmp::ReleaseBmp()
{
if (m_pBmp)
{
free(m_pBmp);
m_pBmp = NULL;
}
m_bIsLoad = FALSE;
}
Сериализация объекта и его десериализация производится одной функцией. Сериализация – это сохранение текущего состояния объекта, со всеми его конкретными значениями переменных. Это как бы моментальный снимок объекта и сохранение этого состояния на диск. Восстановление конкретного состояния в оперативную память называется десериализацией. За открытие/сохранение состояния картинки файла (объекта) отвечает MyBMPDoc.cpp и MyBMPView.cpp
void CMyBMPDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// Пишем на диск:
ar << m_filelength;
BYTE *a;
m_bmp.GetDump(&a);
ar.Write(a, m_filelength);
}
else
{
TCHAR drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
_tsplitpath(m_path, drive, dir, fname, ext);
if (_tcsicmp(ext,_T(".bmp")))
{
m_bin = true;
// Считываем информацию:
size_t L;
ar >> L;
BYTE *a = new BYTE [L];
ar.Read(a, L);
m_bmp.LoadBmpFromMemory((const char *)a, L);
delete [] a;
}
}
}