WiP: gradients and others

This commit is contained in:
DataHoarder 2023-08-03 01:48:26 +02:00
parent fd89b9b5f6
commit 22c2fca7be
23 changed files with 333 additions and 82 deletions

12
composer.lock generated
View file

@ -51,16 +51,16 @@
},
{
"name": "markrogoyski/math-php",
"version": "v2.5.0",
"version": "v2.6.0",
"source": {
"type": "git",
"url": "https://github.com/markrogoyski/math-php.git",
"reference": "ca71ca97dc136e7bb9e9e1fe05782f343a5692d4"
"reference": "85d7d7fe205a6df2b20f956720e25341f3b1462a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/markrogoyski/math-php/zipball/ca71ca97dc136e7bb9e9e1fe05782f343a5692d4",
"reference": "ca71ca97dc136e7bb9e9e1fe05782f343a5692d4",
"url": "https://api.github.com/repos/markrogoyski/math-php/zipball/85d7d7fe205a6df2b20f956720e25341f3b1462a",
"reference": "85d7d7fe205a6df2b20f956720e25341f3b1462a",
"shasum": ""
},
"require": {
@ -120,9 +120,9 @@
],
"support": {
"issues": "https://github.com/markrogoyski/math-php/issues",
"source": "https://github.com/markrogoyski/math-php/tree/v2.5.0"
"source": "https://github.com/markrogoyski/math-php/tree/v2.6.0"
},
"time": "2021-11-22T05:14:07+00:00"
"time": "2022-04-10T05:15:37+00:00"
}
],
"packages-dev": [],

View file

@ -15,21 +15,21 @@ function outputFrame($frame, $endFrame, $frameDurationMs) {
$shape = $path->commands;
$line->tags = [
new \swf2ass\ass\borderTag(0),
new \swf2ass\ass\borderTag(new \swf2ass\Vector2(0, 0)),
new \swf2ass\ass\shadowTag(0),
\swf2ass\ass\fillColorTag::fromStyleRecord($path->style)
];
if($shape->getRecords()[0] instanceof \swf2ass\MoveRecord){
/*if($shape->getRecords()[0] instanceof \swf2ass\MoveRecord){
$shape = (new \swf2ass\MatrixTransform(null, null, $shape->getRecords()[0]->to->multiply(-1)))->applyToShape($shape);
}
$line->tags[] = new \swf2ass\ass\positionTag($shape->getRecords()[0]->getStart()->divide(-\swf2ass\Constants::TWIP_SIZE), $shape->getRecords()[0]->getStart()->divide(-\swf2ass\Constants::TWIP_SIZE), $endFrame, $endFrame);
}*/
$line->tags[] = new \swf2ass\ass\positionTag(new \swf2ass\Vector2(0, 0), new \swf2ass\Vector2(0, 0), 1, 1);
$line->tags[] = new \swf2ass\ass\drawTag($shape, 1);
echo $line->encode($frameDurationMs) . PHP_EOL;
}
}
$fps = 30000 / 1001;//30;
$colorN = 64;
$fps = 30;//30000 / 1001;//30;
$colorN = 2;
$pframeColorDistance = 16;
if (is_dir($argv[1])) {
@ -43,7 +43,7 @@ sort($frames);
$frameBuffer = null;
$quantizedFrameBuffer = $frameBuffer;
$dynamicPalette = true;
$dynamicPalette = false;
$palette = [];
for ($i = 0; $i < $colorN; ++$i) {
$palette[] = new \swf2ass\Color((int)round($i * (255 / ($colorN - 1))), (int)round($i * (255 / ($colorN - 1))), (int)round($i * (255 / ($colorN - 1))));
@ -51,7 +51,7 @@ for ($i = 0; $i < $colorN; ++$i) {
$frameNumber = 1;
$endFrame = 100;
$endFrame = 6522;
$lastFrameNumber = null;
$currentFrames = [];

View file

@ -3,7 +3,7 @@
namespace swf2ass;
class Circle extends Oval {
class Circle extends Ellipse {
public function __construct(Vector2 $center, float $radius) {
parent::__construct($center, new Vector2($radius, $radius));
}

View file

@ -27,7 +27,29 @@ class Color {
return new Color($element["red"], $element["green"], $element["blue"], $element["alpha"] ?? 255);
}
public function toString($noAlpha = false){
public function __toString() : string{
return $this->toString(false);
}
public function toString(bool $noAlpha = false) : string{
return $noAlpha ? "rgb({$this->r},{$this->g},{$this->b})" : "rgba({$this->r},{$this->g},{$this->b},{$this->alpha})";
}
public function toLinearRGB() : Color{
return new Color(
pow($this->r / 255, 2.2) * 255,
pow($this->g / 255, 2.2) * 255,
pow($this->b / 255, 2.2) * 255,
pow($this->alpha / 255, 2.2) * 255,
);
}
public function tosRGB() : Color{
return new Color(
pow($this->r / 255, 0.4545) * 255,
pow($this->g / 255, 0.4545) * 255,
pow($this->b / 255, 0.4545) * 255,
pow($this->alpha / 255, 0.4545) * 255,
);
}
}

View file

@ -3,7 +3,7 @@
namespace swf2ass;
class Oval implements ComplexShape {
class Ellipse implements ComplexShape {
protected const c = 0.55228474983; // (4/3) * (sqrt(2) - 1)
//protected const c = 0.551915024494; // https://spencermortensen.com/articles/bezier-circle/
@ -28,9 +28,9 @@ class Oval implements ComplexShape {
public function draw(): array {
return [
$this->getQuarter(new Vector2(-$this->radius->x, $this->radius->y)),
$this->getQuarter($this->radius),
$this->getQuarter($this->radius)->reverse(), //Reverse so paths connect
$this->getQuarter(new Vector2($this->radius->x, -$this->radius->y)),
$this->getQuarter(new Vector2(-$this->radius->x, -$this->radius->y)),
$this->getQuarter(new Vector2(-$this->radius->x, -$this->radius->y))->reverse(),
];
}
}

View file

@ -7,7 +7,10 @@ class FillStyleRecord implements StyleRecord {
/** @var Gradient|Color */
public $fill;
public function __construct($fill) {
public ?LineStyleRecord $border = null;
public function __construct($fill, ?LineStyleRecord $border = null) {
$this->fill = $fill;
$this->border = $border;
}
}

View file

@ -3,12 +3,31 @@
namespace swf2ass;
interface Gradient {
const AUTO_SLICES = -1;
const SPREAD_PAD = 0;
const SPREAD_REFLECT = 1;
const SPREAD_REPEAT = 2;
const SPREAD_RESERVED = 3;
//TODO
const INTERPOLATE_NORMAL_RGB = 0;
const INTERPOLATE_LINEAR_RGB = 1;
const INTERPOLATE_RESERVED1 = 2;
const INTERPOLATE_RESERVED2 = 3;
public function getSpreadMode() : int;
public function getInterpolationMode() : int;
/**
* @return GradientItem[]
*/
public function getItems(): array;
public function getInterpolatedDrawPaths(int $overlap = 0, int $slices = self::AUTO_SLICES): DrawPathList;
public function getMatrixTransform(): MatrixTransform;
public function applyColorTransform(ColorTransform $transform): Gradient;

15
src/GradientSlice.php Normal file
View file

@ -0,0 +1,15 @@
<?php
namespace swf2ass;
class GradientSlice {
public Color $color;
public float $startRatio;
public float $endRatio;
public function __construct(Color $color, float $startRatio, float $endRatio){
$this->color = $color;
$this->startRatio = $startRatio;
$this->endRatio = $endRatio;
}
}

View file

@ -3,23 +3,61 @@
namespace swf2ass;
class LinearGradient implements Gradient {
/** @var GradientItem[] */
public array $colors;
public MatrixTransform $transform;
public int $spreadMode;
public int $interpolationMode;
/**
* @param GradientItem[] $colors
* @param MatrixTransform $transform
*/
public function __construct(array $colors, MatrixTransform $transform) {
public function __construct(array $colors, MatrixTransform $transform, int $spreadMode, int $interpolationMode) {
$this->colors = $colors;
$this->transform = $transform;
$this->spreadMode = $spreadMode;
$this->interpolationMode = $interpolationMode;
}
public function getItems(): array {
return $this->colors;
}
public function getSpreadMode() : int{
return $this->spreadMode;
}
public function getInterpolationMode() : int{
return $this->interpolationMode;
}
public function getInterpolatedDrawPaths(int $overlap = 0, int $slices = self::AUTO_SLICES): DrawPathList{
//items is max size 8 to 15 depending on SWF version
$min = -16384;
$max = 16384;
$diff = $max - $min;
//TODO spreadMode
$paths = new DrawPathList();
foreach (Utils::lerpGradient($this, $slices) as $item){
$paths->commands[] = DrawPath::fill(
new FillStyleRecord($item->color),
$this->getMatrixTransform()->applyToShape(new Shape((new Rectangle(
new Vector2($min + ($item->startRatio / 255) * $diff - $overlap / 2, $min),
new Vector2($min + ($item->endRatio / 255) * $diff + $overlap / 2, $max)
))->draw())));
}
return $paths;
}
public function getMatrixTransform(): MatrixTransform {
return $this->transform;
@ -33,7 +71,7 @@ class LinearGradient implements Gradient {
//TODO: interpolationMode, spreadMode
return new LinearGradient($colors, $transform);
return new LinearGradient($colors, $transform, $element["spreadMode"], $element["interpolationMode"]);
}
public function applyColorTransform(ColorTransform $transform): Gradient{

View file

@ -123,11 +123,11 @@ class MorphShapeDefinition implements ObjectDefinition {
//No need to convert types!
if($r1 instanceof LineRecord and $r2 instanceof LineRecord){
$shape->addRecord(new LineRecord(self::lerpVector2($r1->to, $r2->to, $ratio), self::lerpVector2($r1->start, $r2->start, $ratio)));
$shape->addRecord(new LineRecord(Utils::lerpVector2($r1->to, $r2->to, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
}else if($r1 instanceof QuadraticCurveRecord and $r2 instanceof QuadraticCurveRecord){
$shape->addRecord(new QuadraticCurveRecord(self::lerpVector2($r1->control, $r2->control, $ratio), self::lerpVector2($r1->anchor, $r2->anchor, $ratio), self::lerpVector2($r1->start, $r2->start, $ratio)));
$shape->addRecord(new QuadraticCurveRecord(Utils::lerpVector2($r1->control, $r2->control, $ratio), Utils::lerpVector2($r1->anchor, $r2->anchor, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
}else if($r1 instanceof MoveRecord and $r2 instanceof MoveRecord){
$shape->addRecord(new MoveRecord(self::lerpVector2($r1->to, $r2->to, $ratio), self::lerpVector2($r1->start, $r2->start, $ratio)));
$shape->addRecord(new MoveRecord(Utils::lerpVector2($r1->to, $r2->to, $ratio), Utils::lerpVector2($r1->start, $r2->start, $ratio)));
}else{
var_dump($records1);
var_dump($records2);
@ -138,17 +138,17 @@ class MorphShapeDefinition implements ObjectDefinition {
//TODO: morph styles properly
if($c1->style instanceof FillStyleRecord and $c2->style instanceof FillStyleRecord){
if($c1->style->fill instanceof Color){
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(self::lerpColor($c1->style->fill, $c2->style->fill, $ratio)), $shape);
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(Utils::lerpColor($c1->style->fill, $c2->style->fill, $ratio)), $shape);
}else if($c1->style->fill instanceof Gradient){
//TODO: proper gradients
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(self::lerpColor($c1->style->fill->getItems()[0]->color, $c2->style->fill->getItems()[0]->color, $ratio)), $shape);
$drawPathList->commands[] = DrawPath::fill(new FillStyleRecord(Utils::lerpColor($c1->style->fill->getItems()[0]->color, $c2->style->fill->getItems()[0]->color, $ratio)), $shape);
}else{
var_dump($c1->style);
var_dump($c2->style);
throw new \Exception();
}
}else if($c1->style instanceof LineStyleRecord and $c2->style instanceof LineStyleRecord){
$drawPathList->commands[] = DrawPath::stroke(new LineStyleRecord(self::lerpInteger($c1->style->width, $c2->style->width, $ratio), self::lerpColor($c1->style->color, $c2->style->color, $ratio)), $shape);
$drawPathList->commands[] = DrawPath::stroke(new LineStyleRecord(Utils::lerpInteger($c1->style->width, $c2->style->width, $ratio), Utils::lerpColor($c1->style->color, $c2->style->color, $ratio)), $shape);
}else{
var_dump($c1->style);
var_dump($c2->style);
@ -159,22 +159,6 @@ class MorphShapeDefinition implements ObjectDefinition {
return $drawPathList;
}
private static function lerpInteger(int $start, int $end, float $ratio): int {
return $start + ($end - $start) * $ratio;
}
private static function lerpFloat(float $start, float $end, float $ratio): float {
return $start + ($end - $start) * $ratio;
}
private static function lerpVector2(Vector2 $start, Vector2 $end, float $ratio): Vector2 {
return $start->add($end->sub($start)->multiply($ratio));
}
private static function lerpColor(Color $start, Color $end, float $ratio): Color {
return new Color(self::lerpInteger($start->r, $end->r, $ratio), self::lerpInteger($start->g, $end->g, $ratio), self::lerpInteger($start->b, $end->b, $ratio), self::lerpInteger($start->alpha, $end->alpha, $ratio));
}
static function fromArray(array $element): MorphShapeDefinition {
$styles = MorphStyleList::fromArray($element);

View file

@ -8,19 +8,57 @@ class RadialGradient implements Gradient {
public MatrixTransform $transform;
public int $spreadMode;
public int $interpolationMode;
/**
* @param GradientItem[] $colors
* @param MatrixTransform $transform
*/
public function __construct(array $colors, MatrixTransform $transform) {
public function __construct(array $colors, MatrixTransform $transform, int $spreadMode, int $interpolationMode) {
$this->colors = $colors;
$this->transform = $transform;
$this->spreadMode = $spreadMode;
$this->interpolationMode = $interpolationMode;
}
public function getItems(): array {
return $this->colors;
}
public function getSpreadMode() : int{
return $this->spreadMode;
}
public function getInterpolationMode() : int{
return $this->interpolationMode;
}
public function getInterpolatedDrawPaths(int $overlap = 0, int $slices = self::AUTO_SLICES): DrawPathList{
//items is max size 8 to 15 depending on SWF version
$min = -16384;
$max = 16384;
$diff = $max - $min;
//TODO spreadMode
$paths = new DrawPathList();
foreach (Utils::lerpGradient($this, $slices) as $item){
$shape = new Shape();
//Create concentric circles to cut out a shape
$shape = $shape->merge(new Shape((new Circle(new Vector2(0, 0), (($item->endRatio / 255) * $diff) / 2 + $overlap / 4))->draw()));
$shape = $shape->merge(new Shape((new Circle(new Vector2(0, 0), (($item->startRatio / 255) * $diff) / 2 - $overlap / 4))->draw()));
$paths->commands[] = DrawPath::fill(
new FillStyleRecord($item->color),
$this->getMatrixTransform()->applyToShape($shape));
}
return $paths;
}
public function getMatrixTransform(): MatrixTransform {
return $this->transform;
}
@ -33,7 +71,7 @@ class RadialGradient implements Gradient {
//TODO: interpolationMode, spreadMode
return new RadialGradient($colors, $transform);
return new RadialGradient($colors, $transform, $element["spreadMode"], $element["interpolationMode"]);
}
public function applyColorTransform(ColorTransform $transform): Gradient{

View file

@ -69,14 +69,23 @@ class SWFProcessor extends SWFTreeProcessor {
return $node["tagType"];
case "SoundStreamHead":
case "SoundStreamHead2":
if($this->loops > 0){
break;
}
$this->audio = AudioStream::fromSoundStreamHeadTag($node);
return $node["tagType"];
case "DefineSound":
if($this->loops > 0){
break;
}
$this->audio = new AudioStream(0, 0, 0, 0);
$this->audio->setStartFrame($this->getFrame());
//TODO $this->audio = (object)["node" => $node, "start" => $this->getFrame(), "content" => []];
return $node["tagType"];
case "SoundStreamBlock":
if($this->loops > 0){
break;
}
if($this->audio !== null){
if($this->audio->getStartFrame() === null){
$this->audio->setStartFrame($this->getFrame());

View file

@ -59,6 +59,9 @@ class SWFTreeProcessor {
switch ($node["tagType"]) {
case "DefineMorphShape":
case "DefineMorphShape2":
if($this->loops > 0){
break;
}
$shape = MorphShapeDefinition::fromArray($node);
$this->objects->add($shape);
break;
@ -67,11 +70,17 @@ class SWFTreeProcessor {
case "DefineShape3":
case "DefineShape4":
case "DefineShape5":
if($this->loops > 0){
break;
}
$shape = ShapeDefinition::fromArray($node);
$this->objects->add($shape);
break;
case "DefineSprite":
if($this->loops > 0){
break;
}
$objectID = $node["spriteId"];
$framesCount = $node["frameCount"];
@ -81,6 +90,9 @@ class SWFTreeProcessor {
break;
case "DefineBitsLossless":
case "DefineBitsLossless2":
if($this->loops > 0){
break;
}
break; //TODO
$bitmap = BitmapDefinition::fromArray($node);
@ -88,6 +100,9 @@ class SWFTreeProcessor {
break;
case "DefineBitsJPEG2":
case "DefineBitsJPEG3":
if($this->loops > 0){
break;
}
break; //TODO
$bitmap = JPEGBitmapDefinition::fromArray($node);
$this->objects->add($bitmap);

View file

@ -38,27 +38,6 @@ class Shape {
return $this->start()->equals($this->end());
}
/**
* @return Vector2[]
* @throws \Exception
*/
public function toPoints() : array{
$self = $this->flatten();
$points = [];
foreach ($self->getRecords() as $record){
if($record instanceof LineRecord){
$points[] = $record->start;
}else{
var_dump($record);
throw new \Exception("Found record of type " . get_class($record));
}
}
return $points;
}
/**
* @return Record[]
*/
@ -66,16 +45,6 @@ class Shape {
return $this->edges;
}
public function getArea() : float{
$area = 0;
$points = $this->toPoints();
foreach ($points as $i => $p1){
$p2 = $points[($i + 1) % count($points)];
$area += $p1->x * $p2->y - $p1->y * $p2->x;
}
return $area / 2;
}
public function merge(Shape $shape): Shape {
$newShape = new Shape([]);
$newShape->edges = array_merge($this->edges, $shape->edges);

View file

@ -30,7 +30,8 @@ class SpriteDefinition implements MultiFrameObjectDefinition {
}
public function nextFrame(): ViewFrame {
return $this->currentFrame = $this->swf->nextFrame();
//TODO: figure out why this can return null. missing shapes?
return $this->currentFrame = $this->swf->nextFrame() ?? new ViewFrame($this->getObjectId(), new DrawPathList());
}
public function getSafeObject() : SpriteDefinition{

View file

@ -17,4 +17,89 @@ abstract class Utils {
static function binary2dec($bin) {
return gmp_intval(gmp_init($bin, 2));
}
public static function lerpInteger(int $start, int $end, float $ratio): int {
return $start + ($end - $start) * $ratio;
}
public static function lerpFloat(float $start, float $end, float $ratio): float {
return $start + ($end - $start) * $ratio;
}
public static function lerpVector2(Vector2 $start, Vector2 $end, float $ratio): Vector2 {
return $start->add($end->sub($start)->multiply($ratio));
}
public static function lerpColor(Color $start, Color $end, float $ratio): Color {
return new Color(self::lerpInteger($start->r, $end->r, $ratio), self::lerpInteger($start->g, $end->g, $ratio), self::lerpInteger($start->b, $end->b, $ratio), self::lerpInteger($start->alpha, $end->alpha, $ratio));
}
/**
* @param Gradient $gradient
* @param int $slices
* @return \Iterator<GradientSlice>|GradientSlice[]
*/
public static function lerpGradient(Gradient $gradient, int $slices = Gradient::AUTO_SLICES): \Iterator{
$items = $gradient->getItems();
//TODO: spread modes
$first = reset($items);
$last = end($items);
if($first->ratio !== 0){
$first = clone $first;
$first->ratio = 0;
array_unshift($items, $first);
}
if($last->ratio !== 255){
$last = clone $last;
$last->ratio = 255;
array_push($items, $last);
}
$prevItem = null;
foreach ($items as $item){
if($prevItem !== null){
if($gradient->getInterpolationMode() === Gradient::INTERPOLATE_LINEAR_RGB){
$prevColor = $prevItem->color->toLinearRGB();
$currentColor = $item->color->toLinearRGB();
}else{
$prevColor = $prevItem->color;
$currentColor = $item->color;
}
$maxColorDistance = max(abs($prevColor->r - $currentColor->r), abs($prevColor->g - $currentColor->g), abs($prevColor->b - $currentColor->b), abs($prevColor->alpha - $currentColor->alpha));
$prevPosition = $prevItem->ratio;
$currentPosition = $item->ratio;
$distance = abs($prevPosition - $currentPosition);
if($maxColorDistance < Constants::EPSILON){
$partitions = 1;
}else if($slices === Gradient::AUTO_SLICES){
$partitions = min(255 / (count($items) + 1), max(1, ceil($maxColorDistance)));
}else{
$partitions = ($distance / 255) * $slices;
}
$partitions = max(1, ceil($partitions));
$fromPos = $prevPosition;
for($i = 1; $i <= $partitions; ++$i){
$ratio = $i / $partitions;
$color = Utils::lerpColor($prevColor, $currentColor, $ratio);
if($gradient->getInterpolationMode() === Gradient::INTERPOLATE_LINEAR_RGB){
$color = $color->tosRGB();
}
$toPos = Utils::lerpFloat($prevPosition, $currentPosition, $ratio);
yield new GradientSlice($color, $fromPos, $toPos);
$fromPos = $toPos;
}
}
$prevItem = $item;
}
}
}

View file

@ -2,7 +2,12 @@
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;
@ -96,7 +101,6 @@ class ASSLine {
$line->objectId = $object->objectId;
$line->start = $information->getFrameNumber();
$line->end = $information->getFrameNumber();
//TODO: do gradient splitting here
$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;

View file

@ -2,7 +2,13 @@
namespace swf2ass\ass;
use swf2ass\ClipPath;
use swf2ass\Constants;
use swf2ass\DrawPath;
use swf2ass\DrawPathList;
use swf2ass\FillStyleRecord;
use swf2ass\FrameInformation;
use swf2ass\Gradient;
use swf2ass\MatrixTransform;
use swf2ass\Rectangle;
use swf2ass\RenderedFrame;
@ -29,7 +35,7 @@ class ASSRenderer {
$display = $viewPort->toPixel();
$width = $display->getWidth() * self::getSetting("videoScaleMultiplier", 1);
$height = $display->getHeight() * self::getSetting("videoScaleMultiplier", 1);
$ar = $width / $height;
$ar = sprintf("%.6F", $width / $height);
/*if(($frameRate * 2) <= 60){
$frameRate *= 2;
@ -42,6 +48,7 @@ class ASSRenderer {
[Script Info]
; Script generated by swf2ass ASSRenderer
; https://git.gammaspectra.live/WeebDataHoarder/swf2ass
Title: swf2ass
ScriptType: v4.00+
; TODO: maybe set WrapStyle: 2
WrapStyle: 0
@ -55,10 +62,12 @@ Timer: {$timerPrecision}
Last Style Storage: f
Video File: ?dummy:{$frameRate}:10000:{$width}:{$height}:160:160:160:c
Video AR Value: {$ar}
Active Line: 0
Video Zoom Percent: 2.000000
[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
Style: f,Arial,20,&H00000000,&H00000000,&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
@ -66,6 +75,35 @@ ASSHEADER;
}
/**
* @param RenderedObject $object
* @return RenderedObject
*/
public static function bakeGradients(RenderedObject $object) : RenderedObject{
$baked = null;
$drawPathList = new DrawPathList();
foreach ($object->drawPathList->commands as $command){
if($command->style instanceof FillStyleRecord and $command->style->fill instanceof Gradient){
$baked = $baked ?? new RenderedObject($object->depth, $object->objectId, $drawPathList, $object->colorTransform, $object->matrixTransform, $object->clip);
$gradientClip = new ClipPath($command->commands);
//Convert gradients to many tags
foreach ($command->style->fill->getInterpolatedDrawPaths(0, self::getSetting("gradientSlices", Gradient::AUTO_SLICES))->commands as $gradientPath){
//echo (new drawTag($gradientPath->commands, 1))->encode(new ASSEventTime(1, 1, 1)) . "\n";
$newPath = DrawPath::fill($gradientPath->style, $gradientClip->intersect(new ClipPath($gradientPath->commands))->getShape());
//echo (new drawTag($newPath->commands, 1))->encode(new ASSEventTime(1, 1, 1)) . "\n\n";
if(count($newPath->commands->getRecords()) === 0){
continue;
}
$drawPathList->commands[] = $newPath;
}
}else{
$drawPathList->commands[] = $command;
}
}
return $baked ?? $object;
}
public function renderFrame(FrameInformation $information, RenderedFrame $frame): \Generator {
if ($this->header !== null) {
foreach (explode("\n", $this->header) as $line) {
@ -83,7 +121,7 @@ ASSHEADER;
$animated = 0;
foreach ($objects as $object) {
$object = clone $object;
$object = clone self::bakeGradients($object);
$object->matrixTransform = $scale->multiply($object->matrixTransform); //TODO order?
$depth = $object->getDepth();

View file

@ -3,6 +3,7 @@
namespace swf2ass\ass;
use swf2ass\Constants;
use swf2ass\FillStyleRecord;
use swf2ass\LineStyleRecord;
use swf2ass\StyleRecord;
use swf2ass\Vector2;
@ -30,6 +31,8 @@ class borderTag implements ASSStyleTag {
public static function fromStyleRecord(StyleRecord $record): ?borderTag {
if ($record instanceof LineStyleRecord) {
return new borderTag(new Vector2($record->width / Constants::TWIP_SIZE, $record->width / Constants::TWIP_SIZE));
}else if ($record instanceof FillStyleRecord and $record->border !== null) {
return new borderTag(new Vector2($record->border->width / Constants::TWIP_SIZE, $record->border->width / Constants::TWIP_SIZE));
}
return new borderTag(new Vector2(0, 0));
}

View file

@ -7,6 +7,8 @@ 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;

View file

@ -17,6 +17,8 @@ class fillColorTag extends colorTag {
$color = $record->fill;
} else if ($record->fill instanceof Gradient) { //TODO: split this elsewhere
$color = $record->fill->getItems()[0]->color;
var_dump($record->fill);
throw new \Exception("Invalid Gradient Fill record");
} else {
throw new \Exception("Invalid Fill record");
}

View file

@ -3,6 +3,7 @@
namespace swf2ass\ass;
use swf2ass\Color;
use swf2ass\FillStyleRecord;
use swf2ass\LineStyleRecord;
use swf2ass\StyleRecord;
use swf2ass\Utils;
@ -11,6 +12,8 @@ class lineColorTag extends colorTag {
public static function fromStyleRecord(StyleRecord $record): ?lineColorTag {
if ($record instanceof LineStyleRecord) {
return new lineColorTag($record->color, $record->color);
}else if ($record instanceof FillStyleRecord and $record->border !== null){
return new lineColorTag($record->border->color, $record->border->color);
}
return new lineColorTag(null, null);
}

View file

@ -11,6 +11,7 @@ $settings = [
"timerSpeed" => 100, //NOTE: libass does not implement "Timer:", which is used by this setting. Leave at 100 by default
"timePrecision" => 2, //NOTE: libass does not implement anything different from 2. Leave at 2 by default,
"smoothTransitions" => false, //All transitions will happen smoothly from start till finish instead of happening instantly on frame thresholds. NOTE: this can break objects that did not get selected to be animated, or matrix transforms
"gradientSlices" => /*24,*/\swf2ass\Gradient::AUTO_SLICES,
];
$swfContent = file_get_contents($argv[1]);