Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright 2017, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 import {transform, nanos_to_string, get_visible_chip} from './transform.js'
     18 
     19 // Layer flags
     20 const FLAG_HIDDEN = 0x01;
     21 const FLAG_OPAQUE = 0x02;
     22 const FLAG_SECURE = 0x80;
     23 
     24 var RELATIVE_Z_CHIP = {short: 'RelZ',
     25     long: "Is relative Z-ordered to another surface",
     26     class: 'warn'};
     27 var RELATIVE_Z_PARENT_CHIP = {short: 'RelZParent',
     28     long: "Something is relative Z-ordered to this surface",
     29     class: 'warn'};
     30 var MISSING_LAYER = {short: 'MissingLayer',
     31     long: "This layer was referenced from the parent, but not present in the trace",
     32     class: 'error'};
     33 
     34 function transform_layer(layer, {parentBounds, parentHidden}) {
     35 
     36   function get_size(layer) {
     37     var size = layer.size || {w: 0, h: 0};
     38     return {
     39       left: 0,
     40       right: size.w,
     41       top: 0,
     42       bottom: size.h
     43     };
     44   }
     45 
     46   function get_crop(layer) {
     47     var crop = layer.crop || {left: 0, top: 0, right: 0 , bottom:0};
     48     return {
     49       left: crop.left || 0,
     50       right: crop.right  || 0,
     51       top: crop.top || 0,
     52       bottom: crop.bottom || 0
     53     };
     54   }
     55 
     56   function intersect(bounds, crop) {
     57     return {
     58       left: Math.max(crop.left, bounds.left),
     59       right: Math.min(crop.right, bounds.right),
     60       top: Math.max(crop.top, bounds.top),
     61       bottom: Math.min(crop.bottom, bounds.bottom),
     62     };
     63   }
     64 
     65   function is_empty_rect(rect) {
     66     var right = rect.right || 0;
     67     var left = rect.left || 0;
     68     var top = rect.top || 0;
     69     var bottom = rect.bottom || 0;
     70 
     71     return (right - left) <= 0 || (bottom - top) <= 0;
     72   }
     73 
     74   function get_cropped_bounds(layer, parentBounds) {
     75     var size = get_size(layer);
     76     var crop = get_crop(layer);
     77     if (!is_empty_rect(size) && !is_empty_rect(crop)) {
     78       return intersect(size, crop);
     79     }
     80     if (!is_empty_rect(size)) {
     81       return size;
     82     }
     83     if (!is_empty_rect(crop)) {
     84       return crop;
     85     }
     86     return parentBounds || { left: 0, right: 0, top: 0, bottom: 0 };
     87   }
     88 
     89   function offset_to(bounds, x, y) {
     90     return {
     91       right: bounds.right - (bounds.left - x),
     92       bottom: bounds.bottom - (bounds.top - y),
     93       left: x,
     94       top: y,
     95     };
     96   }
     97 
     98   function transform_bounds(layer, parentBounds) {
     99     var result = layer.bounds || get_cropped_bounds(layer, parentBounds);
    100     var tx = (layer.position) ? layer.position.x || 0 : 0;
    101     var ty = (layer.position) ? layer.position.y || 0 : 0;
    102     result = offset_to(result, 0, 0);
    103     result.label = layer.name;
    104     result.transform = layer.transform;
    105     result.transform.tx = tx;
    106     result.transform.ty = ty;
    107     return result;
    108   }
    109 
    110   function is_opaque(layer) {
    111     return layer.color == undefined || (layer.color.a || 0) > 0;
    112   }
    113 
    114   function is_empty(region) {
    115     return region == undefined ||
    116         region.rect == undefined ||
    117         region.rect.length == 0 ||
    118         region.rect.every(function(r) { return is_empty_rect(r) } );
    119   }
    120 
    121   /**
    122    * Checks if the layer is visible on screen according to its type,
    123    * active buffer content, alpha and visible regions.
    124    *
    125    * @param {layer} layer
    126    * @returns if the layer is visible on screen or not
    127    */
    128   function is_visible(layer) {
    129     var visible = (layer.activeBuffer || layer.type === 'ColorLayer')
    130                   && !hidden && is_opaque(layer);
    131     visible &= !is_empty(layer.visibleRegion);
    132     return visible;
    133   }
    134 
    135   function postprocess_flags(layer) {
    136     if (!layer.flags) return;
    137     var verboseFlags = [];
    138     if (layer.flags & FLAG_HIDDEN) {
    139       verboseFlags.push("HIDDEN");
    140     }
    141     if (layer.flags & FLAG_OPAQUE) {
    142       verboseFlags.push("OPAQUE");
    143     }
    144     if (layer.flags & FLAG_SECURE) {
    145       verboseFlags.push("SECURE");
    146     }
    147 
    148     layer.flags = verboseFlags.join('|') + " (" + layer.flags + ")";
    149   }
    150 
    151   var chips = [];
    152   var rect = transform_bounds(layer, parentBounds);
    153   var hidden = (layer.flags & FLAG_HIDDEN) != 0 || parentHidden;
    154   var visible = is_visible(layer);
    155   if (visible) {
    156     chips.push(get_visible_chip());
    157   } else {
    158     rect = undefined;
    159   }
    160 
    161   var bounds = undefined;
    162   if (layer.name.startsWith("Display Root#0") && layer.sourceBounds) {
    163     bounds = {width: layer.sourceBounds.right, height: layer.sourceBounds.bottom};
    164   }
    165 
    166   if ((layer.zOrderRelativeOf || -1) !== -1) {
    167     chips.push(RELATIVE_Z_CHIP);
    168   }
    169   if (layer.zOrderRelativeParentOf !== undefined) {
    170     chips.push(RELATIVE_Z_PARENT_CHIP);
    171   }
    172   if (layer.missing) {
    173     chips.push(MISSING_LAYER);
    174   }
    175 
    176   var transform_layer_with_parent_hidden =
    177       (layer) => transform_layer(layer, {parentBounds: rect, parentHidden: hidden});
    178 
    179   postprocess_flags(layer);
    180 
    181   return transform({
    182     obj: layer,
    183     kind: 'layer',
    184     name: layer.name,
    185     children: [
    186       [layer.resolvedChildren, transform_layer_with_parent_hidden],
    187     ],
    188     rect,
    189     bounds,
    190     highlight: rect,
    191     chips,
    192     visible,
    193   });
    194 }
    195 
    196 function missingLayer(childId) {
    197   return {
    198     name: "layer #" + childId,
    199     missing: true,
    200     zOrderRelativeOf: -1,
    201     transform: {dsdx:1, dtdx:0, dsdy:0, dtdy:1},
    202   }
    203 }
    204 
    205 function transform_layers(layers) {
    206   var idToItem = {};
    207   var isChild = {}
    208 
    209   var layersList = layers.layers || [];
    210 
    211   layersList.forEach((e) => {
    212     idToItem[e.id] = e;
    213   });
    214   layersList.forEach((e) => {
    215     e.resolvedChildren = [];
    216     if (Array.isArray(e.children)) {
    217       e.resolvedChildren = e.children.map(
    218           (childId) => idToItem[childId] || missingLayer(childId));
    219       e.children.forEach((childId) => {
    220         isChild[childId] = true;
    221       });
    222     }
    223     if ((e.zOrderRelativeOf || -1) !== -1) {
    224       idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
    225     }
    226   });
    227 
    228   var roots = layersList.filter((e) => !isChild[e.id]);
    229 
    230   function foreachTree(nodes, fun) {
    231     nodes.forEach((n) => {
    232       fun(n);
    233       foreachTree(n.children, fun);
    234     });
    235   }
    236 
    237   var idToTransformed = {};
    238   var transformed_roots = roots.map((r) =>
    239     transform_layer(r, {parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
    240       parentHidden: false}));
    241 
    242   foreachTree(transformed_roots, (n) => {
    243     idToTransformed[n.obj.id] = n;
    244   });
    245   var flattened = [];
    246   layersList.forEach((e) => {
    247     flattened.push(idToTransformed[e.id]);
    248   });
    249 
    250   return transform({
    251     obj: {},
    252     kind: 'layers',
    253     name: 'layers',
    254     children: [
    255       [transformed_roots, (c) => c],
    256     ],
    257     rects_transform (r) {
    258       var res = [];
    259       flattened.forEach((l) => {
    260         if (l.rect) {
    261           res.push(l.rect);
    262         }
    263       });
    264       return res.reverse();
    265     },
    266     flattened,
    267   });
    268 }
    269 
    270 function transform_layers_entry(entry) {
    271   return transform({
    272     obj: entry,
    273     kind: 'entry',
    274     name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
    275     children: [
    276       [[entry.layers], transform_layers],
    277     ],
    278     timestamp: entry.elapsedRealtimeNanos,
    279     stableId: 'entry',
    280   });
    281 }
    282 
    283 function transform_layers_trace(entries) {
    284   var r = transform({
    285     obj: entries,
    286     kind: 'layerstrace',
    287     name: 'layerstrace',
    288     children: [
    289       [entries.entry, transform_layers_entry],
    290     ],
    291   });
    292 
    293   return r;
    294 }
    295 
    296 export {transform_layers, transform_layers_trace};
    297