Skip to content

Commit

Permalink
Use Color ubiquitously
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Nov 1, 2017
1 parent e6f3399 commit 670108d
Show file tree
Hide file tree
Showing 29 changed files with 229 additions and 201 deletions.
9 changes: 5 additions & 4 deletions src/data/program_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type StyleLayer from '../style/style_layer';
import type {ViewType, StructArray, SerializedStructArray, StructArrayTypeParameters} from '../util/struct_array';
import type Program from '../render/program';
import type {Feature} from '../style-spec/expression';
import type Color from '../style-spec/util/color';

type LayoutAttribute = {
name: string,
Expand Down Expand Up @@ -35,10 +36,10 @@ export type ProgramInterface = {
indexArrayType2?: Class<StructArray>
}

function packColor(color: [number, number, number, number]): [number, number] {
function packColor(color: Color): [number, number] {
return [
packUint8ToFloat(255 * color[0], 255 * color[1]),
packUint8ToFloat(255 * color[2], 255 * color[3])
packUint8ToFloat(255 * color.r, 255 * color.g),
packUint8ToFloat(255 * color.b, 255 * color.a)
];
}

Expand Down Expand Up @@ -82,7 +83,7 @@ class ConstantBinder implements Binder {
setUniforms(gl: WebGLRenderingContext, program: Program, layer: StyleLayer, {zoom}: { zoom: number }) {
const value = layer.getPaintValue(this.property, { zoom: this.useIntegerZoom ? Math.floor(zoom) : zoom });
if (this.type === 'color') {
gl.uniform4fv(program.uniforms[`u_${this.name}`], value);
gl.uniform4f(program.uniforms[`u_${this.name}`], value.r, value.g, value.b, value.a);
} else {
gl.uniform1f(program.uniforms[`u_${this.name}`], value);
}
Expand Down
7 changes: 4 additions & 3 deletions src/render/draw_background.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const pattern = require('./pattern');
import type Painter from './painter';
import type SourceCache from '../source/source_cache';
import type BackgroundStyleLayer from '../style/style_layer/background_style_layer';
import type Color from '../style-spec/util/color';

module.exports = drawBackground;

Expand All @@ -14,11 +15,11 @@ function drawBackground(painter: Painter, sourceCache: SourceCache, layer: Backg
const gl = painter.gl;
const transform = painter.transform;
const tileSize = transform.tileSize;
const color = layer.paint['background-color'];
const color: Color = layer.paint['background-color'];
const image = layer.paint['background-pattern'];
const opacity = layer.paint['background-opacity'];

const pass = (!image && color[3] === 1 && opacity === 1) ? 'opaque' : 'translucent';
const pass = (!image && color.a === 1 && opacity === 1) ? 'opaque' : 'translucent';
if (painter.renderPass !== pass) return;

gl.disable(gl.STENCIL_TEST);
Expand All @@ -33,7 +34,7 @@ function drawBackground(painter: Painter, sourceCache: SourceCache, layer: Backg
painter.tileExtentPatternVAO.bind(gl, program, painter.tileExtentBuffer);
} else {
program = painter.useProgram('fill', painter.basicFillProgramConfiguration);
gl.uniform4fv(program.uniforms.u_color, color);
gl.uniform4f(program.uniforms.u_color, color.r, color.g, color.b, color.a);
painter.tileExtentVAO.bind(gl, program, painter.tileExtentBuffer);
}

Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function drawFill(painter: Painter, sourceCache: SourceCache, layer: FillStyleLa
const pass = (!layer.paint['fill-pattern'] &&
layer.isPaintValueFeatureConstant('fill-color') &&
layer.isPaintValueFeatureConstant('fill-opacity') &&
layer.paint['fill-color'][3] === 1 &&
layer.paint['fill-color'].a === 1 &&
layer.paint['fill-opacity'] === 1) ? 'opaque' : 'translucent';

// Draw fill
Expand Down
2 changes: 1 addition & 1 deletion src/render/draw_fill_extrusion.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,5 @@ function setLight(program, painter) {

gl.uniform3fv(program.uniforms.u_lightpos, lightPos);
gl.uniform1f(program.uniforms.u_lightintensity, light.calculated.intensity);
gl.uniform3fv(program.uniforms.u_lightcolor, light.calculated.color.slice(0, 3));
gl.uniform3f(program.uniforms.u_lightcolor, light.calculated.color.r, light.calculated.color.g, light.calculated.color.b);
}
4 changes: 2 additions & 2 deletions src/style-spec/expression/definitions/coercion.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const {
NumberType,
} = require('../types');

const { Color, validateRGBA, unwrap } = require('../values');
const { Color, validateRGBA } = require('../values');
const RuntimeError = require('../runtime_error');

import type { Expression } from '../expression';
Expand Down Expand Up @@ -88,7 +88,7 @@ class Coercion implements Expression {
if (isNaN(num)) continue;
return num;
}
throw new RuntimeError(`Could not convert ${JSON.stringify(unwrap(value))} to number.`);
throw new RuntimeError(`Could not convert ${JSON.stringify(value)} to number.`);
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/style-spec/expression/definitions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ CompoundExpression.register(expressions, {
if (v === null || type === 'string' || type === 'number' || type === 'boolean') {
return String(v);
} else if (v instanceof Color) {
const [r, g, b, a] = v.value;
return `rgba(${r * 255},${g * 255},${b * 255},${a})`;
return `rgba(${v.r * 255},${v.g * 255},${v.b * 255},${v.a})`;
} else {
return JSON.stringify(v);
}
Expand All @@ -118,7 +117,10 @@ CompoundExpression.register(expressions, {
'to-rgba': [
array(NumberType, 4),
[ColorType],
(ctx, [v]) => v.evaluate(ctx).value
(ctx, [v]) => {
const {r, g, b, a} = v.evaluate(ctx);
return [r, g, b, a];
}
],
'rgb': [
ColorType,
Expand Down
8 changes: 1 addition & 7 deletions src/style-spec/expression/definitions/interpolate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const UnitBezier = require('@mapbox/unitbezier');
const interpolate = require('../../util/interpolate');
const { toString, NumberType } = require('../types');
const { Color } = require('../values');
const { findStopLessThanOrEqualTo } = require("../stops");

import type { Stops } from '../stops';
Expand Down Expand Up @@ -167,12 +166,7 @@ class Interpolate implements Expression {
const outputLower = outputs[index].evaluate(ctx);
const outputUpper = outputs[index + 1].evaluate(ctx);

const type = this.type.kind.toLowerCase();
if (type === 'color') {
return new Color(...interpolate.color((outputLower: any).value, (outputUpper: any).value, t));
}

return interpolate[type](outputLower, outputUpper, t);
return (interpolate[this.type.kind.toLowerCase()]: any)(outputLower, outputUpper, t);
}

eachChild(fn: (Expression) => void) {
Expand Down
4 changes: 1 addition & 3 deletions src/style-spec/expression/evaluation_context.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const assert = require('assert');
const Scope = require('./scope');
const parseColor = require('../util/parse_color');
const {Color} = require('./values');

import type { Feature, GlobalProperties } from './index';
Expand Down Expand Up @@ -46,8 +45,7 @@ class EvaluationContext {
parseColor(input: string): ?Color {
let cached = this._parseColorCache[input];
if (!cached) {
const c = parseColor(input);
cached = this._parseColorCache[input] = c ? new Color(c[0], c[1], c[2], c[3]) : null;
cached = this._parseColorCache[input] = Color.parse(input);
}
return cached;
}
Expand Down
24 changes: 11 additions & 13 deletions src/style-spec/expression/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const Coalesce = require('./definitions/coalesce');
const Let = require('./definitions/let');
const definitions = require('./definitions');
const isConstant = require('./is_constant');
const {unwrap} = require('./values');

import type {Type} from './types';
import type {Value} from './values';
Expand Down Expand Up @@ -147,17 +146,17 @@ function createExpression(expression: mixed,
try {
const val = parsed.evaluate(evaluator);
if (val === null || val === undefined) {
return unwrap(defaultValue);
return defaultValue;
}
return unwrap(val);
return val;
} catch (e) {
if (!warningHistory[e.message]) {
warningHistory[e.message] = true;
if (typeof console !== 'undefined') {
console.warn(e.message);
}
}
return unwrap(defaultValue);
return defaultValue;
}
};
}
Expand Down Expand Up @@ -293,20 +292,19 @@ function getExpectedType(spec: StylePropertySpecification): Type | null {
}

const {isFunction} = require('../function');
const parseColor = require('../util/parse_color');
const {Color} = require('./values');

function getDefaultValue(spec: StylePropertySpecification): Value | null {
const defaultValue = spec.default;
if (spec.type === 'color' && isFunction(defaultValue)) {
function getDefaultValue(spec: StylePropertySpecification): Value {
if (spec.type === 'color' && isFunction(spec.default)) {
// Special case for heatmap-color: it uses the 'default:' to define a
// default color ramp, but createExpression expects a simple value to fall
// back to in case of runtime errors
return [0, 0, 0, 0];
return new Color(0, 0, 0, 0);
} else if (spec.type === 'color') {
const c: [number, number, number, number] = (parseColor((defaultValue: any)): any);
assert(Array.isArray(c));
return new Color(c[0], c[1], c[2], c[3]);
return Color.parse(spec.default) || null;
} else if (spec.default === undefined) {
return null;
} else {
return spec.default;
}
return defaultValue === undefined ? null : defaultValue;
}
15 changes: 2 additions & 13 deletions src/style-spec/expression/values.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow

const assert = require('assert');
const Color = require('../util/color');

const {
NullType,
Expand All @@ -15,13 +16,6 @@ const {

import type { Type } from './types';

class Color {
value: [number, number, number, number];
constructor(r: number, g: number, b: number, a: number = 1) {
this.value = [r, g, b, a];
}
}

function validateRGBA(r: mixed, g: mixed, b: mixed, a?: mixed): ?string {
if (!(
typeof r === 'number' && r >= 0 && r <= 255 &&
Expand Down Expand Up @@ -107,14 +101,9 @@ function typeOf(value: Value): Type {
}
}

function unwrap(value: Value): mixed {
return value instanceof Color ? value.value : value;
}

module.exports = {
Color,
validateRGBA,
isValue,
typeOf,
unwrap
typeOf
};
10 changes: 5 additions & 5 deletions src/style-spec/function/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

const colorSpaces = require('../util/color_spaces');
const parseColor = require('../util/parse_color');
const Color = require('../util/color');
const extend = require('../util/extend');
const getType = require('../util/get_type');
const interpolate = require('../util/interpolate');
Expand All @@ -26,14 +26,14 @@ function createFunction(parameters, propertySpec, name) {

if (parameters.stops) {
parameters.stops = parameters.stops.map((stop) => {
return [stop[0], parseColor(stop[1])];
return [stop[0], Color.parse(stop[1])];
});
}

if (parameters.default) {
parameters.default = parseColor(parameters.default);
parameters.default = Color.parse(parameters.default);
} else {
parameters.default = parseColor(propertySpec.default);
parameters.default = Color.parse(propertySpec.default);
}
}

Expand Down Expand Up @@ -216,7 +216,7 @@ function evaluateExponentialFunction(parameters, propertySpec, input) {

function evaluateIdentityFunction(parameters, propertySpec, input) {
if (propertySpec.type === 'color') {
input = parseColor(input);
input = Color.parse(input);
} else if (getType(input) !== propertySpec.type && (propertySpec.type !== 'enum' || !propertySpec.values[input])) {
input = undefined;
}
Expand Down
49 changes: 49 additions & 0 deletions src/style-spec/util/color.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @flow

const {parseCSSColor} = require('csscolorparser');

/**
* An RGBA color value. All components are in the range [0, 1] and R, B, and G are premultiplied by A.
* @private
*/
class Color {
r: number;
g: number;
b: number;
a: number;

constructor(r: number, g: number, b: number, a: number = 1) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}

static parse(input: ?string): Color | void {
if (!input) {
return undefined;
}

if (input instanceof Color) {
return input;
}

if (typeof input !== 'string') {
return undefined;
}

const rgba = parseCSSColor(input);
if (!rgba) {
return undefined;
}

return new Color(
rgba[0] / 255 * rgba[3],
rgba[1] / 255 * rgba[3],
rgba[2] / 255 * rgba[3],
rgba[3]
);
}
}

module.exports = Color;
Loading

0 comments on commit 670108d

Please sign in to comment.