Skip to content

Commit

Permalink
Added Scaled outline and shadow support.
Browse files Browse the repository at this point in the history
Fixed a bug and updated README.md
  • Loading branch information
ILOVEPIE committed Feb 4, 2024
1 parent c5657a3 commit 003bfd7
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 64 deletions.
13 changes: 9 additions & 4 deletions README.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ window.addEventListener("load",() => {
// load the fonts using opentype.js and put them in
// the fonts array.
// YOUR CODE HERE
// pass the font parsing function to the renderer
renderer = sabre.SABRERenderer(parseFont);
renderer.loadSubtitles(subs,fonts);
renderer.setViewport(1280,720); // use the video player's dimensions.
// initialize the renderer
renderer = sabre.SABRERenderer(parseFont,{fonts:fonts,subtitles:subs,colorSpace:sabre.VideoColorSpaces.AUTOMATIC,resolution:[1280,720],nativeResolution:[1280,720]});
// or you can do this:
// renderer = new sabre.SABRERenderer(parseFont);
// renderer.loadSubtitles(subs,fonts);
// renderer.setColorSpace(sabre.VideoColorSpaces.AUTOMATIC,1280,720);
// renderer.setViewport(1280,720);
// schedule your frame callback using either requestAnimationFrame or requestVideoFrameCallback
});
```
Expand All @@ -80,6 +83,8 @@ to render a frame of subtitles.

### API

The documentation generator is a little buggy, anytime it says something is global, that means it's a property of the `sabre.SABRERenderer` object.

{{>main}}

© 2012-2023 Patrick "ILOVEPIE" Rhodes Martin.
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ window.addEventListener("load",() => {
// load the fonts using opentype.js and put them in
// the fonts array.
// YOUR CODE HERE
// pass the font parsing function to the renderer
renderer = sabre.SABRERenderer(parseFont);
renderer.loadSubtitles(subs,fonts);
renderer.setViewport(1280,720); // use the video player's dimensions.
// initialize the renderer
renderer = sabre.SABRERenderer(parseFont,{fonts:fonts,subtitles:subs,colorSpace:sabre.VideoColorSpaces.AUTOMATIC,resolution:[1280,720],nativeResolution:[1280,720]});
// or you can do this:
// renderer = new sabre.SABRERenderer(parseFont);
// renderer.loadSubtitles(subs,fonts);
// renderer.setColorSpace(sabre.VideoColorSpaces.AUTOMATIC,1280,720);
// renderer.setViewport(1280,720);
// schedule your frame callback using either requestAnimationFrame or requestVideoFrameCallback
});
```
Expand All @@ -80,17 +83,20 @@ to render a frame of subtitles.

### API

The documentation generator is a little buggy, anytime it says something is global, that means it's a property of the `sabre.SABRERenderer` object.

#### Functions

<dl>
<dt><a href="#loadSubtitles">loadSubtitles(subsText, fonts)</a> ⇒ <code>void</code></dt>
<dt><a href="#loadSubtitles">loadSubtitles(subtitles, 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)
Note: 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>
video is HDR, so you need to manually set either BT.2100_PQ or BT.2100_HLG if it is.
Note: HDR support is stubbed and unimplemented currently.</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>
Expand All @@ -109,38 +115,44 @@ video is HDR, so you need to manually set either BT.2100_PQ or BT.2100_HLG if it
</dd>
</dl>

#### loadSubtitles(subsText, fonts) ⇒ <code>void</code>
<a name="loadSubtitles"></a>

#### loadSubtitles(subtitles, fonts) ⇒ <code>void</code>
Begins the process of parsing the passed subtitles in SSA/ASS format into subtitle events.

**Kind**: global function
**Access**: public

| Param | Type | Description |
| --- | --- | --- |
| subsText | <code>string</code> | the subtitle file's contents. |
| subtitles | <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)
Note: 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.
Note: HDR support is stubbed and unimplemented currently.

**Kind**: global function
**Access**: public

| 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). |
| [width] | <code>number</code> | the x component of the video's resolution in regular pixels (only required when colorSpace is AUTOMATIC). |
| [height] | <code>number</code> | the y component of the video's resolution in regular pixels (only required when colorSpace is AUTOMATIC). |

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

#### setViewport(width, height) ⇒ <code>void</code>
Updates the resolution (in CSS pixels) at which the subtitles are rendered (if the player is resized, for example).

**Kind**: global function
**Access**: public

| Param | Type | Description |
| --- | --- | --- |
Expand All @@ -154,12 +166,14 @@ Checks if the renderer is ready to render a frame.

**Kind**: global function
**Returns**: <code>boolean</code> - is the renderer ready?
**Access**: public
<a name="getFrame"></a>

#### getFrame(time) ⇒ <code>ImageBitmap</code>
Fetches a rendered frame of subtitles as an ImageBitmap, returns null if ImageBitmap is unsupported.

**Kind**: global function
**Access**: public

| Param | Type | Description |
| --- | --- | --- |
Expand All @@ -171,6 +185,7 @@ Fetches a rendered frame of subtitles as an ImageBitmap, returns null if ImageBi
Fetches a rendered frame of subtitles as an object uri.

**Kind**: global function
**Access**: public

| Param | Type | Description |
| --- | --- | --- |
Expand All @@ -183,6 +198,7 @@ Fetches a rendered frame of subtitles as an object uri.
Fetches a rendered frame of subtitles to a canvas.

**Kind**: global function
**Access**: public

| Param | Type | Description |
| --- | --- | --- |
Expand Down
1 change: 1 addition & 0 deletions include/canvas-2d-shape-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @typedef {!{
* renderEvent:function(number,SSASubtitleEvent,number,boolean):void,
* setPixelScaleRatio:function(number,number):void,
* setScaledOutlineAndShadowEnabled:function(boolean):void,
* getOffset:function():Array<number>,
* getOffsetExternal:function():Array<number>,
* getDimensions:function():Array<number>,
Expand Down
1 change: 1 addition & 0 deletions include/canvas-2d-text-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* renderGlyph:function(number,SSASubtitleEvent,{prevGlyph:?Glyph,glyph:?Glyph,breakOut:boolean},number,boolean):boolean,
* setRequestFont:function(!function(string,number,boolean):!{font:Font,foundItalic:boolean,foundWeight:number}):void,
* setPixelScaleRatio:function(number,number):void,
* setScaledOutlineAndShadowEnabled:function(boolean):void,
* getOffset:function():Array<number>,
* getOffsetExternal:function():Array<number>,
* getDimensions:function():Array<number>,
Expand Down
38 changes: 29 additions & 9 deletions src/canvas-2d-shape-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ const shape_renderer_prototype = global.Object.create(Object, {
writable: true
},

_scaledOutlineAndShadow: {
/**
* If the outline is scaled.
* @type {boolean}
*/
value: false,
writable: true
},

_canvas: {
/**
* The canvas for the shape renderer.
Expand Down Expand Up @@ -251,7 +260,7 @@ const shape_renderer_prototype = global.Object.create(Object, {
) {
//TODO: Figure out a good way to do dimension specific line widths.
let outline = this._calcOutline(time, style, overrides);
this._ctx.lineWidth = Math.min(outline.x, outline.y) * 2;
this._ctx.lineWidth = Math.min((!this._scaledOutlineAndShadow?outline.x/this._pixelScaleRatio.xratio:outline.x), (!this._scaledOutlineAndShadow?outline.y/this._pixelScaleRatio.yratio:outline.y)) * 2;
},
writable: false
},
Expand Down Expand Up @@ -636,12 +645,12 @@ const shape_renderer_prototype = global.Object.create(Object, {
pass === sabre.RenderPasses.BACKGROUND
) {
let outline = this._calcOutline(time, style, overrides);
this._width += outline.x * 2;
this._height += outline.x * 2;
this._offsetX += outline.x;
this._offsetY += outline.y;
outline_x = outline.x;
outline_y = outline.y;
outline_x = (!this._scaledOutlineAndShadow?outline.x/this._pixelScaleRatio.xratio:outline.x);
outline_y = (!this._scaledOutlineAndShadow?outline.y/this._pixelScaleRatio.yratio:outline.y);
this._width += outline_x * 2;
this._height += outline_y * 2;
this._offsetX += outline_x;
this._offsetY += outline_y;
}

let offsetXUnscaled = this._offsetX;
Expand All @@ -660,8 +669,8 @@ const shape_renderer_prototype = global.Object.create(Object, {
let shadowX = overrides.getShadowX() ?? shadowComponent;
let shadowY = overrides.getShadowY() ?? shadowComponent;
if (shadowX === 0 && shadowY === 0) noDraw = true;
this._offsetX -= shadowX;
this._offsetY -= shadowY;
this._offsetX -= shadowX/(this._scaledOutlineAndShadow?this._pixelScaleRatio.xratio:1);
this._offsetY -= shadowY/(this._scaledOutlineAndShadow?this._pixelScaleRatio.yratio:1);
} else if (borderStyle === sabre.BorderStyleModes.NONE) {
noDraw = true;
}
Expand Down Expand Up @@ -831,6 +840,17 @@ const shape_renderer_prototype = global.Object.create(Object, {
writable: false
},

"setScaledOutlineAndShadowEnabled": {
/**
* Sets whether the outline should be scaled when we are rendering to a higher resolution than the video.
* @param {boolean} enabled if true, the outline will be scaled.
*/
value: function setScaledOutlineEnabled (enabled) {
this._scaledOutlineAndShadow = enabled;
},
writable: false
},

"getOffset": {
/**
* Gets the offset of the resulting image.
Expand Down
39 changes: 30 additions & 9 deletions src/canvas-2d-text-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ const text_renderer_prototype = global.Object.create(Object, {
writable: true
},

_scaledOutlineAndShadow: {
/**
* If the outline is scaled.
* @type {boolean}
*/
value: false,
writable: true
},

_canvas: {
/**
* The canvas for the text renderer.
Expand Down Expand Up @@ -327,7 +336,7 @@ const text_renderer_prototype = global.Object.create(Object, {
value: function _setOutline (time, style, overrides) {
let outline = this._calcOutline(time, style, overrides);

this._ctx.lineWidth = Math.min(outline.x, outline.y) * 2;
this._ctx.lineWidth = Math.min((!this._scaledOutlineAndShadow?outline.x/this._pixelScaleRatio.xratio:outline.x), (!this._scaledOutlineAndShadow?outline.y/this._pixelScaleRatio.yratio:outline.y)) * 2;
},
writable: false
},
Expand Down Expand Up @@ -823,9 +832,9 @@ const text_renderer_prototype = global.Object.create(Object, {
}

this._eOffsetX +=
shadow.x * this._scale.x * this._pixelScaleRatio.xratio;
shadow.x * this._scale.x * (this._scaledOutlineAndShadow?this._pixelScaleRatio.xratio:1);
this._eOffsetY +=
shadow.y * this._scale.y * this._pixelScaleRatio.yratio;
shadow.y * this._scale.y * (this._scaledOutlineAndShadow?this._pixelScaleRatio.yratio:1);
} else if (borderStyle === sabre.BorderStyleModes.NONE) {
this._noDraw = true;
}
Expand Down Expand Up @@ -971,16 +980,17 @@ const text_renderer_prototype = global.Object.create(Object, {
outline.x *
2 *
this._scale.x *
this._pixelScaleRatio.xratio;
(this._scaledOutlineAndShadow?this._pixelScaleRatio.xratio:1);
this._height +=
outline.y *
2 *
this._scale.y *
this._pixelScaleRatio.yratio;
this._offsetX += outline.x;
this._offsetY += outline.y;
outline_x = outline.x;
outline_y = outline.y;
(this._scaledOutlineAndShadow?this._pixelScaleRatio.yratio:1);
outline_x = (!this._scaledOutlineAndShadow?outline.x/this._pixelScaleRatio.xratio:outline.x);
outline_y = (!this._scaledOutlineAndShadow?outline.y/this._pixelScaleRatio.yratio:outline.y);
this._offsetX += outline_x;
this._offsetY += outline_y;

}

let offsetXUnscaled = this._offsetX;
Expand Down Expand Up @@ -1187,6 +1197,17 @@ const text_renderer_prototype = global.Object.create(Object, {
writable: false
},

"setScaledOutlineAndShadowEnabled": {
/**
* Sets whether the outline should be scaled when we are rendering to a higher resolution than the video.
* @param {boolean} enabled if true, the outline will be scaled.
*/
value: function setScaledOutlineEnabled (enabled) {
this._scaledOutlineAndShadow = enabled;
},
writable: false
},

"getOffset": {
/**
* Gets the internal offset of the resulting image.
Expand Down
4 changes: 4 additions & 0 deletions src/font-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/
/**
* An enum of platforms.
* @private
* @enum {number}
*/
const platforms = Object.freeze({
UNICODE: 0,
Expand All @@ -19,6 +21,8 @@ const platforms = Object.freeze({

/**
* An enum of name types.
* @private
* @enum {number}
*/
const nameTypes = Object.freeze({
COPYRIGHT: 0,
Expand Down
2 changes: 2 additions & 0 deletions src/global-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,14 @@ sabre["ColorSpaceConversionTypes"] = sabre.totalObjectFreeze({
});
/**
* Defines a non-constant luminance color space conversion.
* @private
* @typedef {!{type:number,offset:!Array<number>,toRGB:!Array<number>,toDisplayP3:!Array<number>,fromRGB:!Array<number>}}
*/
let NonConstantLuminanceColorSpaceConversion;

/**
* Defines a constant luminance color space conversion.
* @private
* @typedef {!{type:number,offset:!Array<number>,scale:!Array<number>,coefficients:!Array<number>,Nr:number,Nb:number,Pr:number,Pb:number}}
*/
let ConstantLuminanceColorSpaceConversion;
Expand Down
8 changes: 5 additions & 3 deletions src/renderer-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2991,8 +2991,8 @@ const renderer_prototype = global.Object.create(Object, {
* @return {Float32Array} result
*/
value: function _calcClipPathCoords (clip, inverse) {
let scale = /** @type {number} */ (clip[0]);
let path = /** @type {string} */ (clip[1]);
let scale = /** @private @type {number} */ (clip[0]);
let path = /** @private @type {string} */ (clip[1]);
let shapes = this._getShapesFromPath(scale, path);
let triangles = [];
for (let i = 0; i < shapes.length; i++) {
Expand Down Expand Up @@ -3344,7 +3344,7 @@ const renderer_prototype = global.Object.create(Object, {
].getTransitionAcceleration();
clip[0] = sabre.performTransition(
time,
/** @type {!number} */ (clip[0]),
/** @private @type {!number} */ (clip[0]),
transitionClip[0],
transitionStart,
transitionEnd,
Expand Down Expand Up @@ -4406,7 +4406,9 @@ const renderer_prototype = global.Object.create(Object, {
return _this._findFont(name, weight, italic);
};
this._textRenderer.setRequestFont(requestFont);
this._textRenderer.setScaledOutlineAndShadowEnabled(config.renderer["scaled_border_and_shadow"]);
this._textMaskRenderer.setRequestFont(requestFont);
this._textMaskRenderer.setScaledOutlineAndShadowEnabled(config.renderer["scaled_border_and_shadow"]);
this._config = config;
this._scheduler.setEvents(
/** @type {Array<SSASubtitleEvent>} */ (
Expand Down
Loading

0 comments on commit 003bfd7

Please sign in to comment.