Skip to content

Commit

Permalink
Add Color Space handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
ILOVEPIE committed Jan 30, 2024
1 parent 65120e5 commit c6af0d1
Show file tree
Hide file tree
Showing 14 changed files with 1,276 additions and 120 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ to render a frame of subtitles.
<dt><a href="#loadSubtitles">loadSubtitles(subsText, fonts)</a> ⇒ <code>void</code></dt>
<dd><p>Begins the process of parsing the passed subtitles in SSA/ASS format into subtitle events.</p>
</dd>
<dt><a href="#setColorSpace">setColorSpace(colorSpace, [width], [height])</a></dt>
<dd><p>Configures the output colorspace to the set value (or guesses when automatic is specified based on resolution).
AUTOMATIC always assumes studio-swing (color values between 16-240), if you need full-swing (color values between 0-255)
that must be set by selecting AUTOMATIC_PC. AUTOMATIC and AUTOMATIC_PC are also incapable of determining if the
video is HDR, so you need to manually set either BT.2100_PQ or BT.2100_HLG if it is.</p>
</dd>
<dt><a href="#setViewport">setViewport(width, height)</a> ⇒ <code>void</code></dt>
<dd><p>Updates the resolution (in CSS pixels) at which the subtitles are rendered (if the player is resized, for example).</p>
</dd>
Expand Down Expand Up @@ -113,6 +119,22 @@ Begins the process of parsing the passed subtitles in SSA/ASS format into subtit
| subsText | <code>string</code> | the subtitle file's contents. |
| fonts | <code>Array.&lt;Font&gt;</code> | preloaded fonts necessary for this subtitle file (one of these MUST be Arial). |

<a name="setColorSpace"></a>

#### setColorSpace(colorSpace, [width], [height])
Configures the output colorspace to the set value (or guesses when automatic is specified based on resolution).
AUTOMATIC always assumes studio-swing (color values between 16-240), if you need full-swing (color values between 0-255)
that must be set by selecting AUTOMATIC_PC. AUTOMATIC and AUTOMATIC_PC are also incapable of determining if the
video is HDR, so you need to manually set either BT.2100_PQ or BT.2100_HLG if it is.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| colorSpace | <code>number</code> | the colorspace to use for output. |
| [width] | <code>number</code> | the x component of the video's resolution (only required when colorSpace is AUTOMATIC). |
| [height] | <code>number</code> | the y component of the video's resolution (only required when colorSpace is AUTOMATIC). |

<a name="setViewport"></a>

#### setViewport(width, height) ⇒ <code>void</code>
Expand Down
123 changes: 123 additions & 0 deletions include/global-constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* Defines collision modes.
* @private
* @enum {number}
*/
sabre.CollisionModes = {
Expand All @@ -9,6 +10,7 @@ sabre.CollisionModes = {

/**
* Defines karaoke modes.
* @private
* @enum {number}
*/
sabre.KaraokeModes = {
Expand All @@ -20,6 +22,7 @@ sabre.KaraokeModes = {

/**
* Defines different border style modes.
* @private
* @enum {number}
*/
sabre.BorderStyleModes = {
Expand All @@ -32,6 +35,7 @@ sabre.BorderStyleModes = {

/**
* Defines passes.
* @private
* @enum {number}
*/
sabre.RenderPasses = {
Expand All @@ -42,6 +46,7 @@ sabre.RenderPasses = {

/**
* Defines wrap style modes.
* @private
* @enum {number}
*/
sabre.WrapStyleModes = {
Expand All @@ -50,3 +55,121 @@ sabre.WrapStyleModes = {
NONE: 2,
SMART_INVERSE: 3
};

/**
* Defines the different color spaces internally.
* @private
* @enum {number}
*/
sabre.ColorSpaces = {
RGB: 0,
BT601_TV: 1,
BT601_PC: 2,
BT709_TV: 3,
BT709_PC: 4,
BT2020_TV: 5,
BT2020_PC: 6,
BT2020_CL_TV: 7,
BT2020_CL_PC: 8,
BT2100_PQ: 9,
BT2100_HLG: 10,
SMPTE240M_TV: 32,
SMPTE240M_PC: 33,
FCC_TV: 34,
FCC_PC: 35,
DISPLAY_P3: 36
}

/**
* Defines all native display color spaces for the browser.
* @private
* @enum {number}
*/
sabre.NativeColorSpaces = {
RGB: sabre.ColorSpaces.RGB,
DISPLAY_P3: sabre.ColorSpaces.DISPLAY_P3,
BT2100_PQ: sabre.ColorSpaces.BT2100_PQ, // For when browsers support this.
BT2100_HLG: sabre.ColorSpaces.BT2100_HLG // For when browsers support this.
};

/**
* Defines Color Mangling modes for different color spaces.
* @private
* @enum {number}
*/
sabre.ColorManglingModes = {
DEFAULT: sabre.ColorSpaces.BT601_TV,
NONE: -1,
RGB: sabre.ColorSpaces.RGB,
BT601_TV: sabre.ColorSpaces.BT601_TV,
BT601_PC: sabre.ColorSpaces.BT601_PC,
BT709_TV: sabre.ColorSpaces.BT709_TV,
BT709_PC: sabre.ColorSpaces.BT709_PC,
BT2020_TV: sabre.ColorSpaces.BT2020_TV,
BT2020_PC: sabre.ColorSpaces.BT2020_PC,
BT2020_CL_TV: sabre.ColorSpaces.BT2020_CL_TV,
BT2020_CL_PC: sabre.ColorSpaces.BT2020_CL_PC,
BT2100_PQ: sabre.ColorSpaces.BT2100_PQ,
BT2100_HLG: sabre.ColorSpaces.BT2100_HLG,
SMPTE240M_TV: sabre.ColorSpaces.SMPTE240M_TV,
SMPTE240M_PC: sabre.ColorSpaces.SMPTE240M_PC,
FCC_TV: sabre.ColorSpaces.FCC_TV,
FCC_PC: sabre.ColorSpaces.FCC_PC,
//Skip 36 as it is Display-P3 which video can't use.
};

/**
*
*/
/**
* Defines the different color spaces externally.
* @public
* @enum {number}
*/
external.VideoColorSpaces = {
AUTOMATIC: -1,
AUTOMATIC_PC: -2,
RGB: sabre.ColorSpaces.RGB,
BT601_TV: sabre.ColorSpaces.BT601_TV,
BT601_PC: sabre.ColorSpaces.BT601_PC,
BT709_TV: sabre.ColorSpaces.BT709_TV,
BT709_PC: sabre.ColorSpaces.BT709_PC,
BT2020_TV: sabre.ColorSpaces.BT2020_TV,
BT2020_PC: sabre.ColorSpaces.BT2020_PC,
BT2100_PQ: sabre.ColorSpaces.BT2100_PQ,
BT2100_HLG: sabre.ColorSpaces.BT2100_HLG,
SMPTE240M_TV: sabre.ColorSpaces.SMPTE240M_TV,
SMPTE240M_PC: sabre.ColorSpaces.SMPTE240M_PC,
FCC_TV: sabre.ColorSpaces.FCC_TV,
FCC_PC: sabre.ColorSpaces.FCC_PC
};

/**
* Defines a list of types of color space conversions.
* @private
* @enum {number}
*/
sabre.ColorSpaceConversionTypes = {
NON_CONSTANT_LUMINANCE: 0,
CONSTANT_LUMINANCE: 1,
PERCEPTUAL_QUANTIZATION: 2, //TODO: Implement this
HYBRID_LOG_GAMMA: 3 //TODO: Implement this
};

/**
* Defines a non-constant luminance color space conversion.
* @typedef {!{type:number,offset:!Array<number>,toRGB:!Array<number>,toDisplayP3:!Array<number>,fromRGB:!Array<number>}}
*/
let NonConstantLuminanceColorSpaceConversion;

/**
* Defines a constant luminance color space conversion.
* @typedef {!{type:number,offset:!Array<number>,scale:!Array<number>,coefficients:!Array<number>,Nr:number,Nb:number,Pr:number,Pb:number}}
*/
let ConstantLuminanceColorSpaceConversion;

/**
* Defines conversions between color spaces for the renderer.
* @const {!Object<number,(ConstantLuminanceColorSpaceConversion|NonConstantLuminanceColorSpaceConversion)>}
*/
sabre.ColorSpaceConversionTable = {};
1 change: 1 addition & 0 deletions include/renderer-main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @typedef {{
* load:function(RendererData):void,
* setColorSpace:function(number):void,
* updateViewport:function(number,number):void,
* canRender:function():boolean,
* frame:function(number):void,
Expand Down
18 changes: 16 additions & 2 deletions include/shared.include.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,21 @@ var sabre = {};
sabre.$ = null;

//These are definitions for APIs the closure compiler doesn't know by default.
/** @type {function():void} */
global.CanvasRenderingContext2D.prototype.resetTransform = function () {};
/** @type {string|undefined} */
global.WebGLRenderingContext.prototype.unpackColorSpace = 'srgb';
/** @type {string|undefined} */
global.WebGLRenderingContext.prototype.drawingBufferColorSpace = 'srgb';
/** @type {string|undefined} */
global.WebGLRenderingContext.prototype.colorSpace = 'srgb';
/** @type {string|undefined} */
global.WebGL2RenderingContext.prototype.unpackColorSpace = 'srgb';
/** @type {string|undefined} */
global.WebGL2RenderingContext.prototype.drawingBufferColorSpace = 'srgb';
/** @type {string|undefined} */
global.WebGL2RenderingContext.prototype.colorSpace = 'srgb';
// This seems to have been added to Closure Compiler, but if you get bad compiles you can uncomment it.
// /** @type {function():void} */
// global.CanvasRenderingContext2D.prototype.resetTransform = function () {};
/** @type {number|undefined} */
global.CanvasRenderingContext2D.prototype.webkitBackingStorePixelRatio = 1;
/** @type {number|undefined} */
Expand Down Expand Up @@ -58,6 +71,7 @@ global.HTMLCanvasElement.prototype.toBlobHD = function () {};
*/
global.OffscreenCanvas.prototype.toBlobHD = function () {};

//These are definitions for opentype.js types.
/**
* @typedef{{x1:number,y1:number,x2:number,y2:number}}
*/
Expand Down
11 changes: 10 additions & 1 deletion include/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,20 @@ sabre.cloneEventWithoutText = function (event) {
return new sabre.SSASubtitleEvent();
};

/**
* Freezes an object and all of its own child properties.
* @param {!Object} obj the object to freeze.
* @return {!Object} the frozen object.
*/
sabre.totalObjectFreeze = function (obj) {
return {};
}

/**
* Hashes an object or array.
* @private
* @param {(!Object|!Array<*>)} obj Object or Array to hash.
* @return {number} The Hash of the events.
* @return {number} The hash of the object or array.
*/
sabre.hashObject = function (obj) {
return 0;
Expand Down
17 changes: 9 additions & 8 deletions src/canvas-2d-text-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ const text_renderer_prototype = global.Object.create(Object, {
value: function _init () {
const options = Object.freeze({
"alpha": true,
"colorSpace": "srgb",
"desynchronized": true
});
if (typeof global.OffscreenCanvas === "undefined") {
Expand All @@ -221,7 +222,7 @@ const text_renderer_prototype = global.Object.create(Object, {
} else {
this._canvas = new global.OffscreenCanvas(64, 64);
}
this._height = this._width = 1;
this._height = this._width = 64;
this._ctx = /** @type {CanvasRenderingContext2D} */(this._canvas.getContext("2d", options));
if(!this._pixelScaleRatio.preFactoredBacking){
const backingRatio = sabre.getBackingRatio(this._ctx);
Expand Down Expand Up @@ -311,7 +312,7 @@ const text_renderer_prototype = global.Object.create(Object, {
transitionOverrides[i].getTransitionAcceleration()
);
}
return { x: outlineX, y: outlineY };
return { x: Math.max(outlineX,0), y: Math.max(outlineY,0) };
},
writable: false
},
Expand Down Expand Up @@ -540,7 +541,7 @@ const text_renderer_prototype = global.Object.create(Object, {
* @param {number} time
* @param {SSAStyleDefinition} style
* @param {SSAStyleOverride} overrides
* @returns {{x:number, y:number}} Shadow x and y offsets.
* @return {{x:number, y:number}} Shadow x and y offsets.
*/
value: function _calcShadow (time, style, overrides){
const shadowComponent =
Expand All @@ -558,7 +559,7 @@ const text_renderer_prototype = global.Object.create(Object, {
* @param {number} time
* @param {SSAStyleDefinition} style
* @param {SSAStyleOverride} overrides
* @returns {number} Ratio of completion of the wipe in the range 0 to 1.
* @return {number} Ratio of completion of the wipe in the range 0 to 1.
*/
value: function _calcKaraokeWipeProgress (time, style, overrides){
return Math.max(time - overrides.getKaraokeStart(), 0) / (overrides.getKaraokeEnd() - overrides.getKaraokeStart());
Expand All @@ -573,7 +574,7 @@ const text_renderer_prototype = global.Object.create(Object, {
* @param {SSASubtitleEvent} event the subtitle event to render
* @param {number} pass the pass we are on.
* @param {boolean} mask is this a mask for setable colors.
* @returns {number} Hash of the state.
* @return {number} Hash of the state.
*/
value: function _getStateHash (time, event, pass, mask) {
const style = event.getStyle();
Expand Down Expand Up @@ -785,7 +786,7 @@ const text_renderer_prototype = global.Object.create(Object, {
* @param {SSASubtitleEvent} event the subtitle event to render
* @param {number} pass the pass we are on.
* @param {boolean} mask is this a mask for setable colors.
* @returns {number} event style state hash.
* @return {number} event style state hash.
*/
value: function startEventRender (time, event, pass, mask) {
if (!this._initialized) this._init();
Expand Down Expand Up @@ -837,7 +838,7 @@ const text_renderer_prototype = global.Object.create(Object, {
"nextGlyph": {
/**
* Return info on the next glyph for rendering.
* @returns {{prevGlyph:?Glyph, glyph:?Glyph, breakOut:boolean}} Information on the glyph to render.
* @return {{prevGlyph:?Glyph, glyph:?Glyph, breakOut:boolean}} Information on the glyph to render.
*/
value: function nextGlyph () {
if (
Expand Down Expand Up @@ -898,7 +899,7 @@ const text_renderer_prototype = global.Object.create(Object, {
* @param {{prevGlyph:?Glyph, glyph:?Glyph, breakOut:boolean}} glyphInfo Glyph information.
* @param {number} pass the pass we are on.
* @param {boolean} mask is this a mask for setable colors.
* @returns {boolean} Is glyph cachable.
* @return {boolean} Is glyph cachable.
*/
value: function renderGlyph (time, event, glyphInfo, pass, mask) {
if (glyphInfo.breakOut && (glyphInfo.glyph === null || typeof(glyphInfo.glyph) === "undefined"))
Expand Down
32 changes: 0 additions & 32 deletions src/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,38 +98,6 @@ sabre["SSAColor"] = function SSAColor (r, g, b, a) {
return obj.a;
},
writable: false
},

"getYUVA": {
value: function getYUVA () {
//https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB
throw "METHOD_STUBBED: SSAColor.getYUVA";
},
writable: false
},

"getYUV": {
value: function getYUV () {
//https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB
throw "METHOD_STUBBED: SSAColor.getYUV";
},
writable: false
},

"getYCbCrA": {
value: function getYCbCrA () {
//https://en.wikipedia.org/wiki/YCbCr
throw "METHOD_STUBBED: SSAColor.getYCbCrA";
},
writable: false
},

"getYCbCr": {
value: function getYCbCr () {
//https://en.wikipedia.org/wiki/YCbCr
throw "METHOD_STUBBED: SSAColor.getYCbCr";
},
writable: false
}
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/font-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const font_server_prototype = Object.create(Object, {
/**
* converts a wchar byte array to a string.
* @param {Array<number>} arr
* @returns {string}
* @return {string}
*/
value: function _wcharByteArrayToString (arr) {
let array = [];
Expand Down Expand Up @@ -85,7 +85,7 @@ const font_server_prototype = Object.create(Object, {
/**
* @private
* @param {string} name
* @returns {Array<{
* @return {Array<{
* font:Font,
* ascent:number,
* descent:number,
Expand Down
Loading

0 comments on commit c6af0d1

Please sign in to comment.