assjs
Version:
A lightweight JavaScript ASS subtitle renderer
115 lines (112 loc) • 3.51 kB
JavaScript
import { color2rgba, createSVGEl, uuid } from '../utils.js';
export function createStrokeFilter(tag, scale) {
const id = `ASS-${uuid()}`;
const hasBorder = tag.xbord || tag.ybord;
const hasShadow = tag.xshad || tag.yshad;
const isOpaque = (tag.a1 || '00').toLowerCase() !== 'ff';
const blur = (tag.blur || tag.be || 0) * scale;
const $filter = createSVGEl('filter', [['id', id]]);
$filter.append(createSVGEl('feGaussianBlur', [
['stdDeviation', hasBorder ? 0 : blur],
['in', 'SourceGraphic'],
['result', 'sg_b'],
]));
$filter.append(createSVGEl('feFlood', [
['flood-color', 'var(--ass-fill-color)'],
['result', 'c1'],
]));
$filter.append(createSVGEl('feComposite', [
['operator', 'in'],
['in', 'c1'],
['in2', 'sg_b'],
['result', 'main'],
]));
if (hasBorder) {
$filter.append(createSVGEl('feMorphology', [
['radius', `${tag.xbord * scale} ${tag.ybord * scale}`],
['operator', 'dilate'],
['in', 'SourceGraphic'],
['result', 'dil'],
]));
$filter.append(createSVGEl('feGaussianBlur', [
['stdDeviation', blur],
['in', 'dil'],
['result', 'dil_b'],
]));
$filter.append(createSVGEl('feComposite', [
['operator', 'out'],
['in', 'dil_b'],
['in2', 'SourceGraphic'],
['result', 'dil_b_o'],
]));
$filter.append(createSVGEl('feFlood', [
['flood-color', 'var(--ass-border-color)'],
['result', 'c3'],
]));
$filter.append(createSVGEl('feComposite', [
['operator', 'in'],
['in', 'c3'],
['in2', 'dil_b_o'],
['result', 'border'],
]));
}
if (hasShadow && (hasBorder || isOpaque)) {
$filter.append(createSVGEl('feOffset', [
['dx', tag.xshad * scale],
['dy', tag.yshad * scale],
['in', hasBorder ? (isOpaque ? 'dil' : 'dil_b_o') : 'SourceGraphic'],
['result', 'off'],
]));
$filter.append(createSVGEl('feGaussianBlur', [
['stdDeviation', blur],
['in', 'off'],
['result', 'off_b'],
]));
if (!isOpaque) {
$filter.append(createSVGEl('feOffset', [
['dx', tag.xshad * scale],
['dy', tag.yshad * scale],
['in', 'SourceGraphic'],
['result', 'sg_off'],
]));
$filter.append(createSVGEl('feComposite', [
['operator', 'out'],
['in', 'off_b'],
['in2', 'sg_off'],
['result', 'off_b_o'],
]));
}
$filter.append(createSVGEl('feFlood', [
['flood-color', 'var(--ass-shadow-color)'],
['result', 'c4'],
]));
$filter.append(createSVGEl('feComposite', [
['operator', 'in'],
['in', 'c4'],
['in2', isOpaque ? 'off_b' : 'off_b_o'],
['result', 'shadow'],
]));
}
const $merge = createSVGEl('feMerge', []);
if (hasShadow && (hasBorder || isOpaque)) {
$merge.append(createSVGEl('feMergeNode', [['in', 'shadow']]));
}
if (hasBorder) {
$merge.append(createSVGEl('feMergeNode', [['in', 'border']]));
}
$merge.append(createSVGEl('feMergeNode', [['in', 'main']]));
$filter.append($merge);
return { id, el: $filter };
}
export function createStrokeVars(tag) {
return [
['border-width', tag.xbord * 2],
['border-color', color2rgba(`${tag.a3}${tag.c3}`)],
['shadow-color', color2rgba(`${tag.a4}${tag.c4}`)],
['tag-blur', tag.blur || tag.be || 0],
['tag-xbord', tag.xbord],
['tag-ybord', tag.ybord],
['tag-xshad', tag.xshad],
['tag-yshad', tag.yshad],
].map(([k, v]) => [`--ass-${k}`, v]);
}