| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Image::PNG::Write::BW; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 15843 | use v5.10; | 
|  | 1 |  |  |  |  | 2 |  | 
| 4 | 1 |  |  | 1 |  | 4 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 19 |  | 
| 5 | 1 |  |  | 1 |  | 3 | use warnings FATAL => 'all'; | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 1 |  |  | 1 |  | 446 | use Digest::CRC; | 
|  | 1 |  |  |  |  | 1869 |  | 
|  | 1 |  |  |  |  | 63 |  | 
| 8 | 1 |  |  | 1 |  | 673 | use Compress::Raw::Zlib; | 
|  | 1 |  |  |  |  | 4177 |  | 
|  | 1 |  |  |  |  | 202 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 1 |  |  | 1 |  | 8 | use base 'Exporter'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 903 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | our @EXPORT_OK = qw( | 
| 13 |  |  |  |  |  |  | make_png_string | 
| 14 |  |  |  |  |  |  | make_png_bitstream_array | 
| 15 |  |  |  |  |  |  | make_png_bitstream_packed | 
| 16 |  |  |  |  |  |  | make_png_bitstream_raw | 
| 17 |  |  |  |  |  |  | ); | 
| 18 |  |  |  |  |  |  | our %EXPORT_TAGS = ( all => \@EXPORT_OK ); | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | # ABSTRACT: Create minimal black-and-white PNG files. | 
| 21 |  |  |  |  |  |  | our $VERSION = '0.01'; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub make_png_string($) { | 
| 25 | 1 |  |  | 1 | 1 | 332 | my ( $data ) = @_; | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 1 | 50 |  |  |  | 3 | die "cannot make 0-height png" if @$data == 0; | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 1 | 50 |  |  |  | 5 | my $deflate = Compress::Raw::Zlib::Deflate->new( -AppendOutput => 1 ) or die "failed to create Deflate module"; | 
| 30 | 1 |  |  |  |  | 276 | my $out; | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 1 |  |  |  |  | 1 | my $width = undef; | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 1 |  |  |  |  | 3 | foreach my $line ( @$data ) { | 
| 35 | 1 |  |  |  |  | 1 | my $lineCp = $line; # We actually need a copy; | 
| 36 | 1 | 50 |  |  |  | 3 | if ( ! defined $width ) { | 
| 37 | 1 |  |  |  |  | 1 | $width = length( $lineCp ); | 
| 38 | 1 | 50 |  |  |  | 3 | die "cannot make 0-width png" if $width == 0; | 
| 39 |  |  |  |  |  |  | } | 
| 40 | 1 | 50 |  |  |  | 2 | die "all lines must have same width" if $width != length( $lineCp ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 1 |  |  |  |  | 4 | $lineCp =~ s/\S/0/g; | 
| 43 | 1 |  |  |  |  | 2 | $lineCp =~ s/\s/1/g; | 
| 44 |  |  |  |  |  |  |  | 
| 45 | 1 | 50 |  |  |  | 9 | $deflate->deflate( pack("xB*",$lineCp) , $out ) == Z_OK or die "failed to deflate"; | 
| 46 |  |  |  |  |  |  | } | 
| 47 | 1 | 50 |  |  |  | 7 | $deflate->flush( $out, Z_FINISH ) == Z_OK or die "failed to finish"; | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 1 |  |  |  |  | 15 | return _make_png_raw_idat( $out, $width, scalar( @$data ) ); | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub make_png_bitstream_array($$) { | 
| 55 | 1 |  |  | 1 | 1 | 333 | my ( $data, $width ) = @_; | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 1 | 50 |  |  |  | 3 | die "cannot make 0-height png" if @$data == 0; | 
| 58 | 1 | 50 |  |  |  | 5 | die "cannot make 0-width png" if $width <= 0; | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 1 |  |  |  |  | 3 | my $width_bytes = int( ( $width + 7 ) / 8 ); | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 1 | 50 |  |  |  | 4 | my $deflate = Compress::Raw::Zlib::Deflate->new( -AppendOutput => 1 ) or die "failed to create Deflate module"; | 
| 63 | 1 |  |  |  |  | 289 | my $out; | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 1 |  |  |  |  | 3 | my $cBuf = "\0" . "\0" x $width_bytes; | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 1 |  |  |  |  | 4 | for ( my $i = 0; $i < @$data; ++$i ) { | 
| 68 | 1 | 50 |  |  |  | 4 | die "data has wrong number of bytes on row $i" unless $width_bytes == length( $data->[$i] ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 1 |  |  |  |  | 3 | substr( $cBuf, 1, $width_bytes ) = $data->[$i]; | 
| 71 | 1 | 50 |  |  |  | 6 | $deflate->deflate( $cBuf, $out ) == Z_OK or die "failed to deflate"; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 1 | 50 |  |  |  | 6 | $deflate->flush( $out, Z_FINISH ) == Z_OK or die "failed to finish"; | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 1 |  |  |  |  | 22 | return _make_png_raw_idat( $out, $width, scalar( @$data ) ); | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub make_png_bitstream_packed($$$) { | 
| 81 | 1 |  |  | 1 | 1 | 446 | my ( $data, $width, $height ) = ( \$_[0], $_[1], $_[2] ); | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 1 | 50 |  |  |  | 4 | die "cannot make 0-height png" if $height <= 0; | 
| 84 | 1 | 50 |  |  |  | 3 | die "cannot make 0-width png"  if $width <= 0; | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 1 |  |  |  |  | 3 | my $width_bytes = int( ( $width + 7 ) / 8 ); | 
| 87 | 1 | 50 |  |  |  | 3 | die "data has wrong number of bytes" unless $width_bytes*$height == length($$data); | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 1 | 50 |  |  |  | 5 | my $deflate = Compress::Raw::Zlib::Deflate->new( -AppendOutput => 1 ) or die "failed to create Deflate module"; | 
| 90 | 1 |  |  |  |  | 303 | my $out; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 1 |  |  |  |  | 4 | my $cBuf = "\0" . "\0" x $width_bytes; | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 1 |  |  |  |  | 3 | for ( my $i = 0; $i < $height; ++$i ) { | 
| 95 | 1 |  |  |  |  | 3 | substr( $cBuf, 1, $width_bytes ) = substr( $$data, $width_bytes * $i, $width_bytes ); | 
| 96 | 1 | 50 |  |  |  | 8 | $deflate->deflate( $cBuf, $out ) == Z_OK or die "failed to deflate"; | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 1 | 50 |  |  |  | 7 | $deflate->flush( $out, Z_FINISH ) == Z_OK or die "failed to finish"; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 1 |  |  |  |  | 15 | return _make_png_raw_idat( $out, $width, $height ); | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub make_png_bitstream_raw($$$) { | 
| 106 | 1 |  |  | 1 | 1 | 71913 | my ( $data, $width, $height ) = ( \$_[0], $_[1], $_[2] ); | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 1 | 50 |  |  |  | 4 | die "cannot make 0-height png" if $height <= 0; | 
| 109 | 1 | 50 |  |  |  | 3 | die "cannot make 0-width png"  if $width <= 0; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 1 |  |  |  |  | 4 | my $width_bytes = int( ( $width + 7 ) / 8 ) + 1; | 
| 112 | 1 | 50 |  |  |  | 3 | die "data has wrong number of bytes" unless $width_bytes*$height == length($$data); | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 1 | 50 |  |  |  | 11 | my $deflate = Compress::Raw::Zlib::Deflate->new( -AppendOutput => 1 ) or die "failed to create Deflate module"; | 
| 115 | 1 |  |  |  |  | 444 | my $out; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 1 | 50 |  |  |  | 4 | if ( length($$data) ) { | 
| 118 | 1 | 50 |  |  |  | 16 | $deflate->deflate( $$data, $out ) == Z_OK or die "failed to deflate"; | 
| 119 |  |  |  |  |  |  | } | 
| 120 | 1 | 50 |  |  |  | 28 | $deflate->flush( $out, Z_FINISH ) == Z_OK or die "failed to finish"; | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 1 |  |  |  |  | 43 | return _make_png_raw_idat( $out, $width, $height ); | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | # Internal method to make a PNG file from all parts ( including raw IDAT content ) | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | my $PNG_SIGNATURE = pack("C8",137,80,78,71,13,10,26,10); | 
| 129 |  |  |  |  |  |  | my $PNG_IEND      = _make_png_chunk( "IEND", "" ); | 
| 130 |  |  |  |  |  |  | sub _make_png_raw_idat($$$) { | 
| 131 | 4 |  |  | 4 |  | 5 | my ( $data, $width, $height ) = ( \$_[0], $_[1], $_[2] ); | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 4 |  |  |  |  | 18 | return join("", $PNG_SIGNATURE, | 
| 134 |  |  |  |  |  |  | _make_png_chunk( "IHDR", pack("NNCCCCC",$width,$height,1,0,0,0,0) ), | 
| 135 |  |  |  |  |  |  | _make_png_chunk( "IDAT", $$data ), | 
| 136 |  |  |  |  |  |  | $PNG_IEND); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # Internal method to make a PNG chunk | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | sub _make_png_chunk { | 
| 142 | 9 |  |  | 9 |  | 75 | my ($type,$data) = ( $_[0], \$_[1] ); | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 9 |  |  |  |  | 30 | my $ctx = Digest::CRC->new(type => "crc32"); | 
| 145 | 9 |  |  |  |  | 464 | $ctx->add( $type ); | 
| 146 | 9 |  |  |  |  | 52 | $ctx->add( $$data ); | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 9 |  |  |  |  | 50 | return join("", pack("N",length($$data)), $type, $$data, pack("N",$ctx->digest) ); | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | 1; # End of Image::PNG::Write::BW | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | __END__ |