mirror of https://github.com/odrling/Aegisub
251 lines
7.3 KiB
Perl
251 lines
7.3 KiB
Perl
#/usr/bin/perl
|
|
|
|
#########
|
|
#
|
|
# Written by Karl Blomster (TheFluff) 2008.
|
|
# (OK, mostly just a translation of utils-auto4.lua.)
|
|
#
|
|
# This script is hereby given into the public domain.
|
|
# If that is not possible according to local laws, I, the author, hereby grant
|
|
# anyone the right to use this script for any purpose.
|
|
#
|
|
#########
|
|
|
|
package Auto4Utils;
|
|
require Exporter;
|
|
|
|
use warnings;
|
|
use strict;
|
|
use feature ":5.10";
|
|
use utf8; # just to be safe
|
|
use POSIX (); # gah, we only need floor(), no need to import all of IEEE 1003.1
|
|
|
|
|
|
# Export everything by default
|
|
our @ISA = qw(Exporter);
|
|
our @EXPORT = qw(extract_color alpha_from_style color_from_style HSV_to_RGB HSL_to_RGB interpolate_color interpolate_alpha
|
|
ass_color ass_alpha ass_style_color string_trim clamp interpolate);
|
|
|
|
|
|
# Given 3 integers R,G,B, returns ASS formatted &HBBGGRR& string
|
|
sub ass_color {
|
|
my ($r, $g, $b) = @_;
|
|
return(sprintf("&H%02X%02X%02X&", $b, $g, $r));
|
|
}
|
|
# Perlier version of that:
|
|
# sub ass_color { return sprintf "&H%02X%02X%02X&", reverse }
|
|
# I don't think reverse reverses @_ by default, rats :(
|
|
|
|
# Convert decimal alpha value to &H00& form
|
|
sub ass_alpha {
|
|
return(sprintf("&H%02X&", shift(@_)));
|
|
}
|
|
|
|
# Given 4 integers R,G,B,A, returns a v4+ formatted style color string
|
|
# (note no terminating &)
|
|
sub ass_style_color {
|
|
my ($r, $g, $b, $a) = @_;
|
|
return(sprintf("&H%02X%02X%02X%02X", $a, $b, $g, $r));
|
|
}
|
|
|
|
# Tries its best to convert a string to 4 integers R,G,B,A.
|
|
# Returns them in that order if it succeeds, or undef if it can't do it.
|
|
# Useless in scalar context.
|
|
sub extract_color {
|
|
my $string = shift(@_);
|
|
|
|
# This here thingie is a switch statement. Magic!
|
|
given ( $string ) {
|
|
# try v4+ style (ABGR)
|
|
when ( /\&H([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})/i ) {
|
|
return(hex($4), hex($3), hex($2), hex($1));
|
|
}
|
|
# color override? (BGR)
|
|
when ( /\&H([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})\&H/i ) {
|
|
return(0, hex($3), hex($2), hex($1));
|
|
}
|
|
# alpha override? (A)
|
|
# (bug: bogus results with \c&H<hex>& with the first four zeros omitted)
|
|
when ( /\&H([[:xdigit:]]{2})\&/i ) {
|
|
return(hex($1), 0, 0, 0);
|
|
}
|
|
# try HTML format for laffs (RGB)
|
|
when ( /\#([[:xdigit:]]{2})([[:xdigit:]]{2})?([[:xdigit:]]{2})?/i ) {
|
|
return(0, (hex($2) or 0), (hex($2) or 0), (hex($3) or 0));
|
|
}
|
|
default {
|
|
return(undef, undef, undef, undef);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Given a a style color string, returns the alpha part formatted as override
|
|
sub alpha_from_style {
|
|
my $color_string = shift(@_);
|
|
my ($r, $g, $b, $a) = extract_color($color_string);
|
|
return(ass_alpha($a or 0));
|
|
}
|
|
|
|
# Given a style color string, returns the color part formatted as override
|
|
sub color_from_style {
|
|
my $color_string = shift(@_);
|
|
my ($r, $g, $b, $a) = extract_color($color_string);
|
|
return(ass_color(($r or 0), ($g or 0), ($b or 0)));
|
|
}
|
|
|
|
|
|
# Converts 3 integers H, S, V (hue, saturation, value) to R, G, B
|
|
sub HSV_to_RGB {
|
|
my ($H, $S, $V) = @_;
|
|
my ($r, $g, $b);
|
|
|
|
# saturation is zero, make grey
|
|
if ($S == 0) {
|
|
$r = $V * 255;
|
|
$r = clamp($r, 0, 255);
|
|
($g, $b) = ($r, $r);
|
|
}
|
|
# else calculate color
|
|
else {
|
|
# calculate subvalues
|
|
$H = $H % 360; # put $h in range [0,360]
|
|
my $Hi = POSIX::floor($H/60);
|
|
my $f = $H/60 - $Hi;
|
|
my $p = $V * (1 - $S);
|
|
my $q = $V * (1 - $f * $S);
|
|
my $t = $V * (1 - (1 - $f) * $S);
|
|
|
|
# do math based on hue index
|
|
if ($Hi == 0) { $r = $V*255; $g = $t*255; $b = $p*255; }
|
|
elsif ($Hi == 1) { $r = $q*255; $g = $V*255; $b = $p*255; }
|
|
elsif ($Hi == 2) { $r = $p*255; $g = $V*255; $b = $t*255; }
|
|
elsif ($Hi == 3) { $r = $p*255; $g = $q*255; $b = $V*255; }
|
|
elsif ($Hi == 4) { $r = $t*255; $g = $p*255; $b = $V*255; }
|
|
elsif ($Hi == 5) { $r = $V*255; $g = $p*255; $b = $q*255; }
|
|
# TODO: replace this with Aegisub::Script::debug_out() or whatever it is
|
|
else { warn("HSV_to_RGB: Hi got an unexpected value: $Hi"); }
|
|
}
|
|
|
|
$r = POSIX::floor($r);
|
|
$g = POSIX::floor($g);
|
|
$b = POSIX::floor($b);
|
|
return($r, $g, $b);
|
|
}
|
|
|
|
|
|
# Converts 3 integers H, S, L (hue, saturation, luminance) to R, G, B
|
|
# NOTE: THE OUTPUT AND S,V INPUT IS IN THE RANGE [0,1]!
|
|
# Routine is best performed to "The HSL Song" by Diablo-D3 and the #darkhold idlers.
|
|
# The lyrics are as follows:
|
|
# I see a little silluetto of a man
|
|
# It's in color, its in color, can you convert to HSL?
|
|
# Cyan, yellow and magenta, very very outdated now
|
|
# Alvy Smith, Alvy Smith, Alvy Smith, Alvy Smith, Fiigaarrooo
|
|
# I'm just a poor boy, stuck with RGB
|
|
# (He's just a poor boy, from a poor colorspace, spare him his eyes from this monstrosity)
|
|
#
|
|
# Easy come, easy go, will you let me HSL?
|
|
# (No! We will not let you HSL!)
|
|
# Let him HSL!
|
|
# (No! We will not let you HSL!)
|
|
# Let him HSL!
|
|
# (No! We will not let you HSL!)
|
|
# Let me HSL!
|
|
# (Will not HSL!)
|
|
# Let me HSL!
|
|
# (Will not HSL!)
|
|
# Let me HSL! Let me HSL!
|
|
# (Never never never never never!)
|
|
# Let me HHHHHSSSSSLLLLL!
|
|
# (No no no no no no no!)
|
|
#
|
|
# [70's rock/bad humour segment ends here. We now return to your regularily scheduled Perl hacking...]
|
|
sub HSL_to_RGB {
|
|
my ($H, $S, $L) = @_;
|
|
my ($r, $g, $b, $Q);
|
|
|
|
# make sure input is in range
|
|
$H = $H % 360;
|
|
$S = clamp($S, 0, 1);
|
|
$L = clamp($L, 0, 1);
|
|
|
|
# simple case if saturation is 0, all grey
|
|
if ($S == 0) {
|
|
($r, $g, $b) = ($L, $L, $L);
|
|
}
|
|
# more common case, saturated color
|
|
else {
|
|
if ($L < 0.5) { $Q = $L * (1 + $S); }
|
|
else { $Q = $L + $S - ($L * $S); }
|
|
|
|
my $P = 2 * $L - $Q;
|
|
my $Hk = $H / 360;
|
|
|
|
my ($Tr, $Tg, $Tb);
|
|
$Tg = $Hk;
|
|
if ($Hk < 1/3) { $Tr = $Hk + 1/3; $Tb = $Hk + 2/3; }
|
|
elsif ($Hk > 2/3) { $Tr = $Hk - 2/3; $Tb = $Hk - 1/3; }
|
|
else { $Tr = $Hk + 1/3; $Tb = $Hk - 1/3; }
|
|
|
|
# anonymous subroutine required for closure reasons
|
|
my $get_component = sub {
|
|
my $T = shift(@_);
|
|
if ($T < 1/6) { return($P + (($Q - $P) * 6 * $T)) }
|
|
elsif (1/6 <= $T and $T < 1/2) { return($Q) }
|
|
elsif (1/2 <= $T and $T < 2/3) { return($P + (($Q - $P) * (2/3 - $T) * 6)) }
|
|
else { return($P) }
|
|
};
|
|
|
|
$r = $get_component->($Tr);
|
|
$g = $get_component->($Tg);
|
|
$b = $get_component->($Tb);
|
|
}
|
|
|
|
return($r, $g, $b);
|
|
}
|
|
|
|
|
|
# Removes whitespace at the start and end of a string
|
|
# (will anyone ever use this in a perl program?)
|
|
sub string_trim {
|
|
my $string = shift(@_);
|
|
$string =~ s!^\s*(.+?)\s*$!$1!;
|
|
return($string);
|
|
}
|
|
|
|
|
|
# Clamp a numeric value to a range
|
|
sub clamp {
|
|
my ($val, $min, $max) = @_;
|
|
if ($val < $min) { return($min) }
|
|
elsif ($val > $max) { return($max) }
|
|
else { return($val) }
|
|
}
|
|
|
|
# interpolate between two numbers
|
|
sub interpolate {
|
|
my ($pct, $min, $max) = @_;
|
|
if ($pct <= 0) { return($min) }
|
|
elsif ($pct >= 1) { return($max) }
|
|
else { return($pct * ($max - $min) + $min) }
|
|
}
|
|
|
|
# interpolate between two color values (given as &HBBGGRR strings)
|
|
# returns string formatted with \c&H override format
|
|
sub interpolate_color {
|
|
my ($pct, $start, $end) = @_;
|
|
my ($r1, $g1, $b1) = extract_color($start);
|
|
my ($r2, $g2, $b2) = extract_color($end);
|
|
my ($r, $g, $b) =
|
|
(interpolate($pct, $r1, $r2), interpolate($pct, $g1, $g2), interpolate($pct, $b1, $b2));
|
|
return(ass_color($r, $g, $b));
|
|
}
|
|
|
|
# interpolate between two alpha values (given as either override or part of style color strings)
|
|
# returns string formatted with \c&H override format
|
|
sub interpolate_alpha {
|
|
my ($pct, $start, $end) = @_;
|
|
my ($r1, $g1, $b1, $a1) = extract_color($start);
|
|
my ($r2, $g2, $b2, $a2) = extract_color($end);
|
|
return(ass_alpha(interpolate($pct, $a1, $a2)));
|
|
} |