swf2ass/src/ShapeConverter.php

175 lines
6 KiB
PHP

<?php
namespace swf2ass;
class ShapeConverter {
private StyleList $styles;
private ?ActivePath $fill_style0 = null;
private ?ActivePath $fill_style1 = null;
private ?ActivePath $line_style = null;
private Vector2 $position;
private PendingPathMap $fills;
private PendingPathMap $strokes;
public DrawPathList $commands;
public function __construct(\DOMElement $element, StyleList $currentStyles) {
$this->styles = $currentStyles;
$this->position = new Vector2(0, 0);
$this->fills = new PendingPathMap();
$this->strokes = new PendingPathMap();
$this->commands = new DrawPathList([]);
foreach ($element->getElementsByTagName("edges")->item(0)->childNodes as $node) {
if ($node->nodeName === "ShapeSetup") {
//Utils::dump_element($node);
if ($node->hasAttribute("x")) {
$move = MoveRecord::fromXML($node, $this->position);
$this->position = $move->coord;
$this->flush_paths();
}
if ($node->hasChildNodes()) {
$this->flush_layer();
$this->styles = StyleList::fromXML($node->getElementsByTagName("styles")->item(0)->getElementsByTagName("StyleList")->item(0));
}
if ($node->hasAttribute("fillStyle1")) {
if ($this->fill_style1 !== null) {
$this->fills->merge_path($this->fill_style1, true);
}
$id = (int)$node->getAttribute("fillStyle1");
$this->fill_style1 = $id > 0 ? new ActivePath($id, $this->position) : null;
}
if ($node->hasAttribute("fillStyle0")) {
if ($this->fill_style0 !== null) {
if (!$this->fill_style0->segment->is_empty()) {
$this->fill_style0->flip();
$this->fills->merge_path($this->fill_style0, true);
}
}
$id = (int)$node->getAttribute("fillStyle0");
$this->fill_style0 = $id > 0 ? new ActivePath($id, $this->position) : null;
}
if ($node->hasAttribute("lineStyle")) {
if ($this->line_style !== null) {
$this->strokes->merge_path($this->line_style, false);
}
$id = (int)$node->getAttribute("lineStyle");
$this->line_style = $id > 0 ? new ActivePath($id, $this->position) : null;
}
} else if ($node->nodeName === "LineTo") {
$line = LineRecord::fromXML($node, $this->position);
$this->visit_point($line->coord, false);
$this->position = $line->coord;
} else if ($node->nodeName === "CurveTo") {
$curve = QuadraticCurveRecord::fromXML($node, $this->position);
$this->visit_point($curve->control, true);
$this->visit_point($curve->anchor, false);
$this->position = $curve->anchor;
}
}
$this->flush_layer();
}
public function visit_point(Vector2 $coordinate, bool $isBezierControlPoint) {
$point = new VisitedPoint($coordinate, $isBezierControlPoint);
if ($this->fill_style0 !== null) {
$this->fill_style0->add_point($point);
}
if ($this->fill_style1 !== null) {
$this->fill_style1->add_point($point);
}
if ($this->line_style !== null) {
$this->line_style->add_point($point);
}
}
public function flush_paths() {
if ($this->fill_style1 !== null) {
$this->fills->merge_path($this->fill_style1, true);
$this->fill_style1 = new ActivePath($this->fill_style1->style, $this->position);
}
if ($this->fill_style0 !== null) {
if (!$this->fill_style0->segment->is_empty()) {
$this->fill_style0->flip();
$this->fills->merge_path($this->fill_style0, true);
}
$this->fill_style0 = new ActivePath($this->fill_style0->style, $this->position);
}
if ($this->line_style !== null) {
$this->strokes->merge_path($this->line_style, false);
$this->line_style = new ActivePath($this->line_style->style, $this->position);
}
}
public function flush_layer() {
$this->flush_paths();
$this->fill_style0 = null;
$this->fill_style1 = null;
$this->line_style = null;
foreach ($this->fills->map as $styleId => $path) {
assert($styleId > 0 && $styleId < count($this->styles->fillStyles)); // ?????
$style = $this->styles->getFillStyle($styleId - 1);
if ($style === null) {
var_dump($this->styles);
var_dump($styleId);
}
$this->commands->commands[] = DrawPath::fill($style, $path->getShape());
}
$this->fills->map = [];
foreach ($this->strokes->map as $styleId => $path) {
assert($styleId > 0 && $styleId < count($this->styles->lineStyles)); // ?????
$style = $this->styles->getLineStyle($styleId - 1);
foreach ($path->segments as $segment) {
$segmentStyle = $style;
//Close non-closed segments by double drawing backwards
if (!$segment->is_closed()) {
$other = clone $segment;
$other->flip();
$segment->merge($other);
//Reduce width of line style to account for double border
$segmentStyle = clone $style;
$segmentStyle->width /= 2;
}
$this->commands->commands[] = DrawPath::stroke($segmentStyle, $segment->getShape(), $segment->is_closed());
}
}
$this->strokes->map = [];
}
}