Skip to content

Commit cc595be

Browse files
authored
Merge branch 'master' into scroll-container-double-precision
2 parents 7ed84be + fe60bf5 commit cc595be

File tree

3 files changed

+191
-15
lines changed

3 files changed

+191
-15
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using NUnit.Framework;
9+
using osu.Framework.Allocation;
10+
using osu.Framework.Graphics;
11+
using osu.Framework.Graphics.Containers;
12+
using osu.Framework.Graphics.Shapes;
13+
using osu.Framework.Graphics.Sprites;
14+
using osu.Framework.Graphics.Textures;
15+
using osu.Framework.IO.Stores;
16+
using osu.Framework.Platform;
17+
using osuTK;
18+
using osuTK.Graphics;
19+
using SixLabors.ImageSharp;
20+
using SixLabors.ImageSharp.PixelFormats;
21+
22+
namespace osu.Framework.Tests.Visual.Graphics
23+
{
24+
public partial class TestSceneTexturePremultiplication : FrameworkTestScene
25+
{
26+
private TextureStore textures = null!;
27+
28+
[BackgroundDependencyLoader]
29+
private void load(GameHost host)
30+
{
31+
textures = new TextureStore(host.Renderer, host.CreateTextureLoaderStore(new CustomResourceStore()), false, TextureFilteringMode.Nearest);
32+
}
33+
34+
[Test]
35+
public void TestComparison()
36+
{
37+
AddStep("setup", () =>
38+
{
39+
Child = new FillFlowContainer
40+
{
41+
Anchor = Anchor.Centre,
42+
Origin = Anchor.Centre,
43+
AutoSizeAxes = Axes.Both,
44+
Spacing = new Vector2(0f, 5f),
45+
Direction = FillDirection.Vertical,
46+
Children = new Drawable[]
47+
{
48+
new Container
49+
{
50+
AutoSizeAxes = Axes.Both,
51+
Children = new[]
52+
{
53+
new Box
54+
{
55+
Size = new Vector2(256, 128),
56+
Colour = Color4.Blue,
57+
},
58+
new Sprite
59+
{
60+
Texture = textures.Get("zero-to-red"),
61+
Size = new Vector2(256, 128),
62+
}
63+
},
64+
},
65+
new SpriteText
66+
{
67+
Text = "Rendering of the sprite above should be identical to the one below",
68+
},
69+
new Sprite
70+
{
71+
Texture = textures.Get("blue-to-red"),
72+
Size = new Vector2(256, 128),
73+
},
74+
}
75+
};
76+
});
77+
}
78+
79+
private class CustomResourceStore : IResourceStore<byte[]>
80+
{
81+
public byte[] Get(string name) => throw new System.NotImplementedException();
82+
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = default) => throw new System.NotImplementedException();
83+
84+
public Stream GetStream(string name)
85+
{
86+
switch (name)
87+
{
88+
case "zero-to-red":
89+
{
90+
var memoryStream = new MemoryStream();
91+
92+
Image<Rgba32> image = new Image<Rgba32>(256, 1);
93+
94+
for (int i = 0; i < 256; i++)
95+
image[i, 0] = new Rgba32(255, 0, 0, (byte)i);
96+
97+
image.SaveAsPng(memoryStream);
98+
memoryStream.Seek(0, SeekOrigin.Begin);
99+
return memoryStream;
100+
}
101+
102+
case "blue-to-red":
103+
{
104+
var memoryStream = new MemoryStream();
105+
106+
Image<Rgba32> image = new Image<Rgba32>(256, 1);
107+
108+
for (int i = 0; i < 256; i++)
109+
image[i, 0] = new Rgba32((byte)i, 0, (byte)(255 - i), 255);
110+
111+
image.SaveAsPng(memoryStream);
112+
memoryStream.Seek(0, SeekOrigin.Begin);
113+
return memoryStream;
114+
}
115+
116+
default:
117+
return Stream.Null;
118+
}
119+
}
120+
121+
public IEnumerable<string> GetAvailableResources() => new[] { "zero-to-red", "blue-to-red" };
122+
123+
public void Dispose()
124+
{
125+
}
126+
}
127+
}
128+
}

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

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
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;
1014
using SixLabors.ImageSharp;
15+
using SixLabors.ImageSharp.Processing;
1116
using UIKit;
1217

1318
namespace 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
}

osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,10 @@ private IntPtr getProcAddress(string symbol)
156156
if (window.SDLWindowHandle == null)
157157
return null;
158158

159-
// TODO: Migrate to SDL3 when https://github.com/libsdl-org/SDL/issues/9430 is resolved
160-
// var wmInfo = window.GetWindowSystemInformation();
161-
//
162-
// switch (wmInfo.subsystem)
163-
// {
164-
// case SDL_SYSWM_TYPE.SDL_SYSWM_UIKIT:
165-
// return (int)wmInfo.info.uikit.framebuffer;
166-
// }
159+
var props = SDL_GetWindowProperties(window.SDLWindowHandle);
160+
161+
if (SDL_HasProperty(props, SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER))
162+
return (int)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER, 0);
167163

168164
return null;
169165
}

0 commit comments

Comments
 (0)