swf2ass/src/Utils.php

105 lines
3.6 KiB
PHP

<?php
namespace swf2ass;
abstract class Utils {
static function padHex($h, $l = 2) {
return str_pad($h, $l, "0", STR_PAD_LEFT);
}
static function bin2binary($bin) {
return gmp_strval(gmp_init(bin2hex($bin), 16), 2);
}
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;
}
}
}