Browse Source

Merge branch 'master' into features/child-windows-api

pull/3943/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
4f6bdd59d0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 44
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  2. 26
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  3. 19
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  4. 73
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  5. 42
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  6. 18
      src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs
  7. 24
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  8. 66
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  9. 16
      tests/Avalonia.Benchmarks/NullRenderingPlatform.cs
  10. 16
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  11. 16
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

44
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -11,6 +11,46 @@ namespace Avalonia.Media.Imaging
/// </summary>
public class Bitmap : IBitmap
{
/// <summary>
/// Loads a Bitmap from a stream and decodes at the desired width. Aspect ratio is maintained.
/// This is more efficient than loading and then resizing.
/// </summary>
/// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
/// <param name="width">The desired width of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new Bitmap(factory.LoadBitmapToWidth(stream, width, interpolationMode));
}
/// <summary>
/// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained.
/// This is more efficient than loading and then resizing.
/// </summary>
/// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
/// <param name="height">The desired height of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new Bitmap(factory.LoadBitmapToHeight(stream, height, interpolationMode));
}
/// <summary>
/// Creates a Bitmap scaled to a specified size from the current bitmap.
/// </summary>
/// <param name="destinationSize">The destination size.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
/// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
return new Bitmap(factory.ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode));
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
@ -39,7 +79,7 @@ namespace Avalonia.Media.Imaging
{
PlatformImpl = impl.Clone();
}
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
@ -48,7 +88,7 @@ namespace Avalonia.Media.Imaging
{
PlatformImpl = RefCountable.Create(impl);
}
/// <inheritdoc/>
public virtual void Dispose()
{

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

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Platform
{
@ -87,17 +89,37 @@ namespace Avalonia.Platform
/// <summary>
/// Loads a bitmap implementation from a file..
/// </summary>
/// <param name="fileName">The filename of the bitmap.</param>
/// <param name="fileName">The filename of the bitmap.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmap(string fileName);
/// <summary>
/// Loads a bitmap implementation from a file..
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmap(Stream stream);
/// <summary>
/// Loads a bitmap implementation from a stream to a specified width maintaining aspect ratio.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <param name="width">The desired width of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should resizing be required.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality);
/// <summary>
/// Loads a bitmap implementation from a stream to a specified height maintaining aspect ratio.
/// </summary>
/// <param name="stream">The stream to read the bitmap from.</param>
/// <param name="height">The desired height of the resulting bitmap.</param>
/// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should resizing be required.</param>
/// <returns>An <see cref="IBitmapImpl"/>.</returns>
IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality);
IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality);
/// <summary>
/// Loads a bitmap implementation from a pixels in memory.
/// </summary>

19
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -124,29 +124,12 @@ namespace Avalonia.Skia
Color = new SKColor(255, 255, 255, (byte)(255 * opacity * _currentOpacity))
})
{
paint.FilterQuality = GetInterpolationMode(bitmapInterpolationMode);
paint.FilterQuality = bitmapInterpolationMode.ToSKFilterQuality();
drawableImage.Draw(this, s, d, paint);
}
}
private static SKFilterQuality GetInterpolationMode(BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case BitmapInterpolationMode.LowQuality:
return SKFilterQuality.Low;
case BitmapInterpolationMode.MediumQuality:
return SKFilterQuality.Medium;
case BitmapInterpolationMode.HighQuality:
return SKFilterQuality.High;
case BitmapInterpolationMode.Default:
return SKFilterQuality.None;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
}
/// <inheritdoc />
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{

73
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -1,7 +1,11 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Skia.Helpers;
using Avalonia.Visuals.Media.Imaging;
using SkiaSharp;
namespace Avalonia.Skia
@ -36,6 +40,75 @@ namespace Avalonia.Skia
}
}
public ImmutableBitmap(ImmutableBitmap src, PixelSize destinationSize, BitmapInterpolationMode interpolationMode)
{
SKImageInfo info = new SKImageInfo(destinationSize.Width, destinationSize.Height, SKColorType.Bgra8888);
SKImage output = SKImage.Create(info);
src._image.ScalePixels(output.PeekPixels(), interpolationMode.ToSKFilterQuality());
_image = output;
PixelSize = new PixelSize(_image.Width, _image.Height);
// TODO: Skia doesn't have an API for DPI.
Dpi = new Vector(96, 96);
}
public ImmutableBitmap(Stream stream, int decodeSize, bool horizontal, BitmapInterpolationMode interpolationMode)
{
using (var skStream = new SKManagedStream(stream))
using (var codec = SKCodec.Create(skStream))
{
var info = codec.Info;
// get the scale that is nearest to what we want (eg: jpg returned 512)
var supportedScale = codec.GetScaledDimensions(horizontal ? ((float)decodeSize / info.Width) : ((float)decodeSize / info.Height));
// decode the bitmap at the nearest size
var nearest = new SKImageInfo(supportedScale.Width, supportedScale.Height);
var bmp = SKBitmap.Decode(codec, nearest);
// now scale that to the size that we want
var realScale = horizontal ? ((double)info.Height / info.Width) : ((double)info.Width / info.Height);
SKImageInfo desired;
if (horizontal)
{
desired = new SKImageInfo(decodeSize, (int)(realScale * decodeSize));
}
else
{
desired = new SKImageInfo((int)(realScale * decodeSize), decodeSize);
}
if (bmp.Width != desired.Width || bmp.Height != desired.Height)
{
if (bmp.Height != bmp.Width)
{
}
var scaledBmp = bmp.Resize(desired, interpolationMode.ToSKFilterQuality());
bmp.Dispose();
bmp = scaledBmp;
}
_image = SKImage.FromBitmap(bmp);
bmp.Dispose();
if (_image == null)
{
throw new ArgumentException("Unable to load bitmap from provided data");
}
PixelSize = new PixelSize(_image.Width, _image.Height);
// TODO: Skia doesn't have an API for DPI.
Dpi = new Vector(96, 96);
}
}
/// <summary>
/// Create immutable bitmap from given pixel data copy.
/// </summary>

42
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -1,13 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO;
using System.Security.Cryptography;
using System.Linq;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
using SkiaSharp;
namespace Avalonia.Skia
@ -57,12 +60,6 @@ namespace Avalonia.Skia
return new StreamGeometryImpl();
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(Stream stream)
{
return new ImmutableBitmap(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(string fileName)
{
@ -72,12 +69,43 @@ namespace Avalonia.Skia
}
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(Stream stream)
{
return new ImmutableBitmap(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new ImmutableBitmap(size, dpi, stride, format, data);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new ImmutableBitmap(stream, width, true, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new ImmutableBitmap(stream, height, false, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
if (bitmapImpl is ImmutableBitmap ibmp)
{
return new ImmutableBitmap(ibmp, destinationSize, interpolationMode);
}
else
{
throw new Exception("Invalid source bitmap type.");
}
}
/// <inheritdoc />
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
{

18
src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs

@ -1,12 +1,30 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
using SkiaSharp;
namespace Avalonia.Skia
{
public static class SkiaSharpExtensions
{
public static SKFilterQuality ToSKFilterQuality(this BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case BitmapInterpolationMode.LowQuality:
return SKFilterQuality.Low;
case BitmapInterpolationMode.MediumQuality:
return SKFilterQuality.Medium;
case BitmapInterpolationMode.HighQuality:
return SKFilterQuality.High;
case BitmapInterpolationMode.Default:
return SKFilterQuality.None;
default:
throw new ArgumentOutOfRangeException(nameof(interpolationMode), interpolationMode, null);
}
}
public static SKPoint ToSKPoint(this Point p)
{
return new SKPoint((float)p.X, (float)p.Y);

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

@ -7,7 +7,9 @@ using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
using SharpDX.DirectWrite;
using GlyphRun = Avalonia.Media.GlyphRun;
using TextAlignment = Avalonia.Media.TextAlignment;
@ -179,16 +181,38 @@ namespace Avalonia.Direct2D1
public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
/// <inheritdoc />
public IBitmapImpl LoadBitmap(string fileName)
{
return new WicBitmapImpl(fileName);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(Stream stream)
{
return new WicBitmapImpl(stream);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WicBitmapImpl(stream, width, true, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new WicBitmapImpl(stream, height, false, interpolationMode);
}
/// <inheritdoc />
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
// https://github.com/sharpdx/SharpDX/issues/959 blocks implementation.
throw new NotImplementedException();
}
/// <inheritdoc />
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new WicBitmapImpl(format, data, size, dpi, stride);

66
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Security.Cryptography;
using Avalonia.Win32.Interop;
using SharpDX.WIC;
using APixelFormat = Avalonia.Platform.PixelFormat;
@ -14,6 +15,26 @@ namespace Avalonia.Direct2D1.Media
{
private BitmapDecoder _decoder;
private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode)
{
switch (interpolationMode)
{
case Visuals.Media.Imaging.BitmapInterpolationMode.Default:
return BitmapInterpolationMode.Fant;
case Visuals.Media.Imaging.BitmapInterpolationMode.LowQuality:
return BitmapInterpolationMode.NearestNeighbor;
case Visuals.Media.Imaging.BitmapInterpolationMode.MediumQuality:
return BitmapInterpolationMode.Fant;
default:
case Visuals.Media.Imaging.BitmapInterpolationMode.HighQuality:
return BitmapInterpolationMode.HighQualityCubic;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
@ -27,6 +48,12 @@ namespace Avalonia.Direct2D1.Media
}
}
private WicBitmapImpl(Bitmap bmp)
{
WicImpl = bmp;
Dpi = new Vector(96, 96);
}
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
/// </summary>
@ -60,7 +87,7 @@ namespace Avalonia.Direct2D1.Media
size.Height,
pixelFormat.Value.ToWic(),
BitmapCreateCacheOption.CacheOnLoad);
WicImpl.SetResolution(dpi.X, dpi.Y);
Dpi = dpi;
}
@ -84,6 +111,43 @@ namespace Avalonia.Direct2D1.Media
}
}
public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode)
{
_decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad);
var frame = _decoder.GetFrame(0);
// now scale that to the size that we want
var realScale = horizontal ? ((double)frame.Size.Height / frame.Size.Width) : ((double)frame.Size.Width / frame.Size.Height);
PixelSize desired;
if (horizontal)
{
desired = new PixelSize(decodeSize, (int)(realScale * decodeSize));
}
else
{
desired = new PixelSize((int)(realScale * decodeSize), decodeSize);
}
if (frame.Size.Width != desired.Width || frame.Size.Height != desired.Height)
{
using (var scaler = new BitmapScaler(Direct2D1Platform.ImagingFactory))
{
scaler.Initialize(frame, desired.Width, desired.Height, ConvertInterpolationMode(interpolationMode));
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, scaler, BitmapCreateCacheOption.CacheOnLoad);
}
}
else
{
WicImpl = new Bitmap(Direct2D1Platform.ImagingFactory, frame, BitmapCreateCacheOption.CacheOnLoad);
}
Dpi = new Vector(96, 96);
}
public override Vector Dpi { get; }
public override PixelSize PixelSize => WicImpl.Size.ToAvalonia();

16
tests/Avalonia.Benchmarks/NullRenderingPlatform.cs

@ -4,6 +4,7 @@ using System.IO;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Benchmarks
{
@ -65,6 +66,21 @@ namespace Avalonia.Benchmarks
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IFontManagerImpl CreateFontManager()
{
return new MockFontManagerImpl();

16
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Visuals.Media.Imaging;
using Moq;
namespace Avalonia.UnitTests
@ -69,6 +70,21 @@ namespace Avalonia.UnitTests
return Mock.Of<IBitmapImpl>();
}
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return Mock.Of<IBitmapImpl>();
}
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return Mock.Of<IBitmapImpl>();
}
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return Mock.Of<IBitmapImpl>();
}
public IBitmapImpl LoadBitmap(
PixelFormat format,
IntPtr data,

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

@ -4,6 +4,7 @@ using System.IO;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Visuals.UnitTests.VisualTree
{
@ -83,6 +84,21 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
throw new NotImplementedException();
}
class MockStreamGeometry : IStreamGeometryImpl
{
private MockStreamGeometryContext _impl = new MockStreamGeometryContext();

Loading…
Cancel
Save