В проекте 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; } } }