File Coverage

blib/lib/Image/Xbm.pm
Criterion Covered Total %
statement 177 214 82.7
branch 64 116 55.1
condition 31 95 32.6
subroutine 17 22 77.2
pod 14 14 100.0
total 303 461 65.7


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