Aprimorar gráficos com amplo conteúdo de cores

O Android 8.0 (API de nível 26) introduziu suporte ao gerenciamento de cores para espaços de cor além padrão RGB (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

Perfis de cores amplos são Perfis ICC, como RGB da Adobe, Pro Photo RGB DCI-P3, que são capazes de representar uma gama mais ampla de cores do que sRGB. Telas com suporte a perfis de cores amplos pode exibir imagens com cores primárias mais profundas (vermelhos, verdes e azuis), bem como cores primárias mais ricas cores (como magentas, ciano e amarelo).

Em dispositivos com o Android 8.0 (nível 26 da API) ou versões mais recentes, seu app pode ativar o modo ampla gama de cores para uma atividade na qual o sistema reconhece e processar corretamente imagens bitmap com perfis de cores amplos incorporados. A 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 a ampla gama de cores você deve considerar cuidadosamente se a atividade realmente se beneficia dele. Por exemplo, uma atividade que exibe 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 exibir 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 retorna true somente se a tela for compatível com a ampla gama de cores e o dispositivo oferecer suporte a essa ampla gama de cores renderização de cores.

Uma tela pode ser compatível com a ampla gama de cores, mas não ser gerenciada por cores. Nesse caso, a sistema não vai conceder a um app o modo ampla gama de cores. Quando uma tela não é gerenciada por cores (como era o caso de todas as versões do Android anteriores à 8.0), o sistema remapeia o cores desenhadas pelo app na gama da tela.

Para ativar a ampla gama de cores na sua atividade, defina a colorMode como wideColorGamut no arquivo AndroidManifest.xml. Você precisa fazer isso para cada atividade para a qual deseja ativar o modo de gama ampla de cores.

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 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údo da ampla gama de cores, seu app precisa carregar um bitmap de cores ampla, que é um bitmap com um perfil de cor com um espaço de cor maior do 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. em um valor de 64 bits, em vez da representação mais comum que usa um número 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 o método getColorMode() retorna COLOR_MODE_WIDE_COLOR_GAMUT (esse método não indica, no entanto, se o modo de gama ampla 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 cores P3 ao criar seu 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 em 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, é preciso escolher o espaço de cores desejado e passar pelas nas superfícies de dispositivos físicos disponíveis e escolha um formato de cor válido espaço de cores.

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;
}