作者: Lin Peiyong, 軟件工程師
Android 現(xiàn)已迎來新一輪的圖像革新,由于 sRGB 的每個(gè)色彩通道只有 8 個(gè)比特,因此標(biāo)準(zhǔn) sRGB 色域無法充分體現(xiàn)屏幕與攝像頭最新技術(shù)的優(yōu)勢(shì)所在。Android 一直在努力實(shí)現(xiàn)對(duì)廣色域圖像的端到端支持,例如,呈現(xiàn)數(shù)據(jù)更多、色域更寬的畫面。這意味著,用戶最終能夠捕捉到實(shí)景的豐富色彩,在手機(jī)上觀賞并與朋友分享廣色域圖片。從 Android Q 開始,這一切將成為可能: 廣色域圖片即將亮相 Android。因此,讓應(yīng)用做好支持準(zhǔn)備極為重要。本文介紹的兩項(xiàng)測(cè)試可用于判定應(yīng)用是否具備相應(yīng)的條件與能力來顯示廣色域圖片。另外,本文還會(huì)提供一些技術(shù)上的建議,幫助您為應(yīng)用添加廣色域支持。
切入正題之前,讓我先解答一下大家的疑惑: 為什么要支持廣色域呢?實(shí)際上,移動(dòng)設(shè)備的屏幕與攝像頭傳感器每年都在更新?lián)Q代,越來越多的新機(jī)型即將搭載校準(zhǔn)顯示面板,其中部分還會(huì)提供廣色域支持?,F(xiàn)代攝像頭感應(yīng)器能夠捕捉到 sRGB 范圍以外的顏色,然后生成廣色域圖片。屏幕與傳感器的雙重升級(jí)將帶給用戶端到端的攝影體驗(yàn),讓他們用更鮮明的色彩留影真實(shí)世界。
從技術(shù)層面來說,這意味著應(yīng)用需要處理的圖片與之前不同了。圖片內(nèi)嵌的 ICC 配置文件將不再采用 sRGB 色彩空間,而是轉(zhuǎn)用其它色域更加豐富的格式,如 Display P3 和 Adobe RGB。對(duì)于消費(fèi)者而言,廣色域能讓照片看上去更加真實(shí)。



以上兩組圖片為同一張照片的 Display P3 和 sRGB 版本。如果您正在使用已校準(zhǔn)且支持廣色域的顯示屏上閱讀本文,您會(huì)發(fā)現(xiàn)兩者存在明顯差別。
色彩測(cè)試
您可通過以下兩項(xiàng)測(cè)試來判定應(yīng)用是否已做好支持準(zhǔn)備。第一項(xiàng)是色彩校正測(cè)試,另一項(xiàng)則是廣色域測(cè)試。
色彩校正測(cè)試: 您的應(yīng)用能夠兼容廣色域嗎?
如果應(yīng)用能夠主動(dòng)進(jìn)行顏色管理,那就說明它已經(jīng)準(zhǔn)備好支持廣色域了。在收到圖片之后,應(yīng)用首先會(huì)檢查圖片的色彩空間,然后再根據(jù)自身的廣色域顯色能力,進(jìn)行必要轉(zhuǎn)換。在這種情況下,即使應(yīng)用無法處理廣色域,圖片中的 sRGB 色域仍舊能夠正常顯示,不存在色彩失真的問題。
下圖為內(nèi)嵌 Display P3 ICC 配置文件的圖片進(jìn)行色彩校正之后的效果。

但是,如果應(yīng)用不具備色彩校正條件,那么它往往會(huì)在色彩空間轉(zhuǎn)換不當(dāng)?shù)那闆r下對(duì)顯示圖片進(jìn)行處理,最終導(dǎo)致圖片顏色失真。比如說,您可能會(huì)得到下面這種顯得褪色且失真的圖片。

廣色域測(cè)試: 您的應(yīng)用能夠支持廣色域嗎?
如果應(yīng)用可以顯示 sRGB 色彩空間之外的顏色,那就證明它具備支持廣色域的能力。您可以利用下面這張圖片來測(cè)試應(yīng)用能否支持廣色域圖像: 若能看到 Android 機(jī)器人圖標(biāo),則說明您的應(yīng)用可以支持。請(qǐng)注意,該測(cè)試需要在具備廣色域硬件支持的設(shè)備上進(jìn)行,如 Pixel 3 或三星 Galaxy S10。

您需要做哪些準(zhǔn)備?
如需正確處理廣色域圖像,您的應(yīng)用至少需要通過廣色域兼容測(cè)試,即色彩校正測(cè)試。如果您的應(yīng)用已測(cè)試成功,那就太棒了!尚未通過測(cè)試的開發(fā)者們也不用著急,您可以按照以下步驟為應(yīng)用添加支持:
如何才能確保應(yīng)用做好準(zhǔn)備并且滿足未來需求呢?關(guān)鍵點(diǎn)在于,應(yīng)用不可以假設(shè)輸入的外部圖片使用 sRGB 色彩空間,也就是說,應(yīng)用必須自行檢查已解碼圖片的色彩空間,并進(jìn)行必要轉(zhuǎn)換。如果沒有做到這一點(diǎn),可能會(huì)導(dǎo)致色彩失真,或者色彩配置文件在某個(gè)環(huán)節(jié)不被接受。
必要: 色彩校正
通過色彩校正測(cè)試為最低要求。如果應(yīng)用無法采用廣色域,您很有可能只需要把每張圖片解碼為 sRGB 色彩空間。您可借助 BitmapFactory 或者 ImageDecoder 來實(shí)現(xiàn)這個(gè)邏輯。
使用 BitmapFactory
在 API 26 中,我們?yōu)?BitmapFactory.Option 添加了 inPreferredColorSpace,允許您為已解碼的 Bitmap 文件指定目標(biāo)色彩空間。假設(shè)您現(xiàn)在想要解碼一個(gè)文件,那么您可以用下面的代碼進(jìn)行色彩管理:
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);
使用 ImageDecoder
從 Android P (API 等級(jí) 28) 開始,我們引入了現(xiàn)代化圖片解碼工具 ImageDecoder。如果您已將 APK 升級(jí)至 API 等級(jí) 28 或更高,我們建議您使用 ImageDecoder,而非 BitmapFactory 或 BitmapFactory.Option API。
在以下示例代碼中,我們使用 ImageDecoder#decodeBitmap API 將圖片轉(zhuǎn)換為 sRGB 位圖。
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));
}
});
} catch (IOException e) {
// handle exception.
}
ImageDecoder 的另一個(gè)優(yōu)勢(shì)在于: 通過傳入一個(gè) ImageDecoder.OnHeaderDecodedListener 并檢查 ImageDecoder.ImageInfo#getColorSpace(),您可以在獲取最終的位圖之前,就能知道圖像的編碼色彩空間。這樣一來,您便能根據(jù)應(yīng)用對(duì)色彩空間的處理方式,來檢查圖像的編碼色彩空間,并分別設(shè)置相應(yīng)的目標(biāo)色彩空間。
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) {
ColorSpace cs = info.getColorSpace();
// Do something...
}
});
} catch (IOException e) {
// handle exception.
}
更多使用技巧,請(qǐng)參閱 ImageDecoder API 官方文檔。
已知不良做法
典型的不良做法包括但不限于:
- 總是假定圖片處于 sRGB 色彩空間
- 沒有進(jìn)行必要轉(zhuǎn)換,便將圖片上傳為紋理
- 在壓縮時(shí)忽略 ICC 配置文件
以上做法均會(huì)嚴(yán)重影響用戶的視覺體驗(yàn),令色彩失真。例如,以下示例代碼會(huì)導(dǎo)致應(yīng)用色彩校正錯(cuò)誤:
// This is bad, don't do it!
final BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeFile(FILE_PATH, options);
glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES31.GL_RGBA, bitmap.getWidth(),
bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, bitmap,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
在將位圖作為紋理上傳之前,應(yīng)用并沒有檢查色彩空間,因此色彩校正測(cè)試的結(jié)果會(huì)是下面這張失真圖片。

可選: 支持廣色域
為了妥善處理圖片,除上述必要變更之外,如果您的應(yīng)用是一個(gè)圖像類應(yīng)用,您可能希望通過采取一些額外措施,例如在清單文件中啟用廣域模式或創(chuàng)建一個(gè) Display P3 surface,來實(shí)現(xiàn)圖片的全彩色域顯示。
如需在 activity 中啟用廣色域,請(qǐng)將 AndroidManifest.xml 文件中的 colorMode 屬性設(shè)定為 wideColorGamut。請(qǐng)注意,您需要為每一個(gè)啟用廣色域模式的 activity 重復(fù)以上設(shè)置。
android:colorMode="wideColorGamut"
您也可以通過程序的方式在 activity 中設(shè)定色彩模式,具體方法為: 調(diào)用 setColorMode(int) 方法并傳入 COLOR_MODE_WIDE_COLOR_GAMUT。
在渲染廣色域圖像時(shí),除了具體的廣色域內(nèi)容之外,您還需要?jiǎng)?chuàng)建一個(gè)廣色域 surface,以 OpenGL 為例,應(yīng)用必須先檢查以下擴(kuò)展:
然后,在創(chuàng)建 surface 時(shí)請(qǐng)求 Display P3 作為色彩空間,具體代碼見下:
private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
EGLConfig config, Object nativeWindow) {
EGLSurface surface = null;
try {
int attribs[] = {
EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
egl.EGL_NONE
};
surface = egl.eglCreateWindowSurface(display, config, nativeWindow, attribs);
} catch (IllegalArgumentException e) {}
return surface;
}
如果您想了解如何在原生代碼中采用廣色域,請(qǐng)參閱官方文檔《使用廣色域內(nèi)容增強(qiáng)圖形》。
圖片庫 API 設(shè)計(jì)指南
最后,如果您擁有或維護(hù)一個(gè)圖片編解碼庫,通過色彩校正測(cè)試依舊是最低要求。為了現(xiàn)代化您的圖片庫,我們強(qiáng)烈建議您進(jìn)行下列兩項(xiàng)工作以擴(kuò)展色彩管理 API:
在設(shè)計(jì)新 API 或擴(kuò)展現(xiàn)有 API 時(shí),請(qǐng)顯式傳入 ColorSpace 參數(shù)。相比于硬編碼一個(gè)色彩空間,顯式的 ColorSpace 參數(shù)更能滿足未來開發(fā)工作的需求。
所有舊版本 API 應(yīng)該顯式將位圖解碼為 sRGB 色彩空間。在 Android 8.0 (API 等級(jí) 26) 引入色彩管理之前,所有內(nèi)容都被設(shè)定為 sRGB 色域。此項(xiàng)操作有助于您確??蛻魬?yīng)用向后兼容。
完成上述工作后,就請(qǐng)著手開展前文所述的兩項(xiàng)色彩測(cè)試工作吧!
點(diǎn)擊這里了解更多移動(dòng)開發(fā)相關(guān)產(chǎn)品內(nèi)容
