Ik heb een WPF applicatie die een redelijk complexe view laat zien met verschillende animaties etc. Mijn doel is om deze view in real-time, met ~60 fps, om te zetten naar een BitmapSource. Deze bitmap wordt vervolgens over het netwerk doorgestuurd als video source voor NDI.
Momenteel gebruik ik de RenderTargetBitmap Render method om de Visual om te zetten naar een BitmapSource. Hoewel dit prima werkt, is het helaas niet snel genoeg in veel gevallen. Enkel met een simpele view met een paar simpele elementen haal ik iets wat richting de 60 fps lijkt, maar met iets complexe scene is het al snel ~5 fps of nog minder.
Ik heb zitten timen waar het probleem ligt en het zit overduidelijk in de RenderTargetBitmap call. Deze duurt voor een simpele scene < 1 ms, maar zodra ik mijn iets complexe scene laat zien gaat het al richting ~250 ms en is de richtlijn van 60 fps dus compleet onhaalbaar.
Ik ben dus op zoek naar een andere manier om een willekeurige Visual / UIElement naar een BitmapSource te renderen, welke sneller is dan de RenderTargetBitmap. Daarnaast moet het ook werken in een transparent window en moet het ook de semi-transparency van elementen kunnen opnemen.
Na wat zoeken heb ik twee mogelijke opties gevonden:
1. https://stackoverflow.com...-a-wpf-visual-to-a-bitmap
Dit topic laat zien hoe je een Visual naar een Bitmap kan renderen. Het werkt ook nog Async maar dat is verder niet zo belangrijk.
Dit heb ik min of meer werkend gekregen na wat aanpassingen om het iets simpeler te maken (bijv async eruit):
Dit lijkt OK te werken in een normaal window maar twee problemen:
- Hij neemt de complete Window op en niet alleen de Visual in de Window. Dit is nog wel te overzien want de Window waarin mijn Visual zit is in principe toch leeg.
- Groter probleem: het werkt niet meer zodra ik mijn Window transparent maak via "AllowsTransparency=True". Na deze aanpassing krijg ik enkel een zwart beeld terug. Zonder transparency werkt het wel en is het ook veel sneller dan RenderTargetBitmap
2. Ik zie hier en daar wat dingen langs komen om DirectX renderer te gebruiken voor een soort screen capture, ipv RenderTargetBitmap. Maar ik kan nergens een complete source of uitleg vinden en dit soort dingen heb ik absoluut geen ervaring mee, dus hier loop ik al snel vast. Maar het klinkt ergens wel veelbelovend...
Een aantal opties worden hier genoemd: MSDN: RenderTargetBitmap and Hardware WPF Rendering
Mijn specifieke vraag is dus eigenlijk of iemand een alternatief voor RenderTargetBitmap kent, welke sneller is en ook nog werkt met transparent windows? Mijn eerste gevonden oplossing hierboven lijkt wel snel genoeg maar werkt helaas niet met een transparent window, wellicht weet iemand daar een oplossing voor?
Een commercieel product is ook te overwegen (afhankelijk van de prijs) maar ook daar kan ik niks van vinden helaas.
Bedankt voor enige input!
Momenteel gebruik ik de RenderTargetBitmap Render method om de Visual om te zetten naar een BitmapSource. Hoewel dit prima werkt, is het helaas niet snel genoeg in veel gevallen. Enkel met een simpele view met een paar simpele elementen haal ik iets wat richting de 60 fps lijkt, maar met iets complexe scene is het al snel ~5 fps of nog minder.
Ik heb zitten timen waar het probleem ligt en het zit overduidelijk in de RenderTargetBitmap call. Deze duurt voor een simpele scene < 1 ms, maar zodra ik mijn iets complexe scene laat zien gaat het al richting ~250 ms en is de richtlijn van 60 fps dus compleet onhaalbaar.
Ik ben dus op zoek naar een andere manier om een willekeurige Visual / UIElement naar een BitmapSource te renderen, welke sneller is dan de RenderTargetBitmap. Daarnaast moet het ook werken in een transparent window en moet het ook de semi-transparency van elementen kunnen opnemen.
Na wat zoeken heb ik twee mogelijke opties gevonden:
1. https://stackoverflow.com...-a-wpf-visual-to-a-bitmap
Dit topic laat zien hoe je een Visual naar een Bitmap kan renderen. Het werkt ook nog Async maar dat is verder niet zo belangrijk.
Dit heb ik min of meer werkend gekregen na wat aanpassingen om het iets simpeler te maken (bijv async eruit):
C#:
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
| public static BitmapSource Render(this UIElement visual, int width, int height) { var src = (HwndSource)PresentationSource.FromVisual(visual); if (src == null) return null; BitmapSource bitmapSourceT = Render(src, width, height); bitmapSourceT.Freeze(); return bitmapSourceT; } private static BitmapSource Render(HwndSource src, int width, int height) { Bitmap bitmap = VisualToBitmapConverter.GetBitmap(src.Handle, width, height); return Convert(bitmap); } public static class VisualToBitmapConverter { private enum TernaryRasterOperations : uint { SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, SRCAND = 0x008800C6, SRCINVERT = 0x00660046, SRCERASE = 0x00440328, NOTSRCCOPY = 0x00330008, NOTSRCERASE = 0x001100A6, MERGECOPY = 0x00C000CA, MERGEPAINT = 0x00BB0226, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, PATINVERT = 0x005A0049, DSTINVERT = 0x00550009, BLACKNESS = 0x00000042, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000 } [DllImport("gdi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); public static Bitmap GetBitmap(IntPtr hwnd, int width, int height) { var bitmap = new Bitmap(width, height); using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwnd)) { using (Graphics graphicsFromImage = Graphics.FromImage(bitmap)) { IntPtr source = graphicsFromVisual.GetHdc(); IntPtr destination = graphicsFromImage.GetHdc(); BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY); graphicsFromVisual.ReleaseHdc(source); graphicsFromImage.ReleaseHdc(destination); } } return bitmap; } } [DllImport("gdi32")] static extern int DeleteObject(IntPtr o); public static BitmapSource Convert(System.Drawing.Bitmap source) { IntPtr ip = source.GetHbitmap(); BitmapSource bs = null; try { bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); } finally { DeleteObject(ip); } return bs; } } |
Dit lijkt OK te werken in een normaal window maar twee problemen:
- Hij neemt de complete Window op en niet alleen de Visual in de Window. Dit is nog wel te overzien want de Window waarin mijn Visual zit is in principe toch leeg.
- Groter probleem: het werkt niet meer zodra ik mijn Window transparent maak via "AllowsTransparency=True". Na deze aanpassing krijg ik enkel een zwart beeld terug. Zonder transparency werkt het wel en is het ook veel sneller dan RenderTargetBitmap
2. Ik zie hier en daar wat dingen langs komen om DirectX renderer te gebruiken voor een soort screen capture, ipv RenderTargetBitmap. Maar ik kan nergens een complete source of uitleg vinden en dit soort dingen heb ik absoluut geen ervaring mee, dus hier loop ik al snel vast. Maar het klinkt ergens wel veelbelovend...
Een aantal opties worden hier genoemd: MSDN: RenderTargetBitmap and Hardware WPF Rendering
Mijn specifieke vraag is dus eigenlijk of iemand een alternatief voor RenderTargetBitmap kent, welke sneller is en ook nog werkt met transparent windows? Mijn eerste gevonden oplossing hierboven lijkt wel snel genoeg maar werkt helaas niet met een transparent window, wellicht weet iemand daar een oplossing voor?
Een commercieel product is ook te overwegen (afhankelijk van de prijs) maar ook daar kan ik niks van vinden helaas.
Bedankt voor enige input!