Commit bc1ae6d6 authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

color: Detect Android wide color gamut displays

There are two variables that determine if Chrome should render P3
content:
  * Configuration.isScreenWideColorGamut reports whether or not the
    system compositor will perform color correction for EGL surfaces
    that are tagged with a color space.
  * Display.isWideColorGamut reports whether or not this particular
    display has a wide color gamut.

The Configuration.isScreenWideColorGamut variable must be read from the
WindowAndroid's context.

Update both of these variables, and plumb their conjunction down into
the native code, where it is used to select P3 or sRGB.

This mechanism is only used by Chrome, not WebView. For WebView, add hooks
in WebContentsViewDelegate to override the color space that is passed to
the renderer (instead of just using the one from the display::Display).

R=boliu
TBR=mthiesse

Bug: 735658
Change-Id: I7781b4a1068a57a480123a58302fe484273565fd
Reviewed-on: https://chromium-review.googlesource.com/571053
Commit-Queue: ccameron chromium <ccameron@chromium.org>
Reviewed-by: default avatarBo Liu <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#487391}
parent 94cba8e2
......@@ -6,6 +6,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/context_menu_params.h"
#include "ui/gfx/color_space.h"
namespace android_webview {
......@@ -30,4 +31,13 @@ content::WebDragDestDelegate* AwWebContentsViewDelegate::GetDragDestDelegate() {
return NULL;
}
void AwWebContentsViewDelegate::OverrideDisplayColorSpace(
gfx::ColorSpace* color_space) {
// TODO(ccameron): WebViews that are embedded in WCG windows will want to
// override the display color space to gfx::ColorSpace::CreateExtendedSRGB().
// This situation is not yet detected.
// https://crbug.com/735658
*color_space = gfx::ColorSpace::CreateSRGB();
}
} // namespace android_webview
......@@ -24,6 +24,7 @@ class AwWebContentsViewDelegate : public content::WebContentsViewDelegate {
// content::WebContentsViewDelegate implementation.
content::WebDragDestDelegate* GetDragDestDelegate() override;
void OverrideDisplayColorSpace(gfx::ColorSpace* color_space) override;
private:
AwWebContentsViewDelegate(content::WebContents* web_contents);
......
......@@ -81,6 +81,12 @@ SurfacesInstance::SurfacesInstance()
std::move(output_surface_holder), std::move(scheduler),
std::move(texture_mailbox_deleter));
display_->Initialize(this, frame_sink_manager_->surface_manager());
// TODO(ccameron): WebViews that are embedded in WCG windows will want to
// specify gfx::ColorSpace::CreateExtendedSRGB(). This situation is not yet
// detected.
// https://crbug.com/735658
gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateSRGB();
display_->SetColorSpace(display_color_space, display_color_space);
frame_sink_manager_->RegisterBeginFrameSource(begin_frame_source_.get(),
frame_sink_id_);
......
......@@ -480,7 +480,7 @@ public class VrShellImpl
int surfaceHeight = (int) Math.ceil(height * dpr);
Point size = new Point(surfaceWidth, surfaceHeight);
mContentVirtualDisplay.update(size, dpr, null, null, null);
mContentVirtualDisplay.update(size, dpr, null, null, null, null, null);
if (mTab != null && mTab.getContentViewCore() != null) {
mTab.getContentViewCore().onSizeChanged(surfaceWidth, surfaceHeight, 0, 0);
nativeOnPhysicalBackingSizeChanged(mNativeVrShell,
......
......@@ -764,8 +764,6 @@ void CompositorImpl::OnGpuChannelEstablished(
constexpr bool support_locking = false;
constexpr bool automatic_flushes = false;
// TODO(ccameron): Update the display color space based on isWideColorGamut.
// https://crbug.com/735658
display_color_space_ = display::Screen::GetScreen()
->GetDisplayNearestWindow(root_window_)
.color_space();
......
......@@ -172,6 +172,8 @@ void WebContentsViewAndroid::GetScreenInfo(ScreenInfo* result) const {
? display::Screen::GetScreen()->GetDisplayNearestView(native_view)
: display::Screen::GetScreen()->GetPrimaryDisplay();
DisplayToScreenInfo(display, result);
if (delegate_)
delegate_->OverrideDisplayColorSpace(&result->color_space);
}
void WebContentsViewAndroid::GetContainerBounds(gfx::Rect* out) const {
......
......@@ -40,6 +40,9 @@ void WebContentsViewDelegate::TakeFocus(bool reverse) {
void WebContentsViewDelegate::SizeChanged(const gfx::Size& size) {
}
void WebContentsViewDelegate::OverrideDisplayColorSpace(
gfx::ColorSpace* color_space) {}
void* WebContentsViewDelegate::CreateRenderWidgetHostViewDelegate(
RenderWidgetHost* render_widget_host) {
return nullptr;
......
......@@ -18,6 +18,7 @@
#endif
namespace gfx {
class ColorSpace;
class Size;
}
......@@ -53,6 +54,11 @@ class CONTENT_EXPORT WebContentsViewDelegate {
virtual void TakeFocus(bool reverse);
virtual void SizeChanged(const gfx::Size& size);
// This method allows the embedder to specify the display color space (instead
// of using the color space specified by display::Display) and write it in
// |*color_space|. The default behavior is to leave |*color_space| unchanged.
virtual void OverrideDisplayColorSpace(gfx::ColorSpace* color_space);
// Returns a newly-created delegate for the RenderWidgetHostViewMac, to handle
// events on the responder chain.
#if defined(__OBJC__)
......
......@@ -84,7 +84,8 @@ void DisplayAndroidManager::UpdateDisplay(
jfloat dipScale,
jint rotationDegrees,
jint bitsPerPixel,
jint bitsPerComponent) {
jint bitsPerComponent,
jboolean isWideColorGamut) {
gfx::Rect bounds_in_pixels = gfx::Rect(width, height);
const gfx::Rect bounds_in_dip = gfx::Rect(
gfx::ScaleToCeiledSize(bounds_in_pixels.size(), 1.0f / dipScale));
......@@ -92,10 +93,12 @@ void DisplayAndroidManager::UpdateDisplay(
display::Display display(sdkDisplayId, bounds_in_dip);
if (!Display::HasForceDeviceScaleFactor())
display.set_device_scale_factor(dipScale);
// TODO(ccameron): Know the ideal color profile to use here.
// https://crbug.com/735658
if (!Display::HasForceColorProfile())
if (!Display::HasForceColorProfile()) {
if (isWideColorGamut)
display.set_color_space(gfx::ColorSpace::CreateDisplayP3D65());
else
display.set_color_space(gfx::ColorSpace::CreateSRGB());
}
display.set_size_in_pixels(bounds_in_pixels.size());
display.SetRotationAsDegree(rotationDegrees);
......
......@@ -37,7 +37,8 @@ class DisplayAndroidManager : public display::ScreenBase {
jfloat dipScale,
jint rotationDegrees,
jint bitsPerPixel,
jint bitsPerComponent);
jint bitsPerComponent,
jboolean isWideColorGamut);
void RemoveDisplay(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jobject,
jint sdkDisplayId);
......
......@@ -14,11 +14,11 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
......@@ -26,8 +26,10 @@ import android.view.Window;
import android.view.accessibility.AccessibilityManager;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.BuildInfo;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
......@@ -37,6 +39,8 @@ import org.chromium.ui.display.DisplayAndroid;
import org.chromium.ui.widget.Toast;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
......@@ -211,6 +215,19 @@ public class WindowAndroid {
mAccessibilityManager = (AccessibilityManager) mApplicationContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
mDisplayAndroid = display;
// Configuration.isDisplayServerWideColorGamut must be queried from the window's context.
// TODO(boliu): Observe configuration changes to update the value of isScreenWideColorGamut.
if (BuildInfo.isAtLeastO() && activityFromContext(context) != null) {
Configuration configuration = context.getResources().getConfiguration();
boolean isScreenWideColorGamut = false;
try {
Method method = configuration.getClass().getMethod("isScreenWideColorGamut");
isScreenWideColorGamut = (Boolean) method.invoke(configuration);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Error invoking isScreenWideColorGamut:", e);
}
display.updateIsDisplayServerWideColorGamut(isScreenWideColorGamut);
}
}
@CalledByNative
......
......@@ -50,6 +50,8 @@ public class DisplayAndroid {
private int mBitsPerPixel;
private int mBitsPerComponent;
private int mRotation;
protected boolean mIsDisplayWideColorGamut;
protected boolean mIsDisplayServerWideColorGamut;
protected static DisplayAndroidManager getManager() {
return DisplayAndroidManager.getInstance();
......@@ -145,6 +147,13 @@ public class DisplayAndroid {
return mBitsPerComponent;
}
/**
* @return Whether or not it is possible to use wide color gamut rendering with this display.
*/
public boolean getIsWideColorGamut() {
return mIsDisplayWideColorGamut && mIsDisplayServerWideColorGamut;
}
/**
* Add observer. Note repeat observers will be called only one.
* Observers are held only weakly by Display.
......@@ -187,11 +196,16 @@ public class DisplayAndroid {
return mObservers.keySet().toArray(EMPTY_OBSERVER_ARRAY);
}
public void updateIsDisplayServerWideColorGamut(Boolean isDisplayServerWideColorGamut) {
update(null, null, null, null, null, null, isDisplayServerWideColorGamut);
}
/**
* Update the display to the provided parameters. Null values leave the parameter unchanged.
*/
protected void update(Point size, Float dipScale, Integer bitsPerPixel,
Integer bitsPerComponent, Integer rotation) {
Integer bitsPerComponent, Integer rotation, Boolean isDisplayWideColorGamut,
Boolean isDisplayServerWideColorGamut) {
boolean sizeChanged = size != null && !mSize.equals(size);
// Intentional comparison of floats: we assume that if scales differ, they differ
// significantly.
......@@ -200,9 +214,14 @@ public class DisplayAndroid {
boolean bitsPerComponentChanged =
bitsPerComponent != null && mBitsPerComponent != bitsPerComponent;
boolean rotationChanged = rotation != null && mRotation != rotation;
boolean isDisplayWideColorGamutChanged = isDisplayWideColorGamut != null
&& mIsDisplayWideColorGamut != isDisplayWideColorGamut;
boolean isDisplayServerWideColorGamutChanged = isDisplayServerWideColorGamut != null
&& mIsDisplayServerWideColorGamut != isDisplayServerWideColorGamut;
boolean changed = sizeChanged || dipScaleChanged || bitsPerPixelChanged
|| bitsPerComponentChanged || rotationChanged;
|| bitsPerComponentChanged || rotationChanged || isDisplayWideColorGamutChanged
|| isDisplayServerWideColorGamutChanged;
if (!changed) return;
if (sizeChanged) mSize = size;
......@@ -210,6 +229,10 @@ public class DisplayAndroid {
if (bitsPerPixelChanged) mBitsPerPixel = bitsPerPixel;
if (bitsPerComponentChanged) mBitsPerComponent = bitsPerComponent;
if (rotationChanged) mRotation = rotation;
if (isDisplayWideColorGamutChanged) mIsDisplayWideColorGamut = isDisplayWideColorGamut;
if (isDisplayServerWideColorGamutChanged) {
mIsDisplayServerWideColorGamut = isDisplayServerWideColorGamut;
}
getManager().updateDisplayOnNativeSide(this);
if (rotationChanged) {
......
......@@ -314,12 +314,13 @@ public class DisplayAndroidManager {
nativeUpdateDisplay(mNativePointer, displayAndroid.getDisplayId(),
displayAndroid.getDisplayWidth(), displayAndroid.getDisplayHeight(),
displayAndroid.getDipScale(), displayAndroid.getRotationDegrees(),
displayAndroid.getBitsPerPixel(), displayAndroid.getBitsPerComponent());
displayAndroid.getBitsPerPixel(), displayAndroid.getBitsPerComponent(),
displayAndroid.getIsWideColorGamut());
}
private native void nativeUpdateDisplay(long nativeDisplayAndroidManager, int sdkDisplayId,
int width, int height, float dipScale, int rotationDegrees, int bitsPerPixel,
int bitsPerComponent);
int bitsPerComponent, boolean isWideColorGamut);
private native void nativeRemoveDisplay(long nativeDisplayAndroidManager, int sdkDisplayId);
private native void nativeSetPrimaryDisplayId(
long nativeDisplayAndroidManager, int sdkDisplayId);
......
......@@ -11,9 +11,13 @@ import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import org.chromium.base.BuildInfo;
import org.chromium.base.CommandLine;
import org.chromium.base.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* A DisplayAndroid implementation tied to a physical Display.
*/
......@@ -101,6 +105,15 @@ import org.chromium.base.Log;
display.getMetrics(displayMetrics);
}
if (hasForcedDIPScale()) displayMetrics.density = sForcedDIPScale.floatValue();
boolean isWideColorGamut = false;
if (BuildInfo.isAtLeastO()) {
try {
Method method = display.getClass().getMethod("isWideColorGamut");
isWideColorGamut = (Boolean) method.invoke(display);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Error invoking isWideColorGamut:", e);
}
}
// JellyBean MR1 and later always uses RGBA_8888.
int pixelFormatId = (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
......@@ -108,6 +121,6 @@ import org.chromium.base.Log;
: PixelFormat.RGBA_8888;
PixelFormat.getPixelFormatInfo(pixelFormatId, pixelFormat);
super.update(size, displayMetrics.density, pixelFormat.bitsPerPixel,
bitsPerComponent(pixelFormatId), display.getRotation());
bitsPerComponent(pixelFormatId), display.getRotation(), isWideColorGamut, null);
}
}
......@@ -23,13 +23,16 @@ public class VirtualDisplayAndroid extends DisplayAndroid {
*/
public void setTo(DisplayAndroid other) {
update(new Point(other.getDisplayWidth(), other.getDisplayHeight()), other.getDipScale(),
other.getBitsPerPixel(), other.getBitsPerComponent(), other.getRotation());
other.getBitsPerPixel(), other.getBitsPerComponent(), other.getRotation(),
other.mIsDisplayWideColorGamut, other.mIsDisplayServerWideColorGamut);
}
@Override
public void update(Point size, Float dipScale, Integer bitsPerPixel, Integer bitsPerComponent,
Integer rotation) {
super.update(size, dipScale, bitsPerPixel, bitsPerComponent, rotation);
Integer rotation, Boolean isDisplayWideColorGamut,
Boolean isDisplayServerWideColorGamut) {
super.update(size, dipScale, bitsPerPixel, bitsPerComponent, rotation,
isDisplayWideColorGamut, isDisplayServerWideColorGamut);
}
/**
......
......@@ -93,6 +93,10 @@ uint32_t DisplayList::UpdateDisplay(const Display& display, Type type) {
local_display->set_device_scale_factor(display.device_scale_factor());
changed_values |= DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR;
}
if (local_display->color_space() != display.color_space()) {
local_display->set_color_space(display.color_space());
changed_values |= DisplayObserver::DISPLAY_METRIC_COLOR_SPACE;
}
if (should_notify_observers()) {
for (DisplayObserver& observer : observers_)
observer.OnDisplayMetricsChanged(*local_display, changed_values);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment