Een week geleden ben ik begonnen aan wat een mini-projectje had moeten zijn, maar helaas wil het totaal niet vlotten. Ik ben goed bekend met VB6, maar DirectDraw is nieuw voor mij.
Ik probeer iets te bereiken dat op het eerste gezicht heel raar klinkt, maar ondanks dat toch makkelijk te realiseren zou moeten zijn.
Wij gebruiken een bepaalde applicatie, genaamd Scala, een soort van PowerPoint-achtig pakket. De output van Scala wordt gewoon geblit op het scherm zonder een hardware overlay (surface). Voor ons is dat een probleem, omdat we heel graag de Feature Display van onze Matrox Parhelia-videokaart willen gebruiken. De Feature Display is feitelijk de TV-out, die een hoge kwaliteit composite-output levert. De Matrox-drivers zetten alles wat op een hardware overlay getekend is door op de Feature Display. Je raadt het al: aangezien Scala geen hardware overlay gebruikt, verschijnen onze mooie presentaties ook niet via de Feature Display op de TV-out.
De truc die ik wil toepassen met een klein extern programma'tje is dus:
1. Doe CreateSurface (overlay maken)
2. Maak een screenshot van Scala
3. Teken de screenshot op de overlay
4. Herhaal stappen 2 en 3 minstens 25x per seconde
Een screenshot maken en dat vervolgens herhalen gaat moeiteloos; het systeem waarop het moet draaien maakt eenvoudig honderden screenshots per seconde.
Ik heb m'n best gedaan om me in te lezen in DirectDraw, maar ik kom er niet helemaal uit. Wat me tot nu op basis van webvoorbeelden is gelukt, is het opslaan van een screenshot op de harde schijf als bitmap. Vervolgens kan ik met CreateSurfaceFromFile de bitmap aanroepen, die vervolgens inderdaad op m'n scherm verschijnt als Surface.
Het probleem hierbij is natuurlijk de harde schijf: de screenshot zit in het geheugen, en het slaat nergens op dat ie eerst opgeslagen moet worden en dan van de harde schijf weer aangeroepen moet worden om vervolgens op de Surface te krijgen. Ik heb geprobeerd met GetDC de device context handle van de DirectDraw surface op te vragen, en vervolgens de screenshot daarop te plakken, maar dat gaat op één of andere manier mis. Ik krijg geen foutmeldingen, maar er gebeurt ook niks.
Hier is de code tot nu toe. Ik heb de API-declaraties ff weggelaten.
Door middel van:
...wordt de screenshot opgeslagen op de C-schijf. Dan het stukje DDraw:
Eigenlijk kom ik hier in de problemen. Voorzover ik begrijp is er nu een primary surface getekend, en was er zojuist ook een screenshot in het geheugen. Nu moet ik de één dus op de ander zien te tekenen, en dan ben ik er. Toch?
Ik hoop dat iemand me een eindje verder kan helpen door uit te leggen hoe ik deze twee eindjes aan elkaar knoop.
Ik probeer iets te bereiken dat op het eerste gezicht heel raar klinkt, maar ondanks dat toch makkelijk te realiseren zou moeten zijn.
Wij gebruiken een bepaalde applicatie, genaamd Scala, een soort van PowerPoint-achtig pakket. De output van Scala wordt gewoon geblit op het scherm zonder een hardware overlay (surface). Voor ons is dat een probleem, omdat we heel graag de Feature Display van onze Matrox Parhelia-videokaart willen gebruiken. De Feature Display is feitelijk de TV-out, die een hoge kwaliteit composite-output levert. De Matrox-drivers zetten alles wat op een hardware overlay getekend is door op de Feature Display. Je raadt het al: aangezien Scala geen hardware overlay gebruikt, verschijnen onze mooie presentaties ook niet via de Feature Display op de TV-out.
De truc die ik wil toepassen met een klein extern programma'tje is dus:
1. Doe CreateSurface (overlay maken)
2. Maak een screenshot van Scala
3. Teken de screenshot op de overlay
4. Herhaal stappen 2 en 3 minstens 25x per seconde
Een screenshot maken en dat vervolgens herhalen gaat moeiteloos; het systeem waarop het moet draaien maakt eenvoudig honderden screenshots per seconde.
Ik heb m'n best gedaan om me in te lezen in DirectDraw, maar ik kom er niet helemaal uit. Wat me tot nu op basis van webvoorbeelden is gelukt, is het opslaan van een screenshot op de harde schijf als bitmap. Vervolgens kan ik met CreateSurfaceFromFile de bitmap aanroepen, die vervolgens inderdaad op m'n scherm verschijnt als Surface.
Het probleem hierbij is natuurlijk de harde schijf: de screenshot zit in het geheugen, en het slaat nergens op dat ie eerst opgeslagen moet worden en dan van de harde schijf weer aangeroepen moet worden om vervolgens op de Surface te krijgen. Ik heb geprobeerd met GetDC de device context handle van de DirectDraw surface op te vragen, en vervolgens de screenshot daarop te plakken, maar dat gaat op één of andere manier mis. Ik krijg geen foutmeldingen, maar er gebeurt ook niks.
Hier is de code tot nu toe. Ik heb de API-declaraties ff weggelaten.
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| ' Capture the contents of a window or the entire screen Function GetScreenSnapshot(Optional ByVal hWnd As Long) As IPictureDisp Dim hDC As Long Dim tempPict As Long Dim oldPict As Long Dim wndWidth As Long Dim wndHeight As Long Dim Pic As PICTDESC Dim rcWindow As RECT Dim guid(3) As Long ' provide the right handle for the desktop window If hWnd = 0 Then hWnd = GetDesktopWindow ' get window's size GetWindowRect hWnd, rcWindow wndWidth = rcWindow.Right - rcWindow.Left wndHeight = rcWindow.Bottom - rcWindow.Top ' get window's device context targetDC = GetWindowDC(hWnd) ' create a compatible DC hDC = CreateCompatibleDC(targetDC) ' create a memory bitmap in the DC just created ' the has the size of the window we're capturing tempPict = CreateCompatibleBitmap(targetDC, wndWidth, wndHeight) oldPict = SelectObject(hDC, tempPict) ' copy the screen image into the DC BitBlt hDC, 0, 0, wndWidth, wndHeight, targetDC, 0, 0, vbSrcCopy ' set the old DC image and release the DC tempPict = SelectObject(hDC, oldPict) DeleteDC hDC ReleaseDC GetDesktopWindow, targetDC ' fill the ScreenPic structure With Pic .cbSize = Len(Pic) .pictType = 1 ' means picture .hIcon = tempPict .hPal = 0 ' (you can omit this of course) End With ' convert the image to a IpictureDisp object ' this is the IPicture GUID {7BF80980-BF32-101A-8BBB-00AA00300CAB} ' we use an array of Long to initialize it faster guid(0) = &H7BF80980 guid(1) = &H101ABF32 guid(2) = &HAA00BB8B guid(3) = &HAB0C3000 ' create the picture, ' return an object reference right into the function result OleCreatePictureIndirect Pic, guid(0), True, GetScreenSnapshot |
Door middel van:
Visual Basic:
1
| SavePicture GetScreenSnapShot, "C:\test.bmp" |
...wordt de screenshot opgeslagen op de C-schijf. Dan het stukje DDraw:
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| ''Get reference to new directx object Set objDx = New DirectX7 ''Create directdraw object Set objDraw7 = objDx.DirectDrawCreate("") ''Set initial cooperative level objDraw7.SetCooperativeLevel Me.hWnd, DDSCL_NORMAL 'Or DDSCL_EXCLUSIVE ''Set initial display mode 'objDraw7.SetDisplayMode 640, 480, 16, 0, DDSDM_DEFAULT 'weggecomment want ik wil geen resolutie switch ''Set primary surface ddsdPrim.lFlags = DDSD_CAPS ddsdPrim.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Set objPrimary = objDraw7.CreateSurface(ddsdPrim) ''Store the primary display properties objPrimary.GetSurfaceDesc ddsdPrim |
Eigenlijk kom ik hier in de problemen. Voorzover ik begrijp is er nu een primary surface getekend, en was er zojuist ook een screenshot in het geheugen. Nu moet ik de één dus op de ander zien te tekenen, en dan ben ik er. Toch?
Ik hoop dat iemand me een eindje verder kan helpen door uit te leggen hoe ik deze twee eindjes aan elkaar knoop.