15 мая "Исходники.РУ" отмечают 20-летие присутствия в сети!
Поздравляем всех причастных и неравнодушных с юбилеем сайта!
И огромное спасибо всем, кто был с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Создаём регион из файла


Автор: Yuriy Zaporozhets

Описание

Это очень простая функция, создающая регион из bitmap (.bmp) файла. Так же в примере представлен усовершенствованный вариант этой функции, позволяющий задавать маску изображения.

Компилятор: Visual C++ 4-6

Функция называется CreateRgn и имеет два параметра:
szFileName - имя файла, содержащего картинку.
pPoint - указатель на структуру POINT , которая содержит координаты цвета, который используется для конструирования региона, если параметр NULL, то будет использоваться цвет в координате (0, 0).

HRGN CreateRgn(LPTSTR szFileName, LPPOINT pPoint)
{
	HBITMAP hBmp = (HBITMAP)LoadImage( NULL, szFileName, 
                               IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
	if ( !hBmp ) return NULL;

	BITMAP bi;
	BYTE bpp;
	DWORD e;
	DWORD f, t;
	INT i, j;
	bool b = false;
	HRGN Rgn, ResRgn = CreateRectRgn( 0, 0, 0, 0 );

	GetObject( hBmp, sizeof( BITMAP ), &bi );

	bpp = bi.bmBitsPixel >> 3;

	BYTE *pBits = new BYTE[ bi.bmWidth * bi.bmHeight * bpp ];

	int  p = GetBitmapBits( hBmp, bi.bmWidth * bi.bmHeight * bpp,
                                                                pBits );

	if ( pPoint == NULL || pPoint->x >= bi.bmWidth ||
                                             pPoint->y >= bi.bmHeight )
		e = *(DWORD*)pBits;
	else 
		e = *(DWORD*)(pBits + (pPoint->y * bi.bmWidth + pPoint->x) * bpp );

	e <<= 32 - bi.bmBitsPixel;

	for ( i = 0; i < bi.bmHeight; i++ )
	for ( j = 0; j < bi.bmWidth; j++ )
	{
		t = *(DWORD*)(pBits + (i * bi.bmWidth +
                                        j) * bpp) << (32 - bi.bmBitsPixel);

		if ( t == e )
		{
			if ( !b )
			{
				f = j;
				b = true;
			} else if ( j == (bi.bmWidth - 1) )
			{
				Rgn = CreateRectRgn( f, i, j, i + 1 );
				CombineRgn( ResRgn, ResRgn, Rgn, RGN_OR );
				b = false;
			}
		} else if ( b )
		{
			Rgn = CreateRectRgn( f, i, j, i + 1 );
			CombineRgn( ResRgn, ResRgn, Rgn, RGN_OR );
			b = false;
		}
	}

	delete pBits;

	return ResRgn;
}

Update: Ниже представлена улучшенная версия функции, которая требует маску битмапа для TrueColor. В Win9x ширина маски битмапа  должна быть DWORD.

HRGN CreateRgnFromFile(HBITMAP hBmp, COLORREF color)
{
	// получаем размер маски
	BITMAP bm = { 0 };
	GetObject( hBmp, sizeof(BITMAP), &bm );

	m_dwWidth	= bm.bmWidth;		// ширина bitmap
	m_dwHeight	= bm.bmHeight;		// высота bitmap

	// Распределяем буфер для маски
	LPBYTE pBits = new BYTE[ bm.bmWidthBytes * bm.bmHeight ];
	// заполняем буфер свойствами
	BITMAPINFO bi = { 0 };
	bi.bmiHeader.biSize	= sizeof( BITMAPINFOHEADER );
	bi.bmiHeader.biBitCount	= bm.bmBitsPixel;
	bi.bmiHeader.biHeight	= bm.bmHeight;
	bi.bmiHeader.biWidth	= bm.bmWidth;
	bi.bmiHeader.biPlanes	= bm.bmPlanes;
	// заполняем биты буфера
	HDC hdc = ::GetDC( NULL );
	GetDIBits( hdc, hBmp, 0, bm.bmHeight, pBits, &bi, DIB_RGB_COLORS );
	::ReleaseDC( NULL, hdc );

	if ( bi.bmiHeader.biBitCount < 16 ) return NULL;

	// сдвигаем биты и байт на пиксель (для сравнения цветов)
	BYTE sb = 32 - bi.bmiHeader.biBitCount;	
	BYTE bpp = bi.bmiHeader.biBitCount >> 3;

	LPBYTE pClr = (LPBYTE)&color;
	// переставляем красный и синий компоненты
	BYTE tmp = pClr[0]; pClr[0] = pClr[2]; pClr[2] = tmp;
	// convert color if curent DC is 16-bit (5:5:5)
	if ( bpp == 2 )	color = ((DWORD)(pClr[0] & 0xf8) >> 3) |
				((DWORD)(pClr[1] & 0xf8) << 2) |
				((DWORD)(pClr[2] & 0xf8) << 7);
	// выравниваем цвет для сравнения
	color <<= sb;

	const DWORD RDHDR = sizeof(RGNDATAHEADER);
	const DWORD MAXBUF = 40;	// размер одного блока в RECT-ах
					// (т.e. MAXBUF*sizeof(RECT)
                                        // в байтах)
	LPBYTE	pColor = pBits;
	LPRECT	pRects;								
	DWORD	cBlocks = 0;		// кол-во распределённых блоков

	INT	i, j;			// текущая позиция в маске картинки
	INT	first = 0;		// левая координата текущей линии,
                                        // в которой была найдена маска
	bool	wasfirst = false;	// устанавливается, если маска была
                                        // найдена в текущей линии
	bool	ismask;			// устанавливается, если текущий
                                        // цвет является маской

	// распределяем память для данных региона
	RGNDATAHEADER* pRgnData = (RGNDATAHEADER*)new BYTE[ RDHDR +
                                 ++cBlocks * MAXBUF * sizeof(RECT) ];
	memset( pRgnData, 0, RDHDR + cBlocks * MAXBUF * sizeof(RECT) );
	// заполняем его по умолчанию
	pRgnData->dwSize	= RDHDR;
	pRgnData->iType		= RDH_RECTANGLES;
	pRgnData->nCount	= 0;

	for ( i = 0; i < bm.bmHeight; i++ )
	for ( j = 0; j < bm.bmWidth; j++ )
	{
		// получаем цвет
		ismask = *(DWORD*)pColor << sb != color;
		// сдвиг указателя на следующий цвет
		pColor += bpp;
		// помещаем часть сканируемой линии как регион RECT,
                // если прозрачный цвет найден после цвета маски либо
		// цвет маски найден в конце маски изображения
		if ( wasfirst && (ismask ^ (j < bm.bmWidth - 1)) )
		{
			// получаем смещение в массиве RECT
			pRects = (LPRECT)((LPBYTE)pRgnData + RDHDR);
			// сохраняем текущий RECT
			pRects[ pRgnData->nCount++ ] = CRect( first, 
                                 bm.bmHeight - i - 1, j, bm.bmHeight - i );
			// если буфер полон, то перераспределяем его
			if ( pRgnData->nCount >= cBlocks * MAXBUF )
			{
				LPBYTE pRgnDataNew = new BYTE[ RDHDR +
                                       ++cBlocks * MAXBUF * sizeof(RECT) ];
				memcpy( pRgnDataNew, pRgnData, RDHDR +
                                    (cBlocks - 1) * MAXBUF * sizeof(RECT));

				delete pRgnData;
				pRgnData = (RGNDATAHEADER*)pRgnDataNew;
			}
			wasfirst = false;
		}
		else if ( !wasfirst && ismask )	    // устанавливаем
                                                    // wasfirst когда
                                                    // маска найдена
		{
			first = j;
			wasfirst = true;
		}
	}

	delete pBits;

	// создаём регион
	HRGN hRgn = ExtCreateRegion( NULL, RDHDR +
                    pRgnData->nCount * sizeof(RECT), (LPRGNDATA)pRgnData );

	delete pRgnData;

	return hRgn;
}