Added reasons for invisibility of a layer.

Test: Create an SFTrace that opens an app, then closes. Then test the
wide variety of layers created by this file.

Change-Id: Idd198cc6507c67a03f38cf3e34d737d319295c16
Merged-In: Idd198cc6507c67a03f38cf3e34d737d319295c16
diff --git a/tools/winscope/src/transform_sf.js b/tools/winscope/src/transform_sf.js
index 809c54f..e125ccf 100644
--- a/tools/winscope/src/transform_sf.js
+++ b/tools/winscope/src/transform_sf.js
@@ -32,7 +32,6 @@
     class: 'error'};
 
 function transform_layer(layer, {parentBounds, parentHidden}) {
-
   function get_size(layer) {
     var size = layer.size || {w: 0, h: 0};
     return {
@@ -118,6 +117,11 @@
         region.rect.every(function(r) { return is_empty_rect(r) } );
   }
 
+  function is_rect_empty_and_valid(rect) {
+    return rect &&
+      (rect.left - rect.right === 0 || rect.top - rect.bottom === 0);
+  }
+
   /**
    * Checks if the layer is visible on screen according to its type,
    * active buffer content, alpha and visible regions.
@@ -172,12 +176,42 @@
   if (layer.missing) {
     chips.push(MISSING_LAYER);
   }
-
+  function visibilityReason(layer) {
+    let reasons = [];
+    if (!layer.color || layer.color.a === 0) {
+      reasons.push('Alpha is 0');
+    }
+    if (layer.flags && (layer.flags & FLAG_HIDDEN != 0)) {
+      reasons.push('Flag is hidden');
+    }
+    if (is_rect_empty_and_valid(layer.crop)) {
+      reasons.push('Crop is zero');
+    }
+    if (!layer.transform || (layer.type === 'BufferLayer'
+        && !layer.bufferTransform)) {
+      reasons.push('Transform is invalid');
+    }
+    return reasons.join();
+  }
+  if (parentHidden) {
+    layer.invisibleDueTo = 'Hidden by parent with ID: ' + parentHidden;
+  } else {
+    var reasons_hidden = visibilityReason(layer);
+    if (reasons_hidden) {
+      layer.invisibleDueTo = reasons_hidden;
+      parentHidden = layer.id
+    } else if (layer.type === 'ContainerLayer') {
+        layer.invisibleDueTo = 'This is a ContainerLayer.';
+    } else if (layer.type === 'BufferLayer' && (!layer.activeBuffer ||
+          layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
+        reasons.push('The buffer is zero');
+    } else if (!visible) {
+        layer.invisibleDueTo = 'Occluded by another layer.';
+    }
+  }
   var transform_layer_with_parent_hidden =
-      (layer) => transform_layer(layer, {parentBounds: rect, parentHidden: hidden});
-
+      (layer) => transform_layer(layer, {parentBounds: rect, parentHidden: parentHidden});
   postprocess_flags(layer);
-
   return transform({
     obj: layer,
     kind: 'layer',
@@ -237,7 +271,7 @@
   var idToTransformed = {};
   var transformed_roots = roots.map((r) =>
     transform_layer(r, {parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
-      parentHidden: false}));
+      parentHidden: null}));
 
   foreachTree(transformed_roots, (n) => {
     idToTransformed[n.obj.id] = n;