Compare commits

...

3 commits
master ... wip

Author SHA1 Message Date
DataHoarder 76b26189af a 2023-08-03 01:52:18 +02:00
DataHoarder 12ebd54c61 wip: clip 2023-08-03 01:52:18 +02:00
DataHoarder 56cf40c28a wip: clip 2023-08-03 01:52:18 +02:00
8 changed files with 379 additions and 65 deletions

55
audio.php Normal file
View 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";
}

View file

@ -9,6 +9,7 @@ namespace MartinezRueda;
*/
class Contour
{
/** @var Point[] */
public $points = [];
protected $holes = [];

View file

@ -7,6 +7,7 @@ namespace MartinezRueda;
*/
class Polygon
{
/** @var Contour[] */
public $contours = [];
/**

View file

@ -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
View 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
View 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);
}
}

View file

@ -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
View 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);
}