В проекте MyBMP  можно открыть и пересохранить bmp-файл.  Сохранение осуществляется не в файл формата .bmp, а в файл с придуманным расширением .tss.

Целью данного проекта было изучение работы с оперативной памятью, хранением и представлением bmp-файла в ней, также  сохранением текущего состояния объекта на диск (сериализация или дамп) и восстановление образа с диска в оперативную память.

Программа реализована через интерфейс с дочерними окнами. Загружаем Файл через стандартное меню «Открыть»

pr5.1

pr5.2

За обработку 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;
		}
	}
}