swf2ass/src/ass/ASSRenderer.php

136 lines
4.2 KiB
PHP

<?php
namespace swf2ass\ass;
use swf2ass\FrameInformation;
use swf2ass\Rectangle;
use swf2ass\RenderedFrame;
use swf2ass\RenderedObject;
class ASSRenderer {
private ?string $header;
/** @var ASSLine[] */
private array $dupeBuffer = [];
public function __construct(int $frameRate, Rectangle $viewPort) {
$display = $viewPort->toPixel();
$width = $display->getWidth();
$height = $display->getHeight();
$ar = $width / $height;
$this->header = <<<ASSHEADER
[Script Info]
; Script generated by swf2ass ASSRenderer
; https://git.gammaspectra.live/WeebDataHoarder/swf2ass
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.709
PlayResX: {$width}
PlayResY: {$height}
[Aegisub Project Garbage]
Last Style Storage: Default
Video File: ?dummy:{$frameRate}:10000:{$width}:{$height}:160:160:160:c
Video AR Value: {$ar}
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: f,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,0,0,7,0,0,0,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
ASSHEADER;
}
public function renderFrame(FrameInformation $information, RenderedFrame $frame): \Generator {
if ($this->header !== null) {
foreach (explode("\n", $this->header) as $line) {
yield $line;
}
$this->header = null;
}
$objects = $frame->getObjects();
usort($objects, [self::class, "depthSort"]);
$dupeBuffer = [];
foreach ($objects as $object) {
foreach ($this->renderObject($object) as $line) {
$line->layer = $object->depth[0] === 0 ? $object->depth[1] : $object->depth[0];
$line->start = $information->getStartTimeMilliSeconds();
$line->end = $information->getEndTimeMilliSeconds();
$line->frames = 1;
$line->style = "f";
foreach ($this->dupeBuffer as $i => $dup) {
if ($dup->layer === $line->layer and $dup->text === $line->text) {
$line->start = $dup->start;
$line->frames += $dup->frames;
unset($this->dupeBuffer[$i]);
break;
}
}
$dupeBuffer[] = $line;
}
}
//Flush non dupes
foreach ($this->dupeBuffer as $line) {
$line->name .= " dup:{$line->frames}";
yield $line->encode();
}
$this->dupeBuffer = $dupeBuffer;
}
public function flush(): \Generator {
foreach ($this->dupeBuffer as $line) {
$line->name .= " dup:{$line->frames}";
yield $line->encode();
}
$this->dupeBuffer = [];
}
public static function depthSort(RenderedObject $a, RenderedObject $b) {
if (count($b->depth) > count($a->depth)) {
foreach ($b->depth as $i => $depth) {
$otherDepth = $a->depth[$i] ?? 0;
if ($depth !== $otherDepth) {
return $otherDepth - $depth;
}
}
} else {
foreach ($a->depth as $i => $depth) {
$otherDepth = $b->depth[$i] ?? 0;
if ($depth !== $otherDepth) {
return $depth - $otherDepth;
}
}
}
return 0;
}
/**
* @param RenderedObject $object
* @return ASSLine[]
*/
public function renderObject(RenderedObject $object): array {
$lines = [];
foreach ($object->drawPathList->commands as $drawPath) {
$container = containerTag::fromPathEntry($drawPath, $object->clip, $object->colorTransform, $object->matrixTransform);
$line = new ASSLine("{" . $container->encode() . "}");
$line->name = "o:{$object->objectId} d:" . implode(".", array_slice($object->depth, 1));
$lines[] = $line;
}
return $lines;
}
}