Fix DA bugs

 * Now aware of transform of DrawDisplayListOp
 * Supports projection

 Bug: 15539677
 Bug: 15506680

Change-Id: Ic16f482cd48c3add12e49eca529281be12b93491
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 8aa8c92..898e81a 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -26,8 +26,23 @@
 namespace android {
 namespace uirenderer {
 
+NullDamageAccumulator NullDamageAccumulator::sInstance;
+
+NullDamageAccumulator* NullDamageAccumulator::instance() {
+    return &sInstance;
+}
+
+enum TransformType {
+    TransformRenderNode,
+    TransformMatrix4,
+};
+
 struct DirtyStack {
-    const RenderNode* node;
+    TransformType type;
+    union {
+        const RenderNode* renderNode;
+        const Matrix4* matrix4;
+    };
     // When this frame is pop'd, this rect is mapped through the above transform
     // and applied to the previous (aka parent) frame
     SkRect pendingDirty;
@@ -42,7 +57,7 @@
     mHead->prev = mHead;
 }
 
-void DamageAccumulator::pushNode(const RenderNode* node) {
+void DamageAccumulator::pushCommon() {
     if (!mHead->next) {
         DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
         nextFrame->next = 0;
@@ -50,42 +65,120 @@
         mHead->next = nextFrame;
     }
     mHead = mHead->next;
-    mHead->node = node;
     mHead->pendingDirty.setEmpty();
 }
 
-void DamageAccumulator::popNode() {
+void DamageAccumulator::pushTransform(const RenderNode* transform) {
+    pushCommon();
+    mHead->type = TransformRenderNode;
+    mHead->renderNode = transform;
+}
+
+void DamageAccumulator::pushTransform(const Matrix4* transform) {
+    pushCommon();
+    mHead->type = TransformMatrix4;
+    mHead->matrix4 = transform;
+}
+
+void DamageAccumulator::popTransform() {
     LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
     DirtyStack* dirtyFrame = mHead;
     mHead = mHead->prev;
-    if (!dirtyFrame->pendingDirty.isEmpty()) {
-        SkRect mappedDirty;
-        const RenderProperties& props = dirtyFrame->node->properties();
-        const SkMatrix* transform = props.getTransformMatrix();
-        if (transform && !transform->isIdentity()) {
-            transform->mapRect(&mappedDirty, dirtyFrame->pendingDirty);
-        } else {
-            mappedDirty = dirtyFrame->pendingDirty;
+    if (dirtyFrame->type == TransformRenderNode) {
+        applyRenderNodeTransform(dirtyFrame);
+    } else {
+        applyMatrix4Transform(dirtyFrame);
+    }
+}
+
+static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
+    if (in.isEmpty()) return;
+    Rect temp(in);
+    matrix->mapRect(temp);
+    out->join(RECT_ARGS(temp));
+}
+
+void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
+    mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
+}
+
+static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
+    if (in.isEmpty()) return;
+    const SkMatrix* transform = props.getTransformMatrix();
+    SkRect temp(in);
+    if (transform && !transform->isIdentity()) {
+        transform->mapRect(&temp);
+    }
+    temp.offset(props.getLeft(), props.getTop());
+    out->join(temp);
+}
+
+static DirtyStack* findParentRenderNode(DirtyStack* frame) {
+    while (frame->prev != frame) {
+        frame = frame->prev;
+        if (frame->type == TransformRenderNode) {
+            return frame;
         }
-        if (CC_LIKELY(mHead->node)) {
-            const RenderProperties& parentProps = mHead->node->properties();
-            mappedDirty.offset(props.getLeft() - parentProps.getScrollX(),
-                    props.getTop() - parentProps.getScrollY());
-            if (props.getClipToBounds()) {
-                if (!mappedDirty.intersect(0, 0, parentProps.getWidth(), parentProps.getHeight())) {
-                    mappedDirty.setEmpty();
-                }
+    }
+    return NULL;
+}
+
+static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
+    if (frame) {
+        while (frame->prev != frame) {
+            frame = frame->prev;
+            if (frame->type == TransformRenderNode
+                    && frame->renderNode->hasProjectionReceiver()) {
+                return frame;
             }
-            if (CC_UNLIKELY(!MathUtils::isZero(props.getTranslationZ()))) {
-                // TODO: Can we better bound the shadow damage area? For now
-                // match the old damageShadowReceiver() path and just dirty
-                // the entire parent bounds
-                mappedDirty.join(0, 0, parentProps.getWidth(), parentProps.getHeight());
-            }
-        } else {
-            mappedDirty.offset(props.getLeft(), props.getTop());
         }
-        dirty(mappedDirty.fLeft, mappedDirty.fTop, mappedDirty.fRight, mappedDirty.fBottom);
+    }
+    return NULL;
+}
+
+static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
+    SkRect* rect = &frame->pendingDirty;
+    while (frame != end) {
+        if (frame->type == TransformRenderNode) {
+            mapRect(frame->renderNode->properties(), *rect, rect);
+        } else {
+            mapRect(frame->matrix4, *rect, rect);
+        }
+        frame = frame->prev;
+    }
+}
+
+void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
+    if (frame->pendingDirty.isEmpty()) {
+        return;
+    }
+
+    const RenderProperties& props = frame->renderNode->properties();
+
+    // Perform clipping
+    if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
+        if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
+            frame->pendingDirty.setEmpty();
+        }
+    }
+
+    // apply all transforms
+    mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
+
+    // project backwards if necessary
+    if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
+        // First, find our parent RenderNode:
+        DirtyStack* parentNode = findParentRenderNode(frame);
+        // Find our parent's projection receiver, which is what we project onto
+        DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
+        if (projectionReceiver) {
+            applyTransforms(frame, projectionReceiver);
+            projectionReceiver->pendingDirty.join(frame->pendingDirty);
+        } else {
+            ALOGW("Failed to find projection receiver? Dropping on the floor...");
+        }
+
+        frame->pendingDirty.setEmpty();
     }
 }
 
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index c62a351..2ca30d4 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -16,6 +16,7 @@
 #ifndef DAMAGEACCUMULATOR_H
 #define DAMAGEACCUMULATOR_H
 
+#include <cutils/compiler.h>
 #include <utils/LinearAllocator.h>
 
 #include <SkMatrix.h>
@@ -28,8 +29,19 @@
 
 struct DirtyStack;
 class RenderNode;
+class Matrix4;
 
-class DamageAccumulator {
+class IDamageAccumulator {
+public:
+    virtual void pushTransform(const RenderNode* transform) = 0;
+    virtual void pushTransform(const Matrix4* transform) = 0;
+    virtual void popTransform() = 0;
+    virtual void dirty(float left, float top, float right, float bottom) = 0;
+protected:
+    virtual ~IDamageAccumulator() {}
+};
+
+class DamageAccumulator : public IDamageAccumulator {
     PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
 public:
     DamageAccumulator();
@@ -37,20 +49,44 @@
 
     // Push a transform node onto the stack. This should be called prior
     // to any dirty() calls. Subsequent calls to dirty()
-    // will be affected by the node's transform when popNode() is called.
-    void pushNode(const RenderNode* node);
+    // will be affected by the transform when popTransform() is called.
+    virtual void pushTransform(const RenderNode* transform);
+    virtual void pushTransform(const Matrix4* transform);
+
     // Pops a transform node from the stack, propagating the dirty rect
-    // up to the parent node.
-    void popNode();
-    void dirty(float left, float top, float right, float bottom);
+    // up to the parent node. Returns the IDamageTransform that was just applied
+    virtual void popTransform();
+
+    virtual void dirty(float left, float top, float right, float bottom);
 
     void finish(SkRect* totalDirty);
 
 private:
+    void pushCommon();
+    void applyMatrix4Transform(DirtyStack* frame);
+    void applyRenderNodeTransform(DirtyStack* frame);
+
     LinearAllocator mAllocator;
     DirtyStack* mHead;
 };
 
+class NullDamageAccumulator : public IDamageAccumulator {
+    PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator);
+public:
+    virtual void pushTransform(const RenderNode* transform) { }
+    virtual void pushTransform(const Matrix4* transform) { }
+    virtual void popTransform() { }
+    virtual void dirty(float left, float top, float right, float bottom) { }
+
+    ANDROID_API static NullDamageAccumulator* instance();
+
+private:
+    NullDamageAccumulator() {}
+    ~NullDamageAccumulator() {}
+
+    static NullDamageAccumulator sInstance;
+};
+
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 2268386..9f2014f 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -417,6 +417,8 @@
 }
 
 void Matrix4::mapRect(Rect& r) const {
+    if (isIdentity()) return;
+
     if (isSimple()) {
         MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
         MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index e33a001..1c5c578 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -147,6 +147,7 @@
             data[kTranslateX] += x;
             data[kTranslateY] += y;
             data[kTranslateZ] += z;
+            mType |= kTypeUnknown;
         } else {
             // Doing a translation will only affect the translate bit of the type
             // Save the type
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index c2f6df8..83ad76f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -15,6 +15,7 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_VIEW
+#define LOG_TAG "RenderNode"
 
 #include "RenderNode.h"
 
@@ -111,26 +112,20 @@
     prepareTreeImpl(info);
 }
 
-static inline void pushNode(RenderNode* self, TreeInfo& info) {
-    if (info.damageAccumulator) {
-        info.damageAccumulator->pushNode(self);
-    }
-}
-
-static inline void popNode(TreeInfo& info) {
-    if (info.damageAccumulator) {
-        info.damageAccumulator->popNode();
-    }
-}
-
 void RenderNode::damageSelf(TreeInfo& info) {
-    if (info.damageAccumulator && isRenderable() && properties().getAlpha() > 0) {
-        info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
+    if (isRenderable() && properties().getAlpha() > 0) {
+        if (properties().getClipToBounds()) {
+            info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
+        } else {
+            // Hope this is big enough?
+            // TODO: Get this from the display list ops or something
+            info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
+        }
     }
 }
 
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
-    pushNode(this, info);
+    info.damageAccumulator->pushTransform(this);
     if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingChanges(info);
         evaluateAnimations(info);
@@ -140,7 +135,7 @@
         evaluateAnimations(info);
     }
     prepareSubTree(info, mDisplayListData);
-    popNode(info);
+    info.damageAccumulator->popTransform();
 }
 
 class PushAnimatorsFunctor {
@@ -173,14 +168,14 @@
     if (mDirtyPropertyFields) {
         mDirtyPropertyFields = 0;
         damageSelf(info);
-        popNode(info);
+        info.damageAccumulator->popTransform();
         mProperties = mStagingProperties;
-        pushNode(this, info);
         // We could try to be clever and only re-damage if the matrix changed.
         // However, we don't need to worry about that. The cost of over-damaging
         // here is only going to be a single additional map rect of this node
         // plus a rect join(). The parent's transform (and up) will only be
         // performed once.
+        info.damageAccumulator->pushTransform(this);
         damageSelf(info);
     }
     if (mNeedsDisplayListDataSync) {
@@ -217,7 +212,7 @@
     // property push and just damage self before and after animators are run
 
     damageSelf(info);
-    popNode(info);
+    info.damageAccumulator->popTransform();
 
     AnimateFunctor functor(this, info);
     std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
@@ -226,7 +221,7 @@
     mProperties.updateMatrix();
     info.out.hasAnimations |= mAnimators.size();
 
-    pushNode(this, info);
+    info.damageAccumulator->pushTransform(this);
     damageSelf(info);
 }
 
@@ -243,8 +238,11 @@
             info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
         }
         for (size_t i = 0; i < subtree->children().size(); i++) {
-            RenderNode* childNode = subtree->children()[i]->mDisplayList;
+            DrawDisplayListOp* op = subtree->children()[i];
+            RenderNode* childNode = op->mDisplayList;
+            info.damageAccumulator->pushTransform(&op->mTransformFromParent);
             childNode->prepareTreeImpl(info);
+            info.damageAccumulator->popTransform();
         }
     }
 }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 393d4ea..f0f6e7c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -39,6 +39,7 @@
 
 #include <androidfw/ResourceTypes.h>
 
+#include "DamageAccumulator.h"
 #include "Debug.h"
 #include "Matrix.h"
 #include "DeferredDisplayList.h"
@@ -125,6 +126,10 @@
         return mDisplayListData && mDisplayListData->hasDrawOps;
     }
 
+    bool hasProjectionReceiver() const {
+        return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+    }
+
     const char* getName() const {
         return mName.string();
     }
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 6163df5..5f7d4e3 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -44,7 +44,6 @@
         , mPivotX(0), mPivotY(0)
         , mLeft(0), mTop(0), mRight(0), mBottom(0)
         , mWidth(0), mHeight(0)
-        , mScrollX(0), mScrollY(0)
         , mPivotExplicitlySet(false)
         , mMatrixOrPivotDirty(false)
         , mCaching(false) {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index c294f38..b012fc5 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -366,22 +366,6 @@
         return false;
     }
 
-    bool setScrollX(int scrollX) {
-        return RP_SET(mPrimitiveFields.mScrollX, scrollX);
-    }
-
-    bool setScrollY(int scrollY) {
-        return RP_SET(mPrimitiveFields.mScrollY, scrollY);
-    }
-
-    int getScrollX() const {
-        return mPrimitiveFields.mScrollX;
-    }
-
-    int getScrollY() const {
-        return mPrimitiveFields.mScrollY;
-    }
-
     bool setCaching(bool caching) {
         return RP_SET(mPrimitiveFields.mCaching, caching);
     }
@@ -481,7 +465,6 @@
         float mPivotX, mPivotY;
         int mLeft, mTop, mRight, mBottom;
         int mWidth, mHeight;
-        int mScrollX, mScrollY;
         bool mPivotExplicitlySet;
         bool mMatrixOrPivotDirty;
         bool mCaching;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 2096f98..fd78f8e 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -18,6 +18,7 @@
 
 #include <utils/Timers.h>
 
+#include "DamageAccumulator.h"
 #include "utils/Macros.h"
 
 namespace android {
@@ -25,7 +26,6 @@
 
 class BaseRenderNodeAnimator;
 class AnimationListener;
-class DamageAccumulator;
 
 class AnimationHook {
 public:
@@ -62,7 +62,7 @@
         , frameTimeMs(0)
         , animationHook(NULL)
         , prepareTextures(mode == MODE_FULL)
-        , damageAccumulator(0)
+        , damageAccumulator(NullDamageAccumulator::instance())
     {}
 
     const TraversalMode mode;
@@ -71,7 +71,8 @@
     // TODO: Remove this? Currently this is used to signal to stop preparing
     // textures if we run out of cache space.
     bool prepareTextures;
-    DamageAccumulator* damageAccumulator;
+    // Must not be null
+    IDamageAccumulator* damageAccumulator;
 
     struct Out {
         Out()