pixi.js
Version:
PixiJS — The HTML5 Creation Engine =============
395 lines (392 loc) • 12.5 kB
JavaScript
import { extend, colord } from '@pixi/colord';
import namesPlugin from '@pixi/colord/plugins/names';
"use strict";
extend([namesPlugin]);
const _Color = class _Color {
/**
* @param {ColorSource} value - Optional value to use, if not provided, white is used.
*/
constructor(value = 16777215) {
this._value = null;
this._components = new Float32Array(4);
this._components.fill(1);
this._int = 16777215;
this.value = value;
}
/** Get red component (0 - 1) */
get red() {
return this._components[0];
}
/** Get green component (0 - 1) */
get green() {
return this._components[1];
}
/** Get blue component (0 - 1) */
get blue() {
return this._components[2];
}
/** Get alpha component (0 - 1) */
get alpha() {
return this._components[3];
}
/**
* Set the value, suitable for chaining
* @param value
* @see Color.value
*/
setValue(value) {
this.value = value;
return this;
}
/**
* The current color source.
*
* When setting:
* - Setting to an instance of `Color` will copy its color source and components.
* - Otherwise, `Color` will try to normalize the color source and set the components.
* If the color source is invalid, an `Error` will be thrown and the `Color` will left unchanged.
*
* Note: The `null` in the setter's parameter type is added to match the TypeScript rule: return type of getter
* must be assignable to its setter's parameter type. Setting `value` to `null` will throw an `Error`.
*
* When getting:
* - A return value of `null` means the previous value was overridden (e.g., {@link Color.multiply multiply},
* {@link Color.premultiply premultiply} or {@link Color.round round}).
* - Otherwise, the color source used when setting is returned.
*/
set value(value) {
if (value instanceof _Color) {
this._value = this._cloneSource(value._value);
this._int = value._int;
this._components.set(value._components);
} else if (value === null) {
throw new Error("Cannot set Color#value to null");
} else if (this._value === null || !this._isSourceEqual(this._value, value)) {
this._normalize(value);
this._value = this._cloneSource(value);
}
}
get value() {
return this._value;
}
/**
* Copy a color source internally.
* @param value - Color source
*/
_cloneSource(value) {
if (typeof value === "string" || typeof value === "number" || value instanceof Number || value === null) {
return value;
} else if (Array.isArray(value) || ArrayBuffer.isView(value)) {
return value.slice(0);
} else if (typeof value === "object" && value !== null) {
return { ...value };
}
return value;
}
/**
* Equality check for color sources.
* @param value1 - First color source
* @param value2 - Second color source
* @returns `true` if the color sources are equal, `false` otherwise.
*/
_isSourceEqual(value1, value2) {
const type1 = typeof value1;
const type2 = typeof value2;
if (type1 !== type2) {
return false;
} else if (type1 === "number" || type1 === "string" || value1 instanceof Number) {
return value1 === value2;
} else if (Array.isArray(value1) && Array.isArray(value2) || ArrayBuffer.isView(value1) && ArrayBuffer.isView(value2)) {
if (value1.length !== value2.length) {
return false;
}
return value1.every((v, i) => v === value2[i]);
} else if (value1 !== null && value2 !== null) {
const keys1 = Object.keys(value1);
const keys2 = Object.keys(value2);
if (keys1.length !== keys2.length) {
return false;
}
return keys1.every((key) => value1[key] === value2[key]);
}
return value1 === value2;
}
/**
* Convert to a RGBA color object.
* @example
* import { Color } from 'pixi.js';
* new Color('white').toRgb(); // returns { r: 1, g: 1, b: 1, a: 1 }
*/
toRgba() {
const [r, g, b, a] = this._components;
return { r, g, b, a };
}
/**
* Convert to a RGB color object.
* @example
* import { Color } from 'pixi.js';
* new Color('white').toRgb(); // returns { r: 1, g: 1, b: 1 }
*/
toRgb() {
const [r, g, b] = this._components;
return { r, g, b };
}
/** Convert to a CSS-style rgba string: `rgba(255,255,255,1.0)`. */
toRgbaString() {
const [r, g, b] = this.toUint8RgbArray();
return `rgba(${r},${g},${b},${this.alpha})`;
}
toUint8RgbArray(out) {
const [r, g, b] = this._components;
if (!this._arrayRgb) {
this._arrayRgb = [];
}
out = out || this._arrayRgb;
out[0] = Math.round(r * 255);
out[1] = Math.round(g * 255);
out[2] = Math.round(b * 255);
return out;
}
toArray(out) {
if (!this._arrayRgba) {
this._arrayRgba = [];
}
out = out || this._arrayRgba;
const [r, g, b, a] = this._components;
out[0] = r;
out[1] = g;
out[2] = b;
out[3] = a;
return out;
}
toRgbArray(out) {
if (!this._arrayRgb) {
this._arrayRgb = [];
}
out = out || this._arrayRgb;
const [r, g, b] = this._components;
out[0] = r;
out[1] = g;
out[2] = b;
return out;
}
/**
* Convert to a hexadecimal number.
* @example
* import { Color } from 'pixi.js';
* new Color('white').toNumber(); // returns 16777215
*/
toNumber() {
return this._int;
}
/**
* Convert to a BGR number
* @example
* import { Color } from 'pixi.js';
* new Color(0xffcc99).toBgrNumber(); // returns 0x99ccff
*/
toBgrNumber() {
const [r, g, b] = this.toUint8RgbArray();
return (b << 16) + (g << 8) + r;
}
/**
* Convert to a hexadecimal number in little endian format (e.g., BBGGRR).
* @example
* import { Color } from 'pixi.js';
* new Color(0xffcc99).toLittleEndianNumber(); // returns 0x99ccff
* @returns {number} - The color as a number in little endian format.
*/
toLittleEndianNumber() {
const value = this._int;
return (value >> 16) + (value & 65280) + ((value & 255) << 16);
}
/**
* Multiply with another color. This action is destructive, and will
* override the previous `value` property to be `null`.
* @param {ColorSource} value - The color to multiply by.
*/
multiply(value) {
const [r, g, b, a] = _Color._temp.setValue(value)._components;
this._components[0] *= r;
this._components[1] *= g;
this._components[2] *= b;
this._components[3] *= a;
this._refreshInt();
this._value = null;
return this;
}
/**
* Converts color to a premultiplied alpha format. This action is destructive, and will
* override the previous `value` property to be `null`.
* @param alpha - The alpha to multiply by.
* @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels.
* @returns {Color} - Itself.
*/
premultiply(alpha, applyToRGB = true) {
if (applyToRGB) {
this._components[0] *= alpha;
this._components[1] *= alpha;
this._components[2] *= alpha;
}
this._components[3] = alpha;
this._refreshInt();
this._value = null;
return this;
}
/**
* Premultiplies alpha with current color.
* @param {number} alpha - The alpha to multiply by.
* @param {boolean} [applyToRGB=true] - Whether to premultiply RGB channels.
* @returns {number} tint multiplied by alpha
*/
toPremultiplied(alpha, applyToRGB = true) {
if (alpha === 1) {
return (255 << 24) + this._int;
}
if (alpha === 0) {
return applyToRGB ? 0 : this._int;
}
let r = this._int >> 16 & 255;
let g = this._int >> 8 & 255;
let b = this._int & 255;
if (applyToRGB) {
r = r * alpha + 0.5 | 0;
g = g * alpha + 0.5 | 0;
b = b * alpha + 0.5 | 0;
}
return (alpha * 255 << 24) + (r << 16) + (g << 8) + b;
}
/**
* Convert to a hexadecimal string.
* @example
* import { Color } from 'pixi.js';
* new Color('white').toHex(); // returns "#ffffff"
*/
toHex() {
const hexString = this._int.toString(16);
return `#${"000000".substring(0, 6 - hexString.length) + hexString}`;
}
/**
* Convert to a hexadecimal string with alpha.
* @example
* import { Color } from 'pixi.js';
* new Color('white').toHexa(); // returns "#ffffffff"
*/
toHexa() {
const alphaValue = Math.round(this._components[3] * 255);
const alphaString = alphaValue.toString(16);
return this.toHex() + "00".substring(0, 2 - alphaString.length) + alphaString;
}
/**
* Set alpha, suitable for chaining.
* @param alpha
*/
setAlpha(alpha) {
this._components[3] = this._clamp(alpha);
return this;
}
/**
* Normalize the input value into rgba
* @param value - Input value
*/
_normalize(value) {
let r;
let g;
let b;
let a;
if ((typeof value === "number" || value instanceof Number) && value >= 0 && value <= 16777215) {
const int = value;
r = (int >> 16 & 255) / 255;
g = (int >> 8 & 255) / 255;
b = (int & 255) / 255;
a = 1;
} else if ((Array.isArray(value) || value instanceof Float32Array) && value.length >= 3 && value.length <= 4) {
value = this._clamp(value);
[r, g, b, a = 1] = value;
} else if ((value instanceof Uint8Array || value instanceof Uint8ClampedArray) && value.length >= 3 && value.length <= 4) {
value = this._clamp(value, 0, 255);
[r, g, b, a = 255] = value;
r /= 255;
g /= 255;
b /= 255;
a /= 255;
} else if (typeof value === "string" || typeof value === "object") {
if (typeof value === "string") {
const match = _Color.HEX_PATTERN.exec(value);
if (match) {
value = `#${match[2]}`;
}
}
const color = colord(value);
if (color.isValid()) {
({ r, g, b, a } = color.rgba);
r /= 255;
g /= 255;
b /= 255;
}
}
if (r !== void 0) {
this._components[0] = r;
this._components[1] = g;
this._components[2] = b;
this._components[3] = a;
this._refreshInt();
} else {
throw new Error(`Unable to convert color ${value}`);
}
}
/** Refresh the internal color rgb number */
_refreshInt() {
this._clamp(this._components);
const [r, g, b] = this._components;
this._int = (r * 255 << 16) + (g * 255 << 8) + (b * 255 | 0);
}
/**
* Clamps values to a range. Will override original values
* @param value - Value(s) to clamp
* @param min - Minimum value
* @param max - Maximum value
*/
_clamp(value, min = 0, max = 1) {
if (typeof value === "number") {
return Math.min(Math.max(value, min), max);
}
value.forEach((v, i) => {
value[i] = Math.min(Math.max(v, min), max);
});
return value;
}
/**
* Check if the value is a color-like object
* @param value - Value to check
* @returns True if the value is a color-like object
* @static
* @example
* import { Color } from 'pixi.js';
* Color.isColorLike('white'); // returns true
* Color.isColorLike(0xffffff); // returns true
* Color.isColorLike([1, 1, 1]); // returns true
*/
static isColorLike(value) {
return typeof value === "number" || typeof value === "string" || value instanceof Number || value instanceof _Color || Array.isArray(value) || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Float32Array || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 || value.r !== void 0 && value.g !== void 0 && value.b !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 || value.h !== void 0 && value.s !== void 0 && value.l !== void 0 && value.a !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 || value.h !== void 0 && value.s !== void 0 && value.v !== void 0 && value.a !== void 0;
}
};
/**
* Default Color object for static uses
* @example
* import { Color } from 'pixi.js';
* Color.shared.setValue(0xffffff).toHex(); // '#ffffff'
*/
_Color.shared = new _Color();
/**
* Temporary Color object for static uses internally.
* As to not conflict with Color.shared.
* @ignore
*/
_Color._temp = new _Color();
/** Pattern for hex strings */
// eslint-disable-next-line @typescript-eslint/naming-convention
_Color.HEX_PATTERN = /^(#|0x)?(([a-f0-9]{3}){1,2}([a-f0-9]{2})?)$/i;
let Color = _Color;
export { Color };
//# sourceMappingURL=Color.mjs.map