blob: cfada074e5a775d5ba59737126b788aa662a9033 [file] [log] [blame]
/*
* Copyright 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* transform type flags */
const TRANSLATE_VAL = 0x0001;
const ROTATE_VAL = 0x0002;
const SCALE_VAL = 0x0004;
/* orientation flags */
const FLIP_H_VAL = 0x0100; // (1 << 0 << 8)
const FLIP_V_VAL = 0x0200; // (1 << 1 << 8)
const ROT_90_VAL = 0x0400; // (1 << 2 << 8)
const ROT_INVALID_VAL = 0x8000; // (0x80 << 8)
function is_proto_2(transform) {
/*
* Checks if the loaded file was a stored with ProtoBuf2 or Protobuf3
*
* Proto2 files don't have a Type for the transform object but all other
* fields of the transform are set.
*
* Proto3 has a type field for the transform but doesn't store default
* values (0 for transform type), also, the framework/native implementation
* doesn't write a transform in case it is an identity matrix.
*/
var propertyNames = Object.getOwnPropertyNames(transform);
return (!propertyNames.includes("type") && propertyNames.includes("dsdx"));
}
function is_simple_transform(transform) {
transform = transform || {};
if (is_proto_2(transform)) {
return false;
}
return is_type_flag_clear(transform, ROT_INVALID_VAL|SCALE_VAL);
}
/**
* Converts a transform type into readable format.
* Adapted from the dump function from framework/native
*
* @param {*} transform Transform object ot be converter
*/
function format_transform_type(transform) {
if (is_proto_2(transform)) {
return "";
}
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL | TRANSLATE_VAL)) {
return "IDENTITY";
}
var type_flags = [];
if (is_type_flag_set(transform, SCALE_VAL)) {
type_flags.push("SCALE");
}
if (is_type_flag_set(transform, TRANSLATE_VAL)) {
type_flags.push("TRANSLATE");
}
if (is_type_flag_set(transform, ROT_INVALID_VAL)) {
type_flags.push("ROT_INVALID");
} else if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
type_flags.push("ROT_270");
} else if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
type_flags.push("ROT_180");
} else {
if (is_type_flag_set(transform, ROT_90_VAL)) {
type_flags.push("ROT_90");
}
if (is_type_flag_set(transform, FLIP_V_VAL)) {
type_flags.push("FLIP_V");
}
if (is_type_flag_set(transform, FLIP_H_VAL)) {
type_flags.push("FLIP_H");
}
}
if (type_flags.length == 0) {
throw "Unknown transform type " + transform ;
}
return type_flags.join(', ');
}
/**
* Ensures all values of the transform object are set.
*/
function fill_transform_data(transform) {
function fill_simple_transform(transform) {
// ROT_270 = ROT_90|FLIP_H|FLIP_V;
if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
transform.dsdx = 0.0;
transform.dtdx = -1.0;
transform.dsdy = 1.0;
transform.dtdy = 0.0;
return;
}
// ROT_180 = FLIP_H|FLIP_V;
if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
transform.dsdx = -1.0;
transform.dtdx = 0.0;
transform.dsdy = 0.0;
transform.dtdy = -1.0;
return;
}
// ROT_90
if (is_type_flag_set(transform, ROT_90_VAL)) {
transform.dsdx = 0.0;
transform.dtdx = 1.0;
transform.dsdy = -1.0;
transform.dtdy = 0.0;
return;
}
// IDENTITY
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL)) {
transform.dsdx = 1.0;
transform.dtdx = 0.0;
transform.dsdy = 0.0;
transform.dtdy = 1.0;
transform.type = 0;
return;
}
throw "Unknown transform type " + transform;
}
if (!transform) {
return;
}
if (is_proto_2(transform)) {
return;
}
if (is_simple_transform(transform)){
fill_simple_transform(transform);
}
transform.dsdx = transform.dsdx || 0.0;
transform.dtdx = transform.dtdx || 0.0;
transform.dsdy = transform.dsdy || 0.0;
transform.dtdy = transform.dtdy || 0.0;
}
function is_type_flag_set(transform, bits) {
transform = transform || {};
var type = transform.type || 0;
return (type & bits) === bits;
}
function is_type_flag_clear(transform, bits) {
transform = transform || {};
var type = transform.type || 0;
return (type & bits) === 0;
}
function multiply_vec2(matrix, x, y) {
if (!matrix) return {x, y};
// |dsdx dsdy tx| | x |
// |dtdx dtdy ty| x | y |
// |0 0 1 | | 1 |
return {
x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty
};
}
function multiply_rect(matrix, rect) {
// |dsdx dsdy tx| | left, top |
// matrix = |dtdx dtdy ty| rect = | |
// |0 0 1 | | right, bottom |
var left_top = multiply_vec2(matrix, rect.left, rect.top);
var right_top = multiply_vec2(matrix, rect.right, rect.top);
var left_bottom = multiply_vec2(matrix, rect.left, rect.bottom);
var right_bottom = multiply_vec2(matrix, rect.right, rect.bottom);
var outrect = {};
outrect.left = Math.min(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
outrect.top = Math.min(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
outrect.right = Math.max(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
outrect.bottom = Math.max(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
return outrect;
}
// Returns true if the applying the transform on an an axis aligned rectangle
// results in another axis aligned rectangle.
function is_simple_rotation(transform) {
return !is_type_flag_set(transform, ROT_INVALID_VAL);
}
export {format_transform_type, fill_transform_data, is_simple_transform,
multiply_rect, is_simple_rotation};