Skip to content

Commit 2940bfb

Browse files
authored
Pixel view refactoring (#506)
1 parent 76f4768 commit 2940bfb

File tree

2 files changed

+47
-70
lines changed

2 files changed

+47
-70
lines changed

LottieViewer/MainPage.xaml.cs

+1-17
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,7 @@ async Task OnPickFileAsync()
193193
{
194194
// Loading succeeded, start playing.
195195
_playStopButton.IsChecked = true;
196-
197-
// Loading succeeded, update pixel view resolution.
198-
_pixelView.OnResolutionUpdated((int)_stage.PlayerContainer.ActualWidth, (int)_stage.PlayerContainer.ActualHeight);
199-
}
196+
}
200197
}
201198
finally
202199
{
@@ -290,9 +287,6 @@ async void LottieDropHandler(object sender, DragEventArgs e)
290287
{
291288
// Loading succeeded, start playing.
292289
_playStopButton.IsChecked = true;
293-
294-
// Loading succeeded, update pixel view resolution.
295-
_pixelView.OnResolutionUpdated((int)_stage.PlayerContainer.ActualWidth, (int)_stage.PlayerContainer.ActualHeight);
296290
}
297291
}
298292

@@ -421,11 +415,6 @@ void ControlPanelButtonChecked(object sender, RoutedEventArgs e)
421415
ControlPanel.Children.Add(ColorPanel);
422416
}
423417

424-
// PixelView should be active only when Info page is open.
425-
// Otherwise changing surface that is not yet on the screen
426-
// will throw an exception.
427-
_pixelView.Active = sender == InfoButton;
428-
429418
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsControlPanelVisible)));
430419
}
431420

@@ -438,11 +427,6 @@ void ControlPanelButtonUnchecked(object sender, RoutedEventArgs e)
438427
// trigger the PaneThemeTransition so that the pane slides in and out.
439428
ControlPanel.Children.Clear();
440429

441-
// PixelView should be active only when Info page is open.
442-
// Otherwise changing surface that is not yet on the screen
443-
// will throw an exception.
444-
_pixelView.Active = false;
445-
446430
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsControlPanelVisible)));
447431
}
448432

LottieViewer/PixelViewElement.cs

+46-53
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,20 @@ public sealed class PixelViewElement : UserControl
3333
readonly SpriteVisual _spriteVisual;
3434
Visual? _capturedVisual = null;
3535

36-
// Indicates if PixelView should update the image.
37-
public bool Active { get; set; } = false;
38-
3936
// Checkerboard pattern bitmap.
4037
static CanvasBitmap? _patternBitmap = null;
4138

4239
// Previously rendered bitmap info.
43-
CanvasBitmap? _previousBitmap = null;
44-
Color[]? _previousColors = null;
40+
CanvasBitmap? _currentBitmap = null;
41+
Color[]? _currentColors = null;
4542

4643
Color? _currentColor = null;
4744

48-
Direct3D11CaptureFramePool? _framePool = null;
4945
GraphicsCaptureSession? _session = null;
50-
CanvasDevice? _canvasDevice = null;
46+
Direct3D11CaptureFramePool _framePool;
47+
ICompositionSurface _surface;
48+
CanvasSwapChain _swapchain;
49+
CanvasDevice _canvasDevice;
5150

5251
public static readonly DependencyProperty CurrentColorStringProperty =
5352
DependencyProperty.RegisterAttached(
@@ -85,6 +84,19 @@ public PixelViewElement()
8584
_spriteVisual = compositor.CreateSpriteVisual();
8685
_spriteVisual.RelativeSizeAdjustment = Vector2.One;
8786
ElementCompositionPreview.SetElementChildVisual(this, _spriteVisual);
87+
88+
_canvasDevice = CanvasDevice.GetSharedDevice();
89+
90+
// Swapchain will be resized later
91+
_swapchain = new CanvasSwapChain(_canvasDevice, 512, 512, 96);
92+
_surface = CanvasComposition.CreateCompositionSurfaceForSwapChain(compositor, _swapchain);
93+
_spriteVisual.Brush = compositor.CreateSurfaceBrush(_surface);
94+
95+
// Create a frame pool with room for only 1 frame because we're getting a single frame, not a video.
96+
_framePool =
97+
Direct3D11CaptureFramePool.Create(_canvasDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 3, new SizeInt32 { Width = 512, Height = 512 });
98+
99+
_framePool.FrameArrived += OnFrameArrived;
88100
}
89101

90102
public void SetElementToCapture(FrameworkElement element)
@@ -96,58 +108,63 @@ public void SetElementToCapture(FrameworkElement element)
96108
_capturedVisual.BorderMode = CompositionBorderMode.Soft;
97109

98110
OnResolutionUpdated((int)element.ActualWidth, (int)element.ActualHeight);
111+
112+
_session = _framePool.CreateCaptureSession(GraphicsCaptureItem.CreateFromVisual(_capturedVisual));
113+
_session.StartCapture();
99114
}
100115

101116
public void OnMouseMove(object sender, PointerRoutedEventArgs e)
102117
{
103118
var position = e.GetCurrentPoint(this).Position;
104119

105-
if (_previousBitmap == null || ActualHeight <= 0)
120+
if (_currentBitmap == null || ActualHeight <= 0)
106121
{
107122
return;
108123
}
109124

110-
int pixelX = (int)Math.Floor(position.X * (_previousBitmap.Size.Height / ActualHeight));
111-
int pixelY = (int)Math.Floor(position.Y * (_previousBitmap.Size.Width / ActualWidth));
125+
int pixelX = (int)Math.Floor(position.X * (_currentBitmap.Size.Height / ActualHeight));
126+
int pixelY = (int)Math.Floor(position.Y * (_currentBitmap.Size.Width / ActualWidth));
112127

113-
if (pixelX < 0 || pixelX >= _previousBitmap.SizeInPixels.Width || pixelY < 0 || pixelY >= _previousBitmap.SizeInPixels.Height)
128+
if (pixelX < 0 || pixelX >= _currentBitmap.SizeInPixels.Width || pixelY < 0 || pixelY >= _currentBitmap.SizeInPixels.Height)
114129
{
115130
return;
116131
}
117132

118-
if (_previousColors == null)
133+
if (_currentColors == null)
119134
{
120-
_previousColors = _previousBitmap.GetPixelColors();
135+
_currentColors = _currentBitmap.GetPixelColors();
121136
}
122137

123-
_currentColor = _previousColors[(pixelY * _previousBitmap.SizeInPixels.Width) + pixelX];
138+
_currentColor = _currentColors[(pixelY * _currentBitmap.SizeInPixels.Width) + pixelX];
124139
SetValue(CurrentColorStringProperty, _currentColor.ToString());
125140
SetValue(CurrentPositionProperty, new Vector2(pixelX, pixelY));
126141
}
127142

128143
async Task ShowBitmapOnTargetAsync(CanvasBitmap bitmap)
129144
{
130-
int scale = (int)Math.Ceiling(ActualSize.Y / bitmap.Size.Height);
145+
int scale = Math.Max((int)Math.Ceiling(ActualSize.Y / bitmap.Size.Height), 1);
131146

132147
Height = bitmap.Size.Height / bitmap.Size.Width * ActualSize.X;
133148

134-
_previousBitmap = bitmap;
135-
_previousColors = null;
149+
_currentBitmap?.Dispose();
150+
_currentBitmap = bitmap;
151+
_currentColors = null;
136152

137153
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
138-
var compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, CanvasDevice.GetSharedDevice());
139154

140155
if (_patternBitmap is null)
141156
{
142157
_patternBitmap = await CanvasBitmap.LoadAsync(CanvasDevice.GetSharedDevice(), @"Assets\BackgroundPattern.png");
143158
}
144159

145-
var surface = compositionGraphicsDevice.CreateDrawingSurface(
146-
new Size(bitmap.Size.Width * scale, bitmap.Size.Height * scale),
147-
DirectXPixelFormat.B8G8R8A8UIntNormalized,
148-
DirectXAlphaMode.Premultiplied);
160+
var size = new Size(bitmap.Size.Width * scale, bitmap.Size.Height * scale);
161+
162+
if (_swapchain.Size.Width != size.Width || _swapchain.Size.Height != size.Height)
163+
{
164+
_swapchain.ResizeBuffers(size);
165+
}
149166

150-
using (var drawingSession = CanvasComposition.CreateDrawingSession(surface))
167+
using (var drawingSession = _swapchain!.CreateDrawingSession(Colors.Transparent))
151168
{
152169
var border = new BorderEffect
153170
{
@@ -168,19 +185,15 @@ async Task ShowBitmapOnTargetAsync(CanvasBitmap bitmap)
168185
drawingSession.DrawImage(scaleEffect);
169186
}
170187

171-
var surfaceBrush = compositor.CreateSurfaceBrush(surface);
172-
_spriteVisual.Brush = surfaceBrush;
188+
_swapchain.Present();
173189
}
174190

175191
void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
176192
{
177-
if (Active)
193+
using (var frame = sender.TryGetNextFrame())
178194
{
179-
using (var frame = sender.TryGetNextFrame())
180-
{
181-
CanvasBitmap bitmap = CanvasBitmap.CreateFromDirect3D11Surface(_canvasDevice!, frame.Surface);
182-
_ = ShowBitmapOnTargetAsync(bitmap);
183-
}
195+
CanvasBitmap bitmap = CanvasBitmap.CreateFromDirect3D11Surface(_canvasDevice!, frame.Surface);
196+
_ = ShowBitmapOnTargetAsync(bitmap);
184197
}
185198
}
186199

@@ -189,32 +202,12 @@ public void OnResolutionUpdated(int width, int height)
189202

190203
public void UpdateResolution(SizeInt32 size)
191204
{
192-
if (size.Height <= 0 || size.Width <= 0)
205+
if (size.Width == 0 || size.Height == 0)
193206
{
194207
return;
195208
}
196209

197-
if (_framePool is not null && _session is not null)
198-
{
199-
// Unsubscribe from old framePool
200-
_framePool.FrameArrived -= OnFrameArrived;
201-
202-
// Dispose old session and framePool
203-
_session.Dispose();
204-
_framePool.Dispose();
205-
}
206-
207-
_canvasDevice = CanvasDevice.GetSharedDevice();
208-
209-
// Create a frame pool with room for only 1 frame because we're getting a single frame, not a video.
210-
_framePool =
211-
Direct3D11CaptureFramePool.Create(_canvasDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 1, size);
212-
213-
_session = _framePool.CreateCaptureSession(GraphicsCaptureItem.CreateFromVisual(_capturedVisual));
214-
215-
_framePool.FrameArrived += OnFrameArrived;
216-
217-
_session.StartCapture();
210+
_framePool.Recreate(_canvasDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 3, new SizeInt32 { Width = size.Width, Height = size.Height });
218211
}
219212
}
220213
}

0 commit comments

Comments
 (0)