GMSAPR
.pdf81
//указатель на заголовок растра
LPBITMAPINFO GetBMInfoPtr(){return m_pBMI;}
//указатель на таблицу цветов
RGBQUAD* GetBMColorTabPtr();
//ширину в пикселах; LONG GetBMWidth();
//высоту в пикселах
LONG GetBMHeight();
//указатель на растровые данные
BYTE* GetBMDataPtr(){return m_pData;};
//указатель на пиксел
BYTE* GetPixPtr(LONG x, LONG y);
// Загружает из файла
BOOL LoadBMP(CString FileName);
//Выводит DIB на контекст pDC
//x, y - позиция левого верхнего угла области назначения
//cx, cy - размер области назначения
//x0, y0 - позиция левого верхнего угла
//выводимой части изображения
//cx0, cy0 - размер выводимой части изображения
//str_mode – режим масштабирования
//rop - растровая операция, определяет способ
//наложения изображения
void DrawBitmap(CDC *pDC, LONG x=0, LONG y=0, LONG cx=0, LONG cy=0, LONG x0=0, LONG y0=0, LONG cx0=0, LONG cy0=0, int str_mode=COLORONCOLOR, DWORD rop=SRCCOPY);
//Выводит DIB на контекст pDC
//с позиции (x,y) в масштабе scale
void DrawBitmap(CDC *pDC, LONG x, LONG y, double scale, int str_mode=COLORONCOLOR, DWORD rop=SRCCOPY);
// Записывает BMP в файл
BOOL SaveBMP(CString FileName); // Создает копию
BOOL CreateCopy(CRaster *pOrg);
82
//Создает растр заданного размера,
//совместимый с параметрами BITMAPINFO
BOOL CreateCompatible(LPBITMAPINFO pBMI, LONG width=0, LONG height=0);
// Возвращает гисторамму изображения
BOOL GetHistogram(DWORD *pHist, int Range);
};
Теперь реализуем объявленный класс:
// Raster.cpp: CRaster implementation
/////////////////////////////////////////////////
#include "stdafx.h" #include "Raster.h"
CRaster::CRaster()
{
m_pData=NULL;
m_pBMI=NULL;
}
CRaster::~CRaster()
{
Clear();
};
void CRaster::Clear()
{
if(m_pData!=NULL) delete[] m_pData; m_pData=NULL;
if(m_pBMI!=NULL) delete[] m_pBMI; m_pBMI=NULL;
};
RGBQUAD* CRaster::GetBMColorTabPtr()
83
{
return(LPRGBQUAD)(((BYTE*)(m_pBMI))+sizeof (BITMAPINFOHEADER));
};
LONG CRaster::GetBMWidth()
{
if(m_pBMI==NULL) return 0; return m_pBMI->bmiHeader.biWidth;
};
LONG CRaster::GetBMHeight()
{
if(m_pBMI==NULL) return 0;
return m_pBMI->bmiHeader.biHeight;
};
BYTE* CRaster::GetPixPtr(LONG x, LONG y)
{
if( x<0 || x>= m_pBMI->bmiHeader.biWidth || y<0 || y>= m_pBMI->bmiHeader.biHeight || m_pData == NULL)
return NULL;
return ( m_pData + ( BYTESPERLINE( m_pBMI-> bmiHeader.biWidth, m_pBMI->bmiHeader.biBitCount)*y + x*m_pBMI->bmiHeader.biBitCount/8));
};
BOOL CRaster::LoadBMP(CString FileName)
{
//Очистим
Clear(); //Открываем файл
CFile File;
if(!File.Open(FileName, CFile::modeRead)) return FALSE;
//Загружаем изображение
84
//Читаем заголовок файла: размер и положение начала данных
BITMAPFILEHEADER FI;
File.Read(&FI, sizeof(BITMAPFILEHEADER));
//Проверяем – это Windows Bitmap изображение? if(FI.bfType!=0x4D42)
{ File.Close(); return FALSE;}
//Смещаем позицию
File.Seek(sizeof(BITMAPFILEHEADER), CFile::begin);
//Считаем, что в заголовке файла есть BITMAPINFO m_pBMI=(LPBITMAPINFO) new BYTE[FI.bfOffBits-
sizeof(BITMAPFILEHEADER)]; //Выделяем память под заголовок if(m_pBMI==NULL)
{ File.Close(); return FALSE; } //Читаем BITMAPINFO
File.Read ( m_pBMI, FI.bfOffBits - sizeof (BITMAPFILEHEADER));
//Умеем работать только с несжатыми данными if(m_pBMI->bmiHeader.biCompression!=0)
{ File.Close(); return FALSE;}
//Переход к началу данных
File.Seek(FI.bfOffBits, CFile::begin); //Выделяем память под данные
//расчет размера if(m_pBMI->bmiHeader.biSizeImage==0)
m_pBMI->bmiHeader.biSizeImage= BYTESPERLINE(m_pBMI-> bmiHeader.biWidth,
m_pBMI->bmiHeader.biBitCount) * m_pBMI->bmiHeader.biHeight;
m_pData = new BYTE[m_pBMI-> bmiHeader.biSizeImage]; if(m_pData==NULL)
{ File.Close(); return FALSE;} //Читаем данные
85
File.Read(m_pData, m_pBMI-> bmiHeader.biSizeImage);
File.Close(); return TRUE;
};
void CRaster::DrawBitmap(CDC *pDC,
LONG x/*=0*/, LONG y/*=0*/, LONG cx/*=0*/, LONG cy/*=0*/, LONG x0/*=0*/, LONG y0/*=0*/, LONG cx0/*=0*/, LONG cy0/*=0*/, int str_mode/*=COLORONCOLOR*/, DWORD rop /*=SRCCOPY*/)
{
if(m_pBMI==NULL || m_pData==NULL) return; //размеры не заданы — габариты в пикселах if(cx==0) cx=GetBMWidth();
if(cy==0) cy=GetBMHeight();
if(cx0==0) cx0=GetBMWidth(); if(cy0==0) cy0=GetBMHeight(); HDC hdc=pDC->GetSafeHdc(); if(hdc==NULL) return;
// Установка режима масштабирования
int oldStretchMode=pDC->SetStretchBltMode (str_mode); ::StretchDIBits(hdc, // дескриптор контекста устройства
x, y, |
// |
позиция |
в области назначения |
cx, cy, // |
размеры |
обл. назначения |
x0, y0, //позиция в исходной области
cx0, cy0, |
//размеры исх. обл. |
|
m_pData, |
//данные |
|
m_pBMI, |
//заголовок растра |
|
DIB_RGB_COLORS, |
//опции |
|
rop); |
//код |
растровой операции |
if(oldStretchMode!=0) pDC->SetStretchBltMode(oldStretchMode);
86
};
void CRaster::DrawBitmap(CDC *pDC, LONG x, LONG y, double scale, int str_mode/*=COLORONCOLOR*/, DWORD rop /*=SRCCOPY*/)
{
if(m_pBMI==NULL || m_pData==NULL) return;
LONG x0=0, y0=0;
LONG cx0=GetBMWidth();
LONG cy0=GetBMHeight();
LONG cx=static_cast<LONG>(scale*cx0+0.5);
LONG cy=static_cast<LONG>(scale*cy0+0.5);
DrawBitmap(pDC, x, y, cx, cy, x0, y0, cx0, cy0, str_mode, rop);
};
BOOL CRaster::SaveBMP(CString FileName)
{
//Открываем файл
CFile File;
if(!File.Open(FileName, CFile::modeCreate|CFile::modeWrite)) return FALSE;
//Записываем изображение
//Вычислим размер заголовка растра
//вместе с таблицей цветов
DWORD SizeOfBMI= (DWORD)m_pBMI-> bmiHeader.biSize + m_pBMI-> bmiHeader.biClrUsed*sizeof(RGBQUAD);
// Заголовок файла
BITMAPFILEHEADER FI;
//Идентификатор типа файла BMP:
//0x42 = "B" 0x4d = "M" FI.bfType = 0x4d42;
//Размер всего файла
//вместе с заголовками и данными
87
FI.bfSize = (DWORD) sizeof(BITMAPFILEHEADER) + SizeOfBMI + m_pBMI->bmiHeader.biSizeImage;
FI.bfReserved1 = 0;
FI.bfReserved2 = 0;
// Вычисляем смещение до начала растровых данных
FI.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + SizeOfBMI; // Записываем заголовок файла
File.Write(&FI, sizeof(BITMAPFILEHEADER));
// Записываем BITMAPINFO вместе с таблицей цветов
File.Write(m_pBMI, SizeOfBMI); //Данные
File.Write(m_pData, m_pBMI-> bmiHeader.biSizeImage);
File.Close(); return TRUE;
};
BOOL CRaster::CreateCopy(CRaster *pOrg)
{
Clear();
if(!pOrg) return FALSE;
LPBITMAPINFO pOrgBMI=pOrg->GetBMInfoPtr();
//Вычислим размер заголовка растра вместе с таблицей цветов
DWORD SizeOfBMI= (DWORD)pOrgBMI-> bmiHeader.biSize + pOrgBMI-> bmiHeader.biClrUsed*sizeof(RGBQUAD);
// Выделим память под заголовок растра m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI]; if(!m_pBMI) return FALSE;
// Копируем заголовок растра
memcpy(m_pBMI, pOrg->GetBMInfoPtr(), SizeOfBMI);
// Расчет размера памяти под данные if(m_pBMI->bmiHeader.biSizeImage==0)
88
m_pBMI->bmiHeader.biSizeImage= BYTESPERLINE(m_pBMI->bmiHeader.biWidth,
m_pBMI->bmiHeader.biBitCount)* m_pBMI->bmiHeader.biHeight; // Выделяем память под данные
m_pData= new BYTE[m_pBMI-> bmiHeader.biSizeImage]; if(!m_pData) return FALSE;
// Копируем данные
memcpy(m_pData, pOrg->GetBMDataPtr(), m_pBMI-> bmiHeader.biSizeImage);
return TRUE;
};
BOOL CRaster::CreateCompatible(LPBITMAPINFO pBMI, LONG width/*=0*/,LONG height/*=0*/)
{
if(!pBMI) return FALSE;
if(width==0) width=pBMI->bmiHeader.biWidth; if(height==0) height=pBMI->bmiHeader.biHeight;
// Проверяем, может существующий растр и так совместим if( m_pBMI!=NULL && // существует
m_pBMI->bmiHeader.biWidth==width && // такого же размера m_pBMI->bmiHeader.biHeight==height &&
// и глубина цвета совпадает m_pBMI->bmiHeader.biBitCount==pBMI-> bmiHeader.biBitCount) return TRUE; // Растр и так совместим
//Создаем совместимый растр
Clear();
//Вычислим размер заголовка растра вместе с таблицей цветов
DWORD SizeOfBMI= (DWORD) pBMI-> bmiHeader.biSize + pBMI-> bmiHeader.biClrUsed*sizeof(RGBQUAD);
// Выделим память под заголовок растра
89
m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI]; if(!m_pBMI) return FALSE;
// Копируем заголовок растра memcpy(m_pBMI, pBMI, SizeOfBMI);
// Устанавливаем размер m_pBMI->bmiHeader.biWidth=width; m_pBMI->bmiHeader.biHeight=height;
// Расчет размера памяти под данные
m_pBMI-> bmiHeader.biSizeImage=BYTESPERLINE(m_pBMI-> bmiHeader.biWidth, m_pBMI-> bmiHeader.biBitCount)* m_pBMI->bmiHeader.biHeight;
// Выделяем память под данные
m_pData= new BYTE[m_pBMI-> bmiHeader.biSizeImage]; if(!m_pData) return FALSE;
return TRUE;
};
BOOL CRaster::GetHistogram(DWORD *pHist, int Range)
{
//Умеет работать только с данными RGB888 if(m_pBMI->bmiHeader.biBitCount!=24)
return FALSE;
//Обнулим таблицу
for(int i=0; i<Range; i++) pHist[i]=0;
LONG DataStrLength=
BYTESPERLINE(m_pBMI->bmiHeader.biWidth,
m_pBMI->bmiHeader.biBitCount); BYTE *pCurPix=NULL;
BYTE Brightness=0;
90
for(int y=0, x=0; y<m_pBMI-> bmiHeader.biHeight; y++) for(x=0; x<m_pBMI->bmiHeader.biWidth; x++)
{
// Адрес пиксела pCurPix=m_pData+y*DataStrLength+x*3;
//Яркость рассчитывается как
//0.3*Red+0.59*Green+0.11*Blue,
//но пиксельные данные хранятся
//в файле BMP, в порядке BGR Brightness=(BYTE)(( 0.11*(*pCurPix) +
0.59*(*(pCurPix+1))+ 0.3*(*(pCurPix+2))) *Range/256); pHist[Brightness]+=1;
}
return TRUE;
};
Назначение большинства методов класса CRaster понятно из их названий и комментариев. В реализации также нет каких-то особенностей, которые достойны были бы специального рассмотрения. Этот класс никак не связан со структурой приложения, его можно использовать в любой программе. Причем, если заменить использование классов CFile и CString в методах
LoadBMP() и SaveBMP(), на реализацию операций с помощью API-функций, то можно обойтись и без MFC. Однако, с MFC всё же удобнее.
В методе LoadBMP()не реализована распаковка сжатых изображений,
поэтому класс CRaster умеет работать только с данными, так сказать, в их натуральном виде.
Кстати, в случае, когда данные хранятся в несжатом виде, мы могли бы и не выделять динамически память под хранение заголовков и данных изображения. Вместо этого можно было бы использовать механизм, называемый «файлы, проецируемые в память». В остальном работа с данными осуществлялась бы точно также. Механизм проецирования файлов особенно удобен при работе с файлами большого размера.