| Index: runtime/lib/pixels.dart
|
| diff --git a/runtime/lib/pixels.dart b/runtime/lib/pixels.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fa5eaed7dc8c1053adda41ad092c064bcd6a2ed5
|
| --- /dev/null
|
| +++ b/runtime/lib/pixels.dart
|
| @@ -0,0 +1,355 @@
|
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +// TODO(regis): Move abstract class pixels to sdk/lib/core/pixels.dart:
|
| +abstract class pixels extends num {
|
| + static const pixels NAN = 0px / 0px;
|
| + static const pixels INFINITY = 1px / 0px;
|
| + static const pixels NEGATIVE_INFINITY = -INFINITY;
|
| + // TODO(regis): Expand as needed with API documentation.
|
| +}
|
| +
|
| +class _Pixels implements pixels {
|
| + static const int _SCALING_BITS = 6;
|
| + static const int _SCALING_FACTOR = 1 << _SCALING_BITS;
|
| + static const int _HALF_VALUE = _SCALING_FACTOR >> 1;
|
| + static const int _NAN_VALUE = (1 << 30) - 1; // Same as kSmiMax32. Positive.
|
| + static const int _PLUS_INF_VALUE = _NAN_VALUE - 1; // Same as kSmiMax32 - 1.
|
| + static const int _MINUS_INF_VALUE = -_PLUS_INF_VALUE; // Same as kSmiMin32 + 2.
|
| +
|
| + // TODO(regis): Detect _NAN_VALUE, _PLUS_INF_VALUE and _MINUS_INF_VALUE as
|
| + // operands to all operations and treat as non finite values, rather than min and max.
|
| +
|
| + final int _value;
|
| + static const pixels _PLUS_ONE = const _Pixels(1 << _SCALING_BITS);
|
| + static const pixels _MINUS_ONE = const _Pixels(-1 << _SCALING_BITS);
|
| + static const pixels _PLUS_INF = const _Pixels(_PLUS_INF_VALUE);
|
| + static const pixels _MINUS_INF = const _Pixels(_MINUS_INF_VALUE);
|
| + static const pixels _NAN = const _Pixels(_NAN_VALUE);
|
| +
|
| + // Argument is already a scaled value in valid range.
|
| + const _Pixels(int this._value);
|
| +
|
| + // Argument is already a scaled value, but not necessarily in valid range.
|
| + _Pixels._clamp(int this._value) {
|
| + if (_value > _PLUS_INF_VALUE) _value = _PLUS_INF_VALUE;
|
| + else if (_value < _MINUS_INF_VALUE) _value = _MINUS_INF_VALUE;
|
| + }
|
| +
|
| + factory _Pixels.fromInteger(int value) => new _Pixels._clamp(value << _SCALING_BITS);
|
| +
|
| + factory _Pixels.fromDouble(double value) => value.toPixels();
|
| +
|
| + _Pixels.fromPixelsLiteral(String value) {
|
| + final decimalPoint = ".".codeUnits.first;
|
| + final digit0 = "0".codeUnits.first;
|
| + final digit9 = "9".codeUnits.first;
|
| + var numerator = 0;
|
| + var denominator = 1;
|
| + bool seenDecimalPoint = false;
|
| + for (var i = 0; i < value.length; i++) {
|
| + var c = value.codeUnitAt(i);
|
| + if (!seenDecimalPoint && c == decimalPoint) {
|
| + seenDecimalPoint = true;
|
| + } else if (digit0 <= c && c <= digit9) {
|
| + numerator = numerator*10 + (c - digit0);
|
| + if (seenDecimalPoint) denominator *= 10;
|
| + } else {
|
| + break; // TODO(regis): Error handling. Expect px or PX here.
|
| + }
|
| + }
|
| + _value = (numerator << _SCALING_BITS) ~/ denominator;
|
| + if (_value > _PLUS_INF_VALUE) _value = _PLUS_INF_VALUE;
|
| + }
|
| +
|
| + Type get runtimeType => pixels;
|
| +
|
| + int get _identityHashCode {
|
| + return _value;
|
| + }
|
| +
|
| + num operator +(num other) {
|
| + return other._toDoubleOrPixels()._addFromPixels(this);
|
| + }
|
| + pixels _addFromPixels(pixels other) {
|
| + return other._add(this);
|
| + }
|
| + pixels _add(pixels other) {
|
| + return new _Pixels._clamp(_value + other._value);
|
| + }
|
| +
|
| + num operator -(num other) {
|
| + return other._toDoubleOrPixels()._subFromPixels(this);
|
| + }
|
| + pixels _subFromPixels(pixels other) {
|
| + return other._sub(this);
|
| + }
|
| + pixels _sub(pixels other) {
|
| + return new _Pixels._clamp(_value - other._value);
|
| + }
|
| +
|
| + num operator *(num other) {
|
| + return other._toDoubleOrPixels()._mulFromPixels(this);
|
| + }
|
| + pixels _mulFromPixels(pixels other) {
|
| + return other._mul(this);
|
| + }
|
| + pixels _mul(pixels other) {
|
| + // Use rounding.
|
| + return new _Pixels._clamp((_value*other._value + _HALF_VALUE) >> _SCALING_BITS);
|
| + }
|
| +
|
| + num operator /(num other) {
|
| + return other._toDoubleOrPixels()._divFromPixels(this);
|
| + }
|
| + pixels _divFromPixels(pixels other) {
|
| + return other._div(this);
|
| + }
|
| + pixels _div(pixels other) {
|
| + if (other._value == 0) {
|
| + if (_value > 0) {
|
| + return _PLUS_INF;
|
| + } else if (_value == 0) {
|
| + return _NAN;
|
| + } else {
|
| + return _MINUS_INF;
|
| + }
|
| + }
|
| + return new _Pixels._clamp((_value << _SCALING_BITS) ~/ other._value);
|
| + }
|
| +
|
| + num operator %(num other) {
|
| + return other._toDoubleOrPixels()._moduloFromPixels(this);
|
| + }
|
| + pixels _moduloFromPixels(pixels other) {
|
| + return other._modulo(this);
|
| + }
|
| + pixels _modulo(pixels other) {
|
| + var q = _div(other); // TODO(regis): Check NAN.
|
| + if (other._value < 0) {
|
| + q = q.ceil();
|
| + } else {
|
| + q = q.floor();
|
| + }
|
| + return _sub(q.toPixels()._mul(other));
|
| + }
|
| +
|
| + int operator ~/(num other) {
|
| + return other._toDoubleOrPixels()._truncDivFromPixels(this);
|
| + }
|
| + int _truncDivFromPixels(pixels other) {
|
| + return other._truncDiv(this);
|
| + }
|
| + int _truncDiv(pixels other) {
|
| + if (other._value == 0) throw const IntegerDivisionByZeroException();
|
| + return _value ~/ other._value;
|
| + }
|
| +
|
| + num remainder(num other) {
|
| + return other._toDoubleOrPixels()._remainderFromPixels(this);
|
| + }
|
| + pixels _remainderFromPixels(pixels other) {
|
| + return other._remainder(this);
|
| + }
|
| + pixels _remainder(pixels other) {
|
| + if (other._value == 0) throw const IntegerDivisionByZeroException();
|
| + return new _Pixels._clamp(_value - other._value*(_value ~/ other._value));
|
| + }
|
| +
|
| + pixels operator -() {
|
| + if (isNaN) return this;
|
| + return new _Pixels(-_value);
|
| + }
|
| +
|
| + bool operator ==(other) {
|
| + if (!(other is num)) return false;
|
| + return other._equalToPixels(this);
|
| + }
|
| + bool _equalToPixels(pixels other) {
|
| + return other._equal(this);
|
| + }
|
| + bool _equal(pixels other) {
|
| + return _value == other._value;
|
| + }
|
| +
|
| + bool _equalToInteger(int other) {
|
| + return _value == (other << _SCALING_BITS);
|
| + }
|
| +
|
| + bool operator <(num other) {
|
| + return other > this;
|
| + }
|
| + bool operator >(num other) {
|
| + return other._greaterThanFromPixels(this);
|
| + }
|
| + bool _greaterThanFromPixels(pixels other) {
|
| + return other._value > _value;
|
| + }
|
| + bool operator >=(num other) {
|
| + return (this == other) || (this > other);
|
| + }
|
| + bool operator <=(num other) {
|
| + return (this == other) || (this < other);
|
| + }
|
| +
|
| + pixels _addFromInteger(int other) {
|
| + return new _Pixels._clamp((other << _SCALING_BITS) + _value);
|
| + }
|
| +
|
| + pixels _subFromInteger(int other) {
|
| + return new _Pixels._clamp((other << _SCALING_BITS) - _value);
|
| + }
|
| +
|
| + pixels _mulFromInteger(int other) {
|
| + return new _Pixels._clamp(other * _value);
|
| + }
|
| +
|
| + pixels _divFromInteger(int other) {
|
| + if (_value == 0) {
|
| + if (other > 0) {
|
| + return _PLUS_INF;
|
| + } else if (other == 0) {
|
| + return _NAN;
|
| + } else {
|
| + return _MINUS_INF;
|
| + }
|
| + }
|
| + return new _Pixels._clamp((other << (2*_SCALING_BITS)) ~/ _value);
|
| + }
|
| +
|
| + pixels _moduloFromInteger(int other) {
|
| + var q = _divFromInteger(other);
|
| + if (_value < 0) {
|
| + q = q.ceil();
|
| + } else {
|
| + q = q.floor();
|
| + }
|
| + return _mul(q.toPixels())._subFromInteger(other);
|
| + }
|
| +
|
| + int _truncDivFromInteger(int other) {
|
| + if (_value == 0) throw const IntegerDivisionByZeroException();
|
| + return (other << _SCALING_BITS) ~/ _value;
|
| + }
|
| +
|
| + pixels _remainderFromInteger(int other) {
|
| + if (_value == 0) throw const IntegerDivisionByZeroException();
|
| + return new _Pixels._clamp(
|
| + (other << _SCALING_BITS) - _value*((other << _SCALING_BITS) ~/ _value));
|
| + }
|
| +
|
| + bool _greaterThanFromInteger(int other) {
|
| + return (other << _SCALING_BITS) > _value;
|
| + }
|
| +
|
| + bool get isNegative {
|
| + return _value < 0;
|
| + }
|
| +
|
| + bool get isInfinite {
|
| + return (_value == _PLUS_INF_VALUE) || (_value == _MINUS_INF_VALUE);
|
| + }
|
| + bool get isNaN => _value == _NAN_VALUE;
|
| + bool get isFinite {
|
| + return (_value != _PLUS_INF_VALUE) &&
|
| + (_value != _MINUS_INF_VALUE) &&
|
| + (_value != _NAN_VALUE);
|
| + }
|
| +
|
| + pixels abs() {
|
| + return isNegative ? -this : this;
|
| + }
|
| +
|
| + pixels get sign {
|
| + if (_value > 0) return _PLUS_ONE;
|
| + if (_value < 0) return _MINUS_ONE;
|
| + return this;
|
| + }
|
| +
|
| + int round() {
|
| + if (!isFinite) throw new UnsupportedError("Must be finite");
|
| + return (_value + _HALF_VALUE) >> _SCALING_BITS;
|
| + }
|
| + int floor() {
|
| + if (!isFinite) throw new UnsupportedError("Must be finite");
|
| + return _value >> _SCALING_BITS;
|
| + }
|
| + int ceil() {
|
| + if (!isFinite) throw new UnsupportedError("Must be finite");
|
| + return (_value + _SCALING_FACTOR - 1) >> _SCALING_BITS;
|
| + }
|
| + int truncate() {
|
| + if (!isFinite) throw new UnsupportedError("Must be finite");
|
| + return (_value + ((_value < 0) ? (_SCALING_FACTOR - 1) : 0)) >> _SCALING_BITS;
|
| + }
|
| +
|
| + double roundToDouble() => round().toDouble();
|
| + double floorToDouble() => floor().toDouble();
|
| + double ceilToDouble() => ceil().toDouble();
|
| + double truncateToDouble() => truncate().toDouble();
|
| +
|
| + num clamp(num lowerLimit, num upperLimit) {
|
| + if (lowerLimit is! num) {
|
| + throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number");
|
| + }
|
| + if (upperLimit is! num) {
|
| + throw new ArgumentError.value(upperLimit, "upperLimit", "not a number");
|
| + }
|
| +
|
| + if (lowerLimit.compareTo(upperLimit) > 0) {
|
| + throw new ArgumentError(lowerLimit);
|
| + }
|
| + if (lowerLimit.isNaN) return lowerLimit;
|
| + if (this.compareTo(lowerLimit) < 0) return lowerLimit;
|
| + if (this.compareTo(upperLimit) > 0) return upperLimit;
|
| + return this;
|
| + }
|
| +
|
| + int toInt() => truncate(); // TODO(regis): Should we use round() instead?
|
| + double toDouble() {
|
| + if (_value == _PLUS_INF_VALUE) return double.INFINITY;
|
| + if (_value == _MINUS_INF_VALUE) return double.NEGATIVE_INFINITY;
|
| + if (_value == _NAN_VALUE) return double.NAN;
|
| + return _value / _SCALING_FACTOR;
|
| + }
|
| + pixels toPixels() { return this; }
|
| + pixels _toDoubleOrPixels() { return this; }
|
| + num _toBigintIfInteger() { return this; }
|
| +
|
| + String toString() => "${toDouble()}px";
|
| +
|
| + int compareTo(num other) {
|
| + const int EQUAL = 0, LESS = -1, GREATER = 1;
|
| + if (isNaN) {
|
| + return GREATER; // TODO(regis): Correct?
|
| + }
|
| + if (other is double) {
|
| + double d = other;
|
| + if (d.isInfinite) {
|
| + if (isInfinite) {
|
| + if (((d == double.NEGATIVE_INFINITY) && (_value < 0)) ||
|
| + ((d == double.INFINITY) && (_value > 0))) {
|
| + return EQUAL;
|
| + }
|
| + }
|
| + return _value > 0 ? GREATER : LESS;
|
| + }
|
| + if (d.isNaN) {
|
| + return LESS; // TODO(regis): unless this.isNaN? See above.
|
| + }
|
| + if ((d == 0.0) && (_value == 0)) {
|
| + return d.isNegative ? GREATER : EQUAL;
|
| + }
|
| + other = other.toPixels();
|
| + }
|
| + if (this < other) {
|
| + return LESS;
|
| + } else if (this > other) {
|
| + return GREATER;
|
| + } else {
|
| + return EQUAL;
|
| + }
|
| + }
|
| +}
|
| +
|
|
|