Android 8.0 (API level 26) introduced color management support for additional color spaces besides standard RGB (sRGB) for rendering graphics on devices with compatible displays. With this support, your app can render bitmaps with embedded wide color profiles loaded from PNG, JPEG, and WebP files via Java or native code. Apps using OpenGL or Vulkan can directly output wide color gamut content (using Display P3 and scRGB). This capability is useful for creating apps that involve high fidelity color reproduction, such as image and video editing apps.
Understand the wide color gamut mode
Wide color profiles are ICC profiles, such as Adobe RGB, Pro Photo RGB, and DCI-P3, that are capable of representing a wider range of colors than sRGB. Screens supporting wide color profiles can display images with deeper primary colors (reds, greens, and blues) as well as richer secondary colors (such as magentas, cyans, and yellows).
On Android devices running Android 8.0 (API level 26) or higher that support it, your app can
enable the wide color gamut color mode for an activity whereby the system recognizes and
correctly process bitmap images with embedded wide color profiles. The
ColorSpace.Named
class enumerates a partial list of commonly used
color spaces that Android supports.
Note: When wide color gamut mode is enabled, the activity's window uses more memory and GPU processing for screen composition. Before enabling wide color gamut mode, you should carefully consider if the activity truly benefits from it. For example, an activity that displays photos in fullscreen is a good candidate for wide color gamut mode, but an activity that shows small thumbnails is not.
Enable wide color gamut mode
Use the colorMode
attribute to request the activity to be displayed
in wide color gamut mode on compatible devices. In wide color gamut mode, a window can render
outside of the sRGB gamut to display more vibrant colors. If the device does not support wide color
gamut rendering, this attribute has no effect. If your app needs to determine whether a given
display is wide color gamut capable, call the
isWideColorGamut()
method. You app can also call
isScreenWideColorGamut()
, which returns true
only if the display is wide color gamut capable and the device supports wide color gamut
color rendering.
A display might be wide color gamut capable but not color-managed, in which case, the system will not grant an app the wide color gamut mode. When a display is not color-managed —as was the case for all versions of Android prior to 8.0—the system remaps the colors drawn by the app to the display's gamut.
To enable the wide color gamut in your activity, set the colorMode
attribute to wideColorGamut
in your AndroidManifest.xml
file. You
need to do this for each activity for which you want to enable wide color mode.
android:colorMode="wideColorGamut"
You can also set the color mode programmatically in your activity by calling the
setColorMode(int)
method and passing in
COLOR_MODE_WIDE_COLOR_GAMUT
.
Render wide color gamut content
To render wide color gamut content, your app must load a wide color bitmap, that is a bitmap with a color profile containing a color space wider than sRGB. Common wide color profiles include Adobe RGB, DCI-P3 and Display P3.
Your app can query the color space of a bitmap, by calling
getColorSpace()
. To determine if the system recognizes a
specific color space to be wide gamut, you can call the
isWideGamut()
method.
The Color
class allows you to represent a color with four components
packed into a 64-bit long value, instead of the most common representation that uses an integer
value. Using long values, you can define colors with
more precision than integer values. If you need to create or encode a color as a long value, use
one of the pack()
methods in the Color
class.
You can verify whether your app properly requested the wide color gamut mode, by checking that
the getColorMode()
method returns
COLOR_MODE_WIDE_COLOR_GAMUT
(this method does not indicate,
however, whether the wide color gamut mode was actually granted).
Use wide color gamut support in native code
This section describes how to enable wide color gamut mode with the OpenGL and Vulkan APIs if your app uses native code.
OpenGL
In order to use wide color gamut mode in OpenGL, your app must include the EGL 1.4 library with one of the following extensions:
To enable the feature, you must first create a GL context via
eglChooseConfig
, with one of the three supported
color buffer formats for wide color in the attributes. The color buffer format for wide
color must be one of these sets of RGBA values:
- 8, 8, 8, 8
- 10, 10, 10, 2
- FP16, FP16, FP16, FP16
Then, request the P3 color space extension when creating your render targets, as shown in the following code snippet:
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
The Vulkan support for wide color gamut is provided through the
VK_EXT_swapchain_colorspace
extension.
Before enabling wide color support in your Vulkan code, first check that the
extension is supported via
vkEnumerateInstanceExtensionProperties
.
If the extension is available, you must enable it during
vkCreateInstance
before creating any swapchain images that
use the additional color spaces defined by the extension.
Before creating the swapchain, you need choose your desired color space, then loop through the available physical device surfaces and choose a valid color format for that color space.
On Android devices, Vulkan supports wide color gamut with the following color spaces and
VkSurfaceFormatKHR
color formats:
- Vulkan wide color gamut color spaces:
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
- Vulkan color formats with wide color gamut support:
VK_FORMAT_R16G16B16A16_SFLOAT
VK_FORMAT_A2R10G10B10_UNORM_PACK32
VK_FORMAT_R8G8B8A8_UNORM
The following code snippet shows how you can check that the device supports the Display P3 color space:
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 }
The following code snippet shows how to request a Vulkan swapchain with
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
:
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; }