IMHO.WS

IMHO.WS (http://www.imho.ws/index.php)
-   Программирование (http://www.imho.ws/forumdisplay.php?f=40)
-   -   Создание непрямоугольного окна с изменяемым размером (WinAPI) (http://www.imho.ws/showthread.php?t=68521)

EjikVTumane 15.09.2004 17:04

Создание непрямоугольного окна с изменяемым размером (WinAPI)
 
Для получения окна произвольной формы с изменяемым размером обрабатываю WM_SIZE, но при уменьшении размера окна за правой и нижней границей тянется шлейф. Причем только за частями границы, которые отличаются от исходного прямоугольного региона (например за скругленными углами окна).

Самое забавное - есть замечательный пример написанный на MFC http://www.rsdn.ru/article/files/Classes/skindlg.xml и все прекрасно работает. А на WinAPI не получается :(

Как можно решить данную проблему?

Код:

case WM_SIZE:
{
      RECT clientRect, wndRect;
      GetClientRect(hWnd, &clientRect);
      UpdateMainFrame(((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam)));
      WindowRgn = BitmapToRegion(MainFrame, RGB(255, 0, 255));
      SetWindowRgn(hWnd, WindowRgn, FALSE);
      hdc = GetDC(hWnd);
      HDC MainFrameDC;
      MainFrameDC = CreateCompatibleDC(NULL);
      SelectObject(MainFrameDC, MainFrame);
      BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, MainFrameDC, 0, 0, SRCCOPY);
      ReleaseDC(hWnd, hdc);
      DeleteDC(MainFrameDC);

      return 0;
}

Пара пояснений к коду:
UpdateMainFrame - создается BITMAP
BitmapToRegion - создается регион на основе BITMAP'а

Teklbery 21.09.2004 14:55

Вот попробуй етот код на Дельфяне, если что разберешся
 
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Timer1: TTimer;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure buttonclick(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure muve(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
kk:byte;
xx,yy,xxl,yyl:integer;
implementation

{$R *.DFM}


procedure TForm1.Button1Click(Sender: TObject);
var hwnd:longint;
rgn:hrgn;
poi:array[1..10] of tpoint;
n,m,l,i,j:integer;
begin m:=5;
poi[1].x:=0;
poi[1].y:=0;
poi[2].x:=0;
poi[2].y:=form1.Height div 2;
poi[3].x:=form1.Width div 2;
poi[3].y:=form1.Height;
poi[4].x:=form1.Width;
poi[4].y:=form1.Height div 2;
poi[5].x:=form1.Width;
poi[5].y:=0;
rgn:=CreatePolygonRgn(poi,m,i);
setwindowrgn(form1.Handle ,rgn,true);
end;

function BitmapToRegion(Bitmap: TBitmap; TransColor: TColor): HRGN;
var X,Y: Integer;
XStart: Integer;
pb:pByteArray;
begin Result:= 0;
for Y:= 0 to Bitmap.Height - 1 do
begin
X:= 0;
//with bitmap do
pb:=bitmap.ScanLine[y];
while X <Bitmap.Width do
begin
while (X <Bitmap.Width) and (pb^[X] =TransColor) do
inc(X);
if X >=Bitmap.Width then
Break;
XStart := X;
while (X <Bitmap.Width) and (pb^[x]<>TransColor) do
Inc(X);
if Result = 0 then
Result := CreateRectRgn(XStart, Y, X, Y + 1)
else
CombineRgn(Result, Result,CreateRectRgn(XStart, Y, X, Y + 1), RGN_OR);
end;
{with bitmap do
while X <Width do
begin
while (X <Width) and (Canvas.Pixels[X, Y] = TransColor) do
Inc(X);
if X >=Width then
Break;
XStart := X;
while (X <Width) and (Canvas.Pixels[X, Y] <>TransColor) do
Inc(X);
if Result = 0 then
Result := CreateRectRgn(XStart, Y, X, Y + 1)
else
CombineRgn(Result, Result,
CreateRectRgn(XStart, Y, X, Y + 1), RGN_OR);
end;}
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var rgn:hrgn;
Bitmap: TBitmap;
color:TColor;
begin Bitmap := TBitmap.Create;
Bitmap.LoadFromFile('c:\11.bmp');

color:=255;
rgn:=BitmapToRegion(bitmap,color);
setwindowrgn(form1.Handle ,rgn,true);
form1.Canvas.Draw(-3,-24,bitmap);
end;


procedure TForm1.Button3Click(Sender: TObject);
begin
CLOSE
end;

procedure TForm1.buttonclick(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin if ord(button)=0 then if kk=0 then kk:=1
else kk:=0
else kk:=1;
end;

procedure TForm1.muve(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var c:cardinal;
begin if kk=0 then begin
xx:=xx-(xxl-x);
yy:=yy-(yyl-y);
xxl:=x;
yyl:=y;
form1.Caption:=inttostr(xx);
SetWindowPos(form1.handle,form1.handle,xx,yy,form1.Width,form1.Height, c);
timer1.Interval:=100;
end
else form1.Caption:='aaaaaaaaaa';
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var c:cardinal;
begin //SetWindowPos(form1.handle,getnextwindow(form1.handle,1),xx,yy,form1.Wi dth,form1.Height,c);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin kk:=12;
end;

end.

EjikVTumane 21.09.2004 18:22

thx конечно, но вопрос был не в способе реализации, а с какого перепуга отрисовка на DC'шке приводит к отрисовке последнего пикселя за границами региона (при ресайзе), при этом если уменьшить высоту и ширину на пиксель при BitBlt, то последний пиксель останется не отрисованым (хотя это изначально не вариант, так как с координатами все нормально).
А тут экзампл на дельфях помощник в последнюю очередь.

Teklbery 22.09.2004 10:07

В примере конкретно происходит ресайз, и нигде нету лишних пикселей, я даже тебе со вставкой картинок дал пример, для наглядности если у тебя последний пиксель прорисовывается :( , это у тебя скорее всего обновления не происходит либо скапливается мусор, попробуй очистить его перед тем как делать ресайз, сделай апдейт и потом ресайз.

EjikVTumane 22.09.2004 12:37

Вложений: 1
Смотри, переносим код в обработку WM_ERASEBKGND (это к вопросу о том, что обновление не происходит):
Код:

case WM_ERASEBKGND:
{
        GetWindowRect(hWnd, &windowRect);
        UpdateMainFrame(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
        frameRegion = BitmapToRegion(MainFrame, RGB(255, 0, 255));
        SetWindowRgn(hWnd, frameRegion, FALSE);
        HBRUSH brush;
        brush = CreatePatternBrush(MainFrame);
        FillRgn(hdc, frameRegion, brush);
        DeleteObject(brush);
        return 0;
}

BITMAP тоже чистится каждый раз перед созданием:
Код:

void UpdateMainFrame(int width, int height)
{
        HDC MainFrameDC;

        if(MainFrame != NULL)
                DeleteObject(MainFrame);
        MainFrame = CreateBitmap(width, height, 1, 32, NULL);
        MainFrameDC = CreateCompatibleDC(NULL);
        SelectObject(MainFrameDC, MainFrame);

        //собираем BITMAP из кусочков
        BitBlt(MainFrameDC, 0, 0, bitmap11info.bmWidth, bitmap11info.bmHeight, bitmap11hdc, 0, 0, SRCCOPY);
        StretchBlt(MainFrameDC, bitmap11info.bmWidth, 0, width-bitmap13info.bmWidth-bitmap11info.bmWidth, bitmap12info.bmHeight,
                bitmap12hdc, 0, 0, bitmap12info.bmWidth, bitmap12info.bmHeight, SRCCOPY);
        BitBlt(MainFrameDC, width-bitmap13info.bmWidth, 0, bitmap13info.bmWidth, bitmap13info.bmHeight, bitmap13hdc, 0, 0, SRCCOPY);

        StretchBlt(MainFrameDC, 0, bitmap11info.bmHeight, bitmap21info.bmWidth, height-bitmap11info.bmHeight-bitmap31info.bmHeight,
                bitmap21hdc, 0, 0, bitmap21info.bmWidth, bitmap21info.bmHeight, SRCCOPY);
        StretchBlt(MainFrameDC, bitmap21info.bmWidth, bitmap11info.bmHeight, width-bitmap23info.bmWidth-bitmap21info.bmWidth, height-bitmap12info.bmHeight-bitmap32info.bmHeight,
                bitmap22hdc, 0, 0, bitmap22info.bmWidth, bitmap22info.bmHeight, SRCCOPY);
        StretchBlt(MainFrameDC, width-bitmap23info.bmWidth, bitmap11info.bmHeight, bitmap23info.bmWidth, height-bitmap11info.bmHeight-bitmap31info.bmHeight,
                bitmap23hdc, 0, 0, bitmap23info.bmWidth, bitmap23info.bmHeight, SRCCOPY);


        BitBlt(MainFrameDC, 0, height-bitmap31info.bmHeight, bitmap31info.bmWidth, bitmap31info.bmHeight, bitmap31hdc, 0, 0, SRCCOPY);
        StretchBlt(MainFrameDC, bitmap31info.bmWidth, height-bitmap32info.bmHeight, width-bitmap33info.bmWidth-bitmap31info.bmWidth, bitmap32info.bmHeight,
                bitmap32hdc, 0, 0, bitmap32info.bmWidth, bitmap32info.bmHeight, SRCCOPY);
        BitBlt(MainFrameDC, width-bitmap33info.bmWidth, height-bitmap33info.bmHeight, bitmap33info.bmWidth, bitmap33info.bmHeight, bitmap33hdc, 0, 0, SRCCOPY);

        DeleteDC(MainFrameDC);
        GetObject(MainFrame, sizeof(MainFrameInfo), &MainFrameInfo);
}

Получаем - при каждой перерисовке устанавливаем окну новый регион, и заполняем его кистью, созданной на основе BITAMP'а. И ежу понятно :) , что запихивать этот код в обработку WM_ERASEBKGND - это гнусно и ОЧЕНЬ не практично (но для иллюстрации потянет).

Далее смотрим MSDN :contract: :
The SetWindowRgn function sets the window region of a window. The window region determines the area within the window where the system permits drawing. The system does not display any portion of a window that lies outside of the window region.

Иными словами говоря - не должно изображение за регион выходить.

А что получается смотри во вложении.

Gem Single 23.09.2004 23:57

Вот наверное из-за того, что вынес изменение региона в WM_ERASEBKGND, такая фигня и получается... При изменении региона винда рекурсивно вызывает WM_ERASEBKGND ещё раз, тут то всё и происходит.

EjikVTumane 24.09.2004 04:12

В WM_ERASEBKGND я вынес для примера, этого оно в WM_SIZE было, этот код хоть куда пихай (в тот же WM_NCPAINT) - результат один. Ну и вообще в SetWindowRgn третий параметр FALSE - указывает что перерисовка после изменения региона не требуется. Также, я могу в обработчике WM_ERASEBKGND ничего не перерисовывать, а просто return 0 делать - это не поможет, то бишь дело тут в другом. Ну и как видно на картинке - шлейф то только за частями, отличающимися от первоначального прямоугольного региона, а так был бы за всем окном.
Да к томуже FillRgn делается для того же региона, который установлен как регион окна :confused: .

VeryLucky 24.09.2004 15:00

http://www.rsdn.ru/article/files/Classes/skindlg.xml

EjikVTumane 24.09.2004 16:47

VeryLucky
Ну да, а из первого поста не заметно, что про эту статью я в курсе и под MFC все работает?

Gem Single 26.09.2004 00:55

Цитата:

Сообщение от Gunslinger
В WM_ERASEBKGND я вынес для примера, этого оно в WM_SIZE было, этот код хоть куда пихай (в тот же WM_NCPAINT) - результат один. Ну и вообще в SetWindowRgn третий параметр FALSE - указывает что перерисовка после изменения региона не требуется. Также, я могу в обработчике WM_ERASEBKGND ничего не перерисовывать, а просто return 0 делать - это не поможет, то бишь дело тут в другом. Ну и как видно на картинке - шлейф то только за частями, отличающимися от первоначального прямоугольного региона, а так был бы за всем окном.
Да к томуже FillRgn делается для того же региона, который установлен как регион окна :confused: .

Блин, скачал я тот пример на MFC.
SetWindowRgn там вызывается с bRepaint=TRUE!!! Попробуй...
А ещё там Sleep(0) сразу после SetWindowRgn().
:contract:

EjikVTumane 26.09.2004 07:54

Gem Single
Похоже на правду, посмотрим ...


Часовой пояс GMT +4, время: 04:05.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.