HWUI: reimplement rendernode ouput for single stream

bug: 26565102
Change-Id: I90b449b2dce52683c50b48091354104d76a5e44a
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index c90abad..dd20a76 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -23,6 +23,7 @@
 #include "RecordedOp.h"
 #include "TreeInfo.h"
 #include "utils/MathUtils.h"
+#include "utils/StringUtils.h"
 #include "utils/TraceUtils.h"
 #include "VectorDrawable.h"
 #include "renderstate/RenderState.h"
@@ -68,31 +69,36 @@
  * This function is a simplified version of replay(), where we simply retrieve and log the
  * display list. This function should remain in sync with the replay() function.
  */
-void RenderNode::output(uint32_t level, const char* label) {
-    ALOGD("%s (%s %p%s%s%s%s%s)",
-            label,
-            getName(),
-            this,
-            (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""),
-            (properties().hasShadow() ? ", casting shadow" : ""),
-            (isRenderable() ? "" : ", empty"),
-            (properties().getProjectBackwards() ? ", projected" : ""),
-            (mLayer != nullptr ? ", on HW Layer" : ""));
-    properties().debugOutputProperties(level + 1);
+void RenderNode::output() {
+    LogcatStream strout;
+    strout << "Root";
+    output(strout, 0);
+}
+
+void RenderNode::output(std::ostream& output, uint32_t level) {
+    output << "  (" << getName() << " " << this
+            << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "")
+            << (properties().hasShadow() ? ", casting shadow" : "")
+            << (isRenderable() ? "" : ", empty")
+            << (properties().getProjectBackwards() ? ", projected" : "")
+            << (mLayer != nullptr ? ", on HW Layer" : "")
+            << ")" << std::endl;
+
+    properties().debugOutputProperties(output, level + 1);
 
     if (mDisplayList) {
         for (auto&& op : mDisplayList->getOps()) {
-            std::stringstream strout;
-            OpDumper::dump(*op, strout, level + 1);
+            OpDumper::dump(*op, output, level + 1);
             if (op->opId == RecordedOpId::RenderNodeOp) {
                 auto rnOp = reinterpret_cast<const RenderNodeOp*>(op);
-                rnOp->renderNode->output(level + 1, strout.str().c_str());
+                rnOp->renderNode->output(output, level + 1);
             } else {
-                ALOGD("%s", strout.str().c_str());
+                output << std::endl;
             }
         }
     }
-    ALOGD("%*s/RenderNode(%s %p)", level * 2, "", getName(), this);
+    output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")";
+    output << std::endl;
 }
 
 void RenderNode::copyTo(proto::RenderNode *pnode) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index ee045aa..a0679b1 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -102,7 +102,7 @@
 
     void computeOrdering();
 
-    ANDROID_API void output(uint32_t level = 0, const char* label = "Root");
+    ANDROID_API void output();
     ANDROID_API int getDebugSize();
     void copyTo(proto::RenderNode* node);
 
@@ -247,6 +247,7 @@
 
     void incParentRefCount() { mParentCount++; }
     void decParentRefCount(TreeObserver* observer, TreeInfo* info = nullptr);
+    void output(std::ostream& output, uint32_t level);
 
     String8 mName;
     sp<VirtualLightRefBase> mUserContext;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 7e3cad4..b0114bc 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -99,26 +99,34 @@
     return *this;
 }
 
-void RenderProperties::debugOutputProperties(const int level) const {
+static void dumpMatrix(std::ostream& output, std::string& indent,
+        const char* label, SkMatrix* matrix) {
+   if (matrix) {
+        output << indent << "(" <<  label << " " << matrix << ": ";
+        output << std::fixed << std::setprecision(2);
+        output << "[" << matrix->get(0) << " "<< matrix->get(1) << " " << matrix->get(2) << "]";
+        output << " [" << matrix->get(3) << " "<< matrix->get(4) << " " << matrix->get(5) << "]";
+        output << " [" << matrix->get(6) << " "<< matrix->get(7) << " " << matrix->get(8) << "]";
+        output << ")" << std::endl;
+    }
+}
+
+void RenderProperties::debugOutputProperties(std::ostream& output, const int level) const {
+    auto indent = std::string(level * 2, ' ');
     if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) {
-        ALOGD("%*s(Translate (left, top) %d, %d)", level * 2, "",
-                mPrimitiveFields.mLeft, mPrimitiveFields.mTop);
+        output << indent << "(Translate (left, top) " << mPrimitiveFields.mLeft
+                << ", " << mPrimitiveFields.mTop << ")" << std::endl;
     }
-    if (mStaticMatrix) {
-        ALOGD("%*s(ConcatMatrix (static) %p: " SK_MATRIX_STRING ")",
-                level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix));
-    }
-    if (mAnimationMatrix) {
-        ALOGD("%*s(ConcatMatrix (animation) %p: " SK_MATRIX_STRING ")",
-                level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix));
-    }
+    dumpMatrix(output, indent, "ConcatMatrix (static)", mStaticMatrix);
+    dumpMatrix(output, indent, "ConcatMatrix (animation)", mAnimationMatrix);
+
+    output << std::fixed << std::setprecision(2);
     if (hasTransformMatrix()) {
         if (isTransformTranslateOnly()) {
-            ALOGD("%*s(Translate %.2f, %.2f, %.2f)",
-                    level * 2, "", getTranslationX(), getTranslationY(), getZ());
+            output << indent << "(Translate " << getTranslationX() << ", " << getTranslationY()
+                    << ", " << getZ() << ")" << std::endl;
         } else {
-            ALOGD("%*s(ConcatMatrix %p: " SK_MATRIX_STRING ")",
-                    level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix));
+            dumpMatrix(output, indent, "ConcatMatrix ", mComputedFields.mTransformMatrix);
         }
     }
 
@@ -132,7 +140,7 @@
 
         if (CC_LIKELY(isLayer || !getHasOverlappingRendering())) {
             // simply scale rendering content's alpha
-            ALOGD("%*s(ScaleAlpha %.2f)", level * 2, "", mPrimitiveFields.mAlpha);
+            output << indent << "(ScaleAlpha " << mPrimitiveFields.mAlpha << ")" << std::endl;
         } else {
             // savelayeralpha to create an offscreen buffer to apply alpha
             Rect layerBounds(0, 0, getWidth(), getHeight());
@@ -140,35 +148,40 @@
                 getClippingRectForFlags(clipFlags, &layerBounds);
                 clipFlags = 0; // all clipping done by savelayer
             }
-            ALOGD("%*s(SaveLayerAlpha %d, %d, %d, %d, %d, 0x%x)", level * 2, "",
-                    (int)layerBounds.left, (int)layerBounds.top,
-                    (int)layerBounds.right, (int)layerBounds.bottom,
-                    (int)(mPrimitiveFields.mAlpha * 255),
-                    SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
+            output << indent << "(SaveLayerAlpha "
+                    << (int)layerBounds.left << ", " << (int)layerBounds.top << ", "
+                    << (int)layerBounds.right << ", " << (int)layerBounds.bottom << ", "
+                    << (int)(mPrimitiveFields.mAlpha * 255) << ", 0x" << std::hex
+                    << (SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer) << ")" << std::dec
+                    << std::endl;
         }
     }
 
     if (clipFlags) {
         Rect clipRect;
         getClippingRectForFlags(clipFlags, &clipRect);
-        ALOGD("%*s(ClipRect %d, %d, %d, %d)", level * 2, "",
-                (int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom);
+        output << indent << "(ClipRect "
+                << (int)clipRect.left << ", " << (int)clipRect.top << ", "
+                << (int)clipRect.right << ", " << (int)clipRect.bottom << ")" << std::endl;
     }
 
     if (getRevealClip().willClip()) {
         Rect bounds;
         getRevealClip().getBounds(&bounds);
-        ALOGD("%*s(Clip to reveal clip with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
-                RECT_ARGS(bounds));
+        output << indent << "(Clip to reveal clip with bounds "
+                << bounds.left << ", " << bounds.top << ", "
+                << bounds.right << ", " << bounds.bottom << ")" << std::endl;
     }
 
     auto& outline = mPrimitiveFields.mOutline;
     if (outline.getShouldClip()) {
         if (outline.isEmpty()) {
-            ALOGD("%*s(Clip to empty outline)", level * 2, "");
+            output << indent << "(Clip to empty outline)";
         } else if (outline.willClip()) {
-            ALOGD("%*s(Clip to outline with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
-                    RECT_ARGS(outline.getBounds()));
+            const Rect& bounds = outline.getBounds();
+            output << indent << "(Clip to outline with bounds "
+                    << bounds.left << ", " << bounds.top << ", "
+                    << bounds.right << ", " << bounds.bottom << ")" << std::endl;
         }
     }
 }
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 00494a1..2f5223c 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -35,6 +35,7 @@
 #include <cutils/compiler.h>
 #include <androidfw/ResourceTypes.h>
 #include <utils/Log.h>
+#include <ostream>
 
 class SkBitmap;
 class SkColorFilter;
@@ -574,7 +575,7 @@
         return mPrimitiveFields.mProjectBackwards;
     }
 
-    void debugOutputProperties(const int level) const;
+    void debugOutputProperties(std::ostream& output, const int level) const;
 
     void updateMatrix();
 
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
index 5add957..af5d10f 100644
--- a/libs/hwui/utils/StringUtils.h
+++ b/libs/hwui/utils/StringUtils.h
@@ -16,10 +16,14 @@
 #ifndef STRING_UTILS_H
 #define STRING_UTILS_H
 
+#include <iomanip>
+#include <iostream>
+#include <ostream>
+#include <sstream>
 #include <string>
 #include <unordered_set>
-#include <ostream>
-#include <iomanip>
+
+#include <utils/Log.h>
 
 namespace android {
 namespace uirenderer {
@@ -51,6 +55,22 @@
     }
 };
 
+class LogcatStream: public std::ostream {
+    class LogcatStreamBuf: public std::stringbuf {
+        virtual int sync() {
+            ALOGD("%s", str().c_str());
+            str("");
+            return 0;
+        }
+    };
+
+    LogcatStreamBuf buffer;
+public:
+    LogcatStream()
+            :std::ostream(&buffer) {
+    }
+};
+
 } /* namespace uirenderer */
 } /* namespace android */