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__ |