1/*
2 * Copyright 2019, 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/* transform type flags */
18const TRANSLATE_VAL   = 0x0001;
19const ROTATE_VAL      = 0x0002;
20const SCALE_VAL       = 0x0004;
21
22/* orientation flags */
23const FLIP_H_VAL      = 0x0100; // (1 << 0 << 8)
24const FLIP_V_VAL      = 0x0200; // (1 << 1 << 8)
25const ROT_90_VAL      = 0x0400; // (1 << 2 << 8)
26const ROT_INVALID_VAL = 0x8000; // (0x80 << 8)
27
28function is_proto_2(transform) {
29  /*
30  * Checks if the loaded file was a stored with ProtoBuf2 or Protobuf3
31  *
32  * Proto2 files don't have a Type for the transform object but all other
33  * fields of the transform are set.
34  *
35  * Proto3 has a type field for the transform but doesn't store default
36  * values (0 for transform type), also, the framework/native implementation
37  * doesn't write a transform in case it is an identity matrix.
38  */
39  var propertyNames = Object.getOwnPropertyNames(transform);
40  return (!propertyNames.includes("type") && propertyNames.includes("dsdx"));
41}
42
43function is_simple_transform(transform) {
44  transform = transform || {};
45  if (is_proto_2(transform)) {
46    return false;
47  }
48  return is_type_flag_clear(transform, ROT_INVALID_VAL|SCALE_VAL);
49}
50
51/**
52 * Converts a transform type into readable format.
53 * Adapted from the dump function from framework/native
54 *
55 * @param {*} transform Transform object ot be converter
56 */
57function format_transform_type(transform) {
58  if (is_proto_2(transform)) {
59    return "";
60  }
61
62  if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL | TRANSLATE_VAL)) {
63    return "IDENTITY";
64  }
65
66  var type_flags = [];
67  if (is_type_flag_set(transform, SCALE_VAL)) {
68    type_flags.push("SCALE");
69  }
70  if (is_type_flag_set(transform, TRANSLATE_VAL)) {
71    type_flags.push("TRANSLATE");
72  }
73
74  if (is_type_flag_set(transform, ROT_INVALID_VAL)) {
75    type_flags.push("ROT_INVALID");
76  } else if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
77    type_flags.push("ROT_270");
78  } else if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
79    type_flags.push("ROT_180");
80  } else {
81    if (is_type_flag_set(transform, ROT_90_VAL)) {
82      type_flags.push("ROT_90");
83    }
84    if (is_type_flag_set(transform, FLIP_V_VAL)) {
85      type_flags.push("FLIP_V");
86    }
87    if (is_type_flag_set(transform, FLIP_H_VAL)) {
88      type_flags.push("FLIP_H");
89    }
90  }
91
92  if (type_flags.length == 0) {
93    throw "Unknown transform type " + transform ;
94  }
95
96  return type_flags.join(', ');
97}
98
99
100/**
101 * Ensures all values of the transform object are set.
102 */
103function fill_transform_data(transform) {
104  function fill_simple_transform(transform) {
105    // ROT_270 = ROT_90|FLIP_H|FLIP_V;
106    if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
107      transform.dsdx =  0.0;
108      transform.dtdx = -1.0;
109      transform.dsdy = 1.0;
110      transform.dtdy = 0.0;
111      return;
112    }
113
114    // ROT_180 = FLIP_H|FLIP_V;
115    if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
116      transform.dsdx = -1.0;
117      transform.dtdx = 0.0;
118      transform.dsdy = 0.0;
119      transform.dtdy = -1.0;
120      return;
121    }
122
123    // ROT_90
124    if (is_type_flag_set(transform, ROT_90_VAL)) {
125      transform.dsdx = 0.0;
126      transform.dtdx = 1.0;
127      transform.dsdy = -1.0;
128      transform.dtdy = 0.0;
129      return;
130    }
131
132    // IDENTITY
133    if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL)) {
134      transform.dsdx = 1.0;
135      transform.dtdx = 0.0;
136      transform.dsdy = 0.0;
137      transform.dtdy = 1.0;
138      transform.type = 0;
139      return;
140    }
141
142    throw "Unknown transform type " + transform;
143  }
144
145  if (!transform) {
146    return;
147  }
148
149  if (is_proto_2(transform)) {
150    return;
151  }
152
153  if (is_simple_transform(transform)){
154    fill_simple_transform(transform);
155  }
156
157  transform.dsdx = transform.dsdx || 0.0;
158  transform.dtdx = transform.dtdx || 0.0;
159  transform.dsdy = transform.dsdy || 0.0;
160  transform.dtdy = transform.dtdy || 0.0;
161}
162
163function is_type_flag_set(transform, bits) {
164    transform = transform || {};
165    var type = transform.type || 0;
166    return (type & bits) === bits;
167}
168
169function is_type_flag_clear(transform, bits) {
170  transform = transform || {};
171  var type = transform.type || 0;
172  return (type & bits) === 0;
173}
174
175function multiply_vec2(matrix, x, y) {
176  if (!matrix) return {x, y};
177  // |dsdx dsdy  tx|     | x |
178  // |dtdx dtdy  ty|  x  | y |
179  // |0    0     1 |     | 1 |
180  return {
181    x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
182    y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty
183  };
184}
185
186function multiply_rect(matrix, rect) {
187  //          |dsdx dsdy  tx|         | left, top         |
188  // matrix = |dtdx dtdy  ty|  rect = |                   |
189  //          |0    0     1 |         |     right, bottom |
190
191  var left_top = multiply_vec2(matrix, rect.left, rect.top);
192  var right_top = multiply_vec2(matrix, rect.right, rect.top);
193  var left_bottom = multiply_vec2(matrix, rect.left, rect.bottom);
194  var right_bottom = multiply_vec2(matrix, rect.right, rect.bottom);
195
196  var outrect = {};
197  outrect.left   = Math.min(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
198  outrect.top    = Math.min(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
199  outrect.right  = Math.max(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
200  outrect.bottom = Math.max(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
201  return outrect;
202}
203
204// Returns true if the applying the transform on an an axis aligned rectangle
205// results in another axis aligned rectangle.
206function is_simple_rotation(transform) {
207  return !is_type_flag_set(transform, ROT_INVALID_VAL);
208}
209
210export {format_transform_type, fill_transform_data, is_simple_transform,
211        multiply_rect, is_simple_rotation};