1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
# /=====================================================================\ #
# | LaTeXML::Common::Color ,... | #
# | Representation of colors in various color models | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Common::Color;
use strict;
use warnings;
use LaTeXML::Global;
use LaTeXML::Common::Object;
use LaTeXML::Common::Error;
use base qw(LaTeXML::Common::Object);
use base qw(Exporter);
our @EXPORT = ( # Global STATE; This gets bound by LaTeXML.pm
qw( &Color &Black &White),
);
#======================================================================
# Exported constructors
sub Color {
my ($model, @components) = @_;
return LaTeXML::Common::Color->new(ToString($model), map { ToString($_) } @components); }
use constant Black => bless ['rgb', 0, 0, 0], 'LaTeXML::Common::Color::rgb';
use constant White => bless ['rgb', 1, 1, 1], 'LaTeXML::Common::Color::rgb';
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Color objects; objects representing color in "arbitrary" color models
# We'd like to provide a set of "core" color models (rgb,cmy,cmyk,hsb)
# and allow derived color models (with scaled ranges, or whatever; see xcolor).
# There is some awkwardness in that we'd like to support the core models
# directly with built-in code, but support derived models that possibly
# are defined in terms of macros defined as part of a style file.
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# NOTE: This class is in Common since it could conceivably be useful
# in Postprocessing --- But the API, includes, etc haven't been tuned for that!
# They only use $STATE to get derived color information, Error, min & max.
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Color Objects
our %core_color_models = map { ($_ => 1) } qw(rgb cmy cmyk hsb gray); # [CONSTANT]
# slightly contrived to avoid 'use'ing all the models in here
# (which causes compiler redefined issues, and preloads them all)
sub new {
my ($class, @components) = @_;
if (ref $class) { # from $self->new(...)
return bless [$$class[0], @components], ref $class; }
else { # Else, $model is the 1st element of @components;
my $model = shift(@components);
my $type = ($core_color_models{$model} ? $model : 'Derived');
my $class = 'LaTeXML::Common::Color::' . $type;
if (($type eq 'Derived')
&& !$STATE->lookupValue('derived_color_model_' . $model)) {
Error('unexpected', $model, undef, "Unrecognized color model '$model'"); }
my $module = $class . '.pm';
$module =~ s|::|/|g;
require $module unless exists $INC{$module}; # Load if not already loaded
return bless [$model, @components], $class; } }
sub model {
my ($self) = @_;
return $$self[0]; }
sub components {
my ($self) = @_;
my ($m, @comp) = @$self;
return @comp; }
# Convert a color to another model
sub convert {
my ($self, $tomodel) = @_;
if ($self->model eq $tomodel) { # Already the correct model
return $self; }
elsif ($core_color_models{$tomodel}) { # target must be core model
return $self->toCore->$tomodel; }
elsif (my $data = $STATE->lookupValue('derived_color_model_' . $tomodel)) { # Ah, target is a derived color
my $coremodel = $$data[0];
my $convertfrom = $$data[2];
return &{$convertfrom}($self->$coremodel); }
else {
Error('unexpected', $tomodel, undef, "Unrecognized color model '$tomodel'");
return $self; } }
sub toString {
my ($self) = @_;
my ($model, @comp) = @$self;
return $model . "(" . join(',', @comp) . ")"; }
sub stringify {
my ($self) = @_;
my ($model, @comp) = @$self;
return "Color::" . $model . "(" . join(',', @comp) . ")"; }
sub toHex {
my ($self) = @_;
return $self->rgb->toHex; }
sub toAttribute {
my ($self) = @_;
return $self->rgb->toHex; }
# Convert the color to a core model; Assume it already is!
# Color::Derived MUST override this...
sub toCore { my ($self) = @_; return $self; }
#======================================================================
# By default, just complement components (works for rgb, cmy, gray)
sub complement {
my ($self) = @_;
return $self->new(map { 1 - $_ } $self->components); }
# Mix $self*$fraction + $color*(1-$fraction)
sub mix {
my ($self, $color, $fraction) = @_;
$color = $color->convert($self->model) unless $self->model eq $color->model;
my @a = $self->components;
my @b = $color->components;
return $self->new(map { $fraction * $a[$_] + (1 - $fraction) * $b[$_] } 0 .. $#a); }
sub add {
my ($self, $color) = @_;
$color = $color->convert($self->model) unless $self->model eq $color->model;
my @a = $self->components;
my @b = $color->components;
return $self->new(map { $a[$_] + $b[$_] } 0 .. $#a); }
# The next 2 methods multiply the components of a color by some value(s)
# This assumes that such a thing makes sense in the given model, for some purpose.
# It may be that the components should be truncated to 1 (or some other max?)
# Multiply all components by a constant
sub scale {
my ($self, $m) = @_;
return $self->new(map { $m * $_ } $self->components); }
# Multiply by a vector (must have same number of components)
# This may or may not make sense for any given color model or purpose.
sub multiply {
my ($self, @m) = @_;
my @c = $self->components;
if (scalar(@m) != scalar(@c)) {
Error('misdefined', 'multiply', "Multiplying color components by wrong number of parts",
"The color is " . ToString($self) . " while the multipliers are " . join(',', @m));
return $self; }
else {
return $self->new(map { $c[$_] * $m[$_] } 0 .. $#c); } }
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1;
__END__
=pod
=head1 NAME
C<LaTeXML::Common::Color> - abstract class representating colors using various color models;
extends L<LaTeXML::Common::Object>.
=head2 Exported functions
=over 4
=item C<< $color = Color($model,@components); >>
Creates a Color object using the given color model, and with the given components.
The core color models are C<rgb>, C<hsv>, C<cmy>, C<cmyk> and C<gray>.
The components of colors using core color models are between 0 and 1 (inclusive)
=item C<< Black >>, C<< White >>
Constant color objects representing black and white, respectively.
=back
=head2 Methods
=over 4
=item C<< $model = $color->model; >>
Return the name of the color model.
=item C<< @components = $color->components; >>
Return the components of the color.
=item C<< $other = $color->convert($tomodel); >>
Converts the color to another color model.
=item C<< $string = $color->toString; >>
Returns a printed representation of the color.
=item C<< $hex = $color->toHex; >>
Returns a string representing the color as RGB in hexadecimal (6 digits).
=item C<< $other = $color->toCore(); >>
Converts the color to one of the core colors.
=item C<< $complement = $color->complement(); >>
Returns the complement color (works for colors in C<rgb>, C<cmy> and C<gray> color models).
=item C<< $new = $color->mix($other,$fraction); >>
Returns a new color which results from mixing a C<$fraction> of C<$color>
with C<(1-$fraction)> of color C<$other>.
=item C<< $new = $color->add($other); >>
Returns a new color made by adding the components of the two colors.
=item C<< $new = $color->scale($m); >>
Returns a new color made by mulitiplying the components by C<$n>.
=item C<< $new = $color->multiply(@m); >>
Returns a new color made by mulitiplying the components by the corresponding component from C<@n>.
=back
=head1 SEE ALSO
Supported color models:
L<LaTeXML::Common::Color::rgb>,
L<LaTeXML::Common::Color::hsb>,
L<LaTeXML::Common::Color::cmy>,
L<LaTeXML::Common::Color::cmyk>,
L<LaTeXML::Common::Color::gray> and
L<LaTeXML::Common::Color::Derived>.
=head1 AUTHOR
Bruce Miller <bruce.miller@nist.gov>
=head1 COPYRIGHT
Public domain software, produced as part of work done by the
United States Government & not subject to copyright in the US.
=cut
|