שיפור הגרפיקה באמצעות תוכן בצבע רחב

ב-Android 8.0 (רמת API‏ 26) נוספה תמיכה בניהול צבעים במרחבי צבעים נוספים, בנוסף ל-RGB (sRGB) הסטנדרטי, לצורך עיבוד גרפיקה במכשירים עם מסכים תואמים. בעזרת התמיכה הזו, האפליקציה יכולה ליצור עיבוד (רנדור) של בימפטים עם פרופילי צבע רחבים מוטמעים, שנטענים מקובצי PNG,‏ JPEG ו-WebP באמצעות Java או קוד מקורי. אפליקציות שמשתמשות ב-OpenGL או ב-Vulkan יכולות להפיק תוכן עם מגוון רחב של צבעים ישירות (באמצעות Display P3 ו-scRGB). היכולת הזו שימושי ליצירת אפליקציות שכוללות שחזור של צבעים באיכות גבוהה, כמו תמונות וסרטונים עריכת אפליקציות.

הסבר על מצב של טווח צבעים רחב

פרופילים של צבעים רחבים הם פרופילים של ICC, כמו Adobe RGB, Pro Photos RGB, וגם DCI-P3, שיכול לייצג מגוון רחב יותר של צבעים מאשר sRGB. מסכים שתומכים בפרופילים של צבעים רחבים ניתן להציג תמונות בעלות צבעי יסוד עמוקים יותר (אדום, ירוק וכחול) וכן תמונות משניות עשירות יותר צבעים (כגון מג'נטה, ציאן וצהוב).

במכשירי Android עם Android 8.0 ואילך (רמת API‏ 26 ואילך) שתומכים בכך, האפליקציה יכולה להפעיל את מצב הצבע של מגוון רחב של צבעים לפעילות מסוימת, שבה המערכת מזהה תמונות בפורמט בייטמאפ עם פרופילים מוטמעים של מגוון רחב של צבעים ומעבדת אותן בצורה נכונה. כיתה ColorSpace.Named מסווגת רשימה חלקית של כלים נפוצים מרחבי צבעים שנתמכים ב-Android.

הערה: כשמצב 'סולם צבעים רחב' מופעל, החלון משתמש ביותר זיכרון ובעיבוד GPU להרכבת המסך. לפני שמפעילים את המצב של מגוון רחב של צבעים, כדאי לבדוק היטב אם הוא באמת משפר את הפעילות. לדוגמה, פעילות שמציגה תמונות במסך מלא מתאימה למצב של סולם צבעים רחב, פעילות שמציגה תמונות ממוזערות קטנות היא לא פעילות.

הפעלת מצב של מגוון רחב של צבעים

אפשר להשתמש במאפיין colorMode כדי לבקש שהפעילות תוצג במצב של מגוון רחב של צבעים במכשירים תואמים. במצב של מרחב צבעים רחב, חלון יכול לבצע רינדור מחוץ למרחב הצבעים sRGB כדי להציג צבעים מלאי חיים יותר. אם המכשיר לא תומך בצבע רחב עיבוד סולם, למאפיין הזה אין השפעה. אם האפליקציה שלך צריכה להחליט אם לתצוגה יש סולם צבעים רחב, אמצעי תשלום אחד (isWideColorGamut()). האפליקציה יכולה גם להתקשר isScreenWideColorGamut(), שמחזיר true רק אם המסך תומך בסולם צבעים רחב והמכשיר תומך במערך צבעים רחב. ורינדור.

יכול להיות שמסך מסוים תומך בטווח רחב של צבעים אבל לא כולל ניהול צבעים. במקרה כזה, המערכת לא תעניק לאפליקציה את מצב טווח רחב של צבעים. כשהתצוגה לא מנוהלת לפי צבעים —כפי שהיה בכל הגרסאות של Android לפני 8.0 — המערכת תמפה מחדש את צבעים שהאפליקציה יצרה לסולם התצוגה.

כדי להפעיל את מגוון הצבעים הרחב בפעילות, מגדירים את המאפיין colorMode לערך wideColorGamut בקובץ AndroidManifest.xml. צריך לעשות זאת לכל פעילות שרוצים להפעיל בה את מצב צבע רחב.

android:colorMode="wideColorGamut"

אפשר גם להגדיר את מצב הצבע באופן פרוגרמטי בפעילות על ידי קריאה לשיטה setColorMode(int) והעברת הערך COLOR_MODE_WIDE_COLOR_GAMUT.

עיבוד תוכן של סולם צבעים רחב

איור 1. מרחבי צבעים של תצוגה P3 (כתום) לעומת sRGB (לבן)

כדי להציג תוכן עם מגוון רחב של צבעים, האפליקציה צריכה לטעון קובץ בייטמאפ עם מגוון רחב של צבעים, כלומר קובץ בייטמאפ עם פרופיל צבע שמכיל מרחב צבעים רחב יותר מ-sRGB. פרופילים נפוצים של מרחב צבעים רחב כוללים את Adobe RGB,‏ DCI-P3 ו-Display P3.

האפליקציה יכולה לשלוח שאילתה על מרחב הצבעים של מפת סיביות באמצעות קריאה getColorSpace() כדי לקבוע אם המערכת מזהה מרחב צבעים ספציפי כמרחב צבעים רחב, אפשר להפעיל את השיטה isWideGamut().

המחלקה Color מאפשרת לייצג צבע עם ארבעה רכיבים דחוסים בערך ארוך של 64 ביט, במקום הייצוג הנפוץ ביותר שמשתמש במספר שלם עם ערך מסוים. בעזרת ערכים ארוכים אפשר להגדיר צבעים בצורה מדויקת יותר מאשר בעזרת ערכים שלמים. אם צריך ליצור או לקודד צבע כערך long, משתמשים באחת מהשיטות pack() בכיתה Color.

כדי לוודא שהאפליקציה שלכם ביקשה כראוי את מצב הצבעים הרחב, עליכם לבדוק שה-method‏ getColorMode() מחזיר את הערך COLOR_MODE_WIDE_COLOR_GAMUT (עם זאת, ה-method הזה לא מציין אם מצב הצבעים הרחב אושר בפועל).

שימוש בערכת צבעים רחבה בקוד מקורי

בקטע הזה נסביר איך להפעיל מצב של סולם צבעים רחב באמצעות OpenGL ממשקי API של Vulkan אם האפליקציה שלכם משתמשת בקוד נייטיב.

OpenGL

כדי להשתמש במצב מטווח צבעים רחב ב-OpenGL, צריך לכלול באפליקציה את ספריית EGL 1.4 עם אחד מהתוספים הבאים:

כדי להפעיל את התכונה, קודם צריך ליצור הקשר GL באמצעות eglChooseConfig, עם אחד משלושת הפורמטים הנתמכים של מאגרי צבע לצבע רחב במאפיינים. פורמט מאגר הצבע לצבע רחב חייב להיות אחת מהקבוצות הבאות של ערכי RGBA:

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

לאחר מכן, מבקשים את תוסף מרחב הצבעים P3 כשיוצרים את לעבד יעדים, כפי שמוצג בקטע הקוד הבא:

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

התמיכה של Vulkan במערך צבעים רחב מסופקת באמצעות התוסף VK_EXT_swapchain_colorspace.

לפני שמפעילים תמיכה בצבעים רחבים בקוד Vulkan, צריך קודם לבדוק אם התוסף נתמך באמצעות vkEnumerateInstanceExtensionProperties. אם התוסף זמין, צריך להפעיל אותו במהלך vkCreateInstance לפני שיוצרים תמונות החלפה שמותאמות להשתמש במרחבי הצבע הנוספים שהתוסף מגדיר.

לפני יצירת ההחלפה, עליך לבחור את מרחב הצבעים הרצוי, ואז לעבור על את הפלטפורמות הזמינות של מכשירים פיזיים ולבחור בשבילם פורמט צבע תקין מרחב צבעים.

במכשירי Android, ב-Vulkan יש תמיכה במערך צבעים רחב עם מרחבי הצבעים הבאים וגם VkSurfaceFormatKHR פורמטים של צבעים:

  • מרחבי צבעים רחבים של Vulkan עם סולם צבעים:
    • VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT
    • VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT
  • פורמטים של צבע ב-Vulkan עם תמיכה בטווח רחב של צבעים:
    • VK_FORMAT_R16G16B16A16_SFLOAT
    • VK_FORMAT_A2R10G10B10_UNORM_PACK32
    • VK_FORMAT_R8G8B8A8_UNORM

קטע הקוד הבא מראה איך אפשר לבדוק אם המכשיר תומך בטכנולוגיית Display P3 מרחב צבעים:

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
}

קטע הקוד הבא מראה איך לבקש החלפה של Vulkan עם 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;
}