mourningdove/cgi-bin/S2/Color.pm

184 lines
4 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
#
# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
#
package S2::Color;
use strict;
# This is a helper package, useful for creating color lightening/darkening
# functions in core layers.
#
# rgb to hsv
# r, g, b = [0, 255]
# h, s, v = [0, 1), [0, 1], [0, 1]
sub rgb_to_hsv {
my ( $r, $g, $b ) = map { $_ / 255 } @_;
my ( $h, $s, $v );
my ( $max, $min ) = ( $r, $r );
foreach ( $g, $b ) {
$max = $_ if $_ > $max;
$min = $_ if $_ < $min;
}
return ( 0, 0, 0 ) if $max == 0;
$v = $max;
my $delta = $max - $min;
$s = $delta / $max;
return ( 0, $s, $v ) unless $delta;
if ( $r == $max ) {
$h = ( $g - $b ) / $delta;
}
elsif ( $g == $max ) {
$h = 2 + ( $b - $r ) / $delta;
}
else {
$h = 4 + ( $r - $g ) / $delta;
}
$h = ( $h * 60 ) % 360 / 360;
return ( $h, $s, $v );
}
# hsv to rgb
# h, s, v = [0, 1), [0, 1], [0, 1]
# r, g, b = [0, 255], [0, 255], [0, 255]
sub hsv_to_rgb {
my ( $H, $S, $V ) = @_;
if ( $S == 0 ) {
$V *= 255;
return ( $V, $V, $V );
}
$H *= 6;
my $I = POSIX::floor($H);
my $F = $H - $I;
my $P = $V * ( 1 - $S );
my $Q = $V * ( 1 - $S * $F );
my $T = $V * ( 1 - $S * ( 1 - $F ) );
foreach ( $V, $T, $P, $Q ) {
$_ = int( $_ * 255 + 0.5 );
}
return ( $V, $T, $P ) if $I == 0;
return ( $Q, $V, $P ) if $I == 1;
return ( $P, $V, $T ) if $I == 2;
return ( $P, $Q, $V ) if $I == 3;
return ( $T, $P, $V ) if $I == 4;
return ( $V, $P, $Q );
}
# rgb to hsv
# r, g, b = [0, 255], [0, 255], [0, 255]
# returns: (h, s, l) = [0, 1), [0, 1], [0, 1]
sub rgb_to_hsl {
# convert rgb to 0-1
my ( $R, $G, $B ) = map { $_ / 255 } @_;
# get min/max of {r, g, b}
my ( $max, $min ) = ( $R, $R );
foreach ( $G, $B ) {
$max = $_ if $_ > $max;
$min = $_ if $_ < $min;
}
# is gray?
my $delta = $max - $min;
if ( $delta == 0 ) {
return ( 0, 0, $max );
}
my ( $H, $S );
my $L = ( $max + $min ) / 2;
if ( $L < 0.5 ) {
$S = $delta / ( $max + $min );
}
else {
$S = $delta / ( 2.0 - $max - $min );
}
if ( $R == $max ) {
$H = ( $G - $B ) / $delta;
}
elsif ( $G == $max ) {
$H = 2 + ( $B - $R ) / $delta;
}
elsif ( $B == $max ) {
$H = 4 + ( $R - $G ) / $delta;
}
$H *= 60;
$H += 360.0 if $H < 0.0;
$H -= 360.0 if $H >= 360.0;
$H /= 360.0;
return ( $H, $S, $L );
}
# h, s, l = [0,1), [0,1], [0,1]
# returns: rgb: [0,255], [0,255], [0,255]
sub hsl_to_rgb {
my ( $H, $S, $L ) = @_;
# gray.
if ( $S < 0.0000000000001 ) {
my $gv = int( 255 * $L + 0.5 );
return ( $gv, $gv, $gv );
}
my ( $t1, $t2 );
if ( $L < 0.5 ) {
$t2 = $L * ( 1.0 + $S );
}
else {
$t2 = $L + $S - $L * $S;
}
$t1 = 2.0 * $L - $t2;
my $fromhue = sub {
my $hue = shift;
if ( $hue < 0 ) { $hue += 1.0; }
if ( $hue > 1 ) { $hue -= 1.0; }
if ( 6.0 * $hue < 1 ) {
return $t1 + ( $t2 - $t1 ) * $hue * 6.0;
}
elsif ( 2.0 * $hue < 1 ) {
return $t2;
}
elsif ( 3.0 * $hue < 2.0 ) {
return ( $t1 + ( $t2 - $t1 ) * ( ( 2.0 / 3.0 ) - $hue ) * 6.0 );
}
else {
return $t1;
}
};
return map { int( 255 * $fromhue->($_) + 0.5 ) } ( $H + 1.0 / 3.0, $H, $H - 1.0 / 3.0 );
}
1;