#E6E6E6 【VB 物件】FadeSmart 實現色彩漸層表單 作者:吳文成

[[img src=computer/FadeSmart.gif height=357 width=202 align=left]]  我們先來看看 ,由 FadeSmart 物件類別所繪製的三個不同呈現方式的漸層圖,分別是由上到下的色彩漸變、由右上到左下的色彩漸變,以及圓形發散的色彩漸變。

  在上一篇,介紹實現動態漸層圓圈的物件 FadeCircle 的時候 ,我同時也介紹了色彩學的幾個要點,尤其是色彩數值解析的理論( RGB 與 HSB ),在這個單元我要先來補充談談,在編程領域,色彩的表示法。

  在程式設計的一般表示裡,顏色的紅、綠、藍三分色的數值範圍都在 0 ∼ 255 之間
,各有 256(=162) 個可能數字。之所以定出這樣的數值範圍是為了要遷就十六進位的表示法 , 於是當 Red=255 的時候 ,我們也可以寫成 Red=FF( F 在十六進位代表 15 ,兩個 F 就表示 15*160+15*161=255 )。有寫網頁經驗的人一定知道,如果要將網頁背景顏色設為白色,那麼語法就是BgColor=#FFFFFF( 請注意有六個 FF , 我們可以將各兩個 FF 視為一個單位, 於是共有三個單位, 分別代表紅、綠、藍三個分色值 ), 在 HTML 網頁語言裡,色彩的表示法是用兩個位元組的十六進位來表示各分色,以剛剛的例子來說,白色就是 Red=FF、Green=FF、Blue=FF 的「組合」, 那麼黃色就是 #FFFF00(即 Red=255、Green=255、Blue=0), 這是網頁語言的表示法 。 在 Visual Basic 裡,黃色表示為 RGB(255,255,0) 的函數值,那這個值到底是多少呢?在微軟的視窗作業環境乃是用一個長整數來代表個別唯一的色彩值,例如黃色就是 255*2560+255*2561+0*2562=65535,顯然當三個分色都是 255 的時候 , 就是最大的色彩值 ,即 RGB(255,255,255)=16777215,這就是白色( 於是,在這樣的數值定義下,所有可能的顏色就有 16,777,216 =2563 個。 請注意,這種數值定義看起來是「客觀」的,但卻也是「人為」的,我們仍然可以定出不同的數值系統,該系統的色彩數可能不只是 2563 個 , 也就是說,即使是公認的數值理論,仍然是「相對」於人的定義系統!哪一天,如果人們可以對色彩做出更精緻的解析,那麼色彩的域度將不止於目前的數值範圍)

  知道了色彩的數值定義,如果給我們一個長整數的色彩值,把它換算成三分色的表示法應該也難不倒我們( 範例裡有算法 ),但是 RGB 轉換成 HSB 就「粉難」, 以後有空談到影像灰階化的演算法的時候 ,我再來談。我們將進入另一個問題,要如何將粉紅色( =RGB(255,128,255) )
漸層」到天藍色( =RGB(128,255,255) )( 即第一個圖所演示的)呢,究竟是色彩的什麼本質從前者改變到後者?這個答案是頗令人驚訝的,因為改變的本質是純紅、純綠與純藍!當粉紅色漸層到天藍色,其漸次改變的本質其實是純紅色從數值 255 變到 128(負改變)、 純綠色從數值 128 變到 255(正改變)、 純藍色從數值 255 變到 255(即不改變), 所以我在上一篇文章才如此驚嘆地說「色彩的最偉大解析就是十九世紀初的三原色說」。瞭解了色彩漸層的原理,下一個問題是怎麼去(均勻地)分佈兩個色彩之間的系列,例如在寬度 100 (像素)的區域裡,粉紅色漸層到天藍色的各分色都有不同的變化率,我們要算的就是這個變化率,我們稱之為漸層梯度,在這個例子裡,純紅色的漸層梯度就是 (128-255)/100=-1.27
負梯度)
, 純綠色的漸層梯度是 (255-128)/100=1.27(正梯度 ), 純藍色的漸層梯度是 (255-255)/100=0(零梯度)。求出梯度是為了比例值或是內插法的計算,算出從什麼位置畫線到什麼位置,然後以什麼顏色畫。

  第一個圖與第三個圖的漸層製作方式並不難理解,但是製作「斜向」的均勻漸層就需要花腦筋,例如第二個圖是從由右上均勻漸層到左下,它的內插法計算較為複雜,可能不同的程式設計者會有不同的演算法,我的算法是將整個區域切割成兩個斜向的三角形,它們各自分擔其中一個色彩主要系列(目的為了「均勻」漸層),這樣的規劃(即對於問題點的切割
會使得問題變得比較簡化與容易程式撰寫,我建議讀者可以先用自己的想法去試著寫演算法 ,然後再比較我的版本 。

  FadeCircle 與 FadeSmart 兩個物件 ,所要解決的問題並不同 , 前者需要變異度概念的介入(以增加色彩複雜性,與新色彩的創製),後者著重於梯度概念的運用 , 前者是動態的,後者是靜態的。我一樣將 FadeSmart 物件較為關鍵的部分原始碼,給列舉在下面,倘若你有更好的演算法,希望能夠不吝與我分享:

' 定義色彩的資料型態
Private Type ColorRGB
 Red As Integer
 Green As Integer
 Blue As Integer
End Type

' 定義色彩運算的資料型態
Private Type ColorSingle
 Red As Single
 Green As Single
 Blue As Single
End Type

' 此物件的繪圖方法,必須指定實現漸層的控制項
' FadeMode 是色彩漸層在方向分布上的呈現模式
' FirstColor 是第一個色彩值,為選擇性參數
' SecondColor 是第二個色彩值,為選擇性參數

Public Sub FadeShow(DestObject As Object, Optional FadeMode As FadeShowMode = -1, Optional FirstColor As FadeColor = -1, Optional SecondColor As FadeColor = -1)
 On Error GoTo errorLabel
 
 ' 賦值給所需的變數
 If FadeMode <> -1 Then m_FadeMode = FadeMode
 If FirstColor <> -1 Then m_FirstColor = FirstColor
 If SecondColor <> -1 Then m_SecondColor = SecondColor
 Set toObj = DestObject
 
 With toObj
  Dim c1 As ColorRGB, c2 As ColorRGB
  ' 定義色彩各分色的漸變梯度
  Dim slopeRGB As ColorSingle
  Dim mainSide As Long, singleSide As Long, i As Long
  ' 轉換長或寬的對應值(即運用內插法計算,或比例法)
  Dim transRate As Single, transRate2 As Single
  Dim Xo As Long, Yo As Long, Xd As Long, Yd As Long, MaxR As Long
  
  .ScaleMode = vbPixels
  .DrawWidth = 1
  .AutoRedraw = True
  .Cls
  
  c1 = GetRGB(m_FirstColor)
  c2 = GetRGB(m_SecondColor)
 
  Select Case m_FadeMode
  Case 1, 2  ' ↓、→
   ' 取得決定色彩分布的基底邊
   mainSide = .ScaleWidth
   If m_FadeMode = 1 Then mainSide = .ScaleHeight
   ' 根據 mainSide,取得色彩的漸變梯度
   slopeRGB = getSlope(c1, c2, mainSide)
   For i = 0 To mainSide
    ' 設定繪製的前景色彩
    setDrawColor c1, slopeRGB, i
    If m_FadeMode = 1 Then
     toObj.Line (0, i)-(.ScaleWidth, i)
    Else
     toObj.Line (i, 0)-(i, .ScaleHeight)
    End If
   Next i
  Case 3, 4  ' ↘、↙
   ' 取得決定色彩分布的基底邊(這裡即最大邊)
   If .ScaleWidth > .ScaleHeight Then
    mainSide = .ScaleWidth
    transRate = 1
    transRate2 = .ScaleHeight / .ScaleWidth
   Else
    mainSide = .ScaleHeight
    transRate = .ScaleWidth / .ScaleHeight
    transRate2 = 1
   End If
   ' 根據 mainSide,取得色彩的漸變梯度
   ' mainSide 乘上 2 是為了使長與寬的色彩漸變梯度等價
   slopeRGB = getSlope(c1, c2, mainSide * 2)
   For i = 0 To mainSide * 2
    ' 設定繪製的前景色彩
    setDrawColor c1, slopeRGB, i
    If i <= mainSide Then
     ' 針對某半邊(上方)三角形的色彩繪製
     singleSide = i
     If m_FadeMode = 3 Then
      toObj.Line (singleSide * transRate, 0)-(0, singleSide * transRate2)
     Else
      toObj.Line (.ScaleWidth - singleSide * transRate, 0)-(.ScaleWidth, singleSide * transRate2)
     End If
    Else
     ' 針對另一半邊(下方)三角形的色彩繪製
     singleSide = i - mainSide
     If m_FadeMode = 3 Then
      toObj.Line (.ScaleWidth, singleSide * transRate2)-(singleSide * transRate, .ScaleHeight)
     Else
      toObj.Line (0, singleSide * transRate2)-(.ScaleWidth - singleSide * transRate, .ScaleHeight)
     End If
    End If
   Next i
  Case 5   ' ◎
   ' 取得圓心位於目標控制項的座標
   Xo = m_Xpercent * .ScaleWidth / 100
   Yo = m_Ypercent * .ScaleHeight / 100
   ' 取得繪製圓的最大半徑
   If Xo > .ScaleWidth / 2 Then Xd = Xo Else Xd = .ScaleWidth - Xo
   If Yo > .ScaleHeight / 2 Then Yd = Yo Else Yd = .ScaleHeight - Yo
   MaxR = Int((Xd ^ 2 + Yd ^ 2) ^ (1 / 2))
   ' 根據 MaxR,取得色彩的漸變梯度
   slopeRGB = getSlope(c1, c2, MaxR)
   .DrawWidth = 2
   For i = 0 To MaxR
    ' 設定繪製的前景色彩
    setDrawColor c1, slopeRGB, i
    toObj.Circle (Xo, Yo), i
   Next i
   .DrawWidth = 1
  Case Else
   GoTo errorLabel
  End Select
 End With
Exit Sub
errorLabel:
 Err.Raise 513, , "目的控制項不支援 FadeSmart 的功能" & vbNewLine & "或者,指定了錯誤的屬性值"
End Sub

' 取得指定色彩的 Red、Green、Blue 值
Private Function GetRGB(ByVal CrColor As Long) As ColorRGB
 GetRGB.Red = CrColor And &HFF
 GetRGB.Green = (CrColor \ &H100) And &HFF
 GetRGB.Blue = (CrColor \ &H10000) And &HFF
End Function

' 取得各分色值在指定基底(spreadBase)下的漸變梯度
Private Function getSlope(c1 As ColorRGB, c2 As ColorRGB, spreadBase As Long) As ColorSingle
 getSlope.Red = (c2.Red - c1.Red) / spreadBase
 getSlope.Green = (c2.Green - c1.Green) / spreadBase
 getSlope.Blue = (c2.Blue - c1.Blue) / spreadBase
End Function

' 設定繪製的前景色彩
' 在指定的漸變梯度與基底下,求出(內差法計算後)的色彩值

Private Sub setDrawColor(c1 As ColorRGB, slopeRGB As ColorSingle, spreadBase As Long)
 toObj.ForeColor = RGB(slopeRGB.Red * spreadBase + c1.Red, _
            slopeRGB.Green * spreadBase + c1.Green, _
            slopeRGB.Blue * spreadBase + c1.Blue)
End Sub

範例源碼下載
2004/10/17