Revert "Add captureDisplay API in WMS."
This reverts commit c184fd711a.
Reason for revert: 247801379
Change-Id: I20ac6772656d116ca03bc12d60a0c331f7079df2
This commit is contained in:
@@ -67,7 +67,6 @@ import android.view.SurfaceControl;
|
||||
import android.view.displayhash.DisplayHash;
|
||||
import android.view.displayhash.VerifiedDisplayHash;
|
||||
import android.window.ITaskFpsCallback;
|
||||
import android.window.ScreenCapture;
|
||||
|
||||
/**
|
||||
* System private interface to the window manager.
|
||||
@@ -969,7 +968,4 @@ interface IWindowManager
|
||||
* treatment.
|
||||
*/
|
||||
boolean isLetterboxBackgroundMultiColored();
|
||||
|
||||
oneway void captureDisplay(int displayId, in @nullable ScreenCapture.CaptureArgs captureArgs,
|
||||
in ScreenCapture.ScreenCaptureListener listener);
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.window;
|
||||
|
||||
/** @hide */
|
||||
parcelable ScreenCapture.CaptureArgs;
|
||||
|
||||
/** @hide */
|
||||
parcelable ScreenCapture.ScreenshotHardwareBuffer;
|
||||
|
||||
/** @hide */
|
||||
parcelable ScreenCapture.ScreenCaptureListener;
|
||||
@@ -24,16 +24,11 @@ import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import libcore.util.NativeAllocationRegistry;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Handles display and layer captures for the system.
|
||||
@@ -44,23 +39,18 @@ public class ScreenCapture {
|
||||
private static final String TAG = "ScreenCapture";
|
||||
|
||||
private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
|
||||
long captureListener);
|
||||
ScreenCaptureListener captureListener);
|
||||
private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
|
||||
long captureListener);
|
||||
private static native long nativeCreateScreenCaptureListener(
|
||||
Consumer<ScreenshotHardwareBuffer> consumer);
|
||||
private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
|
||||
private static native long nativeReadListenerFromParcel(Parcel in);
|
||||
private static native long getNativeListenerFinalizer();
|
||||
ScreenCaptureListener captureListener);
|
||||
|
||||
/**
|
||||
* @param captureArgs Arguments about how to take the screenshot
|
||||
* @param captureArgs Arguments about how to take the screenshot
|
||||
* @param captureListener A listener to receive the screenshot callback
|
||||
* @hide
|
||||
*/
|
||||
public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
|
||||
@NonNull ScreenCaptureListener captureListener) {
|
||||
return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
|
||||
return nativeCaptureDisplay(captureArgs, captureListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,8 +61,10 @@ public class ScreenCapture {
|
||||
*/
|
||||
public static ScreenshotHardwareBuffer captureDisplay(
|
||||
DisplayCaptureArgs captureArgs) {
|
||||
SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
|
||||
int status = captureDisplay(captureArgs, screenCaptureListener.getScreenCaptureListener());
|
||||
SyncScreenCaptureListener
|
||||
screenCaptureListener = new SyncScreenCaptureListener();
|
||||
|
||||
int status = captureDisplay(captureArgs, screenCaptureListener);
|
||||
if (status != 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -83,13 +75,14 @@ public class ScreenCapture {
|
||||
/**
|
||||
* Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
|
||||
*
|
||||
* @param layer The root layer to capture.
|
||||
* @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
|
||||
* Rect()' or null if no cropping is desired. If the root layer does not
|
||||
* have a buffer or a crop set, then a non-empty source crop must be
|
||||
* specified.
|
||||
* @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
|
||||
* up/down.
|
||||
* @param layer The root layer to capture.
|
||||
* @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
|
||||
* Rect()' or null if no cropping is desired. If the root layer does not
|
||||
* have a buffer or a crop set, then a non-empty source crop must be
|
||||
* specified.
|
||||
* @param frameScale The desired scale of the returned buffer; the raw
|
||||
* screen will be scaled up/down.
|
||||
*
|
||||
* @return Returns a HardwareBuffer that contains the layer capture.
|
||||
* @hide
|
||||
*/
|
||||
@@ -101,14 +94,15 @@ public class ScreenCapture {
|
||||
/**
|
||||
* Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
|
||||
*
|
||||
* @param layer The root layer to capture.
|
||||
* @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
|
||||
* Rect()' or null if no cropping is desired. If the root layer does not
|
||||
* have a buffer or a crop set, then a non-empty source crop must be
|
||||
* specified.
|
||||
* @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
|
||||
* up/down.
|
||||
* @param format The desired pixel format of the returned buffer.
|
||||
* @param layer The root layer to capture.
|
||||
* @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
|
||||
* Rect()' or null if no cropping is desired. If the root layer does not
|
||||
* have a buffer or a crop set, then a non-empty source crop must be
|
||||
* specified.
|
||||
* @param frameScale The desired scale of the returned buffer; the raw
|
||||
* screen will be scaled up/down.
|
||||
* @param format The desired pixel format of the returned buffer.
|
||||
*
|
||||
* @return Returns a HardwareBuffer that contains the layer capture.
|
||||
* @hide
|
||||
*/
|
||||
@@ -130,7 +124,7 @@ public class ScreenCapture {
|
||||
LayerCaptureArgs captureArgs) {
|
||||
SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
|
||||
|
||||
int status = captureLayers(captureArgs, screenCaptureListener.getScreenCaptureListener());
|
||||
int status = captureLayers(captureArgs, screenCaptureListener);
|
||||
if (status != 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -141,7 +135,6 @@ public class ScreenCapture {
|
||||
/**
|
||||
* Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
|
||||
* handles to exclude.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
|
||||
@@ -157,13 +150,24 @@ public class ScreenCapture {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param captureArgs Arguments about how to take the screenshot
|
||||
* @param captureArgs Arguments about how to take the screenshot
|
||||
* @param captureListener A listener to receive the screenshot callback
|
||||
* @hide
|
||||
*/
|
||||
public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
|
||||
@NonNull ScreenCaptureListener captureListener) {
|
||||
return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
|
||||
return nativeCaptureLayers(captureArgs, captureListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public interface ScreenCaptureListener {
|
||||
/**
|
||||
* The callback invoked when the screen capture is complete.
|
||||
* @param hardwareBuffer Data containing info about the screen capture.
|
||||
*/
|
||||
void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,16 +190,15 @@ public class ScreenCapture {
|
||||
mContainsHdrLayers = containsHdrLayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
|
||||
*
|
||||
* @param hardwareBuffer The existing HardwareBuffer object
|
||||
* @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
|
||||
* @param containsSecureLayers Indicates whether this graphic buffer contains captured
|
||||
* contents of secure layers, in which case the screenshot
|
||||
* should not be persisted.
|
||||
* @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
|
||||
*/
|
||||
/**
|
||||
* Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
|
||||
* @param hardwareBuffer The existing HardwareBuffer object
|
||||
* @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
|
||||
* @param containsSecureLayers Indicates whether this graphic buffer contains captured
|
||||
* contents of secure layers, in which case the screenshot
|
||||
* should not be persisted.
|
||||
* @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
|
||||
*/
|
||||
private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
|
||||
int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
|
||||
ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
|
||||
@@ -217,7 +220,6 @@ public class ScreenCapture {
|
||||
public boolean containsSecureLayers() {
|
||||
return mContainsSecureLayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the screenshot contains at least one HDR layer.
|
||||
* This information may be useful for informing the display whether this screenshot
|
||||
@@ -232,7 +234,7 @@ public class ScreenCapture {
|
||||
* Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
|
||||
* into
|
||||
* a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
|
||||
* <p>
|
||||
*
|
||||
* CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
|
||||
* directly
|
||||
* use the {@link HardwareBuffer} directly.
|
||||
@@ -248,13 +250,34 @@ public class ScreenCapture {
|
||||
}
|
||||
}
|
||||
|
||||
private static class SyncScreenCaptureListener implements ScreenCaptureListener {
|
||||
private static final int SCREENSHOT_WAIT_TIME_S = 1;
|
||||
private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
|
||||
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
|
||||
mScreenshotHardwareBuffer = hardwareBuffer;
|
||||
mCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
private ScreenshotHardwareBuffer waitForScreenshot() {
|
||||
try {
|
||||
mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to wait for screen capture result", e);
|
||||
}
|
||||
|
||||
return mScreenshotHardwareBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A common arguments class used for various screenshot requests. This contains arguments that
|
||||
* are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class CaptureArgs implements Parcelable {
|
||||
private abstract static class CaptureArgs {
|
||||
private final int mPixelFormat;
|
||||
private final Rect mSourceCrop = new Rect();
|
||||
private final float mFrameScaleX;
|
||||
@@ -264,7 +287,7 @@ public class ScreenCapture {
|
||||
private final long mUid;
|
||||
private final boolean mGrayscale;
|
||||
|
||||
private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
|
||||
private CaptureArgs(Builder<? extends Builder<?>> builder) {
|
||||
mPixelFormat = builder.mPixelFormat;
|
||||
mSourceCrop.set(builder.mSourceCrop);
|
||||
mFrameScaleX = builder.mFrameScaleX;
|
||||
@@ -275,23 +298,12 @@ public class ScreenCapture {
|
||||
mGrayscale = builder.mGrayscale;
|
||||
}
|
||||
|
||||
private CaptureArgs(Parcel in) {
|
||||
mPixelFormat = in.readInt();
|
||||
mSourceCrop.readFromParcel(in);
|
||||
mFrameScaleX = in.readFloat();
|
||||
mFrameScaleY = in.readFloat();
|
||||
mCaptureSecureLayers = in.readBoolean();
|
||||
mAllowProtected = in.readBoolean();
|
||||
mUid = in.readLong();
|
||||
mGrayscale = in.readBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* The Builder class used to construct {@link CaptureArgs}
|
||||
*
|
||||
* @param <T> A builder that extends {@link CaptureArgs.Builder}
|
||||
* @param <T> A builder that extends {@link Builder}
|
||||
*/
|
||||
public static class Builder<T extends CaptureArgs.Builder<T>> {
|
||||
abstract static class Builder<T extends Builder<T>> {
|
||||
private int mPixelFormat = PixelFormat.RGBA_8888;
|
||||
private final Rect mSourceCrop = new Rect();
|
||||
private float mFrameScaleX = 1;
|
||||
@@ -301,14 +313,6 @@ public class ScreenCapture {
|
||||
private long mUid = -1;
|
||||
private boolean mGrayscale;
|
||||
|
||||
/**
|
||||
* Construct a new {@link CaptureArgs} with the set parameters. The builder remains
|
||||
* valid.
|
||||
*/
|
||||
public CaptureArgs build() {
|
||||
return new CaptureArgs(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The desired pixel format of the returned buffer.
|
||||
*/
|
||||
@@ -391,47 +395,15 @@ public class ScreenCapture {
|
||||
/**
|
||||
* Each sub class should return itself to allow the builder to chain properly
|
||||
*/
|
||||
T getThis() {
|
||||
return (T) this;
|
||||
}
|
||||
abstract T getThis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mPixelFormat);
|
||||
mSourceCrop.writeToParcel(dest, flags);
|
||||
dest.writeFloat(mFrameScaleX);
|
||||
dest.writeFloat(mFrameScaleY);
|
||||
dest.writeBoolean(mCaptureSecureLayers);
|
||||
dest.writeBoolean(mAllowProtected);
|
||||
dest.writeLong(mUid);
|
||||
dest.writeBoolean(mGrayscale);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CaptureArgs> CREATOR =
|
||||
new Parcelable.Creator<CaptureArgs>() {
|
||||
@Override
|
||||
public CaptureArgs createFromParcel(Parcel in) {
|
||||
return new CaptureArgs(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureArgs[] newArray(int size) {
|
||||
return new CaptureArgs[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments class used to make display capture requests.
|
||||
*
|
||||
* @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
|
||||
* @hide
|
||||
* @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
|
||||
*/
|
||||
public static class DisplayCaptureArgs extends CaptureArgs {
|
||||
private final IBinder mDisplayToken;
|
||||
@@ -516,8 +488,8 @@ public class ScreenCapture {
|
||||
/**
|
||||
* The arguments class used to make layer capture requests.
|
||||
*
|
||||
* @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
|
||||
* @hide
|
||||
* @see #nativeCaptureLayers(LayerCaptureArgs, long)
|
||||
*/
|
||||
public static class LayerCaptureArgs extends CaptureArgs {
|
||||
private final long mNativeLayer;
|
||||
@@ -558,17 +530,6 @@ public class ScreenCapture {
|
||||
return new LayerCaptureArgs(this);
|
||||
}
|
||||
|
||||
public Builder(SurfaceControl layer, CaptureArgs args) {
|
||||
setLayer(layer);
|
||||
setPixelFormat(args.mPixelFormat);
|
||||
setSourceCrop(args.mSourceCrop);
|
||||
setFrameScale(args.mFrameScaleX, args.mFrameScaleY);
|
||||
setCaptureSecureLayers(args.mCaptureSecureLayers);
|
||||
setAllowProtected(args.mAllowProtected);
|
||||
setUid(args.mUid);
|
||||
setGrayscale(args.mGrayscale);
|
||||
}
|
||||
|
||||
public Builder(SurfaceControl layer) {
|
||||
setLayer(layer);
|
||||
}
|
||||
@@ -581,6 +542,7 @@ public class ScreenCapture {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An array of layer handles to exclude.
|
||||
*/
|
||||
@@ -602,106 +564,8 @@ public class ScreenCapture {
|
||||
Builder getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The object used to receive the results when invoking screen capture requests via
|
||||
* {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
|
||||
* {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
|
||||
*/
|
||||
public static class ScreenCaptureListener implements Parcelable {
|
||||
private final long mNativeObject;
|
||||
private static final NativeAllocationRegistry sRegistry =
|
||||
NativeAllocationRegistry.createMalloced(
|
||||
ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());
|
||||
|
||||
/**
|
||||
* @param consumer The callback invoked when the screen capture is complete.
|
||||
*/
|
||||
public ScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
|
||||
mNativeObject = nativeCreateScreenCaptureListener(consumer);
|
||||
sRegistry.registerNativeAllocation(this, mNativeObject);
|
||||
}
|
||||
|
||||
private ScreenCaptureListener(Parcel in) {
|
||||
if (in.readBoolean()) {
|
||||
mNativeObject = nativeReadListenerFromParcel(in);
|
||||
sRegistry.registerNativeAllocation(this, mNativeObject);
|
||||
} else {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
if (mNativeObject == 0) {
|
||||
dest.writeBoolean(false);
|
||||
} else {
|
||||
dest.writeBoolean(true);
|
||||
nativeWriteListenerToParcel(mNativeObject, dest);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ScreenCaptureListener> CREATOR =
|
||||
new Parcelable.Creator<ScreenCaptureListener>() {
|
||||
@Override
|
||||
public ScreenCaptureListener createFromParcel(Parcel in) {
|
||||
return new ScreenCaptureListener(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScreenCaptureListener[] newArray(int size) {
|
||||
return new ScreenCaptureListener[0];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class to handle the async screencapture callbacks synchronously. This should only
|
||||
* be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
|
||||
*/
|
||||
public static class SyncScreenCaptureListener {
|
||||
private static final int SCREENSHOT_WAIT_TIME_S = 1;
|
||||
private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
|
||||
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
|
||||
private final ScreenCaptureListener mScreenCaptureListener;
|
||||
|
||||
public SyncScreenCaptureListener() {
|
||||
mScreenCaptureListener = new ScreenCaptureListener(screenshotHardwareBuffer -> {
|
||||
mScreenshotHardwareBuffer = screenshotHardwareBuffer;
|
||||
mCountDownLatch.countDown();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The underlying {@link ScreenCaptureListener}
|
||||
*/
|
||||
public ScreenCaptureListener getScreenCaptureListener() {
|
||||
return mScreenCaptureListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the screenshot callback has been invoked and the screenshot is ready. This
|
||||
* can return {@code null} if the screenshot callback wasn't invoked after
|
||||
* {@link #SCREENSHOT_WAIT_TIME_S} or the screencapture request resulted in an error
|
||||
*
|
||||
* @return A ScreenshotHardwareBuffer for the content that was captured.
|
||||
*/
|
||||
@Nullable
|
||||
public ScreenshotHardwareBuffer waitForScreenshot() {
|
||||
try {
|
||||
mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to wait for screen capture result", e);
|
||||
}
|
||||
|
||||
return mScreenshotHardwareBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,9 @@ static struct {
|
||||
} gLayerCaptureArgsClassInfo;
|
||||
|
||||
static struct {
|
||||
jmethodID accept;
|
||||
} gConsumerClassInfo;
|
||||
jclass clazz;
|
||||
jmethodID onScreenCaptureComplete;
|
||||
} gScreenCaptureListenerClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
@@ -97,14 +98,14 @@ class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
|
||||
public:
|
||||
explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
|
||||
env->GetJavaVM(&mVm);
|
||||
mConsumerObject = env->NewGlobalRef(jobject);
|
||||
LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
|
||||
mScreenCaptureListenerObject = env->NewGlobalRef(jobject);
|
||||
LOG_ALWAYS_FATAL_IF(!mScreenCaptureListenerObject, "Failed to make global ref");
|
||||
}
|
||||
|
||||
~ScreenCaptureListenerWrapper() {
|
||||
if (mConsumerObject) {
|
||||
getenv()->DeleteGlobalRef(mConsumerObject);
|
||||
mConsumerObject = nullptr;
|
||||
if (mScreenCaptureListenerObject) {
|
||||
getenv()->DeleteGlobalRef(mScreenCaptureListenerObject);
|
||||
mScreenCaptureListenerObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +113,9 @@ public:
|
||||
const gui::ScreenCaptureResults& captureResults) override {
|
||||
JNIEnv* env = getenv();
|
||||
if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
|
||||
env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
|
||||
checkAndClearException(env, "accept");
|
||||
env->CallVoidMethod(mScreenCaptureListenerObject,
|
||||
gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
|
||||
checkAndClearException(env, "onScreenCaptureComplete");
|
||||
return binder::Status::ok();
|
||||
}
|
||||
captureResults.fenceResult.value()->waitForever(LOG_TAG);
|
||||
@@ -128,15 +130,17 @@ public:
|
||||
captureResults.capturedSecureLayers,
|
||||
captureResults.capturedHdrLayers);
|
||||
checkAndClearException(env, "builder");
|
||||
env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
|
||||
checkAndClearException(env, "accept");
|
||||
env->CallVoidMethod(mScreenCaptureListenerObject,
|
||||
gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
|
||||
screenshotHardwareBuffer);
|
||||
checkAndClearException(env, "onScreenCaptureComplete");
|
||||
env->DeleteLocalRef(jhardwareBuffer);
|
||||
env->DeleteLocalRef(screenshotHardwareBuffer);
|
||||
return binder::Status::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
jobject mConsumerObject;
|
||||
jobject mScreenCaptureListenerObject;
|
||||
JavaVM* mVm;
|
||||
|
||||
JNIEnv* getenv() {
|
||||
@@ -190,7 +194,7 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
|
||||
}
|
||||
|
||||
static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
|
||||
jlong screenCaptureListenerObject) {
|
||||
jobject screenCaptureListenerObject) {
|
||||
const DisplayCaptureArgs captureArgs =
|
||||
displayCaptureArgsFromObject(env, displayCaptureArgsObject);
|
||||
|
||||
@@ -198,13 +202,13 @@ static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptu
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
sp<gui::IScreenCaptureListener> captureListener =
|
||||
reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
|
||||
sp<IScreenCaptureListener> captureListener =
|
||||
sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
|
||||
return ScreenshotClient::captureDisplay(captureArgs, captureListener);
|
||||
}
|
||||
|
||||
static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
|
||||
jlong screenCaptureListenerObject) {
|
||||
jobject screenCaptureListenerObject) {
|
||||
LayerCaptureArgs captureArgs;
|
||||
getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
|
||||
SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
|
||||
@@ -234,70 +238,21 @@ static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureA
|
||||
}
|
||||
}
|
||||
|
||||
sp<gui::IScreenCaptureListener> captureListener =
|
||||
reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
|
||||
sp<IScreenCaptureListener> captureListener =
|
||||
sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
|
||||
return ScreenshotClient::captureLayers(captureArgs, captureListener);
|
||||
}
|
||||
|
||||
static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
|
||||
sp<gui::IScreenCaptureListener> listener =
|
||||
sp<ScreenCaptureListenerWrapper>::make(env, consumerObj);
|
||||
listener->incStrong((void*)nativeCreateScreenCaptureListener);
|
||||
return reinterpret_cast<jlong>(listener.get());
|
||||
}
|
||||
|
||||
static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject,
|
||||
jobject parcelObj) {
|
||||
Parcel* parcel = parcelForJavaObject(env, parcelObj);
|
||||
if (parcel == NULL) {
|
||||
jniThrowNullPointerException(env, NULL);
|
||||
return;
|
||||
}
|
||||
ScreenCaptureListenerWrapper* const self =
|
||||
reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject);
|
||||
if (self != nullptr) {
|
||||
parcel->writeStrongBinder(IInterface::asBinder(self));
|
||||
}
|
||||
}
|
||||
|
||||
static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
|
||||
Parcel* parcel = parcelForJavaObject(env, parcelObj);
|
||||
if (parcel == NULL) {
|
||||
jniThrowNullPointerException(env, NULL);
|
||||
return 0;
|
||||
}
|
||||
sp<gui::IScreenCaptureListener> listener =
|
||||
interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder());
|
||||
if (listener == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
listener->incStrong((void*)nativeCreateScreenCaptureListener);
|
||||
return reinterpret_cast<jlong>(listener.get());
|
||||
}
|
||||
|
||||
void destroyNativeListener(void* ptr) {
|
||||
ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr);
|
||||
listener->decStrong((void*)nativeCreateScreenCaptureListener);
|
||||
}
|
||||
|
||||
static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) {
|
||||
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static const JNINativeMethod sScreenCaptureMethods[] = {
|
||||
// clang-format off
|
||||
{"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
|
||||
{"nativeCaptureDisplay",
|
||||
"(Landroid/window/ScreenCapture$DisplayCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
|
||||
(void*)nativeCaptureDisplay },
|
||||
{"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
|
||||
{"nativeCaptureLayers",
|
||||
"(Landroid/window/ScreenCapture$LayerCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
|
||||
(void*)nativeCaptureLayers },
|
||||
{"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J",
|
||||
(void*)nativeCreateScreenCaptureListener },
|
||||
{"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel },
|
||||
{"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J",
|
||||
(void*)nativeReadListenerFromParcel },
|
||||
{"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
@@ -338,8 +293,12 @@ int register_android_window_ScreenCapture(JNIEnv* env) {
|
||||
gLayerCaptureArgsClassInfo.childrenOnly =
|
||||
GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
|
||||
|
||||
jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
|
||||
gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
|
||||
jclass screenCaptureListenerClazz =
|
||||
FindClassOrDie(env, "android/window/ScreenCapture$ScreenCaptureListener");
|
||||
gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
|
||||
gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
|
||||
GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
|
||||
"(Landroid/window/ScreenCapture$ScreenshotHardwareBuffer;)V");
|
||||
|
||||
jclass screenshotGraphicsBufferClazz =
|
||||
FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
|
||||
|
||||
@@ -9277,38 +9277,4 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
"Unexpected letterbox background type: " + letterboxBackgroundType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
|
||||
ScreenCapture.ScreenCaptureListener listener) {
|
||||
Slog.d(TAG, "captureDisplay");
|
||||
if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
|
||||
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
|
||||
}
|
||||
|
||||
final SurfaceControl displaySurfaceControl;
|
||||
synchronized (mGlobalLock) {
|
||||
DisplayContent displayContent = mRoot.getDisplayContent(displayId);
|
||||
if (displayContent == null) {
|
||||
throw new IllegalArgumentException("Trying to screenshot and invalid display: "
|
||||
+ displayId);
|
||||
}
|
||||
|
||||
displaySurfaceControl = displayContent.getSurfaceControl();
|
||||
|
||||
if (captureArgs == null) {
|
||||
displayContent.getBounds(mTmpRect);
|
||||
mTmpRect.offsetTo(0, 0);
|
||||
captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
|
||||
.setSourceCrop(mTmpRect)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
ScreenCapture.LayerCaptureArgs args =
|
||||
new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs)
|
||||
.build();
|
||||
|
||||
ScreenCapture.captureLayers(args, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,10 +79,7 @@
|
||||
<activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
|
||||
android:turnScreenOn="true"
|
||||
android:showWhenLocked="true" />
|
||||
<activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity"
|
||||
android:theme="@style/WhiteBackgroundTheme"
|
||||
android:turnScreenOn="true"
|
||||
android:showWhenLocked="true"/>
|
||||
<activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
|
||||
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
|
||||
|
||||
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<style name="WhiteBackgroundTheme" parent="@android:style/Theme.DeviceDefault">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowInsets.Type.displayCutout;
|
||||
import static android.view.WindowInsets.Type.statusBars;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -27,31 +23,20 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.DataSpace;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.cts.surfacevalidator.BitmapPixelChecker;
|
||||
import android.view.cts.surfacevalidator.PixelColor;
|
||||
import android.view.cts.surfacevalidator.SaveBitmapHelper;
|
||||
import android.view.WindowManager;
|
||||
import android.window.ScreenCapture;
|
||||
import android.window.ScreenCapture.SyncScreenCaptureListener;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -60,7 +45,6 @@ import androidx.test.rule.ActivityTestRule;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -76,8 +60,6 @@ public class ScreenshotTests {
|
||||
private static final int BUFFER_HEIGHT = 100;
|
||||
|
||||
private final Instrumentation mInstrumentation = getInstrumentation();
|
||||
@Rule
|
||||
public TestName mTestName = new TestName();
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<ScreenshotActivity> mActivityRule =
|
||||
@@ -113,8 +95,8 @@ public class ScreenshotTests {
|
||||
buffer.unlockCanvasAndPost(canvas);
|
||||
|
||||
t.show(secureSC)
|
||||
.setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
|
||||
.setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
|
||||
.setBuffer(secureSC, buffer)
|
||||
.setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB))
|
||||
.apply(true);
|
||||
|
||||
ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
|
||||
@@ -130,69 +112,15 @@ public class ScreenshotTests {
|
||||
Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
|
||||
screenshot.recycle();
|
||||
|
||||
BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
|
||||
Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
|
||||
int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
|
||||
int sizeOfBitmap = bounds.width() * bounds.height();
|
||||
int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap,
|
||||
new PixelColor(PixelColor.RED));
|
||||
long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight();
|
||||
boolean success = numMatchingPixels == sizeOfBitmap;
|
||||
swBitmap.recycle();
|
||||
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDisplay() throws RemoteException {
|
||||
IWindowManager windowManager = IWindowManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.WINDOW_SERVICE));
|
||||
SurfaceControl sc = new SurfaceControl.Builder()
|
||||
.setName("Layer")
|
||||
.setCallsite("testCaptureDisplay")
|
||||
.build();
|
||||
|
||||
SurfaceControl.Transaction t = mActivity.addChildSc(sc);
|
||||
mInstrumentation.waitForIdleSync();
|
||||
|
||||
GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT,
|
||||
PixelFormat.RGBA_8888,
|
||||
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
|
||||
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
|
||||
|
||||
Canvas canvas = buffer.lockCanvas();
|
||||
canvas.drawColor(Color.RED);
|
||||
buffer.unlockCanvasAndPost(canvas);
|
||||
|
||||
Point point = mActivity.getPositionBelowStatusBar();
|
||||
t.show(sc)
|
||||
.setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
|
||||
.setDataSpace(sc, DataSpace.DATASPACE_SRGB)
|
||||
.setPosition(sc, point.x, point.y)
|
||||
.apply(true);
|
||||
|
||||
SyncScreenCaptureListener listener = new SyncScreenCaptureListener();
|
||||
windowManager.captureDisplay(DEFAULT_DISPLAY, null, listener.getScreenCaptureListener());
|
||||
ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = listener.waitForScreenshot();
|
||||
assertNotNull(hardwareBuffer);
|
||||
|
||||
Bitmap screenshot = hardwareBuffer.asBitmap();
|
||||
assertNotNull(screenshot);
|
||||
|
||||
Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
|
||||
screenshot.recycle();
|
||||
|
||||
BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
|
||||
Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
|
||||
int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
|
||||
int pixelMatchSize = bounds.width() * bounds.height();
|
||||
boolean success = numMatchingPixels == pixelMatchSize;
|
||||
|
||||
if (!success) {
|
||||
SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
|
||||
}
|
||||
swBitmap.recycle();
|
||||
assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
|
||||
success);
|
||||
}
|
||||
|
||||
public static class ScreenshotActivity extends Activity {
|
||||
private static final long WAIT_TIMEOUT_S = 5;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
@@ -202,6 +130,7 @@ public class ScreenshotTests {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().getDecorView().setPointerIcon(
|
||||
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
|
||||
@@ -219,14 +148,88 @@ public class ScreenshotTests {
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
public Point getPositionBelowStatusBar() {
|
||||
Insets statusBarInsets = getWindow()
|
||||
.getDecorView()
|
||||
.getRootWindowInsets()
|
||||
.getInsets(statusBars() | displayCutout());
|
||||
public abstract static class PixelChecker {
|
||||
static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) {
|
||||
int numMatchingPixels = 0;
|
||||
for (int x = 0; x < bitmap.getWidth(); x++) {
|
||||
for (int y = 0; y < bitmap.getHeight(); y++) {
|
||||
int color = bitmap.getPixel(x, y);
|
||||
if (matchesColor(pixelColor, color)) {
|
||||
numMatchingPixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return numMatchingPixels;
|
||||
}
|
||||
|
||||
return new Point(statusBarInsets.left, statusBarInsets.top);
|
||||
static boolean matchesColor(PixelColor expectedColor, int color) {
|
||||
final float red = Color.red(color);
|
||||
final float green = Color.green(color);
|
||||
final float blue = Color.blue(color);
|
||||
final float alpha = Color.alpha(color);
|
||||
|
||||
return alpha <= expectedColor.mMaxAlpha
|
||||
&& alpha >= expectedColor.mMinAlpha
|
||||
&& red <= expectedColor.mMaxRed
|
||||
&& red >= expectedColor.mMinRed
|
||||
&& green <= expectedColor.mMaxGreen
|
||||
&& green >= expectedColor.mMinGreen
|
||||
&& blue <= expectedColor.mMaxBlue
|
||||
&& blue >= expectedColor.mMinBlue;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PixelColor {
|
||||
public static final int BLACK = 0xFF000000;
|
||||
public static final int RED = 0xFF0000FF;
|
||||
public static final int GREEN = 0xFF00FF00;
|
||||
public static final int BLUE = 0xFFFF0000;
|
||||
public static final int YELLOW = 0xFF00FFFF;
|
||||
public static final int MAGENTA = 0xFFFF00FF;
|
||||
public static final int WHITE = 0xFFFFFFFF;
|
||||
|
||||
public static final int TRANSPARENT_RED = 0x7F0000FF;
|
||||
public static final int TRANSPARENT_BLUE = 0x7FFF0000;
|
||||
public static final int TRANSPARENT = 0x00000000;
|
||||
|
||||
// Default to black
|
||||
public short mMinAlpha;
|
||||
public short mMaxAlpha;
|
||||
public short mMinRed;
|
||||
public short mMaxRed;
|
||||
public short mMinBlue;
|
||||
public short mMaxBlue;
|
||||
public short mMinGreen;
|
||||
public short mMaxGreen;
|
||||
|
||||
public PixelColor(int color) {
|
||||
short alpha = (short) ((color >> 24) & 0xFF);
|
||||
short blue = (short) ((color >> 16) & 0xFF);
|
||||
short green = (short) ((color >> 8) & 0xFF);
|
||||
short red = (short) (color & 0xFF);
|
||||
|
||||
mMinAlpha = (short) getMinValue(alpha);
|
||||
mMaxAlpha = (short) getMaxValue(alpha);
|
||||
mMinRed = (short) getMinValue(red);
|
||||
mMaxRed = (short) getMaxValue(red);
|
||||
mMinBlue = (short) getMinValue(blue);
|
||||
mMaxBlue = (short) getMaxValue(blue);
|
||||
mMinGreen = (short) getMinValue(green);
|
||||
mMaxGreen = (short) getMaxValue(green);
|
||||
}
|
||||
|
||||
public PixelColor() {
|
||||
this(BLACK);
|
||||
}
|
||||
|
||||
private int getMinValue(short color) {
|
||||
return Math.max(color - 4, 0);
|
||||
}
|
||||
|
||||
private int getMaxValue(short color) {
|
||||
return Math.min(color + 4, 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user