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 sắc cho không gian màu bên cạnh RGB (sRGB) tiêu chuẩn để 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 với cấu hình màu rộng được nhúng được tải từ 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 tạo ra nội dung gam màu rộng (sử dụng Display P3scRGB). Chức năng này hữu ích khi tạo các ứng dụng liên quan đến việc tái tạo màu có độ chân thực cao, chẳng hạn như hình ảnh và video chỉnh sửa ứng dụng.

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

Hồ sơ màu rộng Hồ sơ ICC, chẳng hạn như Adobe RGB, Pro Photo RGBDCI-P3, tức là có khả năng thể hiện dải màu rộng hơn so với sRGB. Màn hình hỗ trợ cấu hình màu rộng có thể hiển thị hình ảnh có các màu cơ bản sâu sắc hơn (đỏ, xanh lục và xanh lam) cũng như các hình ảnh phụ phong phú hơn (chẳng hạn như đỏ tươi, xanh lơ và vàng).

Trên thiết bị Android chạy Android 8.0 (API cấp 26) trở lên có hỗ trợ phiên bản 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, trong đó hệ thống nhận dạng và xử lý chính xác hình ảnh bitmap với cấu hình màu rộng được nhúng. Chiến lược phát hành đĩa đơn Lớp ColorSpace.Named liệt kê một phần danh sách các lớp thường dùng hệ màu mà Android hỗ trợ.

Lưu ý: Khi bạn bật chế độ gam màu rộng, giá trị cửa sổ sử dụng nhiều bộ nhớ và xử lý GPU hơn cho cấu trúc màn hình. Trước khi bật 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ừ nó hay không. Ví dụ: một hiển thị hình ảnh ở chế độ toàn màn hình là một lựa chọn phù hợp cho chế độ gam màu rộng, nhưng thì hoạt động có hiển thị hình thu nhỏ nhỏ thì không.

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

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ợ dải màu rộng hiển thị gam màu, thuộc tính này 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 có gam màu rộng, hãy gọi phương thức isWideColorGamut(). Ứng dụng của bạn cũng có thể gọi isScreenWideColorGamut(), trả về true chỉ khi màn hình có gam màu rộng và thiết bị hỗ trợ gam màu rộng kết xuất màu.

Một màn hình có thể có 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ý bằng màu sắc —như trường hợp của tất cả các phiên bản Android trước 8.0 — hệ thống đã ánh xạ lại các màu do ứng dụng vẽ theo gam màu của màn hình.

Để bật gam màu rộng trong hoạt động, hãy đặt giá trị colorMode cho 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 cách lập trình trong hoạt động của mình 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ị P3 (cam) so với sRGB (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ó thể truy vấn hệ 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 hệ màu cụ thể là gam rộng, bạn có thể gọi hàm isWideGamut().

Lớp Color cho phép bạn biểu thị màu bằng 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 số nguyên giá trị. Khi sử dụng các giá trị dài, bạn có thể xác định màu bằng chính xác hơn giá trị số nguyên. Nếu bạn cần tạo hoặc mã hoá một màu dưới dạng một 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 phương thức getColorMode() trả về COLOR_MODE_WIDE_COLOR_GAMUT (phương pháp này không biểu thị, tuy nhiên, 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 OpenGL và Các API Vulkan nếu ứng dụng của bạn 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 chứa thư viện EGL 1.4 có 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 bối cảnh GL qua eglChooseConfig, với 1 trong 3 tính năng được hỗ trợ định dạng vùng đệm màu cho màu rộng trong các thuộc tính. Định dạng vùng đệm màu cho quảng cáo rộng màu phải là một trong các nhóm giá trị RGBA sau:

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

Sau đó, yêu cầu tiện ích hệ màu P3 khi tạo đích hiển thị, như thể hiện 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 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 xem tiện ích được hỗ trợ qua vkEnumerateInstanceExtensionProperties. Nếu tiện ích này có sẵn, bạn phải bật tiện ích đó trong thời gian 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 mình muốn, sau đó lặp lại bề mặt thiết bị thực có sẵn và chọn định dạng màu hợp lệ cho bề mặt đó 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:

  • Hệ 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 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 biết cách kiểm tra để đảm bảo rằng thiết bị có hỗ trợ Display P3 hệ màu:

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 một 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;
}