| #E6E6E6 | 【VB 物件】PictureScroll 實現圖片循環捲動 | 作者:吳文成 在我的經驗裡撰寫過許多影像特效的函數,例如將影像作交錯顯示、雨滴顯示、移動顯示、淡出與淡入、拉幕技巧、放大縮小、螺旋方塊顯[[img src=think/poem_all.jpg height=259 width=346 align=left]]示 、螺旋圓形顯示,還包括影像任意角度旋轉,我在控件 CommonBox 裡實現了這些功能。另一些影像處理的技巧,也是大家很有興趣的,例如將影像給灰階化、調整影像的明暗度與對比度等等,而影像的透背處理(指定透明顏色而去除背景)更是貼圖一族的最愛,我在控件 TransImage 便實現了這項功能 , 而在我新開發的控件 FlatButton(平面按鈕)也需要運用到特定的影像灰階化處理。這些控件,我應該會找機會釋出給大家使用。與上面這些特效比較起來,圖片循環捲動是比較簡單的技巧,它的(流程圖)構思不失為一個好的入門範例,所以我將在這裡介紹它。 一般來說 , Visual Basic 的影像處理速度是不符合程式開發者的要求的 , 例如使用 PaintPicture 方法( 或 ListImage.Draw 方法 )來貼圖,你會看到「 慢動作 」, 這時候我們會使用 Window API 或者 DirectX 的相關函數來取而代之 。BitBlt 函數是 Window API 裡最主要的位圖繪製函數 , 它是 PaintPicture 方法的原始祖宗 ,原始祖宗總是講求簡潔與速度 , 所以敬老尊賢在這裡是必要的。如果選擇使用 BitBlt 這個函數,我們必須輸入參數:目的裝置與來源裝置的圖形裝置代碼(hDC,週邊設定內容物件代碼 )。 表單與控件 PictureBox 都有這個代碼 , 然而標準物件 StdPicture 卻沒有 ,這時候我們便需要利用 StdPicture 的 Handle 與 CreateCompatibleDC 函數、SelectObject 函數來取得其暫時的、相容的 hDC。 BitBlt 的貼圖功能其實就是把來源裝置之指定位置開始的影像,複製轉貼到目的裝置的指定位置與區域上,因而它也兼具有影像放大或縮小的功能。 我所撰寫的 PictureScroll 物件類別便是使用 BitBlt 函數 , 來實現指定控件裡圖片的循環捲動,包括方向與速度自訂的水平捲動或垂直捲動。我們需要在物件裡動態新增控件: 一個 Timer 與 PictureBox( 我們也可以用標準物件 StdPicture 來取代 PictureBox,不過這樣會多使用到上一段提到的那幾個 Window API)。Timer 用來按照時間間隔以執行圖片捲動與重繪,PictureBox( 範例裡的 picSrc 物件 )則用來儲存與保留目的裝置的原始圖片。圖片循環捲動,最簡單、最視覺化的貼圖流程是這樣:根據每次捲動[[img src=computer/PicScroll.gif height=159 width=202 align=left]]的移動量(ScrollStep)直接將目的裝置的影像分成兩部分,然後再交換分貼到目的裝置自己身上。 這樣講起來似乎很簡單,但是裡面卻有很多想像不到的細節,這些細節可能使得我們修正原來的構想,例如這裡牽涉到不同方向的捲動問題,圖片大小與目的裝置大小不一致的問題,原圖狀態如何保留的問題,是否真的可以不須藉助媒介而貼圖的問題。這些細節的考慮都增加了若干複雜度。在勢必藉助媒介(範例裡的 picSrc 物件)與考量貼圖速度,以及演算法推廣化,等等相關的因素下,使得我們必須改用圖片目前捲動到的位置( ScrollLocation),來將來源裝置( picSrc )的圖片分成兩部分,再轉貼到目的裝置。也就是說,原本的視覺化的、類比式的構思,有時不能成為真正的程式流程規劃,因為後者才會遭遇到那些非簡化的邏輯程序問題。例如自己分割自己,然後再不經任何媒介而交換貼自己,這聽起來很自然,可是在程式撰寫上這是做不到的。這就是類比的與數位的本質差別。 看看以下的程式碼,大概可以挖掘出問題所在,也許你會想出比我更好、更簡潔或更有效率的技巧 。限於篇幅,我只列舉出 PictureScroll.cls 的部分關鍵的程式碼,有需要請下載列於文末的完整源碼: Option Explicit ' 將一幅位圖從某個裝置複製重繪到另一個裝置 Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long ' 宣告圖片捲動模式的列舉型態 Public Enum PicScrollMode [無捲動] = 0 ' 預設捲動方式 [水平捲動] = 1 ' 當 m_ScrollStep > 0 則由右向左捲動,反之相反 [垂直捲動] = 2 ' 當 m_ScrollStep > 0 則由下向上捲動,反之相反 End Enum ' 此物件的初始化方法,必須指定放置圖片的目的控制項 ' 圖片捲動的方式、每次移動單位(像素)與間隔時間(毫秒),則為選擇性參數 Public Sub Initialize(PicContainer As Object, Optional ByVal ScrollMode As PicScrollMode, Optional ByVal ScrollStep As Long, Optional ByVal DelayInterval As Long) On Error GoTo errorLabel ' 動態新增控件,Timer 與 PictureBox 各一個 creatControl PicContainer m_ScrollLocation = 0 m_ScrollMode = ScrollMode ' 設置控件 tmrScroll 的屬性值 With tmrScroll If DelayInterval <= 0 Then DelayInterval = 50 .Interval = DelayInterval If ScrollStep = 0 Then ScrollStep = 2 m_ScrollStep = ScrollStep .Enabled = False End With ' 設置控件 picSrc 的屬性值 With picSrc .ScaleMode = vbPixels .AutoSize = True .AutoRedraw = True .Visible = False End With ' 改變目的裝置 PicContainer 的屬性值 Set picCtn = PicContainer With picCtn ' 在 picSrc 中紀錄原本目的裝置的圖片 Set picSrc.Picture = .Picture picSrc.Tag = .AutoRedraw .AutoRedraw = False .ScaleMode = vbPixels End With Exit Sub errorLabel: Err.Raise 513, , "目標控制項不支援 PictureScroll 的功能" End Sub ' 按照計時間隔與指定的捲動方式,來執行圖片捲動與重繪 Private Sub tmrScroll_Timer() ' 分別紀錄目的裝置與來源裝置的長寬 Dim wPicCtn As Long, hPicCtn As Long Dim wPicSrc As Long, hPicSrc As Long wPicCtn = picCtn.ScaleWidth hPicCtn = picCtn.ScaleHeight wPicSrc = picSrc.ScaleWidth hPicSrc = picSrc.ScaleHeight ' 表示 m_ScrollLocation 上邊或左邊的圖像 Dim part1 As Long ' 表示 m_ScrollLocation 下邊或右邊的圖像 Dim part2 As Long Select Case m_ScrollMode Case 垂直捲動 ' 當 m_ScrollStep > 0 則由下向上捲動,反之相反 part1 = hPicSrc - m_ScrollLocation part2 = m_ScrollLocation If m_ScrollLocation + hPicCtn <= hPicSrc Then ' 直接繪製 part2 區域 BitBlt picCtn.hDC, 0, 0, wPicCtn, hPicCtn, picSrc.hDC, 0, part2, vbSrcCopy Else ' 將來源裝置的 part2 區域繪製到目的裝置 BitBlt picCtn.hDC, 0, 0, wPicCtn, part1, picSrc.hDC, 0, part2, vbSrcCopy ' 將來源裝置的 part1 區域繪製到目的裝置 BitBlt picCtn.hDC, 0, part1, wPicCtn, part2, picSrc.hDC, 0, 0, vbSrcCopy End If ' 依序改變圖片目前捲動到的位置 m_ScrollLocation = m_ScrollLocation + m_ScrollStep If m_ScrollLocation >= hPicSrc Then m_ScrollLocation = m_ScrollLocation - hPicSrc If m_ScrollLocation < 0 Then m_ScrollLocation = m_ScrollLocation + hPicSrc Case 水平捲動 ' 當 m_ScrollStep > 0 則由右向左捲動,反之相反 part1 = wPicSrc - m_ScrollLocation part2 = m_ScrollLocation If m_ScrollLocation + wPicCtn <= wPicSrc Then ' 直接繪製 part2 區域 BitBlt picCtn.hDC, 0, 0, wPicCtn, hPicCtn, picSrc.hDC, part2, 0, vbSrcCopy Else ' 將來源裝置的 part2 區域繪製到目的裝置 BitBlt picCtn.hDC, 0, 0, part1, hPicCtn, picSrc.hDC, part2, 0, vbSrcCopy ' 將來源裝置的 part1 區域繪製到目的裝置 BitBlt picCtn.hDC, part1, 0, part2, hPicCtn, picSrc.hDC, 0, 0, vbSrcCopy End If ' 依序改變圖片目前捲動到的位置 m_ScrollLocation = m_ScrollLocation + m_ScrollStep If m_ScrollLocation >= wPicSrc Then m_ScrollLocation = m_ScrollLocation - wPicSrc If m_ScrollLocation < 0 Then m_ScrollLocation = m_ScrollLocation + wPicSrc End Select End Sub |
2004/10/09 |