Aprimorar gráficos com amplo conteúdo de cores

O Android 8.0 (API de nível 26) introduziu a compatibilidade com o gerenciamento de cores a outros espaços de cor (link em inglês) além do RGB padrão (sRGB) para renderizar gráficos em dispositivos com telas compatíveis. Com esse apoio, seu app pode renderizar bitmaps com perfis de cores amplos incorporados carregados de arquivos PNG, JPEG e WebP. via Java ou código nativo. Apps que usam OpenGL ou Vulkan podem gerar diretamente conteúdos com uma ampla gama de cores (usando o Display P3 e scRGB). Esse recurso útil para criar aplicativos que envolvem reprodução de cores de alta fidelidade, como imagens e vídeos e edição de texto.

Compreender o modo ampla gama de cores

Os perfis de cores amplos são perfis ICC, como Adobe RGB, Pro Photo RGB e DCI-P3, que são capazes de representar uma gama mais ampla de cores do que o sRGB. As telas que oferecem suporte a perfis de cores amplas podem mostrar imagens com cores primárias mais profundas (vermelho, verde e azul) e cores secundárias mais ricas (como magenta, ciano e amarelo).

Em dispositivos Android com Android 8.0 (nível 26 da API) ou versões mais recentes, o app pode ativar o modo ampla gama de cores para uma atividade em que o sistema reconhece e processa corretamente imagens de bitmap com perfis de cores amplos incorporados. O A classe ColorSpace.Named enumera uma lista parcial de objetos comumente usados espaços de cor compatíveis com o Android.

Observação:quando o modo ampla gama de cores estiver ativado, o método usa mais memória e processamento de GPU para a composição da tela. Antes de ativar o modo ampla gama de cores, considere se a atividade realmente se beneficia dele. Por exemplo, uma atividades que exibem fotos em tela cheia é uma boa candidata para o modo ampla gama de cores, mas uma atividade que mostra miniaturas pequenas não é.

Ativar o modo ampla gama de cores

Use o atributo colorMode para solicitar que a atividade seja mostrada. no modo ampla gama de cores em dispositivos compatíveis. No modo ampla gama de cores, uma janela pode renderizar fora da gama sRGB para mostrar cores mais vibrantes. Se o dispositivo não oferecer suporte à ampla gama de cores renderização de gama, esse atributo não terá efeito. Caso seu app precise determinar se uma tela é compatível com a ampla gama de cores, chame o método isWideColorGamut(). O app também pode chamar isScreenWideColorGamut(), que só retornará true se a tela for compatível com a ampla gama de cores e o dispositivo compatível com a renderização de cores da ampla gama de cores.

Uma tela pode ser compatível com a ampla gama de cores, mas não ser gerenciada por cores. Nesse caso, o sistema não concederá ao app o modo ampla gama de cores. Quando uma tela não é gerenciada por cores, o que era o caso de todas as versões do Android anteriores à 8.0, o sistema refaz o mapeamento das cores desenhadas pelo app para a gama da tela.

Para ativar a ampla gama de cores na sua atividade, defina a colorMode como wideColorGamut no arquivo AndroidManifest.xml. É necessário fazer isso para cada atividade para a qual você quer ativar o modo de cores amplas.

android:colorMode="wideColorGamut"

Você também pode definir o modo de cores de forma programática na sua atividade, chamando o método setColorMode(int) e transmitindo-o a COLOR_MODE_WIDE_COLOR_GAMUT.

Renderizar conteúdos de ampla gama de cores

Figura 1. Exibir espaços de cor P3 (laranja) x sRGB (branco)

Para renderizar conteúdos com ampla gama de cores, seu app precisa carregar um bitmap de cores amplo, ou seja, um bitmap com um perfil de cores que contenha um espaço de cor maior que sRGB. Os perfis de cores amplos comuns incluem Adobe RGB, DCI-P3 e Display P3.

Seu app pode consultar o espaço de cores de um bitmap chamando getColorSpace(): Para determinar se o sistema reconhece espaço de cores específico para ter ampla gama, você pode chamar isWideGamut().

A classe Color permite representar uma cor com quatro componentes compactados em um valor de 64 bits, em vez da representação mais comum que usa um valor inteiro. Usando valores longos, você pode definir cores com maior precisão do que valores inteiros. Se você precisar criar ou codificar uma cor como um valor longo, use um dos métodos pack() na classe Color.

Você pode verificar se o app solicitou corretamente o modo ampla gama de cores conferindo se o método getColorMode() retorna COLOR_MODE_WIDE_COLOR_GAMUT. Contudo, esse método não indica se o modo ampla gama de cores foi realmente concedido.

Usar a compatibilidade com a ampla gama de cores no código nativo

Esta seção descreve como ativar o modo ampla gama de cores com o OpenGL (em inglês) e APIs Vulkan se o app usar código nativo.

OpenGL

Para usar o modo de gama ampla de cores no OpenGL, seu app precisa incluir a biblioteca EGL 1.4 com uma das seguintes extensões:

Para ativar o recurso, é preciso primeiro criar um contexto de GL via eglChooseConfig, sendo que um dos três é compatível formatos de buffer de cores para cores amplas nos atributos. O formato do buffer de cores para modelos amplos a cor precisa ser um destes conjuntos de valores RGBA:

  • 8, 8, 8, 8
  • 10, 10, 10, 2
  • FP16, FP16, FP16, FP16

Em seguida, solicite a extensão do espaço de cor P3 ao criar seus destinos de renderização, conforme mostrado no snippet de código a seguir:

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

O suporte do Vulkan à ampla gama de cores é fornecido pelo VK_EXT_swapchain_colorspace.

Antes de ativar o suporte à ampla gama de cores no seu código Vulkan, verifique se o elemento tem suporte de vkEnumerateInstanceExtensionProperties: Se a extensão estiver disponível, você precisará ativá-la durante vkCreateInstance antes de criar imagens de cadeia de troca que use os espaços de cor adicionais definidos pela extensão.

Antes de criar a cadeia de troca, é necessário escolher o espaço de cor, percorrer as superfícies disponíveis do dispositivo físico e escolher um formato de cor válido para esse espaço.

Em dispositivos Android, o Vulkan oferece suporte à ampla gama de cores com os seguintes espaços de cor e Formatos de cor VkSurfaceFormatKHR:

  • Espaços de cor da gama ampla de cores do Vulkan:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Formatos de cores do Vulkan compatíveis com ampla gama de cores:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

O snippet de código a seguir mostra como verificar se o dispositivo é compatível com o Display P3. espaço de cores:

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
}

O snippet de código a seguir mostra como solicitar uma cadeia de troca do Vulkan com 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;
}