Статьи

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

Впервые я столкнулся с градиентной заливкой еще на заре своей молодости :-), инсталлируя Windows 3.1. С тех пор сине-черный переход фонового цвета в инсталляционных программах стал стандартом де-факто. Многие стали применять плавный переход из одного цвета в другой в своих программах, всячески колдуя с палитрами. Сейчас эта мода постепенно сходит на нет, и все же...
Использование градиентной заливки может существенно сократить объем программы, исключив тяжелые картинки, практически без потери привлекательности.

В данной статье описывается создание модуля, который может быть использован в любой программе. Используя API-функции, мы можем сократить объем кодов в программе. Поэтому начнем, пожалуй, с них.
NB! Все API-функции, используемые в модуле, объявляются как Private. Это связано с тем, что все они используются внутри процедур самого модуля. Однако, если Вы используете их в своей программе еще и вне данного модуля - никто Вам не запрещает объявить их как Public.

Вся сложность при работе с цветом заключается в преобразовании цветов. Т.е. получаем мы цвет в формате OLE_COLOR, а для преобразования внутри процедуры нам необходима его раскладка на составляющие (красный, зеленый, синий). Для этой ситуации очень хорошо подходит функция OleTranslateColor.
Private Declare Function OleTranslateColor Lib "OLEPRO32.DLL" _
    (ByVal OLE_COLOR As Long, _
    ByVal HPALETTE As Long, _
    pccolorref As Long) _
    As Long

Кроме этого нам потребуются еще 3 API-функции для заливки части прямоугольника, это:
Private Declare Function FillRect Lib "user32" _
    (ByVal hDC As Long, _
    lpRect As RECT, _
    ByVal hBrush As Long) _
    As Long
Private Declare Function CreateSolidBrush Lib "gdi32" _
    (ByVal crColor As Long) _
    As Long
Private Declare Function DeleteObject Lib "gdi32" _
    (ByVal hObject As Long) _
    As Long

Как Вы заметили, функция FillRect для одного из своих аргументов требует тип RECT, который мы и объявим в разделе деклараций перед всеми вышеуказанными функциями:
Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Прежде, чем мы перейдем написанию самих процедур, еще раз внимательно взглянем на функцию FillRect. Первым аргументом ее является hDC, а это значит, что мы сможем применить градиентную заливку только к объектам, которые имеют это свойство. К сожалению, таких немного: это сама форма, элемент PictureBox и UserControl.
NB! С помощью UserControl, Вы сами сможете создавать различные ЭУ (кнопки, списки, радиокнопки и т.п.), обладающих градиентной заливкой, включив создаваемый модуль в их состав.

Итак, начнем с простого, создадим процедуру, рисующую градиентную заливку от одного цвета к другому. В качестве параметров процедуры определим объект приложения (мы помним что он может быть как формой, так и картинкой), стартовый и конечный цвета и тип заливки (вертикальный или горизонтальный). Для последнего аргумента, в разделе деклараций объявим нумерованную константу:
Public Enum constOrientation
    Horizontal = 0
    Vertical = 1
End Enum

Там же объявим два массива, для хранения, разложенного на составляющие, цвета.
Private RGBStart(1 To 3) As Integer 'массивы цвета
Private RGBEnd(1 To 3) As Integer 'массивы цвета

Вначале, с помощью функции OleTranslateColor, получим раскладку на составляющие для начального и конечного цветов:
Public Sub DrawGradient(ObjDestGradient As Object, _
    ColorStart As OLE_COLOR, _
    ColorEnd As OLE_COLOR, _
    eOrientation As constOrientation)
    
Dim lColor As Long
Dim lHeight As Long, lWidth As Long
Dim lYStep As Long, lXStep As Long
Dim i As Long
Dim iRGB(1 To 3) As Integer
Dim dR(1 To 3) As Double
Dim rct As RECT
Dim hBr As Long, lhDC As Long

OleTranslateColor ColorEnd, 0, lColor
RGBEnd(1) = lColor And &HFF&
RGBEnd(2) = ((lColor And &HFF00&) \ &H100)
RGBEnd(3) = ((lColor And &HFF0000) \ &H10000)

OleTranslateColor ColorStart, 0, lColor
RGBStart(1) = lColor And &HFF&
RGBStart(2) = ((lColor And &HFF00&) \ &H100)
RGBStart(3) = ((lColor And &HFF0000) \ &H10000)

Далее очищаем объект от предыдущей графики и переводим его размеры в пикселы:
ObjDestGradient.Cls 

lhDC = ObjDestGradient.hDC
lHeight = ObjDestGradient.Height \ Screen.TwipsPerPixelY 
lWidth = ObjDestGradient.Width \ Screen.TwipsPerPixelX
rct.Right = lWidth
rct.Bottom = lHeight

Передаем, разложенные на составные части цвета внутренним переменным:
iRGB(1) = RGBStart(1)
iRGB(2) = RGBStart(2)
iRGB(3) = RGBStart(3)
dR(1) = RGBEnd(1) - RGBStart(1)
dR(2) = RGBEnd(2) - RGBStart(2)
dR(3) = RGBEnd(3) - RGBStart(3)

Ну а теперь займемся собственно рисованием. Для начала вычислим шаг изменения заливки. Это должно быть целое число и не меньше 1 пиксела
Select Case eOrientation
Case 0 
    lYStep = lHeight \ 255
    If (lYStep = 0) Then
        lYStep = 1
    End If

В цикле, по убывающей, рисуем градиент снизу вверх. Создаем кисть, склеивая промежуточный цвет в формат OLE_COLOR, делаем заливку выбранной части
For i = lHeight To 0 Step -lYStep
    rct.Top = rct.Bottom - lYStep
    hBr = CreateSolidBrush(iRGB(3) * &H10000 + iRGB(2) * &H100& + iRGB(1))
    FillRect lhDC, rct, hBr
    DeleteObject hBr
и в конце переписываем координаты для рисования следующего промежуточного цвета:
    rct.Bottom = rct.Top
    iRGB(1) = RGBStart(1) + dR(1) * (lHeight - i) / lHeight
    iRGB(2) = RGBStart(2) + dR(2) * (lHeight - i) / lHeight
    iRGB(3) = RGBStart(3) + dR(3) * (lHeight - i) / lHeight
Next

Если выбран вертикальный тип заливки градиентом, то все делается идентично, только применительно к ширине:
Case 1
    lXStep = lWidth \ 255
    If (lXStep = 0) Then
        lXStep = 1
    End If
    
    For i = lWidth To 0 Step -lXStep
        rct.Left = rct.Right - lXStep
        hBr = CreateSolidBrush(iRGB(3) * &H10000 + iRGB(2) * &H100& + iRGB(1))
        FillRect lhDC, rct, hBr
        DeleteObject hBr
        rct.Right = rct.Left
        iRGB(1) = RGBStart(1) + dR(1) * (lWidth - i) / lWidth
        iRGB(2) = RGBStart(2) + dR(2) * (lWidth - i) / lWidth
        iRGB(3) = RGBStart(3) + dR(3) * (lWidth - i) / lWidth
    Next
End Select

Вот, собственно говоря, и все. Перейдите на форму и примените данную процедуру к самой форме и двум PictureBox. Лучше всего заливку расположить в событии Form_Resise. Кроме того, чтобы избежать "моргания" цвета, необходимо установить AutoRedraw = True.
NB! Мы можем объявлять цвета любым из пяти доступных в бейсике способов: 
- как целое десятичное число (713060 - зеленый)
- как шестнацетиричное число (&H4F00FF - фиолетовый)
- как VB-константы (vbRed - красный)
- как функция RGB (RGB(100, 225, 10) - синий)
- как функция QBColor (QBColor(6) - желтый)

Ну вот, с двумя цветами разобрались. Используя те же самые приемы можно создать градиентную заливку, включающую три и более цветов. Ничего сверхсложного в этом нет и подробно расписывать в статье эту процедуру я не буду, она представлена в Примере. Хочу обратить внимание лишь на несколько ключевых моментов, которые облегчат Вам восприятие кодов:

Удачного использования!


Назад

Скачать пример

Hosted by uCoz