Оптимизация скинов для окошек сложной формы

Previous  Top  Next

    
 

 

Автор: Бочаров Александр

 

Немного предистории: надо было мне создать скиновое окошко. Вроде несложно, исходников по этому делу везде лежит навалом, бери да делай. Проблема организовалась в том, что для сложных фигур просчет такого окна из растра занимает достаточно много времени. А когда окон несколько? Короче, я решил все это дело написать самостоятельно, причем отказавшись от таких вещей, как GetPixel() и CombineRgn(). Получилось вроде здорово и быстро.

 

Далее следует исходный код с комментариями:

Code:

unit RgnUnit;

 

{©Drkb v.3(2007): www.drkb.ru}

 

interface

 

uses

Windows, SysUtils, Classes;

 

function CreateBitmapRgn(DC : hDC; Bitmap: hBitmap; TransClr: TColorRef): hRgn;

{

Данная функция создает регион, используя для этого растр Bitmap

и исключая из него цвет TransClr. Все расчеты производятся для

устройства DC.

 

данная функция состоит из двух частей:

 

первая часть выделяет память и копирует туда исходное изображение в формате

24 бита на точку, без палитры, т.е. фактически в каждых трех байтах

данного раздела памяти будет записан цвет точки исходного изображения.

Данный формат был выбран из удобства его обработки

(нет необходимости создавать палитру), к тому же нет потери качества

при конвертации исходного изображения. Однако, теоретически можно использовать

любой формат.

 

Для выделения памяти под конвертируемое изображение используется функция

WinAPI CreateDIBSection. Данная функция выделяет память и создает

независмый растр. Для вызова данной функции необходимо заполнить структуру

BITMAPINFO, что достаточно не сложно.

Внимание! для изображений Windows Bitmap используется разрешение в формате

dots per metr (pixels per metr), стандартному разрешению 72dpi соответствует

2834dpm.

 

Фактически, данную функция можно не использовать, вручную выделив память

для последующего переноса исходного изображения.

 

Для конвертации и переноса исходного изображения в выделнную память

используется функция WinAPI GetDIBits. Функции передаются следуюшие параметры:

исходное изображение, количество рядов для переноса, указатель на память,

куда следует перенести изображение, структура BITMAPINFO с заполнеными первыми

шестью членами (именно здесь задяются параметры для конвертирования

изображения). Фактически, данная функция может перевести любой исходный растр

в любой необходимый растр.

 

вторая чать описываемой функции проходится по области памяти, куда было

занесено конвертируемое изображение, отсекает ненужные области и содает регион.

Для создания региона используется функция WinAPI ExtCreateRegion. Для вызова

данной функции необходимо заполнить структуру RGNDATA, состоящую из структуры

RGNDATAHEADER и необходимого количества структур RECT. в Дельфи структура

RGNDATA описана так:

 

_RGNDATA = record

   rdh: TRgnDataHeader;

   Buffer: array[0..0] of CHAR;

   Reserved: array[0..2] of CHAR;

end;

RGNDATA = _RGNDATA;

 

Скорее всего, поле Reserved было введено программистами Дельфи только для того,

чтобы в нее умещался хотя бы один прямоугольник, т.к. в Microsoft Platfrom SDK

этого поля нет. Однако, данная структура нам не подходит, т.к. нам необходимо

учитывать сразу несколько прямоугольников. Для решения этой задачи приходится

выделять память вручную, с учетом RGNDATAHEADER и количества прямоугольников,

необходимых нам, заносить туда прямоугольники (после RGNDATAHEADER),

создавать указатель на структуру RGNDATA и ставить его на выделнную память.

 

Следовательно, придется два раза пройтись по растру: первый раз - для расчета

количества прямоугольников, а второй - для уже фактического их занесения

в выделенную память.

 

Есть несколько способов для избежания двойного прохода растра, но все они

имеют свои недостатки и здесь не рассматриваются. В любом случае, даже для

больших и сложных изображений эти два прохода достаточно быстры.

 

по окнчании работы функции освобождается память, выделенная на конвертируемый

растр и структуру RGNDATA.

}

 

implementation

 

//создает регион из растра Bitmap для DC с удалением цвета TransClr

//внимание! TColorRef и TColor не одно и тоже.

//Для перевода используется функция ColorToRGB().

 

function CreateBitmapRgn(DC: hDC; Bitmap: hBitmap; TransClr: TColorRef): hRgn;

var

bmInfo: TBitmap;                //структура BITMAP WinAPI

W, H: Integer;                  //высота и ширина растра

bmDIB: hBitmap;                 //дискрептор независимого растра

bmiInfo: BITMAPINFO;            //структура BITMAPINFO WinAPI

lpBits, lpOldBits: PRGBTriple;  //указатели на структуры RGBTRIPLE WinAPI

lpData: PRgnData;               //указатель на структуру RGNDATA WinAPI

X, Y, C, F, I: Integer;         //переменные циклов

Buf: Pointer;                   //указатель

BufSize: Integer;               //размер указателя

rdhInfo: TRgnDataHeader;        //структура RGNDATAHEADER WinAPI

lpRect: PRect;                  //указатель на TRect (RECT WinAPI)

begin

Result:=0;

if Bitmap=0 then Exit;          //если растр не задан, выходим

 

GetObject(Bitmap, SizeOf(bmInfo), @bmInfo);  //узнаем размеры растра

W:=bmInfo.bmWidth;                           //используя структуру BITMAP

H:=bmInfo.bmHeight;

I:=(W*3)-((W*3) div 4)*4;                    //определяем смещение в байтах

if I<>0 then I:=4-I;

 

//Пояснение: растр Windows Bitmap читается снизу вверх, причем каждая строка

//дополняется нулевыми байтами до ее кратности 4.

//для 32-х битный растров такой сдвиг делать не надо.

 

//заполняем BITMAPINFO для передачи в CreateDIBSection

 

bmiInfo.bmiHeader.biWidth:=W;             //ширина

bmiInfo.bmiHeader.biHeight:=H;            //высота

bmiInfo.bmiHeader.biPlanes:=1;            //всегда 1

bmiInfo.bmiHeader.biBitCount:=24;         //три байта на пиксель

bmiInfo.bmiHeader.biCompression:=BI_RGB;  //без компрессии

bmiInfo.bmiHeader.biSizeImage:=0;         //размер не знаем, ставим в ноль

bmiInfo.bmiHeader.biXPelsPerMeter:=2834//пикселей на метр, гор.

bmiInfo.bmiHeader.biYPelsPerMeter:=2834//пикселей на метр, верт.

bmiInfo.bmiHeader.biClrUsed:=0;           //палитры нет, все в ноль

bmiInfo.bmiHeader.biClrImportant:=0;      //то же

bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); //размер структруы

bmDIB:=CreateDIBSection(DC, bmiInfo, DIB_RGB_COLORS,

                         Pointer(lpBits), 0, 0);

//создаем независимый растр WxHx24, без палитры, в указателе lpBits получаем

//адрес первого байта этого растра. bmDIB - дискрептор растра

 

//заполняем первые шесть членов BITMAPINFO для передачи в GetDIBits

 

bmiInfo.bmiHeader.biWidth:=W;             //ширина

bmiInfo.bmiHeader.biHeight:=H;            //высота

bmiInfo.bmiHeader.biPlanes:=1;            //всегда 1

bmiInfo.bmiHeader.biBitCount:=24;         //три байта на пиксель

bmiInfo.bmiHeader.biCompression:=BI_RGB;  //без компресси

bmiInfo.bmiHeader.biSize:=SizeOf(bmiInfo.bmiHeader); //размер структуры

GetDIBits(DC, Bitmap, 0, H, lpBits, bmiInfo, DIB_RGB_COLORS);

//конвертируем исходный растр в наш с его копированием по адресу lpBits

 

lpOldBits:=lpBits;  //запоминаем адрес lpBits

 

//первый проход - подсчитываем число прямоугольников, необходимых для

//создания региона

C:=0;                         //сначала ноль

for Y:=H-1 downto 0 do begin //проход снизу вверх

   X:=0;

   while X<W do begin             //от 0 до ширины-1

//пропускаем прзрачный цвет, увеличивая координату и указатель

     while (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)=TransClr) do begin

       Inc(lpBits);

       X:=X+1;

     end;

//если нашли не прозрачный цвет, то считаем, сколько точек в ряду он идет

     if (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)<>TransClr) then begin

       while (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)<>TransClr) do begin

         Inc(lpBits);

         X:=X+1;

       end;

       C:=C+1//увиличиваем счетчик прямоугольников

     end;

   end;

//ряд закончился, необходимо увеличить указатель до кратности 4

   PChar(lpBits):=PChar(lpBits)+I;

end;

 

lpBits:=lpOldBits;  //восстанавливаем значение lpBits

 

//Заполняем структуру RGNDATAHEADER

rdhInfo.iType:=RDH_RECTANGLES;             //будем использовать прямоугольники

rdhInfo.nCount:=C;                         //их количество

rdhInfo.nRgnSize:=0;                       //размер выделяем памяти не знаем

rdhInfo.rcBound:=Rect(0, 0, W, H);         //размер региона

rdhInfo.dwSize:=SizeOf(rdhInfo);           //размер структуры

 

//выделяем память для струтуры RGNDATA:

//сумма RGNDATAHEADER и необходимых на прямоугольников

BufSize:=SizeOf(rdhInfo)+SizeOf(TRect)*C;

GetMem(Buf, BufSize);

lpData:=Buf;             //ставим указатель на выделенную память

lpData.rdh:=rdhInfo;     //заносим в память RGNDATAHEADER

 

//Заполдяенм память прямоугольниками

lpRect:=@lpData.Buffer;  //первый прямоугольник

for Y:=H-1 downto 0 do begin

   X:=0;

   while X<W do begin

     while (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)=TransClr) do begin

       Inc(lpBits);

       X:=X+1;

     end;

     if (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)<>TransClr) then begin

       F:=X;

       while (X<W) and (RGB(lpBits.rgbtRed,lpBits.rgbtGreen,lpBits.rgbtBlue)<>TransClr) do begin

         Inc(lpBits);

         X:=X+1;

       end;

       lpRect^:=Rect(F, Y, X, Y+1);  //заносим координаты

       Inc(lpRect);                  //переходим к следующему

     end;

   end;

   PChar(lpBits):=PChar(lpBits)+I;

end;

 

//после окночания заполнения структуры RGNDATA можно создавать регион.

//трансформации нам не нужны, ставим в nil, указываем размер

//созданной структуры и ее саму.

Result:=ExtCreateRegion(nil, BufSize, lpData^);  //создаем регион

 

FreeMem(Buf, BufSize);  //теперь структура RGNDATA больше не нужна, удаляем

DeleteObject(bmDIB);    //созданный растр тоже удаляем

end;

 

end.

 

 

©Drkb::03627

Взято из http://forum.sources.ru

 

 

 

Код исправлен Петровичем

Взято с Vingrad.ru http://forum.vingrad.ru