File Coverage

blib/lib/Image/Xbm.pm
Criterion Covered Total %
statement 180 215 83.7
branch 69 118 58.4
condition 31 95 32.6
subroutine 17 22 77.2
pod 14 14 100.0
total 311 464 67.0


line stmt bran cond sub pod time code
1             package Image::Xbm ; # Documented at the __END__
2              
3 2     2   296688 use strict ;
  2         4  
  2         103  
4              
5 2     2   11 use vars qw( $VERSION @ISA ) ;
  2         5  
  2         177  
6             $VERSION = '1.11' ;
7              
8 2     2   1250 use Image::Base ;
  2         5066  
  2         114  
9              
10             @ISA = qw( Image::Base ) ;
11              
12 2     2   15 use Carp qw( carp croak ) ;
  2         6  
  2         200  
13 2     2   15 use Symbol () ;
  2         3  
  2         7890  
14              
15              
16             # Private class data
17              
18             my $DEF_SIZE = 8192 ;
19             my $UNSET = -1 ;
20             my $MASK = 7 ;
21             my $ROWS = 12 ;
22              
23             # If you inherit don't clobber these fields!
24             my @FIELD = qw( -file -width -height -hotx -hoty -bits
25             -setch -unsetch -sethotch -unsethotch ) ;
26              
27             my @MASK = ( 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ) ;
28              
29              
30             ### Private methods
31             #
32             # _class_get class object
33             # _class_set class object
34             # _get object inherited
35             # _set object inherited
36              
37             {
38             my %Ch = ( -setch => '#', -unsetch => '-',
39             -sethotch => 'H', -unsethotch => 'h' ) ;
40            
41              
42             sub _class_get { # Class and object method
43 16     16   29 my $self = shift ;
44 16   33     49 my $class = ref( $self ) || $self ;
45              
46 16         61 $Ch{shift()} ;
47             }
48              
49              
50             sub _class_set { # Class and object method
51 0     0   0 my $self = shift ;
52 0   0     0 my $class = ref( $self ) || $self ;
53              
54 0         0 my $field = shift ;
55 0         0 my $val = shift ;
56              
57 0 0       0 croak "_class_set() `$field' has no value" unless defined $val ;
58              
59 0         0 $Ch{$field} = $val ;
60             }
61             }
62              
63              
64       0     sub DESTROY {
65             ; # Save's time
66             }
67              
68              
69             ### Public methods
70              
71             sub new_from_string { # Class and object method
72 4     4 1 408200 my $self = shift ;
73 4   33     25 my $class = ref( $self ) || $self ;
74              
75 4         9 my @line ;
76            
77 4 50       13 if( @_ > 1 ) {
78 0         0 chomp( @line = @_ ) ;
79             }
80             else {
81 4         20 @line = split /\n/, $_[0] ;
82             }
83              
84 4         18 my( $setch, $sethotch, $unsethotch ) =
85             $class->get( '-setch', '-sethotch', '-unsethotch' ) ;
86              
87 4         14 my $width ;
88 4         8 my $y = 0 ;
89            
90 4         14 $self = $class->new( '-width' => $DEF_SIZE, '-height' => $DEF_SIZE ) ;
91              
92 4         10 foreach my $line ( @line ) {
93 24 50       145 next if $line =~ /^\s*$/ ;
94 24 100       72 unless( defined $width ) {
95 4         7 $width = length $line ;
96 4         16 $self->_set( '-width' => $width ) ;
97             }
98 24         69 for( my $x = 0 ; $x < $width ; $x++ ) {
99 120         216 my $c = substr( $line, $x, 1 ) ;
100 120 50       383 $self->xybit( $x, $y, $c eq $setch ? 1 : $c eq $sethotch ? 1 : 0 ) ;
    100          
101 120 50 33     531 $self->set( '-hotx' => $x, '-hoty' => $y )
102             if $c eq $sethotch or $c eq $unsethotch ;
103             }
104 24         67 $y++ ;
105             }
106              
107 4         16 $self->_set( '-height' => $y ) ;
108              
109 4         40 $self ;
110             }
111              
112              
113             sub new { # Class and object method
114 12     12 1 8642 my $self = shift ;
115 12   66     63 my $class = ref( $self ) || $self ;
116 12 100       28 my $obj = ref $self ? $self : undef ;
117 12         41 my %arg = @_ ;
118              
119             # Defaults
120 12         45 $self = {
121             '-hotx' => $UNSET,
122             '-hoty' => $UNSET,
123             '-bits' => '',
124             } ;
125              
126 12         24 bless $self, $class ;
127              
128             # If $obj->new copy original object's data
129 12 100       29 if( defined $obj ) {
130 1         3 foreach my $field ( @FIELD ) {
131 10         63 $self->_set( $field, $obj->get( $field ) ) ;
132             }
133             }
134              
135             # Any options specified override
136 12         32 foreach my $field ( @FIELD ) {
137 120 100       378 $self->_set( $field, $arg{$field} ) if defined $arg{$field} ;
138             }
139              
140 12         28 my $file = $self->get( '-file' ) ;
141 12 100 66     60 if (defined $file and not $self->{-bits}) {
142 6 50 66     132 $self->load if ref $file or -r $file;
143             }
144              
145 12 50 66     89 croak "new() `$file' not found or unreadable"
146             if defined $file and not defined $self->get( '-width' ) ;
147              
148              
149 12         25 foreach my $field ( qw( -width -height ) ) {
150 24 50       45 croak "new() $field must be set" unless defined $self->get( $field ) ;
151             }
152              
153 12         42 $self ;
154             }
155              
156              
157             sub new_from_serialised { # Class and object method
158 1     1 1 293 my $self = shift ;
159 1   33     5 my $class = ref( $self ) || $self ;
160 1         2 my $serialised = shift ;
161              
162 1         3 $self = $class->new( '-width' => $DEF_SIZE, '-height' => $DEF_SIZE ) ;
163              
164 1         5 my( $flen, $blen, $width, $height, $hotx, $hoty, $data ) =
165             unpack "n N n n n n A*", $serialised ;
166            
167 1         4 my( $file, $bits ) = unpack "A$flen A$blen", $data ;
168              
169 1         3 $self->_set( '-file' => $file ) ;
170 1         5 $self->_set( '-width' => $width ) ;
171 1         5 $self->_set( '-height' => $height ) ;
172 1 50       5 $self->_set( '-hotx' => $hotx > $width ? $UNSET : $hotx ) ;
173 1 50       5 $self->_set( '-hoty' => $hoty > $height ? $UNSET : $hoty ) ;
174 1         5 $self->_set( '-bits' => $bits ) ;
175              
176 1         4 $self ;
177             }
178              
179              
180             sub serialise { # Object method
181 1     1 1 27 my $self = shift ;
182             # my $class = ref( $self ) || $self ;
183              
184 1         5 my( $file, $bits ) = $self->get( -file, -bits ) ;
185 1         2 my $flen = length( $file ) ;
186 1         2 my $blen = length( $bits ) ;
187              
188 1         4 pack "n N n n n n A$flen A$blen",
189             $flen, $blen,
190             $self->get( -width ), $self->get( -height ),
191             $self->get( -hotx ), $self->get( -hoty ),
192             $file, $bits ;
193             }
194              
195              
196             sub get { # Object method (and class method for class attributes)
197 343     343 1 464 my $self = shift ;
198 343   66     705 my $class = ref( $self ) || $self ;
199            
200 343         442 my @result ;
201              
202 343         658 while( @_ ) {
203 364         581 my $field = shift ;
204              
205 364 100       856 if( $field =~ /^-(?:un)?set(?:hot)?ch$/o ) {
206 16         41 push @result, $class->_class_get( $field ) ;
207             }
208             else {
209 348         718 push @result, $self->_get( $field ) ;
210             }
211             }
212              
213 343 100       2261 wantarray ? @result : shift @result ;
214             }
215              
216              
217             sub set { # Object method (and class method for class attributes)
218 22     22 1 35 my $self = shift ;
219 22   33     46 my $class = ref( $self ) || $self ;
220            
221 22         41 while( @_ ) {
222 22         55 my $field = shift ;
223 22         27 my $val = shift ;
224              
225 22 50       42 carp "set() -field has no value" unless defined $val ;
226 22 50 33     106 carp "set() $field is read-only"
      33        
227             if $field eq '-bits' or $field eq '-width' or $field eq '-height' ;
228 22 50 33     55 carp "set() -hotx `$val' is out of range"
      66        
229             if $field eq '-hotx' and ( $val < $UNSET or $val >= $self->get( '-width' ) ) ;
230 22 50 33     55 carp "set() -hoty `$val' is out of range"
      66        
231             if $field eq '-hoty' and ( $val < $UNSET or $val >= $self->get( '-height' ) ) ;
232              
233 22 50       47 if( $field =~ /^-(?:un)?set(?:hot)?ch$/o ) {
234 0         0 $class->_class_set( $field, $val ) ;
235             }
236             else {
237 22         46 $self->_set( $field, $val ) ;
238             }
239             }
240             }
241              
242              
243             sub xybit { # Object method
244 240     240 1 335 my $self = shift ;
245             # my $class = ref( $self ) || $self ;
246              
247 240         423 my( $x, $y, $val ) = @_ ;
248              
249             # No range checking
250 240         409 my $offset = ( $y * $self->get( '-width' ) ) + $x ;
251              
252 240 100       475 if( defined $val ) {
253 120         386 CORE::vec( $self->{'-bits'}, $offset, 1 ) = $val ;
254             }
255             else {
256 120         333 CORE::vec( $self->{'-bits'}, $offset, 1 ) ;
257             }
258             }
259              
260              
261             sub xy { # Object method
262 0     0 1 0 my $self = shift ;
263             # my $class = ref( $self ) || $self ;
264              
265 0         0 my( $x, $y, $val ) = @_ ;
266              
267             # No range checking
268 0         0 my $offset = ( $y * $self->get( '-width' ) ) + $x ;
269              
270 0 0       0 if( defined $val ) {
271 0 0 0     0 $val = 1 if ( $val =~ /^\d+$/ and $val >= 1 ) or
      0        
      0        
      0        
272             ( lc $val eq 'black' ) or
273             ( $val =~ /^#(\d+)$/ and hex $1 ) ;
274 0         0 CORE::vec( $self->{'-bits'}, $offset, 1 ) = $val ;
275             }
276             else {
277 0 0       0 CORE::vec( $self->{'-bits'}, $offset, 1 ) ? 'black' : 'white' ;
278             }
279             }
280              
281              
282             sub vec { # Object method
283 0     0 1 0 my $self = shift ;
284             # my $class = ref( $self ) || $self ;
285              
286 0         0 my( $offset, $val ) = @_ ;
287              
288             # No range checking
289 0 0       0 if( defined $val ) {
290 0         0 CORE::vec( $self->{'-bits'}, $offset, 1 ) = $val ;
291             }
292             else {
293 0         0 CORE::vec( $self->{'-bits'}, $offset, 1 ) ;
294             }
295             }
296              
297              
298             sub is_equal { # Object method
299 1     1 1 285 my $self = shift ;
300 1   33     4 my $class = ref( $self ) || $self ;
301 1         1 my $obj = shift ;
302              
303 1 50 33     17 croak "is_equal() can only compare $class objects"
304             unless ref $obj and $obj->isa( __PACKAGE__ ) ;
305              
306             # We ignore -file, -hotx and -hoty when we consider equality.
307 1 50 33     6 return 0 if $self->get( '-width' ) != $obj->get( '-width' ) or
      33        
308             $self->get( '-height' ) != $obj->get( '-height' ) or
309             $self->get( '-bits' ) ne $obj->get( '-bits' ) ;
310              
311 1         3 1 ;
312             }
313              
314              
315             sub as_string { # Object method
316 0     0 1 0 my $self = shift ;
317             # my $class = ref( $self ) || $self ;
318              
319 0   0     0 my $hotch = shift || 0 ;
320              
321 0         0 my( $setch, $unsetch,
322             $sethotch, $unsethotch,
323             $hotx, $hoty,
324             $bits,
325             $width, $height ) =
326             $self->get(
327             '-setch', '-unsetch',
328             '-sethotch', '-unsethotch',
329             '-hotx', '-hoty',
330             '-bits',
331             '-width', '-height' ) ;
332              
333 0         0 my $bitindex = 0 ;
334 0         0 my $string = '' ;
335              
336 0         0 for( my $y = 0 ; $y < $height ; $y++ ) {
337 0         0 for( my $x = 0 ; $x < $width ; $x++ ) {
338 0 0 0     0 if( $hotch and $x == $hotx and $y == $hoty ) {
      0        
339 0 0       0 $string .= CORE::vec( $bits, $bitindex, 1 ) ?
340             $sethotch : $unsethotch ;
341             }
342             else {
343 0 0       0 $string .= CORE::vec( $bits, $bitindex, 1 ) ?
344             $setch : $unsetch ;
345             }
346 0         0 $bitindex++ ;
347             }
348 0         0 $string .= "\n" ;
349             }
350              
351 0         0 $string ;
352             }
353              
354              
355             sub as_binstring { # Object method
356 11     11 1 2026 my $self = shift ;
357             # my $class = ref( $self ) || $self ;
358              
359 11         26 unpack "b*", $self->get( '-bits' ) ;
360             }
361              
362              
363             # The algorithm is based on the one used in Thomas Boutell's GD library.
364             sub load { # Object method
365 6     6 1 14 my $self = shift ;
366             # my $class = ref( $self ) || $self ;
367              
368 6   33     19 my $file = shift() || $self->get( '-file' ) ;
369              
370 6 50       15 croak "load() no file specified" unless $file ;
371              
372 6         18 $self->set( '-file', $file ) ;
373              
374 6         40 my( @val, $width, $height, $hotx, $hoty ) ;
375 6         8 local $_ ;
376 6         22 my $fh = Symbol::gensym ;
377              
378 6 100       105 if( not ref $file ) {
    50          
379 4 50       182 open $fh, $file or croak "load() failed to open `$file': $!" ;
380             }
381             elsif( ref($file) eq 'SCALAR' ) {
382 0         0 require IO::String;
383 0         0 $fh = IO::String->new( $$file );
384             }
385             else {
386 2 50       8 seek($file, 0, 0) or croak "load() can't rewind handle for `$file': $!";
387 2         3 $fh = $file;
388             }
389              
390 6         144 while( <$fh> ) {
391 24 100       108 $width = $1, next if /#define.*width\s+(\d+)/o ;
392 18 100       82 $height = $1, next if /#define.*height\s+(\d+)/o ;
393 12 50       27 $hotx = $1, next if /#define.*_x_hot\s+(\d+)/o ;
394 12 50       24 $hoty = $1, next if /#define.*_y_hot\s+(\d+)/o ;
395 12         76 push @val, map { hex } /0[xX]([A-Fa-f\d][A-Fa-f\d]?)\b/g ;
  36         122  
396             }
397 6 50 33     43 croak "load() failed to find dimension(s) in `$file'"
398             unless defined $width and defined $height ;
399              
400 6 50       66 close $fh or croak "load() failed to close `$file': $!" ;
401              
402 6         24 $self->_set( '-width', $width ) ;
403 6         45 $self->_set( '-height', $height ) ;
404 6 50       51 $self->set( '-hotx', defined $hotx ? $hotx : $UNSET ) ;
405 6 50       45 $self->set( '-hoty', defined $hoty ? $hoty : $UNSET ) ;
406              
407 6         58 my( $x, $y ) = ( 0, 0 ) ;
408 6         9 my $bitindex = 0 ;
409 6         9 my $bits = '' ;
410             BYTE:
411 6         10 for( my $i = 0 ; ; $i++ ) {
412             BIT:
413 36         66 for( my $bit = 1 ; $bit <= 128 ; $bit <<= 1 ) {
414 180 100       423 CORE::vec( $bits, $bitindex++, 1 ) = ( $val[$i] & $bit ) ? 1 : 0 ;
415 180         272 $x++ ;
416 180 100       379 if( $x == $width ) {
417 36         54 $x = 0 ;
418 36         45 $y++ ;
419 36 100       64 last BYTE if $y == $height ;
420 30         54 last BIT ;
421             }
422             }
423             }
424              
425 6         20 $self->_set( '-bits', $bits ) ;
426             }
427              
428              
429             # The algorithm is based on the X Consortium's bmtoa program.
430             sub save { # Object method
431 4     4 1 24 my $self = shift ;
432             # my $class = ref( $self ) || $self ;
433              
434 4   33     14 my $file = shift() || $self->get( '-file' ) ;
435              
436 4 50       11 croak "save() no file specified" unless $file ;
437              
438 4         14 $self->set( '-file', $file ) ;
439              
440 4         37 my( $width, $height, $hotx, $hoty ) =
441             $self->get( '-width', '-height', '-hotx', '-hoty' ) ;
442              
443 4         10 my $MASK1 = $MASK + 1 ;
444 4         9 my $ROWSn1 = $ROWS - 1 ;
445              
446 4         21 my $fh = Symbol::gensym ;
447 4 50       430 open $fh, ">$file" or croak "save() failed to open `$file': $!" ;
448              
449 4         19 $file =~ s,^.*/,,o ;
450 4         29 $file =~ s/\.xbm$//o ;
451 4         10 $file =~ tr/_A-Za-z0-9/_/c ;
452            
453 4         75 print $fh "#define ${file}_width $width\n#define ${file}_height $height\n" ;
454 4 50 33     17 print $fh "#define ${file}_x_hot $hotx\n#define ${file}_y_hot $hoty\n"
455             if $hotx > $UNSET and $hoty > $UNSET ;
456 4         11 print $fh "static unsigned char ${file}_bits[] = {\n" ;
457              
458 4         14 my $padded = ( $width & $MASK ) != 0 ;
459 4         8 my @char ;
460 4         7 my $char = 0 ;
461 4         14 for( my $y = 0 ; $y < $height ; $y++ ) {
462 24         55 for( my $x = 0 ; $x < $width ; $x++ ) {
463 120         191 my $mask = $x & $MASK ;
464 120 100       257 $char[$char] = 0 unless defined $char[$char] ;
465 120 100       214 $char[$char] |= $MASK[$mask] if $self->xybit( $x, $y ) ;
466 120 50       301 $char++ if $mask == $MASK ;
467             }
468 24 50       70 $char++ if $padded ;
469             }
470              
471 4         9 my $i = 0 ;
472 4         12 my $bytes_per_char = ( $width + $MASK ) / $MASK1 ;
473 4         10 foreach $char ( @char ) {
474 24         64 printf $fh " 0x%02x", $char ;
475 24 100       59 print $fh "," unless $i == $#char ;
476 24 50       60 print $fh "\n" if $i % $ROWS == $ROWSn1 ;
477 24         54 $i++ ;
478             }
479 4         8 print $fh " } ;\n";
480              
481 4 50       895 close $fh or croak "save() failed to close `$file': $!" ;
482             }
483              
484              
485             1 ;
486              
487              
488             __END__
489              
490             =head1 NAME
491              
492             Image::Xbm - Load, create, manipulate and save xbm image files.
493              
494             =head1 SYNOPSIS
495              
496             use Image::Xbm ;
497              
498             my $j = Image::Xbm->new( -file, 'balArrow.xbm' ) ;
499              
500             my $i = Image::Xbm->new( -width => 10, -height => 16 ) ;
501              
502             my $h = $i->new ; # Copy of $i
503              
504             my $p = Image::Xbm->new_from_string( "###\n#-#\n###" ) ;
505              
506             my $q = $p->new_from_string( "H##", "#-#", "###" ) ;
507              
508             my $s = $q->serialse ; # Compresses a little too.
509             my $t = Image::Xbm->new_from_serialsed( $s ) ;
510              
511             $i->xybit( 5, 8, 1 ) ; # Set a bit
512             print '1' if $i->xybit( 9, 3 ) ; # Get a bit
513             print $i->xy( 4, 5 ) ; # Will print black or white
514              
515             $i->vec( 24, 0 ) ; # Set a bit using a vector offset
516             print '1' if $i->vec( 24 ) ; # Get a bit using a vector offset
517              
518             print $i->get( -width ) ; # Get and set object and class attributes
519             $i->set( -height, 15 ) ;
520              
521             $i->load( 'test.xbm' ) ;
522             $i->save ;
523              
524             print "equal\n" if $i->is_equal( $j ) ;
525              
526             print $j->as_string ;
527              
528             #####-
529             ###---
530             ###---
531             #--#--
532             #---#-
533             -----#
534              
535             print $j->as_binstring ;
536              
537             1111101110001110001001001000100000010000
538              
539             View an xbm file from the command line:
540              
541             % perl -MImage::Xbm -e'print Image::Xbm->new(-file,shift)->as_string' file
542              
543             Create an xbm file from the command line:
544              
545             % perl -MImage::Xbm -e'Image::Xbm->new_from_string("###\n#-#\n-#-")->save("test.xbm")'
546              
547             =head1 DESCRIPTION
548              
549             This class module provides basic load, manipulate and save functionality for
550             the xbm file format. It inherits from C<Image::Base> which provides additional
551             manipulation functionality, e.g. C<new_from_image()>. See the C<Image::Base>
552             pod for information on adding your own functionality to all the C<Image::Base>
553             derived classes.
554              
555             =head2 new()
556              
557             my $i = Image::Xbm->new( -file => 'test.xbm' ) ;
558             my $j = Image::Xbm->new( -width => 12, -height => 18 ) ;
559             my $k = $i->new ;
560              
561             We can create a new xbm image by reading in a file, or by creating an image
562             from scratch (all the bits are unset by default), or by copying an image
563             object that we created earlier.
564              
565             If we set C<-file> then all the other arguments are ignored (since they're
566             taken from the file). If we don't specify a file, C<-width> and C<-height> are
567             mandatory.
568              
569             =over
570              
571             =item C<-file>
572              
573             The name of the file to read when creating the image. May contain a full path.
574             This is also the default name used for C<load>ing and C<save>ing, though it
575             can be overridden when you load or save.
576              
577             =item C<-width>
578              
579             The width of the image; taken from the file or set when the object is created;
580             read-only.
581              
582             =item C<-height>
583              
584             The height of the image; taken from the file or set when the object is created;
585             read-only.
586              
587             =item C<-hotx>
588              
589             The x-coord of the image's hotspot; taken from the file or set when the object
590             is created. Set to -1 if there is no hotspot.
591              
592             =item C<-hoty>
593              
594             The y-coord of the image's hotspot; taken from the file or set when the object
595             is created. Set to -1 if there is no hotspot.
596              
597             =item C<-bits>
598              
599             The bit vector that stores the image; read-only.
600              
601             =back
602              
603             =head2 new_from_string()
604              
605             my $p = Image::Xbm->new_from_string( "###\n#-#\n###" ) ;
606             my $q = $p->new_from_string( "H##", "#-#", "###" ) ;
607             my $r = $p->new_from_string( $p->as_string ) ;
608              
609             Create a new bitmap from a string or from an array or list of strings. If you
610             want to use different characters you can:
611              
612             Image::Xbm->set( -setch => 'X', -unsetch => ' ' ) ;
613             my $s = $p->new_from_string( "XXX", "X X", "XhX" ) ;
614              
615             You can also specify a hotspot by making one of the characters a 'H' (set bit
616             hotspot) or 'h' (unset bit hotspot) -- you can use different characters by
617             setting C<-sethotch> and C<-unsethotch> respectively.
618              
619             =head2 new_from_serialised()
620              
621             my $i = Image::Xbm->new_from_serialised( $s ) ;
622              
623             Creates an image from a string created with the C<serialse()> method. Since
624             such strings are a little more compressed than xbm files or Image::Xbm objects
625             they might be useful if storing a lot of bitmaps, or for transferring bitmaps
626             over comms links.
627              
628             =head2 serialise()
629              
630             my $s = $i->serialise ;
631              
632             Creates a string version of the image which can be completed recreated using
633             the C<new_from_serialised> method.
634              
635             =head2 get()
636            
637             my $width = $i->get( -width ) ;
638             my( $hotx, $hoty ) = $i->get( -hotx, -hoty ) ;
639              
640             Get any of the object's attributes. Multiple attributes may be requested in a
641             single call.
642              
643             See C<xy> and C<vec> to get/set bits of the image itself.
644              
645             =head2 set()
646              
647             $i->set( -hotx => 120, -hoty => 32 ) ;
648              
649             Set any of the object's attributes. Multiple attributes may be set in a single
650             call. Except for C<-setch> and C<-unsetch> all attributes are object
651             attributes; some attributes are read-only.
652              
653             See C<xy> and C<vec> to get/set bits of the image itself.
654              
655             =head2 class attributes
656              
657             Image::Xbm->set( -setch => 'X' ) ;
658             $i->set( -setch => '@', -unsetch => '*' ) ;
659              
660             =over
661              
662             =item C<-setch>
663              
664             The character to print set bits as when using C<as_string>, default is '#'.
665             This is a class attribute accessible from the class or an object via C<get>
666             and C<set>.
667              
668             =item C<-unsetch>
669              
670             The character to print set bits as when using C<as_string>, default is '-'.
671             This is a class attribute accessible from the class or an object via C<get>
672             and C<set>.
673              
674             =item C<-sethotch>
675              
676             The character to print set bits as when using C<as_string>, default is 'H'.
677             This is a class attribute accessible from the class or an object via C<get>
678             and C<set>.
679              
680             =item C<-unsethotch>
681              
682             The character to print set bits as when using C<as_string>, default is 'h'.
683             This is a class attribute accessible from the class or an object via C<get>
684             and C<set>.
685              
686             =back
687              
688             =head2 xybit()
689              
690             $i->xy( 4, 11, 1 ) ; # Set the bit at point 4,11
691             my $v = $i->xy( 9, 17 ) ; # Get the bit at point 9,17
692              
693             Get/set bits using x, y coordinates; coordinates start at 0.
694              
695             =head2 xy()
696              
697             $i->xy( 4, 11, 'black' ) ; # Set the bit from a colour at point 4,11
698             my $v = $i->xy( 9, 17 ) ; # Get the bit as a colour at point 9,17
699              
700             Get/set bits using colours using x, y coordinates; coordinates start at 0.
701              
702             If set with a colour of 'black' or a numeric value > 0 or a string not
703             matching /^#0+$/ then the bit will be set, otherwise it will be cleared.
704              
705             If you get a colour you will always get 'black' or 'white'.
706              
707             =head2 vec()
708              
709             $i->vec( 43, 0 ) ; # Unset the bit at offset 43
710             my $v = $i->vec( 87 ) ; # Get the bit at offset 87
711              
712             Get/set bits using vector offsets; offsets start at 0.
713              
714             =head2 load()
715              
716             $i->load ;
717             $i->load( 'test.xbm' ) ;
718              
719             Load the image whose name is given, or if none is given load the image whose
720             name is in the C<-file> attribute.
721              
722             =head2 save()
723              
724             $i->save ;
725             $i->save( 'test.xbm' ) ;
726              
727             Save the image using the name given, or if none is given save the image using
728             the name in the C<-file> attribute. The image is saved in xbm format, e.g.
729              
730             #define test_width 6
731             #define test_height 6
732             static unsigned char test_bits[] = {
733             0x1f, 0x07, 0x07, 0x09, 0x11, 0x20 } ;
734              
735             =head2 is_equal()
736              
737             print "equal\n" if $i->is_equal( $j ) ;
738              
739             Returns true (1) if the images are equal, false (0) otherwise. Note that
740             hotspots and filenames are ignored, so we compare width, height and the actual
741             bits only.
742              
743             =head2 as_string()
744              
745             print $i->as_string ;
746              
747             Returns the image as a string, e.g.
748              
749             #####-
750             ###---
751             ###---
752             #--#--
753             #---#-
754             -----#
755              
756             The characters used may be changed by C<set>ting the C<-setch> and C<-unsetch>
757             characters. If you give C<as_string> a parameter it will print out the hotspot
758             if present using C<-sethotch> or C<-unsethotch> as appropriate, e.g.
759              
760             print $n->as_string( 1 ) ;
761              
762             H##
763             #-#
764             ###
765              
766             =head2 as_binstring()
767              
768             print $i->as_binstring ;
769              
770             Returns the image as a string of 0's and 1's, e.g.
771              
772             1111101110001110001001001000100000010000
773              
774             =head1 CHANGES
775              
776             2024/11/10
777              
778             Allow filehandles in new()
779              
780              
781             2016/02/23 (Slaven Rezic)
782              
783             Make sure macro/variable names are always sane.
784              
785             More strict parsing of bits.
786              
787              
788             2000/11/09
789              
790             Added Jerrad Pierce's patch to allow load() to accept filehandles or strings;
791             will document in next release.
792              
793              
794             2000/05/05
795              
796             Added new_from_serialised() and serialise() methods.
797              
798              
799             2000/05/04
800              
801             Made xy() compatible with Image::Base, use xybit() for the earlier
802             functionality.
803              
804              
805             2000/05/01
806              
807             Improved speed of vec(), xy() and as_string().
808              
809             Tried use integer to improve speed but according to Benchmark it made the code
810             slower so I dropped it; interestingly perl 5.6.0 was around 25% slower than
811             perl 5.004 with and without use integer.
812              
813              
814             2000/04/30
815              
816             Created.
817              
818              
819             =head1 AUTHOR
820              
821             Mark Summerfield. I can be contacted as <summer@perlpress.com> -
822             please include the word 'xbm' in the subject line.
823              
824             =head1 COPYRIGHT
825              
826             Copyright (c) Mark Summerfield 2000. All Rights Reserved.
827              
828             This module may be used/distributed/modified under the LGPL.
829              
830             =cut
831