API GradientFill не работает должным образом

ВВЕДЕНИЕ И СООТВЕТСТВУЮЩАЯ ИНФОРМАЦИЯ:

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

Я хочу сделать это следующим образом:

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

Для этого я создал переменную RECT в обработчике WM_PAINT, которая позиционирует градиент в том месте, где должен быть статический элемент управления.

Кроме того, я стараюсь использовать двойную буферизацию, чтобы избежать мерцания (я также обработал WM_ERASEBKGND, удалил флаги CS_VREDRAW и CS_HREDRAW из класса окна).

Я также обработал сообщение WM_SIZE, чтобы сделать окно недействительным и правильно переместить статический элемент управления.

В моем обработчике WM_CTLCOLORSTATIC я вернул NULL_BRUSH.

Чтобы проиллюстрировать это, я сделал демонстрационное приложение с помощью мастера приложений в Visual Studio.

Я работаю на Windows XP, используя чистый Win32 API и C++.

Ниже я представлю измененный код для WM_PAINT и фрагменты для других обработчиков, перечисленных выше:

    case WM_CREATE:
         {
             // get rectangle dimensions of the main window

             RECT rec;
             GetClientRect( hWnd, &rec );

             /******* main window's static control ******/

             HWND StaticControl = CreateWindowEx( 0, L"Static", L"", 
                                     WS_VISIBLE | WS_CHILD | SS_NOTIFY,
                                     ( 3 * ( rec.right - rec.left ) / 4 - 340 ) / 3,
                                     120 + ( rec.bottom - rec.top - 450 ) / 3, 
                                     150, 150, hWnd, (HMENU)4000, hInst, 0);
         }
         return (LRESULT)0;

    case WM_SIZE:
        {
             RECT rec; // main window's client rectangle

             GetClientRect( hWnd, &rec ); 

             SetWindowPos( GetDlgItem( hWnd, 4000 ),
                           NULL,
                           ( 3  * ( rec.right - rec.left ) / 4 - 340 ) / 3, 
                           120 + ( rec.bottom - rec.top - 450 ) / 3, 150, 150,
                           SWP_NOZORDER );

            InvalidateRect( hWnd, NULL, FALSE); 
        }
        return (LRESULT)0;

    case WM_ERASEBKGND:
        return (LRESULT)1;

    case WM_CTLCOLORSTATIC:
        return (LRESULT)( (HBRUSH)GetStockObject(NULL_BRUSH) );

    case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code here...

            RECT r; // rectangle for main window's client area

            GetClientRect( hWnd, &r);

            HDC MemDC = CreateCompatibleDC(hdc); // back buffer

            // compatible bitmap for MemDC

            HBITMAP bmp = CreateCompatibleBitmap( hdc,
                                                  r.right - r.left, 
                                                  r.bottom - r.top ),
                    oldBmp = (HBITMAP)SelectObject( MemDC, bmp ); // needed for cleanup

            /***** draw a reference header at the top of the window *******/

            // position it properly at the top

            RECT rect;

            rect.left = r.left;
            rect.top = r.top;
            rect.right = r.right;
            rect.bottom = 120;

            FillRect( MemDC, &rect, (HBRUSH)GetStockObject(LTGRAY_BRUSH) );

            /**** main window's gradient background *******/

            //============ down triangle =========//

            TRIVERTEX vertex[3];

            vertex[0].x     = r.right;
            vertex[0].y     = r.bottom - r.top;
            vertex[0].Red   = 0xDB00;
            vertex[0].Green = 0xE500;
            vertex[0].Blue  = 0xF100;
            vertex[0].Alpha = 0x0000;

            vertex[1].x     = r.left;
            vertex[1].y     = r.bottom - r.top;
            vertex[1].Red   = 0x9500;
            vertex[1].Green = 0xB300;
            vertex[1].Blue  = 0xD700;
            vertex[1].Alpha = 0x0000;

            vertex[2].x     = r.left;
            vertex[2].y     = r.top + 120; 
            vertex[2].Red   = 0xDB00;
            vertex[2].Green = 0xE500;
            vertex[2].Blue  = 0xF100;
            vertex[2].Alpha = 0x0000;

            // Create a GRADIENT_TRIANGLE structure that
            // references the TRIVERTEX vertices.

            GRADIENT_TRIANGLE gTriangle;

            gTriangle.Vertex1 = 0;
            gTriangle.Vertex2 = 1;
            gTriangle.Vertex3 = 2;

            // Draw a shaded triangle.

            GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);

            //=============== upper triangle =================//

            TRIVERTEX vertex1[3];

            vertex1[0].x     = r.right;
            vertex1[0].y     = r.bottom - r.top;
            vertex1[0].Red   = 0xDB00;
            vertex1[0].Green = 0xE500;
            vertex1[0].Blue  = 0xF100;
            vertex1[0].Alpha = 0x0000;

            vertex1[1].x     = r.right;
            vertex1[1].y     = r.top + 120;
            vertex1[1].Red   = 0x9500;
            vertex1[1].Green = 0xB300;
            vertex1[1].Blue  = 0xD700;
            vertex1[1].Alpha = 0x0000;

            vertex1[2].x     = r.left;
            vertex1[2].y     = r.top + 120; 
            vertex1[2].Red   = 0xDB00;
            vertex1[2].Green = 0xE500;
            vertex1[2].Blue  = 0xF100;
            vertex1[2].Alpha = 0x0000;

            // Draw a shaded triangle.

            GradientFill( MemDC, vertex1, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);

            //**** draw background of the static control ****//

            //position it properly 

            rect.left = ( 3  * ( r.right - r.left ) / 4 - 340 ) / 3;
            rect.top = 120 + ( r.bottom - r.top - 450 ) / 3;
            rect.right = 150 + ( 3  * ( r.right - r.left ) / 4 - 340 ) / 3;
            rect.bottom = 270 + ( r.bottom - r.top - 450 ) / 3; // this one fails!!!

            //FillRect( MemDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH) );

            // vertexes for static's gradient color

            //================= top rectangle =====================//

            TRIVERTEX vertexS[2], vertex1S[2] ;

            vertexS[0].x     = rect.left;
            vertexS[0].y     = rect.top;
            vertexS[0].Red   = 0x9500;
            vertexS[0].Green = 0xB300;
            vertexS[0].Blue  = 0xD700;
            vertexS[0].Alpha = 0x0000;

            vertexS[1].x     = rect.right;
            vertexS[1].y     = ( rect.bottom - rect.top ) / 2; 
            vertexS[1].Red   = 0x4F00;
            vertexS[1].Green = 0x8B00;
            vertexS[1].Blue  = 0xBD00;
            vertexS[1].Alpha = 0x0000;

            //================== bottom rectangle ====================//

            vertex1S[0].x     = rect.left;
            vertex1S[0].y     = ( rect.bottom - rect.top ) / 2; 
            vertex1S[0].Red   = 0x4F00;
            vertex1S[0].Green = 0x8B00;
            vertex1S[0].Blue  = 0xBD00;
            vertex1S[0].Alpha = 0x0000;

            vertex1S[1].x     = rect.right;
            vertex1S[1].y     = rect.bottom;
            vertex1S[1].Red   = 0x9500;
            vertex1S[1].Green = 0xB300;
            vertex1S[1].Blue  = 0xD700;
            vertex1S[1].Alpha = 0x0000;

            // Create a GRADIENT_RECT structure that 
            // references the TRIVERTEX vertices.

            GRADIENT_RECT gRect;

            gRect.UpperLeft  = 0;
            gRect.LowerRight = 1;

            // Draw a shaded rectangle. 

            GradientFill( MemDC, vertexS, 2, &gRect, 1, GRADIENT_FILL_RECT_V );
            GradientFill( MemDC, vertex1S, 2, &gRect, 1, GRADIENT_FILL_RECT_V );

            /****** draw back buffer on the screen DC *****************/

            BitBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top,
                    MemDC, 0, 0, SRCCOPY );

            /************** cleanup *******************/

            SelectObject( MemDC, oldBmp );

            DeleteObject(bmp); // compatible bitmap for MemDC

            DeleteDC(MemDC);

            EndPaint(hWnd, &ps);
        }
        return (LRESULT)0;

ПРОБЛЕМА:

Когда я использую GradientFill API в обработчике WM_PAINT, я получаю на экране прямоугольник большего размера, чем должен быть.

Нижеприведенное изображение иллюстрирует этот результат:

введите здесь описание изображения

Если я попытаюсь заполнить тот же прямоугольник какой-нибудь сплошной кистью, все будет нормально.

Нижеприведенное изображение иллюстрирует этот результат:

введите здесь описание изображения

МОИ УСИЛИЯ ПО РЕШЕНИЮ ПРОБЛЕМЫ:

Я поставил точки останова в местах, где задаются координаты прямоугольника, и не заметил никаких проблем.

Кроме того, GradientFill возвращает TRUE, поэтому ошибка не возникает.

ВОПРОС:

Как это исправить?

Заранее спасибо.

С уважением.


person AlwaysLearningNewStuff    schedule 11.11.2013    source источник


Ответы (1)


Я думаю, что ваши вычисления для вершин просто неверны:

        vertexS[1].y     = ( rect.bottom - rect.top ) / 2; 
        /* .... */
        vertex1S[0].y     = ( rect.bottom - rect.top ) / 2;

Должны ли они быть rect.top + ( rect.bottom - rect.top ) / 2 или что-то подобное? Или даже просто rect.bottom и rect.top соответственно?

Это зависит от того, чего вы пытаетесь достичь, но в любом случае я думаю, что в настоящее время это не то, чего вы хотите. Вам нужно будет использовать rect.bottom и rect.top, чтобы получить ту же форму, которую рисует ваш закомментированный FillRect.

person Jonathan Potter    schedule 11.11.2013
comment
Мистер Поттер, я пытаюсь создать градиент с более светлым цветом вверху и внизу прямоугольника и более темным в середине. Я решил заполнить верхнюю половину прямоугольника градиентной кистью, которая светлее вверху и ярче внизу. Затем нижняя половина прямоугольника будет заполнена кистью, которая светлее внизу и темнее вверху. Заполнив прямоугольник этими двумя кистями, я бы добился эффекта одной градиентной кисти, светлой вверху и внизу и темной посередине. Ваше решение работает, так что +1 от меня. Спасибо. - person AlwaysLearningNewStuff; 11.11.2013