Merge "Support both HDR and display-native screenshots." into udc-dev
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user