做者: Lin Peiyong, 軟件工程師html
Android 現已迎來新一輪的圖像革新,因爲 sRGB 的每一個色彩通道只有 8 個比特,所以標準 sRGB 色域沒法充分體現屏幕與攝像頭最新技術的優點所在。Android 一直在努力實現對廣色域圖像的端到端支持,例如,呈現數據更多、色域更寬的畫面。這意味着,用戶最終可以捕捉到實景的豐富色彩,在手機上觀賞並與朋友分享廣色域圖片。從 Android Q 開始,這一切將成爲可能: 廣色域圖片即將亮相 Android。所以,讓應用作好支持準備極爲重要。本文介紹的兩項測試可用於斷定應用是否具有相應的條件與能力來顯示廣色域圖片。另外,本文還會提供一些技術上的建議,幫助您爲應用添加廣色域支持。android
切入正題以前,讓我先解答一下你們的疑惑: 爲何要支持廣色域呢?實際上,移動設備的屏幕與攝像頭傳感器每一年都在更新換代,愈來愈多的新機型即將搭載校準顯示面板,其中部分還會提供廣色域支持。現代攝像頭感應器可以捕捉到 sRGB 範圍之外的顏色,而後生成廣色域圖片。屏幕與傳感器的雙重升級將帶給用戶端到端的攝影體驗,讓他們用更鮮明的色彩留影真實世界。bash
從技術層面來講,這意味着應用須要處理的圖片與以前不一樣了。圖片內嵌的 ICC 配置文件將再也不採用 sRGB 色彩空間,而是轉用其它色域更加豐富的格式,如 Display P3 和 Adobe RGB。對於消費者而言,廣色域能讓照片看上去更加真實。ide
您可經過如下兩項測試來斷定應用是否已作好支持準備。第一項是色彩校訂測試,另外一項則是廣色域測試。工具
色彩校訂測試: 您的應用可以兼容廣色域嗎?測試
若是應用可以主動進行顏色管理,那就說明它已經準備好支持廣色域了。在收到圖片以後,應用首先會檢查圖片的色彩空間,而後再根據自身的廣色域顯色能力,進行必要轉換。在這種狀況下,即便應用沒法處理廣色域,圖片中的 sRGB 色域仍舊可以正常顯示,不存在色彩失真的問題。ui
下圖爲內嵌 Display P3 ICC 配置文件的圖片進行色彩校訂以後的效果。this
若是應用能夠顯示 sRGB 色彩空間以外的顏色,那就證實它具有支持廣色域的能力。您能夠利用下面這張圖片來測試應用可否支持廣色域圖像: 若能看到 Android 機器人圖標,則說明您的應用能夠支持。請注意,該測試須要在具有廣色域硬件支持的設備上進行,如 Pixel 3 或三星 Galaxy S10。google
如需正確處理廣色域圖像,您的應用至少須要經過廣色域兼容測試,即色彩校訂測試。若是您的應用已測試成功,那就太棒了!還沒有經過測試的開發者們也不用着急,您能夠按照如下步驟爲應用添加支持:編碼
如何才能確保應用作好準備而且知足將來需求呢?關鍵點在於,應用不能夠假設輸入的外部圖片使用 sRGB 色彩空間,也就是說,應用必須自行檢查已解碼圖片的色彩空間,並進行必要轉換。若是沒有作到這一點,可能會致使色彩失真,或者色彩配置文件在某個環節不被接受。
必要: 色彩校訂
經過色彩校訂測試爲最低要求。若是應用沒法採用廣色域,您頗有可能只須要把每張圖片解碼爲 sRGB 色彩空間。您可藉助 BitmapFactory 或者 ImageDecoder 來實現這個邏輯。
使用 BitmapFactory
在 API 26 中,咱們爲 BitmapFactory.Option 添加了 inPreferredColorSpace,容許您爲已解碼的 Bitmap 文件指定目標色彩空間。假設您如今想要解碼一個文件,那麼您能夠用下面的代碼進行色彩管理:
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 等級 28) 開始,咱們引入了現代化圖片解碼工具 ImageDecoder。若是您已將 APK 升級至 API 等級 28 或更高,咱們建議您使用 ImageDecoder,而非 BitmapFactory 或 BitmapFactory.Option API。
在如下示例代碼中,咱們使用 ImageDecoder#decodeBitmap API 將圖片轉換爲 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 的另外一個優點在於: 經過傳入一個 ImageDecoder.OnHeaderDecodedListener 並檢查 ImageDecoder.ImageInfo#getColorSpace(),您能夠在獲取最終的位圖以前,就能知道圖像的編碼色彩空間。這樣一來,您便能根據應用對色彩空間的處理方式,來檢查圖像的編碼色彩空間,並分別設置相應的目標色彩空間。
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.
}
複製代碼
更多使用技巧,請參閱 ImageDecoder API 官方文檔。
已知不良作法
典型的不良作法包括但不限於:
以上作法均會嚴重影響用戶的視覺體驗,令色彩失真。例如,如下示例代碼會致使應用色彩校訂錯誤:
// 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); 複製代碼
在將位圖做爲紋理上傳以前,應用並無檢查色彩空間,所以色彩校訂測試的結果會是下面這張失真圖片。
爲了妥善處理圖片,除上述必要變動以外,若是您的應用是一個圖像類應用,您可能但願經過採起一些額外措施,例如在清單文件中啓用廣域模式或建立一個 Display P3 surface,來實現圖片的全綵色域顯示。
如需在 activity 中啓用廣色域,請將 AndroidManifest.xml 文件中的 colorMode 屬性設定爲 wideColorGamut。請注意,您須要爲每個啓用廣色域模式的 activity 重複以上設置。
android:colorMode="wideColorGamut"
複製代碼
您也能夠經過程序的方式在 activity 中設定色彩模式,具體方法爲: 調用 setColorMode(int) 方法並傳入 COLOR_MODE_WIDE_COLOR_GAMUT。
在渲染廣色域圖像時,除了具體的廣色域內容以外,您還須要建立一個廣色域 surface,以 OpenGL 爲例,應用必須先檢查如下擴展:
而後,在建立 surface 時請求 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;
}
複製代碼
若是您想了解如何在原生代碼中採用廣色域,請參閱官方文檔《使用廣色域內容加強圖形》。
圖片庫 API 設計指南
最後,若是您擁有或維護一個圖片編解碼庫,經過色彩校訂測試依舊是最低要求。爲了現代化您的圖片庫,咱們強烈建議您進行下列兩項工做以擴展色彩管理 API:
完成上述工做後,就請着手開展前文所述的兩項色彩測試工做吧!
點擊這裏瞭解更多移動開發相關產品內容