利用廣色內容增強圖像效果

Android 8.0 (API 級別 26) 推出了色彩管理支援功能,可為 多色空間 在具有相容顯示器的裝置上呈現圖形的標準 RGB (sRGB)。有了這項支援,應用程式就能透過 Java 或原生程式碼,從 PNG、JPEG 和 WebP 檔案載入嵌入式寬色彩設定檔,並轉譯位元陣列。使用 OpenGL 或 Vulkan 的應用程式可以直接輸出廣色域內容 (使用 Display P3scRGB)。這項功能適用於建立涉及高保真色彩重現的應用程式,例如圖片和影片編輯應用程式。

瞭解廣色域模式

廣色域有 ICC 設定檔,例如 Adobe RGB 專業相片 RGB DCI-P3,也就是 能夠呈現比 sRGB 更多的色彩範圍。支援廣色設定檔的螢幕 可顯示深層主色 (紅色、綠色和藍色) 的圖片,以及更豐富的次要顏色 顏色 (例如洋紅色、青色和黃色)。

在支援 Android 8.0 (API 級別 26) 以上版本的 Android 裝置上,應用程式可以 針對系統辨識且 能正確處理含內嵌廣色設定檔的點陣圖圖片。 ColorSpace.Named 類別列舉一部分的常用 Android 支援的色域

注意:啟用廣色域模式後,活動視窗會使用更多記憶體和 GPU 處理作業來進行螢幕合成。啟用寬色域模式前,請仔細考量活動是否確實能從中受益。舉例來說 以全螢幕顯示相片的活動是廣色域模式的理想選擇 顯示小型縮圖的活動則不屬於。

啟用廣色域模式

使用 colorMode 屬性要求顯示的活動 在相容裝置上支援廣色域模式。在廣色域模式下,視窗可轉譯 sRGB 以外的色域,顯示更加鮮豔的色彩。如果裝置不支援廣色 銀河算繪,這個屬性不會有任何作用。如果應用程式需要判斷 螢幕是廣色域功能 isWideColorGamut() 方法。您的應用程式也可以呼叫 isScreenWideColorGamut(),但只有在螢幕支援廣色域且裝置支援廣色域色彩算繪時,才會傳回 true

螢幕可能是廣色可變色,但無法管理顏色。在這種情況下, 系統將不會授予應用程式廣色域模式。如果螢幕未經過色彩管理 (例如 8.0 之前的所有 Android 版本),系統會將應用程式繪製的顏色重新對應至螢幕的色域。

如要在活動中啟用廣色域,請在 AndroidManifest.xml 檔案中將 colorMode 屬性設為 wideColorGamut。您必須針對要啟用廣色域模式的每個活動執行這項操作。

android:colorMode="wideColorGamut"

您也可以呼叫 setColorMode(int) 方法並傳入 COLOR_MODE_WIDE_COLOR_GAMUT,以程式輔助方式在活動中設定色彩模式。

算繪廣色域內容

圖 1. Display P3 (橘色) 與 sRGB (白色) 色域

如要算繪廣色域的內容,應用程式必須載入寬色點陣圖, 包含大於 sRGB 色彩空間的色彩設定檔。常見的廣角色彩描述檔包括 Adobe RGB、DCI-P3 和 Display P3。

應用程式可以呼叫 getColorSpace(),查詢位圖的色彩空間。如要判斷系統是否將特定色域視為廣色域,您可以呼叫 isWideGamut() 方法。

Color 類別可讓您以四個元件組成的 64 位元長值表示顏色,而非使用整數值的常見表示法。您可以使用長值定義顏色 精確度大於整數值如果您需要建立或將顏色編碼為長值,請使用 Color 類別中的其中一個 pack() 方法。

您可以檢查應用程式是否正確要求廣色域模式,方法是檢查 getColorMode() 方法會傳回 COLOR_MODE_WIDE_COLOR_GAMUT (這個方法不會指出 但卻表示實際上是否授予廣色域模式)。

在原生程式碼中使用廣色域支援功能

如果應用程式使用原生程式碼,本節將說明如何使用 OpenGLVulkan 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;
}