Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d63b77c6 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Creating HW bitmaps from RenderNode" into oc-dev

parents c8124cb9 1fedd916
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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 {
@@ -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);
}
+75 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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
// ----------------------------------------------------------------------------
@@ -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) {
+9 −0
Original line number Diff line number Diff line
@@ -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">
+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);
    }
}