Merge "Framework-side of SurfaceView#getBitmap" into nyc-dev
am: d2eec0e
* commit 'd2eec0efb2e47b23b2a12ab2967e422f0be49094':
Framework-side of SurfaceView#getBitmap
Change-Id: I4da75d89a536c11dbaa9ee1feba8e5838fbb5a41
This commit is contained in:
@@ -893,6 +893,10 @@ public final class ThreadedRenderer {
|
|||||||
nSerializeDisplayListTree(mNativeProxy);
|
nSerializeDisplayListTree(mNativeProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
|
||||||
|
return nCopySurfaceInto(surface, bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
try {
|
try {
|
||||||
@@ -1029,4 +1033,6 @@ public final class ThreadedRenderer {
|
|||||||
|
|
||||||
private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
|
private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
|
||||||
private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
|
private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
|
||||||
|
|
||||||
|
private static native boolean nCopySurfaceInto(Surface surface, Bitmap bitmap);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -662,6 +662,14 @@ static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
|
|||||||
proxy->setContentDrawBounds(left, top, right, bottom);
|
proxy->setContentDrawBounds(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jboolean android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
|
||||||
|
jobject clazz, jobject jsurface, jobject jbitmap) {
|
||||||
|
SkBitmap bitmap;
|
||||||
|
GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
|
||||||
|
sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
|
||||||
|
return RenderProxy::copySurfaceInto(surface, &bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// FrameMetricsObserver
|
// FrameMetricsObserver
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -767,6 +775,8 @@ static const JNINativeMethod gMethods[] = {
|
|||||||
{ "nRemoveFrameMetricsObserver",
|
{ "nRemoveFrameMetricsObserver",
|
||||||
"(JJ)V",
|
"(JJ)V",
|
||||||
(void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
|
(void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
|
||||||
|
{ "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)Z",
|
||||||
|
(void*)android_view_ThreadedRenderer_copySurfaceInto },
|
||||||
};
|
};
|
||||||
|
|
||||||
int register_android_view_ThreadedRenderer(JNIEnv* env) {
|
int register_android_view_ThreadedRenderer(JNIEnv* env) {
|
||||||
|
|||||||
104
graphics/java/android/graphics/PixelCopy.java
Normal file
104
graphics/java/android/graphics/PixelCopy.java
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package android.graphics;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.ThreadedRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a mechanisms to issue pixel copy requests to allow for copy
|
||||||
|
* operations from {@link Surface} to {@link Bitmap}
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class PixelCopy {
|
||||||
|
/**
|
||||||
|
* Contains the result of a pixel copy request
|
||||||
|
*/
|
||||||
|
public static final class Response {
|
||||||
|
/**
|
||||||
|
* Indicates whether or not the copy request completed successfully.
|
||||||
|
* If this is true, then {@link #bitmap} contains the result of the copy.
|
||||||
|
* If this is false, {@link #bitmap} is unmodified from the originally
|
||||||
|
* passed destination.
|
||||||
|
*
|
||||||
|
* For example a request might fail if the source is protected content
|
||||||
|
* so copies are not allowed. Similarly if the source has nothing to
|
||||||
|
* copy from, because either no frames have been produced yet or because
|
||||||
|
* it has already been destroyed, then this will be false.
|
||||||
|
*/
|
||||||
|
public boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output bitmap. This is always the same object that was passed
|
||||||
|
* to request() as the 'dest' bitmap. If {@link #success} is true this
|
||||||
|
* contains a copy of the pixels of the source object. If {@link #success}
|
||||||
|
* is false then this is unmodified.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Bitmap bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnPixelCopyFinished {
|
||||||
|
/**
|
||||||
|
* Callback for when a pixel copy request has completed. This will be called
|
||||||
|
* regardless of whether the copy succeeded or failed.
|
||||||
|
*
|
||||||
|
* @param response Contains the result of the copy request which includes
|
||||||
|
* whether or not the copy was successful.
|
||||||
|
*/
|
||||||
|
void onPixelCopyFinished(PixelCopy.Response response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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, @NonNull Bitmap dest,
|
||||||
|
@NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
|
||||||
|
request(source.getHolder().getSurface(), dest, listener, listenerThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests a copy of the pixels from a {@link Surface} 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 Surface will be used as the source of the copy.
|
||||||
|
*
|
||||||
|
* @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 Surface source, @NonNull Bitmap dest,
|
||||||
|
@NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
|
||||||
|
// TODO: Make this actually async and fast and cool and stuff
|
||||||
|
final PixelCopy.Response response = new PixelCopy.Response();
|
||||||
|
response.success = ThreadedRenderer.copySurfaceInto(source, dest);
|
||||||
|
response.bitmap = dest;
|
||||||
|
listenerThread.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onPixelCopyFinished(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,6 +84,7 @@ hwui_src_files := \
|
|||||||
Properties.cpp \
|
Properties.cpp \
|
||||||
PropertyValuesHolder.cpp \
|
PropertyValuesHolder.cpp \
|
||||||
PropertyValuesAnimatorSet.cpp \
|
PropertyValuesAnimatorSet.cpp \
|
||||||
|
Readback.cpp \
|
||||||
RenderBufferCache.cpp \
|
RenderBufferCache.cpp \
|
||||||
RenderNode.cpp \
|
RenderNode.cpp \
|
||||||
RenderProperties.cpp \
|
RenderProperties.cpp \
|
||||||
|
|||||||
159
libs/hwui/Readback.cpp
Normal file
159
libs/hwui/Readback.cpp
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Readback.h"
|
||||||
|
|
||||||
|
#include "Caches.h"
|
||||||
|
#include "Image.h"
|
||||||
|
#include "GlopBuilder.h"
|
||||||
|
#include "renderstate/RenderState.h"
|
||||||
|
#include "renderthread/EglManager.h"
|
||||||
|
#include "utils/GLUtils.h"
|
||||||
|
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <ui/Fence.h>
|
||||||
|
#include <ui/GraphicBuffer.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace uirenderer {
|
||||||
|
|
||||||
|
bool Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
|
||||||
|
Surface& surface, SkBitmap* bitmap) {
|
||||||
|
// TODO: Clean this up and unify it with LayerRenderer::copyLayer,
|
||||||
|
// of which most of this is copied from.
|
||||||
|
renderThread.eglManager().initialize();
|
||||||
|
|
||||||
|
Caches& caches = Caches::getInstance();
|
||||||
|
RenderState& renderState = renderThread.renderState();
|
||||||
|
int destWidth = bitmap->width();
|
||||||
|
int destHeight = bitmap->height();
|
||||||
|
if (destWidth > caches.maxTextureSize
|
||||||
|
|| destHeight > caches.maxTextureSize) {
|
||||||
|
ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
|
||||||
|
destWidth, destHeight, caches.maxTextureSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GLuint fbo = renderState.createFramebuffer();
|
||||||
|
if (!fbo) {
|
||||||
|
ALOGW("Could not obtain an FBO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkAutoLockPixels alp(*bitmap);
|
||||||
|
|
||||||
|
GLuint texture;
|
||||||
|
|
||||||
|
GLenum format;
|
||||||
|
GLenum type;
|
||||||
|
|
||||||
|
switch (bitmap->colorType()) {
|
||||||
|
case kAlpha_8_SkColorType:
|
||||||
|
format = GL_ALPHA;
|
||||||
|
type = GL_UNSIGNED_BYTE;
|
||||||
|
break;
|
||||||
|
case kRGB_565_SkColorType:
|
||||||
|
format = GL_RGB;
|
||||||
|
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||||
|
break;
|
||||||
|
case kARGB_4444_SkColorType:
|
||||||
|
format = GL_RGBA;
|
||||||
|
type = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||||
|
break;
|
||||||
|
case kN32_SkColorType:
|
||||||
|
default:
|
||||||
|
format = GL_RGBA;
|
||||||
|
type = GL_UNSIGNED_BYTE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderState.bindFramebuffer(fbo);
|
||||||
|
|
||||||
|
// TODO: Use layerPool or something to get this maybe? But since we
|
||||||
|
// need explicit format control we can't currently.
|
||||||
|
|
||||||
|
// Setup the rendertarget
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
caches.textureState().activateTexture(0);
|
||||||
|
caches.textureState().bindTexture(texture);
|
||||||
|
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
|
||||||
|
0, format, type, nullptr);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_TEXTURE_2D, texture, 0);
|
||||||
|
|
||||||
|
// Setup the source
|
||||||
|
sp<GraphicBuffer> sourceBuffer;
|
||||||
|
sp<Fence> sourceFence;
|
||||||
|
// FIXME: Waiting on an API from libgui for this
|
||||||
|
// surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
|
||||||
|
if (!sourceBuffer.get()) {
|
||||||
|
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int err = sourceFence->wait(500 /* ms */);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Image sourceImage(sourceBuffer);
|
||||||
|
if (!sourceImage.getTexture()) {
|
||||||
|
ALOGW("Failed to make an EGLImage from the GraphicBuffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Texture sourceTexture(caches);
|
||||||
|
sourceTexture.wrap(sourceImage.getTexture(),
|
||||||
|
sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Draw & readback
|
||||||
|
renderState.setViewport(destWidth, destHeight);
|
||||||
|
renderState.scissor().setEnabled(false);
|
||||||
|
renderState.blend().syncEnabled();
|
||||||
|
renderState.stencil().disable();
|
||||||
|
|
||||||
|
Rect destRect(destWidth, destHeight);
|
||||||
|
Glop glop;
|
||||||
|
GlopBuilder(renderState, caches, &glop)
|
||||||
|
.setRoundRectClipState(nullptr)
|
||||||
|
.setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
|
||||||
|
.setFillLayer(sourceTexture, nullptr, 1.0f, SkXfermode::kSrc_Mode,
|
||||||
|
Blend::ModeOrderSwap::NoSwap)
|
||||||
|
.setTransform(Matrix4::identity(), TransformFlags::None)
|
||||||
|
.setModelViewMapUnitToRect(destRect)
|
||||||
|
.build();
|
||||||
|
Matrix4 ortho;
|
||||||
|
ortho.loadOrtho(destWidth, destHeight);
|
||||||
|
renderState.render(glop, ortho);
|
||||||
|
|
||||||
|
glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
|
||||||
|
type, bitmap->getPixels());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
caches.textureState().deleteTexture(texture);
|
||||||
|
renderState.deleteFramebuffer(fbo);
|
||||||
|
|
||||||
|
GL_CHECKPOINT(MODERATE);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace uirenderer
|
||||||
|
} // namespace android
|
||||||
34
libs/hwui/Readback.h
Normal file
34
libs/hwui/Readback.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "renderthread/RenderThread.h"
|
||||||
|
|
||||||
|
#include <SkBitmap.h>
|
||||||
|
#include <gui/Surface.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace uirenderer {
|
||||||
|
|
||||||
|
class Readback {
|
||||||
|
public:
|
||||||
|
static bool copySurfaceInto(renderthread::RenderThread& renderThread,
|
||||||
|
Surface& surface, SkBitmap* bitmap);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace uirenderer
|
||||||
|
} // namespace android
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "DeferredLayerUpdater.h"
|
#include "DeferredLayerUpdater.h"
|
||||||
#include "DisplayList.h"
|
#include "DisplayList.h"
|
||||||
#include "LayerRenderer.h"
|
#include "LayerRenderer.h"
|
||||||
|
#include "Readback.h"
|
||||||
#include "Rect.h"
|
#include "Rect.h"
|
||||||
#include "renderthread/CanvasContext.h"
|
#include "renderthread/CanvasContext.h"
|
||||||
#include "renderthread/RenderTask.h"
|
#include "renderthread/RenderTask.h"
|
||||||
@@ -604,6 +605,20 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
|
|||||||
post(task);
|
post(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
|
||||||
|
Surface* surface, SkBitmap* bitmap) {
|
||||||
|
return (void*) Readback::copySurfaceInto(*args->thread,
|
||||||
|
*args->surface, args->bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
|
||||||
|
SETUP_TASK(copySurfaceInto);
|
||||||
|
args->bitmap = bitmap;
|
||||||
|
args->surface = surface.get();
|
||||||
|
args->thread = &RenderThread::getInstance();
|
||||||
|
return (bool) staticPostAndWait(task);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderProxy::post(RenderTask* task) {
|
void RenderProxy::post(RenderTask* task) {
|
||||||
mRenderThread.queue(task);
|
mRenderThread.queue(task);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ public:
|
|||||||
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
|
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
|
||||||
ANDROID_API long getDroppedFrameReportCount();
|
ANDROID_API long getDroppedFrameReportCount();
|
||||||
|
|
||||||
|
ANDROID_API static bool copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderThread& mRenderThread;
|
RenderThread& mRenderThread;
|
||||||
CanvasContext* mContext;
|
CanvasContext* mContext;
|
||||||
|
|||||||
@@ -373,6 +373,15 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="GetBitmapSurfaceViewActivity"
|
||||||
|
android:label="SurfaceView/GetBitmap with Camera source">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="com.android.test.hwui.TEST" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="GLTextureViewActivity"
|
android:name="GLTextureViewActivity"
|
||||||
android:label="TextureView/OpenGL">
|
android:label="TextureView/OpenGL">
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 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 com.android.test.hwui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.PixelCopy;
|
||||||
|
import android.graphics.PixelCopy.OnPixelCopyFinished;
|
||||||
|
import android.graphics.PixelCopy.Response;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class GetBitmapSurfaceViewActivity extends Activity implements SurfaceHolder.Callback {
|
||||||
|
private Camera mCamera;
|
||||||
|
private SurfaceView mSurfaceView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
FrameLayout content = new FrameLayout(this);
|
||||||
|
|
||||||
|
mSurfaceView = new SurfaceView(this);
|
||||||
|
mSurfaceView.getHolder().addCallback(this);
|
||||||
|
|
||||||
|
Button button = new Button(this);
|
||||||
|
button.setText("Copy bitmap to /sdcard/surfaceview.png");
|
||||||
|
button.setOnClickListener((View v) -> {
|
||||||
|
Bitmap b = Bitmap.createBitmap(
|
||||||
|
mSurfaceView.getWidth(),
|
||||||
|
mSurfaceView.getHeight(),
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
PixelCopy.request(mSurfaceView, b,
|
||||||
|
mOnCopyFinished, mSurfaceView.getHandler());
|
||||||
|
});
|
||||||
|
|
||||||
|
content.addView(mSurfaceView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
|
||||||
|
content.addView(button, new FrameLayout.LayoutParams(
|
||||||
|
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
|
||||||
|
setContentView(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
|
||||||
|
@Override
|
||||||
|
public void onPixelCopyFinished(Response response) {
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.makeText(GetBitmapSurfaceViewActivity.this,
|
||||||
|
"Failed to copy", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
try (FileOutputStream out = new FileOutputStream(
|
||||||
|
Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
|
||||||
|
response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
mCamera = Camera.open();
|
||||||
|
|
||||||
|
try {
|
||||||
|
mCamera.setPreviewSurface(holder.getSurface());
|
||||||
|
} catch (IOException t) {
|
||||||
|
android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
mCamera.startPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
mCamera.stopPreview();
|
||||||
|
mCamera.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,18 +17,29 @@
|
|||||||
package com.android.test.hwui;
|
package com.android.test.hwui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelCopy;
|
||||||
|
import android.graphics.PixelCopy.OnPixelCopyFinished;
|
||||||
|
import android.graphics.PixelCopy.Response;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Gravity;
|
import android.os.Environment;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceHolder.Callback;
|
import android.view.SurfaceHolder.Callback;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
@SuppressWarnings({"UnusedDeclaration"})
|
|
||||||
public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
|
public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
|
||||||
private SurfaceView mSurfaceView;
|
private SurfaceView mSurfaceView;
|
||||||
private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
|
private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
|
||||||
@@ -42,13 +53,49 @@ public class HardwareCanvasSurfaceViewActivity extends Activity implements Callb
|
|||||||
mSurfaceView = new SurfaceView(this);
|
mSurfaceView = new SurfaceView(this);
|
||||||
mSurfaceView.getHolder().addCallback(this);
|
mSurfaceView.getHolder().addCallback(this);
|
||||||
|
|
||||||
content.addView(mSurfaceView, new FrameLayout.LayoutParams(
|
Button button = new Button(this);
|
||||||
|
button.setText("Copy bitmap to /sdcard/surfaceview.png");
|
||||||
|
button.setOnClickListener((View v) -> {
|
||||||
|
Bitmap b = Bitmap.createBitmap(
|
||||||
|
mSurfaceView.getWidth(),
|
||||||
|
mSurfaceView.getHeight(),
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
PixelCopy.request(mSurfaceView, b,
|
||||||
|
mOnCopyFinished, mSurfaceView.getHandler());
|
||||||
|
});
|
||||||
|
|
||||||
|
LinearLayout layout = new LinearLayout(this);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
layout.addView(mSurfaceView, LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT);
|
||||||
|
|
||||||
|
content.addView(layout, new FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
FrameLayout.LayoutParams.MATCH_PARENT));
|
||||||
Gravity.CENTER));
|
|
||||||
setContentView(content);
|
setContentView(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
|
||||||
|
@Override
|
||||||
|
public void onPixelCopyFinished(Response response) {
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.makeText(HardwareCanvasSurfaceViewActivity.this,
|
||||||
|
"Failed to copy", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
try (FileOutputStream out = new FileOutputStream(
|
||||||
|
Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
|
||||||
|
response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(SurfaceHolder holder) {
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
mThread = new RenderingThread(holder.getSurface());
|
mThread = new RenderingThread(holder.getSurface());
|
||||||
|
|||||||
Reference in New Issue
Block a user