除了標準 RGB (sRGB),Android 8.0 (API 級別 26) 還支援其他色域,支援在相容螢幕裝置上算繪圖像。透過這項支援,您的應用程式可以使用從 PNG、JPEG 和 WebP 檔案載入的 PNG、JPEG 和 WebP 檔案,透過 Java 或原生程式碼載入點陣圖。使用 OpenGL 或 Vulkan 的應用程式可以直接輸出廣色域內容 (使用 Display P3 和 scRGB)。這項功能有助於建立涉及高擬真色彩複製的應用程式,例如圖片和影片編輯應用程式。
瞭解廣色域模式
廣色設定檔是 ICC 設定檔,例如 Adobe RGB、 Pro Photo RGB 和 DCI-P3,能夠代表 sRGB 的顏色範圍更廣。支援寬色設定檔的螢幕可顯示圖片具有更深的主要顏色 (紅色、綠色和藍色),以及更豐富的次要顏色 (例如洋紅色、青色和黃色)。
在搭載 Android 8.0 (API 級別 26) 以上版本且支援這項功能的 Android 裝置上,應用程式可以針對該活動啟用廣色域色彩模式。在這個情況下,系統會辨識並正確處理含有內嵌廣色設定檔的點陣圖圖片。ColorSpace.Named
類別會列舉 Android 支援的常用色域部分清單。
注意:啟用廣色域模式時,活動的視窗會使用更多記憶體和 GPU 處理畫面組合。啟用廣色域模式之前,您應謹慎考量活動是否確實有益處。舉例來說,在全螢幕模式中顯示相片的活動適合採用廣色域模式,但顯示小縮圖的活動則不適合。
啟用廣色域模式
使用 colorMode
屬性,要求在相容裝置上以廣色域模式顯示活動。在廣色域模式下,視窗可在 sRGB 色域外轉譯,顯示更加鮮豔的色彩。如果裝置不支援廣色域轉譯,則這個屬性不會產生任何作用。如果應用程式需要判斷特定螢幕是否支援廣色域,請呼叫 isWideColorGamut()
方法。您也可以呼叫 isScreenWideColorGamut()
,只有在螢幕支援廣色域,且裝置支援廣色域色算繪時,才會傳回 true
。
螢幕可能可以有廣色域,但無法使用顏色管理,在這種情況下,系統不會將廣色域模式授予應用程式。如果螢幕未以顏色管理 (例如 Android 8.0 以下版本的所有版本),系統會將應用程式繪製的顏色重新對應至螢幕的色域。
如要在活動中啟用廣色域,請在 AndroidManifest.xml
檔案中將 colorMode
屬性設為 wideColorGamut
。您必須為要啟用廣色模式的每個活動執行此操作。
android:colorMode="wideColorGamut"
您也可以呼叫 setColorMode(int)
方法並傳入 COLOR_MODE_WIDE_COLOR_GAMUT
,以程式輔助方式在活動中設定色彩模式。
顯示廣色域內容
![](https://developer.android.com/static/images/colorspace_display_p3.png?authuser=0000&hl=zh-tw)
如要算繪廣色域內容,應用程式必須載入寬色點陣圖,也就是具有色彩設定檔的點陣圖,其中包含寬度大於 sRGB 的色域。常見的廣色設定檔包括 Adobe RGB、DCI-P3 和 Display P3。
應用程式可以呼叫 getColorSpace()
,查詢點陣圖的色域。如要判斷系統是否將特定色域識別為廣域,您可以呼叫 isWideGamut()
方法。
Color
類別可讓您以四個元件表示顏色,並封裝成 64 位元長值,而非使用整數值的最常見表示法。使用長值時,您可以定義比整數值更精準的顏色。如果您需要建立顏色,或將顏色編碼為長值,請使用 Color
類別中的任一 pack()
方法。
您可以檢查 getColorMode()
方法是否傳回 COLOR_MODE_WIDE_COLOR_GAMUT
,藉此確認應用程式是否正確要求廣色域模式。不過,這個方法不會表示是否已取得廣色域模式。
在原生程式碼中使用廣色域支援
本節說明如果應用程式使用原生程式碼,該如何透過 OpenGL 和 Vulkan API 啟用廣色域模式。
OpenGL
如要在 OpenGL 中使用廣色域模式,應用程式必須含有 EGL 1.4 程式庫和下列任一擴充功能:
如要啟用這項功能,您必須先透過 eglChooseConfig
建立 GL 結構定義,並使用屬性中適用於廣色色彩的三種顏色緩衝區格式之一。廣色域的色彩緩衝區格式必須是下列其中一種 RGBA 值組合:
- 8、8、8、8
- 10、10、10、2
- FP16、FP16、FP16、FP16
接著,請在建立轉譯目標時要求 P3 色域擴充功能,如以下程式碼片段所示:
std::vector<EGLint> attributes; attributes.push_back(EGL_GL_COLORSPACE_KHR); attributes.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); attributes.push_back(EGL_NONE); engine->surface_ = eglCreateWindowSurface( engine->display_, config, engine->app->window, attributes.data());
Vulkan
廣色域的 Vulkan 支援透過 VK_EXT_swapchain_colorspace
擴充功能提供。
在 Vulkan 程式碼中啟用廣色支援功能之前,請先檢查透過 vkEnumerateInstanceExtensionProperties
是否支援擴充功能。如果有可用的擴充功能,您必須先在 vkCreateInstance
期間啟用擴充功能,才能建立使用擴充功能定義的其他色彩空間的任何交換鏈圖片。
建立交換鏈之前,您需要先選擇想要的顏色空間,然後循環檢視可用的實體裝置介面,並為該色域選擇有效的色彩格式。
在 Android 裝置上,Vulkan 支援採用下列色域和 VkSurfaceFormatKHR
顏色格式的廣色域:
- Vulkan 廣色域色域:
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
- 支援廣色域的 Vulkan 顏色格式:
VK_FORMAT_R16G16B16A16_SFLOAT
VK_FORMAT_A2R10G10B10_UNORM_PACK32
VK_FORMAT_R8G8B8A8_UNORM
下列程式碼片段說明如何檢查裝置是否支援 Display P3 色域:
uint32_t formatCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR( vkPhysicalDev, vkSurface, &formatCount, nullptr); VkSurfaceFormatKHR *formats = new VkSurfaceFormatKHR[formatCount]; vkGetPhysicalDeviceSurfaceFormatsKHR( vkPhysicalDev, vkSurface, &formatCount, formats); uint32_t displayP3Index = formatCount; for (uint32_t idx = 0; idx < formatCount; idx++) { if (formats[idx].format == requiredSwapChainFmt && formats[idx].colorSpace==VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT) { displayP3Index = idx; break; } } if (displayP3Index == formatCount) { // Display P3 is not supported on the platform // choose other format }
下列程式碼片段說明如何使用 VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
要求 Vulkan 交換鏈:
uint32_t queueFamily = 0; VkSwapchainCreateInfoKHR swapchainCreate { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, .pNext = nullptr, .surface = AndroidVkSurface_, .minImageCount = surfaceCapabilities.minImageCount, .imageFormat = requiredSwapChainFmt, .imageColorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, .imageExtent = surfaceCapabilities.currentExtent, .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, .imageArrayLayers = 1, .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 1, .pQueueFamilyIndices = &queueFamily, .presentMode = VK_PRESENT_MODE_FIFO_KHR, .oldSwapchain = VK_NULL_HANDLE, .clipped = VK_FALSE, }; VkRresult status = vkCreateSwapchainKHR( vkDevice, &swapchainCreate, nullptr, &vkSwapchain); if (status != VK_SUCCESS) { // Display P3 is not supported return false; }