22// See the LICENCE file in the repository root for full licence text.
33
44using System ;
5+ using System . Diagnostics ;
56using System . IO ;
7+ using System . Runtime . InteropServices ;
8+ using Accelerate ;
69using CoreGraphics ;
710using Foundation ;
11+ using ObjCRuntime ;
812using osu . Framework . Graphics . Textures ;
913using osu . Framework . IO . Stores ;
1014using SixLabors . ImageSharp ;
15+ using SixLabors . ImageSharp . Processing ;
1116using UIKit ;
1217
1318namespace osu . Framework . iOS . Graphics . Textures
@@ -19,7 +24,7 @@ public IOSTextureLoaderStore(IResourceStore<byte[]> store)
1924 {
2025 }
2126
22- protected override Image < TPixel > ImageFromStream < TPixel > ( Stream stream )
27+ protected override unsafe Image < TPixel > ImageFromStream < TPixel > ( Stream stream )
2328 {
2429 using ( var nativeData = NSData . FromStream ( stream ) )
2530 {
@@ -33,17 +38,64 @@ protected override Image<TPixel> ImageFromStream<TPixel>(Stream stream)
3338 int width = ( int ) uiImage . Size . Width ;
3439 int height = ( int ) uiImage . Size . Height ;
3540
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 ) ;
41+ var format = new vImage_CGImageFormat
42+ {
43+ BitsPerComponent = 8 ,
44+ BitsPerPixel = 32 ,
45+ ColorSpace = CGColorSpace . CreateDeviceRGB ( ) . Handle ,
46+ // notably, iOS generally uses premultiplied alpha when rendering image to pixels via CGBitmapContext or otherwise,
47+ // but vImage offers using straight alpha directly without any conversion from our side (by specifying Last instead of PremultipliedLast).
48+ BitmapInfo = ( CGBitmapFlags ) CGImageAlphaInfo . Last ,
49+ Decode = null ,
50+ RenderingIntent = CGColorRenderingIntent . Default ,
51+ } ;
4152
42- var image = Image . LoadPixelData < TPixel > ( data , width , height ) ;
53+ vImageBuffer accelerateImage = default ;
4354
55+ // perform initial call to retrieve preferred alignment and bytes-per-row values for the given image dimensions.
56+ long alignment = ( long ) vImageBuffer_Init ( & accelerateImage , ( uint ) height , ( uint ) width , 32 , vImageFlags . NoAllocate ) ;
57+ Debug . Assert ( alignment > 0 ) ;
58+
59+ // allocate aligned memory region to contain image pixel data.
60+ int bytesCount = accelerateImage . BytesPerRow * accelerateImage . Height ;
61+ accelerateImage . Data = ( IntPtr ) NativeMemory . AlignedAlloc ( ( nuint ) ( accelerateImage . BytesPerRow * accelerateImage . Height ) , ( nuint ) alignment ) ;
62+
63+ var result = vImageBuffer_InitWithCGImage ( & accelerateImage , & format , null , uiImage . CGImage ! . Handle , vImageFlags . NoAllocate ) ;
64+ Debug . Assert ( result == vImageError . NoError ) ;
65+
66+ var dataSpan = new ReadOnlySpan < byte > ( accelerateImage . Data . ToPointer ( ) , bytesCount ) ;
67+
68+ int stride = accelerateImage . BytesPerRow / 4 ;
69+ var image = Image . LoadPixelData < TPixel > ( dataSpan , stride , height ) ;
70+ image . Mutate ( i => i . Crop ( width , height ) ) ;
71+
72+ NativeMemory . AlignedFree ( accelerateImage . Data . ToPointer ( ) ) ;
4473 return image ;
4574 }
4675 }
4776 }
77+
78+ #region Accelerate API
79+
80+ [ DllImport ( Constants . AccelerateLibrary ) ]
81+ private static extern unsafe vImageError vImageBuffer_Init ( vImageBuffer * buf , uint height , uint width , uint pixelBits , vImageFlags flags ) ;
82+
83+ [ DllImport ( Constants . AccelerateLibrary ) ]
84+ private static extern unsafe vImageError vImageBuffer_InitWithCGImage ( vImageBuffer * buf , vImage_CGImageFormat * format , nfloat * backgroundColour , NativeHandle image , vImageFlags flags ) ;
85+
86+ // ReSharper disable once InconsistentNaming
87+ [ StructLayout ( LayoutKind . Sequential ) ]
88+ public unsafe struct vImage_CGImageFormat
89+ {
90+ public uint BitsPerComponent ;
91+ public uint BitsPerPixel ;
92+ public NativeHandle ColorSpace ;
93+ public CGBitmapFlags BitmapInfo ;
94+ public uint Version ;
95+ public nfloat * Decode ;
96+ public CGColorRenderingIntent RenderingIntent ;
97+ }
98+
99+ #endregion
48100 }
49101}
0 commit comments