Skip to content

Commit 37aefc4

Browse files
committed
Use Accelerate framework for texture image processing
1 parent ffdbdca commit 37aefc4

File tree

1 file changed

+60
-7
lines changed

1 file changed

+60
-7
lines changed

osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
using System;
5+
using System.Diagnostics;
56
using System.IO;
7+
using System.Runtime.InteropServices;
8+
using Accelerate;
69
using CoreGraphics;
710
using Foundation;
11+
using ObjCRuntime;
812
using osu.Framework.Graphics.Textures;
913
using osu.Framework.IO.Stores;
14+
using osu.Framework.Logging;
1015
using SixLabors.ImageSharp;
16+
using SixLabors.ImageSharp.Processing;
1117
using UIKit;
1218

1319
namespace osu.Framework.iOS.Graphics.Textures
@@ -19,7 +25,7 @@ public IOSTextureLoaderStore(IResourceStore<byte[]> store)
1925
{
2026
}
2127

22-
protected override Image<TPixel> ImageFromStream<TPixel>(Stream stream)
28+
protected override unsafe Image<TPixel> ImageFromStream<TPixel>(Stream stream)
2329
{
2430
using (var nativeData = NSData.FromStream(stream))
2531
{
@@ -33,17 +39,64 @@ protected override Image<TPixel> ImageFromStream<TPixel>(Stream stream)
3339
int width = (int)uiImage.Size.Width;
3440
int height = (int)uiImage.Size.Height;
3541

36-
// TODO: Use pool/memory when builds success with Xamarin.
37-
// Probably at .NET Core 3.1 time frame.
38-
byte[] data = new byte[width * height * 4];
39-
using (CGBitmapContext textureContext = new CGBitmapContext(data, width, height, 8, width * 4, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast))
40-
textureContext.DrawImage(new CGRect(0, 0, width, height), uiImage.CGImage);
42+
var format = new vImage_CGImageFormat
43+
{
44+
BitsPerComponent = 8,
45+
BitsPerPixel = 32,
46+
ColorSpace = CGColorSpace.CreateDeviceRGB().Handle,
47+
// notably, iOS generally uses premultiplied alpha when rendering image to pixels via CGBitmapContext or otherwise,
48+
// but vImage offers using straight alpha directly without any conversion from our side (by specifying Last instead of PremultipliedLast).
49+
BitmapInfo = (CGBitmapFlags)CGImageAlphaInfo.Last,
50+
Decode = null,
51+
RenderingIntent = CGColorRenderingIntent.Default,
52+
};
4153

42-
var image = Image.LoadPixelData<TPixel>(data, width, height);
54+
vImageBuffer accelerateImage = default;
4355

56+
// perform initial call to retrieve preferred alignment and bytes-per-row values for the given image dimensions.
57+
long alignment = (long)vImageBuffer_Init(&accelerateImage, (uint)height, (uint)width, 32, vImageFlags.NoAllocate);
58+
Debug.Assert(alignment > 0);
59+
60+
// allocate aligned memory region to contain image pixel data.
61+
int bytesCount = accelerateImage.BytesPerRow * accelerateImage.Height;
62+
accelerateImage.Data = (IntPtr)NativeMemory.AlignedAlloc((nuint)(accelerateImage.BytesPerRow * accelerateImage.Height), (nuint)alignment);
63+
64+
var result = vImageBuffer_InitWithCGImage(&accelerateImage, &format, null, uiImage.CGImage!.Handle, vImageFlags.NoAllocate);
65+
Debug.Assert(result == vImageError.NoError);
66+
67+
var dataSpan = new ReadOnlySpan<byte>(accelerateImage.Data.ToPointer(), bytesCount);
68+
69+
int stride = accelerateImage.BytesPerRow / 4;
70+
var image = Image.LoadPixelData<TPixel>(dataSpan, stride, height);
71+
image.Mutate(i => i.Crop(width, height));
72+
73+
NativeMemory.AlignedFree(accelerateImage.Data.ToPointer());
4474
return image;
4575
}
4676
}
4777
}
78+
79+
#region Accelerate API
80+
81+
[DllImport(Constants.AccelerateLibrary)]
82+
private static extern unsafe vImageError vImageBuffer_Init(vImageBuffer* buf, uint height, uint width, uint pixelBits, vImageFlags flags);
83+
84+
[DllImport(Constants.AccelerateLibrary)]
85+
private static extern unsafe vImageError vImageBuffer_InitWithCGImage(vImageBuffer* buf, vImage_CGImageFormat* format, nfloat* backgroundColour, NativeHandle image, vImageFlags flags);
86+
87+
// ReSharper disable once InconsistentNaming
88+
[StructLayout(LayoutKind.Sequential)]
89+
public unsafe struct vImage_CGImageFormat
90+
{
91+
public uint BitsPerComponent;
92+
public uint BitsPerPixel;
93+
public NativeHandle ColorSpace;
94+
public CGBitmapFlags BitmapInfo;
95+
public uint Version;
96+
public nfloat* Decode;
97+
public CGColorRenderingIntent RenderingIntent;
98+
}
99+
100+
#endregion
48101
}
49102
}

0 commit comments

Comments
 (0)