Wzbogacanie grafiki o bogatą kolorystykę

W Androidzie 8.0 (poziom API 26) wprowadziliśmy obsługę zarządzania kolorami przestrzenie kolorów oprócz standardowy RGB (sRGB) do renderowania grafiki na urządzeniach ze zgodnymi wyświetlaczami. Dzięki tej funkcji aplikacja może renderować bitmapy z wbudowanymi profilami szerokiej gamy kolorów wczytanymi z plików PNG, JPEG i WebP za pomocą kodu Java lub natywnego. Aplikacje korzystające z platformy OpenGL lub Vulkan mogą bezpośrednio generować treści o szerokiej gamie kolorów (za pomocą Display P3 oraz scRGB). Ta funkcja jest przydatna do tworzenia aplikacji, które wymagają wiernego odwzorowania kolorów, takich jak aplikacje do edycji zdjęć i filmów.

Informacje o trybie szerokiej gamy kolorów

Profile o szerokiej gamie kolorów to profile ICC, takie jak Adobe RGB, Pro Photo RGB i DCI-P3, które mogą reprezentować szerszy zakres kolorów niż sRGB. Ekrany obsługujące szerokie profile kolorów może wyświetlać obrazy o głębszych kolorach podstawowych (czerwonym, zielonym i niebieskim) oraz bogatsze kolory dodatkowe kolorów (takich jak purpurowe, błękitne i żółte).

Na urządzeniach z Androidem 8.0 (poziom interfejsu API 26) lub nowszym, które obsługują tę funkcję, aplikacja może włączyć tryb kolorów o szerokim zakresie barw w ramach aktywności, w której system rozpoznaje i prawidłowo przetwarza obrazy bitmapowe z wbudowanymi profilami kolorów o szerokim zakresie barw. Klasa ColorSpace.Named wylicza częściową listę często używanych przestrzeni kolorów obsługiwanych przez Androida.

Uwaga: gdy włączony jest tryb szerokiej gamy kolorów, okno aktywności zużywa więcej pamięci i przetwarzania przez procesor graficzny na potrzeby komponowania obrazu. Zanim włączysz tryb szerokiej gamy kolorów, dokładnie zastanów się, czy to naprawdę przyniesie korzyści. Na przykład funkcja wyświetlania zdjęć na pełnym ekranie jest dobrym rozwiązaniem do stosowania w trybie szerokiej gamy kolorów, aktywność, która pokazuje małe miniatury, już nie.

Włącz tryb szerokiej gamy kolorów

Użyj atrybutu colorMode, aby poprosić o wyświetlenie aktywności w trybie szerokiej gamy kolorów na zgodnych urządzeniach. W trybie szerokiej palety kolorów okno może renderować poza zakresem sRGB, aby wyświetlać bardziej żywe kolory. Jeśli urządzenie nie obsługuje renderowania w szerokiej gamie kolorów, ten atrybut nie ma żadnego wpływu. Jeśli aplikacja musi określić, czy dany wyświetlacz obsługuje szeroką gamę kolorów, wywołaj metodę isWideColorGamut(). Aplikacja może też wywołać funkcję isScreenWideColorGamut(), która zwraca true tylko wtedy, gdy wyświetlacz obsługuje szeroką gamę kolorów, a urządzenie obsługuje renderowanie kolorów w szerokim zakresie kolorów.

Wyświetlacz może obsługiwać szeroką gamę kolorów, ale nie może zarządzać kolorami. W takim przypadku system nie przyzna aplikacji trybu szerokiej gamy kolorów. Gdy wyświetlacz nie jest zarządzany kolorem – tak jak we wszystkich wersjach Androida przed 8.0 – system ponownie mapuje kolorów wybranych przez aplikację do palety wyświetlacza.

Aby włączyć w swojej aktywności szeroką gamę kolorów, ustaw colorMode wideColorGamut w pliku AndroidManifest.xml. Ty Zrobisz to w przypadku każdej aktywności, dla której chcesz włączyć tryb szerokiego kolorów.

android:colorMode="wideColorGamut"

Tryb kolorów możesz też ustawić programowo w aktywności, wywołując metodę setColorMode(int) i przekazując parametr COLOR_MODE_WIDE_COLOR_GAMUT.

Renderuj zawartość o szerokim zakresie kolorów

Rysunek 1. Przestrzenie barw Display P3 (pomarańczowy) a sRGB (biały)

Aby renderować treści o szerokiej gamie kolorów, aplikacja musi wczytać dużą mapę bitową kolorów, czyli profil kolorów zawierający przestrzeń kolorów szerszą niż sRGB. Typowe szerokie profile kolorów to Adobe RGB, DCI-P3 i Display P3.

Aplikacja może wysyłać zapytania o przestrzeń kolorów bitmapy, wywołując getColorSpace() Aby określić, czy system rozpoznaje konkretnej przestrzeni kolorów, możesz nazywać Metoda isWideGamut().

Klasa Color umożliwia reprezentowanie koloru za pomocą 4 komponentów zapakowanych w wartość 64-bitową zamiast najczęstszej reprezentacji, która używa wartości całkowitej. Używając długich wartości, możesz definiować kolory za pomocą większą precyzję niż wartości całkowite. Jeśli chcesz utworzyć lub zakodować kolor jako długą wartość, użyj funkcji jedną z metod pack() w klasie Color.

Aby sprawdzić, czy aplikacja prawidłowo poprosiła o tryb szerokiej gamy kolorów, sprawdź, czy metoda getColorMode() zwraca COLOR_MODE_WIDE_COLOR_GAMUT (nie wskazuje ona jednak, czy tryb szerokiej gamy kolorów został faktycznie przyznany).

Używanie obsługi szerokiej gamy kolorów w kodzie natywnym

W tej sekcji opisujemy, jak włączyć tryb szerokiej gamy kolorów za pomocą funkcji OpenGL i interfejsów API Vulkan, jeśli aplikacja używa kodu natywnego.

OpenGL

Aby używać trybu szerokiej gamy kolorów w OpenGL, aplikacja musi zawierać bibliotekę EGL 1.4 z jednym z tych rozszerzeń:

Aby włączyć tę funkcję, musisz najpierw utworzyć kontekst GL za pomocą eglChooseConfig, używając jednego z 3 obsługiwanych formatów bufora kolorów do obsługi szerokiej palety kolorów w atrybutach. Format bufora kolorów dla szerokiego kolor musi być jednym z następujących zbiorów wartości RGBA:

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

Następnie podczas tworzenia przestrzeni kolorów P3 poproś o rozszerzenie przestrzeni kolorów zgodnie z tym fragmentem kodu:

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

Obsługę interfejsu Vulkan w przypadku szerokiej gamy kolorów zapewnia VK_EXT_swapchain_colorspace.

Zanim włączysz obsługę szerokiej gamy kolorów w kodzie Vulkan, sprawdź, czy rozszerzenie jest obsługiwane przez vkEnumerateInstanceExtensionProperties. Jeśli rozszerzenie jest dostępne, musisz je włączyć w okresie vkCreateInstance, zanim utworzysz obrazy swapchain, które używają dodatkowych przestrzeni barw zdefiniowanych przez rozszerzenie.

Przed utworzeniem łańcucha kolorów musisz wybrać odpowiednią przestrzeń kolorów, a potem wykonać pętlę na dostępnych powierzchniach na urządzeniach fizycznych i wybrać odpowiedni format kolorów. przestrzeni kolorów.

Na urządzeniach z Androidem Vulkan obsługuje szeroką gamę kolorów z tymi przestrzeniami kolorów i formatami kolorów:VkSurfaceFormatKHR

  • Przestrzenie kolorów w szerokej gamie kolorów Vulkan:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Formaty kolorów Vulkan z obsługą szerokiej gamy kolorów:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

Ten fragment kodu pokazuje, jak sprawdzić, czy urządzenie obsługuje format Display P3. przestrzeń kolorów:

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
}

Fragment kodu poniżej pokazuje, jak poprosić o zamianę interfejsu Vulkan na 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;
}