261 lines
8.7 KiB
PHP
261 lines
8.7 KiB
PHP
<?php
|
|
|
|
namespace swf2ass\ass;
|
|
|
|
|
|
use MathPHP\LinearAlgebra\MatrixFactory;
|
|
use swf2ass\ClipPath;
|
|
use swf2ass\ColorTransform;
|
|
use swf2ass\DrawPath;
|
|
use swf2ass\FillStyleRecord;
|
|
use swf2ass\Gradient;
|
|
use swf2ass\LineStyleRecord;
|
|
use swf2ass\MatrixTransform;
|
|
use swf2ass\Shape;
|
|
use swf2ass\StyleRecord;
|
|
use swf2ass\Vector2;
|
|
|
|
class containerTag implements ASSColorTag, ASSPositioningTag, ASSStyleTag, ASSPathTag, ASSClipPathTag {
|
|
|
|
/** @var ASSTag[] */
|
|
private array $tags = [];
|
|
/** @var ASSTag[][] */
|
|
private array $transitions = [];
|
|
|
|
private ?MatrixTransform $bakeTransforms = null;
|
|
|
|
public function transitionColor(ASSLine $line, ColorTransform $transform): ?containerTag {
|
|
$container = clone $this;
|
|
|
|
$index = $line->end - $line->start;
|
|
if(!isset($container->transitions[$index])){
|
|
$container->transitions[$index] = [];
|
|
}
|
|
|
|
foreach ($container->tags as $tag) {
|
|
if ($tag instanceof ASSColorTag) {
|
|
$newTag = $tag->transitionColor($line, $transform);
|
|
if ($newTag === null) {
|
|
return null;
|
|
}
|
|
if(!$newTag->equals($tag)){
|
|
$container->transitions[$index][] = $newTag;
|
|
}
|
|
}
|
|
}
|
|
return $container;
|
|
}
|
|
|
|
public function transitionMatrixTransform(ASSLine $line, MatrixTransform $transform): ?containerTag {
|
|
if($this->bakeTransforms !== null){
|
|
if(!$transform->getMatrix()->submatrix(0, 0, 1, 1)->isEqual($this->bakeTransforms->getMatrix()->submatrix(0, 0, 1, 1))){ //Do not allow matrix changes but moves
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
$container = clone $this;
|
|
|
|
$index = $line->end - $line->start;
|
|
if(!isset($container->transitions[$index])){
|
|
$container->transitions[$index] = [];
|
|
}
|
|
|
|
foreach ($container->tags as $i => $tag) {
|
|
if ($tag instanceof ASSPositioningTag) {
|
|
$newTag = $tag->transitionMatrixTransform($line, $transform);
|
|
if ($newTag === null) {
|
|
return null;
|
|
}
|
|
if(!$newTag->equals($tag)){
|
|
//Special case!
|
|
if($newTag instanceof positionTag){
|
|
$container->tags[$i] = $newTag;
|
|
}else{
|
|
$container->transitions[$index][] = $newTag;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $container;
|
|
}
|
|
|
|
public function transitionStyleRecord(ASSLine $line, StyleRecord $record): ?containerTag {
|
|
$container = clone $this;
|
|
|
|
$index = $line->end - $line->start;
|
|
if(!isset($container->transitions[$index])){
|
|
$container->transitions[$index] = [];
|
|
}
|
|
|
|
foreach ($container->tags as $tag) {
|
|
if ($tag instanceof ASSStyleTag) {
|
|
$newTag = $tag->transitionStyleRecord($line, $record);
|
|
if ($newTag === null) {
|
|
return null;
|
|
}
|
|
if(!$newTag->equals($tag)){
|
|
$container->transitions[$index][] = $newTag;
|
|
}
|
|
}
|
|
}
|
|
return $container;
|
|
}
|
|
|
|
protected function try_append(?ASSTag $tag, $throw_on_null = false) {
|
|
if ($tag !== null) {
|
|
$this->tags[] = $tag;
|
|
return;
|
|
}
|
|
|
|
if ($throw_on_null) {
|
|
throw new \Exception();
|
|
}
|
|
}
|
|
|
|
public static function fromPathEntry(DrawPath $path, ?ClipPath $clip, ?ColorTransform $colorTransform, ?MatrixTransform $matrixTransform, bool $bakeTransforms = false): containerTag {
|
|
$container = new containerTag();
|
|
|
|
$container->try_append(new clipTag($clip));
|
|
|
|
if($path->style instanceof LineStyleRecord){ //Convert to fill
|
|
|
|
}
|
|
|
|
$container->try_append(borderTag::fromStyleRecord($path->style));
|
|
$container->try_append(shadowTag::fromStyleRecord($path->style));
|
|
$container->try_append(lineColorTag::fromStyleRecord($path->style)->applyColorTransform($colorTransform));
|
|
$container->try_append(fillColorTag::fromStyleRecord($path->style)->applyColorTransform($colorTransform));
|
|
$matrixTransform = $matrixTransform ?? MatrixTransform::identity();
|
|
|
|
if($bakeTransforms){
|
|
$container->bakeTransforms = $matrixTransform;
|
|
|
|
$container->try_append(positionTag::fromMatrixTransform($matrixTransform));
|
|
$drawTag = new drawTag($path->commands);
|
|
if(!$matrixTransform->getMatrix()->isEqual(MatrixFactory::identity(3))){
|
|
$drawTag = $drawTag->applyMatrixTransform($matrixTransform, false);
|
|
}
|
|
$container->try_append($drawTag);
|
|
}else{
|
|
$container->try_append(positionTag::fromMatrixTransform($matrixTransform));
|
|
$container->try_append(matrixTransformTag::fromMatrixTransform($matrixTransform));
|
|
|
|
$container->try_append(new drawTag($path->commands));
|
|
}
|
|
|
|
return $container;
|
|
}
|
|
|
|
public static function fromMatrixTransform(MatrixTransform $transform): ?ASSPositioningTag {
|
|
throw new \Exception();
|
|
}
|
|
|
|
public static function fromStyleRecord(StyleRecord $record): ?ASSStyleTag {
|
|
throw new \Exception();
|
|
}
|
|
|
|
public function equals(ASSTag $tag): bool {
|
|
if ($tag instanceof $this and count($this->tags) === count($tag->tags)) {
|
|
$tags = $this->tags;
|
|
$otherTags = $tag->tags;
|
|
foreach ($tags as $i => $t) {
|
|
foreach ($otherTags as $j => $t2) {
|
|
if ($t->equals($t2)) {
|
|
unset($tags[$i]);
|
|
unset($otherTags[$j]);
|
|
}
|
|
break;
|
|
}
|
|
if (isset($tags[$i])) {
|
|
break;
|
|
}
|
|
}
|
|
return count($tags) === 0 and count($otherTags) === 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function encode(ASSEventTime $event): string {
|
|
$ret = "";
|
|
foreach ($this->tags as $tag) {
|
|
if(!($tag instanceof drawingTag)){
|
|
$ret .= $tag->encode($event);
|
|
}
|
|
}
|
|
|
|
foreach ($this->transitions as $index => $transitions){
|
|
if(count($transitions) === 0){
|
|
continue;
|
|
}
|
|
|
|
//TODO: clone $line?
|
|
//TODO: animations with smoothing really don't play well. maybe allow them when only one animation "direction" exists, or smooth them manually?
|
|
//Or just don't animate MatrixTransform / do it in a single tick
|
|
|
|
if(ASSRenderer::getSetting("smoothTransitions", false)){
|
|
$startTime = $event->getMillisecondsFromStartOffset($index - 1) + 1;
|
|
$endTime = $event->getMillisecondsFromStartOffset($index + 1) - 1;
|
|
}else{
|
|
$startTime = $event->getMillisecondsFromStartOffset($index) - 1;
|
|
$endTime = $event->getMillisecondsFromStartOffset($index);
|
|
}
|
|
|
|
$ret .= "\\t({$startTime},{$endTime},";
|
|
foreach ($transitions as $tag){
|
|
$ret .= $tag->encode($event);
|
|
}
|
|
$ret .= ")";
|
|
}
|
|
foreach ($this->tags as $tag) {
|
|
if($tag instanceof drawingTag){
|
|
$ret .= $tag->encode($event);
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
public function transitionShape(ASSLine $line, Shape $shape): ?ASSPathTag {
|
|
$container = clone $this;
|
|
|
|
$index = $line->end - $line->start;
|
|
if(!isset($container->transitions[$index])){
|
|
$container->transitions[$index] = [];
|
|
}
|
|
|
|
foreach ($container->tags as $tag) {
|
|
if ($tag instanceof ASSPathTag) {
|
|
$newTag = $tag->transitionShape($line, $shape);
|
|
if ($newTag === null) {
|
|
return null;
|
|
}
|
|
if(!$newTag->equals($tag)){
|
|
$container->transitions[$index][] = $newTag;
|
|
}
|
|
}
|
|
}
|
|
return $container;
|
|
}
|
|
|
|
public function transitionClipPath(ASSLine $line, ?ClipPath $clip): ?ASSClipPathTag {
|
|
$container = clone $this;
|
|
|
|
$index = $line->end - $line->start;
|
|
if(!isset($container->transitions[$index])){
|
|
$container->transitions[$index] = [];
|
|
}
|
|
|
|
foreach ($container->tags as $tag) {
|
|
if ($tag instanceof ASSClipPathTag) {
|
|
$newTag = $tag->transitionClipPath($line, $clip);
|
|
if ($newTag === null) {
|
|
return null;
|
|
}
|
|
if(!$newTag->equals($tag)){
|
|
$container->transitions[$index][] = $newTag;
|
|
}
|
|
}
|
|
}
|
|
return $container;
|
|
}
|
|
} |