Merge "Support both HDR and display-native screenshots." into udc-dev

This commit is contained in:
Alec Mouri
2023-04-28 20:01:58 +00:00
committed by Android (Google) Code Review
5 changed files with 43 additions and 29 deletions

View File

@@ -198,17 +198,21 @@ public class ScreenCapture {
* Create ScreenshotHardwareBuffer from an existing HardwareBuffer object. * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
* *
* @param hardwareBuffer The existing HardwareBuffer object * @param hardwareBuffer The existing HardwareBuffer object
* @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named} * @param dataspace Dataspace describing the content.
* {@see android.hardware.DataSpace}
* @param containsSecureLayers Indicates whether this graphic buffer contains captured * @param containsSecureLayers Indicates whether this graphic buffer contains captured
* contents of secure layers, in which case the screenshot * contents of secure layers, in which case the screenshot
* should not be persisted. * should not be persisted.
* @param containsHdrLayers Indicates whether this graphic buffer contains HDR content. * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
*/ */
private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer, private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) { int dataspace, boolean containsSecureLayers, boolean containsHdrLayers) {
ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]); ColorSpace colorSpace = ColorSpace.getFromDataSpace(dataspace);
return new ScreenshotHardwareBuffer( return new ScreenshotHardwareBuffer(
hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers); hardwareBuffer,
colorSpace != null ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB),
containsSecureLayers,
containsHdrLayers);
} }
public ColorSpace getColorSpace() { public ColorSpace getColorSpace() {
@@ -271,8 +275,8 @@ public class ScreenCapture {
public final boolean mAllowProtected; public final boolean mAllowProtected;
public final long mUid; public final long mUid;
public final boolean mGrayscale; public final boolean mGrayscale;
final SurfaceControl[] mExcludeLayers; final SurfaceControl[] mExcludeLayers;
public final boolean mHintForSeamlessTransition;
private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) { private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
mPixelFormat = builder.mPixelFormat; mPixelFormat = builder.mPixelFormat;
@@ -284,6 +288,7 @@ public class ScreenCapture {
mUid = builder.mUid; mUid = builder.mUid;
mGrayscale = builder.mGrayscale; mGrayscale = builder.mGrayscale;
mExcludeLayers = builder.mExcludeLayers; mExcludeLayers = builder.mExcludeLayers;
mHintForSeamlessTransition = builder.mHintForSeamlessTransition;
} }
private CaptureArgs(Parcel in) { private CaptureArgs(Parcel in) {
@@ -305,6 +310,7 @@ public class ScreenCapture {
} else { } else {
mExcludeLayers = null; mExcludeLayers = null;
} }
mHintForSeamlessTransition = in.readBoolean();
} }
/** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */ /** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */
@@ -352,6 +358,7 @@ public class ScreenCapture {
private long mUid = -1; private long mUid = -1;
private boolean mGrayscale; private boolean mGrayscale;
private SurfaceControl[] mExcludeLayers; private SurfaceControl[] mExcludeLayers;
private boolean mHintForSeamlessTransition;
/** /**
* Construct a new {@link CaptureArgs} with the set parameters. The builder remains * Construct a new {@link CaptureArgs} with the set parameters. The builder remains
@@ -448,6 +455,21 @@ public class ScreenCapture {
return getThis(); return getThis();
} }
/**
* Set whether the screenshot will be used in a system animation.
* This hint is used for picking the "best" colorspace for the screenshot, in particular
* for mixing HDR and SDR content.
* E.g., hintForSeamlessTransition is false, then a colorspace suitable for file
* encoding, such as BT2100, may be chosen. Otherwise, then the display's color space
* would be chosen, with the possibility of having an extended brightness range. This
* is important for screenshots that are directly re-routed to a SurfaceControl in
* order to preserve accurate colors.
*/
public T setHintForSeamlessTransition(boolean hintForSeamlessTransition) {
mHintForSeamlessTransition = hintForSeamlessTransition;
return getThis();
}
/** /**
* Each sub class should return itself to allow the builder to chain properly * Each sub class should return itself to allow the builder to chain properly
*/ */
@@ -471,7 +493,6 @@ public class ScreenCapture {
dest.writeBoolean(mAllowProtected); dest.writeBoolean(mAllowProtected);
dest.writeLong(mUid); dest.writeLong(mUid);
dest.writeBoolean(mGrayscale); dest.writeBoolean(mGrayscale);
if (mExcludeLayers != null) { if (mExcludeLayers != null) {
dest.writeInt(mExcludeLayers.length); dest.writeInt(mExcludeLayers.length);
for (SurfaceControl excludeLayer : mExcludeLayers) { for (SurfaceControl excludeLayer : mExcludeLayers) {
@@ -480,6 +501,7 @@ public class ScreenCapture {
} else { } else {
dest.writeInt(0); dest.writeInt(0);
} }
dest.writeBoolean(mHintForSeamlessTransition);
} }
public static final Parcelable.Creator<CaptureArgs> CREATOR = public static final Parcelable.Creator<CaptureArgs> CREATOR =
@@ -627,6 +649,7 @@ public class ScreenCapture {
setUid(args.mUid); setUid(args.mUid);
setGrayscale(args.mGrayscale); setGrayscale(args.mGrayscale);
setExcludeLayers(args.mExcludeLayers); setExcludeLayers(args.mExcludeLayers);
setHintForSeamlessTransition(args.mHintForSeamlessTransition);
} }
public Builder(SurfaceControl layer) { public Builder(SurfaceControl layer) {

View File

@@ -46,6 +46,7 @@ static struct {
jfieldID uid; jfieldID uid;
jfieldID grayscale; jfieldID grayscale;
jmethodID getNativeExcludeLayers; jmethodID getNativeExcludeLayers;
jfieldID hintForSeamlessTransition;
} gCaptureArgsClassInfo; } gCaptureArgsClassInfo;
static struct { static struct {
@@ -69,23 +70,6 @@ static struct {
jmethodID builder; jmethodID builder;
} gScreenshotHardwareBufferClassInfo; } gScreenshotHardwareBufferClassInfo;
enum JNamedColorSpace : jint {
// ColorSpace.Named.SRGB.ordinal() = 0;
SRGB = 0,
// ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
DISPLAY_P3 = 7,
};
constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
switch (dataspace) {
case ui::Dataspace::DISPLAY_P3:
return JNamedColorSpace::DISPLAY_P3;
default:
return JNamedColorSpace::SRGB;
}
}
static void checkAndClearException(JNIEnv* env, const char* methodName) { static void checkAndClearException(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) { if (env->ExceptionCheck()) {
ALOGE("An exception was thrown by callback '%s'.", methodName); ALOGE("An exception was thrown by callback '%s'.", methodName);
@@ -119,12 +103,11 @@ public:
captureResults.fenceResult.value()->waitForever(LOG_TAG); captureResults.fenceResult.value()->waitForever(LOG_TAG);
jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
env, captureResults.buffer->toAHardwareBuffer()); env, captureResults.buffer->toAHardwareBuffer());
const jint namedColorSpace =
fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
jobject screenshotHardwareBuffer = jobject screenshotHardwareBuffer =
env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz, env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
gScreenshotHardwareBufferClassInfo.builder, gScreenshotHardwareBufferClassInfo.builder,
jhardwareBuffer, namedColorSpace, jhardwareBuffer,
static_cast<jint>(captureResults.capturedDataspace),
captureResults.capturedSecureLayers, captureResults.capturedSecureLayers,
captureResults.capturedHdrLayers); captureResults.capturedHdrLayers);
checkAndClearException(env, "builder"); checkAndClearException(env, "builder");
@@ -185,6 +168,9 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs&
captureArgs.excludeHandles.emplace(excludeObject->getHandle()); captureArgs.excludeHandles.emplace(excludeObject->getHandle());
} }
} }
captureArgs.hintForSeamlessTransition =
env->GetBooleanField(captureArgsObject,
gCaptureArgsClassInfo.hintForSeamlessTransition);
} }
static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
@@ -318,9 +304,10 @@ int register_android_window_ScreenCapture(JNIEnv* env) {
GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z"); GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J"); gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z"); gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
gCaptureArgsClassInfo.getNativeExcludeLayers = gCaptureArgsClassInfo.getNativeExcludeLayers =
GetMethodIDOrDie(env, captureArgsClazz, "getNativeExcludeLayers", "()[J"); GetMethodIDOrDie(env, captureArgsClazz, "getNativeExcludeLayers", "()[J");
gCaptureArgsClassInfo.hintForSeamlessTransition =
GetFieldIDOrDie(env, captureArgsClazz, "mHintForSeamlessTransition", "Z");
jclass displayCaptureArgsClazz = jclass displayCaptureArgsClazz =
FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs"); FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs");

View File

@@ -144,6 +144,7 @@ class ScreenRotationAnimation {
.setCaptureSecureLayers(true) .setCaptureSecureLayers(true)
.setAllowProtected(true) .setAllowProtected(true)
.setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight)) .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
.setHintForSeamlessTransition(true)
.build(); .build();
ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
ScreenCapture.captureLayers(args); ScreenCapture.captureLayers(args);

View File

@@ -193,6 +193,7 @@ class ScreenRotationAnimation {
.setSourceCrop(new Rect(0, 0, width, height)) .setSourceCrop(new Rect(0, 0, width, height))
.setAllowProtected(true) .setAllowProtected(true)
.setCaptureSecureLayers(true) .setCaptureSecureLayers(true)
.setHintForSeamlessTransition(true)
.build(); .build();
screenshotBuffer = ScreenCapture.captureDisplay(captureArgs); screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
} else { } else {
@@ -202,6 +203,7 @@ class ScreenRotationAnimation {
.setCaptureSecureLayers(true) .setCaptureSecureLayers(true)
.setAllowProtected(true) .setAllowProtected(true)
.setSourceCrop(new Rect(0, 0, width, height)) .setSourceCrop(new Rect(0, 0, width, height))
.setHintForSeamlessTransition(true)
.build(); .build();
screenshotBuffer = ScreenCapture.captureLayers(captureArgs); screenshotBuffer = ScreenCapture.captureLayers(captureArgs);
} }

View File

@@ -2999,11 +2999,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Rect cropBounds = new Rect(bounds); Rect cropBounds = new Rect(bounds);
cropBounds.offsetTo(0, 0); cropBounds.offsetTo(0, 0);
final boolean isDisplayRotation = wc.asDisplayContent() != null
&& wc.asDisplayContent().isRotationChanging();
ScreenCapture.LayerCaptureArgs captureArgs = ScreenCapture.LayerCaptureArgs captureArgs =
new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl()) new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl())
.setSourceCrop(cropBounds) .setSourceCrop(cropBounds)
.setCaptureSecureLayers(true) .setCaptureSecureLayers(true)
.setAllowProtected(true) .setAllowProtected(true)
.setHintForSeamlessTransition(isDisplayRotation)
.build(); .build();
ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
ScreenCapture.captureLayers(captureArgs); ScreenCapture.captureLayers(captureArgs);
@@ -3014,8 +3017,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
Slog.w(TAG, "Failed to capture screenshot for " + wc); Slog.w(TAG, "Failed to capture screenshot for " + wc);
return false; return false;
} }
final boolean isDisplayRotation = wc.asDisplayContent() != null
&& wc.asDisplayContent().isRotationChanging();
// Some tests may check the name "RotationLayer" to detect display rotation. // Some tests may check the name "RotationLayer" to detect display rotation.
final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc; final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc;
SurfaceControl snapshotSurface = wc.makeAnimationLeash() SurfaceControl snapshotSurface = wc.makeAnimationLeash()