天天看點

Led控件(2)——Led顯示屏模拟

這裡延續講Led控件的第三種,即高分辨率的Led顯示屏,它是由很多密集的發光二極管組成的陣列,和顯示器的像素顯示的原理類似。(顯示器的像素是一種RGB蜂窩狀密集排列)。

類似的例子,例如vs.net裡面的圖檔,光标設計器,可以看到它可以把圖檔像素放大成密集網格顯示。效果如下圖所示:

Led控件(2)——Led顯示屏模拟

這個控件的實作原理是很直覺的,簡單描述一下繪制的方法。即首先我們需要準備一個真實的圖檔作為複制源,稱為源圖(src bitmap),我們把它複制到led顯示屏圖檔(dest bitmap)中,每個像素被我們放大成一個網格(cell),大小為(cellsize * cellsize),這些網格的間距是celldistance。一個cell對應的就是一個pixel。在cell的間隙之間,可能是背景色,或者是Grid線。

複制源圖的每個像素到一個cell中,我們可以使用bitmap的getpixel,setpixel方法,但是這樣做顯然效率會非常低下。這種針對精确到像素的操作應該使用指針直接操作記憶體,首先我們需要擷取在記憶體中的位圖資料(BitmapData對象),并将它鎖定,亦即告訴作業系統在現在不要移動這塊記憶體。在處理前我們必須了解,位圖資料在記憶體中是一塊位址連續的存儲區域。

解釋一下BitmapData對象提供的兩個最重要屬性:

1.IntPtr Scan0:這是一個指針,指向資料的第一個像素的第一個通道,也就是說從這向後數,都是像素内容。通道的順序是BGR,BGR,BGR,...,你必須記住通道的順序,不要搞錯。

2.int Stride:它稱為掃描行寬度,這是一個非常關鍵的概念,它告訴你圖檔中的每一行在這塊記憶體中占據的位元組長度。請注意,基于bitmap的存儲方式,這個數字一定是4的倍數,也就是說,一個掃描行需要湊成4位元組的整數倍,是以可能在末尾具有一些補0的備援位元組。

是以掃描行寬度可以描述為下面的等式:

stride(位元組)=Bitmap.Width(像素)*bpp/8+行尾補0位元組數 

(其中bpp為色深度,bits per pixel, 位/像素)

對RGB圖像,具有三個通道,每個通道占據1個位元組,可了解為:

 stride=Bitmap.Width*3+行尾補0位元組數

則行尾補0位元組數=4-(Bitmap.Width*3)%4; 

是以了解了以上概念,我們就可以用下面的代碼操作像素了,對于一個位于(i,j)位置的像素,它的RGB可以按照如下的下标通路:

Led控件(2)——Led顯示屏模拟

//像素(i,j)

Led控件(2)——Led顯示屏模拟

byte* p=(byte*)(void*)bmData.Scan0;

Led控件(2)——Led顯示屏模拟

p[ stride*j + i*3 ]=(byte)

Led控件(2)——Led顯示屏模拟

;//Blue channel

Led控件(2)——Led顯示屏模拟

p[ stride*j + i*3 +1 ]=(byte)

Led控件(2)——Led顯示屏模拟

;//Green channel

Led控件(2)——Led顯示屏模拟

p[ stride*j + i*3 +2 ]=(byte)

Led控件(2)——Led顯示屏模拟

;//Red channel

在處理完成以後,必須将bitmapData解鎖記憶體。

由于以上操作需要使用unsafe代碼,是以必須在項目屬性中設定允許執行不安全代碼。否則會編譯出錯。

現在,可以看一下繪制LedScreen Bitmap的函數:

 1

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

/**//// <summary>

 2

Led控件(2)——Led顯示屏模拟

/// 建立一個Led顯示屏位圖

 3

Led控件(2)——Led顯示屏模拟

/// </summary>

 4

Led控件(2)——Led顯示屏模拟

/// <param name="srcBitmap">貼圖的源圖</param>

 5

Led控件(2)——Led顯示屏模拟

/// <param name="nWidth">顯示屏寬度</param>

 6

Led控件(2)——Led顯示屏模拟

/// <param name="nHeight">顯示屏高度</param>

 7

Led控件(2)——Led顯示屏模拟

/// <param name="drawGrid">是否繪制網格</param>

 8

Led控件(2)——Led顯示屏模拟

/// <param name="srcX">源圖起始坐标x</param>

 9

Led控件(2)——Led顯示屏模拟

/// <param name="srcY">源圖起始坐标y</param>

10

Led控件(2)——Led顯示屏模拟

/// <param name="cellsize">像素格大小</param>

11

Led控件(2)——Led顯示屏模拟

/// <param name="celldistance">像素格間距</param>

12

Led控件(2)——Led顯示屏模拟

/// <returns></returns>

13

Led控件(2)——Led顯示屏模拟

public static Bitmap CreateLedBitmap(Bitmap srcBitmap,int nWidth,int nHeight,bool drawGrid,int srcX,int srcY,int cellsize,int celldistance)

14

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

{

15

Led控件(2)——Led顯示屏模拟

    Bitmap bm=new Bitmap(nWidth,nHeight);

16

Led控件(2)——Led顯示屏模拟

    //這是bm全是黑色的。(都被設定為0)

17

Led控件(2)——Led顯示屏模拟

    //獲得圖檔的記憶體

18

Led控件(2)——Led顯示屏模拟

    BitmapData bmData=bm.LockBits(

19

Led控件(2)——Led顯示屏模拟

        new Rectangle(0,0,bm.Width,bm.Height),

20

Led控件(2)——Led顯示屏模拟

        ImageLockMode.ReadWrite,

21

Led控件(2)——Led顯示屏模拟

        PixelFormat.Format24bppRgb);

22

Led控件(2)——Led顯示屏模拟

    //掃描行寬度

23

Led控件(2)——Led顯示屏模拟

    int stride=bmData.Stride;

24

Led控件(2)——Led顯示屏模拟

25

Led控件(2)——Led顯示屏模拟

    //擷取源圖的bmdata

26

Led控件(2)——Led顯示屏模拟

    BitmapData srcData=srcBitmap.LockBits(

27

Led控件(2)——Led顯示屏模拟

        new Rectangle(0,0,srcBitmap.Width,srcBitmap.Height),

28

Led控件(2)——Led顯示屏模拟

        ImageLockMode.ReadOnly,

29

Led控件(2)——Led顯示屏模拟

        PixelFormat.Format24bppRgb

30

Led控件(2)——Led顯示屏模拟

        );

31

Led控件(2)——Led顯示屏模拟

32

Led控件(2)——Led顯示屏模拟

    int strideSrc=srcData.Stride;    //源圖的行長度

33

Led控件(2)——Led顯示屏模拟

    //操作記憶體的不安全代碼段

34

Led控件(2)——Led顯示屏模拟

    unsafe

35

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

36

Led控件(2)——Led顯示屏模拟

        byte* p=(byte*)(void*)bmData.Scan0;

37

Led控件(2)——Led顯示屏模拟

        byte* pSrc=(byte*)(void*)srcData.Scan0;    //源圖

38

Led控件(2)——Led顯示屏模拟

39

Led控件(2)——Led顯示屏模拟

        for(int j=srcY;j<srcBitmap.Height;j++)

40

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

41

Led控件(2)——Led顯示屏模拟

            for(int i=srcX;i<srcBitmap.Width;i++)

42

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

43

Led控件(2)——Led顯示屏模拟

                int nmin=(cellsize+celldistance)*(j-srcY);

44

Led控件(2)——Led顯示屏模拟

                int nmax=nmin+cellsize;

45

Led控件(2)——Led顯示屏模拟

                int mmin=(cellsize+celldistance)*(i-srcX);

46

Led控件(2)——Led顯示屏模拟

                int mmax=mmin+cellsize;

47

Led控件(2)——Led顯示屏模拟

                //源圖i,j位置的像素複制給一個目标圖的一個cell塊!

48

Led控件(2)——Led顯示屏模拟

                for(int n=nmin;(n<nmax && n<bm.Height);n++)

49

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

50

Led控件(2)——Led顯示屏模拟

                    for(int m=mmin;(m<mmax && m<bm.Width);m++)

51

Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟
Led控件(2)——Led顯示屏模拟

52

Led控件(2)——Led顯示屏模拟

                        p[ stride*n + m*3 ]=pSrc[ strideSrc*j + i*3];        //B

53

Led控件(2)——Led顯示屏模拟

                        p[ stride*n + m*3 +1 ]=pSrc[ strideSrc*j + i*3+1];    //G

54

Led控件(2)——Led顯示屏模拟

                        p[ stride*n + m*3 +2 ]=pSrc[ strideSrc*j + i*3+2];    //R

55

Led控件(2)——Led顯示屏模拟

                    }

56

Led控件(2)——Led顯示屏模拟

                }

57

Led控件(2)——Led顯示屏模拟

            }

58

Led控件(2)——Led顯示屏模拟

        }//    </for i>

59

Led控件(2)——Led顯示屏模拟

    }//    </unsafe>

60

Led控件(2)——Led顯示屏模拟

    srcBitmap.UnlockBits(srcData);

61

Led控件(2)——Led顯示屏模拟

    bm.UnlockBits(bmData);

62

Led控件(2)——Led顯示屏模拟

    return bm;

63

Led控件(2)——Led顯示屏模拟

}

在上面的函數中,暫時忽略了DrawGrid這個參數。并且我們給出了srcX,srcY,這是源圖複制的起始點。這兩個參數主要是基于實作ledscreen滾動字幕效果的考慮。例如不斷遞增srcX,并更新位圖,可以實作水準方向的字幕滾動。

下面為demo中的代碼,臨時建立了一個圖檔,并把生成的led screen圖檔顯示在一個picurebox中:

1

Led控件(2)——Led顯示屏模拟

Bitmap bm=new Bitmap(150,24);

2

Led控件(2)——Led顯示屏模拟

Graphics g=Graphics.FromImage(bm);

3

Led控件(2)——Led顯示屏模拟

g.FillRectangle(Brushes.DarkGreen,0,0,bm.Width,bm.Height);

4

Led控件(2)——Led顯示屏模拟

SolidBrush brush=new SolidBrush(Color.FromArgb(20,255,20));

5

Led控件(2)——Led顯示屏模拟

g.DrawString("hello cnblogs!",new Font("Arial",9f),brush,1,1);

6

Led控件(2)——Led顯示屏模拟

g.Dispose();

7

Led控件(2)——Led顯示屏模拟

Bitmap bm2=FigFactory.CreateLedBitmap(bm,750,120,false,0,0,5,3);

8

Led控件(2)——Led顯示屏模拟

this.pictureBox1.Image=bm2;

對于vs.net中的圖檔編輯器來說,它相當于celldistance=1的情況,并且在cell的間隔之間繪制了Grid,我發現這些grid線是由兩種顔色交替而組成的,是以這可以保證Grid線不會被任何顔色遮蓋住。

下面是源代碼的下載下傳連結。我原來以為這個代碼直接複制就可以用了,但是考慮到一些朋友還是向我提出一些問題。我把原來項目的代碼上傳并發在這裡。這個裡面不僅有這個led“走馬燈”效果,也包含我一些分形圖形有關的代碼。

(源代碼下載下傳):

<a href="http://files.cnblogs.com/hoodlum1980/FractalMaker.rar">http://files.cnblogs.com/hoodlum1980/FractalMaker.rar</a>

<a></a>

繼續閱讀