160 lines
5.8 KiB
PHP
160 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace swf2ass\ass;
|
|
|
|
use swf2ass\ClipPath;
|
|
use swf2ass\DrawPath;
|
|
use swf2ass\FillStyleRecord;
|
|
use swf2ass\FrameInformation;
|
|
use swf2ass\Gradient;
|
|
use swf2ass\MatrixTransform;
|
|
use swf2ass\RenderedFrame;
|
|
use swf2ass\RenderedObject;
|
|
|
|
class ASSLine {
|
|
|
|
/** @var int[] */
|
|
public array $layer;
|
|
public int $shapeIndex;
|
|
public int $objectId;
|
|
public int $start;
|
|
public int $end;
|
|
public string $style;
|
|
public string $name = "";
|
|
public int $marginLeft = 0;
|
|
public int $marginRight = 0;
|
|
public int $marginVertical = 0;
|
|
public string $effect = "";
|
|
|
|
public bool $isComment = false;
|
|
|
|
/** @var ASSTag[] */
|
|
public array $tags = [];
|
|
|
|
private ?string $cachedEncode = null;
|
|
|
|
public function __construct() {
|
|
|
|
}
|
|
|
|
public function transition(FrameInformation $information, RenderedObject $object) : ?ASSLine{
|
|
$line = clone $this;
|
|
$line->end = $information->getFrameNumber();
|
|
$line->tags = [];
|
|
//TODO: clip?
|
|
|
|
|
|
if($object->getDepth() === $this->layer and $object->objectId === $this->objectId) {
|
|
$command = $object->drawPathList->commands[$line->shapeIndex] ?? null;
|
|
|
|
if($command === null){
|
|
return null;
|
|
}
|
|
|
|
foreach ($this->tags as $tag){
|
|
if($tag instanceof ASSPositioningTag){
|
|
$tag = $tag->transitionMatrixTransform($line, $object->matrixTransform);
|
|
if($tag === null){
|
|
return null;
|
|
}
|
|
}
|
|
if($tag instanceof ASSColorTag){
|
|
$tag = $tag->transitionColor($line, $object->colorTransform);
|
|
if($tag === null){
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if($tag instanceof ASSPathTag){
|
|
$tag = $tag->transitionShape($line, $command->commands);
|
|
if($tag === null){
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if($tag instanceof ASSClipPathTag){
|
|
$tag = $tag->transitionClipPath($line, $object->clip);
|
|
if($tag === null){
|
|
return null;
|
|
}
|
|
}
|
|
|
|
$line->tags[] = $tag;
|
|
}
|
|
}
|
|
$line->dropCache();
|
|
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* @param FrameInformation $information
|
|
* @param RenderedObject $object
|
|
* @return ASSLine[]
|
|
*/
|
|
public static function fromRenderObject(FrameInformation $information, RenderedObject $object, bool $bakeTransforms = false): array {
|
|
$lines = [];
|
|
foreach ($object->drawPathList->commands as $i => $drawPath) {
|
|
$line = new ASSLine();
|
|
$line->layer = $object->getDepth();
|
|
$line->shapeIndex = $i;
|
|
$line->objectId = $object->objectId;
|
|
$line->start = $information->getFrameNumber();
|
|
$line->end = $information->getFrameNumber();
|
|
$line->tags[] = containerTag::fromPathEntry($drawPath, $object->clip, $object->colorTransform, $object->matrixTransform, $bakeTransforms);
|
|
$line->name = "o:{$object->objectId} d:" . implode(".", array_slice($object->depth, 1));
|
|
$lines[] = $line;
|
|
}
|
|
|
|
return $lines;
|
|
}
|
|
|
|
public function dropCache(){
|
|
$this->cachedEncode = null;
|
|
}
|
|
|
|
public function getPackedLayer() : int{
|
|
//Segment depth into specific layers, leaving 2^16 for first, at least 2^8 for second (and 2^8 for third), if no third it'll use whole for second TODO: handle higher depths gracefully
|
|
//It is known layers CAN overlap, TODO: check if limiting range might make sense?
|
|
//TODO: change this to a truly dynamic mode. might need 2-pass to check for hole overlap
|
|
$layer = $this->layer[0] << 16;
|
|
if(isset($this->layer[2])){
|
|
$layer |= $this->layer[1] & 0xFF;
|
|
$layer |= $this->layer[2] & 0xFF;
|
|
}else{
|
|
$layer |= $this->layer[1] ?? 0;
|
|
}
|
|
return $layer;
|
|
}
|
|
|
|
public function encode($frameDurationMs, int $msPrecision = 2): string {
|
|
if($frameDurationMs === 1000 and $msPrecision === 2 and $this->cachedEncode !== null){
|
|
return $this->cachedEncode;
|
|
}
|
|
|
|
$assEventTime = new ASSEventTime($this->start, $this->end - $this->start + 1, $frameDurationMs, $msPrecision);
|
|
|
|
$line = ($this->isComment ? "Comment" : "Dialogue") . ": " . $this->getPackedLayer() . "," . $assEventTime->start->encode() . "," . $assEventTime->end->encode() . "," . $this->style . "," . $this->name . "," . $this->marginLeft . "," . $this->marginRight . "," . $this->marginVertical . "," . $this->effect . ",";
|
|
|
|
|
|
if($assEventTime->start->adjusted_ms_error !== 0 or $assEventTime->end->adjusted_ms_error !== 0){
|
|
//Maybe use fade?
|
|
$frameStartTime = $assEventTime->getMillisecondsFromStartOffset(0);
|
|
$frameEndTime = $assEventTime->getMillisecondsFromEndOffset(0);
|
|
//TODO: maybe needs to be -1?
|
|
$line .= "{\\fade(255,0,255,{$frameStartTime},{$frameStartTime},{$frameEndTime},{$frameEndTime})\\err({$assEventTime->start->milliseconds}~{$assEventTime->start->adjusted_ms_error},{$assEventTime->end->milliseconds}~{$assEventTime->end->adjusted_ms_error})}";
|
|
}
|
|
|
|
foreach ($this->tags as $tag){
|
|
$line .= "{" . $tag->encode($assEventTime) . "}";
|
|
}
|
|
if($frameDurationMs === 1000){
|
|
$this->cachedEncode = $line;
|
|
}
|
|
return $line;
|
|
}
|
|
|
|
public function equalish(ASSLine $line): bool {
|
|
return $this->layer === $line->layer and $this->objectId === $line->objectId and count($this->tags) === count($line->tags) and $this->encode(1000) === $line->encode(1000);
|
|
}
|
|
} |