Refactor ol.color to use Array.<number>
This commit is contained in:
+169
-58
@@ -1,72 +1,183 @@
|
|||||||
goog.provide('ol.Color');
|
// ol.color is based on goog.color and goog.color.alpha
|
||||||
|
// goog.color and goog.color.alpha use a hex string representation that encodes
|
||||||
|
// each channel as a byte (a two character hex string). This causes occasional
|
||||||
|
// loss of precision and rounding errors, especially in the alpha channel.
|
||||||
|
// FIXME don't use goog.color or goog.color.alpha
|
||||||
|
// FIXME move the color matrix code from ol.renderer.webgl.Layer to here
|
||||||
|
|
||||||
|
goog.provide('ol.color');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
goog.require('goog.color');
|
goog.require('goog.color');
|
||||||
|
goog.require('goog.color.alpha');
|
||||||
goog.require('goog.math');
|
goog.require('goog.math');
|
||||||
|
goog.require('goog.vec.Mat4');
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
* @param {number} r Red, 0 to 255.
|
|
||||||
* @param {number} g Green, 0 to 255.
|
|
||||||
* @param {number} b Blue, 0 to 255.
|
|
||||||
* @param {number} a Alpha, 0 (fully transparent) to 1 (fully opaque).
|
|
||||||
*/
|
|
||||||
ol.Color = function(r, g, b, a) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.r = goog.math.clamp(r, 0, 255);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.g = goog.math.clamp(g, 0, 255);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.b = goog.math.clamp(b, 0, 255);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.a = goog.math.clamp(a, 0, 1);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} str String.
|
* A color represented as a short array [red, green, blue, alpha].
|
||||||
* @param {number=} opt_a Alpha.
|
* red, green, and blue should be integers in the range 0..255 inclusive.
|
||||||
* @return {ol.Color} Color.
|
* alpha should be a float in the range 0..1 inclusive.
|
||||||
|
* @typedef {Array.<number>}
|
||||||
*/
|
*/
|
||||||
ol.Color.createFromString = function(str, opt_a) {
|
ol.Color;
|
||||||
var rgb = goog.color.hexToRgb(goog.color.parse(str).hex);
|
|
||||||
var a = goog.isDef(opt_a) ? opt_a : 1;
|
|
||||||
return new ol.Color(rgb[0], rgb[1], rgb[2], a);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {ol.Color} color1 Color 1.
|
|
||||||
* @param {ol.Color} color2 Color 2.
|
|
||||||
* @return {boolean} Equals.
|
|
||||||
*/
|
|
||||||
ol.Color.equals = function(color1, color2) {
|
|
||||||
return (color1.r == color2.r &&
|
|
||||||
color1.g == color2.g &&
|
|
||||||
color1.b == color2.b &&
|
|
||||||
color1.a == color2.a);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} s String.
|
* @param {string} s String.
|
||||||
|
* @param {ol.Color=} opt_color Color.
|
||||||
* @return {ol.Color} Color.
|
* @return {ol.Color} Color.
|
||||||
*/
|
*/
|
||||||
ol.Color.parse = function(s) {
|
ol.color.fromString = (function() {
|
||||||
var rgb = goog.color.hexToRgb(goog.color.parse(s).hex);
|
|
||||||
return new ol.Color(rgb[0], rgb[1], rgb[2], 1);
|
// We maintain a small cache of parsed strings. To provide cheap LRU-like
|
||||||
|
// semantics, whenever the cache grows too large we simply delete an
|
||||||
|
// arbitrary 25% of the entries.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @const
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
var MAX_CACHE_SIZE = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object.<string, ol.Color>}
|
||||||
|
*/
|
||||||
|
var cache = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
var cacheSize = 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
/**
|
||||||
|
* @param {string} s String.
|
||||||
|
* @param {ol.Color=} opt_color Color.
|
||||||
|
* @return {ol.Color} Color.
|
||||||
|
*/
|
||||||
|
function(s, opt_color) {
|
||||||
|
var color;
|
||||||
|
if (cache.hasOwnProperty(s)) {
|
||||||
|
color = cache[s];
|
||||||
|
} else {
|
||||||
|
if (cacheSize >= MAX_CACHE_SIZE) {
|
||||||
|
var i = 0;
|
||||||
|
var key;
|
||||||
|
for (key in cache) {
|
||||||
|
if (i++ & 3 === 0) {
|
||||||
|
delete cache[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color = ol.color.fromStringInternal_(s);
|
||||||
|
cache[s] = color;
|
||||||
|
++cacheSize;
|
||||||
|
}
|
||||||
|
return ol.color.returnOrUpdate(color, opt_color);
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s String.
|
||||||
|
* @private
|
||||||
|
* @return {ol.Color} Color.
|
||||||
|
*/
|
||||||
|
ol.color.fromStringInternal_ = function(s) {
|
||||||
|
|
||||||
|
/** @preserveTry */
|
||||||
|
try {
|
||||||
|
var rgba = goog.color.alpha.parse(s);
|
||||||
|
return goog.color.alpha.hexToRgba(rgba.hex);
|
||||||
|
} catch (e) {
|
||||||
|
// goog.color.alpha.parse throws an Error on named and rgb-style colors.
|
||||||
|
var rgb = goog.color.parse(s);
|
||||||
|
var result = goog.color.hexToRgb(rgb.hex);
|
||||||
|
result.push(1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Color} color Color.
|
||||||
|
* @return {boolean} Is valid.
|
||||||
|
*/
|
||||||
|
ol.color.isValid = function(color) {
|
||||||
|
return 0 <= color[0] && color[0] < 256 &&
|
||||||
|
0 <= color[1] && color[1] < 256 &&
|
||||||
|
0 <= color[2] && color[2] < 256 &&
|
||||||
|
0 <= color[3] && color[3] <= 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Color} color Color.
|
||||||
|
* @param {ol.Color=} opt_color Color.
|
||||||
|
* @return {ol.Color} Clamped color.
|
||||||
|
*/
|
||||||
|
ol.color.normalize = function(color, opt_color) {
|
||||||
|
var result = goog.isDef(opt_color) ? opt_color : [];
|
||||||
|
result[0] = goog.math.clamp((color[0] + 0.5) | 0, 0, 255);
|
||||||
|
result[1] = goog.math.clamp((color[1] + 0.5) | 0, 0, 255);
|
||||||
|
result[2] = goog.math.clamp((color[2] + 0.5) | 0, 0, 255);
|
||||||
|
result[3] = goog.math.clamp(color[3], 0, 1);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Color} color Color.
|
||||||
|
* @param {ol.Color=} opt_color Color.
|
||||||
|
* @return {ol.Color} Color.
|
||||||
|
*/
|
||||||
|
ol.color.returnOrUpdate = function(color, opt_color) {
|
||||||
|
if (goog.isDef(opt_color)) {
|
||||||
|
opt_color[0] = color[0];
|
||||||
|
opt_color[1] = color[1];
|
||||||
|
opt_color[2] = color[2];
|
||||||
|
opt_color[3] = color[3];
|
||||||
|
return opt_color;
|
||||||
|
} else {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Color} color Color.
|
||||||
|
* @return {string} String.
|
||||||
|
*/
|
||||||
|
ol.color.toString = function(color) {
|
||||||
|
var r = color[0];
|
||||||
|
if (r != (r | 0)) {
|
||||||
|
r = (r + 0.5) | 0;
|
||||||
|
}
|
||||||
|
var g = color[1];
|
||||||
|
if (g != (g | 0)) {
|
||||||
|
g = (g + 0.5) | 0;
|
||||||
|
}
|
||||||
|
var b = color[2];
|
||||||
|
if (b != (b | 0)) {
|
||||||
|
b = (b + 0.5) | 0;
|
||||||
|
}
|
||||||
|
var a = color[3];
|
||||||
|
return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Color} color Color.
|
||||||
|
* @param {goog.vec.Mat4.AnyType} transform Transform.
|
||||||
|
* @param {ol.Color=} opt_color Color.
|
||||||
|
* @return {ol.Color} Transformed color.
|
||||||
|
*/
|
||||||
|
ol.color.transform = function(color, transform, opt_color) {
|
||||||
|
var result = goog.isDef(opt_color) ? opt_color : [];
|
||||||
|
result = goog.vec.Mat4.multVec3(transform, color, result);
|
||||||
|
goog.asserts.assert(goog.isArray(result));
|
||||||
|
result[3] = color[3];
|
||||||
|
return ol.color.normalize(result, result);
|
||||||
};
|
};
|
||||||
|
|||||||
+72
-56
@@ -1,75 +1,91 @@
|
|||||||
goog.provide('ol.test.Color');
|
goog.provide('ol.test.color');
|
||||||
|
|
||||||
describe('ol.Color', function() {
|
|
||||||
|
|
||||||
describe('constructor', function() {
|
describe('ol.color', function() {
|
||||||
|
|
||||||
it('limits r to 0-255', function() {
|
describe('ol.color.fromString', function() {
|
||||||
var c;
|
|
||||||
|
|
||||||
// legit r
|
before(function() {
|
||||||
c = new ol.Color(10.5, 11, 12, 0.5);
|
sinon.spy(ol.color, 'fromStringInternal_');
|
||||||
expect(c.r).to.be(10.5);
|
|
||||||
|
|
||||||
// under r
|
|
||||||
c = new ol.Color(-10, 11, 12, 0.5);
|
|
||||||
expect(c.r).to.be(0);
|
|
||||||
|
|
||||||
// over r
|
|
||||||
c = new ol.Color(300, 11, 12, 0.5);
|
|
||||||
expect(c.r).to.be(255);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('limits g to 0-255', function() {
|
after(function() {
|
||||||
var c;
|
ol.color.fromStringInternal_.restore();
|
||||||
|
|
||||||
// legit g
|
|
||||||
c = new ol.Color(10, 11.5, 12, 0.5);
|
|
||||||
expect(c.g).to.be(11.5);
|
|
||||||
|
|
||||||
// under g
|
|
||||||
c = new ol.Color(10, -11, 12, 0.5);
|
|
||||||
expect(c.g).to.be(0);
|
|
||||||
|
|
||||||
// over g
|
|
||||||
c = new ol.Color(10, 275, 12, 0.5);
|
|
||||||
expect(c.g).to.be(255);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('limits b to 0-255', function() {
|
it('can parse named colors', function() {
|
||||||
var c;
|
expect(ol.color.fromString('red')).to.eql([255, 0, 0, 1]);
|
||||||
|
|
||||||
// legit b
|
|
||||||
c = new ol.Color(10, 11, 12.5, 0.5);
|
|
||||||
expect(c.b).to.be(12.5);
|
|
||||||
|
|
||||||
// under b
|
|
||||||
c = new ol.Color(10, 11, -12, 0.5);
|
|
||||||
expect(c.b).to.be(0);
|
|
||||||
|
|
||||||
// over b
|
|
||||||
c = new ol.Color(10, 11, 500, 0.5);
|
|
||||||
expect(c.b).to.be(255);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('limits a to 0-1', function() {
|
it('can parse hex colors', function() {
|
||||||
var c;
|
expect(ol.color.fromString('#00ff0080')).to.eql([0, 255, 0, 128 / 255]);
|
||||||
|
});
|
||||||
|
|
||||||
// legit a
|
it('can parse rgb colors', function() {
|
||||||
c = new ol.Color(10, 11, 12, 0.5);
|
expect(ol.color.fromString('rgb(0, 0, 255)')).to.eql([0, 0, 255, 1]);
|
||||||
expect(c.a).to.be(0.5);
|
});
|
||||||
|
|
||||||
// under a
|
it('can parse rgba colors', function() {
|
||||||
c = new ol.Color(10, 11, 12, -0.5);
|
expect(ol.color.fromString('rgba(255, 255, 0, 0.1)')).to.eql(
|
||||||
expect(c.a).to.be(0);
|
[255, 255, 0, 25 / 255]);
|
||||||
|
});
|
||||||
|
|
||||||
// over a
|
it('caches parsed values', function() {
|
||||||
c = new ol.Color(10, 11, 12, 2.5);
|
var count = ol.color.fromStringInternal_.callCount;
|
||||||
expect(c.a).to.be(1);
|
ol.color.fromString('aquamarine');
|
||||||
|
expect(ol.color.fromStringInternal_.callCount).to.be(count + 1);
|
||||||
|
ol.color.fromString('aquamarine');
|
||||||
|
expect(ol.color.fromStringInternal_.callCount).to.be(count + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ol.color.isValid', function() {
|
||||||
|
|
||||||
|
it('identifies valid colors', function() {
|
||||||
|
expect(ol.color.isValid([0, 0, 0, 0])).to.be(true);
|
||||||
|
expect(ol.color.isValid([255, 255, 255, 1])).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('identifies out-of-range channels', function() {
|
||||||
|
expect(ol.color.isValid([-1, 0, 0, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([256, 0, 0, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, -1, 0, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 256, 0, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, -1, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, 256, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, -1, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, 256, 0])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, 0, -1])).to.be(false);
|
||||||
|
expect(ol.color.isValid([0, 0, 0, 2])).to.be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ol.color.normalize', function() {
|
||||||
|
|
||||||
|
it('clamps out-of-range channels', function() {
|
||||||
|
expect(ol.color.normalize([-1, 256, 0, 2])).to.eql([0, 255, 0, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds color channels to integers', function() {
|
||||||
|
expect(ol.color.normalize([1.2, 2.5, 3.7, 1])).to.eql([1, 3, 4, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ol.color.toString', function() {
|
||||||
|
|
||||||
|
it('converts valid colors', function() {
|
||||||
|
expect(ol.color.toString([1, 2, 3, 0.4])).to.be('rgba(1,2,3,0.4)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds to integers if needed', function() {
|
||||||
|
expect(ol.color.toString([1.2, 2.5, 3.7, 0.4])).to.be('rgba(1,3,4,0.4)');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
goog.require('ol.Color');
|
|
||||||
|
goog.require('ol.color');
|
||||||
|
|||||||
Reference in New Issue
Block a user