前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开启新视界!百款应用广色域适配评测报告

开启新视界!百款应用广色域适配评测报告

作者头像
软件绿色联盟
发布2022-03-31 15:19:55
1.3K0
发布2022-03-31 15:19:55
举报

随着移动设备屏幕与摄像头传感器的换代升级,越来越多的设备能够通过摄像头感应器捕捉到sRGB范围以外的颜色,生成广色域图片,实现色彩更饱和、更绚烂的渲染效果。但是如果应用未适配广色域,就会出现广色域图片显示异常情况,影响用户体验。

软件绿色联盟联合华为终端开放实验室,对100款应用广色域适配情况进行了分析,进而查看当前市场上应用在广色域方面的表现,并对适配过程中主要出现的问题进行归类、总结,帮助应用开发者尽快完成适配,为用户带来更丰富的色彩。

在100款应用广色域测试结果中,可以看到通过率仅为57%,说明当前应用在广色域适配方面表现并不理想,需要重点关注。

进一步分析发现,未适配应用主要表现为P3广色域图片失真,涉及发送、打开大图、缩略图、发布动态等场景。涉及到购物比价、实用工具、社交通讯等16个类别,从测试结果来看,问题比较大的在拍摄美化、购物比价和社交通讯分类中,用户对这几类应用的图片真实度和质感要求较高,尤其购物类应用,图片色差会影响销量、评价等,需要开发者重点优化。

如下图所示,如应用没有适配广色域,会导致P3广色域图片解码异常,颜色比较暗淡,不能将图片本身的质感体现出来。

1. 100款应用适配详情

1.1 已适配应用列表

1.2 待适配应用列表

注:Soul、快手已在待发布新版本优化该问题,用户可及时关注并更新版本,以获取更好的使用体验。

2. 广色域适配建议

先来看看已知的几个未适配场景:

  • 总是假定图片处于sRGB 色彩空间;
  • 没有进行必要转换,便将图片上传为纹理,比如一些视频效果场景,直接拿解码的pixels上传gpu;
  • 在压缩时忽略 ICC 配置文件,比如有些场景会对图片上传或者压缩处理,丢失了icc;

通过上述问题场景可以看出,应用不可以假设输入的外部图片默认使用 sRGB 色彩空间,也就是说应用必须自行检查已解码图片的色彩空间,并进行必要转换。如果没有做到这一点,可能会导致色彩失真,或者色彩配置文件在某个环节不被接受。

总结一下,适配建议可以归纳为这几点:

  1. 不要设置应用的固定色域,要根据设备和显示屏是否支持广色域,activity是否启用广色域模式以及业务场景,动态的设置色域;
  2. 如果设备不支持或者Activity未启用广色域模式,则需要把每张图片解码为sRGB色域。

由于启用广色域模式时,Activity 的窗口将更多内存和GPU 处理能力用于画面构成,在启用广色域模式之前,应权衡 Activity 是否真的能从中受益。例如,以全屏模式显示照片的 Activity 适合使用广色域模式,但显示较小缩略图的 Activity 则不适合。

3. P3广色域兼容性方案

广色域适配方案主要有三种,系统方案、Fresco适配、其他非Android系统接口适配。这里分别做简单介绍。

3.1 系统方案

针对走系统接口的应用,Android 对P3的格式转换有以下3种API实现。

1)BitmapFactory

2)BitmapRegionDecoder

3)ImageDecoder

详细的实现部分可以参考下面几个小节:

3.1.1 BitmapFactory

在Android API中通过android.graphics.BitmapFactory可以实现解码图片文件(全解码),这其中包括了通过文件、流、数字创建解码器。通过在 BitmapFactory.Option中设置 inPreferredColorSpace参数,允许您为已解码的 Bitmap 文件指定目标色彩空间。假设您现在想要解码一个文件,那么您可以用下面的代码进行色彩管理。现在大部分应用调用的是BitmapFactory解码。APP可以使用如下代码进行设置。

代码语言:javascript
复制
final BitmapFactory.Options options = new BitmapFactory.Options();
// Decode this file to sRGB color space.
options.inPreferredColorSpace = ColorSpace.get(Named.SRGB);
Bitmap bitmap = BitmapFactory.decodeFile(FILE_PATH, options);

上述代码把目标色域空间设置成SRGB, 保存在options 参数里。然后调用解码API decodeFile进行解码(其他decodeXXX接口类似)。BitmapFactory里有5个API解码接口供应用使用。如下:

public static Bitmap decodeFile(String pathName, Options opts)

public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)

public static Bitmap decodeResource(Resources res, int id, Options opts)

Android中与本次讨论的图片色域设置相关的图片格式如下:

SRGB

ADOBE_RGB

DISPLAY_P3

ADOBE_RGB基本不会涉及,因此您只需要考虑SRGB和DISPLAY_P3两种图片格式。

3.1.2 BitmapRegionDecoder

在Android API中通过android.graphics.BitmapRegionDecoder实现解码图片的某一块矩形区域(区域解码)。通过在 BitmapFactory.Option 设置 inPreferredColorSpace参数,允许您为已解码的 Bitmap 文件指定目标色彩空间。假设您现在想要解码一个文件,那么您可以用下面的代码进行色彩管理。如APP可以使用如下代码进行设置目标ColorSpace。

代码语言:javascript
复制
final BitmapFactory.Options options = new BitmapFactory.Options();
// Set sRGB colorspace when decoding by setting Options.inPreferredColorSpace
options.inPreferredColorSpace = ColorSpace.get(Named.SRGB);
Rect rect = new Rect(100, 100); // create a rect region
// Decodes a rectangle region in the image specified by rect

上述代码把目标色域空间设置成SRGB, 保存在options 参数里。然后调用解码API decodeRegion进行解码。

3.1.3 ImageDecoder

从 Android P (API 等级 28) 开始,Android引入了现代化图片解码工具 ImageDecoder。如果您已将 APK 升级至 API 等级 28 或更高,我们建议您使用 ImageDecoder,而非 BitmapFactory 或 BitmapFactory.Option API。但是大部分应用还是使用BitmapFactory。

针对ImageDecoder可以通过ImageDecoder.setTargetColorSpace实现目标色彩空间转换:

代码语言:javascript
复制
ImageDecoder.Source source = ImageDecoder.createSource(FILE_PATH);
try {
    bitmap = ImageDecoder.decodeBitmap(source,
            new ImageDecoder.OnHeaderDecodedListener() {
                @Override
                public void onHeaderDecoded(ImageDecoder decoder,
                        ImageDecoder.ImageInfo info,
                        ImageDecoder.Source source) {
                    decoder.setTargetColorSpace(ColorSpace.get(Named.SRGB));

如上示例代码使用 ImageDecoder#decodeBitmap API 将图片转换为 sRGB 位图。应用在调用解码的API接口前,把色域设置成SRGB。

经过上文的讨论得知,解码后的图片色域与设备及Activity色域模式不匹配将会造成三方应用的广色域图片显示失真问题,则当前产生异常的配置以及显示效果如表格所示:

其中COLOR_MODE_DEFAULT表示Activity 为默认非广色域模式,COLOR_MODE_WIDE_COLOR_GAMUT表示Activity为广色域模式。

经过测试发现,当前的解码方式返回的图片色域如下:

其中,红色的部分表明该返回的图片格式与Activity色域模式不匹配,这会造成图片的显示失真。

为了确保能够正确、高效地呈现广色域效果,我们在系统侧解码模块也将加入方案作为保险。该方案将检测设备是否支持广色域从而对解码返回的图片格式进行调整,而Activity的色域模式的适配则需要您在应用中特别注意。如果您的设备已适配广色域,则系统将按照解码模块设置解码并返回图片。然而,如果您的设备不支持广色域并且您的应用没有对图片的解码色域进行设置,即采用默认设置,系统都将统一按照sRGB色域设置进行解码,并返回sRGB色域的图片。具体真值表如下:

表格中灰色的部分表明该方案不会考虑Activity的色域模式,请开发者在应用中自行适配。黄色的部分表明系统将改变Android的原始行为,请开发者特别注意。所以若应用想要实现广色域显示效果,则在启用广色域Activity配置(可通过代码setColorMode)后,并需要针对大图显式指定为P3作为目标解码空间。

系统规避方案详细说明(方案仅针对以上几个系统标准接口):

如果应用在图片解码时,没有指定目标色彩空间,认为应用没有进行广色域适配,解码组件按照SRGB色彩空间进行解码(根据三方调研和评估,如果应用进行了广色域适配,在进行图片解码时,会指定目标色彩空间,以便解码输出的Bitmap位图对象的色彩空间匹配应用界面图层的色彩空间;如果应用没有指定目标色彩空间,通常是没有对广色域进行适配。)

3.2 Fresco适配

从我们目前的测试结果来看,当应用使用Fresco三方开源框架并且进行尺寸缩放或者旋转的操作之后,P3图片显示会有失真的问题。为了解决该问题,我们建议可以使用java接口进行转换而不是使用默认的native C/C++接口。具体代码实现如下:

代码语言:javascript
复制
ImagePipelineConfig.Builder builder = ImagePipelineConfig.newBuilder(this);
builder.setImageTranscoderFactory(new SimpleImageTranscoderFactory((int) BitmapUtil.MAX_BITMAP_SIZE));
ImagePipelineConfig config = builder.build();

3.3 其他非Android系统接口适配指导

当应用不支持广色域时,则需要兼容广色域图片,就需要保证广色域P3的图片解码转换至sRGB。

3.3.1 色域转换伪代码实现

  • Degamma线性化
代码语言:javascript
复制
% 输入rgb 3*1向量,gamma值
function out = gammik(rgb, gamma)
sR=rgb(:,1)./1;
sG=rgb(:,2)./1;
sB=rgb(:,3)./1;

g = gamma;
%apply gamma function to convert to sRGB
sr(sR >= 0) = sR(sR >= 0).^g;
sr(sR <  0) = -((-sR(sR < 0)).^g);
sg(sG >= 0) = sG(sG >= 0).^g;
sg(sG <  0) = -((-sG(sG < 0)).^g);
sb(sB >= 0) = sB(sB >= 0).^g;
sb(sB <  0) = -((-sB(sB < 0)).^g);
out=[sr',sg',sb'];
end

注:gamma = 2.2 为非线性到线性, gamma =1/2.2 线性转非线性

  • GamutMapping a. 计算矩阵M

其中为原色域RGB到XYZ的转换矩阵,M2为目标色域RGB到XYZ的转换矩阵,为XYZ到目标色域RGB的转换矩阵即M2的逆矩阵,具体矩阵数值参考:

http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html

补充说明sRGB和Display-P3矩阵:

b. M*RGB

代码语言:javascript
复制
% 输入 RGB 3x1向量,M 3x3转换矩阵,输出RGB 3x1向量
function out = ColorSpaceTransform(RGB,M)
ourt = M * RGB;
end 

比如P3 –> sRGB, 则为M = [MsRGBToxyz]-1*Mp3toxyz = MxyztosRGB* Mp3toxyz

c. Gamma非线性化

代码语言:javascript
复制
function out = gammik2(sRGB, gamma)
sR=sRGB(:,1);
sG=sRGB(:,2);
sB=sRGB(:,3);
g=1/gamma;
sR(sR >= 0) = sR(sR >= 0).^g;
sR(sR <  0) = -((-sR(sR < 0)).^g);
  
sG(sG >= 0) = sG(sG >= 0).^g;
sG(sG <  0) = -((-sG(sG < 0)).^g);
    
sB(sB >= 0) = sB(sB >= 0).^g;
sB(sB <  0) = -((-sB(sB < 0)).^g);
    
R=sR*1;
G=sG*1;
B=sB*1;
    
out=[R,G,B];  
end

按照如上方式可以简单实现针对P3到sRGB的色域转换。

三方库可以实现转换,具体三方库链接如下

https://github.com/google/skia/blob/master/third_party/skcms/skcms.cc

其中的skcms_Transform函数可以参考。

· END ·

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件绿色联盟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3.1 系统方案
    • 3.1.1 BitmapFactory
      • 3.1.2 BitmapRegionDecoder
        • 3.1.3 ImageDecoder
        • 3.2 Fresco适配
        • 3.3 其他非Android系统接口适配指导
          • 3.3.1 色域转换伪代码实现
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档