Xamarin.iOS 中的宽色
本文介绍宽色以及如何在 Xamarin.iOS 或 Xamarin.Mac 应用中使用它。
iOS 10 和 macOS Sierra 在整个系统中增强了对扩展范围像素格式和广色域颜色空间的支持,包括核心图形、核心图像、金属和 AVFoundation 等框架。 通过在整个图形堆栈中提供此行为,进一步简化了对具有宽色域显示器的设备的支持。
资产目录
通过资产目录支持宽色
在 iOS 10 和 macOS Sierra 中,Apple 扩展了应用捆绑包中用于包含和分类静态图像内容的资产目录,以支持宽色。
使用资产目录可为应用提供以下好处:
- 为静态映像资产提供了最佳部署选项。
- 支持自动颜色更正。
- 支持自动像素格式优化。
- 支持应用切片和应用瘦身,这可确保仅将相关内容传送到最终用户的设备。
Apple 对资产目录进行了以下增强,以支持宽色:
- 它们支持 16 位(每个颜色通道)源内容。
- 它们支持按照显示色域对内容进行编录。 内容可以被标记为 sRGB 或 Display P3 色域。
开发人员可以通过三个选项支持其应用中的宽色内容:
- 不执行任何操作 - 由于宽色内容只应在应用需要显示生动颜色(增强用户体验的地方)的情况下使用,因此,超出此要求的内容应按原样保留。 在所有硬件情况下,它将继续正确呈现。
- 将现有内容升级为 Display P3 - 这要求开发人员将资产目录中的现有图像内容替换为 P3 格式的新升级文件,并在资源编辑器中将其标记为 P3 格式。 在生成时,将从这些资产生成 sRGB 派生图像。
- 提供优化的资产内容 - 在这种情况下,开发人员将提供资产目录中每个图像的 8 位 sRGB 和 16 位 Display P3 视觉(已在资产编辑器中正确标记)。
资产目录部署
当开发者向 App Store 提交包含宽色图像内容的资源目录的应用时,将发生以下情况:
- 将应用部署到最终用户时,应用切片将确保仅将适当的内容变体发送到用户设备。
- 在不支持宽色的设备上,包括宽色内容不会产生有效负载成本,因为它永远不会发送到设备。
- macOS Sierra (及更高版本)上的
NSImage
将会自动选择硬件显示的最佳内容表示形式。 - 当设备硬件显示特征发生更改时,或如果发生这种情况,将会自动刷新显示的内容。
资产目录存储
资产目录存储对应用具有以下属性和含义:
- 在生成时,Apple 会尝试通过高质量图像转换优化图像内容的存储。
- 每个颜色通道使用 16 位用于宽色内容。
- 内容依赖图像压缩用于降低可交付内容大小。 新增了“有损”压缩,以进一步优化内容大小。
在应用中呈现离屏图像
根据正在创建的应用类型,它可能允许用户包括从 Internet 收集的图像内容,或直接在应用内部创建图像内容(例如矢量绘图应用)。
在这两种情况下,应用都可以使用 iOS 和 macOS 新增的增强功能以宽色呈现所需的离屏图像。
在 iOS 中绘制宽色
在讨论如何在 iOS 10 中正确绘制宽色图像之前,请查看以下常见的 iOS 绘图代码:
public UIImage DrawWideColorImage ()
{
var size = new CGSize (250, 250);
UIGraphics.BeginImageContext (size);
...
UIGraphics.EndImageContext ();
return image = UIGraphics.GetImageFromCurrentImageContext ();
}
标准代码存在问题,需要先解决它们,然后才能用于绘制宽色图像。 用于启动 iOS 图像绘制的 UIGraphics.BeginImageContext (size)
方法存在以下限制:
- 它无法创建每个颜色通道超过 8 位的图像上下文。
- 它不能表示扩展范围 sRGB 颜色空间中的颜色。
- 由于在后台调用了低级 C 例程,因此它无法提供在非 sRGB 颜色空间中创建上下文的接口。
要处理这些限制并在 iOS 10 中绘制宽色图像,请改用以下代码:
public UIImage DrawWideColorImage ()
{
var size = new CGSize (250, 250);
var render = new UIGraphicsImageRenderer (size);
var image = render.CreateImage ((UIGraphicsImageRendererContext context) => {
var bounds = context.Format.Bounds;
CGRect slice;
CGRect remainder;
bounds.Divide (bounds.Width / 2, CGRectEdge.MinXEdge, out slice, out remainder);
var redP3 = UIColor.FromDisplayP3 (1.0F, 0.0F, 0.0F, 1.0F);
redP3.SetColor ();
context.FillRect (slice);
var redRGB = UIColor.Red;
redRGB.SetColor ();
context.FillRect (remainder);
});
// Return results
return image;
}
新的 UIGraphicsImageRenderer
类会创建一个新的图像上下文,该上下文能够处理扩展范围 sRGB 颜色空间,并且具有以下功能:
- 默认情况下,它是完全颜色管理的。
- 默认情况下,它支持扩展范围 sRGB 颜色空间。
- 它会智能地根据正在运行应用的 iOS 设备的功能,确定是在 sRGB 还是扩展范围 sRGB 颜色空间中呈现。
- 它完全自动管理图像上下文(
CGContext
)生存期,因此开发人员无需担心调用开始和结束上下文命令。 - 它与
UIGraphics.GetCurrentContext()
该方法兼容。
调用 CreateImage
类的 UIGraphicsImageRenderer
方法来创建宽色图像,并通过图像上下文传递要绘制到其中的完成事件处理器。 所有绘图都在此完成事件处理器完成,如下所示:
UIColor.FromDisplayP3
方法会在宽域 Display P3 颜色空间中创建新的完全饱和红色,并用于绘制矩形的前半部分。- 矩形的后半部分以普通 sRGB 完全饱和红色绘制,以进行比较。
在 macOS 中绘制宽色
NSImage
类已在 macOS Sierra 中扩展,以支持绘制宽色图像。 例如:
var size = CGSize(250,250);
var wideColorImage = new NSImage(size, false, (drawRect) =>{
var rects = drawRect.Divide(drawRect.Size.Width/2,CGRect.MinXEdge);
var color = new NSColor(1.0, 0.0, 0.0, 1.0).Set();
var path = new NSBezierPath(rects.Slice).Fill();
color = new NSColor(1.0, 0.0, 0.0, 1.0).Set();
path = new NSBezierPath(rects.Remainder).Fill();
// Return modified
return true;
});
在应用中呈现屏幕图像
要在屏幕上呈现宽色图像,该过程的工作方式类似于上文所述的绘制 macOS 和 iOS 的离屏宽色图像。
在 iOS 中呈现屏幕
当应用需要在 iOS 中在屏幕上以宽色呈现图像时,请按通常方式替代存在问题的 UIView
的 Draw
方法。 例如:
using System;
using UIKit;
using CoreGraphics;
namespace MonkeyTalk
{
public class MonkeyView : UIView
{
public MonkeyView ()
{
}
public override void Draw (CGRect rect)
{
base.Draw (rect);
// Draw the image to screen
...
}
}
}
正如 iOS 10 如上文所示对 UIGraphicsImageRenderer
类执行的操作一样,它会根据调用 Draw
方法时正在运行应用的 iOS 设备的功能,智能地确定它是应在 sRGB 还是扩展范围 sRGB 颜色空间中呈现。 此外,自 iOS 9.3 以来,UIImageView
也已经处于颜色管理状态。
如果应用需要知道如何在 UIView
或 UIViewController
上实现呈现,它可以检查 UITraitCollection
类的新增 DisplayGamut
属性。 此值将会是以下项的 UIDisplayGamut
枚举:
- P3
- Srgb
- 未指定
如果应用想要控制用于绘制图像的颜色空间,则可以使用 CALayer
的新增 ContentsFormat
属性来指定所需的颜色空间。 此值可以是以下项的 CAContentsFormat
枚举:
- Gray8Uint
- Rgba16Float
- Rgba8Uint
在 macOS 中在屏幕上呈现
当应用需要在 macOS 中在屏幕上以宽色呈现图像时,请按通常方式替代存在问题的 NSView
的 DrawRect
方法。 例如:
using System;
using AppKit;
using CoreGraphics;
using CoreAnimation;
namespace MonkeyTalkMac
{
public class MonkeyView : NSView
{
public MonkeyView ()
{
}
public override void DrawRect (CGRect dirtyRect)
{
base.DrawRect (dirtyRect);
// Draw graphics into the view
...
}
}
}
同样,它会根据调用 DrawRect
方法时正在运行应用的 Mac 硬件功能,智能地决定它是否在 sRGB 还是扩展范围 sRGB 颜色空间中呈现。
如果应用想要控制用于绘制图像的颜色空间,则可以使用 NSWindow
类的新增 DepthLimit
属性来指定所需的颜色空间。 此值可以是以下项的 NSWindowDepth
枚举:
- OneHundredTwentyEightBitRgb
- SixtyfourBitRgb
- TwentyfourBitRgb