mirror of
https://github.com/galacean/engine.git
synced 2026-07-03 07:04:42 +08:00
388 lines
9.6 KiB
TypeScript
388 lines
9.6 KiB
TypeScript
import { IClone } from "./IClone";
|
|
import { ICopy } from "./ICopy";
|
|
import { MathUtil } from "./MathUtil";
|
|
|
|
/**
|
|
* Describes a color in the from of RGBA (in order: R, G, B, A).
|
|
*/
|
|
export class Color implements IClone<Color>, ICopy<ColorLike, Color> {
|
|
/**
|
|
* Modify a value from the sRGB space to the linear space.
|
|
* @param value - The value in sRGB space
|
|
* @returns The value in linear space
|
|
*/
|
|
static sRGBToLinearSpace(value: number): number {
|
|
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_sRGB.txt
|
|
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_sRGB_decode.txt
|
|
|
|
if (value <= 0.0) return 0.0;
|
|
else if (value <= 0.04045) return value / 12.92;
|
|
else if (value < 1.0) return Math.pow((value + 0.055) / 1.055, 2.4);
|
|
else return Math.pow(value, 2.4);
|
|
}
|
|
|
|
/**
|
|
* Modify a value from the linear space to the sRGB space.
|
|
* @param value - The value in linear space
|
|
* @returns The value in sRGB space
|
|
*/
|
|
static linearToSRGBSpace(value: number): number {
|
|
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_sRGB.txt
|
|
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_sRGB_decode.txt
|
|
|
|
if (value <= 0.0) return 0.0;
|
|
else if (value < 0.0031308) return 12.92 * value;
|
|
else if (value < 1.0) return 1.055 * Math.pow(value, 0.41666) - 0.055;
|
|
else return Math.pow(value, 0.41666);
|
|
}
|
|
|
|
/**
|
|
* Determines whether the specified colors are equals.
|
|
* @param left - The first color to compare
|
|
* @param right - The second color to compare
|
|
* @returns True if the specified colors are equals, false otherwise
|
|
*/
|
|
static equals(left: Color, right: Color): boolean {
|
|
return (
|
|
MathUtil.equals(left._r, right._r) &&
|
|
MathUtil.equals(left._g, right._g) &&
|
|
MathUtil.equals(left._b, right._b) &&
|
|
MathUtil.equals(left._a, right._a)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Determines the sum of two colors.
|
|
* @param left - The first color to add
|
|
* @param right - The second color to add
|
|
* @param out - The sum of two colors
|
|
* @returns The added color
|
|
*/
|
|
static add(left: Color, right: Color, out: Color): Color {
|
|
out._r = left._r + right._r;
|
|
out._g = left._g + right._g;
|
|
out._b = left._b + right._b;
|
|
out._a = left._a + right._a;
|
|
out._onValueChanged?.();
|
|
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Determines the difference between two colors.
|
|
* @param left - The first color to subtract
|
|
* @param right - The second color to subtract
|
|
* @param out - The difference between two colors
|
|
*/
|
|
static subtract(left: Color, right: Color, out: Color): void {
|
|
out._r = left._r - right._r;
|
|
out._g = left._g - right._g;
|
|
out._b = left._b - right._b;
|
|
out._a = left._a - right._a;
|
|
out._onValueChanged?.();
|
|
}
|
|
|
|
/**
|
|
* Scale a color by the given value.
|
|
* @param left - The color to scale
|
|
* @param s - The amount by which to scale the color
|
|
* @param out - The scaled color
|
|
* @returns The scaled color
|
|
*/
|
|
static scale(left: Color, s: number, out: Color): Color {
|
|
out._r = left._r * s;
|
|
out._g = left._g * s;
|
|
out._b = left._b * s;
|
|
out._a = left._a * s;
|
|
out._onValueChanged?.();
|
|
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Performs a linear interpolation between two color.
|
|
* @param start - The first color
|
|
* @param end - The second color
|
|
* @param t - The blend amount where 0 returns start and 1 end
|
|
* @param out - The result of linear blending between two color
|
|
*/
|
|
static lerp(start: Color, end: Color, t: number, out: Color): Color {
|
|
const { _r, _g, _b, _a } = start;
|
|
out._r = _r + (end._r - _r) * t;
|
|
out._g = _g + (end._g - _g) * t;
|
|
out._b = _b + (end._b - _b) * t;
|
|
out._a = _a + (end._a - _a) * t;
|
|
out._onValueChanged?.();
|
|
|
|
return out;
|
|
}
|
|
|
|
/** @internal */
|
|
_r: number;
|
|
/** @internal */
|
|
_g: number;
|
|
/** @internal */
|
|
_b: number;
|
|
/** @internal */
|
|
_a: number;
|
|
/** @internal */
|
|
_onValueChanged: () => void = null;
|
|
|
|
/**
|
|
* The red component of the color, 0~1.
|
|
*/
|
|
public get r(): number {
|
|
return this._r;
|
|
}
|
|
|
|
public set r(value: number) {
|
|
this._r = value;
|
|
this._onValueChanged?.();
|
|
}
|
|
|
|
/**
|
|
* The green component of the color, 0~1.
|
|
*/
|
|
public get g(): number {
|
|
return this._g;
|
|
}
|
|
|
|
public set g(value: number) {
|
|
this._g = value;
|
|
this._onValueChanged?.();
|
|
}
|
|
|
|
/**
|
|
* The blue component of the color, 0~1.
|
|
*/
|
|
public get b(): number {
|
|
return this._b;
|
|
}
|
|
|
|
public set b(value: number) {
|
|
this._b = value;
|
|
this._onValueChanged?.();
|
|
}
|
|
|
|
/**
|
|
* The alpha component of the color, 0~1.
|
|
*/
|
|
public get a(): number {
|
|
return this._a;
|
|
}
|
|
|
|
public set a(value: number) {
|
|
this._a = value;
|
|
this._onValueChanged?.();
|
|
}
|
|
|
|
/**
|
|
* Constructor of Color.
|
|
* @param r - The red component of the color
|
|
* @param g - The green component of the color
|
|
* @param b - The blue component of the color
|
|
* @param a - The alpha component of the color
|
|
*/
|
|
constructor(r: number = 1, g: number = 1, b: number = 1, a: number = 1) {
|
|
this._r = r;
|
|
this._g = g;
|
|
this._b = b;
|
|
this._a = a;
|
|
}
|
|
|
|
/**
|
|
* Set the value of this color.
|
|
* @param r - The red component of the color
|
|
* @param g - The green component of the color
|
|
* @param b - The blue component of the color
|
|
* @param a - The alpha component of the color
|
|
* @returns This color.
|
|
*/
|
|
set(r: number, g: number, b: number, a: number): Color {
|
|
this._r = r;
|
|
this._g = g;
|
|
this._b = b;
|
|
this._a = a;
|
|
this._onValueChanged?.();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Determines the sum of this color and the specified color.
|
|
* @param color - The specified color
|
|
* @returns The added color
|
|
*/
|
|
add(color: Color): Color {
|
|
this._r += color._r;
|
|
this._g += color._g;
|
|
this._b += color._b;
|
|
this._a += color._a;
|
|
this._onValueChanged?.();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Scale this color by the given value.
|
|
* @param s - The amount by which to scale the color
|
|
* @returns The scaled color
|
|
*/
|
|
scale(s: number): Color {
|
|
this._r *= s;
|
|
this._g *= s;
|
|
this._b *= s;
|
|
this._a *= s;
|
|
this._onValueChanged?.();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Creates a clone of this color.
|
|
* @returns A clone of this color
|
|
*/
|
|
clone(): Color {
|
|
const ret = new Color(this._r, this._g, this._b, this._a);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Copy from color like object.
|
|
* @param source - Color like object.
|
|
* @returns This vector
|
|
*/
|
|
copyFrom(source: ColorLike): Color {
|
|
this._r = source.r;
|
|
this._g = source.g;
|
|
this._b = source.b;
|
|
this._a = source.a;
|
|
this._onValueChanged?.();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Copy to color like object.
|
|
* @param target - Color like object.
|
|
* @returns This Color like object
|
|
*/
|
|
copyTo(target: ColorLike): ColorLike {
|
|
target.r = this._r;
|
|
target.g = this._g;
|
|
target.b = this._b;
|
|
target.a = this._a;
|
|
return target;
|
|
}
|
|
|
|
/**
|
|
* Copy from array like object.
|
|
* @param source - Array like object
|
|
* @param offset - The start offset
|
|
* @returns This color
|
|
*/
|
|
copyFromArray(source: ArrayLike<number>, offset: number = 0): Color {
|
|
this._r = source[offset];
|
|
this._g = source[offset + 1];
|
|
this._b = source[offset + 2];
|
|
this._a = source[offset + 3];
|
|
this._onValueChanged?.();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Copy the value of this color to an array.
|
|
* @param out - The color
|
|
* @param outOffset - The start offset
|
|
*/
|
|
copyToArray(out: number[] | Float32Array | Float64Array, outOffset: number = 0): void {
|
|
out[outOffset] = this._r;
|
|
out[outOffset + 1] = this._g;
|
|
out[outOffset + 2] = this._b;
|
|
out[outOffset + 3] = this._a;
|
|
}
|
|
|
|
/**
|
|
* Modify components (r, g, b) of this color from gamma space to linear space.
|
|
* @param out - The color in linear space
|
|
* @returns The color in linear space
|
|
*/
|
|
toLinear(out: Color): Color {
|
|
out._r = Color.sRGBToLinearSpace(this._r);
|
|
out._g = Color.sRGBToLinearSpace(this._g);
|
|
out._b = Color.sRGBToLinearSpace(this._b);
|
|
out._a = this._a;
|
|
out._onValueChanged?.();
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Modify components (r, g, b) of this color from linear space to sRGB space.
|
|
* @param out - The color in sRGB space
|
|
* @returns The color in sRGB space
|
|
*/
|
|
toSRGB(out: Color): Color {
|
|
out._r = Color.linearToSRGBSpace(this._r);
|
|
out._g = Color.linearToSRGBSpace(this._g);
|
|
out._b = Color.linearToSRGBSpace(this._b);
|
|
out._a = this._a;
|
|
out._onValueChanged?.();
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Gets the brightness.
|
|
* @returns The Hue-Saturation-Brightness (HSB) saturation for this
|
|
*/
|
|
getBrightness(): number {
|
|
const r = this.r;
|
|
const g = this.g;
|
|
const b = this.b;
|
|
|
|
let max = r;
|
|
let min = r;
|
|
if (g > max) max = g;
|
|
if (b > max) max = b;
|
|
|
|
if (g < min) min = g;
|
|
if (b < min) min = b;
|
|
|
|
return (max + min) / 2;
|
|
}
|
|
|
|
/**
|
|
* Serialize this color to a JSON representation.
|
|
* @return A JSON representation of this color
|
|
*/
|
|
toJSON(): ColorLike {
|
|
return {
|
|
r: this._r,
|
|
g: this._g,
|
|
b: this._b,
|
|
a: this._a
|
|
};
|
|
}
|
|
|
|
/** @deprecated Please use `sRGBToLinearSpace` instead. */
|
|
static gammaToLinearSpace(value: number): number {
|
|
return Color.sRGBToLinearSpace(value);
|
|
}
|
|
|
|
/** @deprecated Please use `linearToSRGBSpace` instead. */
|
|
static linearToGammaSpace(value: number): number {
|
|
return Color.linearToSRGBSpace(value);
|
|
}
|
|
|
|
/** @deprecated Please use `toSRGB` instead. */
|
|
toGamma(out: Color): Color {
|
|
return this.toSRGB(out);
|
|
}
|
|
}
|
|
|
|
interface ColorLike {
|
|
/** {@inheritDoc Color._r} */
|
|
r: number;
|
|
/** {@inheritDoc Color._g} */
|
|
g: number;
|
|
/** {@inheritDoc Color._b} */
|
|
b: number;
|
|
/** {@inheritDoc Color._a} */
|
|
a: number;
|
|
}
|