Object-based DisplayList recording

bug:8037003

Changes the DisplayList from using stream read/write commands to use an array of
objects manually allocated on a linear buffer.

Depends on frameworks/native change https://googleplex-android-review.googlesource.com/#/c/257695/ which adds LinearAllocator

Also changes drawRects to use float count instead of rect count, to be more like drawLines/drawPoints

Change-Id: Ia2e4a11acd8f0a757042a05cbc9e7563cb73ee47
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
new file mode 100644
index 0000000..a4de56d
--- /dev/null
+++ b/libs/hwui/DisplayListOp.h
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
+#define ANDROID_HWUI_DISPLAY_OPERATION_H
+
+#include <SkXfermode.h>
+
+#include "OpenGLRenderer.h"
+#include "DisplayListRenderer.h"
+#include "utils/LinearAllocator.h"
+
+#define CRASH() do { \
+    *(int *)(uintptr_t)0xbbadbeef = 0; \
+    ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
+} while(false)
+
+#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define MATRIX_ARGS(m) \
+    m->get(0), m->get(1), m->get(2), \
+    m->get(3), m->get(4), m->get(5), \
+    m->get(6), m->get(7), m->get(8)
+#define RECT_STRING "%.2f %.2f %.2f %.2f"
+#define RECT_ARGS(r) \
+    r.left, r.top, r.right, r.bottom
+
+// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
+#define OP_LOGS(s) OP_LOG("%s", s)
+#define OP_LOG(s, ...) ALOGD( "%*s--%p " s, level * 2, "", this, __VA_ARGS__ )
+
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure for storing canvas operations when they are recorded into a DisplayList, so that they
+ * may be replayed to an OpenGLRenderer.
+ *
+ * To avoid individual memory allocations, DisplayListOps may only be allocated into a
+ * LinearAllocator's managed memory buffers.  Each pointer held by a DisplayListOp is either a
+ * pointer into memory also allocated in the LinearAllocator (mostly for text and float buffers) or
+ * references a externally refcounted object (Sk... and Skia... objects). ~DisplayListOp() is
+ * never called as LinearAllocators are simply discarded, so no memory management should be done in
+ * this class.
+ */
+class DisplayListOp {
+public:
+    // These objects should always be allocated with a LinearAllocator, and never destroyed/deleted.
+    // standard new() intentionally not implemented, and delete/deconstructor should never be used.
+    virtual ~DisplayListOp() { CRASH(); }
+    static void operator delete(void* ptr) { CRASH(); }
+    /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    enum OpLogFlag {
+        kOpLogFlag_Recurse = 0x1,
+        kOpLogFlag_JSON = 0x2 // TODO: add?
+    };
+
+    //TODO: for draw batching, DrawOps should override a virtual sub-method, with
+    // DrawOps::apply deferring operations to a different list if possible
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha) = 0;
+
+    virtual void output(int level, uint32_t flags = 0) = 0;
+
+    // NOTE: it would be nice to declare constants and overriding the implementation in each op to
+    // point at the constants, but that seems to require a .cpp file
+    virtual const char* name() = 0;
+};
+
+class StateOp : public DisplayListOp {
+public:
+    StateOp() {};
+
+    virtual ~StateOp() {}
+
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha) {
+        applyState(renderer, saveCount);
+        return DrawGlInfo::kStatusDone;
+    }
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
+};
+
+class DrawOp : public DisplayListOp {
+public:
+    DrawOp(SkPaint* paint)
+            : mPaint(paint), mQuickRejected(false) {}
+
+    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+            uint32_t level, bool caching, int multipliedAlpha) {
+        if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
+            return DrawGlInfo::kStatusDone;
+        }
+
+        return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) = 0;
+
+    // returns true if bounds exist
+    virtual bool getLocalBounds(Rect& localBounds) { return false; }
+
+    // TODO: better refine localbounds usage
+    void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
+    bool getQuickRejected() { return mQuickRejected; }
+
+protected:
+    SkPaint* getPaint(OpenGLRenderer& renderer) {
+        return renderer.filterPaint(mPaint);
+    }
+
+    SkPaint* mPaint; // should be accessed via getPaint() when applying
+    bool mQuickRejected;
+};
+
+class DrawBoundedOp : public DrawOp {
+public:
+    DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
+            : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+
+    // default constructor for area, to be overridden in child constructor body
+    DrawBoundedOp(SkPaint* paint)
+            : DrawOp(paint) {}
+
+    bool getLocalBounds(Rect& localBounds) {
+        localBounds.set(mLocalBounds);
+        return true;
+    }
+
+protected:
+    Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// STATE OPERATIONS - these may affect the state of the canvas/renderer, but do
+//         not directly draw or alter output
+///////////////////////////////////////////////////////////////////////////////
+
+class SaveOp : public StateOp {
+public:
+    SaveOp(int flags)
+            : mFlags(flags) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.save(mFlags);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Save flags %x", mFlags);
+    }
+
+    virtual const char* name() { return "Save"; }
+
+private:
+    int mFlags;
+};
+
+class RestoreToCountOp : public StateOp {
+public:
+    RestoreToCountOp(int count)
+            : mCount(count) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.restoreToCount(saveCount + mCount);
+
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Restore to count %d", mCount);
+    }
+
+    virtual const char* name() { return "RestoreToCount"; }
+
+private:
+    int mCount;
+};
+
+class SaveLayerOp : public StateOp {
+public:
+    SaveLayerOp(float left, float top, float right, float bottom, SkPaint* paint, int flags)
+            : mArea(left, top, right, bottom), mPaint(paint), mFlags(flags) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        SkPaint* paint = renderer.filterPaint(mPaint);
+        renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, paint, mFlags);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SaveLayer of area " RECT_STRING, RECT_ARGS(mArea));
+    }
+
+    virtual const char* name() { return "SaveLayer"; }
+
+private:
+    Rect mArea;
+    SkPaint* mPaint;
+    int mFlags;
+};
+
+class SaveLayerAlphaOp : public StateOp {
+public:
+    SaveLayerAlphaOp(float left, float top, float right, float bottom, int alpha, int flags)
+            : mArea(left, top, right, bottom), mAlpha(alpha), mFlags(flags) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.saveLayerAlpha(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mFlags);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SaveLayerAlpha of area " RECT_STRING, RECT_ARGS(mArea));
+    }
+
+    virtual const char* name() { return "SaveLayerAlpha"; }
+private:
+    Rect mArea;
+    int mAlpha;
+    int mFlags;
+};
+
+class TranslateOp : public StateOp {
+public:
+    TranslateOp(float dx, float dy)
+            : mDx(dx), mDy(dy) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.translate(mDx, mDy);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Translate by %f %f", mDx, mDy);
+    }
+
+    virtual const char* name() { return "Translate"; }
+
+private:
+    float mDx;
+    float mDy;
+};
+
+class RotateOp : public StateOp {
+public:
+    RotateOp(float degrees)
+            : mDegrees(degrees) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.rotate(mDegrees);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Rotate by %f degrees", mDegrees);
+    }
+
+    virtual const char* name() { return "Rotate"; }
+
+private:
+    float mDegrees;
+};
+
+class ScaleOp : public StateOp {
+public:
+    ScaleOp(float sx, float sy)
+            : mSx(sx), mSy(sy) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.scale(mSx, mSy);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Scale by %f %f", mSx, mSy);
+    }
+
+    virtual const char* name() { return "Scale"; }
+
+private:
+    float mSx;
+    float mSy;
+};
+
+class SkewOp : public StateOp {
+public:
+    SkewOp(float sx, float sy)
+            : mSx(sx), mSy(sy) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.skew(mSx, mSy);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("Skew by %f %f", mSx, mSy);
+    }
+
+    virtual const char* name() { return "Skew"; }
+
+private:
+    float mSx;
+    float mSy;
+};
+
+class SetMatrixOp : public StateOp {
+public:
+    SetMatrixOp(SkMatrix* matrix)
+            : mMatrix(matrix) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.setMatrix(mMatrix);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+    }
+
+    virtual const char* name() { return "SetMatrix"; }
+
+private:
+    SkMatrix* mMatrix;
+};
+
+class ConcatMatrixOp : public StateOp {
+public:
+    ConcatMatrixOp(SkMatrix* matrix)
+            : mMatrix(matrix) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.concatMatrix(mMatrix);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+    }
+
+    virtual const char* name() { return "ConcatMatrix"; }
+
+private:
+    SkMatrix* mMatrix;
+};
+
+class ClipRectOp : public StateOp {
+public:
+    ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
+            : mArea(left, top, right, bottom), mOp(op) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
+    }
+
+    virtual const char* name() { return "ClipRect"; }
+
+private:
+    Rect mArea;
+    SkRegion::Op mOp;
+};
+
+class ClipPathOp : public StateOp {
+public:
+    ClipPathOp(SkPath* path, SkRegion::Op op)
+            : mPath(path), mOp(op) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.clipPath(mPath, mOp);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        SkRect bounds = mPath->getBounds();
+        OP_LOG("ClipPath bounds " RECT_STRING,
+                bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+    }
+
+    virtual const char* name() { return "ClipPath"; }
+
+private:
+    SkPath* mPath;
+    SkRegion::Op mOp;
+};
+
+class ClipRegionOp : public StateOp {
+public:
+    ClipRegionOp(SkRegion* region, SkRegion::Op op)
+            : mRegion(region), mOp(op) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.clipRegion(mRegion, mOp);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        SkIRect bounds = mRegion->getBounds();
+        OP_LOG("ClipRegion bounds %d %d %d %d",
+                bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+    }
+
+    virtual const char* name() { return "ClipRegion"; }
+
+private:
+    SkRegion* mRegion;
+    SkRegion::Op mOp;
+};
+
+class ResetShaderOp : public StateOp {
+public:
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.resetShader();
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOGS("ResetShader");
+    }
+
+    virtual const char* name() { return "ResetShader"; }
+};
+
+class SetupShaderOp : public StateOp {
+public:
+    SetupShaderOp(SkiaShader* shader)
+            : mShader(shader) {}
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.setupShader(mShader);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SetupShader, shader %p", mShader);
+    }
+
+    virtual const char* name() { return "SetupShader"; }
+
+private:
+    SkiaShader* mShader;
+};
+
+class ResetColorFilterOp : public StateOp {
+public:
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.resetColorFilter();
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOGS("ResetColorFilter");
+    }
+
+    virtual const char* name() { return "ResetColorFilter"; }
+};
+
+class SetupColorFilterOp : public StateOp {
+public:
+    SetupColorFilterOp(SkiaColorFilter* colorFilter)
+            : mColorFilter(colorFilter) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.setupColorFilter(mColorFilter);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SetupColorFilter, filter %p", mColorFilter);
+    }
+
+    virtual const char* name() { return "SetupColorFilter"; }
+
+private:
+    SkiaColorFilter* mColorFilter;
+};
+
+class ResetShadowOp : public StateOp {
+public:
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.resetShadow();
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOGS("ResetShadow");
+    }
+
+    virtual const char* name() { return "ResetShadow"; }
+};
+
+class SetupShadowOp : public StateOp {
+public:
+    SetupShadowOp(float radius, float dx, float dy, int color)
+            : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.setupShadow(mRadius, mDx, mDy, mColor);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
+    }
+
+    virtual const char* name() { return "SetupShadow"; }
+
+private:
+    float mRadius;
+    float mDx;
+    float mDy;
+    int mColor;
+};
+
+class ResetPaintFilterOp : public StateOp {
+public:
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.resetPaintFilter();
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOGS("ResetPaintFilter");
+    }
+
+    virtual const char* name() { return "ResetPaintFilter"; }
+};
+
+class SetupPaintFilterOp : public StateOp {
+public:
+    SetupPaintFilterOp(int clearBits, int setBits)
+            : mClearBits(clearBits), mSetBits(setBits) {}
+
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+        renderer.setupPaintFilter(mClearBits, mSetBits);
+    }
+
+    virtual void output(int level, uint32_t flags = 0) {
+        OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
+    }
+
+    virtual const char* name() { return "SetupPaintFilter"; }
+
+private:
+    int mClearBits;
+    int mSetBits;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DRAW OPERATIONS - these are operations that can draw to the canvas's device
+///////////////////////////////////////////////////////////////////////////////
+
+class DrawBitmapOp : public DrawBoundedOp {
+public:
+    DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
+            : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(),
+                    paint),
+            mBitmap(bitmap) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        SkPaint* paint = getPaint(renderer);
+        int oldAlpha = -1;
+        if (caching && multipliedAlpha < 255) {
+            oldAlpha = paint->getAlpha();
+            paint->setAlpha(multipliedAlpha);
+        }
+        status_t ret = renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top, paint);
+        if (oldAlpha >= 0) {
+            paint->setAlpha(oldAlpha);
+        }
+        return ret;
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
+    }
+
+    virtual const char* name() { return "DrawBitmap"; }
+
+protected:
+    SkBitmap* mBitmap;
+};
+
+class DrawBitmapMatrixOp : public DrawBoundedOp {
+public:
+    DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint)
+            : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
+        mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
+        const mat4 transform(*matrix);
+        transform.mapRect(mLocalBounds);
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
+    }
+
+    virtual const char* name() { return "DrawBitmap"; }
+
+private:
+    SkBitmap* mBitmap;
+    SkMatrix* mMatrix;
+};
+
+class DrawBitmapRectOp : public DrawBoundedOp {
+public:
+    DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint)
+            : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
+            mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
+                mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
+                getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+                mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
+    }
+
+    virtual const char* name() { return "DrawBitmapRect"; }
+
+private:
+    SkBitmap* mBitmap;
+    Rect mSrc;
+};
+
+class DrawBitmapDataOp : public DrawBitmapOp {
+public:
+    DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
+            : DrawBitmapOp(bitmap, left, top, paint) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
+                mLocalBounds.top, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw bitmap %p", mBitmap);
+    }
+
+    virtual const char* name() { return "DrawBitmapData"; }
+};
+
+class DrawBitmapMeshOp : public DrawOp {
+public:
+    DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
+            float* vertices, int* colors, SkPaint* paint)
+            : DrawOp(paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
+            mVertices(vertices), mColors(colors) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
+                mVertices, mColors, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
+    }
+
+    virtual const char* name() { return "DrawBitmapMesh"; }
+
+private:
+    SkBitmap* mBitmap;
+    int mMeshWidth;
+    int mMeshHeight;
+    float* mVertices;
+    int* mColors;
+};
+
+class DrawPatchOp : public DrawBoundedOp {
+public:
+    DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs,
+            const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
+            int8_t numColors, float left, float top, float right, float bottom,
+            int alpha, SkXfermode::Mode mode)
+            : DrawBoundedOp(left, top, right, bottom, 0),
+            mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
+            mColors(colors), mxDivsCount(width), myDivsCount(height),
+            mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        // NOTE: not calling the virtual method, which takes a paint
+        return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
+                mxDivsCount, myDivsCount, mNumColors,
+                mLocalBounds.left, mLocalBounds.top,
+                mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+    }
+
+    virtual const char* name() { return "DrawPatch"; }
+
+private:
+    SkBitmap* mBitmap;
+    const int32_t* mxDivs;
+    const int32_t* myDivs;
+    const uint32_t* mColors;
+    uint32_t mxDivsCount;
+    uint32_t myDivsCount;
+    int8_t mNumColors;
+    int mAlpha;
+    SkXfermode::Mode mMode;
+};
+
+class DrawColorOp : public DrawOp {
+public:
+    DrawColorOp(int color, SkXfermode::Mode mode)
+            : DrawOp(0), mColor(color), mMode(mode) {};
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawColor(mColor, mMode);
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw color %#x, mode %d", mColor, mMode);
+    }
+
+    virtual const char* name() { return "DrawColor"; }
+
+private:
+    int mColor;
+    SkXfermode::Mode mMode;
+};
+
+class DrawStrokableOp : public DrawBoundedOp {
+public:
+    DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint)
+            : DrawBoundedOp(left, top, right, bottom, paint) {};
+
+    bool getLocalBounds(Rect& localBounds) {
+        if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
+            float outset = mPaint->getStrokeWidth() * 0.5f;
+            localBounds.set(mLocalBounds.left - outset, mLocalBounds.top - outset,
+                    mLocalBounds.right + outset, mLocalBounds.bottom + outset);
+        } else {
+            localBounds.set(mLocalBounds);
+        }
+        return true;
+    }
+};
+
+class DrawRectOp : public DrawStrokableOp {
+public:
+    DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
+            : DrawStrokableOp(left, top, right, bottom, paint) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
+                mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+    }
+
+    virtual const char* name() { return "DrawRect"; }
+};
+
+class DrawRectsOp : public DrawOp {
+public:
+    DrawRectsOp(const float* rects, int count, SkPaint* paint)
+            : DrawOp(paint), mRects(rects), mCount(count) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawRects(mRects, mCount, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Rects count %d", mCount);
+    }
+
+    virtual const char* name() { return "DrawRects"; }
+
+private:
+    const float* mRects;
+    int mCount;
+};
+
+class DrawRoundRectOp : public DrawStrokableOp {
+public:
+    DrawRoundRectOp(float left, float top, float right, float bottom,
+            float rx, float ry, SkPaint* paint)
+            : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
+                mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+    }
+
+    virtual const char* name() { return "DrawRoundRect"; }
+
+private:
+    float mRx;
+    float mRy;
+};
+
+class DrawCircleOp : public DrawStrokableOp {
+public:
+    DrawCircleOp(float x, float y, float radius, SkPaint* paint)
+            : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
+            mX(x), mY(y), mRadius(radius) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
+    }
+
+    virtual const char* name() { return "DrawCircle"; }
+
+private:
+    float mX;
+    float mY;
+    float mRadius;
+};
+
+class DrawOvalOp : public DrawStrokableOp {
+public:
+    DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
+            : DrawStrokableOp(left, top, right, bottom, paint) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
+                mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+    }
+
+    virtual const char* name() { return "DrawOval"; }
+};
+
+class DrawArcOp : public DrawStrokableOp {
+public:
+    DrawArcOp(float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, bool useCenter, SkPaint* paint)
+            : DrawStrokableOp(left, top, right, bottom, paint),
+            mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
+                mLocalBounds.right, mLocalBounds.bottom,
+                mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+                RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
+    }
+
+    virtual const char* name() { return "DrawArc"; }
+
+private:
+    float mStartAngle;
+    float mSweepAngle;
+    bool mUseCenter;
+};
+
+class DrawPathOp : public DrawBoundedOp {
+public:
+    DrawPathOp(SkPath* path, SkPaint* paint)
+            : DrawBoundedOp(paint), mPath(path) {
+        float left, top, offset;
+        uint32_t width, height;
+        computePathBounds(path, paint, left, top, offset, width, height);
+        left -= offset;
+        top -= offset;
+        mLocalBounds.set(left, top, left + width, top + height);
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawPath(mPath, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+    }
+
+    virtual const char* name() { return "DrawPath"; }
+
+private:
+    SkPath* mPath;
+};
+
+class DrawLinesOp : public DrawOp {
+public:
+    DrawLinesOp(float* points, int count, SkPaint* paint)
+            : DrawOp(paint), mPoints(points), mCount(count) {
+        /* TODO: inherit from DrawBoundedOp and calculate localbounds something like:
+        for (int i = 0; i < count; i += 2) {
+            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
+            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
+            mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
+            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
+        }
+        */
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawLines(mPoints, mCount, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Lines count %d", mCount);
+    }
+
+    virtual const char* name() { return "DrawLines"; }
+
+protected:
+    float* mPoints;
+    int mCount;
+};
+
+class DrawPointsOp : public DrawLinesOp {
+public:
+    DrawPointsOp(float* points, int count, SkPaint* paint)
+            : DrawLinesOp(points, count, paint) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Points count %d", mCount);
+    }
+
+    virtual const char* name() { return "DrawPoints"; }
+};
+
+class DrawSomeTextOp : public DrawOp {
+public:
+    DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
+            : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw some text, %d bytes", mBytesCount);
+    }
+protected:
+    const char* mText;
+    int mBytesCount;
+    int mCount;
+};
+
+class DrawTextOnPathOp : public DrawSomeTextOp {
+public:
+    DrawTextOnPathOp(const char* text, int bytesCount, int count,
+            SkPath* path, float hOffset, float vOffset, SkPaint* paint)
+            : DrawSomeTextOp(text, bytesCount, count, paint),
+            mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
+        /* TODO: inherit from DrawBounded and init mLocalBounds */
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
+                mHOffset, mVOffset, getPaint(renderer));
+    }
+
+    virtual const char* name() { return "DrawTextOnPath"; }
+
+private:
+    SkPath* mPath;
+    float mHOffset;
+    float mVOffset;
+};
+
+class DrawPosTextOp : public DrawSomeTextOp {
+public:
+    DrawPosTextOp(const char* text, int bytesCount, int count,
+            const float* positions, SkPaint* paint)
+            : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
+        /* TODO: inherit from DrawBounded and init mLocalBounds */
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
+    }
+
+    virtual const char* name() { return "DrawPosText"; }
+
+private:
+    const float* mPositions;
+};
+
+class DrawTextOp : public DrawBoundedOp {
+public:
+    DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
+            const float* positions, SkPaint* paint, float length)
+            : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
+            mX(x), mY(y), mPositions(positions), mLength(length) {
+        SkPaint::FontMetrics metrics;
+        paint->getFontMetrics(&metrics, 0.0f);
+        mLocalBounds.set(mX, mY + metrics.fTop, mX + length, mY + metrics.fBottom);
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
+                mPositions, getPaint(renderer), mLength);
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
+    }
+
+    virtual const char* name() { return "DrawText"; }
+
+private:
+    const char* mText;
+    int mBytesCount;
+    int mCount;
+    float mX;
+    float mY;
+    const float* mPositions;
+    float mLength;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SPECIAL DRAW OPERATIONS
+///////////////////////////////////////////////////////////////////////////////
+
+class DrawFunctorOp : public DrawOp {
+public:
+    DrawFunctorOp(Functor* functor)
+            : DrawOp(0), mFunctor(functor) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        renderer.startMark("GL functor");
+        status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
+        renderer.endMark();
+        return ret;
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Functor %p", mFunctor);
+    }
+
+    virtual const char* name() { return "DrawFunctor"; }
+
+private:
+    Functor* mFunctor;
+};
+
+class DrawDisplayListOp : public DrawOp {
+public:
+    DrawDisplayListOp(DisplayList* displayList, int flags)
+            : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        return renderer.drawDisplayList(mDisplayList, dirty, mFlags, level + 1);
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
+        if (mDisplayList && (flags & kOpLogFlag_Recurse)) {
+            mDisplayList->output(level + 1);
+        }
+    }
+
+    virtual const char* name() { return "DrawDisplayList"; }
+
+private:
+    DisplayList* mDisplayList;
+    int mFlags;
+};
+
+class DrawLayerOp : public DrawOp {
+public:
+    DrawLayerOp(Layer* layer, float x, float y, SkPaint* paint)
+            : DrawOp(paint), mLayer(layer), mX(x), mY(y) {}
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+            bool caching, int multipliedAlpha) {
+        int oldAlpha = -1;
+
+        if (caching && multipliedAlpha < 255) {
+            oldAlpha = mLayer->getAlpha();
+            mLayer->setAlpha(multipliedAlpha);
+        }
+        status_t ret = renderer.drawLayer(mLayer, mX, mY, getPaint(renderer));
+        if (oldAlpha >= 0) {
+            mLayer->setAlpha(oldAlpha);
+        }
+        return ret;
+    }
+
+    virtual void output(int level, uint32_t flags) {
+        OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
+    }
+
+    virtual const char* name() { return "DrawLayer"; }
+
+private:
+    Layer* mLayer;
+    float mX;
+    float mY;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_OPERATION_H