static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong regionPtr, jlong paintPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    if (paint->getStyle() != Paint::kFill_Style ||
            (paint->isAntiAlias() && !renderer->isCurrentTransformSimple())) {
        SkRegion::Iterator it(*region);
        while (!it.done()) {
            const SkIRect& r = it.rect();
            renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
            it.next();
        }
    } else {
        int count = 0;
        Vector<float> rects;
        SkRegion::Iterator it(*region);
        while (!it.done()) {
            const SkIRect& r = it.rect();
            rects.push(r.fLeft);
            rects.push(r.fTop);
            rects.push(r.fRight);
            rects.push(r.fBottom);
            count += 4;
            it.next();
        }
        renderer->drawRects(rects.array(), count, paint);
    }
}
static void android_view_GLES20Canvas_drawArc(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
        jfloat startAngle, jfloat sweepAngle, jboolean useCenter, jlong paintPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
}
static jboolean android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong regionPtr, jint op) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
    const bool result = renderer->clipRegion(region, static_cast<SkRegion::Op>(op));
    return result ? JNI_TRUE : JNI_FALSE;
}
static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong pathPtr, jlong paintPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawPath(path, paint);
}
static jint android_view_GLES20Canvas_saveLayerAlphaClip(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jint alpha, jint saveFlags) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
    return renderer->saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
            alpha, saveFlags);
}
static jint android_view_GLES20Canvas_saveLayer(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
        jlong paintPtr, jint saveFlags) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
}
static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong functorPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
    android::uirenderer::Rect dirty;
    return renderer->callDrawGLFunction(functor, dirty);
}
static void android_view_GLES20Canvas_drawOval(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
        jlong paintPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawOval(left, top, right, bottom, paint);
}
static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jintArray colors, jint offset, jint stride,
        jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
    // correct the alphaType to kOpaque_SkAlphaType.
    const SkImageInfo info = SkImageInfo::Make(width, height,
                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
                               kPremul_SkAlphaType);
    SkBitmap* bitmap = new SkBitmap;
    if (!bitmap->allocPixels(info)) {
        delete bitmap;
        return;
    }

    if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap)) {
        delete bitmap;
        return;
    }

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);

    // apply transform directly to canvas, so it affects shaders correctly
    renderer->save(SkCanvas::kMatrix_SaveFlag);
    renderer->translate(left, top);
    renderer->drawBitmapData(bitmap, paint);
    renderer->restore();

    // Note - bitmap isn't deleted as DisplayListRenderer owns it now
}
static jboolean android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
        jint op) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    const bool result = renderer->clipRect(left, top, right, bottom,
                                           static_cast<SkRegion::Op>(op));
    return result ? JNI_TRUE : JNI_FALSE;
}
static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    jfloat* storage = env->GetFloatArrayElements(points, NULL);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawLines(storage + offset, count, paint);
    env->ReleaseFloatArrayElements(points, storage, 0);
}
static void android_view_GLES20Canvas_drawCircleProps(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
    CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
    CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
    renderer->drawCircle(xProp, yProp, radiusProp, paintProp);
}
static jboolean android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jobject rect) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());

    env->CallVoidMethod(rect, gRectClassInfo.set,
            int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));

    return !bounds.isEmpty() ? JNI_TRUE : JNI_FALSE;
}
static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jlong patchPtr,
        float left, float top, float right, float bottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
}
static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr,
        float srcLeft, float srcTop, float srcRight, float srcBottom,
        float dstLeft, float dstTop, float dstRight, float dstBottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
            dstLeft, dstTop, dstRight, dstBottom, paint);
}
static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer, jlong patchPtr,
        float left, float top, float right, float bottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
}
static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jfloat left, jfloat top, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);

    // apply transform directly to canvas, so it affects shaders correctly
    renderer->save(SkCanvas::kMatrix_SaveFlag);
    renderer->translate(left, top);
    renderer->drawBitmap(bitmap, paint);
    renderer->restore();
}
static jint android_view_GLES20Canvas_drawRenderNode(JNIEnv* env,
        jobject clazz, jlong rendererPtr, jlong renderNodePtr,
        jobject dirty, jint flags) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
    android::uirenderer::Rect bounds;
    status_t status = renderer->drawRenderNode(renderNode, bounds, flags);
    if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
        env->CallVoidMethod(dirty, gRectClassInfo.set,
                int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
    }
    return status;
}
static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        float srcLeft, float srcTop, float srcRight, float srcBottom,
        float dstLeft, float dstTop, float dstRight, float dstBottom, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
            dstLeft, dstTop, dstRight, dstBottom, paint);
}
static void android_view_GLES20Canvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
        jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
    CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
    CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr);
    CanvasPropertyPrimitive* bottomProp = reinterpret_cast<CanvasPropertyPrimitive*>(bottomPropPtr);
    CanvasPropertyPrimitive* rxProp = reinterpret_cast<CanvasPropertyPrimitive*>(rxPropPtr);
    CanvasPropertyPrimitive* ryProp = reinterpret_cast<CanvasPropertyPrimitive*>(ryPropPtr);
    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
    renderer->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
}
sp<RenderNode> createCard(int x, int y, int width, int height) {
    sp<RenderNode> node = new RenderNode();
    node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
    node->mutateStagingProperties().setElevation(dp(16));
    node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
    node->mutateStagingProperties().mutableOutline().setShouldClip(true);
    node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);

    DisplayListRenderer* renderer = startRecording(node.get());
    renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
    endRecording(renderer, node.get());

    return node;
}
static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jint meshWidth, jint meshHeight,
        jfloatArray vertices, jint offset, jintArray colors, jint colorOffset, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);

    jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
    jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);
    renderer->drawBitmapMesh(bitmap, meshWidth, meshHeight, verticesArray, colorsArray, paint);

    if (vertices) env->ReleaseFloatArrayElements(vertices, verticesArray, 0);
    if (colors) env->ReleaseIntArrayElements(colors, colorsArray, 0);
}
static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong bitmapPtr, jbyteArray buffer,
        jfloat left, jfloat top, jlong paintPtr) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    // This object allows the renderer to allocate a global JNI ref to the buffer object.
    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);

    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    Paint* paint = reinterpret_cast<Paint*>(paintPtr);

    // apply transform directly to canvas, so it affects shaders correctly
    renderer->save(SkCanvas::kMatrix_SaveFlag);
    renderer->translate(left, top);
    renderer->drawBitmap(bitmap, paint);
    renderer->restore();
}
static void android_view_GLES20Canvas_setupPaintFilter(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jint clearBits, jint setBits) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    renderer->setupPaintFilter(clearBits, setBits);
}
int main() {
    createTestEnvironment();

    // create the native surface
    const int width = gDisplay.w;
    const int height = gDisplay.h;
    sp<SurfaceControl> control = createWindow(width, height);
    sp<Surface> surface = control->getSurface();

    RenderNode* rootNode = new RenderNode();
    rootNode->incStrong(0);
    rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
    rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
    rootNode->mutateStagingProperties().setClipToBounds(false);
    rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);

    ContextFactory factory;
    RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
    proxy->loadSystemProperties();
    proxy->initialize(surface);
    float lightX = width / 2.0;
    proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
            dp(800.0f), 255 * 0.075, 255 * 0.15);

    android::uirenderer::Rect DUMMY;

    std::vector< sp<RenderNode> > cards;

    DisplayListRenderer* renderer = startRecording(rootNode);
    renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
    renderer->insertReorderBarrier(true);

    for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
        for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
            sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
            renderer->drawRenderNode(card.get(), DUMMY, 0);
            cards.push_back(card);
        }
    }

    renderer->insertReorderBarrier(false);
    endRecording(renderer, rootNode);

    for (int i = 0; i < 150; i++) {
        ATRACE_NAME("UI-Draw Frame");
        for (size_t ci = 0; ci < cards.size(); ci++) {
            cards[ci]->mutateStagingProperties().setTranslationX(i);
            cards[ci]->mutateStagingProperties().setTranslationY(i);
            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
        }
        nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
        proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
        usleep(12000);
    }

    sleep(5);

    delete proxy;
    rootNode->decStrong(0);

    printf("Success!\n");
    return 0;
}
static DisplayListRenderer* startRecording(RenderNode* node) {
    DisplayListRenderer* renderer = new DisplayListRenderer();
    renderer->setViewport(node->getWidth(), node->getHeight());
    renderer->prepare(false);
    return renderer;
}
static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jint width, jint height) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    renderer->setViewport(width, height);
}
static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
        jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
    renderer->drawLayer(layer, x, y);
}
static void android_view_GLES20Canvas_resetPaintFilter(JNIEnv* env, jobject clazz,
        jlong rendererPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    renderer->resetPaintFilter();
}
static jlong android_view_GLES20Canvas_finishRecording(JNIEnv* env,
        jobject clazz, jlong rendererPtr) {
    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
    return reinterpret_cast<jlong>(renderer->finishRecording());
}