Skip to content

Commit

Permalink
Use Accelerate framework for texture image processing
Browse files Browse the repository at this point in the history
  • Loading branch information
frenzibyte committed Dec 22, 2024
1 parent ffdbdca commit 37aefc4
Showing 1 changed file with 60 additions and 7 deletions.
67 changes: 60 additions & 7 deletions osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Accelerate;
using CoreGraphics;
using Foundation;
using ObjCRuntime;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using UIKit;

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

protected override Image<TPixel> ImageFromStream<TPixel>(Stream stream)
protected override unsafe Image<TPixel> ImageFromStream<TPixel>(Stream stream)
{
using (var nativeData = NSData.FromStream(stream))
{
Expand All @@ -33,17 +39,64 @@ protected override Image<TPixel> ImageFromStream<TPixel>(Stream stream)
int width = (int)uiImage.Size.Width;
int height = (int)uiImage.Size.Height;

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

var image = Image.LoadPixelData<TPixel>(data, width, height);
vImageBuffer accelerateImage = default;

// perform initial call to retrieve preferred alignment and bytes-per-row values for the given image dimensions.
long alignment = (long)vImageBuffer_Init(&accelerateImage, (uint)height, (uint)width, 32, vImageFlags.NoAllocate);
Debug.Assert(alignment > 0);

// allocate aligned memory region to contain image pixel data.
int bytesCount = accelerateImage.BytesPerRow * accelerateImage.Height;
accelerateImage.Data = (IntPtr)NativeMemory.AlignedAlloc((nuint)(accelerateImage.BytesPerRow * accelerateImage.Height), (nuint)alignment);

var result = vImageBuffer_InitWithCGImage(&accelerateImage, &format, null, uiImage.CGImage!.Handle, vImageFlags.NoAllocate);
Debug.Assert(result == vImageError.NoError);

var dataSpan = new ReadOnlySpan<byte>(accelerateImage.Data.ToPointer(), bytesCount);

int stride = accelerateImage.BytesPerRow / 4;
var image = Image.LoadPixelData<TPixel>(dataSpan, stride, height);
image.Mutate(i => i.Crop(width, height));

NativeMemory.AlignedFree(accelerateImage.Data.ToPointer());
return image;
}
}
}

#region Accelerate API

[DllImport(Constants.AccelerateLibrary)]
private static extern unsafe vImageError vImageBuffer_Init(vImageBuffer* buf, uint height, uint width, uint pixelBits, vImageFlags flags);

[DllImport(Constants.AccelerateLibrary)]
private static extern unsafe vImageError vImageBuffer_InitWithCGImage(vImageBuffer* buf, vImage_CGImageFormat* format, nfloat* backgroundColour, NativeHandle image, vImageFlags flags);

// ReSharper disable once InconsistentNaming
[StructLayout(LayoutKind.Sequential)]
public unsafe struct vImage_CGImageFormat
{
public uint BitsPerComponent;
public uint BitsPerPixel;
public NativeHandle ColorSpace;
public CGBitmapFlags BitmapInfo;
public uint Version;
public nfloat* Decode;
public CGColorRenderingIntent RenderingIntent;
}

#endregion
}
}

0 comments on commit 37aefc4

Please sign in to comment.