Loading core/java/android/view/ThreadedRenderer.java +11 −0 Original line number Diff line number Diff line Loading @@ -872,6 +872,15 @@ public final class ThreadedRenderer { } } /** * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and * not the RenderNode from a View. **/ public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) { return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height); } @Override protected void finalize() throws Throwable { try { Loading Loading @@ -1015,4 +1024,6 @@ public final class ThreadedRenderer { private static native int nCopySurfaceInto(Surface surface, int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap); private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height); } core/jni/android_view_ThreadedRenderer.cpp +75 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,10 @@ #include <GraphicsJNI.h> #include <ScopedPrimitiveArray.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueue.h> #include <gui/Surface.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <private/EGL/cache.h> Loading Loading @@ -851,6 +855,75 @@ static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap); } class ContextFactory : public IContextFactory { public: virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { return new AnimationContext(clock); } }; static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); if (jwidth <= 0 || jheight <= 0) { ALOGW("Invalid width %d or height %d", jwidth, jheight); return nullptr; } uint32_t width = jwidth; uint32_t height = jheight; // Create a Surface wired up to a BufferItemConsumer sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> rawConsumer; BufferQueue::createBufferQueue(&producer, &rawConsumer); rawConsumer->setMaxBufferCount(1); sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer, GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER); consumer->setDefaultBufferSize(width, height); sp<Surface> surface = new Surface(producer); // Render into the surface { ContextFactory factory; RenderProxy proxy{false, renderNode, &factory}; proxy.loadSystemProperties(); proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); proxy.initialize(surface); // Shadows can't be used via this interface, so just set the light source // to all 0s. proxy.setup(0, 0, 0); proxy.setLightCenter((Vector3){0, 0, 0}); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy.frameInfo()) .setVsync(vsync, vsync) .addFlag(FrameInfoFlags::SurfaceCanvas); proxy.syncAndDrawFrame(); } // Yank out the GraphicBuffer BufferItem bufferItem; status_t err; if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) { ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err)); return nullptr; } sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer; // We don't really care if this fails or not since we're just going to destroy this anyway consumer->releaseBuffer(bufferItem); if (!buffer.get()) { ALOGW("GraphicBuffer is null?"); return nullptr; } if (buffer->getWidth() != width || buffer->getHeight() != height) { ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d", buffer->getWidth(), buffer->getHeight(), width, height); // Continue I guess? } sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable); } // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- Loading Loading @@ -947,6 +1020,8 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I", (void*)android_view_ThreadedRenderer_copySurfaceInto }, { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { Loading tests/HwAccelerationTest/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,15 @@ </intent-filter> </activity> <activity android:name="DrawIntoHwBitmapActivity" android:label="Bitmaps/DrawIntoHwBitmap"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.android.test.hwui.TEST" /> </intent-filter> </activity> <activity android:name="PathOffsetActivity" android:label="Path/Offset"> Loading tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER; import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER; import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_RARELY; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.os.Bundle; import android.view.DisplayListCanvas; import android.view.RenderNode; import android.view.Surface; import android.view.ThreadedRenderer; import android.widget.ImageView; public class DrawIntoHwBitmapActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ImageView view = new ImageView(this); view.setScaleType(ImageView.ScaleType.CENTER_INSIDE); setContentView(view); view.setImageBitmap(createBitmap()); } Bitmap createBitmap() { RenderNode node = RenderNode.create("HwuiCanvas", null); node.setLeftTopRightBottom(0, 0, 500, 500); node.setClipToBounds(false); DisplayListCanvas canvas = node.start(500, 500); Paint p = new Paint(); p.setColor(Color.BLACK); p.setTextSize(20 * getResources().getDisplayMetrics().density); canvas.drawColor(0xFF2196F3); p.setColor(0xFFBBDEFB); canvas.drawRect(0, 0, 500, 100, p); p.setColor(Color.BLACK); canvas.drawText("Hello, World!", 0, 90, p); node.end(canvas); return ThreadedRenderer.createHardwareBitmap(node, 500, 500); } } Loading
core/java/android/view/ThreadedRenderer.java +11 −0 Original line number Diff line number Diff line Loading @@ -872,6 +872,15 @@ public final class ThreadedRenderer { } } /** * Creates a {@link android.graphics.Bitmap.Config#HARDWARE} bitmap from the given * RenderNode. Note that the RenderNode should be created as a root node (so x/y of 0,0), and * not the RenderNode from a View. **/ public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) { return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height); } @Override protected void finalize() throws Throwable { try { Loading Loading @@ -1015,4 +1024,6 @@ public final class ThreadedRenderer { private static native int nCopySurfaceInto(Surface surface, int srcLeft, int srcTop, int srcRight, int srcBottom, Bitmap bitmap); private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height); }
core/jni/android_view_ThreadedRenderer.cpp +75 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,10 @@ #include <GraphicsJNI.h> #include <ScopedPrimitiveArray.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueue.h> #include <gui/Surface.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <private/EGL/cache.h> Loading Loading @@ -851,6 +855,75 @@ static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap); } class ContextFactory : public IContextFactory { public: virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { return new AnimationContext(clock); } }; static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(JNIEnv* env, jobject clazz, jlong renderNodePtr, jint jwidth, jint jheight) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); if (jwidth <= 0 || jheight <= 0) { ALOGW("Invalid width %d or height %d", jwidth, jheight); return nullptr; } uint32_t width = jwidth; uint32_t height = jheight; // Create a Surface wired up to a BufferItemConsumer sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> rawConsumer; BufferQueue::createBufferQueue(&producer, &rawConsumer); rawConsumer->setMaxBufferCount(1); sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer, GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER); consumer->setDefaultBufferSize(width, height); sp<Surface> surface = new Surface(producer); // Render into the surface { ContextFactory factory; RenderProxy proxy{false, renderNode, &factory}; proxy.loadSystemProperties(); proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer); proxy.initialize(surface); // Shadows can't be used via this interface, so just set the light source // to all 0s. proxy.setup(0, 0, 0); proxy.setLightCenter((Vector3){0, 0, 0}); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy.frameInfo()) .setVsync(vsync, vsync) .addFlag(FrameInfoFlags::SurfaceCanvas); proxy.syncAndDrawFrame(); } // Yank out the GraphicBuffer BufferItem bufferItem; status_t err; if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) { ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err)); return nullptr; } sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer; // We don't really care if this fails or not since we're just going to destroy this anyway consumer->releaseBuffer(bufferItem); if (!buffer.get()) { ALOGW("GraphicBuffer is null?"); return nullptr; } if (buffer->getWidth() != width || buffer->getHeight() != height) { ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d", buffer->getWidth(), buffer->getHeight(), width, height); // Continue I guess? } sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer); return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable); } // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- Loading Loading @@ -947,6 +1020,8 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I", (void*)android_view_ThreadedRenderer_copySurfaceInto }, { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { Loading
tests/HwAccelerationTest/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,15 @@ </intent-filter> </activity> <activity android:name="DrawIntoHwBitmapActivity" android:label="Bitmaps/DrawIntoHwBitmap"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.android.test.hwui.TEST" /> </intent-filter> </activity> <activity android:name="PathOffsetActivity" android:label="Path/Offset"> Loading
tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java 0 → 100644 +65 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER; import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER; import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_RARELY; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.os.Bundle; import android.view.DisplayListCanvas; import android.view.RenderNode; import android.view.Surface; import android.view.ThreadedRenderer; import android.widget.ImageView; public class DrawIntoHwBitmapActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ImageView view = new ImageView(this); view.setScaleType(ImageView.ScaleType.CENTER_INSIDE); setContentView(view); view.setImageBitmap(createBitmap()); } Bitmap createBitmap() { RenderNode node = RenderNode.create("HwuiCanvas", null); node.setLeftTopRightBottom(0, 0, 500, 500); node.setClipToBounds(false); DisplayListCanvas canvas = node.start(500, 500); Paint p = new Paint(); p.setColor(Color.BLACK); p.setTextSize(20 * getResources().getDisplayMetrics().density); canvas.drawColor(0xFF2196F3); p.setColor(0xFFBBDEFB); canvas.drawRect(0, 0, 500, 100, p); p.setColor(Color.BLACK); canvas.drawText("Hello, World!", 0, 90, p); node.end(canvas); return ThreadedRenderer.createHardwareBitmap(node, 500, 500); } }