Улучшите графику с помощью широкого цветового контента.

В Android 8.0 (уровень API 26) появилась поддержка управления цветом для дополнительных цветовых пространств помимо стандартного RGB (sRGB) для рендеринга графики на устройствах с совместимыми дисплеями. Благодаря этой поддержке ваше приложение может отображать растровые изображения со встроенными широкими цветовыми профилями, загруженными из файлов PNG, JPEG и WebP через Java или собственный код. Приложения, использующие OpenGL или Vulkan, могут напрямую выводить контент с широкой цветовой гаммой (с использованием Display P3 и scRGB ). Эта возможность полезна для создания приложений, обеспечивающих высокую точность цветопередачи, например приложений для редактирования изображений и видео.

Понимание режима широкой цветовой гаммы

Широкие цветовые профили — это профили ICC , такие как Adobe RGB , Pro Photo RGB и DCI-P3 , которые способны отображать более широкий диапазон цветов, чем sRGB. Экраны, поддерживающие широкие цветовые профили, могут отображать изображения с более глубокими основными цветами (красный, зеленый и синий), а также с более насыщенными вторичными цветами (например, пурпурным, голубым и желтым).

На устройствах Android под управлением Android 8.0 (уровень API 26) или выше, которые его поддерживают, ваше приложение может включить цветовой режим широкой цветовой гаммы для действия, при котором система распознает и правильно обрабатывает растровые изображения со встроенными широкими цветовыми профилями. Класс ColorSpace.Named перечисляет неполный список часто используемых цветовых пространств, которые поддерживает Android.

Примечание. Если включен режим широкой цветовой гаммы, окно действия использует больше памяти и обработки графического процессора для композиции экрана. Прежде чем включать режим широкой цветовой гаммы, вам следует тщательно подумать, действительно ли это занятие приносит пользу. Например, действие, отображающее фотографии в полноэкранном режиме, является хорошим кандидатом на режим широкой цветовой гаммы, а действие, отображающее небольшие миниатюры, — нет.

Включить режим широкой цветовой гаммы

Используйте атрибут colorMode , чтобы запросить отображение активности в режиме широкой цветовой гаммы на совместимых устройствах. В режиме широкой цветовой гаммы окно может отображаться за пределами гаммы sRGB, чтобы отображать более яркие цвета. Если устройство не поддерживает рендеринг широкой цветовой гаммы, этот атрибут не имеет никакого эффекта. Если вашему приложению необходимо определить, поддерживает ли данный дисплей широкую цветовую гамму, вызовите метод isWideColorGamut() . Приложение также может вызвать isScreenWideColorGamut() , который возвращает true только в том случае, если дисплей поддерживает широкую цветовую гамму и устройство поддерживает цветопередачу с широкой цветовой гаммой.

Дисплей может поддерживать широкую цветовую гамму, но не управлять цветом. В этом случае система не предоставит приложению режим широкой цветовой гаммы. Если дисплей не управляет цветом — как это было во всех версиях Android до 8.0 — система переназначает цвета, нарисованные приложением, на гамму дисплея.

Чтобы включить широкую цветовую гамму в своей деятельности, установите для атрибута colorMode значение wideColorGamut в файле AndroidManifest.xml . Это необходимо сделать для каждого действия, для которого вы хотите включить широкий цветовой режим.

android:colorMode="wideColorGamut"

Вы также можете программно установить цветовой режим в своей деятельности, вызвав метод setColorMode(int) и передав COLOR_MODE_WIDE_COLOR_GAMUT .

Рендеринг содержимого с широкой цветовой гаммой

Рис. 1. Цветовые пространства дисплея P3 (оранжевый) и sRGB (белый)

Для рендеринга содержимого с широкой цветовой гаммой ваше приложение должно загрузить широкоцветное растровое изображение, то есть растровое изображение с цветовым профилем, содержащим цветовое пространство шире, чем sRGB. Распространенные широкие цветовые профили включают Adobe RGB, DCI-P3 и Display P3.

Ваше приложение может запросить цветовое пространство растрового изображения, вызвав getColorSpace() . Чтобы определить, распознает ли система определенное цветовое пространство как широкое, вы можете вызвать метод isWideGamut() .

Класс Color позволяет представлять цвет с четырьмя компонентами, упакованными в 64-битное значение вместо наиболее распространенного представления, использующего целочисленное значение. Используя длинные значения, вы можете определять цвета с большей точностью, чем целые значения. Если вам нужно создать или закодировать цвет как длинное значение, используйте один из методов pack() в классе Color .

Вы можете проверить, правильно ли ваше приложение запросило режим широкой цветовой гаммы, проверив, что метод getColorMode() возвращает COLOR_MODE_WIDE_COLOR_GAMUT (однако этот метод не указывает, был ли фактически предоставлен режим широкой цветовой гаммы).

Используйте поддержку широкой цветовой гаммы в собственном коде.

В этом разделе описывается, как включить режим широкой цветовой гаммы с помощью API OpenGL и Vulkan, если ваше приложение использует собственный код.

OpenGL

Чтобы использовать режим широкой цветовой гаммы в OpenGL, ваше приложение должно включать библиотеку EGL 1.4 с одним из следующих расширений:

Чтобы включить эту функцию, вы должны сначала создать контекст GL с помощью eglChooseConfig с одним из трех поддерживаемых форматов цветового буфера для широкого цвета в атрибутах. Формат цветового буфера для широкого цвета должен быть одним из следующих наборов значений RGBA:

  • 8, 8, 8, 8
  • 10, 10, 10, 2
  • РП16, РП16, РП16, РП16

Затем запросите расширение цветового пространства 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 широкой цветовой гаммы обеспечивается через расширение 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
}

В следующем фрагменте кода показано, как запросить цепочку обмена Vulkan с помощью 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;
}