| blib/lib/Color/Model/RGB.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 177 | 206 | 85.9 |
| branch | 48 | 80 | 60.0 |
| condition | 16 | 43 | 37.2 |
| subroutine | 41 | 42 | 97.6 |
| pod | 14 | 26 | 53.8 |
| total | 296 | 397 | 74.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | # ============================================================================= | ||||||
| 2 | package Color::Model::RGB; | ||||||
| 3 | # ----------------------------------------------------------------------------- | ||||||
| 4 | $Color::Model::RGB::VERSION = '1.02'; | ||||||
| 5 | # ----------------------------------------------------------------------------- | ||||||
| 6 | 4 | 4 | 201391 | use warnings; | |||
| 4 | 10 | ||||||
| 4 | 159 | ||||||
| 7 | 4 | 4 | 25 | use strict; | |||
| 4 | 9 | ||||||
| 4 | 203 | ||||||
| 8 | |||||||
| 9 | =head1 NAME | ||||||
| 10 | |||||||
| 11 | Color::Model::RGB - Color model of RGB | ||||||
| 12 | |||||||
| 13 | =head1 SYNOPSIS | ||||||
| 14 | |||||||
| 15 | $navy = rgb(0, 0, 0.5); | ||||||
| 16 | $limegreen = rgb('#32CD32'); | ||||||
| 17 | |||||||
| 18 | # use Color::Model::RGB qw(:primary); | ||||||
| 19 | $white = R + G + B; # addition (Constant O and W are also prepared) | ||||||
| 20 | $yellow = $white - $b; # subtraction | ||||||
| 21 | $midgray = $while / 2; # divide | ||||||
| 22 | $hilight = $midgray * 1.5; # multiply | ||||||
| 23 | print qq(see); # stringify | ||||||
| 24 | |||||||
| 25 | @rgbval = $color->array(); # decimal | ||||||
| 26 | @rgb256 = $color->array256(); # integers | ||||||
| 27 | |||||||
| 28 | # applying ... | ||||||
| 29 | @gradation = map { rgb('#010101') << $_ } (0..7); | ||||||
| 30 | @tricolor = ( $c, rgb(($c->array)[1,2,0]), rgb(($c->array)[2,0,1]) ); | ||||||
| 31 | |||||||
| 32 | # use Color::Model::RGB qw(:blender); | ||||||
| 33 | $violet = blend_half(R, B); | ||||||
| 34 | $pink = blend_plus(R, $hilight); | ||||||
| 35 | |||||||
| 36 | =head1 DESCRIPTION | ||||||
| 37 | |||||||
| 38 | Color::Model::RGB is a color model of RGB implemented by 3D mathematical | ||||||
| 39 | vector. | ||||||
| 40 | This provides abstruct calculation for colors with overloding and methods | ||||||
| 41 | to convert values to simply hexadecimal string designed for HTML, CSS and etc. | ||||||
| 42 | |||||||
| 43 | Color::Model::RGB is based on B |
||||||
| 44 | |||||||
| 45 | =cut | ||||||
| 46 | |||||||
| 47 | # ============================================================================= | ||||||
| 48 | 4 | 4 | 31 | use Carp; | |||
| 4 | 12 | ||||||
| 4 | 409 | ||||||
| 49 | 4 | 4 | 3949 | use POSIX qw(ceil); | |||
| 4 | 48419 | ||||||
| 4 | 49 | ||||||
| 50 | 4 | 4 | 6173 | use Scalar::Util (); | |||
| 4 | 12 | ||||||
| 4 | 132 | ||||||
| 51 | |||||||
| 52 | 4 | 4 | 22 | use base qw(Math::VectorReal Exporter); | |||
| 4 | 9 | ||||||
| 4 | 6113 | ||||||
| 53 | our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS); | ||||||
| 54 | @EXPORT = qw( rgb rgb256 rgbhex ); | ||||||
| 55 | @EXPORT_OK = qw( O R G B W | ||||||
| 56 | set_format get_format | ||||||
| 57 | blend_alpha blend_half blend_plus blend_minus | ||||||
| 58 | ); | ||||||
| 59 | %EXPORT_TAGS = ( | ||||||
| 60 | primary => [ qw(O R G B W) ], RGB => [ qw(O R G B W) ], | ||||||
| 61 | format => [ qw(set_format get_format) ], | ||||||
| 62 | blender => [ qw(blend_alpha blend_half blend_plus blend_minus) ], | ||||||
| 63 | all => [@EXPORT, @EXPORT_OK], | ||||||
| 64 | ); | ||||||
| 65 | |||||||
| 66 | |||||||
| 67 | our $FORMAT = '%02x%02x%02x'; | ||||||
| 68 | our $FORMAT_HEXED = 1; # flag of magic to represent hexadecimal numbers. | ||||||
| 69 | |||||||
| 70 | |||||||
| 71 | |||||||
| 72 | # ============================================================================= | ||||||
| 73 | |||||||
| 74 | =head1 CONSTANTS | ||||||
| 75 | |||||||
| 76 | Some primary colors below are defined as constant. To use these, import them | ||||||
| 77 | with tag ':primary' or ':RGB' | ||||||
| 78 | |||||||
| 79 | # R G B | ||||||
| 80 | O = [ 0 0 0 ] | ||||||
| 81 | R = [ 1 0 0 ] | ||||||
| 82 | G = [ 0 1 0 ] | ||||||
| 83 | B = [ 0 0 1 ] | ||||||
| 84 | W = [ 1 1 1 ] | ||||||
| 85 | |||||||
| 86 | =cut | ||||||
| 87 | |||||||
| 88 | # ----------------------------------------------------------------------------- | ||||||
| 89 | 2 | 2 | 0 | 359 | sub O() { bless __PACKAGE__->SUPER::O(), __PACKAGE__ } | ||
| 90 | 4 | 4 | 0 | 47 | sub R() { bless __PACKAGE__->SUPER::X(), __PACKAGE__ } | ||
| 91 | 4 | 4 | 0 | 49 | sub G() { bless __PACKAGE__->SUPER::Y(), __PACKAGE__ } | ||
| 92 | 4 | 4 | 0 | 95 | sub B() { bless __PACKAGE__->SUPER::Z(), __PACKAGE__ } | ||
| 93 | 3 | 3 | 0 | 164 | sub W() { bless [ [[1,1,1]], 1,3 ], __PACKAGE__; } | ||
| 94 | |||||||
| 95 | |||||||
| 96 | |||||||
| 97 | |||||||
| 98 | # ============================================================================= | ||||||
| 99 | |||||||
| 100 | =head1 CONSTRUCTORS | ||||||
| 101 | |||||||
| 102 | $col1 = Color::Model::RGB->new(0.1, 0.2, 0.3); | ||||||
| 103 | $col2 = rgb(0.5,0.6,0.7); | ||||||
| 104 | $col3 = rgb256(128,128,255); | ||||||
| 105 | $col3 = rgbhex('0080ff'); # rgbhex('#0080ff') is also ok. | ||||||
| 106 | # and rgb($hexstr) is also ok. | ||||||
| 107 | $col4 = $col1->clone(); | ||||||
| 108 | |||||||
| 109 | There are functions to make an object. | ||||||
| 110 | |||||||
| 111 | Method I |
||||||
| 112 | returns new Color::Model::RGB object as I |
||||||
| 113 | |||||||
| 114 | Method I |
||||||
| 115 | out of a range, from -1.0 to 1.0, will be set -1.0 or 1.0. | ||||||
| 116 | If one argument is given to I |
||||||
| 117 | string and call I |
||||||
| 118 | |||||||
| 119 | Method I |
||||||
| 120 | value will be set -255 or 255. | ||||||
| 121 | |||||||
| 122 | Method I |
||||||
| 123 | starts with '#' is also allowed. | ||||||
| 124 | |||||||
| 125 | I |
||||||
| 126 | |||||||
| 127 | =cut | ||||||
| 128 | |||||||
| 129 | # ----------------------------------------------------------------------------- | ||||||
| 130 | sub new | ||||||
| 131 | { | ||||||
| 132 | 88 | 88 | 1 | 1627 | my $class = shift; | ||
| 133 | 88 | 100 | 257 | my $ref = ref($class) || __PACKAGE__; | |||
| 134 | 88 | 412 | return bless __PACKAGE__->SUPER::new(@_), $ref; | ||||
| 135 | } | ||||||
| 136 | |||||||
| 137 | sub rgb | ||||||
| 138 | { | ||||||
| 139 | 5 | 50 | 5 | 0 | 23 | if ( !ref($_[0]) ){ | |
| 140 | 5 | 50 | 21 | if ( @_ == 1 ){ | |||
| 141 | # Assume hex string is given | ||||||
| 142 | 0 | 0 | return rgbhex($_[0]); | ||||
| 143 | } | ||||||
| 144 | } else { | ||||||
| 145 | 0 | 0 | shift; | ||||
| 146 | } | ||||||
| 147 | 15 | 50 | 82 | my @rgb = map { | |||
| 50 | |||||||
| 148 | 5 | 12 | ($_ < -1)? -1: | ||||
| 149 | ($_ > 1)? 1: | ||||||
| 150 | $_ | ||||||
| 151 | } @_; | ||||||
| 152 | 5 | 35 | return bless __PACKAGE__->SUPER::new(@rgb), __PACKAGE__; | ||||
| 153 | } | ||||||
| 154 | |||||||
| 155 | sub rgb256 | ||||||
| 156 | { | ||||||
| 157 | 12 | 50 | 12 | 0 | 34 | shift if ( @_ == 4 ); | |
| 158 | 36 | 100 | 143 | my @rgb = map { | |||
| 50 | |||||||
| 159 | 12 | 19 | ($_ < -255)? -1: | ||||
| 160 | ($_ > 255)? 1: | ||||||
| 161 | ($_/255) | ||||||
| 162 | } @_; | ||||||
| 163 | 12 | 51 | return bless __PACKAGE__->SUPER::new(@rgb), __PACKAGE__; | ||||
| 164 | } | ||||||
| 165 | |||||||
| 166 | sub rgbhex | ||||||
| 167 | { | ||||||
| 168 | 1 | 1 | 0 | 4 | my $h = lc(shift); | ||
| 169 | 1 | 50 | 33 | 26 | if ( defined($h) && $h =~ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/ ){ | ||
| 170 | 3 | 16 | return bless __PACKAGE__->SUPER::new( | ||||
| 171 | 1 | 3 | map { hex($_)/255 } ($1,$2,$3) | ||||
| 172 | ), __PACKAGE__; | ||||||
| 173 | } else { | ||||||
| 174 | 0 | 0 | Carp::croak("rgbHex() needs a hex values argument. ($h was given)"); | ||||
| 175 | } | ||||||
| 176 | } | ||||||
| 177 | |||||||
| 178 | |||||||
| 179 | # ============================================================================= | ||||||
| 180 | |||||||
| 181 | =head1 METHODS | ||||||
| 182 | |||||||
| 183 | =over | ||||||
| 184 | |||||||
| 185 | =item r(), g(), b() | ||||||
| 186 | |||||||
| 187 | Returns decimal value of an element. | ||||||
| 188 | |||||||
| 189 | =item r256(), g256(), b256() | ||||||
| 190 | |||||||
| 191 | Returns integer value of an element, which is multiplyed by 255 and rounded by | ||||||
| 192 | I |
||||||
| 193 | |||||||
| 194 | =item array() | ||||||
| 195 | =item array256() | ||||||
| 196 | |||||||
| 197 | These methods return an array contains values of elements. I |
||||||
| 198 | returns values multiplyed by 255 and rounded by I |
||||||
| 199 | |||||||
| 200 | =item hexstr([ $head_letter ]) | ||||||
| 201 | |||||||
| 202 | Returns 6 digits hexadecimal string. If some string is given as argument, | ||||||
| 203 | value starting with it returns. | ||||||
| 204 | |||||||
| 205 | =item truncate(), limit() | ||||||
| 206 | |||||||
| 207 | These methods return new clone object, values of elements of which are set in | ||||||
| 208 | regulated range. I |
||||||
| 209 | grater than 1 set to 1. And I |
||||||
| 210 | |||||||
| 211 | =item stringify( [ $format [, $flag2hex] ] ) | ||||||
| 212 | |||||||
| 213 | This method can take 2 arguments. The first is format string for I |
||||||
| 214 | and the second is a boolean flag to convert to hexadecimal or not. If this | ||||||
| 215 | flag is true, values multiplyed by 255 will be used at outputing. | ||||||
| 216 | Default values of the format and the flag are keeped by package variable; | ||||||
| 217 | |||||||
| 218 | $Color::Model::RGB::FORMAT = "%02x%02x%02x"; | ||||||
| 219 | $Color::Model::RGB::FORMAT_HEXED = 1; | ||||||
| 220 | |||||||
| 221 | Arguments are omitted at I |
||||||
| 222 | used. | ||||||
| 223 | |||||||
| 224 | Function I |
||||||
| 225 | change these defalut values simply. | ||||||
| 226 | |||||||
| 227 | =back | ||||||
| 228 | |||||||
| 229 | =cut | ||||||
| 230 | |||||||
| 231 | # ----------------------------------------------------------------------------- | ||||||
| 232 | sub _treat_elem | ||||||
| 233 | { | ||||||
| 234 | 36 | 36 | 140 | my $self = shift; | |||
| 235 | 36 | 59 | my $colno= shift; | ||||
| 236 | 36 | 100 | 104 | if ( !@_ ){ | |||
| 50 | |||||||
| 237 | 33 | 420 | return $self->[0][0][$colno]; | ||||
| 238 | } elsif ( @_ == 1 ){ | ||||||
| 239 | 3 | 12 | $self->[0][0][$colno] = $_[0]; | ||||
| 240 | } else { | ||||||
| 241 | 0 | 0 | Carp::carp("Too many arguments. Ignored"); | ||||
| 242 | } | ||||||
| 243 | } | ||||||
| 244 | |||||||
| 245 | 12 | 12 | 1 | 81 | sub r { my $self = shift; return _treat_elem($self,0,@_) } | ||
| 12 | 36 | ||||||
| 246 | 12 | 12 | 1 | 39 | sub g { my $self = shift; return _treat_elem($self,1,@_) } | ||
| 12 | 29 | ||||||
| 247 | 12 | 12 | 1 | 50 | sub b { my $self = shift; return _treat_elem($self,2,@_) } | ||
| 12 | 28 | ||||||
| 248 | |||||||
| 249 | 5 | 5 | 1 | 23 | sub r256 { ceil($_[0]->r * 255) } | ||
| 250 | 5 | 5 | 1 | 32 | sub g256 { ceil($_[0]->g * 255) } | ||
| 251 | 5 | 5 | 1 | 34 | sub b256 { ceil($_[0]->b * 255) } | ||
| 252 | |||||||
| 253 | |||||||
| 254 | sub array256 | ||||||
| 255 | { | ||||||
| 256 | 57 | 57 | 1 | 81 | my $v = shift; | ||
| 257 | 57 | 74 | return map {ceil($_ * 255)} @{$v->[0][0]}; | ||||
| 171 | 938 | ||||||
| 57 | 134 | ||||||
| 258 | } | ||||||
| 259 | |||||||
| 260 | sub hexstr | ||||||
| 261 | { | ||||||
| 262 | 27 | 27 | 1 | 380 | my( $v, $head ) = @_; | ||
| 263 | 27 | 50 | 181 | $head ||= ''; | |||
| 264 | 27 | 105 | return $v->stringify("$head%02x%02x%02x",1); | ||||
| 265 | } | ||||||
| 266 | |||||||
| 267 | sub truncate | ||||||
| 268 | { | ||||||
| 269 | 62 | 62 | 1 | 230 | my $v = shift; | ||
| 270 | 62 | 241 | my $c = $v->clone(); | ||||
| 271 | 62 | 933 | for ( 0 .. 2 ) { | ||||
| 272 | 186 | 100 | 520 | $c->[0][0][$_] = 0 if $c->[0][0][$_] < 0; | |||
| 273 | 186 | 100 | 593 | $c->[0][0][$_] = 1 if $c->[0][0][$_] > 1; | |||
| 274 | } | ||||||
| 275 | 62 | 98 | $#{$c} = 2; | ||||
| 62 | 184 | ||||||
| 276 | 62 | 211 | return $c; | ||||
| 277 | } | ||||||
| 278 | |||||||
| 279 | sub limit | ||||||
| 280 | { | ||||||
| 281 | 0 | 0 | 1 | 0 | my $v = shift; | ||
| 282 | 0 | 0 | for ( 0 .. 2 ) { | ||||
| 283 | 0 | 0 | 0 | $v->[0][0][$_] = -1 if $v->[0][0][$_] < -1; | |||
| 284 | 0 | 0 | 0 | $v->[0][0][$_] = 1 if $v->[0][0][$_] > 1; | |||
| 285 | } | ||||||
| 286 | 0 | 0 | $#{$v} = 2; | ||||
| 0 | 0 | ||||||
| 287 | 0 | 0 | return $v; | ||||
| 288 | } | ||||||
| 289 | |||||||
| 290 | sub stringify | ||||||
| 291 | { | ||||||
| 292 | 45 | 45 | 1 | 279 | my( $v, $fmt, $hexed ) = @_; | ||
| 293 | 45 | 100 | 143 | $fmt = $FORMAT unless defined $fmt; # if not given use current default | |||
| 294 | 45 | 100 | 102 | $hexed = $FORMAT_HEXED unless defined $hexed; | |||
| 295 | 45 | 100 | 102 | if ( $hexed ){ | |||
| 296 | 43 | 122 | return sprintf($fmt, $v->truncate->array256()); | ||||
| 297 | } else { | ||||||
| 298 | 2 | 9 | return sprintf($fmt, $v->array()); | ||||
| 299 | } | ||||||
| 300 | } | ||||||
| 301 | |||||||
| 302 | |||||||
| 303 | |||||||
| 304 | |||||||
| 305 | # ============================================================================= | ||||||
| 306 | |||||||
| 307 | =head1 OPERATOR OVERLOAD | ||||||
| 308 | |||||||
| 309 | Color::Model::RGB inherits operators overloading from Math::VextorReal. These | ||||||
| 310 | functions are so useful for mathematical calculation of colors. | ||||||
| 311 | |||||||
| 312 | Note: for avoiding error of conflcting with File Test Operation, put a constant | ||||||
| 313 | object, | ||||||
| 314 | R, B, W or O, in blanckets"()" or separate with space when using expression | ||||||
| 315 | with muinus and them. | ||||||
| 316 | |||||||
| 317 | $c = -(W) # OK | ||||||
| 318 | $c = W - R # OK | ||||||
| 319 | $c = -W # error or raises bug. ( Perl thinks as "-W $_" ) | ||||||
| 320 | $c = W-R # error too. | ||||||
| 321 | |||||||
| 322 | =over | ||||||
| 323 | |||||||
| 324 | =item Negation (unary minus) | ||||||
| 325 | |||||||
| 326 | $c = -$x # -object -> rgb(-r,-b,-c) | ||||||
| 327 | |||||||
| 328 | A Color::Model::RGB object some values of which are minus is allowed for | ||||||
| 329 | calculation. When stringifying such object, minus value will be represented as | ||||||
| 330 | 0. | ||||||
| 331 | |||||||
| 332 | =item Addition (+) | ||||||
| 333 | |||||||
| 334 | $c = R + G; # object1 + object2 -> rgb(r1+r2, g1+g2, b1+b2) | ||||||
| 335 | $c = B + 10; # object + scalar -> rgb(r +x, g +x, b +x) | ||||||
| 336 | |||||||
| 337 | =item Subtraction (-) | ||||||
| 338 | |||||||
| 339 | $c = W - B; # object1 - objext2 -> rgb(r1-r2, g1-g2, b1-b2) | ||||||
| 340 | $c = W - 10; # object - scalar -> rgb(r1-x, g1-x, b1-x) | ||||||
| 341 | |||||||
| 342 | =item Object scalar multiplication (*) | ||||||
| 343 | |||||||
| 344 | $c = W * 0.5 # object * scalar -> rgb(r1*x, g1*x, b1*x) | ||||||
| 345 | # use Math::MatrixReal | ||||||
| 346 | $c = $col * $m # Color::Model::RGB * Math::MatrixReal | ||||||
| 347 | |||||||
| 348 | Color::Model::RGB multiplication by a object is allowed by | ||||||
| 349 | B |
||||||
| 350 | rotation of a color. | ||||||
| 351 | |||||||
| 352 | # hue rotation sample | ||||||
| 353 | $r = 2 * (atan2(1,1)*4) / 10; # for 2pi/10 radian | ||||||
| 354 | ($sin,$cos) = (sin($r), cos($r)); | ||||||
| 355 | $p = (1/3) * (1-$cos); | ||||||
| 356 | $q = sqrt(1/3) * $sin; # (1/3,1/3,1/3) is norm of W | ||||||
| 357 | |||||||
| 358 | $matrix = Math::MatrixReal->new_from_rows([ | ||||||
| 359 | [ $p+$cos, $p-$q, $p+$q, ], | ||||||
| 360 | [ $p+$q, $p+$cos, $p-$q, ], | ||||||
| 361 | [ $p-$q, $p+$q, $p+$cos,], | ||||||
| 362 | ]); | ||||||
| 363 | |||||||
| 364 | $rgb = R; | ||||||
| 365 | foreach ( 1..10 ){ | ||||||
| 366 | print qq(#$rgb \n); |
||||||
| 367 | $rgb *= $matrix; | ||||||
| 368 | } | ||||||
| 369 | |||||||
| 370 | =item Object scalar division (/) | ||||||
| 371 | |||||||
| 372 | $c = W / 3 # object / scalar -> rgb(r1/x, g1/x, b1/x) | ||||||
| 373 | # object1 / object2 is not allowed (croaking) | ||||||
| 374 | |||||||
| 375 | =item Cross and dot products (x and .) | ||||||
| 376 | |||||||
| 377 | Calculation corss and dot product are seldom used at color manipulation. | ||||||
| 378 | These may be used for hue rotation, too. | ||||||
| 379 | |||||||
| 380 | # hue rotation sample 2 | ||||||
| 381 | $r = 2 * (atan2(1,1)*4) / 10; # for 2pi/10 radian | ||||||
| 382 | $n = W->norm; | ||||||
| 383 | $rgb = R; | ||||||
| 384 | foreach ( 1..10 ){ | ||||||
| 385 | print qq(#$rgb \n); |
||||||
| 386 | $p = $n * ($n . $rgb); | ||||||
| 387 | $rgb = $p + ($rgb - $p)*cos($r) - ($rgb x $n)*sin($r); | ||||||
| 388 | } | ||||||
| 389 | |||||||
| 390 | =item Bitwise operations | ||||||
| 391 | |||||||
| 392 | There are bitwise operations in Color::Model::RGB such as '<<', '>>','&', | ||||||
| 393 | '|', '^' and '~'. | ||||||
| 394 | |||||||
| 395 | $col1 = rgbhex('010101'); | ||||||
| 396 | $col2 = $col1 << 7; # Bit shift left, becomes 808080 | ||||||
| 397 | $col3 = $col2 >> 1; # Bit shift right, becomes 404040 | ||||||
| 398 | |||||||
| 399 | $col4 = $col2 | $col3; # Object-object bit OR, becomes c0c0c0 | ||||||
| 400 | $col5 = $col2 | 0x66; # Object-scalar bit OR, becomes e6e6e6 | ||||||
| 401 | |||||||
| 402 | $col6 = $col4 & $col5 # Object-object bit AND, becomes c0c0c0 | ||||||
| 403 | $col7 = $col4 & 0x80 # Object-scalar bit AND, becomes 808080 | ||||||
| 404 | |||||||
| 405 | $col8 = $col6 ^ $col7 # Object-object bit XOR, becomes 404040 | ||||||
| 406 | $col9 = $col6 ^ 0xff; # Object-scalar bit XOR, becomes 3f3f3f | ||||||
| 407 | |||||||
| 408 | $col10 = ~$col8; # Bit Negate, becomes bfbfbf | ||||||
| 409 | |||||||
| 410 | In bitwise operation, each element values of Color::Model::RGB are internaly | ||||||
| 411 | conveted to integers from 0 to 255 and than caluculated individually, and | ||||||
| 412 | converted to decimal again. | ||||||
| 413 | |||||||
| 414 | Package parameter, $Color::Model::RGB::BIT_SHIFT_RIGID, changes bit shift | ||||||
| 415 | operation's result. If this is true value, caluculated value will be ANDed | ||||||
| 416 | with 0xff. If it is false, valuse over 0xff will be set to 0xff(255). Default | ||||||
| 417 | is false(0). | ||||||
| 418 | |||||||
| 419 | $Color::Model::RGB::BIT_SHIFT_RIGID = 1; | ||||||
| 420 | $col = rgbhex('010101')<<8; # becomes 000000 | ||||||
| 421 | $Color::Model::RGB::BIT_SHIFT_RIGID = 0; | ||||||
| 422 | $col = rgbhex('010101')<<8; # becomes ffffff | ||||||
| 423 | |||||||
| 424 | =back | ||||||
| 425 | |||||||
| 426 | =cut | ||||||
| 427 | |||||||
| 428 | # ----------------------------------------------------------------------------- | ||||||
| 429 | $Color::Model::RGB::BIT_SHIFT_RIGID = 0; | ||||||
| 430 | #$Math::VectorReal::TRACE = 1; | ||||||
| 431 | |||||||
| 432 | use overload | ||||||
| 433 | 4 | 68 | '*' => \&_multiply, | ||||
| 434 | '<<' => \&_bit_shiftl, | ||||||
| 435 | '>>' => \&_bit_shiftr, | ||||||
| 436 | '&' => \&_bit_and, | ||||||
| 437 | '|' => \&_bit_or, | ||||||
| 438 | '^' => \&_bit_xor, | ||||||
| 439 | '~' => \&_bit_not, | ||||||
| 440 | 4 | 4 | 57047 | 'fallback' => undef; | |||
| 4 | 516 | ||||||
| 441 | |||||||
| 442 | sub _trace | ||||||
| 443 | { | ||||||
| 444 | 21 | 21 | 68 | Math::VectorReal::_trace(@_); | |||
| 445 | } | ||||||
| 446 | |||||||
| 447 | sub _multiply { | ||||||
| 448 | # copied and improved from Math::VectorReal | ||||||
| 449 | 10 | 10 | 1270 | my($object,$argument,$flip) = @_; | |||
| 450 | 10 | 27 | _trace("'*'",$object,$argument,$flip); | ||||
| 451 | 10 | 50 | 73 | if ( ref($argument) ){ | |||
| 50 | |||||||
| 452 | 0 | 0 | 0 | if ( $argument->isa('Math::MatrixReal') ) { | |||
| 453 | # Assume multiply by Math::MatrixReal object EG: $v * $M --> $new_v | ||||||
| 454 | # Order is communicative, but $flip should NOT be true | ||||||
| 455 | 0 | 0 | 0 | if ( ! $flip ) { | |||
| 456 | 0 | 0 | my $v = ( $object->vector2matrix_row($argument) | ||||
| 457 | * $argument )->matrix_row2vector; | ||||||
| 458 | 0 | 0 | return bless $v, __PACKAGE__; | ||||
| 459 | } else { # just in case flip is true.. | ||||||
| 460 | 0 | 0 | my $v = ( $argument * | ||||
| 461 | $object->vector2matrix_row($argument) )->matrix_row2vector; | ||||||
| 462 | 0 | 0 | return bless $v, __PACKAGE__; | ||||
| 463 | } | ||||||
| 464 | } else { | ||||||
| 465 | 0 | 0 | Carp::croak("multiplication(*) is allowed by Math::MatrixReal object or scalar"); | ||||
| 466 | } | ||||||
| 467 | } | ||||||
| 468 | elsif ( defined $argument ) { | ||||||
| 469 | # defined $argument must be a scalar, so Scalar Multiply | ||||||
| 470 | # Communitive - order does not matter, $flip can be ignored | ||||||
| 471 | 10 | 36 | my $v = $object->clone; | ||||
| 472 | 10 | 144 | for ( 0 .. 2 ) { $v->[0][0][$_] *= $argument; } | ||||
| 30 | 79 | ||||||
| 473 | 10 | 100 | 46 | $v->[6] *= abs($argument) if defined $v->[6]; # multiply vector length | |||
| 474 | 10 | 60 | return $v; | ||||
| 475 | } | ||||||
| 476 | 0 | 0 | Carp::croak("undefined argument given for vector multiply"); | ||||
| 477 | } | ||||||
| 478 | |||||||
| 479 | sub _bit_shiftl | ||||||
| 480 | { | ||||||
| 481 | 3 | 3 | 8 | my($object,$argument,$flip) = @_; | |||
| 482 | 3 | 8 | _trace("'<<'",$object,$argument,$flip); | ||||
| 483 | # $argument must be scalar and plus | ||||||
| 484 | 3 | 50 | 33 | 45 | if ( (defined $argument) && !ref($argument) && $argument>=0 ){ | ||
| 33 | |||||||
| 485 | 3 | 9 | my @rgb = $object->truncate()->array256(); | ||||
| 486 | 9 | 13 | my $v = rgb256( map { | ||||
| 487 | 3 | 12 | $_ <<= $argument; | ||||
| 488 | 9 | 100 | 20 | $_ &= 0xff if $Color::Model::RGB::BIT_SHIFT_RIGID; | |||
| 489 | 9 | 19 | $_; | ||||
| 490 | } @rgb ); | ||||||
| 491 | 3 | 39 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 3 | 9 | ||||||
| 492 | 3 | 13 | return $v; | ||||
| 493 | } | ||||||
| 494 | 0 | 0 | Carp::croak("non-scalar given or minus for vector scalar bit shift left"); | ||||
| 495 | } | ||||||
| 496 | |||||||
| 497 | sub _bit_shiftr | ||||||
| 498 | { | ||||||
| 499 | 1 | 1 | 3 | my($object,$argument,$flip) = @_; | |||
| 500 | 1 | 4 | _trace("'>>'",$object,$argument,$flip); | ||||
| 501 | # $argument must be scalar and plus | ||||||
| 502 | 1 | 50 | 33 | 23 | if ( (defined $argument) && ! ref($argument) && $argument>=0 ){ | ||
| 33 | |||||||
| 503 | 1 | 6 | my @rgb = $object->truncate()->array256(); | ||||
| 504 | 3 | 9 | my $v = rgb256( map { | ||||
| 505 | 1 | 5 | $_ >>= $argument; | ||||
| 506 | } @rgb ); | ||||||
| 507 | 1 | 13 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 3 | ||||||
| 508 | 1 | 5 | return $v; | ||||
| 509 | } | ||||||
| 510 | 0 | 0 | Carp::croak("non-scalar given or minus for vector scalar bit shift right"); | ||||
| 511 | } | ||||||
| 512 | |||||||
| 513 | sub _bit_and | ||||||
| 514 | { | ||||||
| 515 | 2 | 2 | 40 | my($object,$argument,$flip) = @_; | |||
| 516 | 2 | 6 | _trace("'&'",$object,$argument,$flip); | ||||
| 517 | 2 | 100 | 14 | if ( ref($argument) ) { | |||
| 50 | |||||||
| 518 | # bitwise and of two Color::Model::RGB | ||||||
| 519 | 1 | 5 | my @vrgb = $object->truncate()->array256(); | ||||
| 520 | 1 | 5 | my @argb = $argument->truncate()->array256(); | ||||
| 521 | 1 | 11 | my $v = rgb256( | ||||
| 522 | $vrgb[0] & $argb[0], | ||||||
| 523 | $vrgb[1] & $argb[1], | ||||||
| 524 | $vrgb[2] & $argb[2] | ||||||
| 525 | ); | ||||||
| 526 | 1 | 13 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 4 | ||||||
| 527 | 1 | 5 | return $v; | ||||
| 528 | } | ||||||
| 529 | elsif ( defined($argument) ){ | ||||||
| 530 | # bitwise and of Color::Model::RGB with scalar | ||||||
| 531 | 1 | 5 | my @rgb = $object->truncate()->array256(); | ||||
| 532 | 3 | 10 | my $v = rgb256( map { | ||||
| 533 | 1 | 4 | $_ & $argument; | ||||
| 534 | } @rgb ); | ||||||
| 535 | 1 | 12 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 4 | ||||||
| 536 | 1 | 5 | return $v; | ||||
| 537 | } | ||||||
| 538 | 0 | 0 | Carp::croak("undefined argument given for vector bitwise and"); | ||||
| 539 | } | ||||||
| 540 | |||||||
| 541 | sub _bit_or | ||||||
| 542 | { | ||||||
| 543 | 2 | 2 | 5 | my($object,$argument,$flip) = @_; | |||
| 544 | 2 | 4 | _trace("'|'",$object,$argument,$flip); | ||||
| 545 | 2 | 100 | 13 | if ( ref($argument) ) { | |||
| 50 | |||||||
| 546 | # bitwise or of two Color::Model::RGB | ||||||
| 547 | 1 | 3 | my @vrgb = $object->truncate()->array256(); | ||||
| 548 | 1 | 6 | my @argb = $argument->truncate()->array256(); | ||||
| 549 | 1 | 7 | my $v = rgb256( | ||||
| 550 | $vrgb[0] | $argb[0], | ||||||
| 551 | $vrgb[1] | $argb[1], | ||||||
| 552 | $vrgb[2] | $argb[2] | ||||||
| 553 | ); | ||||||
| 554 | 1 | 12 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 3 | ||||||
| 555 | 1 | 5 | return $v; | ||||
| 556 | } | ||||||
| 557 | elsif ( defined($argument) ){ | ||||||
| 558 | # bitwise or of Color::Model::RGB with scalar | ||||||
| 559 | 1 | 3 | my @rgb = $object->truncate()->array256(); | ||||
| 560 | 3 | 4 | my $v = rgb256( map { | ||||
| 561 | 1 | 5 | $_ |= $argument; | ||||
| 562 | 3 | 8 | $_ &= 0xff; | ||||
| 563 | } @rgb ); | ||||||
| 564 | 1 | 11 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 3 | ||||||
| 565 | 1 | 5 | return $v; | ||||
| 566 | } | ||||||
| 567 | 0 | 0 | Carp::croak("undefined argument given for vector bitwise or"); | ||||
| 568 | } | ||||||
| 569 | |||||||
| 570 | sub _bit_xor | ||||||
| 571 | { | ||||||
| 572 | 2 | 2 | 5 | my($object,$argument,$flip) = @_; | |||
| 573 | 2 | 7 | _trace("'^'",$object,$argument,$flip); | ||||
| 574 | 2 | 100 | 15 | if ( ref($argument) ) { | |||
| 50 | |||||||
| 575 | # bitwise exclusive or of two Color::Model::RGB | ||||||
| 576 | 1 | 18 | my @vrgb = $object->truncate()->array256(); | ||||
| 577 | 1 | 6 | my @argb = $argument->truncate()->array256(); | ||||
| 578 | 1 | 7 | my $v = rgb256( | ||||
| 579 | $vrgb[0] ^ $argb[0], | ||||||
| 580 | $vrgb[1] ^ $argb[1], | ||||||
| 581 | $vrgb[2] ^ $argb[2] | ||||||
| 582 | ); | ||||||
| 583 | 1 | 10 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 3 | ||||||
| 584 | 1 | 4 | return $v; | ||||
| 585 | } | ||||||
| 586 | elsif ( defined($argument) ){ | ||||||
| 587 | # bitwise exclusive or of Color::Model::RGB with scalar | ||||||
| 588 | 1 | 5 | my @rgb = $object->truncate()->array256(); | ||||
| 589 | 3 | 6 | my $v = rgb256( map { | ||||
| 590 | 1 | 6 | $_ ^= $argument; | ||||
| 591 | 3 | 11 | $_ &= 0xff; | ||||
| 592 | } @rgb ); | ||||||
| 593 | 1 | 11 | $#{$v} = 2; # any cached vector length is now invalid | ||||
| 1 | 4 | ||||||
| 594 | 1 | 6 | return $v; | ||||
| 595 | } | ||||||
| 596 | 0 | 0 | Carp::croak("undefined argument given for vector bitwise exclusive or"); | ||||
| 597 | } | ||||||
| 598 | |||||||
| 599 | sub _bit_not | ||||||
| 600 | { | ||||||
| 601 | 1 | 1 | 4 | my($object,$argument,$flip) = @_; | |||
| 602 | 1 | 14 | _trace("'~'",$object,$argument,$flip); | ||||
| 603 | # bitwise complement of Color::Model::RGB with scalar | ||||||
| 604 | 1 | 6 | my @rgb = $object->truncate()->array256(); | ||||
| 605 | 3 | 7 | my $v = rgb256( map { | ||||
| 606 | 1 | 6 | $_ = ~$_; | ||||
| 607 | 3 | 8 | $_ &= 0xff; | ||||
| 608 | } @rgb ); | ||||||
| 609 | 1 | 14 | return $v; | ||||
| 610 | } | ||||||
| 611 | |||||||
| 612 | |||||||
| 613 | # ============================================================================= | ||||||
| 614 | |||||||
| 615 | =head1 EXPORTING FUNCTION | ||||||
| 616 | |||||||
| 617 | There are few froups for exporting. | ||||||
| 618 | |||||||
| 619 | Defalut exporting functions are I |
||||||
| 620 | |||||||
| 621 | Primary colors, I |
||||||
| 622 | I |
||||||
| 623 | or ':RGB'. | ||||||
| 624 | |||||||
| 625 | Functions changes defalut about stringifying, I |
||||||
| 626 | will be exported with tag ':format'. | ||||||
| 627 | |||||||
| 628 | And color blending functions, I |
||||||
| 629 | I |
||||||
| 630 | |||||||
| 631 | |||||||
| 632 | =head2 CHANGING STRINGIFYING DEFALUT | ||||||
| 633 | |||||||
| 634 | =over | ||||||
| 635 | |||||||
| 636 | =item set_format( $format [, $flag2hex] ) | ||||||
| 637 | |||||||
| 638 | =item get_format() | ||||||
| 639 | |||||||
| 640 | Set and get defalut values of stringifying. See method I |
||||||
| 641 | above. | ||||||
| 642 | |||||||
| 643 | =back | ||||||
| 644 | |||||||
| 645 | =cut | ||||||
| 646 | |||||||
| 647 | # ----------------------------------------------------------------------------- | ||||||
| 648 | sub set_format | ||||||
| 649 | { | ||||||
| 650 | 4 | 4 | 1 | 1626 | my ($fmt, $hexed) = @_; | ||
| 651 | |||||||
| 652 | 4 | 50 | 66 | if ( !@_ ) { | |||
| 653 | 0 | 0 | Carp::croak("No argument given"); | ||||
| 654 | } | ||||||
| 655 | 4 | 50 | 19 | if ( @_ == 2 ){ | |||
| 656 | 4 | 50 | 25 | $FORMAT_HEXED = $hexed? 1: 0; | |||
| 657 | } | ||||||
| 658 | 4 | 50 | 32 | if ( @_ >= 1 ){ | |||
| 659 | 4 | 50 | 36 | $FORMAT = $fmt if defined $fmt; | |||
| 660 | } | ||||||
| 661 | } | ||||||
| 662 | |||||||
| 663 | sub get_format | ||||||
| 664 | { | ||||||
| 665 | 1 | 1 | 1 | 2 | my ($fmt, $hexed) = @_; | ||
| 666 | |||||||
| 667 | 1 | 4 | return ($FORMAT,$FORMAT_HEXED); | ||||
| 668 | } | ||||||
| 669 | |||||||
| 670 | |||||||
| 671 | |||||||
| 672 | |||||||
| 673 | # ============================================================================= | ||||||
| 674 | |||||||
| 675 | =head2 BLENDING FUNCTIONS | ||||||
| 676 | |||||||
| 677 | Color::Model::RGB has several blending functions which make a new object from | ||||||
| 678 | two objects. | ||||||
| 679 | |||||||
| 680 | $blend_alpha = blend_alpha($col1,0.3,$col2,0.7); # any transparency rate | ||||||
| 681 | $blend_half = blend_half($col1,$col2); # 50%:50% | ||||||
| 682 | $blend_plus = blend_plus($col1,$col2); # $col1 + $col2 | ||||||
| 683 | $blend_minus = blend_plus($col1,$col2); # $col1 - $col2 | ||||||
| 684 | |||||||
| 685 | =cut | ||||||
| 686 | |||||||
| 687 | # ----------------------------------------------------------------------------- | ||||||
| 688 | sub blend_alpha | ||||||
| 689 | { | ||||||
| 690 | 4 | 4 | 0 | 17 | my ($src,$src_rate, $dist,$dist_rate) = @_; | ||
| 691 | 4 | 50 | 33 | 48 | unless ( Scalar::Util::blessed($src) && $src->isa(__PACKAGE__) ){ | ||
| 692 | 0 | 0 | Carp::croak("First argumenst must be object of ".__PACKAGE__); | ||||
| 693 | } | ||||||
| 694 | 4 | 50 | 33 | 94 | unless ( !ref($src_rate) && $src_rate =~ /^[0-9\.\-]+$/ && | ||
| 33 | |||||||
| 33 | |||||||
| 695 | $src_rate >=-1 && $src_rate <= 1 ){ | ||||||
| 696 | 0 | 0 | Carp::croak("Second argumenst must be a number between -1.0 to 1.0"); | ||||
| 697 | } | ||||||
| 698 | 4 | 50 | 33 | 38 | unless ( Scalar::Util::blessed($dist) && $dist->isa(__PACKAGE__) ){ | ||
| 699 | 0 | 0 | Carp::croak("Third argumenst must be object of ".__PACKAGE__); | ||||
| 700 | } | ||||||
| 701 | 4 | 50 | 33 | 59 | unless ( !ref($dist_rate) && $dist_rate =~ /^[0-9\.\-]+$/ && | ||
| 33 | |||||||
| 33 | |||||||
| 702 | $dist_rate >=-1 && $dist_rate <= 1 ){ | ||||||
| 703 | 0 | 0 | Carp::croak("Fourth argumenst must be a number between -1.0 to 1.0"); | ||||
| 704 | } | ||||||
| 705 | |||||||
| 706 | 4 | 13 | return ( $src * $src_rate + $dist * $dist_rate )->truncate(); | ||||
| 707 | } | ||||||
| 708 | |||||||
| 709 | |||||||
| 710 | sub blend_half | ||||||
| 711 | { | ||||||
| 712 | 1 | 1 | 0 | 12 | return blend_alpha($_[0], 0.5, $_[1], 0.5); | ||
| 713 | } | ||||||
| 714 | |||||||
| 715 | |||||||
| 716 | sub blend_plus | ||||||
| 717 | { | ||||||
| 718 | 1 | 1 | 0 | 14 | return blend_alpha($_[0], 1.0, $_[1], 1.0); | ||
| 719 | } | ||||||
| 720 | |||||||
| 721 | sub blend_minus | ||||||
| 722 | { | ||||||
| 723 | 1 | 1 | 0 | 14 | return blend_alpha($_[0], 1.0, $_[1], -1.0); | ||
| 724 | } | ||||||
| 725 | |||||||
| 726 | |||||||
| 727 | |||||||
| 728 | |||||||
| 729 | # ============================================================================= | ||||||
| 730 | 1; | ||||||
| 731 | |||||||
| 732 | __END__ |