File Coverage

blib/lib/Image/TextMode/Reader/XBin.pm
Criterion Covered Total %
statement 54 83 65.0
branch 8 34 23.5
condition 1 3 33.3
subroutine 7 8 87.5
pod n/a
total 70 128 54.6


line stmt bran cond sub pod time code
1             package Image::TextMode::Reader::XBin;
2              
3 2     2   19010 use Moo;
  2         2  
  2         15  
4 2     2   585 use Carp 'croak';
  2         3  
  2         1959  
5              
6             extends 'Image::TextMode::Reader';
7              
8             # Header byte constants
9             my $PALETTE = 1;
10             my $FONT = 2;
11             my $COMPRESSED = 4;
12             my $NON_BLINK = 8;
13             my $FIVETWELVE_CHARS = 16;
14              
15             # Compression type constants
16             my $NO_COMPRESSION = 0;
17             my $CHARACTER_COMPRESSION = 64;
18             my $ATTRIBUTE_COMPRESSION = 128;
19             my $FULL_COMPRESSION = 192;
20              
21             # Compression byte constants
22             my $COMPRESSION_TYPE = 192;
23             my $COMPRESSION_COUNTER = 63;
24              
25             my $header_template = 'A4 C v v C C';
26             my $eof_char = chr( 26 );
27             my @header_fields = qw( id eofchar width height fontsize flags );
28              
29             sub _read {
30 3     3   7 my ( $self, $image, $fh, $options ) = @_;
31              
32 3         4 my $headerdata;
33 3         22 my $headerlength = read( $fh, $headerdata, 11 );
34              
35             # does it start with the right data?
36 3 50 33     52 croak 'Not an XBin file.'
37             unless $headerlength == 11 and $headerdata =~ m{^XBIN$eof_char}s;
38              
39             # parse header data
40 3         9 _read_header( $image, $headerdata );
41              
42 3 50       1200 if ( $image->header->{ flags } & $PALETTE ) {
43 3         23 my $paldata;
44 3         9 read( $fh, $paldata, 48 );
45 3         8 _parse_palette( $image, $paldata );
46             }
47              
48 3 50       4210 if ( $image->header->{ flags } & $FONT ) {
49 3         73 my $fontsize = $image->header->{ fontsize };
50             my $chars = $fontsize
51 3 50       54 * ( $image->header->{ flags } & $FIVETWELVE_CHARS ? 512 : 256 );
52 3         19 my $fontdata;
53 3         14 read( $fh, $fontdata, $chars );
54 3         9 _parse_font( $image, $fontdata );
55             }
56              
57 3 50       6209 if ( $image->header->{ flags } & $COMPRESSED ) {
58 0         0 _parse_compressed( $image, $fh );
59             }
60             else {
61 3         30 _parse_uncompressed( $image, $fh );
62             }
63              
64 3         45 return $image;
65             }
66              
67             sub _read_header {
68 3     3   7 my ( $image, $content ) = @_;
69              
70 3         4 my %header;
71 3         31 @header{ @header_fields } = unpack( $header_template, $content );
72              
73 3         28 $image->header( \%header );
74             }
75              
76             sub _parse_font {
77 3     3   4 my ( $image, $data ) = @_;
78 3         45 my $height = $image->header->{ fontsize };
79 3         14 my @chars;
80              
81 3         12 for ( 0 .. ( length( $data ) / $height ) - 1 ) {
82 768         2262 push @chars,
83             [ unpack( 'C*', substr( $data, $_ * $height, $height ) ) ];
84             }
85              
86             $image->font(
87 3         65 Image::TextMode::Font->new(
88             { width => 8,
89             height => $height,
90             chars => \@chars,
91             }
92             )
93             );
94             }
95              
96             sub _parse_palette {
97 3     3   4 my ( $image, $data ) = @_;
98              
99 3         21 my @values = unpack( 'C*', $data );
100 3         7 my @palette;
101              
102 3         20 for my $i ( 0 .. @values / 3 - 1 ) {
103 48         45 my $offset = $i * 3;
104 48         103 $palette[ $i ] = [
105             $values[ $offset ] << 2 | $values[ $offset ] >> 4,
106             $values[ $offset + 1 ] << 2 | $values[ $offset + 1 ] >> 4,
107             $values[ $offset + 2 ] << 2 | $values[ $offset + 2 ] >> 4,
108             ];
109             }
110              
111             $image->palette(
112 3         39 Image::TextMode::Palette->new( { colors => \@palette } ) );
113             }
114              
115             sub _parse_compressed {
116 0     0   0 my ( $image, $fh ) = @_;
117              
118 0         0 my $x = 0;
119 0         0 my $y = 0;
120 0         0 my $width = $image->header->{ width };
121 0         0 my $height = $image->header->{ height };
122 0         0 my $info;
123              
124 0         0 READ: while ( read( $fh, $info, 1 ) ) {
125 0         0 $info = unpack( 'C', $info );
126              
127 0         0 my $type = $info & $COMPRESSION_TYPE;
128 0         0 my $counter = ( $info & $COMPRESSION_COUNTER ) + 1;
129              
130 0         0 my ( $char, $attr );
131 0         0 while ( $counter-- ) {
132 0 0       0 if ( $type == $NO_COMPRESSION ) {
    0          
    0          
133 0         0 read( $fh, $char, 1 );
134 0         0 read( $fh, $attr, 1 );
135             }
136             elsif ( $type == $CHARACTER_COMPRESSION ) {
137 0 0       0 read( $fh, $char, 1 ) if !defined $char;
138 0         0 read( $fh, $attr, 1 );
139             }
140             elsif ( $type == $ATTRIBUTE_COMPRESSION ) {
141 0 0       0 read( $fh, $attr, 1 ) if !defined $attr;
142 0         0 read( $fh, $char, 1 );
143             }
144             else { # $FULL_COMPRESSION
145 0 0       0 read( $fh, $char, 1 ) if !defined $char;
146 0 0       0 read( $fh, $attr, 1 ) if !defined $attr;
147             }
148              
149 0         0 my $pchar = unpack( 'a', $char );
150 0 0       0 $image->putpixel(
151             { char => length $pchar ? $pchar : ' ',
152             attr => scalar unpack( 'C', $attr )
153             },
154             $x, $y,
155             );
156              
157 0         0 $x++;
158 0 0       0 if ( $x == $width ) {
159 0         0 $x = 0;
160 0         0 $y++;
161 0 0       0 last READ if $y == $height;
162             }
163             }
164             }
165             }
166              
167             sub _parse_uncompressed {
168 3     3   5 my ( $image, $fh ) = @_;
169              
170 3         7 my ( $x, $y ) = ( 0, 0 );
171 3         5 my $chardata;
172 3         46 my $width = $image->header->{ width };
173 3         53 my $height = $image->header->{ height };
174 3         24 while ( read( $fh, $chardata, 2 ) ) {
175 240         578 my @data = unpack( 'aC', $chardata );
176              
177 240         737 $image->putpixel( { char => $data[ 0 ], attr => $data[ 1 ] },
178             $x, $y, );
179              
180 240         8447 $x++;
181 240 100       835 if ( $x == $width ) {
182 3         4 $x = 0;
183 3         5 $y++;
184 3 50       15 last if $y == $height;
185             }
186             }
187             }
188              
189             =head1 NAME
190              
191             Image::TextMode::Reader::XBin - Reads XBin files
192              
193             =head1 DESCRIPTION
194              
195             Provides reading capabilities for the XBin format.
196              
197             =head1 AUTHOR
198              
199             Brian Cassidy Ebricas@cpan.orgE
200              
201             =head1 COPYRIGHT AND LICENSE
202              
203             Copyright 2008-2014 by Brian Cassidy
204              
205             This library is free software; you can redistribute it and/or modify
206             it under the same terms as Perl itself.
207              
208             =cut
209              
210             1;