Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(346)

Unified Diff: runtime/lib/pixels.dart

Issue 2035453002: Pixels class prototype and test (not to be committed). Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: work in progress Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/lib/integers.dart ('k') | runtime/observatory/tests/service/get_source_report_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
+ }
+ }
+}
+
« no previous file with comments | « runtime/lib/integers.dart ('k') | runtime/observatory/tests/service/get_source_report_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698