Pieni esimerkinpoikanen SetDIBits funktion käytöstä.
Plasmaefekti pohjautuu pitkälti pekin oppaaseen.
Suhteellisen vähän kommentoitu koodi, mutta samalla sen verran yksinkertainen kuitenkin, että eiköhän tuo kaikille avaudu.
Plasman saisi toki VB:llä tehtyä huomattavasti nopeamminkin, esim. hyödyntäen gdi32:sta löytyviä palettifunktioita. No, tuskinpa ainakaan mikään hitain variaatio VB:llä luodusta plasmaefektistä.
Eli tee formi, johon laitat yhden pictureboxin nimeltä Picture1 ja liitä koodi projektiin.
Ja on sitten aika nihkeetä yrittää pyörittää tuota debugmodessa, että kääntäkää suoraan vaan exeksi.
Toimiva esimerkki löytynee osoitteesta: http://www.omena.org/~rykker/plasmi.zip
Declarations
Option Explicit
Private Const leveys As Long = 640
Private Const korkeus As Long = 480
Private X As Long, Y As Long, i As Long, j As Long
Private Type paletti
Punane As Byte
Vihree As Byte
Sinine As Byte
End Type
Private lasku As Long, lasku2 As Long, dumb As Single
Private liiku1 As Long
Private arvoo(1 To leveys * 4, 1 To korkeus * 4) As Long
Private aika As Long
Private Const pii As Long = 3 'jep, kolme.
Private Declare Function GetInputState Lib "user32" () As Long
Private Declare Function SetDIBits Lib "gdi32" (ByVal hdc As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long
Private Type BITMAP '14 tavua
bmType As Long
bmWidth As Long
bmHeight As Long
bmWidthBytes As Long
bmPlanes As Integer
bmBitsPixel As Integer
bmBits As Long
End Type
Private Type BITMAPINFOHEADER '40 tavua
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
Private Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Const DIB_RGB_COLORS = 0&
Private Const BI_RGB = 0&plasma
Private Sub plasma()
Dim varit(1 To 256) As paletti
Dim wid As Integer
Dim hgt As Integer
Dim bitmap_info As BITMAPINFO
Dim pixels() As Byte
Dim bytes_per_scanLine As Integer
Dim pad_per_scanLine As Integer
'Otetaan bittikartan tietoja ylös, näistä ei kannata niin paljoa välittää :p
wid = Picture1.ScaleWidth
hgt = Picture1.ScaleHeight
With bitmap_info.bmiHeader
.biSize = 40
.biWidth = wid
' Negatiivinen arvo = ylhäältä alas
.biHeight = -hgt
.biPlanes = 1
.biBitCount = 32
.biCompression = BI_RGB
bytes_per_scanLine = ((((.biWidth * .biBitCount) + 31) \ 32) * 4)
pad_per_scanLine = bytes_per_scanLine - (((.biWidth * .biBitCount) + 7) \ 8)
.biSizeImage = bytes_per_scanLine * Abs(.biHeight)
End With
'luodaan oikean kokoinen rgb-kartta pikseleille
ReDim pixels(1 To 4, 1 To wid, 1 To hgt)
Do
If GetInputState Then End
aika = aika + 5
dumb = aika * pii / 360
lasku = leveys * 2 + 100 * Sin(dumb)
lasku2 = korkeus * 2 + 100 * Cos(dumb)
For i = 1 To 256
With varit(i)
.Punane = 72 + 71 * Cos(i * pii / 128 + aika / 74)
.Sinine = 72 - 71 * Cos(i * pii / 128 + aika / 71)
.Vihree = 72 + 71 * Sin(i * pii / 128 + aika / 64)
End With
Next i
' Muokataan pikseleitä
For Y = 1 To korkeus
For X = 1 To leveys
liiku1 = arvoo(lasku + X, lasku2 + Y)
If liiku1 > 255 Then liiku1 = 255
pixels(3, X, Y) = varit(liiku1).Punane 'r
pixels(2, X, Y) = varit(liiku1).Vihree 'g
pixels(1, X, Y) = varit(liiku1).Sinine 'b
Next X
Next Y
' Näytetään muunneltu kuva
SetDIBits Picture1.hdc, Picture1.Image, 0, hgt, pixels(1, 1, 1), bitmap_info, DIB_RGB_COLORS
Picture1.Picture = Picture1.Image
Loop
End SubForm_Load
Private Sub Form_Load() Me.ScaleMode = vbPixels Picture1.AutoRedraw = False Picture1.ScaleMode = vbPixels Picture1.Width = 640 Picture1.Height = 480 Me.Width = 640 * 15 Me.Height = 480 * 15 Picture1.Left = 0 Picture1.Top = 0 Picture1.BorderStyle = 0 Me.Show 'luodaan palettikartta For i = 1 To leveys * 4 For j = 1 To korkeus * 4 arvoo(i, j) = 64 + 63 * Sin(i / (90 + 10 * Cos(j / 74))) * Cos(j / (110 + 10 * Sin(i / 60))) Next j Next i plasma End Sub
Form_Load on aika jännästi sisennetty. Ja mä laittasin GetInputState sinne form_loadiin just ennen plasma-subin kutsumista sen pufferin tyhjentämiseks että sulkeutuu vasta sitten ku oikeesti halutaan eikä ennen sitä jopa. Mulla ainaki sun exe sulkeutui vähän väliä ennen aikojaan. Mutta hieno efekti ja hyvin näkee ton funkkarin käytön. En uskalla kuvitellakaan kuinka hidas tosta tulis jos pikselit psettais yks kerrallaan :D
Tuonne silmukkaan olisi ihan hyvä lisätä Doevents, niin onnistuisi ohjelmasta poistuminen nopeammin..
Hieno efekti.
Kuten ohjelmoinninopettajallani on tapana sanoa "Koodilla ei oo väliä miltä näyttää, kunhan se toimii ja itse tajuaa mitä tapahtuu." Jotenkin syöpyny päähän toi :P
Ja unohin ihan ton GetInputStaten käyttäytymisen :D
Koittakaa siis olla painelematta turhia nappeja ja käynnistäkää toi .exe vaikka enterillä...
Ja tuomas: GetInputState hoitaa tossa sen ohjelmasta poistumisen. Tai ainakin pitäisi.
DoEventsiä ei muutenkaa kannata käyttää tommosessa. Hidastais turhan paljon kun pitäs käsitellä jotain muutaki ku effua ;)
Ohhoh, Rykker on hengissä :)
Joissain määrin ainakin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.