Xamarin.iOS 中的宽色

本文介绍宽色以及如何在 Xamarin.iOS 或 Xamarin.Mac 应用中使用它。

iOS 10 和 macOS Sierra 在整个系统中增强了对扩展范围像素格式和广色域颜色空间的支持,包括核心图形、核心图像、金属和 AVFoundation 等框架。 通过在整个图形堆栈中提供此行为,进一步简化了对具有宽色域显示器的设备的支持。

资产目录

通过资产目录支持宽色

在 iOS 10 和 macOS Sierra 中,Apple 扩展了应用捆绑包中用于包含和分类静态图像内容的资产目录,以支持宽色。

使用资产目录可为应用提供以下好处:

  • 为静态映像资产提供了最佳部署选项。
  • 支持自动颜色更正。
  • 支持自动像素格式优化。
  • 支持应用切片和应用瘦身,这可确保仅将相关内容传送到最终用户的设备。

Apple 对资产目录进行了以下增强,以支持宽色:

  • 它们支持 16 位(每个颜色通道)源内容。
  • 它们支持按照显示色域对内容进行编录。 内容可以被标记为 sRGB 或 Display P3 色域。

开发人员可以通过三个选项支持其应用中的宽色内容:

  1. 不执行任何操作 - 由于宽色内容只应在应用需要显示生动颜色(增强用户体验的地方)的情况下使用,因此,超出此要求的内容应按原样保留。 在所有硬件情况下,它将继续正确呈现。
  2. 将现有内容升级为 Display P3 - 这要求开发人员将资产目录中的现有图像内容替换为 P3 格式的新升级文件,并在资源编辑器中将其标记为 P3 格式。 在生成时,将从这些资产生成 sRGB 派生图像。
  3. 提供优化的资产内容 - 在这种情况下,开发人员将提供资产目录中每个图像的 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 中在屏幕上以宽色呈现图像时,请按通常方式替代存在问题的 UIViewDraw 方法。 例如:

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 也已经处于颜色管理状态。

如果应用需要知道如何在 UIViewUIViewController 上实现呈现,它可以检查 UITraitCollection 类的新增 DisplayGamut 属性。 此值将会是以下项的 UIDisplayGamut 枚举:

  • P3
  • Srgb
  • 未指定

如果应用想要控制用于绘制图像的颜色空间,则可以使用 CALayer 的新增 ContentsFormat 属性来指定所需的颜色空间。 此值可以是以下项的 CAContentsFormat 枚举:

  • Gray8Uint
  • Rgba16Float
  • Rgba8Uint

在 macOS 中在屏幕上呈现

当应用需要在 macOS 中在屏幕上以宽色呈现图像时,请按通常方式替代存在问题的 NSViewDrawRect 方法。 例如:

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