Skip to content

RenderColor

Under Construction

This documentation is auto-generated, and is a work in progress. Please see the source code at https://github.com/phetsims/alpenglow/blob/main/js/render-program/RenderColor.ts for the most up-to-date information.

Overview

RenderColor displays a single solid color everywhere, and is a basic building-block for many other RenderPrograms.

@author Jonathan Olson <jonathan.olson@colorado.edu>

Class RenderColor

import { RenderColor } from 'scenerystack/alpenglow';

Constructor

new RenderColor( color : Vector4 )

Instance Methods

getName() : string

withChildren( children : RenderProgram[] ) : RenderColor

equalsTyped( other : this ) : boolean

(protected)

evaluate( context : RenderEvaluationContext ) : Vector4

writeInstructions( instructions : RenderInstruction[] )

getExtraDebugString() : string

(protected)

serialize() : SerializedRenderColor

Static Methods

applyProgram( vector : Vector4, color : Vector4 )

sRGBToLinear( sRGB : Vector4 ) : Vector4

linearToSRGB( linear : Vector4 ) : Vector4

linearToOklab( linear : Vector4 ) : Vector4

Oklab is a perceptually uniform color space, which is useful for color blending. https://bottosson.github.io/posts/oklab/ returned as (L,a,b,alpha)

oklabToLinear( oklab : Vector4 ) : Vector4

multiplyMatrixTimesColor( matrix : Matrix3, color : Vector4 ) : Vector4

linearToLinearDisplayP3( color : Vector4 ) : Vector4

linearDisplayP3ToLinear( color : Vector4 ) : Vector4

linearDisplayP3ToDisplayP3( linear : Vector4 ) : Vector4

displayP3ToLinearDisplayP3( displayP3 : Vector4 ) : Vector4

oklabToOklch( oklab : Vector4 ) : Vector4

A radian-based oklch?

oklchToOklab( oklch : Vector4 ) : Vector4

A radian-based oklch

linearToOklch( linear : Vector4 ) : Vector4

oklchToLinear( oklch : Vector4 ) : Vector4

TODO: consistent "linear" naming? (means linear SRGB here)

linearDisplayP3ToOklch( linear : Vector4 ) : Vector4

oklchToLinearDisplayP3( oklch : Vector4 ) : Vector4

isColorInRange( color : Vector4 ) : boolean

public static convert( color: Vector4, fromSpace: RenderColorSpace, toSpace: RenderColorSpace ): Vector4 { if ( fromSpace === toSpace ) { return color; }

if ( assert ) { // If we add more, add in the conversions here const spaces = [ RenderColorSpace.XYZ, RenderColorSpace.xyY, RenderColorSpace.sRGB, RenderColorSpace.premultipliedSRGB, RenderColorSpace.linearSRGB, RenderColorSpace.premultipliedLinearSRGB, RenderColorSpace.displayP3, RenderColorSpace.premultipliedDisplayP3, RenderColorSpace.linearDisplayP3, RenderColorSpace.premultipliedLinearDisplayP3, RenderColorSpace.oklab, RenderColorSpace.premultipliedOklab ];

assert( spaces.includes( fromSpace ) );
assert( spaces.includes( toSpace ) );

}

if ( fromSpace.name === toSpace.name ) { if ( fromSpace.isLinear === toSpace.isLinear ) { // Just a premultiply change! return fromSpace.isPremultiplied ? RenderColor.unpremultiply( color ) : RenderColor.premultiply( color ); } else { // We're different in linearity! if ( fromSpace.isPremultiplied ) { color = RenderColor.unpremultiply( color ); } if ( fromSpace.name === 'srgb' || fromSpace.name === 'display-p3' ) { // sRGB transfer function color = fromSpace.isLinear ? RenderColor.linearToSRGB( color ) : RenderColor.sRGBToLinear( color ); } if ( toSpace.isPremultiplied ) { color = RenderColor.premultiply( color ); } return color; } } else { // essentially, we'll convert to linear sRGB and back

if ( fromSpace.isPremultiplied ) {
  color = RenderColor.unpremultiply( color );
}

if ( fromSpace === RenderColorSpace.xyY ) {
  color = color.y === 0 ? new Vector4( 0, 0, 0, color.w ) : new Vector4(
    // TODO: separate out into a function
    color.x * color.z / color.y,
    color.z,
    ( 1 - color.x - color.y ) * color.z / color.y,
    color.w
  );
}
if ( fromSpace === RenderColorSpace.xyY || fromSpace === RenderColorSpace.XYZ ) {
  color = RenderColor.multiplyMatrixTimesColor( RenderColor.XYZTosRGBMatrix, color );
}
if (
  fromSpace === RenderColorSpace.sRGB ||
  fromSpace === RenderColorSpace.premultipliedSRGB ||
  fromSpace === RenderColorSpace.displayP3 ||
  fromSpace === RenderColorSpace.premultipliedDisplayP3
) {
  color = RenderColor.sRGBToLinear( color );
}
if ( fromSpace === RenderColorSpace.displayP3 || fromSpace === RenderColorSpace.premultipliedDisplayP3 ) {
  color = RenderColor.linearDisplayP3ToLinear( color );
}
if ( fromSpace === RenderColorSpace.oklab || fromSpace === RenderColorSpace.premultipliedOklab ) {
  color = RenderColor.oklabToLinear( color );
}

// Now reverse the process, but for the other color space
if ( toSpace === RenderColorSpace.oklab || toSpace === RenderColorSpace.premultipliedOklab ) {
  color = RenderColor.linearToOklab( color );
}
if ( toSpace === RenderColorSpace.displayP3 || toSpace === RenderColorSpace.premultipliedDisplayP3 ) {
  color = RenderColor.linearToLinearDisplayP3( color );
}
if (
  toSpace === RenderColorSpace.sRGB ||
  toSpace === RenderColorSpace.premultipliedSRGB ||
  toSpace === RenderColorSpace.displayP3 ||
  toSpace === RenderColorSpace.premultipliedDisplayP3
) {
  color = RenderColor.linearToSRGB( color );
}
if ( toSpace === RenderColorSpace.xyY || toSpace === RenderColorSpace.XYZ ) {
  color = RenderColor.multiplyMatrixTimesColor( RenderColor.sRGBToXYZMatrix, color );
}
if ( toSpace === RenderColorSpace.xyY ) {
  color = ( color.x + color.y + color.z === 0 ) ? new Vector4(
    // TODO: white point change to the other functions, I think we have some of this duplicated.
    // TODO: separate out into a function
    // using white point for D65
    sRGBWhiteChromaticity.x,
    sRGBWhiteChromaticity.y,
    0,
    color.w
  ) : new Vector4(
    color.x / ( color.x + color.y + color.z ),
    color.y / ( color.x + color.y + color.z ),
    color.y,
    color.w
  );
}

if ( toSpace.isPremultiplied ) {
  color = RenderColor.premultiply( color );
}

return color;

} } ONLY remaps the r,g,b parts, not alpha

gamutMapColor( color : Vector4, toOklab : ( c: Vector4 ) => Vector4, fromOklab : ( c: Vector4 ) => Vector4 ) : Vector4

Relative colorimetric mapping. We could add more of a perceptual intent, but this is a good start.

Modeled after https://drafts.csswg.org/css-color-4/#binsearch

gamutMapLinearSRGB( color : Vector4 ) : Vector4

gamutMapLinearDisplayP3( color : Vector4 ) : Vector4

gamutMapSRGB( color : Vector4 ) : Vector4

gamutMapDisplayP3( color : Vector4 ) : Vector4

gamutMapPremultipliedSRGB( color : Vector4 ) : Vector4

OUTPUTS unpremultiplied sRGB, with a valid alpha value

gamutMapPremultipliedDisplayP3( color : Vector4 ) : Vector4

OUTPUTS unpremultiplied Display P3, with a valid alpha value

oklabToLinearDisplayP3( oklab : Vector4 ) : Vector4

linearDisplayP3ToOklab( linearP3 : Vector4 ) : Vector4

premultiply( color : Vector4 ) : Vector4

unpremultiply( color : Vector4 ) : Vector4

xyYToXYZ( xyY : Vector3 ) : Vector3

xyToXYZ( xy : Vector2 ) : Vector3

xyzToLinear( xyz : Vector4 ) : Vector4

linearToXYZ( linear : Vector4 ) : Vector4

getMatrixRGBToXYZ( redChromaticity : Vector2, greenChromaticity : Vector2, blueChromaticity : Vector2, whiteChromaticity : Vector2 ) : Matrix3

canvasSupportsDisplayP3() : boolean

ratioBlend( zeroColor : Vector4, oneColor : Vector4, ratio : number ) : Vector4

Static Properties

sRGBToXYZMatrix

XYZTosRGBMatrix

displayP3ToXYZMatrix

XYZToDisplayP3Matrix

sRGBToDisplayP3Matrix

displayP3TosRGBMatrix

TRANSPARENT : RenderColor

(readonly)

Type SerializedRenderColor

import type { SerializedRenderColor } from 'scenerystack/alpenglow';
  • type: "RenderColor"
  • color:
  • r: number
  • g: number
  • b: number
  • a: number

Source Code

See the source for RenderColor.ts in the alpenglow repository.