Cải thiện đồ hoạ với nội dung màu rộng

Android 8.0 (API cấp 26) đã ra mắt tính năng hỗ trợ quản lý màu cho không gian màu bổ sung bên cạnh RGB tiêu chuẩn (sRGB) để kết xuất đồ hoạ trên các thiết bị có màn hình tương thích. Với sự hỗ trợ này, ứng dụng của bạn có thể kết xuất bitmap có cấu hình màu rộng được nhúng được tải từ các tệp PNG, JPEG và WebP thông qua Java hoặc mã gốc. Các ứng dụng sử dụng OpenGL hoặc Vulkan có thể trực tiếp xuất nội dung gam màu rộng (sử dụng Display P3scRGB). Chức năng này rất hữu ích khi tạo các ứng dụng cần tái tạo màu sắc có độ chân thực cao, chẳng hạn như các ứng dụng chỉnh sửa hình ảnh và video.

Tìm hiểu về chế độ gam màu rộng

Cấu hình màu rộng là cấu hình ICC, chẳng hạn như Adobe RGB, Pro Photo RGBDCI-P3, có thể trình bày dải màu rộng hơn sRGB. Các màn hình hỗ trợ cấu hình màu rộng có thể hiển thị hình ảnh có màu cơ bản sâu hơn (đỏ, xanh lục và xanh dương) cũng như các màu phụ giàu màu sắc hơn (chẳng hạn như đỏ tươi, cyan và vàng).

Trên các thiết bị Android chạy Android 8.0 (API cấp 26) trở lên có hỗ trợ công nghệ này, ứng dụng của bạn có thể bật chế độ màu gam màu rộng cho một hoạt động nhờ đó hệ thống nhận dạng và xử lý chính xác hình ảnh bitmap có cấu hình màu rộng được nhúng. Lớp ColorSpace.Named liệt kê một phần danh sách các không gian màu thường dùng mà Android hỗ trợ.

Lưu ý: Khi bật chế độ gam màu rộng, cửa sổ của hoạt động sẽ sử dụng nhiều bộ nhớ và quá trình xử lý GPU hơn cho thành phần màn hình. Trước khi bật chế độ gam màu rộng, bạn nên cân nhắc cẩn thận xem hoạt động có thực sự hưởng lợi từ chế độ này hay không. Ví dụ: một hoạt động hiển thị ảnh ở chế độ toàn màn hình sẽ phù hợp với chế độ gam màu rộng, nhưng một hoạt động hiển thị hình thu nhỏ nhỏ thì không.

Bật chế độ gam màu rộng

Sử dụng thuộc tính colorMode để yêu cầu hiển thị hoạt động ở chế độ gam màu rộng trên các thiết bị tương thích. Ở chế độ gam màu rộng, một cửa sổ có thể hiển thị bên ngoài gam màu sRGB để hiển thị các màu rực rỡ hơn. Nếu thiết bị không hỗ trợ tính năng hiển thị gam màu rộng, thì thuộc tính này sẽ không có hiệu ứng. Nếu ứng dụng của bạn cần xác định xem một màn hình nhất định có khả năng gam màu rộng hay không, hãy gọi phương thức isWideColorGamut(). Ứng dụng của bạn cũng có thể gọi isScreenWideColorGamut() để chỉ trả về true nếu màn hình có khả năng gam màu rộng và thiết bị hỗ trợ tính năng hiển thị gam màu rộng.

Một màn hình có thể có khả năng gam màu rộng nhưng không được quản lý màu. Trong trường hợp đó, hệ thống sẽ không cấp cho ứng dụng chế độ gam màu rộng. Khi màn hình không được quản lý màu (như trường hợp của mọi phiên bản Android trước 8.0), hệ thống sẽ ánh xạ lại các màu do ứng dụng vẽ với gam màu của màn hình.

Để bật gam màu rộng trong hoạt động, hãy đặt thuộc tính colorMode thành wideColorGamut trong tệp AndroidManifest.xml. Bạn cần thực hiện việc này cho từng hoạt động mà bạn muốn bật chế độ màu rộng.

android:colorMode="wideColorGamut"

Bạn cũng có thể đặt chế độ màu theo phương thức lập trình trong hoạt động bằng cách gọi phương thức setColorMode(int) và truyền vào COLOR_MODE_WIDE_COLOR_GAMUT.

Hiển thị nội dung gam màu rộng

Hình 1. Hiển thị hệ màu P3 (màu cam) so với hệ màu sRGB (màu trắng)

Để hiển thị nội dung gam màu rộng, ứng dụng của bạn phải tải một bitmap màu rộng, đó là một bitmap có cấu hình màu chứa hệ màu rộng hơn sRGB. Các cấu hình màu rộng phổ biến bao gồm Adobe RGB, DCI-P3 và Display P3.

Ứng dụng của bạn có thể truy vấn không gian màu của bitmap bằng cách gọi getColorSpace(). Để xác định xem hệ thống có nhận ra một không gian màu cụ thể là gam màu rộng hay không, bạn có thể gọi phương thức isWideGamut().

Lớp Color cho phép bạn biểu thị một màu có 4 thành phần được đóng gói thành một giá trị dài 64 bit, thay vì cách biểu diễn phổ biến nhất sử dụng giá trị số nguyên. Khi sử dụng giá trị dài, bạn có thể xác định màu có độ chính xác cao hơn giá trị số nguyên. Nếu bạn cần tạo hoặc mã hoá màu dưới dạng giá trị dài, hãy sử dụng một trong các phương thức pack() trong lớp Color.

Bạn có thể xác minh xem ứng dụng của mình có yêu cầu chế độ gam màu rộng đúng cách hay không bằng cách kiểm tra để đảm bảo rằng phương thức getColorMode() trả về COLOR_MODE_WIDE_COLOR_GAMUT (tuy nhiên, phương thức này không cho biết liệu chế độ gam màu rộng có thực sự được cấp hay không).

Sử dụng tính năng hỗ trợ gam màu rộng trong mã gốc

Phần này mô tả cách bật chế độ gam màu rộng bằng các API OpenGLVulkan nếu ứng dụng của bạn sử dụng mã gốc.

OpenGL

Để sử dụng chế độ gam màu rộng trong OpenGL, ứng dụng của bạn phải bao gồm thư viện EGL 1.4 với một trong các tiện ích sau:

Để bật tính năng này, trước tiên bạn phải tạo ngữ cảnh GL qua eglChooseConfig, với một trong ba định dạng vùng đệm màu được hỗ trợ cho màu rộng trong các thuộc tính. Định dạng vùng đệm màu cho màu rộng phải là một trong các tập hợp giá trị RGBA sau:

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

Sau đó, hãy yêu cầu tiện ích không gian màu P3 khi tạo mục tiêu kết xuất, như minh hoạ trong đoạn mã sau:

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

Tính năng hỗ trợ Vulkan cho gam màu rộng được cung cấp thông qua tiện ích VK_EXT_swapchain_colorspace.

Trước khi bật tính năng hỗ trợ màu rộng trong mã Vulkan, trước tiên, hãy kiểm tra để đảm bảo tiện ích này được hỗ trợ thông qua vkEnumerateInstanceExtensionProperties. Nếu tiện ích này có sẵn, bạn phải bật tiện ích đó trong vkCreateInstance trước khi tạo bất kỳ hình ảnh chuỗi hoán đổi nào sử dụng hệ màu bổ sung do tiện ích xác định.

Trước khi tạo chuỗi hoán đổi, bạn cần chọn hệ màu mong muốn, sau đó lặp qua các bề mặt thiết bị thực hiện có và chọn một định dạng màu hợp lệ cho hệ màu đó.

Trên thiết bị Android, Vulkan hỗ trợ gam màu rộng với các hệ màu và định dạng màu VkSurfaceFormatKHR sau đây:

  • Không gian màu gam màu rộng của Vulkan:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • Các định dạng màu Vulkan có hỗ trợ gam màu rộng:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

Đoạn mã sau đây cho bạn biết cách kiểm tra xem thiết bị có hỗ trợ hệ màu Display P3 hay không:

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
}

Đoạn mã sau đây cho biết cách yêu cầu chuỗi hoán đổi Vulkan bằng 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;
}