diff options
author | 2016-09-01 09:44:09 -0700 | |
---|---|---|
committer | 2016-09-07 11:41:15 -0700 | |
commit | 9580146f5076aaa7c498f86bd3d724c00599f6f4 (patch) | |
tree | 33df26fef261fe9ce563b20438726e0094d3c583 | |
parent | 51c1b3466acfb77c14ee5332ff1ff2a273af4670 (diff) |
Add API to copy a window
Change-Id: I9bb5209010db6665be4b6f8db81a6fc1b7debc45
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | api/system-current.txt | 4 | ||||
-rw-r--r-- | api/test-current.txt | 4 | ||||
-rw-r--r-- | core/java/android/view/ThreadedRenderer.java | 13 | ||||
-rw-r--r-- | core/jni/android_view_ThreadedRenderer.cpp | 7 | ||||
-rw-r--r-- | graphics/java/android/view/PixelCopy.java | 139 | ||||
-rw-r--r-- | libs/hwui/Readback.cpp | 21 | ||||
-rw-r--r-- | libs/hwui/Readback.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 3 |
10 files changed, 190 insertions, 18 deletions
diff --git a/api/current.txt b/api/current.txt index 4c8e61abd6e7..1b923cefdfbb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42152,7 +42152,11 @@ package android.view { public final class PixelCopy { method public static void request(android.view.SurfaceView, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.SurfaceView, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); method public static void request(android.view.Surface, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Surface, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); field public static final int ERROR_DESTINATION_INVALID = 5; // 0x5 field public static final int ERROR_SOURCE_INVALID = 4; // 0x4 field public static final int ERROR_SOURCE_NO_DATA = 3; // 0x3 diff --git a/api/system-current.txt b/api/system-current.txt index f2049a92cf06..16955cabd647 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -45323,7 +45323,11 @@ package android.view { public final class PixelCopy { method public static void request(android.view.SurfaceView, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.SurfaceView, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); method public static void request(android.view.Surface, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Surface, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); field public static final int ERROR_DESTINATION_INVALID = 5; // 0x5 field public static final int ERROR_SOURCE_INVALID = 4; // 0x4 field public static final int ERROR_SOURCE_NO_DATA = 3; // 0x3 diff --git a/api/test-current.txt b/api/test-current.txt index a4753e4d48e7..5704dd6a8a41 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42235,7 +42235,11 @@ package android.view { public final class PixelCopy { method public static void request(android.view.SurfaceView, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.SurfaceView, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); method public static void request(android.view.Surface, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Surface, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); + method public static void request(android.view.Window, android.graphics.Rect, android.graphics.Bitmap, android.view.PixelCopy.OnPixelCopyFinishedListener, android.os.Handler); field public static final int ERROR_DESTINATION_INVALID = 5; // 0x5 field public static final int ERROR_SOURCE_INVALID = 4; // 0x4 field public static final int ERROR_SOURCE_NO_DATA = 3; // 0x3 diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 1034275c8702..ce390a239d2c 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -883,8 +883,14 @@ public final class ThreadedRenderer { nSerializeDisplayListTree(mNativeProxy); } - public static int copySurfaceInto(Surface surface, Bitmap bitmap) { - return nCopySurfaceInto(surface, bitmap); + public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) { + if (srcRect == null) { + // Empty rect means entire surface + return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap); + } else { + return nCopySurfaceInto(surface, srcRect.left, srcRect.top, + srcRect.right, srcRect.bottom, bitmap); + } } @Override @@ -1036,5 +1042,6 @@ public final class ThreadedRenderer { private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer); private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver); - private static native int nCopySurfaceInto(Surface surface, Bitmap bitmap); + private static native int nCopySurfaceInto(Surface surface, + int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap); } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 87a8ef835743..649cc1edf185 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -899,11 +899,12 @@ static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env, } static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, - jobject clazz, jobject jsurface, jobject jbitmap) { + jobject clazz, jobject jsurface, jint left, jint top, + jint right, jint bottom, jobject jbitmap) { SkBitmap bitmap; GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); sp<Surface> surface = android_view_Surface_getSurface(env, jsurface); - return RenderProxy::copySurfaceInto(surface, &bitmap); + return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap); } // ---------------------------------------------------------------------------- @@ -1001,7 +1002,7 @@ static const JNINativeMethod gMethods[] = { { "nRemoveFrameMetricsObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, - { "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)I", + { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I", (void*)android_view_ThreadedRenderer_copySurfaceInto }, }; diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java index 29bf963842da..a14609f3e924 100644 --- a/graphics/java/android/view/PixelCopy.java +++ b/graphics/java/android/view/PixelCopy.java @@ -18,8 +18,11 @@ package android.view; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Bitmap; +import android.graphics.Rect; import android.os.Handler; +import android.view.ViewTreeObserver.OnDrawListener; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -105,6 +108,32 @@ public final class PixelCopy { } /** + * Requests for the display content of a {@link SurfaceView} to be copied + * into a provided {@link Bitmap}. + * + * The contents of the source will be scaled to fit exactly inside the bitmap. + * The pixel format of the source buffer will be converted, as part of the copy, + * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer + * in the SurfaceView's Surface will be used as the source of the copy. + * + * @param source The source from which to copy + * @param srcRect The area of the source to copy from. If this is null + * the copy area will be the entire surface. The rect will be clamped to + * the bounds of the Surface. + * @param dest The destination of the copy. The source will be scaled to + * match the width, height, and format of this bitmap. + * @param listener Callback for when the pixel copy request completes + * @param listenerThread The callback will be invoked on this Handler when + * the copy is finished. + */ + public static void request(@NonNull SurfaceView source, @Nullable Rect srcRect, + @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, + @NonNull Handler listenerThread) { + request(source.getHolder().getSurface(), srcRect, + dest, listener, listenerThread); + } + + /** * Requests a copy of the pixels from a {@link Surface} to be copied into * a provided {@link Bitmap}. * @@ -122,12 +151,40 @@ public final class PixelCopy { */ public static void request(@NonNull Surface source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) { + request(source, null, dest, listener, listenerThread); + } + + /** + * Requests a copy of the pixels at the provided {@link Rect} from + * a {@link Surface} to be copied into a provided {@link Bitmap}. + * + * The contents of the source rect will be scaled to fit exactly inside the bitmap. + * The pixel format of the source buffer will be converted, as part of the copy, + * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer + * in the Surface will be used as the source of the copy. + * + * @param source The source from which to copy + * @param srcRect The area of the source to copy from. If this is null + * the copy area will be the entire surface. The rect will be clamped to + * the bounds of the Surface. + * @param dest The destination of the copy. The source will be scaled to + * match the width, height, and format of this bitmap. + * @param listener Callback for when the pixel copy request completes + * @param listenerThread The callback will be invoked on this Handler when + * the copy is finished. + */ + public static void request(@NonNull Surface source, @Nullable Rect srcRect, + @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, + @NonNull Handler listenerThread) { validateBitmapDest(dest); if (!source.isValid()) { throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false"); } + if (srcRect != null && srcRect.isEmpty()) { + throw new IllegalArgumentException("sourceRect is empty"); + } // TODO: Make this actually async and fast and cool and stuff - int result = ThreadedRenderer.copySurfaceInto(source, dest); + int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest); listenerThread.post(new Runnable() { @Override public void run() { @@ -136,6 +193,86 @@ public final class PixelCopy { }); } + /** + * Requests a copy of the pixels from a {@link Window} to be copied into + * a provided {@link Bitmap}. + * + * The contents of the source will be scaled to fit exactly inside the bitmap. + * The pixel format of the source buffer will be converted, as part of the copy, + * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer + * in the Window's Surface will be used as the source of the copy. + * + * Note: This is limited to being able to copy from Window's with a non-null + * DecorView. If {@link Window#peekDecorView()} is null this throws an + * {@link IllegalArgumentException}. It will similarly throw an exception + * if the DecorView has not yet acquired a backing surface. It is recommended + * that {@link OnDrawListener} is used to ensure that at least one draw + * has happened before trying to copy from the window, otherwise either + * an {@link IllegalArgumentException} will be thrown or an error will + * be returned to the {@link OnPixelCopyFinishedListener}. + * + * @param source The source from which to copy + * @param dest The destination of the copy. The source will be scaled to + * match the width, height, and format of this bitmap. + * @param listener Callback for when the pixel copy request completes + * @param listenerThread The callback will be invoked on this Handler when + * the copy is finished. + */ + public static void request(@NonNull Window source, @NonNull Bitmap dest, + @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) { + request(source, null, dest, listener, listenerThread); + } + + /** + * Requests a copy of the pixels at the provided {@link Rect} from + * a {@link Window} to be copied into a provided {@link Bitmap}. + * + * The contents of the source rect will be scaled to fit exactly inside the bitmap. + * The pixel format of the source buffer will be converted, as part of the copy, + * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer + * in the Window's Surface will be used as the source of the copy. + * + * Note: This is limited to being able to copy from Window's with a non-null + * DecorView. If {@link Window#peekDecorView()} is null this throws an + * {@link IllegalArgumentException}. It will similarly throw an exception + * if the DecorView has not yet acquired a backing surface. It is recommended + * that {@link OnDrawListener} is used to ensure that at least one draw + * has happened before trying to copy from the window, otherwise either + * an {@link IllegalArgumentException} will be thrown or an error will + * be returned to the {@link OnPixelCopyFinishedListener}. + * + * @param source The source from which to copy + * @param srcRect The area of the source to copy from. If this is null + * the copy area will be the entire surface. The rect will be clamped to + * the bounds of the Surface. + * @param dest The destination of the copy. The source will be scaled to + * match the width, height, and format of this bitmap. + * @param listener Callback for when the pixel copy request completes + * @param listenerThread The callback will be invoked on this Handler when + * the copy is finished. + */ + public static void request(@NonNull Window source, @Nullable Rect srcRect, + @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, + @NonNull Handler listenerThread) { + validateBitmapDest(dest); + if (source == null) { + throw new IllegalArgumentException("source is null"); + } + if (source.peekDecorView() == null) { + throw new IllegalArgumentException( + "Only able to copy windows with decor views"); + } + Surface surface = null; + if (source.peekDecorView().getViewRootImpl() != null) { + surface = source.peekDecorView().getViewRootImpl().mSurface; + } + if (surface == null || !surface.isValid()) { + throw new IllegalArgumentException( + "Window doesn't have a backing surface!"); + } + request(surface, srcRect, dest, listener, listenerThread); + } + private static void validateBitmapDest(Bitmap bitmap) { // TODO: Pre-check max texture dimens if we can if (bitmap == null) { diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 65a8ebba2ae7..ddca122788d1 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -32,7 +32,8 @@ namespace android { namespace uirenderer { static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, - Texture& sourceTexture, Matrix4& texTransform, SkBitmap* bitmap) { + Texture& sourceTexture, Matrix4& texTransform, const Rect& srcRect, + SkBitmap* bitmap) { int destWidth = bitmap->width(); int destHeight = bitmap->height(); if (destWidth > caches.maxTextureSize @@ -100,11 +101,19 @@ static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, renderState.blend().syncEnabled(); renderState.stencil().disable(); + Matrix4 croppedTexTransform(texTransform); + if (!srcRect.isEmpty()) { + croppedTexTransform.loadTranslate(srcRect.left / sourceTexture.width(), + srcRect.top / sourceTexture.height(), 0); + croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), + srcRect.getHeight() / sourceTexture.height(), 1); + croppedTexTransform.multiply(texTransform); + } Glop glop; GlopBuilder(renderState, caches, &glop) .setRoundRectClipState(nullptr) .setMeshTexturedUnitQuad(nullptr) - .setFillExternalTexture(sourceTexture, texTransform) + .setFillExternalTexture(sourceTexture, croppedTexTransform) .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) .build(); @@ -126,7 +135,8 @@ static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, } CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, SkBitmap* bitmap) { + Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { + ATRACE_CALL(); renderThread.eglManager().initialize(); Caches& caches = Caches::getInstance(); @@ -190,7 +200,7 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */); CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(), - sourceTexture, texTransform, bitmap); + sourceTexture, texTransform, srcRect, bitmap); sourceTexture.deleteTexture(); // All we're flushing & finishing is the deletion of the texture since // copyTextureInto already did a major flush & finish as an implicit @@ -202,8 +212,9 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread, Layer& layer, SkBitmap* bitmap) { + ATRACE_CALL(); return copyTextureInto(Caches::getInstance(), renderThread.renderState(), - layer.getTexture(), layer.getTexTransform(), bitmap); + layer.getTexture(), layer.getTexTransform(), Rect(), bitmap); } } // namespace uirenderer diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index bd73734949f3..55c943c0ebee 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -17,6 +17,7 @@ #pragma once #include "renderthread/RenderThread.h" +#include "Rect.h" #include <SkBitmap.h> #include <gui/Surface.h> @@ -42,7 +43,7 @@ public: * Copies the surface's most recently queued buffer into the provided bitmap. */ static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, SkBitmap* bitmap); + Surface& surface, const Rect& srcRect, SkBitmap* bitmap); /** * Copies the TextureLayer's texture content (thus, the currently rendering buffer) into the diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 3edd42372653..dcbc980763ec 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -617,17 +617,19 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) { post(task); } -CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread, - Surface* surface, SkBitmap* bitmap) { +CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread, + Surface* surface, Rect srcRect, SkBitmap* bitmap) { return (void*) Readback::copySurfaceInto(*args->thread, - *args->surface, args->bitmap); + *args->surface, args->srcRect, args->bitmap); } -int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) { +int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, + int right, int bottom, SkBitmap* bitmap) { SETUP_TASK(copySurfaceInto); args->bitmap = bitmap; args->surface = surface.get(); args->thread = &RenderThread::getInstance(); + args->srcRect.set(left, top, right, bottom); return static_cast<int>( reinterpret_cast<intptr_t>( staticPostAndWait(task) )); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d8129705b940..d4aaea6d7280 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -127,7 +127,8 @@ public: ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API long getDroppedFrameReportCount(); - ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap); + ANDROID_API static int copySurfaceInto(sp<Surface>& surface, + int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(const SkBitmap& bitmap); private: |