Améliorer les graphiques avec du contenu à large gamme de couleurs

Android 8.0 (niveau d'API 26) prend en charge la gestion des couleurs pour d'autres espaces de couleur en plus du RVB standard (sRVB) pour le rendu des graphiques sur les appareils équipés d'écrans compatibles. Grâce à cette prise en charge, votre application peut afficher des bitmaps avec des profils de couleur larges intégrés chargés à partir de fichiers PNG, JPEG et WebP via Java ou du code natif. Les applications utilisant OpenGL ou Vulkan peuvent générer directement du contenu à large gamme de couleurs (à l'aide de Display P3 et scRGB). Cette fonctionnalité est utile pour créer des applications qui impliquent une reproduction des couleurs haute fidélité, telles que des applications de montage d'images et de vidéos.

Comprendre le mode large gamme de couleurs

Les profils de couleurs larges sont des profils ICC, tels que Adobe RVB, Pro Photo RVB et DCI-P3, capables de représenter une gamme de couleurs plus large que le sRVB. Les écrans compatibles avec les profils de couleurs larges peuvent afficher des images aux couleurs primaires plus profondes (rouge, vert et bleu) ainsi que des couleurs secondaires plus riches (telles que le magentas, le cyans et le jaune).

Sur les appareils Android équipés d'Android 8.0 (niveau d'API 26) ou version ultérieure compatible, votre application peut activer le mode couleur large gamme de couleurs pour une activité permettant au système de reconnaître et de traiter correctement les images bitmap avec des profils de couleur larges intégrés. La classe ColorSpace.Named énumère une liste partielle des espaces de couleur couramment utilisés compatibles avec Android.

Remarque:Lorsque le mode large gamme de couleurs est activé, la fenêtre de l'activité utilise davantage de mémoire et de traitement GPU pour la composition de l'écran. Avant d'activer le mode large gamme de couleurs, vous devez soigneusement déterminer si l'activité en profite réellement. Par exemple, une activité qui affiche des photos en plein écran est adaptée au mode large gamme de couleurs, mais pas une activité qui affiche de petites vignettes.

Activer le mode large gamme de couleurs

Utilisez l'attribut colorMode pour demander l'affichage de l'activité en mode large gamme de couleurs sur les appareils compatibles. En mode large gamme de couleurs, une fenêtre peut s'afficher en dehors de la gamme sRVB pour afficher des couleurs plus vives. Si l'appareil n'est pas compatible avec l'affichage en mode large gamme de couleurs, cet attribut n'a aucun effet. Si votre application doit déterminer si un écran donné est compatible avec la gamme de couleurs large, appelez la méthode isWideColorGamut(). Votre application peut également appeler isScreenWideColorGamut(), qui renvoie true uniquement si l'écran est compatible avec la gamme de couleurs large et si l'appareil est compatible avec le rendu de couleurs à large gamme de couleurs.

Un écran peut être compatible avec la gamme de couleurs large, mais pas géré par couleur. Dans ce cas, le système n'accordera pas à une application le mode large gamme de couleurs. Lorsqu'un écran n'est pas géré par les couleurs (comme c'était le cas pour toutes les versions d'Android antérieures à la version 8.0), le système remappe les couleurs dessinées par l'application sur la gamme de l'écran.

Pour activer la large gamme de couleurs dans votre activité, définissez l'attribut colorMode sur wideColorGamut dans votre fichier AndroidManifest.xml. Vous devez effectuer cette opération pour chaque activité pour laquelle vous souhaitez activer le mode large gamme de couleurs.

android:colorMode="wideColorGamut"

Vous pouvez également définir le mode couleur par programmation dans votre activité en appelant la méthode setColorMode(int) et en transmettant COLOR_MODE_WIDE_COLOR_GAMUT.

Afficher du contenu à large gamme de couleurs

Figure 1. : Écran P3 (orange) ou sRVB (blanc) avec espaces de couleur

Pour afficher le contenu à large gamme de couleurs, votre application doit charger un bitmap de couleur large, c'est-à-dire un bitmap avec un profil de couleur contenant un espace colorimétrique plus large que sRVB. Les profils de couleurs larges courants incluent Adobe RVB, DCI-P3 et Display P3.

Votre application peut interroger l'espace colorimétrique d'un bitmap en appelant getColorSpace(). Pour déterminer si le système reconnaît un espace colorimétrique spécifique comme étant à large gamme, vous pouvez appeler la méthode isWideGamut().

La classe Color vous permet de représenter une couleur avec quatre composants dans une valeur de 64 bits, au lieu de la représentation la plus courante qui utilise une valeur entière. Les valeurs longues vous permettent de définir des couleurs avec plus de précision que les valeurs entières. Si vous devez créer ou encoder une couleur en tant que valeur longue, utilisez l'une des méthodes pack() de la classe Color.

Vous pouvez vérifier si votre application a correctement demandé le mode large gamme de couleurs en vérifiant que la méthode getColorMode() renvoie COLOR_MODE_WIDE_COLOR_GAMUT (cette méthode n'indique toutefois pas si le mode large gamme de couleurs a été accordé).

Utiliser la compatibilité avec la gamme de couleurs large dans le code natif

Cette section explique comment activer le mode large gamme de couleurs avec les API OpenGL et Vulkan si votre application utilise du code natif.

OpenGL

Pour utiliser le mode large gamme de couleurs dans OpenGL, votre application doit inclure la bibliothèque EGL 1.4 avec l'une des extensions suivantes:

Pour activer cette fonctionnalité, vous devez d'abord créer un contexte GL via eglChooseConfig, avec l'un des trois formats de tampon de couleur acceptés pour la couleur large dans les attributs. Le format du tampon de couleur pour la couleur large doit correspondre à l'un des ensembles de valeurs RVBA suivants:

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

Demandez ensuite l'extension d'espace colorimétrique P3 lors de la création de vos cibles de rendu, comme indiqué dans l'extrait de code suivant:

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

La prise en charge de la large gamme de couleurs avec Vulkan est fournie via l'extension VK_EXT_swapchain_colorspace.

Avant d'activer la prise en charge étendue des couleurs dans votre code Vulkan, vérifiez d'abord que l'extension est compatible via vkEnumerateInstanceExtensionProperties. Si l'extension est disponible, vous devez l'activer pendant vkCreateInstance avant de créer des images de chaîne de permutation qui utilisent les espaces de couleur supplémentaires définis par l'extension.

Avant de créer la chaîne de permutation, vous devez choisir l'espace colorimétrique souhaité, puis parcourir les surfaces physiques disponibles de l'appareil et choisir un format de couleur valide pour cet espace de couleur.

Sur les appareils Android, Vulkan est compatible avec une large gamme de couleurs avec les espaces de couleurs et les formats de couleurs VkSurfaceFormatKHR suivants:

  • Espaces de couleurs à large gamme de couleurs Vulkan :
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Formats de couleurs Vulkan compatibles avec la large gamme de couleurs :
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

L'extrait de code suivant montre comment vérifier que l'appareil est compatible avec l'espace colorimétrique 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
}

L'extrait de code suivant montre comment demander une chaîne d'échange Vulkan avec 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;
}