|
|
@@ -1,121 +1,82 @@
|
|
|
-import { SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, Rec709Primaries, P3Primaries, SRGBTransfer, LinearTransfer, NoColorSpace, } from '../constants.js';
|
|
|
+import { SRGBColorSpace, LinearSRGBColorSpace, SRGBTransfer, LinearTransfer, NoColorSpace } from '../constants.js';
|
|
|
import { Matrix3 } from './Matrix3.js';
|
|
|
|
|
|
-/**
|
|
|
- * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping
|
|
|
- * or clipping. Based on W3C specifications for sRGB and Display P3,
|
|
|
- * and ICC specifications for the D50 connection space. Values in/out
|
|
|
- * are _linear_ sRGB and _linear_ Display P3.
|
|
|
- *
|
|
|
- * Note that both sRGB and Display P3 use the sRGB transfer functions.
|
|
|
- *
|
|
|
- * Reference:
|
|
|
- * - http://www.russellcottrell.com/photo/matrixCalculator.htm
|
|
|
- */
|
|
|
-
|
|
|
-const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set(
|
|
|
- 0.8224621, 0.177538, 0.0,
|
|
|
- 0.0331941, 0.9668058, 0.0,
|
|
|
- 0.0170827, 0.0723974, 0.9105199,
|
|
|
-);
|
|
|
-
|
|
|
-const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set(
|
|
|
- 1.2249401, - 0.2249404, 0.0,
|
|
|
- - 0.0420569, 1.0420571, 0.0,
|
|
|
- - 0.0196376, - 0.0786361, 1.0982735
|
|
|
-);
|
|
|
-
|
|
|
-/**
|
|
|
- * Defines supported color spaces by transfer function and primaries,
|
|
|
- * and provides conversions to/from the Linear-sRGB reference space.
|
|
|
- */
|
|
|
-const COLOR_SPACES = {
|
|
|
- [ LinearSRGBColorSpace ]: {
|
|
|
- transfer: LinearTransfer,
|
|
|
- primaries: Rec709Primaries,
|
|
|
- luminanceCoefficients: [ 0.2126, 0.7152, 0.0722 ],
|
|
|
- toReference: ( color ) => color,
|
|
|
- fromReference: ( color ) => color,
|
|
|
- },
|
|
|
- [ SRGBColorSpace ]: {
|
|
|
- transfer: SRGBTransfer,
|
|
|
- primaries: Rec709Primaries,
|
|
|
- luminanceCoefficients: [ 0.2126, 0.7152, 0.0722 ],
|
|
|
- toReference: ( color ) => color.convertSRGBToLinear(),
|
|
|
- fromReference: ( color ) => color.convertLinearToSRGB(),
|
|
|
- },
|
|
|
- [ LinearDisplayP3ColorSpace ]: {
|
|
|
- transfer: LinearTransfer,
|
|
|
- primaries: P3Primaries,
|
|
|
- luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
|
|
|
- toReference: ( color ) => color.applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),
|
|
|
- fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ),
|
|
|
- },
|
|
|
- [ DisplayP3ColorSpace ]: {
|
|
|
- transfer: SRGBTransfer,
|
|
|
- primaries: P3Primaries,
|
|
|
- luminanceCoefficients: [ 0.2289, 0.6917, 0.0793 ],
|
|
|
- toReference: ( color ) => color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ),
|
|
|
- fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(),
|
|
|
- },
|
|
|
-};
|
|
|
-
|
|
|
-const SUPPORTED_WORKING_COLOR_SPACES = new Set( [ LinearSRGBColorSpace, LinearDisplayP3ColorSpace ] );
|
|
|
-
|
|
|
export const ColorManagement = {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
- _workingColorSpace: LinearSRGBColorSpace,
|
|
|
+ workingColorSpace: LinearSRGBColorSpace,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Implementations of supported color spaces.
|
|
|
+ *
|
|
|
+ * Required:
|
|
|
+ * - primaries: chromaticity coordinates [ rx ry gx gy bx by ]
|
|
|
+ * - whitePoint: reference white [ x y ]
|
|
|
+ * - transfer: transfer function (pre-defined)
|
|
|
+ * - toXYZ: Matrix3 RGB to XYZ transform
|
|
|
+ * - fromXYZ: Matrix3 XYZ to RGB transform
|
|
|
+ * - luminanceCoefficients: RGB luminance coefficients
|
|
|
+ *
|
|
|
+ * Optional:
|
|
|
+ * - outputColorSpaceConfig: { drawingBufferColorSpace: ColorSpace }
|
|
|
+ * - workingColorSpaceConfig: { unpackColorSpace: ColorSpace }
|
|
|
+ *
|
|
|
+ * Reference:
|
|
|
+ * - https://www.russellcottrell.com/photo/matrixCalculator.htm
|
|
|
+ */
|
|
|
+ spaces: {},
|
|
|
|
|
|
- get workingColorSpace() {
|
|
|
+ convert: function ( color, sourceColorSpace, targetColorSpace ) {
|
|
|
|
|
|
- return this._workingColorSpace;
|
|
|
+ if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
|
|
|
|
|
|
- },
|
|
|
+ return color;
|
|
|
|
|
|
- set workingColorSpace( colorSpace ) {
|
|
|
+ }
|
|
|
|
|
|
- if ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) {
|
|
|
+ if ( this.spaces[ sourceColorSpace ].transfer === SRGBTransfer ) {
|
|
|
|
|
|
- throw new Error( `Unsupported working color space, "${ colorSpace }".` );
|
|
|
+ color.r = SRGBToLinear( color.r );
|
|
|
+ color.g = SRGBToLinear( color.g );
|
|
|
+ color.b = SRGBToLinear( color.b );
|
|
|
|
|
|
}
|
|
|
|
|
|
- this._workingColorSpace = colorSpace;
|
|
|
+ if ( this.spaces[ sourceColorSpace ].primaries !== this.spaces[ targetColorSpace ].primaries ) {
|
|
|
|
|
|
- },
|
|
|
+ color.applyMatrix3( this.spaces[ sourceColorSpace ].toXYZ );
|
|
|
+ color.applyMatrix3( this.spaces[ targetColorSpace ].fromXYZ );
|
|
|
|
|
|
- convert: function ( color, sourceColorSpace, targetColorSpace ) {
|
|
|
+ }
|
|
|
|
|
|
- if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
|
|
|
+ if ( this.spaces[ targetColorSpace ].transfer === SRGBTransfer ) {
|
|
|
|
|
|
- return color;
|
|
|
+ color.r = LinearToSRGB( color.r );
|
|
|
+ color.g = LinearToSRGB( color.g );
|
|
|
+ color.b = LinearToSRGB( color.b );
|
|
|
|
|
|
}
|
|
|
|
|
|
- const sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference;
|
|
|
- const targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference;
|
|
|
-
|
|
|
- return targetFromReference( sourceToReference( color ) );
|
|
|
+ return color;
|
|
|
|
|
|
},
|
|
|
|
|
|
fromWorkingColorSpace: function ( color, targetColorSpace ) {
|
|
|
|
|
|
- return this.convert( color, this._workingColorSpace, targetColorSpace );
|
|
|
+ return this.convert( color, this.workingColorSpace, targetColorSpace );
|
|
|
|
|
|
},
|
|
|
|
|
|
toWorkingColorSpace: function ( color, sourceColorSpace ) {
|
|
|
|
|
|
- return this.convert( color, sourceColorSpace, this._workingColorSpace );
|
|
|
+ return this.convert( color, sourceColorSpace, this.workingColorSpace );
|
|
|
|
|
|
},
|
|
|
|
|
|
getPrimaries: function ( colorSpace ) {
|
|
|
|
|
|
- return COLOR_SPACES[ colorSpace ].primaries;
|
|
|
+ return this.spaces[ colorSpace ].primaries;
|
|
|
|
|
|
},
|
|
|
|
|
|
@@ -123,16 +84,44 @@ export const ColorManagement = {
|
|
|
|
|
|
if ( colorSpace === NoColorSpace ) return LinearTransfer;
|
|
|
|
|
|
- return COLOR_SPACES[ colorSpace ].transfer;
|
|
|
+ return this.spaces[ colorSpace ].transfer;
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) {
|
|
|
+
|
|
|
+ return target.fromArray( this.spaces[ colorSpace ].luminanceCoefficients );
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ define: function ( colorSpaces ) {
|
|
|
+
|
|
|
+ Object.assign( this.spaces, colorSpaces );
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ // Internal APIs
|
|
|
+
|
|
|
+ _getMatrix: function ( targetMatrix, sourceColorSpace, targetColorSpace ) {
|
|
|
+
|
|
|
+ return targetMatrix
|
|
|
+ .copy( this.spaces[ sourceColorSpace ].toXYZ )
|
|
|
+ .multiply( this.spaces[ targetColorSpace ].fromXYZ );
|
|
|
|
|
|
},
|
|
|
|
|
|
- getLuminanceCoefficients: function ( target, colorSpace = this._workingColorSpace ) {
|
|
|
+ _getDrawingBufferColorSpace: function ( colorSpace ) {
|
|
|
|
|
|
- return target.fromArray( COLOR_SPACES[ colorSpace ].luminanceCoefficients );
|
|
|
+ return this.spaces[ colorSpace ].outputColorSpaceConfig.drawingBufferColorSpace;
|
|
|
|
|
|
},
|
|
|
|
|
|
+ _getUnpackColorSpace: function ( colorSpace = this.workingColorSpace ) {
|
|
|
+
|
|
|
+ return this.spaces[ colorSpace ].workingColorSpaceConfig.unpackColorSpace;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -147,3 +136,47 @@ export function LinearToSRGB( c ) {
|
|
|
return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+/******************************************************************************
|
|
|
+ * sRGB definitions
|
|
|
+ */
|
|
|
+
|
|
|
+const REC709_PRIMARIES = [ 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 ];
|
|
|
+const REC709_LUMINANCE_COEFFICIENTS = [ 0.2126, 0.7152, 0.0722 ];
|
|
|
+const D65 = [ 0.3127, 0.3290 ];
|
|
|
+
|
|
|
+const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
|
|
|
+ 0.4123908, 0.3575843, 0.1804808,
|
|
|
+ 0.2126390, 0.7151687, 0.0721923,
|
|
|
+ 0.0193308, 0.1191948, 0.9505322
|
|
|
+);
|
|
|
+
|
|
|
+const XYZ_TO_LINEAR_REC709 = /*@__PURE__*/ new Matrix3().set(
|
|
|
+ 3.2409699, - 1.5373832, - 0.4986108,
|
|
|
+ - 0.9692436, 1.8759675, 0.0415551,
|
|
|
+ 0.0556301, - 0.2039770, 1.0569715
|
|
|
+);
|
|
|
+
|
|
|
+ColorManagement.define( {
|
|
|
+
|
|
|
+ [ LinearSRGBColorSpace ]: {
|
|
|
+ primaries: REC709_PRIMARIES,
|
|
|
+ whitePoint: D65,
|
|
|
+ transfer: LinearTransfer,
|
|
|
+ toXYZ: LINEAR_REC709_TO_XYZ,
|
|
|
+ fromXYZ: XYZ_TO_LINEAR_REC709,
|
|
|
+ luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
|
|
|
+ workingColorSpaceConfig: { unpackColorSpace: SRGBColorSpace }
|
|
|
+ },
|
|
|
+
|
|
|
+ [ SRGBColorSpace ]: {
|
|
|
+ primaries: REC709_PRIMARIES,
|
|
|
+ whitePoint: D65,
|
|
|
+ transfer: SRGBTransfer,
|
|
|
+ toXYZ: LINEAR_REC709_TO_XYZ,
|
|
|
+ fromXYZ: XYZ_TO_LINEAR_REC709,
|
|
|
+ luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
|
|
|
+ outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
|
|
|
+ },
|
|
|
+
|
|
|
+} );
|