VB 5/6-Tipp 0629: Embosseffekte auf Grafiken per ASM anwenden
von Udo Schmidt
Beschreibung
Dieser Tipp stammt aus eine Reihe von Tipps rund um das Thema Grafikmanipulation mit ASM. Das Modul "asm_001ROU.bas" ist daher in allen Tipps das Gleiche. In diesem Tipp wird gezeigt, wie man auf ein Bild einen Embosseffekt anwendet. Dabei wird sowohl eine Lösung mit VB, wie auch eine Lösung in ASM (der Code liegt bei) angeboten.
Schwierigkeitsgrad: | Verwendete API-Aufrufe: CLSIDFromString, CallWindowProcA (CallWindowProc), RtlMoveMemory (CopyMemory), CreateDIBSection, OleCreatePictureIndirect (CreatePic), DeleteObject, GetDC, GetDIBits, GetObjectA (GetObject), ReleaseDC | Download: |
'Dieser Quellcode stammt von http://www.activevb.de 'und kann frei verwendet werden. Für eventuelle Schäden 'wird nicht gehaftet. 'Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. 'Ansonsten viel Spaß und Erfolg mit diesem Source! '-------------- Anfang Projektdatei Emboss.vbp -------------- ' Die Komponente 'Microsoft Common Dialog Control 6.0 (SP3) (comdlg32.ocx)' wird benötigt. '------ Anfang Modul "asm_Emboss" alias asm_Emboss.bas ------ Option Explicit ' asmEmboss ' ' CALL asmEmboss(ptr:lpAsmBmpPara, [fct:factor]) ' ' IN: lng:ptr Points to asmBmpPara-structure ' lng:fct 0 to 7 (default = 1) ' ' OUT: bol success ' Function asmEmboss(lpAsmBmpPara As Long, Optional fct As Long = 10) As Boolean Static asm(40) As Long If asm(0) = 0 Then asm(0) = &HEC8B5590: asm(1) = &H8B575653 asm(2) = &H738B085D: asm(3) = &H2C7B8B14 asm(4) = &HF1C4B8B: asm(5) = &HF3204BAF asm(6) = &HC4D8BA5: asm(7) = &H3307E180 asm(8) = &HE88B99C0: asm(9) = &HA8E0D240 asm(10) = &HB20874DD: asm(11) = &H74C1A804 asm(12) = &HA8DAF702: asm(13) = &H8B097477 asm(14) = &H7A80C6B: asm(15) = &HDDF70274 asm(16) = &H738BEA03: asm(17) = &H49CE8B04 asm(18) = &H8B4D7E49: asm(19) = &H4A4A0853 asm(20) = &HC146467E: asm(21) = &HFE8B02E6 asm(22) = &H3147303: asm(23) = &H438B2C7B asm(24) = &HF7DD8B0C: asm(25) = &H565150DB asm(26) = &H44B60F57: asm(27) = &H42A0035 asm(28) = &HFFD48033: asm(29) = &H7F04C422 asm(30) = &HC40AE41A: asm(31) = &H46470788 asm(32) = &H3C6F7: asm(33) = &HE17B0000 asm(34) = &H75494746: asm(35) = &H595E5FDC asm(36) = &H3F80358: asm(37) = &HCD754AF0 asm(38) = &H5FC01BF9: asm(39) = &HC25D5B5E asm(40) = &H10 End If ' ************************************************************ asmEmboss = CallWindowProc(asm(0), lpAsmBmpPara, fct, 0, 0) End Function ' basEmboss ' ' AUFRUF: basEmboss(src, tgt, [fct:Faktor]) ' ' EIN: bar:src Herkunftsdaten (ByteArray) ' bar:tgt Zieldaten (ByteArray) ' lng:fct Faktor 0-7 (Vorgabe 1) ' log:bw Schwarz/Weiß (Vorgabe nein) ' ' AUS: log Erfolg ' Function basEmboss(src() As Byte, tgt() As Byte, Optional fct As Long = 1) _ As Boolean Dim X1 As Long Dim Y1 As Long Dim N1 As Long Dim DX1 As Long Dim DY1 As Long Dim DX2 As Long Dim DY2 As Long N1 = (fct And 7) + 1 DX1 = Choose(N1, -4, 0, 4, 4, 4, 0, -4, -4) DX2 = -DX1 DY1 = Choose(N1, -1, -1, -1, 0, 1, 1, 1, 0) DY2 = -DY1 CopyMemory tgt(1, 1), src(1, 1), UBound(src, 1) * UBound(src, 2) For X1 = 5 To UBound(src, 1) - 7 X1 = X1 - ((X1 And 3) = 0) For Y1 = 2 To UBound(src, 2) - 1 N1 = 0& - src(X1 + DX1, Y1 + DY1) + src(X1 + DX2, Y1 + DY2) + 127 If N1 < 127 Then N1 = 127 If N1 > 255 Then N1 = 255 tgt(X1, Y1) = N1 Next Y1 Next X1 basEmboss = True End Function '------- Ende Modul "asm_Emboss" alias asm_Emboss.bas ------- '---- Anfang Formular "frm_Emboss" alias frm_Emboss.frm ---- ' Steuerelement: Schaltfläche "Command3" ' Steuerelement: Schaltfläche "Command2" ' Steuerelement: Schaltfläche "Command1" ' Steuerelement: Horizontale Scrollbar "HScroll1" ' Steuerelement: Bildfeld-Steuerelement "Picture1" ' Steuerelement: Standarddialog-Steuerelement "CommonDialog1" '--------------------------------------------------------------------------------------- ' (softKUS) - VIII/2003 '--------------------------------------------------------------------------------------- ' Die Funktion "Emboss" setzt ein Bild in Grautöne - einzig Ränder werden ' farblich hervorgehoben. ' ' Für die Umsetzung wird eine Assembler-(Maschinensprache)-Routine benutzt, ' deren Funktionsweise nicht ohne weiteres ersichtlich ist. Deshalb ist eine ' Basic-Funktion, basEmboss, angefügt. Per Mausklick auf Command3 kann ' zwischen Basic- und Assemblerbenutzung hin- und hergeschaltet werden. ' ' Das Beispiel besteht aus drei Modulen: ' 1. Form1 - Dient bloß zur Demonstration ' 2. Module1 - Enthält grundlegendes zum Lesen/Schreiben der Bilder ' 3. Module2 - Enthält die eigentliche Bildbearbeitung (asmEmboss/basEmboss) ' ' Die ASM-Routine, asmEmboss, kann auch völlig losgelöst vom hiesigen Rahmen ' benutzt werden. Es muß lediglich gewährleistet sein, daß der erste Parameter ' (lpAsmBmpPara) auf eine genau nach unten beschriebenem Muster erstellte und ' mit gültigen Werten initialisierte Struktur verweist - andernfalls kommt ' es ' garantiert zum PC-Absturz. ' '--------------------------------------------------------------------------------------- ' ' Ein Formular mit: ' Picture1, Command1 bis Command3, HScroll1, CommonDialog1 ' '--------------------------------------------------------------------------------------- Option Explicit Option Base 0 ' * ' ** ' *** Konstanten ' *** - für die Bildbearbeitung sind nur die Parameter für die Bildlaufleiste ' *** interessant (HPar): Min;Max;(Standard-)Value;SmallChange;LargeChange ' ** ' * ' größerer Wert => größere Form Private Const XYcm As Long = 567 Private Const Ftxt As String = "asm-Beispiel Emboss" Private Const HPar As String = "00000;00007;00000;00001;00001" ' * ' ** ' *** Variablen ' ** ' * ' log: Bildlaufleiste aktiv J/N Dim scrH_flg As Boolean ' lng: Bildlaufleisten-Standardwert Dim scrH_std As Long ' udt: Datenstruktur für ASM-Routine Dim abp As asmBmpPara ' bin: Bilddaten Dim src() As Byte ' bin: Puffer für die Bildbearbeitung Dim tgt() As Byte ' log: .T.=Assember, .F.=Basic Dim pmd As Boolean Private Sub Form_Load() ' Initialisieren der Byte-Arrays ReDim src(1, 1) ' (vermeidet Zugriffsfehler) ReDim tgt(1, 1) With Me .ScaleMode = vbTwips .Width = (Me.Controls.Count * 2 - 2) * XYcm .Height = (Me.Controls.Count * 2 - 2) * XYcm .Caption = Ftxt End With With HScroll1 .Min = Val(Mid$(HPar, 1, 5)) .Max = Val(Mid$(HPar, 7, 5)) .Value = Val(Mid$(HPar, 13, 5)) .SmallChange = Val(Mid$(HPar, 19, 5)) .LargeChange = Val(Mid$(HPar, 25, 5)) scrH_std = .Value End With Command1.Caption = "Bild laden" Command2.Caption = "Undo" Command3.Caption = "Basic" Picture1.ScaleMode = vbTwips pmd = True End Sub ' dummy Bild erzeugen Private Sub Form_Initialize() Dim X1 As Long Randomize Timer Picture1.AutoRedraw = True For X1 = 1 To 400 Picture1.DrawWidth = 1 + Rnd() * 30 Picture1.Line (Rnd * Picture1.Width, Rnd * Picture1.Height)-(Rnd * Picture1.Width, _ Rnd * Picture1.Height), RGB(Rnd * 255, Rnd * 255, Rnd * 255) Next X1 Picture1.AutoRedraw = False scrH_flg = ReadData(Picture1.Image, abp, src, tgt, pmd, False) If scrH_flg Then WriteData Picture1, abp.src, True End Sub Private Sub Form_Resize() Dim dfw As Long Dim dfh As Long Dim tmp As Long dfw = Me.Width - Me.ScaleWidth dfh = Me.Height - Me.ScaleHeight tmp = ((Me.Controls.Count - 3) * 2.25 + 0.25) * XYcm + dfw If Me.Width < tmp Then Me.Width = tmp If Me.Height < 5 * XYcm Then Me.Height = 5 * XYcm With Command1 ' Bild laden .Top = 0.25 * XYcm .Left = 0.25 * XYcm .Width = 2 * XYcm .Height = 0.5 * XYcm End With With Command2 ' Undo .Top = 0.25 * XYcm .Left = Me.Width - 4.5 * XYcm - dfw .Width = 2 * XYcm .Height = 0.5 * XYcm End With With Command3 ' bas/asm .Top = 0.25 * XYcm .Left = Me.Width - 2.25 * XYcm - dfw .Width = 2 * XYcm .Height = 0.5 * XYcm End With With Picture1 .Top = 1 * XYcm .Left = 0.25 * XYcm .Width = Me.Width - 0.5 * XYcm - dfw .Height = Me.Height - 2 * XYcm - dfh End With With HScroll1 .Top = Picture1.Top + Picture1.Height + 0.25 * XYcm .Left = 0.25 * XYcm .Width = Picture1.Width .Height = 0.5 * XYcm End With End Sub Private Sub Picture1_DblClick() Form_Initialize End Sub ' Bild laden Private Sub Command1_Click() ' obj: enthält das zu ladende Bild Dim pic As IPicture On Error Resume Next With CommonDialog1 .CancelError = True .Flags = &H281800 .Filter = "Bilder |*.bmp;*.jpg||" .ShowOpen If Err = 0 Then Set pic = LoadPicture(.FileName) If pic Is Nothing Then MsgBox "Bild konnte nicht geladen werden" ElseIf ReadData(pic, abp, src, tgt, pmd) Then ' alles okay => setze pic auf die PictureBox Set Picture1.Picture = pic ' setze HScroll1 auf den Standardwert Command2_Click scrH_flg = True End If End If End With Picture1.SetFocus End Sub ' Undo Private Sub Command2_Click() ' Bildlaufleiste deaktivieren scrH_flg = False ' Änderung am Bild zurücknehmen WriteData Picture1, abp.src ' Bildlaufleiste auf Ausgangswert HScroll1 = scrH_std ' Bildlaufleiste aktivieren scrH_flg = True Picture1.SetFocus End Sub ' Umschalten zw. bas und asm Private Sub Command3_Click() pmd = Not pmd Command3.Caption = Choose(pmd + 2, "Basic", "Assembler") If pmd = False And Picture1.Picture.Handle <> 0 And VarPtr(tgt(1, 1)) <> abp.tgt.bmBits _ Then ' Wenn abp.tgt direkt auf den Speicher der Bitmap ' weist, muß für die Basic-Routine auf ein ByteArray ' umgelenkt werden. Dazu kann auch mit einer SAVEARRAY- ' Struktur gearbeitet werden - das führte hier aber zu weit. ReDim tgt(1 To UBound(src, 1), 1 To UBound(src, 2)) abp.tgt.bmBits = VarPtr(tgt(1, 1)) End If Picture1.SetFocus End Sub Private Sub HScroll1_Scroll() HScroll1_Change End Sub Private Sub HScroll1_Change() Dim B1 As Boolean If scrH_flg = False Or Picture1.Picture.Handle = 0 Then ' deaktiviert/kein Bild geladen => ignorieren ElseIf pmd Then B1 = asmEmboss(VarPtr(abp), HScroll1) Else B1 = basEmboss(src, tgt, HScroll1) End If 'geändertes Bild ausgeben If B1 Then WriteData Picture1, abp.tgt End Sub '----- Ende Formular "frm_Emboss" alias frm_Emboss.frm ----- '------ Anfang Modul "asm_001ROU" alias asm_001ROU.bas ------ Option Explicit Option Base 0 ' * ' ** ' *** Konstanten ' ** ' * ' gdi/ole Private Const S_OK As Long = 0 Private Const BI_RGB As Long = 0 Private Const DIB_RGB_COLORS As Long = 0 Private Const IID_IPicture As String = "{7BF80980-BF32-101A-8BBB-00AA00300" & _ "CAB}" ' * ' ** ' *** Strukturen ' ** ' * ' Windows-Standard ' udt: Windows-Bitmap-Struktur Public Type BITMAP '-> Einlesen der Bildinformationen 'mit GDI_GetObject() bmType As Long bmWidth As Long bmHeight As Long bmWidthBytes As Long bmPlanes As Integer bmBitsPixel As Integer bmBits As Long End Type ' udt: Windows-Bitmap-Struktur Public Type BITMAPINFOHEADER '-> Erstellen von Bildern im Speicher, 'mit GDI_CreateDibSection biSize As Long biWidth As Long biHeight As Long '-> Einlesen von Bilddaten (binär) 'mit GDI_GetDiBits biPlanes As Integer biBitCount As Integer ' 'wird nur als Unterstruktur von 'BITMAPINFO benötigt (s.u.) biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type ' udt: Windows-Bitmap-Struktur Public Type BITMAPINFO bmiHeader As BITMAPINFOHEADER bmiColors As Long End Type ' udt: PictureDescription Private Type PictDesc '-> Erstellen eines IPicture-Objektes 'mit OleCreatePictureIndirect cbSizeofStruct As Long picType As Long hImage As Long xExt As Long yExt As Long End Type ' von asm-Routinen genutzte Struktur ' udt: AsmStruktur Public Type asmBmpPara '-> fasst Infos über Herkunftsdaten 'und Zieldaten zusammen 'dient vor allem dazu, die Anzahl 'der Funktionsparamter zu begrenzen src As BITMAP tgt As BITMAP srcExpansion As Long End Type ' * ' ** ' *** Externe Funktionen ' ** ' * ' div ' hiermit werden Daten recht schnell kopiert Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef hpvDest _ As Any, ByRef hpvSource As Any, ByVal cbCopy As Long) ' hiermit wird die asm-Routine aufgerufen Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _ (ByRef lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal _ wParam As Long, ByVal lParam As Long) As Long ' GDI (graphical device interface) ' Erstellen eines Gerätekontextes Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) _ As Long ' Freigeben eines Gerätekontextes Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd _ As Long, ByVal hDC As Long) As Long ' Einlesen von Informationen über GDI-Objekte (z.B. Bilder) Private Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject _ As Long, ByVal nCount As Long, ByRef lpObject As Any) As Long ' Löschen eines GDI-Objektes Private Declare Function DeleteObject Lib "gdi32" (ByVal _ hObject As Long) As Long ' Erstellen eines geräte-unabhängigen Bildes (Device Independent Bitmap, DIB) Private Declare Function CreateDIBSection Lib "gdi32" _ (ByVal hDC As Long, ByRef pbmi As BITMAPINFO, ByVal iUsage As Long, ByRef _ ppvBits As Long, ByVal hSection As Long, ByVal dwOffset As Long) As Long ' Einlesen binärer Bilddaten Private Declare Function GetDIBits Lib "gdi32" (ByVal hDC _ As Long, ByVal hBM As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, _ ByRef lpvBits As Any, ByRef lpBmi As BITMAPINFO, ByVal uUsage As Long) As Long ' OLE (Object Linked Embedding, heute: COM oder ActiveX genannt) ' Umwandeln eines CLSID-Strings in Binärcode Private Declare Function CLSIDFromString Lib "ole32" _ (ByVal lpszProgID As Long, ByVal pCLSID As Long) As Long ' Erstellen eines IPicture-Objektes aus einem GDI-Bitmap-Handle Private Declare Function CreatePic Lib "olepro32" Alias "OleCreatePictureIndirect" _ (ByRef lpPictDesc As PictDesc, ByVal riid As Long, ByVal fPictureOwnsHandle _ As Long, ByRef ipic As IPicture) As Long Function ReadData(pic As IPicture, abp As asmBmpPara, src() As Byte, tgt() As Byte, _ Optional pmd As Boolean = True, Optional eMD As Boolean = True) As Boolean ' infos über das einzulesende Bild Dim bmp As BITMAP ' erweiterte infos über das einzulesende Bild Dim bmi As BITMAPINFO ' Gerätekontext-Handle Dim hDC As Long ' Zeiger auf die binären Bilddaten Dim ptr As Long Dim tmp As Long ' Einlesen der Bitmapinfos If GetObject(pic.Handle, Len(bmp), bmp) = 0 Then If eMD Then MsgBox "Bildinformationen konnten nicht gelesen werden" ElseIf pmd = True And bmp.bmBits <> 0 And bmp.bmBitsPixel = 32 Then ' wenn die Daten im richtigen Format vorliegen, diesen Pointer nehmen ptr = bmp.bmBits Else ' sonst per GDI-Funktion konvertieren lassen und im tgt-bArray() speichern With bmi.bmiHeader .biSize = Len(bmi.bmiHeader) .biCompression = BI_RGB .biHeight = bmp.bmHeight .biWidth = bmp.bmWidth .biPlanes = 1 .biBitCount = 32 .biSizeImage = .biWidth * 4 * .biHeight ReDim tgt(1 To .biWidth * 4, 1 To .biHeight) hDC = GetDC(0) If hDC = 0 Then If eMD Then MsgBox "Gerätekontext konnte nicht erzeugt werden" ElseIf GetDIBits(hDC, pic.Handle, 0, .biHeight, tgt(1, 1), bmi, DIB_RGB_COLORS) _ Then ptr = VarPtr(tgt(1, 1)) Else ' bei Fehler: tgt-bArray wieder auf usrprüngliche Dimension setzen ReDim tgt(1 To UBound(src, 1), 1 To UBound(src, 2)) If eMD Then MsgBox "Bilddaten konnten nicht gelesen werden" End If If hDC Then ReleaseDC 0, hDC End With End If If ptr Then bmp.bmBitsPixel = 32 bmp.bmWidthBytes = bmp.bmWidth * 4 tmp = bmp.bmWidthBytes * bmp.bmHeight 'src-bArray dimensionieren und mit Bilddaten füllen ReDim src(1 To bmp.bmWidthBytes, 1 To bmp.bmHeight) CopyMemory src(1, 1), ByVal ptr, tmp 'asmBmpPara-Struktur setzen abp.src = bmp abp.src.bmBits = VarPtr(src(1, 1)) abp.tgt = bmp abp.tgt.bmBits = ptr ReadData = True End If End Function Function WriteData(pbx As PictureBox, bmp As BITMAP, Optional force As Boolean _ = False) As Boolean 'Neues Picture-Objekt Dim pic As IPicture 'infos über das zu überschreibende Bild Dim tmp As BITMAP 'erw. infos über das Bild Dim bmi As BITMAPINFO 'info zum Erstellen eines neuen Pictures Dim dsc As PictDesc 'temporär benutzter Gerätekontext Dim hDC As Long 'Zeiger auf die binären Bilddaten Dim ptr As Long Dim flg As Long 'clsid des IPicture-Objektes Dim iid(15) As Byte flg = pbx.Picture.Handle And (force = False) If flg Then flg = GetObject(flg, Len(tmp), tmp) If bmp.bmBits = tmp.bmBits Then 'tgt weist direkt auf den Bildobjekt-Speicher => nothing to do pbx.Refresh WriteData = True ElseIf flg > 0 And tmp.bmBits <> 0 And bmp.bmWidth = tmp.bmWidth And bmp.bmHeight _ = tmp.bmHeight And bmp.bmBitsPixel = tmp.bmBitsPixel Then ' wenn das Ziel dasselbe Datenformat aufweist, einfach kopieren CopyMemory ByVal tmp.bmBits, ByVal bmp.bmBits, bmp.bmWidthBytes * bmp.bmHeight pbx.Refresh WriteData = True Else ' sonst per OLE-Funktion ein neues IPicture-Objekt erstellen dsc.cbSizeofStruct = Len(dsc) dsc.picType = vbPicTypeBitmap With bmi.bmiHeader .biSize = Len(bmi.bmiHeader) .biCompression = BI_RGB .biBitCount = 32 .biHeight = bmp.bmHeight .biWidth = bmp.bmWidth .biPlanes = 1 .biSizeImage = bmp.bmWidthBytes * bmp.bmHeight End With ' 1. Gerätekontext hDC = GetDC(0) ' 2. DIB-Section If hDC Then dsc.hImage = CreateDIBSection(hDC, bmi, DIB_RGB_COLORS, ptr, 0, _ 0) If hDC = 0 Then MsgBox "Gerätekontext konnte nicht erzeugt werden" ElseIf dsc.hImage = 0 Or ptr = 0 Then MsgBox "Bildkopie konnte nicht erstellt werden" Else CopyMemory ByVal ptr, ByVal bmp.bmBits, bmp.bmWidthBytes * bmp.bmHeight If CLSIDFromString(StrPtr(IID_IPicture), VarPtr(iid(0))) <> S_OK Then MsgBox "OLE Fehler" ' 3. IPicture-Objekt ElseIf CreatePic(dsc, VarPtr(iid(0)), True, pic) <> S_OK Then MsgBox "OLE picture creation error" Else ' ... und das Ergebnis auf die PictureBox setzen Set pbx.Picture = Nothing Set pbx.Picture = pic dsc.hImage = 0 WriteData = True End If End If If hDC Then ReleaseDC 0, hDC If dsc.hImage Then DeleteObject dsc.hImage End If End Function '------- Ende Modul "asm_001ROU" alias asm_001ROU.bas ------- '--------------- Ende Projektdatei Emboss.vbp ---------------
Tipp-Kompatibilität:
Windows/VB-Version | Win32s | Win95 | Win98 | WinME | WinNT4 | Win2000 | WinXP |
VB4 | |||||||
VB5 | |||||||
VB6 |
Ihre Meinung
Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.