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 ;
14+ using osu . Framework . Logging ;
1015using SixLabors . ImageSharp ;
16+ using SixLabors . ImageSharp . Processing ;
1117using UIKit ;
1218
1319namespace 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