区域和裁减区域-Window GDI

http://blog.csdn.net/augusdi/article/details/6422958


区域的创建和选择

一个应用程序通过调用指定形状的函数来创建一个区域。下表显示了创建标准图形的函数

形状

函数

矩形区域

CreateRectRgn, CreateRectRgnIndirect, SetRectRgn

圆角的矩形区域

CreateRoundRectRgn

椭圆区域

CreateEllipticRgn, CreateEllipticRgnIndirect

多边形区域

CreatePolygonRgn, CreatePolyPolygonRgn

每个区域创建函数返回一个HANDLE来标示新创建的区域。一个应用程序可以通过调用SelectObject函数并将这个HANDLE作为第二个参数来将一个区域选择进设备内容中。在一个区域被选择进设备内容之后,应用程序可以对其执行各种操作。

区域操作

应用程序能够合并区域、比较区域、着色和返回区域内部、给区域画边框、得到区域的大小和测试光标是否位于区域的范围。

 

 

 

 

 

合并区域

应用程序通过调用CombineRgn函数来合并两个区域。使用这个函数,应用程序能够合并两个区域的交集、两个区域除交集之外的区域、两个区域的并集等等。下面5个值定理了区域合并的类型。

 

 

 

 

意义

 

 

 

 

RGN_AND

 

 

 

 

两个区域的交集

 

 

 

 

RGN_COPY

 

 

 

 

两个区域的第一个区域作为新的区域

 

 

 

 

RGN_DIFF

 

 

 

 

第一个区域的部分减去两个区域的交集

 

 

 

 

RGN_OR

 

 

 

 

两个区域的并集

 

 

 

 

RGN_XOR

 

 

 

 

两个区域的并集减去两个区域的交集

 

 

 

 

下面图形显示了5种使用CombineRgn来操作正方形和圆合并的可能结果:

 

比较区域

应用程序通过调用EqualRgn来比较两个区域来判断他们是否一样。EqualRgn认为如果两个区域在形状和大小上相等就认为他们相等。

填充区域

应用程序可以通过调用FillRgn并提供一个刷子来填充某区域的内部。当程序调用FillRgn时,系统使用特定的设备内容的当前填充模式刷子来填充区域。有良种填充模式:交替的和缠绕的。程序能调用SetPolyFillMode函数来设置一个设备内容的填充模式。应用程序调用GetPolyFillMode函数来得到设备内容的当前填充模式。

下图描述了良种填充模式,一种使用交替模式,另一种使用缠绕模式。

 

 

交替模式

在交替模式下,为了判断那些像素需要高亮,执行下面的测试:

1.在区域内部选择一个像素

2.延正X轴方向画一条虚设的光线,从像素开始到无穷远

3.每次光线和边界线相交的,增加一个记数值

系统高亮那些记数值是奇数的像素。

 

 

 

 

 

缠绕模式

为了判断缠绕模式下那些像素需要高亮,需要执行下面测试:

1.在画每个边界线的时候判断方向

2.在区域的内部选择一个像素

3.画一条光线,延正X方向,从像素点到无穷远

4.每次光线与边界线在正Y部分正交的时候,增加记数值,如果在Y负方向,减记数值。

所有非0的像素被高亮。

 

AA

A

B

 

在交替模式下:

A发出的光线经过了三条边界,那么记数值为3,B经过了两条边界,记数值为2,所以A所在的区域为高亮,B所在的区域不是。

 

 

 

 

 

区域着色

应用程序使用当前选进设备内容的刷子来填充区域的内部,刷子通过PaintRgn函数来选择的,这个函数使用当前的多边形填充模式(交替和缠绕)。

 

 

 

 

 

反转区域

程序通过调用InvertRgn函数来使区域的颜色反色。在单色的显示设备上,InvertRgn是白色像素变黑,黑色像素变白。在彩色屏幕上,这个反转依赖于产生屏幕颜色的技术类型。

 

 

 

 

 

为区域增加框架

应用程序可以调用FrameRgn函数来为区域周围画一个边界,并指定边界的宽度和刷子的模式。

 

 

 

 

 

得到区域的矩形范围

应用程序通过调用GetRgnBox函数来得到区域边界矩形的大小。如果区域是规则的,GetRgnBox返回区域的大小;如果区域是椭圆形,函数返回包围在椭圆周围的最小矩形的大小,长边的长度和椭圆的主轴相等,短边和椭圆的次轴相同。如果区域是多边形,GetRgnBox返回最小的包围多边形的矩形。

 

 

 

 

 

移动区域

应用程序调用OffsetRgn函数来移动区域。给定的延X和Y轴防线的偏移决定了左右上下移动的逻辑单位。

 

 

 

 

 

点击测试区域

应用在区域上执行点击测试来判断当前光标位置的坐标。然后传递这些坐标和区域的HANDLE给PtInRegion函数。光标的坐标可以由多种鼠标操作得到,例如WM_LBUTTONDOWN、WM_LBUTTONUP、WM_RBUTTONDOWN和WM_RBUTTONUP。PtInRegion函数的返回值指出当前光标位置是否在给定的区域内。

 

 

 

 

 

使用区域来裁减输出

本节包含一个简单的例子,介绍你怎么样使用区域是用户可以定义这样输出客户区域的一部分。用作这个目的的区域被叫做裁减区域。

本节的例子,用户可以通过程序来抓获整个桌面为一位图,但能保存图象的一部分作为BMP文件。

通过单击Define Clip Region菜单,用户能够通过单击鼠标左键并拖动鼠标来选择一个裁减区域。当用户拖动鼠标的时候,应用程序画一个相对于新裁减区域的矩形。

通过单击Clip菜单,用户能够重画指定矩形边界内的图像独立部分。

本节提供下面的主题:

定义裁减区域

当用户单击Define Clip Region时,系统产生一个WM_COMMAND消息。该消息的wParam参数包含了程序自定义常量IDM_DEFINE,这指出用户选择了菜单,程序通过处理该输出并设置布尔变量fDefineRegion,如下面的代码:

case WM_COMMAND:

 

 

 

 

    switch (wParam)

 

 

 

 

    {

 

 

 

 

 

 

 

 

 

        case IDM_DEFINE:

 

 

 

 

            fDefineRegion = TRUE;

 

 

 

 

            break;

 

 

 

 

在Define Clipping Region之后,用户开始通过鼠标的单击和拖动来画矩形。

 

 

 

 

当用户按下鼠标左键时,系统产生WM_LBUTTONDOWN消息,lParam参数包含了光标的位置,它对应于裁减区域矩形的左上角。程序处理WM_LBUTTONDOWN消息如下:

 

 

 

 

// These variables are required for clipping. 
static POINT ptUpperLeft; 
static POINT ptLowerRight; 
static POINT aptRect[5]; 
static POINT ptTmp; 
static POINTS ptsTmp; 
static BOOL fDefineRegion; 
static BOOL fRegionExists; 
static HRGN hrgn; 
static RECT rctTmp; 
int i; 
 
switch (message) 
{ 
    case WM_LBUTTONDOWN: 
        if (fDefineRegion) 
        { 
 
        // Retrieve the new upper left corner. 
 
            ptsTmp = MAKEPOINTS(lParam); 
            ptUpperLeft.x = (LONG) ptsTmp.x; 
            ptUpperLeft.y = (LONG) ptsTmp.y; 
        } 
 
        if (fRegionExists) 
        { 
 
            // Erase the previous rectangle. 
 
            hdc = GetDC(hwnd); 
            SetROP2(hdc, R2_NOTXORPEN); 
 
            if (!Polyline(hdc, (CONST POINT *) aptRect, 5)) 
                errhandler("Polyline Failed", hwnd); 
            ReleaseDC(hwnd, hdc); 
 
            // Clear the rectangle coordinates. 
 
            for (i = 0; i < 4; i++) 
            { 
                aptRect[i].x = 0; 
                aptRect[i].y = 0; 
            } 
 
            // Clear the temporary point structure. 
 
            ptTmp.x = 0; 
            ptTmp.y = 0; 
 
            // Clear the lower right coordinates. 
 
            ptLowerRight.x = 0; 
            ptLowerRight.y = 0; 
 
            // Reset the flag. 
 
            fRegionExists = FALSE; 
            fDefineRegion = TRUE; 
 
            // Retrieve the new upper left corner. 
 
            ptsTmp = MAKEPOINTS(lParam); 
            ptUpperLeft.x = (LONG) ptsTmp.x; 
            ptUpperLeft.y = (LONG) ptsTmp.y; 
        } 
    break; 
}

当用户拖动鼠标的时候,系统产生WM_MOUSEMOVE消息并存储新的光标位置到lParam参数中。每次应用程序接收到WM_MOUSEMOVE消息时,它都删除先前的矩形(如果存在的话)并通过PolyLine函数来画一个新的区域,以矩形的四个角的坐标,程序执行下面的工作。

// These variables are required for clipping. 
 
static POINT ptUpperLeft; 
static POINT ptLowerRight; 
static POINT aptRect[5]; 
static POINT ptTmp; 
static POINTS ptsTmp; 
static BOOL fDefineRegion; 
static BOOL fRegionExists; 
static HRGN hrgn; 
static RECT rctTmp; 
int i; 
 
switch (message) 
{ 
 
    case WM_MOUSEMOVE: 
 
    if (wParam & MK_LBUTTON && fDefineRegion) 
    { 
 
        // Get a window DC. 
 
        hdc = GetDC(hwnd); 
 
        if (!SetROP2(hdc, R2_NOTXORPEN)) 
            errhandler("SetROP2 Failed", hwnd); 
 
        // If previous mouse movement occurred, store the original 
        // lower right corner coordinates in a temporary structure. 
       //如果先前鼠标移动了,原来较小的右下角坐标到ptLowerRight
        if (ptLowerRight.x) 
        { 
            ptTmp.x = ptLowerRight.x; 
            ptTmp.y = ptLowerRight.y; 
        } 
 
        // Get the new coordinates of the clipping region's lower 
        // right corner. 
        //取得新的右下角的坐标
        ptsTmp = MAKEPOINTS(lParam); 
        ptLowerRight.x = (LONG) ptsTmp.x; 
        ptLowerRight.y = (LONG) ptsTmp.y; 
 
        // If previous mouse movement occurred, erase the original 
        // rectangle. 
        //如果鼠标已经移动了,删除原来的矩形
        if (ptTmp.x) 
        { 
            aptRect[0].x = ptUpperLeft.x; 
            aptRect[0].y = ptUpperLeft.y; 
            aptRect[1].x = ptTmp.x; 
            aptRect[1].y = ptUpperLeft.y; 
            aptRect[2].x = ptTmp.x; 
            aptRect[2].y = ptTmp.y; 
            aptRect[3].x = ptUpperLeft.x; 
            aptRect[3].y = ptTmp.y; 
            aptRect[4].x = aptRect[0].x; 
            aptRect[4].y = aptRect[0].y; 
 
            if (!Polyline(hdc, (CONST POINT *) aptRect, 5)) 
                errhandler("Polyline Failed", hwnd); 
        } 
 
        aptRect[0].x = ptUpperLeft.x; 
        aptRect[0].y = ptUpperLeft.y; 
        aptRect[1].x = ptLowerRight.x; 
        aptRect[1].y = ptUpperLeft.y; 
        aptRect[2].x = ptLowerRight.x; 
        aptRect[2].y = ptLowerRight.y; 
        aptRect[3].x = ptUpperLeft.x; 
        aptRect[3].y = ptLowerRight.y; 
        aptRect[4].x = aptRect[0].x; 
        aptRect[4].y = aptRect[0].y; 
 
        if (!Polyline(hdc, (CONST POINT *) aptRect, 5)) 
             errhandler("Polyline Failed", hwnd); 
 
        ReleaseDC(hwnd, hdc); 
    } 
    break; 

 

 

 

 

 

裁减输出

在用户选择Clip菜单之后,应用程序使用用户创建的裁减区域的矩形坐标,在定义裁减区域后,将它选进程序的设备内容中,程序重画位图,程序执行下面的工作:

// These variables are required for clipping. 
 
static POINT ptUpperLeft; 
static POINT ptLowerRight; 
static POINT aptRect[5]; 
static POINT ptTmp; 
static POINTS ptsTmp; 
static BOOL fDefineRegion; 
static BOOL fRegionExists; 
static HRGN hrgn; 
static RECT rctTmp; 
int i; 
 
case WM_COMMAND: 
    switch (wParam) 
    { 
 
    case IDM_CLIP: 
 
    hdc = GetDC(hwnd); 
 
    // Retrieve the application's client rectangle and paint 
// with the default (white) brush. 
//得到程序的客户区矩形,并用白刷子填充
    GetClientRect(hwnd, &rctTmp); 
    FillRect(hdc, &rctTmp, GetStockObject(WHITE_BRUSH)); 
 
// Use the rect coordinates to define a clipping region. 
//使用rect的坐标来定义一个裁减区域
    hrgn = CreateRectRgn(aptRect[0].x, aptRect[0].y, 
        aptRect[2].x, aptRect[2].y); 
    SelectClipRgn(hdc, hrgn); 
 
// Transfer (draw) the bitmap into the clipped rectangle. 
//将位图画到裁减矩形中
    BitBlt(hdc, 
       0, 0, 
       bmp.bmWidth, bmp.bmHeight, 
       hdcCompatible, 
       0, 0, 
       SRCCOPY); 
 
    ReleaseDC(hwnd, hdc); 
    break; 
    }

裁剪是在一个应用程序的窗口中限制输出区域或路径的过程,下面讨论裁剪.

 

 

关于裁剪

 

 

应用程序以各种方法来使用剪切。WORD程序和电子表格程序裁剪键盘输入来避免其出现在页或表格的边界;CAD和绘画程序裁剪图形输出是为了避免在图画的边界被覆盖。

 

 

一个裁剪区域是一个直线或曲线边的区域;一个裁剪路径是边可以为直线、曲线或他们的组合的区域。更多的信息参见区域和路径。

 

 

 

 

 

区域

 

 

区域可以是一个矩形、多边形、椭圆形(或他们两个或三个的组合的图形),他们能够被填充、着色、被转向、也可以加边框并且被用来执行点击测试(为光标位置测试)。

 

 

关于区域

 

 

下面的三种已经被填充和加边框了。

 

 

 

 

 

 

 

 

路径

 

 

路径是一到多个图像(图形),他们可以被填充,有轮廓,或者两者都有。路径用在绘画和着色的应用程序中。CAD应用程序使用路径来创建独特的裁减区域,画出不规则的图形外观,然后填充这些不规则的图形内部。一个不规则的图形是由Bzier曲线和直线组成的图形。(规则的图形包括椭圆、圆、矩形、多边形)

 

 

关于路径

 

 

路径是与DC相关联的对象之一;然而不像默认对象(笔、刷子、字体)是任何新的DC的一部分,没有默认的路径对象。

 

 

 

 

 

裁减区域

 

 

裁减区域是应用程序可以选进DC的图形对象之一。通常是一个矩形。如果没有提供裁减区域的话,有些DC提供一个预定义的或默认的裁减区域。例如,如果你从BeginPaint函数获得了一个DC的HANDLE,DC包含一个相对应于需要被重绘的无效矩形的预定义矩形的裁减区域。然而,当你通过GetDC而提供NULL作为hWnd参数时获得的设备HANDLE,或者通过CreateDC函数创建的,DC并不包含默认的裁减区域。更多的关于BeginPaint返回的DC的内容参见Painting and Drawing;而CreateDC和GetDC返回的DC信息参见Device Contexts.

 

 

应用程序可以在裁减区域上执行各种操作。许多操作需要一个标示区域的HANDLE,而有些不需要。例如,应用程序可以直接在一个DC的裁减区域上执行下面操作:

 

 

ü         通过传递相应的线、弧、位图、文本或填充图形给PtVisible函数来判断图形输出是否在区域的边界。

 

 

ü         通过调用RectVisible函数来判断区域和客户区的部分存在交集。

 

 

ü         通过传递给OffsetClipRgn函数一个指定的偏移来移动一个存在的区域

 

 

ü         通过调用ExcludeClipRect函数来从当前裁减区中去除客户区的某矩形部分。

 

 

ü         调用IntersectClipRect函数来合并当前裁减区域和客户区的矩形区域

 

 

在获得一个裁减区域的HANDLE之后,应用程序能执行通常区域进行的任何操作,如下:

 

 

ü         通过调用CombineRgn来合并当前的裁减区域和第二个区域

 

 

ü         调用EqualRgn来比较当前裁减区域和第二个区域

 

 

ü         通过调用PtInRegion来判断一个点是否在当前裁减区域的副本内部

 

 

 

 

 

裁减路径

 

 

和裁减区域一样,裁减路径是另一个可被应用程序选进DC中的图形对象。和裁减区域不同的是,裁减路径一直由应用成天许来创建,通常用来裁减一到多个不规则图形。例如,一个应用程序使用线和曲线得到一个字符串文本中字符路径。

 

 

为了创建裁减路径,首先创建一个路径来描述不规则图形是必要的。路径通过在BeginPath和EndPath之间调用适当的GDI函数来创建,这个函数集合被叫做路径支架。更多的关于路径和路径支架的信息参见Paths.

 

 

在路径被创建之后,能通过SelectClipPath函数来将其转换为裁减路径,标示一个DC并指定使用模式。使用模式决定了系统怎么样合并原来的裁减区域和新的裁减路径。下面是使用模式的介绍:

 

 

模式

 

 

描述

 

 

RGN_AND

 

 

裁减路径包含当前路径和设备参见区域的交集(重叠部分)

 

 

RGN_COPY

 

 

裁减路径是当前路径

 

 

RGN_DIFF

 

 

裁减路径是设备内容裁减区域减去当前路径与其的交集

 

 

RGN_OR

 

 

包含两者,是并集

 

 

RGN_XOR

 

 

两者的并去掉交集

 

 

 

 

 

使用裁减

 

 

本节包含一个例子介绍怎么样生成一个由字符串组成的裁减路径。例子创建一个逻辑字体并使用它在一个裁减路径中画一个字符串,然后用水平和垂直线来填充路径。

 

 

// DoClipPat - Draws a clip path using the specified string 

 

// Return value - TRUE if successful; FALSE otherwise 

 

// lplf - address of a LOGFONT structure that defines the font to 

 

//        use to draw the clip path 

 

// lpsz - address of a string to use for the clip path 

 

 

 

BOOL DoClipPath(LPLOGFONT lplf, LPSTR lpsz) 

 

{ 

 

    LOGFONT lf;           // logical font structure 

 

    HFONT hfont;          // new logical font handle 

 

    HFONT hfontOld;       // original logical font handle 

 

    HDC hdc;              // display DC handle 

 

    int nXStart, nYStart; // drawing coordinates 

 

    RECT rc;              // rectangle structure for painting window 

 

    SIZE sz;              // size structure that receives text extents 

 

    int nStrLen;          // length of the string 

 

    int i;                // loop counter 

 

        HRESULT hr;

 

        size_t * pcch;

 

 

 

    // Retrieve a cached DC for the window. 

 

 

 

    hdc = GetDC(hwnd); 

 

 

 

    // Erase the current window contents. 

 

 

 

    GetClientRect(hwnd, &rc); 

 

    FillRect(hdc, &rc, GetStockObject(WHITE_BRUSH)); 

 

 

 

    // Use the specified font to create a logical font and select it 

 

    // into the DC. 

 

 

 

    hfont = CreateFontIndirect(lplf); 

 

    if (hfont == NULL) 

 

        return FALSE; 

 

    hfontOld = SelectObject(hdc, hfont); 

 

 

 

    // Create a clip path. 

 

 

 

        hr = StringCchLength(lpsz, STRSAFE_MAX_CCH, pcch);

 

        if (FAILED(hr))

 

        {

 

        // TODO: write error handler

 

        }

 

        nStrLen = *pcch 

 

    BeginPath(hdc); 

 

        TextOut(hdc, nXStart, nYStart, lpsz, nStrLen); 

 

    EndPath(hdc); 

 

    SelectClipPath(hdc, RGN_DIFF); 

 

 

 

    // Retrieve the dimensions of the rectangle surrounding 

 

    // the text. 

 

 

 

    GetTextExtentPoint32(hdc, lpsz, nStrLen, &sz); 

 

 

 

    // Draw horizontal lines through the clip path. 

 

 

 

    for (i = nYStart + 1; i < (nYStart + sz.cy); i += 3) 

 

    { 

 

       MoveToEx(hdc, nXStart, i, (LPPOINT) NULL); 

 

       LineTo(hdc, (nXStart + sz.cx), i); 

 

    } 

 

 

 

    // Draw vertical lines through the clip path. 

 

 

 

    for (i = nXStart + 1; i < (nXStart + sz.cx); i += 3)

 

    { 

 

       MoveToEx(hdc, i, nYStart, (LPPOINT) NULL); 

 

       LineTo(hdc, i, (nYStart + sz.cy)); 

 

    } 

 

 

 

    // Select the original font into the DC and release the DC. 

 

 

 

    SelectObject(hdc, hfontOld); 

 

    DeleteObject(hfont); 

 

    ReleaseDC(hwnd, hdc); 

 

 

 

    return TRUE; 

 

}
http://blog.csdn.net/windcsn/archive/2005/09/29/492436.aspx


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的用C++和OpenGL实现Sutherland-Hodgman裁剪算法的代码示例: ```c++ #include <iostream> #include <vector> #include <GL/glut.h> using namespace std; struct Point { float x, y; Point(float x = 0, float y = 0) : x(x), y(y) {} }; vector<Point> clipPolygon(vector<Point> polygon, Point p1, Point p2) { vector<Point> outputList; for (int i = 0; i < polygon.size(); i++) { Point P = polygon[i]; Point Q = polygon[(i+1)%polygon.size()]; float t1 = (p1.x - P.x) * (p2.y - P.y) - (p1.y - P.y) * (p2.x - P.x); float t2 = (p1.x - Q.x) * (p2.y - Q.y) - (p1.y - Q.y) * (p2.x - Q.x); if (t1 >= 0) outputList.push_back(P); if (t1 * t2 < 0) { Point intersect; intersect.x = (Q.x - P.x) * t1 / (t1 - t2) + P.x; intersect.y = (Q.y - P.y) * t1 / (t1 - t2) + P.y; outputList.push_back(intersect); } } return outputList; } void display() { glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); glColor3f(0, 0, 0); glLineWidth(2); // 原多边形 vector<Point> polygon; polygon.push_back(Point(0.2, 0.2)); polygon.push_back(Point(0.5, 0.6)); polygon.push_back(Point(0.8, 0.2)); polygon.push_back(Point(0.6, 0.1)); polygon.push_back(Point(0.4, 0.1)); glBegin(GL_LINE_LOOP); for (int i = 0; i < polygon.size(); i++) glVertex2f(polygon[i].x, polygon[i].y); glEnd(); // 裁剪线段 Point p1(0.3, 0.1); Point p2(0.7, 0.7); glBegin(GL_LINES); glVertex2f(p1.x, p1.y); glVertex2f(p2.x, p2.y); glEnd(); // 裁剪后的多边形 vector<Point> clippedPolygon = polygon; for (int i = 0; i < clippedPolygon.size(); i++) clippedPolygon = clipPolygon(clippedPolygon, p1, p2); glBegin(GL_LINE_LOOP); for (int i = 0; i < clippedPolygon.size(); i++) glVertex2f(clippedPolygon[i].x, clippedPolygon[i].y); glEnd(); glFlush(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(400, 400); glutCreateWindow("Sutherland-Hodgman Clipping"); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 该示例使用OpenGL绘制多边形和线段,并使用Sutherland-Hodgman裁剪算法将多边形剪切为指定的线段。该算法的实现是通过迭代每个边缘并计算与剪切线段的交点来完成的。计算交点后,将其添加到输出列表中以生成剪切后的多边形。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值