• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/python
2
3# Copyright 2019 The ANGLE Project Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6#
7# gen_overlay_widgets.py:
8#  Code generation for overlay widgets.  Should be run when the widgets declaration file,
9#  overlay_widgets.json, is changed.
10#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.
11
12from datetime import date
13import json
14import sys
15
16out_file = 'Overlay_autogen.cpp'
17in_file = 'overlay_widgets.json'
18
19template_out_file = u"""// GENERATED FILE - DO NOT EDIT.
20// Generated by {script_name} using data from {input_file_name}.
21//
22// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
23// Use of this source code is governed by a BSD-style license that can be
24// found in the LICENSE file.
25//
26// {out_file_name}:
27//   Autogenerated overlay widget declarations.
28
29#include "libANGLE/renderer/driver_utils.h"
30#include "libANGLE/Overlay.h"
31#include "libANGLE/OverlayWidgets.h"
32#include "libANGLE/Overlay_font_autogen.h"
33
34namespace gl
35{{
36using namespace overlay;
37
38namespace
39{{
40int GetFontSize(int fontSize, bool largeFont)
41{{
42    if (largeFont && fontSize > 0)
43    {{
44        return fontSize - 1;
45    }}
46    return fontSize;
47}}
48}}  // anonymous namespace
49
50void Overlay::initOverlayWidgets()
51{{
52    const bool kLargeFont = rx::IsAndroid();
53
54    {init_widgets}
55}}
56
57}}  // namespace gl
58
59"""
60
61template_init_widget = u"""{{
62const int32_t fontSize = GetFontSize({font_size}, kLargeFont);
63const int32_t offsetX = {offset_x};
64const int32_t offsetY = {offset_y};
65const int32_t width = {width};
66const int32_t height = {height};
67
68widget->{subwidget}type      = WidgetType::{type};
69widget->{subwidget}fontSize  = fontSize;
70widget->{subwidget}coords[0] = {coord0};
71widget->{subwidget}coords[1] = {coord1};
72widget->{subwidget}coords[2] = {coord2};
73widget->{subwidget}coords[3] = {coord3};
74widget->{subwidget}color[0]  = {color_r};
75widget->{subwidget}color[1]  = {color_g};
76widget->{subwidget}color[2]  = {color_b};
77widget->{subwidget}color[3]  = {color_a};
78}}
79"""
80
81
82def extract_type_and_constructor(properties):
83    constructor = properties['type']
84    args_separated = constructor.split('(', 1)
85    if len(args_separated) == 1:
86        return constructor, constructor
87
88    type_no_constructor = args_separated[0]
89    return type_no_constructor, constructor
90
91
92def get_font_size_constant(properties):
93    return 'kFontLayer' + properties['font'].capitalize()
94
95
96def is_graph_type(type):
97    return type == 'RunningGraph' or type == 'RunningHistogram'
98
99
100def is_text_type(type):
101    return not is_graph_type(type)
102
103
104class OverlayWidget:
105
106    def __init__(self, properties, is_graph_description=False):
107        if not is_graph_description:
108            self.name = properties['name']
109        self.type, self.constructor = extract_type_and_constructor(properties)
110        self.extract_common(properties)
111
112        if is_graph_type(self.type):
113            description_properties = properties['description']
114            description_properties['type'] = 'Text'
115            self.description = OverlayWidget(description_properties, True)
116
117    def extract_common(self, properties):
118        self.color = properties['color']
119        self.coords = properties['coords']
120        if is_graph_type(self.type):
121            self.bar_width = properties['bar_width']
122            self.height = properties['height']
123        else:
124            self.font = get_font_size_constant(properties)
125            self.length = properties['length']
126
127        self.negative_alignment = [False, False]
128
129
130def is_negative_coord(coords, axis, widgets_so_far):
131
132    if isinstance(coords[axis], unicode):
133        coord_split = coords[axis].split('.')
134        # The coordinate is in the form other_widget.edge.mode
135        # We simply need to know if other_widget's coordinate is negative or not.
136        return widgets_so_far[coord_split[0]].negative_alignment[axis]
137
138    return coords[axis] < 0
139
140
141def set_alignment_flags(overlay_widget, widgets_so_far):
142    overlay_widget.negative_alignment[0] = is_negative_coord(overlay_widget.coords, 0,
143                                                             widgets_so_far)
144    overlay_widget.negative_alignment[1] = is_negative_coord(overlay_widget.coords, 1,
145                                                             widgets_so_far)
146
147    if is_graph_type(overlay_widget.type):
148        set_alignment_flags(overlay_widget.description, widgets_so_far)
149
150
151def get_offset_helper(widget, axis, smaller_coord_side):
152    # Assume axis is X.  This function returns two values:
153    # - An offset where the bounding box is placed at,
154    # - Whether this offset is for the left or right edge.
155    #
156    # The input coordinate (widget.coord[axis]) is either:
157    #
158    # - a number: in this case, the offset is that number, and its sign determines whether this refers to the left or right edge of the bounding box.
159    # - other_widget.edge.mode: this has multiple possibilities:
160    #   * edge=left, mode=align: the offset is other_widget.left, the edge is left.
161    #   * edge=left, mode=adjacent: the offset is other_widget.left, the edge is right.
162    #   * edge=right, mode=align: the offset is other_widget.right, the edge is right.
163    #   * edge=right, mode=adjacent: the offset is other_widget.right, the edge is left.
164    #
165    # The case for the Y axis is similar, with the edge values being top or bottom.
166
167    coord = widget.coords[axis]
168    if not isinstance(coord, unicode):
169        is_left = coord >= 0
170        return coord, is_left
171
172    coord_split = coord.split('.')
173
174    is_left = coord_split[1] == smaller_coord_side
175    is_align = coord_split[2] == 'align'
176
177    other_widget_coords = 'mState.mOverlayWidgets[WidgetId::' + coord_split[0] + ']->coords'
178    other_widget_coord_index = axis + (0 if is_left else 2)
179    offset = other_widget_coords + '[' + str(other_widget_coord_index) + ']'
180
181    return offset, is_left == is_align
182
183
184def get_offset_x(widget):
185    return get_offset_helper(widget, 0, 'left')
186
187
188def get_offset_y(widget):
189    return get_offset_helper(widget, 1, 'top')
190
191
192def get_bounding_box_coords(offset, width, offset_is_left, is_left_aligned):
193    # See comment in generate_widget_init_helper.  This function is implementing the following:
194    #
195    # -  offset_is_left &&  is_left_aligned: [offset, offset + width]
196    # -  offset_is_left && !is_left_aligned: [offset, std::min(offset + width, -1)]
197    # - !offset_is_left &&  is_left_aligned: [std::max(1, offset - width), offset]
198    # - !offset_is_left && !is_left_aligned: [offset - width, offset]
199
200    coord_left = offset if offset_is_left else (offset + ' - ' + width)
201    coord_right = (offset + ' + ' + width) if offset_is_left else offset
202
203    if offset_is_left and not is_left_aligned:
204        coord_right = 'std::min(' + coord_right + ', -1)'
205    if not offset_is_left and is_left_aligned:
206        coord_left = 'std::max(' + coord_left + ', 1)'
207
208    return coord_left, coord_right
209
210
211def generate_widget_init_helper(widget, is_graph_description=False):
212    font_size = '0'
213
214    # Common attributes
215    color = [channel / 255.0 for channel in widget.color]
216    offset_x, offset_x_is_left = get_offset_x(widget)
217    offset_y, offset_y_is_top = get_offset_y(widget)
218
219    if is_text_type(widget.type):
220        # Attributes deriven from text properties
221        font_size = widget.font
222        width = str(widget.length) + ' * kFontGlyphWidths[fontSize]'
223        height = 'kFontGlyphHeights[fontSize]'
224    else:
225        # Attributes deriven from graph properties
226        width = str(widget.bar_width) + ' * static_cast<uint32_t>(widget->runningValues.size())'
227        height = widget.height
228
229    is_left_aligned = not widget.negative_alignment[0]
230    is_top_aligned = not widget.negative_alignment[1]
231
232    # We have offset_x, offset_y, width and height which together determine the bounding box.  If
233    # offset_x_is_left, the bounding box X would be in [offset_x, offset_x + width], otherwise it
234    # would be in [offset_x - width, offset_x].  Similarly for y.  Since we use negative values to
235    # mean aligned to the right side of the screen, we need to make sure that:
236    #
237    # - if left aligned: offset_x - width is at minimum 1
238    # - if right aligned: offset_x + width is at maximum -1
239    #
240    # We therefore have the following combinations for the X axis:
241    #
242    # -  offset_x_is_left &&  is_left_aligned: [offset_x, offset_x + width]
243    # -  offset_x_is_left && !is_left_aligned: [offset_x, std::min(offset_x + width, -1)]
244    # - !offset_x_is_left &&  is_left_aligned: [std::max(1, offset_x - width), offset_x]
245    # - !offset_x_is_left && !is_left_aligned: [offset_x - width, offset_x]
246    #
247    # Similarly for y.
248    coord0, coord2 = get_bounding_box_coords('offsetX', 'width', offset_x_is_left, is_left_aligned)
249    coord1, coord3 = get_bounding_box_coords('offsetY', 'height', offset_y_is_top, is_top_aligned)
250
251    return template_init_widget.format(
252        subwidget='description.' if is_graph_description else '',
253        offset_x=offset_x,
254        offset_y=offset_y,
255        width=width,
256        height=height,
257        type=widget.type,
258        font_size=font_size,
259        coord0=coord0,
260        coord1=coord1,
261        coord2=coord2,
262        coord3=coord3,
263        color_r=color[0],
264        color_g=color[1],
265        color_b=color[2],
266        color_a=color[3])
267
268
269def generate_widget_init(widget):
270    widget_init = '{\n' + widget.type + ' *widget = new ' + widget.constructor + ';\n'
271
272    widget_init += generate_widget_init_helper(widget)
273    widget_init += 'mState.mOverlayWidgets[WidgetId::' + widget.name + '].reset(widget);\n'
274
275    if is_graph_type(widget.type):
276        widget_init += generate_widget_init_helper(widget.description, True)
277
278    widget_init += '}\n'
279
280    return widget_init
281
282
283def main():
284    if len(sys.argv) == 2 and sys.argv[1] == 'inputs':
285        print(in_file)
286        return
287    if len(sys.argv) == 2 and sys.argv[1] == 'outputs':
288        print(out_file)
289        return
290
291    with open(in_file) as fin:
292        layout = json.loads(fin.read())
293
294    # Read the layouts from the json file and determine alignment of widgets (as they can refer to
295    # other widgets.
296    overlay_widgets = {}
297    for widget_properties in layout['widgets']:
298        widget = OverlayWidget(widget_properties)
299        overlay_widgets[widget.name] = widget
300        set_alignment_flags(widget, overlay_widgets)
301
302    # Go over the widgets again and generate initialization code.  Note that we need to iterate over
303    # the widgets in order, so we can't use the overlay_widgets dictionary for iteration.
304    init_widgets = []
305    for widget_properties in layout['widgets']:
306        init_widgets.append(generate_widget_init(overlay_widgets[widget_properties['name']]))
307
308    with open(out_file, 'w') as outfile:
309        outfile.write(
310            template_out_file.format(
311                script_name=__file__,
312                copyright_year=date.today().year,
313                input_file_name=in_file,
314                out_file_name=out_file,
315                init_widgets='\n'.join(init_widgets)))
316        outfile.close()
317
318
319if __name__ == '__main__':
320    sys.exit(main())
321