Imported Upstream version 0.86+dfsg
gregor herrmann
12 years ago
0 | 0 | Imager release history. Older releases can be found in Changes.old |
1 | ||
2 | Imager 0.86 - 31 Oct 2011 | |
3 | =========== | |
4 | ||
5 | - improve error reporting for W32 tests | |
6 | ||
7 | Imager 0.85_02 - 24 Oct 2011 | |
8 | ============== | |
9 | ||
10 | Bug fixes: | |
11 | ||
12 | - eliminate unused i_gif_opts type (clean-up) | |
13 | https://rt.cpan.org/Ticket/Display.html?id=69245 | |
14 | ||
15 | - fix combine=0 fill color anti-aliasing on the double/sample path | |
16 | https://rt.cpan.org/Ticket/Display.html?id=71309 | |
17 | ||
18 | - make default text color non-transparent | |
19 | https://rt.cpan.org/Ticket/Display.html?id=71469 | |
20 | ||
21 | - apply the last of the Debian unforwarded spelling fixes | |
22 | https://rt.cpan.org/Ticket/Display.html?id=70656 | |
23 | ||
24 | - the log() method used its message parameter as a C level format | |
25 | string. | |
26 | https://rt.cpan.org/Ticket/Display.html?id=71653 | |
27 | ||
28 | - provide our own STRLEN typemap entry for older perls. | |
29 | https://rt.cpan.org/Ticket/Display.html?id=71641 | |
30 | ||
31 | - add extra ppport.h configuration to support older perls. | |
32 | ||
33 | - depend on Scalar::Util, since we use it and older perls don't have | |
34 | it. | |
35 | ||
36 | - add overloaded eq to Imager::Matrix2d, since older perls don't seem | |
37 | to synthesize it from overloaded "". | |
38 | ||
39 | - use T1_StrError() for error messages on modern libt1 | |
40 | https://rt.cpan.org/Ticket/Display.html?id=69879 | |
41 | ||
42 | - actually load the font rather than just adding it to the catalog on | |
43 | creation. | |
44 | ||
45 | - Imager::Font->new now produces better error messages for the T1 | |
46 | engine. | |
47 | ||
48 | - the font has_chars() method now returns perl's true and false | |
49 | values in list context rather than integers, which should be more | |
50 | efficient. | |
51 | https://rt.cpan.org/Ticket/Display.html?id=69158 | |
52 | ||
53 | - the btm data structure used by the flood_fill code is now | |
54 | initialized more efficiently. | |
55 | https://rt.cpan.org/Ticket/Display.html?id=68994 | |
56 | ||
57 | - updated the Thanks list in README | |
58 | https://rt.cpan.org/Ticket/Display.html?id=71607 | |
59 | ||
60 | - check there's at least one coefficient for the convolution filter | |
61 | https://rt.cpan.org/Ticket/Display.html?id=68993 | |
62 | ||
63 | - make the APIRef synopsis ordering consistent, older versions of | |
64 | perl could order it differently. | |
65 | https://rt.cpan.org/Ticket/Display.html?id=71675 | |
66 | ||
67 | - we rely on Config.pm's d_vsnprintf as to whether we use | |
68 | vsnprintf/snprintf, which is defined in the Win32 Config.pm even | |
69 | though it only has _ prefixed versions of these. Define our own | |
70 | prefixed names on Win32. | |
71 | https://rt.cpan.org/Ticket/Display.html?id=71642 | |
72 | ||
73 | - fix library detection with MSVC | |
74 | ||
75 | - search a few more library directories, so EU::MM doesn't discard | |
76 | them. Hopefully fixes: | |
77 | https://rt.cpan.org/Ticket/Display.html?id=71643 | |
78 | ||
79 | Imager 0.85_01 - 10 Oct 2011 | |
80 | ============== | |
81 | ||
82 | - add simple tests for the Imager::Test test_image generators | |
83 | ||
84 | - io_glue I/O buffering re-work: | |
85 | ||
86 | - reorganize io_glue to do it's own buffering by default | |
87 | ||
88 | - the unbuffered functions are available as i_io_raw_read() (or | |
89 | raw_read() from perl) etc but are not recommended for typical | |
90 | use. | |
91 | ||
92 | - use the new i_io_peekn() when checking for file magic to avoid | |
93 | seek, allowing Imager to detect the file type and read the file | |
94 | from an unseekable stream (for formats that don't use random | |
95 | access) | |
96 | ||
97 | - added several new I/O layer API functions. | |
98 | ||
99 | - fix the TGA performance problem, most noticably on Win32 | |
100 | https://rt.cpan.org/Ticket/Display.html?id=70037 | |
101 | ||
102 | - TIFF now uses wrapper functions of the correct types to avoid casts | |
103 | https://rt.cpan.org/Ticket/Display.html?id=69912 | |
104 | ||
105 | - the callback IO object did its own buffering, controlled by the | |
106 | maxbuffer parameter supplied to the read() and write() methods. | |
107 | This buffering has been removed, to avoid redundancy with the | |
108 | common io_glue buffering. This also avoids a bug in that code | |
109 | which could rarely pass a zero length to the read callback and | |
110 | then panic about the result. | |
111 | ||
112 | - the callback IO object now tests the result of calling the close | |
113 | callback, which should return true for success. | |
114 | ||
115 | - the PNM reader did its own buffering. This buffering has been | |
116 | removed to avoid redundancy with the common io_glue buffering. | |
117 | ||
118 | - previously the read handlers for fd and callback I/O layers would | |
119 | call the underlying interface (POSIX read or your supplied callback) | |
120 | until it filled the buffer. It now only makes one call. | |
121 | ||
122 | - added public constructors for I/O layer objects (see Imager::IO) | |
123 | ||
124 | - all core file handlers now use the i_io_foo() wrappers to gain | |
125 | access to buffered I/O rather than calling the I/O layer | |
126 | callbacks directly. | |
127 | ||
128 | - all core file handlers now check for error on close. | |
129 | ||
130 | - Backward compatibility: if you hava custom file handlers, you can | |
131 | use i_io_write() etc since they're available as macros in older | |
132 | versions of Imager. | |
133 | ||
134 | - eliminate the final remnants of io_glue_commit_types(). | |
135 | ||
136 | - bump IMAGER_API_VERSION, since the above may break assumptions. | |
137 | ||
138 | - removed the long unused i_gen_reader() and i_gen_writer() utility | |
139 | functions. | |
1 | 140 | |
2 | 141 | Imager 0.85 - 29 Aug 2011 |
3 | 142 | =========== |
4 | 4 | @ISA = qw(Imager::Font); |
5 | 5 | |
6 | 6 | BEGIN { |
7 | $VERSION = "0.82"; | |
7 | $VERSION = "0.83"; | |
8 | 8 | |
9 | 9 | eval { |
10 | 10 | require XSLoader; |
21 | 21 | |
22 | 22 | sub new { |
23 | 23 | my $class = shift; |
24 | my %hsh=(color=>Imager::Color->new(255,0,0,0), | |
24 | my %hsh=(color=>Imager::Color->new(255,0,0,255), | |
25 | 25 | size=>15, |
26 | 26 | @_); |
27 | 27 |
227 | 227 | if (GIMME_V == G_ARRAY) { |
228 | 228 | EXTEND(SP, count); |
229 | 229 | for (i = 0; i < count; ++i) { |
230 | PUSHs(sv_2mortal(newSViv(work[i]))); | |
230 | PUSHs(boolSV(work[i])); | |
231 | 231 | } |
232 | 232 | } |
233 | 233 | else { |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 189; | |
2 | use Test::More tests => 193; | |
3 | 3 | use Cwd qw(getcwd abs_path); |
4 | 4 | |
5 | 5 | use Imager qw(:all); |
6 | 6 | |
7 | use Imager::Test qw(diff_text_with_nul is_color3); | |
7 | use Imager::Test qw(diff_text_with_nul is_color3 is_color4 isnt_image); | |
8 | 8 | |
9 | 9 | -d "testout" or mkdir "testout"; |
10 | 10 | |
20 | 20 | |
21 | 21 | my $fontname=$ENV{'TTFONTTEST'} || $deffont; |
22 | 22 | |
23 | -f $fontname or skip("cannot find fontfile $fontname", 188); | |
23 | -f $fontname or skip("cannot find fontfile $fontname", 189); | |
24 | 24 | |
25 | 25 | |
26 | 26 | my $bgcolor=i_color_new(255,0,0,0); |
514 | 514 | ok($font, "found font by drive relative path") |
515 | 515 | or print "# path $drive_path\n"; |
516 | 516 | } |
517 | ||
517 | { # RT 71469 | |
518 | my $font1 = Imager::Font->new(file => $deffont, type => "ft2", index => 0); | |
519 | my $font2 = Imager::Font::FT2->new(file => $deffont, index => 0); | |
520 | ||
521 | for my $font ($font1, $font2) { | |
522 | print "# ", join(",", $font->{color}->rgba), "\n"; | |
523 | ||
524 | my $im = Imager->new(xsize => 20, ysize => 20, channels => 4); | |
525 | ||
526 | ok($im->string(text => "T", font => $font, y => 15), | |
527 | "draw with default color") | |
528 | or print "# ", $im->errstr, "\n"; | |
529 | my $work = Imager->new(xsize => 20, ysize => 20); | |
530 | my $cmp = $work->copy; | |
531 | $work->rubthrough(src => $im); | |
532 | isnt_image($work, $cmp, "make sure something was drawn"); | |
533 | } | |
534 | } | |
518 | 535 | } |
519 | 536 | |
520 | 537 | sub align_test { |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.81"; | |
6 | $VERSION = "0.82"; | |
7 | 7 | |
8 | 8 | eval { |
9 | 9 | require XSLoader; |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.85" ); | |
40 | my @Imager_req = ( Imager => "0.86" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
851 | 851 | io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) { |
852 | 852 | io_glue *ig = (io_glue *)gft->UserData; |
853 | 853 | |
854 | return ig->readcb(ig, buf, length); | |
854 | return i_io_read(ig, buf, length); | |
855 | 855 | } |
856 | 856 | |
857 | 857 | i_img * |
1771 | 1771 | io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) { |
1772 | 1772 | io_glue *ig = (io_glue *)gft->UserData; |
1773 | 1773 | |
1774 | return ig->writecb(ig, data, length); | |
1774 | return i_io_write(ig, data, length); | |
1775 | 1775 | } |
1776 | 1776 | |
1777 | 1777 | |
1799 | 1799 | |
1800 | 1800 | result = i_writegif_low(quant, GifFile, imgs, count); |
1801 | 1801 | |
1802 | ig->closecb(ig); | |
1802 | if (i_io_close(ig)) | |
1803 | return 0; | |
1803 | 1804 | |
1804 | 1805 | return result; |
1805 | 1806 | } |
13 | 13 | $|=1; |
14 | 14 | use Test::More; |
15 | 15 | use Imager qw(:all); |
16 | use Imager::Test qw(is_color3 test_image test_image_raw); | |
16 | use Imager::Test qw(is_color3 test_image test_image_raw test_image_mono); | |
17 | 17 | use Imager::File::GIF; |
18 | 18 | |
19 | 19 | use Carp 'confess'; |
23 | 23 | |
24 | 24 | init_log("testout/t105gif.log",1); |
25 | 25 | |
26 | plan tests => 144; | |
26 | plan tests => 146; | |
27 | 27 | |
28 | 28 | my $green=i_color_new(0,255,0,255); |
29 | 29 | my $blue=i_color_new(0,0,255,255); |
736 | 736 | my ($im) = Imager->read_multi(file => "testimg/zerocomm.gif"); |
737 | 737 | ok($im, "read image with zero-length extension"); |
738 | 738 | } |
739 | ||
740 | ||
741 | { # check close failures are handled correctly | |
742 | my $im = test_image_mono(); | |
743 | my $fail_close = sub { | |
744 | Imager::i_push_error(0, "synthetic close failure"); | |
745 | return 0; | |
746 | }; | |
747 | ok(!$im->write(type => "gif", callback => sub { 1 }, | |
748 | closecb => $fail_close), | |
749 | "check failing close fails"); | |
750 | like($im->errstr, qr/synthetic close failure/, | |
751 | "check error message"); | |
752 | } | |
753 | ||
739 | 754 | |
740 | 755 | sub test_readgif_cb { |
741 | 756 | my ($size) = @_; |
0 | 0 | #ifndef IMAGER_MSICON_H_ |
1 | 1 | #define IMAGER_MSICON_H_ |
2 | 2 | |
3 | #include "iolayer.h" | |
3 | #include "iolayert.h" | |
4 | 4 | |
5 | 5 | typedef struct ico_reader_tag ico_reader_t; |
6 | 6 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 100; | |
3 | use Imager::Test qw(is_image); | |
2 | use Test::More tests => 102; | |
3 | use Imager::Test qw(is_image test_image); | |
4 | 4 | |
5 | 5 | BEGIN { use_ok('Imager::File::ICO'); } |
6 | 6 | |
370 | 370 | $vs->box(filled => 1, color => '#333366'); |
371 | 371 | is_image($im, $vs, "check we got the right colors"); |
372 | 372 | } |
373 | ||
374 | ||
375 | { # check close failures are handled correctly | |
376 | my $im = test_image(); | |
377 | my $fail_close = sub { | |
378 | Imager::i_push_error(0, "synthetic close failure"); | |
379 | return 0; | |
380 | }; | |
381 | ok(!$im->write(type => "ico", callback => sub { 1 }, | |
382 | closecb => $fail_close), | |
383 | "check failing close fails"); | |
384 | like($im->errstr, qr/synthetic close failure/, | |
385 | "check error message"); | |
386 | } |
76 | 76 | # low level write failure tests for each entry point (fail on write) |
77 | 77 | { |
78 | 78 | my $im = Imager->new(xsize => 10, ysize => 10); |
79 | ok(!$im->write(callback => \&write_failure, maxbuffer => 1, type=>'ico'), | |
79 | my $io = Imager::io_new_cb(\&write_failure, undef, undef, undef, 1); | |
80 | $io->set_buffered(0); | |
81 | ok(!$im->write(io => $io, type=>'ico'), | |
80 | 82 | "low level write failure (ico)"); |
81 | 83 | is($im->errstr, "Write failure: synthetic error", "check message"); |
82 | 84 | } |
83 | 85 | |
84 | 86 | { |
85 | 87 | my $im = Imager->new(xsize => 10, ysize => 10); |
86 | ok(!$im->write(callback => \&write_failure, maxbuffer => 1, type=>'cur'), | |
88 | my $io = Imager::io_new_cb(\&write_failure, undef, undef, undef, 1); | |
89 | $io->set_buffered(0); | |
90 | ok(!$im->write(io => $io, type=>'cur'), | |
87 | 91 | "low level write failure (cur)"); |
88 | 92 | is($im->errstr, "Write failure: synthetic error", "check message"); |
89 | 93 | } |
90 | 94 | |
91 | 95 | { |
92 | 96 | my $im = Imager->new(xsize => 10, ysize => 10); |
93 | ok(!Imager->write_multi({ callback => \&write_failure, maxbuffer => 1, type=>'ico' }, $im, $im), | |
97 | my $io = Imager::io_new_cb(\&write_failure, undef, undef, undef, 1); | |
98 | $io->set_buffered(0); | |
99 | ok(!Imager->write_multi({ io => $io, type=>'ico' }, $im, $im), | |
94 | 100 | "low level write_multi failure (ico)"); |
95 | 101 | is(Imager->errstr, "Write failure: synthetic error", "check message"); |
96 | 102 | Imager->_set_error(''); |
98 | 104 | |
99 | 105 | { |
100 | 106 | my $im = Imager->new(xsize => 10, ysize => 10); |
101 | ok(!Imager->write_multi({ callback => \&write_failure, maxbuffer => 1, type=>'cur' }, $im, $im), | |
107 | my $io = Imager::io_new_cb(\&write_failure, undef, undef, undef, 1); | |
108 | $io->set_buffered(0); | |
109 | ok(!Imager->write_multi({ io => $io, type=>'cur' }, $im, $im), | |
102 | 110 | "low level write_multi failure (cur)"); |
103 | 111 | is(Imager->errstr, "Write failure: synthetic error", "check message"); |
104 | 112 | Imager->_set_error(''); |
106 | 114 | |
107 | 115 | { |
108 | 116 | my $im = Imager->new(xsize => 10, ysize => 10); |
109 | ok(!$im->write(type => 'ico', callback => limited_write(6), maxbuffer => 1), | |
117 | ok(!$im->write(type => 'ico', io => limited_write_io(6)), | |
110 | 118 | "second write (resource) should fail (ico)"); |
111 | 119 | is($im->errstr, "Write failure: limit reached", "check message"); |
112 | 120 | $im->_set_error(''); |
113 | 121 | |
114 | ok(!$im->write(type => 'cur', callback => limited_write(6), maxbuffer => 1), | |
122 | ok(!$im->write(type => 'cur', io => limited_write_io(6)), | |
115 | 123 | "second (resource) write should fail (cur)"); |
116 | 124 | is($im->errstr, "Write failure: limit reached", "check message"); |
117 | 125 | $im->_set_error(''); |
118 | 126 | |
119 | ok(!$im->write(type => 'ico', callback => limited_write(22), maxbuffer => 1), | |
127 | ok(!$im->write(type => 'ico', io => limited_write_io(22)), | |
120 | 128 | "third write (bmi) should fail (32-bit)"); |
121 | 129 | is($im->errstr, "Write failure: limit reached", "check message"); |
122 | 130 | $im->_set_error(''); |
123 | 131 | |
124 | ok(!$im->write(type => 'ico', callback => limited_write(62), maxbuffer => 1), | |
132 | ok(!$im->write(type => 'ico', io => limited_write_io(62)), | |
125 | 133 | "fourth write (data) should fail (32-bit)"); |
126 | 134 | is($im->errstr, "Write failure: limit reached", "check message"); |
127 | 135 | $im->_set_error(''); |
128 | 136 | |
129 | ok(!$im->write(type => 'ico', callback => limited_write(462), maxbuffer => 1), | |
137 | ok(!$im->write(type => 'ico', io => limited_write_io(462)), | |
130 | 138 | "mask write should fail (32-bit)"); |
131 | 139 | is($im->errstr, "Write failure: limit reached", "check message"); |
132 | 140 | } |
138 | 146 | $im->addcolors(colors => [ $red, $blue ]); |
139 | 147 | $im->box(filled => 1, color => $red, ymax => 5); |
140 | 148 | $im->box(filled => 1, color => $blue, ymin => 6); |
141 | ok(!$im->write(type => 'ico', callback => limited_write(22), maxbuffer => 1), | |
149 | ok(!$im->write(type => 'ico', io => limited_write_io(22)), | |
142 | 150 | "third write (bmi) should fail (1-bit)"); |
143 | 151 | is($im->errstr, "Write failure: limit reached", "check message"); |
144 | 152 | |
145 | ok(!$im->write(type => 'ico', callback => limited_write(66), maxbuffer => 1), | |
153 | ok(!$im->write(type => 'ico', io => limited_write_io(66)), | |
146 | 154 | "fourth write (palette) should fail (1-bit)"); |
147 | 155 | is($im->errstr, "Write failure: limit reached", "check message"); |
148 | ok(!$im->write(type => 'ico', callback => limited_write(74), maxbuffer => 1), | |
156 | ok(!$im->write(type => 'ico', io => limited_write_io(74)), | |
149 | 157 | "fifth write (image) should fail (1-bit)"); |
150 | 158 | is($im->errstr, "Write failure: limit reached", "check message"); |
151 | 159 | my $data; |
164 | 172 | $im->addcolors(colors => [ ($red, $blue) x 8 ]); |
165 | 173 | $im->box(filled => 1, color => $red, ymax => 5); |
166 | 174 | $im->box(filled => 1, color => $blue, ymin => 6); |
167 | ok(!$im->write(type => 'ico', callback => limited_write(22), maxbuffer => 1), | |
175 | ok(!$im->write(type => 'ico', io => limited_write_io(22)), | |
168 | 176 | "third write (bmi) should fail (4-bit)"); |
169 | 177 | is($im->errstr, "Write failure: limit reached", "check message"); |
170 | 178 | |
171 | ok(!$im->write(type => 'ico', callback => limited_write(66), maxbuffer => 1), | |
179 | ok(!$im->write(type => 'ico', io => limited_write_io(66)), | |
172 | 180 | "fourth write (palette) should fail (4-bit)"); |
173 | 181 | is($im->errstr, "Write failure: limit reached", "check message"); |
174 | ok(!$im->write(type => 'ico', callback => limited_write(130), maxbuffer => 1), | |
182 | ok(!$im->write(type => 'ico', io => limited_write_io(130)), | |
175 | 183 | "fifth write (image) should fail (4-bit)"); |
176 | 184 | is($im->errstr, "Write failure: limit reached", "check message"); |
177 | 185 | my $data; |
190 | 198 | $im->addcolors(colors => [ ($red, $blue) x 9 ]); |
191 | 199 | $im->box(filled => 1, color => $red, ymax => 5); |
192 | 200 | $im->box(filled => 1, color => $blue, ymin => 6); |
193 | ok(!$im->write(type => 'ico', callback => limited_write(22), maxbuffer => 1), | |
201 | ok(!$im->write(type => 'ico', io => limited_write_io(22)), | |
194 | 202 | "third write (bmi) should fail (8-bit)"); |
195 | 203 | is($im->errstr, "Write failure: limit reached", "check message"); |
196 | 204 | |
197 | ok(!$im->write(type => 'ico', callback => limited_write(62), maxbuffer => 1), | |
205 | ok(!$im->write(type => 'ico', io => limited_write_io(62)), | |
198 | 206 | "fourth write (palette) should fail (8-bit)"); |
199 | 207 | is($im->errstr, "Write failure: limit reached", "check message"); |
200 | ok(!$im->write(type => 'ico', callback => limited_write(62 + 1024), maxbuffer => 1), | |
208 | ok(!$im->write(type => 'ico', io => limited_write_io(62 + 1024)), | |
201 | 209 | "fifth write (image) should fail (8-bit)"); |
202 | 210 | is($im->errstr, "Write failure: limit reached", "check message"); |
203 | ok(!$im->write(type => 'ico', callback => limited_write(62 + 1024 + 10), maxbuffer => 1), | |
211 | ok(!$im->write(type => 'ico', io => limited_write_io(62 + 1024 + 10)), | |
204 | 212 | "sixth write (zeroes) should fail (8-bit)"); |
205 | 213 | is($im->errstr, "Write failure: limit reached", "check message"); |
206 | 214 | my $data; |
217 | 225 | print "# synthesized write failure\n"; |
218 | 226 | Imager::i_push_error(0, "synthetic error"); |
219 | 227 | return; |
228 | } | |
229 | ||
230 | sub limited_write_io { | |
231 | my ($limit) = @_; | |
232 | ||
233 | my $io = Imager::io_new_cb(limited_write($limit), undef, undef, undef, 1); | |
234 | $io->set_buffered(0); | |
235 | ||
236 | return $io; | |
220 | 237 | } |
221 | 238 | |
222 | 239 | sub limited_write { |
147 | 147 | if ($ex_version < 5.57) { |
148 | 148 | @ISA = qw(Exporter); |
149 | 149 | } |
150 | $VERSION = '0.85'; | |
150 | $VERSION = '0.86'; | |
151 | 151 | eval { |
152 | 152 | require XSLoader; |
153 | 153 | XSLoader::load(Imager => $VERSION); |
1337 | 1337 | sub _get_writer_io { |
1338 | 1338 | my ($self, $input) = @_; |
1339 | 1339 | |
1340 | my $buffered = exists $input->{buffered} ? $input->{buffered} : 1; | |
1341 | ||
1342 | my $io; | |
1343 | my @extras; | |
1340 | 1344 | if ($input->{io}) { |
1341 | return $input->{io}; | |
1345 | $io = $input->{io}; | |
1342 | 1346 | } |
1343 | 1347 | elsif ($input->{fd}) { |
1344 | return io_new_fd($input->{fd}); | |
1348 | $io = io_new_fd($input->{fd}); | |
1345 | 1349 | } |
1346 | 1350 | elsif ($input->{fh}) { |
1347 | 1351 | my $fd = fileno($input->{fh}); |
1354 | 1358 | # flush anything that's buffered, and make sure anything else is flushed |
1355 | 1359 | $| = 1; |
1356 | 1360 | select($oldfh); |
1357 | return io_new_fd($fd); | |
1361 | $io = io_new_fd($fd); | |
1358 | 1362 | } |
1359 | 1363 | elsif ($input->{file}) { |
1360 | 1364 | my $fh = new IO::File($input->{file},"w+"); |
1363 | 1367 | return; |
1364 | 1368 | } |
1365 | 1369 | binmode($fh) or die; |
1366 | return (io_new_fd(fileno($fh)), $fh); | |
1370 | $io = io_new_fd(fileno($fh)); | |
1371 | push @extras, $fh; | |
1367 | 1372 | } |
1368 | 1373 | elsif ($input->{data}) { |
1369 | return io_new_bufchain(); | |
1374 | $io = io_new_bufchain(); | |
1370 | 1375 | } |
1371 | 1376 | elsif ($input->{callback} || $input->{writecb}) { |
1372 | if ($input->{maxbuffer}) { | |
1373 | return io_new_cb($input->{callback} || $input->{writecb}, | |
1374 | $input->{readcb}, | |
1375 | $input->{seekcb}, $input->{closecb}, | |
1376 | $input->{maxbuffer}); | |
1377 | } | |
1378 | else { | |
1379 | return io_new_cb($input->{callback} || $input->{writecb}, | |
1380 | $input->{readcb}, | |
1381 | $input->{seekcb}, $input->{closecb}); | |
1382 | } | |
1377 | if ($input->{maxbuffer} && $input->{maxbuffer} == 1) { | |
1378 | $buffered = 0; | |
1379 | } | |
1380 | $io = io_new_cb($input->{callback} || $input->{writecb}, | |
1381 | $input->{readcb}, | |
1382 | $input->{seekcb}, $input->{closecb}); | |
1383 | 1383 | } |
1384 | 1384 | else { |
1385 | 1385 | $self->_set_error("file/fd/fh/data/callback parameter missing"); |
1386 | 1386 | return; |
1387 | 1387 | } |
1388 | ||
1389 | unless ($buffered) { | |
1390 | $io->set_buffered(0); | |
1391 | } | |
1392 | ||
1393 | return ($io, @extras); | |
1388 | 1394 | } |
1389 | 1395 | |
1390 | 1396 | # Read an image from file |
4344 | 4350 | compose() - L<Imager::Transformations/compose()> - compose one image |
4345 | 4351 | over another. |
4346 | 4352 | |
4347 | convert() - L<Imager::Transformations/"Color transformations"> - | |
4348 | transform the color space | |
4353 | convert() - L<Imager::Transformations/convert()> - transform the color | |
4354 | space | |
4349 | 4355 | |
4350 | 4356 | copy() - L<Imager::Transformations/copy()> - make a duplicate of an |
4351 | 4357 | image |
4357 | 4363 | |
4358 | 4364 | deltag() - L<Imager::ImageTypes/deltag()> - delete image tags |
4359 | 4365 | |
4360 | difference() - L<Imager::Filters/"Image Difference"> - produce a | |
4361 | difference images from two input images. | |
4362 | ||
4363 | errstr() - L</"Basic Overview"> - the error from the last failed | |
4364 | operation. | |
4365 | ||
4366 | filter() - L<Imager::Filters> - image filtering | |
4366 | difference() - L<Imager::Filters/difference()> - produce a difference | |
4367 | images from two input images. | |
4368 | ||
4369 | errstr() - L</errstr()> - the error from the last failed operation. | |
4370 | ||
4371 | filter() - L<Imager::Filters/filter()> - image filtering | |
4367 | 4372 | |
4368 | 4373 | findcolor() - L<Imager::ImageTypes/findcolor()> - search the image |
4369 | 4374 | palette, if it has one |
4466 | 4471 | |
4467 | 4472 | preload() - L<Imager::Files/preload()> |
4468 | 4473 | |
4469 | read() - L<Imager::Files> - read a single image from an image file | |
4470 | ||
4471 | read_multi() - L<Imager::Files> - read multiple images from an image | |
4474 | read() - L<Imager::Files/read()> - read a single image from an image file | |
4475 | ||
4476 | read_multi() - L<Imager::Files/read_multi()> - read multiple images from an image | |
4472 | 4477 | file |
4473 | 4478 | |
4474 | 4479 | read_types() - L<Imager::Files/read_types()> - list image types Imager |
4532 | 4537 | virtual() - L<Imager::ImageTypes/virtual()> - whether the image has it's own |
4533 | 4538 | data |
4534 | 4539 | |
4535 | write() - L<Imager::Files> - write an image to a file | |
4536 | ||
4537 | write_multi() - L<Imager::Files> - write multiple image to an image | |
4540 | write() - L<Imager::Files/write()> - write an image to a file | |
4541 | ||
4542 | write_multi() - L<Imager::Files/write_multi()> - write multiple image to an image | |
4538 | 4543 | file. |
4539 | 4544 | |
4540 | 4545 | write_types() - L<Imager::Files/read_types()> - list image types Imager |
4782 | 4787 | |
4783 | 4788 | =head2 Patches |
4784 | 4789 | |
4785 | I accept patches, preferably against the main branch in subversion. | |
4786 | You should include an explanation of the reason for why the patch is | |
4787 | needed or useful. | |
4790 | I accept patches, preferably against the master branch in git. Please | |
4791 | include an explanation of the reason for why the patch is needed or | |
4792 | useful. | |
4788 | 4793 | |
4789 | 4794 | Your patch should include regression tests where possible, otherwise |
4790 | 4795 | it will be delayed until I get a chance to write them. |
4796 | ||
4797 | To browse Imager's git repository: | |
4798 | ||
4799 | http://git.imager.perl.org/imager.git | |
4800 | ||
4801 | or: | |
4802 | ||
4803 | https://github.com/tonycoz/imager | |
4804 | ||
4805 | To clone: | |
4806 | ||
4807 | git clone git://git.imager.perl.org/imager.git | |
4808 | ||
4809 | or: | |
4810 | ||
4811 | git clone git://github.com/tonycoz/imager.git | |
4791 | 4812 | |
4792 | 4813 | =head1 AUTHOR |
4793 | 4814 |
6 | 6 | #include "XSUB.h" |
7 | 7 | #define NEED_newRV_noinc |
8 | 8 | #define NEED_sv_2pv_nolen |
9 | #define NEED_sv_2pvbyte | |
9 | 10 | #include "ppport.h" |
10 | 11 | #ifdef __cplusplus |
11 | 12 | } |
130 | 131 | |
131 | 132 | static void |
132 | 133 | i_log_entry(char *string, int level) { |
133 | mm_log((level, string)); | |
134 | mm_log((level, "%s", string)); | |
134 | 135 | } |
135 | 136 | |
136 | 137 | |
142 | 143 | SV *readcb; |
143 | 144 | SV *seekcb; |
144 | 145 | SV *closecb; |
145 | ||
146 | /* we need to remember whether the buffer contains write data or | |
147 | read data | |
148 | */ | |
149 | int reading; | |
150 | int writing; | |
151 | ||
152 | /* how far we've read into the buffer (not used for writing) */ | |
153 | int where; | |
154 | ||
155 | /* the amount of space used/data available in the buffer */ | |
156 | int used; | |
157 | ||
158 | /* the maximum amount to fill the buffer before flushing | |
159 | If any write is larger than this then the buffer is flushed and | |
160 | the full write is performed. The write is _not_ split into | |
161 | maxwrite sized calls | |
162 | */ | |
163 | int maxlength; | |
164 | ||
165 | char buffer[CBDATA_BUFSIZE]; | |
166 | 146 | }; |
167 | 147 | |
168 | /* | |
169 | ||
170 | call_writer(cbd, buf, size) | |
171 | ||
172 | Low-level function to call the perl writer callback. | |
173 | ||
174 | */ | |
175 | ||
176 | static ssize_t call_writer(struct cbdata *cbd, void const *buf, size_t size) { | |
177 | dTHX; | |
178 | int count; | |
179 | int success; | |
180 | SV *sv; | |
181 | dSP; | |
182 | ||
183 | if (!SvOK(cbd->writecb)) | |
184 | return -1; | |
185 | ||
186 | ENTER; | |
187 | SAVETMPS; | |
188 | EXTEND(SP, 1); | |
189 | PUSHMARK(SP); | |
190 | PUSHs(sv_2mortal(newSVpv((char *)buf, size))); | |
191 | PUTBACK; | |
192 | ||
193 | count = perl_call_sv(cbd->writecb, G_SCALAR); | |
194 | ||
195 | SPAGAIN; | |
196 | if (count != 1) | |
197 | croak("Result of perl_call_sv(..., G_SCALAR) != 1"); | |
198 | ||
199 | sv = POPs; | |
200 | success = SvTRUE(sv); | |
201 | ||
202 | ||
203 | PUTBACK; | |
204 | FREETMPS; | |
205 | LEAVE; | |
206 | ||
207 | return success ? size : -1; | |
208 | } | |
209 | ||
210 | static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size, | |
211 | size_t maxread) { | |
148 | static ssize_t | |
149 | call_reader(struct cbdata *cbd, void *buf, size_t size, | |
150 | size_t maxread) { | |
212 | 151 | dTHX; |
213 | 152 | int count; |
214 | 153 | int result; |
237 | 176 | |
238 | 177 | if (SvOK(data)) { |
239 | 178 | STRLEN len; |
240 | char *ptr = SvPV(data, len); | |
179 | char *ptr = SvPVbyte(data, len); | |
241 | 180 | if (len > maxread) |
242 | croak("Too much data returned in reader callback"); | |
181 | croak("Too much data returned in reader callback (wanted %d, got %d, expected %d)", | |
182 | (int)size, (int)len, (int)maxread); | |
243 | 183 | |
244 | 184 | memcpy(buf, ptr, len); |
245 | 185 | result = len; |
255 | 195 | return result; |
256 | 196 | } |
257 | 197 | |
258 | static ssize_t write_flush(struct cbdata *cbd) { | |
259 | dTHX; | |
260 | ssize_t result; | |
261 | ||
262 | if (cbd->used) { | |
263 | result = call_writer(cbd, cbd->buffer, cbd->used); | |
264 | cbd->used = 0; | |
265 | return result; | |
266 | } | |
267 | else { | |
268 | return 1; /* success of some sort */ | |
269 | } | |
270 | } | |
271 | ||
272 | static off_t io_seeker(void *p, off_t offset, int whence) { | |
198 | static off_t | |
199 | io_seeker(void *p, off_t offset, int whence) { | |
273 | 200 | dTHX; |
274 | 201 | struct cbdata *cbd = p; |
275 | 202 | int count; |
279 | 206 | if (!SvOK(cbd->seekcb)) |
280 | 207 | return -1; |
281 | 208 | |
282 | if (cbd->writing) { | |
283 | if (cbd->used && write_flush(cbd) <= 0) | |
284 | return -1; | |
285 | cbd->writing = 0; | |
286 | } | |
287 | if (whence == SEEK_CUR && cbd->reading && cbd->where != cbd->used) { | |
288 | offset -= cbd->where - cbd->used; | |
289 | } | |
290 | cbd->reading = 0; | |
291 | cbd->where = cbd->used = 0; | |
292 | ||
293 | 209 | ENTER; |
294 | 210 | SAVETMPS; |
295 | 211 | EXTEND(SP, 2); |
314 | 230 | return result; |
315 | 231 | } |
316 | 232 | |
317 | static ssize_t io_writer(void *p, void const *data, size_t size) { | |
233 | static ssize_t | |
234 | io_writer(void *p, void const *data, size_t size) { | |
318 | 235 | dTHX; |
319 | 236 | struct cbdata *cbd = p; |
320 | ||
321 | /* printf("io_writer(%p, %p, %u)\n", p, data, size); */ | |
322 | if (!cbd->writing) { | |
323 | if (cbd->reading && cbd->where < cbd->used) { | |
324 | /* we read past the place where the caller expected us to be | |
325 | so adjust our position a bit */ | |
326 | if (io_seeker(p, cbd->where - cbd->used, SEEK_CUR) < 0) { | |
327 | return -1; | |
328 | } | |
329 | cbd->reading = 0; | |
330 | } | |
331 | cbd->where = cbd->used = 0; | |
332 | } | |
333 | cbd->writing = 1; | |
334 | if (cbd->used && cbd->used + size > cbd->maxlength) { | |
335 | int write_res = write_flush(cbd); | |
336 | if (write_res <= 0) { | |
337 | return write_res; | |
338 | } | |
339 | cbd->used = 0; | |
340 | } | |
341 | if (cbd->used+size <= cbd->maxlength) { | |
342 | memcpy(cbd->buffer + cbd->used, data, size); | |
343 | cbd->used += size; | |
344 | return size; | |
345 | } | |
346 | /* it doesn't fit - just pass it up */ | |
347 | return call_writer(cbd, data, size); | |
237 | I32 count; | |
238 | SV *sv; | |
239 | dSP; | |
240 | bool success; | |
241 | ||
242 | if (!SvOK(cbd->writecb)) | |
243 | return -1; | |
244 | ||
245 | ENTER; | |
246 | SAVETMPS; | |
247 | EXTEND(SP, 1); | |
248 | PUSHMARK(SP); | |
249 | PUSHs(sv_2mortal(newSVpv((char *)data, size))); | |
250 | PUTBACK; | |
251 | ||
252 | count = perl_call_sv(cbd->writecb, G_SCALAR); | |
253 | ||
254 | SPAGAIN; | |
255 | if (count != 1) | |
256 | croak("Result of perl_call_sv(..., G_SCALAR) != 1"); | |
257 | ||
258 | sv = POPs; | |
259 | success = SvTRUE(sv); | |
260 | ||
261 | ||
262 | PUTBACK; | |
263 | FREETMPS; | |
264 | LEAVE; | |
265 | ||
266 | return success ? size : -1; | |
348 | 267 | } |
349 | 268 | |
350 | 269 | static ssize_t |
351 | 270 | io_reader(void *p, void *data, size_t size) { |
352 | dTHX; | |
353 | 271 | struct cbdata *cbd = p; |
354 | ssize_t total; | |
355 | char *out = data; /* so we can do pointer arithmetic */ | |
356 | ||
357 | /* printf("io_reader(%p, %p, %d)\n", p, data, size); */ | |
358 | if (cbd->writing) { | |
359 | if (write_flush(cbd) <= 0) | |
360 | return 0; | |
361 | cbd->writing = 0; | |
362 | } | |
363 | ||
364 | cbd->reading = 1; | |
365 | if (size <= cbd->used - cbd->where) { | |
366 | /* simplest case */ | |
367 | memcpy(data, cbd->buffer+cbd->where, size); | |
368 | cbd->where += size; | |
369 | return size; | |
370 | } | |
371 | total = 0; | |
372 | memcpy(out, cbd->buffer + cbd->where, cbd->used - cbd->where); | |
373 | total += cbd->used - cbd->where; | |
374 | size -= cbd->used - cbd->where; | |
375 | out += cbd->used - cbd->where; | |
376 | if (size < sizeof(cbd->buffer)) { | |
377 | int did_read = 0; | |
378 | int copy_size; | |
379 | while (size | |
380 | && (did_read = call_reader(cbd, cbd->buffer, size, | |
381 | sizeof(cbd->buffer))) > 0) { | |
382 | cbd->where = 0; | |
383 | cbd->used = did_read; | |
384 | ||
385 | copy_size = i_min(size, cbd->used); | |
386 | memcpy(out, cbd->buffer, copy_size); | |
387 | cbd->where += copy_size; | |
388 | out += copy_size; | |
389 | total += copy_size; | |
390 | size -= copy_size; | |
391 | } | |
392 | if (did_read < 0) | |
393 | return -1; | |
394 | } | |
395 | else { | |
396 | /* just read the rest - too big for our buffer*/ | |
397 | int did_read; | |
398 | while ((did_read = call_reader(cbd, out, size, size)) > 0) { | |
399 | size -= did_read; | |
400 | total += did_read; | |
401 | out += did_read; | |
402 | } | |
403 | if (did_read < 0) | |
404 | return -1; | |
405 | } | |
406 | ||
407 | return total; | |
272 | ||
273 | return call_reader(cbd, data, size, size); | |
408 | 274 | } |
409 | 275 | |
410 | 276 | static int io_closer(void *p) { |
411 | 277 | dTHX; |
412 | 278 | struct cbdata *cbd = p; |
413 | ||
414 | if (cbd->writing && cbd->used > 0) { | |
415 | if (write_flush(cbd) < 0) | |
416 | return -1; | |
417 | cbd->writing = 0; | |
418 | } | |
279 | int success = 1; | |
419 | 280 | |
420 | 281 | if (SvOK(cbd->closecb)) { |
421 | 282 | dSP; |
283 | I32 count; | |
284 | SV *sv; | |
422 | 285 | |
423 | 286 | ENTER; |
424 | 287 | SAVETMPS; |
425 | 288 | PUSHMARK(SP); |
426 | 289 | PUTBACK; |
427 | 290 | |
428 | perl_call_sv(cbd->closecb, G_VOID); | |
291 | count = perl_call_sv(cbd->closecb, G_SCALAR); | |
429 | 292 | |
430 | 293 | SPAGAIN; |
294 | ||
295 | sv = POPs; | |
296 | success = SvTRUE(sv); | |
297 | ||
431 | 298 | PUTBACK; |
432 | 299 | FREETMPS; |
433 | 300 | LEAVE; |
434 | 301 | } |
435 | 302 | |
436 | return 0; | |
303 | return success ? 0 : -1; | |
437 | 304 | } |
438 | 305 | |
439 | 306 | static void io_destroyer(void *p) { |
445 | 312 | SvREFCNT_dec(cbd->seekcb); |
446 | 313 | SvREFCNT_dec(cbd->closecb); |
447 | 314 | myfree(cbd); |
315 | } | |
316 | ||
317 | static i_io_glue_t * | |
318 | do_io_new_buffer(pTHX_ SV *data_sv) { | |
319 | const char *data; | |
320 | STRLEN length; | |
321 | ||
322 | data = SvPVbyte(data_sv, length); | |
323 | SvREFCNT_inc(data_sv); | |
324 | return io_new_buffer(data, length, my_SvREFCNT_dec, data_sv); | |
325 | } | |
326 | ||
327 | static i_io_glue_t * | |
328 | do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) { | |
329 | struct cbdata *cbd; | |
330 | ||
331 | cbd = mymalloc(sizeof(struct cbdata)); | |
332 | cbd->writecb = newSVsv(writecb); | |
333 | cbd->readcb = newSVsv(readcb); | |
334 | cbd->seekcb = newSVsv(seekcb); | |
335 | cbd->closecb = newSVsv(closecb); | |
336 | ||
337 | return io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, | |
338 | io_destroyer); | |
448 | 339 | } |
449 | 340 | |
450 | 341 | struct value_name { |
896 | 787 | |
897 | 788 | #define i_img_epsilonf() (DBL_EPSILON * 4) |
898 | 789 | |
790 | /* avoid some xsubpp strangeness */ | |
791 | #define NEWLINE '\n' | |
792 | ||
899 | 793 | MODULE = Imager PACKAGE = Imager::Color PREFIX = ICL_ |
900 | 794 | |
901 | 795 | Imager::Color |
1047 | 941 | |
1048 | 942 | |
1049 | 943 | Imager::IO |
1050 | io_new_buffer(data) | |
1051 | char *data | |
1052 | PREINIT: | |
1053 | size_t length; | |
944 | io_new_buffer(data_sv) | |
945 | SV *data_sv | |
1054 | 946 | CODE: |
1055 | SvPV(ST(0), length); | |
1056 | SvREFCNT_inc(ST(0)); | |
1057 | RETVAL = io_new_buffer(data, length, my_SvREFCNT_dec, ST(0)); | |
947 | RETVAL = do_io_new_buffer(aTHX_ data_sv); | |
1058 | 948 | OUTPUT: |
1059 | 949 | RETVAL |
1060 | 950 | |
1065 | 955 | SV *seekcb; |
1066 | 956 | SV *closecb; |
1067 | 957 | int maxwrite; |
1068 | PREINIT: | |
1069 | struct cbdata *cbd; | |
1070 | 958 | CODE: |
1071 | cbd = mymalloc(sizeof(struct cbdata)); | |
1072 | SvREFCNT_inc(writecb); | |
1073 | cbd->writecb = writecb; | |
1074 | SvREFCNT_inc(readcb); | |
1075 | cbd->readcb = readcb; | |
1076 | SvREFCNT_inc(seekcb); | |
1077 | cbd->seekcb = seekcb; | |
1078 | SvREFCNT_inc(closecb); | |
1079 | cbd->closecb = closecb; | |
1080 | cbd->reading = cbd->writing = cbd->where = cbd->used = 0; | |
1081 | if (maxwrite > CBDATA_BUFSIZE) | |
1082 | maxwrite = CBDATA_BUFSIZE; | |
1083 | cbd->maxlength = maxwrite; | |
1084 | RETVAL = io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, | |
1085 | io_destroyer); | |
959 | RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb); | |
1086 | 960 | OUTPUT: |
1087 | 961 | RETVAL |
1088 | 962 | |
1089 | void | |
963 | SV * | |
1090 | 964 | io_slurp(ig) |
1091 | 965 | Imager::IO ig |
1092 | 966 | PREINIT: |
1093 | 967 | unsigned char* data; |
1094 | 968 | size_t tlength; |
1095 | PPCODE: | |
969 | CODE: | |
1096 | 970 | data = NULL; |
1097 | 971 | tlength = io_slurp(ig, &data); |
1098 | EXTEND(SP,1); | |
1099 | PUSHs(sv_2mortal(newSVpv((char *)data,tlength))); | |
972 | RETVAL = newSVpv((char *)data,tlength); | |
1100 | 973 | myfree(data); |
974 | OUTPUT: | |
975 | RETVAL | |
1101 | 976 | |
1102 | 977 | |
1103 | 978 | undef_int |
1119 | 994 | PUSHs(sv_2mortal(newSVuv(bytes))); |
1120 | 995 | } |
1121 | 996 | |
997 | MODULE = Imager PACKAGE = Imager::IO PREFIX = io_ | |
998 | ||
999 | Imager::IO | |
1000 | io_new_fd(class, fd) | |
1001 | int fd | |
1002 | CODE: | |
1003 | RETVAL = io_new_fd(fd); | |
1004 | OUTPUT: | |
1005 | RETVAL | |
1006 | ||
1007 | Imager::IO | |
1008 | io_new_buffer(class, data_sv) | |
1009 | SV *data_sv | |
1010 | CODE: | |
1011 | RETVAL = do_io_new_buffer(aTHX_ data_sv); | |
1012 | OUTPUT: | |
1013 | RETVAL | |
1014 | ||
1015 | Imager::IO | |
1016 | io_new_cb(class, writecb, readcb, seekcb, closecb) | |
1017 | SV *writecb; | |
1018 | SV *readcb; | |
1019 | SV *seekcb; | |
1020 | SV *closecb; | |
1021 | CODE: | |
1022 | RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb); | |
1023 | OUTPUT: | |
1024 | RETVAL | |
1025 | ||
1026 | Imager::IO | |
1027 | io_new_bufchain(class) | |
1028 | CODE: | |
1029 | RETVAL = io_new_bufchain(); | |
1030 | OUTPUT: | |
1031 | RETVAL | |
1032 | ||
1033 | SV * | |
1034 | io_slurp(class, ig) | |
1035 | Imager::IO ig | |
1036 | PREINIT: | |
1037 | unsigned char* data; | |
1038 | size_t tlength; | |
1039 | CODE: | |
1040 | data = NULL; | |
1041 | tlength = io_slurp(ig, &data); | |
1042 | RETVAL = newSVpv((char *)data,tlength); | |
1043 | myfree(data); | |
1044 | OUTPUT: | |
1045 | RETVAL | |
1046 | ||
1122 | 1047 | MODULE = Imager PACKAGE = Imager::IO PREFIX = i_io_ |
1123 | 1048 | |
1124 | int | |
1125 | i_io_write(ig, data_sv) | |
1049 | IV | |
1050 | i_io_raw_write(ig, data_sv) | |
1126 | 1051 | Imager::IO ig |
1127 | 1052 | SV *data_sv |
1128 | 1053 | PREINIT: |
1137 | 1062 | } |
1138 | 1063 | #endif |
1139 | 1064 | data = SvPV(data_sv, size); |
1140 | RETVAL = i_io_write(ig, data, size); | |
1065 | RETVAL = i_io_raw_write(ig, data, size); | |
1141 | 1066 | OUTPUT: |
1142 | 1067 | RETVAL |
1068 | ||
1069 | void | |
1070 | i_io_raw_read(ig, buffer_sv, size) | |
1071 | Imager::IO ig | |
1072 | SV *buffer_sv | |
1073 | IV size | |
1074 | PREINIT: | |
1075 | void *buffer; | |
1076 | ssize_t result; | |
1077 | PPCODE: | |
1078 | if (size <= 0) | |
1079 | croak("size negative in call to i_io_raw_read()"); | |
1080 | /* prevent an undefined value warning if they supplied an | |
1081 | undef buffer. | |
1082 | Orginally conditional on !SvOK(), but this will prevent the | |
1083 | downgrade from croaking */ | |
1084 | sv_setpvn(buffer_sv, "", 0); | |
1085 | #ifdef SvUTF8 | |
1086 | if (SvUTF8(buffer_sv)) | |
1087 | sv_utf8_downgrade(buffer_sv, FALSE); | |
1088 | #endif | |
1089 | buffer = SvGROW(buffer_sv, size+1); | |
1090 | result = i_io_raw_read(ig, buffer, size); | |
1091 | if (result >= 0) { | |
1092 | SvCUR_set(buffer_sv, result); | |
1093 | *SvEND(buffer_sv) = '\0'; | |
1094 | SvPOK_only(buffer_sv); | |
1095 | EXTEND(SP, 1); | |
1096 | PUSHs(sv_2mortal(newSViv(result))); | |
1097 | } | |
1098 | ST(1) = buffer_sv; | |
1099 | SvSETMAGIC(ST(1)); | |
1100 | ||
1101 | void | |
1102 | i_io_raw_read2(ig, size) | |
1103 | Imager::IO ig | |
1104 | IV size | |
1105 | PREINIT: | |
1106 | SV *buffer_sv; | |
1107 | void *buffer; | |
1108 | ssize_t result; | |
1109 | PPCODE: | |
1110 | if (size <= 0) | |
1111 | croak("size negative in call to i_io_read2()"); | |
1112 | buffer_sv = newSV(size); | |
1113 | buffer = SvGROW(buffer_sv, size+1); | |
1114 | result = i_io_raw_read(ig, buffer, size); | |
1115 | if (result >= 0) { | |
1116 | SvCUR_set(buffer_sv, result); | |
1117 | *SvEND(buffer_sv) = '\0'; | |
1118 | SvPOK_only(buffer_sv); | |
1119 | EXTEND(SP, 1); | |
1120 | PUSHs(sv_2mortal(buffer_sv)); | |
1121 | } | |
1122 | else { | |
1123 | /* discard it */ | |
1124 | SvREFCNT_dec(buffer_sv); | |
1125 | } | |
1126 | ||
1127 | off_t | |
1128 | i_io_raw_seek(ig, position, whence) | |
1129 | Imager::IO ig | |
1130 | off_t position | |
1131 | int whence | |
1132 | ||
1133 | int | |
1134 | i_io_raw_close(ig) | |
1135 | Imager::IO ig | |
1136 | ||
1137 | void | |
1138 | i_io_DESTROY(ig) | |
1139 | Imager::IO ig | |
1140 | ||
1141 | int | |
1142 | i_io_CLONE_SKIP(...) | |
1143 | CODE: | |
1144 | (void)items; /* avoid unused warning for XS variable */ | |
1145 | RETVAL = 1; | |
1146 | OUTPUT: | |
1147 | RETVAL | |
1148 | ||
1149 | int | |
1150 | i_io_getc(ig) | |
1151 | Imager::IO ig | |
1152 | ||
1153 | int | |
1154 | i_io_putc(ig, c) | |
1155 | Imager::IO ig | |
1156 | int c | |
1157 | ||
1158 | int | |
1159 | i_io_close(ig) | |
1160 | Imager::IO ig | |
1161 | ||
1162 | int | |
1163 | i_io_flush(ig) | |
1164 | Imager::IO ig | |
1165 | ||
1166 | int | |
1167 | i_io_peekc(ig) | |
1168 | Imager::IO ig | |
1169 | ||
1170 | int | |
1171 | i_io_seek(ig, off, whence) | |
1172 | Imager::IO ig | |
1173 | off_t off | |
1174 | int whence | |
1175 | ||
1176 | void | |
1177 | i_io_peekn(ig, size) | |
1178 | Imager::IO ig | |
1179 | STRLEN size | |
1180 | PREINIT: | |
1181 | SV *buffer_sv; | |
1182 | void *buffer; | |
1183 | ssize_t result; | |
1184 | PPCODE: | |
1185 | buffer_sv = newSV(size+1); | |
1186 | buffer = SvGROW(buffer_sv, size+1); | |
1187 | result = i_io_peekn(ig, buffer, size); | |
1188 | if (result >= 0) { | |
1189 | SvCUR_set(buffer_sv, result); | |
1190 | *SvEND(buffer_sv) = '\0'; | |
1191 | SvPOK_only(buffer_sv); | |
1192 | EXTEND(SP, 1); | |
1193 | PUSHs(sv_2mortal(buffer_sv)); | |
1194 | } | |
1195 | else { | |
1196 | /* discard it */ | |
1197 | SvREFCNT_dec(buffer_sv); | |
1198 | } | |
1143 | 1199 | |
1144 | 1200 | void |
1145 | 1201 | i_io_read(ig, buffer_sv, size) |
1176 | 1232 | void |
1177 | 1233 | i_io_read2(ig, size) |
1178 | 1234 | Imager::IO ig |
1179 | IV size | |
1235 | STRLEN size | |
1180 | 1236 | PREINIT: |
1181 | 1237 | SV *buffer_sv; |
1182 | 1238 | void *buffer; |
1183 | 1239 | ssize_t result; |
1184 | 1240 | PPCODE: |
1185 | if (size <= 0) | |
1186 | croak("size negative in call to i_io_read2()"); | |
1241 | if (size == 0) | |
1242 | croak("size zero in call to read2()"); | |
1187 | 1243 | buffer_sv = newSV(size); |
1188 | 1244 | buffer = SvGROW(buffer_sv, size+1); |
1189 | 1245 | result = i_io_read(ig, buffer, size); |
1190 | if (result >= 0) { | |
1246 | if (result > 0) { | |
1191 | 1247 | SvCUR_set(buffer_sv, result); |
1192 | 1248 | *SvEND(buffer_sv) = '\0'; |
1193 | 1249 | SvPOK_only(buffer_sv); |
1199 | 1255 | SvREFCNT_dec(buffer_sv); |
1200 | 1256 | } |
1201 | 1257 | |
1202 | off_t | |
1203 | i_io_seek(ig, position, whence) | |
1258 | void | |
1259 | i_io_gets(ig, size = 8192, eol = NEWLINE) | |
1204 | 1260 | Imager::IO ig |
1205 | off_t position | |
1206 | int whence | |
1207 | ||
1208 | int | |
1209 | i_io_close(ig) | |
1261 | STRLEN size | |
1262 | int eol | |
1263 | PREINIT: | |
1264 | SV *buffer_sv; | |
1265 | void *buffer; | |
1266 | ssize_t result; | |
1267 | PPCODE: | |
1268 | if (size < 2) | |
1269 | croak("size too small in call to gets()"); | |
1270 | buffer_sv = sv_2mortal(newSV(size+1)); | |
1271 | buffer = SvPVX(buffer_sv); | |
1272 | result = i_io_gets(ig, buffer, size+1, eol); | |
1273 | if (result > 0) { | |
1274 | SvCUR_set(buffer_sv, result); | |
1275 | *SvEND(buffer_sv) = '\0'; | |
1276 | SvPOK_only(buffer_sv); | |
1277 | EXTEND(SP, 1); | |
1278 | PUSHs(buffer_sv); | |
1279 | } | |
1280 | ||
1281 | IV | |
1282 | i_io_write(ig, data_sv) | |
1210 | 1283 | Imager::IO ig |
1211 | ||
1212 | void | |
1213 | i_io_DESTROY(ig) | |
1214 | Imager::IO ig | |
1215 | ||
1216 | int | |
1217 | i_io_CLONE_SKIP(...) | |
1218 | CODE: | |
1219 | (void)items; /* avoid unused warning for XS variable */ | |
1220 | RETVAL = 1; | |
1221 | OUTPUT: | |
1284 | SV *data_sv | |
1285 | PREINIT: | |
1286 | void *data; | |
1287 | STRLEN size; | |
1288 | CODE: | |
1289 | #ifdef SvUTF8 | |
1290 | if (SvUTF8(data_sv)) { | |
1291 | data_sv = sv_2mortal(newSVsv(data_sv)); | |
1292 | /* yes, we want this to croak() if the SV can't be downgraded */ | |
1293 | sv_utf8_downgrade(data_sv, FALSE); | |
1294 | } | |
1295 | #endif | |
1296 | data = SvPV(data_sv, size); | |
1297 | RETVAL = i_io_write(ig, data, size); | |
1298 | OUTPUT: | |
1222 | 1299 | RETVAL |
1300 | ||
1301 | void | |
1302 | i_io_dump(ig, flags = I_IO_DUMP_DEFAULT) | |
1303 | Imager::IO ig | |
1304 | int flags | |
1305 | ||
1306 | bool | |
1307 | i_io_set_buffered(ig, flag = 1) | |
1308 | Imager::IO ig | |
1309 | int flag | |
1310 | ||
1311 | bool | |
1312 | i_io_is_buffered(ig) | |
1313 | Imager::IO ig | |
1314 | ||
1315 | bool | |
1316 | i_io_eof(ig) | |
1317 | Imager::IO ig | |
1318 | ||
1319 | bool | |
1320 | i_io_error(ig) | |
1321 | Imager::IO ig | |
1223 | 1322 | |
1224 | 1323 | MODULE = Imager PACKAGE = Imager |
1225 | 1324 | |
2109 | 2208 | if (GIMME_V == G_ARRAY) { |
2110 | 2209 | EXTEND(SP, count); |
2111 | 2210 | for (i = 0; i < count; ++i) { |
2112 | PUSHs(sv_2mortal(newSViv(work[i]))); | |
2211 | PUSHs(boolSV(work[i])); | |
2113 | 2212 | } |
2114 | 2213 | } |
2115 | 2214 | else { |
2311 | 2410 | XSRETURN(col_cnt); |
2312 | 2411 | |
2313 | 2412 | |
2314 | Imager::ImgRaw | |
2413 | void | |
2315 | 2414 | i_transform(im,opx,opy,parm) |
2316 | 2415 | Imager::ImgRaw im |
2317 | 2416 | PREINIT: |
2324 | 2423 | AV* av; |
2325 | 2424 | SV* sv1; |
2326 | 2425 | int i; |
2327 | CODE: | |
2426 | i_img *result; | |
2427 | PPCODE: | |
2328 | 2428 | if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n"); |
2329 | 2429 | if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to an array\n"); |
2330 | 2430 | if (!SvROK(ST(3))) croak("Imager: Parameter 3 must be a reference to an array\n"); |
2352 | 2452 | sv1=(*(av_fetch(av,i,0))); |
2353 | 2453 | parm[i]=(double)SvNV(sv1); |
2354 | 2454 | } |
2355 | RETVAL=i_transform(im,opx,opxl,opy,opyl,parm,parmlen); | |
2455 | result=i_transform(im,opx,opxl,opy,opyl,parm,parmlen); | |
2356 | 2456 | myfree(parm); |
2357 | 2457 | myfree(opy); |
2358 | 2458 | myfree(opx); |
2359 | ST(0) = sv_newmortal(); | |
2360 | if (RETVAL == 0) ST(0)=&PL_sv_undef; | |
2361 | else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL); | |
2362 | ||
2363 | Imager::ImgRaw | |
2459 | if (result) { | |
2460 | SV *result_sv = sv_newmortal(); | |
2461 | EXTEND(SP, 1); | |
2462 | sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result); | |
2463 | PUSHs(result_sv); | |
2464 | } | |
2465 | ||
2466 | void | |
2364 | 2467 | i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs) |
2365 | 2468 | SV *sv_width |
2366 | 2469 | SV *sv_height |
2384 | 2487 | SV *sv1; |
2385 | 2488 | IV tmp; |
2386 | 2489 | int i; |
2387 | CODE: | |
2490 | i_img *result; | |
2491 | PPCODE: | |
2388 | 2492 | |
2389 | 2493 | in_imgs_count = av_len(av_in_imgs)+1; |
2390 | 2494 | for (i = 0; i < in_imgs_count; ++i) { |
2439 | 2543 | c_regs = mymalloc(c_regs_count * sizeof(i_color)); |
2440 | 2544 | /* I don't bother initializing the colou?r registers */ |
2441 | 2545 | |
2442 | RETVAL=i_transform2(width, height, channels, ops, ops_count, | |
2546 | result=i_transform2(width, height, channels, ops, ops_count, | |
2443 | 2547 | n_regs, n_regs_count, |
2444 | 2548 | c_regs, c_regs_count, in_imgs, in_imgs_count); |
2445 | 2549 | if (in_imgs) |
2446 | 2550 | myfree(in_imgs); |
2447 | 2551 | myfree(n_regs); |
2448 | 2552 | myfree(c_regs); |
2449 | ST(0) = sv_newmortal(); | |
2450 | if (RETVAL == 0) ST(0)=&PL_sv_undef; | |
2451 | else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL); | |
2553 | if (result) { | |
2554 | SV *result_sv = sv_newmortal(); | |
2555 | EXTEND(SP, 1); | |
2556 | sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result); | |
2557 | PUSHs(result_sv); | |
2558 | } | |
2452 | 2559 | |
2453 | 2560 | |
2454 | 2561 | void |
3779 | 3886 | |
3780 | 3887 | BOOT: |
3781 | 3888 | PERL_SET_GLOBAL_CALLBACKS; |
3782 | PERL_PL_SET_GLOBAL_CALLBACKS;⏎ | |
3889 | PERL_PL_SET_GLOBAL_CALLBACKS; |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.81"; | |
6 | $VERSION = "0.82"; | |
7 | 7 | |
8 | 8 | eval { |
9 | 9 | require XSLoader; |
19 | 19 | Imager->register_reader |
20 | 20 | ( |
21 | 21 | type=>'jpeg', |
22 | single => | |
22 | single => | |
23 | 23 | sub { |
24 | 24 | my ($im, $io, %hsh) = @_; |
25 | 25 |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.85" ); | |
40 | my @Imager_req = ( Imager => "0.86" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
104 | 104 | |
105 | 105 | mm_log((1,"wiol_fill_input_buffer(cinfo %p)\n", cinfo)); |
106 | 106 | |
107 | nbytes = src->data->readcb(src->data, src->buffer, JPGS); | |
107 | nbytes = i_io_read(src->data, src->buffer, JPGS); | |
108 | 108 | |
109 | 109 | if (nbytes <= 0) { /* Insert a fake EOI marker */ |
110 | 110 | src->pub.next_input_byte = fake_eoi; |
218 | 218 | */ |
219 | 219 | |
220 | 220 | mm_log((1,"wiol_empty_output_buffer(cinfo %p)\n", cinfo)); |
221 | rc = dest->data->writecb(dest->data, dest->buffer, JPGS); | |
221 | rc = i_io_write(dest->data, dest->buffer, JPGS); | |
222 | 222 | |
223 | 223 | if (rc != JPGS) { /* XXX: Should raise some jpeg error */ |
224 | 224 | myfree(dest->buffer); |
237 | 237 | /* yes, this needs to flush the buffer */ |
238 | 238 | /* needs error handling */ |
239 | 239 | |
240 | if (dest->data->writecb(dest->data, dest->buffer, nbytes) != nbytes) { | |
240 | if (i_io_write(dest->data, dest->buffer, nbytes) != nbytes) { | |
241 | 241 | myfree(dest->buffer); |
242 | 242 | ERREXIT(cinfo, JERR_FILE_WRITE); |
243 | 243 | } |
244 | ||
244 | ||
245 | 245 | if (dest != NULL) myfree(dest->buffer); |
246 | 246 | } |
247 | 247 | |
720 | 720 | |
721 | 721 | jpeg_destroy_compress(&cinfo); |
722 | 722 | |
723 | ig->closecb(ig); | |
723 | if (i_io_close(ig)) | |
724 | return 0; | |
724 | 725 | |
725 | 726 | return(1); |
726 | 727 | } |
10 | 10 | $Imager::formats{"jpeg"} |
11 | 11 | or plan skip_all => "no jpeg support"; |
12 | 12 | |
13 | plan tests => 101; | |
13 | plan tests => 103; | |
14 | 14 | |
15 | 15 | my $green=i_color_new(0,255,0,255); |
16 | 16 | my $blue=i_color_new(0,0,255,255); |
54 | 54 | # write failure test |
55 | 55 | open FH, "< testout/t101.jpg" or die "Cannot open testout/t101.jpg: $!"; |
56 | 56 | binmode FH; |
57 | ok(!$imoo->write(fd=>fileno(FH), type=>'jpeg'), 'failure handling'); | |
57 | my $io = Imager::io_new_fd(fileno(FH)); | |
58 | $io->set_buffered(0); | |
59 | ok(!$imoo->write(io => $io, type=>'jpeg'), 'failure handling'); | |
58 | 60 | close FH; |
59 | 61 | print "# ",$imoo->errstr,"\n"; |
60 | 62 | |
323 | 325 | } |
324 | 326 | my $data; |
325 | 327 | ok($im->write(data => \$data, type=>'jpeg', jpegquality => 100), |
326 | "write big file to ensure wiol_empty_output_buffer is called"); | |
328 | "write big file to ensure wiol_empty_output_buffer is called") | |
329 | or print "# ", $im->errstr, "\n"; | |
327 | 330 | |
328 | 331 | # code coverage - write failure path in wiol_empty_output_buffer |
329 | 332 | ok(!$im->write(callback => sub { return }, |
431 | 434 | |
432 | 435 | is_image($rdprog, $norm, "prog vs norm should be the same image"); |
433 | 436 | } |
437 | ||
438 | { # check close failures are handled correctly | |
439 | my $im = test_image(); | |
440 | my $fail_close = sub { | |
441 | Imager::i_push_error(0, "synthetic close failure"); | |
442 | return 0; | |
443 | }; | |
444 | ok(!$im->write(type => "jpeg", callback => sub { 1 }, | |
445 | closecb => $fail_close), | |
446 | "check failing close fails"); | |
447 | like($im->errstr, qr/synthetic close failure/, | |
448 | "check error message"); | |
449 | } |
0 | 0 | --- #YAML:1.0 |
1 | 1 | name: Imager |
2 | version: 0.85 | |
2 | version: 0.86 | |
3 | 3 | abstract: Perl extension for Generating 24 bit Images |
4 | 4 | author: |
5 | 5 | - Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson |
10 | 10 | build_requires: |
11 | 11 | ExtUtils::MakeMaker: 0 |
12 | 12 | requires: |
13 | Test::More: 0.47 | |
13 | Scalar::Util: 1 | |
14 | Test::More: 0.47 | |
14 | 15 | resources: |
15 | 16 | bugtracker: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Imager |
16 | 17 | homepage: http://imager.perl.org/ |
166 | 166 | bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o |
167 | 167 | imext.o scale.o rubthru.o render.o paste.o compose.o flip.o); |
168 | 168 | |
169 | my %opts=( | |
170 | 'NAME' => 'Imager', | |
171 | 'VERSION_FROM' => 'Imager.pm', | |
172 | 'LIBS' => "$LFLAGS -lm $lib_lflags $OSLIBS $F_LIBS", | |
173 | 'DEFINE' => "$OSDEF $CFLAGS", | |
174 | 'INC' => "$lib_cflags $DFLAGS $F_INC", | |
175 | 'OBJECT' => join(' ', @objs, $F_OBJECT), | |
176 | clean => { FILES=>'testout rubthru.c scale.c conv.c filters.c gaussian.c render.c rubthru.c' }, | |
177 | PM => gen_PM(), | |
178 | PREREQ_PM => { 'Test::More' => 0.47 }, | |
179 | ); | |
169 | my %opts= | |
170 | ( | |
171 | 'NAME' => 'Imager', | |
172 | 'VERSION_FROM' => 'Imager.pm', | |
173 | 'LIBS' => "$LFLAGS -lm $lib_lflags $OSLIBS $F_LIBS", | |
174 | 'DEFINE' => "$OSDEF $CFLAGS", | |
175 | 'INC' => "$lib_cflags $DFLAGS $F_INC", | |
176 | 'OBJECT' => join(' ', @objs, $F_OBJECT), | |
177 | clean => { FILES=>'testout rubthru.c scale.c conv.c filters.c gaussian.c render.c rubthru.c' }, | |
178 | PM => gen_PM(), | |
179 | PREREQ_PM => | |
180 | { | |
181 | 'Test::More' => 0.47, | |
182 | 'Scalar::Util' => 1.00, | |
183 | }, | |
184 | ); | |
180 | 185 | |
181 | 186 | if ($coverage) { |
182 | 187 | if ($Config{gccversion}) { |
183 | push @ARGV, 'OPTIMIZE=-ftest-coverage -fprofile-arcs'; | |
184 | #$opts{dynamic_lib} = { OTHERLDFLAGS => '-ftest-coverage -fprofile-arcs' }; | |
188 | push @ARGV, 'OPTIMIZE=-ftest-coverage -fprofile-arcs -g'; | |
189 | $opts{dynamic_lib} = { OTHERLDFLAGS => '-ftest-coverage -fprofile-arcs' }; | |
185 | 190 | } |
186 | 191 | else { |
187 | 192 | die "Don't know the coverage C flags for your compiler\n"; |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.85" ); | |
40 | my @Imager_req = ( Imager => "0.86" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.81"; | |
6 | $VERSION = "0.82"; | |
7 | 7 | |
8 | 8 | eval { |
9 | 9 | require XSLoader; |
33 | 33 | static void |
34 | 34 | wiol_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
35 | 35 | io_glue *ig = png_get_io_ptr(png_ptr); |
36 | int rc = ig->readcb(ig, data, length); | |
36 | int rc = i_io_read(ig, data, length); | |
37 | 37 | if (rc != length) png_error(png_ptr, "Read overflow error on an iolayer source."); |
38 | 38 | } |
39 | 39 | |
41 | 41 | wiol_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { |
42 | 42 | int rc; |
43 | 43 | io_glue *ig = png_get_io_ptr(png_ptr); |
44 | rc = ig->writecb(ig, data, length); | |
44 | rc = i_io_write(ig, data, length); | |
45 | 45 | if (rc != length) png_error(png_ptr, "Write error on an iolayer source."); |
46 | 46 | } |
47 | 47 | |
176 | 176 | |
177 | 177 | png_destroy_write_struct(&png_ptr, &info_ptr); |
178 | 178 | |
179 | ig->closecb(ig); | |
179 | if (i_io_close(ig)) | |
180 | return 0; | |
180 | 181 | |
181 | 182 | return(1); |
182 | 183 | } |
1 | 1 | use strict; |
2 | 2 | use Imager qw(:all); |
3 | 3 | use Test::More; |
4 | use Imager::Test qw(test_image_raw); | |
4 | use Imager::Test qw(test_image_raw test_image); | |
5 | 5 | |
6 | 6 | -d "testout" or mkdir "testout"; |
7 | 7 | |
10 | 10 | $Imager::formats{"png"} |
11 | 11 | or plan skip_all => "No png support"; |
12 | 12 | |
13 | plan tests => 33; | |
13 | plan tests => 35; | |
14 | 14 | |
15 | 15 | my $green = i_color_new(0, 255, 0, 255); |
16 | 16 | my $blue = i_color_new(0, 0, 255, 255); |
146 | 146 | 'test write_multi() callback failure'); |
147 | 147 | } |
148 | 148 | |
149 | { # check close failures are handled correctly | |
150 | my $im = test_image(); | |
151 | my $fail_close = sub { | |
152 | Imager::i_push_error(0, "synthetic close failure"); | |
153 | return 0; | |
154 | }; | |
155 | ok(!$im->write(type => "png", callback => sub { 1 }, | |
156 | closecb => $fail_close), | |
157 | "check failing close fails"); | |
158 | like($im->errstr, qr/synthetic close failure/, | |
159 | "check error message"); | |
160 | } | |
161 | ||
149 | 162 | { |
150 | 163 | ok(grep($_ eq 'png', Imager->read_types), "check png in read types"); |
151 | 164 | ok(grep($_ eq 'png', Imager->write_types), "check png in write types"); |
440 | 440 | Roderick Schertler ( Roderick ) |
441 | 441 | Nathan Torkington ( gnat ) |
442 | 442 | Gabriel Vasseur |
443 | kmx | |
444 | Nicolas Roggli | |
445 | Justin Davis | |
446 | Maurice Height | |
447 | Krzysztof Wojtaś | |
448 | David Cantrell | |
449 | Eleneldil G. Arilou | |
450 | Slaven Rezic | |
451 | Richard Fairhurst | |
452 | Nikita Dedik | |
443 | 453 | |
444 | 454 | (and just to play it safe) all those I forgot to mention. |
181 | 181 | mm_log((1,"i_readsgi(ig %p, partial %d)\n", ig, partial)); |
182 | 182 | i_clear_error(); |
183 | 183 | |
184 | if (ig->readcb(ig, headbuf, 512) != 512) { | |
184 | if (i_io_read(ig, headbuf, 512) != 512) { | |
185 | 185 | i_push_error(errno, "SGI image: could not read header"); |
186 | 186 | return NULL; |
187 | 187 | } |
374 | 374 | for(y = 0; y < height; y++) { |
375 | 375 | int x; |
376 | 376 | |
377 | if (ig->readcb(ig, databuf, width) != width) { | |
377 | if (i_io_read(ig, databuf, width) != width) { | |
378 | 378 | i_push_error(0, "SGI image: cannot read image data"); |
379 | 379 | i_img_destroy(img); |
380 | 380 | myfree(linebuf); |
435 | 435 | length_tab = mymalloc(height*channels*sizeof(unsigned long)); |
436 | 436 | |
437 | 437 | /* Read offset table */ |
438 | if (ig->readcb(ig, databuf, height * channels * 4) != height * channels * 4) { | |
438 | if (i_io_read(ig, databuf, height * channels * 4) != height * channels * 4) { | |
439 | 439 | i_push_error(0, "SGI image: short read reading RLE start table"); |
440 | 440 | goto ErrorReturn; |
441 | 441 | } |
446 | 446 | |
447 | 447 | |
448 | 448 | /* Read length table */ |
449 | if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) { | |
449 | if (i_io_read(ig, databuf, height*channels*4) != height*channels*4) { | |
450 | 450 | i_push_error(0, "SGI image: short read reading RLE length table"); |
451 | 451 | goto ErrorReturn; |
452 | 452 | } |
519 | 519 | int pixels_left = width; |
520 | 520 | i_sample_t sample; |
521 | 521 | |
522 | if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) { | |
522 | if (i_io_seek(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) { | |
523 | 523 | i_push_error(0, "SGI image: cannot seek to RLE data"); |
524 | 524 | goto ErrorReturn; |
525 | 525 | } |
526 | if (ig->readcb(ig, databuf, datalen) != datalen) { | |
526 | if (i_io_read(ig, databuf, datalen) != datalen) { | |
527 | 527 | i_push_error(0, "SGI image: cannot read RLE data"); |
528 | 528 | goto ErrorReturn; |
529 | 529 | } |
655 | 655 | for(y = 0; y < height; y++) { |
656 | 656 | int x; |
657 | 657 | |
658 | if (ig->readcb(ig, databuf, width*2) != width*2) { | |
658 | if (i_io_read(ig, databuf, width*2) != width*2) { | |
659 | 659 | i_push_error(0, "SGI image: cannot read image data"); |
660 | 660 | i_img_destroy(img); |
661 | 661 | myfree(linebuf); |
737 | 737 | i_push_error(0, "SGI image: invalid RLE length value for BPC=2"); |
738 | 738 | goto ErrorReturn; |
739 | 739 | } |
740 | if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) { | |
740 | if (i_io_seek(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) { | |
741 | 741 | i_push_error(0, "SGI image: cannot seek to RLE data"); |
742 | 742 | goto ErrorReturn; |
743 | 743 | } |
744 | if (ig->readcb(ig, databuf, datalen) != datalen) { | |
744 | if (i_io_read(ig, databuf, datalen) != datalen) { | |
745 | 745 | i_push_error(0, "SGI image: cannot read RLE data"); |
746 | 746 | goto ErrorReturn; |
747 | 747 | } |
911 | 911 | for (c = 0; c < img->channels; ++c) { |
912 | 912 | for (y = img->ysize - 1; y >= 0; --y) { |
913 | 913 | i_gsamp(img, 0, width, y, linebuf, &c, 1); |
914 | if (ig->writecb(ig, linebuf, width) != width) { | |
914 | if (i_io_write(ig, linebuf, width) != width) { | |
915 | 915 | i_push_error(errno, "SGI image: error writing image data"); |
916 | 916 | myfree(linebuf); |
917 | 917 | return 0; |
919 | 919 | } |
920 | 920 | } |
921 | 921 | myfree(linebuf); |
922 | ||
923 | if (i_io_close(ig)) | |
924 | return 0; | |
922 | 925 | |
923 | 926 | return 1; |
924 | 927 | } |
1010 | 1013 | store_32(lengths + offset_pos, comp_size); |
1011 | 1014 | offset_pos += 4; |
1012 | 1015 | current_offset += comp_size; |
1013 | if (ig->writecb(ig, comp_buf, comp_size) != comp_size) { | |
1016 | if (i_io_write(ig, comp_buf, comp_size) != comp_size) { | |
1014 | 1017 | i_push_error(errno, "SGI image: error writing RLE data"); |
1015 | 1018 | goto Error; |
1016 | 1019 | } |
1031 | 1034 | myfree(offsets); |
1032 | 1035 | myfree(comp_buf); |
1033 | 1036 | myfree(linebuf); |
1037 | ||
1038 | if (i_io_close(ig)) | |
1039 | return 0; | |
1034 | 1040 | |
1035 | 1041 | return 1; |
1036 | 1042 | |
1060 | 1066 | unsigned short samp16 = SampleFTo16(linebuf[x]); |
1061 | 1067 | store_16(outp, samp16); |
1062 | 1068 | } |
1063 | if (ig->writecb(ig, encbuf, width * 2) != width * 2) { | |
1069 | if (i_io_write(ig, encbuf, width * 2) != width * 2) { | |
1064 | 1070 | i_push_error(errno, "SGI image: error writing image data"); |
1065 | 1071 | myfree(linebuf); |
1066 | 1072 | myfree(encbuf); |
1070 | 1076 | } |
1071 | 1077 | myfree(linebuf); |
1072 | 1078 | myfree(encbuf); |
1079 | ||
1080 | if (i_io_close(ig)) | |
1081 | return 0; | |
1073 | 1082 | |
1074 | 1083 | return 1; |
1075 | 1084 | } |
1170 | 1179 | store_32(lengths + offset_pos, comp_size); |
1171 | 1180 | offset_pos += 4; |
1172 | 1181 | current_offset += comp_size; |
1173 | if (ig->writecb(ig, comp_buf, comp_size) != comp_size) { | |
1182 | if (i_io_write(ig, comp_buf, comp_size) != comp_size) { | |
1174 | 1183 | i_push_error(errno, "SGI image: error writing RLE data"); |
1175 | 1184 | goto Error; |
1176 | 1185 | } |
1192 | 1201 | myfree(comp_buf); |
1193 | 1202 | myfree(linebuf); |
1194 | 1203 | myfree(sampbuf); |
1204 | ||
1205 | if (i_io_close(ig)) | |
1206 | return 0; | |
1195 | 1207 | |
1196 | 1208 | return 1; |
1197 | 1209 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager; |
3 | use Test::More tests => 55; | |
3 | use Test::More tests => 57; | |
4 | 4 | use Imager::Test qw(test_image test_image_16 is_image); |
5 | 5 | use IO::Seekable; |
6 | 6 | |
160 | 160 | ); |
161 | 161 | for my $test (@tests) { |
162 | 162 | my ($im, $limit, $expected_msg, $desc) = @$test; |
163 | my ($writecb, $seekcb) = limited_write($limit); | |
164 | ok(!$im->write(type => 'sgi', writecb => $writecb, | |
165 | seekcb => $seekcb, maxbuffer => 1), | |
163 | my $io = limited_write_io($limit); | |
164 | ok(!$im->write(type => 'sgi', io => $io), | |
166 | 165 | "write should fail - $desc"); |
167 | 166 | is($im->errstr, "$expected_msg: limit reached", "check error - $desc"); |
168 | 167 | } |
168 | } | |
169 | ||
170 | ||
171 | { # check close failures are handled correctly | |
172 | my $im = test_image(); | |
173 | my $fail_close = sub { | |
174 | Imager::i_push_error(0, "synthetic close failure"); | |
175 | return 0; | |
176 | }; | |
177 | ok(!$im->write(type => "sgi", callback => sub { 1 }, | |
178 | closecb => $fail_close), | |
179 | "check failing close fails"); | |
180 | like($im->errstr, qr/synthetic close failure/, | |
181 | "check error message"); | |
182 | } | |
183 | ||
184 | sub limited_write_io { | |
185 | my ($limit) = @_; | |
186 | ||
187 | my ($writecb, $seekcb) = limited_write($limit); | |
188 | ||
189 | my $io = Imager::io_new_cb($writecb, undef, $seekcb, undef, 1); | |
190 | $io->set_buffered(0); | |
191 | ||
192 | return $io; | |
169 | 193 | } |
170 | 194 | |
171 | 195 | sub limited_write { |
4 | 4 | @ISA = qw(Imager::Font); |
5 | 5 | |
6 | 6 | BEGIN { |
7 | $VERSION = "1.014"; | |
7 | $VERSION = "1.015"; | |
8 | 8 | |
9 | 9 | eval { |
10 | 10 | require XSLoader; |
34 | 34 | |
35 | 35 | sub new { |
36 | 36 | my $class = shift; |
37 | my %hsh=(color=>Imager::Color->new(255,0,0,0), | |
37 | my %hsh=(color=>Imager::Color->new(255,0,0,255), | |
38 | 38 | size=>15, |
39 | 39 | @_); |
40 | 40 | |
73 | 73 | |
74 | 74 | my $id = i_t1_new($hsh{file},$hsh{afm}); |
75 | 75 | unless ($id >= 0) { # the low-level code may miss some error handling |
76 | $Imager::ERRSTR = "Could not load font ($id)"; | |
76 | Imager->_set_error(Imager->_error_as_msg); | |
77 | 77 | return; |
78 | 78 | } |
79 | 79 | return bless { |
133 | 133 | if (GIMME_V == G_ARRAY) { |
134 | 134 | EXTEND(SP, count); |
135 | 135 | for (i = 0; i < count; ++i) { |
136 | PUSHs(sv_2mortal(newSViv(work[i]))); | |
136 | PUSHs(boolSV(work[i])); | |
137 | 137 | } |
138 | 138 | } |
139 | 139 | else { |
92 | 92 | font_id = T1_AddFont(pfb); |
93 | 93 | if (font_id<0) { |
94 | 94 | mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); |
95 | t1_push_error(); | |
95 | 96 | return font_id; |
96 | 97 | } |
97 | 98 | |
100 | 101 | if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm)); |
101 | 102 | } |
102 | 103 | |
104 | if (T1_LoadFont(font_id)) { | |
105 | mm_log((1, "i_t1_new() -> -1 - T1_LoadFont failed (%d)\n", T1_errno)); | |
106 | t1_push_error(); | |
107 | i_push_error(0, "loading font"); | |
108 | T1_DeleteFont(font_id); | |
109 | return -1; | |
110 | } | |
111 | ||
103 | 112 | ++t1_active_fonts; |
113 | ||
114 | mm_log((1, "i_t1_new() -> %d\n", font_id)); | |
104 | 115 | |
105 | 116 | return font_id; |
106 | 117 | } |
571 | 582 | |
572 | 583 | static void |
573 | 584 | t1_push_error(void) { |
585 | #if T1LIB_VERSION > 5 || T1LIB_VERSION == 5 && T1LIB_VERSION >= 1 | |
586 | /* I don't know when T1_StrError() was introduced, be conservative */ | |
587 | i_push_error(T1_errno, T1_StrError(T1_errno)); | |
588 | #else | |
574 | 589 | switch (T1_errno) { |
575 | 590 | case 0: |
576 | 591 | i_push_error(0, "No error"); |
681 | 696 | default: |
682 | 697 | i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); |
683 | 698 | } |
684 | } | |
685 | ||
699 | #endif | |
700 | } | |
701 |
0 | 0 | #!/usr/bin/perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager; |
3 | use Test::More tests => 9; | |
3 | use Imager::Test qw(isnt_image); | |
4 | use Test::More tests => 14; | |
4 | 5 | |
5 | 6 | # extracted from t/t36oofont.t |
6 | 7 | |
59 | 60 | "write t36oofont1.ppm") |
60 | 61 | or print "# ",$img->errstr,"\n"; |
61 | 62 | |
63 | { # RT 71469 | |
64 | my $font1 = Imager::Font->new(file => $fontname_pfb, type => "t1"); | |
65 | my $font2 = Imager::Font::T1->new(file => $fontname_pfb); | |
66 | ||
67 | for my $font ($font1, $font2) { | |
68 | print "# ", join(",", $font->{color}->rgba), "\n"; | |
69 | ||
70 | my $im = Imager->new(xsize => 20, ysize => 20, channels => 4); | |
71 | ||
72 | ok($im->string(text => "T", font => $font, y => 15), | |
73 | "draw with default color") | |
74 | or print "# ", $im->errstr, "\n"; | |
75 | my $work = Imager->new(xsize => 20, ysize => 20); | |
76 | my $cmp = $work->copy; | |
77 | $work->rubthrough(src => $im); | |
78 | isnt_image($work, $cmp, "make sure something was drawn"); | |
79 | } | |
80 | } | |
81 | ||
82 | { # open a non-font as a font (test open failure) | |
83 | local $ENV{LANG} = "C"; | |
84 | local $ENV{LC_ALL} = "C"; | |
85 | my $font = Imager::Font->new(file => "t/t20oo.t", type => "t1"); | |
86 | ok(!$font, "should fail to open test script as a font"); | |
87 | print "# ", Imager->errstr, "\n"; | |
88 | } | |
89 | ||
62 | 90 | unless ($ENV{IMAGER_KEEP_FILES}) { |
63 | 91 | unlink "testout/t36oofont1.ppm"; |
64 | 92 | } |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.85" ); | |
40 | my @Imager_req = ( Imager => "0.86" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.81"; | |
6 | $VERSION = "0.82"; | |
7 | 7 | |
8 | 8 | eval { |
9 | 9 | require XSLoader; |
233 | 233 | toff_t |
234 | 234 | comp_seek(thandle_t h, toff_t o, int w) { |
235 | 235 | io_glue *ig = (io_glue*)h; |
236 | return (toff_t) ig->seekcb(ig, o, w); | |
236 | return (toff_t) i_io_seek(ig, o, w); | |
237 | 237 | } |
238 | 238 | |
239 | 239 | /* |
265 | 265 | static void |
266 | 266 | comp_munmap(thandle_t h, tdata_t p, toff_t off) { |
267 | 267 | /* do nothing */ |
268 | } | |
269 | ||
270 | static tsize_t | |
271 | comp_read(thandle_t h, tdata_t p, tsize_t size) { | |
272 | return i_io_read((io_glue *)h, p, size); | |
273 | } | |
274 | ||
275 | static tsize_t | |
276 | comp_write(thandle_t h, tdata_t p, tsize_t size) { | |
277 | return i_io_write((io_glue *)h, p, size); | |
278 | } | |
279 | ||
280 | static int | |
281 | comp_close(thandle_t h) { | |
282 | return i_io_close((io_glue *)h); | |
268 | 283 | } |
269 | 284 | |
270 | 285 | static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) { |
546 | 561 | tif = TIFFClientOpen("(Iolayer)", |
547 | 562 | "rm", |
548 | 563 | (thandle_t) ig, |
549 | (TIFFReadWriteProc) ig->readcb, | |
550 | (TIFFReadWriteProc) ig->writecb, | |
551 | (TIFFSeekProc) comp_seek, | |
552 | (TIFFCloseProc) ig->closecb, | |
553 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
554 | (TIFFMapFileProc) comp_mmap, | |
555 | (TIFFUnmapFileProc) comp_munmap); | |
564 | comp_read, | |
565 | comp_write, | |
566 | comp_seek, | |
567 | comp_close, | |
568 | sizeproc, | |
569 | comp_mmap, | |
570 | comp_munmap); | |
556 | 571 | |
557 | 572 | if (!tif) { |
558 | 573 | mm_log((1, "i_readtiff_wiol: Unable to open tif file\n")); |
596 | 611 | TIFFErrorHandler old_warn_handler; |
597 | 612 | i_img **results = NULL; |
598 | 613 | int result_alloc = 0; |
599 | int dirnum = 0; | |
600 | 614 | |
601 | 615 | i_clear_error(); |
602 | 616 | old_handler = TIFFSetErrorHandler(error_handler); |
612 | 626 | tif = TIFFClientOpen("(Iolayer)", |
613 | 627 | "rm", |
614 | 628 | (thandle_t) ig, |
615 | (TIFFReadWriteProc) ig->readcb, | |
616 | (TIFFReadWriteProc) ig->writecb, | |
617 | (TIFFSeekProc) comp_seek, | |
618 | (TIFFCloseProc) ig->closecb, | |
619 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
620 | (TIFFMapFileProc) comp_mmap, | |
621 | (TIFFUnmapFileProc) comp_munmap); | |
629 | comp_read, | |
630 | comp_write, | |
631 | comp_seek, | |
632 | comp_close, | |
633 | sizeproc, | |
634 | comp_mmap, | |
635 | comp_munmap); | |
622 | 636 | |
623 | 637 | if (!tif) { |
624 | 638 | mm_log((1, "i_readtiff_wiol: Unable to open tif file\n")); |
1329 | 1343 | tif = TIFFClientOpen("No name", |
1330 | 1344 | "wm", |
1331 | 1345 | (thandle_t) ig, |
1332 | (TIFFReadWriteProc) ig->readcb, | |
1333 | (TIFFReadWriteProc) ig->writecb, | |
1334 | (TIFFSeekProc) comp_seek, | |
1335 | (TIFFCloseProc) ig->closecb, | |
1336 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
1337 | (TIFFMapFileProc) comp_mmap, | |
1338 | (TIFFUnmapFileProc) comp_munmap); | |
1346 | comp_read, | |
1347 | comp_write, | |
1348 | comp_seek, | |
1349 | comp_close, | |
1350 | sizeproc, | |
1351 | comp_mmap, | |
1352 | comp_munmap); | |
1339 | 1353 | |
1340 | 1354 | |
1341 | 1355 | |
1364 | 1378 | TIFFSetErrorHandler(old_handler); |
1365 | 1379 | (void) TIFFClose(tif); |
1366 | 1380 | |
1381 | if (i_io_close(ig)) | |
1382 | return 0; | |
1383 | ||
1367 | 1384 | return 1; |
1368 | 1385 | } |
1369 | 1386 | |
1397 | 1414 | tif = TIFFClientOpen("No name", |
1398 | 1415 | "wm", |
1399 | 1416 | (thandle_t) ig, |
1400 | (TIFFReadWriteProc) ig->readcb, | |
1401 | (TIFFReadWriteProc) ig->writecb, | |
1402 | (TIFFSeekProc) comp_seek, | |
1403 | (TIFFCloseProc) ig->closecb, | |
1404 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
1405 | (TIFFMapFileProc) comp_mmap, | |
1406 | (TIFFUnmapFileProc) comp_munmap); | |
1417 | comp_read, | |
1418 | comp_write, | |
1419 | comp_seek, | |
1420 | comp_close, | |
1421 | sizeproc, | |
1422 | comp_mmap, | |
1423 | comp_munmap); | |
1407 | 1424 | |
1408 | 1425 | |
1409 | 1426 | |
1431 | 1448 | |
1432 | 1449 | (void) TIFFClose(tif); |
1433 | 1450 | TIFFSetErrorHandler(old_handler); |
1451 | ||
1452 | if (i_io_close(ig)) | |
1453 | return 0; | |
1434 | 1454 | |
1435 | 1455 | return 1; |
1436 | 1456 | } |
1460 | 1480 | tif = TIFFClientOpen("No name", |
1461 | 1481 | "wm", |
1462 | 1482 | (thandle_t) ig, |
1463 | (TIFFReadWriteProc) ig->readcb, | |
1464 | (TIFFReadWriteProc) ig->writecb, | |
1465 | (TIFFSeekProc) comp_seek, | |
1466 | (TIFFCloseProc) ig->closecb, | |
1467 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
1468 | (TIFFMapFileProc) comp_mmap, | |
1469 | (TIFFUnmapFileProc) comp_munmap); | |
1483 | comp_read, | |
1484 | comp_write, | |
1485 | comp_seek, | |
1486 | comp_close, | |
1487 | sizeproc, | |
1488 | comp_mmap, | |
1489 | comp_munmap); | |
1470 | 1490 | |
1471 | 1491 | |
1472 | 1492 | |
1486 | 1506 | (void) TIFFClose(tif); |
1487 | 1507 | TIFFSetErrorHandler(old_handler); |
1488 | 1508 | |
1509 | if (i_io_close(ig)) | |
1510 | return 0; | |
1511 | ||
1489 | 1512 | return 1; |
1490 | 1513 | } |
1491 | 1514 | |
1521 | 1544 | tif = TIFFClientOpen("No name", |
1522 | 1545 | "wm", |
1523 | 1546 | (thandle_t) ig, |
1524 | (TIFFReadWriteProc) ig->readcb, | |
1525 | (TIFFReadWriteProc) ig->writecb, | |
1526 | (TIFFSeekProc) comp_seek, | |
1527 | (TIFFCloseProc) ig->closecb, | |
1528 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, | |
1529 | (TIFFMapFileProc) comp_mmap, | |
1530 | (TIFFUnmapFileProc) comp_munmap); | |
1547 | comp_read, | |
1548 | comp_write, | |
1549 | comp_seek, | |
1550 | comp_close, | |
1551 | sizeproc, | |
1552 | comp_mmap, | |
1553 | comp_munmap); | |
1531 | 1554 | |
1532 | 1555 | |
1533 | 1556 | |
1546 | 1569 | |
1547 | 1570 | (void) TIFFClose(tif); |
1548 | 1571 | TIFFSetErrorHandler(old_handler); |
1572 | ||
1573 | if (i_io_close(ig)) | |
1574 | return 0; | |
1549 | 1575 | |
1550 | 1576 | return 1; |
1551 | 1577 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 235; | |
2 | use Test::More tests => 239; | |
3 | 3 | use Imager qw(:all); |
4 | 4 | use Imager::Test qw(is_image is_image_similar test_image test_image_16 test_image_double test_image_raw); |
5 | 5 | |
203 | 203 | } |
204 | 204 | sub io_reader { |
205 | 205 | my ($size, $maxread) = @_; |
206 | #print "io_reader($size, $maxread) pos $seekpos\n"; | |
206 | print "# io_reader($size, $maxread) pos $seekpos\n"; | |
207 | if ($seekpos + $maxread > length $work) { | |
208 | $maxread = length($work) - $seekpos; | |
209 | } | |
207 | 210 | my $out = substr($work, $seekpos, $maxread); |
208 | 211 | $seekpos += length $out; |
209 | 212 | $out; |
210 | 213 | } |
211 | 214 | sub io_reader2 { |
212 | 215 | my ($size, $maxread) = @_; |
213 | #print "io_reader2($size, $maxread) pos $seekpos\n"; | |
216 | print "# io_reader2($size, $maxread) pos $seekpos\n"; | |
214 | 217 | my $out = substr($work, $seekpos, $size); |
215 | 218 | $seekpos += length $out; |
216 | 219 | $out; |
218 | 221 | use IO::Seekable; |
219 | 222 | sub io_seeker { |
220 | 223 | my ($offset, $whence) = @_; |
221 | #print "io_seeker($offset, $whence)\n"; | |
224 | print "# io_seeker($offset, $whence)\n"; | |
222 | 225 | if ($whence == SEEK_SET) { |
223 | 226 | $seekpos = $offset; |
224 | 227 | } |
285 | 288 | print D3 $work; |
286 | 289 | close D3; |
287 | 290 | |
291 | ||
292 | { # check close failures are handled correctly | |
293 | { # single image | |
294 | my $im = test_image(); | |
295 | my $fail_close = sub { | |
296 | Imager::i_push_error(0, "synthetic close failure"); | |
297 | return 0; | |
298 | }; | |
299 | $work = ''; | |
300 | $seekpos = 0; | |
301 | ok(!$im->write(type => "tiff", | |
302 | readcb => \&io_reader, | |
303 | writecb => \&io_writer, | |
304 | seekcb => \&io_seeker, | |
305 | closecb => $fail_close), | |
306 | "check failing close fails"); | |
307 | like($im->errstr, qr/synthetic close failure/, | |
308 | "check error message"); | |
309 | } | |
310 | { # multiple images | |
311 | my $im = test_image(); | |
312 | my $fail_close = sub { | |
313 | Imager::i_push_error(0, "synthetic close failure"); | |
314 | return 0; | |
315 | }; | |
316 | $work = ''; | |
317 | $seekpos = 0; | |
318 | ok(!Imager->write_multi({type => "tiff", | |
319 | readcb => \&io_reader, | |
320 | writecb => \&io_writer, | |
321 | seekcb => \&io_seeker, | |
322 | closecb => $fail_close}, $im, $im), | |
323 | "check failing close fails"); | |
324 | like(Imager->errstr, qr/synthetic close failure/, | |
325 | "check error message"); | |
326 | } | |
327 | } | |
328 | ||
288 | 329 | # multi-image write/read |
289 | 330 | my @imgs; |
290 | 331 | push(@imgs, map $ooim->copy(), 1..3); |
380 | 421 | or skip "Cannot open testout/t106_empty.tif for reading", 8; |
381 | 422 | binmode TIFF; |
382 | 423 | my $im = Imager->new(xsize=>100, ysize=>100); |
383 | ok(!$im->write(fh => \*TIFF, type=>'tiff'), | |
424 | ok(!$im->write(fh => \*TIFF, type=>'tiff', buffered => 0), | |
384 | 425 | "fail to write to read only handle"); |
385 | 426 | cmp_ok($im->errstr, '=~', 'Could not create TIFF object: Error writing TIFF header: write\(\)', |
386 | 427 | "check error message"); |
387 | ok(!Imager->write_multi({ type => 'tiff', fh => \*TIFF }, $im), | |
428 | ok(!Imager->write_multi({ type => 'tiff', fh => \*TIFF, buffered => 0 }, $im), | |
388 | 429 | "fail to write multi to read only handle"); |
389 | 430 | cmp_ok(Imager->errstr, '=~', 'Could not create TIFF object: Error writing TIFF header: write\(\)', |
390 | 431 | "check error message"); |
391 | ok(!$im->write(fh => \*TIFF, type=>'tiff', class=>'fax'), | |
432 | ok(!$im->write(fh => \*TIFF, type=>'tiff', class=>'fax', buffered => 0), | |
392 | 433 | "fail to write to read only handle (fax)"); |
393 | 434 | cmp_ok($im->errstr, '=~', 'Could not create TIFF object: Error writing TIFF header: write\(\)', |
394 | 435 | "check error message"); |
395 | ok(!Imager->write_multi({ type => 'tiff', fh => \*TIFF, class=>'fax' }, $im), | |
436 | ok(!Imager->write_multi({ type => 'tiff', fh => \*TIFF, class=>'fax', buffered => 0 }, $im), | |
396 | 437 | "fail to write multi to read only handle (fax)"); |
397 | 438 | cmp_ok(Imager->errstr, '=~', 'Could not create TIFF object: Error writing TIFF header: write\(\)', |
398 | 439 | "check error message"); |
Binary diff not shown
4 | 4 | @ISA = qw(Imager::Font); |
5 | 5 | |
6 | 6 | BEGIN { |
7 | $VERSION = "0.81"; | |
7 | $VERSION = "0.82"; | |
8 | 8 | |
9 | 9 | eval { |
10 | 10 | require XSLoader; |
21 | 21 | # since Win32's HFONTs include the size information this |
22 | 22 | # is just a stub |
23 | 23 | sub new { |
24 | my ($class, %opts) = @_; | |
24 | my $class = shift; | |
25 | my %opts = | |
26 | ( | |
27 | color => Imager::Color->new(255, 0, 0), | |
28 | size => 15, | |
29 | @_, | |
30 | ); | |
25 | 31 | |
26 | 32 | return bless \%opts, $class; |
27 | 33 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 55; | |
2 | use Test::More tests => 59; | |
3 | 3 | use Imager qw(:all); |
4 | use Imager::Test qw(diff_text_with_nul); | |
4 | use Imager::Test qw(diff_text_with_nul isnt_image); | |
5 | 5 | ++$|; |
6 | 6 | |
7 | 7 | ok(-d "testout" or mkdir("testout"), "testout directory"); |
28 | 28 | "i_wf_cp smoke test"); |
29 | 29 | i_line($overlay,0,50,100,50,$bgcolor, 1); |
30 | 30 | |
31 | open(FH,">testout/t10font.ppm") || die "cannot open testout/t10font.ppm\n"; | |
32 | binmode(FH); | |
33 | my $io = Imager::io_new_fd(fileno(FH)); | |
34 | i_writeppm_wiol($overlay,$io); | |
35 | close(FH); | |
31 | if (open(FH,">testout/t10font.ppm")) { | |
32 | binmode(FH); | |
33 | my $io = Imager::io_new_fd(fileno(FH)); | |
34 | i_writeppm_wiol($overlay,$io); | |
35 | close(FH); | |
36 | } | |
37 | else { | |
38 | diag "cannot open testout/t10font.ppm: $!"; | |
39 | } | |
36 | 40 | |
37 | 41 | $bgcolor=i_color_set($bgcolor,200,200,200,0); |
38 | 42 | my $backgr=Imager::ImgRaw::new(500,300,3); |
41 | 45 | "i_wf_text smoke test"); |
42 | 46 | i_line($backgr,0, 100, 499, 100, NC(0, 0, 255), 1); |
43 | 47 | |
44 | open(FH,">testout/t10font2.ppm") || die "cannot open testout/t10font2.ppm\n"; | |
45 | binmode(FH); | |
46 | $io = Imager::io_new_fd(fileno(FH)); | |
47 | i_writeppm_wiol($backgr,$io); | |
48 | close(FH); | |
49 | ||
48 | if (open(FH,">testout/t10font2.ppm")) { | |
49 | binmode(FH); | |
50 | my $io = Imager::io_new_fd(fileno(FH)); | |
51 | i_writeppm_wiol($backgr,$io); | |
52 | close(FH); | |
53 | } | |
54 | else { | |
55 | diag "cannot open testout/t10font2.ppm: $!"; | |
56 | } | |
57 | ||
50 | 58 | my $img = Imager->new(xsize=>200, ysize=>200); |
51 | 59 | my $font = Imager::Font->new(face=>$fontname, size=>20); |
52 | 60 | ok($img->string('x'=>30, 'y'=>30, string=>"Imager", color=>NC(255, 0, 0), |
53 | 61 | font=>$font), |
54 | 62 | "string with win32 smoke test") |
55 | or print "# ",$img->errstr,"\n"; | |
56 | $img->write(file=>'testout/t10_oo.ppm') or print "not "; | |
63 | or diag "simple string output: ",$img->errstr; | |
64 | $img->write(file=>'testout/t10_oo.ppm') | |
65 | or diag "Cannot save t10_oo.ppm: ", $img->errstr; | |
57 | 66 | my @bbox2 = $font->bounding_box(string=>'Imager'); |
58 | 67 | is(@bbox2, 8, "got 8 values from bounding_box"); |
59 | 68 | |
69 | 78 | SKIP: |
70 | 79 | { |
71 | 80 | $^O eq 'cygwin' and skip("Too hard to get correct directory for test font on cygwin", 13); |
72 | ok(Imager::Font::W32::i_wf_addfont("fontfiles/ExistenceTest.ttf"), "add test font") | |
73 | or print "# ",Imager::_error_as_msg(),"\n"; | |
81 | my $extra_font = "fontfiles/ExistenceTest.ttf"; | |
82 | unless (ok(Imager::Font::W32::i_wf_addfont($extra_font), "add test font")) { | |
83 | diag "adding font resource: ",Imager::_error_as_msg(); | |
84 | skip("Could not add font resource", 12); | |
85 | } | |
74 | 86 | |
75 | 87 | my $namefont = Imager::Font->new(face=>"ExistenceTest"); |
76 | 88 | ok($namefont, "create font based on added font"); |
81 | 93 | is(@bbox, 8, "should be 8 entries"); |
82 | 94 | isnt($bbox[6], $bbox[2], "different advance width"); |
83 | 95 | $bbox = $namefont->bounding_box(string=>"/", size=>100); |
84 | ok($bbox->pos_width != $bbox->advance_width, "OO check"); | |
96 | isnt($bbox->pos_width, $bbox->advance_width, "OO check"); | |
85 | 97 | |
86 | 98 | cmp_ok($bbox->right_bearing, '<', 0, "check right bearing"); |
87 | 99 | |
99 | 111 | $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100); |
100 | 112 | ok($im->string(font=>$namefont, text=>"/", x=>20, y=>100, color=>'white', size=>100), |
101 | 113 | "draw / from ExistenceText") |
102 | or print "# ", $im->errstr, "\n"; | |
114 | or diag "draw / from ExistenceTest:", $im->errstr; | |
103 | 115 | $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red'); |
104 | 116 | $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red'); |
105 | 117 | $im->write(file=>'testout/t10_slash.ppm'); |
126 | 138 | $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100); |
127 | 139 | ok($im->string(font=>$namefont, text=>"!", x=>20, y=>100, color=>'white', size=>100), |
128 | 140 | "draw / from ExistenceText") |
129 | or print "# ", $im->errstr, "\n"; | |
141 | or diag "draw / from ExistenceTest: ", $im->errstr; | |
130 | 142 | $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red'); |
131 | 143 | $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red'); |
132 | 144 | $im->write(file=>'testout/t10_bang.ppm'); |
166 | 178 | ok($im->string(string => "\xE2\x98\xBA", size => 80, aa => 1, utf8 => 1, |
167 | 179 | color => "white", font => $font, x => 5, y => 80), |
168 | 180 | "draw in utf8 (hand encoded)") |
169 | or print "# ", $im->errstr, "\n"; | |
170 | ok($im->write(file=>'testout/t10utf8.ppm'), "save utf8 image"); | |
181 | or diag "draw utf8 hand-encoded ", $im->errstr; | |
182 | ok($im->write(file=>'testout/t10utf8.ppm'), "save utf8 image") | |
183 | or diag "save t10utf8.ppm: ", $im->errstr; | |
171 | 184 | |
172 | 185 | # native perl utf8 |
173 | 186 | # Win32 only supported on 5.6+ |
179 | 192 | ok($im2->string(string => $text, size => 80, aa => 1, |
180 | 193 | color => 'white', font => $font, x => 5, y => 80), |
181 | 194 | "draw in utf8 (perl utf8)") |
182 | or print "# ", $im->errstr, "\n"; | |
195 | or diag "draw in utf8: ", $im->errstr; | |
183 | 196 | ok($im2->write(file=>'testout/t10utf8b.ppm'), "save utf8 image"); |
184 | 197 | is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0, |
185 | 198 | "check result is the same"); |
206 | 219 | diff_text_with_nul("utf8 dash\0dash vs dash - channel", "$dash\0$dash", $dash, |
207 | 220 | font => $font, channel => 1, utf8 => 1); |
208 | 221 | } |
222 | ||
223 | { # RT 71469 | |
224 | my $font1 = Imager::Font->new(face => $fontname, type => "w32"); | |
225 | my $font2 = Imager::Font::W32->new(face => $fontname ); | |
226 | ||
227 | for my $font ($font1, $font2) { | |
228 | print "# ", join(",", $font->{color}->rgba), "\n"; | |
229 | ||
230 | my $im = Imager->new(xsize => 20, ysize => 20, channels => 4); | |
231 | ||
232 | ok($im->string(text => "T", font => $font, y => 15), | |
233 | "draw with default color") | |
234 | or diag "draw with default color: ", $im->errstr; | |
235 | my $work = Imager->new(xsize => 20, ysize => 20); | |
236 | my $cmp = $work->copy; | |
237 | $work->rubthrough(src => $im); | |
238 | isnt_image($work, $cmp, "make sure something was drawn"); | |
239 | } | |
240 | } | |
209 | 241 | } |
321 | 321 | i_wf_addfont(char const *filename) { |
322 | 322 | i_clear_error(); |
323 | 323 | |
324 | mm_log((1, "i_wf_addfont(%s)\n", filename)); | |
324 | 325 | if (!gdi_dll) { |
325 | 326 | gdi_dll = GetModuleHandle("GDI32"); |
326 | 327 | if (gdi_dll) { |
327 | 328 | AddFontResourceExAp = (AddFontResourceExA_t)GetProcAddress(gdi_dll, "AddFontResourceExA"); |
328 | 329 | RemoveFontResourceExAp = (RemoveFontResourceExA_t)GetProcAddress(gdi_dll, "RemoveFontResourceExA"); |
329 | } | |
330 | } | |
331 | ||
332 | if (AddFontResourceExAp && RemoveFontResourceExAp | |
333 | && AddFontResourceExAp(filename, FR_PRIVATE, 0)) { | |
334 | return 1; | |
335 | } | |
336 | else if (AddFontResource(filename)) { | |
337 | return 1; | |
338 | } | |
339 | else { | |
340 | i_push_errorf(0, "Could not add resource: %ld", GetLastError()); | |
341 | return 0; | |
342 | } | |
330 | mm_log((1, "i_wf_addfont: AddFontResourceExA %p RemoveFontResourceExA %p\n", | |
331 | AddFontResourceExAp, RemoveFontResourceExAp)); | |
332 | } | |
333 | } | |
334 | ||
335 | if (AddFontResourceExAp && RemoveFontResourceExAp) { | |
336 | mm_log((1, "i_wf_addfont: adding via AddFontResourceEx()\n")); | |
337 | if (AddFontResourceExAp(filename, FR_PRIVATE, 0)) { | |
338 | return 1; | |
339 | } | |
340 | } | |
341 | else { | |
342 | mm_log((1, "i_wf_addfont: adding via AddFontResource()\n")); | |
343 | if (AddFontResource(filename)) { | |
344 | return 1; | |
345 | } | |
346 | } | |
347 | ||
348 | mm_log((1, "i_wf_addfont failed: %ld\n", GetLastError())); | |
349 | i_push_errorf(0, "Could not add resource: %ld", GetLastError()); | |
350 | return 0; | |
343 | 351 | } |
344 | 352 | |
345 | 353 | /* |
353 | 361 | i_wf_delfont(char const *filename) { |
354 | 362 | i_clear_error(); |
355 | 363 | |
356 | if (AddFontResourceExAp && RemoveFontResourceExAp | |
357 | && RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) { | |
358 | return 1; | |
359 | } | |
360 | else if (RemoveFontResource(filename)) { | |
361 | return 1; | |
362 | } | |
363 | else { | |
364 | i_push_errorf(0, "Could not remove resource: %ld", GetLastError()); | |
365 | return 0; | |
366 | } | |
364 | mm_log((1, "i_wf_delfont(%s)\n", filename)); | |
365 | ||
366 | if (AddFontResourceExAp && RemoveFontResourceExAp) { | |
367 | mm_log((1, "i_wf_delfont: removing via RemoveFontResourceEx()\n")); | |
368 | if (RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) | |
369 | return 1; | |
370 | } | |
371 | else { | |
372 | mm_log((1, "i_wf_delfont: adding via RemoveFontResourceEx()\n")); | |
373 | if (RemoveFontResource(filename)) | |
374 | return 1; | |
375 | } | |
376 | ||
377 | mm_log((1, "i_wf_delfont failed: %ld\n", GetLastError())); | |
378 | i_push_errorf(0, "Could not remove resource: %ld", GetLastError()); | |
379 | return 0; | |
367 | 380 | } |
368 | 381 | |
369 | 382 | /* |
9 | 9 | # look for files to parse |
10 | 10 | |
11 | 11 | my $mani = maniread; |
12 | my @files = grep /\.(c|im|h)$/, keys %$mani; | |
12 | my @files = sort grep /\.(c|im|h)$/, keys %$mani; | |
13 | 13 | |
14 | 14 | # scan each file for =item <func>\b |
15 | 15 | my $func; |
106 | 106 | |
107 | 107 | for my $cat (sort { lc $a cmp lc $b } keys %cats) { |
108 | 108 | print OUT "\n # $cat\n"; |
109 | for my $func (grep $funcsyns{$_}, sort { $order{$a} <=> $order{$b} } @{$cats{$cat}}) { | |
109 | my @funcs = @{$cats{$cat}}; | |
110 | my %orig; | |
111 | @orig{@funcs} = 0 .. $#funcs; | |
112 | @funcs = sort { $order{$a} <=> $order{$b} || $orig{$a} <=> $orig{$b} } @funcs; | |
113 | for my $func (grep $funcsyns{$_}, @funcs) { | |
110 | 114 | my $syn = $funcsyns{$func}; |
111 | 115 | $syn =~ s/^/ /gm; |
112 | 116 | print OUT $syn; |
188 | 192 | my $in_struct; |
189 | 193 | while (<FUNCS>) { |
190 | 194 | /^typedef struct/ && ++$in_struct; |
191 | if ($in_struct && /\(\*f_(i_\w+)/) { | |
192 | push @funcs, $1; | |
195 | if ($in_struct && /\(\*f_(io?_\w+)/) { | |
196 | my $name = $1; | |
197 | $name =~ s/_imp$//; | |
198 | push @funcs, $name; | |
193 | 199 | } |
194 | 200 | if (/^\} im_ext_funcs;$/) { |
195 | 201 | $in_struct |
75 | 75 | */ |
76 | 76 | int |
77 | 77 | i_writebmp_wiol(i_img *im, io_glue *ig) { |
78 | io_glue_commit_types(ig); | |
79 | 78 | i_clear_error(); |
80 | 79 | |
81 | 80 | /* pick a format */ |
119 | 118 | |
120 | 119 | mm_log((1, "i_readbmp_wiol(ig %p)\n", ig)); |
121 | 120 | |
122 | io_glue_commit_types(ig); | |
123 | 121 | i_clear_error(); |
124 | 122 | |
125 | 123 | if (!read_packed(ig, "CCVvvVVV!V!vvVVVVVV", &b_magic, &m_magic, &filesize, |
236 | 234 | |
237 | 235 | switch (code) { |
238 | 236 | case 'v': |
239 | if (ig->readcb(ig, buf, 2) != 2) | |
237 | if (i_io_read(ig, buf, 2) != 2) | |
240 | 238 | return 0; |
241 | 239 | work = buf[0] + ((i_packed_t)buf[1] << 8); |
242 | 240 | if (shrieking) |
246 | 244 | break; |
247 | 245 | |
248 | 246 | case 'V': |
249 | if (ig->readcb(ig, buf, 4) != 4) | |
247 | if (i_io_read(ig, buf, 4) != 4) | |
250 | 248 | return 0; |
251 | 249 | work = buf[0] + (buf[1] << 8) + ((i_packed_t)buf[2] << 16) + ((i_packed_t)buf[3] << 24); |
252 | 250 | if (shrieking) |
256 | 254 | break; |
257 | 255 | |
258 | 256 | case 'C': |
259 | if (ig->readcb(ig, buf, 1) != 1) | |
257 | if (i_io_read(ig, buf, 1) != 1) | |
260 | 258 | return 0; |
261 | 259 | *p = buf[0]; |
262 | 260 | break; |
263 | 261 | |
264 | 262 | case 'c': |
265 | if (ig->readcb(ig, buf, 1) != 1) | |
263 | if (i_io_read(ig, buf, 1) != 1) | |
266 | 264 | return 0; |
267 | 265 | *p = (char)buf[0]; |
268 | 266 | break; |
269 | 267 | |
270 | 268 | case '3': /* extension - 24-bit number */ |
271 | if (ig->readcb(ig, buf, 3) != 3) | |
269 | if (i_io_read(ig, buf, 3) != 3) | |
272 | 270 | return 0; |
273 | 271 | *p = buf[0] + (buf[1] << 8) + ((i_packed_t)buf[2] << 16); |
274 | 272 | break; |
305 | 303 | case 'v': |
306 | 304 | buf[0] = i & 255; |
307 | 305 | buf[1] = i / 256; |
308 | if (ig->writecb(ig, buf, 2) == -1) | |
306 | if (i_io_write(ig, buf, 2) == -1) | |
309 | 307 | return 0; |
310 | 308 | break; |
311 | 309 | |
314 | 312 | buf[1] = (i >> 8) & 0xFF; |
315 | 313 | buf[2] = (i >> 16) & 0xFF; |
316 | 314 | buf[3] = (i >> 24) & 0xFF; |
317 | if (ig->writecb(ig, buf, 4) == -1) | |
315 | if (i_io_write(ig, buf, 4) == -1) | |
318 | 316 | return 0; |
319 | 317 | break; |
320 | 318 | |
321 | 319 | case 'C': |
322 | 320 | case 'c': |
323 | 321 | buf[0] = i & 0xFF; |
324 | if (ig->writecb(ig, buf, 1) == -1) | |
322 | if (i_io_write(ig, buf, 1) == -1) | |
325 | 323 | return 0; |
326 | 324 | break; |
327 | 325 | |
490 | 488 | if (mask != 0x80) { |
491 | 489 | *out++ = byte; |
492 | 490 | } |
493 | if (ig->writecb(ig, packed, line_size) < 0) { | |
491 | if (i_io_write(ig, packed, line_size) < 0) { | |
494 | 492 | myfree(packed); |
495 | 493 | myfree(line); |
496 | 494 | i_push_error(0, "writing 1 bit/pixel packed data"); |
500 | 498 | myfree(packed); |
501 | 499 | myfree(line); |
502 | 500 | |
503 | ig->closecb(ig); | |
501 | if (i_io_close(ig)) | |
502 | return 0; | |
504 | 503 | |
505 | 504 | return 1; |
506 | 505 | } |
549 | 548 | for (x = 0; x < im->xsize; x += 2) { |
550 | 549 | *out++ = (line[x] << 4) + line[x+1]; |
551 | 550 | } |
552 | if (ig->writecb(ig, packed, line_size) < 0) { | |
551 | if (i_io_write(ig, packed, line_size) < 0) { | |
553 | 552 | myfree(packed); |
554 | 553 | myfree(line); |
555 | 554 | i_push_error(0, "writing 4 bit/pixel packed data"); |
559 | 558 | myfree(packed); |
560 | 559 | myfree(line); |
561 | 560 | |
562 | ig->closecb(ig); | |
561 | if (i_io_close(ig)) | |
562 | return 0; | |
563 | 563 | |
564 | 564 | return 1; |
565 | 565 | } |
597 | 597 | |
598 | 598 | for (y = im->ysize-1; y >= 0; --y) { |
599 | 599 | i_gpal(im, 0, im->xsize, y, line); |
600 | if (ig->writecb(ig, line, line_size) < 0) { | |
600 | if (i_io_write(ig, line, line_size) < 0) { | |
601 | 601 | myfree(line); |
602 | 602 | i_push_error(0, "writing 8 bit/pixel packed data"); |
603 | 603 | return 0; |
605 | 605 | } |
606 | 606 | myfree(line); |
607 | 607 | |
608 | ig->closecb(ig); | |
608 | if (i_io_close(ig)) | |
609 | return 0; | |
609 | 610 | |
610 | 611 | return 1; |
611 | 612 | } |
651 | 652 | samplep[0] = tmp; |
652 | 653 | samplep += 3; |
653 | 654 | } |
654 | if (ig->writecb(ig, samples, line_size) < 0) { | |
655 | if (i_io_write(ig, samples, line_size) < 0) { | |
655 | 656 | i_push_error(0, "writing image data"); |
656 | 657 | myfree(samples); |
657 | 658 | return 0; |
659 | 660 | } |
660 | 661 | myfree(samples); |
661 | 662 | |
662 | ig->closecb(ig); | |
663 | if (i_io_close(ig)) | |
664 | return 0; | |
663 | 665 | |
664 | 666 | return 1; |
665 | 667 | } |
772 | 774 | rare */ |
773 | 775 | char buffer; |
774 | 776 | while (base_offset < offbits) { |
775 | if (ig->readcb(ig, &buffer, 1) != 1) { | |
777 | if (i_io_read(ig, &buffer, 1) != 1) { | |
776 | 778 | i_img_destroy(im); |
777 | 779 | i_push_error(0, "failed skipping to image data offset"); |
778 | 780 | return NULL; |
786 | 788 | packed = mymalloc(line_size); /* checked 29jun05 tonyc */ |
787 | 789 | line = mymalloc(xsize+8); /* checked 29jun05 tonyc */ |
788 | 790 | while (y != lasty) { |
789 | if (ig->readcb(ig, packed, line_size) != line_size) { | |
791 | if (i_io_read(ig, packed, line_size) != line_size) { | |
790 | 792 | myfree(packed); |
791 | 793 | myfree(line); |
792 | 794 | if (allow_incomplete) { |
889 | 891 | rare */ |
890 | 892 | char buffer; |
891 | 893 | while (base_offset < offbits) { |
892 | if (ig->readcb(ig, &buffer, 1) != 1) { | |
894 | if (i_io_read(ig, &buffer, 1) != 1) { | |
893 | 895 | i_img_destroy(im); |
894 | 896 | i_push_error(0, "failed skipping to image data offset"); |
895 | 897 | return NULL; |
907 | 909 | if (compression == BI_RGB) { |
908 | 910 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); |
909 | 911 | while (y != lasty) { |
910 | if (ig->readcb(ig, packed, line_size) != line_size) { | |
912 | if (i_io_read(ig, packed, line_size) != line_size) { | |
911 | 913 | myfree(packed); |
912 | 914 | myfree(line); |
913 | 915 | if (allow_incomplete) { |
942 | 944 | x = 0; |
943 | 945 | while (1) { |
944 | 946 | /* there's always at least 2 bytes in a sequence */ |
945 | if (ig->readcb(ig, packed, 2) != 2) { | |
947 | if (i_io_read(ig, packed, 2) != 2) { | |
946 | 948 | myfree(packed); |
947 | 949 | myfree(line); |
948 | 950 | if (allow_incomplete) { |
987 | 989 | return im; |
988 | 990 | |
989 | 991 | case BMPRLE_DELTA: |
990 | if (ig->readcb(ig, packed, 2) != 2) { | |
992 | if (i_io_read(ig, packed, 2) != 2) { | |
991 | 993 | myfree(packed); |
992 | 994 | myfree(line); |
993 | 995 | if (allow_incomplete) { |
1017 | 1019 | } |
1018 | 1020 | size = (count + 1) / 2; |
1019 | 1021 | read_size = (size+1) / 2 * 2; |
1020 | if (ig->readcb(ig, packed, read_size) != read_size) { | |
1022 | if (i_io_read(ig, packed, read_size) != read_size) { | |
1021 | 1023 | myfree(packed); |
1022 | 1024 | myfree(line); |
1023 | 1025 | if (allow_incomplete) { |
1116 | 1118 | rare */ |
1117 | 1119 | char buffer; |
1118 | 1120 | while (base_offset < offbits) { |
1119 | if (ig->readcb(ig, &buffer, 1) != 1) { | |
1121 | if (i_io_read(ig, &buffer, 1) != 1) { | |
1120 | 1122 | i_img_destroy(im); |
1121 | 1123 | i_push_error(0, "failed skipping to image data offset"); |
1122 | 1124 | return NULL; |
1129 | 1131 | if (compression == BI_RGB) { |
1130 | 1132 | i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); |
1131 | 1133 | while (y != lasty) { |
1132 | if (ig->readcb(ig, line, line_size) != line_size) { | |
1134 | if (i_io_read(ig, line, line_size) != line_size) { | |
1133 | 1135 | myfree(line); |
1134 | 1136 | if (allow_incomplete) { |
1135 | 1137 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1156 | 1158 | x = 0; |
1157 | 1159 | while (1) { |
1158 | 1160 | /* there's always at least 2 bytes in a sequence */ |
1159 | if (ig->readcb(ig, packed, 2) != 2) { | |
1161 | if (i_io_read(ig, packed, 2) != 2) { | |
1160 | 1162 | myfree(line); |
1161 | 1163 | if (allow_incomplete) { |
1162 | 1164 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1192 | 1194 | return im; |
1193 | 1195 | |
1194 | 1196 | case BMPRLE_DELTA: |
1195 | if (ig->readcb(ig, packed, 2) != 2) { | |
1197 | if (i_io_read(ig, packed, 2) != 2) { | |
1196 | 1198 | myfree(line); |
1197 | 1199 | if (allow_incomplete) { |
1198 | 1200 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1220 | 1222 | return NULL; |
1221 | 1223 | } |
1222 | 1224 | read_size = (count+1) / 2 * 2; |
1223 | if (ig->readcb(ig, line, read_size) != read_size) { | |
1225 | if (i_io_read(ig, line, read_size) != read_size) { | |
1224 | 1226 | myfree(line); |
1225 | 1227 | if (allow_incomplete) { |
1226 | 1228 | i_tags_setn(&im->tags, "i_incomplete", 1); |
1323 | 1325 | /* there's a potential "palette" after the header */ |
1324 | 1326 | for (i = 0; i < clr_used; ++clr_used) { |
1325 | 1327 | char buf[4]; |
1326 | if (ig->readcb(ig, buf, 4) != 4) { | |
1328 | if (i_io_read(ig, buf, 4) != 4) { | |
1327 | 1329 | i_push_error(0, "skipping colors"); |
1328 | 1330 | return 0; |
1329 | 1331 | } |
1367 | 1369 | rare */ |
1368 | 1370 | char buffer; |
1369 | 1371 | while (base_offset < offbits) { |
1370 | if (ig->readcb(ig, &buffer, 1) != 1) { | |
1372 | if (i_io_read(ig, &buffer, 1) != 1) { | |
1371 | 1373 | i_push_error(0, "failed skipping to image data offset"); |
1372 | 1374 | return NULL; |
1373 | 1375 | } |
1417 | 1419 | } |
1418 | 1420 | i_plin(im, 0, xsize, y, line); |
1419 | 1421 | if (extras) |
1420 | ig->readcb(ig, junk, extras); | |
1422 | i_io_read(ig, junk, extras); | |
1421 | 1423 | y += yinc; |
1422 | 1424 | } |
1423 | 1425 | myfree(line); |
21 | 21 | |
22 | 22 | mm_log((1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len)); |
23 | 23 | i_clear_error(); |
24 | ||
25 | if (len < 1) { | |
26 | i_push_error(0, "there must be at least one coefficient"); | |
27 | return 0; | |
28 | } | |
24 | 29 | |
25 | 30 | center=(len-1)/2; |
26 | 31 |
21 | 21 | btm->data=(char*)mymalloc(bytes); /* checked 4jul05 tonyc */ |
22 | 22 | btm->xsize=xsize; |
23 | 23 | btm->ysize=ysize; |
24 | for(i=0;i<(xsize*ysize+8)/8;i++) btm->data[i]=0; /* Is this always needed */ | |
24 | memset(btm->data, 0, bytes); | |
25 | 25 | return btm; |
26 | 26 | } |
27 | 27 |
317 | 317 | /* |
318 | 318 | =item i_img_setmask(C<im>, C<ch_mask>) |
319 | 319 | =category Image Information |
320 | =synopsis // only channel 0 writeable | |
320 | =synopsis // only channel 0 writable | |
321 | 321 | =synopsis i_img_setmask(img, 0x01); |
322 | 322 | |
323 | 323 | Set the image channel mask for C<im> to C<ch_mask>. |
1488 | 1488 | } |
1489 | 1489 | } |
1490 | 1490 | |
1491 | /* | |
1492 | =back | |
1493 | ||
1494 | =head2 Stream reading and writing wrapper functions | |
1495 | ||
1496 | =over | |
1497 | ||
1498 | =item i_gen_reader(i_gen_read_data *info, char *buf, int length) | |
1499 | ||
1500 | Performs general read buffering for file readers that permit reading | |
1501 | to be done through a callback. | |
1502 | ||
1503 | The final callback gets two parameters, a I<need> value, and a I<want> | |
1504 | value, where I<need> is the amount of data that the file library needs | |
1505 | to read, and I<want> is the amount of space available in the buffer | |
1506 | maintained by these functions. | |
1507 | ||
1508 | This means if you need to read from a stream that you don't know the | |
1509 | length of, you can return I<need> bytes, taking the performance hit of | |
1510 | possibly expensive callbacks (eg. back to perl code), or if you are | |
1511 | reading from a stream where it doesn't matter if some data is lost, or | |
1512 | if the total length of the stream is known, you can return I<want> | |
1513 | bytes. | |
1514 | ||
1515 | =cut | |
1516 | */ | |
1517 | ||
1518 | int | |
1519 | i_gen_reader(i_gen_read_data *gci, char *buf, int length) { | |
1520 | int total; | |
1521 | ||
1522 | if (length < gci->length - gci->cpos) { | |
1523 | /* simplest case */ | |
1524 | memcpy(buf, gci->buffer+gci->cpos, length); | |
1525 | gci->cpos += length; | |
1526 | return length; | |
1527 | } | |
1528 | ||
1529 | total = 0; | |
1530 | memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos); | |
1531 | total += gci->length - gci->cpos; | |
1532 | length -= gci->length - gci->cpos; | |
1533 | buf += gci->length - gci->cpos; | |
1534 | if (length < (int)sizeof(gci->buffer)) { | |
1535 | int did_read; | |
1536 | int copy_size; | |
1537 | while (length | |
1538 | && (did_read = (gci->cb)(gci->userdata, gci->buffer, length, | |
1539 | sizeof(gci->buffer))) > 0) { | |
1540 | gci->cpos = 0; | |
1541 | gci->length = did_read; | |
1542 | ||
1543 | copy_size = i_min(length, gci->length); | |
1544 | memcpy(buf, gci->buffer, copy_size); | |
1545 | gci->cpos += copy_size; | |
1546 | buf += copy_size; | |
1547 | total += copy_size; | |
1548 | length -= copy_size; | |
1549 | } | |
1550 | } | |
1551 | else { | |
1552 | /* just read the rest - too big for our buffer*/ | |
1553 | int did_read; | |
1554 | while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) { | |
1555 | length -= did_read; | |
1556 | total += did_read; | |
1557 | buf += did_read; | |
1558 | } | |
1559 | } | |
1560 | return total; | |
1561 | } | |
1562 | ||
1563 | /* | |
1564 | =item i_gen_read_data_new(i_read_callback_t cb, char *userdata) | |
1565 | ||
1566 | For use by callback file readers to initialize the reader buffer. | |
1567 | ||
1568 | Allocates, initializes and returns the reader buffer. | |
1569 | ||
1570 | See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>. | |
1571 | ||
1572 | =cut | |
1573 | */ | |
1574 | i_gen_read_data * | |
1575 | i_gen_read_data_new(i_read_callback_t cb, char *userdata) { | |
1576 | i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data)); | |
1577 | self->cb = cb; | |
1578 | self->userdata = userdata; | |
1579 | self->length = 0; | |
1580 | self->cpos = 0; | |
1581 | ||
1582 | return self; | |
1583 | } | |
1584 | ||
1585 | /* | |
1586 | =item i_free_gen_read_data(i_gen_read_data *) | |
1587 | ||
1588 | Cleans up. | |
1589 | ||
1590 | =cut | |
1591 | */ | |
1592 | void i_free_gen_read_data(i_gen_read_data *self) { | |
1593 | myfree(self); | |
1594 | } | |
1595 | ||
1596 | /* | |
1597 | =item i_gen_writer(i_gen_write_data *info, char const *data, int size) | |
1598 | ||
1599 | Performs write buffering for a callback based file writer. | |
1600 | ||
1601 | Failures are considered fatal, if a write fails then data will be | |
1602 | dropped. | |
1603 | ||
1604 | =cut | |
1605 | */ | |
1606 | int | |
1607 | i_gen_writer( | |
1608 | i_gen_write_data *self, | |
1609 | char const *data, | |
1610 | int size) | |
1611 | { | |
1612 | if (self->filledto && self->filledto+size > self->maxlength) { | |
1613 | if (self->cb(self->userdata, self->buffer, self->filledto)) { | |
1614 | self->filledto = 0; | |
1615 | } | |
1616 | else { | |
1617 | self->filledto = 0; | |
1618 | return 0; | |
1619 | } | |
1620 | } | |
1621 | if (self->filledto+size <= self->maxlength) { | |
1622 | /* just save it */ | |
1623 | memcpy(self->buffer+self->filledto, data, size); | |
1624 | self->filledto += size; | |
1625 | return 1; | |
1626 | } | |
1627 | /* doesn't fit - hand it off */ | |
1628 | return self->cb(self->userdata, data, size); | |
1629 | } | |
1630 | ||
1631 | /* | |
1632 | =item i_gen_write_data_new(i_write_callback_t cb, char *userdata, int max_length) | |
1633 | ||
1634 | Allocates and initializes the data structure used by i_gen_writer. | |
1635 | ||
1636 | This should be released with L<image.c/i_free_gen_write_data> | |
1637 | ||
1638 | =cut | |
1639 | */ | |
1640 | i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb, | |
1641 | char *userdata, int max_length) | |
1642 | { | |
1643 | i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data)); | |
1644 | self->cb = cb; | |
1645 | self->userdata = userdata; | |
1646 | self->maxlength = i_min(max_length, sizeof(self->buffer)); | |
1647 | if (self->maxlength < 0) | |
1648 | self->maxlength = sizeof(self->buffer); | |
1649 | self->filledto = 0; | |
1650 | ||
1651 | return self; | |
1652 | } | |
1653 | ||
1654 | /* | |
1655 | =item i_free_gen_write_data(i_gen_write_data *info, int flush) | |
1656 | ||
1657 | Cleans up the write buffer. | |
1658 | ||
1659 | Will flush any left-over data if I<flush> is non-zero. | |
1660 | ||
1661 | Returns non-zero if flush is zero or if info->cb() returns non-zero. | |
1662 | ||
1663 | Return zero only if flush is non-zero and info->cb() returns zero. | |
1664 | ie. if it fails. | |
1665 | ||
1666 | =cut | |
1667 | */ | |
1668 | ||
1669 | int i_free_gen_write_data(i_gen_write_data *info, int flush) | |
1670 | { | |
1671 | int result = !flush || | |
1672 | info->filledto == 0 || | |
1673 | info->cb(info->userdata, info->buffer, info->filledto); | |
1674 | myfree(info); | |
1675 | ||
1676 | return result; | |
1677 | } | |
1678 | ||
1679 | 1491 | struct magic_entry { |
1680 | 1492 | unsigned char *magic; |
1681 | 1493 | size_t magic_size; |
1800 | 1612 | unsigned char head[18]; |
1801 | 1613 | ssize_t rc; |
1802 | 1614 | |
1803 | io_glue_commit_types(data); | |
1804 | rc = data->readcb(data, head, 18); | |
1615 | rc = i_io_peekn(data, head, 18); | |
1805 | 1616 | if (rc == -1) return NULL; |
1806 | data->seekcb(data, -rc, SEEK_CUR); | |
1617 | #if 0 | |
1618 | { | |
1619 | int i; | |
1620 | fprintf(stderr, "%d bytes -", (int)rc); | |
1621 | for (i = 0; i < rc; ++i) | |
1622 | fprintf(stderr, " %02x", head[i]); | |
1623 | fprintf(stderr, "\n"); | |
1624 | } | |
1625 | #endif | |
1807 | 1626 | |
1808 | 1627 | for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) { |
1809 | 1628 | struct magic_entry const *entry = formats + i; |
244 | 244 | size_t name_buf_size); |
245 | 245 | |
246 | 246 | #endif /* End of freetype headers */ |
247 | ||
248 | /* functions for reading and writing formats */ | |
249 | ||
250 | /* general reader callback | |
251 | userdata - data the user passed into the reader | |
252 | buffer - the buffer to fill with data | |
253 | need - the amount of data needed | |
254 | want - the amount of space we have to store data | |
255 | fill buffer and return the number of bytes read, 0 for eof, -1 for error | |
256 | */ | |
257 | ||
258 | typedef int (*i_read_callback_t)(char *userdata, char *buffer, int need, | |
259 | int want); | |
260 | ||
261 | /* i_gen_reader() translates the low-level requests from whatever library | |
262 | into buffered requests. | |
263 | but the called function can always bypass buffering by only ever | |
264 | reading I<need> bytes. | |
265 | */ | |
266 | #define CBBUFSIZ 4096 | |
267 | ||
268 | typedef struct { | |
269 | i_read_callback_t cb; | |
270 | char *userdata; | |
271 | char buffer[CBBUFSIZ]; | |
272 | int length; | |
273 | int cpos; | |
274 | } i_gen_read_data; | |
275 | ||
276 | extern int i_gen_reader(i_gen_read_data *info, char *buffer, int need); | |
277 | extern i_gen_read_data *i_gen_read_data_new(i_read_callback_t cb, char *userdata); | |
278 | extern void i_free_gen_read_data(i_gen_read_data *); | |
279 | ||
280 | /* general writer callback | |
281 | userdata - the data the user passed into the writer | |
282 | data - the data to write | |
283 | data_size - the number of bytes to write | |
284 | write the data, return non-zero on success, zero on failure. | |
285 | */ | |
286 | typedef int (*i_write_callback_t)(char *userdata, char const *data, int size); | |
287 | ||
288 | typedef struct { | |
289 | i_write_callback_t cb; | |
290 | char *userdata; | |
291 | char buffer[CBBUFSIZ]; | |
292 | int maxlength; | |
293 | int filledto; | |
294 | } i_gen_write_data; | |
295 | ||
296 | extern int i_gen_writer(i_gen_write_data *info, char const *data, int size); | |
297 | extern i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb, char *userdata, int maxlength); | |
298 | extern int i_free_gen_write_data(i_gen_write_data *, int flush); | |
299 | 247 | |
300 | 248 | extern void i_quant_makemap(i_quantize *quant, i_img **imgs, int count); |
301 | 249 | extern i_palidx *i_quant_translate(i_quantize *quant, i_img *img); |
607 | 607 | od_custom /* custom 8x8 map */ |
608 | 608 | } i_ord_dith; |
609 | 609 | |
610 | typedef struct i_gif_pos_tag { | |
611 | int x, y; | |
612 | } i_gif_pos; | |
613 | ||
614 | 610 | /* passed into i_writegif_gen() to control quantization */ |
615 | 611 | typedef struct i_quantize_tag { |
616 | 612 | int version; |
653 | 649 | /* version 2 members after here */ |
654 | 650 | } i_quantize; |
655 | 651 | |
656 | typedef struct i_gif_opts { | |
657 | /* each image has a local color map */ | |
658 | int each_palette; | |
659 | ||
660 | /* images are interlaced */ | |
661 | int interlace; | |
662 | ||
663 | /* time for which image is displayed | |
664 | (in 1/100 seconds) | |
665 | default: 0 | |
666 | */ | |
667 | int delay_count; | |
668 | int *delays; | |
669 | ||
670 | /* user input flags | |
671 | default: 0 | |
672 | */ | |
673 | int user_input_count; | |
674 | char *user_input_flags; | |
675 | ||
676 | /* disposal | |
677 | default: 0 */ | |
678 | int disposal_count; | |
679 | char *disposal; | |
680 | ||
681 | /* this is added to the color table when we make an image transparent */ | |
682 | i_color tran_color; | |
683 | ||
684 | /* image positions */ | |
685 | int position_count; | |
686 | i_gif_pos *positions; | |
687 | ||
688 | /* Netscape loop extension - number of loops */ | |
689 | int loop_count; | |
690 | ||
691 | /* should be eliminate unused colors? */ | |
692 | int eliminate_unused; | |
693 | } i_gif_opts; | |
694 | ||
695 | 652 | /* distance measures used by some filters */ |
696 | 653 | enum { |
697 | 654 | i_dmeasure_euclidean = 0, |
711 | 668 | #define I_FORMAT_ATTR(format_index, va_index) |
712 | 669 | #endif |
713 | 670 | |
671 | #ifdef _MSC_VER | |
672 | # ifndef vsnprintf | |
673 | # define vsnprintf _vsnprintf | |
674 | # endif | |
675 | # ifndef snprintf | |
676 | # define snprintf _snprintf | |
677 | # endif | |
678 | #endif | |
679 | ||
714 | 680 | /* |
715 | 681 | =item i_DF |
716 | 682 | =category Data Types |
130 | 130 | i_render_color, |
131 | 131 | i_render_fill, |
132 | 132 | i_render_line, |
133 | i_render_linef | |
133 | i_render_linef, | |
134 | ||
135 | /* level 6 */ | |
136 | i_io_getc_imp, | |
137 | i_io_peekc_imp, | |
138 | i_io_peekn, | |
139 | i_io_putc_imp, | |
140 | i_io_read, | |
141 | i_io_write, | |
142 | i_io_seek, | |
143 | i_io_flush, | |
144 | i_io_close, | |
145 | i_io_set_buffered, | |
146 | i_io_gets, | |
147 | io_new_fd, | |
148 | io_new_bufchain, | |
149 | io_new_buffer, | |
150 | io_new_cb, | |
151 | io_slurp, | |
152 | io_glue_destroy | |
134 | 153 | }; |
135 | 154 | |
136 | 155 | /* in general these functions aren't called by Imager internally, but |
233 | 233 | #define i_render_linef(r, x, y, width, src, line, combine) \ |
234 | 234 | ((im_extt->f_i_render_linef)((r), (x), (y), (width), (src), (line), (combine))) |
235 | 235 | |
236 | #define i_io_getc_imp (im_extt->f_i_io_getc_imp) | |
237 | #define i_io_peekc_imp (im_extt->f_i_io_peekc_imp) | |
238 | #define i_io_peekn (im_extt->f_i_io_peekn) | |
239 | #define i_io_putc_imp (im_extt->f_i_io_putc_imp) | |
240 | #define i_io_read (im_extt->f_i_io_read) | |
241 | #define i_io_write (im_extt->f_i_io_write) | |
242 | #define i_io_seek (im_extt->f_i_io_seek) | |
243 | #define i_io_flush (im_extt->f_i_io_flush) | |
244 | #define i_io_close (im_extt->f_i_io_close) | |
245 | #define i_io_set_buffered (im_extt->f_i_io_set_buffered) | |
246 | #define i_io_gets (im_extt->f_i_io_gets) | |
247 | #define io_new_fd(fd) ((im_extt->f_io_new_fd)(fd)) | |
248 | #define io_new_bufchain() ((im_extt->f_io_new_bufchain)()) | |
249 | #define io_new_buffer(data, len, closecb, closedata) \ | |
250 | ((im_extt->f_io_new_buffer)((data), (len), (closecb), (closedata))) | |
251 | #define io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb) \ | |
252 | ((im_extt->f_io_new_cb)((p), (readcb), (writecb), (seekcb), (closecb), (destroycb))) | |
253 | #define io_slurp(ig, datap) ((im_extt->f_io_slurp)((ig), (datap))) | |
254 | #define io_glue_destroy(ig) ((im_extt->f_io_glue_destroy)(ig)) | |
255 | ||
236 | 256 | #ifdef IMAGER_LOG |
237 | 257 | #define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; } |
238 | 258 | #else |
13 | 13 | Version 2 changed the types of some parameters and pointers. A |
14 | 14 | simple recompile should be enough in most cases. |
15 | 15 | |
16 | Version 3 changed the behaviour of some of the I/O layer functions, | |
17 | and in some cases the initial seek position when calling file | |
18 | readers. Switching away from calling readcb etc to i_io_read() etc | |
19 | should fix your code. | |
20 | ||
16 | 21 | */ |
17 | #define IMAGER_API_VERSION 2 | |
22 | #define IMAGER_API_VERSION 3 | |
18 | 23 | |
19 | 24 | /* |
20 | 25 | IMAGER_API_LEVEL is the level of the structure. New function pointers |
22 | 27 | will result in an increment of IMAGER_API_LEVEL. |
23 | 28 | */ |
24 | 29 | |
25 | #define IMAGER_API_LEVEL 6 | |
30 | #define IMAGER_API_LEVEL 7 | |
26 | 31 | |
27 | 32 | typedef struct { |
28 | 33 | int version; |
184 | 189 | i_img_dim width, const double *src, |
185 | 190 | i_fcolor *line, i_fill_combinef_f combine); |
186 | 191 | |
187 | /* IMAGER_API_LEVEL 6 functions will be added here */ | |
192 | /* Level 6 lost to mis-numbering */ | |
193 | /* IMAGER_API_LEVEL 7 */ | |
194 | int (*f_i_io_getc_imp)(io_glue *ig); | |
195 | int (*f_i_io_peekc_imp)(io_glue *ig); | |
196 | ssize_t (*f_i_io_peekn)(io_glue *ig, void *buf, size_t size); | |
197 | int (*f_i_io_putc_imp)(io_glue *ig, int c); | |
198 | ssize_t (*f_i_io_read)(io_glue *, void *buf, size_t size); | |
199 | ssize_t (*f_i_io_write)(io_glue *, const void *buf, size_t size); | |
200 | off_t (*f_i_io_seek)(io_glue *, off_t offset, int whence); | |
201 | int (*f_i_io_flush)(io_glue *ig); | |
202 | int (*f_i_io_close)(io_glue *ig); | |
203 | int (*f_i_io_set_buffered)(io_glue *ig, int buffered); | |
204 | ssize_t (*f_i_io_gets)(io_glue *ig, char *, size_t, int); | |
205 | ||
206 | i_io_glue_t *(*f_io_new_fd)(int fd); | |
207 | i_io_glue_t *(*f_io_new_bufchain)(void); | |
208 | i_io_glue_t *(*f_io_new_buffer)(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
209 | i_io_glue_t *(*f_io_new_cb)(void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb); | |
210 | size_t (*f_io_slurp)(i_io_glue_t *ig, unsigned char **c); | |
211 | void (*f_io_glue_destroy)(i_io_glue_t *ig); | |
212 | ||
213 | /* IMAGER_API_LEVEL 8 functions will be added here */ | |
214 | ||
188 | 215 | } im_ext_funcs; |
189 | 216 | |
190 | 217 | #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table" |
244 | 244 | } |
245 | 245 | } |
246 | 246 | if(defined($args{INC})) { |
247 | foreach my $arg (split(' ', $args{INC})) { | |
247 | foreach my $arg (_shellwords($args{INC})) { | |
248 | 248 | die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); |
249 | 249 | push @incpaths, substr($arg, 2); |
250 | 250 | } |
251 | 251 | } |
252 | 252 | |
253 | my @cc = _findcc(); | |
253 | my ($cc, $ld) = _findcc(); | |
254 | 254 | my @missing; |
255 | 255 | my @wrongresult; |
256 | 256 | my @use_headers; |
270 | 270 | if ( $Config{cc} eq 'cl' ) { # Microsoft compiler |
271 | 271 | require Win32; |
272 | 272 | @sys_cmd = ( |
273 | @cc, | |
273 | @$cc, | |
274 | 274 | $cfile, |
275 | 275 | "/Fe$exefile", |
276 | (map { '/I'.Win32::GetShortPathName($_) } @incpaths) | |
276 | (map { '/I'.Win32::GetShortPathName($_) } @incpaths), | |
277 | "/link", | |
278 | @$ld | |
277 | 279 | ); |
278 | 280 | } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland |
279 | 281 | @sys_cmd = ( |
280 | @cc, | |
282 | @$cc, | |
281 | 283 | (map { "-I$_" } @incpaths), |
282 | 284 | "-o$exefile", |
283 | $cfile | |
285 | $cfile, | |
286 | @$ld | |
284 | 287 | ); |
285 | 288 | } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... |
286 | 289 | @sys_cmd = ( |
287 | @cc, | |
290 | @$cc, | |
291 | @$ld, | |
288 | 292 | $cfile, |
289 | 293 | (map { "-I$_" } @incpaths), |
290 | 294 | "-o", "$exefile" |
316 | 320 | } @libpaths; |
317 | 321 | # this is horribly sensitive to the order of arguments |
318 | 322 | @sys_cmd = ( |
319 | @cc, | |
323 | @$cc, | |
320 | 324 | $cfile, |
321 | 325 | ( map { "$_.lib" } @$libs ), |
322 | 326 | "/Fe$exefile", |
323 | 327 | (map { '/I'.Win32::GetShortPathName($_) } @incpaths), |
324 | 328 | "/link", |
329 | @$ld, | |
325 | 330 | (map {'/libpath:'.Win32::GetShortPathName($_)} @$paths), |
326 | 331 | ); |
327 | 332 | } elsif($Config{cc} eq 'CC/DECC') { # VMS |
328 | 333 | } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland |
329 | 334 | @sys_cmd = ( |
330 | @cc, | |
335 | @$cc, | |
336 | @$ld, | |
331 | 337 | "-o$exefile", |
332 | 338 | (map { "-l$_" } @$libs ), |
333 | 339 | (map { "-I$_" } @incpaths), |
336 | 342 | } else { # Unix-ish |
337 | 343 | # gcc, Sun, AIX (gcc, cc) |
338 | 344 | @sys_cmd = ( |
339 | @cc, | |
345 | @$cc, | |
346 | @$ld, | |
340 | 347 | $cfile, |
341 | 348 | "-o", "$exefile", |
342 | 349 | (map { "-l$_" } @$libs ), |
367 | 374 | unlink $exefile if -f $exefile; |
368 | 375 | unlink $ofile if -f $ofile; |
369 | 376 | unlink "$exefile\.manifest" if -f "$exefile\.manifest"; |
377 | if ( $Config{cc} eq 'cl' ) { | |
378 | # MSVC also creates foo.ilk and foo.pdb | |
379 | my $ilkfile = $exefile; | |
380 | $ilkfile =~ s/$Config{_exe}$/.ilk/; | |
381 | my $pdbfile = $exefile; | |
382 | $pdbfile =~ s/$Config{_exe}$/.pdb/; | |
383 | unlink $ilkfile if -f $ilkfile; | |
384 | unlink $pdbfile if -f $pdbfile; | |
385 | } | |
370 | 386 | return |
371 | 387 | } |
372 | ||
388 | ||
389 | # return ($cc, $ld) | |
390 | # where $cc is an array ref of compiler name, compiler flags | |
391 | # where $ld is an array ref of linker flags | |
373 | 392 | sub _findcc { |
374 | 393 | # Need to use $keep=1 to work with MSWin32 backslashes and quotes |
375 | my @Config_ccflags_ldflags = @Config{qw(ccflags ldflags)}; # use copy so ASPerl will compile | |
376 | my @flags = grep { length } map { quotewords('\s+', 1, $_ || ()) } @Config_ccflags_ldflags; | |
394 | my $Config_ccflags = $Config{ccflags}; # use copy so ASPerl will compile | |
395 | my @Config_ldflags = @Config{qw(ldflags perllibs)}; | |
396 | my @ccflags = grep { length } quotewords('\s+', 1, $Config_ccflags); | |
397 | my @ldflags = grep { length } quotewords('\s+', 1, @Config_ldflags); | |
377 | 398 | my @paths = split(/$Config{path_sep}/, $ENV{PATH}); |
378 | 399 | my @cc = split(/\s+/, $Config{cc}); |
379 | return (@cc, @flags) if -x $cc[0]; | |
400 | return ( [ @cc, @ccflags ], \@ldflags ) if -x $cc[0]; | |
380 | 401 | foreach my $path (@paths) { |
381 | 402 | my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; |
382 | return ($compiler, @cc[1 .. $#cc], @flags) if -x $compiler; | |
403 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) | |
404 | if -x $compiler; | |
383 | 405 | } |
384 | 406 | die("Couldn't find your C compiler\n"); |
407 | } | |
408 | ||
409 | sub _shellwords { | |
410 | my $line = shift; | |
411 | ||
412 | if ($^O eq "MSWin32") { | |
413 | my @elements; | |
414 | $line =~ s/^\s+//; | |
415 | while ($line =~ s/^"([^"]*)"// || $line =~ s/^(\S+)//) { | |
416 | push @elements, $1; | |
417 | $line =~ s/^\s+//; | |
418 | } | |
419 | return @elements; | |
420 | } | |
421 | else { | |
422 | return grep defined && /\S/, quotewords('\s+', 0, $line); | |
423 | } | |
385 | 424 | } |
386 | 425 | |
387 | 426 | # code substantially borrowed from IPC::Run3 |
11 | 11 | #include "imageri.h" |
12 | 12 | |
13 | 13 | #define IOL_DEB(x) |
14 | ||
14 | #define IOL_DEBs stderr | |
15 | ||
16 | #define IO_BUF_SIZE 8192 | |
15 | 17 | |
16 | 18 | char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" }; |
17 | 19 | |
24 | 26 | } io_blink; |
25 | 27 | |
26 | 28 | |
27 | /* Structures that describe callback interfaces */ | |
28 | ||
29 | 29 | typedef struct { |
30 | off_t offset; | |
30 | i_io_glue_t base; | |
31 | int fd; | |
32 | } io_fdseek; | |
33 | ||
34 | typedef struct { | |
35 | i_io_glue_t base; | |
36 | const char *data; | |
37 | size_t len; | |
38 | i_io_closebufp_t closecb; /* free memory mapped segment or decrement refcount */ | |
39 | void *closedata; | |
31 | 40 | off_t cpos; |
32 | } io_ex_rseek; | |
33 | ||
41 | } io_buffer; | |
34 | 42 | |
35 | 43 | typedef struct { |
36 | off_t offset; | |
37 | off_t cpos; | |
38 | io_blink *head; | |
39 | io_blink *tail; | |
40 | io_blink *cp; | |
41 | } io_ex_fseek; | |
42 | ||
44 | i_io_glue_t base; | |
45 | void *p; /* Callback data */ | |
46 | i_io_readl_t readcb; | |
47 | i_io_writel_t writecb; | |
48 | i_io_seekl_t seekcb; | |
49 | i_io_closel_t closecb; | |
50 | i_io_destroyl_t destroycb; | |
51 | } io_cb; | |
43 | 52 | |
44 | 53 | typedef struct { |
45 | 54 | off_t offset; /* Offset of the source - not used */ |
52 | 61 | off_t gpos; /* Global position in stream */ |
53 | 62 | } io_ex_bchain; |
54 | 63 | |
55 | typedef struct { | |
56 | off_t offset; /* Offset of the source - not used */ | |
57 | off_t cpos; /* Offset within the current */ | |
58 | } io_ex_buffer; | |
59 | ||
60 | static void io_obj_setp_buffer(io_obj *io, char *p, size_t len, i_io_closebufp_t closecb, void *closedata); | |
61 | static void io_obj_setp_cb2 (io_obj *io, void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb); | |
62 | ||
63 | 64 | /* turn current offset, file length, whence and offset into a new offset */ |
64 | 65 | #define calc_seek_offset(curr_off, length, offset, whence) \ |
65 | 66 | (((whence) == SEEK_SET) ? (offset) : \ |
75 | 76 | |
76 | 77 | io_glue *ig = io_new_fd( fileno(stdin) ); |
77 | 78 | method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet |
78 | io_glue_commit_types(ig); // always assume IOL_SEEK for now | |
79 | ||
79 | 80 | switch (method) { |
80 | 81 | case IOL_NOSEEK: |
81 | 82 | code that uses ig->readcb() |
110 | 111 | =cut |
111 | 112 | */ |
112 | 113 | |
114 | static void | |
115 | i_io_init(io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb, | |
116 | i_io_seekp_t seekcb); | |
117 | ||
113 | 118 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count); |
114 | 119 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count); |
115 | 120 | static off_t fd_seek(io_glue *ig, off_t offset, int whence); |
116 | 121 | static int fd_close(io_glue *ig); |
117 | 122 | static ssize_t fd_size(io_glue *ig); |
118 | 123 | static const char *my_strerror(int err); |
124 | static void i_io_setup_buffer(io_glue *ig); | |
125 | static void | |
126 | i_io_start_write(io_glue *ig); | |
127 | static int | |
128 | i_io_read_fill(io_glue *ig, ssize_t needed); | |
129 | static void | |
130 | dump_data(unsigned char *start, unsigned char *end, int bias); | |
131 | static ssize_t realseek_read(io_glue *igo, void *buf, size_t count); | |
132 | static ssize_t realseek_write(io_glue *igo, const void *buf, size_t count); | |
133 | static int realseek_close(io_glue *igo); | |
134 | static off_t realseek_seek(io_glue *igo, off_t offset, int whence); | |
135 | static void realseek_destroy(io_glue *igo); | |
136 | static ssize_t buffer_read(io_glue *igo, void *buf, size_t count); | |
137 | static ssize_t buffer_write(io_glue *ig, const void *buf, size_t count); | |
138 | static int buffer_close(io_glue *ig); | |
139 | static off_t buffer_seek(io_glue *igo, off_t offset, int whence); | |
140 | static void buffer_destroy(io_glue *igo); | |
141 | static io_blink*io_blink_new(void); | |
142 | static void io_bchain_advance(io_ex_bchain *ieb); | |
143 | static void io_destroy_bufchain(io_ex_bchain *ieb); | |
144 | static ssize_t bufchain_read(io_glue *ig, void *buf, size_t count); | |
145 | static ssize_t bufchain_write(io_glue *ig, const void *buf, size_t count); | |
146 | static int bufchain_close(io_glue *ig); | |
147 | static off_t bufchain_seek(io_glue *ig, off_t offset, int whence); | |
148 | static void bufchain_destroy(io_glue *ig); | |
149 | ||
150 | /* | |
151 | * Methods for setting up data source | |
152 | */ | |
153 | ||
154 | /* | |
155 | =item io_new_bufchain() | |
156 | =order 10 | |
157 | =category I/O Layers | |
158 | ||
159 | returns a new io_glue object that has the 'empty' source and but can | |
160 | be written to and read from later (like a pseudo file). | |
161 | ||
162 | =cut | |
163 | */ | |
164 | ||
165 | io_glue * | |
166 | io_new_bufchain() { | |
167 | io_glue *ig; | |
168 | io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain)); | |
169 | ||
170 | mm_log((1, "io_new_bufchain()\n")); | |
171 | ||
172 | ig = mymalloc(sizeof(io_glue)); | |
173 | memset(ig, 0, sizeof(*ig)); | |
174 | i_io_init(ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek); | |
175 | ||
176 | ieb->offset = 0; | |
177 | ieb->length = 0; | |
178 | ieb->cpos = 0; | |
179 | ieb->gpos = 0; | |
180 | ieb->tfill = 0; | |
181 | ||
182 | ieb->head = io_blink_new(); | |
183 | ieb->cp = ieb->head; | |
184 | ieb->tail = ieb->head; | |
185 | ||
186 | ig->exdata = ieb; | |
187 | ig->closecb = bufchain_close; | |
188 | ig->destroycb = bufchain_destroy; | |
189 | ||
190 | return ig; | |
191 | } | |
192 | ||
193 | /* | |
194 | =item io_new_buffer(data, length) | |
195 | =order 10 | |
196 | =category I/O Layers | |
197 | ||
198 | Returns a new io_glue object that has the source defined as reading | |
199 | from specified buffer. Note that the buffer is not copied. | |
200 | ||
201 | data - buffer to read from | |
202 | length - length of buffer | |
203 | ||
204 | =cut | |
205 | */ | |
206 | ||
207 | io_glue * | |
208 | io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { | |
209 | io_buffer *ig; | |
210 | ||
211 | mm_log((1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); | |
212 | ||
213 | ig = mymalloc(sizeof(io_buffer)); | |
214 | memset(ig, 0, sizeof(*ig)); | |
215 | i_io_init(&ig->base, BUFFER, buffer_read, buffer_write, buffer_seek); | |
216 | ig->data = data; | |
217 | ig->len = len; | |
218 | ig->closecb = closecb; | |
219 | ig->closedata = closedata; | |
220 | ||
221 | ig->cpos = 0; | |
222 | ||
223 | ig->base.closecb = buffer_close; | |
224 | ig->base.destroycb = buffer_destroy; | |
225 | ||
226 | return (io_glue *)ig; | |
227 | } | |
228 | ||
229 | ||
230 | /* | |
231 | =item io_new_fd(fd) | |
232 | =order 10 | |
233 | =category I/O Layers | |
234 | ||
235 | returns a new io_glue object that has the source defined as reading | |
236 | from specified file descriptor. Note that the the interface to receiving | |
237 | data from the io_glue callbacks hasn't been done yet. | |
238 | ||
239 | fd - file descriptor to read/write from | |
240 | ||
241 | =cut | |
242 | */ | |
243 | ||
244 | io_glue * | |
245 | io_new_fd(int fd) { | |
246 | io_fdseek *ig; | |
247 | ||
248 | mm_log((1, "io_new_fd(fd %d)\n", fd)); | |
249 | ||
250 | ig = mymalloc(sizeof(io_fdseek)); | |
251 | memset(ig, 0, sizeof(*ig)); | |
252 | i_io_init(&ig->base, FDSEEK, fd_read, fd_write, fd_seek); | |
253 | ig->fd = fd; | |
254 | ||
255 | ig->base.closecb = fd_close; | |
256 | ig->base.sizecb = fd_size; | |
257 | ig->base.destroycb = NULL; | |
258 | ||
259 | mm_log((1, "(%p) <- io_new_fd\n", ig)); | |
260 | return (io_glue *)ig; | |
261 | } | |
262 | ||
263 | /* | |
264 | =item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
265 | =category I/O Layers | |
266 | =order 10 | |
267 | ||
268 | Create a new I/O layer object that calls your supplied callbacks. | |
269 | ||
270 | In general the callbacks should behave like the corresponding POSIX | |
271 | primitives. | |
272 | ||
273 | =over | |
274 | ||
275 | =item * | |
276 | ||
277 | C<read_cb>(p, buffer, length) should read up to C<length> bytes into | |
278 | C<buffer> and return the number of bytes read. At end of file, return | |
279 | 0. On error, return -1. | |
280 | ||
281 | =item * | |
282 | ||
283 | C<write_cb>(p, buffer, length) should write up to C<length> bytes from | |
284 | C<buffer> and return the number of bytes written. A return value <= 0 | |
285 | will be treated as an error. | |
286 | ||
287 | =item * | |
288 | ||
289 | C<seekcb>(p, offset, whence) should seek and return the new offset. | |
290 | ||
291 | =item * | |
292 | ||
293 | C<close_cb>(p) should return 0 on success, -1 on failure. | |
294 | ||
295 | =item * | |
296 | ||
297 | C<destroy_cb>(p) should release any memory specific to your callback | |
298 | handlers. | |
299 | ||
300 | =back | |
301 | ||
302 | =cut | |
303 | */ | |
304 | ||
305 | io_glue * | |
306 | io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, | |
307 | i_io_seekl_t seekcb, i_io_closel_t closecb, | |
308 | i_io_destroyl_t destroycb) { | |
309 | io_cb *ig; | |
310 | ||
311 | mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " | |
312 | "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb)); | |
313 | ig = mymalloc(sizeof(io_cb)); | |
314 | memset(ig, 0, sizeof(*ig)); | |
315 | i_io_init(&ig->base, CBSEEK, realseek_read, realseek_write, realseek_seek); | |
316 | mm_log((1, "(%p) <- io_new_cb\n", ig)); | |
317 | ||
318 | ig->base.closecb = realseek_close; | |
319 | ig->base.destroycb = realseek_destroy; | |
320 | ||
321 | ig->p = p; | |
322 | ig->readcb = readcb; | |
323 | ig->writecb = writecb; | |
324 | ig->seekcb = seekcb; | |
325 | ig->closecb = closecb; | |
326 | ig->destroycb = destroycb; | |
327 | ||
328 | return (io_glue *)ig; | |
329 | } | |
330 | ||
331 | /* | |
332 | =item io_slurp(ig, c) | |
333 | =category I/O Layers | |
334 | ||
335 | Takes the source that the io_glue is bound to and allocates space for | |
336 | a return buffer and returns the entire content in a single buffer. | |
337 | Note: This only works for io_glue objects created by | |
338 | io_new_bufchain(). It is useful for saving to scalars and such. | |
339 | ||
340 | ig - io_glue object | |
341 | c - pointer to a pointer to where data should be copied to | |
342 | ||
343 | char *data; | |
344 | size_t size = io_slurp(ig, &data); | |
345 | ... do something with the data ... | |
346 | myfree(data); | |
347 | ||
348 | io_slurp() will abort the program if the supplied I/O layer is not | |
349 | from io_new_bufchain(). | |
350 | ||
351 | =cut | |
352 | */ | |
353 | ||
354 | size_t | |
355 | io_slurp(io_glue *ig, unsigned char **c) { | |
356 | ssize_t rc; | |
357 | off_t orgoff; | |
358 | io_ex_bchain *ieb; | |
359 | unsigned char *cc; | |
360 | io_type inn = ig->type; | |
361 | ||
362 | if ( inn != BUFCHAIN ) { | |
363 | i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n"); | |
364 | } | |
365 | ||
366 | ieb = ig->exdata; | |
367 | cc = *c = mymalloc( ieb->length ); | |
368 | ||
369 | orgoff = ieb->gpos; | |
370 | ||
371 | bufchain_seek(ig, 0, SEEK_SET); | |
372 | ||
373 | rc = bufchain_read(ig, cc, ieb->length); | |
374 | ||
375 | if (rc != ieb->length) | |
376 | i_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length); | |
377 | ||
378 | return rc; | |
379 | } | |
380 | ||
381 | /* | |
382 | =item io_glue_destroy(ig) | |
383 | =category I/O Layers | |
384 | =order 90 | |
385 | =synopsis io_glue_destroy(ig); | |
386 | ||
387 | Destroy an io_glue objects. Should clean up all related buffers. | |
388 | ||
389 | ig - io_glue object to destroy. | |
390 | ||
391 | =cut | |
392 | */ | |
393 | ||
394 | void | |
395 | io_glue_destroy(io_glue *ig) { | |
396 | mm_log((1, "io_glue_DESTROY(ig %p)\n", ig)); | |
397 | ||
398 | if (ig->destroycb) | |
399 | ig->destroycb(ig); | |
400 | ||
401 | if (ig->buffer) | |
402 | myfree(ig->buffer); | |
403 | ||
404 | myfree(ig); | |
405 | } | |
406 | ||
407 | /* | |
408 | =item i_io_getc(ig) | |
409 | =category I/O Layers | |
410 | ||
411 | A macro to read a single byte from a buffered I/O glue object. | |
412 | ||
413 | Returns EOF on failure, or a byte. | |
414 | ||
415 | =cut | |
416 | */ | |
417 | ||
418 | int | |
419 | i_io_getc_imp(io_glue *ig) { | |
420 | if (ig->write_ptr) | |
421 | return EOF; | |
422 | ||
423 | if (ig->error || ig->buf_eof) | |
424 | return EOF; | |
425 | ||
426 | if (!ig->buffered) { | |
427 | unsigned char buf; | |
428 | ssize_t rc = i_io_raw_read(ig, &buf, 1); | |
429 | if (rc > 0) { | |
430 | return buf; | |
431 | } | |
432 | else if (rc == 0) { | |
433 | ig->buf_eof = 1; | |
434 | return EOF; | |
435 | } | |
436 | else { | |
437 | ig->error = 1; | |
438 | return EOF; | |
439 | } | |
440 | } | |
441 | ||
442 | if (!ig->buffer) | |
443 | i_io_setup_buffer(ig); | |
444 | ||
445 | if (!ig->read_ptr || ig->read_ptr == ig->read_end) { | |
446 | if (!i_io_read_fill(ig, 1)) | |
447 | return EOF; | |
448 | } | |
449 | ||
450 | return *(ig->read_ptr++); | |
451 | } | |
452 | ||
453 | /* | |
454 | =item i_io_peekc(ig) | |
455 | =category I/O Layers | |
456 | ||
457 | Read the next character from the stream without advancing the stream. | |
458 | ||
459 | On error or end of file, return EOF. | |
460 | ||
461 | For unbuffered streams a single character buffer will be setup. | |
462 | ||
463 | =cut | |
464 | */ | |
465 | ||
466 | int | |
467 | i_io_peekc_imp(io_glue *ig) { | |
468 | if (ig->write_ptr) | |
469 | return EOF; | |
470 | ||
471 | if (!ig->buffer) | |
472 | i_io_setup_buffer(ig); | |
473 | ||
474 | if (!ig->buffered) { | |
475 | ssize_t rc = i_io_raw_read(ig, ig->buffer, 1); | |
476 | if (rc > 0) { | |
477 | ig->read_ptr = ig->buffer; | |
478 | ig->read_end = ig->buffer + 1; | |
479 | return *(ig->buffer); | |
480 | } | |
481 | else if (rc == 0) { | |
482 | ig->buf_eof = 1; | |
483 | return EOF; | |
484 | } | |
485 | else { | |
486 | ig->error = 1; | |
487 | return EOF; | |
488 | } | |
489 | } | |
490 | ||
491 | if (!ig->read_ptr || ig->read_ptr == ig->read_end) { | |
492 | if (ig->error || ig->buf_eof) | |
493 | return EOF; | |
494 | ||
495 | if (!i_io_read_fill(ig, 1)) | |
496 | return EOF; | |
497 | } | |
498 | ||
499 | return *(ig->read_ptr); | |
500 | } | |
501 | ||
502 | /* | |
503 | =item i_io_peekn(ig, buffer, size) | |
504 | =category I/O Layers | |
505 | =synopsis ssize_t count = i_io_peekn(ig, buffer, sizeof(buffer)); | |
506 | ||
507 | Buffer at least C<size> (at most C<< ig->buf_size >> bytes of data | |
508 | from the stream and return C<size> bytes of it to the caller in | |
509 | C<buffer>. | |
510 | ||
511 | This ignores the buffered state of the stream, and will always setup | |
512 | buffering if needed. | |
513 | ||
514 | If no C<type> parameter is provided to Imager::read() or | |
515 | Imager::read_multi(), Imager will call C<i_io_peekn()> when probing | |
516 | for the file format. | |
517 | ||
518 | Returns -1 on error, 0 if there is no data before EOF, or the number | |
519 | of bytes read into C<buffer>. | |
520 | ||
521 | =cut | |
522 | */ | |
523 | ||
524 | ssize_t | |
525 | i_io_peekn(io_glue *ig, void *buf, size_t size) { | |
526 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn(%p, %p, %d)\n", ig, buf, (int)size)); | |
527 | ||
528 | if (size == 0) { | |
529 | i_push_error(0, "peekn size must be positive"); | |
530 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (zero size)\n")); | |
531 | return -1; | |
532 | } | |
533 | ||
534 | if (ig->write_ptr) { | |
535 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (write_ptr set)\n")); | |
536 | return -1; | |
537 | } | |
538 | ||
539 | if (!ig->buffer) | |
540 | i_io_setup_buffer(ig); | |
541 | ||
542 | if ((!ig->read_ptr || size > ig->read_end - ig->read_ptr) | |
543 | && !(ig->buf_eof || ig->error)) { | |
544 | i_io_read_fill(ig, size); | |
545 | } | |
546 | ||
547 | if (size > ig->read_end - ig->read_ptr) | |
548 | size = ig->read_end - ig->read_ptr; | |
549 | ||
550 | if (size) | |
551 | memcpy(buf, ig->read_ptr, size); | |
552 | else if (ig->buf_eof) { | |
553 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => 0 (eof)\n")); | |
554 | return 0; | |
555 | } | |
556 | else if (ig->error) { | |
557 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (error)\n")); | |
558 | return -1; | |
559 | } | |
560 | else { | |
561 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() - size 0 but not eof or error!\n")); | |
562 | return -1; | |
563 | } | |
564 | ||
565 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => %d\n", (int)size)); | |
566 | ||
567 | return size; | |
568 | } | |
569 | ||
570 | /* | |
571 | =item i_io_putc(ig, c) | |
572 | =category I/O Layers | |
573 | ||
574 | Write a single character to the stream. | |
575 | ||
576 | On success return c, on error returns EOF | |
577 | ||
578 | =cut | |
579 | */ | |
580 | ||
581 | int | |
582 | i_io_putc_imp(io_glue *ig, int c) { | |
583 | IOL_DEB(fprintf(IOL_DEBs, "i_io_putc_imp(%p, %d)\n", ig, c)); | |
584 | ||
585 | if (!ig->buffered) { | |
586 | char buf = c; | |
587 | ssize_t write_result; | |
588 | int result = c; | |
589 | ||
590 | if (ig->error) | |
591 | return EOF; | |
592 | ||
593 | write_result = i_io_raw_write(ig, &buf, 1); | |
594 | if (write_result != 1) { | |
595 | ig->error = 1; | |
596 | result = EOF; | |
597 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered putc() failed, setting error mode\n")); | |
598 | } | |
599 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered: result %d\n", result)); | |
600 | ||
601 | return result; | |
602 | } | |
603 | ||
604 | if (ig->read_ptr) | |
605 | return EOF; | |
606 | ||
607 | if (ig->error) | |
608 | return EOF; | |
609 | ||
610 | if (!ig->buffer) | |
611 | i_io_setup_buffer(ig); | |
612 | ||
613 | if (ig->write_ptr && ig->write_ptr == ig->write_end) { | |
614 | if (!i_io_flush(ig)) | |
615 | return EOF; | |
616 | } | |
617 | ||
618 | i_io_start_write(ig); | |
619 | ||
620 | *(ig->write_ptr)++ = c; | |
621 | ||
622 | return (unsigned char)c; | |
623 | } | |
624 | ||
625 | /* | |
626 | =item i_io_read(io, buffer, size) | |
627 | =category I/O Layers | |
628 | ||
629 | Read up to C<size> bytes from the stream C<io> into C<buffer>. | |
630 | ||
631 | Returns the number of bytes read. Returns 0 on end of file. Returns | |
632 | -1 on error. | |
633 | ||
634 | =cut | |
635 | */ | |
636 | ||
637 | ssize_t | |
638 | i_io_read(io_glue *ig, void *buf, size_t size) { | |
639 | unsigned char *pbuf = buf; | |
640 | ssize_t read_total = 0; | |
641 | ||
642 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read(%p, %p, %u)\n", ig, buf, (unsigned)size)); | |
643 | ||
644 | if (ig->write_ptr) { | |
645 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => -1 (write_ptr set)\n")); | |
646 | return -1; | |
647 | } | |
648 | ||
649 | if (!ig->buffer && ig->buffered) | |
650 | i_io_setup_buffer(ig); | |
651 | ||
652 | if (ig->read_ptr && ig->read_ptr < ig->read_end) { | |
653 | size_t alloc = ig->read_end - ig->read_ptr; | |
654 | ||
655 | if (alloc > size) | |
656 | alloc = size; | |
657 | ||
658 | memcpy(pbuf, ig->read_ptr, alloc); | |
659 | ig->read_ptr += alloc; | |
660 | pbuf += alloc; | |
661 | size -= alloc; | |
662 | read_total += alloc; | |
663 | } | |
664 | ||
665 | if (size > 0 && !(ig->error || ig->buf_eof)) { | |
666 | if (!ig->buffered || size > ig->buf_size) { | |
667 | ssize_t rc; | |
668 | ||
669 | while (size > 0 && (rc = i_io_raw_read(ig, pbuf, size)) > 0) { | |
670 | size -= rc; | |
671 | pbuf += rc; | |
672 | read_total += rc; | |
673 | } | |
674 | ||
675 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => %d (raw read)\n", (int)read_total)); | |
676 | ||
677 | if (rc < 0) | |
678 | ig->error = 1; | |
679 | else if (rc == 0) | |
680 | ig->buf_eof = 1; | |
681 | ||
682 | if (!read_total) | |
683 | return rc; | |
684 | } | |
685 | else { | |
686 | if (i_io_read_fill(ig, size)) { | |
687 | size_t alloc = ig->read_end - ig->read_ptr; | |
688 | if (alloc > size) | |
689 | alloc = size; | |
690 | ||
691 | memcpy(pbuf, ig->read_ptr, alloc); | |
692 | ig->read_ptr += alloc; | |
693 | pbuf += alloc; | |
694 | size -= alloc; | |
695 | read_total += alloc; | |
696 | } | |
697 | else { | |
698 | if (!read_total && ig->error) { | |
699 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => -1 (fill failure)\n")); | |
700 | return -1; | |
701 | } | |
702 | } | |
703 | } | |
704 | } | |
705 | ||
706 | if (!read_total && ig->error) | |
707 | read_total = -1; | |
708 | ||
709 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read() => %d\n", (int)read_total)); | |
710 | ||
711 | return read_total; | |
712 | } | |
713 | ||
714 | /* | |
715 | =item i_io_write(io, buffer, size) | |
716 | =category I/O Layers | |
717 | =synopsis ssize_t result = i_io_write(io, buffer, size) | |
718 | ||
719 | Write to the given I/O stream. | |
720 | ||
721 | Returns the number of bytes written. | |
722 | ||
723 | =cut | |
724 | */ | |
725 | ||
726 | ssize_t | |
727 | i_io_write(io_glue *ig, const void *buf, size_t size) { | |
728 | const unsigned char *pbuf = buf; | |
729 | size_t write_count = 0; | |
730 | ||
731 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write(%p, %p, %u)\n", ig, buf, (unsigned)size)); | |
732 | ||
733 | if (!ig->buffered) { | |
734 | ssize_t result; | |
735 | ||
736 | if (ig->error) { | |
737 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, error state\n")); | |
738 | return -1; | |
739 | } | |
740 | ||
741 | result = i_io_raw_write(ig, buf, size); | |
742 | ||
743 | if (result != size) { | |
744 | ig->error = 1; | |
745 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, setting error flag\n")); | |
746 | } | |
747 | ||
748 | IOL_DEB(fprintf(IOL_DEBs, " unbuffered, result: %d\n", (int)result)); | |
749 | ||
750 | return result; | |
751 | } | |
752 | ||
753 | if (ig->read_ptr) { | |
754 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (read_ptr set)\n")); | |
755 | return -1; | |
756 | } | |
757 | ||
758 | if (ig->error) { | |
759 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (error)\n")); | |
760 | return -1; | |
761 | } | |
762 | ||
763 | if (!ig->buffer) | |
764 | i_io_setup_buffer(ig); | |
765 | ||
766 | if (!ig->write_ptr) | |
767 | i_io_start_write(ig); | |
768 | ||
769 | if (ig->write_ptr && ig->write_ptr + size <= ig->write_end) { | |
770 | size_t alloc = ig->write_end - ig->write_ptr; | |
771 | if (alloc > size) | |
772 | alloc = size; | |
773 | memcpy(ig->write_ptr, pbuf, alloc); | |
774 | write_count += alloc; | |
775 | size -= alloc; | |
776 | pbuf += alloc; | |
777 | ig->write_ptr += alloc; | |
778 | } | |
779 | ||
780 | if (size) { | |
781 | if (!i_io_flush(ig)) { | |
782 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => %d (i_io_flush failure)\n", (int)write_count)); | |
783 | return write_count ? write_count : -1; | |
784 | } | |
785 | ||
786 | i_io_start_write(ig); | |
787 | ||
788 | if (size > ig->buf_size) { | |
789 | ssize_t rc; | |
790 | while (size > 0 && (rc = i_io_raw_write(ig, pbuf, size)) > 0) { | |
791 | write_count += rc; | |
792 | pbuf += rc; | |
793 | size -= rc; | |
794 | } | |
795 | if (rc <= 0) { | |
796 | ig->error = 1; | |
797 | if (!write_count) { | |
798 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => -1 (direct write failure)\n")); | |
799 | return -1; | |
800 | } | |
801 | } | |
802 | } | |
803 | else { | |
804 | memcpy(ig->write_ptr, pbuf, size); | |
805 | write_count += size; | |
806 | ig->write_ptr += size; | |
807 | } | |
808 | } | |
809 | ||
810 | IOL_DEB(fprintf(IOL_DEBs, "i_io_write() => %d\n", (int)write_count)); | |
811 | ||
812 | return write_count; | |
813 | } | |
814 | ||
815 | /* | |
816 | =item i_io_seek(io, offset, whence) | |
817 | =category I/O Layers | |
818 | ||
819 | Seek within the stream. | |
820 | ||
821 | Acts like perl's seek. | |
822 | ||
823 | =cut | |
824 | */ | |
825 | ||
826 | off_t | |
827 | i_io_seek(io_glue *ig, off_t offset, int whence) { | |
828 | off_t new_off; | |
829 | ||
830 | IOL_DEB(fprintf(IOL_DEBs, "i_io_seek(%p, %ld, %d)\n", ig, (long)offset, whence)); | |
831 | ||
832 | if (ig->write_ptr && ig->write_ptr != ig->write_end) { | |
833 | if (!i_io_flush(ig)) | |
834 | return (off_t)(-1); | |
835 | } | |
836 | ||
837 | if (whence == SEEK_CUR && ig->read_ptr && ig->read_ptr != ig->read_end) | |
838 | offset -= ig->read_end - ig->read_ptr; | |
839 | ||
840 | ig->read_ptr = ig->read_end = NULL; | |
841 | ig->write_ptr = ig->write_end = NULL; | |
842 | ig->error = 0; | |
843 | ig->buf_eof = 0; | |
844 | ||
845 | new_off = i_io_raw_seek(ig, offset, whence); | |
846 | if (new_off < 0) | |
847 | ig->error = 1; | |
848 | ||
849 | IOL_DEB(fprintf(IOL_DEBs, "i_io_seek() => %ld\n", (long)new_off)); | |
850 | ||
851 | return new_off; | |
852 | } | |
853 | ||
854 | /* | |
855 | =item i_io_flush(io) | |
856 | =category I/O Layers | |
857 | ||
858 | Flush any buffered output. | |
859 | ||
860 | Returns true on success, | |
861 | ||
862 | =cut | |
863 | */ | |
864 | ||
865 | int | |
866 | i_io_flush(io_glue *ig) { | |
867 | unsigned char *bufp; | |
868 | ||
869 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush(%p)\n", ig)); | |
870 | ||
871 | if (ig->error) { | |
872 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 0 (error set)\n", ig)); | |
873 | return 0; | |
874 | } | |
875 | ||
876 | /* nothing to do */ | |
877 | if (!ig->write_ptr) | |
878 | return 1; | |
879 | ||
880 | bufp = ig->buffer; | |
881 | while (bufp < ig->write_ptr) { | |
882 | ssize_t rc = i_io_raw_write(ig, bufp, ig->write_ptr - bufp); | |
883 | if (rc <= 0) { | |
884 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 0 (write error)\n", ig)); | |
885 | ig->error = 1; | |
886 | return 0; | |
887 | } | |
888 | ||
889 | bufp += rc; | |
890 | } | |
891 | ||
892 | ig->write_ptr = ig->write_end = NULL; | |
893 | ||
894 | IOL_DEB(fprintf(IOL_DEBs, "i_io_flush() => 1\n", ig)); | |
895 | ||
896 | return 1; | |
897 | } | |
898 | ||
899 | /* | |
900 | =item i_io_close(io) | |
901 | =category I/O Layers | |
902 | ||
903 | Flush any pending output and perform the close action for the stream. | |
904 | ||
905 | Returns 0 on success. | |
906 | ||
907 | =cut | |
908 | */ | |
909 | ||
910 | int | |
911 | i_io_close(io_glue *ig) { | |
912 | int result = 0; | |
913 | ||
914 | IOL_DEB(fprintf(IOL_DEBs, "i_io_close(%p)\n", ig)); | |
915 | if (ig->error) | |
916 | result = -1; | |
917 | ||
918 | if (ig->write_ptr && !i_io_flush(ig)) | |
919 | result = -1; | |
920 | ||
921 | if (i_io_raw_close(ig)) | |
922 | result = -1; | |
923 | ||
924 | IOL_DEB(fprintf(IOL_DEBs, "i_io_close() => %d\n", result)); | |
925 | ||
926 | return result; | |
927 | } | |
928 | ||
929 | /* | |
930 | =item i_io_gets(ig, buffer, size, end_of_line) | |
931 | =category I/O Layers | |
932 | =synopsis char buffer[BUFSIZ] | |
933 | =synopsis ssize_t len = i_io_gets(buffer, sizeof(buffer), '\n'); | |
934 | ||
935 | Read up to C<size>-1 bytes from the stream C<ig> into C<buffer>. | |
936 | ||
937 | If the byte C<end_of_line> is seen then no further bytes will be read. | |
938 | ||
939 | Returns the number of bytes read. | |
940 | ||
941 | Always C<NUL> terminates the buffer. | |
942 | ||
943 | =cut | |
944 | */ | |
945 | ||
946 | ssize_t | |
947 | i_io_gets(io_glue *ig, char *buffer, size_t size, int eol) { | |
948 | ssize_t read_count = 0; | |
949 | if (size < 2) | |
950 | return 0; | |
951 | --size; /* room for nul */ | |
952 | while (size > 0) { | |
953 | int byte = i_io_getc(ig); | |
954 | if (byte == EOF) | |
955 | break; | |
956 | *buffer++ = byte; | |
957 | ++read_count; | |
958 | if (byte == eol) | |
959 | break; | |
960 | --size; | |
961 | } | |
962 | *buffer++ = '\0'; | |
963 | ||
964 | return read_count; | |
965 | } | |
966 | ||
967 | /* | |
968 | =item i_io_init(ig, readcb, writecb, seekcb) | |
969 | ||
970 | Do common initialization for io_glue objects. | |
971 | ||
972 | =cut | |
973 | */ | |
974 | ||
975 | static void | |
976 | i_io_init(io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb, | |
977 | i_io_seekp_t seekcb) { | |
978 | ig->type = type; | |
979 | ig->exdata = NULL; | |
980 | ig->readcb = readcb; | |
981 | ig->writecb = writecb; | |
982 | ig->seekcb = seekcb; | |
983 | ig->closecb = NULL; | |
984 | ig->sizecb = NULL; | |
985 | ig->destroycb = NULL; | |
986 | ||
987 | ig->buffer = NULL; | |
988 | ig->read_ptr = NULL; | |
989 | ig->read_end = NULL; | |
990 | ig->write_ptr = NULL; | |
991 | ig->write_end = NULL; | |
992 | ig->buf_size = IO_BUF_SIZE; | |
993 | ig->buf_eof = 0; | |
994 | ig->error = 0; | |
995 | ig->buffered = 1; | |
996 | } | |
997 | ||
998 | /* | |
999 | =item i_io_set_buffered(io, buffered) | |
1000 | =category I/O Layers | |
1001 | ||
1002 | Set the buffering mode of the stream. | |
1003 | ||
1004 | If you switch buffering off on a stream with buffering on: | |
1005 | ||
1006 | =over | |
1007 | ||
1008 | =item * | |
1009 | ||
1010 | any buffered output will be flushed. | |
1011 | ||
1012 | =item * | |
1013 | ||
1014 | any existing buffered input will be consumed before reads become | |
1015 | unbuffered. | |
1016 | ||
1017 | =back | |
1018 | ||
1019 | Returns true on success. This may fail if any buffered output cannot | |
1020 | be flushed. | |
1021 | ||
1022 | =cut | |
1023 | */ | |
1024 | ||
1025 | int | |
1026 | i_io_set_buffered(io_glue *ig, int buffered) { | |
1027 | if (!buffered && ig->write_ptr) { | |
1028 | if (!i_io_flush(ig)) { | |
1029 | ig->error = 1; | |
1030 | return 0; | |
1031 | } | |
1032 | } | |
1033 | ig->buffered = buffered; | |
1034 | ||
1035 | return 1; | |
1036 | } | |
1037 | ||
1038 | /* | |
1039 | =item i_io_dump(ig) | |
1040 | ||
1041 | Dump the base fields of an io_glue object to stdout. | |
1042 | ||
1043 | =cut | |
1044 | */ | |
1045 | void | |
1046 | i_io_dump(io_glue *ig, int flags) { | |
1047 | fprintf(IOL_DEBs, "ig %p:\n", ig); | |
1048 | fprintf(IOL_DEBs, " type: %d\n", ig->type); | |
1049 | fprintf(IOL_DEBs, " exdata: %p\n", ig->exdata); | |
1050 | if (flags & I_IO_DUMP_CALLBACKS) { | |
1051 | fprintf(IOL_DEBs, " readcb: %p\n", ig->readcb); | |
1052 | fprintf(IOL_DEBs, " writecb: %p\n", ig->writecb); | |
1053 | fprintf(IOL_DEBs, " seekcb: %p\n", ig->seekcb); | |
1054 | fprintf(IOL_DEBs, " closecb: %p\n", ig->closecb); | |
1055 | fprintf(IOL_DEBs, " sizecb: %p\n", ig->sizecb); | |
1056 | } | |
1057 | if (flags & I_IO_DUMP_BUFFER) { | |
1058 | fprintf(IOL_DEBs, " buffer: %p\n", ig->buffer); | |
1059 | fprintf(IOL_DEBs, " read_ptr: %p\n", ig->read_ptr); | |
1060 | if (ig->read_ptr) { | |
1061 | fprintf(IOL_DEBs, " "); | |
1062 | dump_data(ig->read_ptr, ig->read_end, 0); | |
1063 | putc('\n', IOL_DEBs); | |
1064 | } | |
1065 | fprintf(IOL_DEBs, " read_end: %p\n", ig->read_end); | |
1066 | fprintf(IOL_DEBs, " write_ptr: %p\n", ig->write_ptr); | |
1067 | if (ig->write_ptr) { | |
1068 | fprintf(IOL_DEBs, " "); | |
1069 | dump_data(ig->buffer, ig->write_ptr, 1); | |
1070 | putc('\n', IOL_DEBs); | |
1071 | } | |
1072 | fprintf(IOL_DEBs, " write_end: %p\n", ig->write_end); | |
1073 | fprintf(IOL_DEBs, " buf_size: %u\n", (unsigned)(ig->buf_size)); | |
1074 | } | |
1075 | if (flags & I_IO_DUMP_STATUS) { | |
1076 | fprintf(IOL_DEBs, " buf_eof: %d\n", ig->buf_eof); | |
1077 | fprintf(IOL_DEBs, " error: %d\n", ig->error); | |
1078 | fprintf(IOL_DEBs, " buffered: %d\n", ig->buffered); | |
1079 | } | |
1080 | } | |
1081 | ||
1082 | /* | |
1083 | =back | |
1084 | ||
1085 | =head1 INTERNAL FUNCTIONS | |
1086 | ||
1087 | =over | |
1088 | ||
1089 | =item my_strerror | |
1090 | ||
1091 | Calls strerror() and ensures we don't return NULL. | |
1092 | ||
1093 | On some platforms it's possible for strerror() to return NULL, this | |
1094 | wrapper ensures we only get non-NULL values. | |
1095 | ||
1096 | =cut | |
1097 | */ | |
1098 | ||
1099 | static | |
1100 | const char *my_strerror(int err) { | |
1101 | const char *result = strerror(err); | |
1102 | ||
1103 | if (!result) | |
1104 | result = "Unknown error"; | |
1105 | ||
1106 | return result; | |
1107 | } | |
1108 | ||
1109 | static void | |
1110 | i_io_setup_buffer(io_glue *ig) { | |
1111 | ig->buffer = mymalloc(ig->buf_size); | |
1112 | } | |
1113 | ||
1114 | static void | |
1115 | i_io_start_write(io_glue *ig) { | |
1116 | ig->write_ptr = ig->buffer; | |
1117 | ig->write_end = ig->buffer + ig->buf_size; | |
1118 | } | |
1119 | ||
1120 | static int | |
1121 | i_io_read_fill(io_glue *ig, ssize_t needed) { | |
1122 | unsigned char *buf_end = ig->buffer + ig->buf_size; | |
1123 | unsigned char *buf_start = ig->buffer; | |
1124 | unsigned char *work = ig->buffer; | |
1125 | ssize_t rc; | |
1126 | int good = 0; | |
1127 | ||
1128 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%p, %d)\n", ig, (int)needed)); | |
1129 | ||
1130 | /* these conditions may be unused, callers should also be checking them */ | |
1131 | if (ig->error || ig->buf_eof) | |
1132 | return 0; | |
1133 | ||
1134 | if (needed > ig->buf_size) | |
1135 | needed = ig->buf_size; | |
1136 | ||
1137 | if (ig->read_ptr && ig->read_ptr < ig->read_end) { | |
1138 | size_t kept = ig->read_end - ig->read_ptr; | |
1139 | ||
1140 | if (needed < kept) { | |
1141 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%u) -> 1 (already have enough)\n", (unsigned)needed)); | |
1142 | return 1; | |
1143 | } | |
1144 | ||
1145 | if (ig->read_ptr != ig->buffer) | |
1146 | memmove(ig->buffer, ig->read_ptr, kept); | |
1147 | ||
1148 | good = 1; /* we have *something* available to read */ | |
1149 | work = buf_start + kept; | |
1150 | needed -= kept; | |
1151 | } | |
1152 | else { | |
1153 | work = ig->buffer; | |
1154 | } | |
1155 | ||
1156 | while (work < buf_end && (rc = i_io_raw_read(ig, work, buf_end - work)) > 0) { | |
1157 | work += rc; | |
1158 | good = 1; | |
1159 | if (needed < rc) | |
1160 | break; | |
1161 | ||
1162 | needed -= rc; | |
1163 | } | |
1164 | ||
1165 | if (rc < 0) { | |
1166 | ig->error = 1; | |
1167 | IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc %d, setting error\n", | |
1168 | (int)rc)); | |
1169 | } | |
1170 | else if (rc == 0) { | |
1171 | ig->buf_eof = 1; | |
1172 | IOL_DEB(fprintf(IOL_DEBs, " i_io_read_fill -> rc 0, setting eof\n")); | |
1173 | } | |
1174 | ||
1175 | if (good) { | |
1176 | ig->read_ptr = buf_start; | |
1177 | ig->read_end = work; | |
1178 | } | |
1179 | ||
1180 | IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill => %d, %u buffered\n", good, | |
1181 | (unsigned)(ig->read_end - ig->read_ptr))); | |
1182 | return good; | |
1183 | } | |
1184 | ||
1185 | /* | |
1186 | =item dump_data(start, end, bias) | |
1187 | ||
1188 | Hex dump the data between C<start> and C<end>. | |
1189 | ||
1190 | If there is more than a pleasing amount of data, either dump the | |
1191 | beginning (C<bias == 0>) or dump the end C(<bias != 0>) of the range. | |
1192 | ||
1193 | =cut | |
1194 | */ | |
1195 | ||
1196 | static void | |
1197 | dump_data(unsigned char *start, unsigned char *end, int bias) { | |
1198 | unsigned char *p; | |
1199 | size_t count = end - start; | |
1200 | ||
1201 | if (start == end) { | |
1202 | fprintf(IOL_DEBs, "(empty)"); | |
1203 | return; | |
1204 | } | |
1205 | ||
1206 | if (count > 15) { | |
1207 | if (bias) { | |
1208 | fprintf(IOL_DEBs, "... "); | |
1209 | start = end - 14; | |
1210 | } | |
1211 | else { | |
1212 | end = start + 14; | |
1213 | } | |
1214 | ||
1215 | for (p = start; p < end; ++p) { | |
1216 | fprintf(IOL_DEBs, " %02x", *p); | |
1217 | } | |
1218 | putc(' ', IOL_DEBs); | |
1219 | putc('<', IOL_DEBs); | |
1220 | for (p = start; p < end; ++p) { | |
1221 | if (*p < ' ' || *p > '~') | |
1222 | putc('.', IOL_DEBs); | |
1223 | else | |
1224 | putc(*p, IOL_DEBs); | |
1225 | } | |
1226 | putc('>', IOL_DEBs); | |
1227 | if (!bias) | |
1228 | fprintf(IOL_DEBs, " ..."); | |
1229 | } | |
1230 | else { | |
1231 | for (p = start; p < end; ++p) { | |
1232 | fprintf(IOL_DEBs, " %02x", *p); | |
1233 | } | |
1234 | putc(' ', IOL_DEBs); | |
1235 | for (p = start; p < end; ++p) { | |
1236 | if (*p < ' ' || *p > '~') | |
1237 | putc('.', IOL_DEBs); | |
1238 | else | |
1239 | putc(*p, IOL_DEBs); | |
1240 | } | |
1241 | } | |
1242 | } | |
119 | 1243 | |
120 | 1244 | /* |
121 | 1245 | * Callbacks for sources that cannot seek |
122 | 1246 | */ |
123 | ||
124 | /* fakeseek_read: read method for when emulating a seekable source | |
125 | static | |
126 | ssize_t | |
127 | fakeseek_read(io_glue *ig, void *buf, size_t count) { | |
128 | io_ex_fseek *exdata = ig->exdata; | |
129 | return 0; | |
130 | } | |
131 | */ | |
132 | ||
133 | ||
134 | 1247 | |
135 | 1248 | /* |
136 | 1249 | * Callbacks for sources that can seek |
150 | 1263 | |
151 | 1264 | static |
152 | 1265 | ssize_t |
153 | realseek_read(io_glue *ig, void *buf, size_t count) { | |
154 | io_ex_rseek *ier = ig->exdata; | |
155 | void *p = ig->source.cb.p; | |
1266 | realseek_read(io_glue *igo, void *buf, size_t count) { | |
1267 | io_cb *ig = (io_cb *)igo; | |
1268 | void *p = ig->p; | |
156 | 1269 | ssize_t rc = 0; |
157 | size_t bc = 0; | |
158 | char *cbuf = buf; | |
159 | ||
160 | IOL_DEB( printf("realseek_read: buf = %p, count = %d\n", | |
161 | buf, count) ); | |
162 | /* Is this a good idea? Would it be better to handle differently? | |
163 | skip handling? */ | |
164 | while( count!=bc && (rc = ig->source.cb.readcb(p,cbuf+bc,count-bc))>0 ) { | |
165 | bc+=rc; | |
166 | } | |
167 | ||
168 | ier->cpos += bc; | |
169 | IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) ); | |
170 | return rc < 0 ? rc : bc; | |
1270 | ||
1271 | IOL_DEB( fprintf(IOL_DEBs, "realseek_read: buf = %p, count = %u\n", | |
1272 | buf, (unsigned)count) ); | |
1273 | rc = ig->readcb(p,buf,count); | |
1274 | ||
1275 | IOL_DEB( fprintf(IOL_DEBs, "realseek_read: rc = %d\n", (int)rc) ); | |
1276 | ||
1277 | return rc; | |
171 | 1278 | } |
172 | 1279 | |
173 | 1280 | |
185 | 1292 | |
186 | 1293 | static |
187 | 1294 | ssize_t |
188 | realseek_write(io_glue *ig, const void *buf, size_t count) { | |
189 | io_ex_rseek *ier = ig->exdata; | |
190 | void *p = ig->source.cb.p; | |
1295 | realseek_write(io_glue *igo, const void *buf, size_t count) { | |
1296 | io_cb *ig = (io_cb *)igo; | |
1297 | void *p = ig->p; | |
191 | 1298 | ssize_t rc = 0; |
192 | 1299 | size_t bc = 0; |
193 | 1300 | char *cbuf = (char*)buf; |
194 | 1301 | |
195 | IOL_DEB( printf("realseek_write: ig = %p, ier->cpos = %ld, buf = %p, " | |
196 | "count = %d\n", ig, (long) ier->cpos, buf, count) ); | |
1302 | IOL_DEB( fprintf(IOL_DEBs, "realseek_write: ig = %p, buf = %p, " | |
1303 | "count = %u\n", ig, buf, (unsigned)count) ); | |
197 | 1304 | |
198 | 1305 | /* Is this a good idea? Would it be better to handle differently? |
199 | 1306 | skip handling? */ |
200 | while( count!=bc && (rc = ig->source.cb.writecb(p,cbuf+bc,count-bc))>0 ) { | |
1307 | while( count!=bc && (rc = ig->writecb(p,cbuf+bc,count-bc))>0 ) { | |
201 | 1308 | bc+=rc; |
202 | 1309 | } |
203 | 1310 | |
204 | ier->cpos += bc; | |
205 | IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) ); | |
1311 | IOL_DEB( fprintf(IOL_DEBs, "realseek_write: rc = %d, bc = %u\n", (int)rc, (unsigned)bc) ); | |
206 | 1312 | return rc < 0 ? rc : bc; |
207 | 1313 | } |
208 | 1314 | |
219 | 1325 | |
220 | 1326 | static |
221 | 1327 | int |
222 | realseek_close(io_glue *ig) { | |
1328 | realseek_close(io_glue *igo) { | |
1329 | io_cb *ig = (io_cb *)igo; | |
1330 | ||
1331 | IOL_DEB(fprintf(IOL_DEBs, "realseek_close(%p)\n", ig)); | |
223 | 1332 | mm_log((1, "realseek_close(ig %p)\n", ig)); |
224 | if (ig->source.cb.closecb) | |
225 | return ig->source.cb.closecb(ig->source.cb.p); | |
1333 | if (ig->closecb) | |
1334 | return ig->closecb(ig->p); | |
226 | 1335 | else |
227 | 1336 | return 0; |
228 | 1337 | } |
242 | 1351 | |
243 | 1352 | static |
244 | 1353 | off_t |
245 | realseek_seek(io_glue *ig, off_t offset, int whence) { | |
246 | /* io_ex_rseek *ier = ig->exdata; Needed later */ | |
247 | void *p = ig->source.cb.p; | |
1354 | realseek_seek(io_glue *igo, off_t offset, int whence) { | |
1355 | io_cb *ig = (io_cb *)igo; | |
1356 | void *p = ig->p; | |
248 | 1357 | off_t rc; |
249 | IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
250 | rc = ig->source.cb.seekcb(p, offset, whence); | |
251 | ||
252 | IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) ); | |
1358 | IOL_DEB( fprintf(IOL_DEBs, "realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
1359 | rc = ig->seekcb(p, offset, whence); | |
1360 | ||
1361 | IOL_DEB( fprintf(IOL_DEBs, "realseek_seek: rc %ld\n", (long) rc) ); | |
253 | 1362 | return rc; |
254 | 1363 | /* FIXME: How about implementing this offset handling stuff? */ |
255 | 1364 | } |
256 | 1365 | |
257 | 1366 | static |
258 | 1367 | void |
259 | realseek_destroy(io_glue *ig) { | |
260 | io_ex_rseek *ier = ig->exdata; | |
261 | ||
262 | if (ig->source.cb.destroycb) | |
263 | ig->source.cb.destroycb(ig->source.cb.p); | |
264 | ||
265 | myfree(ier); | |
1368 | realseek_destroy(io_glue *igo) { | |
1369 | io_cb *ig = (io_cb *)igo; | |
1370 | ||
1371 | if (ig->destroycb) | |
1372 | ig->destroycb(ig->p); | |
266 | 1373 | } |
267 | 1374 | |
268 | 1375 | /* |
283 | 1390 | |
284 | 1391 | static |
285 | 1392 | ssize_t |
286 | buffer_read(io_glue *ig, void *buf, size_t count) { | |
287 | io_ex_buffer *ieb = ig->exdata; | |
288 | ||
289 | IOL_DEB( printf("buffer_read: ieb->cpos = %ld, buf = %p, count = %d\n", (long) ieb->cpos, buf, count) ); | |
290 | ||
291 | if ( ieb->cpos+count > ig->source.buffer.len ) { | |
292 | mm_log((1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ieb->cpos, (long)ig->source.buffer.len, (long)count)); | |
293 | count = ig->source.buffer.len - ieb->cpos; | |
294 | } | |
295 | ||
296 | memcpy(buf, ig->source.buffer.data+ieb->cpos, count); | |
297 | ieb->cpos += count; | |
298 | IOL_DEB( printf("buffer_read: count = %ld\n", (long)count) ); | |
1393 | buffer_read(io_glue *igo, void *buf, size_t count) { | |
1394 | io_buffer *ig = (io_buffer *)igo; | |
1395 | ||
1396 | IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ig->cpos = %ld, buf = %p, count = %u\n", (long) ig->cpos, buf, (unsigned)count) ); | |
1397 | ||
1398 | if ( ig->cpos+count > ig->len ) { | |
1399 | mm_log((1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count)); | |
1400 | count = ig->len - ig->cpos; | |
1401 | } | |
1402 | ||
1403 | memcpy(buf, ig->data+ig->cpos, count); | |
1404 | ig->cpos += count; | |
1405 | IOL_DEB( fprintf(IOL_DEBs, "buffer_read: count = %ld\n", (long)count) ); | |
299 | 1406 | return count; |
300 | 1407 | } |
301 | 1408 | |
353 | 1460 | |
354 | 1461 | static |
355 | 1462 | off_t |
356 | buffer_seek(io_glue *ig, off_t offset, int whence) { | |
357 | io_ex_buffer *ieb = ig->exdata; | |
1463 | buffer_seek(io_glue *igo, off_t offset, int whence) { | |
1464 | io_buffer *ig = (io_buffer *)igo; | |
358 | 1465 | off_t reqpos = |
359 | calc_seek_offset(ieb->cpos, ig->source.buffer.len, offset, whence); | |
360 | ||
361 | if (reqpos > ig->source.buffer.len) { | |
1466 | calc_seek_offset(ig->cpos, ig->len, offset, whence); | |
1467 | ||
1468 | if (reqpos > ig->len) { | |
362 | 1469 | mm_log((1, "seeking out of readable range\n")); |
363 | 1470 | return (off_t)-1; |
364 | 1471 | } |
367 | 1474 | return (off_t)-1; |
368 | 1475 | } |
369 | 1476 | |
370 | ieb->cpos = reqpos; | |
371 | IOL_DEB( printf("buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
1477 | ig->cpos = reqpos; | |
1478 | IOL_DEB( fprintf(IOL_DEBs, "buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) ); | |
372 | 1479 | |
373 | 1480 | return reqpos; |
374 | 1481 | /* FIXME: How about implementing this offset handling stuff? */ |
376 | 1483 | |
377 | 1484 | static |
378 | 1485 | void |
379 | buffer_destroy(io_glue *ig) { | |
380 | io_ex_buffer *ieb = ig->exdata; | |
381 | ||
382 | if (ig->source.buffer.closecb) { | |
1486 | buffer_destroy(io_glue *igo) { | |
1487 | io_buffer *ig = (io_buffer *)igo; | |
1488 | ||
1489 | if (ig->closecb) { | |
383 | 1490 | mm_log((1,"calling close callback %p for io_buffer\n", |
384 | ig->source.buffer.closecb)); | |
385 | ig->source.buffer.closecb(ig->source.buffer.closedata); | |
386 | } | |
387 | myfree(ieb); | |
1491 | ig->closecb)); | |
1492 | ig->closecb(ig->closedata); | |
1493 | } | |
388 | 1494 | } |
389 | 1495 | |
390 | 1496 | |
451 | 1557 | =cut |
452 | 1558 | */ |
453 | 1559 | |
454 | void | |
1560 | static void | |
455 | 1561 | io_destroy_bufchain(io_ex_bchain *ieb) { |
456 | 1562 | io_blink *cp; |
457 | 1563 | mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb)); |
566 | 1672 | } |
567 | 1673 | */ |
568 | 1674 | |
569 | ||
570 | ||
571 | ||
572 | ||
573 | ||
574 | ||
575 | ||
576 | ||
577 | ||
578 | ||
579 | 1675 | /* |
580 | 1676 | =item bufchain_read(ig, buf, count) |
581 | 1677 | |
646 | 1742 | |
647 | 1743 | mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count)); |
648 | 1744 | |
649 | IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) ); | |
1745 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) ); | |
650 | 1746 | |
651 | 1747 | while(count) { |
652 | 1748 | mm_log((2, "bufchain_write: - looping - count = %ld\n", (long)count)); |
690 | 1786 | int |
691 | 1787 | bufchain_close(io_glue *ig) { |
692 | 1788 | mm_log((1, "bufchain_close(ig %p)\n",ig)); |
693 | IOL_DEB( printf("bufchain_close(ig %p)\n", ig) ); | |
1789 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_close(ig %p)\n", ig) ); | |
694 | 1790 | |
695 | 1791 | return 0; |
696 | 1792 | } |
781 | 1877 | } |
782 | 1878 | |
783 | 1879 | /* |
784 | * Methods for setting up data source | |
785 | */ | |
786 | ||
787 | /* | |
788 | =item io_obj_setp_buffer(io, p, len) | |
789 | ||
790 | Sets an io_object for reading from a buffer source | |
791 | ||
792 | io - io object that describes a source | |
793 | p - pointer to buffer | |
794 | len - length of buffer | |
795 | ||
796 | =cut | |
797 | */ | |
798 | ||
799 | static void | |
800 | io_obj_setp_buffer(io_obj *io, char *p, size_t len, i_io_closebufp_t closecb, | |
801 | void *closedata) { | |
802 | io->buffer.type = BUFFER; | |
803 | io->buffer.data = p; | |
804 | io->buffer.len = len; | |
805 | io->buffer.closecb = closecb; | |
806 | io->buffer.closedata = closedata; | |
807 | } | |
808 | ||
809 | ||
810 | ||
811 | /* | |
812 | =item io_obj_setp_cb2(io, p, readcb, writecb, seekcb, closecb, destroycb) | |
813 | ||
814 | Sets an io_object for reading from a source that uses callbacks | |
815 | ||
816 | io - io object that describes a source | |
817 | p - pointer to data for callbacks | |
818 | readcb - read callback to read from source | |
819 | writecb - write callback to write to source | |
820 | seekcb - seek callback to seek on source | |
821 | closecb - flush any pending data | |
822 | destroycb - release any extra resources | |
823 | ||
824 | =cut | |
825 | */ | |
826 | ||
827 | static void | |
828 | io_obj_setp_cb2(io_obj *io, void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb) { | |
829 | io->cb.type = CBSEEK; | |
830 | io->cb.p = p; | |
831 | io->cb.readcb = readcb; | |
832 | io->cb.writecb = writecb; | |
833 | io->cb.seekcb = seekcb; | |
834 | io->cb.closecb = closecb; | |
835 | io->cb.destroycb = destroycb; | |
836 | } | |
837 | ||
838 | /* | |
839 | =item io_glue_commit_types(ig) | |
840 | ||
841 | This is now effectively a no-op. | |
842 | ||
843 | =cut | |
844 | */ | |
845 | ||
846 | void | |
847 | io_glue_commit_types(io_glue *ig) { | |
848 | io_type inn = ig->source.type; | |
849 | ||
850 | mm_log((1, "io_glue_commit_types(ig %p)\n", ig)); | |
851 | mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn])); | |
852 | ||
853 | if (ig->flags & 0x01) { | |
854 | mm_log((1, "io_glue_commit_types: type already set up\n")); | |
855 | return; | |
856 | } | |
857 | ||
858 | ig->flags |= 0x01; /* indicate source has been setup already */ | |
859 | } | |
860 | ||
861 | /* | |
862 | =item io_glue_gettypes(ig, reqmeth) | |
863 | ||
864 | Returns a set of compatible interfaces to read data with. | |
865 | ||
866 | ig - io_glue object | |
867 | reqmeth - request mask | |
868 | ||
869 | The request mask is a bit mask (of something that hasn't been implemented yet) | |
870 | of interfaces that it would like to read data from the source which the ig | |
871 | describes. | |
872 | ||
873 | =cut | |
874 | */ | |
875 | ||
876 | void | |
877 | io_glue_gettypes(io_glue *ig, int reqmeth) { | |
878 | ||
879 | ig = NULL; | |
880 | reqmeth = 0; | |
881 | ||
882 | /* FIXME: Implement this function! */ | |
883 | /* if (ig->source.type = | |
884 | if (reqmeth & IO_BUFF) */ | |
885 | ||
886 | } | |
887 | ||
888 | ||
889 | /* | |
890 | =item io_new_bufchain() | |
891 | ||
892 | returns a new io_glue object that has the 'empty' source and but can | |
893 | be written to and read from later (like a pseudo file). | |
894 | ||
895 | =cut | |
896 | */ | |
897 | ||
898 | io_glue * | |
899 | io_new_bufchain() { | |
900 | io_glue *ig; | |
901 | io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain)); | |
902 | ||
903 | mm_log((1, "io_new_bufchain()\n")); | |
904 | ||
905 | ig = mymalloc(sizeof(io_glue)); | |
906 | memset(ig, 0, sizeof(*ig)); | |
907 | ig->source.type = BUFCHAIN; | |
908 | ||
909 | ieb->offset = 0; | |
910 | ieb->length = 0; | |
911 | ieb->cpos = 0; | |
912 | ieb->gpos = 0; | |
913 | ieb->tfill = 0; | |
914 | ||
915 | ieb->head = io_blink_new(); | |
916 | ieb->cp = ieb->head; | |
917 | ieb->tail = ieb->head; | |
918 | ||
919 | ig->exdata = ieb; | |
920 | ig->readcb = bufchain_read; | |
921 | ig->writecb = bufchain_write; | |
922 | ig->seekcb = bufchain_seek; | |
923 | ig->closecb = bufchain_close; | |
924 | ig->destroycb = bufchain_destroy; | |
925 | ||
926 | return ig; | |
927 | } | |
928 | ||
929 | /* | |
930 | =item io_new_buffer(data, len) | |
931 | ||
932 | Returns a new io_glue object that has the source defined as reading | |
933 | from specified buffer. Note that the buffer is not copied. | |
934 | ||
935 | data - buffer to read from | |
936 | len - length of buffer | |
937 | ||
938 | =cut | |
939 | */ | |
940 | ||
941 | io_glue * | |
942 | io_new_buffer(char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { | |
943 | io_glue *ig; | |
944 | io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer)); | |
945 | ||
946 | mm_log((1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); | |
947 | ||
948 | ig = mymalloc(sizeof(io_glue)); | |
949 | memset(ig, 0, sizeof(*ig)); | |
950 | io_obj_setp_buffer(&ig->source, data, len, closecb, closedata); | |
951 | ig->flags = 0; | |
952 | ||
953 | ieb->offset = 0; | |
954 | ieb->cpos = 0; | |
955 | ||
956 | ig->exdata = ieb; | |
957 | ig->readcb = buffer_read; | |
958 | ig->writecb = buffer_write; | |
959 | ig->seekcb = buffer_seek; | |
960 | ig->closecb = buffer_close; | |
961 | ig->destroycb = buffer_destroy; | |
962 | ||
963 | return ig; | |
964 | } | |
965 | ||
966 | ||
967 | /* | |
968 | =item io_new_fd(fd) | |
969 | ||
970 | returns a new io_glue object that has the source defined as reading | |
971 | from specified filedescriptor. Note that the the interface to recieving | |
972 | data from the io_glue callbacks hasn't been done yet. | |
973 | ||
974 | fd - file descriptor to read/write from | |
975 | ||
976 | =cut | |
977 | */ | |
978 | ||
979 | io_glue * | |
980 | io_new_fd(int fd) { | |
981 | io_glue *ig; | |
982 | ||
983 | mm_log((1, "io_new_fd(fd %d)\n", fd)); | |
984 | ||
985 | ig = mymalloc(sizeof(io_glue)); | |
986 | memset(ig, 0, sizeof(*ig)); | |
987 | ig->source.type = FDSEEK; | |
988 | ig->source.fdseek.fd = fd; | |
989 | ig->flags = 0; | |
990 | ||
991 | ig->exdata = NULL; | |
992 | ig->readcb = fd_read; | |
993 | ig->writecb = fd_write; | |
994 | ig->seekcb = fd_seek; | |
995 | ig->closecb = fd_close; | |
996 | ig->sizecb = fd_size; | |
997 | ig->destroycb = NULL; | |
998 | ||
999 | mm_log((1, "(%p) <- io_new_fd\n", ig)); | |
1000 | return ig; | |
1001 | } | |
1002 | ||
1003 | io_glue *io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, | |
1004 | i_io_seekl_t seekcb, i_io_closel_t closecb, | |
1005 | i_io_destroyl_t destroycb) { | |
1006 | io_glue *ig; | |
1007 | io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek)); | |
1008 | ||
1009 | mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " | |
1010 | "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb)); | |
1011 | ig = mymalloc(sizeof(io_glue)); | |
1012 | memset(ig, 0, sizeof(*ig)); | |
1013 | io_obj_setp_cb2(&ig->source, p, readcb, writecb, seekcb, closecb, destroycb); | |
1014 | mm_log((1, "(%p) <- io_new_cb\n", ig)); | |
1015 | ||
1016 | ier->offset = 0; | |
1017 | ier->cpos = 0; | |
1018 | ||
1019 | ig->exdata = ier; | |
1020 | ig->readcb = realseek_read; | |
1021 | ig->writecb = realseek_write; | |
1022 | ig->seekcb = realseek_seek; | |
1023 | ig->closecb = realseek_close; | |
1024 | ig->destroycb = realseek_destroy; | |
1025 | ||
1026 | return ig; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | =item io_slurp(ig) | |
1031 | ||
1032 | Takes the source that the io_glue is bound to and allocates space | |
1033 | for a return buffer and returns the entire content in a single buffer. | |
1034 | Note: This only works for io_glue objects that contain a bufchain. It | |
1035 | is usefull for saving to scalars and such. | |
1036 | ||
1037 | ig - io_glue object | |
1038 | c - pointer to a pointer to where data should be copied to | |
1039 | ||
1040 | =cut | |
1041 | */ | |
1042 | ||
1043 | size_t | |
1044 | io_slurp(io_glue *ig, unsigned char **c) { | |
1045 | ssize_t rc; | |
1046 | off_t orgoff; | |
1047 | io_ex_bchain *ieb; | |
1048 | unsigned char *cc; | |
1049 | io_type inn = ig->source.type; | |
1050 | ||
1051 | if ( inn != BUFCHAIN ) { | |
1052 | i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n"); | |
1053 | } | |
1054 | ||
1055 | ieb = ig->exdata; | |
1056 | cc = *c = mymalloc( ieb->length ); | |
1057 | ||
1058 | orgoff = ieb->gpos; | |
1059 | ||
1060 | bufchain_seek(ig, 0, SEEK_SET); | |
1061 | ||
1062 | rc = bufchain_read(ig, cc, ieb->length); | |
1063 | ||
1064 | if (rc != ieb->length) | |
1065 | i_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length); | |
1066 | ||
1067 | return rc; | |
1068 | } | |
1069 | ||
1070 | /* | |
1071 | 1880 | =item fd_read(ig, buf, count) |
1072 | 1881 | |
1073 | =cut | |
1074 | */ | |
1075 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count) { | |
1882 | Read callback for file descriptor IO objects. | |
1883 | ||
1884 | =cut | |
1885 | */ | |
1886 | static ssize_t fd_read(io_glue *igo, void *buf, size_t count) { | |
1887 | io_fdseek *ig = (io_fdseek *)igo; | |
1076 | 1888 | ssize_t result; |
1077 | 1889 | #ifdef _MSC_VER |
1078 | result = _read(ig->source.fdseek.fd, buf, count); | |
1890 | result = _read(ig->fd, buf, count); | |
1079 | 1891 | #else |
1080 | result = read(ig->source.fdseek.fd, buf, count); | |
1892 | result = read(ig->fd, buf, count); | |
1081 | 1893 | #endif |
1894 | ||
1895 | IOL_DEB(fprintf(IOL_DEBs, "fd_read(%p, %p, %u) => %d\n", ig, buf, | |
1896 | (unsigned)count, (int)result)); | |
1082 | 1897 | |
1083 | 1898 | /* 0 is valid - means EOF */ |
1084 | 1899 | if (result < 0) { |
1088 | 1903 | return result; |
1089 | 1904 | } |
1090 | 1905 | |
1091 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count) { | |
1906 | static ssize_t fd_write(io_glue *igo, const void *buf, size_t count) { | |
1907 | io_fdseek *ig = (io_fdseek *)igo; | |
1092 | 1908 | ssize_t result; |
1093 | 1909 | #ifdef _MSC_VER |
1094 | result = _write(ig->source.fdseek.fd, buf, count); | |
1910 | result = _write(ig->fd, buf, count); | |
1095 | 1911 | #else |
1096 | result = write(ig->source.fdseek.fd, buf, count); | |
1912 | result = write(ig->fd, buf, count); | |
1097 | 1913 | #endif |
1914 | ||
1915 | IOL_DEB(fprintf(IOL_DEBs, "fd_write(%p, %p, %u) => %d\n", ig, buf, | |
1916 | (unsigned)count, (int)result)); | |
1098 | 1917 | |
1099 | 1918 | if (result <= 0) { |
1100 | 1919 | i_push_errorf(errno, "write() failure: %s (%d)", my_strerror(errno), errno); |
1103 | 1922 | return result; |
1104 | 1923 | } |
1105 | 1924 | |
1106 | static off_t fd_seek(io_glue *ig, off_t offset, int whence) { | |
1925 | static off_t fd_seek(io_glue *igo, off_t offset, int whence) { | |
1926 | io_fdseek *ig = (io_fdseek *)igo; | |
1107 | 1927 | off_t result; |
1108 | 1928 | #ifdef _MSC_VER |
1109 | result = _lseek(ig->source.fdseek.fd, offset, whence); | |
1929 | result = _lseek(ig->fd, offset, whence); | |
1110 | 1930 | #else |
1111 | result = lseek(ig->source.fdseek.fd, offset, whence); | |
1931 | result = lseek(ig->fd, offset, whence); | |
1112 | 1932 | #endif |
1113 | 1933 | |
1114 | 1934 | if (result == (off_t)-1) { |
1129 | 1949 | return -1; |
1130 | 1950 | } |
1131 | 1951 | |
1132 | /* | |
1133 | =item io_glue_destroy(ig) | |
1134 | ||
1135 | A destructor method for io_glue objects. Should clean up all related buffers. | |
1136 | Might leave us with a dangling pointer issue on some buffers. | |
1137 | ||
1138 | ig - io_glue object to destroy. | |
1139 | ||
1140 | =cut | |
1141 | */ | |
1142 | ||
1143 | void | |
1144 | io_glue_destroy(io_glue *ig) { | |
1145 | mm_log((1, "io_glue_DESTROY(ig %p)\n", ig)); | |
1146 | ||
1147 | if (ig->destroycb) | |
1148 | ig->destroycb(ig); | |
1149 | ||
1150 | myfree(ig); | |
1151 | } | |
1152 | 1952 | |
1153 | 1953 | /* |
1154 | 1954 | =back |
1155 | 1955 | |
1156 | =head1 INTERNAL FUNCTIONS | |
1157 | ||
1158 | =over | |
1159 | ||
1160 | =item my_strerror | |
1161 | ||
1162 | Calls strerror() and ensures we don't return NULL. | |
1163 | ||
1164 | On some platforms it's possible for strerror() to return NULL, this | |
1165 | wrapper ensures we only get non-NULL values. | |
1166 | ||
1167 | =cut | |
1168 | */ | |
1169 | ||
1170 | static | |
1171 | const char *my_strerror(int err) { | |
1172 | const char *result = strerror(err); | |
1173 | ||
1174 | if (!result) | |
1175 | result = "Unknown error"; | |
1176 | ||
1177 | return result; | |
1178 | } | |
1179 | ||
1180 | /* | |
1181 | =back | |
1182 | ||
1183 | 1956 | =head1 AUTHOR |
1184 | 1957 | |
1185 | 1958 | Arnar M. Hrafnkelsson <addi@umich.edu> |
3 | 3 | |
4 | 4 | /* How the IO layer works: |
5 | 5 | * |
6 | * Start by getting an io_glue object. Then define its | |
7 | * datasource via io_obj_setp_buffer or io_obj_setp_cb. Before | |
8 | * using the io_glue object be sure to call io_glue_commit_types(). | |
9 | * After that data can be read via the io_glue->readcb() method. | |
6 | * Start by getting an io_glue object by calling the appropriate | |
7 | * io_new...() function. After that data can be read via the | |
8 | * io_glue->readcb() method. | |
10 | 9 | * |
11 | 10 | */ |
12 | 11 | |
19 | 18 | #define IO_TEMP_SEEK 1<<1L |
20 | 19 | |
21 | 20 | |
22 | void io_glue_commit_types(io_glue *ig); | |
23 | 21 | void io_glue_gettypes (io_glue *ig, int reqmeth); |
24 | 22 | |
25 | 23 | /* XS functions */ |
26 | 24 | io_glue *io_new_fd(int fd); |
27 | 25 | io_glue *io_new_bufchain(void); |
28 | io_glue *io_new_buffer(char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
26 | io_glue *io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
29 | 27 | io_glue *io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb); |
30 | 28 | size_t io_slurp(io_glue *ig, unsigned char **c); |
31 | 29 | void io_glue_destroy(io_glue *ig); |
32 | 30 | |
31 | void i_io_dump(io_glue *ig, int flags); | |
32 | ||
33 | /* Buffered I/O */ | |
34 | extern int i_io_getc_imp(io_glue *ig); | |
35 | extern int i_io_peekc_imp(io_glue *ig); | |
36 | extern ssize_t i_io_peekn(io_glue *ig, void *buf, size_t size); | |
37 | extern int i_io_putc_imp(io_glue *ig, int c); | |
38 | extern ssize_t i_io_read(io_glue *ig, void *buf, size_t size); | |
39 | extern ssize_t i_io_write(io_glue *ig, const void *buf, size_t size); | |
40 | extern off_t i_io_seek(io_glue *ig, off_t offset, int whence); | |
41 | extern int i_io_flush(io_glue *ig); | |
42 | extern int i_io_close(io_glue *ig); | |
43 | extern int i_io_set_buffered(io_glue *ig, int buffered); | |
44 | extern ssize_t i_io_gets(io_glue *ig, char *, size_t, int); | |
45 | ||
33 | 46 | #endif /* _IOLAYER_H_ */ |
45 | 45 | |
46 | 46 | /* Structures to describe data sources */ |
47 | 47 | |
48 | typedef struct { | |
49 | io_type type; | |
50 | int fd; | |
51 | } io_fdseek; | |
52 | ||
53 | typedef struct { | |
54 | io_type type; /* Must be first parameter */ | |
55 | char *name; /* Data source name */ | |
56 | char *data; | |
57 | size_t len; | |
58 | i_io_closebufp_t closecb; /* free memory mapped segment or decrement refcount */ | |
59 | void *closedata; | |
60 | } io_buffer; | |
61 | ||
62 | typedef struct { | |
63 | io_type type; /* Must be first parameter */ | |
64 | char *name; /* Data source name */ | |
65 | void *p; /* Callback data */ | |
66 | i_io_readl_t readcb; | |
67 | i_io_writel_t writecb; | |
68 | i_io_seekl_t seekcb; | |
69 | i_io_closel_t closecb; | |
70 | i_io_destroyl_t destroycb; | |
71 | } io_cb; | |
72 | ||
73 | typedef union { | |
74 | io_type type; | |
75 | io_fdseek fdseek; | |
76 | io_buffer buffer; | |
77 | io_cb cb; | |
78 | } io_obj; | |
79 | ||
80 | 48 | struct i_io_glue_t { |
81 | io_obj source; | |
82 | int flags; /* Flags */ | |
83 | void *exdata; /* Pair specific data */ | |
49 | io_type type; | |
50 | void *exdata; | |
84 | 51 | i_io_readp_t readcb; |
85 | 52 | i_io_writep_t writecb; |
86 | 53 | i_io_seekp_t seekcb; |
87 | 54 | i_io_closep_t closecb; |
88 | 55 | i_io_sizep_t sizecb; |
89 | 56 | i_io_destroyp_t destroycb; |
57 | unsigned char *buffer; | |
58 | unsigned char *read_ptr; | |
59 | unsigned char *read_end; | |
60 | unsigned char *write_ptr; | |
61 | unsigned char *write_end; | |
62 | size_t buf_size; | |
63 | ||
64 | /* non-zero if we encountered EOF */ | |
65 | int buf_eof; | |
66 | ||
67 | /* non-zero if we've seen an error */ | |
68 | int error; | |
69 | ||
70 | /* if non-zero we do write buffering (enabled by default) */ | |
71 | int buffered; | |
90 | 72 | }; |
91 | 73 | |
74 | #define I_IO_DUMP_CALLBACKS 1 | |
75 | #define I_IO_DUMP_BUFFER 2 | |
76 | #define I_IO_DUMP_STATUS 4 | |
77 | #define I_IO_DUMP_DEFAULT (I_IO_DUMP_BUFFER | I_IO_DUMP_STATUS) | |
78 | ||
92 | 79 | #define i_io_type(ig) ((ig)->source.ig_type) |
93 | #define i_io_read(ig, buf, size) ((ig)->readcb((ig), (buf), (size))) | |
94 | #define i_io_write(ig, data, size) ((ig)->writecb((ig), (data), (size))) | |
95 | #define i_io_seek(ig, offset, whence) ((ig)->seekcb((ig), (offset), (whence))) | |
96 | #define i_io_close(ig) ((ig)->closecb(ig)) | |
80 | #define i_io_raw_read(ig, buf, size) ((ig)->readcb((ig), (buf), (size))) | |
81 | #define i_io_raw_write(ig, data, size) ((ig)->writecb((ig), (data), (size))) | |
82 | #define i_io_raw_seek(ig, offset, whence) ((ig)->seekcb((ig), (offset), (whence))) | |
83 | #define i_io_raw_close(ig) ((ig)->closecb(ig)) | |
84 | #define i_io_is_buffered(ig) ((int)((ig)->buffered)) | |
97 | 85 | |
86 | #define i_io_getc(ig) \ | |
87 | ((ig)->read_ptr < (ig)->read_end ? \ | |
88 | *((ig)->read_ptr++) : \ | |
89 | i_io_getc_imp(ig)) | |
90 | #define i_io_peekc(ig) \ | |
91 | ((ig)->read_ptr < (ig)->read_end ? \ | |
92 | *((ig)->read_ptr) : \ | |
93 | i_io_peekc_imp(ig)) | |
94 | #define i_io_putc(ig, c) \ | |
95 | ((ig)->write_ptr < (ig)->write_end && !(ig)->error ? \ | |
96 | *(ig)->write_ptr++ = (c) : \ | |
97 | i_io_putc_imp(ig, (c))) | |
98 | #define i_io_eof(ig) \ | |
99 | ((ig)->read_ptr == (ig)->read_end && (ig)->buf_eof) | |
100 | #define i_io_error(ig) \ | |
101 | ((ig)->read_ptr == (ig)->read_end && (ig)->error) | |
98 | 102 | |
99 | 103 | #endif |
59 | 59 | i_fr_triangle, 0, i_fts_grid, 9, 1, segs); |
60 | 60 | i_fill_destroy(fill); |
61 | 61 | |
62 | # I/O Layers | |
63 | ssize_t count = i_io_peekn(ig, buffer, sizeof(buffer)); | |
64 | ssize_t result = i_io_write(io, buffer, size) | |
65 | char buffer[BUFSIZ] | |
66 | ssize_t len = i_io_gets(buffer, sizeof(buffer), '\n'); | |
67 | io_glue_destroy(ig); | |
68 | ||
62 | 69 | # Image |
63 | 70 | |
64 | 71 | # Image creation/destruction |
65 | i_img *img = i_img_8_new(width, height, channels); | |
66 | 72 | i_img *img = i_sametype(src, width, height); |
67 | 73 | i_img *img = i_sametype_chans(src, width, height, channels); |
74 | i_img *img = i_img_16_new(width, height, channels); | |
75 | i_img *img = i_img_8_new(width, height, channels); | |
76 | i_img *img = i_img_double_new(width, height, channels); | |
68 | 77 | i_img *img = i_img_pal_new(width, height, channels, max_palette_size) |
69 | i_img *img = i_img_double_new(width, height, channels); | |
70 | i_img *img = i_img_16_new(width, height, channels); | |
71 | 78 | i_img_destroy(img) |
72 | 79 | |
73 | 80 | # Image Implementation |
74 | 81 | |
75 | 82 | # Image Information |
76 | // only channel 0 writeable | |
83 | // only channel 0 writable | |
77 | 84 | i_img_setmask(img, 0x01); |
78 | 85 | int mask = i_img_getmask(img); |
79 | 86 | int channels = i_img_getchannels(img); |
1029 | 1036 | |
1030 | 1037 | =back |
1031 | 1038 | |
1039 | =head2 I/O Layers | |
1040 | ||
1041 | =over | |
1042 | ||
1043 | =item io_new_bufchain() | |
1044 | ||
1045 | returns a new io_glue object that has the 'empty' source and but can | |
1046 | be written to and read from later (like a pseudo file). | |
1047 | ||
1048 | ||
1049 | =for comment | |
1050 | From: File iolayer.c | |
1051 | ||
1052 | =item io_new_buffer(data, length) | |
1053 | ||
1054 | Returns a new io_glue object that has the source defined as reading | |
1055 | from specified buffer. Note that the buffer is not copied. | |
1056 | ||
1057 | data - buffer to read from | |
1058 | length - length of buffer | |
1059 | ||
1060 | ||
1061 | =for comment | |
1062 | From: File iolayer.c | |
1063 | ||
1064 | =item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
1065 | ||
1066 | Create a new I/O layer object that calls your supplied callbacks. | |
1067 | ||
1068 | In general the callbacks should behave like the corresponding POSIX | |
1069 | primitives. | |
1070 | ||
1071 | =over | |
1072 | ||
1073 | =item * | |
1074 | ||
1075 | C<read_cb>(p, buffer, length) should read up to C<length> bytes into | |
1076 | C<buffer> and return the number of bytes read. At end of file, return | |
1077 | 0. On error, return -1. | |
1078 | ||
1079 | =item * | |
1080 | ||
1081 | C<write_cb>(p, buffer, length) should write up to C<length> bytes from | |
1082 | C<buffer> and return the number of bytes written. A return value <= 0 | |
1083 | will be treated as an error. | |
1084 | ||
1085 | =item * | |
1086 | ||
1087 | C<seekcb>(p, offset, whence) should seek and return the new offset. | |
1088 | ||
1089 | =item * | |
1090 | ||
1091 | C<close_cb>(p) should return 0 on success, -1 on failure. | |
1092 | ||
1093 | =item * | |
1094 | ||
1095 | C<destroy_cb>(p) should release any memory specific to your callback | |
1096 | handlers. | |
1097 | ||
1098 | =back | |
1099 | ||
1100 | ||
1101 | =for comment | |
1102 | From: File iolayer.c | |
1103 | ||
1104 | =item io_new_fd(fd) | |
1105 | ||
1106 | returns a new io_glue object that has the source defined as reading | |
1107 | from specified file descriptor. Note that the the interface to receiving | |
1108 | data from the io_glue callbacks hasn't been done yet. | |
1109 | ||
1110 | fd - file descriptor to read/write from | |
1111 | ||
1112 | ||
1113 | =for comment | |
1114 | From: File iolayer.c | |
1115 | ||
1116 | =item i_io_close(io) | |
1117 | ||
1118 | Flush any pending output and perform the close action for the stream. | |
1119 | ||
1120 | Returns 0 on success. | |
1121 | ||
1122 | ||
1123 | =for comment | |
1124 | From: File iolayer.c | |
1125 | ||
1126 | =item i_io_flush(io) | |
1127 | ||
1128 | Flush any buffered output. | |
1129 | ||
1130 | Returns true on success, | |
1131 | ||
1132 | ||
1133 | =for comment | |
1134 | From: File iolayer.c | |
1135 | ||
1136 | =item i_io_getc(ig) | |
1137 | ||
1138 | A macro to read a single byte from a buffered I/O glue object. | |
1139 | ||
1140 | Returns EOF on failure, or a byte. | |
1141 | ||
1142 | ||
1143 | =for comment | |
1144 | From: File iolayer.c | |
1145 | ||
1146 | =item i_io_gets(ig, buffer, size, end_of_line) | |
1147 | ||
1148 | char buffer[BUFSIZ] | |
1149 | ssize_t len = i_io_gets(buffer, sizeof(buffer), '\n'); | |
1150 | ||
1151 | Read up to C<size>-1 bytes from the stream C<ig> into C<buffer>. | |
1152 | ||
1153 | If the byte C<end_of_line> is seen then no further bytes will be read. | |
1154 | ||
1155 | Returns the number of bytes read. | |
1156 | ||
1157 | Always C<NUL> terminates the buffer. | |
1158 | ||
1159 | ||
1160 | =for comment | |
1161 | From: File iolayer.c | |
1162 | ||
1163 | =item i_io_peekc(ig) | |
1164 | ||
1165 | Read the next character from the stream without advancing the stream. | |
1166 | ||
1167 | On error or end of file, return EOF. | |
1168 | ||
1169 | For unbuffered streams a single character buffer will be setup. | |
1170 | ||
1171 | ||
1172 | =for comment | |
1173 | From: File iolayer.c | |
1174 | ||
1175 | =item i_io_peekn(ig, buffer, size) | |
1176 | ||
1177 | ssize_t count = i_io_peekn(ig, buffer, sizeof(buffer)); | |
1178 | ||
1179 | Buffer at least C<size> (at most C<< ig->buf_size >> bytes of data | |
1180 | from the stream and return C<size> bytes of it to the caller in | |
1181 | C<buffer>. | |
1182 | ||
1183 | This ignores the buffered state of the stream, and will always setup | |
1184 | buffering if needed. | |
1185 | ||
1186 | If no C<type> parameter is provided to Imager::read() or | |
1187 | Imager::read_multi(), Imager will call C<i_io_peekn()> when probing | |
1188 | for the file format. | |
1189 | ||
1190 | Returns -1 on error, 0 if there is no data before EOF, or the number | |
1191 | of bytes read into C<buffer>. | |
1192 | ||
1193 | ||
1194 | =for comment | |
1195 | From: File iolayer.c | |
1196 | ||
1197 | =item i_io_putc(ig, c) | |
1198 | ||
1199 | Write a single character to the stream. | |
1200 | ||
1201 | On success return c, on error returns EOF | |
1202 | ||
1203 | ||
1204 | =for comment | |
1205 | From: File iolayer.c | |
1206 | ||
1207 | =item i_io_read(io, buffer, size) | |
1208 | ||
1209 | Read up to C<size> bytes from the stream C<io> into C<buffer>. | |
1210 | ||
1211 | Returns the number of bytes read. Returns 0 on end of file. Returns | |
1212 | -1 on error. | |
1213 | ||
1214 | ||
1215 | =for comment | |
1216 | From: File iolayer.c | |
1217 | ||
1218 | =item i_io_seek(io, offset, whence) | |
1219 | ||
1220 | Seek within the stream. | |
1221 | ||
1222 | Acts like perl's seek. | |
1223 | ||
1224 | ||
1225 | =for comment | |
1226 | From: File iolayer.c | |
1227 | ||
1228 | =item i_io_set_buffered(io, buffered) | |
1229 | ||
1230 | Set the buffering mode of the stream. | |
1231 | ||
1232 | If you switch buffering off on a stream with buffering on: | |
1233 | ||
1234 | =over | |
1235 | ||
1236 | =item * | |
1237 | ||
1238 | any buffered output will be flushed. | |
1239 | ||
1240 | =item * | |
1241 | ||
1242 | any existing buffered input will be consumed before reads become | |
1243 | unbuffered. | |
1244 | ||
1245 | =back | |
1246 | ||
1247 | Returns true on success. This may fail if any buffered output cannot | |
1248 | be flushed. | |
1249 | ||
1250 | ||
1251 | =for comment | |
1252 | From: File iolayer.c | |
1253 | ||
1254 | =item i_io_write(io, buffer, size) | |
1255 | ||
1256 | ssize_t result = i_io_write(io, buffer, size) | |
1257 | ||
1258 | Write to the given I/O stream. | |
1259 | ||
1260 | Returns the number of bytes written. | |
1261 | ||
1262 | ||
1263 | =for comment | |
1264 | From: File iolayer.c | |
1265 | ||
1266 | =item io_slurp(ig, c) | |
1267 | ||
1268 | Takes the source that the io_glue is bound to and allocates space for | |
1269 | a return buffer and returns the entire content in a single buffer. | |
1270 | Note: This only works for io_glue objects created by | |
1271 | io_new_bufchain(). It is useful for saving to scalars and such. | |
1272 | ||
1273 | ig - io_glue object | |
1274 | c - pointer to a pointer to where data should be copied to | |
1275 | ||
1276 | char *data; | |
1277 | size_t size = io_slurp(ig, &data); | |
1278 | ... do something with the data ... | |
1279 | myfree(data); | |
1280 | ||
1281 | io_slurp() will abort the program if the supplied I/O layer is not | |
1282 | from io_new_bufchain(). | |
1283 | ||
1284 | ||
1285 | =for comment | |
1286 | From: File iolayer.c | |
1287 | ||
1288 | =item io_glue_destroy(ig) | |
1289 | ||
1290 | io_glue_destroy(ig); | |
1291 | ||
1292 | Destroy an io_glue objects. Should clean up all related buffers. | |
1293 | ||
1294 | ig - io_glue object to destroy. | |
1295 | ||
1296 | ||
1297 | =for comment | |
1298 | From: File iolayer.c | |
1299 | ||
1300 | ||
1301 | =back | |
1302 | ||
1032 | 1303 | =head2 Image |
1033 | 1304 | |
1034 | 1305 | =over |
1343 | 1614 | |
1344 | 1615 | =item i_img_setmask(C<im>, C<ch_mask>) |
1345 | 1616 | |
1346 | // only channel 0 writeable | |
1617 | // only channel 0 writable | |
1347 | 1618 | i_img_setmask(img, 0x01); |
1348 | 1619 | |
1349 | 1620 | Set the image channel mask for C<im> to C<ch_mask>. |
72 | 72 | |
73 | 73 | =over |
74 | 74 | |
75 | =item read | |
75 | =item read() | |
76 | 76 | |
77 | 77 | Reading writing to and from files is simple, use the C<read()> |
78 | 78 | method to read an image: |
87 | 87 | $img->read(file => $filename) |
88 | 88 | or die "Cannot read $filename: ", $img->errstr; |
89 | 89 | |
90 | The read() method accepts the C<allow_partial> parameter. If this is | |
91 | non-zero then read() can return true on an incomplete image and set | |
90 | The read() method accepts the C<allow_incomplete> parameter. If this | |
91 | is non-zero then read() can return true on an incomplete image and set | |
92 | 92 | the C<i_incomplete> tag. |
93 | 93 | |
94 | 94 | From Imager 0.68 you can supply most read() parameters to the new() |
99 | 99 | my $img = Imager->new(file => $filename) |
100 | 100 | or die "Cannot read $filename: ", Imager->errstr; |
101 | 101 | |
102 | =item write | |
102 | =item write() | |
103 | 103 | |
104 | 104 | and the C<write()> method to write an image: |
105 | 105 | |
106 | 106 | $img->write(file=>$filename, type=>$type) |
107 | 107 | or die "Cannot write $filename: ", $img->errstr; |
108 | 108 | |
109 | =item read_multi | |
109 | =item read_multi() | |
110 | 110 | |
111 | 111 | If you're reading from a format that supports multiple images per |
112 | 112 | file, use the C<read_multi()> method: |
117 | 117 | As with the read() method, Imager will normally detect the C<type> |
118 | 118 | automatically. |
119 | 119 | |
120 | =item write_multi | |
120 | =item write_multi() | |
121 | 121 | |
122 | 122 | and if you want to write multiple images to a single file use the |
123 | 123 | C<write_multi()> method: |
251 | 251 | |
252 | 252 | =item * |
253 | 253 | |
254 | C<callback> - Imager will make calls back to your supplied coderefs to | |
255 | read, write and seek from/to/through the image file. | |
254 | C<callback>, C<readcb>, C<writecb>, C<seekcb>, C<closecb> - Imager | |
255 | will make calls back to your supplied coderefs to read, write and seek | |
256 | from/to/through the image file. See L</"I/O Callbacks"> below for details. | |
257 | ||
258 | =item * | |
259 | ||
260 | C<io> - an L<Imager::IO> object. | |
261 | ||
262 | =back | |
263 | ||
264 | X<buffering>X<unbuffered>By default Imager will use buffered I/O when | |
265 | reading or writing an image. You can disabled buffering for output by | |
266 | supplying a C<< buffered => 0 >> parameter to C<write()> or | |
267 | C<write_multi()>. | |
268 | ||
269 | =head2 I/O Callbacks | |
256 | 270 | |
257 | 271 | When reading from a file you can use either C<callback> or C<readcb> |
258 | 272 | to supply the read callback, and when writing C<callback> or |
259 | 273 | C<writecb> to supply the write callback. |
260 | 274 | |
261 | When writing you can also supply the C<maxbuffer> option to set the | |
262 | maximum amount of data that will be buffered before your write | |
263 | callback is called. Note: the amount of data supplied to your | |
264 | callback can be smaller or larger than this size. | |
265 | ||
266 | The read callback is called with 2 parameters, the minimum amount of | |
267 | data required, and the maximum amount that Imager will store in it's C | |
268 | level buffer. You may want to return the minimum if you have a slow | |
269 | data source, or the maximum if you have a fast source and want to | |
270 | prevent many calls to your perl callback. The read data should be | |
271 | returned as a scalar. | |
272 | ||
273 | Your write callback takes exactly one parameter, a scalar containing | |
274 | the data to be written. Return true for success. | |
275 | ||
276 | The seek callback takes 2 parameters, a I<POSITION>, and a I<WHENCE>, | |
277 | defined in the same way as perl's seek function. | |
278 | ||
279 | You can also supply a C<closecb> which is called with no parameters | |
280 | when there is no more data to be written. This could be used to flush | |
281 | buffered data. | |
275 | Some file formats, currently only C<TIFF>, also require a C<seekcb> | |
276 | parameter to change position in the file. If no C<seekcb> parameter | |
277 | is provided a default will be provided that fails. | |
278 | ||
279 | You can also provide a C<closecb> parameter called when writing the | |
280 | file is complete. | |
282 | 281 | |
283 | 282 | # contrived |
284 | 283 | my $data; |
289 | 288 | Imager->write_multi({ callback => \&mywrite, type => 'gif'}, @images) |
290 | 289 | or die Imager->errstr; |
291 | 290 | |
292 | Note that for reading you'll almost always need to provide a | |
293 | C<seekcb>. | |
294 | ||
295 | =back | |
291 | =head3 C<readcb> | |
292 | ||
293 | The read callback is called with 2 parameters: | |
294 | ||
295 | =over | |
296 | ||
297 | =item * | |
298 | ||
299 | C<size> - the minimum amount of data required. | |
300 | ||
301 | =item * | |
302 | ||
303 | C<maxsize> - previously this was the maximum amount of data returnable | |
304 | - currently it's always the same as C<size> | |
305 | ||
306 | =back | |
307 | ||
308 | Your read callback should return the data as a scalar: | |
309 | ||
310 | =over | |
311 | ||
312 | =item * | |
313 | ||
314 | on success, a string containing the bytes read. | |
315 | ||
316 | =item * | |
317 | ||
318 | on end of file, an empty string | |
319 | ||
320 | =item * | |
321 | ||
322 | on error, C<undef>. | |
323 | ||
324 | =back | |
325 | ||
326 | If your return value contains more data than C<size> Imager will | |
327 | panic. | |
328 | ||
329 | Your return value must not contain any characters over C<\xFF> or | |
330 | Imager will panic. | |
331 | ||
332 | =head3 C<writecb> | |
333 | ||
334 | Your write callback takes exactly one parameter, a scalar containing | |
335 | the data to be written. | |
336 | ||
337 | Return true for success. | |
338 | ||
339 | =head3 C<seekcb> | |
340 | ||
341 | The seek callback takes 2 parameters, a I<POSITION>, and a I<WHENCE>, | |
342 | defined in the same way as perl's seek function. | |
343 | ||
344 | Previously you always needed a C<seekcb> callback if you called | |
345 | Imager's L</read()> or L</read_multi()> without a C<type> parameter, | |
346 | but this is no longer necessary unless the file handler requires | |
347 | seeking, such as for TIFF files. | |
348 | ||
349 | Returns the new position in the file, or -1 on failure. | |
350 | ||
351 | =head3 C<closecb> | |
352 | ||
353 | You can also supply a C<closecb> which is called with no parameters | |
354 | when there is no more data to be written. This could be used to flush | |
355 | buffered data. | |
356 | ||
357 | Return true on success. | |
296 | 358 | |
297 | 359 | =head2 Guessing types |
360 | X<FORMATGUESS> | |
298 | 361 | |
299 | 362 | When writing to a file, if you don't supply a C<type> parameter Imager |
300 | 363 | will attempt to guess it from the file name. This is done by calling |
307 | 370 | =over |
308 | 371 | |
309 | 372 | =item def_guess_type() |
373 | X<methods, def_guess_type()> | |
310 | 374 | |
311 | 375 | This is the default function Imager uses to derive a file type from a |
312 | 376 | file name. This is a function, not a method. |
8 | 8 | |
9 | 9 | sub new { |
10 | 10 | my $class = shift; |
11 | my %hsh=(color=>Imager::Color->new(255,0,0,0), | |
11 | my %hsh=(color=>Imager::Color->new(255,0,0,255), | |
12 | 12 | size=>15, |
13 | 13 | @_); |
14 | 14 |
50 | 50 | my $class = shift; |
51 | 51 | my $self = {}; |
52 | 52 | my ($file, $type, $id); |
53 | my %hsh=(color => Imager::Color->new(255,0,0,0), | |
53 | my %hsh=(color => Imager::Color->new(255,0,0,255), | |
54 | 54 | size => 15, |
55 | 55 | @_); |
56 | 56 |
19 | 19 | If you're writing an Imager file handler your code will be passed an |
20 | 20 | Imager::IO object to write to or read from. |
21 | 21 | |
22 | =head1 METHODS | |
23 | ||
24 | =over | |
25 | ||
26 | =item write | |
22 | X<UTF-8>X<Unicode>Note that Imager::IO can only work with collections of bytes - | |
23 | if you need to read UTF-8 data you will need to read the bytes and | |
24 | decode them. If you want to write UTF-8 data you will need to encode | |
25 | your characters to bytes and write the bytes. | |
26 | ||
27 | =head1 CONSTRUCTORS | |
28 | ||
29 | =over | |
30 | ||
31 | =item new_fd($fd) | |
32 | ||
33 | Create a new I/O layer based on a file descriptor. | |
34 | ||
35 | my $io = Imager::IO->new(fileno($fh)); | |
36 | ||
37 | =item new_buffer($data) | |
38 | ||
39 | Create a new I/O layer based on a memory buffer. | |
40 | ||
41 | The supplied variable must not be changed on the the life of the I/O | |
42 | object. | |
43 | ||
44 | Buffer I/O layers are read only. | |
45 | ||
46 | =item new_cb($writecb, $readcb, $seekcb, $closecb) | |
47 | ||
48 | Create a new I/O layer based on callbacks. See | |
49 | L<Imager::Files/"I/O Callbacks"> for details on the behavior of | |
50 | the callbacks. | |
51 | ||
52 | =item new_bufchain() | |
53 | ||
54 | Create a new C<bufchain> based I/O layer. This accumulates the file | |
55 | data as a chain of buffers starting from an empty stream. | |
56 | ||
57 | Use the L</slurp()> method to retrieve the accumulated content into a | |
58 | perl string. | |
59 | ||
60 | =back | |
61 | ||
62 | =head1 BUFFERED I/O METHODS | |
63 | ||
64 | These methods use buffered I/O to improve performance unless you call | |
65 | set_buffered() to disable buffering. | |
66 | ||
67 | Prior to Imager 0.86 the write and read methods performed raw I/O. | |
68 | ||
69 | =over | |
70 | ||
71 | =item write($data) | |
27 | 72 | |
28 | 73 | Call to write to the file. Returns the number of bytes written. The |
29 | 74 | data provided may contain only characters \x00 to \xFF - characters |
36 | 81 | isn't the number of bytes supplied you'll want to treat it as an error |
37 | 82 | anyway. |
38 | 83 | |
39 | =item read | |
84 | =item read($buffer, $size) | |
40 | 85 | |
41 | 86 | my $buffer; |
42 | 87 | my $count = $io->read($buffer, $max_bytes); |
46 | 91 | success or an empty list on failure. Note that a read of zero bytes |
47 | 92 | is B<not> a failure, this indicates end of file. |
48 | 93 | |
49 | =item read2 | |
94 | =item read2($size) | |
50 | 95 | |
51 | 96 | my $buffer = $io->read2($max_bytes); |
52 | 97 | |
53 | 98 | An alternative interface to read, that might be simpler to use in some |
54 | 99 | cases. |
55 | 100 | |
56 | Returns the data read or an empty list. | |
57 | ||
58 | =item seek | |
101 | Returns the data read or an empty list. At end of file the data read | |
102 | will be an empty string. | |
103 | ||
104 | =item seek($offset, $whence) | |
59 | 105 | |
60 | 106 | my $new_position = $io->seek($offset, $whence); |
61 | 107 | |
82 | 128 | Note that seeking past the end of the file may or may not result in an |
83 | 129 | error. |
84 | 130 | |
131 | Any buffered output will be flushed, if flushing fails, seek() will | |
132 | return -1. | |
133 | ||
85 | 134 | Returns the new position in the file, or -1 on error. |
86 | 135 | |
87 | =item close | |
136 | =item getc() | |
137 | ||
138 | Return the next byte from the stream. | |
139 | ||
140 | Returns the ordinal of the byte or -1 on error or end of file. | |
141 | ||
142 | while ((my $c = $io->getc) != -1) { | |
143 | print chr($c); | |
144 | } | |
145 | ||
146 | =item gets() | |
147 | ||
148 | =item gets($max_size) | |
149 | ||
150 | =item gets($max_size, $end_of_line) | |
151 | ||
152 | Returns the next line of input from the stream, as terminated by | |
153 | C<end_of_line>. | |
154 | ||
155 | The default C<max_size> is 8192. | |
156 | ||
157 | The default C<end_of_line> is C<ord "\n">. | |
158 | ||
159 | Returns nothing if the stream is in error or at end of file. | |
160 | ||
161 | Returns the line as a string, including the line terminator (if one | |
162 | was found) on success. | |
163 | ||
164 | while (defined(my $line = $io->gets)) { | |
165 | # do something with $line | |
166 | } | |
167 | ||
168 | =item peekc() | |
169 | ||
170 | Return the buffered next character from the stream, loading the buffer | |
171 | if necessary. | |
172 | ||
173 | For an unbuffered stream a buffer will be setup and loaded with a | |
174 | single character. | |
175 | ||
176 | Returns the ordinal of the byte or -1 on error or end of file. | |
177 | ||
178 | my $c = $io->peekc; | |
179 | ||
180 | =item peekn($size) | |
181 | ||
182 | Returns up to the next C<size> bytes from the file as a string. | |
183 | ||
184 | Only up to the stream buffer size bytes (currently 8192) can be peeked. | |
185 | ||
186 | This method ignores the buffering state of the stream. | |
187 | ||
188 | Returns nothing on EOF. | |
189 | ||
190 | my $s = $io->peekn(4); | |
191 | if ($s =~ /^(II|MM)\*\0/) { | |
192 | print "TIFF image"; | |
193 | } | |
194 | ||
195 | =item putc($code) | |
196 | ||
197 | Write a single character to the stream. | |
198 | ||
199 | Returns C<code> on success, or -1 on failure. | |
200 | ||
201 | =item close() | |
88 | 202 | |
89 | 203 | my $result = $io->close; |
90 | 204 | |
91 | Call when you're with the file. If the IO object is connected to a | |
92 | file this won't close the file handle, but buffers may be flushed (if | |
93 | any). | |
205 | Call when you're done with the file. If the IO object is connected to | |
206 | a file this won't close the file handle, but buffers may be flushed | |
207 | (if any). | |
94 | 208 | |
95 | 209 | Returns 0 on success, -1 on failure. |
96 | 210 | |
211 | =item eof() | |
212 | ||
213 | $io->eof | |
214 | ||
215 | Test if the stream is at end of file. No further read requests will | |
216 | be passed to your read callback until you seek(). | |
217 | ||
218 | =item error() | |
219 | ||
220 | Test if the stream has encountered a read or write error. | |
221 | ||
222 | my $data = $io->read2(100); | |
223 | $io->error | |
224 | and die "Failed"; | |
225 | ||
226 | When the stream has the error flag set no further read or write | |
227 | requests will be passed to your callbacks until you seek. | |
228 | ||
229 | =item flush() | |
230 | ||
231 | $io->flush | |
232 | or die "Flush error"; | |
233 | ||
234 | Flush any buffered output. This will not call lower write layers when | |
235 | the stream has it's error flag set. | |
236 | ||
237 | Returns a true value on success. | |
238 | ||
239 | =item is_buffered() | |
240 | ||
241 | Test if buffering is enabled for this stream. | |
242 | ||
243 | Returns a true value if the stream is buffered. | |
244 | ||
245 | =item set_buffered($enabled) | |
246 | ||
247 | If C<$enabled> is a non-zero integer, enable buffering, other disable | |
248 | it. | |
249 | ||
250 | Disabling buffering will flush any buffered output, but any buffered | |
251 | input will be retained and consumed by input methods. | |
252 | ||
253 | Returns true if any buffered output was flushed successfully, false if | |
254 | there was an error flushing output. | |
255 | ||
256 | =back | |
257 | ||
258 | =head1 RAW I/O METHODS | |
259 | ||
260 | These call the underlying I/O abstraction directly. | |
261 | ||
262 | =over | |
263 | ||
264 | =item raw_write() | |
265 | ||
266 | Call to write to the file. Returns the number of bytes written. The | |
267 | data provided may contain only characters \x00 to \xFF - characters | |
268 | outside this range will cause this method to croak(). | |
269 | ||
270 | If you supply a UTF-8 flagged string it will be converted to a byte | |
271 | string, which may have a performance impact. | |
272 | ||
273 | Returns -1 on error, though in most cases if the result of the write | |
274 | isn't the number of bytes supplied you'll want to treat it as an error | |
275 | anyway. | |
276 | ||
277 | =item raw_read() | |
278 | ||
279 | my $buffer; | |
280 | my $count = $io->raw_read($buffer, $max_bytes); | |
281 | ||
282 | Reads up to I<$max_bytes> bytes from the current position in the file | |
283 | and stores them in I<$buffer>. Returns the number of bytes read on | |
284 | success or an empty list on failure. Note that a read of zero bytes | |
285 | is B<not> a failure, this indicates end of file. | |
286 | ||
287 | =item raw_read2() | |
288 | ||
289 | my $buffer = $io->raw_read2($max_bytes); | |
290 | ||
291 | An alternative interface to raw_read, that might be simpler to use in some | |
292 | cases. | |
293 | ||
294 | Returns the data read or an empty list. | |
295 | ||
296 | =item raw_seek() | |
297 | ||
298 | my $new_position = $io->raw_seek($offset, $whence); | |
299 | ||
300 | Seek to a new position in the file. Possible values for I<$whence> are: | |
301 | ||
302 | =over | |
303 | ||
304 | =item * | |
305 | ||
306 | C<SEEK_SET> - I<$offset> is the new position in the file. | |
307 | ||
308 | =item * | |
309 | ||
310 | C<SEEK_CUR> - I<$offset> is the offset from the current position in | |
311 | the file. | |
312 | ||
313 | =item * | |
314 | ||
315 | C<SEEK_END> - I<$offset> is the offset relative to the end of the | |
316 | file. | |
317 | ||
318 | =back | |
319 | ||
320 | Note that seeking past the end of the file may or may not result in an | |
321 | error. | |
322 | ||
323 | Returns the new position in the file, or -1 on error. | |
324 | ||
325 | =item raw_close() | |
326 | ||
327 | my $result = $io->raw_close; | |
328 | ||
329 | Call when you're done with the file. If the IO object is connected to | |
330 | a file this won't close the file handle. | |
331 | ||
332 | Returns 0 on success, -1 on failure. | |
333 | ||
334 | =back | |
335 | ||
336 | =head1 UTILITY METHODS | |
337 | ||
338 | =over | |
339 | ||
340 | =item slurp() | |
341 | ||
342 | Retrieve the data accumulated from an I/O layer object created with | |
343 | the new_bufchain() method. | |
344 | ||
345 | my $data = $io->slurp; | |
346 | ||
347 | =item dump() | |
348 | ||
349 | Dump the internal buffering state of the I/O object to C<stderr>. | |
350 | ||
351 | $io->dump(); | |
352 | ||
97 | 353 | =back |
98 | 354 | |
99 | 355 | =head1 AUTHOR |
3 | 3 | use Scalar::Util qw(reftype looks_like_number); |
4 | 4 | use Carp qw(croak); |
5 | 5 | |
6 | $VERSION = "1.009"; | |
6 | $VERSION = "1.0010"; | |
7 | 7 | |
8 | 8 | =head1 NAME |
9 | 9 | |
60 | 60 | use overload |
61 | 61 | '*' => \&_mult, |
62 | 62 | '+' => \&_add, |
63 | '""'=>\&_string; | |
63 | '""'=>\&_string, | |
64 | "eq" => \&_eq; | |
64 | 65 | |
65 | 66 | =item identity() |
66 | 67 | |
373 | 374 | $out; |
374 | 375 | } |
375 | 376 | |
377 | =item _eq | |
378 | ||
379 | Implement the overloaded equality operator. | |
380 | ||
381 | Provided for older perls that don't handle magic auto generation of eq | |
382 | from "". | |
383 | ||
384 | =cut | |
385 | ||
386 | sub _eq { | |
387 | my ($left, $right) = @_; | |
388 | ||
389 | return $left . "" eq $right . ""; | |
390 | } | |
391 | ||
376 | 392 | =back |
377 | 393 | |
378 | 394 | The following functions are shortcuts to the various constructors. |
1 | 1 | use strict; |
2 | 2 | use File::Spec; |
3 | 3 | use Config; |
4 | use Cwd (); | |
4 | 5 | |
5 | 6 | my @alt_transfer = qw/altname incsuffix libbase/; |
6 | 7 | |
129 | 130 | return; |
130 | 131 | } |
131 | 132 | |
133 | sub _is_msvc { | |
134 | return $Config{cc} eq "cl"; | |
135 | } | |
136 | ||
137 | sub _lib_basename { | |
138 | my ($base) = @_; | |
139 | ||
140 | if (_is_msvc()) { | |
141 | return $base; | |
142 | } | |
143 | else { | |
144 | return "lib$base"; | |
145 | } | |
146 | } | |
147 | ||
148 | sub _lib_option { | |
149 | my ($base) = @_; | |
150 | ||
151 | if (_is_msvc()) { | |
152 | return $base . $Config{_a}; | |
153 | } | |
154 | else { | |
155 | return "-l$base"; | |
156 | } | |
157 | } | |
158 | ||
159 | sub _quotearg { | |
160 | my ($opt) = @_; | |
161 | ||
162 | return $opt =~ /\s/ ? qq("$opt") : $opt; | |
163 | } | |
164 | ||
132 | 165 | sub _probe_check { |
133 | 166 | my ($req) = @_; |
134 | 167 | |
138 | 171 | # synthesize a libcheck |
139 | 172 | my $lext=$Config{'so'}; # Get extensions of libraries |
140 | 173 | my $aext=$Config{'_a'}; |
174 | my $basename = _lib_basename($libbase); | |
141 | 175 | $libcheck = sub { |
142 | -e File::Spec->catfile($_[0], "lib$libbase$aext") | |
143 | || -e File::Spec->catfile($_[0], "lib$libbase.$lext") | |
176 | -e File::Spec->catfile($_[0], "$basename$aext") | |
177 | || -e File::Spec->catfile($_[0], "$basename.$lext") | |
144 | 178 | }; |
145 | 179 | } |
146 | 180 | |
186 | 220 | push @libs, $req->{libopts}; |
187 | 221 | } |
188 | 222 | elsif ($libbase) { |
189 | push @libs, "-l$libbase"; | |
223 | push @libs, _lib_option($libbase); | |
190 | 224 | } |
191 | 225 | else { |
192 | 226 | die "$req->{altname}: inccheck but no libbase or libopts"; |
194 | 228 | |
195 | 229 | return |
196 | 230 | { |
197 | INC => "-I$found_incpath", | |
198 | LIBS => "@libs", | |
231 | INC => _quotearg("-I$found_incpath"), | |
232 | LIBS => join(" ", map _quotearg($_), @libs), | |
199 | 233 | DEFINE => "", |
200 | 234 | }; |
201 | 235 | } |
289 | 323 | $^O eq "cygwin" ? "/usr/lib/w32api" : "", |
290 | 324 | "/usr/lib", |
291 | 325 | "/usr/local/lib", |
326 | _gcc_lib_paths(), | |
292 | 327 | ); |
328 | } | |
329 | ||
330 | sub _gcc_lib_paths { | |
331 | $Config{gccversion} | |
332 | or return; | |
333 | ||
334 | my ($base_version) = $Config{gccversion} =~ /^([0-9]+)/ | |
335 | or return; | |
336 | ||
337 | $base_version >= 4 | |
338 | or return; | |
339 | ||
340 | my ($lib_line) = grep /^libraries:/, `$Config{cc} -print-search-dirs` | |
341 | or return; | |
342 | $lib_line =~ s/^libraries: =//; | |
343 | chomp $lib_line; | |
344 | ||
345 | return grep !/gcc/ && -d, split /:/, $lib_line; | |
293 | 346 | } |
294 | 347 | |
295 | 348 | sub _inc_paths { |
331 | 384 | |
332 | 385 | push @out, grep -d $_, split /\Q$Config{path_sep}/, $path; |
333 | 386 | } |
387 | ||
388 | @out = map Cwd::realpath($_), @out; | |
389 | ||
390 | my %seen; | |
391 | @out = grep !$seen{$_}++, @out; | |
334 | 392 | |
335 | 393 | return @out; |
336 | 394 | } |
2 | 2 | use Test::Builder; |
3 | 3 | require Exporter; |
4 | 4 | use vars qw(@ISA @EXPORT_OK $VERSION); |
5 | use Carp qw(croak); | |
5 | 6 | |
6 | 7 | $VERSION = "1.000"; |
7 | 8 | |
13 | 14 | test_image_16 |
14 | 15 | test_image |
15 | 16 | test_image_double |
17 | test_image_mono | |
18 | test_image_gray | |
19 | test_image_gray_16 | |
20 | test_image_named | |
16 | 21 | is_color1 |
17 | 22 | is_color3 |
18 | 23 | is_color4 |
320 | 325 | my $blue = Imager::Color->new(0, 0, 255, 255); |
321 | 326 | my $red = Imager::Color->new(255, 0, 0, 255); |
322 | 327 | my $img = Imager->new(xsize => 150, ysize => 150, bits => 16); |
323 | $img->box(filled => 1, color => $green, box => [ 70, 25, 130, 125 ]); | |
324 | $img->box(filled => 1, color => $blue, box => [ 20, 25, 80, 125 ]); | |
328 | $img->box(filled => 1, color => $green, box => [ 70, 24, 130, 124 ]); | |
329 | $img->box(filled => 1, color => $blue, box => [ 20, 26, 80, 126 ]); | |
325 | 330 | $img->arc(x => 75, y => 75, r => 30, color => $red); |
326 | 331 | $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]); |
327 | 332 | |
333 | 338 | my $blue = Imager::Color->new(0, 0, 255, 255); |
334 | 339 | my $red = Imager::Color->new(255, 0, 0, 255); |
335 | 340 | my $img = Imager->new(xsize => 150, ysize => 150, bits => 'double'); |
336 | $img->box(filled => 1, color => $green, box => [ 70, 25, 130, 125 ]); | |
337 | $img->box(filled => 1, color => $blue, box => [ 20, 25, 80, 125 ]); | |
341 | $img->box(filled => 1, color => $green, box => [ 70, 24, 130, 124 ]); | |
342 | $img->box(filled => 1, color => $blue, box => [ 20, 26, 80, 126 ]); | |
338 | 343 | $img->arc(x => 75, y => 75, r => 30, color => $red); |
339 | 344 | $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]); |
340 | 345 | |
341 | 346 | $img; |
347 | } | |
348 | ||
349 | sub test_image_gray { | |
350 | my $g50 = Imager::Color->new(128, 128, 128); | |
351 | my $g30 = Imager::Color->new(76, 76, 76); | |
352 | my $g70 = Imager::Color->new(178, 178, 178); | |
353 | my $img = Imager->new(xsize => 150, ysize => 150, channels => 1); | |
354 | $img->box(filled => 1, color => $g50, box => [ 70, 24, 130, 124 ]); | |
355 | $img->box(filled => 1, color => $g30, box => [ 20, 26, 80, 126 ]); | |
356 | $img->arc(x => 75, y => 75, r => 30, color => $g70); | |
357 | $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]); | |
358 | ||
359 | return $img; | |
360 | } | |
361 | ||
362 | sub test_image_gray_16 { | |
363 | my $g50 = Imager::Color->new(128, 128, 128); | |
364 | my $g30 = Imager::Color->new(76, 76, 76); | |
365 | my $g70 = Imager::Color->new(178, 178, 178); | |
366 | my $img = Imager->new(xsize => 150, ysize => 150, channels => 1, bits => 16); | |
367 | $img->box(filled => 1, color => $g50, box => [ 70, 24, 130, 124 ]); | |
368 | $img->box(filled => 1, color => $g30, box => [ 20, 26, 80, 126 ]); | |
369 | $img->arc(x => 75, y => 75, r => 30, color => $g70); | |
370 | $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]); | |
371 | ||
372 | return $img; | |
373 | } | |
374 | ||
375 | sub test_image_mono { | |
376 | require Imager::Fill; | |
377 | my $fh = Imager::Fill->new(hatch => 'check1x1'); | |
378 | my $img = Imager->new(xsize => 150, ysize => 150, type => "paletted"); | |
379 | my $black = Imager::Color->new(0, 0, 0); | |
380 | my $white = Imager::Color->new(255, 255, 255); | |
381 | $img->addcolors(colors => [ $black, $white ]); | |
382 | $img->box(fill => $fh, box => [ 70, 24, 130, 124 ]); | |
383 | $img->box(filled => 1, color => $white, box => [ 20, 26, 80, 126 ]); | |
384 | $img->arc(x => 75, y => 75, r => 30, color => $black, aa => 0); | |
385 | ||
386 | return $img; | |
387 | } | |
388 | ||
389 | my %name_to_sub = | |
390 | ( | |
391 | basic => \&test_image, | |
392 | basic16 => \&test_image_16, | |
393 | basic_double => \&test_image_double, | |
394 | gray => \&test_image_gray, | |
395 | gray16 => \&test_image_gray_16, | |
396 | mono => \&test_image_mono, | |
397 | ); | |
398 | ||
399 | sub test_image_named { | |
400 | my $name = shift | |
401 | or croak("No name supplied to test_image_named()"); | |
402 | my $sub = $name_to_sub{$name} | |
403 | or croak("Unknown name $name supplied to test_image_named()"); | |
404 | ||
405 | return $sub->(); | |
342 | 406 | } |
343 | 407 | |
344 | 408 | sub _low_image_diff_check { |
821 | 885 | |
822 | 886 | =item test_image() |
823 | 887 | |
824 | Returns a 150x150x3 8-bit/sample OO test image. | |
888 | Returns a 150x150x3 8-bit/sample OO test image. Name: C<basic>. | |
825 | 889 | |
826 | 890 | =item test_image_16() |
827 | 891 | |
828 | Returns a 150x150x3 16-bit/sample OO test image. | |
892 | Returns a 150x150x3 16-bit/sample OO test image. Name: C<basic16> | |
829 | 893 | |
830 | 894 | =item test_image_double() |
831 | 895 | |
832 | Returns a 150x150x3 double/sample OO test image. | |
896 | Returns a 150x150x3 double/sample OO test image. Name: C<basic_double>. | |
897 | ||
898 | =item test_image_gray() | |
899 | ||
900 | Returns a 150x150 single channel OO test image. Name: C<gray>. | |
901 | ||
902 | =item test_image_gray_16() | |
903 | ||
904 | Returns a 150x150 16-bit/sample single channel OO test image. Name: | |
905 | C<gray16>. | |
906 | ||
907 | =item test_image_mono() | |
908 | ||
909 | Returns a 150x150 bilevel image that passes the is_bilevel() test. | |
910 | Name: C<mono>. | |
911 | ||
912 | =item test_image_named($name) | |
913 | ||
914 | Return one of the other test images above based on name. | |
833 | 915 | |
834 | 916 | =item color_cmp($c1, $c2) |
835 | 917 |
35 | 35 | */ |
36 | 36 | |
37 | 37 | |
38 | #define BSIZ 1024 | |
39 | 38 | #define misspace(x) (x==' ' || x=='\n' || x=='\r' || x=='\t' || x=='\f' || x=='\v') |
40 | 39 | #define misnumber(x) (x <= '9' && x>='0') |
41 | 40 | |
42 | 41 | static char *typenames[]={"ascii pbm", "ascii pgm", "ascii ppm", "binary pbm", "binary pgm", "binary ppm"}; |
43 | 42 | |
44 | 43 | /* |
45 | * Type to encapsulate the local buffer | |
46 | * management skipping over in a file | |
47 | */ | |
48 | ||
49 | typedef struct { | |
50 | io_glue *ig; | |
51 | int len; | |
52 | int cp; | |
53 | char buf[BSIZ]; | |
54 | } mbuf; | |
55 | ||
56 | ||
57 | static | |
58 | void init_buf(mbuf *mb, io_glue *ig) { | |
59 | mb->len = 0; | |
60 | mb->cp = 0; | |
61 | mb->ig = ig; | |
62 | } | |
63 | ||
64 | ||
65 | ||
66 | /* | |
67 | =item gnext(mbuf *mb) | |
68 | ||
69 | Fetches a character and advances in stream by one character. | |
70 | Returns a pointer to the byte or NULL on failure (internal). | |
71 | ||
72 | mb - buffer object | |
44 | =item skip_spaces(ig) | |
45 | ||
46 | Advances in stream until it is positioned at a | |
47 | non white space character. (internal) | |
48 | ||
49 | ig - io_glue | |
73 | 50 | |
74 | 51 | =cut |
75 | 52 | */ |
76 | 53 | |
77 | #define gnext(mb) (((mb)->cp == (mb)->len) ? gnextf(mb) : (mb)->buf + (mb)->cp++) | |
78 | ||
79 | static | |
80 | char * | |
81 | gnextf(mbuf *mb) { | |
82 | io_glue *ig = mb->ig; | |
83 | if (mb->cp == mb->len) { | |
84 | mb->cp = 0; | |
85 | mb->len = ig->readcb(ig, mb->buf, BSIZ); | |
86 | if (mb->len == -1) { | |
87 | i_push_error(errno, "file read error"); | |
88 | mm_log((1, "i_readpnm: read error\n")); | |
89 | return NULL; | |
90 | } | |
91 | if (mb->len == 0) { | |
92 | mm_log((1, "i_readpnm: end of file\n")); | |
93 | return NULL; | |
94 | } | |
95 | } | |
96 | return &mb->buf[mb->cp++]; | |
54 | static | |
55 | int | |
56 | skip_spaces(io_glue *ig) { | |
57 | int c; | |
58 | while( (c = i_io_peekc(ig)) != EOF && misspace(c) ) { | |
59 | if ( i_io_getc(ig) == EOF ) | |
60 | break; | |
61 | } | |
62 | if (c == EOF) | |
63 | return 0; | |
64 | ||
65 | return 1; | |
97 | 66 | } |
98 | 67 | |
99 | 68 | |
100 | 69 | /* |
101 | =item gpeek(mbuf *mb) | |
102 | ||
103 | Fetches a character but does NOT advance. Returns a pointer to | |
104 | the byte or NULL on failure (internal). | |
105 | ||
106 | mb - buffer object | |
70 | =item skip_comment(ig) | |
71 | ||
72 | Advances in stream over whitespace and a comment if one is found. (internal) | |
73 | ||
74 | ig - io_glue object | |
107 | 75 | |
108 | 76 | =cut |
109 | 77 | */ |
110 | 78 | |
111 | #define gpeek(mb) ((mb)->cp == (mb)->len ? gpeekf(mb) : (mb)->buf + (mb)->cp) | |
112 | ||
113 | static | |
114 | char * | |
115 | gpeekf(mbuf *mb) { | |
116 | io_glue *ig = mb->ig; | |
117 | if (mb->cp == mb->len) { | |
118 | mb->cp = 0; | |
119 | mb->len = ig->readcb(ig, mb->buf, BSIZ); | |
120 | if (mb->len == -1) { | |
121 | i_push_error(errno, "read error"); | |
122 | mm_log((1, "i_readpnm: read error\n")); | |
123 | return NULL; | |
124 | } | |
125 | if (mb->len == 0) { | |
126 | mm_log((1, "i_readpnm: end of file\n")); | |
127 | return NULL; | |
128 | } | |
129 | } | |
130 | return &mb->buf[mb->cp]; | |
131 | } | |
132 | ||
79 | static | |
133 | 80 | int |
134 | gread(mbuf *mb, unsigned char *buf, size_t read_size) { | |
135 | int total_read = 0; | |
136 | if (mb->cp != mb->len) { | |
137 | int avail_size = mb->len - mb->cp; | |
138 | int use_size = read_size > avail_size ? avail_size : read_size; | |
139 | memcpy(buf, mb->buf+mb->cp, use_size); | |
140 | mb->cp += use_size; | |
141 | total_read += use_size; | |
142 | read_size -= use_size; | |
143 | buf += use_size; | |
144 | } | |
145 | if (read_size) { | |
146 | io_glue *ig = mb->ig; | |
147 | int read_res = i_io_read(ig, buf, read_size); | |
148 | if (read_res >= 0) { | |
149 | total_read += read_res; | |
150 | } | |
151 | } | |
152 | return total_read; | |
81 | skip_comment(io_glue *ig) { | |
82 | int c; | |
83 | ||
84 | if (!skip_spaces(ig)) | |
85 | return 0; | |
86 | ||
87 | if ((c = i_io_peekc(ig)) == EOF) | |
88 | return 0; | |
89 | ||
90 | if (c == '#') { | |
91 | while( (c = i_io_peekc(ig)) != EOF && (c != '\n' && c != '\r') ) { | |
92 | if ( i_io_getc(ig) == EOF ) | |
93 | break; | |
94 | } | |
95 | } | |
96 | if (c == EOF) | |
97 | return 0; | |
98 | ||
99 | return 1; | |
153 | 100 | } |
154 | 101 | |
155 | 102 | |
156 | 103 | /* |
157 | =item skip_spaces(mb) | |
158 | ||
159 | Advances in stream until it is positioned at a | |
160 | non white space character. (internal) | |
104 | =item gnum(mb, i) | |
105 | ||
106 | Fetches the next number from stream and stores in i, returns true | |
107 | on success else false. | |
161 | 108 | |
162 | 109 | mb - buffer object |
110 | i - integer to store result in | |
163 | 111 | |
164 | 112 | =cut |
165 | 113 | */ |
166 | 114 | |
167 | 115 | static |
168 | 116 | int |
169 | skip_spaces(mbuf *mb) { | |
170 | char *cp; | |
171 | while( (cp = gpeek(mb)) && misspace(*cp) ) if ( !gnext(mb) ) break; | |
172 | if (!cp) return 0; | |
173 | return 1; | |
174 | } | |
175 | ||
176 | ||
177 | /* | |
178 | =item skip_comment(mb) | |
179 | ||
180 | Advances in stream over whitespace and a comment if one is found. (internal) | |
181 | ||
182 | mb - buffer object | |
183 | ||
184 | =cut | |
185 | */ | |
186 | ||
187 | static | |
188 | int | |
189 | skip_comment(mbuf *mb) { | |
190 | char *cp; | |
191 | ||
192 | if (!skip_spaces(mb)) return 0; | |
193 | ||
194 | if (!(cp = gpeek(mb))) return 0; | |
195 | if (*cp == '#') { | |
196 | while( (cp = gpeek(mb)) && (*cp != '\n' && *cp != '\r') ) { | |
197 | if ( !gnext(mb) ) break; | |
198 | } | |
199 | } | |
200 | if (!cp) return 0; | |
201 | ||
202 | return 1; | |
203 | } | |
204 | ||
205 | ||
206 | /* | |
207 | =item gnum(mb, i) | |
208 | ||
209 | Fetches the next number from stream and stores in i, returns true | |
210 | on success else false. | |
211 | ||
212 | mb - buffer object | |
213 | i - integer to store result in | |
214 | ||
215 | =cut | |
216 | */ | |
217 | ||
218 | static | |
219 | int | |
220 | gnum(mbuf *mb, int *i) { | |
221 | char *cp; | |
117 | gnum(io_glue *ig, int *i) { | |
118 | int c; | |
222 | 119 | *i = 0; |
223 | 120 | |
224 | if (!skip_spaces(mb)) return 0; | |
225 | ||
226 | if (!(cp = gpeek(mb))) | |
121 | if (!skip_spaces(ig)) return 0; | |
122 | ||
123 | if ((c = i_io_peekc(ig)) == EOF) | |
227 | 124 | return 0; |
228 | if (!misnumber(*cp)) | |
125 | if (!misnumber(c)) | |
229 | 126 | return 0; |
230 | while( (cp = gpeek(mb)) && misnumber(*cp) ) { | |
231 | int work = *i*10+(*cp-'0'); | |
127 | while( (c = i_io_peekc(ig)) != EOF && misnumber(c) ) { | |
128 | int work = *i * 10 + (c - '0'); | |
232 | 129 | if (work < *i) { |
233 | 130 | /* overflow */ |
234 | 131 | i_push_error(0, "integer overflow"); |
235 | 132 | return 0; |
236 | 133 | } |
237 | 134 | *i = work; |
238 | cp = gnext(mb); | |
239 | } | |
135 | i_io_getc(ig); | |
136 | } | |
137 | ||
240 | 138 | return 1; |
241 | 139 | } |
242 | 140 | |
243 | 141 | static |
244 | 142 | i_img * |
245 | read_pgm_ppm_bin8(mbuf *mb, i_img *im, int width, int height, | |
143 | read_pgm_ppm_bin8(io_glue *ig, i_img *im, int width, int height, | |
246 | 144 | int channels, int maxval, int allow_incomplete) { |
247 | 145 | i_color *line, *linep; |
248 | 146 | int read_size; |
256 | 154 | for(y=0;y<height;y++) { |
257 | 155 | linep = line; |
258 | 156 | readp = read_buf; |
259 | if (gread(mb, read_buf, read_size) != read_size) { | |
157 | if (i_io_read(ig, read_buf, read_size) != read_size) { | |
260 | 158 | myfree(line); |
261 | 159 | myfree(read_buf); |
262 | 160 | if (allow_incomplete) { |
300 | 198 | |
301 | 199 | static |
302 | 200 | i_img * |
303 | read_pgm_ppm_bin16(mbuf *mb, i_img *im, int width, int height, | |
201 | read_pgm_ppm_bin16(io_glue *ig, i_img *im, int width, int height, | |
304 | 202 | int channels, int maxval, int allow_incomplete) { |
305 | 203 | i_fcolor *line, *linep; |
306 | 204 | int read_size; |
314 | 212 | for(y=0;y<height;y++) { |
315 | 213 | linep = line; |
316 | 214 | readp = read_buf; |
317 | if (gread(mb, read_buf, read_size) != read_size) { | |
215 | if (i_io_read(ig, read_buf, read_size) != read_size) { | |
318 | 216 | myfree(line); |
319 | 217 | myfree(read_buf); |
320 | 218 | if (allow_incomplete) { |
348 | 246 | |
349 | 247 | static |
350 | 248 | i_img * |
351 | read_pbm_bin(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) { | |
249 | read_pbm_bin(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { | |
352 | 250 | i_palidx *line, *linep; |
353 | 251 | int read_size; |
354 | 252 | unsigned char *read_buf, *readp; |
359 | 257 | read_size = (width + 7) / 8; |
360 | 258 | read_buf = mymalloc(read_size); |
361 | 259 | for(y = 0; y < height; y++) { |
362 | if (gread(mb, read_buf, read_size) != read_size) { | |
260 | if (i_io_read(ig, read_buf, read_size) != read_size) { | |
363 | 261 | myfree(line); |
364 | 262 | myfree(read_buf); |
365 | 263 | if (allow_incomplete) { |
398 | 296 | */ |
399 | 297 | static |
400 | 298 | i_img * |
401 | read_pbm_ascii(mbuf *mb, i_img *im, int width, int height, int allow_incomplete) { | |
299 | read_pbm_ascii(io_glue *ig, i_img *im, int width, int height, int allow_incomplete) { | |
402 | 300 | i_palidx *line, *linep; |
403 | 301 | int x, y; |
404 | 302 | |
406 | 304 | for(y = 0; y < height; y++) { |
407 | 305 | linep = line; |
408 | 306 | for(x = 0; x < width; ++x) { |
409 | char *cp; | |
410 | skip_spaces(mb); | |
411 | if (!(cp = gnext(mb)) || (*cp != '0' && *cp != '1')) { | |
307 | int c; | |
308 | skip_spaces(ig); | |
309 | if ((c = i_io_getc(ig)) == EOF || (c != '0' && c != '1')) { | |
412 | 310 | myfree(line); |
413 | 311 | if (allow_incomplete) { |
414 | 312 | i_tags_setn(&im->tags, "i_incomplete", 1); |
416 | 314 | return im; |
417 | 315 | } |
418 | 316 | else { |
419 | if (cp) | |
317 | if (c != EOF) | |
420 | 318 | i_push_error(0, "invalid data for ascii pnm"); |
421 | 319 | else |
422 | 320 | i_push_error(0, "short read - file truncated?"); |
424 | 322 | return NULL; |
425 | 323 | } |
426 | 324 | } |
427 | *linep++ = *cp == '0' ? 0 : 1; | |
325 | *linep++ = c == '0' ? 0 : 1; | |
428 | 326 | } |
429 | 327 | i_ppal(im, 0, width, y, line); |
430 | 328 | } |
435 | 333 | |
436 | 334 | static |
437 | 335 | i_img * |
438 | read_pgm_ppm_ascii(mbuf *mb, i_img *im, int width, int height, int channels, | |
336 | read_pgm_ppm_ascii(io_glue *ig, i_img *im, int width, int height, int channels, | |
439 | 337 | int maxval, int allow_incomplete) { |
440 | 338 | i_color *line, *linep; |
441 | 339 | int x, y, ch; |
448 | 346 | for(ch=0; ch<channels; ch++) { |
449 | 347 | int sample; |
450 | 348 | |
451 | if (!gnum(mb, &sample)) { | |
349 | if (!gnum(ig, &sample)) { | |
452 | 350 | myfree(line); |
453 | 351 | if (allow_incomplete) { |
454 | 352 | i_tags_setn(&im->tags, "i_incomplete", 1); |
456 | 354 | return im; |
457 | 355 | } |
458 | 356 | else { |
459 | if (gpeek(mb)) | |
357 | if (i_io_peekc(ig) != EOF) | |
460 | 358 | i_push_error(0, "invalid data for ascii pnm"); |
461 | 359 | else |
462 | 360 | i_push_error(0, "short read - file truncated?"); |
479 | 377 | |
480 | 378 | static |
481 | 379 | i_img * |
482 | read_pgm_ppm_ascii_16(mbuf *mb, i_img *im, int width, int height, | |
380 | read_pgm_ppm_ascii_16(io_glue *ig, i_img *im, int width, int height, | |
483 | 381 | int channels, int maxval, int allow_incomplete) { |
484 | 382 | i_fcolor *line, *linep; |
485 | 383 | int x, y, ch; |
492 | 390 | for(ch=0; ch<channels; ch++) { |
493 | 391 | int sample; |
494 | 392 | |
495 | if (!gnum(mb, &sample)) { | |
393 | if (!gnum(ig, &sample)) { | |
496 | 394 | myfree(line); |
497 | 395 | if (allow_incomplete) { |
498 | 396 | i_tags_setn(&im->tags, "i_incomplete", 1); |
500 | 398 | return im; |
501 | 399 | } |
502 | 400 | else { |
503 | if (gpeek(mb)) | |
401 | if (i_io_peekc(ig) != EOF) | |
504 | 402 | i_push_error(0, "invalid data for ascii pnm"); |
505 | 403 | else |
506 | 404 | i_push_error(0, "short read - file truncated?"); |
531 | 429 | |
532 | 430 | =cut |
533 | 431 | */ |
534 | static i_img *i_readpnm_wiol_low( mbuf*, int); | |
535 | 432 | |
536 | 433 | i_img * |
537 | i_readpnm_wiol(io_glue *ig, int allow_incomplete) { | |
538 | mbuf buf; | |
539 | io_glue_commit_types(ig); | |
540 | init_buf(&buf, ig); | |
541 | ||
542 | return i_readpnm_wiol_low( &buf, allow_incomplete ); | |
543 | } | |
544 | ||
545 | static i_img * | |
546 | i_readpnm_wiol_low( mbuf *buf, int allow_incomplete) { | |
434 | i_readpnm_wiol( io_glue *ig, int allow_incomplete) { | |
547 | 435 | i_img* im; |
548 | 436 | int type; |
549 | 437 | int width, height, maxval, channels; |
550 | 438 | int rounder; |
551 | char *cp; | |
439 | int c; | |
552 | 440 | |
553 | 441 | i_clear_error(); |
554 | mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", buf->ig, allow_incomplete)); | |
555 | ||
556 | cp = gnext(buf); | |
557 | ||
558 | if (!cp || *cp != 'P') { | |
442 | mm_log((1,"i_readpnm(ig %p, allow_incomplete %d)\n", ig, allow_incomplete)); | |
443 | ||
444 | c = i_io_getc(ig); | |
445 | ||
446 | if (c != 'P') { | |
559 | 447 | i_push_error(0, "bad header magic, not a PNM file"); |
560 | 448 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
561 | 449 | return NULL; |
562 | 450 | } |
563 | 451 | |
564 | if ( !(cp = gnext(buf)) ) { | |
452 | if ((c = i_io_getc(ig)) == EOF ) { | |
565 | 453 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
566 | 454 | return NULL; |
567 | 455 | } |
568 | 456 | |
569 | type = *cp-'0'; | |
457 | type = c - '0'; | |
570 | 458 | |
571 | 459 | if (type < 1 || type > 6) { |
572 | 460 | i_push_error(0, "unknown PNM file type, not a PNM file"); |
574 | 462 | return NULL; |
575 | 463 | } |
576 | 464 | |
577 | if ( !(cp = gnext(buf)) ) { | |
465 | if ( (c = i_io_getc(ig)) == EOF ) { | |
578 | 466 | mm_log((1, "i_readpnm: Could not read header of file\n")); |
579 | 467 | return NULL; |
580 | 468 | } |
581 | 469 | |
582 | if ( !misspace(*cp) ) { | |
470 | if ( !misspace(c) ) { | |
583 | 471 | i_push_error(0, "unexpected character, not a PNM file"); |
584 | 472 | mm_log((1, "i_readpnm: Not a pnm file\n")); |
585 | 473 | return NULL; |
590 | 478 | |
591 | 479 | /* Read sizes and such */ |
592 | 480 | |
593 | if (!skip_comment(buf)) { | |
481 | if (!skip_comment(ig)) { | |
594 | 482 | i_push_error(0, "while skipping to width"); |
595 | 483 | mm_log((1, "i_readpnm: error reading before width\n")); |
596 | 484 | return NULL; |
597 | 485 | } |
598 | 486 | |
599 | if (!gnum(buf, &width)) { | |
487 | if (!gnum(ig, &width)) { | |
600 | 488 | i_push_error(0, "could not read image width"); |
601 | 489 | mm_log((1, "i_readpnm: error reading width\n")); |
602 | 490 | return NULL; |
603 | 491 | } |
604 | 492 | |
605 | if (!skip_comment(buf)) { | |
493 | if (!skip_comment(ig)) { | |
606 | 494 | i_push_error(0, "while skipping to height"); |
607 | 495 | mm_log((1, "i_readpnm: error reading before height\n")); |
608 | 496 | return NULL; |
609 | 497 | } |
610 | 498 | |
611 | if (!gnum(buf, &height)) { | |
499 | if (!gnum(ig, &height)) { | |
612 | 500 | i_push_error(0, "could not read image height"); |
613 | 501 | mm_log((1, "i_readpnm: error reading height\n")); |
614 | 502 | return NULL; |
615 | 503 | } |
616 | 504 | |
617 | 505 | if (!(type == 1 || type == 4)) { |
618 | if (!skip_comment(buf)) { | |
506 | if (!skip_comment(ig)) { | |
619 | 507 | i_push_error(0, "while skipping to maxval"); |
620 | 508 | mm_log((1, "i_readpnm: error reading before maxval\n")); |
621 | 509 | return NULL; |
622 | 510 | } |
623 | 511 | |
624 | if (!gnum(buf, &maxval)) { | |
512 | if (!gnum(ig, &maxval)) { | |
625 | 513 | i_push_error(0, "could not read maxval"); |
626 | 514 | mm_log((1, "i_readpnm: error reading maxval\n")); |
627 | 515 | return NULL; |
641 | 529 | } else maxval=1; |
642 | 530 | rounder = maxval / 2; |
643 | 531 | |
644 | if (!(cp = gnext(buf)) || !misspace(*cp)) { | |
532 | if ((c = i_io_getc(ig)) == EOF || !misspace(c)) { | |
645 | 533 | i_push_error(0, "garbage in header, invalid PNM file"); |
646 | 534 | mm_log((1, "i_readpnm: garbage in header\n")); |
647 | 535 | return NULL; |
673 | 561 | |
674 | 562 | switch (type) { |
675 | 563 | case 1: /* Ascii types */ |
676 | im = read_pbm_ascii(buf, im, width, height, allow_incomplete); | |
564 | im = read_pbm_ascii(ig, im, width, height, allow_incomplete); | |
677 | 565 | break; |
678 | 566 | |
679 | 567 | case 2: |
680 | 568 | case 3: |
681 | 569 | if (maxval > 255) |
682 | im = read_pgm_ppm_ascii_16(buf, im, width, height, channels, maxval, allow_incomplete); | |
570 | im = read_pgm_ppm_ascii_16(ig, im, width, height, channels, maxval, allow_incomplete); | |
683 | 571 | else |
684 | im = read_pgm_ppm_ascii(buf, im, width, height, channels, maxval, allow_incomplete); | |
572 | im = read_pgm_ppm_ascii(ig, im, width, height, channels, maxval, allow_incomplete); | |
685 | 573 | break; |
686 | 574 | |
687 | 575 | case 4: /* binary pbm */ |
688 | im = read_pbm_bin(buf, im, width, height, allow_incomplete); | |
576 | im = read_pbm_bin(ig, im, width, height, allow_incomplete); | |
689 | 577 | break; |
690 | 578 | |
691 | 579 | case 5: /* binary pgm */ |
692 | 580 | case 6: /* binary ppm */ |
693 | 581 | if (maxval > 255) |
694 | im = read_pgm_ppm_bin16(buf, im, width, height, channels, maxval, allow_incomplete); | |
582 | im = read_pgm_ppm_bin16(ig, im, width, height, channels, maxval, allow_incomplete); | |
695 | 583 | else |
696 | im = read_pgm_ppm_bin8(buf, im, width, height, channels, maxval, allow_incomplete); | |
584 | im = read_pgm_ppm_bin8(ig, im, width, height, channels, maxval, allow_incomplete); | |
697 | 585 | break; |
698 | 586 | |
699 | 587 | default: |
724 | 612 | i_img **i_readpnm_multi_wiol(io_glue *ig, int *count, int allow_incomplete) { |
725 | 613 | i_img **results = NULL; |
726 | 614 | i_img *img = NULL; |
727 | char *cp = NULL; | |
728 | mbuf buf; | |
615 | char c = EOF; | |
729 | 616 | int result_alloc = 0, |
730 | 617 | value = 0, |
731 | 618 | eof = 0; |
732 | 619 | *count=0; |
733 | io_glue_commit_types(ig); | |
734 | init_buf(&buf, ig); | |
620 | ||
735 | 621 | do { |
736 | 622 | mm_log((1, "read image %i\n", 1+*count)); |
737 | img = i_readpnm_wiol_low( &buf, allow_incomplete ); | |
623 | img = i_readpnm_wiol( ig, allow_incomplete ); | |
738 | 624 | if( !img ) { |
739 | 625 | free_images( results, *count ); |
740 | 626 | return NULL; |
757 | 643 | if( i_tags_get_int(&img->tags, "i_incomplete", 0, &value ) && value) { |
758 | 644 | eof = 1; |
759 | 645 | } |
760 | else if( skip_spaces( &buf ) && ( cp=gpeek( &buf ) ) && *cp == 'P' ) { | |
646 | else if( skip_spaces( ig ) && ( c=i_io_peekc( ig ) ) != EOF && c == 'P' ) { | |
761 | 647 | eof = 0; |
762 | 648 | } |
763 | 649 | else { |
892 | 778 | /* Add code to get the filename info from the iolayer */ |
893 | 779 | /* Also add code to check for mmapped code */ |
894 | 780 | |
895 | io_glue_commit_types(ig); | |
896 | ||
897 | 781 | if (i_img_is_monochrome(im, &zero_is_white)) { |
898 | return write_pbm(im, ig, zero_is_white); | |
782 | if (!write_pbm(im, ig, zero_is_white)) | |
783 | return 0; | |
899 | 784 | } |
900 | 785 | else { |
901 | 786 | int type; |
927 | 812 | sprintf(header,"P%d\n#CREATOR: Imager\n%" i_DF " %" i_DF"\n%d\n", |
928 | 813 | type, i_DFc(im->xsize), i_DFc(im->ysize), maxval); |
929 | 814 | |
930 | if (ig->writecb(ig,header,strlen(header)) != strlen(header)) { | |
815 | if (i_io_write(ig,header,strlen(header)) != strlen(header)) { | |
931 | 816 | i_push_error(errno, "could not write ppm header"); |
932 | 817 | mm_log((1,"i_writeppm: unable to write ppm header.\n")); |
933 | 818 | return(0); |
935 | 820 | |
936 | 821 | if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type |
937 | 822 | && im->channels == want_channels) { |
938 | if (ig->writecb(ig,im->idata,im->bytes) != im->bytes) { | |
823 | if (i_io_write(ig,im->idata,im->bytes) != im->bytes) { | |
939 | 824 | i_push_error(errno, "could not write ppm data"); |
940 | 825 | return 0; |
941 | 826 | } |
949 | 834 | return 0; |
950 | 835 | } |
951 | 836 | } |
952 | ig->closecb(ig); | |
837 | if (i_io_close(ig)) { | |
838 | i_push_errorf(i_io_error(ig), "Error closing stream: %d", i_io_error(ig)); | |
839 | return 0; | |
840 | } | |
953 | 841 | |
954 | 842 | return(1); |
955 | 843 | } |
69 | 69 | |
70 | 70 | i_clear_error(); |
71 | 71 | |
72 | io_glue_commit_types(ig); | |
73 | 72 | mm_log((1, "i_readraw(ig %p,x %" i_DF ",y %" i_DF ",datachannels %d,storechannels %d,intrl %d)\n", |
74 | 73 | ig, i_DFc(x), i_DFc(y), datachannels, storechannels, intrl)); |
75 | 74 | |
101 | 100 | |
102 | 101 | k=0; |
103 | 102 | while( k<im->ysize ) { |
104 | rc = ig->readcb(ig, inbuffer, inbuflen); | |
103 | rc = i_io_read(ig, inbuffer, inbuflen); | |
105 | 104 | if (rc != inbuflen) { |
106 | 105 | if (rc < 0) |
107 | 106 | i_push_error(0, "error reading file"); |
135 | 134 | i_writeraw_wiol(i_img* im, io_glue *ig) { |
136 | 135 | ssize_t rc; |
137 | 136 | |
138 | io_glue_commit_types(ig); | |
139 | 137 | i_clear_error(); |
140 | 138 | mm_log((1,"writeraw(im %p,ig %p)\n", im, ig)); |
141 | 139 | |
142 | 140 | if (im == NULL) { mm_log((1,"Image is empty\n")); return(0); } |
143 | 141 | if (!im->virtual) { |
144 | rc = ig->writecb(ig,im->idata,im->bytes); | |
142 | rc = i_io_write(ig,im->idata,im->bytes); | |
145 | 143 | if (rc != im->bytes) { |
146 | 144 | i_push_error(errno, "Could not write to file"); |
147 | 145 | mm_log((1,"i_writeraw: Couldn't write to file\n")); |
158 | 156 | rc = line_size; |
159 | 157 | while (rc == line_size && y < im->ysize) { |
160 | 158 | i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); |
161 | rc = ig->writecb(ig, data, line_size); | |
159 | rc = i_io_write(ig, data, line_size); | |
162 | 160 | ++y; |
163 | 161 | } |
164 | 162 | if (rc != line_size) { |
177 | 175 | rc = line_size; |
178 | 176 | while (rc == line_size && y < im->ysize) { |
179 | 177 | i_gpal(im, 0, im->xsize, y, data); |
180 | rc = ig->writecb(ig, data, line_size); | |
178 | rc = i_io_write(ig, data, line_size); | |
181 | 179 | ++y; |
182 | 180 | } |
183 | 181 | myfree(data); |
188 | 186 | } |
189 | 187 | } |
190 | 188 | |
191 | ig->closecb(ig); | |
189 | if (i_io_close(ig)) | |
190 | return 0; | |
192 | 191 | |
193 | 192 | return(1); |
194 | 193 | } |
332 | 332 | } |
333 | 333 | else if (*src) { |
334 | 334 | for (ch = 0; ch < im->channels; ++ch) { |
335 | IM_WORK_T work = (destc->channel[ch] * (IM_SAMPLE_MAX - *src) | |
336 | + srcc->channel[ch] * *src) / IM_SAMPLE_MAX; | |
335 | IM_WORK_T work = (destc->channel[ch] * (255 - *src) | |
336 | + srcc->channel[ch] * *src) / 255.0; | |
337 | 337 | destc->channel[ch] = IM_LIMIT(work); |
338 | 338 | } |
339 | 339 | } |
137 | 137 | expensive CGI request to generate the image, but it means you need |
138 | 138 | some mechanism to manage the files (for example, a cron job to delete |
139 | 139 | old files), and you need to make some directory under the document |
140 | root writeable by the user that your web server runs CGI programs as, | |
140 | root writable by the user that your web server runs CGI programs as, | |
141 | 141 | which may be a security concern. |
142 | 142 | |
143 | 143 | Also, if you're generating large numbers of large images, you may end |
25 | 25 | return $pod->{identifiers} || []; |
26 | 26 | } |
27 | 27 | |
28 | sub _get_syms { | |
29 | my ($self, $package) = @_; | |
30 | ||
31 | if ($self->{module}) { | |
32 | eval "require $self->{module}"; | |
33 | return if $@; | |
34 | ||
35 | # fake out require | |
36 | (my $file = $package) =~ s(::)(/)g; | |
37 | $file .= ".pm"; | |
38 | $INC{$file} = 1; | |
39 | } | |
40 | ||
41 | return $self->SUPER::_get_syms($package); | |
42 | } | |
43 | ||
28 | 44 | 1; |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 68; | |
2 | use Test::More tests => 246; | |
3 | 3 | # for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03 |
4 | 4 | use IO::Seekable; |
5 | 5 | |
6 | 6 | BEGIN { use_ok(Imager => ':all') }; |
7 | 7 | |
8 | 8 | -d "testout" or mkdir "testout"; |
9 | ||
10 | $| = 1; | |
9 | 11 | |
10 | 12 | Imager->open_log(log => "testout/t07iolayer.log"); |
11 | 13 | |
30 | 32 | binmode(FH); |
31 | 33 | $data = <FH>; |
32 | 34 | close(FH); |
33 | my $IO3 = Imager::io_new_buffer($data); | |
35 | my $IO3 = Imager::IO->new_buffer($data); | |
34 | 36 | #undef($data); |
35 | 37 | $im = Imager::i_readpnm_wiol($IO3, -1); |
36 | 38 | |
40 | 42 | open(FH, "<testimg/penguin-base.ppm") or die $!; |
41 | 43 | binmode(FH); |
42 | 44 | $fd = fileno(FH); |
43 | my $IO4 = Imager::io_new_fd( $fd ); | |
45 | my $IO4 = Imager::IO->new_fd( $fd ); | |
44 | 46 | my $im2 = Imager::i_readpnm_wiol($IO4, -1); |
45 | 47 | close(FH); |
46 | 48 | undef($IO4); |
76 | 78 | $pos += length $out; |
77 | 79 | $out; |
78 | 80 | } |
79 | my $IO7 = Imager::io_new_cb(undef, \&io_reader, undef, undef); | |
81 | my $IO7 = Imager::IO->new_cb(undef, \&io_reader, undef, undef); | |
80 | 82 | ok($IO7, "making readcb object"); |
81 | 83 | my $im4 = Imager::i_readpnm_wiol($IO7, -1); |
82 | 84 | ok($im4, "read from cb"); |
125 | 127 | my $io9 = Imager::io_new_buffer($buf_data); |
126 | 128 | is(ref $io9, "Imager::IO", "check class"); |
127 | 129 | my $work; |
128 | is($io9->read($work, 4), 4, "read 4 from buffer object"); | |
130 | is($io9->raw_read($work, 4), 4, "read 4 from buffer object"); | |
129 | 131 | is($work, "Test", "check data read"); |
130 | is($io9->read($work, 5), 5, "read the rest"); | |
132 | is($io9->raw_read($work, 5), 5, "read the rest"); | |
131 | 133 | is($work, " data", "check data read"); |
132 | is($io9->seek(5, SEEK_SET), 5, "seek"); | |
133 | is($io9->read($work, 5), 4, "short read"); | |
134 | is($io9->raw_seek(5, SEEK_SET), 5, "seek"); | |
135 | is($io9->raw_read($work, 5), 4, "short read"); | |
134 | 136 | is($work, "data", "check data read"); |
135 | is($io9->seek(-1, SEEK_CUR), 8, "seek relative"); | |
136 | is($io9->seek(-5, SEEK_END), 4, "seek relative to end"); | |
137 | is($io9->seek(-10, SEEK_CUR), -1, "seek failure"); | |
137 | is($io9->raw_seek(-1, SEEK_CUR), 8, "seek relative"); | |
138 | is($io9->raw_seek(-5, SEEK_END), 4, "seek relative to end"); | |
139 | is($io9->raw_seek(-10, SEEK_CUR), -1, "seek failure"); | |
138 | 140 | undef $io9; |
139 | 141 | } |
140 | 142 | { |
141 | my $io = Imager::io_new_bufchain(); | |
143 | my $io = Imager::IO->new_bufchain(); | |
142 | 144 | is(ref $io, "Imager::IO", "check class"); |
143 | is($io->write("testdata"), 8, "check write"); | |
144 | is($io->seek(-8, SEEK_CUR), 0, "seek relative"); | |
145 | is($io->raw_write("testdata"), 8, "check write"); | |
146 | is($io->raw_seek(-8, SEEK_CUR), 0, "seek relative"); | |
145 | 147 | my $work; |
146 | is($io->read($work, 8), 8, "check read"); | |
148 | is($io->raw_read($work, 8), 8, "check read"); | |
147 | 149 | is($work, "testdata", "check data read"); |
148 | is($io->seek(-3, SEEK_END), 5, "seek end relative"); | |
149 | is($io->read($work, 5), 3, "short read"); | |
150 | is($io->raw_seek(-3, SEEK_END), 5, "seek end relative"); | |
151 | is($io->raw_read($work, 5), 3, "short read"); | |
150 | 152 | is($work, "ata", "check read data"); |
151 | is($io->seek(4, SEEK_SET), 4, "absolute seek to write some"); | |
152 | is($io->write("testdata"), 8, "write"); | |
153 | is($io->seek(0, SEEK_CUR), 12, "check size"); | |
154 | $io->close(); | |
153 | is($io->raw_seek(4, SEEK_SET), 4, "absolute seek to write some"); | |
154 | is($io->raw_write("testdata"), 8, "write"); | |
155 | is($io->raw_seek(0, SEEK_CUR), 12, "check size"); | |
156 | $io->raw_close(); | |
155 | 157 | |
156 | 158 | # grab the data |
157 | 159 | my $data = Imager::io_slurp($io); |
162 | 164 | my $fail_io = Imager::io_new_cb(\&fail_write, \&fail_read, \&fail_seek, undef, 1); |
163 | 165 | # scalar context |
164 | 166 | my $buffer; |
165 | my $read_result = $fail_io->read($buffer, 10); | |
167 | my $read_result = $fail_io->raw_read($buffer, 10); | |
166 | 168 | is($read_result, undef, "read failure undef in scalar context"); |
167 | my @read_result = $fail_io->read($buffer, 10); | |
169 | my @read_result = $fail_io->raw_read($buffer, 10); | |
168 | 170 | is(@read_result, 0, "empty list in list context"); |
169 | $read_result = $fail_io->read2(10); | |
170 | is($read_result, undef, "read2 failure (scalar)"); | |
171 | @read_result = $fail_io->read2(10); | |
172 | is(@read_result, 0, "read2 failure (list)"); | |
173 | ||
174 | my $write_result = $fail_io->write("test"); | |
171 | $read_result = $fail_io->raw_read2(10); | |
172 | is($read_result, undef, "raw_read2 failure (scalar)"); | |
173 | @read_result = $fail_io->raw_read2(10); | |
174 | is(@read_result, 0, "raw_read2 failure (list)"); | |
175 | ||
176 | my $write_result = $fail_io->raw_write("test"); | |
175 | 177 | is($write_result, -1, "failed write"); |
176 | 178 | |
177 | my $seek_result = $fail_io->seek(-1, SEEK_SET); | |
179 | my $seek_result = $fail_io->raw_seek(-1, SEEK_SET); | |
178 | 180 | is($seek_result, -1, "failed seek"); |
179 | 181 | } |
180 | 182 | |
182 | 184 | my $good_io = Imager::io_new_cb(\&good_write, \&good_read, \&good_seek, undef, 1); |
183 | 185 | # scalar context |
184 | 186 | my $buffer; |
185 | my $read_result = $good_io->read($buffer, 10); | |
186 | is($read_result, 10, "read success (scalar)"); | |
187 | is($buffer, "testdatate", "check data"); | |
188 | my @read_result = $good_io->read($buffer, 10); | |
189 | is_deeply(\@read_result, [ 10 ], "read success (list)"); | |
190 | is($buffer, "testdatate", "check data"); | |
191 | $read_result = $good_io->read2(10); | |
192 | is($read_result, "testdatate", "read2 success (scalar)"); | |
193 | @read_result = $good_io->read2(10); | |
194 | is_deeply(\@read_result, [ "testdatate" ], "read2 success (list)"); | |
187 | my $read_result = $good_io->raw_read($buffer, 10); | |
188 | is($read_result, 8, "read success (scalar)"); | |
189 | is($buffer, "testdata", "check data"); | |
190 | my @read_result = $good_io->raw_read($buffer, 10); | |
191 | is_deeply(\@read_result, [ 8 ], "read success (list)"); | |
192 | is($buffer, "testdata", "check data"); | |
193 | $read_result = $good_io->raw_read2(10); | |
194 | is($read_result, "testdata", "read2 success (scalar)"); | |
195 | @read_result = $good_io->raw_read2(10); | |
196 | is_deeply(\@read_result, [ "testdata" ], "read2 success (list)"); | |
195 | 197 | } |
196 | 198 | |
197 | 199 | { # end of file |
198 | 200 | my $eof_io = Imager::io_new_cb(undef, \&eof_read, undef, undef, 1); |
199 | 201 | my $buffer; |
200 | my $read_result = $eof_io->read($buffer, 10); | |
202 | my $read_result = $eof_io->raw_read($buffer, 10); | |
201 | 203 | is($read_result, 0, "read eof (scalar)"); |
202 | 204 | is($buffer, '', "check data"); |
203 | my @read_result = $eof_io->read($buffer, 10); | |
205 | my @read_result = $eof_io->raw_read($buffer, 10); | |
204 | 206 | is_deeply(\@read_result, [ 0 ], "read eof (list)"); |
205 | 207 | is($buffer, '', "check data"); |
206 | 208 | } |
207 | 209 | |
208 | 210 | { # no callbacks |
209 | 211 | my $none_io = Imager::io_new_cb(undef, undef, undef, undef, 0); |
210 | is($none_io->write("test"), -1, "write with no writecb should fail"); | |
212 | is($none_io->raw_write("test"), -1, "write with no writecb should fail"); | |
211 | 213 | my $buffer; |
212 | is($none_io->read($buffer, 10), undef, "read with no readcb should fail"); | |
213 | is($none_io->seek(0, SEEK_SET), -1, "seek with no seekcb should fail"); | |
214 | is($none_io->raw_read($buffer, 10), undef, "read with no readcb should fail"); | |
215 | is($none_io->raw_seek(0, SEEK_SET), -1, "seek with no seekcb should fail"); | |
214 | 216 | } |
215 | 217 | |
216 | 218 | SKIP: |
223 | 225 | is(ord $data, 0x100, "make sure we got what we expected"); |
224 | 226 | my $result = |
225 | 227 | eval { |
226 | $io->write($data); | |
228 | $io->raw_write($data); | |
227 | 229 | }; |
228 | 230 | ok($@, "should have croaked") |
229 | 231 | and print "# $@\n"; |
245 | 247 | sub { print "# seek\n"; 0 }, |
246 | 248 | sub { print "# close\n"; 1 }); |
247 | 249 | my $buffer; |
248 | is($io->read($buffer, 10), 10, "read 10"); | |
250 | is($io->raw_read($buffer, 10), 10, "read 10"); | |
249 | 251 | is($buffer, "xxxxxxxxxx", "read value"); |
250 | ok($io->write("foo"), "write"); | |
251 | is($io->close, 0, "close"); | |
252 | ok($io->raw_write("foo"), "write"); | |
253 | is($io->raw_close, 0, "close"); | |
254 | } | |
255 | ||
256 | SKIP: | |
257 | { # fd_seek write failure | |
258 | -c "/dev/full" | |
259 | or skip("No /dev/full", 3); | |
260 | open my $fh, "> /dev/full" | |
261 | or skip("Can't open /dev/full: $!", 3); | |
262 | my $io = Imager::io_new_fd(fileno($fh)); | |
263 | ok($io, "make fd io for /dev/full"); | |
264 | Imager::i_clear_error(); | |
265 | is($io->raw_write("test"), -1, "fail to write"); | |
266 | my $msg = Imager->_error_as_msg; | |
267 | like($msg, qr/^write\(\) failure: /, "check error message"); | |
268 | print "# $msg\n"; | |
269 | ||
270 | # /dev/full succeeds on seek on Linux | |
271 | ||
272 | undef $io; | |
273 | } | |
274 | ||
275 | SKIP: | |
276 | { # fd_seek seek failure | |
277 | my $seekfail = "testout/t07seekfail.dat"; | |
278 | open my $fh, "> $seekfail" | |
279 | or skip("Can't open $seekfail: $!", 3); | |
280 | my $io = Imager::io_new_fd(fileno($fh)); | |
281 | ok($io, "make fd io for $seekfail"); | |
282 | ||
283 | Imager::i_clear_error(); | |
284 | is($io->raw_seek(-1, SEEK_SET), -1, "shouldn't be able to seek to -1"); | |
285 | my $msg = Imager->_error_as_msg; | |
286 | like($msg, qr/^lseek\(\) failure: /, "check error message"); | |
287 | print "# $msg\n"; | |
288 | ||
289 | undef $io; | |
290 | close $fh; | |
291 | unlink $seekfail; | |
292 | } | |
293 | ||
294 | SKIP: | |
295 | { # fd_seek read failure | |
296 | open my $fh, "> testout/t07writeonly.txt" | |
297 | or skip("Can't open testout/t07writeonly.txt: $!", 3); | |
298 | my $io = Imager::io_new_fd(fileno($fh)); | |
299 | ok($io, "make fd io for write-only"); | |
300 | ||
301 | Imager::i_clear_error(); | |
302 | my $buf; | |
303 | is($io->raw_read($buf, 10), undef, | |
304 | "file open for write shouldn't be readable"); | |
305 | my $msg = Imager->_error_as_msg; | |
306 | like($msg, qr/^read\(\) failure: /, "check error message"); | |
307 | print "# $msg\n"; | |
308 | ||
309 | undef $io; | |
310 | } | |
311 | ||
312 | SKIP: | |
313 | { # fd_seek eof | |
314 | open my $fh, "> testout/t07readeof.txt" | |
315 | or skip("Can't open testout/t07readeof.txt: $!", 5); | |
316 | binmode $fh; | |
317 | print $fh "test"; | |
318 | close $fh; | |
319 | open my $fhr, "< testout/t07readeof.txt", | |
320 | or skip("Can't open testout/t07readeof.txt: $!", 5); | |
321 | my $io = Imager::io_new_fd(fileno($fhr)); | |
322 | ok($io, "make fd io for read eof"); | |
323 | ||
324 | Imager::i_clear_error(); | |
325 | my $buf; | |
326 | is($io->raw_read($buf, 10), 4, | |
327 | "10 byte read on 4 byte file should return 4"); | |
328 | my $msg = Imager->_error_as_msg; | |
329 | is($msg, "", "should be no error message") | |
330 | or print STDERR "# read(4) message is: $msg\n"; | |
331 | ||
332 | Imager::i_clear_error(); | |
333 | $buf = ''; | |
334 | is($io->raw_read($buf, 10), 0, | |
335 | "10 byte read at end of 4 byte file should return 0 (eof)"); | |
336 | ||
337 | $msg = Imager->_error_as_msg; | |
338 | is($msg, "", "should be no error message") | |
339 | or print STDERR "# read(4), eof message is: $msg\n"; | |
340 | ||
341 | undef $io; | |
342 | } | |
343 | ||
344 | { # buffered I/O | |
345 | my $data="P2\n2 2\n255\n 255 0\n0 255\n"; | |
346 | my $io = Imager::io_new_buffer($data); | |
347 | ||
348 | my $c = $io->getc(); | |
349 | ||
350 | is($c, ord "P", "getc"); | |
351 | my $peekc = $io->peekc(); | |
352 | ||
353 | is($peekc, ord "2", "peekc"); | |
354 | ||
355 | my $peekn = $io->peekn(2); | |
356 | is($peekn, "2\n", "peekn"); | |
357 | ||
358 | $c = $io->getc(); | |
359 | is($c, ord "2", "getc after peekc/peekn"); | |
360 | ||
361 | is($io->seek(0, SEEK_SET), "0", "seek"); | |
362 | is($io->getc, ord "P", "check we got back to the start"); | |
363 | } | |
364 | ||
365 | { # test closecb result is propagated | |
366 | my $success_cb = sub { 1 }; | |
367 | my $failure_cb = sub { 0 }; | |
368 | ||
369 | { | |
370 | my $io = Imager::io_new_cb(undef, $success_cb, undef, $success_cb); | |
371 | is($io->close(), 0, "test successful close"); | |
372 | } | |
373 | { | |
374 | my $io = Imager::io_new_cb(undef, $success_cb, undef, $failure_cb); | |
375 | is($io->close(), -1, "test failed close"); | |
376 | } | |
377 | } | |
378 | ||
379 | { # buffered coverage/function tests | |
380 | # some data to play with | |
381 | my $base = pack "C*", map rand(26) + ord("a"), 0 .. 20_001; | |
382 | ||
383 | { # buffered accessors | |
384 | my $io = Imager::io_new_buffer($base); | |
385 | ok($io->set_buffered(0), "set unbuffered"); | |
386 | ok(!$io->is_buffered, "verify unbuffered"); | |
387 | ok($io->set_buffered(1), "set buffered"); | |
388 | ok($io->is_buffered, "verify buffered"); | |
389 | } | |
390 | ||
391 | { # initial i_io_read(), buffered | |
392 | my $pos = 0; | |
393 | my $ops = ""; | |
394 | my $work = $base; | |
395 | my $read = sub { | |
396 | my ($size) = @_; | |
397 | ||
398 | my $req_size = $size; | |
399 | ||
400 | if ($pos + $size > length $work) { | |
401 | $size = length($work) - $pos; | |
402 | } | |
403 | ||
404 | my $result = substr($work, $pos, $size); | |
405 | $pos += $size; | |
406 | $ops .= "R$req_size>$size;"; | |
407 | ||
408 | print "# read $req_size>$size\n"; | |
409 | ||
410 | return $result; | |
411 | }; | |
412 | my $write = sub { | |
413 | my ($data) = @_; | |
414 | ||
415 | substr($work, $pos, length($data), $data); | |
416 | ||
417 | return 1; | |
418 | }; | |
419 | { | |
420 | my $io = Imager::io_new_cb(undef, $read, undef, undef); | |
421 | my $buf; | |
422 | is($io->read($buf, 1000), 1000, "read initial 1000"); | |
423 | is($buf, substr($base, 0, 1000), "check data read"); | |
424 | is($ops, "R8192>8192;", "check read op happened to buffer size"); | |
425 | ||
426 | undef $buf; | |
427 | is($io->read($buf, 1001), 1001, "read another 1001"); | |
428 | is($buf, substr($base, 1000, 1001), "check data read"); | |
429 | is($ops, "R8192>8192;", "should be no further reads"); | |
430 | ||
431 | undef $buf; | |
432 | is($io->read($buf, 40_000), length($base) - 2001, | |
433 | "read the rest in one chunk"); | |
434 | is($buf, substr($base, 2001), "check the data read"); | |
435 | my $buffer_left = 8192 - 2001; | |
436 | my $after_buffer = length($base) - 8192; | |
437 | is($ops, "R8192>8192;R".(40_000 - $buffer_left).">$after_buffer;R21999>0;", | |
438 | "check we tried to read the remainder"); | |
439 | } | |
440 | { | |
441 | # read after write errors | |
442 | my $io = Imager::io_new_cb($write, $read, undef, undef); | |
443 | is($io->write("test"), 4, "write 4 bytes, io in write mode"); | |
444 | is($io->read2(10), undef, "read should fail"); | |
445 | is($io->peekn(10), undef, "peekn should fail"); | |
446 | is($io->getc(), -1, "getc should fail"); | |
447 | is($io->peekc(), -1, "peekc should fail"); | |
448 | } | |
449 | } | |
450 | ||
451 | { | |
452 | my $io = Imager::io_new_buffer($base); | |
453 | print "# buffer fill check\n"; | |
454 | ok($io, "make memory io"); | |
455 | my $buf; | |
456 | is($io->read($buf, 4096), 4096, "read 4k"); | |
457 | is($buf, substr($base, 0, 4096), "check data is correct"); | |
458 | ||
459 | # peek a bit | |
460 | undef $buf; | |
461 | is($io->peekn(5120), substr($base, 4096, 5120), | |
462 | "peekn() 5120, which should exceed the buffer, and only read the left overs"); | |
463 | } | |
464 | ||
465 | { # initial peekn | |
466 | my $io = Imager::io_new_buffer($base); | |
467 | is($io->peekn(10), substr($base, 0, 10), | |
468 | "make sure initial peekn() is sane"); | |
469 | is($io->read2(10), substr($base, 0, 10), | |
470 | "and that reading 10 gets the expected data"); | |
471 | } | |
472 | ||
473 | { # oversize peekn | |
474 | my $io = Imager::io_new_buffer($base); | |
475 | is($io->peekn(10_000), substr($base, 0, 8192), | |
476 | "peekn() larger than buffer should return buffer-size bytes"); | |
477 | } | |
478 | ||
479 | { # small peekn then large peekn with a small I/O back end | |
480 | # this might happen when reading from a socket | |
481 | my $work = $base; | |
482 | my $pos = 0; | |
483 | my $ops = ''; | |
484 | my $reader = sub { | |
485 | my ($size) = @_; | |
486 | ||
487 | my $req_size = $size; | |
488 | # do small reads, to trigger a possible bug | |
489 | if ($size > 10) { | |
490 | $size = 10; | |
491 | } | |
492 | ||
493 | if ($pos + $size > length $work) { | |
494 | $size = length($work) - $pos; | |
495 | } | |
496 | ||
497 | my $result = substr($work, $pos, $size); | |
498 | $pos += $size; | |
499 | $ops .= "R$req_size>$size;"; | |
500 | ||
501 | print "# read $req_size>$size\n"; | |
502 | ||
503 | return $result; | |
504 | }; | |
505 | my $io = Imager::io_new_cb(undef, $reader, undef, undef); | |
506 | ok($io, "small reader io"); | |
507 | is($io->peekn(25), substr($base, 0, 25), "peek 25"); | |
508 | is($ops, "R8192>10;R8182>10;R8172>10;", | |
509 | "check we got the raw calls expected"); | |
510 | is($io->peekn(65), substr($base, 0, 65), "peek 65"); | |
511 | is($ops, "R8192>10;R8182>10;R8172>10;R8162>10;R8152>10;R8142>10;R8132>10;", | |
512 | "check we got the raw calls expected"); | |
513 | } | |
514 | for my $buffered (1, 0) { # peekn followed by errors | |
515 | my $buffered_desc = $buffered ? "buffered" : "unbuffered"; | |
516 | my $read = 0; | |
517 | my $base = "abcdef"; | |
518 | my $pos = 0; | |
519 | my $reader = sub { | |
520 | my $size = shift; | |
521 | my $req_size = $size; | |
522 | if ($pos + $size > length $base) { | |
523 | $size = length($base) - $pos; | |
524 | } | |
525 | # error instead of eof | |
526 | if ($size == 0) { | |
527 | print "# read $req_size>error\n"; | |
528 | return; | |
529 | } | |
530 | my $result = substr($base, $pos, $size); | |
531 | $pos += $size; | |
532 | ||
533 | print "# read $req_size>$size\n"; | |
534 | ||
535 | return $result; | |
536 | }; | |
537 | my $io = Imager::io_new_cb(undef, $reader, undef, undef); | |
538 | ok($io, "make $buffered_desc cb with error after 6 bytes"); | |
539 | is($io->peekn(5), "abcde", | |
540 | "peekn until just before error ($buffered_desc)"); | |
541 | is($io->peekn(6), "abcdef", "peekn until error ($buffered_desc)"); | |
542 | is($io->peekn(7), "abcdef", "peekn past error ($buffered_desc)"); | |
543 | ok(!$io->error, | |
544 | "should be no error indicator, since data buffered ($buffered_desc)"); | |
545 | ok(!$io->eof, | |
546 | "should be no eof indicator, since data buffered ($buffered_desc)"); | |
547 | ||
548 | # consume it | |
549 | is($io->read2(6), "abcdef", "consume the buffer ($buffered_desc)"); | |
550 | is($io->peekn(10), undef, | |
551 | "peekn should get an error indicator ($buffered_desc)"); | |
552 | ok($io->error, "should be an error state ($buffered_desc)"); | |
553 | ok(!$io->eof, "but not eof ($buffered_desc)"); | |
554 | } | |
555 | { # peekn on an empty file | |
556 | my $io = Imager::io_new_buffer(""); | |
557 | is($io->peekn(10), "", "peekn on empty source"); | |
558 | ok($io->eof, "should be in eof state"); | |
559 | ok(!$io->error, "but not error"); | |
560 | } | |
561 | { # peekn on error source | |
562 | my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef); | |
563 | is($io->peekn(10), undef, "peekn on empty source"); | |
564 | ok($io->error, "should be in error state"); | |
565 | ok(!$io->eof, "but not eof"); | |
566 | } | |
567 | { # peekn on short source | |
568 | my $io = Imager::io_new_buffer("abcdef"); | |
569 | is($io->peekn(4), "abcd", "peekn 4 on 6 byte source"); | |
570 | is($io->peekn(10), "abcdef", "followed by peekn 10 on 6 byte source"); | |
571 | is($io->peekn(10), "abcdef", "and again, now eof is set"); | |
572 | } | |
573 | { # peekn(0) | |
574 | Imager::i_clear_error(); | |
575 | my $io = Imager::io_new_buffer("abcdef"); | |
576 | is($io->peekn(0), undef, "peekn 0 on 6 byte source"); | |
577 | my $msg = Imager->_error_as_msg; | |
578 | is($msg, "peekn size must be positive"); | |
579 | } | |
580 | { # getc through a whole file (buffered) | |
581 | my $io = Imager::io_new_buffer($base); | |
582 | my $out = ''; | |
583 | while ((my $c = $io->getc()) != -1) { | |
584 | $out .= chr($c); | |
585 | } | |
586 | is($out, $base, "getc should return the file byte by byte (buffered)"); | |
587 | is($io->getc, -1, "another getc after eof should fail too"); | |
588 | ok($io->eof, "should be marked eof"); | |
589 | ok(!$io->error, "shouldn't be marked in error"); | |
590 | } | |
591 | { # getc through a whole file (unbuffered) | |
592 | my $io = Imager::io_new_buffer($base); | |
593 | $io->set_buffered(0); | |
594 | my $out = ''; | |
595 | while ((my $c = $io->getc()) != -1) { | |
596 | $out .= chr($c); | |
597 | } | |
598 | is($out, $base, "getc should return the file byte by byte (unbuffered)"); | |
599 | is($io->getc, -1, "another getc after eof should fail too"); | |
600 | ok($io->eof, "should be marked eof"); | |
601 | ok(!$io->error, "shouldn't be marked in error"); | |
602 | } | |
603 | { # buffered getc with an error | |
604 | my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef); | |
605 | is($io->getc, -1, "buffered getc error"); | |
606 | ok($io->error, "io marked in error"); | |
607 | ok(!$io->eof, "but not eof"); | |
608 | } | |
609 | { # unbuffered getc with an error | |
610 | my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef); | |
611 | $io->set_buffered(0); | |
612 | is($io->getc, -1, "unbuffered getc error"); | |
613 | ok($io->error, "io marked in error"); | |
614 | ok(!$io->eof, "but not eof"); | |
615 | } | |
616 | { # initial peekc - buffered | |
617 | my $io = Imager::io_new_buffer($base); | |
618 | my $c = $io->peekc; | |
619 | is($c, ord($base), "buffered peekc matches"); | |
620 | is($io->peekc, $c, "duplicate peekc matchess"); | |
621 | } | |
622 | { # initial peekc - unbuffered | |
623 | my $io = Imager::io_new_buffer($base); | |
624 | $io->set_buffered(0); | |
625 | my $c = $io->peekc; | |
626 | is($c, ord($base), "unbuffered peekc matches"); | |
627 | is($io->peekc, $c, "duplicate peekc matchess"); | |
628 | } | |
629 | { # initial peekc eof - buffered | |
630 | my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef); | |
631 | my $c = $io->peekc; | |
632 | is($c, -1, "buffered eof peekc is -1"); | |
633 | is($io->peekc, $c, "duplicate matches"); | |
634 | ok($io->eof, "io marked eof"); | |
635 | ok(!$io->error, "but not error"); | |
636 | } | |
637 | { # initial peekc eof - unbuffered | |
638 | my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef); | |
639 | $io->set_buffered(0); | |
640 | my $c = $io->peekc; | |
641 | is($c, -1, "buffered eof peekc is -1"); | |
642 | is($io->peekc, $c, "duplicate matches"); | |
643 | ok($io->eof, "io marked eof"); | |
644 | ok(!$io->error, "but not error"); | |
645 | } | |
646 | { # initial peekc error - buffered | |
647 | my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef); | |
648 | my $c = $io->peekc; | |
649 | is($c, -1, "buffered error peekc is -1"); | |
650 | is($io->peekc, $c, "duplicate matches"); | |
651 | ok($io->error, "io marked error"); | |
652 | ok(!$io->eof, "but not eof"); | |
653 | } | |
654 | { # initial peekc error - unbuffered | |
655 | my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef); | |
656 | $io->set_buffered(0); | |
657 | my $c = $io->peekc; | |
658 | is($c, -1, "unbuffered error peekc is -1"); | |
659 | is($io->peekc, $c, "duplicate matches"); | |
660 | ok($io->error, "io marked error"); | |
661 | ok(!$io->eof, "but not eof"); | |
662 | } | |
663 | { # initial putc | |
664 | my $io = Imager::io_new_bufchain(); | |
665 | is($io->putc(ord "A"), ord "A", "initial putc buffered"); | |
666 | is($io->close, 0, "close it"); | |
667 | is(Imager::io_slurp($io), "A", "check it was written"); | |
668 | } | |
669 | { # initial putc - unbuffered | |
670 | my $io = Imager::io_new_bufchain(); | |
671 | $io->set_buffered(0); | |
672 | is($io->putc(ord "A"), ord "A", "initial putc unbuffered"); | |
673 | is($io->close, 0, "close it"); | |
674 | is(Imager::io_slurp($io), "A", "check it was written"); | |
675 | } | |
676 | { # putc unbuffered with error | |
677 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
678 | $io->set_buffered(0); | |
679 | is($io->putc(ord "A"), -1, "initial putc unbuffered error"); | |
680 | ok($io->error, "io in error"); | |
681 | is($io->putc(ord "B"), -1, "still in error"); | |
682 | } | |
683 | { # writes while in read state | |
684 | my $io = Imager::io_new_cb(sub { 1 }, sub { return "AA" }, undef, undef); | |
685 | is($io->getc, ord "A", "read to setup read buffer"); | |
686 | is($io->putc(ord "B"), -1, "putc should fail"); | |
687 | is($io->write("test"), -1, "write should fail"); | |
688 | } | |
689 | { # buffered putc error handling | |
690 | # tests the check for error state in the buffered putc code | |
691 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
692 | $io->putc(ord "A"); | |
693 | ok(!$io->flush, "flush should fail"); | |
694 | ok($io->error, "should be in error state"); | |
695 | is($io->putc(ord "B"), -1, "check for error"); | |
696 | } | |
697 | { # buffered putc flush error handling | |
698 | # test handling of flush failure and of the error state resulting | |
699 | # from that | |
700 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
701 | my $i = 0; | |
702 | while (++$i < 100_000 && $io->putc(ord "A") == ord "A") { | |
703 | # until we have to flush and fail doing do | |
704 | } | |
705 | is($i, 8193, "should have failed on 8193rd byte"); | |
706 | ok($io->error, "should be in error state"); | |
707 | is($io->putc(ord "B"), -1, "next putc should fail"); | |
708 | } | |
709 | { # buffered write flush error handling | |
710 | # test handling of flush failure and of the error state resulting | |
711 | # from that | |
712 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
713 | my $i = 0; | |
714 | while (++$i < 100_000 && $io->write("A") == 1) { | |
715 | # until we have to flush and fail doing do | |
716 | } | |
717 | is($i, 8193, "should have failed on 8193rd byte"); | |
718 | ok($io->error, "should be in error state"); | |
719 | is($io->write("B"), -1, "next write should fail"); | |
720 | } | |
721 | { # buffered read error | |
722 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
723 | is($io->read2(10), undef, "initial read returning error"); | |
724 | ok($io->error, "should be in error state"); | |
725 | } | |
726 | { # unbuffered read error | |
727 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
728 | $io->set_buffered(0); | |
729 | is($io->read2(10), undef, "initial read returning error"); | |
730 | ok($io->error, "should be in error state"); | |
731 | } | |
732 | { # unbuffered write error | |
733 | my $count = 0; | |
734 | my $io = Imager::io_new_cb(sub { return $count++; }, undef, undef, undef); | |
735 | $io->set_buffered(0); | |
736 | is($io->write("A"), -1, "unbuffered write failure"); | |
737 | ok($io->error, "should be in error state"); | |
738 | is($io->write("BC"), -1, "should still fail"); | |
739 | } | |
740 | { # buffered write + large write | |
741 | my $io = Imager::io_new_bufchain(); | |
742 | is($io->write(substr($base, 0, 4096)), 4096, | |
743 | "should be buffered"); | |
744 | is($io->write(substr($base, 4096)), length($base) - 4096, | |
745 | "large write, should fill buffer and fall back to direct write"); | |
746 | is($io->close, 0, "close it"); | |
747 | is(Imager::io_slurp($io), $base, "make sure the data is correct"); | |
748 | } | |
749 | { # initial large write with failure | |
750 | # tests error handling for the case where we bypass the buffer | |
751 | # when the write is too large to fit | |
752 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
753 | ok($io->flush, "flush with nothing buffered should succeed"); | |
754 | is($io->write($base), -1, "large write failure"); | |
755 | ok($io->error, "should be in error state"); | |
756 | is($io->close, -1, "should fail to close"); | |
757 | } | |
758 | { # write that causes a flush then fills the buffer a bit | |
759 | my $io = Imager::io_new_bufchain(); | |
760 | is($io->write(substr($base, 0, 6000)), 6000, "fill the buffer a bit"); | |
761 | is($io->write(substr($base, 6000, 4000)), 4000, | |
762 | "cause it to flush and then fill some more"); | |
763 | is($io->write(substr($base, 10000)), length($base)-10000, | |
764 | "write out the rest of our test data"); | |
765 | is($io->close, 0, "close the stream"); | |
766 | is(Imager::io_slurp($io), $base, "make sure the data is right"); | |
767 | } | |
768 | { # failure on flush on close | |
769 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
770 | is($io->putc(ord "A"), ord "A", "something in the buffer"); | |
771 | ok(!$io->error, "should be no error yet"); | |
772 | is($io->close, -1, "close should failure due to flush error"); | |
773 | } | |
774 | { # seek failure | |
775 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
776 | is($io->seek(0, SEEK_SET), -1, "seek failure"); | |
777 | } | |
778 | { # read a little and seek | |
779 | my $io = Imager::io_new_buffer($base); | |
780 | is($io->getc, ord $base, "read one"); | |
781 | is($io->getc, ord substr($base, 1, 1), "read another"); | |
782 | is($io->seek(-1, SEEK_CUR), 1, "seek relative back to origin+1"); | |
783 | is($io->getc, ord substr($base, 1, 1), "read another again"); | |
784 | } | |
785 | { # seek with failing flush | |
786 | my $io = Imager::io_new_cb(undef, undef, undef, undef); | |
787 | is($io->putc(ord "A"), ord "A", "write one"); | |
788 | ok(!$io->error, "not in error mode (yet)"); | |
789 | is($io->seek(0, SEEK_SET), -1, "seek failure due to flush"); | |
790 | ok($io->error, "in error mode"); | |
791 | } | |
792 | { # gets() | |
793 | my $data = "test1\ntest2\ntest3"; | |
794 | my $io = Imager::io_new_buffer($data); | |
795 | is($io->gets(6), "test1\n", "gets(6)"); | |
796 | is($io->gets(5), "test2", "gets(5) (short for the line)"); | |
797 | is($io->gets(10), "\n", "gets(10) the rest of the line (the newline)"); | |
798 | is($io->gets(), "test3", "gets(default) unterminated line"); | |
799 | } | |
800 | { # more gets() | |
801 | my $data = "test1\ntest2\ntest3"; | |
802 | my $io = Imager::io_new_buffer($data); | |
803 | is($io->gets(6, ord("1")), "test1", "gets(6) (line terminator 1)"); | |
804 | is($io->gets(6, ord("2")), "\ntest2", "gets(6) (line terminator 2)"); | |
805 | is($io->gets(6, ord("3")), "\ntest3", "gets(6) (line terminator 3)"); | |
806 | is($io->getc, -1, "should be eof"); | |
807 | } | |
252 | 808 | } |
253 | 809 | |
254 | 810 | Imager->close_log; |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 47; | |
2 | use Test::More tests => 53; | |
3 | 3 | use Imager qw(:all); |
4 | use Imager::Test qw/is_color3 is_color4/; | |
4 | use Imager::Test qw/is_color3 is_color4 test_image test_image_mono/; | |
5 | 5 | |
6 | 6 | -d "testout" or mkdir "testout"; |
7 | 7 | |
165 | 165 | open RAW, "< testout/t103_empty.raw" |
166 | 166 | or die "Cannot open testout/t103_empty.raw: $!"; |
167 | 167 | my $im = Imager->new(xsize => 50, ysize=>50); |
168 | ok(!$im->write(fh => \*RAW, type => 'raw'), | |
168 | ok(!$im->write(fh => \*RAW, type => 'raw', buffered => 0), | |
169 | 169 | "write to open for read handle"); |
170 | 170 | cmp_ok($im->errstr, '=~', '^Could not write to file: write\(\) failure', |
171 | 171 | "check error message"); |
269 | 269 | "check last channel zeroed"); |
270 | 270 | } |
271 | 271 | |
272 | { | |
273 | my @ims = ( basic => test_image(), mono => test_image_mono() ); | |
274 | push @ims, masked => test_image()->masked(); | |
275 | ||
276 | my $fail_close = sub { | |
277 | Imager::i_push_error(0, "synthetic close failure"); | |
278 | return 0; | |
279 | }; | |
280 | ||
281 | while (my ($type, $im) = splice(@ims, 0, 2)) { | |
282 | my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close); | |
283 | ok(!$im->write(io => $io, type => "raw"), | |
284 | "write $type image with a failing close handler"); | |
285 | like($im->errstr, qr/synthetic close failure/, | |
286 | "check error message"); | |
287 | } | |
288 | } | |
289 | ||
272 | 290 | Imager->close_log; |
273 | 291 | |
274 | 292 | unless ($ENV{IMAGER_KEEP_FILES}) { |
0 | 0 | #!perl -w |
1 | 1 | use Imager ':all'; |
2 | use Test::More tests => 195; | |
2 | use Test::More tests => 205; | |
3 | 3 | use strict; |
4 | use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image); | |
4 | use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image test_image_named); | |
5 | ||
6 | $| = 1; | |
5 | 7 | |
6 | 8 | -d "testout" or mkdir "testout"; |
7 | 9 | |
64 | 66 | "compare written and read greyscale images"); |
65 | 67 | |
66 | 68 | my $ooim = Imager->new; |
67 | ok($ooim->read(file=>"testimg/simple.pbm"), "read simple pbm, via OO"); | |
69 | ok($ooim->read(file=>"testimg/simple.pbm"), "read simple pbm, via OO") | |
70 | or print "# ", $ooim->errstr, "\n"; | |
68 | 71 | |
69 | 72 | check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 0), 0); |
70 | 73 | check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 1), 255); |
232 | 235 | ok($im->write(file=>"testout/t104_alpha.ppm", type=>'pnm'), |
233 | 236 | "should succeed writing 4 channel image"); |
234 | 237 | my $imread = Imager->new; |
235 | ok($imread->read(file => 'testout/t104_alpha.ppm'), "read it back"); | |
238 | ok($imread->read(file => 'testout/t104_alpha.ppm'), "read it back") | |
239 | or print "# ", $imread->errstr, "\n"; | |
236 | 240 | is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, |
237 | 241 | "check transparent became black"); |
238 | 242 | is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192, |
593 | 597 | } |
594 | 598 | } |
595 | 599 | |
596 | Imager->close_log; | |
597 | ||
598 | 600 | { # image too large handling |
599 | 601 | { |
600 | 602 | ok(!Imager->new(file => "testimg/toowide.ppm", filetype => "pnm"), |
610 | 612 | } |
611 | 613 | } |
612 | 614 | |
615 | { # make sure close is checked for each image type | |
616 | my $fail_close = sub { | |
617 | Imager::i_push_error(0, "synthetic close failure"); | |
618 | return 0; | |
619 | }; | |
620 | ||
621 | for my $type (qw(basic basic16 gray gray16 mono)) { | |
622 | my $im = test_image_named($type); | |
623 | my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close); | |
624 | ok(!$im->write(io => $io, type => "pnm"), | |
625 | "write $type image with a failing close handler"); | |
626 | like($im->errstr, qr/synthetic close failure/, | |
627 | "check error message"); | |
628 | } | |
629 | } | |
630 | ||
631 | Imager->close_log; | |
632 | ||
613 | 633 | unless ($ENV{IMAGER_KEEP_FILES}) { |
614 | 634 | unlink "testout/t104ppm.log"; |
615 | 635 | unlink map "testout/$_", @files; |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 213; | |
2 | use Test::More tests => 215; | |
3 | 3 | use Imager qw(:all); |
4 | 4 | use Imager::Test qw(test_image_raw is_image is_color3 test_image); |
5 | 5 | |
612 | 612 | $im->read(file => "testimg/$file") |
613 | 613 | or die "Cannot read $file: ", $im->errstr; |
614 | 614 | |
615 | ok(!$im->write(type => 'bmp', callback => limited_write($limit), | |
616 | maxbuffer => 1), | |
615 | my $io = Imager::io_new_cb(limited_write($limit), undef, undef, undef, 1); | |
616 | $io->set_buffered(0); | |
617 | print "# writing with limit of $limit\n"; | |
618 | ok(!$im->write(type => 'bmp', io => $io), | |
617 | 619 | "$test_index - $desc: write should fail"); |
618 | 620 | is($im->errstr, $error, "$test_index - $desc: check error message"); |
619 | 621 | |
663 | 665 | ok($im->write(data => \$data, type => 'bmp'), "write using OO"); |
664 | 666 | my $size = unpack("V", substr($data, 34, 4)); |
665 | 667 | is($size, 67800, "check data size"); |
668 | } | |
669 | ||
670 | { # check close failures are handled correctly | |
671 | my $im = test_image(); | |
672 | my $fail_close = sub { | |
673 | Imager::i_push_error(0, "synthetic close failure"); | |
674 | return 0; | |
675 | }; | |
676 | ok(!$im->write(type => "bmp", callback => sub { 1 }, | |
677 | closecb => $fail_close), | |
678 | "check failing close fails"); | |
679 | like($im->errstr, qr/synthetic close failure/, | |
680 | "check error message"); | |
666 | 681 | } |
667 | 682 | |
668 | 683 | Imager->close_log; |
0 | 0 | #!perl -w |
1 | 1 | use Imager qw(:all); |
2 | 2 | use strict; |
3 | use Test::More tests=>66; | |
4 | use Imager::Test qw(is_color4 is_image); | |
3 | use Test::More tests=>68; | |
4 | use Imager::Test qw(is_color4 is_image test_image); | |
5 | 5 | |
6 | 6 | -d "testout" or mkdir "testout"; |
7 | 7 | |
231 | 231 | } |
232 | 232 | } |
233 | 233 | |
234 | { # check close failures are handled correctly | |
235 | my $im = test_image(); | |
236 | my $fail_close = sub { | |
237 | Imager::i_push_error(0, "synthetic close failure"); | |
238 | return 0; | |
239 | }; | |
240 | ok(!$im->write(type => "tga", callback => sub { 1 }, | |
241 | closecb => $fail_close), | |
242 | "check failing close fails"); | |
243 | like($im->errstr, qr/synthetic close failure/, | |
244 | "check error message"); | |
245 | } | |
246 | ||
234 | 247 | sub write_test { |
235 | 248 | my ($im, $filename, $wierdpack, $compress, $idstring) = @_; |
236 | 249 | local *FH; |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 156; | |
2 | use Test::More tests => 157; | |
3 | 3 | |
4 | 4 | use Imager ':handy'; |
5 | 5 | use Imager::Fill; |
637 | 637 | } |
638 | 638 | } |
639 | 639 | |
640 | { # RT 71309 | |
641 | my $fount = Imager::Fountain->simple(colors => [ '#804041', '#804041' ], | |
642 | positions => [ 0, 1 ]); | |
643 | my $im = Imager->new(xsize => 40, ysize => 40); | |
644 | $im->box(filled => 1, color => '#804040'); | |
645 | my $fill = Imager::Fill->new | |
646 | ( | |
647 | combine => 0, | |
648 | fountain => "linear", | |
649 | segments => $fount, | |
650 | xa => 0, ya => 0, | |
651 | xb => 40, yb => 40, | |
652 | ); | |
653 | $im->polygon(fill => $fill, | |
654 | points => | |
655 | [ | |
656 | [ 0, 0 ], | |
657 | [ 40, 20 ], | |
658 | [ 20, 40 ], | |
659 | ] | |
660 | ); | |
661 | # the bug magnified the differences between the source and destination | |
662 | # color, blending between the background and fill colors here only allows | |
663 | # for those 2 colors in the result. | |
664 | # with the bug extra colors appeared along the edge of the polygon. | |
665 | is($im->getcolorcount, 2, "only original and fill color"); | |
666 | } | |
667 | ||
640 | 668 | sub color_close { |
641 | 669 | my ($c1, $c2) = @_; |
642 | 670 |
9 | 9 | |
10 | 10 | # Change 1..1 below to 1..last_test_to_print . |
11 | 11 | # (It may become useful if the test is moved to ./t subdirectory.) |
12 | use Test::More tests => 12; | |
12 | use Test::More tests => 16; | |
13 | 13 | |
14 | 14 | BEGIN { use_ok('Imager') }; |
15 | ||
16 | BEGIN { | |
17 | require Imager::Test; | |
18 | Imager::Test->import(qw(isnt_image)); | |
19 | } | |
15 | 20 | |
16 | 21 | -d "testout" or mkdir "testout"; |
17 | 22 | |
27 | 32 | SKIP: |
28 | 33 | { |
29 | 34 | $Imager::formats{"tt"} && -f $fontname_tt |
30 | or skip("FT1.x missing or disabled", 10); | |
35 | or skip("FT1.x missing or disabled", 14); | |
31 | 36 | |
32 | 37 | my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n"; |
33 | 38 | |
72 | 77 | my @has_chars = $font->has_chars(string=>"\x01A"); |
73 | 78 | ok(!$has_chars[0], "has_chars list 0"); |
74 | 79 | ok($has_chars[1], "has_chars list 1"); |
80 | ||
81 | { # RT 71469 | |
82 | my $font1 = Imager::Font->new(file => $fontname_tt, type => "tt"); | |
83 | my $font2 = Imager::Font::Truetype->new(file => $fontname_tt); | |
84 | ||
85 | for my $font ($font1, $font2) { | |
86 | print "# ", join(",", $font->{color}->rgba), "\n"; | |
87 | ||
88 | my $im = Imager->new(xsize => 20, ysize => 20, channels => 4); | |
89 | ||
90 | ok($im->string(text => "T", font => $font, y => 15), | |
91 | "draw with default color") | |
92 | or print "# ", $im->errstr, "\n"; | |
93 | my $work = Imager->new(xsize => 20, ysize => 20); | |
94 | my $cmp = $work->copy; | |
95 | $work->rubthrough(src => $im); | |
96 | isnt_image($work, $cmp, "make sure something was drawn"); | |
97 | } | |
98 | } | |
75 | 99 | } |
76 | 100 | |
77 | 101 | ok(1, "end"); |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager qw(:handy); |
3 | use Test::More tests => 113; | |
3 | use Test::More tests => 116; | |
4 | 4 | |
5 | 5 | -d "testout" or mkdir "testout"; |
6 | 6 | |
21 | 21 | # this one's kind of cool |
22 | 22 | test($imbase, {type=>'conv', coef=>[ 0.3, 1, 0.3, ], }, |
23 | 23 | 'testout/t61_conv_blur.ppm'); |
24 | ||
25 | { | |
26 | my $work = $imbase->copy; | |
27 | ok(!Imager::i_conv($work->{IMG}, []), "conv should fail with empty array"); | |
28 | ok(!$work->filter(type => 'conv', coef => []), | |
29 | "check the conv OO intergave too"); | |
30 | is($work->errstr, "there must be at least one coefficient", | |
31 | "check conv error message"); | |
32 | } | |
24 | 33 | |
25 | 34 | { |
26 | 35 | my $work8 = $imbase->copy; |
17 | 17 | |
18 | 18 | -d "testout" or mkdir "testout"; |
19 | 19 | |
20 | plan tests => 9; | |
20 | plan tests => 16; | |
21 | 21 | require Inline; |
22 | 22 | Inline->import(with => 'Imager'); |
23 | 23 | Inline->import("FORCE"); # force rebuild |
24 | #Inline->import(C => Config => OPTIMIZE => "-g"); | |
24 | 25 | |
25 | 26 | Inline->bind(C => <<'EOS'); |
26 | 27 | #include <math.h> |
232 | 233 | return im; |
233 | 234 | } |
234 | 235 | |
236 | void | |
237 | io_fd(int fd) { | |
238 | Imager::IO io = io_new_fd(fd); | |
239 | i_io_write(io, "test", 4); | |
240 | i_io_close(io); | |
241 | io_glue_destroy(io); | |
242 | } | |
243 | ||
244 | int | |
245 | io_bufchain_test() { | |
246 | Imager::IO io = io_new_bufchain(); | |
247 | unsigned char *result; | |
248 | size_t size; | |
249 | if (i_io_write(io, "test2", 5) != 5) { | |
250 | fprintf(stderr, "write failed\n"); | |
251 | return 0; | |
252 | } | |
253 | if (!i_io_flush(io)) { | |
254 | fprintf(stderr, "flush failed\n"); | |
255 | return 0; | |
256 | } | |
257 | if (i_io_close(io) != 0) { | |
258 | fprintf(stderr, "close failed\n"); | |
259 | return 0; | |
260 | } | |
261 | size = io_slurp(io, &result); | |
262 | if (size != 5) { | |
263 | fprintf(stderr, "wrong size\n"); | |
264 | return 0; | |
265 | } | |
266 | if (memcmp(result, "test2", 5)) { | |
267 | fprintf(stderr, "data mismatch\n"); | |
268 | return 0; | |
269 | } | |
270 | if (i_io_seek(io, 0, 0) != 0) { | |
271 | fprintf(stderr, "seek failure\n"); | |
272 | return 0; | |
273 | } | |
274 | myfree(result); | |
275 | io_glue_destroy(io); | |
276 | ||
277 | return 1; | |
278 | } | |
279 | ||
280 | const char * | |
281 | io_buffer_test(SV *in) { | |
282 | STRLEN len; | |
283 | const char *in_str = SvPV(in, len); | |
284 | static char buf[100]; | |
285 | Imager::IO io = io_new_buffer(in_str, len, NULL, NULL); | |
286 | ssize_t read_size; | |
287 | ||
288 | read_size = i_io_read(io, buf, sizeof(buf)-1); | |
289 | io_glue_destroy(io); | |
290 | if (read_size < 0 || read_size >= sizeof(buf)) { | |
291 | return ""; | |
292 | } | |
293 | ||
294 | buf[read_size] = '\0'; | |
295 | ||
296 | return buf; | |
297 | } | |
298 | ||
299 | const char * | |
300 | io_peekn_test(SV *in) { | |
301 | STRLEN len; | |
302 | const char *in_str = SvPV(in, len); | |
303 | static char buf[100]; | |
304 | Imager::IO io = io_new_buffer(in_str, len, NULL, NULL); | |
305 | ssize_t read_size; | |
306 | ||
307 | read_size = i_io_peekn(io, buf, sizeof(buf)-1); | |
308 | io_glue_destroy(io); | |
309 | if (read_size < 0 || read_size >= sizeof(buf)) { | |
310 | return ""; | |
311 | } | |
312 | ||
313 | buf[read_size] = '\0'; | |
314 | ||
315 | return buf; | |
316 | } | |
317 | ||
318 | const char * | |
319 | io_gets_test(SV *in) { | |
320 | STRLEN len; | |
321 | const char *in_str = SvPV(in, len); | |
322 | static char buf[100]; | |
323 | Imager::IO io = io_new_buffer(in_str, len, NULL, NULL); | |
324 | ssize_t read_size; | |
325 | ||
326 | read_size = i_io_gets(io, buf, sizeof(buf), 's'); | |
327 | io_glue_destroy(io); | |
328 | if (read_size < 0 || read_size >= sizeof(buf)) { | |
329 | return ""; | |
330 | } | |
331 | ||
332 | return buf; | |
333 | } | |
334 | ||
335 | int | |
336 | io_getc_test(SV *in) { | |
337 | STRLEN len; | |
338 | const char *in_str = SvPV(in, len); | |
339 | static char buf[100]; | |
340 | Imager::IO io = io_new_buffer(in_str, len, NULL, NULL); | |
341 | int result; | |
342 | ||
343 | result = i_io_getc(io); | |
344 | io_glue_destroy(io); | |
345 | ||
346 | return result; | |
347 | } | |
348 | ||
349 | int | |
350 | io_peekc_test(SV *in) { | |
351 | STRLEN len; | |
352 | const char *in_str = SvPV(in, len); | |
353 | static char buf[100]; | |
354 | Imager::IO io = io_new_buffer(in_str, len, NULL, NULL); | |
355 | int result; | |
356 | ||
357 | i_io_set_buffered(io, 0); | |
358 | ||
359 | result = i_io_peekc(io); | |
360 | io_glue_destroy(io); | |
361 | ||
362 | return result; | |
363 | } | |
364 | ||
235 | 365 | EOS |
236 | 366 | |
237 | 367 | my $im = Imager->new(xsize=>50, ysize=>50); |
265 | 395 | is ($imb->REFCNT, $im2b->REFCNT, |
266 | 396 | "check refcnt of imager object hash between normal and typemap generated"); |
267 | 397 | } |
398 | ||
399 | SKIP: | |
400 | { | |
401 | use IO::File; | |
402 | my $fd_filename = "testout/t82fd.txt"; | |
403 | { | |
404 | my $fh = IO::File->new($fd_filename, "w") | |
405 | or skip("Can't create file: $!", 1); | |
406 | io_fd(fileno($fh)); | |
407 | $fh->close; | |
408 | } | |
409 | { | |
410 | my $fh = IO::File->new($fd_filename, "r") | |
411 | or skip("Can't open file: $!", 1); | |
412 | my $data = <$fh>; | |
413 | is($data, "test", "make sure data written to fd"); | |
414 | } | |
415 | unlink $fd_filename; | |
416 | } | |
417 | ||
418 | ok(io_bufchain_test(), "check bufchain functions"); | |
419 | ||
420 | is(io_buffer_test("test3"), "test3", "check io_new_buffer() and i_io_read"); | |
421 | ||
422 | is(io_peekn_test("test5"), "test5", "check i_io_peekn"); | |
423 | ||
424 | is(io_gets_test("test"), "tes", "check i_io_gets()"); | |
425 | ||
426 | is(io_getc_test("ABC"), ord "A", "check i_io_getc(_imp)?"); | |
427 | ||
428 | is(io_getc_test("XYZ"), ord "X", "check i_io_peekc(_imp)?"); |
2 | 2 | use lib 't'; |
3 | 3 | use Test::More; |
4 | 4 | use ExtUtils::Manifest qw(maniread); |
5 | #sub Pod::Coverage::TRACE_ALL() { 1 } | |
5 | 6 | eval "use Test::Pod::Coverage 1.08;"; |
6 | 7 | # 1.08 required for coverage_class support |
7 | 8 | plan skip_all => "Test::Pod::Coverage 1.08 required for POD coverage" if $@; |
23 | 24 | ); |
24 | 25 | my @trustme = ( '^open$', ); |
25 | 26 | |
26 | plan tests => 19; | |
27 | plan tests => 20; | |
27 | 28 | |
28 | 29 | { |
29 | 30 | pod_coverage_ok('Imager', { also_private => \@private, |
49 | 50 | pod_coverage_ok('Imager::Regops'); |
50 | 51 | pod_coverage_ok('Imager::Transform'); |
51 | 52 | pod_coverage_ok('Imager::Test'); |
53 | pod_coverage_ok('Imager::IO', | |
54 | { | |
55 | pod_from => "lib/Imager/IO.pod", | |
56 | coverage_class => "Pod::Coverage::Imager", | |
57 | module => "Imager", | |
58 | }); | |
52 | 59 | } |
53 | 60 | |
54 | 61 | { |
432 | 432 | tga_source_read(tga_source *s, unsigned char *buf, size_t pixels) { |
433 | 433 | int cp = 0, j, k; |
434 | 434 | if (!s->compressed) { |
435 | if (s->ig->readcb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
435 | if (i_io_read(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
436 | 436 | return 1; |
437 | 437 | } |
438 | 438 | |
441 | 441 | if (s->len == 0) s->state = NoInit; |
442 | 442 | switch (s->state) { |
443 | 443 | case NoInit: |
444 | if (s->ig->readcb(s->ig, &s->hdr, 1) != 1) return 0; | |
444 | if (i_io_read(s->ig, &s->hdr, 1) != 1) return 0; | |
445 | 445 | |
446 | 446 | s->len = (s->hdr &~(1<<7))+1; |
447 | 447 | s->state = (s->hdr & (1<<7)) ? Rle : Raw; |
451 | 451 | printf("%04d %s: %d\n", cnt++, s->state==Rle?"RLE":"RAW", s->len); |
452 | 452 | */ |
453 | 453 | } |
454 | if (s->state == Rle && s->ig->readcb(s->ig, s->cval, s->bytepp) != s->bytepp) return 0; | |
454 | if (s->state == Rle && i_io_read(s->ig, s->cval, s->bytepp) != s->bytepp) return 0; | |
455 | 455 | |
456 | 456 | break; |
457 | 457 | case Rle: |
463 | 463 | break; |
464 | 464 | case Raw: |
465 | 465 | ml = i_min(s->len, pixels-cp); |
466 | if (s->ig->readcb(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0; | |
466 | if (i_io_read(s->ig, buf+cp*s->bytepp, ml*s->bytepp) != ml*s->bytepp) return 0; | |
467 | 467 | cp += ml; |
468 | 468 | s->len -= ml; |
469 | 469 | break; |
494 | 494 | int cp = 0; |
495 | 495 | |
496 | 496 | if (!s->compressed) { |
497 | if (s->ig->writecb(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
497 | if (i_io_write(s->ig, buf, pixels*s->bytepp) != pixels*s->bytepp) return 0; | |
498 | 498 | return 1; |
499 | 499 | } |
500 | 500 | |
505 | 505 | while(tlen) { |
506 | 506 | unsigned char clen = (tlen>128) ? 128 : tlen; |
507 | 507 | clen--; |
508 | if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0; | |
508 | if (i_io_write(s->ig, &clen, 1) != 1) return 0; | |
509 | 509 | clen++; |
510 | if (s->ig->writecb(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0; | |
510 | if (i_io_write(s->ig, buf+cp*s->bytepp, clen*s->bytepp) != clen*s->bytepp) return 0; | |
511 | 511 | tlen -= clen; |
512 | 512 | cp += clen; |
513 | 513 | } |
517 | 517 | while (tlen) { |
518 | 518 | unsigned char clen = (tlen>128) ? 128 : tlen; |
519 | 519 | clen = (clen - 1) | 0x80; |
520 | if (s->ig->writecb(s->ig, &clen, 1) != 1) return 0; | |
520 | if (i_io_write(s->ig, &clen, 1) != 1) return 0; | |
521 | 521 | clen = (clen & ~0x80) + 1; |
522 | if (s->ig->writecb(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0; | |
522 | if (i_io_write(s->ig, buf+cp*s->bytepp, s->bytepp) != s->bytepp) return 0; | |
523 | 523 | tlen -= clen; |
524 | 524 | cp += clen; |
525 | 525 | } |
557 | 557 | palbsize = colourmaplength*bytepp; |
558 | 558 | palbuf = mymalloc(palbsize); |
559 | 559 | |
560 | if (ig->readcb(ig, palbuf, palbsize) != palbsize) { | |
560 | if (i_io_read(ig, palbuf, palbsize) != palbsize) { | |
561 | 561 | i_push_error(errno, "could not read targa colourmap"); |
562 | 562 | return 0; |
563 | 563 | } |
599 | 599 | color_pack(palbuf+i*bytepp, bitspp, &val); |
600 | 600 | } |
601 | 601 | |
602 | if (ig->writecb(ig, palbuf, palbsize) != palbsize) { | |
602 | if (i_io_write(ig, palbuf, palbsize) != palbsize) { | |
603 | 603 | i_push_error(errno, "could not write targa colourmap"); |
604 | 604 | return 0; |
605 | 605 | } |
640 | 640 | |
641 | 641 | mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length)); |
642 | 642 | |
643 | io_glue_commit_types(ig); | |
644 | ||
645 | if (ig->readcb(ig, &headbuf, 18) != 18) { | |
643 | if (i_io_read(ig, &headbuf, 18) != 18) { | |
646 | 644 | i_push_error(errno, "could not read targa header"); |
647 | 645 | return NULL; |
648 | 646 | } |
665 | 663 | if (header.idlength) { |
666 | 664 | /* max of 256, so this is safe */ |
667 | 665 | idstring = mymalloc(header.idlength+1); |
668 | if (ig->readcb(ig, idstring, header.idlength) != header.idlength) { | |
666 | if (i_io_read(ig, idstring, header.idlength) != header.idlength) { | |
669 | 667 | i_push_error(errno, "short read on targa idstring"); |
670 | 668 | return NULL; |
671 | 669 | } |
897 | 895 | |
898 | 896 | tga_header_pack(&header, headbuf); |
899 | 897 | |
900 | if (ig->writecb(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) { | |
898 | if (i_io_write(ig, &headbuf, sizeof(headbuf)) != sizeof(headbuf)) { | |
901 | 899 | i_push_error(errno, "could not write targa header"); |
902 | 900 | return 0; |
903 | 901 | } |
904 | 902 | |
905 | 903 | if (idlen) { |
906 | if (ig->writecb(ig, idstring, idlen) != idlen) { | |
904 | if (i_io_write(ig, idstring, idlen) != idlen) { | |
907 | 905 | i_push_error(errno, "could not write targa idstring"); |
908 | 906 | return 0; |
909 | 907 | } |
921 | 919 | if (!tga_palette_write(ig, img, bitspp, i_colorcount(img))) return 0; |
922 | 920 | |
923 | 921 | if (!img->virtual && !dest.compressed) { |
924 | if (ig->writecb(ig, img->idata, img->bytes) != img->bytes) { | |
922 | if (i_io_write(ig, img->idata, img->bytes) != img->bytes) { | |
925 | 923 | i_push_error(errno, "could not write targa image data"); |
926 | 924 | return 0; |
927 | 925 | } |
950 | 948 | myfree(vals); |
951 | 949 | } |
952 | 950 | |
953 | ig->closecb(ig); | |
951 | if (i_io_close(ig)) | |
952 | return 0; | |
954 | 953 | |
955 | 954 | return 1; |
956 | 955 | } |