O Android 8.0 (nível 26 da API) introduziu suporte ao gerenciamento de cores com outros espaços de cor além do RGB padrão (sRGB) para renderizar gráficos em dispositivos com telas compatíveis. Com essa compatibilidade, seu app pode renderizar bitmaps com perfis de cores amplos incorporados carregados de arquivos PNG, JPEG e WebP usando Java ou código nativo. Apps que usam OpenGL ou Vulkan podem gerar diretamente conteúdos de ampla gama de cores (usando o Display P3 e o scRGB). Esse recurso é útil para criar apps que envolvem reprodução de cores de alta fidelidade, como apps de edição de imagens e vídeos.
Compreender o modo ampla gama de cores
Perfis amplos de cores são perfis ICC, como Adobe RGB, Pro Photo RGB e DCI-P3, que são capazes de representar uma variedade maior de cores do que o sRGB. Telas com suporte a perfis de cores amplos podem exibir imagens com cores primárias mais profundas (vermelhos, verdes e azuis), além de cores secundárias mais ricas (como magentas, cianos e amarelos).
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. A
classe ColorSpace.Named
enumera uma lista parcial de espaços de cor comumente usados
que têm suporte no Android.
Observação:quando o modo ampla gama de cores está ativado, a janela da atividade usa mais memória e processamento de GPU para a composição da tela. Antes de ativar o modo ampla gama de cores, considere cuidadosamente se a atividade realmente se beneficia dele. Por exemplo, uma atividade que mostra 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. Nesse modo, uma janela pode renderizar
fora da gama sRGB para mostrar cores mais vibrantes. Se o dispositivo não tiver suporte ao modo de gama ampla
de cores, o atributo não terá efeito. Se o app precisar determinar se uma determinada
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 à 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á a um app o modo ampla gama de cores. Quando uma tela não é gerenciada por cores, como acontece com todas as versões do Android anteriores à 8.0, o sistema remapeia as cores desenhadas pelo app para a gama da tela.
Para ativar a gama ampla de cores na sua atividade, defina o atributo colorMode
como wideColorGamut
no arquivo AndroidManifest.xml
. É
necessário fazer isso para cada atividade para a qual você quer ativar o modo amplo de cores.
android:colorMode="wideColorGamut"
Você também pode definir o modo de cor de forma programática na atividade, chamando o
método setColorMode(int)
e transmitindo
COLOR_MODE_WIDE_COLOR_GAMUT
.
Renderizar conteúdos de ampla gama de cores
Para renderizar um conteúdo de 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 cores mais amplo que o sRGB. Os perfis amplos de cores comuns incluem Adobe RGB, DCI-P3 e Display P3.
Seu app pode consultar o espaço de cor de um bitmap chamando
getColorSpace()
. Para determinar se o sistema reconhece um
espaço de cor específico para ter ampla gama, chame o
método isWideGamut()
.
A classe Color
permite representar uma cor com quatro componentes
empacotados em um valor longo de 64 bits, em vez da representação mais comum que usa um valor
inteiro. Usando valores longos, você pode definir cores com
mais 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
. No entanto, esse método não indica
se o modo 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 as APIs OpenGL e Vulkan caso seu app use código nativo.
OpenGL
Para usar o modo ampla gama de cores no OpenGL, seu app precisa incluir a biblioteca EGL 1.4 com uma das seguintes extensões:
Para ativar o recurso, primeiro é necessário criar um contexto de GL via
eglChooseConfig
, com um dos três formatos de buffer de cores
com suporte para cores amplas nos atributos. O formato do buffer de cores para cores amplas
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 de espaço de cores 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 pela
extensão VK_EXT_swapchain_colorspace
.
Antes de ativar o suporte a cores amplas no seu código Vulkan, confira se a
extensão oferece suporte via
vkEnumerateInstanceExtensionProperties
.
Se a extensão estiver disponível, será necessário ativá-la durante
vkCreateInstance
antes de criar qualquer imagem de cadeia de troca que
use os outros espaços de cor definidos pela extensão.
Antes de criar a cadeia de troca, é necessário escolher o espaço de cor desejado, percorrer as superfícies disponíveis do dispositivo físico e escolher um formato de cor válido para esse 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 cores da gama ampla de cores do Vulkan:
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
- Formatos de cores com suporte para ampla gama de cores do Vulkan:
VK_FORMAT_R16G16B16A16_SFLOAT
VK_FORMAT_A2R10G10B10_UNORM_PACK32
VK_FORMAT_R8G8B8A8_UNORM
O snippet de código abaixo mostra como conferir se o dispositivo oferece suporte ao espaço de cores do 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 }
O snippet de código abaixo 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; }