Fix colors, add multi layer system WiP, fix single frame move precision, reduced drawing commands
This commit is contained in:
parent
bc592291bb
commit
ec04e81253
2
deps/swf-4real/src/SWFio.php
vendored
2
deps/swf-4real/src/SWFio.php
vendored
|
@ -107,7 +107,7 @@ class SWFio {
|
|||
|
||||
public function collectSB($num) {
|
||||
$val = $this->collectBits($num);
|
||||
if ($val >= (1 << ($num - 1))) { // If high bit is set
|
||||
if ($num > 0 and ($val & (1 << ($num - 1))) > 0) { // If high bit is set
|
||||
$val -= 1 << $num; // Negate
|
||||
}
|
||||
return $val;
|
||||
|
|
4
deps/swf-4real/src/SWFrec.php
vendored
4
deps/swf-4real/src/SWFrec.php
vendored
|
@ -23,9 +23,9 @@ namespace swf;
|
|||
|
||||
|
||||
class SWFrec {
|
||||
private $io; // SWF for basic I/O
|
||||
private SWFio $io; // SWF for basic I/O
|
||||
|
||||
public function __construct($io) {
|
||||
public function __construct(SWFio $io) {
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
|
|
8
deps/swf-4real/src/SWFtag.php
vendored
8
deps/swf-4real/src/SWFtag.php
vendored
|
@ -24,11 +24,11 @@ namespace swf;
|
|||
|
||||
|
||||
class SWFtag {
|
||||
private $io; // SWFio for basic I/O
|
||||
private $rec; // SWFrec for simple and complex records
|
||||
private $swfVersion; // Version of this SWF file
|
||||
private SWFio $io; // SWFio for basic I/O
|
||||
private SWFrec $rec; // SWFrec for simple and complex records
|
||||
private int $swfVersion; // Version of this SWF file
|
||||
|
||||
public function __construct($io, $rec, $swfVersion) {
|
||||
public function __construct(SWFio $io, SWFrec $rec, int $swfVersion) {
|
||||
$this->io = $io;
|
||||
$this->rec = $rec;
|
||||
$this->swfVersion = $swfVersion;
|
||||
|
|
10
src/Circle.php
Normal file
10
src/Circle.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace swf2ass;
|
||||
|
||||
|
||||
class Circle extends Oval {
|
||||
public function __construct(Vector2 $center, float $radius) {
|
||||
parent::__construct($center, new Vector2($radius, $radius));
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ class ColorTransform {
|
|||
}
|
||||
|
||||
public static function identity(): ColorTransform {
|
||||
return new ColorTransform(new Color(256, 256, 256, 256), new Color(0, 0, 0));
|
||||
return new ColorTransform(new Color(256, 256, 256, 256), new Color(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
public function applyToStyleRecord(StyleRecord $record): StyleRecord {
|
||||
|
|
|
@ -3,5 +3,8 @@
|
|||
namespace swf2ass;
|
||||
|
||||
interface ComplexShape {
|
||||
public function draw();
|
||||
/**
|
||||
* @return Record[]
|
||||
*/
|
||||
public function draw() : array;
|
||||
}
|
|
@ -28,6 +28,26 @@ class CubicCurveRecord implements Record {
|
|||
}
|
||||
|
||||
public static function fromQuadraticRecord(QuadraticCurveRecord $q): CubicCurveRecord {
|
||||
return new CubicCurveRecord($q->start->add($q->control->multiply(2))->divide(3), $q->anchor->add($q->control->multiply(2))->divide(3), $q->anchor, $q->start);
|
||||
return new CubicCurveRecord(
|
||||
$q->start->add($q->control->multiply(2))->divide(3),
|
||||
$q->anchor->add($q->control->multiply(2))->divide(3),
|
||||
$q->anchor,
|
||||
$q->start
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds if Cubic curve is a perfect fit of a Quadratic curve (aka, it was upconverted)
|
||||
*
|
||||
* @return ?QuadraticCurveRecord
|
||||
*/
|
||||
public function toSingleQuadraticRecord() : ?QuadraticCurveRecord{
|
||||
$control1 = $this->control1->multiply(3)->sub($this->start)->divide(2);
|
||||
$control2 = $this->control2->multiply(3)->sub($this->anchor)->divide(2);
|
||||
if($control1->equals($control2)){
|
||||
return new QuadraticCurveRecord($control1, $this->anchor, $this->start);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -9,24 +9,55 @@ use MathPHP\LinearAlgebra\NumericMatrix;
|
|||
class MatrixTransform {
|
||||
|
||||
private NumericMatrix $matrix;
|
||||
private Vector2 $translation;
|
||||
|
||||
public function __construct(?Vector2 $scale, ?Vector2 $rotateSkew, ?Vector2 $translation) {
|
||||
$this->translation = $translation ?? new Vector2(0, 0);
|
||||
$this->matrix = MatrixFactory::createNumeric([
|
||||
[$scale !== null ? $scale->x : 1, $rotateSkew !== null ? $rotateSkew->y : 0],
|
||||
[$rotateSkew !== null ? $rotateSkew->x : 0, $scale !== null ? $scale->y : 1]
|
||||
[$scale !== null ? $scale->x : 1, $rotateSkew !== null ? $rotateSkew->y : 0, 0],
|
||||
[$rotateSkew !== null ? $rotateSkew->x : 0, $scale !== null ? $scale->y : 1, 0],
|
||||
[$translation !== null ? $translation->x : 0, $translation !== null ? $translation->y : 0, 1]
|
||||
]);
|
||||
}
|
||||
|
||||
public static function identity(): MatrixTransform {
|
||||
return new MatrixTransform(new Vector2(1, 1), new Vector2(0, 0), new Vector2(0, 0));
|
||||
public static function scale(Vector2 $scale) : MatrixTransform{
|
||||
return new MatrixTransform($scale, null, null);
|
||||
}
|
||||
|
||||
public static function rotate(float $angle) : MatrixTransform{
|
||||
$cos = cos($angle);
|
||||
$sin = sin($angle);
|
||||
return new MatrixTransform(new Vector2($cos, $cos), new Vector2(-$sin, $sin), null);
|
||||
}
|
||||
|
||||
public static function translate(Vector2 $translation) : MatrixTransform{
|
||||
return new MatrixTransform(null, null, $translation);
|
||||
}
|
||||
|
||||
public static function identity(): MatrixTransform {
|
||||
return new MatrixTransform(null, null, null);
|
||||
}
|
||||
|
||||
//TODO: skewX, skewY
|
||||
|
||||
public function combine(MatrixTransform $other): MatrixTransform {
|
||||
/*
|
||||
return new MatrixTransform(
|
||||
new Vector2(
|
||||
$this->get_a() * $other->get_a() + $this->get_c() * $other->get_b(),
|
||||
$this->get_b() * $other->get_c() + $this->get_d() * $other->get_d(),
|
||||
),
|
||||
new Vector2(
|
||||
$this->get_b() * $other->get_a() + $this->get_d() * $other->get_b(),
|
||||
$this->get_a() * $other->get_c() + $this->get_c() * $other->get_d(),
|
||||
),
|
||||
new Vector2(
|
||||
$this->get_a() * $other->get_e() + $this->get_c() * $other->get_f() + $this->get_e(),
|
||||
$this->get_b() * $other->get_e() + $this->get_d() * $other->get_f() + $this->get_f()
|
||||
)
|
||||
);
|
||||
*/
|
||||
|
||||
$result = clone $this;
|
||||
$result->matrix = $this->matrix->multiply($other->matrix);
|
||||
$result->translation = $this->translation->add($other->translation); //TODO check
|
||||
$result->matrix = $other->matrix->multiply($this->matrix);
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -47,11 +78,11 @@ class MatrixTransform {
|
|||
}
|
||||
|
||||
public function get_e(){
|
||||
return $this->translation->x;
|
||||
return $this->matrix->get(2, 0);
|
||||
}
|
||||
|
||||
public function get_f(){
|
||||
return $this->translation->y;
|
||||
return $this->matrix->get(2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,9 +114,11 @@ class MatrixTransform {
|
|||
|
||||
$result->scale = new Vector2($this->get_a(), $this->get_d());
|
||||
$result->skew = new Vector2($this->get_c(), $this->get_b());
|
||||
$result->translation = $this->translation;
|
||||
$result->translation = new Vector2($this->get_e(), $this->get_f());
|
||||
return $result;
|
||||
|
||||
//TODO: all down here has to be shifted
|
||||
|
||||
$scaleX = sqrt($this->get_a() * $this->get_a() + $this->get_b() * $this->get_b());
|
||||
$scaleY = sqrt($this->get_c() * $this->get_c() + $this->get_d() * $this->get_d());
|
||||
|
||||
|
@ -143,12 +176,16 @@ class MatrixTransform {
|
|||
}
|
||||
|
||||
public function getTranslation(): Vector2 {
|
||||
return $this->translation;
|
||||
return $this->applyToVector(new Vector2(0, 0));
|
||||
}
|
||||
|
||||
public function applyToVector(Vector2 $vector, bool $applyTranslation = true): Vector2 {
|
||||
$result = MatrixFactory::createNumeric([[$vector->x, $vector->y]])->multiply($this->matrix);
|
||||
return $applyTranslation ? (new Vector2($result->get(0, 0), $result->get(0, 1)))->add($this->translation) : new Vector2($result->get(0, 0), $result->get(0, 1));
|
||||
if($applyTranslation){
|
||||
$result = MatrixFactory::createFromRowVector([$vector->x, $vector->y, 1])->multiply($this->matrix);
|
||||
}else{
|
||||
$result = MatrixFactory::createFromRowVector([$vector->x, $vector->y])->multiply($this->matrix->submatrix(0, 0, 1, 1));
|
||||
}
|
||||
return new Vector2($result->get(0, 0), $result->get(0, 1));
|
||||
}
|
||||
|
||||
public function applyToShape(Shape $shape, bool $applyTranslation = true): Shape {
|
||||
|
@ -161,7 +198,7 @@ class MatrixTransform {
|
|||
}
|
||||
|
||||
public function equals(MatrixTransform $other): bool {
|
||||
return $this->translation->equals($other->translation) and $this->matrix->isEqual($other->matrix);
|
||||
return $this->matrix->isEqual($other->matrix);
|
||||
}
|
||||
|
||||
static function fromArray(array $element): MatrixTransform {
|
||||
|
|
36
src/Oval.php
Normal file
36
src/Oval.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace swf2ass;
|
||||
|
||||
|
||||
class Oval implements ComplexShape {
|
||||
|
||||
protected const c = 0.55228474983; // (4/3) * (sqrt(2) - 1)
|
||||
//protected const c = 0.551915024494; // https://spencermortensen.com/articles/bezier-circle/
|
||||
|
||||
public Vector2 $center;
|
||||
public Vector2 $radius;
|
||||
|
||||
public function __construct(Vector2 $center, Vector2 $radius) {
|
||||
$this->center = $center;
|
||||
$this->radius = $radius;
|
||||
}
|
||||
|
||||
protected function getQuarter(Vector2 $size) : CubicCurveRecord{
|
||||
return new CubicCurveRecord(
|
||||
new Vector2($this->center->x - $size->x, $this->center->y - self::c * $size->y),
|
||||
new Vector2($this->center->x - self::c * $size->x, $this->center->y - $size->y),
|
||||
new Vector2($this->center->x, $this->center->y - $size->y),
|
||||
new Vector2($this->center->x - $size->x, $this->center->y)
|
||||
);
|
||||
}
|
||||
|
||||
public function draw(): array {
|
||||
return [
|
||||
$this->getQuarter(new Vector2(-$this->radius->x, $this->radius->y)),
|
||||
$this->getQuarter($this->radius),
|
||||
$this->getQuarter(new Vector2($this->radius->x, -$this->radius->y)),
|
||||
$this->getQuarter(new Vector2(-$this->radius->x, -$this->radius->y)),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -54,24 +54,4 @@ class Rectangle implements ComplexShape {
|
|||
public static function fromArray(array $element): Rectangle {
|
||||
return new Rectangle(new Vector2($element["xmin"], $element["ymin"]), new Vector2($element["xmax"], $element["ymax"]));
|
||||
}
|
||||
|
||||
public static function fromData($bitdata, &$offset): Rectangle {
|
||||
$nbits = Utils::binary2dec(substr($bitdata, $offset, 5));
|
||||
var_dump($nbits);
|
||||
$offset += 5;
|
||||
|
||||
$xMin = Utils::binary2dec(substr($bitdata, $offset, $nbits));
|
||||
$offset += $nbits;
|
||||
|
||||
$xMax = Utils::binary2dec(substr($bitdata, $offset, $nbits));
|
||||
$offset += $nbits;
|
||||
|
||||
$yMin = Utils::binary2dec(substr($bitdata, $offset, $nbits));
|
||||
$offset += $nbits;
|
||||
|
||||
$yMax = Utils::binary2dec(substr($bitdata, $offset, $nbits));
|
||||
$offset += $nbits;
|
||||
|
||||
return new Rectangle(new Vector2($xMin, $yMin), new Vector2($xMax, $yMax));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ class SWFTreeProcessor {
|
|||
$framesCount = $node["frameCount"];
|
||||
$spriteTree = new SWFTreeProcessor($objectID, $node["tags"], $this->objects);
|
||||
$actions = new ActionList();
|
||||
|
||||
/** @var ViewFrame[] $frames */
|
||||
$frames = [];
|
||||
while (($frame = $spriteTree->nextFrame($actions)) !== null) {
|
||||
|
@ -106,6 +107,8 @@ class SWFTreeProcessor {
|
|||
$this->layout->remove($depth);
|
||||
break;
|
||||
}
|
||||
//TODO: ratio, which also seems to exists for Sprites
|
||||
|
||||
|
||||
|
||||
$transform = isset($node["matrix"]) ? MatrixTransform::fromArray($node["matrix"]) : null;
|
||||
|
@ -117,16 +120,14 @@ class SWFTreeProcessor {
|
|||
$currentObject = $this->layout->get($depth);
|
||||
|
||||
|
||||
if ($replace and $currentObject !== null) {
|
||||
if ($currentObject->getObjectId() === $object->getObjectId()) {
|
||||
if ($transform !== null) {
|
||||
$currentObject->setMatrixTransform($transform);
|
||||
}
|
||||
if ($colorTransform !== null) {
|
||||
$currentObject->setColorTransform($colorTransform);
|
||||
}
|
||||
break;
|
||||
if ($replace and $currentObject !== null and $currentObject->getObjectId() === $object->getObjectId()) {
|
||||
if ($transform !== null) {
|
||||
$currentObject->setMatrixTransform($transform);
|
||||
}
|
||||
if ($colorTransform !== null) {
|
||||
$currentObject->setColorTransform($colorTransform);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$view = $clipDepth !== null ? new ClippingViewLayout($clipDepth, $objectID, $object, $this->layout) : new ViewLayout($objectID, $object, $this->layout);
|
||||
|
|
|
@ -26,7 +26,7 @@ class SpriteDefinition implements MultiFrameObjectDefinition {
|
|||
|
||||
public function getShapeList(): DrawPathList {
|
||||
$list = new DrawPathList();
|
||||
foreach ($this->frames[$this->frameCounter]->render(0, [], ColorTransform::identity(), MatrixTransform::identity())->getObjects() as $object) {
|
||||
foreach ($this->frames[$this->frameCounter]->render(0, [], null, null)->getObjects() as $object) {
|
||||
$list = $list->merge($object->drawPathList);
|
||||
}
|
||||
return $list;
|
||||
|
|
|
@ -14,35 +14,38 @@ class StyleList {
|
|||
$this->lineStyles = $lineStyles;
|
||||
}
|
||||
|
||||
public static function parseFillStyleRecord(array $node): FillStyleRecord {
|
||||
switch ($node["type"]) {
|
||||
case 0x00: // Solid fill
|
||||
return new FillStyleRecord(Color::fromArray($node["color"]));
|
||||
break;
|
||||
case 0x10: // Linear gradient fill
|
||||
return new FillStyleRecord(LinearGradient::fromArray($node["gradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x12: // Radial gradient fill
|
||||
return new FillStyleRecord(RadialGradient::fromArray($node["gradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x13: // Focal gradient fill
|
||||
return new FillStyleRecord(FocalGradient::fromArray($node["focalGradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x40: // Repeating bitmap fill
|
||||
case 0x41: // Clipped bitmap fill
|
||||
case 0x42: // Non-smoothed repeating bitmap
|
||||
case 0x43: // Non-smoothed clipped bitmap
|
||||
var_dump($node);
|
||||
return $node["bitmapId"] === 65535 ? new FillStyleRecord(new Color(0, 0, 0, 0)) : new FillStyleRecord(new Color(0, 0, 0, 20));
|
||||
break;
|
||||
default:
|
||||
var_dump($node);
|
||||
throw new \Exception("Unknown style " . $node["type"]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromArray(array $element): StyleList {
|
||||
$fillStyles = [];
|
||||
$lineStyles = [];
|
||||
foreach ($element["fillStyles"] as $node) {
|
||||
|
||||
switch ($node["type"]) {
|
||||
case 0x00: // Solid fill
|
||||
$fillStyles[] = new FillStyleRecord(Color::fromArray($node["color"]));
|
||||
break;
|
||||
case 0x10: // Linear gradient fill
|
||||
$fillStyles[] = new FillStyleRecord(LinearGradient::fromArray($node["gradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x12: // Radial gradient fill
|
||||
$fillStyles[] = new FillStyleRecord(RadialGradient::fromArray($node["gradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x13: // Focal gradient fill
|
||||
$fillStyles[] = new FillStyleRecord(FocalGradient::fromArray($node["focalGradient"], MatrixTransform::fromArray($node["matrix"])));
|
||||
break;
|
||||
case 0x40: // Repeating bitmap fill
|
||||
case 0x41: // Clipped bitmap fill
|
||||
case 0x42: // Non-smoothed repeating bitmap
|
||||
case 0x43: // Non-smoothed clipped bitmap
|
||||
var_dump($node);
|
||||
$fillStyles[] = new FillStyleRecord(new Color(0, 0, 0, 20));
|
||||
break;
|
||||
default:
|
||||
var_dump($node);
|
||||
throw new \Exception("Unknown style " . $node["type"]);
|
||||
}
|
||||
$fillStyles[] = self::parseFillStyleRecord($node);
|
||||
}
|
||||
foreach ($element["lineStyles"] as $node) {
|
||||
$color = isset($node["color"]) ? Color::fromArray($node["color"]) : null;
|
||||
|
@ -50,6 +53,12 @@ class StyleList {
|
|||
//TODO: fill flag
|
||||
if ($color === null) {
|
||||
var_dump($node);
|
||||
if(isset($node["fillType"])){
|
||||
$color = self::parseFillStyleRecord($node["fillType"])->fill;
|
||||
if($color instanceof Gradient){
|
||||
$color = $color->getItems()[0]->color;
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO: any reason for max(Constants::TWIP_SIZE)?
|
||||
$lineStyles[] = new LineStyleRecord(max(Constants::TWIP_SIZE, $node["width"]), $color);
|
||||
|
|
|
@ -104,11 +104,25 @@ class ASSLine {
|
|||
$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): string {
|
||||
if($frameDurationMs === 1000 and $this->cachedEncode !== null){
|
||||
return $this->cachedEncode;
|
||||
}
|
||||
$line = ($this->isComment ? "Comment" : "Dialogue") . ": " . $this->layer[0] . "," . self::encodeTime($this->start * $frameDurationMs) . "," . self::encodeTime(($this->end + 1) * $frameDurationMs) . "," . $this->style . "," . $this->name . "," . $this->marginLeft . "," . $this->marginRight . "," . $this->marginVertical . "," . $this->effect . ",";
|
||||
$line = ($this->isComment ? "Comment" : "Dialogue") . ": " . $this->getPackedLayer() . "," . self::encodeTime($this->start * $frameDurationMs) . "," . self::encodeTime(($this->end + 1) * $frameDurationMs) . "," . $this->style . "," . $this->name . "," . $this->marginLeft . "," . $this->marginRight . "," . $this->marginVertical . "," . $this->effect . ",";
|
||||
foreach ($this->tags as $tag){
|
||||
$line .= "{" . $tag->encode($this, $frameDurationMs) . "}";
|
||||
}
|
||||
|
|
|
@ -44,11 +44,12 @@ class containerTag implements ASSColorTag, ASSPositioningTag, ASSStyleTag {
|
|||
|
||||
public function transitionMatrixTransform(ASSLine $line, MatrixTransform $transform): ?containerTag {
|
||||
if($this->bakeTransforms !== null){
|
||||
if(!$transform->getMatrix()->isEqual($this->bakeTransforms->getMatrix())){ //Do not allow matrix changes but moves
|
||||
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 - 1;
|
||||
|
@ -126,12 +127,11 @@ class containerTag implements ASSColorTag, ASSPositioningTag, ASSStyleTag {
|
|||
if($bakeTransforms){
|
||||
$container->bakeTransforms = $matrixTransform;
|
||||
|
||||
$container->try_append(positionTag::fromMatrixTransform($matrixTransform));
|
||||
$drawTag = new drawTag($path->commands);
|
||||
if(!$matrixTransform->getMatrix()->isEqual(MatrixFactory::identity(2))){
|
||||
if(!$matrixTransform->getMatrix()->isEqual(MatrixFactory::identity(3))){
|
||||
$drawTag = $drawTag->applyMatrixTransform($matrixTransform, false);
|
||||
}
|
||||
$pos = $matrixTransform->getTranslation()->toPixel();
|
||||
$container->try_append(new positionTag($pos, $pos, 1, 1));
|
||||
$container->try_append($drawTag);
|
||||
}else{
|
||||
$container->try_append(positionTag::fromMatrixTransform($matrixTransform));
|
||||
|
@ -186,7 +186,7 @@ class containerTag implements ASSColorTag, ASSPositioningTag, ASSStyleTag {
|
|||
//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
|
||||
$ret .= "\\t(" . floor($frameDurationMs * $index) . "," . (floor($frameDurationMs * ($index + 1))) . ",";
|
||||
$ret .= "\\t(" . (floor($frameDurationMs * $index) - 1) . "," . (floor($frameDurationMs * ($index + 1)) - 1) . ",";
|
||||
//$ret .= "\\t(" . floor($frameDurationMs * ($index + 1)) . "," . floor($frameDurationMs * ($index + 1)) . ",";
|
||||
foreach ($transitions as $tag){
|
||||
$ret .= $tag->encode($line, $frameDurationMs);
|
||||
|
|
|
@ -37,20 +37,21 @@ abstract class drawingTag implements ASSTag {
|
|||
foreach ($this->shape->edges as $edge) {
|
||||
if ($edge instanceof MoveRecord) {
|
||||
$coords = $edge->coord->multiply($scale / Constants::TWIP_SIZE);
|
||||
$commands[] = "m " . round($coords->x, $precision) . " " . round($coords->y, $precision);
|
||||
$commands[] = ($lastEdge instanceof $edge ? " " : "m ") . round($coords->x, $precision) . " " . round($coords->y, $precision);
|
||||
} else if ($edge instanceof LineRecord) {
|
||||
$coords = $edge->coord->multiply($scale / Constants::TWIP_SIZE);
|
||||
$commands[] = "l " . round($coords->x, $precision) . " " . round($coords->y, $precision);
|
||||
$commands[] = ($lastEdge instanceof $edge ? " " : "l ") . round($coords->x, $precision) . " " . round($coords->y, $precision);
|
||||
} else if ($edge instanceof QuadraticCurveRecord or $edge instanceof CubicCurveRecord or $edge instanceof CubicSplineCurveRecord) {
|
||||
if ($edge instanceof QuadraticCurveRecord) {
|
||||
$edge = CubicCurveRecord::fromQuadraticRecord($edge);
|
||||
//TODO: check "q" command
|
||||
}
|
||||
|
||||
if ($edge instanceof CubicCurveRecord) {
|
||||
$control1 = $edge->control1->multiply($scale / Constants::TWIP_SIZE);
|
||||
$control2 = $edge->control2->multiply($scale / Constants::TWIP_SIZE);
|
||||
$anchor = $edge->anchor->multiply($scale / Constants::TWIP_SIZE);
|
||||
$commands[] = "b " . round($control1->x, $precision) . " " . round($control1->y, $precision) . " " . round($control2->x, $precision) . " " . round($control2->y, $precision) . " " . round($anchor->x, $precision) . " " . round($anchor->y, $precision);
|
||||
$commands[] = ($lastEdge instanceof $edge ? " " : "b ") . round($control1->x, $precision) . " " . round($control1->y, $precision) . " " . round($control2->x, $precision) . " " . round($control2->y, $precision) . " " . round($anchor->x, $precision) . " " . round($anchor->y, $precision);
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
@ -63,6 +64,7 @@ abstract class drawingTag implements ASSTag {
|
|||
}
|
||||
|
||||
$commands[] = "s " . implode(" ", $controlPoints) . " " . round($anchor->x, $precision) . " " . round($anchor->y, $precision);
|
||||
$commands[] = "c";
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,7 @@ class positionTag implements ASSPositioningTag {
|
|||
}
|
||||
|
||||
public function transitionMatrixTransform(ASSLine $line, MatrixTransform $transform): ?positionTag {
|
||||
$translation = $transform->getTranslation()->toPixel();
|
||||
$translation = $transform->applyToVector(new Vector2(0, 0), true)->toPixel();
|
||||
|
||||
$frame = $line->end - $line->start;
|
||||
|
||||
|
@ -68,12 +68,12 @@ class positionTag implements ASSPositioningTag {
|
|||
|
||||
public function encode(ASSLine $line, float $frameDurationMs): string {
|
||||
$frame = $line->end - $line->start;
|
||||
$hasMoved = $this->start < $frame;
|
||||
$hasMoved = $this->start !== $this->end;
|
||||
|
||||
$shift = $this->end - $this->start;
|
||||
|
||||
if($hasMoved){
|
||||
return "\\move(" . $this->from->x ."," . $this->from->y ."," . $this->to->x ."," . $this->to->y .",".ceil(($shift > 1 ? ($this->start - 1) : $this->end) * $frameDurationMs).",".floor($this->end * $frameDurationMs).")";
|
||||
return "\\move(" . $this->from->x ."," . $this->from->y ."," . $this->to->x ."," . $this->to->y .",".(ceil(($shift > 1 ? ($this->start - 1) : $this->start) * $frameDurationMs) - 1).",".(floor(($shift > 1 ? $this->end : $this->end - 1) * $frameDurationMs) - 1).")";
|
||||
}
|
||||
return "\\pos(".$this->from->x ."," . $this->from->y.")";
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class positionTag implements ASSPositioningTag {
|
|||
}
|
||||
|
||||
public static function fromMatrixTransform(MatrixTransform $transform): ?positionTag {
|
||||
$translation = $transform->getTranslation()->toPixel();
|
||||
$translation = $transform->applyToVector(new Vector2(0, 0), true)->toPixel();
|
||||
return new positionTag($translation, $translation, 1, 1);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ if ($swf->header["signature"]) {
|
|||
"bakeTransforms" => true //TODO: fix ASS matrix transform rendering and remove this
|
||||
]);
|
||||
|
||||
$keyFrameInterval = 10 * $processor->getFrameRate(); //kf every 10 seconds
|
||||
$keyFrameInterval = 10 * $processor->getFrameRate(); //kf every 10 seconds TODO: make this dynamic, per-shape
|
||||
$frameOffset = 0;
|
||||
$lastFrame = null;
|
||||
while(($frame = $processor->nextFrameOutput()) !== null){
|
||||
|
|
Loading…
Reference in a new issue