Migliora la grafica con contenuti a colori ampi

Android 8.0 (livello API 26) ha introdotto il supporto della gestione dei colori per spazi colore aggiuntivi oltre allo standard RGB (sRGB) per il rendering della grafica sui dispositivi con display compatibili. Con questo supporto, la tua app può eseguire il rendering di bitmap con profili di colore estesi incorporati caricati da file PNG, JPEG e WebP tramite Java o codice nativo. Le app che utilizzano OpenGL o Vulkan possono produrre direttamente contenuti con un'ampia gamma di colori (utilizzando Display P3 e scRGB). Questa funzionalità è utile per creare app che prevedono una riproduzione dei colori ad alta fedeltà, ad esempio app di modifica di immagini e video.

Informazioni sulla modalità Ampia gamma di colori

I profili di colore ampi sono profili ICC, come Adobe RGB, Pro Photo RGB e DCI-P3, che sono in grado di rappresentare una gamma di colori più ampia rispetto a sRGB. Gli schermi che supportano ampi profili colore possono visualizzare immagini con colori primari più profondi (rosso, verde e blu) e colori secondari più ricchi (come magenta, ciano e gialli).

Sui dispositivi Android con Android 8.0 (livello API 26) o versioni successive che lo supportano, la tua app può attivare la modalità colori con ampia gamma di colori per un'attività in cui il sistema riconosce ed elabora correttamente le immagini bitmap con profili di colore ampi incorporati. La classe ColorSpace.Named riporta un elenco parziale degli spazi colore di uso comune supportati da Android.

Nota: quando è attivata la modalità con gamma di colori ampia, la finestra dell'attività utilizza più memoria e elaborazione GPU per la composizione dello schermo. Prima di abilitare la modalità con gamma di colori ampia, valuta attentamente se l'attività ne trae davvero vantaggio. Ad esempio, un'attività che visualizza le foto a schermo intero è una buona candidata per la modalità con gamma di colori ampia, mentre un'attività che mostra miniature piccole non lo è.

Attiva la modalità gamma di colori ampia

Utilizza l'attributo colorMode per richiedere che l'attività venga visualizzata in modalità vasta gamma di colori sui dispositivi compatibili. In modalità con gamma di colori ampia, una finestra può essere visualizzata al di fuori della gamma sRGB per mostrare colori più vivaci. Se il dispositivo non supporta il rendering con ampia gamma di colori, questo attributo non ha alcun effetto. Se la tua app deve determinare se un determinato display supporta un'ampia gamma di colori, chiama il metodo isWideColorGamut(). Puoi anche chiamare isScreenWideColorGamut(), che restituisce true solo se il display supporta un'ampia gamma di colori e il dispositivo supporta il rendering cromatico con un'ampia gamma di colori.

Un display potrebbe supportare un'ampia gamma di colori, ma non essere gestito con colori; in questo caso, il sistema non concederà a un'app la modalità con un'ampia gamma di colori. Quando un display non è gestito con i colori, come nel caso di tutte le versioni di Android precedenti alla 8.0, il sistema rimappa i colori disegnati dall'app in base alla gamma del display.

Per attivare l'ampia gamma di colori nella tua attività, imposta l'attributo colorMode su wideColorGamut nel file AndroidManifest.xml. È necessario eseguire questa operazione per ogni attività per cui vuoi attivare la modalità colore ampio.

android:colorMode="wideColorGamut"

Puoi anche impostare la modalità colore in modo programmatico nella tua attività chiamando il metodo setColorMode(int) e trasmettendo COLOR_MODE_WIDE_COLOR_GAMUT.

Esegui il rendering di contenuti con un'ampia gamma di colori

Figura 1. Display P3 (arancione) rispetto agli spazi di colore sRGB (bianco)

Per visualizzare contenuti con un'ampia gamma di colori, la tua app deve caricare una bitmap a colori larga, ovvero una bitmap con un profilo colore contenente uno spazio colore più ampio di sRGB. I profili di colore ampi più comuni includono Adobe RGB, DCI-P3 e Display P3.

La tua app può eseguire query sullo spazio dei colori di una bitmap chiamando getColorSpace(). Per determinare se il sistema riconosce che uno spazio colore specifico è con un'ampia gamma, puoi chiamare il metodo isWideGamut().

La classe Color consente di rappresentare un colore con quattro componenti inclusi in un valore lungo a 64 bit, anziché la rappresentazione più comune che utilizza un valore intero. Tramite valori lunghi, puoi definire i colori con maggiore precisione rispetto ai valori interi. Se devi creare o codificare un colore come valore lungo, utilizza uno dei metodi pack() nella classe Color.

Puoi verificare se la tua app ha richiesto correttamente la modalità con gamma di colori ampia, controllando che il metodo getColorMode() restituisca COLOR_MODE_WIDE_COLOR_GAMUT (questo metodo non indica, tuttavia, se la modalità con gamma di colori ampia è stata effettivamente concessa).

Utilizza il supporto di un'ampia gamma di colori nel codice nativo

Questa sezione descrive come attivare la modalità con gamma di colori ampia con le API OpenGL e Vulkan se la tua app utilizza codice nativo.

OpenGL

Per utilizzare la modalità con gamma di colori ampia in OpenGL, l'app deve includere la libreria EGL 1.4 con una delle seguenti estensioni:

Per abilitare la funzionalità, devi prima creare un contesto GL tramite eglChooseConfig, con uno dei tre formati di buffer del colore supportati per il colore largo negli attributi. Il formato del buffer del colore per il colore ampio deve essere uno dei seguenti insiemi di valori RGBA:

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

Quindi, richiedi l'estensione dello spazio colore P3 durante la creazione dei target di rendering, come mostrato nel seguente snippet di codice:

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

Il supporto Vulkan per un'ampia gamma di colori è fornito tramite l'estensione VK_EXT_swapchain_colorspace.

Prima di attivare il supporto dei colori estesi nel codice Vulkan, verifica che l'estensione sia supportata tramite vkEnumerateInstanceExtensionProperties. Se l'estensione è disponibile, devi attivarla durante vkCreateInstance prima di creare immagini scambiabili che utilizzano gli spazi colore aggiuntivi definiti dall'estensione.

Prima di creare la catena di scambio, devi scegliere lo spazio colore desiderato, quindi scorrere tra le piattaforme fisiche dei dispositivi disponibili e scegliere un formato di colore valido per quello spazio colore.

Sui dispositivi Android, Vulkan supporta un'ampia gamma di colori con i seguenti spazi colore e VkSurfaceFormatKHR formati di colore:

  • Spazi colore con ampia gamma di colori Vulkan:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Formati di colore Vulkan con supporto di un'ampia gamma di colori:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

Il seguente snippet di codice mostra come verificare che il dispositivo supporti lo spazio colore di 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
}

Il seguente snippet di codice mostra come richiedere una swapchain Vulkan con 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;
}