Menyempurnakan grafis dengan konten warna lebar

Android 8.0 (API level 26) memperkenalkan dukungan pengelolaan warna untuk ruang warna selain RGB standar (sRGB) untuk merender grafis pada perangkat dengan tampilan yang kompatibel. Dengan dukungan ini, aplikasi Anda dapat merender bitmap dengan profil warna lebar tersemat yang dimuat dari file PNG, JPEG, dan WebP melalui Java atau kode native. Aplikasi yang menggunakan OpenGL atau Vulkan dapat langsung menghasilkan konten wide color gamut (menggunakan Display P3 dan scRGB). Kemampuan ini berguna untuk membuat aplikasi yang melibatkan reproduksi warna {i>high fidelity<i}, seperti gambar dan video aplikasi pengeditan.

Memahami mode wide color gamut

Profil warna yang lebar adalah Profil ICC, seperti Adobe RGB, Pro Photo RGB, dan DCI-P3, yang merupakan yang mampu merepresentasikan rentang warna yang lebih luas daripada sRGB. Layar yang mendukung profil warna lebar dapat menampilkan gambar dengan warna primer yang lebih dalam (merah, hijau, dan biru) serta warna sekunder yang lebih kaya warna (seperti magenta, sian, dan kuning).

Pada perangkat Android yang menjalankan Android 8.0 (API level 26) atau lebih tinggi yang mendukungnya, aplikasi Anda dapat mengaktifkan mode warna gamut warna yang luas untuk aktivitas yang dapat dikenali dan dapat memproses gambar bitmap dengan benar menggunakan profil warna lebar yang disematkan. Tujuan Class ColorSpace.Named mengenumerasi sebagian daftar yang umum digunakan ruang warna yang didukung Android.

Catatan: Jika mode wide color gamut diaktifkan, menggunakan lebih banyak memori dan pemrosesan GPU untuk komposisi layar. Sebelum mengaktifkan wide color gamut Anda harus mempertimbangkan dengan cermat apakah aktivitas tersebut benar-benar memperoleh manfaat. Sebagai contoh, aktivitas yang menampilkan foto dalam layar penuh adalah kandidat yang baik untuk mode {i>wide color gamut<i}, tetapi aktivitas yang menunjukkan {i>thumbnail<i} kecil tidaklah demikian.

Mengaktifkan mode wide color gamut

Gunakan atribut colorMode untuk meminta aktivitas ditampilkan dalam mode gamut warna yang luas pada perangkat yang kompatibel. Dalam mode gamut warna yang luas, jendela bisa merender di luar gamut sRGB untuk menampilkan warna yang lebih cerah. Jika perangkat tidak mendukung warna lebar rendering gamut, atribut ini tidak akan memiliki pengaruh. Jika aplikasi Anda perlu menentukan apakah suatu layarnya mampu menampilkan {i>wide color gamut<i}, panggil Metode isWideColorGamut(). Aplikasi Anda juga dapat memanggil isScreenWideColorGamut(), yang menampilkan true hanya jika layar mampu menampilkan wide color gamut dan perangkat mendukung wide color gamut rendering warna.

Layar mungkin mampu menampilkan {i>wide color gamut<i} tetapi tidak {i>color-managed<i}, dalam hal ini, sistem tidak akan memberi aplikasi mode wide color gamut. Saat layar tidak dikelola warna —seperti yang terjadi untuk semua versi Android sebelum 8.0—sistem akan memetakan ulang warna yang digambar oleh aplikasi ke gamut tampilan.

Untuk mengaktifkan wide color gamut di aktivitas Anda, setel colorMode ke wideColorGamut dalam file AndroidManifest.xml Anda. Anda Anda perlu melakukan ini untuk setiap aktivitas yang mode warna lebarnya ingin Anda aktifkan.

android:colorMode="wideColorGamut"

Anda juga bisa menyetel mode warna secara terprogram dalam aktivitas dengan memanggil metode Metode setColorMode(int) dan meneruskan COLOR_MODE_WIDE_COLOR_GAMUT.

Merender konten wide color gamut

Gambar 1. Ruang warna tampilan P3 (oranye) vs. sRGB (putih)

Untuk merender konten wide color gamut, aplikasi Anda harus memuat bitmap warna lebar, yaitu bitmap dengan profil warna yang berisi ruang warna yang lebih lebar dari sRGB. Profil warna {i>wide<i} yang umum mencakup Adobe RGB, DCI-P3 dan Display P3.

Aplikasi Anda bisa melakukan kueri ruang warna bitmap, dengan memanggil getColorSpace(). Untuk menentukan apakah sistem mengenali ruang warna tertentu menjadi gamut lebar (wide gamut), Anda bisa memanggil Metode isWideGamut().

Class Color memungkinkan Anda merepresentasikan warna dengan empat komponen dikemas ke dalam nilai panjang 64-bit, bukan representasi paling umum yang menggunakan bilangan bulat dengan sejumlah nilai. Menggunakan nilai {i>long<i}, Anda bisa menentukan warna dengan lebih presisi daripada nilai bilangan bulat. Jika Anda perlu membuat atau mengenkode warna sebagai nilai yang panjang, gunakan salah satu metode pack() di class Color.

Anda dapat memverifikasi apakah aplikasi telah meminta mode wide color gamut dengan benar metode getColorMode() akan menampilkan COLOR_MODE_WIDE_COLOR_GAMUT (metode ini tidak menunjukkan, tetapi, apakah mode wide color gamut benar-benar diberikan).

Menggunakan dukungan wide color gamut dalam kode native

Bagian ini menjelaskan cara mengaktifkan mode wide color gamut dengan OpenGL dan Vulkan API jika aplikasi Anda menggunakan kode native.

OpenGL

Untuk menggunakan mode gamut warna yang luas di OpenGL, aplikasi Anda harus menyertakan pustaka EGL 1.4 dengan salah satu ekstensi berikut:

Untuk mengaktifkan fitur ini, Anda harus terlebih dahulu membuat konteks GL melalui eglChooseConfig, dengan salah satu dari ketiganya didukung format buffer warna untuk warna lebar dalam atribut. Format buffer warna untuk warna harus berupa salah satu rangkaian nilai RGBA ini:

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

Lalu, minta ekstensi ruang warna P3 saat membuat target render, seperti yang ditampilkan dalam cuplikan kode berikut:

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

Dukungan Vulkan untuk wide color gamut disediakan melalui VK_EXT_swapchain_colorspace.

Sebelum mengaktifkan dukungan warna lebar pada kode Vulkan, periksa terlebih dahulu apakah ekstensi didukung melalui vkEnumerateInstanceExtensionProperties. Jika ekstensi tersedia, Anda harus mengaktifkannya selama vkCreateInstance sebelum membuat gambar swapchain apa pun yang menggunakan ruang warna tambahan yang ditentukan oleh ekstensi.

Sebelum membuat swapchain, Anda harus memilih ruang warna yang diinginkan, lalu loop melalui perangkat fisik yang tersedia dan pilih format warna yang valid untuk itu ruang warna.

Pada perangkat Android, Vulkan mendukung wide color gamut dengan ruang warna dan Format warna VkSurfaceFormatKHR:

  • Ruang warna wide color gamut Vulkan:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Format warna Vulkan dengan dukungan wide color gamut:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

Cuplikan kode berikut menunjukkan cara memeriksa apakah perangkat mendukung Display P3 {i>color space<i} (ruang warna):

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
}

Cuplikan kode berikut menunjukkan cara meminta swapchain Vulkan dengan 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;
}