Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
DataHoarder | 76b26189af | ||
DataHoarder | 12ebd54c61 | ||
DataHoarder | 56cf40c28a |
55
audio.php
Normal file
55
audio.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
use swf2ass\ass\ASSEventTime;
|
||||
|
||||
require_once __DIR__ . "/vendor/autoload.php";
|
||||
|
||||
$width = 512;
|
||||
$height = 512;
|
||||
$ar = $width / $height;
|
||||
$frameRate = 30;
|
||||
$frameDurationMs = (1 / $frameRate) * 1000;
|
||||
|
||||
echo <<<ASSHEADER
|
||||
[Script Info]
|
||||
; Script generated by swf2ass
|
||||
; https://git.gammaspectra.live/WeebDataHoarder/swf2ass
|
||||
ScriptType: v4.00+
|
||||
WrapStyle: 0
|
||||
ScaledBorderAndShadow: yes
|
||||
YCbCr Matrix: PC.709
|
||||
PlayResX: {$width}
|
||||
PlayResY: {$height}
|
||||
|
||||
[Aegisub Project Garbage]
|
||||
Last Style Storage: f
|
||||
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;
|
||||
|
||||
|
||||
|
||||
$frameDuration = 1;
|
||||
for($frame = 0; $frame < 6523; $frame += $frameDuration){
|
||||
$line = new \swf2ass\ass\ASSLine();
|
||||
$line->layer = [$frame];
|
||||
$line->objectId = 0;
|
||||
$line->start = $frame;
|
||||
$line->end = $frame + $frameDuration - 1;
|
||||
$line->name = "{$line->start}->{$line->end}";
|
||||
$line->style = "f";
|
||||
$assEventTime = new ASSEventTime($line->start, $line->end - $line->start + 1, $frameDurationMs);
|
||||
$l = trim(file_get_contents("/tmp/tmp6_pqjbve/" . str_pad("$frame.ass", 10, "0", STR_PAD_LEFT)));
|
||||
$l = str_replace("pos(0,0)", "pos(0,416)", $l);
|
||||
$l = $line->encode($frameDurationMs) . "{\\blur0.5}" . $l;
|
||||
|
||||
echo $l . "\n";
|
||||
}
|
1
deps/martinez-rueda-php/src/Contour.php
vendored
1
deps/martinez-rueda-php/src/Contour.php
vendored
|
@ -9,6 +9,7 @@ namespace MartinezRueda;
|
|||
*/
|
||||
class Contour
|
||||
{
|
||||
/** @var Point[] */
|
||||
public $points = [];
|
||||
protected $holes = [];
|
||||
|
||||
|
|
1
deps/martinez-rueda-php/src/Polygon.php
vendored
1
deps/martinez-rueda-php/src/Polygon.php
vendored
|
@ -7,6 +7,7 @@ namespace MartinezRueda;
|
|||
*/
|
||||
class Polygon
|
||||
{
|
||||
/** @var Contour[] */
|
||||
public $contours = [];
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,31 +3,25 @@
|
|||
namespace swf2ass;
|
||||
|
||||
|
||||
use MartinezRueda\Algorithm;
|
||||
use MartinezRueda\Polygon;
|
||||
|
||||
class ClipPath {
|
||||
/** @var Shape[] */
|
||||
public array $shapes;
|
||||
|
||||
//private ComplexPolygon $clip;
|
||||
private Shape $clip;
|
||||
|
||||
/**
|
||||
* @param Shape[] $shapes
|
||||
*/
|
||||
public function __construct(array $shapes = []){
|
||||
$this->shapes = $shapes;
|
||||
public function __construct(?Shape $shape = null){
|
||||
$this->clip = $shape ?? new Shape();
|
||||
//$this->clip = ComplexPolygon::fromShape($shape ?? new Shape());
|
||||
}
|
||||
|
||||
public function getShape() : Shape{
|
||||
$shape = new Shape();
|
||||
foreach ($this->shapes as $s){
|
||||
$shape = $shape->merge($s);
|
||||
}
|
||||
return $shape;
|
||||
return $this->clip;
|
||||
return $this->clip->toShape();
|
||||
}
|
||||
|
||||
public function addShape(Shape $shape){
|
||||
$this->shapes[] = $shape;
|
||||
$this->clip = $this->clip->merge($shape);
|
||||
//$this->clip = ComplexPolygon::fromShape($this->clip->toShape()->merge($shape));
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,64 +33,24 @@ class ClipPath {
|
|||
* @return ClipPath
|
||||
*/
|
||||
public function intersect(ClipPath $other) : ClipPath{
|
||||
return new ClipPath($this->clip->merge($other->clip));
|
||||
try{
|
||||
return self::fromPolygon((new Algorithm())->getIntersection($this->toPolygon(), $other->toPolygon()));
|
||||
$clipPath = new ClipPath();
|
||||
$clipPath->clip = $this->clip->intersect($other->clip);
|
||||
return $clipPath;
|
||||
}catch (\Exception $e){
|
||||
var_dump($this);
|
||||
var_dump($other);
|
||||
echo $e;
|
||||
$self = $this->getShape()->flatten();
|
||||
$other = $other->getShape()->flatten();
|
||||
var_dump((new Shape($self->getRecords()))->getArea());
|
||||
$self = $this->getShape();
|
||||
$other = $other->getShape();
|
||||
var_dump((new \swf2ass\ass\drawTag(new Shape($self->getRecords()), 1))->encode(new \swf2ass\ass\ASSEventTime(1, 1, 1)));
|
||||
var_dump((new Shape($other->getRecords()))->getArea());
|
||||
var_dump((new \swf2ass\ass\drawTag(new Shape($other->getRecords()), 1))->encode(new \swf2ass\ass\ASSEventTime(1, 1, 1)));
|
||||
|
||||
//fgets(STDIN);
|
||||
return $this; //TODO: fix this breakage, some clips being overlapping shapes????
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function toPolygon() : Polygon{
|
||||
$contours = [];
|
||||
foreach ($this->shapes as $shape){
|
||||
$contours[] = array_map(function (Vector2 $point){return $point->toArray();}, $shape->toPoints());
|
||||
}
|
||||
return new Polygon($contours);
|
||||
}
|
||||
|
||||
|
||||
private static function fromPolygon(Polygon $p) : ClipPath{
|
||||
$result = $p->toArray();
|
||||
|
||||
$clipPath = new ClipPath();
|
||||
if(count($result) === 0){ //Nothing!
|
||||
return $clipPath;
|
||||
}
|
||||
|
||||
foreach ($result as $contour){
|
||||
$shape = new Shape();
|
||||
$start = $pos = new Vector2(...reset($contour));
|
||||
while (($p = next($contour)) !== false){
|
||||
$point = new Vector2(...$p);
|
||||
$shape->addRecord(new LineRecord($point, $pos));
|
||||
$pos = $point;
|
||||
}
|
||||
|
||||
//if($shape->getArea() > Constants::EPSILON){ //TODO
|
||||
$shape->addRecord(new LineRecord($start, $pos)); //Close shape
|
||||
$clipPath->addShape($shape);
|
||||
//}
|
||||
}
|
||||
|
||||
return $clipPath;
|
||||
}
|
||||
|
||||
public function applyMatrixTransform(MatrixTransform $transform, bool $applyTranslation = true) : ClipPath{
|
||||
$shapes = [];
|
||||
foreach ($this->shapes as $shape){
|
||||
$shapes[] = $transform->applyToShape($shape, $applyTranslation);
|
||||
}
|
||||
return new ClipPath($shapes);
|
||||
return new ClipPath($transform->applyToShape($this->getShape(), $applyTranslation));
|
||||
}
|
||||
}
|
161
src/ComplexPolygon.php
Normal file
161
src/ComplexPolygon.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
namespace swf2ass;
|
||||
|
||||
use MartinezRueda\Algorithm;
|
||||
use MartinezRueda\Contour;
|
||||
|
||||
class ComplexPolygon {
|
||||
|
||||
protected \MartinezRueda\Polygon $polygon;
|
||||
protected \MartinezRueda\Polygon $holes;
|
||||
|
||||
/**
|
||||
* @param Polygon[] $poly
|
||||
*/
|
||||
public function __construct(array $poly){
|
||||
|
||||
/** @var Polygon[] $polygons */
|
||||
$polygons = [];
|
||||
/** @var Polygon[] $holes */
|
||||
$holes = [];
|
||||
|
||||
while (count($poly) > 0){
|
||||
$polygon = reset($poly);
|
||||
|
||||
$found = false;
|
||||
if($polygon->getArea() < Constants::EPSILON){
|
||||
var_dump("WARNING: zero area shape: " . (new \swf2ass\ass\drawTag($polygon->toShape(), 1))->encode(new \swf2ass\ass\ASSEventTime(1, 1, 1)));
|
||||
$found = true; //Remove zero-area shapes
|
||||
}
|
||||
|
||||
if($found){
|
||||
array_shift($poly);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($polygons as $p){
|
||||
if($p->contains($polygon)){
|
||||
$found = true;
|
||||
$holes[] = $polygon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($found){
|
||||
array_shift($poly);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($poly as $p){
|
||||
if($p->contains($polygon)){
|
||||
$found = true;
|
||||
$holes[] = $polygon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$found){
|
||||
$polygons[] = $polygon;
|
||||
}
|
||||
array_shift($poly);
|
||||
}
|
||||
|
||||
$this->polygon = new \MartinezRueda\Polygon(array_map(function (Polygon $polygon){
|
||||
return array_map(function (Vector2 $point){
|
||||
return $point->toArray();
|
||||
}, $polygon->toPoints());
|
||||
}, $polygons));
|
||||
|
||||
$this->holes = new \MartinezRueda\Polygon(array_map(function (Polygon $polygon){
|
||||
return array_map(function (Vector2 $point){
|
||||
return $point->toArray();
|
||||
}, $polygon->toPoints());
|
||||
}, $holes));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function toShape() : Shape{
|
||||
$shape = new Shape();
|
||||
foreach ($this->polygon->contours as $contour){
|
||||
$shape = $shape->merge(Polygon::fromAlgorithmContour($contour)->toShape());
|
||||
}
|
||||
foreach ($this->holes->contours as $contour){
|
||||
$shape = $shape->merge(Polygon::fromAlgorithmContour($contour)->toShape());
|
||||
}
|
||||
return $shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ComplexPolygon $polygon
|
||||
* @return ComplexPolygon
|
||||
*/
|
||||
public function intersect(ComplexPolygon $polygon) : ComplexPolygon{
|
||||
|
||||
// H = H1 OR H2 OR ... Hn
|
||||
// C = P NAND H
|
||||
// Ca AND Cb = Pa AND Pb NAND (Ha OR Hb)
|
||||
|
||||
// (Ha OR Hb)
|
||||
$holes = (new Algorithm())->getUnion($this->holes, $polygon->holes);
|
||||
|
||||
// Pa AND Pb
|
||||
$poly = new ComplexPolygon(array_map(function (Contour $contour){
|
||||
return Polygon::fromAlgorithmContour($contour);
|
||||
}, (new Algorithm())->getIntersection($this->polygon, $polygon->polygon)->contours));
|
||||
|
||||
// Can get holes from above, merge them
|
||||
$holes = (new Algorithm())->getUnion($holes, $poly->holes);
|
||||
|
||||
|
||||
// P NAND H
|
||||
//TODO: maybe only do this at the end? check if correct as well
|
||||
return new ComplexPolygon(array_map(function (Contour $contour){
|
||||
return Polygon::fromAlgorithmContour($contour);
|
||||
}, (new Algorithm())->getDifference($poly->polygon, $holes)->contours));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LineRecord $line
|
||||
* @param bool $includeTouching
|
||||
* @return LineRecord[]|Vector2[]
|
||||
*/
|
||||
public function calculateLineIntersections(LineRecord $line, bool $includeTouching = false) : array{
|
||||
$intersections = $this->calculateLineIntersections($line, $includeTouching);
|
||||
foreach ($this->holes as $p){
|
||||
$intersections = array_merge($intersections, $p->calculateLineIntersections($line, $includeTouching));
|
||||
}
|
||||
return $intersections;
|
||||
}
|
||||
|
||||
public static function fromShape(Shape $shape) : ComplexPolygon {
|
||||
$shape = $shape->flatten();
|
||||
|
||||
$poly = [];
|
||||
/** @var LineRecord[] $edges */
|
||||
$edges = [];
|
||||
|
||||
|
||||
$lastEdge = null;
|
||||
foreach ($shape->getRecords() as $record){
|
||||
if($lastEdge !== null and !$lastEdge->getEnd()->equals($record->getStart())){
|
||||
$poly[] = new Polygon($edges);
|
||||
$edges = [];
|
||||
}
|
||||
if($record instanceof LineRecord){
|
||||
$edges[] = $record;
|
||||
}else{
|
||||
var_dump($record);
|
||||
throw new \Exception("Found record of type " . get_class($record));
|
||||
}
|
||||
$lastEdge = $record;
|
||||
}
|
||||
|
||||
if(count($edges) > 0){
|
||||
$poly[] = new Polygon($edges);
|
||||
}
|
||||
|
||||
return new ComplexPolygon($poly);
|
||||
}
|
||||
}
|
118
src/Polygon.php
Normal file
118
src/Polygon.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace swf2ass;
|
||||
|
||||
|
||||
|
||||
use MartinezRueda\Algorithm;
|
||||
|
||||
class Polygon {
|
||||
|
||||
/** @var LineRecord[] */
|
||||
protected array $edges;
|
||||
|
||||
private ?float $area = null;
|
||||
|
||||
public function __construct(array $edges){
|
||||
$this->edges = $edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LineRecord[]
|
||||
*/
|
||||
public function getEdges() : array{
|
||||
return $this->edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LineRecord $line
|
||||
* @param bool $includeTouching
|
||||
* @return LineRecord[]|Vector2[]
|
||||
*/
|
||||
public function calculateLineIntersections(LineRecord $line, bool $includeTouching = false) : array{
|
||||
$intersections = [];
|
||||
foreach ($this->edges as $edge){
|
||||
if(($intersection = $line->intersect($edge)) !== null){
|
||||
if($intersection instanceof LineRecord and !$includeTouching){
|
||||
continue;
|
||||
}
|
||||
$intersections[] = $intersection;
|
||||
}
|
||||
}
|
||||
|
||||
return $intersections;
|
||||
}
|
||||
|
||||
public function toAlgorithmPolygon() : \MartinezRueda\Polygon{
|
||||
return new \MartinezRueda\Polygon([array_map(function (Vector2 $point){
|
||||
return $point->toArray();
|
||||
}, $this->toPoints())]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \MartinezRueda\Contour $contour
|
||||
* @return Polygon
|
||||
*/
|
||||
public static function fromAlgorithmContour(\MartinezRueda\Contour $contour) : Polygon {
|
||||
$records = [];
|
||||
$i = reset($contour->points);
|
||||
$start = $pos = new Vector2($i->x, $i->y);
|
||||
while (($p = next($contour->points)) !== false){
|
||||
$point = new Vector2($p->x, $p->y);
|
||||
$records[] = new LineRecord($point, $pos);
|
||||
$pos = $point;
|
||||
}
|
||||
|
||||
if(!$start->equals($pos)){
|
||||
$records[] = new LineRecord($start, $pos); //Close shape
|
||||
}
|
||||
|
||||
return new Polygon($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector2[]
|
||||
*/
|
||||
public function toPoints() : array{
|
||||
return array_map(function (LineRecord $record){
|
||||
return $record->getStart();
|
||||
}, $this->edges);
|
||||
}
|
||||
|
||||
public function isPointInside(Vector2 $point, bool $includeTouching = false) : bool{
|
||||
$segment = new LineRecord($point, new Vector2(PHP_INT_MAX, PHP_INT_MAX)); //TODO: maybe pick better falues? this might wrap
|
||||
return count($this->calculateLineIntersections($segment, $includeTouching)) % 2 === 0; //even-odd rule
|
||||
}
|
||||
|
||||
public function contains(Polygon $polygon): bool {
|
||||
foreach ($polygon->edges as $edge){
|
||||
if(!$this->isPointInside($edge->getStart())){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach ($this->edges as $edge){ //Reverse check
|
||||
if($polygon->isPointInside($edge->getStart())){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toShape() : Shape{
|
||||
return new Shape($this->getEdges());
|
||||
}
|
||||
|
||||
public function getArea() : float{
|
||||
if($this->area !== null){
|
||||
return $this->area;
|
||||
}
|
||||
$this->area = 0;
|
||||
|
||||
foreach ($this->edges as $i => $e1){
|
||||
$e2 = $this->edges[($i + 1) % count($this->edges)];
|
||||
$this->area += $e1->getStart()->x * $e2->getStart()->y - $e1->getStart()->y * $e2->getStart()->x;
|
||||
}
|
||||
|
||||
return $this->area = abs($this->area / 2);
|
||||
}
|
||||
}
|
|
@ -92,7 +92,7 @@ class ViewFrame {
|
|||
}
|
||||
}
|
||||
|
||||
if (count($clipShape->shapes) > 0) {
|
||||
if (count($clipShape->getShape()->getRecords()) > 0) {
|
||||
$clipShape = $clipShape->applyMatrixTransform($clipObject->matrixTransform);
|
||||
$clipPath = $clipPath === null ? $clipShape : $clipShape->intersect($clipPath);
|
||||
}
|
||||
|
|
24
swf2json.php
Normal file
24
swf2json.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . "/vendor/autoload.php";
|
||||
|
||||
$swfContent = file_get_contents($argv[1]);
|
||||
|
||||
$swf = new \swf\SWF($swfContent);
|
||||
$swfContent = null;
|
||||
unset($swfContent);
|
||||
|
||||
|
||||
if ($swf->header["signature"]) {
|
||||
$json = [
|
||||
"header" => $swf->header,
|
||||
"tags" => []
|
||||
];
|
||||
foreach ($swf->tags as $tag) {
|
||||
$json["tags"][] = $swf->parseTag($tag);
|
||||
}
|
||||
|
||||
echo json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue