Browse Source

Make DPI scaling work again.

scenegraph-after-breakage
Steven Kirk 10 years ago
parent
commit
a967c9bf82
  1. 3
      src/Avalonia.Controls/TopLevel.cs
  2. 17
      src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs
  3. 8
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  4. 10
      src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs
  5. 26
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  6. 2
      src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs
  7. 2
      src/Avalonia.Visuals/Rendering/IRenderRoot.cs
  8. 11
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  9. 4
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  10. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  11. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  12. 2
      src/Gtk/Avalonia.Cairo/CairoPlatform.cs
  13. 8
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  14. 4
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  15. 15
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  16. 23
      src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
  17. 8
      src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
  18. 2
      src/Windows/Avalonia.Direct2D1/packages.config
  19. 1
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  20. 1
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  21. 2
      tests/Avalonia.UnitTests/TestRoot.cs
  22. 2
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs
  23. 12
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  24. 6
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

3
src/Avalonia.Controls/TopLevel.cs

@ -220,6 +220,9 @@ namespace Avalonia.Controls
/// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling;
/// <inheritdoc/>
double IRenderRoot.RenderScaling => PlatformImpl.Scaling;
IStyleHost IStyleHost.StylingParent
{
get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }

17
src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs

@ -3,7 +3,6 @@
using System;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Media.Imaging
@ -16,10 +15,12 @@ namespace Avalonia.Media.Imaging
/// <summary>
/// Initializes a new instance of the <see cref="RenderTargetBitmap"/> class.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
public RenderTargetBitmap(int width, int height)
: base(CreateImpl(width, height))
/// <param name="pixelWidth">The width of the bitmap in pixels.</param>
/// <param name="pixelHeight">The height of the bitmap in pixels.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
public RenderTargetBitmap(int pixelWidth, int pixelHeight, double dpiX = 96, double dpiY = 96)
: base(CreateImpl(pixelWidth, pixelHeight, dpiX, dpiY))
{
}
@ -41,11 +42,13 @@ namespace Avalonia.Media.Imaging
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
/// <returns>The platform-specific implementation.</returns>
private static IBitmapImpl CreateImpl(int width, int height)
private static IBitmapImpl CreateImpl(int width, int height, double dpiX, double dpiY)
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return factory.CreateRenderTargetBitmap(width, height);
return factory.CreateRenderTargetBitmap(width, height, dpiX, dpiY);
}
public IDrawingContextImpl CreateDrawingContext() => PlatformImpl.CreateDrawingContext();

8
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -51,8 +51,14 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="dpiX">The horizontal DPI of the bitmap.</param>
/// <param name="dpiY">The vertical DPI of the bitmap.</param>
/// <returns>An <see cref="IRenderTargetBitmapImpl"/>.</returns>
IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height);
IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY);
/// <summary>
/// Loads a bitmap implementation from a file..

10
src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs

@ -18,11 +18,17 @@ namespace Avalonia.Rendering
_renderInterface = renderInterface;
}
public IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size)
public IRenderTargetBitmapImpl CreateLayer(
IVisual layerRoot,
Size size,
double dpiX,
double dpiY)
{
return _renderInterface.CreateRenderTargetBitmap(
(int)Math.Ceiling(size.Width),
(int)Math.Ceiling(size.Height));
(int)Math.Ceiling(size.Height),
dpiX,
dpiY);
}
}
}

26
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -166,7 +166,7 @@ namespace Avalonia.Rendering
{
if (DrawFps || DrawDirtyRects)
{
var overlay = GetOverlay(scene.Size);
var overlay = GetOverlay(scene.Size, scene.Scaling);
using (var context = overlay.CreateDrawingContext())
{
@ -240,13 +240,15 @@ namespace Avalonia.Rendering
foreach (var layer in scene.Layers)
{
var renderLayer = _layers[layer.LayerRoot];
context.DrawImage(renderLayer.Bitmap, layer.Opacity, clientRect, clientRect);
var bitmap = _layers[layer.LayerRoot].Bitmap;
var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
}
if (_overlay != null)
{
context.DrawImage(_overlay, 0.5, clientRect, clientRect);
var sourceRect = new Rect(0, 0, _overlay.PixelWidth, _overlay.PixelHeight);
context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
}
}
}
@ -316,15 +318,16 @@ namespace Avalonia.Rendering
Render(scene);
}
private IRenderTargetBitmapImpl GetOverlay(Size size)
private IRenderTargetBitmapImpl GetOverlay(Size size, double scaling)
{
int width = (int)Math.Ceiling(size.Width);
int height = (int)Math.Ceiling(size.Height);
size = new Size(size.Width * scaling, size.Height * scaling);
if (_overlay == null || _overlay.PixelWidth != width || _overlay.PixelHeight != height)
if (_overlay == null ||
_overlay.PixelWidth != size.Width ||
_overlay.PixelHeight != size.Height)
{
_overlay?.Dispose();
_overlay = _layerFactory.CreateLayer(null, size);
_overlay = _layerFactory.CreateLayer(null, size, 96 * scaling, 96 * scaling);
}
return _overlay;
@ -332,18 +335,19 @@ namespace Avalonia.Rendering
private IRenderTargetBitmapImpl GetRenderTargetForLayer(Scene scene, IVisual layerRoot)
{
var size = new Size(scene.Size.Width * scene.Scaling, scene.Size.Height * scene.Scaling);
RenderLayer result;
if (_layers.TryGetValue(layerRoot, out result))
{
if (result.Size != scene.Size)
{
result.ResizeBitmap(scene.Size);
result.ResizeBitmap(size, scene.Scaling);
}
}
else
{
_layers.Add(layerRoot, scene.Size);
_layers.Add(layerRoot, size, scene.Scaling);
}
return _layers[layerRoot].Bitmap;

2
src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs

@ -6,6 +6,6 @@ namespace Avalonia.Rendering
{
public interface IRenderLayerFactory
{
IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size);
IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size, double dpiX, double dpiY);
}
}

2
src/Avalonia.Visuals/Rendering/IRenderRoot.cs

@ -22,6 +22,8 @@ namespace Avalonia.Rendering
/// </summary>
IRenderer Renderer { get; }
double RenderScaling { get; }
/// <summary>
/// Creates a render target for the window.
/// </summary>

11
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -12,23 +12,26 @@ namespace Avalonia.Rendering
public RenderLayer(
IRenderLayerFactory factory,
Size size,
double scaling,
IVisual layerRoot)
{
_factory = factory;
Bitmap = factory.CreateLayer(layerRoot, size);
Bitmap = factory.CreateLayer(layerRoot, size, 96 * scaling, 96 * scaling);
Size = size;
Scaling = scaling;
LayerRoot = layerRoot;
}
public IRenderTargetBitmapImpl Bitmap { get; private set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }
public IVisual LayerRoot { get; }
public void ResizeBitmap(Size size)
public void ResizeBitmap(Size size, double scaling)
{
if (Size != size)
if (Size != size || Scaling != scaling)
{
var resized = _factory.CreateLayer(LayerRoot, size);
var resized = _factory.CreateLayer(LayerRoot, size, 96 * scaling, 96 * scaling);
using (var context = resized.CreateDrawingContext())
{

4
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@ -20,13 +20,13 @@ namespace Avalonia.Rendering
public int Count => _inner.Count;
public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
public RenderLayer Add(IVisual layerRoot, Size size)
public RenderLayer Add(IVisual layerRoot, Size size, double scaling)
{
RenderLayer result;
if (!_index.TryGetValue(layerRoot, out result))
{
result = new RenderLayer(_factory, size, layerRoot);
result = new RenderLayer(_factory, size, scaling, layerRoot);
_inner.Add(result);
_index.Add(layerRoot, result);
}

2
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -34,6 +34,7 @@ namespace Avalonia.Rendering.SceneGraph
public SceneLayers Layers { get; }
public IVisualNode Root { get; }
public Size Size { get; set; }
public double Scaling { get; set; } = 1;
public void Add(IVisualNode node)
{
@ -50,6 +51,7 @@ namespace Avalonia.Rendering.SceneGraph
var result = new Scene(root, index, Layers.Clone(), Id + 1)
{
Size = Size,
Scaling = Scaling,
};
return result;

7
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -16,7 +16,7 @@ namespace Avalonia.Rendering.SceneGraph
Contract.Requires<ArgumentNullException>(scene != null);
Dispatcher.UIThread.VerifyAccess();
scene.Size = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
UpdateSize(scene);
scene.Layers.GetOrAdd(scene.Root.Visual);
using (var impl = new DeferredDrawingContextImpl(scene.Layers))
@ -203,7 +203,10 @@ namespace Avalonia.Rendering.SceneGraph
private void UpdateSize(Scene scene)
{
var newSize = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
var renderRoot = scene.Root.Visual as IRenderRoot;
var newSize = renderRoot?.ClientSize ?? scene.Root.Visual.Bounds.Size;
scene.Scaling = renderRoot?.RenderScaling ?? 1;
if (scene.Size != newSize)
{

2
src/Gtk/Avalonia.Cairo/CairoPlatform.cs

@ -71,7 +71,7 @@ namespace Avalonia.Cairo
handle.HandleDescriptor));
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height, double dpiX, double dpiY)
{
return new RenderTargetBitmapImpl(new ImageSurface(Format.Argb32, width, height));
}

8
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -11,7 +11,7 @@ namespace Avalonia.Skia
{
public IBitmapImpl CreateBitmap(int width, int height)
{
return CreateRenderTargetBitmap(width, height);
return CreateRenderTargetBitmap(width, height, 96, 96);
}
public IFormattedTextImpl CreateFormattedText(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
@ -54,7 +54,11 @@ namespace Avalonia.Skia
return new Renderer(root, renderLoop);
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY)
{
if (width < 1)
throw new ArgumentException("Width can't be less than 1", nameof(width));

4
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -41,8 +41,8 @@
<HintPath>..\..\..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpDX.Direct2D1, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SharpDX.Direct2D1.3.1.0\lib\net45\SharpDX.Direct2D1.dll</HintPath>
<Reference Include="SharpDX.Direct2D1, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\SharpDX.Direct2D1.3.1.1\lib\net45\SharpDX.Direct2D1.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpDX.Direct3D11, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">

15
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -93,9 +93,20 @@ namespace Avalonia.Direct2D1
}
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY)
{
return new RenderTargetBitmapImpl(s_imagingFactory, s_d2D1Factory, s_dwfactory, width, height);
return new RenderTargetBitmapImpl(
s_imagingFactory,
s_d2D1Factory,
s_dwfactory,
width,
height,
dpiX,
dpiY);
}
public IStreamGeometryImpl CreateStreamGeometry()

23
src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs

@ -17,8 +17,8 @@ namespace Avalonia.Direct2D1
private readonly SharpDX.Direct3D11.Device _d3dDevice;
private DeviceContext _deviceContext;
private SharpDX.DXGI.SwapChain1 _swapChain;
private Size2 _savedSize;
private Size2F _savedDpi;
private Size2 _size;
private Size2F _dpi;
/// <summary>
/// Initializes a new instance of the <see cref="HwndRenderTarget"/> class.
@ -48,7 +48,9 @@ namespace Avalonia.Direct2D1
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport,
featureLevels);
CreateSwapChain();
var size = GetWindowSize();
var dpi = GetWindowDpi();
CreateSwapChain(size, dpi);
}
/// <summary>
@ -76,11 +78,9 @@ namespace Avalonia.Direct2D1
var size = GetWindowSize();
var dpi = GetWindowDpi();
if (size != _savedSize || dpi != _savedDpi)
if (size != _size || _dpi != dpi)
{
_savedSize = size;
_savedDpi = dpi;
CreateSwapChain();
CreateSwapChain(size, dpi);
}
return new DrawingContextImpl(_deviceContext, DirectWriteFactory, _swapChain);
@ -93,8 +93,11 @@ namespace Avalonia.Direct2D1
_d3dDevice.Dispose();
}
private void CreateSwapChain()
private void CreateSwapChain(Size2 size, Size2F dpi)
{
_size = size;
_dpi = dpi;
using (var dxgiDevice = _d3dDevice.QueryInterface<SharpDX.DXGI.Device>())
using (var d2dDevice = new Device(dxgiDevice))
using (var dxgiAdaptor = dxgiDevice.Adapter)
@ -102,6 +105,7 @@ namespace Avalonia.Direct2D1
{
_deviceContext?.Dispose();
_deviceContext = new DeviceContext(d2dDevice, DeviceContextOptions.None);
_deviceContext.DotsPerInch = dpi;
var swapChainDesc = new SharpDX.DXGI.SwapChainDescription1
{
@ -121,8 +125,6 @@ namespace Avalonia.Direct2D1
Flags = 0,
};
var dpi = Direct2DFactory.DesktopDpi;
_swapChain?.Dispose();
_swapChain = new SharpDX.DXGI.SwapChain1(dxgiFactory, _d3dDevice, _hwnd, ref swapChainDesc);
@ -144,7 +146,6 @@ namespace Avalonia.Direct2D1
}
}
}
private Size2F GetWindowDpi()
{
if (UnmanagedMethods.ShCoreAvailable)

8
src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs

@ -20,13 +20,15 @@ namespace Avalonia.Direct2D1.Media
Factory d2dFactory,
DirectWriteFactory dwriteFactory,
int width,
int height)
int height,
double dpiX,
double dpiY)
: base(imagingFactory, width, height)
{
var props = new RenderTargetProperties
{
DpiX = 96,
DpiY = 96,
DpiX = (float)dpiX,
DpiY = (float)dpiY,
};
_target = new WicRenderTarget(

2
src/Windows/Avalonia.Direct2D1/packages.config

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="SharpDX" version="3.1.1" targetFramework="net45" />
<package id="SharpDX.Direct2D1" version="3.1.0" targetFramework="net45" />
<package id="SharpDX.Direct2D1" version="3.1.1" targetFramework="net45" />
<package id="SharpDX.Direct3D11" version="3.1.1" targetFramework="net45" />
<package id="SharpDX.DXGI" version="3.1.1" targetFramework="net45" />
</packages>

1
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -318,6 +318,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public double RenderScaling => 1;
public IRenderTarget CreateRenderTarget()
{

1
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -993,6 +993,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public double RenderScaling => 1;
public IRenderTarget CreateRenderTarget()
{

2
tests/Avalonia.UnitTests/TestRoot.cs

@ -48,6 +48,8 @@ namespace Avalonia.UnitTests
public double LayoutScaling => 1;
public double RenderScaling => 1;
public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService<ILayoutManager>();
public IRenderTarget RenderTarget => null;

2
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@ -39,6 +39,8 @@ namespace Avalonia.UnitTests
public double LayoutScaling => 1;
public double RenderScaling => 1;
public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService<ILayoutManager>();
public IRenderTarget RenderTarget => null;

12
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -141,7 +141,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
});
var layers = new Mock<IRenderLayerFactory>();
layers.Setup(x => x.CreateLayer(root, root.ClientSize)).Returns(CreateLayer());
layers.Setup(x => x.CreateLayer(root, root.ClientSize, 96, 96)).Returns(CreateLayer());
var renderInterface = new Mock<IPlatformRenderInterface>();
@ -154,7 +154,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
RunFrame(loop);
layers.Verify(x => x.CreateLayer(root, root.ClientSize));
layers.Verify(x => x.CreateLayer(root, root.ClientSize, 96, 96));
}
[Fact]
@ -241,7 +241,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
private Mock<IRenderLayerFactory> MockLayerFactory(IRenderRoot root)
{
var result = new Mock<IRenderLayerFactory>();
result.Setup(x => x.CreateLayer(root, root.ClientSize)).Returns(CreateLayer());
result.Setup(x => x.CreateLayer(root, root.ClientSize, 96, 96)).Returns(CreateLayer());
return result;
}
@ -262,7 +262,11 @@ namespace Avalonia.Visuals.UnitTests.Rendering
_layers = layers;
}
public IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size)
public IRenderTargetBitmapImpl CreateLayer(
IVisual layerRoot,
Size size,
double dpiX,
double dpiY)
{
return _layers[layerRoot];
}

6
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@ -26,7 +26,11 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
throw new NotImplementedException();
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(
int width,
int height,
double dpiX,
double dpiY)
{
throw new NotImplementedException();
}

Loading…
Cancel
Save