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 temu wsparciu aplikacja może renderować mapy bitowe z osadzonymi szerokimi profilami kolorów wczytywanymi z plików PNG, JPEG i WebP za pomocą Javy lub kodu 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 przydatne przy tworzeniu aplikacji wymagających wysokiej jakości reprodukcji kolorów, takich jak obrazy i filmy do edytowania aplikacji.

Omówienie trybu szerokiej gamy kolorów

Szerokie profile kolorów są profili ICC, takich jak Adobe RGB, Zdjęcie Pro RGB oraz DCI-P3, ale zdolny do reprezentowania szerszego zakresu kolorów niż sRGB. Ekrany obsługujące szerokie profile kolorów może wyświetlać obrazy o głębszych barwach podstawowych (czerwonych, zielonych i niebieskich) oraz bogatszych barwach drugorzędnych kolorów (takich jak purpurowe, błękitne i żółte).

Na urządzeniach z Androidem 8.0 (poziom interfejsu API 26) lub nowszym, które go obsługują, aplikacja może: włączyć tryb kolorów szerokiej gamy kolorów dla aktywności, w której system rozpoznaje i poprawnie przetwarza obrazy bitmapy z osadzonymi szerokimi profilami kolorów. 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, używa więcej pamięci i procesora GPU do tworzenia kompozycji ekranu. Zanim włączysz szerszą gamę kolorów należy dokładnie zastanowić się, czy dana aktywność przynosi 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 gamy kolorów okno może renderować się poza zakresem sRGB, aby wyświetlić bardziej żywe kolory. Jeśli urządzenie nie obsługuje szerokich kolorów gamut, ten atrybut nie ma żadnego efektu. Jeśli aplikacja musi określić, czy obsługuje szeroki zakres kolorów, wywołaj funkcję Metoda isWideColorGamut(). Możesz też zadzwonić do isScreenWideColorGamut(), która zwraca true tylko wtedy, gdy wyświetlacz obsługuje szeroki zakres kolorów, a urządzenie obsługuje szerszą gamę kolorów. renderowanie kolorów.

Ekran może obsługiwać szeroką gamę kolorów, ale nie może zarządzać kolorami. W takim przypadku funkcja system nie przyzna aplikacji trybu szerokiej gamy kolorów. Gdy wyświetlacz nie jest zarządzany kolorem – tak jak we wszystkich wersjach Androida sprzed 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ż automatycznie ustawić w aktywności, używając Metoda setColorMode(int) i przekazywanie COLOR_MODE_WIDE_COLOR_GAMUT

Renderuj zawartość o szerokim zakresie kolorów

Rysunek 1. Przestrzenie kolorów P3 (pomarańczowe) i sRGB (białe)

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 określenie koloru za pomocą 4 komponentów. zapisany do 64-bitowej wartości, zamiast reprezentacji najczęściej używanej liczby 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 się dowiedzieć, czy aplikacja poprawnie zażądała trybu szerokiej gamy kolorów, sprawdź metoda getColorMode() zwraca COLOR_MODE_WIDE_COLOR_GAMUT (ta metoda nie wskazuje, czy tryb szerokiej gamy kolorów został rzeczywiście 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 można było używać trybu szerokiej gamy kolorów w trybie OpenGL, aplikacja musi zawierać bibliotekę EGL 1.4 z jedno z tych rozszerzeń:

Aby włączyć tę funkcję, musisz najpierw utworzyć kontekst GL za pomocą eglChooseConfig, gdzie jedna z 3 obsługiwanych: formatów bufora kolorów, aby uzyskać szeroki kolor. 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());

Wulkan

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

Zanim włączysz obsługę szerokich kolorów w kodzie Vulkan, najpierw sprawdź, czy rozszerzenie jest obsługiwane przez vkEnumerateInstanceExtensionProperties Jeśli rozszerzenie jest dostępne, musisz je włączyć w czasie, gdy vkCreateInstance. należy użyć dodatkowych przestrzeni kolorów 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 następującymi przestrzeniami kolorów i Formaty 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;
}