#!/usr/local/bin/perl
# readbl.pl - Generate readable colors and relative luminance charts
# Copyright 2015 David Meyer <papa@sdf.org> +JMJ
# (License and documentation at bottom of file.)

# Source: Ben Caldwell et al. ed. Web Content Accessibility 
#         Guidelines (WCAG) 2.0. World Wide Web Consortium (W3C).
#         December 11, 2008.
#         <http://www.w3.org/TR/2008/REC-WCAG20-20081211/>
#         accessed January 7, 2015.

use strict;
use warnings;

our @cval = (0, 51, 102, 153, 204, 255); # "Web-safe" palette
#our @cval = (0, 255); 

our %bytesc = (
    0   => 0,
    51  => 3,
    102 => 6,
    153 => 9,
    204 => 12,
    255 => 15
    );

our(@color, %cr, %cg, %cb, %rlumin);

sub bright {
    my($r, $g, $b) = @_;
    return ($r * 299 + $g * 587 + $b * 114) / 1000;
}

# chex2rgb - Argument is hexadecimal string in format "#RRGGBB" or "#RGB", returns array of integers (r, g, b)
sub chex2rgb {
    my($chex) = @_;
    my($r, $g, $b);
    if (length($chex) == 4) {
	$r = hex substr($chex, 1, 1);
	$g = hex substr($chex, 2, 1);
	$b = hex substr($chex, 3, 1);
	return ($r*16+$r, $g*16+$g, $b*16+$b);
    }
    elsif (length($chex) == 7) {
	$r = hex substr($chex, 1, 2);
	$g = hex substr($chex, 3, 2);
	$b = hex substr($chex, 5, 2);
	return ($r, $g, $b);
    }
    else {
	die "Invalid format in chex2rgb(): $chex";
    }
}

sub colorsc {
    my($r, $g, $b) = @_;
    return sprintf("#%x%x%x", $bytesc{$r}, $bytesc{$g}, $bytesc{$b});
}

sub lumc {
    my($c) = @_;
    my $cn = $c/255;
    if ($cn <= .03928) {return $cn/12.92;}
    else {return (($cn+.055)/1.055)**2.4;}
}

sub max {
    my($a, $b) = @_;
    if ($a < $b) {return $b;}
    else {return $a;}
}

sub rlumin {
    my($r, $g, $b) = @_;
    return .2126 * lumc($r) + .7152 * lumc($g) + .0722 * lumc($b);
}

our($r, $g, $b);

for $r (@cval) {
    for $g (@cval) {
	for $b (@cval) {
	    my $colorsc = colorsc($r, $g, $b);
	    push @color, $colorsc;
	    $cr{$colorsc} = $r;
	    $cg{$colorsc} = $g;
	    $cb{$colorsc} = $b;
	    $rlumin{$colorsc} = rlumin($r, $g, $b);
	}
    }
}

our @lcolor = sort {$rlumin{$a} <=> $rlumin{$b}} @color; 

print "<body style='background-color:#555;color:#fff;'>\n";

print "<h2>Readable Color Combinations (by hue/brightness difference)</h2>\n<table>\n<tr><th>BG<br/>color</th><td colspan=3>Text color<br/>Hue difference<br/>Brightness diff.</td></tr>";


# $bgr, $bgg, $bgb: Background red, green, blue
# $fgr, $fgg, $fgb: Foreground red, green, blue

my %combos = (
    cr3a => 0,
    cr2a => 0,
    hdbd => 0
    );

my %output = (
    cr3a => "<table>\n",
    cr2a => "<table>\n",
    hdbd => "<table>\n"
    );

my %bgprev = (
    cr3a => "",
    cr2a => "",
    hdbd => ""
    );

my $bgcd_prev = "";

for my $bg (@lcolor) {
    my $bgr = $cr{$bg}; my $bgg = $cg{$bg}; my $bgb = $cb{$bg}; 
    my $bgcd = colorsc($bgr, $bgg, $bgb);
    for my $fg (@lcolor) {
	my $fgr = $cr{$fg}; my $fgg = $cg{$fg}; my $fgb = $cb{$fg}; 
	my $fgcd = colorsc($fgr, $fgg, $fgb);

	# Contrast ratio ...
	my $rlbg = $rlumin{$bgcd};
	my $rlfg = $rlumin{$fgcd};
	my $cr = $rlfg > $rlbg ?
	    ($rlfg+.05)/($rlbg+.05) :
	    ($rlbg+.05)/($rlfg+.05);

	# Hue difference ...
	my $hd = max($fgr-$bgr, $bgr-$fgr) +
	    max($fgg-$bgg, $bgg-$fgg) +
	    max($fgb-$bgb, $bgb-$fgb);

	# Brightness difference ...
     	my $bd = abs(bright($fgr, $fgg, $fgb) -
		  bright($bgr, $bgg, $bgb));

	# Readability ...
	my $readable;
	if ($hd >= 500 && $bd >= 125) {$readable = 1;}
	else {$readable = 0;}
	
	if ($readable) {
	    if ($bgcd ne $bgcd_prev) {
		print "</tr>\n<tr><th>$bgcd</th>\n";
		$bgcd_prev = $bgcd;
	    }
	    printf("<td style='background-color:%s;color:%s'>%s<br/>%d %d</td>\n", $bgcd, $fgcd, $fgcd, $hd, $bd);
	    ++ $combos;
	}
    }
}

print <<EOF;
</tr>
</table>
<p>$combos combinations</p>
EOF



print "<h2>Relative Luminance Scale</h2>\n<ol>\n";

for my $c (@lcolor) {
    my $fg = $rlumin{$c} < .35 ? '#fff' : '#000';
    print "<li style='background-color:$c;margin:2px;padding:10px;'><span style='color:$fg;'>$c $rlumin{$c}</span></li>\n";
}

print "</ol>\n</body>\n";

exit 0

__END__

# Documentation #####################################################
=head1 NAME

template.pl - Perl script template (command line)

=head1 SYNOPSIS/USAGE

=head1 DESCRIPTION

Mark up code elements with C<>, file names with F<> (or C<> for
readability), command names with B<>. Also I<> for italics, U<> for
underline. Entities: E<lt> ('<'), E<gt> ('>').

=head1 OPTIONS

=item B<-o> I<value>, B<--option>=I<value>

=head1 RETURN VALUE

=head1 ERRORS

=head1 DIAGNOSTICS

=head1 EXAMPLES

=head1 ENVIRONMENT

=over 6

=item VARIABLE

Description of usage of environment variable C<VARIABLE>.

=back

=head1 FILES

=head1 BUGS, LIMITATIONS, AND CAVEATS

=head1 NOTES

=head1 AUTHOR

David Meyer <papa@sdf.org>

=head1 HISTORY

=head1 COPYRIGHT AND LICENSE

Copyright 201x David Meyer

=head1 SEE ALSO



# Emacs control #####################################################
#Local variables:
#mode: perl
#End: