File Coverage

blib/lib/IO/SWF/JPEG.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package IO::SWF::JPEG;
2              
3 1     1   21548 use strict;
  1         2  
  1         35  
4 1     1   4 use warnings;
  1         2  
  1         25  
5              
6 1     1   5 use base 'Class::Accessor::Fast';
  1         1  
  1         770  
7              
8             use IO::SWF::Bit;
9             use Digest::MD5;
10              
11             __PACKAGE__->mk_accessors( qw(
12             _jpegdata
13             _jpegChunk
14             ));
15              
16             our %marker_name_table = (
17             0xD8 => 'SOI',
18             0xE0 => 'APP0', 0xE1 => 'APP1', 0xE2 => 'APP2', 0xE3 => 'APP3',
19             0xE4 => 'APP4', 0xE5 => 'APP5', 0xE6 => 'APP6', 0xE7 => 'APP7',
20             0xE8 => 'APP8', 0xE9 => 'APP9', 0xEA => 'APP10', 0xEB => 'APP11',
21             0xEC => 'APP12', 0xED => 'APP13', 0xEE => 'APP14', 0xEF => 'APP15',
22             0xFE => 'COM',
23             0xDB => 'DQT',
24             0xC0 => 'SOF0', 0xC1 => 'SOF1', 0xC2 => 'SOF2', 0xC3 => 'SOF3',
25             0xC5 => 'SOF5', 0xC6 => 'SOF6', 0xC7 => 'SOF7',
26             0xC8 => 'JPG', 0xC9 => 'SOF9', 0xCA => 'SOF10', 0xCB => 'SOF11',
27             0xCC => 'DAC', 0xCD => 'SOF13', 0xCE => 'SOF14', 0xCF => 'SOF15',
28             0xC4 => 'DHT',
29             0xDA => 'SOS',
30             0xD0 => 'RST0', 0xD1 => 'RST1', 0xD2 => 'RST2', 0xD3 => 'RST3',
31             0xD4 => 'RST4', 0xD5 => 'RST5', 0xD6 => 'RST6', 0xD7 => 'RST7',
32             0xDD => 'DRI',
33             0xD9 => 'EOI',
34             0xDC => 'DNL', 0xDE => 'DHP', 0xDF => 'EXP',
35             0xF0 => 'JPG0', 0xF1 => 'JPG1', 0xF2 => 'JPG2', 0xF3 => 'JPG3',
36             0xF4 => 'JPG4', 0xF5 => 'JPG5', 0xF6 => 'JPG6', 0xF7 => 'JPG7',
37             0xF8 => 'JPG8', 0xF9 => 'JPG9', 0xFA => 'JPG10', 0xFB => 'JPG11',
38             0xFC => 'JPG12', 0xFD => 'JPG13'
39             );
40              
41             sub new {
42             my ($class, $args) = @_;
43             my $self;
44             if(ref $args eq 'HASH') {
45             $self = $class->SUPER::new($args);
46             }else{
47             $self = $class->SUPER::new;
48             }
49             return $self;
50             }
51              
52             sub input {
53             my ($self, $jpegdata) = @_;
54             $self->_jpegdata($jpegdata);
55             }
56              
57             sub _splitChunk {
58             my $self = shift;
59             my $bitin = IO::SWF::Bit->new();
60             $bitin->input($self->_jpegdata);
61             my $marker1;
62             my @jpegChunk = ();
63             while ($marker1 = $bitin->getUI8()) {
64             if ($marker1 != 0xFF) {
65             printf STDERR "dumpChunk: marker1=0x%02X", $marker1;
66             return;
67             }
68             my $marker2 = $bitin->getUI8();
69             if ($marker2 == 0xD8) {
70             # 0xD8: // SOI (Start of Image)
71             push @jpegChunk, {'marker' => $marker2, 'data' => undef(), 'length' => undef()};
72             }
73             elsif ($marker2 == 0xD9) {
74             # 0xD9: // EOE (End of Image)
75             push @jpegChunk, {'marker' => $marker2, 'data' => undef(), 'length' => undef()};
76             last; # while break;
77             }
78             elsif ($marker2 == 0xDA || # SOS
79             $marker2 == 0xD0 || $marker2 == 0xD1 || $marker2 == 0xD2 || $marker2 == 0xD3 || # RST
80             $marker2 == 0xD4 || $marker2 == 0xD5 || $marker2 == 0xD6 || $marker2 == 0xD7 # RST
81             ) {
82             my ($chunk_data_offset, $dummy) = $bitin->getOffset();
83             while (1) {
84             my $next_marker1 = $bitin->getUI8();
85             if ($next_marker1 != 0xFF) {
86             next;
87             }
88             my $next_marker2 = $bitin->getUI8();
89             if ($next_marker2 == 0x00) {
90             next;
91             }
92            
93             $bitin->incrementOffset(-2, 0); # back from next marker
94             my ($next_chunk_offset, $dummy) = $bitin->getOffset();
95             my $length = $next_chunk_offset - $chunk_data_offset;
96             $bitin->setOffset($chunk_data_offset, 0);
97             push @jpegChunk, {'marker' => $marker2, 'data' => $bitin->getData($length), 'length' => undef()};
98             last;
99             }
100             }
101             else {
102             my $length = $bitin->getUI16BE();
103             push @jpegChunk, {'marker' => $marker2, 'data' => $bitin->getData($length - 2), 'length' => $length};
104             }
105             }
106             $self->_jpegChunk(\@jpegChunk);
107             }
108              
109             # from: SOI APP* DQT SOF* DHT SOS EOI
110             # to: SOI APP* SOF* SOS EOI
111             sub getImageData {
112             my $self = shift;
113             if (!$self->_jpegChunk || scalar(@{$self->_jpegChunk}) == 0) {
114             $self->_splitChunk();
115             }
116             my $bitout = IO::SWF::Bit->new();
117             foreach my $chunk (@{$self->_jpegChunk}) {
118             my $marker = $chunk->{'marker'};
119             if (($marker == 0xDB) || ($marker == 0xC4)) {
120             next; # skip DQT(0xDB) or DHT(0xC4)
121             }
122             $bitout->putUI8(0xFF);
123             $bitout->putUI8($marker);
124             if (!defined ($chunk->{'data'})) { # SOI or EOI
125             # nothing to do
126             } else {
127             if (defined ($chunk->{'length'})) {
128             $bitout->putUI16BE($chunk->{'length'});
129             }
130             $bitout->putData($chunk->{'data'});
131             }
132             }
133             return $bitout->output();
134             }
135              
136             # from: SOI APP* DQT SOF* DHT SOS EOI
137             # to: SOI DQT DHT EOI
138             sub getEncodingTables {
139             my $self = shift;
140             if (!$self->_jpegChunk || scalar(@{$self->_jpegChunk}) == 0) {
141             $self->_splitChunk();
142             }
143             my $bitout = IO::SWF::Bit->new();
144             $bitout->putUI8(0xFF);
145             $bitout->putUI8(0xD8); # SOI;
146             foreach my $chunk (@{$self->_jpegChunk}) {
147             my $marker = $chunk->{'marker'};
148             if (($marker != 0xDB) && ($marker != 0xC4)) {
149             next; # skip not ( DQT(0xDB) or DHT(0xC4) )
150             }
151             $bitout->putUI8(0xFF);
152             $bitout->putUI8($marker);
153             $bitout->putUI16BE($chunk->{'length'});
154             $bitout->putData($chunk->{'data'});
155             }
156             $bitout->putUI8(0xFF);
157             $bitout->putUI8(0xD9); # EOI;
158             return $bitout->output();
159             }
160              
161             sub dumpChunk { # for debug
162             my $self = shift;
163             if (!$self->_jpegChunk || scalar(@{$self->_jpegChunk}) == 0) {
164             $self->_splitChunk();
165             }
166             foreach my $chunk (@{$self->_jpegChunk}) {
167             my $marker = $chunk->{'marker'};
168             my $marker_name = $marker_name_table{$marker};
169             if (!defined ($chunk->{'data'})) {
170             print "$marker_name:\n";
171             } else {
172             my $length = length($chunk->{'data'});
173             my $md5 = Digest::MD5->new();
174             $md5->add($chunk->{'data'});
175             my $hash = $md5->hexdigest();
176             print "$marker_name: length=$length md5=$hash\n";
177             }
178             }
179             }
180              
181             1;