Imported Upstream version 0.95+dfsg
gregor herrmann
11 years ago
0 | 0 | Imager release history. Older releases can be found in Changes.old |
1 | ||
2 | Imager 0.95 - 19 Apr 2013 | |
3 | =========== | |
4 | ||
5 | - handle the SVf_UTF8 flag correctly on magic strings passed to | |
6 | Imager::IO's write() and raw_write() methods. | |
7 | This may misbehave in perl 5.6.x since the UTF8 flag may not be | |
8 | visible in the main SV after SvGETMAGIC(). | |
9 | https://rt.cpan.org/Ticket/Display.html?id=83438 | |
10 | ||
11 | - document that bounding_box() ignores the transformation matrix | |
12 | supplied to tranform() for fonts. | |
13 | https://rt.cpan.org/Ticket/Display.html?id=84106 | |
14 | ||
15 | Imager 0.94_02 - 5 Apr 2013 | |
16 | ============== | |
17 | ||
18 | - enable debug logging for the standard font tests | |
19 | ||
20 | - skip the overloaded UTF-8 text output checks on 5.6.x and earlier, | |
21 | due to a bug in that version of perl. | |
22 | ||
23 | - don't test for read() failing on a write-only handle in 5.8.x and | |
24 | earlier, this was fixed in change 862083f7e4 in perl. | |
25 | ||
26 | - report the version of Inline found during testing (an attempt to | |
27 | diagnose some intermittent failures.) | |
28 | ||
29 | Imager 0.94_01 - 2 Mar 2013 | |
30 | ============== | |
31 | ||
32 | - NOTE: possibly backward incompatible: | |
33 | support reading from/writing to perl filehandes that aren't raw | |
34 | files. | |
35 | ||
36 | This allows Imager's I/O to honour handles with layers such as | |
37 | gzip, scalar file handles or tied file handles. | |
38 | ||
39 | This is backward incompatible in that previous Imager would simply | |
40 | use fileno() to retrieve the fd for the file and call write(2) etc | |
41 | on it directly. | |
42 | https://rt.cpan.org/Ticket/Display.html?id=78843 | |
43 | ||
44 | - moved most of README to lib/Imager/Install.pod which should make it | |
45 | more accessible to the "web" generation, also significantly updated | |
46 | and re-worked it. | |
47 | ||
48 | - updated README's for the separately distributed modules to refer to | |
49 | Imager::Install, and that they need -dev versions of packages | |
50 | https://rt.cpan.org/Ticket/Display.html?id=81265 | |
51 | ||
52 | - the JPEG test code now reports the compile-time library version | |
53 | ||
54 | - avoid a possible compiler optimization issue on CentOS 5.9 i386 by | |
55 | rearranging (and mildly optimizing) some code. | |
56 | https://rt.cpan.org/Ticket/Display.html?id=83212 | |
57 | ||
58 | - fix a POD error in Imager::Fill (detected on new Pod-Simple) | |
59 | https://rt.cpan.org/Ticket/Display.html?id=83434 | |
60 | ||
61 | - fix a broken link to Graphics::Magick | |
62 | https://rt.cpan.org/Ticket/Display.html?id=82743 | |
63 | ||
64 | - drawing text to a channel with FT2 would draw with random coverage | |
65 | due to an uninitialized alpha channel. | |
66 | ||
67 | - marked the function pointer underlying the mm_log() API with the | |
68 | correct gcc magic and fixed the resulting warnings. | |
69 | ||
70 | - fixed some other compiler warnings | |
71 | ||
72 | - Imager::Font::W32 now properly reports text drawing errors | |
73 | https://rt.cpan.org/Ticket/Display.html?id=70098 | |
74 | ||
75 | - handle the SVf_UTF8 flag correctly on magic (eg. overloaded) | |
76 | strings passed as text to draw(), bounding_box(), has_chars(), | |
77 | glyph_names() (where supported) in each of the font drivers. | |
78 | This may misbehave in perl 5.6.x since the UTF8 flag may not be | |
79 | visible in the main SV after SvGETMAGIC(). | |
80 | https://rt.cpan.org/Ticket/Display.html?id=83438 (partial) | |
81 | ||
82 | Imager 0.94 - 15 Dec 2012 | |
83 | ========================= | |
84 | ||
85 | Variations on some of these changes were included in development | |
86 | releases. | |
87 | ||
88 | - improved thread safety | |
89 | - the internal error stack and log file handle are now in a per-thread | |
90 | context object | |
91 | - JPEG now captures IPTC information in a thread-safe way | |
92 | - avoid globals where possible for warning capture in libtiff | |
93 | - use a mutex to avoid re-entering thread-unsafe giflib | |
94 | - use a mutex to avoid re-entering thread-unsafe tifflib | |
95 | - use a mutex to avoid re-entering thread-unsafe T1Lib | |
96 | - use a library handle per thread for freetype 2. | |
97 | - use an engine handle per thread for freetype 1.x. | |
98 | - originally these changes broke ABI compatibility, this has been | |
99 | restored. | |
100 | ||
101 | - clarify the return value of getpixel(); | |
102 | https://rt.cpan.org/Ticket/Display.html?id=81198 | |
103 | ||
104 | - fixed a race condition in parallel testing for T1 | |
105 | ||
106 | - fixed a bug in handling yoff for untransformed image-based fills | |
107 | ||
108 | - documentation improvements for Imager::Fill | |
109 | ||
110 | - FT2: report the library version while testing. | |
1 | 111 | |
2 | 112 | Imager 0.93 - 15 Oct 2012 |
3 | 113 | =========== |
17 | 17 | */ |
18 | 18 | |
19 | 19 | |
20 | static | |
20 | 21 | unsigned char |
21 | static | |
22 | 22 | saturate(int in) { |
23 | 23 | if (in>255) { return 255; } |
24 | 24 | else if (in>0) return in; |
0 | 0 | package Imager::Font::FT2; |
1 | 1 | use strict; |
2 | 2 | use Imager; |
3 | use Scalar::Util (); | |
3 | 4 | use vars qw($VERSION @ISA); |
4 | 5 | @ISA = qw(Imager::Font); |
5 | 6 | |
6 | 7 | BEGIN { |
7 | $VERSION = "0.86"; | |
8 | $VERSION = "0.91"; | |
8 | 9 | |
9 | 10 | require XSLoader; |
10 | 11 | XSLoader::load('Imager::Font::FT2', $VERSION); |
49 | 50 | |
50 | 51 | sub _draw { |
51 | 52 | my $self = shift; |
53 | ||
54 | $self->_valid | |
55 | or return; | |
56 | ||
52 | 57 | my %input = @_; |
53 | 58 | if (exists $input{channel}) { |
54 | 59 | i_ft2_cp($self->{id}, $input{image}{IMG}, $input{'x'}, $input{'y'}, |
68 | 73 | my $self = shift; |
69 | 74 | my %input = @_; |
70 | 75 | |
71 | return i_ft2_bbox($self->{id}, $input{size}, $input{sizew}, $input{string}, | |
72 | $input{utf8}); | |
76 | $self->_valid | |
77 | or return; | |
78 | ||
79 | my @result = i_ft2_bbox($self->{id}, $input{size}, $input{sizew}, | |
80 | $input{string}, $input{utf8}); | |
81 | unless (@result) { | |
82 | Imager->_set_error(Imager->_error_as_msg); | |
83 | return; | |
84 | } | |
85 | ||
86 | return @result; | |
73 | 87 | } |
74 | 88 | |
75 | 89 | sub dpi { |
76 | 90 | my $self = shift; |
91 | ||
92 | $self->_valid | |
93 | or return; | |
94 | ||
77 | 95 | my @old = i_ft2_getdpi($self->{id}); |
78 | 96 | if (@_) { |
79 | 97 | my %hsh = @_; |
96 | 114 | sub hinting { |
97 | 115 | my ($self, %opts) = @_; |
98 | 116 | |
117 | $self->_valid | |
118 | or return; | |
119 | ||
99 | 120 | i_ft2_sethinting($self->{id}, $opts{hinting} || 0); |
100 | 121 | } |
101 | 122 | |
102 | 123 | sub _transform { |
103 | 124 | my $self = shift; |
125 | ||
126 | $self->_valid | |
127 | or return; | |
104 | 128 | |
105 | 129 | my %hsh = @_; |
106 | 130 | my $matrix = $hsh{matrix} or return undef; |
116 | 140 | sub has_chars { |
117 | 141 | my ($self, %hsh) = @_; |
118 | 142 | |
143 | $self->_valid | |
144 | or return; | |
145 | ||
119 | 146 | unless (defined $hsh{string} && length $hsh{string}) { |
120 | 147 | $Imager::ERRSTR = "No string supplied to \$font->has_chars()"; |
121 | 148 | return; |
122 | 149 | } |
123 | return i_ft2_has_chars($self->{id}, $hsh{string}, | |
124 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
150 | if (wantarray) { | |
151 | my @result = i_ft2_has_chars($self->{id}, $hsh{string}, | |
152 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
153 | unless (@result) { | |
154 | Imager->_set_error(Imager->_error_as_msg); | |
155 | return; | |
156 | } | |
157 | ||
158 | return @result; | |
159 | } | |
160 | else { | |
161 | my $result = i_ft2_has_chars($self->{id}, $hsh{string}, | |
162 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
163 | unless (defined $result) { | |
164 | Imager->_set_error(Imager->_error_as_msg); | |
165 | return; | |
166 | } | |
167 | return $result; | |
168 | } | |
125 | 169 | } |
126 | 170 | |
127 | 171 | sub face_name { |
128 | 172 | my ($self) = @_; |
129 | 173 | |
174 | $self->_valid | |
175 | or return; | |
176 | ||
130 | 177 | i_ft2_face_name($self->{id}); |
131 | 178 | } |
132 | 179 | |
133 | 180 | sub can_glyph_names { |
134 | i_ft2_can_do_glyph_names(); | |
181 | my ($self) = @_; | |
182 | ||
183 | i_ft2_can_do_glyph_names() | |
184 | or return; | |
185 | ||
186 | if (ref $self) { | |
187 | $self->_valid | |
188 | or return; | |
189 | ||
190 | i_ft2_face_has_glyph_names($self->{id}) | |
191 | or return; | |
192 | } | |
193 | ||
194 | return 1; | |
135 | 195 | } |
136 | 196 | |
137 | 197 | sub glyph_names { |
138 | 198 | my ($self, %input) = @_; |
199 | ||
200 | $self->_valid | |
201 | or return; | |
139 | 202 | |
140 | 203 | my $string = $input{string}; |
141 | 204 | defined $string |
153 | 216 | sub is_mm { |
154 | 217 | my ($self) = @_; |
155 | 218 | |
219 | $self->_valid | |
220 | or return; | |
221 | ||
156 | 222 | i_ft2_is_multiple_master($self->{id}); |
157 | 223 | } |
158 | 224 | |
159 | 225 | sub mm_axes { |
160 | 226 | my ($self) = @_; |
227 | ||
228 | $self->_valid | |
229 | or return; | |
161 | 230 | |
162 | 231 | my ($num_axis, $num_design, @axes) = |
163 | 232 | i_ft2_get_multiple_masters($self->{id}) |
168 | 237 | |
169 | 238 | sub set_mm_coords { |
170 | 239 | my ($self, %opts) = @_; |
240 | ||
241 | $self->_valid | |
242 | or return; | |
171 | 243 | |
172 | 244 | $opts{coords} |
173 | 245 | or return Imager->_set_error("Missing coords parameter"); |
179 | 251 | |
180 | 252 | return 1; |
181 | 253 | } |
254 | ||
255 | # objects may be invalidated on thread creation (or Win32 fork emulation) | |
256 | sub _valid { | |
257 | my $self = shift; | |
258 | ||
259 | unless ($self->{id} && Scalar::Util::blessed($self->{id})) { | |
260 | Imager->_set_error("font object was created in another thread"); | |
261 | return; | |
262 | } | |
263 | ||
264 | return 1; | |
265 | } | |
266 | ||
182 | 267 | 1; |
183 | 268 | |
184 | 269 | __END__ |
32 | 32 | i_ft2_new(name, index) |
33 | 33 | char *name |
34 | 34 | int index |
35 | ||
36 | const char * | |
37 | i_ft2_version(runtime) | |
38 | int runtime | |
39 | PREINIT: | |
40 | char buf[100]; | |
41 | CODE: | |
42 | if (!i_ft2_version(runtime, buf, sizeof(buf))) { | |
43 | XSRETURN_EMPTY; | |
44 | } | |
45 | RETVAL = buf; | |
46 | OUTPUT: | |
47 | RETVAL | |
35 | 48 | |
36 | 49 | undef_int |
37 | 50 | i_ft2_setdpi(font, xdpi, ydpi) |
109 | 122 | } |
110 | 123 | |
111 | 124 | void |
112 | i_ft2_bbox_r(font, cheight, cwidth, text, vlayout, utf8) | |
125 | i_ft2_bbox_r(font, cheight, cwidth, text_sv, vlayout, utf8) | |
113 | 126 | Imager::Font::FT2x font |
114 | 127 | double cheight |
115 | 128 | double cwidth |
116 | char *text | |
129 | SV *text_sv | |
117 | 130 | int vlayout |
118 | 131 | int utf8 |
119 | 132 | PREINIT: |
120 | 133 | i_img_dim bbox[8]; |
121 | 134 | int i; |
122 | PPCODE: | |
123 | #ifdef SvUTF8 | |
124 | if (SvUTF8(ST(3))) | |
125 | utf8 = 1; | |
126 | #endif | |
127 | if (i_ft2_bbox_r(font, cheight, cwidth, text, strlen(text), vlayout, | |
135 | const char *text; | |
136 | STRLEN len; | |
137 | PPCODE: | |
138 | text = SvPV(text_sv, len); | |
139 | #ifdef SvUTF8 | |
140 | if (SvUTF8(text_sv)) | |
141 | utf8 = 1; | |
142 | #endif | |
143 | if (i_ft2_bbox_r(font, cheight, cwidth, text, len, vlayout, | |
128 | 144 | utf8, bbox)) { |
129 | 145 | EXTEND(SP, 8); |
130 | 146 | for (i = 0; i < 8; ++i) |
132 | 148 | } |
133 | 149 | |
134 | 150 | undef_int |
135 | i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, align, aa, vlayout, utf8) | |
151 | i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text_sv, align, aa, vlayout, utf8) | |
136 | 152 | Imager::Font::FT2x font |
137 | 153 | Imager::ImgRaw im |
138 | 154 | i_img_dim tx |
140 | 156 | Imager::Color cl |
141 | 157 | double cheight |
142 | 158 | double cwidth |
159 | SV *text_sv | |
143 | 160 | int align |
144 | 161 | int aa |
145 | 162 | int vlayout |
146 | 163 | int utf8 |
147 | 164 | PREINIT: |
148 | char *text; | |
165 | const char *text; | |
149 | 166 | STRLEN len; |
150 | 167 | CODE: |
151 | #ifdef SvUTF8 | |
152 | if (SvUTF8(ST(7))) { | |
153 | utf8 = 1; | |
154 | } | |
155 | #endif | |
156 | text = SvPV(ST(7), len); | |
168 | text = SvPV(text_sv, len); | |
169 | #ifdef SvUTF8 | |
170 | if (SvUTF8(text_sv)) { | |
171 | utf8 = 1; | |
172 | } | |
173 | #endif | |
157 | 174 | RETVAL = i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, |
158 | 175 | len, align, aa, vlayout, utf8); |
159 | 176 | OUTPUT: |
177 | 194 | char const *text; |
178 | 195 | STRLEN len; |
179 | 196 | CODE: |
180 | #ifdef SvUTF8 | |
181 | if (SvUTF8(ST(7))) | |
182 | utf8 = 1; | |
183 | #endif | |
184 | 197 | text = SvPV(text_sv, len); |
198 | #ifdef SvUTF8 | |
199 | if (SvUTF8(text_sv)) | |
200 | utf8 = 1; | |
201 | #endif | |
185 | 202 | RETVAL = i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, |
186 | len, align, aa, vlayout, 1); | |
203 | len, align, aa, vlayout, utf8); | |
187 | 204 | OUTPUT: |
188 | 205 | RETVAL |
189 | 206 | |
217 | 234 | size_t count; |
218 | 235 | size_t i; |
219 | 236 | PPCODE: |
220 | #ifdef SvUTF8 | |
221 | if (SvUTF8(text_sv)) | |
222 | utf8 = 1; | |
223 | #endif | |
224 | 237 | text = SvPV(text_sv, len); |
238 | #ifdef SvUTF8 | |
239 | if (SvUTF8(text_sv)) | |
240 | utf8 = 1; | |
241 | #endif | |
225 | 242 | work = mymalloc(len); |
226 | 243 | count = i_ft2_has_chars(handle, text, len, utf8, work); |
227 | 244 | if (GIMME_V == G_ARRAY) { |
263 | 280 | STRLEN work_len; |
264 | 281 | size_t len; |
265 | 282 | char name[255]; |
266 | PPCODE: | |
267 | #ifdef SvUTF8 | |
268 | if (SvUTF8(text_sv)) | |
269 | utf8 = 1; | |
270 | #endif | |
283 | SSize_t count = 0; | |
284 | PPCODE: | |
285 | i_clear_error(); | |
271 | 286 | text = SvPV(text_sv, work_len); |
272 | 287 | len = work_len; |
288 | #ifdef SvUTF8 | |
289 | if (SvUTF8(text_sv)) | |
290 | utf8 = 1; | |
291 | #endif | |
273 | 292 | while (len) { |
274 | 293 | unsigned long ch; |
275 | 294 | if (utf8) { |
276 | 295 | ch = i_utf8_advance(&text, &len); |
277 | 296 | if (ch == ~0UL) { |
278 | 297 | i_push_error(0, "invalid UTF8 character"); |
279 | break; | |
298 | XSRETURN_EMPTY; | |
280 | 299 | } |
281 | 300 | } |
282 | 301 | else { |
283 | 302 | ch = *text++; |
284 | 303 | --len; |
285 | 304 | } |
286 | EXTEND(SP, 1); | |
305 | EXTEND(SP, count+1); | |
287 | 306 | if (i_ft2_glyph_name(handle, ch, name, sizeof(name), |
288 | 307 | reliable_only)) { |
289 | PUSHs(sv_2mortal(newSVpv(name, 0))); | |
308 | ST(count) = sv_2mortal(newSVpv(name, 0)); | |
290 | 309 | } |
291 | 310 | else { |
292 | PUSHs(&PL_sv_undef); | |
293 | } | |
294 | } | |
311 | ST(count) = &PL_sv_undef; | |
312 | } | |
313 | ++count; | |
314 | } | |
315 | XSRETURN(count); | |
295 | 316 | |
296 | 317 | int |
297 | 318 | i_ft2_can_do_glyph_names() |
355 | 376 | |
356 | 377 | BOOT: |
357 | 378 | PERL_INITIALIZE_IMAGER_CALLBACKS; |
379 | i_ft2_start(); |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.86" ); | |
40 | my @Imager_req = ( Imager => "0.95" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
60 | 60 | $opts{PREREQ_PM} = |
61 | 61 | { |
62 | 62 | @Imager_req, |
63 | 'Scalar::Util' => 1.00, | |
63 | 64 | XSLoader => 0, |
64 | 65 | }; |
65 | 66 | } |
3 | 3 | It requires libfreetype and its development header files to be |
4 | 4 | installed. |
5 | 5 | |
6 | This is currently shipped as part of Imager, but but Imager may | |
7 | install with out installing Imager::Font::FT2, if your module or | |
8 | application requires FreeType 2 support then add Imager::Font::FT2 as | |
9 | a prerequisite. | |
6 | For Linux distributions this typically requires installation of the | |
7 | associated -dev or -devel package. | |
8 | ||
9 | See Imager::Install for more information. | |
10 | ||
11 | Imager::Font::FT2 is currently shipped as part of Imager, but but | |
12 | Imager may install with out installing Imager::Font::FT2. If your | |
13 | module or application requires FreeType 2 support then add | |
14 | Imager::Font::FT2 as a prerequisite. |
16 | 16 | i_img *im = ...; |
17 | 17 | i_color cl; |
18 | 18 | if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align, |
19 | aa)) { error } | |
19 | aa, vlayout, utf8)) { error } | |
20 | 20 | if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length, |
21 | 21 | align, aa)) { error } |
22 | 22 | i_ft2_destroy(font); |
37 | 37 | #include "imft2.h" |
38 | 38 | #include <stdio.h> |
39 | 39 | #include <math.h> |
40 | #include <string.h> | |
40 | 41 | #include <ft2build.h> |
41 | 42 | #include FT_FREETYPE_H |
42 | 43 | #ifdef FT_MULTIPLE_MASTERS_H |
48 | 49 | |
49 | 50 | static void ft2_push_message(int code); |
50 | 51 | |
51 | static int ft2_initialized = 0; | |
52 | static FT_Library library; | |
52 | static void ft2_final(void *); | |
53 | ||
54 | static im_slot_t slot = -1; | |
55 | ||
56 | typedef struct { | |
57 | int initialized; | |
58 | FT_Library library; | |
59 | im_context_t ctx; | |
60 | } ft2_state; | |
61 | ||
62 | static ft2_state * | |
63 | i_ft2_init(void); | |
53 | 64 | |
54 | 65 | static i_img_dim i_min(i_img_dim a, i_img_dim b); |
55 | 66 | static i_img_dim i_max(i_img_dim a, i_img_dim b); |
56 | 67 | |
68 | int | |
69 | i_ft2_version(int runtime, char *buf, size_t buf_size) { | |
70 | char work[100]; | |
71 | ||
72 | i_clear_error(); | |
73 | ||
74 | if (buf_size == 0) { | |
75 | i_push_error(0, "zero size buffer supplied"); | |
76 | return 0; | |
77 | } | |
78 | if (runtime) { | |
79 | ft2_state *ft2; | |
80 | /* initialized to work around a bug in FT2 | |
81 | http://lists.nongnu.org/archive/html/freetype-devel/2002-09/msg00058.html | |
82 | Though I don't know why I still see this in 2.4.2 | |
83 | */ | |
84 | FT_Int major = 1, minor = 1, patch = 1; | |
85 | ||
86 | if ((ft2 = i_ft2_init()) == NULL) | |
87 | return 0; | |
88 | ||
89 | FT_Library_Version(ft2->library, &major, &minor, &patch); | |
90 | sprintf(work, "%d.%d.%d", (int)major, (int)minor, (int)patch); | |
91 | } | |
92 | else { | |
93 | sprintf(work, "%d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); | |
94 | } | |
95 | strncpy(buf, work, buf_size); | |
96 | buf[buf_size-1] = '\0'; | |
97 | ||
98 | return 1; | |
99 | } | |
100 | ||
101 | void | |
102 | i_ft2_start(void) { | |
103 | if (slot == -1) | |
104 | slot = im_context_slot_new(ft2_final); | |
105 | } | |
106 | ||
57 | 107 | /* |
58 | 108 | =item i_ft2_init(void) |
59 | 109 | |
60 | 110 | Initializes the Freetype 2 library. |
61 | 111 | |
62 | Returns true on success, false on failure. | |
63 | ||
64 | =cut | |
65 | */ | |
66 | int | |
112 | Returns ft2_state * on success or NULL on failure. | |
113 | ||
114 | =cut | |
115 | */ | |
116 | ||
117 | static ft2_state * | |
67 | 118 | i_ft2_init(void) { |
68 | 119 | FT_Error error; |
69 | ||
70 | i_clear_error(); | |
71 | error = FT_Init_FreeType(&library); | |
72 | if (error) { | |
73 | ft2_push_message(error); | |
74 | i_push_error(0, "Initializing Freetype2"); | |
75 | return 0; | |
76 | } | |
77 | ||
78 | ft2_initialized = 1; | |
79 | ||
80 | return 1; | |
120 | im_context_t ctx = im_get_context(); | |
121 | ft2_state *ft2 = im_context_slot_get(ctx, slot); | |
122 | ||
123 | if (ft2 == NULL) { | |
124 | ft2 = mymalloc(sizeof(ft2_state)); | |
125 | ft2->initialized = 0; | |
126 | ft2->library = NULL; | |
127 | ft2->ctx = ctx; | |
128 | im_context_slot_set(ctx, slot, ft2); | |
129 | mm_log((1, "created FT2 state %p for context %p\n", ft2, ctx)); | |
130 | } | |
131 | ||
132 | i_clear_error(); | |
133 | if (!ft2->initialized) { | |
134 | error = FT_Init_FreeType(&ft2->library); | |
135 | if (error) { | |
136 | ft2_push_message(error); | |
137 | i_push_error(0, "Initializing Freetype2"); | |
138 | return NULL; | |
139 | } | |
140 | mm_log((1, "initialized FT2 state %p\n", ft2)); | |
141 | ||
142 | ft2->initialized = 1; | |
143 | } | |
144 | ||
145 | return ft2; | |
146 | } | |
147 | ||
148 | static void | |
149 | ft2_final(void *state) { | |
150 | ft2_state *ft2 = state; | |
151 | ||
152 | if (ft2->initialized) { | |
153 | mm_log((1, "finalizing FT2 state %p\n", state)); | |
154 | FT_Done_FreeType(ft2->library); | |
155 | ft2->library = NULL; | |
156 | ft2->initialized = 0; | |
157 | } | |
158 | ||
159 | mm_log((1, "freeing FT2 state %p\n", state)); | |
160 | myfree(state); | |
81 | 161 | } |
82 | 162 | |
83 | 163 | struct FT2_Fonthandle { |
84 | 164 | FT_Face face; |
165 | ft2_state *state; | |
85 | 166 | int xdpi, ydpi; |
86 | 167 | int hint; |
87 | 168 | FT_Encoding encoding; |
137 | 218 | int i, j; |
138 | 219 | FT_Encoding encoding; |
139 | 220 | int score; |
221 | ft2_state *ft2; | |
140 | 222 | |
141 | 223 | mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index)); |
142 | 224 | |
143 | if (!ft2_initialized && !i_ft2_init()) | |
225 | if ((ft2 = i_ft2_init()) == NULL) | |
144 | 226 | return NULL; |
145 | 227 | |
146 | 228 | i_clear_error(); |
147 | error = FT_New_Face(library, name, index, &face); | |
229 | error = FT_New_Face(ft2->library, name, index, &face); | |
148 | 230 | if (error) { |
149 | 231 | ft2_push_message(error); |
150 | 232 | i_push_error(error, "Opening face"); |
156 | 238 | score = 0; |
157 | 239 | for (i = 0; i < face->num_charmaps; ++i) { |
158 | 240 | FT_Encoding enc_entry = face->charmaps[i]->encoding; |
159 | mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n", | |
160 | enc_entry, face->charmaps[i]->platform_id, | |
241 | mm_log((2, "i_ft2_new, encoding %X platform %u encoding %u\n", | |
242 | (unsigned)enc_entry, face->charmaps[i]->platform_id, | |
161 | 243 | face->charmaps[i]->encoding_id)); |
162 | 244 | for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) { |
163 | 245 | if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) { |
168 | 250 | } |
169 | 251 | } |
170 | 252 | FT_Select_Charmap(face, encoding); |
171 | mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding)); | |
253 | mm_log((2, "i_ft2_new, selected encoding %X\n", (unsigned)encoding)); | |
172 | 254 | |
173 | 255 | result = mymalloc(sizeof(FT2_Fonthandle)); |
174 | 256 | result->face = face; |
257 | result->state = ft2; | |
175 | 258 | result->xdpi = result->ydpi = 72; |
176 | 259 | result->encoding = encoding; |
177 | 260 | |
243 | 326 | if (xdpi > 0 && ydpi > 0) { |
244 | 327 | handle->xdpi = xdpi; |
245 | 328 | handle->ydpi = ydpi; |
246 | return 0; | |
329 | return 1; | |
247 | 330 | } |
248 | 331 | else { |
249 | 332 | i_push_error(0, "resolutions must be positive"); |
340 | 423 | int loadFlags = FT_LOAD_DEFAULT; |
341 | 424 | int rightb = 0; |
342 | 425 | |
343 | mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n", | |
344 | handle, cheight, cwidth, text, len, bbox)); | |
426 | i_clear_error(); | |
427 | ||
428 | mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %u, bbox %p)\n", | |
429 | handle, cheight, cwidth, text, (unsigned)len, bbox)); | |
345 | 430 | |
346 | 431 | error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, |
347 | 432 | handle->xdpi, handle->ydpi); |
373 | 458 | error = FT_Load_Glyph(handle->face, index, loadFlags); |
374 | 459 | if (error) { |
375 | 460 | ft2_push_message(error); |
376 | i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", | |
461 | i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)", | |
377 | 462 | c, index); |
378 | 463 | return 0; |
379 | 464 | } |
415 | 500 | bbox[BBOX_ASCENT] = ascent; |
416 | 501 | bbox[BBOX_ADVANCE_WIDTH] = width; |
417 | 502 | bbox[BBOX_RIGHT_BEARING] = rightb; |
418 | mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7])); | |
503 | mm_log((1, " bbox=> negw=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF | |
504 | " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF | |
505 | " rightb=%" i_DF "\n", | |
506 | i_DFc(bbox[0]), i_DFc(bbox[1]), i_DFc(bbox[2]), i_DFc(bbox[3]), | |
507 | i_DFc(bbox[4]), i_DFc(bbox[5]), i_DFc(bbox[6]), i_DFc(bbox[7]))); | |
419 | 508 | |
420 | 509 | return BBOX_RIGHT_BEARING + 1; |
421 | 510 | } |
503 | 592 | int glyph_ascent, glyph_descent; |
504 | 593 | FT_Glyph_Metrics *gm; |
505 | 594 | i_img_dim work[4]; |
506 | i_img_dim bounds[4]; | |
595 | i_img_dim bounds[4] = { 0 }; | |
507 | 596 | double x = 0, y = 0; |
508 | 597 | int i; |
509 | 598 | FT_GlyphSlot slot; |
541 | 630 | error = FT_Load_Glyph(handle->face, index, loadFlags); |
542 | 631 | if (error) { |
543 | 632 | ft2_push_message(error); |
544 | i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", | |
633 | i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)", | |
545 | 634 | c, index); |
546 | 635 | return 0; |
547 | 636 | } |
654 | 743 | char last_mode = ft_pixel_mode_none; |
655 | 744 | int last_grays = -1; |
656 | 745 | int loadFlags = FT_LOAD_DEFAULT; |
657 | i_render *render; | |
658 | ||
659 | mm_log((1, "i_ft2_text(handle %p, im %p, tx %d, ty %d, cl %p, cheight %f, cwidth %f, text %p, len %d, align %d, aa %d)\n", | |
660 | handle, im, tx, ty, cl, cheight, cwidth, text, align, aa)); | |
746 | i_render *render = NULL; | |
747 | ||
748 | mm_log((1, "i_ft2_text(handle %p, im %p, (tx,ty) (" i_DFp "), cl %p, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n", | |
749 | handle, im, i_DFcp(tx, ty), cl, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8)); | |
750 | ||
751 | i_clear_error(); | |
661 | 752 | |
662 | 753 | if (vlayout) { |
663 | 754 | if (!FT_HAS_VERTICAL(handle->face)) { |
699 | 790 | error = FT_Load_Glyph(handle->face, index, loadFlags); |
700 | 791 | if (error) { |
701 | 792 | ft2_push_message(error); |
702 | i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", | |
793 | i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)", | |
703 | 794 | c, index); |
704 | if (aa) | |
795 | if (render) | |
705 | 796 | i_render_delete(render); |
706 | 797 | return 0; |
707 | 798 | } |
712 | 803 | error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono); |
713 | 804 | if (error) { |
714 | 805 | ft2_push_message(error); |
715 | i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)"); | |
716 | if (aa) | |
806 | i_push_errorf(0, "rendering glyph 0x%04lX (character \\x%02X)", c, index); | |
807 | if (render) | |
717 | 808 | i_render_delete(render); |
718 | 809 | return 0; |
719 | 810 | } |
764 | 855 | ty -= slot->advance.y / 64; |
765 | 856 | } |
766 | 857 | |
767 | if (aa) | |
858 | if (render) | |
768 | 859 | i_render_delete(render); |
769 | 860 | |
770 | 861 | return 1; |
771 | 862 | } |
772 | 863 | |
773 | 864 | /* |
774 | =item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa) | |
865 | =item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa, int vlayout, int utf8) | |
775 | 866 | |
776 | 867 | Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given |
777 | 868 | I<cheight> and I<cwidth>. |
781 | 872 | is rendered with (I<tx>, I<ty>) aligned with the base-line of the |
782 | 873 | characters. |
783 | 874 | |
784 | If aa is non-zero then the text is anti-aliased. | |
875 | If C<utf8> is non-zero the text is treated as UTF-8 encoded | |
876 | ||
877 | If C<aa> is non-zero then the text is drawn anti-aliased. | |
785 | 878 | |
786 | 879 | Returns non-zero on success. |
787 | 880 | |
797 | 890 | i_color cl, cl2; |
798 | 891 | int x, y; |
799 | 892 | |
800 | mm_log((1, "i_ft2_cp(handle %p, im %p, tx %d, ty %d, channel %d, cheight %f, cwidth %f, text %p, len %d, ...)\n", | |
801 | handle, im, tx, ty, channel, cheight, cwidth, text, len)); | |
893 | mm_log((1, "i_ft2_cp(handle %p, im %p, (tx, ty) (" i_DFp "), channel %d, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n", | |
894 | handle, im, i_DFcp(tx, ty), channel, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8)); | |
895 | ||
896 | i_clear_error(); | |
802 | 897 | |
803 | 898 | if (vlayout && !FT_HAS_VERTICAL(handle->face)) { |
804 | 899 | i_push_error(0, "face has no vertical metrics"); |
810 | 905 | |
811 | 906 | work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1); |
812 | 907 | cl.channel[0] = 255; |
908 | cl.channel[1] = 255; | |
813 | 909 | if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth, |
814 | 910 | text, len, 1, aa, vlayout, utf8)) |
815 | 911 | return 0; |
846 | 942 | i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len, |
847 | 943 | int utf8, char *out) { |
848 | 944 | int count = 0; |
849 | mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", | |
850 | handle, text, len, utf8)); | |
945 | mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %u, utf8 %d)\n", | |
946 | handle, text, (unsigned)len, utf8)); | |
947 | ||
948 | i_clear_error(); | |
851 | 949 | |
852 | 950 | while (len) { |
853 | 951 | unsigned long c; |
1040 | 1138 | *name_buf = '\0'; |
1041 | 1139 | return 0; |
1042 | 1140 | } |
1141 | if (strcmp(name_buf, ".notdef") == 0) { | |
1142 | *name_buf = 0; | |
1143 | return 0; | |
1144 | } | |
1043 | 1145 | if (*name_buf) { |
1044 | 1146 | return strlen(name_buf) + 1; |
1045 | 1147 | } |
1048 | 1150 | } |
1049 | 1151 | } |
1050 | 1152 | else { |
1051 | i_push_error(0, "no glyph for that character"); | |
1052 | 1153 | *name_buf = 0; |
1053 | 1154 | return 0; |
1054 | 1155 | } |
1069 | 1170 | #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES |
1070 | 1171 | return 0; |
1071 | 1172 | #else |
1072 | return FT_Has_PS_Glyph_Names(handle->face); | |
1173 | return FT_HAS_GLYPH_NAMES(handle->face); | |
1174 | /* return FT_Has_PS_Glyph_Names(handle->face);*/ | |
1073 | 1175 | #endif |
1074 | 1176 | } |
1075 | 1177 |
6 | 6 | |
7 | 7 | typedef FT2_Fonthandle* Imager__Font__FT2x; |
8 | 8 | |
9 | extern int i_ft2_init(void); | |
9 | extern int i_ft2_version(int runtime, char *buf, size_t buf_size); | |
10 | extern void i_ft2_start(void); | |
10 | 11 | extern FT2_Fonthandle * i_ft2_new(const char *name, int index); |
11 | 12 | extern void i_ft2_destroy(FT2_Fonthandle *handle); |
12 | 13 | extern int i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi); |
4 | 4 | |
5 | 5 | use Imager qw(:all); |
6 | 6 | |
7 | use Imager::Test qw(diff_text_with_nul is_color3 is_color4 isnt_image); | |
7 | use Imager::Test qw(diff_text_with_nul is_color3 is_color4 isnt_image is_image); | |
8 | 8 | |
9 | 9 | -d "testout" or mkdir "testout"; |
10 | 10 | |
22 | 22 | |
23 | 23 | -f $fontname or skip("cannot find fontfile $fontname", 189); |
24 | 24 | |
25 | print STDERR "FreeType2 runtime ", Imager::Font::FT2::i_ft2_version(1), | |
26 | " compile-time ", Imager::Font::FT2::i_ft2_version(0), "\n"; | |
25 | 27 | |
26 | 28 | my $bgcolor=i_color_new(255,0,0,0); |
27 | 29 | my $overlay=Imager::ImgRaw::new(200,70,3); |
134 | 136 | aa=>1), "drawn UTF natively")) { |
135 | 137 | print "# ",$im->errstr,"\n"; |
136 | 138 | } |
139 | ||
137 | 140 | } |
138 | 141 | |
139 | 142 | # an attempt using emulation of UTF8 |
601 | 604 | color => $color); |
602 | 605 | |
603 | 606 | } |
607 | ||
608 |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager; | |
3 | ||
4 | use Config; | |
5 | my $loaded_threads; | |
6 | BEGIN { | |
7 | if ($Config{useithreads} && $] > 5.008007) { | |
8 | $loaded_threads = | |
9 | eval { | |
10 | require threads; | |
11 | threads->import; | |
12 | 1; | |
13 | }; | |
14 | } | |
15 | } | |
16 | ||
17 | use Test::More; | |
18 | ||
19 | $Config{useithreads} | |
20 | or plan skip_all => "can't test Imager's lack of threads support with no threads"; | |
21 | $] > 5.008007 | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's lack of threads support"; | |
23 | $loaded_threads | |
24 | or plan skip_all => "couldn't load threads"; | |
25 | ||
26 | $INC{"Devel/Cover.pm"} | |
27 | and plan skip_all => "threads and Devel::Cover don't get along"; | |
28 | ||
29 | # https://rt.cpan.org/Ticket/Display.html?id=65812 | |
30 | # https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100 | |
31 | $Test::More::VERSION =~ /^2\.00_/ | |
32 | and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*"; | |
33 | ||
34 | plan tests => 8; | |
35 | ||
36 | Imager->open_log(log => "testout/t20thread.log"); | |
37 | ||
38 | my $ft1 = Imager::Font->new(file => "fontfiles/dodge.ttf", type => "ft2"); | |
39 | ok($ft1, "make a font"); | |
40 | ok($ft1->_valid, "and it's valid"); | |
41 | my $ft2; | |
42 | ||
43 | my $thr = threads->create | |
44 | ( | |
45 | sub { | |
46 | ok(!$ft1->_valid, "first font no longer valid"); | |
47 | $ft2 = Imager::Font->new(file => "fontfiles/dodge.ttf", type => "ft2"); | |
48 | ok($ft2, "make a new font in thread"); | |
49 | ok($ft2->_valid, "and it's valid"); | |
50 | 1; | |
51 | }, | |
52 | ); | |
53 | ||
54 | ok($thr->join, "join the thread"); | |
55 | ok($ft1->_valid, "original font still valid in main thread"); | |
56 | is($ft2, undef, "font created in thread shouldn't be set in main thread"); | |
57 | ||
58 | Imager->close_log(); |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager::Test qw(std_font_tests std_font_test_count); | |
3 | use Imager::Font; | |
4 | use Test::More tests => std_font_test_count(); | |
5 | ||
6 | Imager->open_log(log => "testout/t90std.log"); | |
7 | ||
8 | my $font = Imager::Font->new(file => "fontfiles/dodge.ttf", | |
9 | type => "ft2"); | |
10 | my $name_font = | |
11 | Imager::Font->new(file => "fontfiles/ImUgly.ttf", | |
12 | type => "ft2"); | |
13 | ||
14 | SKIP: | |
15 | { | |
16 | $font | |
17 | or skip "Cannot load font", std_font_test_count(); | |
18 | std_font_tests | |
19 | ({ | |
20 | font => $font, | |
21 | has_chars => [ 1, 1, 1 ], | |
22 | files => 1, | |
23 | glyph_name_font => $name_font, | |
24 | glyph_names => [ "A", "uni2010", "A" ], | |
25 | }); | |
26 | } | |
27 | ||
28 | Imager->close_log; |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.85"; | |
6 | $VERSION = "0.87"; | |
7 | 7 | |
8 | 8 | require XSLoader; |
9 | 9 | XSLoader::load('Imager::File::GIF', $VERSION); |
146 | 146 | BOOT: |
147 | 147 | PERL_INITIALIZE_IMAGER_CALLBACKS; |
148 | 148 | PERL_INITIALIZE_IMAGER_PERL_CALLBACKS; |
149 | i_init_gif(); |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.86" ); | |
40 | my @Imager_req = ( Imager => "0.94" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
1 | 1 | |
2 | 2 | It requires libgif to be installed, including development headers. |
3 | 3 | |
4 | This is currently shipped as part of Imager, but Imager may install | |
5 | with out installing Imager::File::GIF, so if you need GIF support, add | |
6 | a dependency on Imager::File::GIF. | |
4 | For Linux distributions this typically requires installation of the | |
5 | associated -dev or -devel package. | |
6 | ||
7 | See Imager::Install for more information. | |
8 | ||
9 | Imager::File::GIF is currently shipped as part of Imager, but Imager | |
10 | may install with out installing Imager::File::GIF, so if you need GIF | |
11 | support, add a dependency on Imager::File::GIF. | |
12 | ||
13 | Makefile.PL will reject libgif 4.2.0 and 5.0.0 due to bugs in those | |
14 | releases.⏎ |
65 | 65 | #define myGifError(gif) ((gif)->Error) |
66 | 66 | #define MakeMapObject GifMakeMapObject |
67 | 67 | #define FreeMapObject GifFreeMapObject |
68 | ||
68 | #define gif_mutex_lock(mutex) | |
69 | #define gif_mutex_unlock(mutex) | |
69 | 70 | #else |
70 | 71 | #define PRE_SET_VERSION |
71 | 72 | static GifFileType * |
85 | 86 | return result; |
86 | 87 | } |
87 | 88 | #define myGifError(gif) GifLastError() |
89 | #define gif_mutex_lock(mutex) i_mutex_lock(mutex) | |
90 | #define gif_mutex_unlock(mutex) i_mutex_unlock(mutex) | |
88 | 91 | |
89 | 92 | #endif |
90 | 93 | |
96 | 99 | static const int |
97 | 100 | InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */ |
98 | 101 | InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ |
102 | ||
103 | #if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5 | |
104 | static i_mutex_t mutex; | |
105 | #endif | |
106 | ||
107 | void | |
108 | i_init_gif(void) { | |
109 | #if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5 | |
110 | mutex = i_mutex_new(); | |
111 | #endif | |
112 | } | |
99 | 113 | |
100 | 114 | static |
101 | 115 | void |
876 | 890 | i_readgif_multi_wiol(io_glue *ig, int *count) { |
877 | 891 | GifFileType *GifFile; |
878 | 892 | int gif_error; |
893 | i_img **result; | |
894 | ||
895 | gif_mutex_lock(mutex); | |
879 | 896 | |
880 | 897 | i_clear_error(); |
881 | 898 | |
883 | 900 | gif_push_error(gif_error); |
884 | 901 | i_push_error(0, "Cannot create giflib callback object"); |
885 | 902 | mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n")); |
903 | gif_mutex_unlock(mutex); | |
886 | 904 | return NULL; |
887 | 905 | } |
888 | 906 | |
889 | return i_readgif_multi_low(GifFile, count, -1); | |
907 | result = i_readgif_multi_low(GifFile, count, -1); | |
908 | ||
909 | gif_mutex_unlock(mutex); | |
910 | ||
911 | return result; | |
890 | 912 | } |
891 | 913 | |
892 | 914 | static int |
900 | 922 | i_readgif_wiol(io_glue *ig, int **color_table, int *colors) { |
901 | 923 | GifFileType *GifFile; |
902 | 924 | int gif_error; |
925 | i_img *result; | |
926 | ||
927 | gif_mutex_lock(mutex); | |
903 | 928 | |
904 | 929 | i_clear_error(); |
905 | 930 | |
907 | 932 | gif_push_error(gif_error); |
908 | 933 | i_push_error(0, "Cannot create giflib callback object"); |
909 | 934 | mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n")); |
935 | gif_mutex_unlock(mutex); | |
910 | 936 | return NULL; |
911 | 937 | } |
912 | 938 | |
913 | return i_readgif_low(GifFile, color_table, colors); | |
939 | result = i_readgif_low(GifFile, color_table, colors); | |
940 | ||
941 | gif_mutex_unlock(mutex); | |
942 | ||
943 | return result; | |
914 | 944 | } |
915 | 945 | |
916 | 946 | /* |
956 | 986 | i_readgif_single_wiol(io_glue *ig, int page) { |
957 | 987 | GifFileType *GifFile; |
958 | 988 | int gif_error; |
989 | i_img *result; | |
959 | 990 | |
960 | 991 | i_clear_error(); |
961 | 992 | if (page < 0) { |
963 | 994 | return NULL; |
964 | 995 | } |
965 | 996 | |
997 | gif_mutex_lock(mutex); | |
966 | 998 | |
967 | 999 | if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) { |
968 | 1000 | gif_push_error(gif_error); |
969 | 1001 | i_push_error(0, "Cannot create giflib callback object"); |
970 | 1002 | mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n")); |
1003 | gif_mutex_unlock(mutex); | |
971 | 1004 | return NULL; |
972 | 1005 | } |
973 | 1006 | |
974 | return i_readgif_single_low(GifFile, page); | |
1007 | result = i_readgif_single_low(GifFile, page); | |
1008 | ||
1009 | gif_mutex_unlock(mutex); | |
1010 | ||
1011 | return result; | |
975 | 1012 | } |
976 | 1013 | |
977 | 1014 | /* |
1829 | 1866 | int gif_error; |
1830 | 1867 | int result; |
1831 | 1868 | |
1869 | gif_mutex_lock(mutex); | |
1870 | ||
1832 | 1871 | i_clear_error(); |
1833 | 1872 | |
1834 | 1873 | #ifdef PRE_SET_VERSION |
1839 | 1878 | gif_push_error(gif_error); |
1840 | 1879 | i_push_error(0, "Cannot create giflib callback object"); |
1841 | 1880 | mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n")); |
1881 | gif_mutex_unlock(mutex); | |
1842 | 1882 | return 0; |
1843 | 1883 | } |
1844 | 1884 | |
1848 | 1888 | |
1849 | 1889 | result = i_writegif_low(quant, GifFile, imgs, count); |
1850 | 1890 | |
1891 | gif_mutex_unlock(mutex); | |
1892 | ||
1851 | 1893 | if (i_io_close(ig)) |
1852 | 1894 | return 0; |
1853 | 1895 | |
1969 | 2011 | |
1970 | 2012 | Arnar M. Hrafnkelsson, addi@umich.edu |
1971 | 2013 | |
2014 | Tony Cook <tonyc@cpan.org> | |
2015 | ||
1972 | 2016 | =head1 SEE ALSO |
1973 | 2017 | |
1974 | 2018 | perl(1), Imager(3) |
2 | 2 | |
3 | 3 | #include "imext.h" |
4 | 4 | |
5 | void i_init_gif(void); | |
5 | 6 | double i_giflib_version(void); |
6 | 7 | i_img *i_readgif_wiol(io_glue *ig, int **colour_table, int *colours); |
7 | 8 | i_img *i_readgif_single_wiol(io_glue *ig, int page); |
2 | 2 | use strict; |
3 | 3 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR %OPCODES $I2P $FORMATGUESS $warn_obsolete); |
4 | 4 | use IO::File; |
5 | ||
5 | use Scalar::Util; | |
6 | 6 | use Imager::Color; |
7 | 7 | use Imager::Font; |
8 | use Config; | |
8 | 9 | |
9 | 10 | @EXPORT_OK = qw( |
10 | 11 | init |
142 | 143 | if ($ex_version < 5.57) { |
143 | 144 | @ISA = qw(Exporter); |
144 | 145 | } |
145 | $VERSION = '0.93'; | |
146 | $VERSION = '0.95'; | |
146 | 147 | require XSLoader; |
147 | 148 | XSLoader::load(Imager => $VERSION); |
148 | 149 | } |
618 | 619 | sub _valid_image { |
619 | 620 | my ($self, $method) = @_; |
620 | 621 | |
621 | $self->{IMG} and return 1; | |
622 | ||
623 | my $msg = 'empty input image'; | |
622 | $self->{IMG} && Scalar::Util::blessed($self->{IMG}) and return 1; | |
623 | ||
624 | my $msg = $self->{IMG} ? "images do not cross threads" : "empty input image"; | |
624 | 625 | $msg = "$method: $msg" if $method; |
625 | 626 | $self->_set_error($msg); |
626 | 627 | |
686 | 687 | |
687 | 688 | sub copy { |
688 | 689 | my $self = shift; |
689 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
690 | ||
691 | $self->_valid_image("copy") | |
692 | or return; | |
690 | 693 | |
691 | 694 | unless (defined wantarray) { |
692 | 695 | my @caller = caller; |
704 | 707 | sub paste { |
705 | 708 | my $self = shift; |
706 | 709 | |
707 | unless ($self->{IMG}) { | |
708 | $self->_set_error('empty input image'); | |
709 | return; | |
710 | } | |
710 | $self->_valid_image("paste") | |
711 | or return; | |
712 | ||
711 | 713 | my %input=(left=>0, top=>0, src_minx => 0, src_miny => 0, @_); |
712 | 714 | my $src = $input{img} || $input{src}; |
713 | 715 | unless($src) { |
714 | 716 | $self->_set_error("no source image"); |
717 | return; | |
718 | } | |
719 | unless ($src->_valid_image("paste")) { | |
720 | $self->{ERRSTR} = $src->{ERRSTR} . " (for src)"; | |
715 | 721 | return; |
716 | 722 | } |
717 | 723 | $input{left}=0 if $input{left} <= 0; |
772 | 778 | |
773 | 779 | sub crop { |
774 | 780 | my $self=shift; |
775 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
781 | ||
782 | $self->_valid_image("crop") | |
783 | or return; | |
776 | 784 | |
777 | 785 | unless (defined wantarray) { |
778 | 786 | my @caller = caller; |
860 | 868 | sub _sametype { |
861 | 869 | my ($self, %opts) = @_; |
862 | 870 | |
863 | $self->{IMG} or return $self->_set_error("Not a valid image"); | |
871 | $self->_valid_image | |
872 | or return; | |
864 | 873 | |
865 | 874 | my $x = $opts{xsize} || $self->getwidth; |
866 | 875 | my $y = $opts{ysize} || $self->getheight; |
923 | 932 | sub masked { |
924 | 933 | my $self = shift; |
925 | 934 | |
926 | $self or return undef; | |
935 | $self->_valid_image("masked") | |
936 | or return; | |
937 | ||
927 | 938 | my %opts = (left => 0, |
928 | 939 | top => 0, |
929 | 940 | right => $self->getwidth, |
964 | 975 | return; |
965 | 976 | } |
966 | 977 | |
967 | $self->_valid_image | |
978 | $self->_valid_image("to_paletted") | |
968 | 979 | or return; |
969 | 980 | |
970 | 981 | my $result = Imager->new; |
1005 | 1016 | return; |
1006 | 1017 | } |
1007 | 1018 | |
1008 | $self->_valid_image | |
1019 | $self->_valid_image("to_rgb8") | |
1009 | 1020 | or return; |
1010 | 1021 | |
1011 | 1022 | my $result = Imager->new; |
1027 | 1038 | return; |
1028 | 1039 | } |
1029 | 1040 | |
1030 | $self->_valid_image | |
1041 | $self->_valid_image("to_rgb16") | |
1031 | 1042 | or return; |
1032 | 1043 | |
1033 | 1044 | my $result = Imager->new; |
1049 | 1060 | return; |
1050 | 1061 | } |
1051 | 1062 | |
1052 | $self->_valid_image | |
1063 | $self->_valid_image("to_rgb_double") | |
1053 | 1064 | or return; |
1054 | 1065 | |
1055 | 1066 | my $result = Imager->new; |
1065 | 1076 | my $self = shift; |
1066 | 1077 | my %opts = (colors=>[], @_); |
1067 | 1078 | |
1068 | unless ($self->{IMG}) { | |
1069 | $self->_set_error("empty input image"); | |
1070 | return; | |
1071 | } | |
1079 | $self->_valid_image("addcolors") | |
1080 | or return -1; | |
1072 | 1081 | |
1073 | 1082 | my @colors = @{$opts{colors}} |
1074 | 1083 | or return undef; |
1088 | 1097 | my $self = shift; |
1089 | 1098 | my %opts = (start=>0, colors=>[], @_); |
1090 | 1099 | |
1091 | unless ($self->{IMG}) { | |
1092 | $self->_set_error("empty input image"); | |
1093 | return; | |
1094 | } | |
1100 | $self->_valid_image("setcolors") | |
1101 | or return; | |
1095 | 1102 | |
1096 | 1103 | my @colors = @{$opts{colors}} |
1097 | 1104 | or return undef; |
1110 | 1117 | sub getcolors { |
1111 | 1118 | my $self = shift; |
1112 | 1119 | my %opts = @_; |
1120 | ||
1121 | $self->_valid_image("getcolors") | |
1122 | or return; | |
1123 | ||
1113 | 1124 | if (!exists $opts{start} && !exists $opts{count}) { |
1114 | 1125 | # get them all |
1115 | 1126 | $opts{start} = 0; |
1121 | 1132 | elsif (!exists $opts{start}) { |
1122 | 1133 | $opts{start} = 0; |
1123 | 1134 | } |
1124 | ||
1125 | $self->{IMG} and | |
1126 | return i_getcolors($self->{IMG}, $opts{start}, $opts{count}); | |
1135 | ||
1136 | return i_getcolors($self->{IMG}, $opts{start}, $opts{count}); | |
1127 | 1137 | } |
1128 | 1138 | |
1129 | 1139 | sub colorcount { |
1130 | i_colorcount($_[0]{IMG}); | |
1140 | my ($self) = @_; | |
1141 | ||
1142 | $self->_valid_image("colorcount") | |
1143 | or return -1; | |
1144 | ||
1145 | return i_colorcount($self->{IMG}); | |
1131 | 1146 | } |
1132 | 1147 | |
1133 | 1148 | sub maxcolors { |
1134 | i_maxcolors($_[0]{IMG}); | |
1149 | my $self = shift; | |
1150 | ||
1151 | $self->_valid_image("maxcolors") | |
1152 | or return -1; | |
1153 | ||
1154 | i_maxcolors($self->{IMG}); | |
1135 | 1155 | } |
1136 | 1156 | |
1137 | 1157 | sub findcolor { |
1138 | 1158 | my $self = shift; |
1139 | 1159 | my %opts = @_; |
1140 | $opts{color} or return undef; | |
1141 | ||
1142 | $self->{IMG} and i_findcolor($self->{IMG}, $opts{color}); | |
1160 | ||
1161 | $self->_valid_image("findcolor") | |
1162 | or return; | |
1163 | ||
1164 | unless ($opts{color}) { | |
1165 | $self->_set_error("findcolor: no color parameter"); | |
1166 | return; | |
1167 | } | |
1168 | ||
1169 | my $color = _color($opts{color}) | |
1170 | or return; | |
1171 | ||
1172 | return i_findcolor($self->{IMG}, $color); | |
1143 | 1173 | } |
1144 | 1174 | |
1145 | 1175 | sub bits { |
1146 | 1176 | my $self = shift; |
1147 | my $bits = $self->{IMG} && i_img_bits($self->{IMG}); | |
1177 | ||
1178 | $self->_valid_image("bits") | |
1179 | or return; | |
1180 | ||
1181 | my $bits = i_img_bits($self->{IMG}); | |
1148 | 1182 | if ($bits && $bits == length(pack("d", 1)) * 8) { |
1149 | 1183 | $bits = 'double'; |
1150 | 1184 | } |
1151 | $bits; | |
1185 | return $bits; | |
1152 | 1186 | } |
1153 | 1187 | |
1154 | 1188 | sub type { |
1155 | 1189 | my $self = shift; |
1156 | if ($self->{IMG}) { | |
1157 | return i_img_type($self->{IMG}) ? "paletted" : "direct"; | |
1158 | } | |
1190 | ||
1191 | $self->_valid_image("type") | |
1192 | or return; | |
1193 | ||
1194 | return i_img_type($self->{IMG}) ? "paletted" : "direct"; | |
1159 | 1195 | } |
1160 | 1196 | |
1161 | 1197 | sub virtual { |
1162 | 1198 | my $self = shift; |
1163 | $self->{IMG} and i_img_virtual($self->{IMG}); | |
1199 | ||
1200 | $self->_valid_image("virtual") | |
1201 | or return; | |
1202 | ||
1203 | return i_img_virtual($self->{IMG}); | |
1164 | 1204 | } |
1165 | 1205 | |
1166 | 1206 | sub is_bilevel { |
1167 | 1207 | my ($self) = @_; |
1168 | 1208 | |
1169 | $self->{IMG} or return; | |
1209 | $self->_valid_image("is_bilevel") | |
1210 | or return; | |
1170 | 1211 | |
1171 | 1212 | return i_img_is_monochrome($self->{IMG}); |
1172 | 1213 | } |
1174 | 1215 | sub tags { |
1175 | 1216 | my ($self, %opts) = @_; |
1176 | 1217 | |
1177 | $self->{IMG} or return; | |
1218 | $self->_valid_image("tags") | |
1219 | or return; | |
1178 | 1220 | |
1179 | 1221 | if (defined $opts{name}) { |
1180 | 1222 | my @result; |
1210 | 1252 | my $self = shift; |
1211 | 1253 | my %opts = @_; |
1212 | 1254 | |
1213 | return -1 unless $self->{IMG}; | |
1255 | $self->_valid_image("addtag") | |
1256 | or return; | |
1257 | ||
1214 | 1258 | if ($opts{name}) { |
1215 | 1259 | if (defined $opts{value}) { |
1216 | 1260 | if ($opts{value} =~ /^\d+$/) { |
1258 | 1302 | my $self = shift; |
1259 | 1303 | my %opts = @_; |
1260 | 1304 | |
1261 | return 0 unless $self->{IMG}; | |
1305 | $self->_valid_image("deltag") | |
1306 | or return 0; | |
1262 | 1307 | |
1263 | 1308 | if (defined $opts{'index'}) { |
1264 | 1309 | return i_tags_delete($self->{IMG}, $opts{'index'}); |
1278 | 1323 | sub settag { |
1279 | 1324 | my ($self, %opts) = @_; |
1280 | 1325 | |
1326 | $self->_valid_image("settag") | |
1327 | or return; | |
1328 | ||
1281 | 1329 | if ($opts{name}) { |
1282 | 1330 | $self->deltag(name=>$opts{name}); |
1283 | 1331 | return $self->addtag(name=>$opts{name}, value=>$opts{value}); |
1302 | 1350 | return io_new_fd($input->{fd}); |
1303 | 1351 | } |
1304 | 1352 | elsif ($input->{fh}) { |
1305 | my $fd = fileno($input->{fh}); | |
1306 | unless (defined $fd) { | |
1353 | unless (Scalar::Util::openhandle($input->{fh})) { | |
1307 | 1354 | $self->_set_error("Handle in fh option not opened"); |
1308 | 1355 | return; |
1309 | 1356 | } |
1310 | return io_new_fd($fd); | |
1357 | return Imager::IO->new_fh($input->{fh}); | |
1311 | 1358 | } |
1312 | 1359 | elsif ($input->{file}) { |
1313 | 1360 | my $file = IO::File->new($input->{file}, "r"); |
1357 | 1404 | $io = io_new_fd($input->{fd}); |
1358 | 1405 | } |
1359 | 1406 | elsif ($input->{fh}) { |
1360 | my $fd = fileno($input->{fh}); | |
1361 | unless (defined $fd) { | |
1407 | unless (Scalar::Util::openhandle($input->{fh})) { | |
1362 | 1408 | $self->_set_error("Handle in fh option not opened"); |
1363 | 1409 | return; |
1364 | 1410 | } |
1365 | # flush it | |
1366 | my $oldfh = select($input->{fh}); | |
1367 | # flush anything that's buffered, and make sure anything else is flushed | |
1368 | $| = 1; | |
1369 | select($oldfh); | |
1370 | $io = io_new_fd($fd); | |
1411 | $io = Imager::IO->new_fh($input->{fh}); | |
1371 | 1412 | } |
1372 | 1413 | elsif ($input->{file}) { |
1373 | 1414 | my $fh = new IO::File($input->{file},"w+"); |
1770 | 1811 | fax_fine=>1, @_); |
1771 | 1812 | my $rc; |
1772 | 1813 | |
1814 | $self->_valid_image("write") | |
1815 | or return; | |
1816 | ||
1773 | 1817 | $self->_set_opts(\%input, "i_", $self) |
1774 | 1818 | or return undef; |
1775 | ||
1776 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
1777 | 1819 | |
1778 | 1820 | my $type = $input{'type'}; |
1779 | 1821 | if (!$type and $input{file}) { |
1867 | 1909 | return; |
1868 | 1910 | } |
1869 | 1911 | # translate to ImgRaw |
1870 | if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) { | |
1871 | $class->_set_error('Usage: Imager->write_multi({ options }, @images)'); | |
1872 | return 0; | |
1912 | my $index = 1; | |
1913 | for my $img (@images) { | |
1914 | unless ($img->_valid_image("write_multi")) { | |
1915 | $class->_set_error($img->errstr . " (image $index)"); | |
1916 | return; | |
1917 | } | |
1918 | ++$index; | |
1873 | 1919 | } |
1874 | 1920 | $class->_set_opts($opts, "i_", @images) |
1875 | 1921 | or return; |
2003 | 2049 | my $self=shift; |
2004 | 2050 | my %input=@_; |
2005 | 2051 | my %hsh; |
2006 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2052 | ||
2053 | $self->_valid_image("filter") | |
2054 | or return; | |
2007 | 2055 | |
2008 | 2056 | if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; } |
2009 | 2057 | |
2178 | 2226 | return; |
2179 | 2227 | } |
2180 | 2228 | |
2181 | unless ($self->{IMG}) { | |
2182 | $self->_set_error('empty input image'); | |
2183 | return undef; | |
2184 | } | |
2229 | $self->_valid_image("scale") | |
2230 | or return; | |
2185 | 2231 | |
2186 | 2232 | my ($x_scale, $y_scale, $new_width, $new_height) = |
2187 | 2233 | $self->scale_calculate(%opts) |
2235 | 2281 | return; |
2236 | 2282 | } |
2237 | 2283 | |
2238 | unless ($self->{IMG}) { | |
2239 | $self->{ERRSTR} = 'empty input image'; | |
2240 | return undef; | |
2241 | } | |
2284 | $self->_valid_image("scaleX") | |
2285 | or return; | |
2242 | 2286 | |
2243 | 2287 | my $img = Imager->new(); |
2244 | 2288 | |
2275 | 2319 | return; |
2276 | 2320 | } |
2277 | 2321 | |
2278 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2322 | $self->_valid_image("scaleY") | |
2323 | or return; | |
2279 | 2324 | |
2280 | 2325 | my $img = Imager->new(); |
2281 | 2326 | |
2306 | 2351 | |
2307 | 2352 | sub transform { |
2308 | 2353 | my $self=shift; |
2309 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2310 | 2354 | my %opts=@_; |
2311 | 2355 | my (@op,@ropx,@ropy,$iop,$or,@parm,$expr,@xt,@yt,@pt,$numre); |
2312 | 2356 | |
2313 | 2357 | # print Dumper(\%opts); |
2314 | 2358 | # xopcopdes |
2359 | ||
2360 | $self->_valid_image("transform") | |
2361 | or return; | |
2315 | 2362 | |
2316 | 2363 | if ( $opts{'xexpr'} and $opts{'yexpr'} ) { |
2317 | 2364 | if (!$I2P) { |
2404 | 2451 | $opts->{variables} = [ qw(x y) ]; |
2405 | 2452 | my ($width, $height) = @{$opts}{qw(width height)}; |
2406 | 2453 | if (@imgs) { |
2454 | my $index = 1; | |
2455 | for my $img (@imgs) { | |
2456 | unless ($img->_valid_image("transform2")) { | |
2457 | Imager->_set_error($img->errstr . " (input image $index)"); | |
2458 | return; | |
2459 | } | |
2460 | ++$index; | |
2461 | } | |
2462 | ||
2407 | 2463 | $width ||= $imgs[0]->getwidth(); |
2408 | 2464 | $height ||= $imgs[0]->getheight(); |
2409 | 2465 | my $img_num = 1; |
2458 | 2514 | my $self=shift; |
2459 | 2515 | my %opts= @_; |
2460 | 2516 | |
2461 | unless ($self->{IMG}) { | |
2462 | $self->{ERRSTR}='empty input image'; | |
2463 | return undef; | |
2464 | } | |
2465 | unless ($opts{src} && $opts{src}->{IMG}) { | |
2466 | $self->{ERRSTR}='empty input image for src'; | |
2467 | return undef; | |
2517 | $self->_valid_image("rubthrough") | |
2518 | or return; | |
2519 | ||
2520 | unless ($opts{src} && $opts{src}->_valid_image("rubthrough")) { | |
2521 | $self->{ERRSTR} = $opts{src}{ERRSTR} . ' (for src)'; | |
2522 | return; | |
2468 | 2523 | } |
2469 | 2524 | |
2470 | 2525 | %opts = (src_minx => 0, |
2501 | 2556 | @_ |
2502 | 2557 | ); |
2503 | 2558 | |
2504 | unless ($self->{IMG}) { | |
2505 | $self->_set_error("compose: empty input image"); | |
2506 | return; | |
2507 | } | |
2559 | $self->_valid_image("compose") | |
2560 | or return; | |
2508 | 2561 | |
2509 | 2562 | unless ($opts{src}) { |
2510 | 2563 | $self->_set_error("compose: src parameter missing"); |
2511 | 2564 | return; |
2512 | 2565 | } |
2513 | 2566 | |
2514 | unless ($opts{src}{IMG}) { | |
2515 | $self->_set_error("compose: src parameter empty image"); | |
2567 | unless ($opts{src}->_valid_image("compose")) { | |
2568 | $self->_set_error($opts{src}->errstr . " (for src)"); | |
2516 | 2569 | return; |
2517 | 2570 | } |
2518 | 2571 | my $src = $opts{src}; |
2548 | 2601 | my $combine = $self->_combine($opts{combine}, 'normal'); |
2549 | 2602 | |
2550 | 2603 | if ($opts{mask}) { |
2551 | unless ($opts{mask}{IMG}) { | |
2552 | $self->_set_error("compose: mask parameter empty image"); | |
2604 | unless ($opts{mask}->_valid_image("compose")) { | |
2605 | $self->_set_error($opts{mask}->errstr . " (for mask)"); | |
2553 | 2606 | return; |
2554 | 2607 | } |
2555 | 2608 | |
2583 | 2636 | sub flip { |
2584 | 2637 | my $self = shift; |
2585 | 2638 | my %opts = @_; |
2639 | ||
2640 | $self->_valid_image("flip") | |
2641 | or return; | |
2642 | ||
2586 | 2643 | my %xlate = (h=>0, v=>1, hv=>2, vh=>2); |
2587 | 2644 | my $dir; |
2588 | 2645 | return () unless defined $opts{'dir'} and defined $xlate{$opts{'dir'}}; |
2600 | 2657 | warn "rotate() called in void context - rotate() returns the rotated image at $caller[1] line $caller[2]\n"; |
2601 | 2658 | return; |
2602 | 2659 | } |
2660 | ||
2661 | $self->_valid_image("rotate") | |
2662 | or return; | |
2603 | 2663 | |
2604 | 2664 | if (defined $opts{right}) { |
2605 | 2665 | my $degrees = $opts{right}; |
2660 | 2720 | my $self = shift; |
2661 | 2721 | my %opts = @_; |
2662 | 2722 | |
2723 | $self->_valid_image("matrix_transform") | |
2724 | or return; | |
2725 | ||
2663 | 2726 | unless (defined wantarray) { |
2664 | 2727 | my @caller = caller; |
2665 | 2728 | warn "copy() called in void context - copy() returns the copied image at $caller[1] line $caller[2]\n"; |
2708 | 2771 | my $self=shift; |
2709 | 2772 | my $raw = $self->{IMG}; |
2710 | 2773 | |
2711 | unless ($raw) { | |
2712 | $self->{ERRSTR}='empty input image'; | |
2713 | return undef; | |
2714 | } | |
2774 | $self->_valid_image("box") | |
2775 | or return; | |
2715 | 2776 | |
2716 | 2777 | my %opts = @_; |
2717 | 2778 | |
2789 | 2850 | |
2790 | 2851 | sub arc { |
2791 | 2852 | my $self=shift; |
2792 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2853 | ||
2854 | $self->_valid_image("arc") | |
2855 | or return; | |
2856 | ||
2793 | 2857 | my $dflcl= [ 255, 255, 255, 255]; |
2794 | 2858 | my $good = 1; |
2795 | 2859 | my %opts= |
2891 | 2955 | my %opts=(color=>$dflcl, |
2892 | 2956 | endp => 1, |
2893 | 2957 | @_); |
2894 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2958 | ||
2959 | $self->_valid_image("line") | |
2960 | or return; | |
2895 | 2961 | |
2896 | 2962 | unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; } |
2897 | 2963 | unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; } |
2922 | 2988 | my $dflcl=i_color_new(0,0,0,0); |
2923 | 2989 | my %opts=(color=>$dflcl,@_); |
2924 | 2990 | |
2925 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
2991 | $self->_valid_image("polyline") | |
2992 | or return; | |
2926 | 2993 | |
2927 | 2994 | if (exists($opts{points})) { @points=@{$opts{points}}; } |
2928 | 2995 | if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) { |
2961 | 3028 | my $dflcl = i_color_new(0,0,0,0); |
2962 | 3029 | my %opts = (color=>$dflcl, @_); |
2963 | 3030 | |
2964 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
3031 | $self->_valid_image("polygon") | |
3032 | or return; | |
2965 | 3033 | |
2966 | 3034 | if (exists($opts{points})) { |
2967 | 3035 | $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ]; |
3008 | 3076 | my $dflcl=i_color_new(0,0,0,0); |
3009 | 3077 | my %opts=(color=>$dflcl,@_); |
3010 | 3078 | |
3011 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
3079 | $self->_valid_image("polybezier") | |
3080 | or return; | |
3012 | 3081 | |
3013 | 3082 | if (exists $opts{points}) { |
3014 | 3083 | $opts{'x'}=map { $_->[0]; } @{$opts{'points'}}; |
3033 | 3102 | my $self = shift; |
3034 | 3103 | my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ ); |
3035 | 3104 | my $rc; |
3105 | ||
3106 | $self->_valid_image("flood_fill") | |
3107 | or return; | |
3036 | 3108 | |
3037 | 3109 | unless (exists $opts{'x'} && exists $opts{'y'}) { |
3038 | 3110 | $self->{ERRSTR} = "missing seed x and y parameters"; |
3251 | 3323 | my $self = shift; |
3252 | 3324 | my %opts = ( type => '8bit', x=>0, @_); |
3253 | 3325 | |
3254 | $self->_valid_image or return; | |
3326 | $self->_valid_image("getscanline") | |
3327 | or return; | |
3255 | 3328 | |
3256 | 3329 | defined $opts{width} or $opts{width} = $self->getwidth - $opts{x}; |
3257 | 3330 | |
3286 | 3359 | my $self = shift; |
3287 | 3360 | my %opts = ( x=>0, @_); |
3288 | 3361 | |
3289 | $self->_valid_image or return; | |
3362 | $self->_valid_image("setscanline") | |
3363 | or return; | |
3290 | 3364 | |
3291 | 3365 | unless (defined $opts{'y'}) { |
3292 | 3366 | $self->_set_error("missing y parameter"); |
3346 | 3420 | sub getsamples { |
3347 | 3421 | my $self = shift; |
3348 | 3422 | my %opts = ( type => '8bit', x=>0, offset => 0, @_); |
3423 | ||
3424 | $self->_valid_image("getsamples") | |
3425 | or return; | |
3349 | 3426 | |
3350 | 3427 | defined $opts{width} or $opts{width} = $self->getwidth - $opts{x}; |
3351 | 3428 | |
3417 | 3494 | sub setsamples { |
3418 | 3495 | my $self = shift; |
3419 | 3496 | |
3420 | unless ($self->{IMG}) { | |
3421 | $self->_set_error('setsamples: empty input image'); | |
3422 | return; | |
3423 | } | |
3497 | $self->_valid_image("setsamples") | |
3498 | or return; | |
3424 | 3499 | |
3425 | 3500 | my %opts = ( x => 0, offset => 0 ); |
3426 | 3501 | my $data_index; |
3501 | 3576 | sub convert { |
3502 | 3577 | my ($self, %opts) = @_; |
3503 | 3578 | my $matrix; |
3579 | ||
3580 | $self->_valid_image("convert") | |
3581 | or return; | |
3504 | 3582 | |
3505 | 3583 | unless (defined wantarray) { |
3506 | 3584 | my @caller = caller; |
3620 | 3698 | $class->_set_error("src must contain image objects"); |
3621 | 3699 | return; |
3622 | 3700 | } |
3623 | unless ($img->{IMG}) { | |
3624 | $class->_set_error("empty input image"); | |
3701 | unless ($img->_valid_image("combine")) { | |
3702 | $Imager::ERRSTR = $img->{ERRSTR} . " (src->[$index])"; | |
3625 | 3703 | return; |
3626 | 3704 | } |
3627 | 3705 | push @imgs, $img->{IMG}; |
3650 | 3728 | sub map { |
3651 | 3729 | my ($self, %opts) = @_; |
3652 | 3730 | my @chlist = qw( red green blue alpha ); |
3731 | ||
3732 | $self->_valid_image("map") | |
3733 | or return; | |
3653 | 3734 | |
3654 | 3735 | if (!exists($opts{'maps'})) { |
3655 | 3736 | # make maps from channel maps |
3671 | 3752 | sub difference { |
3672 | 3753 | my ($self, %opts) = @_; |
3673 | 3754 | |
3755 | $self->_valid_image("difference") | |
3756 | or return; | |
3757 | ||
3674 | 3758 | defined $opts{mindist} or $opts{mindist} = 0; |
3675 | 3759 | |
3676 | 3760 | defined $opts{other} |
3677 | 3761 | or return $self->_set_error("No 'other' parameter supplied"); |
3678 | defined $opts{other}{IMG} | |
3679 | or return $self->_set_error("No image data in 'other' image"); | |
3680 | ||
3681 | $self->{IMG} | |
3682 | or return $self->_set_error("No image data"); | |
3762 | unless ($opts{other}->_valid_image("difference")) { | |
3763 | $self->_set_error($opts{other}->errstr . " (other image)"); | |
3764 | return; | |
3765 | } | |
3683 | 3766 | |
3684 | 3767 | my $result = Imager->new; |
3685 | 3768 | $result->{IMG} = i_diff_image($self->{IMG}, $opts{other}{IMG}, |
3703 | 3786 | sub getwidth { |
3704 | 3787 | my $self = shift; |
3705 | 3788 | |
3706 | if (my $raw = $self->{IMG}) { | |
3707 | return i_img_get_width($raw); | |
3708 | } | |
3709 | else { | |
3710 | $self->{ERRSTR} = 'image is empty'; return undef; | |
3711 | } | |
3789 | $self->_valid_image("getwidth") | |
3790 | or return; | |
3791 | ||
3792 | return i_img_get_width($self->{IMG}); | |
3712 | 3793 | } |
3713 | 3794 | |
3714 | 3795 | # Get the height of an image |
3716 | 3797 | sub getheight { |
3717 | 3798 | my $self = shift; |
3718 | 3799 | |
3719 | if (my $raw = $self->{IMG}) { | |
3720 | return i_img_get_height($raw); | |
3721 | } | |
3722 | else { | |
3723 | $self->{ERRSTR} = 'image is empty'; return undef; | |
3724 | } | |
3800 | $self->_valid_image("getheight") | |
3801 | or return; | |
3802 | ||
3803 | return i_img_get_height($self->{IMG}); | |
3725 | 3804 | } |
3726 | 3805 | |
3727 | 3806 | # Get number of channels in an image |
3728 | 3807 | |
3729 | 3808 | sub getchannels { |
3730 | 3809 | my $self = shift; |
3731 | if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; } | |
3810 | ||
3811 | $self->_valid_image("getchannels") | |
3812 | or return; | |
3813 | ||
3732 | 3814 | return i_img_getchannels($self->{IMG}); |
3733 | 3815 | } |
3734 | 3816 | |
3736 | 3818 | |
3737 | 3819 | sub getmask { |
3738 | 3820 | my $self = shift; |
3739 | if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; } | |
3821 | ||
3822 | $self->_valid_image("getmask") | |
3823 | or return; | |
3824 | ||
3740 | 3825 | return i_img_getmask($self->{IMG}); |
3741 | 3826 | } |
3742 | 3827 | |
3745 | 3830 | sub setmask { |
3746 | 3831 | my $self = shift; |
3747 | 3832 | my %opts = @_; |
3748 | if (!defined($self->{IMG})) { | |
3749 | $self->{ERRSTR} = 'image is empty'; | |
3750 | return undef; | |
3751 | } | |
3833 | ||
3834 | $self->_valid_image("setmask") | |
3835 | or return; | |
3836 | ||
3752 | 3837 | unless (defined $opts{mask}) { |
3753 | 3838 | $self->_set_error("mask parameter required"); |
3754 | 3839 | return; |
3755 | 3840 | } |
3841 | ||
3756 | 3842 | i_img_setmask( $self->{IMG} , $opts{mask} ); |
3757 | 3843 | |
3758 | 3844 | 1; |
3763 | 3849 | sub getcolorcount { |
3764 | 3850 | my $self=shift; |
3765 | 3851 | my %opts=('maxcolors'=>2**30,@_); |
3766 | if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; } | |
3852 | ||
3853 | $self->_valid_image("getcolorcount") | |
3854 | or return; | |
3855 | ||
3767 | 3856 | my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'}); |
3768 | 3857 | return ($rc==-1? undef : $rc); |
3769 | 3858 | } |
3772 | 3861 | # values are the number of pixels in this colour. |
3773 | 3862 | sub getcolorusagehash { |
3774 | 3863 | my $self = shift; |
3775 | ||
3864 | ||
3865 | $self->_valid_image("getcolorusagehash") | |
3866 | or return; | |
3867 | ||
3776 | 3868 | my %opts = ( maxcolors => 2**30, @_ ); |
3777 | 3869 | my $max_colors = $opts{maxcolors}; |
3778 | 3870 | unless (defined $max_colors && $max_colors > 0) { |
3779 | 3871 | $self->_set_error('maxcolors must be a positive integer'); |
3780 | return; | |
3781 | } | |
3782 | ||
3783 | unless (defined $self->{IMG}) { | |
3784 | $self->_set_error('empty input image'); | |
3785 | 3872 | return; |
3786 | 3873 | } |
3787 | 3874 | |
3808 | 3895 | sub getcolorusage { |
3809 | 3896 | my $self = shift; |
3810 | 3897 | |
3898 | $self->_valid_image("getcolorusage") | |
3899 | or return; | |
3900 | ||
3811 | 3901 | my %opts = ( maxcolors => 2**30, @_ ); |
3812 | 3902 | my $max_colors = $opts{maxcolors}; |
3813 | 3903 | unless (defined $max_colors && $max_colors > 0) { |
3815 | 3905 | return; |
3816 | 3906 | } |
3817 | 3907 | |
3818 | unless (defined $self->{IMG}) { | |
3819 | $self->_set_error('empty input image'); | |
3820 | return undef; | |
3821 | } | |
3822 | ||
3823 | 3908 | return i_get_anonymous_color_histo($self->{IMG}, $max_colors); |
3824 | 3909 | } |
3825 | 3910 | |
3827 | 3912 | |
3828 | 3913 | sub string { |
3829 | 3914 | my $self = shift; |
3830 | unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; } | |
3915 | ||
3916 | $self->_valid_image("string") | |
3917 | or return; | |
3831 | 3918 | |
3832 | 3919 | my %input=('x'=>0, 'y'=>0, @_); |
3833 | 3920 | defined($input{string}) or $input{string} = $input{text}; |
3854 | 3941 | |
3855 | 3942 | my $img; |
3856 | 3943 | if (ref $self) { |
3857 | unless ($self->{IMG}) { | |
3858 | $self->{ERRSTR}='empty input image'; | |
3859 | return; | |
3860 | } | |
3944 | $self->_valid_image("align_string") | |
3945 | or return; | |
3946 | ||
3861 | 3947 | $img = $self; |
3862 | 3948 | } |
3863 | 3949 | else { |
4122 | 4208 | eval { require Imager::Font::T1 }; |
4123 | 4209 | } |
4124 | 4210 | |
4211 | package Imager::IO; | |
4212 | use IO::Seekable; | |
4213 | ||
4214 | sub new_fh { | |
4215 | my ($class, $fh) = @_; | |
4216 | ||
4217 | if (tied(*$fh)) { | |
4218 | return $class->new_cb | |
4219 | ( | |
4220 | sub { | |
4221 | local $\; | |
4222 | ||
4223 | return print $fh $_[0]; | |
4224 | }, | |
4225 | sub { | |
4226 | my $tmp; | |
4227 | my $count = CORE::read $fh, $tmp, $_[1]; | |
4228 | defined $count | |
4229 | or return undef; | |
4230 | $count | |
4231 | or return ""; | |
4232 | return $tmp; | |
4233 | }, | |
4234 | sub { | |
4235 | if ($_[1] != SEEK_CUR || $_[0] != 0) { | |
4236 | unless (CORE::seek $fh, $_[0], $_[1]) { | |
4237 | return -1; | |
4238 | } | |
4239 | } | |
4240 | ||
4241 | return tell $fh; | |
4242 | }, | |
4243 | undef, | |
4244 | ); | |
4245 | } | |
4246 | else { | |
4247 | return $class->_new_perlio($fh); | |
4248 | } | |
4249 | } | |
4250 | ||
4125 | 4251 | # backward compatibility for %formats |
4126 | 4252 | package Imager::FORMATS; |
4127 | 4253 | use strict; |
4296 | 4422 | |
4297 | 4423 | =item * |
4298 | 4424 | |
4425 | L<Imager::Install> - installation notes for Imager. | |
4426 | ||
4427 | =item * | |
4428 | ||
4299 | 4429 | L<Imager::Tutorial> - a brief introduction to Imager. |
4300 | 4430 | |
4301 | 4431 | =item * |
4362 | 4492 | |
4363 | 4493 | =item * |
4364 | 4494 | |
4495 | L<Imager::IO> - Imager I/O abstraction. | |
4496 | ||
4497 | =item * | |
4498 | ||
4365 | 4499 | L<Imager::API> - using Imager's C API |
4366 | 4500 | |
4367 | 4501 | =item * |
4379 | 4513 | =item * |
4380 | 4514 | |
4381 | 4515 | L<Imager::Security> - brief security notes. |
4516 | ||
4517 | =item * | |
4518 | ||
4519 | L<Imager::Threads> - brief information on working with threads. | |
4382 | 4520 | |
4383 | 4521 | =back |
4384 | 4522 | |
4821 | 4959 | |
4822 | 4960 | text, measuring - L<Imager::Font/bounding_box()>, L<Imager::Font::BBox> |
4823 | 4961 | |
4962 | threads - L<Imager::Threads> | |
4963 | ||
4824 | 4964 | tiles, color - L<Imager::Filters/mosaic> |
4825 | 4965 | |
4826 | 4966 | transparent images - L<Imager::ImageTypes>, |
4833 | 4973 | watermark - L<Imager::Filters/watermark> |
4834 | 4974 | |
4835 | 4975 | writing an image to a file - L<Imager::Files> |
4836 | ||
4837 | =head1 THREADS | |
4838 | ||
4839 | Imager doesn't support perl threads. | |
4840 | ||
4841 | Imager has limited code to prevent double frees if you create images, | |
4842 | colors etc, and then create a thread, but has no code to prevent two | |
4843 | threads entering Imager's error handling code, and none is likely to | |
4844 | be added. | |
4845 | 4976 | |
4846 | 4977 | =head1 SUPPORT |
4847 | 4978 | |
4968 | 5099 | |
4969 | 5100 | Other perl imaging modules include: |
4970 | 5101 | |
4971 | L<GD>(3), L<Image::Magick>(3), L<Graphics::Magick>(3), | |
5102 | L<GD>(3), L<Image::Magick>(3), | |
5103 | L<Graphics::Magick|http://www.graphicsmagick.org/perl.html>(3), | |
4972 | 5104 | L<Prima::Image>, L<IPA>. |
4973 | 5105 | |
4974 | 5106 | For manipulating image metadata see L<Image::ExifTool>. |
20 | 20 | #include "regmach.h" |
21 | 21 | #include "imextdef.h" |
22 | 22 | #include "imextpltypes.h" |
23 | #include "imperlio.h" | |
23 | 24 | #include <float.h> |
24 | 25 | |
25 | 26 | #if i_int_hlines_testing() |
27 | 28 | #endif |
28 | 29 | |
29 | 30 | #include "imperl.h" |
31 | ||
32 | /* | |
33 | ||
34 | Context object management | |
35 | ||
36 | */ | |
37 | ||
38 | typedef im_context_t Imager__Context; | |
39 | ||
40 | #define im_context_DESTROY(ctx) im_context_refdec((ctx), "DESTROY") | |
41 | ||
42 | #ifdef PERL_IMPLICIT_CONTEXT | |
43 | ||
44 | #define MY_CXT_KEY "Imager::_context" XS_VERSION | |
45 | ||
46 | typedef struct { | |
47 | im_context_t ctx; | |
48 | } my_cxt_t; | |
49 | ||
50 | START_MY_CXT | |
51 | ||
52 | im_context_t fallback_context; | |
53 | ||
54 | static void | |
55 | start_context(pTHX) { | |
56 | dMY_CXT; | |
57 | MY_CXT.ctx = im_context_new(); | |
58 | sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx); | |
59 | ||
60 | /* Ideally we'd free this reference, but the error message memory | |
61 | was never released on exit, so the associated memory here is reasonable | |
62 | to keep. | |
63 | With logging enabled we always need at least one context, since | |
64 | objects may be released fairly late and attempt to get the log file. | |
65 | */ | |
66 | im_context_refinc(MY_CXT.ctx, "start_context"); | |
67 | fallback_context = MY_CXT.ctx; | |
68 | } | |
69 | ||
70 | static im_context_t | |
71 | perl_get_context(void) { | |
72 | dTHX; | |
73 | dMY_CXT; | |
74 | ||
75 | return MY_CXT.ctx ? MY_CXT.ctx : fallback_context; | |
76 | } | |
77 | ||
78 | #else | |
79 | ||
80 | static im_context_t perl_context; | |
81 | ||
82 | static void | |
83 | start_context(pTHX) { | |
84 | perl_context = im_context_new(); | |
85 | im_context_refinc(perl_context, "start_context"); | |
86 | } | |
87 | ||
88 | static im_context_t | |
89 | perl_get_context(void) { | |
90 | return perl_context; | |
91 | } | |
92 | ||
93 | #endif | |
30 | 94 | |
31 | 95 | /* used to represent channel lists parameters */ |
32 | 96 | typedef struct i_channel_list_tag { |
726 | 790 | } |
727 | 791 | } |
728 | 792 | |
729 | ||
730 | 793 | /* I don't think ICLF_* names belong at the C interface |
731 | 794 | this makes the XS code think we have them, to let us avoid |
732 | 795 | putting function bodies in the XS code |
1102 | 1165 | OUTPUT: |
1103 | 1166 | RETVAL |
1104 | 1167 | |
1168 | Imager::IO | |
1169 | io__new_perlio(class, io) | |
1170 | PerlIO *io | |
1171 | CODE: | |
1172 | RETVAL = im_io_new_perlio(aTHX_ io); | |
1173 | OUTPUT: | |
1174 | RETVAL | |
1175 | ||
1105 | 1176 | SV * |
1106 | 1177 | io_slurp(class, ig) |
1107 | 1178 | Imager::IO ig |
1126 | 1197 | void *data; |
1127 | 1198 | STRLEN size; |
1128 | 1199 | CODE: |
1129 | #ifdef SvUTF8 | |
1130 | if (SvUTF8(data_sv)) { | |
1131 | data_sv = sv_2mortal(newSVsv(data_sv)); | |
1132 | /* yes, we want this to croak() if the SV can't be downgraded */ | |
1133 | sv_utf8_downgrade(data_sv, FALSE); | |
1134 | } | |
1135 | #endif | |
1136 | data = SvPV(data_sv, size); | |
1200 | data = SvPVbyte(data_sv, size); | |
1137 | 1201 | RETVAL = i_io_raw_write(ig, data, size); |
1138 | 1202 | OUTPUT: |
1139 | 1203 | RETVAL |
1358 | 1422 | void *data; |
1359 | 1423 | STRLEN size; |
1360 | 1424 | CODE: |
1361 | #ifdef SvUTF8 | |
1362 | if (SvUTF8(data_sv)) { | |
1363 | data_sv = sv_2mortal(newSVsv(data_sv)); | |
1364 | /* yes, we want this to croak() if the SV can't be downgraded */ | |
1365 | sv_utf8_downgrade(data_sv, FALSE); | |
1366 | } | |
1367 | #endif | |
1368 | data = SvPV(data_sv, size); | |
1425 | data = SvPVbyte(data_sv, size); | |
1369 | 1426 | RETVAL = i_io_write(ig, data, size); |
1370 | 1427 | OUTPUT: |
1371 | 1428 | RETVAL |
2059 | 2116 | RETVAL |
2060 | 2117 | |
2061 | 2118 | |
2062 | void | |
2119 | undef_int | |
2063 | 2120 | i_map(im, pmaps) |
2064 | 2121 | Imager::ImgRaw im |
2065 | 2122 | PREINIT: |
2097 | 2154 | } |
2098 | 2155 | i_map(im, maps, mask); |
2099 | 2156 | myfree(maps); |
2157 | RETVAL = 1; | |
2158 | OUTPUT: | |
2159 | RETVAL | |
2100 | 2160 | |
2101 | 2161 | |
2102 | 2162 | |
2160 | 2220 | |
2161 | 2221 | |
2162 | 2222 | undef_int |
2163 | i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8,align=1) | |
2223 | i_tt_text(handle,im,xb,yb,cl,points,str_sv,smooth,utf8,align=1) | |
2164 | 2224 | Imager::Font::TT handle |
2165 | 2225 | Imager::ImgRaw im |
2166 | 2226 | i_img_dim xb |
2175 | 2235 | char *str; |
2176 | 2236 | STRLEN len; |
2177 | 2237 | CODE: |
2238 | str = SvPV(str_sv, len); | |
2178 | 2239 | #ifdef SvUTF8 |
2179 | 2240 | if (SvUTF8(str_sv)) |
2180 | 2241 | utf8 = 1; |
2181 | 2242 | #endif |
2182 | str = SvPV(str_sv, len); | |
2183 | 2243 | RETVAL = i_tt_text(handle, im, xb, yb, cl, points, str, |
2184 | 2244 | len, smooth, utf8, align); |
2185 | 2245 | OUTPUT: |
2187 | 2247 | |
2188 | 2248 | |
2189 | 2249 | undef_int |
2190 | i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8,align=1) | |
2250 | i_tt_cp(handle,im,xb,yb,channel,points,str_sv,smooth,utf8,align=1) | |
2191 | 2251 | Imager::Font::TT handle |
2192 | 2252 | Imager::ImgRaw im |
2193 | 2253 | i_img_dim xb |
2202 | 2262 | char *str; |
2203 | 2263 | STRLEN len; |
2204 | 2264 | CODE: |
2265 | str = SvPV(str_sv, len); | |
2205 | 2266 | #ifdef SvUTF8 |
2206 | 2267 | if (SvUTF8(str_sv)) |
2207 | 2268 | utf8 = 1; |
2208 | 2269 | #endif |
2209 | str = SvPV(str_sv, len); | |
2210 | 2270 | RETVAL = i_tt_cp(handle, im, xb, yb, channel, points, str, len, |
2211 | 2271 | smooth, utf8, align); |
2212 | 2272 | OUTPUT: |
2214 | 2274 | |
2215 | 2275 | |
2216 | 2276 | void |
2217 | i_tt_bbox(handle,point,str_sv,len_ignored, utf8) | |
2277 | i_tt_bbox(handle,point,str_sv,utf8) | |
2218 | 2278 | Imager::Font::TT handle |
2219 | 2279 | double point |
2220 | 2280 | SV* str_sv |
2226 | 2286 | STRLEN len; |
2227 | 2287 | int i; |
2228 | 2288 | PPCODE: |
2289 | str = SvPV(str_sv, len); | |
2229 | 2290 | #ifdef SvUTF8 |
2230 | 2291 | if (SvUTF8(ST(2))) |
2231 | 2292 | utf8 = 1; |
2232 | 2293 | #endif |
2233 | str = SvPV(str_sv, len); | |
2234 | 2294 | if ((rc=i_tt_bbox(handle,point,str,len,cords, utf8))) { |
2235 | 2295 | EXTEND(SP, rc); |
2236 | 2296 | for (i = 0; i < rc; ++i) { |
2250 | 2310 | size_t count; |
2251 | 2311 | size_t i; |
2252 | 2312 | PPCODE: |
2313 | i_clear_error(); | |
2314 | text = SvPV(text_sv, len); | |
2253 | 2315 | #ifdef SvUTF8 |
2254 | 2316 | if (SvUTF8(text_sv)) |
2255 | 2317 | utf8 = 1; |
2256 | 2318 | #endif |
2257 | text = SvPV(text_sv, len); | |
2258 | 2319 | work = mymalloc(len); |
2259 | 2320 | count = i_tt_has_chars(handle, text, len, utf8, work); |
2260 | 2321 | if (GIMME_V == G_ARRAY) { |
2297 | 2358 | size_t len; |
2298 | 2359 | size_t outsize; |
2299 | 2360 | char name[255]; |
2361 | SSize_t count = 0; | |
2300 | 2362 | PPCODE: |
2363 | i_clear_error(); | |
2364 | text = SvPV(text_sv, work_len); | |
2301 | 2365 | #ifdef SvUTF8 |
2302 | 2366 | if (SvUTF8(text_sv)) |
2303 | 2367 | utf8 = 1; |
2304 | 2368 | #endif |
2305 | text = SvPV(text_sv, work_len); | |
2306 | 2369 | len = work_len; |
2307 | 2370 | while (len) { |
2308 | 2371 | unsigned long ch; |
2310 | 2373 | ch = i_utf8_advance(&text, &len); |
2311 | 2374 | if (ch == ~0UL) { |
2312 | 2375 | i_push_error(0, "invalid UTF8 character"); |
2313 | break; | |
2376 | XSRETURN_EMPTY; | |
2314 | 2377 | } |
2315 | 2378 | } |
2316 | 2379 | else { |
2317 | 2380 | ch = *text++; |
2318 | 2381 | --len; |
2319 | 2382 | } |
2320 | EXTEND(SP, 1); | |
2383 | EXTEND(SP, count+1); | |
2321 | 2384 | if ((outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) != 0) { |
2322 | PUSHs(sv_2mortal(newSVpv(name, 0))); | |
2385 | ST(count) = sv_2mortal(newSVpv(name, 0)); | |
2323 | 2386 | } |
2324 | 2387 | else { |
2325 | PUSHs(&PL_sv_undef); | |
2326 | } | |
2327 | } | |
2388 | ST(count) = &PL_sv_undef; | |
2389 | } | |
2390 | ++count; | |
2391 | } | |
2392 | XSRETURN(count); | |
2328 | 2393 | |
2329 | 2394 | #endif |
2330 | 2395 | |
3992 | 4057 | |
3993 | 4058 | #endif |
3994 | 4059 | |
4060 | MODULE = Imager PACKAGE = Imager::Context PREFIX=im_context_ | |
4061 | ||
4062 | void | |
4063 | im_context_DESTROY(ctx) | |
4064 | Imager::Context ctx | |
4065 | ||
4066 | #ifdef PERL_IMPLICIT_CONTEXT | |
4067 | ||
4068 | void | |
4069 | im_context_CLONE(...) | |
4070 | CODE: | |
4071 | MY_CXT_CLONE; | |
4072 | (void)items; | |
4073 | /* the following sv_setref_pv() will free this inc */ | |
4074 | im_context_refinc(MY_CXT.ctx, "CLONE"); | |
4075 | MY_CXT.ctx = im_context_clone(MY_CXT.ctx, "CLONE"); | |
4076 | sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx); | |
4077 | ||
4078 | #endif | |
4079 | ||
3995 | 4080 | BOOT: |
3996 | 4081 | PERL_SET_GLOBAL_CALLBACKS; |
3997 | 4082 | PERL_PL_SET_GLOBAL_CALLBACKS; |
4083 | #ifdef PERL_IMPLICIT_CONTEXT | |
4084 | { | |
4085 | MY_CXT_INIT; | |
4086 | (void)MY_CXT; | |
4087 | } | |
4088 | #endif | |
4089 | start_context(aTHX); | |
4090 | im_get_context = perl_get_context; | |
4091 | #ifdef HAVE_LIBTT | |
4092 | i_tt_start(); | |
4093 | #endif |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.85"; | |
6 | $VERSION = "0.87"; | |
7 | 7 | |
8 | 8 | require XSLoader; |
9 | 9 | XSLoader::load('Imager::File::JPEG', $VERSION); |
11 | 11 | DEFINE_IMAGER_CALLBACKS; |
12 | 12 | |
13 | 13 | MODULE = Imager::File::JPEG PACKAGE = Imager::File::JPEG |
14 | ||
15 | const char * | |
16 | i_libjpeg_version() | |
14 | 17 | |
15 | 18 | undef_int |
16 | 19 | i_writejpeg_wiol(im, ig, qfactor) |
0 | 0 | Imager::File::JPEG provides JPEG file format support for Imager. |
1 | 1 | |
2 | It requires libjpeg to be installed. | |
2 | It requires libjpeg to be installed, any version from jpeg6b or later. | |
3 | libjpeg-turbo is also supported. | |
4 | ||
5 | For Linux distributions this typically requires installation of the | |
6 | associated -dev or -devel package. | |
7 | ||
8 | See Imager::Install for more information. | |
3 | 9 | |
4 | 10 | This is currently shipped as part of Imager, but Imager may install |
5 | 11 | with out installing Imager::File::JPEG, so if you need JPEG support, |
919 | 919 | |
920 | 920 | /* rough check count + 1 entry + next offset */ |
921 | 921 | if (offset + (2+12+4) > tiff->size) { |
922 | mm_log((2, "offset %uld beyond end off Exif block")); | |
922 | mm_log((2, "offset %lu beyond end off Exif block", offset)); | |
923 | 923 | return 0; |
924 | 924 | } |
925 | 925 | |
928 | 928 | /* check we can fit the whole thing */ |
929 | 929 | ifd_size = 2 + count * 12 + 4; /* count + count entries + next offset */ |
930 | 930 | if (offset + ifd_size > tiff->size) { |
931 | mm_log((2, "offset %uld beyond end off Exif block")); | |
931 | mm_log((2, "offset %lu beyond end off Exif block", offset)); | |
932 | 932 | return 0; |
933 | 933 | } |
934 | 934 | |
1419 | 1419 | static unsigned |
1420 | 1420 | tiff_get16(imtiff *tiff, unsigned long offset) { |
1421 | 1421 | if (offset + 2 > tiff->size) { |
1422 | mm_log((3, "attempt to get16 at %uld in %uld image", offset, tiff->size)); | |
1422 | mm_log((3, "attempt to get16 at %lu in %lu image", offset, | |
1423 | (unsigned long)tiff->size)); | |
1423 | 1424 | return 0; |
1424 | 1425 | } |
1425 | 1426 | |
1440 | 1441 | static unsigned |
1441 | 1442 | tiff_get32(imtiff *tiff, unsigned long offset) { |
1442 | 1443 | if (offset + 4 > tiff->size) { |
1443 | mm_log((3, "attempt to get16 at %uld in %uld image", offset, tiff->size)); | |
1444 | mm_log((3, "attempt to get16 at %lu in %lu image", offset, | |
1445 | (unsigned long)tiff->size)); | |
1444 | 1446 | return 0; |
1445 | 1447 | } |
1446 | 1448 | |
1491 | 1493 | int result; |
1492 | 1494 | |
1493 | 1495 | if (offset + 2 > tiff->size) { |
1494 | mm_log((3, "attempt to get16 at %uld in %uld image", offset, tiff->size)); | |
1496 | mm_log((3, "attempt to get16 at %lu in %lu image", offset, | |
1497 | (unsigned long)tiff->size)); | |
1495 | 1498 | return 0; |
1496 | 1499 | } |
1497 | 1500 | |
1519 | 1522 | unsigned work; |
1520 | 1523 | |
1521 | 1524 | if (offset + 4 > tiff->size) { |
1522 | mm_log((3, "attempt to get16 at %uld in %uld image", offset, tiff->size)); | |
1525 | mm_log((3, "attempt to get16 at %lu in %lu image", offset, | |
1526 | (unsigned long)tiff->size)); | |
1523 | 1527 | return 0; |
1524 | 1528 | } |
1525 | 1529 | |
1549 | 1553 | tiff_get_rat(imtiff *tiff, unsigned long offset) { |
1550 | 1554 | unsigned long numer, denom; |
1551 | 1555 | if (offset + 8 > tiff->size) { |
1552 | mm_log((3, "attempt to get_rat at %lu in %lu image", offset, tiff->size)); | |
1556 | mm_log((3, "attempt to get_rat at %lu in %lu image", offset, | |
1557 | (unsigned long)tiff->size)); | |
1553 | 1558 | return 0; |
1554 | 1559 | } |
1555 | 1560 | |
1575 | 1580 | tiff_get_rats(imtiff *tiff, unsigned long offset) { |
1576 | 1581 | long numer, denom; |
1577 | 1582 | if (offset + 8 > tiff->size) { |
1578 | mm_log((3, "attempt to get_rat at %lu in %lu image", offset, tiff->size)); | |
1583 | mm_log((3, "attempt to get_rat at %lu in %lu image", offset, | |
1584 | (unsigned long)tiff->size)); | |
1579 | 1585 | return 0; |
1580 | 1586 | } |
1581 | 1587 |
40 | 40 | #define JPGS 16384 |
41 | 41 | |
42 | 42 | #define JPEG_DIM_MAX JPEG_MAX_DIMENSION |
43 | ||
44 | #define _STRINGIFY(x) #x | |
45 | #define STRINGIFY(x) _STRINGIFY(x) | |
43 | 46 | |
44 | 47 | static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI}; |
45 | 48 | |
214 | 217 | |
215 | 218 | if (rc != JPGS) { /* XXX: Should raise some jpeg error */ |
216 | 219 | myfree(dest->buffer); |
217 | mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc)); | |
220 | mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, (int)rc)); | |
218 | 221 | ERREXIT(cinfo, JERR_FILE_WRITE); |
219 | 222 | } |
220 | 223 | dest->pub.free_in_buffer = JPGS; |
335 | 338 | } |
336 | 339 | |
337 | 340 | typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width); |
341 | ||
342 | static const char version_string[] = | |
343 | #ifdef LIBJPEG_TURBO_VERSION | |
344 | "libjpeg-turbo " STRINGIFY(LIBJPEG_TURBO_VERSION) " api " STRINGIFY(JPEG_LIB_VERSION) | |
345 | #else | |
346 | "libjpeg " STRINGIFY(JPEG_LIB_VERSION) | |
347 | #endif | |
348 | ; | |
349 | ||
350 | /* | |
351 | =item i_libjpeg_version() | |
352 | ||
353 | =cut | |
354 | */ | |
355 | ||
356 | const char * | |
357 | i_libjpeg_version(void) { | |
358 | return version_string; | |
359 | } | |
338 | 360 | |
339 | 361 | /* |
340 | 362 | =item i_readjpeg_wiol(data, length, iptc_itext, itlength) |
524 | 546 | |
525 | 547 | i_tags_set(&im->tags, "i_format", "jpeg", 4); |
526 | 548 | |
527 | mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im)); | |
549 | mm_log((1,"i_readjpeg_wiol -> (%p)\n",im)); | |
528 | 550 | return im; |
529 | 551 | } |
530 | 552 |
8 | 8 | undef_int |
9 | 9 | i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor); |
10 | 10 | |
11 | extern const char * | |
12 | i_libjpeg_version(void); | |
13 | ||
11 | 14 | #endif |
11 | 11 | or plan skip_all => "no jpeg support"; |
12 | 12 | |
13 | 13 | plan tests => 103; |
14 | ||
15 | print STDERR "libjpeg version: ", Imager::File::JPEG::i_libjpeg_version(), "\n"; | |
14 | 16 | |
15 | 17 | my $green=i_color_new(0,255,0,255); |
16 | 18 | my $blue=i_color_new(0,0,255,255); |
5 | 5 | color.c Color translation and handling |
6 | 6 | combine.im Channel combine |
7 | 7 | compose.im |
8 | context.c | |
8 | 9 | conv.im |
9 | 10 | convert.im |
10 | 11 | CountColor/CountColor.pm sample XS access to API |
47 | 48 | Flines/Makefile.PL |
48 | 49 | Flines/t/t00flines.t |
49 | 50 | flip.im |
50 | font.c | |
51 | 51 | fontfiles/dodge.ttf |
52 | 52 | fontfiles/ExistenceTest.ttf generated using pfaedit |
53 | 53 | fontfiles/ImUgly.ttf |
54 | 54 | fontfiles/NameTest.ttf test glyph_names() - see FT2/t/t10ft2.t |
55 | fontft1.c | |
55 | 56 | FT2/fontfiles/dodge.ttf |
56 | 57 | FT2/fontfiles/ExistenceTest.afm |
57 | 58 | FT2/fontfiles/ExistenceTest.pfb |
65 | 66 | FT2/Makefile.PL |
66 | 67 | FT2/README |
67 | 68 | FT2/t/t10ft2.t |
69 | FT2/t/t20thread.t | |
70 | FT2/t/t90std.t Standard font tests for FT2 | |
68 | 71 | FT2/typemap |
69 | 72 | gaussian.im |
70 | 73 | GIF/GIF.pm |
141 | 144 | imio.h |
142 | 145 | immacros.h |
143 | 146 | imperl.h |
147 | imperlio.h | |
144 | 148 | imrender.h Buffer rending engine function declarations |
145 | 149 | inc/Devel/CheckLib.pm David Cantrell's Devel::CheckLib |
146 | 150 | io.c |
180 | 184 | lib/Imager/Font/BBox.pm |
181 | 185 | lib/Imager/Font/FreeType2.pm |
182 | 186 | lib/Imager/Font/Image.pm |
187 | lib/Imager/Font/Test.pm Font for testing (outputs boxes only) | |
183 | 188 | lib/Imager/Font/Truetype.pm |
184 | 189 | lib/Imager/Font/Type1.pm Compatibility wrapper for Imager::Font::T1 |
185 | 190 | lib/Imager/Font/Wrap.pm |
187 | 192 | lib/Imager/Handy.pod |
188 | 193 | lib/Imager/ImageTypes.pod |
189 | 194 | lib/Imager/Inline.pod Using Imager with Inline::C |
195 | lib/Imager/Install.pod Installation notes | |
190 | 196 | lib/Imager/interface.pod |
191 | 197 | lib/Imager/IO.pod Document Imager::IO objects |
192 | 198 | lib/Imager/LargeSamples.pod Track large sample support |
197 | 203 | lib/Imager/Regops.pm |
198 | 204 | lib/Imager/Security.pod |
199 | 205 | lib/Imager/Test.pm |
206 | lib/Imager/Threads.pod | |
200 | 207 | lib/Imager/Transform.pm |
201 | 208 | lib/Imager/Transformations.pod |
202 | 209 | lib/Imager/Tutorial.pod |
213 | 220 | MANIFEST.SKIP |
214 | 221 | map.c |
215 | 222 | maskimg.c |
223 | mutexnull.c | |
224 | mutexpthr.c | |
225 | mutexwin.c | |
216 | 226 | palimg.c |
217 | 227 | paste.im |
228 | perlio.c | |
218 | 229 | plug.h |
219 | 230 | PNG/impng.c |
220 | 231 | PNG/impng.h |
308 | 319 | t/t03test.t Test Imager::Test |
309 | 320 | t/t05error.t |
310 | 321 | t/t07iolayer.t |
322 | t/t080log.t | |
323 | t/t081error.t | |
324 | t/t082limit.t | |
311 | 325 | t/t1000files.t Format independent file tests |
312 | 326 | t/t1000lib/Imager/File/BAD.pm Test failing to load a file handler |
313 | 327 | t/t101nojpeg.t Test handling when jpeg not available |
326 | 340 | t/t31font.t General font interface tests |
327 | 341 | t/t35ttfont.t |
328 | 342 | t/t36oofont.t |
343 | t/t37std.t Standard font tests for TT | |
329 | 344 | t/t40scale.t |
330 | 345 | t/t50basicoo.t |
331 | 346 | t/t55trans.t |
347 | 362 | t/t81hlines.t Test hlines.c |
348 | 363 | t/t82inline.t Test Inline::C integration |
349 | 364 | t/t83extutil.t Test Imager::ExtUtils |
365 | t/t84inlinectx.t | |
350 | 366 | t/t90cc.t |
351 | 367 | t/t91pod.t Test POD with Test::Pod |
352 | 368 | t/t92samples.t |
367 | 383 | T1/README |
368 | 384 | T1/t/t10type1.t |
369 | 385 | T1/t/t20oo.t |
386 | T1/t/t30thread.t | |
387 | T1/t/t90std.t Standard font tests for T1 | |
370 | 388 | T1/T1.pm |
371 | 389 | T1/T1.xs |
390 | T1/typemap | |
372 | 391 | tags.c |
373 | 392 | testimg/alpha16.tga 16-bit/pixel TGA with alpha "channel" RT 32926 |
374 | 393 | testimg/bad1oflow.bmp 1-bit/pixel, overflow integer on 32-bit machines |
490 | 509 | W32/Makefile.PL |
491 | 510 | W32/README |
492 | 511 | W32/t/t10win32.t Tests Win32 GDI font support |
512 | W32/t/t90std.t Standard font tests for W32 | |
493 | 513 | W32/W32.pm |
494 | 514 | W32/W32.xs |
495 | 515 | W32/win32.c Implements font support through Win32 GDI |
0 | 0 | --- #YAML:1.0 |
1 | 1 | name: Imager |
2 | version: 0.93 | |
2 | version: 0.95 | |
3 | 3 | abstract: Perl extension for Generating 24 bit Images |
4 | 4 | author: |
5 | 5 | - Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson |
47 | 47 | my @libpaths; # places to look for libraries |
48 | 48 | my $coverage; # build for coverage testing |
49 | 49 | my $assert; # build with assertions |
50 | my $trace_context; # trace context management to stderr | |
50 | 51 | GetOptions("help" => \$help, |
51 | 52 | "enable=s" => \@enable, |
52 | 53 | "disable=s" => \@disable, |
55 | 56 | "verbose|v" => \$VERBOSE, |
56 | 57 | "nolog" => \$NOLOG, |
57 | 58 | 'coverage' => \$coverage, |
58 | "assert|a" => \$assert); | |
59 | "assert|a" => \$assert, | |
60 | "tracecontext" => \$trace_context); | |
59 | 61 | |
60 | 62 | setenv(); |
61 | 63 | |
158 | 160 | if ($^O eq 'hpux') { $OSLIBS .= ' -ldld'; } |
159 | 161 | if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; } |
160 | 162 | |
161 | my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o | |
162 | log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o | |
163 | my @objs = qw(Imager.o context.o draw.o polygon.o image.o io.o iolayer.o | |
164 | log.o gaussian.o conv.o pnm.o raw.o feat.o combine.o | |
163 | 165 | filters.o dynaload.o stackmach.o datatypes.o |
164 | 166 | regmach.o trans2.o quant.o error.o convert.o |
165 | 167 | map.o tags.o palimg.o maskimg.o img8.o img16.o rotate.o |
166 | 168 | bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o |
167 | imext.o scale.o rubthru.o render.o paste.o compose.o flip.o); | |
169 | imext.o scale.o rubthru.o render.o paste.o compose.o flip.o | |
170 | perlio.o); | |
171 | ||
172 | if ($Config{useithreads}) { | |
173 | if ($Config{i_pthread}) { | |
174 | print "POSIX threads\n"; | |
175 | push @objs, "mutexpthr.o"; | |
176 | } | |
177 | elsif ($^O eq 'MSWin32') { | |
178 | print "Win32 threads\n"; | |
179 | push @objs, "mutexwin.o"; | |
180 | } | |
181 | else { | |
182 | print "Unsupported threading model\n"; | |
183 | push @objs, "mutexnull.o"; | |
184 | if ($ENV{AUTOMATED_TESTING}) { | |
185 | die "OS unsupported: no threading support code for this platform\n"; | |
186 | } | |
187 | } | |
188 | } | |
189 | else { | |
190 | print "No threads\n"; | |
191 | push @objs, "mutexnull.o"; | |
192 | } | |
168 | 193 | |
169 | 194 | my @typemaps = qw(typemap.local typemap); |
170 | 195 | if ($] < 5.008) { |
171 | 196 | unshift @typemaps, "typemap.oldperl"; |
197 | } | |
198 | ||
199 | if ($trace_context) { | |
200 | $CFLAGS .= " -DIMAGER_TRACE_CONTEXT"; | |
172 | 201 | } |
173 | 202 | |
174 | 203 | my %opts= |
520 | 549 | && !-e catfile($_[0], 'fterrors.h') }, |
521 | 550 | libcheck=>sub { $_[0] eq "libttf$aext" or $_[0] eq "libttf.$lext" }, |
522 | 551 | libfiles=>'-lttf', |
523 | objfiles=>'', | |
552 | objfiles=>'fontft1.o', | |
524 | 553 | code => \&freetype1_probe, |
525 | 554 | docs=>q{ |
526 | 555 | Truetype fonts are scalable fonts. They can include |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.86"; | |
6 | $VERSION = "0.87"; | |
7 | 7 | |
8 | 8 | require XSLoader; |
9 | 9 | XSLoader::load('Imager::File::PNG', $VERSION); |
0 | 0 | Imager::File::PNG provides PNG file format support for Imager. |
1 | 1 | |
2 | It requires libpng to be installed. | |
2 | It requires libpng, including development files, to be installed. | |
3 | 3 | |
4 | This is currently shipped as part of Imager, but Imager may install | |
5 | with out installing Imager::File::PNG, so if you need PNG support, | |
6 | add a dependency on Imager::File::PNG. | |
4 | For Linux distributions this typically requires installation of the | |
5 | associated -dev or -devel package. | |
6 | ||
7 | See Imager::Install for more information. | |
8 | ||
9 | Imager::File::PNG is currently shipped as part of Imager, but Imager | |
10 | may install without installing Imager::File::PNG, so if you need PNG | |
11 | support, add a dependency on Imager::File::PNG. |
315 | 315 | png_read_info(png_ptr, info_ptr); |
316 | 316 | png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); |
317 | 317 | |
318 | mm_log((1, | |
319 | "png_get_IHDR results: width %d, height %d, bit_depth %d, color_type %d, interlace_type %d\n", | |
320 | width,height,bit_depth,color_type,interlace_type)); | |
318 | mm_log((1, "png_get_IHDR results: width %u, height %u, bit_depth %d, color_type %d, interlace_type %d\n", | |
319 | (unsigned)width, (unsigned)height, bit_depth,color_type,interlace_type)); | |
321 | 320 | |
322 | 321 | CC2C[PNG_COLOR_TYPE_GRAY]=1; |
323 | 322 | CC2C[PNG_COLOR_TYPE_PALETTE]=3; |
686 | 685 | |
687 | 686 | i_tags_set(&im->tags, "i_format", "png", -1); |
688 | 687 | if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) { |
689 | mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type)); | |
688 | mm_log((1,"pHYs (%u, %u) %d\n", (unsigned)xres, (unsigned)yres, unit_type)); | |
690 | 689 | if (unit_type == PNG_RESOLUTION_METER) { |
691 | 690 | i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 5); |
692 | 691 | i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 5); |
0 | 0 | ================================================================ |
1 | 1 | Copyright (c) 1999-2004 Arnar M. Hrafnkelsson. All rights reserved. |
2 | Copyright (c) 2004-2012 Anthony Cook. | |
2 | Copyright (c) 2004-2013 Anthony Cook. | |
3 | 3 | This program is free software; you can redistribute it and/or |
4 | 4 | modify it under the same terms as Perl itself. |
5 | 5 | |
29 | 29 | 2. Compiling and testing |
30 | 30 | ======================== |
31 | 31 | |
32 | Some care has been taken to make the installation as smooth as | |
33 | possible. This is rather hard due to the difference between operating | |
34 | systems and site setups. To get started just type | |
32 | For details on the this process, including library and platform | |
33 | specific details, see Imager::Install, either by using: | |
35 | 34 | |
36 | $ perl Makefile.PL | |
35 | perldoc lib/Imager/Install.pod | |
37 | 36 | |
38 | It should blurb out a list of which libraries were found and which | |
39 | not. If you add a library to the machine after installing Imager it | |
40 | does not automatically become available in Imager. It only uses the | |
41 | libraries that are found. If the list of found libraries is not what | |
42 | you expected, then the Makefile.PL is either not searching in the | |
43 | right directories or your box does not have the libraries you think it | |
44 | does. For a list of where to get the libraries have a look at | |
45 | 3. External dependencies. To widen the search path for libraries and | |
46 | include files set the IM_INCPATH and IM_LIBPATH variables. The | |
47 | environment variables that matter when Makefile.PL is run are | |
37 | or at: | |
48 | 38 | |
49 | IM_INCPATH colon separated list of paths to extra include files | |
50 | IM_LIBPATH colon separated list of paths to extra library files | |
39 | http://imager.perl.org/docs/Imager/Install.html | |
51 | 40 | |
52 | IM_VERBOSE turns on verbose mode for the library scanning and such | |
53 | IM_MANUAL to manually select which libraries are used and which not | |
54 | IM_NOLOG if true logging will not be compiled into the module | |
55 | IM_DEBUG_MALLOC if true malloc debugging will be compiled into the module | |
56 | do not use IM_DEBUG_MALLOC in production - this slows | |
57 | everything down | |
41 | The basic installation process is similar to most other CPAN modules: | |
58 | 42 | |
59 | IM_CFLAGS Extra flags to pass to the compiler | |
60 | IM_LFLAGS Extra flags to pass to the linker | |
61 | IM_DFLAGS Extra flags to pass to the preprocessor | |
43 | perl Makefile.PL | |
44 | make | |
45 | make test | |
46 | make install | |
62 | 47 | |
63 | ||
64 | ||
65 | When finding the libraries has been sorted out it's time for | |
66 | ||
67 | $ make | |
68 | ||
69 | and if that works then do | |
70 | ||
71 | $ make test | |
72 | ||
73 | If either fails do take a peek at the file errep.perl. It's creates a | |
74 | file report.txt. This is some information which will help me discover | |
75 | where the problem is so I can try to fix it in future releases. If | |
76 | you find running it ok (just remember - no warranty!) please send the | |
77 | report.txt via email to tonyc@cpan.org. | |
78 | ||
79 | Troubleshooting tips: | |
80 | ||
81 | A common problem is that libgif/libungif are sometimes linked to the X | |
82 | libraries and then running the tests fails. In that case something | |
83 | like: | |
84 | ||
85 | $ IM_LFLAGS="-L/usr/X11R6/lib -lX11" perl Makefile.PL | |
86 | ||
87 | Which simply sets the environment variables for the extra libraries | |
88 | to include the X libraries (which we do not use at all, but must | |
89 | included since libgif has been linked with it). | |
90 | ||
91 | Otherwise you could just build giflib without any X11 dependencies: | |
92 | ||
93 | # must be a clean tree | |
94 | cd giflib-4.1.4 | |
95 | ./configure --without-x ... | |
96 | ||
97 | Also note that libgif has a few bugs: You can run something like | |
98 | ||
99 | $ perl -Iblib/lib -Iblib/arch t/t105gif.t | |
100 | ||
101 | This way you can see what comments the test script prints out. | |
102 | t/t105gif.t checks for an bug in libgif and prints out a patch | |
103 | if that bug is present, note that this bug only affects the more | |
104 | "advanced" features of libgif. | |
105 | ||
106 | If for some reason you have libungif-devel package installed but | |
107 | not libungif on RedHat then you will probably get lots of errors | |
108 | like undefined symbol: FreeSavedImages when running make test. | |
109 | Install libungif package to fix it. | |
110 | ||
111 | Stock libungif 4.1.4 or later seems to fix all of the bugs, if you | |
112 | have a problem that version of linungif (or later), let us know and | |
113 | we'll look into it. | |
114 | ||
115 | Imager needs to have a libtiff version of at least 3.5.5, but you | |
116 | should use a later version since some noticable bugs have been fixed. | |
117 | ||
118 | For now you can either configure Imager manually (by setting the | |
119 | IM_MANUAL environment variable to 1, in sh: | |
120 | ||
121 | $ IM_MANUAL=1 perl Makefile.PL | |
122 | ||
123 | and simply say no to tiff support when asked if you want it, the same thing | |
124 | can be used to circumvent problems in gifs to get Imager going. | |
125 | ||
126 | If it worked just continue with the installation as normally | |
127 | (with make install). | |
128 | ||
129 | Freetype 1.x vs Freetype 2.x | |
130 | ---------------------------- | |
131 | ||
132 | These two libraries have some conflicting include file names, but as | |
133 | long as you don't put the Freetype 2.x freetype.h directory in the | |
134 | include path it should all work. | |
135 | ||
136 | Put the directory containing ft2build.h in the include path, but not | |
137 | the directory containing the freetype 2.x freetype.h. | |
138 | ||
139 | If you see compilation errors from font.c you've probably made the | |
140 | mistake of putting the Freetype 2.x freetype.h directory into the | |
141 | include path. | |
142 | ||
143 | To see which directories should be in the include path, try: | |
144 | ||
145 | freetype-config --cflags | |
146 | ||
147 | Ideally, freetype-config should be in the PATH when building Imager | |
148 | with freetype 2.x support. | |
149 | ||
150 | ||
151 | Macintosh dfont and suitcase font support | |
152 | ----------------------------------------- | |
153 | ||
154 | Through Freetype 2.1, Imager can use Macintosh DFON (.dfont) fonts and | |
155 | suitcase font files. | |
156 | ||
157 | If you want to be able to use more than just the first face in the | |
158 | font file though, you will need to configure freetype2 with the | |
159 | --with-old-mac-fonts option: | |
160 | ||
161 | ./configure --with-old-mac-fonts | |
162 | ||
163 | You can use the index option to get to the other font faces in the | |
164 | file: | |
165 | ||
166 | # get the second face from $file | |
167 | my $font = Imager::Font->new(file=>$file, index=>1) | |
168 | or die Imager->errstr; | |
169 | ||
170 | If you're using a suitcase font, you will also need to force the use | |
171 | of freetype 2 with the type argument: | |
172 | ||
173 | my $font = Imager::Font->new(file=>$suitcase, type=>'ft2', index=>$index) | |
174 | or die Imager->errstr; | |
175 | ||
176 | ||
177 | ======================== | |
178 | 3. External dependencies | |
179 | ======================== | |
180 | ||
181 | Some hints about getting the Imager module to find the libraries it | |
182 | needs for specific features. | |
183 | ||
184 | Most Linux distributions pre-package these. For each library I've | |
185 | listed the Debian and Redhat package names. | |
186 | ||
187 | For Debian or Debian based system, such as Ubuntu, you would run: | |
188 | ||
189 | aptitude install package-name | |
190 | ||
191 | or: | |
192 | ||
193 | apt-get install package-name | |
194 | ||
195 | at a root shell (or via sudo). | |
196 | ||
197 | eg. | |
198 | ||
199 | apt-get install libjpeg62-dev | |
200 | ||
201 | For a Redhat based system such as CentOS or Fedora you would run: | |
202 | ||
203 | yum install package-name | |
204 | ||
205 | at a root shell. | |
206 | ||
207 | eg. | |
208 | ||
209 | yum install libjpeg-devel | |
210 | ||
211 | I strongly recommend using your distribution's packages if possible, | |
212 | this will mean any security updates to those packages will be applied | |
213 | when you install security updates. | |
214 | ||
215 | 3.1 libjpeg | |
216 | ----------- | |
217 | ||
218 | http://www.ijg.org/files/ | |
219 | ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz | |
220 | ||
221 | Provides read and write access to JPEG images. | |
222 | ||
223 | Make sure you run: | |
224 | ||
225 | make install-lib | |
226 | ||
227 | to install the development headers and libraries before attempting to | |
228 | build Imager. | |
229 | ||
230 | Debian: libjpeg62-dev | |
231 | Redhat: libjpeg-devel | |
232 | ||
233 | 3.2 libpng | |
234 | ---------- | |
235 | ||
236 | http://www.libpng.org/pub/png/libpng.html | |
237 | ||
238 | Provides read and write access to PNG images. | |
239 | ||
240 | You also need zlib to use png: http://www.gzip.org/zlib/ We have | |
241 | encountered problems with libpng 1.0.1, which were fixed in 1.0.5 | |
242 | Note: you should probably be using zlib 1.1.4, since 1.1.3 has a | |
243 | potential security problem. | |
244 | ||
245 | Debian: libpng12-dev | |
246 | Redhat: libpng-devel | |
247 | ||
248 | 3.3 giflib | |
249 | ---------- | |
250 | ||
251 | http://sourceforge.net/projects/giflib/ | |
252 | ||
253 | Provides read and write access to GIF images. | |
254 | ||
255 | giflib/libungif has come a long way since the buggy versions available | |
256 | when Imager's gif support code was written. Preferably you should get | |
257 | at least version 4.1.4. If you have a recent Linux distribution you | |
258 | should be safe with whatever giflib it provides, but if you're | |
259 | building from source, please try to use the latest version. | |
260 | ||
261 | With the expiration of the LZW patent there's no reason to use | |
262 | libungif. | |
263 | ||
264 | libgif 4.1.4 has no problems known to me at this point. | |
265 | ||
266 | Debian: libgif-dev | |
267 | Redhat: giflib-devel | |
268 | ||
269 | 3.4 libtiff | |
270 | ----------- | |
271 | ||
272 | http://www.remotesensing.org/libtiff/ | |
273 | ||
274 | Provides read and write access to TIFF images. | |
275 | ||
276 | Note: libtiff 3.9.0 shouldn't be used due to a bug in | |
277 | TIFFWriteDirectory(). | |
278 | ||
279 | Debian: libtiff4-dev | |
280 | Redhat: libtiff-devel | |
281 | ||
282 | 3.5 T1Lib | |
283 | --------- | |
284 | ||
285 | http://www.t1lib.org/ | |
286 | ftp://sunsite.unc.edu/pub/Linux/libs/graphics/ | |
287 | ||
288 | Support for text drawing with Type 1 Postscript fonts. | |
289 | ||
290 | Debian: libt1-dev | |
291 | Redhat: t1lib-devel | |
292 | ||
293 | 3.6 Freetype 2.x (libfreetype) | |
294 | ------------------------------ | |
295 | ||
296 | http://www.freetype.org/ | |
297 | ||
298 | Support for text drawing with a large number of font formats, | |
299 | including Truetype, Type 1, Opentype and some bitmap formats. | |
300 | ||
301 | Debian: libfreetype6-dev | |
302 | Redhat: freetype-devel | |
303 | ||
304 | 3.7 Freetype 1.x (libttf) | |
305 | ------------------------- | |
306 | ||
307 | http://www.freetype.org/ | |
308 | ||
309 | Support for text drawing with Truetype fonts. | |
310 | ||
311 | This library has been superceded by Freetype 2, and some Linux | |
312 | distributions are phasing it out. | |
313 | ||
314 | Debian: libttf-dev | |
315 | Redhat: freetype1-devel | |
316 | ||
317 | 3.8 Binaries | |
318 | ------------ | |
319 | ||
320 | Precompiled versions of some of the libraries might be found at: | |
321 | ||
322 | AIX: | |
323 | ||
324 | http://www.bullfreeware.com/ | |
325 | ||
326 | Solaris: | |
327 | ||
328 | http://www.sunfreeware.com/ | |
329 | ||
330 | ======================== | |
331 | 4. Logging and debugging | |
332 | ======================== | |
333 | ||
334 | Logging is compiled in by default - if you should want to get of it | |
335 | from the binaries you can do so by setting the env IMAGER_NOLOG | |
336 | to something. If you want to enable malloc debugging to check for leaks | |
337 | then set IMAGER_DEBUG_MALLOC to something. Needless to say it is | |
338 | pretty pointless to have malloc debug enabled with no logging since you | |
339 | can never see the malloc information that way. | |
340 | ||
341 | ||
342 | ================= | |
343 | 5. Win32 Support | |
344 | ================= | |
345 | ||
346 | Imager can be installed on Win32 systems. This was ported and tested | |
347 | with Microsoft Visual C++ 6.0 with build 623 of ActivePerl. You can | |
348 | use all of the features of Imager. You can also use Win32 GDI fonts | |
349 | directly by supplying the 'face' parameter to Imager::Font->new(...). | |
350 | ||
351 | I've tested with MSVC++ 6.0, cygwin (perl 5.6.1) and gcc (MinGW). | |
352 | ||
353 | If you see an error under cygwin during testing along the lines of: | |
354 | ||
355 | C:\cygwin\bin\perl.exe: *** unable to remap C:\cygwin\...some dll to the | |
356 | same address as parent (0x...) != 0x.... | |
357 | ||
358 | you will need to install the cygwin rebase package and run: | |
359 | ||
360 | $ rebaseall -v | |
361 | ||
362 | If you get errors from your make tool, make sure you're using the same | |
363 | make that was used to build your perl - generally GNU make for cygwin, | |
364 | nmake for Visual C/C++ and dmake for MinGW. | |
365 | ||
366 | Imager and all of the libraries it requires are supplied with | |
367 | Strawberry Perl. | |
368 | ||
369 | ============ | |
370 | 6. Mac OS X | |
371 | ============ | |
372 | ||
373 | Building Imager under OS X is generally straightforward. There are | |
374 | some exceptions though: | |
375 | ||
376 | a) you may find to need to ranlib library files in place after you've | |
377 | installed them, for example: | |
378 | ||
379 | ranlib /usr/local/lib/libgif.a | |
380 | ||
381 | b) the version of GCC enabled by default on OS X 10.4 generates | |
382 | incorrect code for some functions. To work around this run: | |
383 | ||
384 | gcc_select 3 | |
385 | ||
386 | before building Imager and: | |
387 | ||
388 | gcc_select 4 | |
389 | ||
390 | after building Imager. | |
391 | ||
392 | This problem exhibits itself as test failures in t/t20fill.t | |
393 | ||
394 | Imager 0.56 includes a workaround for this problem, but I wasn't | |
395 | able to test it. | |
396 | ||
397 | c) if you want to build GCC 4.0 from scratch and use that you will | |
398 | need to adjust the command-line supplied during the link stage, so | |
399 | that there is some other option before the -bundle option. | |
400 | ||
401 | For example: | |
402 | ||
403 | perl Makefile.PL LDDLFLAGS="`perl -MConfig -e 'print "-g $Config{lddlflags}"'`" | |
404 | ||
405 | d) The default perl build in Snow Leopard and Lion is a fat binary, | |
406 | and default builds of giflib, libpng and libjpeg (and maybe other | |
407 | libraries) will produce link failures. | |
408 | ||
409 | To avoid this you need to supply a CFLAGS parameter to the | |
410 | library's configure script, but since the -arch flag conflicts with | |
411 | the options used to build the dependency files, you need to supply | |
412 | another flag to disable dependency tracking. | |
413 | ||
414 | Snow Leopard fat binaries include i386, x86_64 and PPC objects, | |
415 | hence you would run configure like: | |
416 | ||
417 | ./configure --disable-dependency-tracking CFLAGS='-arch x86_64 -arch i386 -arch ppc' | |
418 | ||
419 | Lion doesn't support PPC, so there you run configure like: | |
420 | ||
421 | ./configure --disable-dependency-tracking CFLAGS='-arch x86_64 -arch i386' | |
422 | ||
423 | For libgif you might also want to supply the --without-x option. | |
424 | ||
425 | 48 | ======================= |
426 | 7. General information | |
49 | 3. General information | |
427 | 50 | ======================= |
428 | 51 | |
429 | 52 | The Imager module homepage is currently at: |
435 | 58 | https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Imager |
436 | 59 | |
437 | 60 | ======================== |
438 | 8. Thanks | |
61 | 4. Thanks | |
439 | 62 | ======================== |
440 | 63 | |
441 | 64 | Thanks go to: |
460 | 460 | |
461 | 461 | mm_log((3, "Offset/length table:\n")); |
462 | 462 | for(i=0; i < height * channels; i++) |
463 | mm_log((3, "%d: %d/%d\n", i, start_tab[i], length_tab[i])); | |
463 | mm_log((3, "%d: %lu/%lu\n", i, start_tab[i], length_tab[i])); | |
464 | 464 | |
465 | 465 | *pstart_tab = start_tab; |
466 | 466 | *plength_tab = length_tab; |
499 | 499 | return NULL; |
500 | 500 | } |
501 | 501 | |
502 | mm_log((1, "maxlen for an rle buffer: %d\n", max_length)); | |
502 | mm_log((1, "maxlen for an rle buffer: %lu\n", max_length)); | |
503 | 503 | |
504 | 504 | if (max_length > (img->xsize + 1) * 2) { |
505 | 505 | i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length); |
575 | 575 | /* RLE run */ |
576 | 576 | if (count > pixels_left) { |
577 | 577 | i_push_error(0, "SGI image: RLE run overflows scanline"); |
578 | mm_log((2, "RLE run overflows scanline (y %d chan %d offset %ld len %ld)\n", y, c, start_tab[ci], length_tab[ci])); | |
578 | mm_log((2, "RLE run overflows scanline (y %" i_DF " chan %d offset %lu len %lu)\n", i_DFc(y), c, start_tab[ci], length_tab[ci])); | |
579 | 579 | goto ErrorReturn; |
580 | 580 | } |
581 | 581 | if (data_left < 1) { |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.86" ); | |
40 | my @Imager_req = ( Imager => "0.95" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
0 | This module provides Type 1 font support for Imager via T1Lib. | |
0 | Imager::Font::T1 provides Type 1 font support for Imager via T1Lib. | |
1 | 1 | |
2 | 2 | http://imager.perl.org/ |
3 | 3 | http://www.t1lib.org/ |
4 | ||
5 | This requires that t1lib is installed, including any development files. | |
6 | ||
7 | For Linux distributions this typically requires installation of the | |
8 | associated -dev or -devel package. | |
9 | ||
10 | See Imager::Install for more information. | |
11 | ||
12 | Imager::Font::T1 is shipped as part of Imager, but if the libraries | |
13 | required aren't found it won't be installed. If you require T1 | |
14 | support via libt1 then add a dependency on Imager::Font::T1. | |
15 | ||
16 | Imager::Font::FT2 provides most of the functionality of | |
17 | Imager::Font::T1 and more.⏎ |
2 | 2 | use Imager::Color; |
3 | 3 | use vars qw(@ISA $VERSION); |
4 | 4 | @ISA = qw(Imager::Font); |
5 | use Scalar::Util (); | |
5 | 6 | |
6 | 7 | BEGIN { |
7 | $VERSION = "1.018"; | |
8 | $VERSION = "1.022"; | |
8 | 9 | |
9 | 10 | require XSLoader; |
10 | 11 | XSLoader::load('Imager::Font::T1', $VERSION); |
13 | 14 | |
14 | 15 | *_first = \&Imager::Font::_first; |
15 | 16 | |
16 | my $t1aa; | |
17 | ||
18 | # $T1AA is in there because for some reason (probably cache related) antialiasing | |
19 | # is a system wide setting in t1 lib. | |
20 | ||
21 | sub t1_set_aa_level { | |
22 | if (!defined $t1aa or $_[0] != $t1aa) { | |
23 | i_t1_set_aa($_[0]); | |
24 | $t1aa=$_[0]; | |
25 | } | |
26 | } | |
17 | my $t1aa = 2; | |
27 | 18 | |
28 | 19 | sub new { |
29 | 20 | my $class = shift; |
64 | 55 | $hsh{afm} = 0; |
65 | 56 | } |
66 | 57 | |
67 | my $id = i_t1_new($hsh{file},$hsh{afm}); | |
68 | unless ($id >= 0) { # the low-level code may miss some error handling | |
58 | my $font = Imager::Font::T1xs->new($hsh{file},$hsh{afm}); | |
59 | unless ($font) { # the low-level code may miss some error handling | |
69 | 60 | Imager->_set_error(Imager->_error_as_msg); |
70 | 61 | return; |
71 | 62 | } |
72 | 63 | return bless { |
73 | id => $id, | |
64 | t1font => $font, | |
74 | 65 | aa => $hsh{aa} || 0, |
75 | 66 | file => $hsh{file}, |
76 | 67 | type => 't1', |
77 | 68 | size => $hsh{size}, |
78 | 69 | color => $hsh{color}, |
70 | t1aa => $t1aa, | |
79 | 71 | }, $class; |
80 | 72 | } |
81 | 73 | |
82 | 74 | sub _draw { |
83 | 75 | my $self = shift; |
84 | my %input = @_; | |
85 | t1_set_aa_level($input{aa}); | |
86 | my $flags = ''; | |
87 | $flags .= 'u' if $input{underline}; | |
88 | $flags .= 's' if $input{strikethrough}; | |
89 | $flags .= 'o' if $input{overline}; | |
90 | if (exists $input{channel}) { | |
91 | i_t1_cp($input{image}{IMG}, $input{'x'}, $input{'y'}, | |
92 | $input{channel}, $self->{id}, $input{size}, | |
93 | $input{string}, length($input{string}), $input{align}, | |
94 | $input{utf8}, $flags); | |
95 | } else { | |
96 | i_t1_text($input{image}{IMG}, $input{'x'}, $input{'y'}, | |
97 | $input{color}, $self->{id}, $input{size}, | |
98 | $input{string}, length($input{string}), | |
99 | $input{align}, $input{utf8}, $flags); | |
100 | } | |
101 | ||
102 | return $self; | |
103 | } | |
104 | ||
105 | sub _bounding_box { | |
106 | my $self = shift; | |
76 | ||
77 | $self->_valid | |
78 | or return; | |
79 | ||
107 | 80 | my %input = @_; |
108 | 81 | my $flags = ''; |
109 | 82 | $flags .= 'u' if $input{underline}; |
110 | 83 | $flags .= 's' if $input{strikethrough}; |
111 | 84 | $flags .= 'o' if $input{overline}; |
112 | return i_t1_bbox($self->{id}, $input{size}, $input{string}, | |
113 | length($input{string}), $input{utf8}, $flags); | |
85 | my $aa = $input{aa} ? $self->{t1aa} : 0; | |
86 | if (exists $input{channel}) { | |
87 | $self->{t1font}->cp($input{image}{IMG}, $input{'x'}, $input{'y'}, | |
88 | $input{channel}, $input{size}, | |
89 | $input{string}, $input{align}, | |
90 | $input{utf8}, $flags, $aa) | |
91 | or return; | |
92 | } else { | |
93 | $self->{t1font}->text($input{image}{IMG}, $input{'x'}, $input{'y'}, | |
94 | $input{color}, $input{size}, | |
95 | $input{string}, $input{align}, $input{utf8}, $flags, $aa) | |
96 | or return; | |
97 | } | |
98 | ||
99 | return $self; | |
100 | } | |
101 | ||
102 | sub _bounding_box { | |
103 | my $self = shift; | |
104 | ||
105 | $self->_valid | |
106 | or return; | |
107 | ||
108 | my %input = @_; | |
109 | my $flags = ''; | |
110 | $flags .= 'u' if $input{underline}; | |
111 | $flags .= 's' if $input{strikethrough}; | |
112 | $flags .= 'o' if $input{overline}; | |
113 | my @bbox = $self->{t1font}->bbox($input{size}, $input{string}, | |
114 | $input{utf8}, $flags); | |
115 | unless (@bbox) { | |
116 | Imager->_set_error(Imager->_error_as_msg); | |
117 | return; | |
118 | } | |
119 | ||
120 | return @bbox; | |
114 | 121 | } |
115 | 122 | |
116 | 123 | # check if the font has the characters in the given string |
117 | 124 | sub has_chars { |
118 | 125 | my ($self, %hsh) = @_; |
119 | 126 | |
120 | unless (defined $hsh{string} && length $hsh{string}) { | |
127 | $self->_valid | |
128 | or return; | |
129 | ||
130 | unless (defined $hsh{string}) { | |
121 | 131 | $Imager::ERRSTR = "No string supplied to \$font->has_chars()"; |
122 | 132 | return; |
123 | 133 | } |
124 | return i_t1_has_chars($self->{id}, $hsh{string}, | |
125 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
134 | if (wantarray) { | |
135 | my @result = $self->{t1font} | |
136 | ->has_chars($hsh{string}, _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
137 | unless (@result) { | |
138 | Imager->_set_error(Imager->_error_as_msg); | |
139 | return; | |
140 | } | |
141 | ||
142 | return @result; | |
143 | } | |
144 | else { | |
145 | my $result = $self->{t1font} | |
146 | ->has_chars($hsh{string}, _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
147 | unless (defined $result) { | |
148 | Imager->_set_error(Imager->_error_as_msg); | |
149 | return; | |
150 | } | |
151 | return $result; | |
152 | } | |
126 | 153 | } |
127 | 154 | |
128 | 155 | sub utf8 { |
129 | 156 | 1; |
130 | 157 | } |
131 | 158 | |
159 | sub can_glyph_names { | |
160 | 1; | |
161 | } | |
162 | ||
132 | 163 | sub face_name { |
133 | 164 | my ($self) = @_; |
134 | 165 | |
135 | i_t1_face_name($self->{id}); | |
166 | $self->_valid | |
167 | or return; | |
168 | ||
169 | return $self->{t1font}->face_name(); | |
136 | 170 | } |
137 | 171 | |
138 | 172 | sub glyph_names { |
139 | 173 | my ($self, %input) = @_; |
174 | ||
175 | $self->_valid | |
176 | or return; | |
140 | 177 | |
141 | 178 | my $string = $input{string}; |
142 | 179 | defined $string |
143 | 180 | or return Imager->_set_error("no string parameter passed to glyph_names"); |
144 | 181 | my $utf8 = _first($input{utf8} || 0); |
145 | 182 | |
146 | i_t1_glyph_name($self->{id}, $string, $utf8); | |
147 | } | |
148 | ||
183 | my @result = $self->{t1font}->glyph_names($string, $utf8); | |
184 | unless (@result) { | |
185 | Imager->_set_error(Imager->_error_as_msg); | |
186 | return; | |
187 | } | |
188 | ||
189 | return @result; | |
190 | } | |
191 | ||
192 | sub set_aa_level { | |
193 | my ($self, $new_t1aa) = @_; | |
194 | ||
195 | if (!defined $new_t1aa || | |
196 | ($new_t1aa != 1 && $new_t1aa != 2)) { | |
197 | Imager->_set_error("set_aa_level: parameter must be 1 or 2"); | |
198 | return; | |
199 | } | |
200 | ||
201 | if (ref $self) { | |
202 | $self->_valid | |
203 | or return; | |
204 | ||
205 | $self->{t1aa} = $new_t1aa; | |
206 | } | |
207 | else { | |
208 | $t1aa = $new_t1aa; | |
209 | } | |
210 | ||
211 | return 1; | |
212 | } | |
213 | ||
214 | sub _valid { | |
215 | my $self = shift; | |
216 | ||
217 | unless ($self->{t1font} && Scalar::Util::blessed($self->{t1font})) { | |
218 | Imager->_set_error("font object was created in another thread"); | |
219 | return; | |
220 | } | |
221 | ||
222 | return 1; | |
223 | } | |
149 | 224 | |
150 | 225 | 1; |
151 | 226 | |
196 | 271 | Obviously, if you're calculating the bounding box the size of the line |
197 | 272 | is included in the box, and the line isn't drawn :) |
198 | 273 | |
274 | =head2 Anti-aliasing | |
275 | ||
276 | T1Lib supports multiple levels of anti-aliasing, by default, if you | |
277 | request anti-aliased output, Imager::Font::T1 will use the maximum | |
278 | level. | |
279 | ||
280 | You can override this with the set_t1_aa() method: | |
281 | ||
282 | =over | |
283 | ||
284 | =item set_aa_level() | |
285 | ||
286 | Usage: | |
287 | ||
288 | $font->set_aa_level(1); | |
289 | Imager::Font::T1->set_aa_level(2); | |
290 | ||
291 | Sets the T1Lib anti-aliasing level either for the specified font, or | |
292 | for new font objects. | |
293 | ||
294 | The only parameter must be 1 or 2. | |
295 | ||
296 | Returns true on success. | |
297 | ||
298 | =back | |
299 | ||
199 | 300 | =head1 AUTHOR |
200 | 301 | |
201 | 302 | Addi, Tony |
10 | 10 | |
11 | 11 | DEFINE_IMAGER_CALLBACKS; |
12 | 12 | |
13 | typedef i_t1_font_t Imager__Font__T1xs; | |
14 | ||
15 | #define i_t1_DESTROY(font) i_t1_destroy(font) | |
16 | ||
13 | 17 | MODULE = Imager::Font::T1 PACKAGE = Imager::Font::T1 |
14 | 18 | |
15 | 19 | undef_int |
16 | 20 | i_init_t1(t1log) |
17 | 21 | int t1log |
18 | 22 | |
19 | void | |
20 | i_t1_set_aa(st) | |
21 | int st | |
22 | ||
23 | int | |
24 | i_t1_new(pfb,afm) | |
23 | MODULE = Imager::Font::T1 PACKAGE = Imager::Font::T1xs PREFIX = i_t1_ | |
24 | ||
25 | Imager::Font::T1xs | |
26 | i_t1_new(class,pfb,afm) | |
25 | 27 | char* pfb |
26 | 28 | char* afm |
27 | ||
28 | int | |
29 | i_t1_destroy(font_id) | |
30 | int font_id | |
29 | C_ARGS: | |
30 | pfb, afm | |
31 | ||
32 | void | |
33 | i_t1_DESTROY(font) | |
34 | Imager::Font::T1xs font | |
31 | 35 | |
32 | 36 | |
33 | 37 | undef_int |
34 | i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") | |
38 | i_t1_cp(font,im,xb,yb,channel,points,str_sv,align,utf8=0,flags="",aa=1) | |
39 | Imager::Font::T1xs font | |
35 | 40 | Imager::ImgRaw im |
36 | 41 | i_img_dim xb |
37 | 42 | i_img_dim yb |
38 | 43 | int channel |
39 | int fontnum | |
40 | 44 | double points |
41 | 45 | SV* str_sv |
42 | 46 | int align |
43 | 47 | int utf8 |
44 | 48 | char* flags |
49 | int aa | |
45 | 50 | PREINIT: |
46 | 51 | char *str; |
47 | 52 | STRLEN len; |
48 | 53 | CODE: |
54 | str = SvPV(str_sv, len); | |
49 | 55 | #ifdef SvUTF8 |
50 | 56 | if (SvUTF8(str_sv)) |
51 | 57 | utf8 = 1; |
52 | 58 | #endif |
53 | str = SvPV(str_sv, len); | |
54 | RETVAL = i_t1_cp(im, xb,yb,channel,fontnum,points,str,len,align, | |
55 | utf8,flags); | |
59 | RETVAL = i_t1_cp(font, im, xb,yb,channel,points,str,len,align, | |
60 | utf8,flags,aa); | |
56 | 61 | OUTPUT: |
57 | 62 | RETVAL |
58 | 63 | |
59 | 64 | |
60 | 65 | void |
61 | i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="") | |
62 | int fontnum | |
66 | i_t1_bbox(fontnum,point,str_sv,utf8=0,flags="") | |
67 | Imager::Font::T1xs fontnum | |
63 | 68 | double point |
64 | 69 | SV* str_sv |
65 | 70 | int utf8 |
66 | 71 | char* flags |
67 | 72 | PREINIT: |
68 | char *str; | |
73 | const char *str; | |
69 | 74 | STRLEN len; |
70 | 75 | i_img_dim cords[BOUNDING_BOX_COUNT]; |
71 | 76 | int i; |
72 | 77 | int rc; |
73 | 78 | PPCODE: |
79 | str = SvPV(str_sv, len); | |
74 | 80 | #ifdef SvUTF8 |
75 | 81 | if (SvUTF8(str_sv)) |
76 | 82 | utf8 = 1; |
77 | 83 | #endif |
78 | str = SvPV(str_sv, len); | |
79 | 84 | rc = i_t1_bbox(fontnum,point,str,len,cords,utf8,flags); |
80 | 85 | if (rc > 0) { |
81 | 86 | EXTEND(SP, rc); |
86 | 91 | |
87 | 92 | |
88 | 93 | undef_int |
89 | i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") | |
94 | i_t1_text(font,im,xb,yb,cl,points,str_sv,align,utf8=0,flags="",aa=1) | |
95 | Imager::Font::T1xs font | |
90 | 96 | Imager::ImgRaw im |
91 | 97 | i_img_dim xb |
92 | 98 | i_img_dim yb |
93 | 99 | Imager::Color cl |
94 | int fontnum | |
95 | 100 | double points |
96 | 101 | SV* str_sv |
97 | 102 | int align |
98 | 103 | int utf8 |
99 | char* flags | |
104 | const char* flags | |
105 | int aa | |
100 | 106 | PREINIT: |
101 | 107 | char *str; |
102 | 108 | STRLEN len; |
103 | 109 | CODE: |
110 | str = SvPV(str_sv, len); | |
104 | 111 | #ifdef SvUTF8 |
105 | 112 | if (SvUTF8(str_sv)) |
106 | 113 | utf8 = 1; |
107 | 114 | #endif |
108 | str = SvPV(str_sv, len); | |
109 | RETVAL = i_t1_text(im, xb,yb,cl,fontnum,points,str,len,align, | |
110 | utf8,flags); | |
115 | RETVAL = i_t1_text(font,im, xb,yb,cl,points,str,len,align, | |
116 | utf8,flags,aa); | |
111 | 117 | OUTPUT: |
112 | 118 | RETVAL |
113 | 119 | |
114 | 120 | void |
115 | i_t1_has_chars(handle, text_sv, utf8 = 0) | |
116 | int handle | |
121 | i_t1_has_chars(font, text_sv, utf8 = 0) | |
122 | Imager::Font::T1xs font | |
117 | 123 | SV *text_sv |
118 | 124 | int utf8 |
119 | 125 | PREINIT: |
123 | 129 | int count; |
124 | 130 | int i; |
125 | 131 | PPCODE: |
132 | text = SvPV(text_sv, len); | |
126 | 133 | #ifdef SvUTF8 |
127 | 134 | if (SvUTF8(text_sv)) |
128 | 135 | utf8 = 1; |
129 | 136 | #endif |
130 | text = SvPV(text_sv, len); | |
131 | 137 | work = mymalloc(len); |
132 | count = i_t1_has_chars(handle, text, len, utf8, work); | |
138 | count = i_t1_has_chars(font, text, len, utf8, work); | |
133 | 139 | if (GIMME_V == G_ARRAY) { |
134 | 140 | EXTEND(SP, count); |
141 | ||
135 | 142 | for (i = 0; i < count; ++i) { |
136 | 143 | PUSHs(boolSV(work[i])); |
137 | 144 | } |
143 | 150 | myfree(work); |
144 | 151 | |
145 | 152 | void |
146 | i_t1_face_name(handle) | |
147 | int handle | |
153 | i_t1_face_name(font) | |
154 | Imager::Font::T1xs font | |
148 | 155 | PREINIT: |
149 | 156 | char name[255]; |
150 | 157 | int len; |
151 | 158 | PPCODE: |
152 | len = i_t1_face_name(handle, name, sizeof(name)); | |
159 | len = i_t1_face_name(font, name, sizeof(name)); | |
153 | 160 | if (len) { |
154 | 161 | EXTEND(SP, 1); |
155 | 162 | PUSHs(sv_2mortal(newSVpv(name, strlen(name)))); |
156 | 163 | } |
157 | 164 | |
158 | 165 | void |
159 | i_t1_glyph_name(handle, text_sv, utf8 = 0) | |
160 | int handle | |
166 | i_t1_glyph_names(font, text_sv, utf8 = 0) | |
167 | Imager::Font::T1xs font | |
161 | 168 | SV *text_sv |
162 | 169 | int utf8 |
163 | 170 | PREINIT: |
165 | 172 | STRLEN work_len; |
166 | 173 | size_t len; |
167 | 174 | char name[255]; |
175 | SSize_t count = 0; | |
168 | 176 | PPCODE: |
177 | text = SvPV(text_sv, work_len); | |
169 | 178 | #ifdef SvUTF8 |
170 | 179 | if (SvUTF8(text_sv)) |
171 | 180 | utf8 = 1; |
172 | 181 | #endif |
173 | text = SvPV(text_sv, work_len); | |
182 | i_clear_error(); | |
174 | 183 | len = work_len; |
175 | 184 | while (len) { |
176 | 185 | unsigned long ch; |
178 | 187 | ch = i_utf8_advance(&text, &len); |
179 | 188 | if (ch == ~0UL) { |
180 | 189 | i_push_error(0, "invalid UTF8 character"); |
181 | break; | |
190 | XSRETURN(0); | |
182 | 191 | } |
183 | 192 | } |
184 | 193 | else { |
185 | 194 | ch = *text++; |
186 | 195 | --len; |
187 | 196 | } |
188 | EXTEND(SP, 1); | |
189 | if (i_t1_glyph_name(handle, ch, name, sizeof(name))) { | |
190 | PUSHs(sv_2mortal(newSVpv(name, 0))); | |
197 | EXTEND(SP, count+1); | |
198 | if (i_t1_glyph_name(font, ch, name, sizeof(name))) { | |
199 | ST(count) = sv_2mortal(newSVpv(name, 0)); | |
191 | 200 | } |
192 | 201 | else { |
193 | PUSHs(&PL_sv_undef); | |
194 | } | |
195 | } | |
202 | ST(count) = &PL_sv_undef; | |
203 | } | |
204 | ++count; | |
205 | } | |
206 | XSRETURN(count); | |
207 | ||
208 | int | |
209 | i_t1_CLONE_SKIP(...) | |
210 | CODE: | |
211 | (void)items; /* avoid unused warning */ | |
212 | RETVAL = 1; | |
213 | OUTPUT: | |
214 | RETVAL | |
196 | 215 | |
197 | 216 | BOOT: |
198 | 217 | PERL_INITIALIZE_IMAGER_CALLBACKS; |
218 | i_t1_start();⏎ |
4 | 4 | |
5 | 5 | static int t1_get_flags(char const *flags); |
6 | 6 | static char *t1_from_utf8(char const *in, size_t len, int *outlen); |
7 | ||
7 | static undef_int i_init_t1_low(int t1log); | |
8 | 8 | static void t1_push_error(void); |
9 | static void i_t1_set_aa(int st); | |
9 | 10 | |
10 | 11 | static int t1_active_fonts = 0; |
11 | 12 | static int t1_initialized = 0; |
13 | static int t1_aa = 0; | |
14 | ||
15 | struct i_t1_font_tag { | |
16 | int font_id; | |
17 | }; | |
18 | ||
19 | static i_mutex_t mutex; | |
20 | ||
21 | /* | |
22 | =item i_t1_start() | |
23 | ||
24 | Initialize the font driver. This does not actually initialize T1Lib, | |
25 | it just allocates the mutex we use to gate access to it. | |
26 | ||
27 | =cut | |
28 | */ | |
29 | ||
30 | void | |
31 | i_t1_start(void) { | |
32 | mutex = i_mutex_new(); | |
33 | } | |
12 | 34 | |
13 | 35 | /* |
14 | 36 | =item i_init_t1(t1log) |
20 | 42 | |
21 | 43 | undef_int |
22 | 44 | i_init_t1(int t1log) { |
45 | undef_int result; | |
46 | i_mutex_lock(mutex); | |
47 | ||
48 | result = i_init_t1_low(t1log); | |
49 | ||
50 | i_mutex_unlock(mutex); | |
51 | ||
52 | return result; | |
53 | } | |
54 | ||
55 | static undef_int | |
56 | i_init_t1_low(int t1log) { | |
23 | 57 | int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; |
24 | mm_log((1,"init_t1()\n")); | |
58 | ||
59 | mm_log((1,"init_t1(%d)\n", t1log)); | |
25 | 60 | |
26 | 61 | i_clear_error(); |
27 | 62 | |
44 | 79 | return(1); |
45 | 80 | } |
46 | 81 | T1_SetLogLevel(T1LOG_DEBUG); |
47 | i_t1_set_aa(1); /* Default Antialias value */ | |
48 | 82 | |
49 | 83 | ++t1_initialized; |
50 | 84 | |
63 | 97 | |
64 | 98 | void |
65 | 99 | i_close_t1(void) { |
100 | i_mutex_lock(mutex); | |
66 | 101 | T1_CloseLib(); |
67 | 102 | t1_initialized = 0; |
103 | i_mutex_unlock(mutex); | |
68 | 104 | } |
69 | 105 | |
70 | 106 | |
79 | 115 | =cut |
80 | 116 | */ |
81 | 117 | |
82 | int | |
118 | i_t1_font_t | |
83 | 119 | i_t1_new(char *pfb,char *afm) { |
84 | 120 | int font_id; |
121 | i_t1_font_t font; | |
122 | ||
123 | i_mutex_lock(mutex); | |
85 | 124 | |
86 | 125 | i_clear_error(); |
87 | 126 | |
88 | if (!t1_initialized && i_init_t1(0)) | |
89 | return -1; | |
127 | if (!t1_initialized && i_init_t1_low(0)) { | |
128 | i_mutex_unlock(mutex); | |
129 | return NULL; | |
130 | } | |
90 | 131 | |
91 | 132 | mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); |
92 | 133 | font_id = T1_AddFont(pfb); |
93 | 134 | if (font_id<0) { |
94 | 135 | mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); |
95 | 136 | t1_push_error(); |
96 | return font_id; | |
137 | i_mutex_unlock(mutex); | |
138 | return NULL; | |
97 | 139 | } |
98 | 140 | |
99 | 141 | if (afm != NULL) { |
106 | 148 | t1_push_error(); |
107 | 149 | i_push_error(0, "loading font"); |
108 | 150 | T1_DeleteFont(font_id); |
109 | return -1; | |
151 | i_mutex_unlock(mutex); | |
152 | return NULL; | |
110 | 153 | } |
111 | 154 | |
112 | 155 | ++t1_active_fonts; |
113 | 156 | |
114 | mm_log((1, "i_t1_new() -> %d\n", font_id)); | |
115 | ||
116 | return font_id; | |
117 | } | |
118 | ||
119 | /* | |
120 | =item i_t1_destroy(font_id) | |
157 | i_mutex_unlock(mutex); | |
158 | ||
159 | font = mymalloc(sizeof(*font)); | |
160 | font->font_id = font_id; | |
161 | ||
162 | mm_log((1, "i_t1_new() -> %p (%d)\n", font, font_id)); | |
163 | ||
164 | return font; | |
165 | } | |
166 | ||
167 | /* | |
168 | =item i_t1_destroy(font) | |
121 | 169 | |
122 | 170 | Frees resources for a t1 font with given font id. |
123 | 171 | |
124 | font_id - number of the font to free | |
172 | font - font to free | |
125 | 173 | |
126 | 174 | =cut |
127 | 175 | */ |
128 | 176 | |
129 | 177 | int |
130 | i_t1_destroy(int font_id) { | |
131 | mm_log((1,"i_t1_destroy(font_id %d)\n",font_id)); | |
178 | i_t1_destroy(i_t1_font_t font) { | |
179 | int result; | |
180 | ||
181 | i_mutex_lock(mutex); | |
182 | ||
183 | mm_log((1,"i_t1_destroy(font %p (%d))\n", font, font->font_id)); | |
132 | 184 | |
133 | 185 | --t1_active_fonts; |
134 | 186 | |
135 | return T1_DeleteFont(font_id); | |
187 | result = T1_DeleteFont(font->font_id); | |
188 | myfree(font); | |
189 | ||
190 | i_mutex_unlock(mutex); | |
191 | ||
192 | return result; | |
136 | 193 | } |
137 | 194 | |
138 | 195 | |
143 | 200 | |
144 | 201 | st - 0 = NONE, 1 = LOW, 2 = HIGH. |
145 | 202 | |
146 | =cut | |
147 | */ | |
148 | ||
149 | void | |
203 | Must be called with the mutex locked. | |
204 | ||
205 | =cut | |
206 | */ | |
207 | ||
208 | static void | |
150 | 209 | i_t1_set_aa(int st) { |
151 | 210 | int i; |
152 | 211 | unsigned long cst[17]; |
212 | ||
213 | if (t1_aa == st) | |
214 | return; | |
215 | ||
153 | 216 | switch(st) { |
154 | 217 | case 0: |
155 | 218 | T1_AASetBitsPerPixel( 8 ); |
170 | 233 | T1_AAHSetGrayValues( cst ); |
171 | 234 | mm_log((1,"setting T1 antialias to high\n")); |
172 | 235 | } |
236 | ||
237 | t1_aa = st; | |
173 | 238 | } |
174 | 239 | |
175 | 240 | |
176 | 241 | /* |
177 | =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align) | |
242 | =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align,aa) | |
178 | 243 | |
179 | 244 | Interface to text rendering into a single channel in an image |
180 | 245 | |
187 | 252 | str - string to render |
188 | 253 | len - string length |
189 | 254 | align - (0 - top of font glyph | 1 - baseline ) |
255 | aa - anti-aliasing level | |
190 | 256 | |
191 | 257 | =cut |
192 | 258 | */ |
193 | 259 | |
194 | 260 | undef_int |
195 | i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double points,char* str,size_t len,int align, int utf8, char const *flags) { | |
261 | i_t1_cp(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char* str,size_t len,int align, int utf8, char const *flags, int aa) { | |
196 | 262 | GLYPH *glyph; |
197 | 263 | int xsize,ysize,x,y; |
198 | 264 | i_color val; |
199 | 265 | int mod_flags = t1_get_flags(flags); |
266 | int fontnum = font->font_id; | |
200 | 267 | |
201 | 268 | unsigned int ch_mask_store; |
202 | 269 | |
203 | if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } | |
270 | i_clear_error(); | |
271 | ||
272 | mm_log((1, "i_t1_cp(font %p (%d), im %p, (xb,yb)=" i_DFp ", channel %d, points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n", | |
273 | font, fontnum, im, i_DFcp(xb, yb), channel, points, str, (unsigned)len, align, utf8, flags, aa)); | |
274 | ||
275 | if (im == NULL) { | |
276 | mm_log((1,"i_t1_cp: Null image in input\n")); | |
277 | i_push_error(0, "null image"); | |
278 | return(0); | |
279 | } | |
280 | ||
281 | i_mutex_lock(mutex); | |
282 | ||
283 | i_t1_set_aa(aa); | |
204 | 284 | |
205 | 285 | if (utf8) { |
206 | 286 | int worklen; |
207 | 287 | char *work = t1_from_utf8(str, len, &worklen); |
288 | if (work == NULL) { | |
289 | i_mutex_unlock(mutex); | |
290 | return 0; | |
291 | } | |
208 | 292 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); |
209 | 293 | myfree(work); |
210 | 294 | } |
211 | 295 | else { |
212 | 296 | glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); |
213 | 297 | } |
214 | if (glyph == NULL) | |
298 | if (glyph == NULL) { | |
299 | t1_push_error(); | |
300 | i_push_error(0, "i_t1_cp: T1_AASetString failed"); | |
301 | i_mutex_unlock(mutex); | |
215 | 302 | return 0; |
303 | } | |
216 | 304 | |
217 | 305 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); |
218 | 306 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); |
219 | 307 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); |
220 | mm_log((1,"bpp: %d\n",glyph->bpp)); | |
308 | mm_log((1,"bpp: %lu\n", (unsigned long)glyph->bpp)); | |
221 | 309 | |
222 | 310 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; |
223 | 311 | ysize=glyph->metrics.ascent-glyph->metrics.descent; |
235 | 323 | } |
236 | 324 | |
237 | 325 | im->ch_mask=ch_mask_store; |
326 | ||
327 | i_mutex_unlock(mutex); | |
328 | ||
238 | 329 | return 1; |
239 | 330 | } |
240 | 331 | |
266 | 357 | */ |
267 | 358 | |
268 | 359 | int |
269 | i_t1_bbox(int fontnum, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) { | |
360 | i_t1_bbox(i_t1_font_t font, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) { | |
270 | 361 | BBox bbox; |
271 | 362 | BBox gbbox; |
272 | 363 | int mod_flags = t1_get_flags(flags); |
273 | 364 | i_img_dim advance; |
274 | int space_position = T1_GetEncodingIndex(fontnum, "space"); | |
275 | ||
276 | mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len)); | |
277 | T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */ | |
365 | int fontnum = font->font_id; | |
366 | int space_position; | |
367 | ||
368 | i_clear_error(); | |
369 | ||
370 | i_mutex_lock(mutex); | |
371 | ||
372 | space_position = T1_GetEncodingIndex(fontnum, "space"); | |
373 | ||
374 | mm_log((1,"i_t1_bbox(font %p (%d),points %.2f,str '%.*s', len %u)\n", | |
375 | font, fontnum,points,(int)len,str,(unsigned)len)); | |
376 | if (T1_LoadFont(fontnum) == -1) { | |
377 | t1_push_error(); | |
378 | i_mutex_unlock(mutex); | |
379 | return 0; | |
380 | } | |
278 | 381 | |
279 | 382 | if (len == 0) { |
280 | 383 | /* len == 0 has special meaning to T1lib, but it means there's |
286 | 389 | if (utf8) { |
287 | 390 | int worklen; |
288 | 391 | char *work = t1_from_utf8(str, len, &worklen); |
392 | if (!work) { | |
393 | i_mutex_unlock(mutex); | |
394 | return 0; | |
395 | } | |
289 | 396 | advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); |
290 | 397 | bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); |
291 | 398 | t1_fix_bbox(&bbox, work, worklen, advance, space_position); |
299 | 406 | } |
300 | 407 | gbbox = T1_GetFontBBox(fontnum); |
301 | 408 | |
302 | mm_log((1,"bbox: (%d,%d,%d,%d)\n", | |
409 | mm_log((1,"bbox: (%d, %d, %d, %d, %d, %d)\n", | |
303 | 410 | (int)(bbox.llx*points/1000), |
304 | 411 | (int)(gbbox.lly*points/1000), |
305 | 412 | (int)(bbox.urx*points/1000), |
321 | 428 | cords[BBOX_RIGHT_BEARING] = |
322 | 429 | cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; |
323 | 430 | |
431 | i_mutex_unlock(mutex); | |
432 | ||
324 | 433 | return BBOX_RIGHT_BEARING+1; |
325 | 434 | } |
326 | 435 | |
327 | 436 | |
328 | 437 | /* |
329 | =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align) | |
438 | =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align, utf8, flags, aa) | |
330 | 439 | |
331 | 440 | Interface to text rendering in a single color onto an image |
332 | 441 | |
339 | 448 | str - char pointer to string to render |
340 | 449 | len - string length |
341 | 450 | align - (0 - top of font glyph | 1 - baseline ) |
451 | utf8 - str is utf8 | |
452 | flags - formatting flags | |
453 | aa - anti-aliasing level | |
342 | 454 | |
343 | 455 | =cut |
344 | 456 | */ |
345 | 457 | |
346 | 458 | undef_int |
347 | i_t1_text(i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl,int fontnum, double points,const char* str,size_t len,int align, int utf8, char const *flags) { | |
459 | i_t1_text(i_t1_font_t font, i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl, double points,const char* str,size_t len,int align, int utf8, char const *flags, int aa) { | |
348 | 460 | GLYPH *glyph; |
349 | 461 | int xsize,ysize,y; |
350 | 462 | int mod_flags = t1_get_flags(flags); |
351 | 463 | i_render *r; |
352 | ||
353 | if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } | |
464 | int fontnum = font->font_id; | |
465 | ||
466 | mm_log((1, "i_t1_text(font %p (%d), im %p, (xb,yb)=" i_DFp ", cl (%d,%d,%d,%d), points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n", | |
467 | font, fontnum, im, i_DFcp(xb, yb), cl->rgba.r, cl->rgba.g, cl->rgba.b, cl->rgba.a, points, str, (unsigned)len, align, utf8, flags, aa)); | |
468 | ||
469 | i_clear_error(); | |
470 | ||
471 | if (im == NULL) { | |
472 | i_push_error(0, "null image"); | |
473 | mm_log((1,"i_t1_text: Null image in input\n")); | |
474 | return(0); | |
475 | } | |
476 | ||
477 | i_mutex_lock(mutex); | |
478 | ||
479 | i_t1_set_aa(aa); | |
354 | 480 | |
355 | 481 | if (utf8) { |
356 | 482 | int worklen; |
357 | 483 | char *work = t1_from_utf8(str, len, &worklen); |
484 | if (!work) { | |
485 | i_mutex_unlock(mutex); | |
486 | return 0; | |
487 | } | |
358 | 488 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); |
359 | 489 | myfree(work); |
360 | 490 | } |
362 | 492 | /* T1_AASetString() accepts a char * not a const char */ |
363 | 493 | glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); |
364 | 494 | } |
365 | if (glyph == NULL) | |
495 | if (glyph == NULL) { | |
496 | mm_log((1, "T1_AASetString failed\n")); | |
497 | t1_push_error(); | |
498 | i_push_error(0, "i_t1_text(): T1_AASetString failed"); | |
499 | i_mutex_unlock(mutex); | |
366 | 500 | return 0; |
501 | } | |
367 | 502 | |
368 | 503 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); |
369 | 504 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); |
370 | 505 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); |
371 | mm_log((1,"bpp: %d\n",glyph->bpp)); | |
506 | mm_log((1,"bpp: %lu\n",(unsigned long)glyph->bpp)); | |
372 | 507 | |
373 | 508 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; |
374 | 509 | ysize=glyph->metrics.ascent-glyph->metrics.descent; |
382 | 517 | i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl); |
383 | 518 | } |
384 | 519 | i_render_delete(r); |
520 | ||
521 | i_mutex_unlock(mutex); | |
385 | 522 | |
386 | 523 | return 1; |
387 | 524 | } |
466 | 603 | */ |
467 | 604 | |
468 | 605 | int |
469 | i_t1_has_chars(int font_num, const char *text, size_t len, int utf8, | |
606 | i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8, | |
470 | 607 | char *out) { |
471 | 608 | int count = 0; |
472 | ||
473 | mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", | |
474 | font_num, text, len, utf8)); | |
609 | int font_num = font->font_id; | |
610 | ||
611 | i_mutex_lock(mutex); | |
612 | ||
613 | mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %u, utf8 %d)\n", | |
614 | font_num, text, (unsigned)len, utf8)); | |
475 | 615 | |
476 | 616 | i_clear_error(); |
477 | 617 | if (T1_LoadFont(font_num)) { |
478 | 618 | t1_push_error(); |
619 | i_mutex_unlock(mutex); | |
479 | 620 | return 0; |
480 | 621 | } |
481 | 622 | |
485 | 626 | c = i_utf8_advance(&text, &len); |
486 | 627 | if (c == ~0UL) { |
487 | 628 | i_push_error(0, "invalid UTF8 character"); |
629 | i_mutex_unlock(mutex); | |
488 | 630 | return 0; |
489 | 631 | } |
490 | 632 | } |
511 | 653 | ++count; |
512 | 654 | } |
513 | 655 | |
656 | i_mutex_unlock(mutex); | |
657 | ||
514 | 658 | return count; |
515 | 659 | } |
516 | 660 | |
517 | 661 | /* |
518 | =item i_t1_face_name(font_num, name_buf, name_buf_size) | |
662 | =item i_t1_face_name(font, name_buf, name_buf_size) | |
519 | 663 | |
520 | 664 | Copies the face name of the given C<font_num> to C<name_buf>. Returns |
521 | 665 | the number of characters required to store the name (which can be |
529 | 673 | */ |
530 | 674 | |
531 | 675 | int |
532 | i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) { | |
676 | i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size) { | |
533 | 677 | char *name; |
678 | int font_num = font->font_id; | |
679 | ||
680 | i_mutex_lock(mutex); | |
534 | 681 | |
535 | 682 | T1_errno = 0; |
536 | 683 | if (T1_LoadFont(font_num)) { |
537 | 684 | t1_push_error(); |
685 | i_mutex_unlock(mutex); | |
538 | 686 | return 0; |
539 | 687 | } |
540 | 688 | name = T1_GetFontName(font_num); |
541 | 689 | |
542 | 690 | if (name) { |
691 | size_t len = strlen(name); | |
543 | 692 | strncpy(name_buf, name, name_buf_size); |
544 | 693 | name_buf[name_buf_size-1] = '\0'; |
545 | return strlen(name) + 1; | |
694 | i_mutex_unlock(mutex); | |
695 | return len + 1; | |
546 | 696 | } |
547 | 697 | else { |
548 | 698 | t1_push_error(); |
699 | i_mutex_unlock(mutex); | |
549 | 700 | return 0; |
550 | 701 | } |
551 | 702 | } |
552 | 703 | |
553 | 704 | int |
554 | i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, | |
705 | i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf, | |
555 | 706 | size_t name_buf_size) { |
556 | 707 | char *name; |
708 | int font_num = font->font_id; | |
557 | 709 | |
558 | 710 | i_clear_error(); |
559 | 711 | if (ch > 0xFF) { |
560 | 712 | return 0; |
561 | 713 | } |
714 | ||
715 | i_mutex_lock(mutex); | |
716 | ||
562 | 717 | if (T1_LoadFont(font_num)) { |
563 | 718 | t1_push_error(); |
719 | i_mutex_unlock(mutex); | |
564 | 720 | return 0; |
565 | 721 | } |
566 | 722 | name = T1_GetCharName(font_num, (unsigned char)ch); |
567 | 723 | if (name) { |
568 | 724 | if (strcmp(name, ".notdef")) { |
725 | size_t len = strlen(name); | |
569 | 726 | strncpy(name_buf, name, name_buf_size); |
570 | 727 | name_buf[name_buf_size-1] = '\0'; |
571 | return strlen(name) + 1; | |
728 | i_mutex_unlock(mutex); | |
729 | return len + 1; | |
572 | 730 | } |
573 | 731 | else { |
732 | i_mutex_unlock(mutex); | |
574 | 733 | return 0; |
575 | 734 | } |
576 | 735 | } |
577 | 736 | else { |
578 | 737 | t1_push_error(); |
738 | i_mutex_unlock(mutex); | |
579 | 739 | return 0; |
580 | 740 | } |
581 | 741 | } |
1 | 1 | #define IMAGER_IMT1_H |
2 | 2 | |
3 | 3 | #include "imdatatypes.h" |
4 | ||
5 | typedef struct i_t1_font_tag *i_t1_font_t; | |
6 | ||
7 | extern void | |
8 | i_t1_start(void); | |
4 | 9 | |
5 | 10 | extern undef_int |
6 | 11 | i_init_t1(int t1log); |
8 | 13 | extern void |
9 | 14 | i_close_t1(void); |
10 | 15 | |
11 | extern int | |
16 | extern i_t1_font_t | |
12 | 17 | i_t1_new(char *pfb,char *afm); |
13 | 18 | |
14 | 19 | extern int |
15 | i_t1_destroy(int font_id); | |
16 | ||
17 | extern void | |
18 | i_t1_set_aa(int st); | |
20 | i_t1_destroy(i_t1_font_t font); | |
19 | 21 | |
20 | 22 | extern undef_int |
21 | i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double points,char* str,size_t len,int align, int utf8, char const *flags); | |
23 | i_t1_cp(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char* str,size_t len,int align, int utf8, char const *flags, int aa); | |
22 | 24 | |
23 | 25 | extern int |
24 | i_t1_bbox(int fontnum,double points,const char *str,size_t len,i_img_dim *cords, int utf8,char const *flags); | |
26 | i_t1_bbox(i_t1_font_t font,double points,const char *str,size_t len,i_img_dim *cords, int utf8,char const *flags); | |
25 | 27 | |
26 | 28 | extern undef_int |
27 | i_t1_text(i_img *im,i_img_dim xb,i_img_dim yb,const i_color *cl,int fontnum,double points,const char* str,size_t len,int align, int utf8, char const *flags); | |
29 | i_t1_text(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,const i_color *cl,double points,const char* str,size_t len,int align, int utf8, char const *flags, int aa); | |
28 | 30 | |
29 | 31 | extern int |
30 | i_t1_has_chars(int font_num, const char *text, size_t len, int utf8, | |
32 | i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8, | |
31 | 33 | char *out); |
32 | 34 | |
33 | 35 | extern int |
34 | i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size); | |
36 | i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size); | |
35 | 37 | |
36 | 38 | extern int |
37 | i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, | |
39 | i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf, | |
38 | 40 | size_t name_buf_size); |
39 | 41 | #endif |
1 | 1 | use strict; |
2 | 2 | use Test::More; |
3 | 3 | use Imager ':all'; |
4 | use Imager::Test qw(diff_text_with_nul is_color3); | |
4 | use Imager::Test qw(diff_text_with_nul is_color3 is_image isnt_image); | |
5 | 5 | use Imager::Font::T1; |
6 | 6 | use Cwd qw(getcwd abs_path); |
7 | 7 | |
8 | 8 | #$Imager::DEBUG=1; |
9 | 9 | |
10 | plan tests => 97; | |
10 | plan tests => 110; | |
11 | 11 | |
12 | 12 | ok($Imager::formats{t1}, "must have t1"); |
13 | 13 | |
14 | ok((-d "testout" or mkdir "testout"), "make output directory"); | |
14 | -d "testout" or mkdir "testout"; | |
15 | ok(-d "testout", "make output directory"); | |
15 | 16 | |
16 | 17 | init_log("testout/t10type1.log",1); |
17 | 18 | |
39 | 40 | init(t1log=>0); |
40 | 41 | unlink "t1lib.log"; |
41 | 42 | |
42 | my $fnum=Imager::Font::T1::i_t1_new($fontname_pfb,$fontname_afm); # this will load the pfb font | |
43 | my $fnum=Imager::Font::T1xs->new($fontname_pfb,$fontname_afm); # this will load the pfb font | |
43 | 44 | unless (ok($fnum >= 0, "load font $fontname_pfb")) { |
44 | 45 | skip("without the font I can't do a thing", 90); |
45 | 46 | } |
46 | 47 | |
47 | my $bgcolor=Imager::Color->new(255,0,0,0); | |
48 | my $bgcolor=Imager::Color->new(255,0,0,255); | |
48 | 49 | my $overlay=Imager::ImgRaw::new(200,70,3); |
49 | 50 | |
50 | ok(Imager::Font::T1::i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1), "i_t1_cp"); | |
51 | ok($fnum->cp($overlay,5,50,1,50.0,'XMCLH',1), "i_t1_cp"); | |
51 | 52 | |
52 | 53 | i_line($overlay,0,50,100,50,$bgcolor,1); |
53 | 54 | |
54 | my @bbox=Imager::Font::T1::i_t1_bbox(0,50.0,'XMCLH',5); | |
55 | my @bbox=$fnum->bbox(50.0,'XMCLH'); | |
55 | 56 | is(@bbox, 8, "i_t1_bbox"); |
56 | 57 | print "# bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n"; |
57 | 58 | |
61 | 62 | i_writeppm_wiol($overlay,$IO); |
62 | 63 | close(FH); |
63 | 64 | |
64 | $bgcolor=Imager::Color::set($bgcolor,200,200,200,0); | |
65 | $bgcolor=Imager::Color::set($bgcolor,200,200,200,255); | |
65 | 66 | my $backgr=Imager::ImgRaw::new(280,300,3); |
66 | 67 | |
67 | Imager::Font::T1::i_t1_set_aa(2); | |
68 | ok(Imager::Font::T1::i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1), "i_t1_text"); | |
68 | ok($fnum->text($backgr,10,100,$bgcolor,150.0,'test',1,2), "i_t1_text"); | |
69 | 69 | |
70 | 70 | # "UTF8" tests |
71 | 71 | # for perl < 5.6 we can hand-encode text |
76 | 76 | my $text = pack("C*", 0x41, 0xC2, 0xA1, 0xE2, 0x80, 0x90, 0x41); |
77 | 77 | my $alttext = "A\xA1A"; |
78 | 78 | |
79 | my @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 1); | |
79 | my @utf8box = $fnum->bbox(50.0, $text, 1); | |
80 | 80 | is(@utf8box, 8, "utf8 bbox element count"); |
81 | my @base = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $alttext, length($alttext), 0); | |
81 | my @base = $fnum->bbox(50.0, $alttext, 0); | |
82 | 82 | is(@base, 8, "alt bbox element count"); |
83 | 83 | my $maxdiff = $fontname_pfb eq $deffont ? 0 : $base[2] / 3; |
84 | 84 | print "# (@utf8box vs @base)\n"; |
86 | 86 | "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)"); |
87 | 87 | |
88 | 88 | # hand-encoded UTF8 drawing |
89 | ok(Imager::Font::T1::i_t1_text($backgr, 10, 140, $bgcolor, $fnum, 32, $text, length($text), 1,1), "draw hand-encoded UTF8"); | |
90 | ||
91 | ok(Imager::Font::T1::i_t1_cp($backgr, 80, 140, 1, $fnum, 32, $text, length($text), 1, 1), | |
89 | ok($fnum->text($backgr, 10, 140, $bgcolor, 32, $text, 1,1), "draw hand-encoded UTF8"); | |
90 | ||
91 | ok($fnum->cp($backgr, 80, 140, 1, 32, $text, 1, 1), | |
92 | 92 | "cp hand-encoded UTF8"); |
93 | ||
94 | { # invalid utf8 | |
95 | my $text = pack("C", 0xC0); | |
96 | ok(!$fnum->text($backgr, 10, 140, $bgcolor, 32, $text, 1, 1), | |
97 | "attempt to draw invalid utf8"); | |
98 | is(Imager->_error_as_msg, "invalid UTF8 character", | |
99 | "check message"); | |
100 | } | |
93 | 101 | |
94 | 102 | # ok, try native perl UTF8 if available |
95 | 103 | SKIP: |
100 | 108 | # versions |
101 | 109 | eval q{$text = "A\xA1\x{2010}A"}; # A, a with ogonek, HYPHEN, A in our test font |
102 | 110 | #$text = "A".chr(0xA1).chr(0x2010)."A"; # this one works too |
103 | ok(Imager::Font::T1::i_t1_text($backgr, 10, 180, $bgcolor, $fnum, 32, $text, length($text), 1), | |
111 | Imager->log("draw UTF8\n"); | |
112 | ok($fnum->text($backgr, 10, 180, $bgcolor, 32, $text, 1), | |
104 | 113 | "draw UTF8"); |
105 | ok(Imager::Font::T1::i_t1_cp($backgr, 80, 180, 1, $fnum, 32, $text, length($text), 1), | |
114 | ok($fnum->cp($backgr, 80, 180, 1, 32, $text, 1), | |
106 | 115 | "cp UTF8"); |
107 | @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 0); | |
116 | @utf8box = $fnum->bbox(50.0, $text, 0); | |
108 | 117 | is(@utf8box, 8, "native utf8 bbox element count"); |
109 | 118 | ok(abs($utf8box[2] - $base[2]) <= $maxdiff, |
110 | 119 | "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)"); |
111 | 120 | eval q{$text = "A\xA1\xA2\x01\x1F\x{0100}A"}; |
112 | ok(Imager::Font::T1::i_t1_text($backgr, 10, 220, $bgcolor, $fnum, 32, $text, 0, 1, 0, "uso"), | |
121 | ok($fnum->text($backgr, 10, 220, $bgcolor, 32, $text, 0, 1, "uso"), | |
113 | 122 | "more complex output"); |
114 | 123 | } |
115 | 124 | |
119 | 128 | i_writeppm_wiol($backgr, $IO); |
120 | 129 | close(FH); |
121 | 130 | |
122 | my $rc=Imager::Font::T1::i_t1_destroy($fnum); | |
123 | unless (ok($rc >= 0, "i_t1_destroy")) { | |
124 | print "# i_t1_destroy failed: rc=$rc\n"; | |
125 | } | |
126 | ||
127 | print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"eses",4) ),"\n"; | |
128 | print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"llll",4) ),"\n"; | |
131 | undef $fnum; | |
129 | 132 | |
130 | 133 | # character existance tests - uses the special ExistenceTest font |
131 | 134 | my $exists_font = 'fontfiles/ExistenceTest.pfb'; |
133 | 136 | |
134 | 137 | -e $exists_font or die "$exists_font not found"; |
135 | 138 | |
136 | my $font_num = Imager::Font::T1::i_t1_new($exists_font, $exists_afm); | |
139 | my $font_num = Imager::Font::T1xs->new($exists_font, $exists_afm); | |
137 | 140 | SKIP: { |
138 | 141 | ok($font_num >= 0, 'loading test font') |
139 | 142 | or skip('Could not load test font', 6); |
140 | 143 | # first the list interface |
141 | my @exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A"); | |
144 | my @exists = $font_num->has_chars("!A"); | |
142 | 145 | is(@exists, 2, "return count from has_chars"); |
143 | 146 | ok($exists[0], "we have an exclamation mark"); |
144 | 147 | ok(!$exists[1], "we have no uppercase A"); |
145 | 148 | |
146 | 149 | # then the scalar interface |
147 | my $exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A"); | |
150 | my $exists = $font_num->has_chars("!A"); | |
148 | 151 | is(length($exists), 2, "return scalar length"); |
149 | 152 | ok(ord(substr($exists, 0, 1)), "we have an exclamation mark"); |
150 | 153 | ok(!ord(substr($exists, 1, 1)), "we have no upper-case A"); |
151 | Imager::Font::T1::i_t1_destroy($font_num); | |
154 | undef $font_num; | |
152 | 155 | } |
153 | 156 | |
154 | 157 | my $font = Imager::Font->new(file=>$exists_font, type=>'t1'); |
173 | 176 | isnt($bbox[2], $bbox[5], "different advance to pos_width"); |
174 | 177 | |
175 | 178 | # names |
176 | my $face_name = Imager::Font::T1::i_t1_face_name($font->{id}); | |
179 | my $face_name = $font->{t1font}->face_name(); | |
177 | 180 | print "# face $face_name\n"; |
178 | 181 | is($face_name, 'ExistenceTest', "face name"); |
179 | 182 | $face_name = $font->face_name; |
366 | 369 | ok($font, "found font by drive relative path") |
367 | 370 | or print "# path $drive_path\n"; |
368 | 371 | } |
372 | ||
373 | { | |
374 | Imager->log("Testing aa levels", 1); | |
375 | my $f1 = Imager::Font->new(file => $deffont, type => "t1"); | |
376 | is($f1->{t1aa}, 2, "should have default aa level"); | |
377 | my $imbase = Imager->new(xsize => 100, ysize => 20); | |
378 | ok($imbase->string(text => "test", size => 18, x => 5, y => 18, | |
379 | color => "#FFF", font => $f1, aa => 1), | |
380 | "draw text with def aa level"); | |
381 | ok(Imager::Font::T1->set_aa_level(1), "set aa level to 1"); | |
382 | my $f2 = Imager::Font->new(file => $deffont, type => "t1"); | |
383 | is($f2->{t1aa}, 1, "new font has new aa level"); | |
384 | my $imaa1 = Imager->new(xsize => 100, ysize => 20); | |
385 | ok($imaa1->string(text => "test", size => 18, x => 5, y => 18, | |
386 | color => "#FFF", font => $f2, aa => 1), | |
387 | "draw text with non-def aa level"); | |
388 | isnt_image($imbase, $imaa1, "images should differ"); | |
389 | ok($f2->set_aa_level(2), "set aa level of font"); | |
390 | is($f2->{t1aa}, 2, "check new aa level"); | |
391 | my $imaa2 = Imager->new(xsize => 100, ysize => 20); | |
392 | ok($imaa2->string(text => "test", size => 18, x => 5, y => 18, | |
393 | color => "#FFF", font => $f2, aa => 1), | |
394 | "draw text with non-def but 2 aa level"); | |
395 | is_image($imbase, $imaa2, "check images match"); | |
396 | } | |
397 | ||
398 | { # error handling check | |
399 | my $im = Imager->new(xsize => 100, ysize => 20); | |
400 | my $fnum = Imager::Font->new(file => $deffont, type => "t1"); | |
401 | ok(!$im->string(font => $fnum, string => "text", size => -10), | |
402 | "set invalid size"); | |
403 | is($im->errstr, "i_t1_text(): T1_AASetString failed: Invalid Argument in Function Call", | |
404 | "check error message"); | |
405 | } | |
369 | 406 | } |
370 | 407 | |
408 | ||
371 | 409 | #malloc_state(); |
410 |
12 | 12 | my $red=Imager::Color->new(205, 92, 92, 255); |
13 | 13 | die $Imager::ERRSTR unless $red; |
14 | 14 | |
15 | ok((-d "testout" or mkdir "testout"), "make output directory"); | |
15 | -d "testout" or mkdir "testout"; | |
16 | ok(-d "testout", "make output directory"); | |
16 | 17 | |
17 | 18 | Imager::init_log("testout/t20oo.log", 1); |
18 | 19 |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager; | |
3 | ||
4 | use Config; | |
5 | my $loaded_threads; | |
6 | BEGIN { | |
7 | if ($Config{useithreads} && $] > 5.008007) { | |
8 | $loaded_threads = | |
9 | eval { | |
10 | require threads; | |
11 | threads->import; | |
12 | 1; | |
13 | }; | |
14 | } | |
15 | } | |
16 | ||
17 | use Test::More; | |
18 | ||
19 | $Config{useithreads} | |
20 | or plan skip_all => "can't test Imager's threads support with no threads"; | |
21 | $] > 5.008007 | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support"; | |
23 | $loaded_threads | |
24 | or plan skip_all => "couldn't load threads"; | |
25 | ||
26 | $INC{"Devel/Cover.pm"} | |
27 | and plan skip_all => "threads and Devel::Cover don't get along"; | |
28 | ||
29 | # https://rt.cpan.org/Ticket/Display.html?id=65812 | |
30 | # https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100 | |
31 | $Test::More::VERSION =~ /^2\.00_/ | |
32 | and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*"; | |
33 | ||
34 | plan tests => 8; | |
35 | ||
36 | Imager->open_log(log => "testout/t30thread.log"); | |
37 | ||
38 | my $ft1 = Imager::Font->new(file => "fontfiles/dcr10.pfb", type => "t1"); | |
39 | ok($ft1, "make a font"); | |
40 | ok($ft1->_valid, "and it's valid"); | |
41 | my $ft2; | |
42 | ||
43 | my $thr = threads->create | |
44 | ( | |
45 | sub { | |
46 | ok(!$ft1->_valid, "first font no longer valid"); | |
47 | $ft2 = Imager::Font->new(file => "fontfiles/dcr10.pfb", type => "t1"); | |
48 | ok($ft2, "make a new font in thread"); | |
49 | ok($ft2->_valid, "and it's valid"); | |
50 | 1; | |
51 | }, | |
52 | ); | |
53 | ||
54 | ok($thr->join, "join the thread"); | |
55 | ok($ft1->_valid, "original font still valid in main thread"); | |
56 | is($ft2, undef, "font created in thread shouldn't be set in main thread"); | |
57 | ||
58 | Imager->close_log(); |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager::Test qw(std_font_tests std_font_test_count); | |
3 | use Imager::Font; | |
4 | use Test::More tests => std_font_test_count(); | |
5 | ||
6 | Imager->open_log(log => "testout/t90std.log"); | |
7 | ||
8 | my $font = Imager::Font->new(file => "fontfiles/dcr10.pfb", | |
9 | type => "t1"); | |
10 | ||
11 | SKIP: | |
12 | { | |
13 | $font | |
14 | or skip "Cannot load font", std_font_test_count(); | |
15 | std_font_tests({ font => $font, | |
16 | has_chars => [ 1, '', 1 ]}); | |
17 | } | |
18 | ||
19 | Imager->close_log; |
0 | Imager::Font::T1xs T_PTROBJ |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.86" ); | |
40 | my @Imager_req = ( Imager => "0.94" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
0 | 0 | Imager::File::TIFF provides TIFF file format support for Imager. |
1 | 1 | |
2 | It requires libtiff to be installed. | |
2 | It requires libtiff to be installed, including development files. | |
3 | ||
4 | For Linux distributions this typically requires installation of the | |
5 | associated -dev or -devel package. | |
6 | ||
7 | See Imager::Install for more information. | |
3 | 8 | |
4 | 9 | This is currently shipped as part of Imager, but Imager may install |
5 | 10 | with out installing Imager::File::TIFF, so if you need TIFF support, |
3 | 3 | use vars qw($VERSION @ISA); |
4 | 4 | |
5 | 5 | BEGIN { |
6 | $VERSION = "0.84"; | |
6 | $VERSION = "0.87"; | |
7 | 7 | |
8 | 8 | require XSLoader; |
9 | 9 | XSLoader::load('Imager::File::TIFF', $VERSION); |
264 | 264 | } |
265 | 265 | |
266 | 266 | #endif |
267 | ||
268 | static i_mutex_t mutex; | |
269 | ||
270 | void | |
271 | i_tiff_init(void) { | |
272 | mutex = i_mutex_new(); | |
273 | } | |
267 | 274 | |
268 | 275 | static int save_tiff_tags(TIFF *tif, i_img *im); |
269 | 276 | |
620 | 627 | int current_page; |
621 | 628 | tiffio_context_t ctx; |
622 | 629 | |
630 | i_mutex_lock(mutex); | |
631 | ||
623 | 632 | i_clear_error(); |
624 | 633 | old_handler = TIFFSetErrorHandler(error_handler); |
625 | 634 | #ifdef USE_EXT_WARN_HANDLER |
657 | 666 | TIFFSetWarningHandlerExt(old_ext_warn_handler); |
658 | 667 | #endif |
659 | 668 | tiffio_context_final(&ctx); |
669 | i_mutex_unlock(mutex); | |
660 | 670 | return NULL; |
661 | 671 | } |
662 | 672 | |
671 | 681 | #endif |
672 | 682 | TIFFClose(tif); |
673 | 683 | tiffio_context_final(&ctx); |
684 | i_mutex_unlock(mutex); | |
674 | 685 | return NULL; |
675 | 686 | } |
676 | 687 | } |
685 | 696 | #endif |
686 | 697 | TIFFClose(tif); |
687 | 698 | tiffio_context_final(&ctx); |
699 | i_mutex_unlock(mutex); | |
688 | 700 | |
689 | 701 | return im; |
690 | 702 | } |
708 | 720 | int result_alloc = 0; |
709 | 721 | tiffio_context_t ctx; |
710 | 722 | |
723 | i_mutex_lock(mutex); | |
724 | ||
711 | 725 | i_clear_error(); |
712 | 726 | old_handler = TIFFSetErrorHandler(error_handler); |
713 | 727 | #ifdef USE_EXT_WARN_HANDLER |
724 | 738 | /* Add code to get the filename info from the iolayer */ |
725 | 739 | /* Also add code to check for mmapped code */ |
726 | 740 | |
727 | mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig)); | |
741 | mm_log((1, "i_readtiff_wiol(ig %p)\n", ig)); | |
728 | 742 | |
729 | 743 | tif = TIFFClientOpen("(Iolayer)", |
730 | 744 | "rm", |
746 | 760 | TIFFSetWarningHandlerExt(old_ext_warn_handler); |
747 | 761 | #endif |
748 | 762 | tiffio_context_final(&ctx); |
763 | i_mutex_unlock(mutex); | |
749 | 764 | return NULL; |
750 | 765 | } |
751 | 766 | |
780 | 795 | #endif |
781 | 796 | TIFFClose(tif); |
782 | 797 | tiffio_context_final(&ctx); |
798 | i_mutex_unlock(mutex); | |
783 | 799 | |
784 | 800 | return results; |
785 | 801 | } |
1445 | 1461 | int i; |
1446 | 1462 | tiffio_context_t ctx; |
1447 | 1463 | |
1464 | i_mutex_lock(mutex); | |
1465 | ||
1448 | 1466 | old_handler = TIFFSetErrorHandler(error_handler); |
1449 | 1467 | |
1450 | 1468 | i_clear_error(); |
1471 | 1489 | i_push_error(0, "Could not create TIFF object"); |
1472 | 1490 | TIFFSetErrorHandler(old_handler); |
1473 | 1491 | tiffio_context_final(&ctx); |
1492 | i_mutex_unlock(mutex); | |
1474 | 1493 | return 0; |
1475 | 1494 | } |
1476 | 1495 | |
1479 | 1498 | TIFFClose(tif); |
1480 | 1499 | TIFFSetErrorHandler(old_handler); |
1481 | 1500 | tiffio_context_final(&ctx); |
1501 | i_mutex_unlock(mutex); | |
1482 | 1502 | return 0; |
1483 | 1503 | } |
1484 | 1504 | |
1487 | 1507 | TIFFClose(tif); |
1488 | 1508 | TIFFSetErrorHandler(old_handler); |
1489 | 1509 | tiffio_context_final(&ctx); |
1510 | i_mutex_unlock(mutex); | |
1490 | 1511 | return 0; |
1491 | 1512 | } |
1492 | 1513 | } |
1494 | 1515 | TIFFSetErrorHandler(old_handler); |
1495 | 1516 | (void) TIFFClose(tif); |
1496 | 1517 | tiffio_context_final(&ctx); |
1518 | ||
1519 | i_mutex_unlock(mutex); | |
1497 | 1520 | |
1498 | 1521 | if (i_io_close(ig)) |
1499 | 1522 | return 0; |
1520 | 1543 | int i; |
1521 | 1544 | TIFFErrorHandler old_handler; |
1522 | 1545 | tiffio_context_t ctx; |
1546 | ||
1547 | i_mutex_lock(mutex); | |
1523 | 1548 | |
1524 | 1549 | old_handler = TIFFSetErrorHandler(error_handler); |
1525 | 1550 | |
1547 | 1572 | i_push_error(0, "Could not create TIFF object"); |
1548 | 1573 | TIFFSetErrorHandler(old_handler); |
1549 | 1574 | tiffio_context_final(&ctx); |
1575 | i_mutex_unlock(mutex); | |
1550 | 1576 | return 0; |
1551 | 1577 | } |
1552 | 1578 | |
1555 | 1581 | TIFFClose(tif); |
1556 | 1582 | TIFFSetErrorHandler(old_handler); |
1557 | 1583 | tiffio_context_final(&ctx); |
1584 | i_mutex_unlock(mutex); | |
1558 | 1585 | return 0; |
1559 | 1586 | } |
1560 | 1587 | |
1563 | 1590 | TIFFClose(tif); |
1564 | 1591 | TIFFSetErrorHandler(old_handler); |
1565 | 1592 | tiffio_context_final(&ctx); |
1593 | i_mutex_unlock(mutex); | |
1566 | 1594 | return 0; |
1567 | 1595 | } |
1568 | 1596 | } |
1570 | 1598 | (void) TIFFClose(tif); |
1571 | 1599 | TIFFSetErrorHandler(old_handler); |
1572 | 1600 | tiffio_context_final(&ctx); |
1601 | ||
1602 | i_mutex_unlock(mutex); | |
1573 | 1603 | |
1574 | 1604 | if (i_io_close(ig)) |
1575 | 1605 | return 0; |
1592 | 1622 | TIFF* tif; |
1593 | 1623 | TIFFErrorHandler old_handler; |
1594 | 1624 | tiffio_context_t ctx; |
1625 | ||
1626 | i_mutex_lock(mutex); | |
1595 | 1627 | |
1596 | 1628 | old_handler = TIFFSetErrorHandler(error_handler); |
1597 | 1629 | |
1618 | 1650 | i_push_error(0, "Could not create TIFF object"); |
1619 | 1651 | tiffio_context_final(&ctx); |
1620 | 1652 | TIFFSetErrorHandler(old_handler); |
1653 | i_mutex_unlock(mutex); | |
1621 | 1654 | return 0; |
1622 | 1655 | } |
1623 | 1656 | |
1625 | 1658 | TIFFClose(tif); |
1626 | 1659 | tiffio_context_final(&ctx); |
1627 | 1660 | TIFFSetErrorHandler(old_handler); |
1661 | i_mutex_unlock(mutex); | |
1628 | 1662 | return 0; |
1629 | 1663 | } |
1630 | 1664 | |
1631 | 1665 | (void) TIFFClose(tif); |
1632 | 1666 | TIFFSetErrorHandler(old_handler); |
1633 | tiffio_context_final(&ctx); | |
1667 | tiffio_context_final(&ctx); | |
1668 | i_mutex_unlock(mutex); | |
1634 | 1669 | |
1635 | 1670 | if (i_io_close(ig)) |
1636 | 1671 | return 0; |
1660 | 1695 | TIFF* tif; |
1661 | 1696 | TIFFErrorHandler old_handler; |
1662 | 1697 | tiffio_context_t ctx; |
1698 | ||
1699 | i_mutex_lock(mutex); | |
1663 | 1700 | |
1664 | 1701 | old_handler = TIFFSetErrorHandler(error_handler); |
1665 | 1702 | |
1686 | 1723 | i_push_error(0, "Could not create TIFF object"); |
1687 | 1724 | TIFFSetErrorHandler(old_handler); |
1688 | 1725 | tiffio_context_final(&ctx); |
1726 | i_mutex_unlock(mutex); | |
1689 | 1727 | return 0; |
1690 | 1728 | } |
1691 | 1729 | |
1693 | 1731 | TIFFClose(tif); |
1694 | 1732 | TIFFSetErrorHandler(old_handler); |
1695 | 1733 | tiffio_context_final(&ctx); |
1734 | i_mutex_unlock(mutex); | |
1696 | 1735 | return 0; |
1697 | 1736 | } |
1698 | 1737 | |
1699 | 1738 | (void) TIFFClose(tif); |
1700 | 1739 | TIFFSetErrorHandler(old_handler); |
1701 | 1740 | tiffio_context_final(&ctx); |
1741 | i_mutex_unlock(mutex); | |
1702 | 1742 | |
1703 | 1743 | if (i_io_close(ig)) |
1704 | 1744 | return 0; |
2684 | 2724 | uint16 *p = state->raster; |
2685 | 2725 | int out_chan = state->img->channels; |
2686 | 2726 | |
2687 | mm_log((4, "putter_cmyk16(%p, %d, %d, %d, %d, %d)\n", x, y, width, height, row_extras)); | |
2727 | mm_log((4, "putter_cmyk16(%p, %" i_DF ", %" i_DF ", %" i_DF | |
2728 | ", %" i_DF ", %d)\n", state, i_DFcp(x, y), i_DFcp(width, height), | |
2729 | row_extras)); | |
2688 | 2730 | |
2689 | 2731 | state->pixels_read += width * height; |
2690 | 2732 | while (height > 0) { |
2 | 2 | |
3 | 3 | #include "imdatatypes.h" |
4 | 4 | |
5 | void i_tiff_init(void); | |
5 | 6 | i_img * i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page); |
6 | 7 | i_img ** i_readtiff_multi_wiol(io_glue *ig, int *count); |
7 | 8 | undef_int i_writetiff_wiol(i_img *im, io_glue *ig); |
37 | 37 | $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ]; |
38 | 38 | |
39 | 39 | # Imager required configure through use |
40 | my @Imager_req = ( Imager => "0.86" ); | |
40 | my @Imager_req = ( Imager => "0.95" ); | |
41 | 41 | if ($MM_ver >= 6.46) { |
42 | 42 | $opts{META_MERGE} = |
43 | 43 | { |
4 | 4 | @ISA = qw(Imager::Font); |
5 | 5 | |
6 | 6 | BEGIN { |
7 | $VERSION = "0.85"; | |
7 | $VERSION = "0.87"; | |
8 | 8 | |
9 | 9 | require XSLoader; |
10 | 10 | XSLoader::load('Imager::Font::W32', $VERSION); |
29 | 29 | my ($self, %opts) = @_; |
30 | 30 | |
31 | 31 | my @bbox = i_wf_bbox($self->{face}, $opts{size}, $opts{string}, $opts{utf8}); |
32 | unless (@bbox) { | |
33 | Imager->_set_error(Imager->_error_as_msg); | |
34 | return; | |
35 | } | |
36 | ||
37 | return @bbox; | |
32 | 38 | } |
33 | 39 | |
34 | 40 | sub _draw { |
36 | 42 | |
37 | 43 | my %input = @_; |
38 | 44 | if (exists $input{channel}) { |
39 | i_wf_cp($self->{face}, $input{image}{IMG}, $input{x}, $input{'y'}, | |
45 | return i_wf_cp($self->{face}, $input{image}{IMG}, $input{x}, $input{'y'}, | |
40 | 46 | $input{channel}, $input{size}, |
41 | 47 | $input{string}, $input{align}, $input{aa}, $input{utf8}); |
42 | 48 | } |
43 | 49 | else { |
44 | i_wf_text($self->{face}, $input{image}{IMG}, $input{x}, | |
50 | return i_wf_text($self->{face}, $input{image}{IMG}, $input{x}, | |
45 | 51 | $input{'y'}, $input{color}, $input{size}, |
46 | 52 | $input{string}, $input{align}, $input{aa}, $input{utf8}); |
47 | 53 | } |
48 | ||
49 | return 1; | |
50 | 54 | } |
51 | 55 | |
52 | 56 | |
53 | 57 | sub utf8 { |
54 | 58 | return 1; |
59 | } | |
60 | ||
61 | sub can_glyph_names { | |
62 | return; | |
55 | 63 | } |
56 | 64 | |
57 | 65 | 1; |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager::Test qw(std_font_tests std_font_test_count); | |
3 | use Imager::Font; | |
4 | use Test::More tests => std_font_test_count(); | |
5 | ||
6 | Imager->open_log(log => "testout/t90std.log"); | |
7 | ||
8 | my $font = Imager::Font->new(face => "Times New Roman Bold", | |
9 | type => "w32"); | |
10 | ||
11 | SKIP: | |
12 | { | |
13 | $font | |
14 | or skip "Cannot load font", std_font_test_count(); | |
15 | std_font_tests | |
16 | ({ | |
17 | font => $font, | |
18 | #has_chars => [ 1, 1, 1 ], | |
19 | #files => 1, | |
20 | #glyph_name_font => $name_font, | |
21 | #glyph_names => [ "A", "uni2010", "A" ], | |
22 | }); | |
23 | } | |
24 | Imager->close_log;⏎ |
542 | 542 | static |
543 | 543 | LPWSTR |
544 | 544 | utf8_to_wide_string(char const *text, int text_len, int *wide_chars) { |
545 | int wide_count = MultiByteToWideChar(CP_UTF8, 0, text, text_len, NULL, 0); | |
545 | int wide_count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, text_len, NULL, 0); | |
546 | 546 | LPWSTR result; |
547 | 547 | |
548 | 548 | if (wide_count < 0) { |
551 | 551 | } |
552 | 552 | ++wide_count; |
553 | 553 | result = mymalloc(sizeof(WCHAR) * wide_count); |
554 | if (MultiByteToWideChar(CP_UTF8, 0, text, text_len, result, wide_count) < 0) { | |
554 | if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, text_len, result, wide_count) < 0) { | |
555 | 555 | i_push_errorf(0, "Could not convert utf8: %ld", GetLastError()); |
556 | 556 | return NULL; |
557 | 557 | } |
177 | 177 | |
178 | 178 | =head1 SEE ALSO |
179 | 179 | |
180 | Imager, Imager::ExtUtils, Imager::Inline | |
180 | Imager, Imager::API, Imager::ExtUtils, Imager::Inline | |
181 | 181 | |
182 | 182 | =cut |
183 | 183 | EOS |
186 | 186 | |
187 | 187 | |
188 | 188 | sub make_func_list { |
189 | my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log i_img_color_channels i_img_has_alpha i_img_dim i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits i_psamp i_psampf); | |
189 | my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log mm_log i_img_color_channels i_img_has_alpha i_img_dim i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits i_psamp i_psampf); | |
190 | 190 | open FUNCS, "< imexttypes.h" |
191 | 191 | or die "Cannot open imexttypes.h: $!\n"; |
192 | 192 | my $in_struct; |
193 | 193 | while (<FUNCS>) { |
194 | 194 | /^typedef struct/ && ++$in_struct; |
195 | if ($in_struct && /\(\*f_(io?_\w+)/) { | |
195 | if ($in_struct && !/SKIP/ && /\(\*f_(i[om]?_\w+)/) { | |
196 | 196 | my $name = $1; |
197 | 197 | $name =~ s/_imp$//; |
198 | 198 | push @funcs, $name; |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include <stdarg.h> |
1 | 2 | #include "imageri.h" |
2 | 3 | |
75 | 76 | */ |
76 | 77 | int |
77 | 78 | i_writebmp_wiol(i_img *im, io_glue *ig) { |
79 | dIMCTXim(im); | |
78 | 80 | i_clear_error(); |
79 | 81 | |
80 | 82 | /* pick a format */ |
115 | 117 | i_packed_t xsize, ysize, planes, bit_count, compression, size_image, xres, yres; |
116 | 118 | i_packed_t clr_used, clr_important, offbits; |
117 | 119 | i_img *im; |
118 | ||
119 | mm_log((1, "i_readbmp_wiol(ig %p)\n", ig)); | |
120 | dIMCTXio(ig); | |
121 | ||
122 | im_log((aIMCTX, 1, "i_readbmp_wiol(ig %p)\n", ig)); | |
120 | 123 | |
121 | 124 | i_clear_error(); |
122 | 125 | |
134 | 137 | return 0; |
135 | 138 | } |
136 | 139 | |
137 | mm_log((1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d " | |
140 | im_log((aIMCTX, 1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d " | |
138 | 141 | "bit_count %d compression %d size %d xres %d yres %d clr_used %d " |
139 | 142 | "clr_important %d\n", (int)filesize, (int)offbits, (int)xsize, |
140 | 143 | (int)ysize, (int)planes, (int)bit_count, (int)compression, |
142 | 145 | (int)clr_important)); |
143 | 146 | |
144 | 147 | if (!i_int_check_image_file_limits(xsize, abs(ysize), 3, sizeof(i_sample_t))) { |
145 | mm_log((1, "i_readbmp_wiol: image size exceeds limits\n")); | |
148 | im_log((aIMCTX, 1, "i_readbmp_wiol: image size exceeds limits\n")); | |
146 | 149 | return NULL; |
147 | 150 | } |
148 | 151 | |
170 | 173 | break; |
171 | 174 | |
172 | 175 | default: |
173 | i_push_errorf(0, "unknown bit count for BMP file (%d)", (int)bit_count); | |
176 | im_push_errorf(aIMCTX, 0, "unknown bit count for BMP file (%d)", (int)bit_count); | |
174 | 177 | return NULL; |
175 | 178 | } |
176 | 179 | |
272 | 275 | break; |
273 | 276 | |
274 | 277 | default: |
275 | i_fatal(1, "Unknown read_packed format code 0x%02x", code); | |
278 | { | |
279 | dIMCTXio(ig); | |
280 | im_fatal(aIMCTX, 1, "Unknown read_packed format code 0x%02x", code); | |
281 | } | |
276 | 282 | } |
277 | 283 | } |
278 | 284 | return 1; |
324 | 330 | break; |
325 | 331 | |
326 | 332 | default: |
327 | i_fatal(1, "Unknown write_packed format code 0x%02x", *format); | |
333 | { | |
334 | dIMCTXio(ig); | |
335 | im_fatal(aIMCTX, 1, "Unknown write_packed format code 0x%02x", *format); | |
336 | } | |
328 | 337 | } |
329 | 338 | ++format; |
330 | 339 | } |
349 | 358 | int got_xres, got_yres, aspect_only; |
350 | 359 | int colors_used = 0; |
351 | 360 | int offset = FILEHEAD_SIZE + INFOHEAD_SIZE; |
361 | dIMCTXim(im); | |
352 | 362 | |
353 | 363 | if (im->xsize > SIGNMAX32 || im->ysize > SIGNMAX32) { |
354 | 364 | i_push_error(0, "image too large to write to BMP"); |
450 | 460 | int line_size = (im->xsize+7) / 8; |
451 | 461 | int x, y; |
452 | 462 | int unpacked_size; |
463 | dIMCTXim(im); | |
453 | 464 | |
454 | 465 | /* round up to nearest multiple of four */ |
455 | 466 | line_size = (line_size + 3) / 4 * 4; |
521 | 532 | int line_size = (im->xsize+1) / 2; |
522 | 533 | int x, y; |
523 | 534 | int unpacked_size; |
535 | dIMCTXim(im); | |
524 | 536 | |
525 | 537 | /* round up to nearest multiple of four */ |
526 | 538 | line_size = (line_size + 3) / 4 * 4; |
579 | 591 | int line_size = im->xsize; |
580 | 592 | int y; |
581 | 593 | int unpacked_size; |
594 | dIMCTXim(im); | |
582 | 595 | |
583 | 596 | /* round up to nearest multiple of four */ |
584 | 597 | line_size = (line_size + 3) / 4 * 4; |
626 | 639 | int y; |
627 | 640 | int line_size = 3 * im->xsize; |
628 | 641 | i_color bg; |
642 | dIMCTXim(im); | |
629 | 643 | |
630 | 644 | i_get_file_background(im, &bg); |
631 | 645 | |
680 | 694 | int i; |
681 | 695 | i_packed_t r, g, b, x; |
682 | 696 | i_color c; |
697 | dIMCTXio(ig); | |
683 | 698 | |
684 | 699 | for (i = 0; i < count; ++i) { |
685 | 700 | if (!read_packed(ig, "CCCC", &b, &g, &r, &x)) { |
718 | 733 | int bit; |
719 | 734 | unsigned char *in; |
720 | 735 | long base_offset; |
736 | dIMCTXio(ig); | |
721 | 737 | |
722 | 738 | if (compression != BI_RGB) { |
723 | i_push_errorf(0, "unknown 1-bit BMP compression (%d)", compression); | |
724 | return NULL; | |
725 | } | |
726 | ||
727 | if (xsize + 8 < xsize) { /* if there was overflow */ | |
739 | im_push_errorf(aIMCTX, 0, "unknown 1-bit BMP compression (%d)", compression); | |
740 | return NULL; | |
741 | } | |
742 | ||
743 | if ((i_img_dim)((i_img_dim_u)xsize + 8) < xsize) { /* if there was overflow */ | |
728 | 744 | /* we check with 8 because we allocate that much for the decoded |
729 | 745 | line buffer */ |
730 | 746 | i_push_error(0, "integer overflow during memory allocation"); |
751 | 767 | if (!clr_used) |
752 | 768 | clr_used = 2; |
753 | 769 | if (clr_used < 0 || clr_used > 2) { |
754 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
770 | im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used); | |
755 | 771 | return NULL; |
756 | 772 | } |
757 | 773 | |
758 | 774 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; |
759 | 775 | if (offbits < base_offset) { |
760 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
776 | im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits); | |
761 | 777 | return NULL; |
762 | 778 | } |
763 | 779 | |
846 | 862 | int size, i; |
847 | 863 | long base_offset; |
848 | 864 | int starty; |
865 | dIMCTXio(ig); | |
849 | 866 | |
850 | 867 | /* line_size is going to be smaller than xsize in most cases (and |
851 | 868 | when it's not, xsize is itself small), and hence not overflow */ |
868 | 885 | clr_used = 16; |
869 | 886 | |
870 | 887 | if (clr_used > 16 || clr_used < 0) { |
871 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
888 | im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used); | |
872 | 889 | return NULL; |
873 | 890 | } |
874 | 891 | |
875 | 892 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; |
876 | 893 | if (offbits < base_offset) { |
877 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
894 | im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits); | |
878 | 895 | return NULL; |
879 | 896 | } |
880 | 897 | |
966 | 983 | myfree(packed); |
967 | 984 | myfree(line); |
968 | 985 | i_push_error(0, "invalid data during decompression"); |
969 | mm_log((1, "read 4-bit: scanline overflow x %d + count %d vs xlimit %d (y %d)\n", | |
986 | im_log((aIMCTX, 1, "read 4-bit: scanline overflow x %d + count %d vs xlimit %d (y %d)\n", | |
970 | 987 | (int)x, count, (int)xlimit, (int)y)); |
971 | 988 | i_img_destroy(im); |
972 | 989 | return NULL; |
1016 | 1033 | myfree(packed); |
1017 | 1034 | myfree(line); |
1018 | 1035 | i_push_error(0, "invalid data during decompression"); |
1019 | mm_log((1, "read 4-bit: scanline overflow (unpacked) x %d + count %d vs xlimit %d (y %d)\n", | |
1036 | im_log((aIMCTX, 1, "read 4-bit: scanline overflow (unpacked) x %d + count %d vs xlimit %d (y %d)\n", | |
1020 | 1037 | (int)x, count, (int)xlimit, (int)y)); |
1021 | 1038 | i_img_destroy(im); |
1022 | 1039 | return NULL; |
1051 | 1068 | else { /*if (compression == BI_RLE4) {*/ |
1052 | 1069 | myfree(packed); |
1053 | 1070 | myfree(line); |
1054 | i_push_errorf(0, "unknown 4-bit BMP compression (%d)", compression); | |
1071 | im_push_errorf(aIMCTX, 0, "unknown 4-bit BMP compression (%d)", compression); | |
1055 | 1072 | i_img_destroy(im); |
1056 | 1073 | return NULL; |
1057 | 1074 | } |
1076 | 1093 | i_palidx *line; |
1077 | 1094 | int line_size = xsize; |
1078 | 1095 | long base_offset; |
1096 | dIMCTXio(ig); | |
1079 | 1097 | |
1080 | 1098 | line_size = (line_size+3) / 4 * 4; |
1081 | 1099 | if (line_size < xsize) { /* if it overflowed (unlikely, but check) */ |
1099 | 1117 | if (!clr_used) |
1100 | 1118 | clr_used = 256; |
1101 | 1119 | if (clr_used > 256 || clr_used < 0) { |
1102 | i_push_errorf(0, "out of range colors used (%d)", clr_used); | |
1120 | im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used); | |
1103 | 1121 | return NULL; |
1104 | 1122 | } |
1105 | 1123 | |
1106 | 1124 | base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; |
1107 | 1125 | if (offbits < base_offset) { |
1108 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
1126 | im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits); | |
1109 | 1127 | return NULL; |
1110 | 1128 | } |
1111 | 1129 | |
1248 | 1266 | } |
1249 | 1267 | else { |
1250 | 1268 | myfree(line); |
1251 | i_push_errorf(0, "unknown 8-bit BMP compression (%d)", compression); | |
1269 | im_push_errorf(aIMCTX, 0, "unknown 8-bit BMP compression (%d)", compression); | |
1252 | 1270 | i_img_destroy(im); |
1253 | 1271 | return NULL; |
1254 | 1272 | } |
1321 | 1339 | const char *compression_name; |
1322 | 1340 | int bytes; |
1323 | 1341 | long base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE; |
1342 | dIMCTXio(ig); | |
1324 | 1343 | |
1325 | 1344 | unpack_code[0] = *("v3V"+pix_size-2); |
1326 | 1345 | unpack_code[1] = '\0'; |
1367 | 1386 | return 0; |
1368 | 1387 | } |
1369 | 1388 | if (rmask == 0) { |
1370 | i_push_errorf(0, "Zero mask for channel %d", i); | |
1389 | im_push_errorf(aIMCTX, 0, "Zero mask for channel %d", i); | |
1371 | 1390 | return NULL; |
1372 | 1391 | } |
1373 | 1392 | masks.masks[i] = rmask; |
1391 | 1410 | base_offset += 3 * 4; |
1392 | 1411 | } |
1393 | 1412 | else { |
1394 | i_push_errorf(0, "unknown 24-bit BMP compression (%d)", compression); | |
1413 | im_push_errorf(aIMCTX, 0, "unknown 24-bit BMP compression (%d)", compression); | |
1395 | 1414 | return NULL; |
1396 | 1415 | } |
1397 | 1416 | |
1398 | 1417 | if (offbits < base_offset) { |
1399 | i_push_errorf(0, "image data offset too small (%ld)", offbits); | |
1418 | im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits); | |
1400 | 1419 | return NULL; |
1401 | 1420 | } |
1402 | 1421 |
0 | #include "imageri.h" | |
1 | #include <stdio.h> | |
2 | ||
3 | static volatile im_slot_t slot_count = 1; | |
4 | static im_slot_destroy_t *volatile slot_destructors; | |
5 | static volatile i_mutex_t slot_mutex; | |
6 | ||
7 | /* | |
8 | =item im_context_new() | |
9 | ||
10 | Create a new Imager context object. | |
11 | ||
12 | =cut | |
13 | */ | |
14 | ||
15 | im_context_t | |
16 | im_context_new(void) { | |
17 | im_context_t ctx = malloc(sizeof(im_context_struct)); | |
18 | int i; | |
19 | ||
20 | if (!slot_mutex) | |
21 | slot_mutex = i_mutex_new(); | |
22 | ||
23 | if (!ctx) | |
24 | return NULL; | |
25 | ||
26 | ctx->error_sp = IM_ERROR_COUNT-1; | |
27 | for (i = 0; i < IM_ERROR_COUNT; ++i) { | |
28 | ctx->error_alloc[i] = 0; | |
29 | ctx->error_stack[i].msg = NULL; | |
30 | ctx->error_stack[i].code = 0; | |
31 | } | |
32 | #ifdef IMAGER_LOG | |
33 | ctx->log_level = 0; | |
34 | ctx->lg_file = NULL; | |
35 | #endif | |
36 | ctx->max_width = 0; | |
37 | ctx->max_height = 0; | |
38 | ctx->max_bytes = DEF_BYTES_LIMIT; | |
39 | ||
40 | ctx->slot_alloc = slot_count; | |
41 | ctx->slots = calloc(sizeof(void *), ctx->slot_alloc); | |
42 | if (!ctx->slots) { | |
43 | free(ctx); | |
44 | return NULL; | |
45 | } | |
46 | ||
47 | ctx->refcount = 1; | |
48 | ||
49 | #ifdef IMAGER_TRACE_CONTEXT | |
50 | fprintf(stderr, "im_context: created %p\n", ctx); | |
51 | #endif | |
52 | ||
53 | ||
54 | return ctx; | |
55 | } | |
56 | ||
57 | /* | |
58 | =item im_context_refinc(ctx, where) | |
59 | X<im_context_refinc API> | |
60 | =section Context objects | |
61 | =synopsis im_context_refinc(aIMCTX, "a description"); | |
62 | ||
63 | Add a new reference to the context. | |
64 | ||
65 | =cut | |
66 | */ | |
67 | ||
68 | void | |
69 | im_context_refinc(im_context_t ctx, const char *where) { | |
70 | ++ctx->refcount; | |
71 | ||
72 | #ifdef IMAGER_TRACE_CONTEXT | |
73 | fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where, | |
74 | ctx, (unsigned long)ctx->refcount); | |
75 | #endif | |
76 | } | |
77 | ||
78 | /* | |
79 | =item im_context_refdec(ctx, where) | |
80 | X<im_context_refdec API> | |
81 | =section Context objects | |
82 | =synopsis im_context_refdec(aIMCTX, "a description"); | |
83 | ||
84 | Remove a reference to the context, releasing it if all references have | |
85 | been removed. | |
86 | ||
87 | =cut | |
88 | */ | |
89 | ||
90 | void | |
91 | im_context_refdec(im_context_t ctx, const char *where) { | |
92 | int i; | |
93 | im_slot_t slot; | |
94 | ||
95 | im_assert(ctx->refcount > 0); | |
96 | ||
97 | --ctx->refcount; | |
98 | ||
99 | #ifdef IMAGER_TRACE_CONTEXT | |
100 | fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where, | |
101 | ctx, (unsigned long)ctx->refcount); | |
102 | #endif | |
103 | ||
104 | if (ctx->refcount != 0) | |
105 | return; | |
106 | ||
107 | /* lock here to avoid slot_destructors from being moved under us */ | |
108 | i_mutex_lock(slot_mutex); | |
109 | for (slot = 0; slot < ctx->slot_alloc; ++slot) { | |
110 | if (ctx->slots[slot] && slot_destructors[slot]) | |
111 | slot_destructors[slot](ctx->slots[slot]); | |
112 | } | |
113 | i_mutex_unlock(slot_mutex); | |
114 | ||
115 | free(ctx->slots); | |
116 | ||
117 | for (i = 0; i < IM_ERROR_COUNT; ++i) { | |
118 | if (ctx->error_stack[i].msg) | |
119 | myfree(ctx->error_stack[i].msg); | |
120 | } | |
121 | #ifdef IMAGER_LOG | |
122 | if (ctx->lg_file && ctx->own_log) | |
123 | fclose(ctx->lg_file); | |
124 | #endif | |
125 | ||
126 | free(ctx); | |
127 | } | |
128 | ||
129 | /* | |
130 | =item im_context_clone(ctx) | |
131 | ||
132 | Clone an Imager context object, returning the result. | |
133 | ||
134 | The error stack is not copied from the original context. | |
135 | ||
136 | =cut | |
137 | */ | |
138 | ||
139 | im_context_t | |
140 | im_context_clone(im_context_t ctx, const char *where) { | |
141 | im_context_t nctx = malloc(sizeof(im_context_struct)); | |
142 | int i; | |
143 | ||
144 | if (!nctx) | |
145 | return NULL; | |
146 | ||
147 | nctx->slot_alloc = slot_count; | |
148 | nctx->slots = calloc(sizeof(void *), nctx->slot_alloc); | |
149 | if (!nctx->slots) { | |
150 | free(nctx); | |
151 | return NULL; | |
152 | } | |
153 | ||
154 | nctx->error_sp = IM_ERROR_COUNT-1; | |
155 | for (i = 0; i < IM_ERROR_COUNT; ++i) { | |
156 | nctx->error_alloc[i] = 0; | |
157 | nctx->error_stack[i].msg = NULL; | |
158 | } | |
159 | #ifdef IMAGER_LOG | |
160 | nctx->log_level = ctx->log_level; | |
161 | if (ctx->lg_file) { | |
162 | if (ctx->own_log) { | |
163 | int newfd = dup(fileno(ctx->lg_file)); | |
164 | nctx->own_log = 1; | |
165 | nctx->lg_file = fdopen(newfd, "w"); | |
166 | if (nctx->lg_file) | |
167 | setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ); | |
168 | } | |
169 | else { | |
170 | /* stderr */ | |
171 | nctx->lg_file = ctx->lg_file; | |
172 | nctx->own_log = 0; | |
173 | } | |
174 | } | |
175 | else { | |
176 | nctx->lg_file = NULL; | |
177 | } | |
178 | #endif | |
179 | nctx->max_width = ctx->max_width; | |
180 | nctx->max_height = ctx->max_height; | |
181 | nctx->max_bytes = ctx->max_bytes; | |
182 | ||
183 | nctx->refcount = 1; | |
184 | ||
185 | #ifdef IMAGER_TRACE_CONTEXT | |
186 | fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx); | |
187 | #endif | |
188 | ||
189 | return nctx; | |
190 | } | |
191 | ||
192 | /* | |
193 | =item im_context_slot_new(destructor) | |
194 | ||
195 | Allocate a new context-local-storage slot. | |
196 | ||
197 | C<desctructor> will be called when the context is destroyed if the | |
198 | corresponding slot is non-NULL. | |
199 | ||
200 | =cut | |
201 | */ | |
202 | ||
203 | im_slot_t | |
204 | im_context_slot_new(im_slot_destroy_t destructor) { | |
205 | im_slot_t new_slot; | |
206 | im_slot_destroy_t *new_destructors; | |
207 | if (!slot_mutex) | |
208 | slot_mutex = i_mutex_new(); | |
209 | ||
210 | i_mutex_lock(slot_mutex); | |
211 | ||
212 | new_slot = slot_count++; | |
213 | new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count); | |
214 | if (!new_destructors) | |
215 | i_fatal(1, "Cannot allocate memory for slot destructors"); | |
216 | slot_destructors = new_destructors; | |
217 | ||
218 | slot_destructors[new_slot] = destructor; | |
219 | ||
220 | i_mutex_unlock(slot_mutex); | |
221 | ||
222 | return new_slot; | |
223 | } | |
224 | ||
225 | /* | |
226 | =item im_context_slot_set(slot, value) | |
227 | ||
228 | Set the value of a slot. | |
229 | ||
230 | Returns true on success. | |
231 | ||
232 | Aborts if the slot supplied is invalid. | |
233 | ||
234 | If reallocation of slot storage fails, returns false. | |
235 | ||
236 | =cut | |
237 | */ | |
238 | ||
239 | int | |
240 | im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) { | |
241 | if (slot < 0 || slot >= slot_count) { | |
242 | fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n", | |
243 | (int)slot, (int)slot_count-1); | |
244 | abort(); | |
245 | } | |
246 | ||
247 | if (slot >= ctx->slot_alloc) { | |
248 | ssize_t i; | |
249 | size_t new_alloc = slot_count; | |
250 | void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc); | |
251 | ||
252 | if (!new_slots) | |
253 | return 0; | |
254 | ||
255 | for (i = ctx->slot_alloc; i < new_alloc; ++i) | |
256 | new_slots[i] = NULL; | |
257 | ||
258 | ctx->slots = new_slots; | |
259 | ctx->slot_alloc = new_alloc; | |
260 | } | |
261 | ||
262 | ctx->slots[slot] = value; | |
263 | ||
264 | return 1; | |
265 | } | |
266 | ||
267 | /* | |
268 | =item im_context_slot_get(ctx, slot) | |
269 | ||
270 | Retrieve the value previously stored in the given slot of the context | |
271 | object. | |
272 | ||
273 | =cut | |
274 | */ | |
275 | ||
276 | void * | |
277 | im_context_slot_get(im_context_t ctx, im_slot_t slot) { | |
278 | if (slot < 0 || slot >= slot_count) { | |
279 | fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n", | |
280 | (int)slot, (int)slot_count-1); | |
281 | abort(); | |
282 | } | |
283 | ||
284 | if (slot >= ctx->slot_alloc) | |
285 | return NULL; | |
286 | ||
287 | return ctx->slots[slot]; | |
288 | } |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include "imageri.h" |
2 | 3 | |
18 | 19 | double pc; |
19 | 20 | double res[MAXCHANNELS]; |
20 | 21 | i_img *timg; |
22 | dIMCTXim(im); | |
21 | 23 | |
22 | mm_log((1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len)); | |
23 | i_clear_error(); | |
24 | im_log((aIMCTX,1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len)); | |
25 | im_clear_error(aIMCTX); | |
24 | 26 | |
25 | 27 | if (len < 1) { |
26 | i_push_error(0, "there must be at least one coefficient"); | |
28 | im_push_error(aIMCTX, 0, "there must be at least one coefficient"); | |
27 | 29 | return 0; |
28 | 30 | } |
29 | 31 |
16 | 16 | =cut |
17 | 17 | */ |
18 | 18 | |
19 | #define IMAGER_NO_CONTEXT | |
19 | 20 | #include "imager.h" |
20 | 21 | |
21 | 22 | struct chan_copy { |
68 | 69 | int i, j; |
69 | 70 | int ilimit; |
70 | 71 | i_img *im = NULL; |
71 | ||
72 | mm_log((1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n", | |
72 | dIMCTXim(src); | |
73 | ||
74 | im_log((aIMCTX,1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n", | |
73 | 75 | im, src, coeff, outchan, inchan)); |
74 | 76 | |
75 | i_clear_error(); | |
77 | im_clear_error(aIMCTX); | |
76 | 78 | |
77 | 79 | ilimit = inchan; |
78 | 80 | if (ilimit > src->channels) |
79 | 81 | ilimit = src->channels; |
80 | 82 | if (outchan > MAXCHANNELS) { |
81 | i_push_error(0, "cannot have outchan > MAXCHANNELS"); | |
83 | im_push_error(aIMCTX, 0, "cannot have outchan > MAXCHANNELS"); | |
82 | 84 | return 0; |
83 | 85 | } |
84 | 86 | |
129 | 131 | i_color *colors; |
130 | 132 | i_palidx *vals; |
131 | 133 | |
132 | im = i_img_pal_new(src->xsize, src->ysize, outchan, | |
133 | i_maxcolors(src)); | |
134 | im = im_img_pal_new(aIMCTX, src->xsize, src->ysize, outchan, | |
135 | i_maxcolors(src)); | |
134 | 136 | |
135 | 137 | /* just translate the color table */ |
136 | 138 | count = i_colorcount(src); |
0 | 0 | #include <stdlib.h> |
1 | 1 | #include <stdio.h> |
2 | 2 | #include <string.h> |
3 | #define IMAGER_NO_CONTEXT | |
3 | 4 | #include "imager.h" |
4 | 5 | |
5 | 6 | /* |
128 | 129 | } |
129 | 130 | } |
130 | 131 | /* fprintf(stderr,"0x%08X\n",l->t); */ |
131 | if (llist_llink_push(l,l->t,data)) { | |
132 | i_fatal(3, "out of memory\n"); | |
132 | if (llist_llink_push(l,l->t,data)) { | |
133 | dIMCTX; | |
134 | im_fatal(aIMCTX, 3, "out of memory\n"); | |
133 | 135 | } |
134 | 136 | } |
135 | 137 |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include "draw.h" |
2 | 3 | #include "log.h" |
235 | 236 | void |
236 | 237 | i_arc(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,const i_color *val) { |
237 | 238 | i_int_hlines hlines; |
239 | dIMCTXim(im); | |
240 | ||
241 | im_log((aIMCTX,1,"i_arc(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, col %p)", | |
242 | im, i_DFcp(x, y), rad, d1, d2, val)); | |
238 | 243 | |
239 | 244 | i_int_init_hlines_img(&hlines, im); |
240 | 245 | |
263 | 268 | void |
264 | 269 | i_arc_cfill(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,i_fill_t *fill) { |
265 | 270 | i_int_hlines hlines; |
271 | dIMCTXim(im); | |
272 | ||
273 | im_log((aIMCTX,1,"i_arc_cfill(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, fill %p)", | |
274 | im, i_DFcp(x, y), rad, d1, d2, fill)); | |
266 | 275 | |
267 | 276 | i_int_init_hlines_img(&hlines, im); |
268 | 277 | |
362 | 371 | const i_color *val) { |
363 | 372 | double *xvals, *yvals; |
364 | 373 | int count; |
374 | dIMCTXim(im); | |
375 | ||
376 | im_log((aIMCTX,1,"i_arc_aa(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, col %p)", | |
377 | im, x, y, rad, d1, d2, val)); | |
365 | 378 | |
366 | 379 | arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2); |
367 | 380 | |
388 | 401 | i_fill_t *fill) { |
389 | 402 | double *xvals, *yvals; |
390 | 403 | int count; |
404 | dIMCTXim(im); | |
405 | ||
406 | im_log((aIMCTX,1,"i_arc_aa_cfill(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, fill %p)", | |
407 | im, x, y, rad, d1, d2, fill)); | |
391 | 408 | |
392 | 409 | arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2); |
393 | 410 | |
412 | 429 | |
413 | 430 | static |
414 | 431 | void |
415 | make_minmax_list(i_mmarray *dot, double x, double y, double radius) { | |
432 | make_minmax_list(pIMCTX, i_mmarray *dot, double x, double y, double radius) { | |
416 | 433 | float angle = 0.0; |
417 | 434 | float astep = radius>0.1 ? .5/radius : 10; |
418 | 435 | frac cx, cy, lx, ly, sx, sy; |
419 | 436 | |
420 | mm_log((1, "make_minmax_list(dot %p, x %.2f, y %.2f, radius %.2f)\n", dot, x, y, radius)); | |
437 | im_log((aIMCTX, 1, "make_minmax_list(dot %p, x %.2f, y %.2f, radius %.2f)\n", dot, x, y, radius)); | |
421 | 438 | |
422 | 439 | polar_to_plane(x, y, angle, radius, &sx, &sy); |
423 | 440 | |
493 | 510 | i_mmarray dot; |
494 | 511 | i_color temp; |
495 | 512 | i_img_dim ly; |
496 | ||
497 | mm_log((1, "i_circle_aa(im %p, centre(" i_DFp "), rad %.2f, val %p)\n", | |
513 | dIMCTXim(im); | |
514 | ||
515 | im_log((aIMCTX, 1, "i_circle_aa(im %p, centre(" i_DFp "), rad %.2f, val %p)\n", | |
498 | 516 | im, i_DFcp(x, y), rad, val)); |
499 | 517 | |
500 | 518 | i_mmarray_cr(&dot,16*im->ysize); |
501 | make_minmax_list(&dot, x, y, rad); | |
519 | make_minmax_list(aIMCTX, &dot, x, y, rad); | |
502 | 520 | |
503 | 521 | for(ly = 0; ly<im->ysize; ly++) { |
504 | 522 | int ix, cy, minx = INT_MAX, maxx = INT_MIN; |
568 | 586 | i_img_dim x, y; |
569 | 587 | i_img_dim dx, dy; |
570 | 588 | int error; |
571 | ||
572 | i_clear_error(); | |
589 | dIMCTXim(im); | |
590 | ||
591 | im_log((aIMCTX, 1, "i_circle_out(im %p, centre(" i_DFp "), rad %" i_DF ", col %p)\n", | |
592 | im, i_DFcp(xc, yc), i_DFc(r), col)); | |
593 | ||
594 | im_clear_error(aIMCTX); | |
573 | 595 | |
574 | 596 | if (r < 0) { |
575 | i_push_error(0, "circle: radius must be non-negative"); | |
597 | im_push_error(aIMCTX, 0, "circle: radius must be non-negative"); | |
576 | 598 | return 0; |
577 | 599 | } |
578 | 600 | |
683 | 705 | i_img_dim seg2 = scale * 4; |
684 | 706 | i_img_dim seg3 = scale * 6; |
685 | 707 | i_img_dim seg4 = scale * 8; |
686 | ||
687 | i_clear_error(); | |
708 | dIMCTXim(im); | |
709 | ||
710 | im_log((aIMCTX,1,"i_arc_out(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)", | |
711 | im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col)); | |
712 | ||
713 | im_clear_error(aIMCTX); | |
688 | 714 | |
689 | 715 | if (r <= 0) { |
690 | i_push_error(0, "arc: radius must be non-negative"); | |
716 | im_push_error(aIMCTX, 0, "arc: radius must be non-negative"); | |
691 | 717 | return 0; |
692 | 718 | } |
693 | 719 | if (d1 + 360 <= d2) |
819 | 845 | double t; |
820 | 846 | i_color workc = *col; |
821 | 847 | int orig_alpha = col->channel[3]; |
822 | ||
823 | i_clear_error(); | |
848 | dIMCTXim(im); | |
849 | ||
850 | im_log((aIMCTX,1,"i_circle_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", col %p)", | |
851 | im, i_DFcp(xc, yc), i_DFc(r), col)); | |
852 | ||
853 | im_clear_error(aIMCTX); | |
824 | 854 | if (r <= 0) { |
825 | i_push_error(0, "arc: radius must be non-negative"); | |
855 | im_push_error(aIMCTX, 0, "arc: radius must be non-negative"); | |
826 | 856 | return 0; |
827 | 857 | } |
828 | 858 | i = r; |
929 | 959 | i_img_dim seg2 = scale * 4; |
930 | 960 | i_img_dim seg3 = scale * 6; |
931 | 961 | i_img_dim seg4 = scale * 8; |
932 | ||
933 | i_clear_error(); | |
962 | dIMCTXim(im); | |
963 | ||
964 | im_log((aIMCTX,1,"i_arc_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)", | |
965 | im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col)); | |
966 | ||
967 | im_clear_error(aIMCTX); | |
934 | 968 | if (r <= 0) { |
935 | i_push_error(0, "arc: radius must be non-negative"); | |
969 | im_push_error(aIMCTX, 0, "arc: radius must be non-negative"); | |
936 | 970 | return 0; |
937 | 971 | } |
938 | 972 | if (d1 + 360 <= d2) |
1052 | 1086 | void |
1053 | 1087 | i_box(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val) { |
1054 | 1088 | i_img_dim x,y; |
1055 | mm_log((1,"i_box(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1089 | dIMCTXim(im); | |
1090 | ||
1091 | im_log((aIMCTX, 1,"i_box(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1056 | 1092 | im, i_DFcp(x1,y1), i_DFcp(x2,y2), val)); |
1057 | 1093 | for(x=x1;x<x2+1;x++) { |
1058 | 1094 | i_ppix(im,x,y1,val); |
1079 | 1115 | i_box_filled(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_color *val) { |
1080 | 1116 | i_img_dim x, y, width; |
1081 | 1117 | i_palidx index; |
1082 | ||
1083 | mm_log((1,"i_box_filled(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1118 | dIMCTXim(im); | |
1119 | ||
1120 | im_log((aIMCTX,1,"i_box_filled(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1084 | 1121 | im, i_DFcp(x1, y1), i_DFcp(x2,y2) ,val)); |
1085 | 1122 | |
1086 | 1123 | if (x1 > x2 || y1 > y2 |
1139 | 1176 | int |
1140 | 1177 | i_box_filledf(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_fcolor *val) { |
1141 | 1178 | i_img_dim x, y, width; |
1142 | ||
1143 | mm_log((1,"i_box_filledf(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1179 | dIMCTXim(im); | |
1180 | ||
1181 | im_log((aIMCTX, 1,"i_box_filledf(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n", | |
1144 | 1182 | im, i_DFcp(x1, y1), i_DFcp(x2, y2), val)); |
1145 | 1183 | |
1146 | 1184 | if (x1 > x2 || y1 > y2 |
1197 | 1235 | void |
1198 | 1236 | i_box_cfill(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_fill_t *fill) { |
1199 | 1237 | i_render r; |
1200 | ||
1201 | mm_log((1,"i_box_cfill(im* %p, p1(" i_DFp "), p2(" i_DFp "), fill %p)\n", | |
1238 | dIMCTXim(im); | |
1239 | ||
1240 | im_log((aIMCTX,1,"i_box_cfill(im* %p, p1(" i_DFp "), p2(" i_DFp "), fill %p)\n", | |
1202 | 1241 | im, i_DFcp(x1, y1), i_DFcp(x2,y2), fill)); |
1203 | 1242 | |
1204 | 1243 | ++x2; |
1815 | 1854 | struct i_bitmap *btm; |
1816 | 1855 | i_img_dim x, y; |
1817 | 1856 | i_color val; |
1818 | ||
1819 | i_clear_error(); | |
1857 | dIMCTXim(im); | |
1858 | ||
1859 | im_log((aIMCTX, 1, "i_flood_fill(im %p, seed(" i_DFp "), col %p)", | |
1860 | im, i_DFcp(seedx, seedy), dcol)); | |
1861 | ||
1862 | im_clear_error(aIMCTX); | |
1820 | 1863 | if (seedx < 0 || seedx >= im->xsize || |
1821 | 1864 | seedy < 0 || seedy >= im->ysize) { |
1822 | i_push_error(0, "i_flood_cfill: Seed pixel outside of image"); | |
1865 | im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image"); | |
1823 | 1866 | return 0; |
1824 | 1867 | } |
1825 | 1868 | |
1856 | 1899 | i_img_dim bxmin, bxmax, bymin, bymax; |
1857 | 1900 | struct i_bitmap *btm; |
1858 | 1901 | i_color val; |
1859 | ||
1860 | i_clear_error(); | |
1902 | dIMCTXim(im); | |
1903 | ||
1904 | im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), fill %p)", | |
1905 | im, i_DFcp(seedx, seedy), fill)); | |
1906 | ||
1907 | im_clear_error(aIMCTX); | |
1861 | 1908 | |
1862 | 1909 | if (seedx < 0 || seedx >= im->xsize || |
1863 | 1910 | seedy < 0 || seedy >= im->ysize) { |
1864 | i_push_error(0, "i_flood_cfill: Seed pixel outside of image"); | |
1911 | im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image"); | |
1865 | 1912 | return 0; |
1866 | 1913 | } |
1867 | 1914 | |
1898 | 1945 | i_img_dim bxmin, bxmax, bymin, bymax; |
1899 | 1946 | struct i_bitmap *btm; |
1900 | 1947 | i_img_dim x, y; |
1901 | ||
1902 | i_clear_error(); | |
1948 | dIMCTXim(im); | |
1949 | ||
1950 | im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), dcol %p, border %p)", | |
1951 | im, i_DFcp(seedx, seedy), dcol, border)); | |
1952 | ||
1953 | im_clear_error(aIMCTX); | |
1903 | 1954 | if (seedx < 0 || seedx >= im->xsize || |
1904 | 1955 | seedy < 0 || seedy >= im->ysize) { |
1905 | i_push_error(0, "i_flood_cfill: Seed pixel outside of image"); | |
1956 | im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image"); | |
1906 | 1957 | return 0; |
1907 | 1958 | } |
1908 | 1959 | |
1937 | 1988 | const i_color *border) { |
1938 | 1989 | i_img_dim bxmin, bxmax, bymin, bymax; |
1939 | 1990 | struct i_bitmap *btm; |
1940 | ||
1941 | i_clear_error(); | |
1991 | dIMCTXim(im); | |
1992 | ||
1993 | im_log((aIMCTX, 1, "i_flood_cfill_border(im %p, seed(" i_DFp "), fill %p, border %p)", | |
1994 | im, i_DFcp(seedx, seedy), fill, border)); | |
1995 | ||
1996 | im_clear_error(aIMCTX); | |
1942 | 1997 | |
1943 | 1998 | if (seedx < 0 || seedx >= im->xsize || |
1944 | 1999 | seedy < 0 || seedy >= im->ysize) { |
1945 | i_push_error(0, "i_flood_cfill_border: Seed pixel outside of image"); | |
2000 | im_push_error(aIMCTX, 0, "i_flood_cfill_border: Seed pixel outside of image"); | |
1946 | 2001 | return 0; |
1947 | 2002 | } |
1948 | 2003 |
10 | 10 | typedef void *minthandle_t; |
11 | 11 | #endif |
12 | 12 | |
13 | #include "plug.h" | |
13 | #include "ext.h" | |
14 | 14 | |
15 | 15 | struct DSO_handle_tag { |
16 | 16 | minthandle_t handle; |
22 | 22 | #include "dynaload.h" |
23 | 23 | /* #include "XSUB.h" so we can compile on threaded perls */ |
24 | 24 | #include "imageri.h" |
25 | ||
26 | static im_context_t | |
27 | do_get_context(void) { | |
28 | return im_get_context(); | |
29 | } | |
25 | 30 | |
26 | 31 | static symbol_table_t symbol_table= |
27 | 32 | { |
28 | 33 | i_has_format, |
29 | 34 | ICL_set_internal, |
30 | 35 | ICL_info, |
31 | i_img_new, | |
32 | i_img_empty, | |
33 | i_img_empty_ch, | |
36 | do_get_context, | |
37 | im_img_empty_ch, | |
34 | 38 | i_img_exorcise, |
35 | 39 | i_img_info, |
36 | 40 | i_img_setmask, |
40 | 40 | rcomm('rm testout/*'); |
41 | 41 | rcomm(@precommands); |
42 | 42 | my $make = $Config{make}; |
43 | rcomm("$^X Makefile.PL") || rcomm("$make") || rcomm("$make test TEST_VERBOSE=1"); | |
43 | rcomm("$^X Makefile.PL --verbose") || rcomm("$make") || rcomm("$make test TEST_VERBOSE=1"); | |
44 | 44 | head("Logfiles from run"); |
45 | 45 | dumplogs(); |
46 | 46 |
60 | 60 | =cut |
61 | 61 | */ |
62 | 62 | |
63 | #include "imager.h" | |
63 | #include "imageri.h" | |
64 | 64 | #include <stdio.h> |
65 | 65 | #include <stdlib.h> |
66 | 66 | |
67 | /* we never actually use the last item - it's the NULL terminator */ | |
68 | #define ERRSTK 20 | |
69 | static i_errmsg error_stack[ERRSTK]; | |
70 | static int error_sp = ERRSTK - 1; | |
71 | /* we track the amount of space used each string, so we don't reallocate | |
72 | space unless we need to. | |
73 | This also means that a memory tracking library may see the memory | |
74 | allocated for this as a leak. */ | |
75 | static int error_space[ERRSTK]; | |
76 | ||
77 | static i_error_cb error_cb; | |
78 | static i_failed_cb failed_cb; | |
79 | static int failures_fatal; | |
80 | static char *argv0; | |
81 | ||
82 | /* | |
83 | =item i_set_argv0(char const *program) | |
84 | ||
85 | Sets the name of the program to be displayed in fatal error messages. | |
86 | ||
87 | The simplest way to use this is just: | |
88 | ||
89 | i_set_argv0(argv[0]); | |
90 | ||
91 | when your program starts. | |
92 | */ | |
93 | void i_set_argv0(char const *name) { | |
94 | char *dupl; | |
95 | if (!name) | |
96 | return; | |
97 | /* if the user has an existing string of MAXINT length then | |
98 | the system is broken anyway */ | |
99 | dupl = mymalloc(strlen(name)+1); /* check 17jul05 tonyc */ | |
100 | strcpy(dupl, name); | |
101 | if (argv0) | |
102 | myfree(argv0); | |
103 | argv0 = dupl; | |
104 | } | |
105 | ||
106 | /* | |
107 | =item i_set_failure_fatal(int failure_fatal) | |
108 | ||
109 | If failure_fatal is non-zero then any future failures will result in | |
110 | Imager exiting your program with a message describing the failure. | |
111 | ||
112 | Returns the previous setting. | |
113 | ||
114 | =cut | |
115 | */ | |
116 | int i_set_failures_fatal(int fatal) { | |
117 | int old = failures_fatal; | |
118 | failures_fatal = fatal; | |
119 | ||
120 | return old; | |
121 | } | |
122 | ||
123 | /* | |
124 | =item i_set_error_cb(i_error_cb) | |
125 | ||
126 | Sets a callback function that is called each time an error is pushed | |
127 | onto the error stack. | |
128 | ||
129 | Returns the previous callback. | |
130 | ||
131 | i_set_failed_cb() is probably more useful. | |
132 | ||
133 | =cut | |
134 | */ | |
135 | i_error_cb i_set_error_cb(i_error_cb cb) { | |
136 | i_error_cb old = error_cb; | |
137 | error_cb = cb; | |
138 | ||
139 | return old; | |
140 | } | |
141 | ||
142 | /* | |
143 | =item i_set_failed_cb(i_failed_cb cb) | |
144 | ||
145 | Sets a callback function that is called each time an Imager function | |
146 | fails. | |
147 | ||
148 | Returns the previous callback. | |
149 | ||
150 | =cut | |
151 | */ | |
152 | i_failed_cb i_set_failed_cb(i_failed_cb cb) { | |
153 | i_failed_cb old = failed_cb; | |
154 | failed_cb = cb; | |
155 | ||
156 | return old; | |
157 | } | |
158 | ||
159 | /* | |
160 | =item i_errors() | |
67 | /* | |
68 | =item im_errors(ctx) | |
69 | =synopsis i_errmsg *errors = im_errors(aIMCTX); | |
70 | =synopsis i_errmsg *errors = i_errors(); | |
161 | 71 | |
162 | 72 | Returns a pointer to the first element of an array of error messages, |
163 | 73 | terminated by a NULL pointer. The highest level message is first. |
164 | 74 | |
165 | =cut | |
166 | */ | |
167 | i_errmsg *i_errors() { | |
168 | return error_stack + error_sp; | |
75 | Also callable as C<i_errors()>. | |
76 | ||
77 | =cut | |
78 | */ | |
79 | i_errmsg *im_errors(im_context_t ctx) { | |
80 | return ctx->error_stack + ctx->error_sp; | |
169 | 81 | } |
170 | 82 | |
171 | 83 | /* |
181 | 93 | |
182 | 94 | =over |
183 | 95 | |
184 | =item i_clear_error() | |
96 | =item im_clear_error(ctx) | |
97 | X<im_clear_error API>X<i_clear_error API> | |
98 | =synopsis im_clear_error(aIMCTX); | |
185 | 99 | =synopsis i_clear_error(); |
186 | 100 | =category Error handling |
187 | 101 | |
189 | 103 | |
190 | 104 | Called by any Imager function before doing any other processing. |
191 | 105 | |
192 | =cut | |
193 | */ | |
194 | void i_clear_error() { | |
106 | Also callable as C<i_clear_error()>. | |
107 | ||
108 | =cut | |
109 | */ | |
110 | ||
111 | void | |
112 | im_clear_error(im_context_t ctx) { | |
195 | 113 | #ifdef IMAGER_DEBUG_MALLOC |
196 | 114 | int i; |
197 | 115 | |
198 | for (i = 0; i < ERRSTK; ++i) { | |
199 | if (error_space[i]) { | |
200 | myfree(error_stack[i].msg); | |
201 | error_stack[i].msg = NULL; | |
202 | error_space[i] = 0; | |
116 | for (i = 0; i < IM_ERROR_COUNT; ++i) { | |
117 | if (ctx->error_space[i]) { | |
118 | myfree(ctx->error_stack[i].msg); | |
119 | ctx->error_stack[i].msg = NULL; | |
120 | ctx->error_space[i] = 0; | |
203 | 121 | } |
204 | 122 | } |
205 | 123 | #endif |
206 | error_sp = ERRSTK-1; | |
207 | } | |
208 | ||
209 | /* | |
210 | =item i_push_error(int code, char const *msg) | |
124 | ctx->error_sp = IM_ERROR_COUNT-1; | |
125 | } | |
126 | ||
127 | /* | |
128 | =item im_push_error(ctx, code, message) | |
129 | X<im_push_error API>X<i_push_error API> | |
211 | 130 | =synopsis i_push_error(0, "Yep, it's broken"); |
212 | 131 | =synopsis i_push_error(errno, "Error writing"); |
132 | =synopsis im_push_error(aIMCTX, 0, "Something is wrong"); | |
213 | 133 | =category Error handling |
214 | 134 | |
215 | 135 | Called by an Imager function to push an error message onto the stack. |
220 | 140 | |
221 | 141 | =cut |
222 | 142 | */ |
223 | void i_push_error(int code, char const *msg) { | |
143 | void | |
144 | im_push_error(im_context_t ctx, int code, char const *msg) { | |
224 | 145 | size_t size = strlen(msg)+1; |
225 | 146 | |
226 | if (error_sp <= 0) | |
147 | if (ctx->error_sp <= 0) | |
227 | 148 | /* bad, bad programmer */ |
228 | 149 | return; |
229 | 150 | |
230 | --error_sp; | |
231 | if (error_space[error_sp] < size) { | |
232 | if (error_stack[error_sp].msg) | |
233 | myfree(error_stack[error_sp].msg); | |
151 | --ctx->error_sp; | |
152 | if (ctx->error_alloc[ctx->error_sp] < size) { | |
153 | if (ctx->error_stack[ctx->error_sp].msg) | |
154 | myfree(ctx->error_stack[ctx->error_sp].msg); | |
234 | 155 | /* memory allocated on the following line is only ever released when |
235 | 156 | we need a bigger string */ |
236 | 157 | /* size is size (len+1) of an existing string, overflow would mean |
237 | 158 | the system is broken anyway */ |
238 | error_stack[error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */ | |
239 | error_space[error_sp] = size; | |
240 | } | |
241 | strcpy(error_stack[error_sp].msg, msg); | |
242 | error_stack[error_sp].code = code; | |
243 | ||
244 | if (error_cb) | |
245 | error_cb(code, msg); | |
246 | } | |
247 | ||
248 | /* | |
249 | =item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>) | |
250 | ||
159 | ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */ | |
160 | ctx->error_alloc[ctx->error_sp] = size; | |
161 | } | |
162 | strcpy(ctx->error_stack[ctx->error_sp].msg, msg); | |
163 | ctx->error_stack[ctx->error_sp].code = code; | |
164 | } | |
165 | ||
166 | /* | |
167 | =item im_push_errorvf(ctx, code, format, args) | |
168 | X<im_push_error_vf API>X<i_push_errorvf API> | |
169 | =synopsis va_args args; | |
170 | =synopsis va_start(args, lastarg); | |
171 | =synopsis im_push_errorvf(ctx, code, format, args); | |
251 | 172 | =category Error handling |
252 | 173 | |
253 | 174 | Intended for use by higher level functions, takes a varargs pointer |
255 | 176 | |
256 | 177 | Does not support perl specific format codes. |
257 | 178 | |
258 | =cut | |
259 | */ | |
260 | void i_push_errorvf(int code, char const *fmt, va_list ap) { | |
179 | Also callable as C<i_push_errorvf(code, format, args)> | |
180 | ||
181 | =cut | |
182 | */ | |
183 | void | |
184 | im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) { | |
261 | 185 | char buf[1024]; |
262 | 186 | #if defined(IMAGER_VSNPRINTF) |
263 | 187 | vsnprintf(buf, sizeof(buf), fmt, ap); |
270 | 194 | */ |
271 | 195 | vsprintf(buf, fmt, ap); |
272 | 196 | #endif |
273 | i_push_error(code, buf); | |
197 | im_push_error(ctx, code, buf); | |
274 | 198 | } |
275 | 199 | |
276 | 200 | /* |
284 | 208 | |
285 | 209 | =cut |
286 | 210 | */ |
287 | void i_push_errorf(int code, char const *fmt, ...) { | |
211 | void | |
212 | i_push_errorf(int code, char const *fmt, ...) { | |
288 | 213 | va_list ap; |
289 | 214 | va_start(ap, fmt); |
290 | 215 | i_push_errorvf(code, fmt, ap); |
216 | va_end(ap); | |
217 | } | |
218 | ||
219 | /* | |
220 | =item im_push_errorf(ctx, code, char const *fmt, ...) | |
221 | =synopsis im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno); | |
222 | =category Error handling | |
223 | ||
224 | A version of im_push_error() that does printf() like formatting. | |
225 | ||
226 | Does not support perl specific format codes. | |
227 | ||
228 | =cut | |
229 | */ | |
230 | void | |
231 | im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) { | |
232 | va_list ap; | |
233 | va_start(ap, fmt); | |
234 | im_push_errorvf(ctx, code, fmt, ap); | |
291 | 235 | va_end(ap); |
292 | 236 | } |
293 | 237 | |
356 | 300 | |
357 | 301 | #endif |
358 | 302 | |
303 | #ifdef IM_ASSERT | |
304 | ||
359 | 305 | /* |
360 | 306 | =item im_assert_fail(file, line, message) |
361 | 307 | |
362 | 308 | Called when an im_assert() assertion fails. |
309 | ||
310 | Only available when Imager is built with assertions. | |
363 | 311 | |
364 | 312 | =cut |
365 | 313 | */ |
368 | 316 | im_assert_fail(char const *file, int line, char const *message) { |
369 | 317 | fprintf(stderr, "Assertion failed line %d file %s: %s\n", |
370 | 318 | line, file, message); |
371 | exit(EXIT_FAILURE); | |
372 | } | |
319 | abort(); | |
320 | } | |
321 | ||
322 | #endif | |
373 | 323 | |
374 | 324 | /* |
375 | 325 | =back |
0 | #include "imager.h" | |
0 | #include "imdatatypes.h" | |
1 | 1 | |
2 | 2 | #ifndef IMAGER_EXT_H |
3 | 3 | #define IMAGER_EXT_H |
19 | 19 | int (*getobj)(void *hv_t,char* key,char* type,void **store); |
20 | 20 | } UTIL_table_t; |
21 | 21 | |
22 | typedef struct { | |
23 | undef_int (*i_has_format)(char *frmt); | |
24 | i_color*(*ICL_set)(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a); | |
25 | void (*ICL_info)(const i_color *cl); | |
26 | ||
27 | im_context_t (*im_get_context_f)(void); | |
28 | i_img*(*im_img_empty_ch_f)(im_context_t, i_img *im,i_img_dim x,i_img_dim y,int ch); | |
29 | void(*i_img_exorcise_f)(i_img *im); | |
30 | ||
31 | void(*i_img_info_f)(i_img *im,i_img_dim *info); | |
32 | ||
33 | void(*i_img_setmask_f)(i_img *im,int ch_mask); | |
34 | int (*i_img_getmask_f)(i_img *im); | |
35 | ||
36 | /* | |
37 | int (*i_ppix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
38 | int (*i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
39 | */ | |
40 | void(*i_box)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val); | |
41 | void(*i_line)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val,int endp); | |
42 | void(*i_arc)(i_img *im,i_img_dim x,i_img_dim y,double rad,double d1,double d2,const i_color *val); | |
43 | void(*i_copyto)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty); | |
44 | void(*i_copyto_trans)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans); | |
45 | int(*i_rubthru)(i_img *im,i_img *src,i_img_dim tx,i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy); | |
46 | ||
47 | } symbol_table_t; | |
48 | ||
22 | 49 | #endif |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include "imageri.h" |
2 | 3 | |
855 | 856 | } |
856 | 857 | else if (f->yoff) { |
857 | 858 | ry += ix * f->yoff; |
858 | iy = ry / f->src->xsize; | |
859 | iy = ry / f->src->ysize; | |
859 | 860 | } |
860 | 861 | rx -= ix * f->src->xsize; |
861 | 862 | ry -= iy * f->src->ysize; |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include "imageri.h" |
2 | 3 | #include <stdlib.h> |
72 | 73 | unsigned char ch; |
73 | 74 | unsigned int new_color; |
74 | 75 | i_color rcolor; |
75 | ||
76 | mm_log((1,"i_contrast(im %p, intensity %f)\n", im, intensity)); | |
76 | dIMCTXim(im); | |
77 | ||
78 | im_log((aIMCTX, 1,"i_contrast(im %p, intensity %f)\n", im, intensity)); | |
77 | 79 | |
78 | 80 | if(intensity < 0) return; |
79 | 81 | |
99 | 101 | i_img_dim x, y; |
100 | 102 | int ch; |
101 | 103 | int invert_channels = all ? im->channels : i_img_color_channels(im); |
102 | ||
103 | mm_log((1,"i_hardinvert(im %p)\n", im)); | |
104 | dIMCTXim(im); | |
105 | ||
106 | im_log((aIMCTX,1,"i_hardinvert)low(im %p, all %d)\n", im, all)); | |
104 | 107 | |
105 | 108 | #code im->bits <= 8 |
106 | 109 | IM_COLOR *row, *entry; |
183 | 186 | float damount = amount * 2; |
184 | 187 | i_color rcolor; |
185 | 188 | int color_inc = 0; |
186 | ||
187 | mm_log((1,"i_noise(im %p, intensity %.2f\n", im, amount)); | |
189 | dIMCTXim(im); | |
190 | ||
191 | im_log((aIMCTX, 1,"i_noise(im %p, intensity %.2f\n", im, amount)); | |
188 | 192 | |
189 | 193 | if(amount < 0) return; |
190 | 194 | |
244 | 248 | double aX, aY, aL; |
245 | 249 | double fZ; |
246 | 250 | unsigned char px1, px2, py1, py2; |
247 | ||
251 | dIMCTXim(im); | |
248 | 252 | i_img new_im; |
249 | 253 | |
250 | mm_log((1, "i_bumpmap(im %p, add_im %p, channel %d, light(" i_DFp "), st %" i_DF ")\n", | |
254 | im_log((aIMCTX, 1, "i_bumpmap(im %p, add_im %p, channel %d, light(" i_DFp "), st %" i_DF ")\n", | |
251 | 255 | im, bump, channel, i_DFcp(light_x, light_y), i_DFc(st))); |
252 | 256 | |
253 | 257 | |
254 | 258 | if(channel >= bump->channels) { |
255 | mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels)); | |
259 | im_log((aIMCTX, 1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels)); | |
256 | 260 | return; |
257 | 261 | } |
258 | 262 | |
426 | 430 | fvec R; /* Reflection vector */ |
427 | 431 | fvec V; /* Vision vector */ |
428 | 432 | |
429 | mm_log((1, "i_bumpmap_complex(im %p, bump %p, channel %d, t(" i_DFp | |
433 | dIMCTXim(im); | |
434 | ||
435 | im_log((aIMCTX, 1, "i_bumpmap_complex(im %p, bump %p, channel %d, t(" i_DFp | |
430 | 436 | "), Lx %.2f, Ly %.2f, Lz %.2f, cd %.2f, cs %.2f, n %.2f, Ia %p, Il %p, Is %p)\n", |
431 | 437 | im, bump, channel, i_DFcp(tx, ty), Lx, Ly, Lz, cd, cs, n, Ia, Il, Is)); |
432 | 438 | |
433 | 439 | if (channel >= bump->channels) { |
434 | mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels)); | |
440 | im_log((aIMCTX, 1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels)); | |
435 | 441 | return; |
436 | 442 | } |
437 | 443 | |
667 | 673 | i_img_dim gsum, gmin, gmax; |
668 | 674 | i_img_dim bsum, bmin, bmax; |
669 | 675 | i_img_dim rcl, rcu, gcl, gcu, bcl, bcu; |
670 | ||
671 | mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew)); | |
676 | dIMCTXim(im); | |
677 | ||
678 | im_log((aIMCTX, 1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew)); | |
672 | 679 | |
673 | 680 | rsum=gsum=bsum=0; |
674 | 681 | for(i=0;i<256;i++) rhist[i]=ghist[i]=bhist[i] = 0; |
923 | 930 | size_t bytes; |
924 | 931 | |
925 | 932 | double *fdist; |
926 | ||
927 | mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure)); | |
933 | dIMCTXim(im); | |
934 | ||
935 | im_log((aIMCTX, 1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure)); | |
928 | 936 | |
929 | 937 | for(p = 0; p<num; p++) { |
930 | mm_log((1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p]))); | |
938 | im_log((aIMCTX,1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p]))); | |
931 | 939 | ICL_info(&ival[p]); |
932 | 940 | } |
933 | 941 | |
963 | 971 | fdist[p] = i_max(xd*xd, yd*yd); /* manhattan distance */ |
964 | 972 | break; |
965 | 973 | default: |
966 | i_fatal(3,"i_gradgen: Unknown distance measure\n"); | |
974 | im_fatal(aIMCTX, 3,"i_gradgen: Unknown distance measure\n"); | |
967 | 975 | } |
968 | 976 | cs += fdist[p]; |
969 | 977 | } |
990 | 998 | i_img_dim x, y; |
991 | 999 | i_img_dim xsize = im->xsize; |
992 | 1000 | i_img_dim ysize = im->ysize; |
993 | ||
994 | mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure)); | |
1001 | dIMCTXim(im); | |
1002 | ||
1003 | im_log((aIMCTX,1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure)); | |
995 | 1004 | |
996 | 1005 | for(p = 0; p<num; p++) { |
997 | mm_log((1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p]))); | |
1006 | im_log((aIMCTX, 1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p]))); | |
998 | 1007 | ICL_info(&ival[p]); |
999 | 1008 | } |
1000 | 1009 | |
1017 | 1026 | mindist = i_max(xd*xd, yd*yd); /* manhattan distance */ |
1018 | 1027 | break; |
1019 | 1028 | default: |
1020 | i_fatal(3,"i_nearest_color: Unknown distance measure\n"); | |
1029 | im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n"); | |
1021 | 1030 | } |
1022 | 1031 | |
1023 | 1032 | for(p = 1; p<num; p++) { |
1034 | 1043 | curdist = i_max(xd*xd, yd*yd); /* manhattan distance */ |
1035 | 1044 | break; |
1036 | 1045 | default: |
1037 | i_fatal(3,"i_nearest_color: Unknown distance measure\n"); | |
1046 | im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n"); | |
1038 | 1047 | } |
1039 | 1048 | if (curdist < mindist) { |
1040 | 1049 | mindist = curdist; |
1124 | 1133 | i_img_dim ysize = im->ysize; |
1125 | 1134 | int *cmatch; |
1126 | 1135 | size_t ival_bytes, tval_bytes; |
1127 | ||
1128 | mm_log((1,"i_nearest_color(im %p, num %d, xo %p, yo %p, oval %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure)); | |
1136 | dIMCTXim(im); | |
1137 | ||
1138 | im_log((aIMCTX, 1,"i_nearest_color(im %p, num %d, xo %p, yo %p, oval %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure)); | |
1129 | 1139 | |
1130 | 1140 | i_clear_error(); |
1131 | 1141 | |
1178 | 1188 | mindist = i_max(xd*xd, yd*yd); /* manhattan distance */ |
1179 | 1189 | break; |
1180 | 1190 | default: |
1181 | i_fatal(3,"i_nearest_color: Unknown distance measure\n"); | |
1191 | im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n"); | |
1182 | 1192 | } |
1183 | 1193 | |
1184 | 1194 | for(p = 1; p<num; p++) { |
1195 | 1205 | curdist = i_max(xd*xd, yd*yd); /* manhattan distance */ |
1196 | 1206 | break; |
1197 | 1207 | default: |
1198 | i_fatal(3,"i_nearest_color: Unknown distance measure\n"); | |
1208 | im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n"); | |
1199 | 1209 | } |
1200 | 1210 | if (curdist < mindist) { |
1201 | 1211 | mindist = curdist; |
1319 | 1329 | i_img *out; |
1320 | 1330 | int outchans, diffchans; |
1321 | 1331 | i_img_dim xsize, ysize; |
1332 | dIMCTXim(im1); | |
1322 | 1333 | |
1323 | 1334 | i_clear_error(); |
1324 | 1335 | if (im1->channels != im2->channels) { |
1641 | 1652 | i_fountain_seg *my_segs; |
1642 | 1653 | i_fill_combine_f combine_func = NULL; |
1643 | 1654 | i_fill_combinef_f combinef_func = NULL; |
1655 | dIMCTXim(im); | |
1644 | 1656 | |
1645 | 1657 | i_clear_error(); |
1646 | 1658 |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | |
2 | 3 | static void flip_h(i_img *im); |
21 | 22 | |
22 | 23 | undef_int |
23 | 24 | i_flipxy(i_img *im, int direction) { |
25 | dIMCTXim(im); | |
24 | 26 | i_clear_error(); |
25 | 27 | |
26 | mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction )); | |
28 | im_log((aIMCTX, 1, "i_flipxy(im %p, direction %d)\n", im, direction )); | |
27 | 29 | |
28 | 30 | if (!im) |
29 | 31 | return 0; |
42 | 44 | break; |
43 | 45 | |
44 | 46 | default: |
45 | mm_log((1, "i_flipxy: direction is invalid\n" )); | |
46 | i_push_errorf(0, "direction %d invalid", direction); | |
47 | im_log((aIMCTX, 1, "i_flipxy: direction is invalid\n" )); | |
48 | im_push_errorf(aIMCTX, 0, "direction %d invalid", direction); | |
47 | 49 | return 0; |
48 | ||
49 | 50 | } |
50 | 51 | return 1; |
51 | 52 | } |
0 | #include "imager.h" | |
1 | #include "imrender.h" | |
2 | ||
3 | #include <sys/types.h> | |
4 | #include <sys/stat.h> | |
5 | #include <fcntl.h> | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | ||
10 | #ifdef HAVE_LIBT1 | |
11 | #endif | |
12 | ||
13 | ||
14 | /* | |
15 | =head1 NAME | |
16 | ||
17 | font.c - implements font handling functions for t1 and truetype fonts | |
18 | ||
19 | =head1 SYNOPSIS | |
20 | ||
21 | i_init_fonts(); | |
22 | ||
23 | #ifdef HAVE_LIBT1 | |
24 | fontnum = i_t1_new(path_to_pfb, path_to_afm); | |
25 | i_t1_bbox(fontnum, points, "foo", 3, i_img_dim cords[BOUNDING_BOX_COUNT]); | |
26 | rc = i_t1_destroy(fontnum); | |
27 | #endif | |
28 | ||
29 | #ifdef HAVE_LIBTT | |
30 | handle = i_tt_new(path_to_ttf); | |
31 | rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8); | |
32 | i_tt_destroy(handle); | |
33 | ||
34 | // and much more | |
35 | ||
36 | =head1 DESCRIPTION | |
37 | ||
38 | font.c implements font creation, rendering, bounding box functions and | |
39 | more for Imager. | |
40 | ||
41 | =head1 FUNCTION REFERENCE | |
42 | ||
43 | Some of these functions are internal. | |
44 | ||
45 | =over | |
46 | ||
47 | =cut | |
48 | ||
49 | */ | |
50 | ||
51 | ||
52 | /* Truetype font support */ | |
53 | #ifdef HAVE_LIBTT | |
54 | ||
55 | /* These are enabled by default when configuring Freetype 1.x | |
56 | I haven't a clue how to reliably detect it at compile time. | |
57 | ||
58 | We need a compilation probe in Makefile.PL | |
59 | */ | |
60 | #define FTXPOST 1 | |
61 | #define FTXERR18 1 | |
62 | ||
63 | #include <freetype.h> | |
64 | #define TT_CHC 5 | |
65 | ||
66 | #ifdef FTXPOST | |
67 | #include <ftxpost.h> | |
68 | #endif | |
69 | ||
70 | #ifdef FTXERR18 | |
71 | #include <ftxerr18.h> | |
72 | #endif | |
73 | ||
74 | /* some versions of FT1.x don't seem to define this - it's font defined | |
75 | so it won't change */ | |
76 | #ifndef TT_MS_LANGID_ENGLISH_GENERAL | |
77 | #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409 | |
78 | #endif | |
79 | ||
80 | /* convert a code point into an index in the glyph cache */ | |
81 | #define TT_HASH(x) ((x) & 0xFF) | |
82 | ||
83 | typedef struct i_glyph_entry_ { | |
84 | TT_Glyph glyph; | |
85 | unsigned long ch; | |
86 | } i_tt_glyph_entry; | |
87 | ||
88 | #define TT_NOCHAR (~0UL) | |
89 | ||
90 | struct TT_Instancehandle_ { | |
91 | TT_Instance instance; | |
92 | TT_Instance_Metrics imetrics; | |
93 | TT_Glyph_Metrics gmetrics[256]; | |
94 | i_tt_glyph_entry glyphs[256]; | |
95 | int smooth; | |
96 | int order; | |
97 | i_img_dim ptsize; | |
98 | }; | |
99 | ||
100 | typedef struct TT_Instancehandle_ TT_Instancehandle; | |
101 | ||
102 | struct TT_Fonthandle_ { | |
103 | TT_Face face; | |
104 | TT_Face_Properties properties; | |
105 | TT_Instancehandle instanceh[TT_CHC]; | |
106 | TT_CharMap char_map; | |
107 | #ifdef FTXPOST | |
108 | int loaded_names; | |
109 | TT_Error load_cond; | |
110 | #endif | |
111 | }; | |
112 | ||
113 | /* Defines */ | |
114 | ||
115 | #define USTRCT(x) ((x).z) | |
116 | #define TT_VALID( handle ) ( ( handle ).z != NULL ) | |
117 | ||
118 | static void i_tt_push_error(TT_Error rc); | |
119 | ||
120 | /* Prototypes */ | |
121 | ||
122 | static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ); | |
123 | static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ); | |
124 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
125 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
126 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ); | |
127 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j ); | |
128 | static void | |
129 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, | |
130 | TT_Raster_Map *bit, TT_Raster_Map *small_bit, | |
131 | i_img_dim x_off, i_img_dim y_off, int smooth ); | |
132 | static int | |
133 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
134 | TT_Raster_Map *small_bit, i_img_dim cords[6], | |
135 | char const* txt, size_t len, int smooth, int utf8 ); | |
136 | static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ); | |
137 | static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ); | |
138 | static int | |
139 | i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], | |
140 | double points, char const* txt, size_t len, int smooth, int utf8 ); | |
141 | static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 ); | |
142 | ||
143 | ||
144 | /* static globals needed */ | |
145 | ||
146 | static int TT_initialized = 0; | |
147 | static TT_Engine engine; | |
148 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
149 | static int LTT_hinted = 1; /* FIXME: this too */ | |
150 | ||
151 | ||
152 | /* | |
153 | * FreeType interface | |
154 | */ | |
155 | ||
156 | ||
157 | /* | |
158 | =item init_tt() | |
159 | ||
160 | Initializes the freetype font rendering engine | |
161 | ||
162 | =cut | |
163 | */ | |
164 | ||
165 | static undef_int | |
166 | i_init_tt(void) { | |
167 | TT_Error error; | |
168 | TT_Byte palette[] = { 0, 64, 127, 191, 255 }; | |
169 | ||
170 | i_clear_error(); | |
171 | ||
172 | mm_log((1,"init_tt()\n")); | |
173 | error = TT_Init_FreeType( &engine ); | |
174 | if ( error ){ | |
175 | mm_log((1,"Initialization of freetype failed, code = 0x%x\n", | |
176 | (unsigned)error)); | |
177 | i_tt_push_error(error); | |
178 | i_push_error(0, "Could not initialize freetype 1.x"); | |
179 | return(1); | |
180 | } | |
181 | ||
182 | #ifdef FTXPOST | |
183 | error = TT_Init_Post_Extension( engine ); | |
184 | if (error) { | |
185 | mm_log((1, "Initialization of Post extension failed = 0x%x\n", | |
186 | (unsigned)error)); | |
187 | ||
188 | i_tt_push_error(error); | |
189 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
190 | return 1; | |
191 | } | |
192 | #endif | |
193 | ||
194 | error = TT_Set_Raster_Gray_Palette(engine, palette); | |
195 | if (error) { | |
196 | mm_log((1, "Initialization of gray levels failed = 0x%x\n", | |
197 | (unsigned)error)); | |
198 | i_tt_push_error(error); | |
199 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
200 | return 1; | |
201 | } | |
202 | ||
203 | TT_initialized = 1; | |
204 | ||
205 | return(0); | |
206 | } | |
207 | ||
208 | ||
209 | /* | |
210 | =item i_tt_get_instance(handle, points, smooth) | |
211 | ||
212 | Finds a points+smooth instance or if one doesn't exist in the cache | |
213 | allocates room and returns its cache entry | |
214 | ||
215 | fontname - path to the font to load | |
216 | handle - handle to the font. | |
217 | points - points of the requested font | |
218 | smooth - boolean (True: antialias on, False: antialias is off) | |
219 | ||
220 | =cut | |
221 | */ | |
222 | ||
223 | static | |
224 | int | |
225 | i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) { | |
226 | int i,idx; | |
227 | TT_Error error; | |
228 | ||
229 | mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n", | |
230 | handle, i_DFc(points), smooth)); | |
231 | ||
232 | if (smooth == -1) { /* Smooth doesn't matter for this search */ | |
233 | for(i=0;i<TT_CHC;i++) { | |
234 | if (handle->instanceh[i].ptsize==points) { | |
235 | mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i)); | |
236 | return i; | |
237 | } | |
238 | } | |
239 | smooth=1; /* We will be adding a font - add it as smooth then */ | |
240 | } else { /* Smooth doesn't matter for this search */ | |
241 | for(i=0;i<TT_CHC;i++) { | |
242 | if (handle->instanceh[i].ptsize == points | |
243 | && handle->instanceh[i].smooth == smooth) { | |
244 | mm_log((1,"i_tt_get_instance: in cache returning %d\n",i)); | |
245 | return i; | |
246 | } | |
247 | } | |
248 | } | |
249 | ||
250 | /* Found the instance in the cache - return the cache index */ | |
251 | ||
252 | for(idx=0;idx<TT_CHC;idx++) { | |
253 | if (!(handle->instanceh[idx].order)) break; /* find the lru item */ | |
254 | } | |
255 | ||
256 | mm_log((1,"i_tt_get_instance: lru item is %d\n",idx)); | |
257 | mm_log((1,"i_tt_get_instance: lru pointer %p\n", | |
258 | USTRCT(handle->instanceh[idx].instance) )); | |
259 | ||
260 | if ( USTRCT(handle->instanceh[idx].instance) ) { | |
261 | mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx)); | |
262 | ||
263 | /* Free cached glyphs */ | |
264 | for(i=0;i<256;i++) | |
265 | if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) ) | |
266 | TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph ); | |
267 | ||
268 | for(i=0;i<256;i++) { | |
269 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
270 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
271 | } | |
272 | ||
273 | /* Free instance if needed */ | |
274 | TT_Done_Instance( handle->instanceh[idx].instance ); | |
275 | } | |
276 | ||
277 | /* create and initialize instance */ | |
278 | /* FIXME: probably a memory leak on fail */ | |
279 | ||
280 | (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || | |
281 | ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) || | |
282 | ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) ); | |
283 | ||
284 | if ( error ) { | |
285 | mm_log((1, "Could not create and initialize instance: error %x.\n", | |
286 | (unsigned)error )); | |
287 | return -1; | |
288 | } | |
289 | ||
290 | /* Now that the instance should the inplace we need to lower all of the | |
291 | ru counts and put `this' one with the highest entry */ | |
292 | ||
293 | for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--; | |
294 | ||
295 | handle->instanceh[idx].order=TT_CHC-1; | |
296 | handle->instanceh[idx].ptsize=points; | |
297 | handle->instanceh[idx].smooth=smooth; | |
298 | TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) ); | |
299 | ||
300 | /* Zero the memory for the glyph storage so they are not thought as | |
301 | cached if they haven't been cached since this new font was loaded */ | |
302 | ||
303 | for(i=0;i<256;i++) { | |
304 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
305 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
306 | } | |
307 | ||
308 | return idx; | |
309 | } | |
310 | ||
311 | ||
312 | /* | |
313 | =item i_tt_new(fontname) | |
314 | ||
315 | Creates a new font handle object, finds a character map and initialise the | |
316 | the font handle's cache | |
317 | ||
318 | fontname - path to the font to load | |
319 | ||
320 | =cut | |
321 | */ | |
322 | ||
323 | TT_Fonthandle* | |
324 | i_tt_new(const char *fontname) { | |
325 | TT_Error error; | |
326 | TT_Fonthandle *handle; | |
327 | unsigned short i,n; | |
328 | unsigned short platform,encoding; | |
329 | ||
330 | if (!TT_initialized && i_init_tt()) { | |
331 | i_push_error(0, "Could not initialize FT1 engine"); | |
332 | return NULL; | |
333 | } | |
334 | ||
335 | i_clear_error(); | |
336 | ||
337 | mm_log((1,"i_tt_new(fontname '%s')\n",fontname)); | |
338 | ||
339 | /* allocate memory for the structure */ | |
340 | ||
341 | handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */ | |
342 | ||
343 | /* load the typeface */ | |
344 | error = TT_Open_Face( engine, fontname, &handle->face ); | |
345 | if ( error ) { | |
346 | if ( error == TT_Err_Could_Not_Open_File ) { | |
347 | mm_log((1, "Could not find/open %s.\n", fontname )); | |
348 | } | |
349 | else { | |
350 | mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, | |
351 | (unsigned)error )); | |
352 | } | |
353 | i_tt_push_error(error); | |
354 | return NULL; | |
355 | } | |
356 | ||
357 | TT_Get_Face_Properties( handle->face, &(handle->properties) ); | |
358 | ||
359 | /* First, look for a Unicode charmap */ | |
360 | n = handle->properties.num_CharMaps; | |
361 | USTRCT( handle->char_map )=NULL; /* Invalidate character map */ | |
362 | ||
363 | for ( i = 0; i < n; i++ ) { | |
364 | TT_Get_CharMap_ID( handle->face, i, &platform, &encoding ); | |
365 | if ( (platform == 3 && encoding == 1 ) | |
366 | || (platform == 0 && encoding == 0 ) ) { | |
367 | mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", | |
368 | platform, encoding)); | |
369 | TT_Get_CharMap( handle->face, i, &(handle->char_map) ); | |
370 | break; | |
371 | } | |
372 | } | |
373 | if (!USTRCT(handle->char_map) && n != 0) { | |
374 | /* just use the first one */ | |
375 | TT_Get_CharMap( handle->face, 0, &(handle->char_map)); | |
376 | } | |
377 | ||
378 | /* Zero the pointsizes - and ordering */ | |
379 | ||
380 | for(i=0;i<TT_CHC;i++) { | |
381 | USTRCT(handle->instanceh[i].instance)=NULL; | |
382 | handle->instanceh[i].order=i; | |
383 | handle->instanceh[i].ptsize=0; | |
384 | handle->instanceh[i].smooth=-1; | |
385 | } | |
386 | ||
387 | #ifdef FTXPOST | |
388 | handle->loaded_names = 0; | |
389 | #endif | |
390 | ||
391 | mm_log((1,"i_tt_new <- %p\n",handle)); | |
392 | return handle; | |
393 | } | |
394 | ||
395 | ||
396 | ||
397 | /* | |
398 | * raster map management | |
399 | */ | |
400 | ||
401 | /* | |
402 | =item i_tt_init_raster_map(bit, width, height, smooth) | |
403 | ||
404 | Allocates internal memory for the bitmap as needed by the parameters (internal) | |
405 | ||
406 | bit - bitmap to allocate into | |
407 | width - width of the bitmap | |
408 | height - height of the bitmap | |
409 | smooth - boolean (True: antialias on, False: antialias is off) | |
410 | ||
411 | =cut | |
412 | */ | |
413 | ||
414 | static | |
415 | void | |
416 | i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) { | |
417 | ||
418 | mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF | |
419 | ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth)); | |
420 | ||
421 | bit->rows = height; | |
422 | bit->width = ( width + 3 ) & -4; | |
423 | bit->flow = TT_Flow_Down; | |
424 | ||
425 | if ( smooth ) { | |
426 | bit->cols = bit->width; | |
427 | bit->size = bit->rows * bit->width; | |
428 | } else { | |
429 | bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */ | |
430 | bit->size = bit->rows * bit->cols; /* number of bytes in buffer */ | |
431 | } | |
432 | ||
433 | /* rows can be 0 for some glyphs, for example ' ' */ | |
434 | if (bit->rows && bit->size / bit->rows != bit->cols) { | |
435 | i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n", | |
436 | bit->width, bit->rows); | |
437 | } | |
438 | ||
439 | mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size )); | |
440 | ||
441 | bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */ | |
442 | if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size ); | |
443 | } | |
444 | ||
445 | ||
446 | /* | |
447 | =item i_tt_clear_raster_map(bit) | |
448 | ||
449 | Frees the bitmap data and sets pointer to NULL (internal) | |
450 | ||
451 | bit - bitmap to free | |
452 | ||
453 | =cut | |
454 | */ | |
455 | ||
456 | static | |
457 | void | |
458 | i_tt_done_raster_map( TT_Raster_Map *bit ) { | |
459 | myfree( bit->bitmap ); | |
460 | bit->bitmap = NULL; | |
461 | } | |
462 | ||
463 | ||
464 | /* | |
465 | =item i_tt_clear_raster_map(bit) | |
466 | ||
467 | Clears the specified bitmap (internal) | |
468 | ||
469 | bit - bitmap to zero | |
470 | ||
471 | =cut | |
472 | */ | |
473 | ||
474 | ||
475 | static | |
476 | void | |
477 | i_tt_clear_raster_map( TT_Raster_Map* bit ) { | |
478 | memset( bit->bitmap, 0, bit->size ); | |
479 | } | |
480 | ||
481 | ||
482 | /* | |
483 | =item i_tt_blit_or(dst, src, x_off, y_off) | |
484 | ||
485 | function that blits one raster map into another (internal) | |
486 | ||
487 | dst - destination bitmap | |
488 | src - source bitmap | |
489 | x_off - x offset into the destination bitmap | |
490 | y_off - y offset into the destination bitmap | |
491 | ||
492 | =cut | |
493 | */ | |
494 | ||
495 | static | |
496 | void | |
497 | i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) { | |
498 | i_img_dim x, y; | |
499 | i_img_dim x1, x2, y1, y2; | |
500 | unsigned char *s, *d; | |
501 | ||
502 | x1 = x_off < 0 ? -x_off : 0; | |
503 | y1 = y_off < 0 ? -y_off : 0; | |
504 | ||
505 | x2 = (int)dst->cols - x_off; | |
506 | if ( x2 > src->cols ) x2 = src->cols; | |
507 | ||
508 | y2 = (int)dst->rows - y_off; | |
509 | if ( y2 > src->rows ) y2 = src->rows; | |
510 | ||
511 | if ( x1 >= x2 ) return; | |
512 | ||
513 | /* do the real work now */ | |
514 | ||
515 | for ( y = y1; y < y2; ++y ) { | |
516 | s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1; | |
517 | d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off; | |
518 | ||
519 | for ( x = x1; x < x2; ++x ) { | |
520 | if (*s > *d) | |
521 | *d = *s; | |
522 | d++; | |
523 | s++; | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
528 | /* useful for debugging */ | |
529 | #if 0 | |
530 | ||
531 | static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) { | |
532 | int x, y; | |
533 | fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow); | |
534 | for (y = 0; y < bit->rows; ++y) { | |
535 | fprintf(out, "%2d:", y); | |
536 | for (x = 0; x < bit->cols; ++x) { | |
537 | if ((x & 7) == 0 && x) putc(' ', out); | |
538 | fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]); | |
539 | } | |
540 | putc('\n', out); | |
541 | } | |
542 | } | |
543 | ||
544 | #endif | |
545 | ||
546 | /* | |
547 | =item i_tt_get_glyph(handle, inst, j) | |
548 | ||
549 | Function to see if a glyph exists and if so cache it (internal) | |
550 | ||
551 | handle - pointer to font handle | |
552 | inst - font instance | |
553 | j - charcode of glyph | |
554 | ||
555 | =cut | |
556 | */ | |
557 | ||
558 | static | |
559 | int | |
560 | i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) { | |
561 | unsigned short load_flags, code; | |
562 | TT_Error error; | |
563 | ||
564 | mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n", | |
565 | handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.'))); | |
566 | ||
567 | /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/ | |
568 | ||
569 | if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) | |
570 | && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) { | |
571 | mm_log((1,"i_tt_get_glyph: %lu in cache\n",j)); | |
572 | return 1; | |
573 | } | |
574 | ||
575 | if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) { | |
576 | /* clean up the entry */ | |
577 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
578 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
579 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR; | |
580 | } | |
581 | ||
582 | /* Ok - it wasn't cached - try to get it in */ | |
583 | load_flags = TTLOAD_SCALE_GLYPH; | |
584 | if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH; | |
585 | ||
586 | if ( !TT_VALID(handle->char_map) ) { | |
587 | code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1); | |
588 | if ( code >= handle->properties.num_Glyphs ) code = 0; | |
589 | } else code = TT_Char_Index( handle->char_map, j ); | |
590 | ||
591 | if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) { | |
592 | mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error )); | |
593 | i_push_error(error, "TT_New_Glyph()"); | |
594 | return 0; | |
595 | } | |
596 | if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) { | |
597 | mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error )); | |
598 | /* Don't leak */ | |
599 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
600 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
601 | i_push_error(error, "TT_Load_Glyph()"); | |
602 | return 0; | |
603 | } | |
604 | ||
605 | /* At this point the glyph should be allocated and loaded */ | |
606 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j; | |
607 | ||
608 | /* Next get the glyph metrics */ | |
609 | error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, | |
610 | &handle->instanceh[inst].gmetrics[TT_HASH(j)] ); | |
611 | if (error) { | |
612 | mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error )); | |
613 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
614 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
615 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR; | |
616 | i_push_error(error, "TT_Get_Glyph_Metrics()"); | |
617 | return 0; | |
618 | } | |
619 | ||
620 | return 1; | |
621 | } | |
622 | ||
623 | /* | |
624 | =item i_tt_has_chars(handle, text, len, utf8, out) | |
625 | ||
626 | Check if the given characters are defined by the font. Note that len | |
627 | is the number of bytes, not the number of characters (when utf8 is | |
628 | non-zero). | |
629 | ||
630 | Returns the number of characters that were checked. | |
631 | ||
632 | =cut | |
633 | */ | |
634 | ||
635 | size_t | |
636 | i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8, | |
637 | char *out) { | |
638 | size_t count = 0; | |
639 | mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n", | |
640 | handle, text, (long)len, utf8)); | |
641 | ||
642 | while (len) { | |
643 | unsigned long c; | |
644 | int index; | |
645 | if (utf8) { | |
646 | c = i_utf8_advance(&text, &len); | |
647 | if (c == ~0UL) { | |
648 | i_push_error(0, "invalid UTF8 character"); | |
649 | return 0; | |
650 | } | |
651 | } | |
652 | else { | |
653 | c = (unsigned char)*text++; | |
654 | --len; | |
655 | } | |
656 | ||
657 | if (TT_VALID(handle->char_map)) { | |
658 | index = TT_Char_Index(handle->char_map, c); | |
659 | } | |
660 | else { | |
661 | index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1); | |
662 | if (index >= handle->properties.num_Glyphs) | |
663 | index = 0; | |
664 | } | |
665 | *out++ = index != 0; | |
666 | ++count; | |
667 | } | |
668 | ||
669 | return count; | |
670 | } | |
671 | ||
672 | /* | |
673 | =item i_tt_destroy(handle) | |
674 | ||
675 | Clears the data taken by a font including all cached data such as | |
676 | pixmaps and glyphs | |
677 | ||
678 | handle - pointer to font handle | |
679 | ||
680 | =cut | |
681 | */ | |
682 | ||
683 | void | |
684 | i_tt_destroy( TT_Fonthandle *handle) { | |
685 | TT_Close_Face( handle->face ); | |
686 | myfree( handle ); | |
687 | ||
688 | /* FIXME: Should these be freed automatically by the library? | |
689 | ||
690 | TT_Done_Instance( instance ); | |
691 | void | |
692 | i_tt_done_glyphs( void ) { | |
693 | int i; | |
694 | ||
695 | if ( !glyphs ) return; | |
696 | ||
697 | for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] ); | |
698 | free( glyphs ); | |
699 | ||
700 | glyphs = NULL; | |
701 | } | |
702 | */ | |
703 | } | |
704 | ||
705 | ||
706 | /* | |
707 | * FreeType Rendering functions | |
708 | */ | |
709 | ||
710 | ||
711 | /* | |
712 | =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth) | |
713 | ||
714 | Renders a single glyph into the bit rastermap (internal) | |
715 | ||
716 | handle - pointer to font handle | |
717 | gmetrics - the metrics for the glyph to be rendered | |
718 | bit - large bitmap that is the destination for the text | |
719 | smallbit - small bitmap that is used only if smooth is true | |
720 | x_off - x offset of glyph | |
721 | y_off - y offset of glyph | |
722 | smooth - boolean (True: antialias on, False: antialias is off) | |
723 | ||
724 | =cut | |
725 | */ | |
726 | ||
727 | static | |
728 | void | |
729 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, i_img_dim x_off, i_img_dim y_off, int smooth ) { | |
730 | ||
731 | mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n", | |
732 | USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off), | |
733 | i_DFc(y_off), smooth)); | |
734 | ||
735 | if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64); | |
736 | else { | |
737 | TT_F26Dot6 xmin, ymin, xmax, ymax; | |
738 | ||
739 | xmin = gmetrics->bbox.xMin & -64; | |
740 | ymin = gmetrics->bbox.yMin & -64; | |
741 | xmax = (gmetrics->bbox.xMax + 63) & -64; | |
742 | ymax = (gmetrics->bbox.yMax + 63) & -64; | |
743 | ||
744 | i_tt_clear_raster_map( small_bit ); | |
745 | TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin ); | |
746 | i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off ); | |
747 | } | |
748 | } | |
749 | ||
750 | ||
751 | /* | |
752 | =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth) | |
753 | ||
754 | calls i_tt_render_glyph to render each glyph into the bit rastermap (internal) | |
755 | ||
756 | handle - pointer to font handle | |
757 | inst - font instance | |
758 | bit - large bitmap that is the destination for the text | |
759 | smallbit - small bitmap that is used only if smooth is true | |
760 | txt - string to render | |
761 | len - length of the string to render | |
762 | smooth - boolean (True: antialias on, False: antialias is off) | |
763 | ||
764 | =cut | |
765 | */ | |
766 | ||
767 | static | |
768 | int | |
769 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
770 | TT_Raster_Map *small_bit, i_img_dim cords[6], | |
771 | char const* txt, size_t len, int smooth, int utf8 ) { | |
772 | unsigned long j; | |
773 | TT_F26Dot6 x,y; | |
774 | ||
775 | mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n", | |
776 | handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8)); | |
777 | ||
778 | /* | |
779 | y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM); | |
780 | */ | |
781 | ||
782 | x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */ | |
783 | y=-cords[4]; | |
784 | ||
785 | while (len) { | |
786 | if (utf8) { | |
787 | j = i_utf8_advance(&txt, &len); | |
788 | if (j == ~0UL) { | |
789 | i_push_error(0, "invalid UTF8 character"); | |
790 | return 0; | |
791 | } | |
792 | } | |
793 | else { | |
794 | j = (unsigned char)*txt++; | |
795 | --len; | |
796 | } | |
797 | if ( !i_tt_get_glyph(handle,inst,j) ) | |
798 | continue; | |
799 | i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, | |
800 | &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, | |
801 | small_bit, x, y, smooth ); | |
802 | x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64; | |
803 | } | |
804 | ||
805 | return 1; | |
806 | } | |
807 | ||
808 | ||
809 | /* | |
810 | * Functions to render rasters (single channel images) onto images | |
811 | */ | |
812 | ||
813 | /* | |
814 | =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth) | |
815 | ||
816 | Function to dump a raster onto an image in color used by i_tt_text() (internal). | |
817 | ||
818 | im - image to dump raster on | |
819 | bit - bitmap that contains the text to be dumped to im | |
820 | xb, yb - coordinates, left edge and baseline | |
821 | cl - color to use for text | |
822 | smooth - boolean (True: antialias on, False: antialias is off) | |
823 | ||
824 | =cut | |
825 | */ | |
826 | ||
827 | static | |
828 | void | |
829 | i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) { | |
830 | unsigned char *bmap; | |
831 | i_img_dim x, y; | |
832 | mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n", | |
833 | im, bit, i_DFc(xb), i_DFc(yb), cl)); | |
834 | ||
835 | bmap = bit->bitmap; | |
836 | ||
837 | if ( smooth ) { | |
838 | ||
839 | i_render r; | |
840 | i_render_init(&r, im, bit->cols); | |
841 | for(y=0;y<bit->rows;y++) { | |
842 | i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl); | |
843 | } | |
844 | i_render_done(&r); | |
845 | } else { | |
846 | for(y=0;y<bit->rows;y++) { | |
847 | unsigned mask = 0x80; | |
848 | unsigned char *p = bmap + y * bit->cols; | |
849 | ||
850 | for(x = 0; x < bit->width; x++) { | |
851 | if (*p & mask) { | |
852 | i_ppix(im, x+xb, y+yb, cl); | |
853 | } | |
854 | mask >>= 1; | |
855 | if (!mask) { | |
856 | mask = 0x80; | |
857 | ++p; | |
858 | } | |
859 | } | |
860 | } | |
861 | ||
862 | } | |
863 | } | |
864 | ||
865 | ||
866 | /* | |
867 | =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth) | |
868 | ||
869 | Function to dump a raster onto a single channel image in color (internal) | |
870 | ||
871 | im - image to dump raster on | |
872 | bit - bitmap that contains the text to be dumped to im | |
873 | xb, yb - coordinates, left edge and baseline | |
874 | channel - channel to copy to | |
875 | smooth - boolean (True: antialias on, False: antialias is off) | |
876 | ||
877 | =cut | |
878 | */ | |
879 | ||
880 | static | |
881 | void | |
882 | i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ) { | |
883 | unsigned char *bmap; | |
884 | i_color val; | |
885 | int c; | |
886 | i_img_dim x,y; | |
887 | int old_mask = im->ch_mask; | |
888 | im->ch_mask = 1 << channel; | |
889 | ||
890 | mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n", | |
891 | im, bit, i_DFc(xb), i_DFc(yb), channel)); | |
892 | ||
893 | bmap = bit->bitmap; | |
894 | ||
895 | if ( smooth ) { | |
896 | for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) { | |
897 | c = bmap[y*(bit->cols)+x]; | |
898 | val.channel[channel] = c; | |
899 | i_ppix(im,x+xb,y+yb,&val); | |
900 | } | |
901 | } else { | |
902 | for(y=0;y<bit->rows;y++) { | |
903 | unsigned mask = 0x80; | |
904 | unsigned char *p = bmap + y * bit->cols; | |
905 | ||
906 | for(x=0;x<bit->width;x++) { | |
907 | val.channel[channel] = (*p & mask) ? 255 : 0; | |
908 | i_ppix(im,x+xb,y+yb,&val); | |
909 | ||
910 | mask >>= 1; | |
911 | if (!mask) { | |
912 | ++p; | |
913 | mask = 0x80; | |
914 | } | |
915 | } | |
916 | } | |
917 | } | |
918 | im->ch_mask = old_mask; | |
919 | } | |
920 | ||
921 | ||
922 | /* | |
923 | =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) | |
924 | ||
925 | interface for generating single channel raster of text (internal) | |
926 | ||
927 | handle - pointer to font handle | |
928 | bit - the bitmap that is allocated, rendered into and NOT freed | |
929 | cords - the bounding box (modified in place) | |
930 | points - font size to use | |
931 | txt - string to render | |
932 | len - length of the string to render | |
933 | smooth - boolean (True: antialias on, False: antialias is off) | |
934 | ||
935 | =cut | |
936 | */ | |
937 | ||
938 | static | |
939 | int | |
940 | i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) { | |
941 | int inst; | |
942 | i_img_dim width, height; | |
943 | TT_Raster_Map small_bit; | |
944 | ||
945 | /* find or install an instance */ | |
946 | if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { | |
947 | mm_log((1,"i_tt_rasterize: get instance failed\n")); | |
948 | return 0; | |
949 | } | |
950 | ||
951 | /* calculate bounding box */ | |
952 | if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 )) | |
953 | return 0; | |
954 | ||
955 | ||
956 | width = cords[2]-cords[0]; | |
957 | height = cords[5]-cords[4]; | |
958 | ||
959 | mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n", | |
960 | i_DFc(width), i_DFc(height) )); | |
961 | ||
962 | i_tt_init_raster_map ( bit, width, height, smooth ); | |
963 | i_tt_clear_raster_map( bit ); | |
964 | if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth ); | |
965 | ||
966 | if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, | |
967 | smooth, utf8 )) { | |
968 | if ( smooth ) | |
969 | i_tt_done_raster_map( &small_bit ); | |
970 | return 0; | |
971 | } | |
972 | ||
973 | if ( smooth ) i_tt_done_raster_map( &small_bit ); | |
974 | return 1; | |
975 | } | |
976 | ||
977 | ||
978 | ||
979 | /* | |
980 | * Exported text rendering interfaces | |
981 | */ | |
982 | ||
983 | ||
984 | /* | |
985 | =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8) | |
986 | ||
987 | Interface to text rendering into a single channel in an image | |
988 | ||
989 | handle - pointer to font handle | |
990 | im - image to render text on to | |
991 | xb, yb - coordinates, left edge and baseline | |
992 | channel - channel to render into | |
993 | points - font size to use | |
994 | txt - string to render | |
995 | len - length of the string to render | |
996 | smooth - boolean (True: antialias on, False: antialias is off) | |
997 | ||
998 | =cut | |
999 | */ | |
1000 | ||
1001 | undef_int | |
1002 | i_tt_cp( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) { | |
1003 | ||
1004 | i_img_dim cords[BOUNDING_BOX_COUNT]; | |
1005 | i_img_dim ascent, st_offset, y; | |
1006 | TT_Raster_Map bit; | |
1007 | ||
1008 | i_clear_error(); | |
1009 | if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; | |
1010 | ||
1011 | ascent=cords[BBOX_ASCENT]; | |
1012 | st_offset=cords[BBOX_NEG_WIDTH]; | |
1013 | y = align ? yb-ascent : yb; | |
1014 | ||
1015 | i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth ); | |
1016 | i_tt_done_raster_map( &bit ); | |
1017 | ||
1018 | return 1; | |
1019 | } | |
1020 | ||
1021 | ||
1022 | /* | |
1023 | =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) | |
1024 | ||
1025 | Interface to text rendering in a single color onto an image | |
1026 | ||
1027 | handle - pointer to font handle | |
1028 | im - image to render text on to | |
1029 | xb, yb - coordinates, left edge and baseline | |
1030 | cl - color to use for text | |
1031 | points - font size to use | |
1032 | txt - string to render | |
1033 | len - length of the string to render | |
1034 | smooth - boolean (True: antialias on, False: antialias is off) | |
1035 | ||
1036 | =cut | |
1037 | */ | |
1038 | ||
1039 | undef_int | |
1040 | i_tt_text( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) { | |
1041 | i_img_dim cords[BOUNDING_BOX_COUNT]; | |
1042 | i_img_dim ascent, st_offset, y; | |
1043 | TT_Raster_Map bit; | |
1044 | ||
1045 | i_clear_error(); | |
1046 | ||
1047 | if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; | |
1048 | ||
1049 | ascent=cords[BBOX_ASCENT]; | |
1050 | st_offset=cords[BBOX_NEG_WIDTH]; | |
1051 | y = align ? yb-ascent : yb; | |
1052 | ||
1053 | i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); | |
1054 | i_tt_done_raster_map( &bit ); | |
1055 | ||
1056 | return 1; | |
1057 | } | |
1058 | ||
1059 | ||
1060 | /* | |
1061 | =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) | |
1062 | ||
1063 | Function to get texts bounding boxes given the instance of the font (internal) | |
1064 | ||
1065 | handle - pointer to font handle | |
1066 | inst - font instance | |
1067 | txt - string to measure | |
1068 | len - length of the string to render | |
1069 | cords - the bounding box (modified in place) | |
1070 | ||
1071 | =cut | |
1072 | */ | |
1073 | ||
1074 | static | |
1075 | undef_int | |
1076 | i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) { | |
1077 | int upm, casc, cdesc, first; | |
1078 | ||
1079 | int start = 0; | |
1080 | i_img_dim width = 0; | |
1081 | int gdescent = 0; | |
1082 | int gascent = 0; | |
1083 | int descent = 0; | |
1084 | int ascent = 0; | |
1085 | int rightb = 0; | |
1086 | ||
1087 | unsigned long j; | |
1088 | unsigned char *ustr; | |
1089 | ustr=(unsigned char*)txt; | |
1090 | ||
1091 | mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n", | |
1092 | handle, inst, (int)len, txt, (long)len, utf8)); | |
1093 | ||
1094 | upm = handle->properties.header->Units_Per_EM; | |
1095 | gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm; | |
1096 | gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm; | |
1097 | ||
1098 | width = 0; | |
1099 | start = 0; | |
1100 | ||
1101 | mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent)); | |
1102 | ||
1103 | first=1; | |
1104 | while (len) { | |
1105 | if (utf8) { | |
1106 | j = i_utf8_advance(&txt, &len); | |
1107 | if (j == ~0UL) { | |
1108 | i_push_error(0, "invalid UTF8 character"); | |
1109 | return 0; | |
1110 | } | |
1111 | } | |
1112 | else { | |
1113 | j = (unsigned char)*txt++; | |
1114 | --len; | |
1115 | } | |
1116 | if ( i_tt_get_glyph(handle,inst,j) ) { | |
1117 | TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j); | |
1118 | width += gm->advance / 64; | |
1119 | casc = (gm->bbox.yMax+63) / 64; | |
1120 | cdesc = (gm->bbox.yMin-63) / 64; | |
1121 | ||
1122 | mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", | |
1123 | (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc)); | |
1124 | ||
1125 | if (first) { | |
1126 | start = gm->bbox.xMin / 64; | |
1127 | ascent = (gm->bbox.yMax+63) / 64; | |
1128 | descent = (gm->bbox.yMin-63) / 64; | |
1129 | first = 0; | |
1130 | } | |
1131 | if (!len) { /* if at end of string */ | |
1132 | /* the right-side bearing - in case the right-side of a | |
1133 | character goes past the right of the advance width, | |
1134 | as is common for italic fonts | |
1135 | */ | |
1136 | rightb = gm->advance - gm->bearingX | |
1137 | - (gm->bbox.xMax - gm->bbox.xMin); | |
1138 | /* fprintf(stderr, "font info last: %d %d %d %d\n", | |
1139 | gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */ | |
1140 | } | |
1141 | ||
1142 | ascent = (ascent > casc ? ascent : casc ); | |
1143 | descent = (descent < cdesc ? descent : cdesc); | |
1144 | } | |
1145 | } | |
1146 | ||
1147 | cords[BBOX_NEG_WIDTH]=start; | |
1148 | cords[BBOX_GLOBAL_DESCENT]=gdescent; | |
1149 | cords[BBOX_POS_WIDTH]=width; | |
1150 | if (rightb < 0) | |
1151 | cords[BBOX_POS_WIDTH] -= rightb / 64; | |
1152 | cords[BBOX_GLOBAL_ASCENT]=gascent; | |
1153 | cords[BBOX_DESCENT]=descent; | |
1154 | cords[BBOX_ASCENT]=ascent; | |
1155 | cords[BBOX_ADVANCE_WIDTH] = width; | |
1156 | cords[BBOX_RIGHT_BEARING] = rightb / 64; | |
1157 | ||
1158 | return BBOX_RIGHT_BEARING + 1; | |
1159 | } | |
1160 | ||
1161 | ||
1162 | /* | |
1163 | =item i_tt_bbox(handle, points, txt, len, cords, utf8) | |
1164 | ||
1165 | Interface to get a strings bounding box | |
1166 | ||
1167 | handle - pointer to font handle | |
1168 | points - font size to use | |
1169 | txt - string to render | |
1170 | len - length of the string to render | |
1171 | cords - the bounding box (modified in place) | |
1172 | ||
1173 | =cut | |
1174 | */ | |
1175 | ||
1176 | undef_int | |
1177 | i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) { | |
1178 | int inst; | |
1179 | ||
1180 | i_clear_error(); | |
1181 | mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n", | |
1182 | handle, points, (int)len, txt, (long)len, utf8)); | |
1183 | ||
1184 | if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) { | |
1185 | i_push_errorf(0, "i_tt_get_instance(%g)", points); | |
1186 | mm_log((1,"i_tt_text: get instance failed\n")); | |
1187 | return 0; | |
1188 | } | |
1189 | ||
1190 | return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8); | |
1191 | } | |
1192 | ||
1193 | /* | |
1194 | =item i_tt_face_name(handle, name_buf, name_buf_size) | |
1195 | ||
1196 | Retrieve's the font's postscript name. | |
1197 | ||
1198 | This is complicated by the need to handle encodings and so on. | |
1199 | ||
1200 | =cut | |
1201 | */ | |
1202 | size_t | |
1203 | i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) { | |
1204 | TT_Face_Properties props; | |
1205 | int name_count; | |
1206 | int i; | |
1207 | TT_UShort platform_id, encoding_id, lang_id, name_id; | |
1208 | TT_UShort name_len; | |
1209 | TT_String *name; | |
1210 | int want_index = -1; /* an acceptable but not perfect name */ | |
1211 | int score = 0; | |
1212 | ||
1213 | i_clear_error(); | |
1214 | ||
1215 | TT_Get_Face_Properties(handle->face, &props); | |
1216 | name_count = props.num_Names; | |
1217 | for (i = 0; i < name_count; ++i) { | |
1218 | TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, | |
1219 | &name_id); | |
1220 | ||
1221 | TT_Get_Name_String(handle->face, i, &name, &name_len); | |
1222 | ||
1223 | if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len | |
1224 | && name_id == TT_NAME_ID_PS_NAME) { | |
1225 | int might_want_index = -1; | |
1226 | int might_score = 0; | |
1227 | if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN) | |
1228 | || | |
1229 | (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) { | |
1230 | /* exactly what we want */ | |
1231 | want_index = i; | |
1232 | break; | |
1233 | } | |
1234 | ||
1235 | if (platform_id == TT_PLATFORM_MICROSOFT | |
1236 | && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) { | |
1237 | /* any english is good */ | |
1238 | might_want_index = i; | |
1239 | might_score = 9; | |
1240 | } | |
1241 | /* there might be something in between */ | |
1242 | else { | |
1243 | /* anything non-unicode is better than nothing */ | |
1244 | might_want_index = i; | |
1245 | might_score = 1; | |
1246 | } | |
1247 | if (might_score > score) { | |
1248 | score = might_score; | |
1249 | want_index = might_want_index; | |
1250 | } | |
1251 | } | |
1252 | } | |
1253 | ||
1254 | if (want_index != -1) { | |
1255 | TT_Get_Name_String(handle->face, want_index, &name, &name_len); | |
1256 | ||
1257 | strncpy(name_buf, name, name_buf_size); | |
1258 | name_buf[name_buf_size-1] = '\0'; | |
1259 | ||
1260 | return strlen(name) + 1; | |
1261 | } | |
1262 | else { | |
1263 | i_push_error(0, "no face name present"); | |
1264 | return 0; | |
1265 | } | |
1266 | } | |
1267 | ||
1268 | void i_tt_dump_names(TT_Fonthandle *handle) { | |
1269 | TT_Face_Properties props; | |
1270 | int name_count; | |
1271 | int i; | |
1272 | TT_UShort platform_id, encoding_id, lang_id, name_id; | |
1273 | TT_UShort name_len; | |
1274 | TT_String *name; | |
1275 | ||
1276 | TT_Get_Face_Properties(handle->face, &props); | |
1277 | name_count = props.num_Names; | |
1278 | for (i = 0; i < name_count; ++i) { | |
1279 | TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, | |
1280 | &name_id); | |
1281 | TT_Get_Name_String(handle->face, i, &name, &name_len); | |
1282 | ||
1283 | printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id, | |
1284 | encoding_id, lang_id, name_id); | |
1285 | if (platform_id == TT_PLATFORM_APPLE_UNICODE) { | |
1286 | printf("(unicode)\n"); | |
1287 | } | |
1288 | else { | |
1289 | printf("'%s'\n", name); | |
1290 | } | |
1291 | } | |
1292 | fflush(stdout); | |
1293 | } | |
1294 | ||
1295 | size_t | |
1296 | i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, | |
1297 | size_t name_buf_size) { | |
1298 | #ifdef FTXPOST | |
1299 | TT_Error rc; | |
1300 | TT_String *psname; | |
1301 | TT_UShort index; | |
1302 | ||
1303 | i_clear_error(); | |
1304 | ||
1305 | if (!handle->loaded_names) { | |
1306 | TT_Post post; | |
1307 | mm_log((1, "Loading PS Names")); | |
1308 | handle->load_cond = TT_Load_PS_Names(handle->face, &post); | |
1309 | ++handle->loaded_names; | |
1310 | } | |
1311 | ||
1312 | if (handle->load_cond) { | |
1313 | i_push_errorf(handle->load_cond, "error loading names (%#x)", | |
1314 | (unsigned)handle->load_cond); | |
1315 | return 0; | |
1316 | } | |
1317 | ||
1318 | index = TT_Char_Index(handle->char_map, ch); | |
1319 | if (!index) { | |
1320 | i_push_error(0, "no such character"); | |
1321 | return 0; | |
1322 | } | |
1323 | ||
1324 | rc = TT_Get_PS_Name(handle->face, index, &psname); | |
1325 | ||
1326 | if (rc) { | |
1327 | i_push_error(rc, "error getting name"); | |
1328 | return 0; | |
1329 | } | |
1330 | ||
1331 | strncpy(name_buf, psname, name_buf_size); | |
1332 | name_buf[name_buf_size-1] = '\0'; | |
1333 | ||
1334 | return strlen(psname) + 1; | |
1335 | #else | |
1336 | mm_log((1, "FTXPOST extension not enabled\n")); | |
1337 | i_clear_error(); | |
1338 | i_push_error(0, "Use of FTXPOST extension disabled"); | |
1339 | ||
1340 | return 0; | |
1341 | #endif | |
1342 | } | |
1343 | ||
1344 | /* | |
1345 | =item i_tt_push_error(code) | |
1346 | ||
1347 | Push an error message and code onto the Imager error stack. | |
1348 | ||
1349 | =cut | |
1350 | */ | |
1351 | static void | |
1352 | i_tt_push_error(TT_Error rc) { | |
1353 | #ifdef FTXERR18 | |
1354 | TT_String const *msg = TT_ErrToString18(rc); | |
1355 | ||
1356 | i_push_error(rc, msg); | |
1357 | #else | |
1358 | i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc); | |
1359 | #endif | |
1360 | } | |
1361 | ||
1362 | #endif /* HAVE_LIBTT */ | |
1363 | ||
1364 | ||
1365 | /* | |
1366 | =back | |
1367 | ||
1368 | =head1 AUTHOR | |
1369 | ||
1370 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
1371 | ||
1372 | =head1 SEE ALSO | |
1373 | ||
1374 | Imager(3) | |
1375 | ||
1376 | =cut | |
1377 | */ |
0 | #include "imager.h" | |
1 | #include "imrender.h" | |
2 | ||
3 | #include <sys/types.h> | |
4 | #include <sys/stat.h> | |
5 | #include <fcntl.h> | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | ||
10 | ||
11 | /* | |
12 | =head1 NAME | |
13 | ||
14 | fontft1.c - Freetype 1.x font driver for Imager | |
15 | ||
16 | =head1 SYNOPSIS | |
17 | ||
18 | handle = i_tt_new(path_to_ttf); | |
19 | rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8); | |
20 | i_tt_destroy(handle); | |
21 | ||
22 | // and much more | |
23 | ||
24 | =head1 DESCRIPTION | |
25 | ||
26 | fontft1.c implements font creation, rendering, bounding box functions and | |
27 | more for Imager using Freetype 1.x. | |
28 | ||
29 | In general this driver should be ignored in favour of the FT2 driver. | |
30 | ||
31 | =head1 FUNCTION REFERENCE | |
32 | ||
33 | Some of these functions are internal. | |
34 | ||
35 | =over | |
36 | ||
37 | =cut | |
38 | ||
39 | */ | |
40 | ||
41 | ||
42 | /* Truetype font support */ | |
43 | /* These are enabled by default when configuring Freetype 1.x | |
44 | I haven't a clue how to reliably detect it at compile time. | |
45 | ||
46 | We need a compilation probe in Makefile.PL | |
47 | */ | |
48 | #define FTXPOST 1 | |
49 | #define FTXERR18 1 | |
50 | ||
51 | #include <freetype.h> | |
52 | #define TT_CHC 5 | |
53 | ||
54 | #ifdef FTXPOST | |
55 | #include <ftxpost.h> | |
56 | #endif | |
57 | ||
58 | #ifdef FTXERR18 | |
59 | #include <ftxerr18.h> | |
60 | #endif | |
61 | ||
62 | /* some versions of FT1.x don't seem to define this - it's font defined | |
63 | so it won't change */ | |
64 | #ifndef TT_MS_LANGID_ENGLISH_GENERAL | |
65 | #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409 | |
66 | #endif | |
67 | ||
68 | static im_slot_t slot = -1; | |
69 | ||
70 | /* convert a code point into an index in the glyph cache */ | |
71 | #define TT_HASH(x) ((x) & 0xFF) | |
72 | ||
73 | typedef struct { | |
74 | int initialized; | |
75 | TT_Engine engine; | |
76 | } i_tt_engine; | |
77 | ||
78 | typedef struct i_glyph_entry_ { | |
79 | TT_Glyph glyph; | |
80 | unsigned long ch; | |
81 | } i_tt_glyph_entry; | |
82 | ||
83 | #define TT_NOCHAR (~0UL) | |
84 | ||
85 | struct TT_Instancehandle_ { | |
86 | TT_Instance instance; | |
87 | TT_Instance_Metrics imetrics; | |
88 | TT_Glyph_Metrics gmetrics[256]; | |
89 | i_tt_glyph_entry glyphs[256]; | |
90 | int smooth; | |
91 | int order; | |
92 | i_img_dim ptsize; | |
93 | }; | |
94 | ||
95 | typedef struct TT_Instancehandle_ TT_Instancehandle; | |
96 | ||
97 | struct TT_Fonthandle_ { | |
98 | TT_Face face; | |
99 | TT_Face_Properties properties; | |
100 | TT_Instancehandle instanceh[TT_CHC]; | |
101 | TT_CharMap char_map; | |
102 | #ifdef FTXPOST | |
103 | int loaded_names; | |
104 | TT_Error load_cond; | |
105 | #endif | |
106 | }; | |
107 | ||
108 | /* Defines */ | |
109 | ||
110 | #define USTRCT(x) ((x).z) | |
111 | #define TT_VALID( handle ) ( ( handle ).z != NULL ) | |
112 | ||
113 | static void i_tt_push_error(TT_Error rc); | |
114 | static void i_tt_uninit(void *); | |
115 | ||
116 | /* Prototypes */ | |
117 | ||
118 | static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ); | |
119 | static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ); | |
120 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
121 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
122 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ); | |
123 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j ); | |
124 | static void | |
125 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, | |
126 | TT_Raster_Map *bit, TT_Raster_Map *small_bit, | |
127 | i_img_dim x_off, i_img_dim y_off, int smooth ); | |
128 | static int | |
129 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
130 | TT_Raster_Map *small_bit, i_img_dim cords[6], | |
131 | char const* txt, size_t len, int smooth, int utf8 ); | |
132 | static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ); | |
133 | static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ); | |
134 | static int | |
135 | i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], | |
136 | double points, char const* txt, size_t len, int smooth, int utf8 ); | |
137 | static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 ); | |
138 | ||
139 | ||
140 | /* static globals needed */ | |
141 | ||
142 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
143 | static int LTT_hinted = 1; /* FIXME: this too */ | |
144 | ||
145 | ||
146 | /* | |
147 | * FreeType interface | |
148 | */ | |
149 | ||
150 | void | |
151 | i_tt_start(void) { | |
152 | if (slot == -1) | |
153 | slot = im_context_slot_new(i_tt_uninit); | |
154 | } | |
155 | ||
156 | ||
157 | /* | |
158 | =item init_tt() | |
159 | ||
160 | Initializes the freetype font rendering engine (if needed) | |
161 | ||
162 | =cut | |
163 | */ | |
164 | ||
165 | static i_tt_engine * | |
166 | i_init_tt(void) { | |
167 | TT_Error error; | |
168 | im_context_t ctx = im_get_context(); | |
169 | TT_Byte palette[] = { 0, 64, 127, 191, 255 }; | |
170 | i_tt_engine *result = im_context_slot_get(ctx, slot); | |
171 | ||
172 | i_clear_error(); | |
173 | ||
174 | if (result == NULL) { | |
175 | result = mymalloc(sizeof(i_tt_engine)); | |
176 | memset(result, 0, sizeof(*result)); | |
177 | im_context_slot_set(ctx, slot, result); | |
178 | mm_log((1, "allocated FT1 state %p\n", result)); | |
179 | } | |
180 | ||
181 | mm_log((1,"init_tt()\n")); | |
182 | ||
183 | if (result->initialized) | |
184 | return result; | |
185 | ||
186 | error = TT_Init_FreeType( &result->engine ); | |
187 | if ( error ){ | |
188 | mm_log((1,"Initialization of freetype failed, code = 0x%x\n", | |
189 | (unsigned)error)); | |
190 | i_tt_push_error(error); | |
191 | i_push_error(0, "Could not initialize freetype 1.x"); | |
192 | return NULL; | |
193 | } | |
194 | ||
195 | #ifdef FTXPOST | |
196 | error = TT_Init_Post_Extension( result->engine ); | |
197 | if (error) { | |
198 | mm_log((1, "Initialization of Post extension failed = 0x%x\n", | |
199 | (unsigned)error)); | |
200 | ||
201 | i_tt_push_error(error); | |
202 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
203 | return NULL; | |
204 | } | |
205 | #endif | |
206 | ||
207 | error = TT_Set_Raster_Gray_Palette(result->engine, palette); | |
208 | if (error) { | |
209 | mm_log((1, "Initialization of gray levels failed = 0x%x\n", | |
210 | (unsigned)error)); | |
211 | i_tt_push_error(error); | |
212 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
213 | return NULL; | |
214 | } | |
215 | ||
216 | mm_log((1, "initialized FT1 state %p\n", result)); | |
217 | ||
218 | result->initialized = 1; | |
219 | ||
220 | return result; | |
221 | } | |
222 | ||
223 | static void | |
224 | i_tt_uninit(void *p) { | |
225 | i_tt_engine *tteng = p; | |
226 | ||
227 | if (tteng->initialized) { | |
228 | mm_log((1, "finalizing FT1 state %p\n", tteng)); | |
229 | TT_Done_FreeType(tteng->engine); | |
230 | } | |
231 | mm_log((1, "freeing FT1 state %p\n", tteng)); | |
232 | myfree(tteng); | |
233 | } | |
234 | ||
235 | /* | |
236 | =item i_tt_get_instance(handle, points, smooth) | |
237 | ||
238 | Finds a points+smooth instance or if one doesn't exist in the cache | |
239 | allocates room and returns its cache entry | |
240 | ||
241 | fontname - path to the font to load | |
242 | handle - handle to the font. | |
243 | points - points of the requested font | |
244 | smooth - boolean (True: antialias on, False: antialias is off) | |
245 | ||
246 | =cut | |
247 | */ | |
248 | ||
249 | static | |
250 | int | |
251 | i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) { | |
252 | int i,idx; | |
253 | TT_Error error; | |
254 | ||
255 | mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n", | |
256 | handle, i_DFc(points), smooth)); | |
257 | ||
258 | if (smooth == -1) { /* Smooth doesn't matter for this search */ | |
259 | for(i=0;i<TT_CHC;i++) { | |
260 | if (handle->instanceh[i].ptsize==points) { | |
261 | mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i)); | |
262 | return i; | |
263 | } | |
264 | } | |
265 | smooth=1; /* We will be adding a font - add it as smooth then */ | |
266 | } else { /* Smooth doesn't matter for this search */ | |
267 | for(i=0;i<TT_CHC;i++) { | |
268 | if (handle->instanceh[i].ptsize == points | |
269 | && handle->instanceh[i].smooth == smooth) { | |
270 | mm_log((1,"i_tt_get_instance: in cache returning %d\n",i)); | |
271 | return i; | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | /* Found the instance in the cache - return the cache index */ | |
277 | ||
278 | for(idx=0;idx<TT_CHC;idx++) { | |
279 | if (!(handle->instanceh[idx].order)) break; /* find the lru item */ | |
280 | } | |
281 | ||
282 | mm_log((1,"i_tt_get_instance: lru item is %d\n",idx)); | |
283 | mm_log((1,"i_tt_get_instance: lru pointer %p\n", | |
284 | USTRCT(handle->instanceh[idx].instance) )); | |
285 | ||
286 | if ( USTRCT(handle->instanceh[idx].instance) ) { | |
287 | mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx)); | |
288 | ||
289 | /* Free cached glyphs */ | |
290 | for(i=0;i<256;i++) | |
291 | if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) ) | |
292 | TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph ); | |
293 | ||
294 | for(i=0;i<256;i++) { | |
295 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
296 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
297 | } | |
298 | ||
299 | /* Free instance if needed */ | |
300 | TT_Done_Instance( handle->instanceh[idx].instance ); | |
301 | } | |
302 | ||
303 | /* create and initialize instance */ | |
304 | /* FIXME: probably a memory leak on fail */ | |
305 | ||
306 | (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || | |
307 | ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) || | |
308 | ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) ); | |
309 | ||
310 | if ( error ) { | |
311 | mm_log((1, "Could not create and initialize instance: error %x.\n", | |
312 | (unsigned)error )); | |
313 | return -1; | |
314 | } | |
315 | ||
316 | /* Now that the instance should the inplace we need to lower all of the | |
317 | ru counts and put `this' one with the highest entry */ | |
318 | ||
319 | for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--; | |
320 | ||
321 | handle->instanceh[idx].order=TT_CHC-1; | |
322 | handle->instanceh[idx].ptsize=points; | |
323 | handle->instanceh[idx].smooth=smooth; | |
324 | TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) ); | |
325 | ||
326 | /* Zero the memory for the glyph storage so they are not thought as | |
327 | cached if they haven't been cached since this new font was loaded */ | |
328 | ||
329 | for(i=0;i<256;i++) { | |
330 | handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR; | |
331 | USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL; | |
332 | } | |
333 | ||
334 | return idx; | |
335 | } | |
336 | ||
337 | ||
338 | /* | |
339 | =item i_tt_new(fontname) | |
340 | ||
341 | Creates a new font handle object, finds a character map and initialise the | |
342 | the font handle's cache | |
343 | ||
344 | fontname - path to the font to load | |
345 | ||
346 | =cut | |
347 | */ | |
348 | ||
349 | TT_Fonthandle* | |
350 | i_tt_new(const char *fontname) { | |
351 | TT_Error error; | |
352 | TT_Fonthandle *handle; | |
353 | unsigned short i,n; | |
354 | unsigned short platform,encoding; | |
355 | i_tt_engine *tteng; | |
356 | ||
357 | if ((tteng = i_init_tt()) == NULL) { | |
358 | i_push_error(0, "Could not initialize FT1 engine"); | |
359 | return NULL; | |
360 | } | |
361 | ||
362 | i_clear_error(); | |
363 | ||
364 | mm_log((1,"i_tt_new(fontname '%s')\n",fontname)); | |
365 | ||
366 | /* allocate memory for the structure */ | |
367 | ||
368 | handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */ | |
369 | ||
370 | /* load the typeface */ | |
371 | error = TT_Open_Face( tteng->engine, fontname, &handle->face ); | |
372 | if ( error ) { | |
373 | if ( error == TT_Err_Could_Not_Open_File ) { | |
374 | mm_log((1, "Could not find/open %s.\n", fontname )); | |
375 | } | |
376 | else { | |
377 | mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, | |
378 | (unsigned)error )); | |
379 | } | |
380 | i_tt_push_error(error); | |
381 | return NULL; | |
382 | } | |
383 | ||
384 | TT_Get_Face_Properties( handle->face, &(handle->properties) ); | |
385 | ||
386 | /* First, look for a Unicode charmap */ | |
387 | n = handle->properties.num_CharMaps; | |
388 | USTRCT( handle->char_map )=NULL; /* Invalidate character map */ | |
389 | ||
390 | for ( i = 0; i < n; i++ ) { | |
391 | TT_Get_CharMap_ID( handle->face, i, &platform, &encoding ); | |
392 | if ( (platform == 3 && encoding == 1 ) | |
393 | || (platform == 0 && encoding == 0 ) ) { | |
394 | mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", | |
395 | platform, encoding)); | |
396 | TT_Get_CharMap( handle->face, i, &(handle->char_map) ); | |
397 | break; | |
398 | } | |
399 | } | |
400 | if (!USTRCT(handle->char_map) && n != 0) { | |
401 | /* just use the first one */ | |
402 | TT_Get_CharMap( handle->face, 0, &(handle->char_map)); | |
403 | } | |
404 | ||
405 | /* Zero the pointsizes - and ordering */ | |
406 | ||
407 | for(i=0;i<TT_CHC;i++) { | |
408 | USTRCT(handle->instanceh[i].instance)=NULL; | |
409 | handle->instanceh[i].order=i; | |
410 | handle->instanceh[i].ptsize=0; | |
411 | handle->instanceh[i].smooth=-1; | |
412 | } | |
413 | ||
414 | #ifdef FTXPOST | |
415 | handle->loaded_names = 0; | |
416 | #endif | |
417 | ||
418 | mm_log((1,"i_tt_new <- %p\n",handle)); | |
419 | return handle; | |
420 | } | |
421 | ||
422 | ||
423 | ||
424 | /* | |
425 | * raster map management | |
426 | */ | |
427 | ||
428 | /* | |
429 | =item i_tt_init_raster_map(bit, width, height, smooth) | |
430 | ||
431 | Allocates internal memory for the bitmap as needed by the parameters (internal) | |
432 | ||
433 | bit - bitmap to allocate into | |
434 | width - width of the bitmap | |
435 | height - height of the bitmap | |
436 | smooth - boolean (True: antialias on, False: antialias is off) | |
437 | ||
438 | =cut | |
439 | */ | |
440 | ||
441 | static | |
442 | void | |
443 | i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) { | |
444 | ||
445 | mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF | |
446 | ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth)); | |
447 | ||
448 | bit->rows = height; | |
449 | bit->width = ( width + 3 ) & -4; | |
450 | bit->flow = TT_Flow_Down; | |
451 | ||
452 | if ( smooth ) { | |
453 | bit->cols = bit->width; | |
454 | bit->size = bit->rows * bit->width; | |
455 | } else { | |
456 | bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */ | |
457 | bit->size = bit->rows * bit->cols; /* number of bytes in buffer */ | |
458 | } | |
459 | ||
460 | /* rows can be 0 for some glyphs, for example ' ' */ | |
461 | if (bit->rows && bit->size / bit->rows != bit->cols) { | |
462 | i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n", | |
463 | bit->width, bit->rows); | |
464 | } | |
465 | ||
466 | mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size )); | |
467 | ||
468 | bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */ | |
469 | if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size ); | |
470 | } | |
471 | ||
472 | ||
473 | /* | |
474 | =item i_tt_clear_raster_map(bit) | |
475 | ||
476 | Frees the bitmap data and sets pointer to NULL (internal) | |
477 | ||
478 | bit - bitmap to free | |
479 | ||
480 | =cut | |
481 | */ | |
482 | ||
483 | static | |
484 | void | |
485 | i_tt_done_raster_map( TT_Raster_Map *bit ) { | |
486 | myfree( bit->bitmap ); | |
487 | bit->bitmap = NULL; | |
488 | } | |
489 | ||
490 | ||
491 | /* | |
492 | =item i_tt_clear_raster_map(bit) | |
493 | ||
494 | Clears the specified bitmap (internal) | |
495 | ||
496 | bit - bitmap to zero | |
497 | ||
498 | =cut | |
499 | */ | |
500 | ||
501 | ||
502 | static | |
503 | void | |
504 | i_tt_clear_raster_map( TT_Raster_Map* bit ) { | |
505 | memset( bit->bitmap, 0, bit->size ); | |
506 | } | |
507 | ||
508 | ||
509 | /* | |
510 | =item i_tt_blit_or(dst, src, x_off, y_off) | |
511 | ||
512 | function that blits one raster map into another (internal) | |
513 | ||
514 | dst - destination bitmap | |
515 | src - source bitmap | |
516 | x_off - x offset into the destination bitmap | |
517 | y_off - y offset into the destination bitmap | |
518 | ||
519 | =cut | |
520 | */ | |
521 | ||
522 | static | |
523 | void | |
524 | i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) { | |
525 | i_img_dim x, y; | |
526 | i_img_dim x1, x2, y1, y2; | |
527 | unsigned char *s, *d; | |
528 | ||
529 | x1 = x_off < 0 ? -x_off : 0; | |
530 | y1 = y_off < 0 ? -y_off : 0; | |
531 | ||
532 | x2 = (int)dst->cols - x_off; | |
533 | if ( x2 > src->cols ) x2 = src->cols; | |
534 | ||
535 | y2 = (int)dst->rows - y_off; | |
536 | if ( y2 > src->rows ) y2 = src->rows; | |
537 | ||
538 | if ( x1 >= x2 ) return; | |
539 | ||
540 | /* do the real work now */ | |
541 | ||
542 | for ( y = y1; y < y2; ++y ) { | |
543 | s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1; | |
544 | d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off; | |
545 | ||
546 | for ( x = x1; x < x2; ++x ) { | |
547 | if (*s > *d) | |
548 | *d = *s; | |
549 | d++; | |
550 | s++; | |
551 | } | |
552 | } | |
553 | } | |
554 | ||
555 | /* useful for debugging */ | |
556 | #if 0 | |
557 | ||
558 | static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) { | |
559 | int x, y; | |
560 | fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow); | |
561 | for (y = 0; y < bit->rows; ++y) { | |
562 | fprintf(out, "%2d:", y); | |
563 | for (x = 0; x < bit->cols; ++x) { | |
564 | if ((x & 7) == 0 && x) putc(' ', out); | |
565 | fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]); | |
566 | } | |
567 | putc('\n', out); | |
568 | } | |
569 | } | |
570 | ||
571 | #endif | |
572 | ||
573 | /* | |
574 | =item i_tt_get_glyph(handle, inst, j) | |
575 | ||
576 | Function to see if a glyph exists and if so cache it (internal) | |
577 | ||
578 | handle - pointer to font handle | |
579 | inst - font instance | |
580 | j - charcode of glyph | |
581 | ||
582 | =cut | |
583 | */ | |
584 | ||
585 | static | |
586 | int | |
587 | i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) { | |
588 | unsigned short load_flags, code; | |
589 | TT_Error error; | |
590 | ||
591 | mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n", | |
592 | handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.'))); | |
593 | ||
594 | /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/ | |
595 | ||
596 | if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) | |
597 | && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) { | |
598 | mm_log((1,"i_tt_get_glyph: %lu in cache\n",j)); | |
599 | return 1; | |
600 | } | |
601 | ||
602 | if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) { | |
603 | /* clean up the entry */ | |
604 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
605 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
606 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR; | |
607 | } | |
608 | ||
609 | /* Ok - it wasn't cached - try to get it in */ | |
610 | load_flags = TTLOAD_SCALE_GLYPH; | |
611 | if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH; | |
612 | ||
613 | if ( !TT_VALID(handle->char_map) ) { | |
614 | code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1); | |
615 | if ( code >= handle->properties.num_Glyphs ) code = 0; | |
616 | } else code = TT_Char_Index( handle->char_map, j ); | |
617 | ||
618 | if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) { | |
619 | mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error )); | |
620 | i_push_error(error, "TT_New_Glyph()"); | |
621 | return 0; | |
622 | } | |
623 | if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) { | |
624 | mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error )); | |
625 | /* Don't leak */ | |
626 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
627 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
628 | i_push_error(error, "TT_Load_Glyph()"); | |
629 | return 0; | |
630 | } | |
631 | ||
632 | /* At this point the glyph should be allocated and loaded */ | |
633 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j; | |
634 | ||
635 | /* Next get the glyph metrics */ | |
636 | error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, | |
637 | &handle->instanceh[inst].gmetrics[TT_HASH(j)] ); | |
638 | if (error) { | |
639 | mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error )); | |
640 | TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ); | |
641 | USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL; | |
642 | handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR; | |
643 | i_push_error(error, "TT_Get_Glyph_Metrics()"); | |
644 | return 0; | |
645 | } | |
646 | ||
647 | return 1; | |
648 | } | |
649 | ||
650 | /* | |
651 | =item i_tt_has_chars(handle, text, len, utf8, out) | |
652 | ||
653 | Check if the given characters are defined by the font. Note that len | |
654 | is the number of bytes, not the number of characters (when utf8 is | |
655 | non-zero). | |
656 | ||
657 | Returns the number of characters that were checked. | |
658 | ||
659 | =cut | |
660 | */ | |
661 | ||
662 | size_t | |
663 | i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8, | |
664 | char *out) { | |
665 | size_t count = 0; | |
666 | mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n", | |
667 | handle, text, (long)len, utf8)); | |
668 | ||
669 | while (len) { | |
670 | unsigned long c; | |
671 | int index; | |
672 | if (utf8) { | |
673 | c = i_utf8_advance(&text, &len); | |
674 | if (c == ~0UL) { | |
675 | i_push_error(0, "invalid UTF8 character"); | |
676 | return 0; | |
677 | } | |
678 | } | |
679 | else { | |
680 | c = (unsigned char)*text++; | |
681 | --len; | |
682 | } | |
683 | ||
684 | if (TT_VALID(handle->char_map)) { | |
685 | index = TT_Char_Index(handle->char_map, c); | |
686 | } | |
687 | else { | |
688 | index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1); | |
689 | if (index >= handle->properties.num_Glyphs) | |
690 | index = 0; | |
691 | } | |
692 | *out++ = index != 0; | |
693 | ++count; | |
694 | } | |
695 | ||
696 | return count; | |
697 | } | |
698 | ||
699 | /* | |
700 | =item i_tt_destroy(handle) | |
701 | ||
702 | Clears the data taken by a font including all cached data such as | |
703 | pixmaps and glyphs | |
704 | ||
705 | handle - pointer to font handle | |
706 | ||
707 | =cut | |
708 | */ | |
709 | ||
710 | void | |
711 | i_tt_destroy( TT_Fonthandle *handle) { | |
712 | TT_Close_Face( handle->face ); | |
713 | myfree( handle ); | |
714 | ||
715 | /* FIXME: Should these be freed automatically by the library? | |
716 | ||
717 | TT_Done_Instance( instance ); | |
718 | void | |
719 | i_tt_done_glyphs( void ) { | |
720 | int i; | |
721 | ||
722 | if ( !glyphs ) return; | |
723 | ||
724 | for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] ); | |
725 | free( glyphs ); | |
726 | ||
727 | glyphs = NULL; | |
728 | } | |
729 | */ | |
730 | } | |
731 | ||
732 | ||
733 | /* | |
734 | * FreeType Rendering functions | |
735 | */ | |
736 | ||
737 | ||
738 | /* | |
739 | =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth) | |
740 | ||
741 | Renders a single glyph into the bit rastermap (internal) | |
742 | ||
743 | handle - pointer to font handle | |
744 | gmetrics - the metrics for the glyph to be rendered | |
745 | bit - large bitmap that is the destination for the text | |
746 | smallbit - small bitmap that is used only if smooth is true | |
747 | x_off - x offset of glyph | |
748 | y_off - y offset of glyph | |
749 | smooth - boolean (True: antialias on, False: antialias is off) | |
750 | ||
751 | =cut | |
752 | */ | |
753 | ||
754 | static | |
755 | void | |
756 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, i_img_dim x_off, i_img_dim y_off, int smooth ) { | |
757 | ||
758 | mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n", | |
759 | USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off), | |
760 | i_DFc(y_off), smooth)); | |
761 | ||
762 | if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64); | |
763 | else { | |
764 | TT_F26Dot6 xmin, ymin, xmax, ymax; | |
765 | ||
766 | xmin = gmetrics->bbox.xMin & -64; | |
767 | ymin = gmetrics->bbox.yMin & -64; | |
768 | xmax = (gmetrics->bbox.xMax + 63) & -64; | |
769 | ymax = (gmetrics->bbox.yMax + 63) & -64; | |
770 | ||
771 | i_tt_clear_raster_map( small_bit ); | |
772 | TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin ); | |
773 | i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off ); | |
774 | } | |
775 | } | |
776 | ||
777 | ||
778 | /* | |
779 | =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth) | |
780 | ||
781 | calls i_tt_render_glyph to render each glyph into the bit rastermap (internal) | |
782 | ||
783 | handle - pointer to font handle | |
784 | inst - font instance | |
785 | bit - large bitmap that is the destination for the text | |
786 | smallbit - small bitmap that is used only if smooth is true | |
787 | txt - string to render | |
788 | len - length of the string to render | |
789 | smooth - boolean (True: antialias on, False: antialias is off) | |
790 | ||
791 | =cut | |
792 | */ | |
793 | ||
794 | static | |
795 | int | |
796 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
797 | TT_Raster_Map *small_bit, i_img_dim cords[6], | |
798 | char const* txt, size_t len, int smooth, int utf8 ) { | |
799 | unsigned long j; | |
800 | TT_F26Dot6 x,y; | |
801 | ||
802 | mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n", | |
803 | handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8)); | |
804 | ||
805 | /* | |
806 | y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM); | |
807 | */ | |
808 | ||
809 | x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */ | |
810 | y=-cords[4]; | |
811 | ||
812 | while (len) { | |
813 | if (utf8) { | |
814 | j = i_utf8_advance(&txt, &len); | |
815 | if (j == ~0UL) { | |
816 | i_push_error(0, "invalid UTF8 character"); | |
817 | return 0; | |
818 | } | |
819 | } | |
820 | else { | |
821 | j = (unsigned char)*txt++; | |
822 | --len; | |
823 | } | |
824 | if ( !i_tt_get_glyph(handle,inst,j) ) | |
825 | continue; | |
826 | i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, | |
827 | &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, | |
828 | small_bit, x, y, smooth ); | |
829 | x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64; | |
830 | } | |
831 | ||
832 | return 1; | |
833 | } | |
834 | ||
835 | ||
836 | /* | |
837 | * Functions to render rasters (single channel images) onto images | |
838 | */ | |
839 | ||
840 | /* | |
841 | =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth) | |
842 | ||
843 | Function to dump a raster onto an image in color used by i_tt_text() (internal). | |
844 | ||
845 | im - image to dump raster on | |
846 | bit - bitmap that contains the text to be dumped to im | |
847 | xb, yb - coordinates, left edge and baseline | |
848 | cl - color to use for text | |
849 | smooth - boolean (True: antialias on, False: antialias is off) | |
850 | ||
851 | =cut | |
852 | */ | |
853 | ||
854 | static | |
855 | void | |
856 | i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) { | |
857 | unsigned char *bmap; | |
858 | i_img_dim x, y; | |
859 | mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n", | |
860 | im, bit, i_DFc(xb), i_DFc(yb), cl)); | |
861 | ||
862 | bmap = bit->bitmap; | |
863 | ||
864 | if ( smooth ) { | |
865 | ||
866 | i_render r; | |
867 | i_render_init(&r, im, bit->cols); | |
868 | for(y=0;y<bit->rows;y++) { | |
869 | i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl); | |
870 | } | |
871 | i_render_done(&r); | |
872 | } else { | |
873 | for(y=0;y<bit->rows;y++) { | |
874 | unsigned mask = 0x80; | |
875 | unsigned char *p = bmap + y * bit->cols; | |
876 | ||
877 | for(x = 0; x < bit->width; x++) { | |
878 | if (*p & mask) { | |
879 | i_ppix(im, x+xb, y+yb, cl); | |
880 | } | |
881 | mask >>= 1; | |
882 | if (!mask) { | |
883 | mask = 0x80; | |
884 | ++p; | |
885 | } | |
886 | } | |
887 | } | |
888 | ||
889 | } | |
890 | } | |
891 | ||
892 | ||
893 | /* | |
894 | =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth) | |
895 | ||
896 | Function to dump a raster onto a single channel image in color (internal) | |
897 | ||
898 | im - image to dump raster on | |
899 | bit - bitmap that contains the text to be dumped to im | |
900 | xb, yb - coordinates, left edge and baseline | |
901 | channel - channel to copy to | |
902 | smooth - boolean (True: antialias on, False: antialias is off) | |
903 | ||
904 | =cut | |
905 | */ | |
906 | ||
907 | static | |
908 | void | |
909 | i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ) { | |
910 | unsigned char *bmap; | |
911 | i_color val; | |
912 | int c; | |
913 | i_img_dim x,y; | |
914 | int old_mask = im->ch_mask; | |
915 | im->ch_mask = 1 << channel; | |
916 | ||
917 | mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n", | |
918 | im, bit, i_DFc(xb), i_DFc(yb), channel)); | |
919 | ||
920 | bmap = bit->bitmap; | |
921 | ||
922 | if ( smooth ) { | |
923 | for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) { | |
924 | c = bmap[y*(bit->cols)+x]; | |
925 | val.channel[channel] = c; | |
926 | i_ppix(im,x+xb,y+yb,&val); | |
927 | } | |
928 | } else { | |
929 | for(y=0;y<bit->rows;y++) { | |
930 | unsigned mask = 0x80; | |
931 | unsigned char *p = bmap + y * bit->cols; | |
932 | ||
933 | for(x=0;x<bit->width;x++) { | |
934 | val.channel[channel] = (*p & mask) ? 255 : 0; | |
935 | i_ppix(im,x+xb,y+yb,&val); | |
936 | ||
937 | mask >>= 1; | |
938 | if (!mask) { | |
939 | ++p; | |
940 | mask = 0x80; | |
941 | } | |
942 | } | |
943 | } | |
944 | } | |
945 | im->ch_mask = old_mask; | |
946 | } | |
947 | ||
948 | ||
949 | /* | |
950 | =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) | |
951 | ||
952 | interface for generating single channel raster of text (internal) | |
953 | ||
954 | handle - pointer to font handle | |
955 | bit - the bitmap that is allocated, rendered into and NOT freed | |
956 | cords - the bounding box (modified in place) | |
957 | points - font size to use | |
958 | txt - string to render | |
959 | len - length of the string to render | |
960 | smooth - boolean (True: antialias on, False: antialias is off) | |
961 | ||
962 | =cut | |
963 | */ | |
964 | ||
965 | static | |
966 | int | |
967 | i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) { | |
968 | int inst; | |
969 | i_img_dim width, height; | |
970 | TT_Raster_Map small_bit; | |
971 | ||
972 | /* find or install an instance */ | |
973 | if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { | |
974 | mm_log((1,"i_tt_rasterize: get instance failed\n")); | |
975 | return 0; | |
976 | } | |
977 | ||
978 | /* calculate bounding box */ | |
979 | if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 )) | |
980 | return 0; | |
981 | ||
982 | ||
983 | width = cords[2]-cords[0]; | |
984 | height = cords[5]-cords[4]; | |
985 | ||
986 | mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n", | |
987 | i_DFc(width), i_DFc(height) )); | |
988 | ||
989 | i_tt_init_raster_map ( bit, width, height, smooth ); | |
990 | i_tt_clear_raster_map( bit ); | |
991 | if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth ); | |
992 | ||
993 | if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, | |
994 | smooth, utf8 )) { | |
995 | if ( smooth ) | |
996 | i_tt_done_raster_map( &small_bit ); | |
997 | return 0; | |
998 | } | |
999 | ||
1000 | if ( smooth ) i_tt_done_raster_map( &small_bit ); | |
1001 | return 1; | |
1002 | } | |
1003 | ||
1004 | ||
1005 | ||
1006 | /* | |
1007 | * Exported text rendering interfaces | |
1008 | */ | |
1009 | ||
1010 | ||
1011 | /* | |
1012 | =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8) | |
1013 | ||
1014 | Interface to text rendering into a single channel in an image | |
1015 | ||
1016 | handle - pointer to font handle | |
1017 | im - image to render text on to | |
1018 | xb, yb - coordinates, left edge and baseline | |
1019 | channel - channel to render into | |
1020 | points - font size to use | |
1021 | txt - string to render | |
1022 | len - length of the string to render | |
1023 | smooth - boolean (True: antialias on, False: antialias is off) | |
1024 | ||
1025 | =cut | |
1026 | */ | |
1027 | ||
1028 | undef_int | |
1029 | i_tt_cp( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) { | |
1030 | ||
1031 | i_img_dim cords[BOUNDING_BOX_COUNT]; | |
1032 | i_img_dim ascent, st_offset, y; | |
1033 | TT_Raster_Map bit; | |
1034 | ||
1035 | i_clear_error(); | |
1036 | if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; | |
1037 | ||
1038 | ascent=cords[BBOX_ASCENT]; | |
1039 | st_offset=cords[BBOX_NEG_WIDTH]; | |
1040 | y = align ? yb-ascent : yb; | |
1041 | ||
1042 | i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth ); | |
1043 | i_tt_done_raster_map( &bit ); | |
1044 | ||
1045 | return 1; | |
1046 | } | |
1047 | ||
1048 | ||
1049 | /* | |
1050 | =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) | |
1051 | ||
1052 | Interface to text rendering in a single color onto an image | |
1053 | ||
1054 | handle - pointer to font handle | |
1055 | im - image to render text on to | |
1056 | xb, yb - coordinates, left edge and baseline | |
1057 | cl - color to use for text | |
1058 | points - font size to use | |
1059 | txt - string to render | |
1060 | len - length of the string to render | |
1061 | smooth - boolean (True: antialias on, False: antialias is off) | |
1062 | ||
1063 | =cut | |
1064 | */ | |
1065 | ||
1066 | undef_int | |
1067 | i_tt_text( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) { | |
1068 | i_img_dim cords[BOUNDING_BOX_COUNT]; | |
1069 | i_img_dim ascent, st_offset, y; | |
1070 | TT_Raster_Map bit; | |
1071 | ||
1072 | i_clear_error(); | |
1073 | ||
1074 | if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0; | |
1075 | ||
1076 | ascent=cords[BBOX_ASCENT]; | |
1077 | st_offset=cords[BBOX_NEG_WIDTH]; | |
1078 | y = align ? yb-ascent : yb; | |
1079 | ||
1080 | i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); | |
1081 | i_tt_done_raster_map( &bit ); | |
1082 | ||
1083 | return 1; | |
1084 | } | |
1085 | ||
1086 | ||
1087 | /* | |
1088 | =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) | |
1089 | ||
1090 | Function to get texts bounding boxes given the instance of the font (internal) | |
1091 | ||
1092 | handle - pointer to font handle | |
1093 | inst - font instance | |
1094 | txt - string to measure | |
1095 | len - length of the string to render | |
1096 | cords - the bounding box (modified in place) | |
1097 | ||
1098 | =cut | |
1099 | */ | |
1100 | ||
1101 | static | |
1102 | undef_int | |
1103 | i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) { | |
1104 | int upm, casc, cdesc, first; | |
1105 | ||
1106 | int start = 0; | |
1107 | i_img_dim width = 0; | |
1108 | int gdescent = 0; | |
1109 | int gascent = 0; | |
1110 | int descent = 0; | |
1111 | int ascent = 0; | |
1112 | int rightb = 0; | |
1113 | ||
1114 | unsigned long j; | |
1115 | unsigned char *ustr; | |
1116 | ustr=(unsigned char*)txt; | |
1117 | ||
1118 | mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n", | |
1119 | handle, inst, (int)len, txt, (long)len, utf8)); | |
1120 | ||
1121 | upm = handle->properties.header->Units_Per_EM; | |
1122 | gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm; | |
1123 | gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm; | |
1124 | ||
1125 | width = 0; | |
1126 | start = 0; | |
1127 | ||
1128 | mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent)); | |
1129 | ||
1130 | first=1; | |
1131 | while (len) { | |
1132 | if (utf8) { | |
1133 | j = i_utf8_advance(&txt, &len); | |
1134 | if (j == ~0UL) { | |
1135 | i_push_error(0, "invalid UTF8 character"); | |
1136 | return 0; | |
1137 | } | |
1138 | } | |
1139 | else { | |
1140 | j = (unsigned char)*txt++; | |
1141 | --len; | |
1142 | } | |
1143 | if ( i_tt_get_glyph(handle,inst,j) ) { | |
1144 | TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j); | |
1145 | width += gm->advance / 64; | |
1146 | casc = (gm->bbox.yMax+63) / 64; | |
1147 | cdesc = (gm->bbox.yMin-63) / 64; | |
1148 | ||
1149 | mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", | |
1150 | (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc)); | |
1151 | ||
1152 | if (first) { | |
1153 | start = gm->bbox.xMin / 64; | |
1154 | ascent = (gm->bbox.yMax+63) / 64; | |
1155 | descent = (gm->bbox.yMin-63) / 64; | |
1156 | first = 0; | |
1157 | } | |
1158 | if (!len) { /* if at end of string */ | |
1159 | /* the right-side bearing - in case the right-side of a | |
1160 | character goes past the right of the advance width, | |
1161 | as is common for italic fonts | |
1162 | */ | |
1163 | rightb = gm->advance - gm->bearingX | |
1164 | - (gm->bbox.xMax - gm->bbox.xMin); | |
1165 | /* fprintf(stderr, "font info last: %d %d %d %d\n", | |
1166 | gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */ | |
1167 | } | |
1168 | ||
1169 | ascent = (ascent > casc ? ascent : casc ); | |
1170 | descent = (descent < cdesc ? descent : cdesc); | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | cords[BBOX_NEG_WIDTH]=start; | |
1175 | cords[BBOX_GLOBAL_DESCENT]=gdescent; | |
1176 | cords[BBOX_POS_WIDTH]=width; | |
1177 | if (rightb < 0) | |
1178 | cords[BBOX_POS_WIDTH] -= rightb / 64; | |
1179 | cords[BBOX_GLOBAL_ASCENT]=gascent; | |
1180 | cords[BBOX_DESCENT]=descent; | |
1181 | cords[BBOX_ASCENT]=ascent; | |
1182 | cords[BBOX_ADVANCE_WIDTH] = width; | |
1183 | cords[BBOX_RIGHT_BEARING] = rightb / 64; | |
1184 | ||
1185 | return BBOX_RIGHT_BEARING + 1; | |
1186 | } | |
1187 | ||
1188 | ||
1189 | /* | |
1190 | =item i_tt_bbox(handle, points, txt, len, cords, utf8) | |
1191 | ||
1192 | Interface to get a strings bounding box | |
1193 | ||
1194 | handle - pointer to font handle | |
1195 | points - font size to use | |
1196 | txt - string to render | |
1197 | len - length of the string to render | |
1198 | cords - the bounding box (modified in place) | |
1199 | ||
1200 | =cut | |
1201 | */ | |
1202 | ||
1203 | undef_int | |
1204 | i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) { | |
1205 | int inst; | |
1206 | ||
1207 | i_clear_error(); | |
1208 | mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n", | |
1209 | handle, points, (int)len, txt, (long)len, utf8)); | |
1210 | ||
1211 | if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) { | |
1212 | i_push_errorf(0, "i_tt_get_instance(%g)", points); | |
1213 | mm_log((1,"i_tt_text: get instance failed\n")); | |
1214 | return 0; | |
1215 | } | |
1216 | ||
1217 | return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8); | |
1218 | } | |
1219 | ||
1220 | /* | |
1221 | =item i_tt_face_name(handle, name_buf, name_buf_size) | |
1222 | ||
1223 | Retrieve's the font's postscript name. | |
1224 | ||
1225 | This is complicated by the need to handle encodings and so on. | |
1226 | ||
1227 | =cut | |
1228 | */ | |
1229 | size_t | |
1230 | i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) { | |
1231 | TT_Face_Properties props; | |
1232 | int name_count; | |
1233 | int i; | |
1234 | TT_UShort platform_id, encoding_id, lang_id, name_id; | |
1235 | TT_UShort name_len; | |
1236 | TT_String *name; | |
1237 | int want_index = -1; /* an acceptable but not perfect name */ | |
1238 | int score = 0; | |
1239 | ||
1240 | i_clear_error(); | |
1241 | ||
1242 | TT_Get_Face_Properties(handle->face, &props); | |
1243 | name_count = props.num_Names; | |
1244 | for (i = 0; i < name_count; ++i) { | |
1245 | TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, | |
1246 | &name_id); | |
1247 | ||
1248 | TT_Get_Name_String(handle->face, i, &name, &name_len); | |
1249 | ||
1250 | if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len | |
1251 | && name_id == TT_NAME_ID_PS_NAME) { | |
1252 | int might_want_index = -1; | |
1253 | int might_score = 0; | |
1254 | if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN) | |
1255 | || | |
1256 | (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) { | |
1257 | /* exactly what we want */ | |
1258 | want_index = i; | |
1259 | break; | |
1260 | } | |
1261 | ||
1262 | if (platform_id == TT_PLATFORM_MICROSOFT | |
1263 | && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) { | |
1264 | /* any english is good */ | |
1265 | might_want_index = i; | |
1266 | might_score = 9; | |
1267 | } | |
1268 | /* there might be something in between */ | |
1269 | else { | |
1270 | /* anything non-unicode is better than nothing */ | |
1271 | might_want_index = i; | |
1272 | might_score = 1; | |
1273 | } | |
1274 | if (might_score > score) { | |
1275 | score = might_score; | |
1276 | want_index = might_want_index; | |
1277 | } | |
1278 | } | |
1279 | } | |
1280 | ||
1281 | if (want_index != -1) { | |
1282 | TT_Get_Name_String(handle->face, want_index, &name, &name_len); | |
1283 | ||
1284 | strncpy(name_buf, name, name_buf_size); | |
1285 | name_buf[name_buf_size-1] = '\0'; | |
1286 | ||
1287 | return strlen(name) + 1; | |
1288 | } | |
1289 | else { | |
1290 | i_push_error(0, "no face name present"); | |
1291 | return 0; | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | void i_tt_dump_names(TT_Fonthandle *handle) { | |
1296 | TT_Face_Properties props; | |
1297 | int name_count; | |
1298 | int i; | |
1299 | TT_UShort platform_id, encoding_id, lang_id, name_id; | |
1300 | TT_UShort name_len; | |
1301 | TT_String *name; | |
1302 | ||
1303 | TT_Get_Face_Properties(handle->face, &props); | |
1304 | name_count = props.num_Names; | |
1305 | for (i = 0; i < name_count; ++i) { | |
1306 | TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, | |
1307 | &name_id); | |
1308 | TT_Get_Name_String(handle->face, i, &name, &name_len); | |
1309 | ||
1310 | printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id, | |
1311 | encoding_id, lang_id, name_id); | |
1312 | if (platform_id == TT_PLATFORM_APPLE_UNICODE) { | |
1313 | printf("(unicode)\n"); | |
1314 | } | |
1315 | else { | |
1316 | printf("'%s'\n", name); | |
1317 | } | |
1318 | } | |
1319 | fflush(stdout); | |
1320 | } | |
1321 | ||
1322 | size_t | |
1323 | i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, | |
1324 | size_t name_buf_size) { | |
1325 | #ifdef FTXPOST | |
1326 | TT_Error rc; | |
1327 | TT_String *psname; | |
1328 | TT_UShort index; | |
1329 | ||
1330 | i_clear_error(); | |
1331 | ||
1332 | if (!handle->loaded_names) { | |
1333 | TT_Post post; | |
1334 | mm_log((1, "Loading PS Names")); | |
1335 | handle->load_cond = TT_Load_PS_Names(handle->face, &post); | |
1336 | ++handle->loaded_names; | |
1337 | } | |
1338 | ||
1339 | if (handle->load_cond) { | |
1340 | i_push_errorf(handle->load_cond, "error loading names (%#x)", | |
1341 | (unsigned)handle->load_cond); | |
1342 | return 0; | |
1343 | } | |
1344 | ||
1345 | index = TT_Char_Index(handle->char_map, ch); | |
1346 | if (!index) { | |
1347 | i_push_error(0, "no such character"); | |
1348 | return 0; | |
1349 | } | |
1350 | ||
1351 | rc = TT_Get_PS_Name(handle->face, index, &psname); | |
1352 | ||
1353 | if (rc) { | |
1354 | i_push_error(rc, "error getting name"); | |
1355 | return 0; | |
1356 | } | |
1357 | ||
1358 | strncpy(name_buf, psname, name_buf_size); | |
1359 | name_buf[name_buf_size-1] = '\0'; | |
1360 | ||
1361 | return strlen(psname) + 1; | |
1362 | #else | |
1363 | mm_log((1, "FTXPOST extension not enabled\n")); | |
1364 | i_clear_error(); | |
1365 | i_push_error(0, "Use of FTXPOST extension disabled"); | |
1366 | ||
1367 | return 0; | |
1368 | #endif | |
1369 | } | |
1370 | ||
1371 | /* | |
1372 | =item i_tt_push_error(code) | |
1373 | ||
1374 | Push an error message and code onto the Imager error stack. | |
1375 | ||
1376 | =cut | |
1377 | */ | |
1378 | static void | |
1379 | i_tt_push_error(TT_Error rc) { | |
1380 | #ifdef FTXERR18 | |
1381 | TT_String const *msg = TT_ErrToString18(rc); | |
1382 | ||
1383 | i_push_error(rc, msg); | |
1384 | #else | |
1385 | i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc); | |
1386 | #endif | |
1387 | } | |
1388 | ||
1389 | ||
1390 | /* | |
1391 | =back | |
1392 | ||
1393 | =head1 AUTHOR | |
1394 | ||
1395 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
1396 | ||
1397 | =head1 SEE ALSO | |
1398 | ||
1399 | Imager(3) | |
1400 | ||
1401 | =cut | |
1402 | */ |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include <math.h> |
2 | 3 | |
24 | 25 | double res[MAXCHANNELS]; |
25 | 26 | i_img *timg; |
26 | 27 | int radius, diameter; |
28 | dIMCTXim(im); | |
27 | 29 | |
28 | mm_log((1,"i_gaussian(im %p, stdev %.2f)\n",im,stddev)); | |
30 | im_log((aIMCTX, 1,"i_gaussian(im %p, stdev %.2f)\n",im,stddev)); | |
29 | 31 | i_clear_error(); |
30 | 32 | |
31 | 33 | if (stddev <= 0) { |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imageri.h" |
1 | 2 | #include <stdlib.h> |
2 | 3 | |
67 | 68 | size_t bytes = count_y * sizeof(i_int_hline_entry *); |
68 | 69 | |
69 | 70 | if (bytes / count_y != sizeof(i_int_hline_entry *)) { |
70 | i_fatal(3, "integer overflow calculating memory allocation\n"); | |
71 | dIMCTX; | |
72 | im_fatal(aIMCTX, 3, "integer overflow calculating memory allocation\n"); | |
71 | 73 | } |
72 | 74 | |
73 | 75 | hlines->start_y = start_y; |
110 | 112 | i_img_dim x_limit = x + width; |
111 | 113 | |
112 | 114 | if (width < 0) { |
113 | i_fatal(3, "negative width %d passed to i_int_hlines_add\n", width); | |
115 | dIMCTX; | |
116 | im_fatal(aIMCTX, 3, "negative width %d passed to i_int_hlines_add\n", width); | |
114 | 117 | } |
115 | 118 | |
116 | 119 | /* just return if out of range */ |
0 | #define IMAGER_NO_CONTEXT | |
1 | ||
0 | 2 | #include "imager.h" |
1 | 3 | #include "imageri.h" |
2 | 4 | |
11 | 13 | i_color *c; |
12 | 14 | c = i_color_new(red, green, blue, alpha); |
13 | 15 | ICL_DESTROY(c); |
14 | i = i_img_new(); | |
16 | i = i_img_8_new(); | |
15 | 17 | i_img_destroy(i); |
16 | 18 | // and much more |
17 | 19 | |
28 | 30 | |
29 | 31 | =cut |
30 | 32 | */ |
33 | ||
34 | im_context_t (*im_get_context)(void) = NULL; | |
31 | 35 | |
32 | 36 | #define XAXIS 0 |
33 | 37 | #define YAXIS 1 |
39 | 43 | void i_linker_bug_fake(void) { ceil(1); } |
40 | 44 | |
41 | 45 | /* |
42 | =item i_img_alloc() | |
46 | =item im_img_alloc(aIMCTX) | |
47 | X<im_img_alloc API>X<i_img_alloc API> | |
43 | 48 | =category Image Implementation |
49 | =synopsis i_img *im = im_img_alloc(aIMCTX); | |
50 | =synopsis i_img *im = i_img_alloc(); | |
44 | 51 | |
45 | 52 | Allocates a new i_img structure. |
46 | 53 | |
69 | 76 | */ |
70 | 77 | |
71 | 78 | i_img * |
72 | i_img_alloc(void) { | |
79 | im_img_alloc(pIMCTX) { | |
73 | 80 | return mymalloc(sizeof(i_img)); |
74 | 81 | } |
75 | 82 | |
76 | 83 | /* |
77 | =item i_img_init(C<img>) | |
84 | =item im_img_init(aIMCTX, image) | |
85 | X<im_img_init API>X<i_img_init API> | |
78 | 86 | =category Image Implementation |
87 | =synopsis im_img_init(aIMCTX, im); | |
88 | =synopsis i_img_init(im); | |
79 | 89 | |
80 | 90 | Imager internal initialization of images. |
81 | 91 | |
82 | Currently this does very little, in the future it may be used to | |
83 | support threads, or color profiles. | |
92 | See L</im_img_alloc(aIMCTX)> for more information. | |
84 | 93 | |
85 | 94 | =cut |
86 | 95 | */ |
87 | 96 | |
88 | 97 | void |
89 | i_img_init(i_img *img) { | |
98 | im_img_init(pIMCTX, i_img *img) { | |
90 | 99 | img->im_data = NULL; |
100 | img->context = aIMCTX; | |
101 | im_context_refinc(aIMCTX, "img_init"); | |
91 | 102 | } |
92 | 103 | |
93 | 104 | /* |
106 | 117 | i_color * |
107 | 118 | ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) { |
108 | 119 | i_color *cl = NULL; |
109 | ||
110 | mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a)); | |
111 | ||
112 | if ( (cl=mymalloc(sizeof(i_color))) == NULL) i_fatal(2,"malloc() error\n"); | |
120 | dIMCTX; | |
121 | ||
122 | im_log((aIMCTX,1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a)); | |
123 | ||
124 | if ( (cl=mymalloc(sizeof(i_color))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n"); | |
113 | 125 | cl->rgba.r = r; |
114 | 126 | cl->rgba.g = g; |
115 | 127 | cl->rgba.b = b; |
116 | 128 | cl->rgba.a = a; |
117 | mm_log((1,"(%p) <- ICL_new_internal\n",cl)); | |
129 | im_log((aIMCTX,1,"(%p) <- ICL_new_internal\n",cl)); | |
118 | 130 | return cl; |
119 | 131 | } |
120 | 132 | |
135 | 147 | |
136 | 148 | i_color * |
137 | 149 | ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) { |
138 | mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a)); | |
150 | dIMCTX; | |
151 | im_log((aIMCTX,1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a)); | |
139 | 152 | if (cl == NULL) |
140 | 153 | if ( (cl=mymalloc(sizeof(i_color))) == NULL) |
141 | i_fatal(2,"malloc() error\n"); | |
154 | im_fatal(aIMCTX, 2,"malloc() error\n"); | |
142 | 155 | cl->rgba.r=r; |
143 | 156 | cl->rgba.g=g; |
144 | 157 | cl->rgba.b=b; |
145 | 158 | cl->rgba.a=a; |
146 | mm_log((1,"(%p) <- ICL_set_internal\n",cl)); | |
159 | im_log((aIMCTX,1,"(%p) <- ICL_set_internal\n",cl)); | |
147 | 160 | return cl; |
148 | 161 | } |
149 | 162 | |
181 | 194 | |
182 | 195 | void |
183 | 196 | ICL_info(i_color const *cl) { |
184 | mm_log((1,"i_color_info(cl* %p)\n",cl)); | |
185 | mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a)); | |
197 | dIMCTX; | |
198 | im_log((aIMCTX, 1,"i_color_info(cl* %p)\n",cl)); | |
199 | im_log((aIMCTX, 1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a)); | |
186 | 200 | } |
187 | 201 | |
188 | 202 | /* |
197 | 211 | |
198 | 212 | void |
199 | 213 | ICL_DESTROY(i_color *cl) { |
200 | mm_log((1,"ICL_DESTROY(cl* %p)\n",cl)); | |
214 | dIMCTX; | |
215 | im_log((aIMCTX, 1,"ICL_DESTROY(cl* %p)\n",cl)); | |
201 | 216 | myfree(cl); |
202 | 217 | } |
203 | 218 | |
208 | 223 | */ |
209 | 224 | i_fcolor *i_fcolor_new(double r, double g, double b, double a) { |
210 | 225 | i_fcolor *cl = NULL; |
211 | ||
212 | mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a)); | |
213 | ||
214 | if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) i_fatal(2,"malloc() error\n"); | |
226 | dIMCTX; | |
227 | ||
228 | im_log((aIMCTX, 1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a)); | |
229 | ||
230 | if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n"); | |
215 | 231 | cl->rgba.r = r; |
216 | 232 | cl->rgba.g = g; |
217 | 233 | cl->rgba.b = b; |
218 | 234 | cl->rgba.a = a; |
219 | mm_log((1,"(%p) <- i_fcolor_new\n",cl)); | |
235 | im_log((aIMCTX, 1,"(%p) <- i_fcolor_new\n",cl)); | |
220 | 236 | |
221 | 237 | return cl; |
222 | 238 | } |
242 | 258 | |
243 | 259 | void |
244 | 260 | i_img_exorcise(i_img *im) { |
245 | mm_log((1,"i_img_exorcise(im* %p)\n",im)); | |
261 | dIMCTXim(im); | |
262 | im_log((aIMCTX,1,"i_img_exorcise(im* %p)\n",im)); | |
246 | 263 | i_tags_destroy(&im->tags); |
247 | 264 | if (im->i_f_destroy) |
248 | 265 | (im->i_f_destroy)(im); |
268 | 285 | |
269 | 286 | void |
270 | 287 | i_img_destroy(i_img *im) { |
271 | mm_log((1,"i_img_destroy(im %p)\n",im)); | |
288 | dIMCTXim(im); | |
289 | im_log((aIMCTX, 1,"i_img_destroy(im %p)\n",im)); | |
272 | 290 | i_img_exorcise(im); |
273 | 291 | if (im) { myfree(im); } |
292 | im_context_refdec(aIMCTX, "img_destroy"); | |
274 | 293 | } |
275 | 294 | |
276 | 295 | /* |
296 | 315 | |
297 | 316 | void |
298 | 317 | i_img_info(i_img *im, i_img_dim *info) { |
299 | mm_log((1,"i_img_info(im %p)\n",im)); | |
318 | dIMCTXim(im); | |
319 | im_log((aIMCTX,1,"i_img_info(im %p)\n",im)); | |
300 | 320 | if (im != NULL) { |
301 | mm_log((1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d " | |
321 | im_log((aIMCTX,1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d " | |
302 | 322 | "mask=%ud\n", |
303 | 323 | i_DFc(im->xsize), i_DFc(im->ysize), im->channels,im->ch_mask)); |
304 | mm_log((1,"i_img_info: idata=%p\n",im->idata)); | |
324 | im_log((aIMCTX,1,"i_img_info: idata=%p\n",im->idata)); | |
305 | 325 | info[0] = im->xsize; |
306 | 326 | info[1] = im->ysize; |
307 | 327 | info[2] = im->channels; |
400 | 420 | i_color pv; |
401 | 421 | i_img_dim x,y,t,ttx,tty,tt; |
402 | 422 | int ch; |
403 | ||
404 | mm_log((1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), " | |
423 | dIMCTXim(im); | |
424 | ||
425 | im_log((aIMCTX, 1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), " | |
405 | 426 | "to(" i_DFp "), trans* %p)\n", |
406 | 427 | im, src, i_DFcp(x1, y1), i_DFcp(x2, y2), i_DFcp(tx, ty), trans)); |
407 | 428 | |
444 | 465 | i_img * |
445 | 466 | i_copy(i_img *src) { |
446 | 467 | i_img_dim y, y1, x1; |
468 | dIMCTXim(src); | |
447 | 469 | i_img *im = i_sametype(src, src->xsize, src->ysize); |
448 | 470 | |
449 | mm_log((1,"i_copy(src %p)\n", src)); | |
471 | im_log((aIMCTX,1,"i_copy(src %p)\n", src)); | |
450 | 472 | |
451 | 473 | if (!im) |
452 | 474 | return NULL; |
532 | 554 | i_img *new_img; |
533 | 555 | int has_alpha = i_img_has_alpha(im); |
534 | 556 | int color_chans = i_img_color_channels(im); |
557 | dIMCTXim(im); | |
535 | 558 | |
536 | 559 | i_clear_error(); |
537 | mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis)); | |
560 | im_log((aIMCTX, 1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis)); | |
538 | 561 | |
539 | 562 | if (Axis == XAXIS) { |
540 | 563 | hsize = (i_img_dim)(0.5 + im->xsize * Value); |
559 | 582 | iEnd = hsize; |
560 | 583 | } |
561 | 584 | |
562 | new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels); | |
585 | new_img = i_img_8_new(hsize, vsize, im->channels); | |
563 | 586 | if (!new_img) { |
564 | 587 | i_push_error(0, "cannot create output image"); |
565 | 588 | return NULL; |
709 | 732 | myfree(l0); |
710 | 733 | myfree(l1); |
711 | 734 | |
712 | mm_log((1,"(%p) <- i_scaleaxis\n", new_img)); | |
735 | im_log((aIMCTX, 1,"(%p) <- i_scaleaxis\n", new_img)); | |
713 | 736 | |
714 | 737 | return new_img; |
715 | 738 | } |
732 | 755 | i_img_dim nxsize,nysize,nx,ny; |
733 | 756 | i_img *new_img; |
734 | 757 | i_color val; |
735 | ||
736 | mm_log((1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy)); | |
758 | dIMCTXim(im); | |
759 | ||
760 | im_log((aIMCTX, 1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy)); | |
737 | 761 | |
738 | 762 | nxsize = (i_img_dim) ((double) im->xsize * scx); |
739 | 763 | if (nxsize < 1) { |
754 | 778 | i_ppix(new_img,nx,ny,&val); |
755 | 779 | } |
756 | 780 | |
757 | mm_log((1,"(%p) <- i_scale_nn\n",new_img)); | |
781 | im_log((aIMCTX, 1,"(%p) <- i_scale_nn\n",new_img)); | |
758 | 782 | |
759 | 783 | return new_img; |
760 | 784 | } |
772 | 796 | =cut |
773 | 797 | */ |
774 | 798 | |
775 | i_img *i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) { | |
799 | i_img * | |
800 | i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) { | |
801 | dIMCTXim(src); | |
802 | ||
776 | 803 | if (src->type == i_direct_type) { |
777 | 804 | if (src->bits == 8) { |
778 | 805 | return i_img_empty_ch(NULL, xsize, ysize, src->channels); |
815 | 842 | =cut |
816 | 843 | */ |
817 | 844 | |
818 | i_img *i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) { | |
845 | i_img * | |
846 | i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) { | |
847 | dIMCTXim(src); | |
848 | ||
819 | 849 | if (src->bits == 8) { |
820 | 850 | return i_img_empty_ch(NULL, xsize, ysize, channels); |
821 | 851 | } |
856 | 886 | i_img_dim nxsize,nysize,nx,ny; |
857 | 887 | i_img *new_img; |
858 | 888 | i_color val; |
889 | dIMCTXim(im); | |
859 | 890 | |
860 | mm_log((1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen)); | |
891 | im_log((aIMCTX, 1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen)); | |
861 | 892 | |
862 | 893 | nxsize = im->xsize; |
863 | 894 | nysize = im->ysize ; |
879 | 910 | i_ppix(new_img,nx,ny,&val); |
880 | 911 | } |
881 | 912 | |
882 | mm_log((1,"(%p) <- i_transform\n",new_img)); | |
913 | im_log((aIMCTX, 1,"(%p) <- i_transform\n",new_img)); | |
883 | 914 | return new_img; |
884 | 915 | } |
885 | 916 | |
902 | 933 | int ch, chb; |
903 | 934 | float tdiff; |
904 | 935 | i_color val1,val2; |
905 | ||
906 | mm_log((1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2)); | |
936 | dIMCTXim(im1); | |
937 | ||
938 | im_log((aIMCTX, 1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2)); | |
907 | 939 | |
908 | 940 | xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize; |
909 | 941 | yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize; |
910 | 942 | chb=(im1->channels<im2->channels)?im1->channels:im2->channels; |
911 | 943 | |
912 | mm_log((1,"i_img_diff: b=(" i_DFp ") chb=%d\n", | |
944 | im_log((aIMCTX, 1,"i_img_diff: b=(" i_DFp ") chb=%d\n", | |
913 | 945 | i_DFcp(xb,yb), chb)); |
914 | 946 | |
915 | 947 | tdiff=0; |
919 | 951 | |
920 | 952 | for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]); |
921 | 953 | } |
922 | mm_log((1,"i_img_diff <- (%.2f)\n",tdiff)); | |
954 | im_log((aIMCTX, 1,"i_img_diff <- (%.2f)\n",tdiff)); | |
923 | 955 | return tdiff; |
924 | 956 | } |
925 | 957 | |
944 | 976 | int ch, chb; |
945 | 977 | double tdiff; |
946 | 978 | i_fcolor val1,val2; |
947 | ||
948 | mm_log((1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2)); | |
979 | dIMCTXim(im1); | |
980 | ||
981 | im_log((aIMCTX, 1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2)); | |
949 | 982 | |
950 | 983 | xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize; |
951 | 984 | yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize; |
952 | 985 | chb=(im1->channels<im2->channels)?im1->channels:im2->channels; |
953 | 986 | |
954 | mm_log((1,"i_img_diffd: b(" i_DFp ") chb=%d\n", | |
987 | im_log((aIMCTX, 1,"i_img_diffd: b(" i_DFp ") chb=%d\n", | |
955 | 988 | i_DFcp(xb, yb), chb)); |
956 | 989 | |
957 | 990 | tdiff=0; |
964 | 997 | tdiff += sdiff * sdiff; |
965 | 998 | } |
966 | 999 | } |
967 | mm_log((1,"i_img_diffd <- (%.2f)\n",tdiff)); | |
1000 | im_log((aIMCTX, 1,"i_img_diffd <- (%.2f)\n",tdiff)); | |
968 | 1001 | |
969 | 1002 | return tdiff; |
970 | 1003 | } |
974 | 1007 | i_img_dim x,y,xb,yb; |
975 | 1008 | int ch, chb; |
976 | 1009 | i_fcolor val1,val2; |
1010 | dIMCTXim(im1); | |
977 | 1011 | |
978 | 1012 | if (what == NULL) |
979 | 1013 | what = "(null)"; |
980 | 1014 | |
981 | mm_log((1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what)); | |
1015 | im_log((aIMCTX,1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what)); | |
982 | 1016 | |
983 | 1017 | xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize; |
984 | 1018 | yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize; |
985 | 1019 | chb=(im1->channels<im2->channels)?im1->channels:im2->channels; |
986 | 1020 | |
987 | mm_log((1,"i_img_samef: b(" i_DFp ") chb=%d\n", | |
1021 | im_log((aIMCTX, 1,"i_img_samef: b(" i_DFp ") chb=%d\n", | |
988 | 1022 | i_DFcp(xb, yb), chb)); |
989 | 1023 | |
990 | 1024 | for(y = 0; y < yb; y++) { |
995 | 1029 | for(ch = 0; ch < chb; ch++) { |
996 | 1030 | double sdiff = val1.channel[ch] - val2.channel[ch]; |
997 | 1031 | if (fabs(sdiff) > epsilon) { |
998 | mm_log((1,"i_img_samef <- different %g @(" i_DFp ")\n", | |
1032 | im_log((aIMCTX, 1,"i_img_samef <- different %g @(" i_DFp ")\n", | |
999 | 1033 | sdiff, i_DFcp(x, y))); |
1000 | 1034 | return 0; |
1001 | 1035 | } |
1002 | 1036 | } |
1003 | 1037 | } |
1004 | 1038 | } |
1005 | mm_log((1,"i_img_samef <- same\n")); | |
1039 | im_log((aIMCTX, 1,"i_img_samef <- same\n")); | |
1006 | 1040 | |
1007 | 1041 | return 1; |
1008 | 1042 | } |
1017 | 1051 | int ch,c; |
1018 | 1052 | i_img *new_img,*new_img2; |
1019 | 1053 | i_color val1,val2,dval1,dval2; |
1054 | dIMCTXim(im); | |
1020 | 1055 | |
1021 | 1056 | mx=im->xsize; |
1022 | 1057 | my=im->ysize; |
1428 | 1463 | i_img_dim |
1429 | 1464 | i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, |
1430 | 1465 | const int *chans, int chan_count, int bits) { |
1466 | dIMCTXim(im); | |
1467 | ||
1431 | 1468 | if (bits < 1 || bits > 32) { |
1432 | 1469 | i_push_error(0, "Invalid bits, must be 1..32"); |
1433 | 1470 | return -1; |
1452 | 1489 | /* make sure we have good channel numbers */ |
1453 | 1490 | for (ch = 0; ch < chan_count; ++ch) { |
1454 | 1491 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
1455 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
1492 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
1456 | 1493 | return -1; |
1457 | 1494 | } |
1458 | 1495 | } |
1 | 1 | #define _IMAGE_H_ |
2 | 2 | |
3 | 3 | #include "imconfig.h" |
4 | #include "immacros.h" | |
4 | 5 | #include "imio.h" |
5 | 6 | #include "iolayer.h" |
6 | #include "log.h" | |
7 | 7 | #include "stackmach.h" |
8 | ||
9 | 8 | |
10 | 9 | #ifndef _MSC_VER |
11 | 10 | #include <unistd.h> |
47 | 46 | extern void i_rgb_to_hsv(i_color *color); |
48 | 47 | extern void i_hsv_to_rgb(i_color *color); |
49 | 48 | |
50 | i_img *i_img_8_new(i_img_dim x,i_img_dim y,int ch); | |
51 | i_img *i_img_new( void ); | |
52 | i_img *i_img_empty(i_img *im,i_img_dim x,i_img_dim y); | |
53 | i_img *i_img_empty_ch(i_img *im,i_img_dim x,i_img_dim y,int ch); | |
49 | i_img *im_img_8_new(pIMCTX, i_img_dim x,i_img_dim y,int ch); | |
50 | #define i_img_empty(im, x, y) i_img_empty_ch((im), (x), (y), 3) | |
51 | i_img *im_img_empty_ch(pIMCTX, i_img *im,i_img_dim x,i_img_dim y,int ch); | |
52 | #define i_img_empty_ch(im, x, y, ch) im_img_empty_ch(aIMCTX, (im), (x), (y), (ch)) | |
54 | 53 | void i_img_exorcise(i_img *im); |
55 | 54 | void i_img_destroy(i_img *im); |
56 | i_img *i_img_alloc(void); | |
57 | void i_img_init(i_img *im); | |
55 | i_img *im_img_alloc(pIMCTX); | |
56 | void im_img_init(pIMCTX, i_img *im); | |
58 | 57 | |
59 | 58 | void i_img_info(i_img *im,i_img_dim *info); |
60 | 59 | |
61 | 60 | extern i_img *i_sametype(i_img *im, i_img_dim xsize, i_img_dim ysize); |
62 | 61 | extern i_img *i_sametype_chans(i_img *im, i_img_dim xsize, i_img_dim ysize, int channels); |
63 | ||
64 | i_img *i_img_pal_new(i_img_dim x, i_img_dim y, int ch, int maxpal); | |
65 | 62 | |
66 | 63 | /* Image feature settings */ |
67 | 64 | |
73 | 70 | |
74 | 71 | /* Base functions */ |
75 | 72 | |
76 | extern int i_ppix(i_img *im,i_img_dim x,i_img_dim y, const i_color *val); | |
77 | extern int i_gpix(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
78 | extern int i_ppixf(i_img *im,i_img_dim x,i_img_dim y, const i_fcolor *val); | |
79 | extern int i_gpixf(i_img *im,i_img_dim x,i_img_dim y,i_fcolor *val); | |
80 | ||
81 | #define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val))) | |
82 | #define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val))) | |
83 | #define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val))) | |
84 | #define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val))) | |
85 | ||
86 | extern i_img_dim i_plin(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals); | |
87 | extern i_img_dim i_glin(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals); | |
88 | extern i_img_dim i_plinf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals); | |
89 | extern i_img_dim i_glinf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals); | |
90 | extern i_img_dim i_gsamp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samp, | |
91 | const int *chans, int chan_count); | |
92 | extern i_img_dim i_gsampf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, | |
93 | const int *chans, int chan_count); | |
94 | extern i_img_dim i_gpal(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, i_palidx *vals); | |
95 | extern i_img_dim i_ppal(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, const i_palidx *vals); | |
96 | extern int i_addcolors(i_img *im, const i_color *colors, int count); | |
97 | extern int i_getcolors(i_img *im, int i, i_color *, int count); | |
98 | extern int i_colorcount(i_img *im); | |
99 | extern int i_maxcolors(i_img *im); | |
100 | extern int i_findcolor(i_img *im, const i_color *color, i_palidx *entry); | |
101 | extern int i_setcolors(i_img *im, int index, const i_color *colors, | |
73 | extern int (i_ppix)(i_img *im,i_img_dim x,i_img_dim y, const i_color *val); | |
74 | extern int (i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
75 | extern int (i_ppixf)(i_img *im,i_img_dim x,i_img_dim y, const i_fcolor *val); | |
76 | extern int (i_gpixf)(i_img *im,i_img_dim x,i_img_dim y,i_fcolor *val); | |
77 | ||
78 | extern i_img_dim (i_plin)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, | |
79 | const i_color *vals); | |
80 | extern i_img_dim (i_glin)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, | |
81 | i_color *vals); | |
82 | extern i_img_dim (i_plinf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, | |
83 | const i_fcolor *vals); | |
84 | extern i_img_dim (i_glinf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, | |
85 | i_fcolor *vals); | |
86 | extern i_img_dim (i_gsamp)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, | |
87 | i_sample_t *samp, const int *chans, int chan_count); | |
88 | extern i_img_dim | |
89 | (i_gsampf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, | |
90 | const int *chans, int chan_count); | |
91 | extern i_img_dim | |
92 | (i_gpal)(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, i_palidx *vals); | |
93 | extern i_img_dim | |
94 | (i_ppal)(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, const i_palidx *vals); | |
95 | extern int (i_addcolors)(i_img *im, const i_color *colors, int count); | |
96 | extern int (i_getcolors)(i_img *im, int i, i_color *, int count); | |
97 | extern int (i_colorcount)(i_img *im); | |
98 | extern int (i_maxcolors)(i_img *im); | |
99 | extern int (i_findcolor)(i_img *im, const i_color *color, i_palidx *entry); | |
100 | extern int (i_setcolors)(i_img *im, int index, const i_color *colors, | |
102 | 101 | int count); |
103 | 102 | |
104 | #define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val)) | |
105 | #define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val)) | |
106 | #define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val)) | |
107 | #define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val)) | |
108 | ||
109 | #define i_gsamp(im, l, r, y, samps, chans, count) \ | |
110 | (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count))) | |
111 | #define i_gsampf(im, l, r, y, samps, chans, count) \ | |
112 | (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count))) | |
113 | ||
114 | #define i_gsamp_bits(im, l, r, y, samps, chans, count, bits) \ | |
115 | (((im)->i_f_gsamp_bits) ? ((im)->i_f_gsamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1) | |
116 | #define i_psamp_bits(im, l, r, y, samps, chans, count, bits) \ | |
117 | (((im)->i_f_psamp_bits) ? ((im)->i_f_psamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1) | |
118 | ||
119 | #define i_findcolor(im, color, entry) \ | |
120 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
121 | ||
122 | #define i_gpal(im, l, r, y, vals) \ | |
123 | (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0) | |
124 | #define i_ppal(im, l, r, y, vals) \ | |
125 | (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0) | |
126 | #define i_addcolors(im, colors, count) \ | |
127 | (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1) | |
128 | #define i_getcolors(im, index, color, count) \ | |
129 | (((im)->i_f_getcolors) ? \ | |
130 | ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0) | |
131 | #define i_setcolors(im, index, color, count) \ | |
132 | (((im)->i_f_setcolors) ? \ | |
133 | ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0) | |
134 | #define i_colorcount(im) \ | |
135 | (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1) | |
136 | #define i_maxcolors(im) \ | |
137 | (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1) | |
138 | #define i_findcolor(im, color, entry) \ | |
139 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
140 | ||
141 | #define i_img_virtual(im) ((im)->virtual) | |
142 | #define i_img_type(im) ((im)->type) | |
143 | #define i_img_bits(im) ((im)->bits) | |
144 | 103 | |
145 | 104 | extern i_fill_t *i_new_fill_solidf(const i_fcolor *c, int combine); |
146 | 105 | extern i_fill_t *i_new_fill_solid(const i_color *c, int combine); |
224 | 183 | |
225 | 184 | /* font routines */ |
226 | 185 | |
227 | undef_int i_init_fonts( int t1log ); | |
228 | ||
229 | 186 | #ifdef HAVE_LIBTT |
187 | ||
188 | extern void i_tt_start(void); | |
230 | 189 | |
231 | 190 | TT_Fonthandle* i_tt_new(const char *fontname); |
232 | 191 | void i_tt_destroy( TT_Fonthandle *handle ); |
246 | 205 | extern i_palidx *i_quant_translate(i_quantize *quant, i_img *img); |
247 | 206 | extern void i_quant_transparent(i_quantize *quant, i_palidx *indices, i_img *img, i_palidx trans_index); |
248 | 207 | |
249 | extern i_img *i_img_pal_new(i_img_dim x, i_img_dim y, int channels, int maxpal); | |
208 | i_img *im_img_pal_new(pIMCTX, i_img_dim x, i_img_dim y, int ch, int maxpal); | |
209 | ||
250 | 210 | extern i_img *i_img_to_pal(i_img *src, i_quantize *quant); |
251 | 211 | extern i_img *i_img_to_rgb(i_img *src); |
252 | 212 | extern i_img *i_img_masked_new(i_img *targ, i_img *mask, i_img_dim x, i_img_dim y, |
253 | 213 | i_img_dim w, i_img_dim h); |
254 | extern i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch); | |
214 | extern i_img *im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch); | |
255 | 215 | extern i_img *i_img_to_rgb16(i_img *im); |
256 | extern i_img *i_img_double_new(i_img_dim x, i_img_dim y, int ch); | |
216 | extern i_img *im_img_double_new(pIMCTX, i_img_dim x, i_img_dim y, int ch); | |
257 | 217 | extern i_img *i_img_to_drgb(i_img *im); |
258 | 218 | |
259 | 219 | extern int i_img_is_monochrome(i_img *im, int *zero_is_white); |
329 | 289 | /* Debug only functions */ |
330 | 290 | |
331 | 291 | void malloc_state( void ); |
332 | ||
333 | /* this is sort of obsolete now */ | |
334 | ||
335 | typedef struct { | |
336 | undef_int (*i_has_format)(char *frmt); | |
337 | i_color*(*ICL_set)(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a); | |
338 | void (*ICL_info)(const i_color *cl); | |
339 | ||
340 | i_img*(*i_img_new)( void ); | |
341 | i_img*(*i_img_empty)(i_img *im,i_img_dim x,i_img_dim y); | |
342 | i_img*(*i_img_empty_ch)(i_img *im,i_img_dim x,i_img_dim y,int ch); | |
343 | void(*i_img_exorcise)(i_img *im); | |
344 | ||
345 | void(*i_img_info)(i_img *im,i_img_dim *info); | |
346 | ||
347 | void(*i_img_setmask)(i_img *im,int ch_mask); | |
348 | int (*i_img_getmask)(i_img *im); | |
349 | ||
350 | /* | |
351 | int (*i_ppix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
352 | int (*i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val); | |
353 | */ | |
354 | void(*i_box)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val); | |
355 | void(*i_line)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val,int endp); | |
356 | void(*i_arc)(i_img *im,i_img_dim x,i_img_dim y,double rad,double d1,double d2,const i_color *val); | |
357 | void(*i_copyto)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty); | |
358 | void(*i_copyto_trans)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans); | |
359 | int(*i_rubthru)(i_img *im,i_img *src,i_img_dim tx,i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy); | |
360 | ||
361 | } symbol_table_t; | |
362 | 292 | |
363 | 293 | #include "imerror.h" |
364 | 294 | |
397 | 327 | |
398 | 328 | /* image file limits */ |
399 | 329 | extern int |
400 | i_set_image_file_limits(i_img_dim width, i_img_dim height, size_t bytes); | |
330 | im_set_image_file_limits(im_context_t ctx, i_img_dim width, i_img_dim height, size_t bytes); | |
401 | 331 | extern int |
402 | i_get_image_file_limits(i_img_dim *width, i_img_dim *height, size_t *bytes); | |
332 | im_get_image_file_limits(im_context_t ctx, i_img_dim *width, i_img_dim *height, size_t *bytes); | |
403 | 333 | extern int |
404 | i_int_check_image_file_limits(i_img_dim width, i_img_dim height, int channels, size_t sample_size); | |
334 | im_int_check_image_file_limits(im_context_t ctx, i_img_dim width, i_img_dim height, int channels, size_t sample_size); | |
405 | 335 | |
406 | 336 | /* memory allocation */ |
407 | 337 | void* mymalloc(size_t size); |
427 | 357 | #endif /* IMAGER_MALLOC_DEBUG */ |
428 | 358 | |
429 | 359 | #include "imrender.h" |
430 | #include "immacros.h" | |
431 | 360 | |
432 | 361 | extern void |
433 | 362 | i_adapt_colors(int dest_channels, int src_channels, i_color *colors, |
451 | 380 | i_gsampf_bg(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samples, |
452 | 381 | int out_channels, i_fcolor const *bg); |
453 | 382 | |
383 | /* context object management */ | |
384 | extern im_context_t im_context_new(void); | |
385 | extern void im_context_refinc(im_context_t ctx, const char *where); | |
386 | extern void im_context_refdec(im_context_t ctx, const char *where); | |
387 | extern im_context_t im_context_clone(im_context_t ctx, const char *where); | |
388 | extern im_slot_t im_context_slot_new(im_slot_destroy_t); | |
389 | extern void *im_context_slot_get(im_context_t ctx, im_slot_t slot); | |
390 | extern int im_context_slot_set(im_context_t ctx, im_slot_t slot, void *); | |
391 | ||
392 | extern im_context_t (*im_get_context)(void); | |
393 | ||
394 | /* mutex API */ | |
395 | extern i_mutex_t i_mutex_new(void); | |
396 | extern void i_mutex_destroy(i_mutex_t m); | |
397 | extern void i_mutex_lock(i_mutex_t m); | |
398 | extern void i_mutex_unlock(i_mutex_t m); | |
399 | ||
454 | 400 | #include "imio.h" |
455 | 401 | |
456 | 402 | #endif |
5 | 5 | #define IMAGEI_H_ |
6 | 6 | |
7 | 7 | #include "imager.h" |
8 | #include <stddef.h> | |
8 | 9 | |
9 | 10 | /* wrapper functions that implement the floating point sample version of a |
10 | 11 | function in terms of the 8-bit sample version |
110 | 111 | |
111 | 112 | #define color_to_grey(col) ((col)->rgb.r * 0.222 + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071) |
112 | 113 | |
114 | #define IM_ERROR_COUNT 20 | |
115 | typedef struct im_context_tag { | |
116 | int error_sp; | |
117 | size_t error_alloc[IM_ERROR_COUNT]; | |
118 | i_errmsg error_stack[IM_ERROR_COUNT]; | |
119 | #ifdef IMAGER_LOG | |
120 | /* the log file and level for this context */ | |
121 | FILE *lg_file; | |
122 | int log_level; | |
123 | ||
124 | /* whether we own the lg_file, false for stderr and for cloned contexts */ | |
125 | int own_log; | |
126 | ||
127 | /* values supplied by lhead */ | |
128 | const char *filename; | |
129 | int line; | |
113 | 130 | #endif |
131 | ||
132 | /* file size limits */ | |
133 | i_img_dim max_width, max_height; | |
134 | size_t max_bytes; | |
135 | ||
136 | /* per context storage */ | |
137 | size_t slot_alloc; | |
138 | void **slots; | |
139 | ||
140 | ptrdiff_t refcount; | |
141 | } im_context_struct; | |
142 | ||
143 | #define DEF_BYTES_LIMIT 0x40000000 | |
144 | ||
145 | #endif |
4 | 4 | #include "imconfig.h" |
5 | 5 | |
6 | 6 | #define MAXCHANNELS 4 |
7 | ||
8 | typedef struct im_context_tag *im_context_t; | |
9 | ||
10 | typedef ptrdiff_t im_slot_t; | |
11 | typedef void (*im_slot_destroy_t)(void *); | |
7 | 12 | |
8 | 13 | /* used for palette indices in some internal code (which might be |
9 | 14 | exposed at some point |
35 | 40 | */ |
36 | 41 | |
37 | 42 | typedef ptrdiff_t i_img_dim; |
43 | typedef size_t i_img_dim_u; | |
38 | 44 | |
39 | 45 | /* |
40 | 46 | =item i_color |
281 | 287 | =item * |
282 | 288 | |
283 | 289 | i_f_psampf - implements psamp() for this image. |
290 | ||
291 | =item * | |
292 | ||
293 | C<im_data> - image specific data internal to Imager. | |
294 | ||
295 | =item * | |
296 | ||
297 | C<context> - the Imager API context this image belongs to. | |
284 | 298 | |
285 | 299 | =back |
286 | 300 | |
334 | 348 | i_f_psampf_t i_f_psampf; |
335 | 349 | |
336 | 350 | void *im_data; |
351 | ||
352 | /* 0.91 */ | |
353 | im_context_t context; | |
337 | 354 | }; |
338 | 355 | |
339 | 356 | /* ext_data for paletted images |
540 | 557 | } i_combine_t; |
541 | 558 | |
542 | 559 | /* |
560 | =item i_mutex_t | |
561 | X<i_mutex> | |
562 | =category mutex | |
563 | =synopsis i_mutex_t mutex; | |
564 | ||
565 | Opaque type for Imager's mutex API. | |
566 | ||
567 | =cut | |
568 | */ | |
569 | typedef struct i_mutex_tag *i_mutex_t; | |
570 | ||
571 | /* | |
543 | 572 | describes an axis of a MM font. |
544 | 573 | Modelled on FT2's FT_MM_Axis. |
545 | 574 | It would be nice to have a default entry too, but FT2 |
680 | 709 | |
681 | 710 | #include "iolayert.h" |
682 | 711 | |
712 | /* error message information returned by im_errors() */ | |
713 | ||
714 | typedef struct { | |
715 | char *msg; | |
716 | int code; | |
717 | } i_errmsg; | |
718 | ||
683 | 719 | typedef struct i_render_tag i_render; |
684 | 720 | |
685 | 721 | #ifdef IMAGER_FORMAT_ATTR |
4 | 4 | see error.c for documentation |
5 | 5 | the error information is currently global |
6 | 6 | */ |
7 | typedef struct { | |
8 | char *msg; | |
9 | int code; | |
10 | } i_errmsg; | |
11 | ||
12 | 7 | typedef void (*i_error_cb)(int code, char const *msg); |
13 | 8 | typedef void (*i_failed_cb)(i_errmsg *msgs); |
14 | 9 | extern i_error_cb i_set_error_cb(i_error_cb); |
15 | 10 | extern i_failed_cb i_set_failed_cb(i_failed_cb); |
16 | 11 | extern void i_set_argv0(char const *); |
17 | 12 | extern int i_set_errors_fatal(int new_fatal); |
18 | extern i_errmsg *i_errors(void); | |
13 | extern i_errmsg *im_errors(pIMCTX); | |
19 | 14 | |
20 | extern void i_push_error(int code, char const *msg); | |
15 | extern void im_push_error(pIMCTX, int code, char const *msg); | |
16 | #ifndef IMAGER_NO_CONTEXT | |
21 | 17 | extern void i_push_errorf(int code, char const *fmt, ...) I_FORMAT_ATTR(2, 3); |
22 | extern void i_push_errorvf(int code, char const *fmt, va_list); | |
23 | extern void i_clear_error(void); | |
18 | #endif | |
19 | extern void im_push_errorf(pIMCTX, int code, char const *fmt, ...) I_FORMAT_ATTR(3, 4); | |
20 | extern void im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list); | |
21 | extern void im_clear_error(pIMCTX); | |
24 | 22 | extern int i_failed(int code, char const *msg); |
25 | 23 | |
26 | 24 | #endif |
0 | 0 | #include "imexttypes.h" |
1 | 1 | #include "imager.h" |
2 | 2 | #include "imio.h" |
3 | ||
4 | static im_context_t get_context(void); | |
5 | static i_img *mathom_i_img_8_new(i_img_dim, i_img_dim, int); | |
6 | static i_img *mathom_i_img_16_new(i_img_dim, i_img_dim, int); | |
7 | static i_img *mathom_i_img_double_new(i_img_dim, i_img_dim, int); | |
8 | static i_img *mathom_i_img_pal_new(i_img_dim, i_img_dim, int, int); | |
9 | static void mathom_i_clear_error(void); | |
10 | static void mathom_i_push_error(int, const char *); | |
11 | static void mathom_i_push_errorvf(int, const char *, va_list); | |
12 | static int mathom_i_set_image_file_limits(i_img_dim, i_img_dim, size_t); | |
13 | static int mathom_i_get_image_file_limits(i_img_dim*, i_img_dim*, size_t*); | |
14 | static int | |
15 | mathom_i_int_check_image_file_limits(i_img_dim, i_img_dim, int, size_t); | |
16 | static i_img *mathom_i_img_alloc(void); | |
17 | static void mathom_i_img_init(i_img *); | |
18 | static i_io_glue_t *mathom_io_new_fd(int); | |
19 | static i_io_glue_t *mathom_io_new_bufchain(void); | |
20 | static i_io_glue_t * | |
21 | mathom_io_new_buffer(const char *data, size_t, i_io_closebufp_t, void *); | |
22 | static i_io_glue_t * | |
23 | mathom_io_new_cb(void *, i_io_readl_t, i_io_writel_t, i_io_seekl_t, | |
24 | i_io_closel_t, i_io_destroyl_t); | |
3 | 25 | |
4 | 26 | /* |
5 | 27 | DON'T ADD CASTS TO THESE |
17 | 39 | myfree_file_line, |
18 | 40 | myrealloc_file_line, |
19 | 41 | |
20 | i_img_8_new, | |
21 | i_img_16_new, | |
22 | i_img_double_new, | |
23 | i_img_pal_new, | |
42 | mathom_i_img_8_new, | |
43 | mathom_i_img_16_new, | |
44 | mathom_i_img_double_new, | |
45 | mathom_i_img_pal_new, | |
24 | 46 | i_img_destroy, |
25 | 47 | i_sametype, |
26 | 48 | i_sametype_chans, |
57 | 79 | i_quant_translate, |
58 | 80 | i_quant_transparent, |
59 | 81 | |
60 | i_clear_error, | |
61 | i_push_error, | |
82 | mathom_i_clear_error, | |
83 | mathom_i_push_error, | |
62 | 84 | i_push_errorf, |
63 | i_push_errorvf, | |
85 | mathom_i_push_errorvf, | |
64 | 86 | |
65 | 87 | i_tags_new, |
66 | 88 | i_tags_set, |
98 | 120 | i_rubthru, |
99 | 121 | |
100 | 122 | /* IMAGER_API_LEVEL 2 functions */ |
101 | i_set_image_file_limits, | |
102 | i_get_image_file_limits, | |
103 | i_int_check_image_file_limits, | |
123 | mathom_i_set_image_file_limits, | |
124 | mathom_i_get_image_file_limits, | |
125 | mathom_i_int_check_image_file_limits, | |
104 | 126 | |
105 | 127 | i_flood_fill_border, |
106 | 128 | i_flood_cfill_border, |
115 | 137 | i_loog, |
116 | 138 | |
117 | 139 | /* IMAGER_API_LEVEL 4 functions */ |
118 | i_img_alloc, | |
119 | i_img_init, | |
140 | mathom_i_img_alloc, | |
141 | mathom_i_img_init, | |
120 | 142 | |
121 | 143 | /* IMAGER_API_LEVEL 5 functions */ |
122 | 144 | i_img_is_monochrome, |
144 | 166 | i_io_close, |
145 | 167 | i_io_set_buffered, |
146 | 168 | i_io_gets, |
147 | io_new_fd, | |
148 | io_new_bufchain, | |
149 | io_new_buffer, | |
150 | io_new_cb, | |
169 | mathom_io_new_fd, | |
170 | mathom_io_new_bufchain, | |
171 | mathom_io_new_buffer, | |
172 | mathom_io_new_cb, | |
151 | 173 | io_slurp, |
152 | io_glue_destroy | |
174 | io_glue_destroy, | |
175 | ||
176 | /* level 8 */ | |
177 | im_img_8_new, | |
178 | im_img_16_new, | |
179 | im_img_double_new, | |
180 | im_img_pal_new, | |
181 | im_clear_error, | |
182 | im_push_error, | |
183 | im_push_errorvf, | |
184 | im_push_errorf, | |
185 | im_set_image_file_limits, | |
186 | im_get_image_file_limits, | |
187 | im_int_check_image_file_limits, | |
188 | im_img_alloc, | |
189 | im_img_init, | |
190 | im_io_new_fd, | |
191 | im_io_new_bufchain, | |
192 | im_io_new_buffer, | |
193 | im_io_new_cb, | |
194 | get_context, | |
195 | im_lhead, | |
196 | im_loog, | |
197 | im_context_refinc, | |
198 | im_context_refdec, | |
199 | im_errors, | |
200 | i_mutex_new, | |
201 | i_mutex_destroy, | |
202 | i_mutex_lock, | |
203 | i_mutex_unlock, | |
204 | im_context_slot_new, | |
205 | im_context_slot_set, | |
206 | im_context_slot_get | |
153 | 207 | }; |
154 | 208 | |
155 | 209 | /* in general these functions aren't called by Imager internally, but |
549 | 603 | return i_setcolors(im, index, colors, count); |
550 | 604 | } |
551 | 605 | |
606 | /* | |
607 | =item im_get_context() | |
608 | ||
609 | Retrieve the context object for the current thread. | |
610 | ||
611 | Inside Imager itself this is just a function pointer, which the | |
612 | F<Imager.xs> BOOT handler initializes for use within perl. If you're | |
613 | taking the Imager code and embedding it elsewhere you need to | |
614 | initialize the C<im_get_context> pointer at some point. | |
615 | ||
616 | =cut | |
617 | */ | |
618 | ||
619 | static im_context_t | |
620 | get_context(void) { | |
621 | return im_get_context(); | |
622 | } | |
623 | ||
624 | static i_img * | |
625 | mathom_i_img_8_new(i_img_dim xsize, i_img_dim ysize, int channels) { | |
626 | return i_img_8_new(xsize, ysize, channels); | |
627 | } | |
628 | ||
629 | static i_img * | |
630 | mathom_i_img_16_new(i_img_dim xsize, i_img_dim ysize, int channels) { | |
631 | return i_img_16_new(xsize, ysize, channels); | |
632 | } | |
633 | ||
634 | static i_img * | |
635 | mathom_i_img_double_new(i_img_dim xsize, i_img_dim ysize, int channels) { | |
636 | return i_img_double_new(xsize, ysize, channels); | |
637 | } | |
638 | ||
639 | static i_img * | |
640 | mathom_i_img_pal_new(i_img_dim xsize, i_img_dim ysize, int channels, | |
641 | int maxpal) { | |
642 | return i_img_pal_new(xsize, ysize, channels, maxpal); | |
643 | } | |
644 | ||
645 | static void | |
646 | mathom_i_clear_error(void) { | |
647 | i_clear_error(); | |
648 | } | |
649 | ||
650 | static void | |
651 | mathom_i_push_error(int code, const char *msg) { | |
652 | i_push_error(code, msg); | |
653 | } | |
654 | ||
655 | static void | |
656 | mathom_i_push_errorvf(int code, const char *fmt, va_list args) { | |
657 | i_push_errorvf(code, fmt, args); | |
658 | } | |
659 | ||
660 | static int | |
661 | mathom_i_set_image_file_limits(i_img_dim max_width, i_img_dim max_height, | |
662 | size_t max_bytes) { | |
663 | return i_set_image_file_limits(max_width, max_height, max_bytes); | |
664 | } | |
665 | ||
666 | static int | |
667 | mathom_i_get_image_file_limits(i_img_dim *pmax_width, i_img_dim *pmax_height, | |
668 | size_t *pmax_bytes) { | |
669 | return i_get_image_file_limits(pmax_width, pmax_height, pmax_bytes); | |
670 | } | |
671 | ||
672 | static int | |
673 | mathom_i_int_check_image_file_limits(i_img_dim width, i_img_dim height, | |
674 | int channels, size_t sample_size) { | |
675 | return i_int_check_image_file_limits(width, height, channels, sample_size); | |
676 | } | |
677 | ||
678 | static i_img * | |
679 | mathom_i_img_alloc(void) { | |
680 | return i_img_alloc(); | |
681 | } | |
682 | ||
683 | static void | |
684 | mathom_i_img_init(i_img *im) { | |
685 | i_img_init(im); | |
686 | } | |
687 | ||
688 | static i_io_glue_t * | |
689 | mathom_io_new_fd(int fd) { | |
690 | return io_new_fd(fd); | |
691 | } | |
692 | static i_io_glue_t * | |
693 | mathom_io_new_bufchain(void) { | |
694 | return io_new_bufchain(); | |
695 | } | |
696 | ||
697 | static i_io_glue_t * | |
698 | mathom_io_new_buffer(const char *data, size_t size, i_io_closebufp_t closefp, | |
699 | void *close_data) { | |
700 | return io_new_buffer(data, size, closefp, close_data); | |
701 | } | |
702 | ||
703 | static i_io_glue_t * | |
704 | mathom_io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, | |
705 | i_io_seekl_t seekcb, i_io_closel_t closecb, | |
706 | i_io_destroyl_t destroycb) { | |
707 | return io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb); | |
708 | } |
29 | 29 | /* just for use here */ |
30 | 30 | #define im_extt imager_function_ext_table |
31 | 31 | |
32 | #define im_get_context() ((im_extt->f_im_get_context)()) | |
33 | #define im_context_refinc(ctx, where) ((im_extt->f_im_context_refinc)((ctx), (where))) | |
34 | #define im_context_refdec(ctx, where) ((im_extt->f_im_context_refdec)((ctx), (where))) | |
35 | ||
32 | 36 | #ifdef IMAGER_DEBUG_MALLOC |
33 | 37 | |
34 | 38 | #define mymalloc(size) ((im_extt->f_mymalloc_file_line)((size), __FILE__, __LINE__)) |
43 | 47 | |
44 | 48 | #endif |
45 | 49 | |
46 | #define i_img_8_new(xsize, ysize, channels) ((im_extt->f_i_img_8_new)((xsize), (ysize), (channels))) | |
47 | #define i_img_16_new(xsize, ysize, channels) ((im_extt->f_i_img_16_new)((xsize), (ysize), (channels))) | |
48 | #define i_img_double_new(xsize, ysize, channels) ((im_extt->f_i_img_double_new)((xsize), (ysize), (channels))) | |
49 | #define i_img_pal_new(xsize, ysize, channels, maxpal) ((im_extt->f_i_img_pal_new)((xsize), (ysize), (channels), (maxpal))) | |
50 | #define im_img_8_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_8_new)((ctx), (xsize), (ysize), (channels))) | |
51 | #define im_img_16_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_16_new)((ctx), (xsize), (ysize), (channels))) | |
52 | #define im_img_double_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_double_new)((ctx), (xsize), (ysize), (channels))) | |
53 | #define im_img_pal_new(ctx, xsize, ysize, channels, maxpal) ((im_extt->f_im_img_pal_new)((ctx), (xsize), (ysize), (channels), (maxpal))) | |
50 | 54 | |
51 | 55 | #define i_img_destroy(im) ((im_extt->f_i_img_destroy)(im)) |
52 | 56 | #define i_sametype(im, xsize, ysize) ((im_extt->f_i_sametype)((im), (xsize), (ysize))) |
57 | 61 | #define IMAGER_DIRECT_IMAGE_CALLS 1 |
58 | 62 | #endif |
59 | 63 | |
60 | #if IMAGER_DIRECT_IMAGE_CALLS | |
61 | #define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val))) | |
62 | #define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val))) | |
63 | #define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val))) | |
64 | #define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val))) | |
65 | #define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val)) | |
66 | #define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val)) | |
67 | #define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val)) | |
68 | #define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val)) | |
69 | ||
70 | #define i_gsamp(im, l, r, y, samps, chans, count) \ | |
71 | (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count))) | |
72 | #define i_gsampf(im, l, r, y, samps, chans, count) \ | |
73 | (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count))) | |
74 | ||
75 | #define i_findcolor(im, color, entry) \ | |
76 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
77 | ||
78 | #define i_gpal(im, l, r, y, vals) \ | |
79 | (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0) | |
80 | #define i_ppal(im, l, r, y, vals) \ | |
81 | (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0) | |
82 | #define i_addcolors(im, colors, count) \ | |
83 | (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1) | |
84 | #define i_getcolors(im, index, color, count) \ | |
85 | (((im)->i_f_getcolors) ? \ | |
86 | ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0) | |
87 | #define i_setcolors(im, index, color, count) \ | |
88 | (((im)->i_f_setcolors) ? \ | |
89 | ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0) | |
90 | #define i_colorcount(im) \ | |
91 | (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1) | |
92 | #define i_maxcolors(im) \ | |
93 | (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1) | |
94 | #define i_findcolor(im, color, entry) \ | |
95 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
96 | #else | |
64 | #if !IMAGER_DIRECT_IMAGE_CALLS | |
65 | ||
97 | 66 | #define i_ppix(im, x, y, val) ((im_extt->f_i_ppix)((im), (x), (y), (val))) |
98 | 67 | #define i_gpix(im, x, y, val) ((im_extt->f_i_gpix)((im), (x), (y), (val))) |
99 | 68 | #define i_ppixf(im, x, y, val) ((im_extt->f_i_ppixf)((im), (x), (y), (val))) |
133 | 102 | #define i_quant_transparent(quant, indices, img, trans_index) \ |
134 | 103 | ((im_extt->f_i_quant_transparent)((quant), (indices), (img), (trans_index))) |
135 | 104 | |
136 | #define i_clear_error() ((im_extt->f_i_clear_error)()) | |
137 | #define i_push_error(code, msg) ((im_extt->f_i_push_error)((code), (msg))) | |
105 | #define im_clear_error(ctx) ((im_extt->f_im_clear_error)(ctx)) | |
106 | #define im_push_error(ctx, code, msg) ((im_extt->f_im_push_error)((ctx), (code), (msg))) | |
138 | 107 | #define i_push_errorf (im_extt->f_i_push_errorf) |
139 | #define i_push_errorvf(code, fmt, list) \ | |
140 | ((im_extt->f_i_push_errorvf)((code), (fmt), (list))) | |
108 | #define im_push_errorvf(ctx, code, fmt, list) \ | |
109 | ((im_extt->f_im_push_errorvf)((ctx), (code), (fmt), (list))) | |
141 | 110 | |
142 | 111 | #define i_tags_new(tags) ((im_extt->f_i_tags_new)(tags)) |
143 | 112 | #define i_tags_set(tags, name, data, size) \ |
193 | 162 | #define i_rubthru(im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy) \ |
194 | 163 | ((im_extt->f_i_rubthru)((im), (src), (tx), (ty), (src_minx), (src_miny), (src_maxx), (src_maxy))) |
195 | 164 | |
196 | #define i_set_image_file_limits(max_width, max_height, max_bytes) \ | |
197 | ((im_extt->f_i_set_image_file_limits)((max_width), (max_height), (max_bytes))) | |
198 | #define i_get_image_file_limits(pmax_width, pmax_height, pmax_bytes) \ | |
199 | ((im_extt->f_i_get_image_file_limits)((pmax_width), (pmax_height), (pmax_bytes))) | |
200 | #define i_int_check_image_file_limits(width, height, channels, sample_size) \ | |
201 | ((im_extt->f_i_int_check_image_file_limits)((width), (height), (channels), (sample_size))) | |
165 | #define im_set_image_file_limits(ctx, max_width, max_height, max_bytes) \ | |
166 | ((im_extt->f_im_set_image_file_limits)((max_width), (max_height), (max_bytes))) | |
167 | #define im_get_image_file_limits(ctx, pmax_width, pmax_height, pmax_bytes) \ | |
168 | ((im_extt->f_im_get_image_file_limits)((ctx), (pmax_width), (pmax_height), (pmax_bytes))) | |
169 | #define im_int_check_image_file_limits(ctx, width, height, channels, sample_size) \ | |
170 | ((im_extt->f_im_int_check_image_file_limits)((ctx), (width), (height), (channels), (sample_size))) | |
202 | 171 | |
203 | 172 | #define i_img_setmask(img, mask) ((im_extt->f_i_img_setmask)((img), (mask))) |
204 | 173 | #define i_img_getmask(img) ((im_extt->f_i_img_getmask)(img)) |
207 | 176 | #define i_img_get_height(img) ((im_extt->f_i_img_get_height)(img)) |
208 | 177 | #define i_lhead(file, line) ((im_extt->f_i_lhead)((file), (line))) |
209 | 178 | #define i_loog (im_extt->f_i_loog) |
210 | ||
211 | #define i_img_alloc() ((im_extt->f_i_img_alloc)()) | |
212 | #define i_img_init(img) ((im_extt->f_i_img_init)(img)) | |
179 | #define im_lhead(ctx, file, line) ((im_extt->f_im_lhead)((ctx), (file), (line))) | |
180 | #define im_loog (im_extt->f_im_loog) | |
181 | ||
182 | #define im_img_alloc(ctx) ((im_extt->f_im_img_alloc)(ctx)) | |
183 | #define im_img_init(ctx, img) ((im_extt->fm_i_img_init)((ctx), (img))) | |
213 | 184 | |
214 | 185 | #define i_img_is_monochrome(img, zero_is_white) ((im_extt->f_i_img_is_monochrome)((img), (zero_is_white))) |
215 | 186 | |
246 | 217 | #define i_io_close (im_extt->f_i_io_close) |
247 | 218 | #define i_io_set_buffered (im_extt->f_i_io_set_buffered) |
248 | 219 | #define i_io_gets (im_extt->f_i_io_gets) |
249 | #define io_new_fd(fd) ((im_extt->f_io_new_fd)(fd)) | |
250 | #define io_new_bufchain() ((im_extt->f_io_new_bufchain)()) | |
251 | #define io_new_buffer(data, len, closecb, closedata) \ | |
252 | ((im_extt->f_io_new_buffer)((data), (len), (closecb), (closedata))) | |
253 | #define io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb) \ | |
254 | ((im_extt->f_io_new_cb)((p), (readcb), (writecb), (seekcb), (closecb), (destroycb))) | |
220 | #define im_io_new_fd(ctx, fd) ((im_extt->f_im_io_new_fd)(ctx, fd)) | |
221 | #define im_io_new_bufchain(ctx) ((im_extt->f_im_io_new_bufchain)(ctx)) | |
222 | #define im_io_new_buffer(ctx, data, len, closecb, closedata) \ | |
223 | ((im_extt->f_im_io_new_buffer)((ctx), (data), (len), (closecb), (closedata))) | |
224 | #define im_io_new_cb(ctx, p, readcb, writecb, seekcb, closecb, destroycb) \ | |
225 | ((im_extt->f_im_io_new_cb)((ctx), (p), (readcb), (writecb), (seekcb), (closecb), (destroycb))) | |
255 | 226 | #define io_slurp(ig, datap) ((im_extt->f_io_slurp)((ig), (datap))) |
256 | 227 | #define io_glue_destroy(ig) ((im_extt->f_io_glue_destroy)(ig)) |
257 | 228 | |
229 | #define i_mutex_new() ((im_extt->f_i_mutex_new)()) | |
230 | #define i_mutex_destroy(m) ((im_extt->f_i_mutex_destroy)(m)) | |
231 | #define i_mutex_lock(m) ((im_extt->f_i_mutex_lock)(m)) | |
232 | #define i_mutex_unlock(m) ((im_extt->f_i_mutex_unlock)(m)) | |
233 | ||
234 | #define im_context_slot_new(destructor) ((im_extt->f_im_context_slot_new)(destructor)) | |
235 | #define im_context_slot_get(ctx, slot) ((im_extt->f_im_context_slot_get)((ctx), (slot))) | |
236 | #define im_context_slot_set(ctx, slot, value) ((im_extt->f_im_context_slot_set)((ctx), (slot), (value))) | |
237 | ||
238 | #define im_push_errorf (im_extt->f_im_push_errorf) | |
239 | ||
258 | 240 | #ifdef IMAGER_LOG |
241 | #ifndef IMAGER_NO_CONTEXT | |
259 | 242 | #define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; } |
243 | #endif | |
244 | #define im_log(x) { im_lhead(aIMCTX, __FILE__,__LINE__); im_loog x; } | |
260 | 245 | #else |
261 | 246 | #define mm_log(x) |
262 | 247 | #endif |
263 | 248 | |
264 | ||
265 | #endif | |
249 | #endif |
33 | 33 | will result in an increment of IMAGER_API_LEVEL. |
34 | 34 | */ |
35 | 35 | |
36 | #define IMAGER_API_LEVEL 7 | |
36 | #define IMAGER_API_LEVEL 8 | |
37 | 37 | |
38 | 38 | typedef struct { |
39 | 39 | int version; |
47 | 47 | void (*f_myfree_file_line)(void *p, char*file, int line); |
48 | 48 | void* (*f_myrealloc_file_line)(void *p, size_t newsize, char* file,int line); |
49 | 49 | |
50 | i_img *(*f_i_img_8_new)(i_img_dim xsize, i_img_dim ysize, int channels); | |
51 | i_img *(*f_i_img_16_new)(i_img_dim xsize, i_img_dim ysize, int channels); | |
52 | i_img *(*f_i_img_double_new)(i_img_dim xsize, i_img_dim ysize, int channels); | |
53 | i_img *(*f_i_img_pal_new)(i_img_dim xsize, i_img_dim ysize, int channels, int maxpal); | |
50 | i_img *(*f_i_img_8_new)(i_img_dim xsize, i_img_dim ysize, int channels); /* SKIP */ | |
51 | i_img *(*f_i_img_16_new)(i_img_dim xsize, i_img_dim ysize, int channels); /* SKIP */ | |
52 | i_img *(*f_i_img_double_new)(i_img_dim xsize, i_img_dim ysize, int channels); /* SKIP */ | |
53 | i_img *(*f_i_img_pal_new)(i_img_dim xsize, i_img_dim ysize, int channels, int maxpal); /* SKIP */ | |
54 | 54 | void (*f_i_img_destroy)(i_img *im); |
55 | 55 | i_img *(*f_i_sametype)(i_img *im, i_img_dim xsize, i_img_dim ysize); |
56 | 56 | i_img *(*f_i_sametype_chans)(i_img *im, i_img_dim xsize, i_img_dim ysize, int channels); |
101 | 101 | void (*f_i_quant_transparent)(i_quantize *quant, i_palidx *indices, |
102 | 102 | i_img *img, i_palidx trans_index); |
103 | 103 | |
104 | void (*f_i_clear_error)(void); | |
105 | void (*f_i_push_error)(int code, char const *msg); | |
106 | void (*f_i_push_errorf)(int code, char const *fmt, ...); | |
107 | void (*f_i_push_errorvf)(int code, char const *fmt, va_list); | |
104 | void (*f_i_clear_error)(void); /* SKIP */ | |
105 | void (*f_i_push_error)(int code, char const *msg); /* SKIP */ | |
106 | void (*f_i_push_errorf)(int code, char const *fmt, ...) I_FORMAT_ATTR(2,3); | |
107 | void (*f_i_push_errorvf)(int code, char const *fmt, va_list); /* SKIP */ | |
108 | 108 | |
109 | 109 | void (*f_i_tags_new)(i_img_tags *tags); |
110 | 110 | int (*f_i_tags_set)(i_img_tags *tags, char const *name, char const *data, |
151 | 151 | int (*f_i_rubthru)(i_img *im, i_img *src, i_img_dim tx, i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy); |
152 | 152 | |
153 | 153 | /* IMAGER_API_LEVEL 2 functions */ |
154 | int (*f_i_set_image_file_limits)(i_img_dim width, i_img_dim height, size_t bytes); | |
155 | int (*f_i_get_image_file_limits)(i_img_dim *width, i_img_dim *height, size_t *bytes); | |
156 | int (*f_i_int_check_image_file_limits)(i_img_dim width, i_img_dim height, int channels, size_t sample_size); | |
154 | int (*f_i_set_image_file_limits)(i_img_dim width, i_img_dim height, size_t bytes); /* SKIP */ | |
155 | int (*f_i_get_image_file_limits)(i_img_dim *width, i_img_dim *height, size_t *bytes); /* SKIP */ | |
156 | int (*f_i_int_check_image_file_limits)(i_img_dim width, i_img_dim height, int channels, size_t sample_size); /* SKIP */ | |
157 | 157 | int (*f_i_flood_fill_border)(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *dcol, const i_color *border); |
158 | 158 | int (*f_i_flood_cfill_border)(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill, const i_color *border); |
159 | 159 | |
164 | 164 | i_img_dim (*f_i_img_get_width)(i_img *im); |
165 | 165 | i_img_dim (*f_i_img_get_height)(i_img *im); |
166 | 166 | void (*f_i_lhead)(const char *file, int line_number); |
167 | void (*f_i_loog)(int level, const char *msg, ...); | |
167 | void (*f_i_loog)(int level, const char *msg, ...) I_FORMAT_ATTR(2,3); | |
168 | 168 | |
169 | 169 | /* IMAGER_API_LEVEL 4 functions will be added here */ |
170 | i_img *(*f_i_img_alloc)(void); | |
171 | void (*f_i_img_init)(i_img *); | |
170 | i_img *(*f_i_img_alloc)(void); /* SKIP */ | |
171 | void (*f_i_img_init)(i_img *); /* SKIP */ | |
172 | 172 | |
173 | 173 | /* IMAGER_API_LEVEL 5 functions will be added here */ |
174 | 174 | /* added i_psampf?_bits macros */ |
209 | 209 | int (*f_i_io_set_buffered)(io_glue *ig, int buffered); |
210 | 210 | ssize_t (*f_i_io_gets)(io_glue *ig, char *, size_t, int); |
211 | 211 | |
212 | i_io_glue_t *(*f_io_new_fd)(int fd); | |
213 | i_io_glue_t *(*f_io_new_bufchain)(void); | |
214 | i_io_glue_t *(*f_io_new_buffer)(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
215 | 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); | |
212 | i_io_glue_t *(*f_io_new_fd)(int fd); /* SKIP */ | |
213 | i_io_glue_t *(*f_io_new_bufchain)(void); /* SKIP */ | |
214 | i_io_glue_t *(*f_io_new_buffer)(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); /* SKIP */ | |
215 | 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); /* SKIP */ | |
216 | 216 | size_t (*f_io_slurp)(i_io_glue_t *ig, unsigned char **c); |
217 | 217 | void (*f_io_glue_destroy)(i_io_glue_t *ig); |
218 | 218 | |
219 | 219 | /* IMAGER_API_LEVEL 8 functions will be added here */ |
220 | ||
220 | i_img *(*f_im_img_8_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels); | |
221 | i_img *(*f_im_img_16_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels); | |
222 | i_img *(*f_im_img_double_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels); | |
223 | i_img *(*f_im_img_pal_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels, int maxpal); | |
224 | ||
225 | void (*f_im_clear_error)(im_context_t ctx); | |
226 | void (*f_im_push_error)(im_context_t ctx, int code, char const *msg); | |
227 | void (*f_im_push_errorvf)(im_context_t ctx, int code, char const *fmt, va_list); | |
228 | void (*f_im_push_errorf)(im_context_t , int code, char const *fmt, ...) I_FORMAT_ATTR(3,4); | |
229 | ||
230 | int (*f_im_set_image_file_limits)(im_context_t ctx, i_img_dim width, i_img_dim height, size_t bytes); | |
231 | int (*f_im_get_image_file_limits)(im_context_t ctx, i_img_dim *width, i_img_dim *height, size_t *bytes); | |
232 | int (*f_im_int_check_image_file_limits)(im_context_t ctx, i_img_dim width, i_img_dim height, int channels, size_t sample_size); | |
233 | ||
234 | i_img *(*f_im_img_alloc)(im_context_t ctx); | |
235 | void (*f_im_img_init)(im_context_t ctx, i_img *); | |
236 | ||
237 | i_io_glue_t *(*f_im_io_new_fd)(im_context_t ctx, int fd); | |
238 | i_io_glue_t *(*f_im_io_new_bufchain)(im_context_t ctx); | |
239 | i_io_glue_t *(*f_im_io_new_buffer)(im_context_t ctx, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
240 | i_io_glue_t *(*f_im_io_new_cb)(im_context_t ctx, 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); | |
241 | ||
242 | im_context_t (*f_im_get_context)(void); | |
243 | ||
244 | void (*f_im_lhead)( im_context_t, const char *file, int line ); | |
245 | void (*f_im_loog)(im_context_t, int level,const char *msg, ... ) I_FORMAT_ATTR(3,4); | |
246 | void (*f_im_context_refinc)(im_context_t, const char *where); | |
247 | void (*f_im_context_refdec)(im_context_t, const char *where); | |
248 | i_errmsg *(*f_im_errors)(im_context_t); | |
249 | i_mutex_t (*f_i_mutex_new)(void); | |
250 | void (*f_i_mutex_destroy)(i_mutex_t m); | |
251 | void (*f_i_mutex_lock)(i_mutex_t m); | |
252 | void (*f_i_mutex_unlock)(i_mutex_t m); | |
253 | im_slot_t (*f_im_context_slot_new)(im_slot_destroy_t); | |
254 | int (*f_im_context_slot_set)(im_context_t, im_slot_t, void *); | |
255 | void *(*f_im_context_slot_get)(im_context_t, im_slot_t); | |
221 | 256 | } im_ext_funcs; |
222 | 257 | |
223 | 258 | #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table" |
18 | 18 | |
19 | 19 | =cut |
20 | 20 | */ |
21 | ||
22 | #define IMAGER_NO_CONTEXT | |
21 | 23 | |
22 | 24 | #include "imager.h" |
23 | 25 | #include "imageri.h" |
147 | 149 | ((((i_sample16_t *)(bytes))[offset]+127) / 257) |
148 | 150 | |
149 | 151 | /* |
150 | =item i_img_16_new(x, y, ch) | |
151 | ||
152 | =item im_img_16_new(ctx, x, y, ch) | |
153 | X<im_img_16_new API>X<i_img_16_new API> | |
152 | 154 | =category Image creation/destruction |
155 | =synopsis i_img *img = im_img_16_new(aIMCTX, width, height, channels); | |
153 | 156 | =synopsis i_img *img = i_img_16_new(width, height, channels); |
154 | 157 | |
155 | 158 | Create a new 16-bit/sample image. |
156 | 159 | |
157 | 160 | Returns the image on success, or NULL on failure. |
161 | ||
162 | Also callable as C<i_img_16_new(x, y, ch)> | |
158 | 163 | |
159 | 164 | =cut |
160 | 165 | */ |
161 | 166 | |
162 | i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch) { | |
167 | i_img * | |
168 | im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) { | |
163 | 169 | i_img *im; |
164 | 170 | size_t bytes, line_bytes; |
165 | 171 | |
166 | mm_log((1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
172 | im_log((aIMCTX, 1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
167 | 173 | i_DFc(x), i_DFc(y), ch)); |
168 | 174 | |
169 | 175 | if (x < 1 || y < 1) { |
170 | i_push_error(0, "Image sizes must be positive"); | |
176 | im_push_error(aIMCTX, 0, "Image sizes must be positive"); | |
171 | 177 | return NULL; |
172 | 178 | } |
173 | 179 | if (ch < 1 || ch > MAXCHANNELS) { |
174 | i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS); | |
180 | im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS); | |
175 | 181 | return NULL; |
176 | 182 | } |
177 | 183 | bytes = x * y * ch * 2; |
178 | 184 | if (bytes / y / ch / 2 != x) { |
179 | i_push_errorf(0, "integer overflow calculating image allocation"); | |
185 | im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation"); | |
180 | 186 | return NULL; |
181 | 187 | } |
182 | 188 | |
185 | 191 | working with the image */ |
186 | 192 | line_bytes = sizeof(i_fcolor) * x; |
187 | 193 | if (line_bytes / x != sizeof(i_fcolor)) { |
188 | i_push_error(0, "integer overflow calculating scanline allocation"); | |
194 | im_push_error(aIMCTX, 0, "integer overflow calculating scanline allocation"); | |
189 | 195 | return NULL; |
190 | 196 | } |
191 | 197 | |
192 | im = i_img_alloc(); | |
198 | im = im_img_alloc(aIMCTX); | |
193 | 199 | *im = IIM_base_16bit_direct; |
194 | 200 | i_tags_new(&im->tags); |
195 | 201 | im->xsize = x; |
200 | 206 | im->idata = mymalloc(im->bytes); |
201 | 207 | memset(im->idata, 0, im->bytes); |
202 | 208 | |
203 | i_img_init(im); | |
209 | im_img_init(aIMCTX, im); | |
204 | 210 | |
205 | 211 | return im; |
206 | 212 | } |
222 | 228 | i_img *targ; |
223 | 229 | i_fcolor *line; |
224 | 230 | i_img_dim y; |
225 | ||
226 | targ = i_img_16_new(im->xsize, im->ysize, im->channels); | |
231 | dIMCTXim(im); | |
232 | ||
233 | targ = im_img_16_new(aIMCTX, im->xsize, im->ysize, im->channels); | |
227 | 234 | if (!targ) |
228 | 235 | return NULL; |
229 | 236 | line = mymalloc(sizeof(i_fcolor) * im->xsize); |
434 | 441 | /* make sure we have good channel numbers */ |
435 | 442 | for (ch = 0; ch < chan_count; ++ch) { |
436 | 443 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
437 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
444 | dIMCTXim(im); | |
445 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
438 | 446 | return 0; |
439 | 447 | } |
440 | 448 | } |
448 | 456 | } |
449 | 457 | else { |
450 | 458 | if (chan_count <= 0 || chan_count > im->channels) { |
451 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
459 | dIMCTXim(im); | |
460 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
452 | 461 | chan_count); |
453 | 462 | return 0; |
454 | 463 | } |
485 | 494 | /* make sure we have good channel numbers */ |
486 | 495 | for (ch = 0; ch < chan_count; ++ch) { |
487 | 496 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
488 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
497 | dIMCTXim(im); | |
498 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
489 | 499 | return 0; |
490 | 500 | } |
491 | 501 | } |
499 | 509 | } |
500 | 510 | else { |
501 | 511 | if (chan_count <= 0 || chan_count > im->channels) { |
502 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
512 | dIMCTXim(im); | |
513 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
503 | 514 | chan_count); |
504 | 515 | return 0; |
505 | 516 | } |
541 | 552 | /* make sure we have good channel numbers */ |
542 | 553 | for (ch = 0; ch < chan_count; ++ch) { |
543 | 554 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
544 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
555 | dIMCTXim(im); | |
556 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
545 | 557 | return -1; |
546 | 558 | } |
547 | 559 | } |
555 | 567 | } |
556 | 568 | else { |
557 | 569 | if (chan_count <= 0 || chan_count > im->channels) { |
570 | dIMCTXim(im); | |
558 | 571 | i_push_error(0, "Invalid channel count"); |
559 | 572 | return -1; |
560 | 573 | } |
570 | 583 | return count; |
571 | 584 | } |
572 | 585 | else { |
586 | dIMCTXim(im); | |
573 | 587 | i_push_error(0, "Image position outside of image"); |
574 | 588 | return -1; |
575 | 589 | } |
583 | 597 | i_img_dim off; |
584 | 598 | |
585 | 599 | if (bits != 16) { |
600 | dIMCTXim(im); | |
586 | 601 | i_push_error(0, "Invalid bits for 16-bit image"); |
587 | 602 | return -1; |
588 | 603 | } |
598 | 613 | /* make sure we have good channel numbers */ |
599 | 614 | for (ch = 0; ch < chan_count; ++ch) { |
600 | 615 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
601 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
616 | dIMCTXim(im); | |
617 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
602 | 618 | return -1; |
603 | 619 | } |
604 | 620 | } |
614 | 630 | } |
615 | 631 | else { |
616 | 632 | if (chan_count <= 0 || chan_count > im->channels) { |
633 | dIMCTXim(im); | |
617 | 634 | i_push_error(0, "Invalid channel count"); |
618 | 635 | return -1; |
619 | 636 | } |
631 | 648 | return count; |
632 | 649 | } |
633 | 650 | else { |
651 | dIMCTXim(im); | |
634 | 652 | i_push_error(0, "Image position outside of image"); |
635 | 653 | return -1; |
636 | 654 | } |
670 | 688 | int all_in_mask = 1; |
671 | 689 | for (ch = 0; ch < chan_count; ++ch) { |
672 | 690 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
673 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
691 | dIMCTXim(im); | |
692 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
674 | 693 | return -1; |
675 | 694 | } |
676 | 695 | if (!((1 << chans[ch]) & im->ch_mask)) |
700 | 719 | } |
701 | 720 | else { |
702 | 721 | if (chan_count <= 0 || chan_count > im->channels) { |
703 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
722 | dIMCTXim(im); | |
723 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
704 | 724 | chan_count); |
705 | 725 | return -1; |
706 | 726 | } |
720 | 740 | return count; |
721 | 741 | } |
722 | 742 | else { |
743 | dIMCTXim(im); | |
723 | 744 | i_push_error(0, "Image position outside of image"); |
724 | 745 | return -1; |
725 | 746 | } |
759 | 780 | int all_in_mask = 1; |
760 | 781 | for (ch = 0; ch < chan_count; ++ch) { |
761 | 782 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
762 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
783 | dIMCTXim(im); | |
784 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
763 | 785 | return -1; |
764 | 786 | } |
765 | 787 | if (!((1 << chans[ch]) & im->ch_mask)) |
792 | 814 | } |
793 | 815 | else { |
794 | 816 | if (chan_count <= 0 || chan_count > im->channels) { |
795 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
817 | dIMCTXim(im); | |
818 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
796 | 819 | chan_count); |
797 | 820 | return -1; |
798 | 821 | } |
814 | 837 | return count; |
815 | 838 | } |
816 | 839 | else { |
840 | dIMCTXim(im); | |
817 | 841 | i_push_error(0, "Image position outside of image"); |
818 | 842 | return -1; |
819 | 843 | } |
0 | #define IMAGER_NO_CONTEXT | |
1 | ||
0 | 2 | #include "imager.h" |
1 | 3 | #include "imageri.h" |
2 | 4 | |
79 | 81 | }*/ |
80 | 82 | |
81 | 83 | /* |
82 | =item i_img_8_new(x, y, ch) | |
83 | ||
84 | =item im_img_8_new(ctx, x, y, ch) | |
85 | X<im_img_8_new API>X<i_img_8_new API> | |
84 | 86 | =category Image creation/destruction |
85 | ||
87 | =synopsis i_img *img = im_img_8_new(aIMCTX, width, height, channels); | |
86 | 88 | =synopsis i_img *img = i_img_8_new(width, height, channels); |
87 | 89 | |
88 | 90 | Creates a new image object I<x> pixels wide, and I<y> pixels high with |
91 | 93 | =cut |
92 | 94 | */ |
93 | 95 | |
94 | ||
95 | 96 | i_img * |
96 | i_img_8_new(i_img_dim x,i_img_dim y,int ch) { | |
97 | im_img_8_new(pIMCTX, i_img_dim x,i_img_dim y,int ch) { | |
97 | 98 | i_img *im; |
98 | 99 | |
99 | mm_log((1,"IIM_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
100 | im_log((aIMCTX, 1,"im_img_8_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
100 | 101 | i_DFc(x), i_DFc(y), ch)); |
101 | 102 | |
102 | im=i_img_empty_ch(NULL,x,y,ch); | |
103 | im = im_img_empty_ch(aIMCTX, NULL,x,y,ch); | |
103 | 104 | |
104 | mm_log((1,"(%p) <- IIM_new\n",im)); | |
105 | return im; | |
106 | } | |
107 | ||
108 | /* | |
109 | =item i_img_new() | |
110 | ||
111 | Create new image reference - notice that this isn't an object yet and | |
112 | this should be fixed asap. | |
113 | ||
114 | =cut | |
115 | */ | |
116 | ||
117 | ||
118 | i_img * | |
119 | i_img_new() { | |
120 | i_img *im; | |
121 | ||
122 | mm_log((1,"i_img_struct()\n")); | |
123 | ||
124 | im = i_img_alloc(); | |
125 | ||
126 | *im = IIM_base_8bit_direct; | |
127 | im->xsize=0; | |
128 | im->ysize=0; | |
129 | im->channels=3; | |
130 | im->ch_mask=MAXINT; | |
131 | im->bytes=0; | |
132 | im->idata=NULL; | |
133 | ||
134 | i_img_init(im); | |
135 | ||
136 | mm_log((1,"(%p) <- i_img_struct\n",im)); | |
105 | im_log((aIMCTX, 1,"(%p) <- IIM_new\n",im)); | |
137 | 106 | return im; |
138 | 107 | } |
139 | 108 | |
154 | 123 | */ |
155 | 124 | |
156 | 125 | i_img * |
157 | i_img_empty(i_img *im,i_img_dim x,i_img_dim y) { | |
158 | mm_log((1,"i_img_empty(*im %p, x %" i_DF ", y %" i_DF ")\n", | |
126 | im_img_empty(pIMCTX, i_img *im,i_img_dim x,i_img_dim y) { | |
127 | im_log((aIMCTX, 1,"i_img_empty(*im %p, x %" i_DF ", y %" i_DF ")\n", | |
159 | 128 | im, i_DFc(x), i_DFc(y))); |
160 | return i_img_empty_ch(im, x, y, 3); | |
129 | return im_img_empty_ch(aIMCTX, im, x, y, 3); | |
161 | 130 | } |
162 | 131 | |
163 | 132 | /* |
174 | 143 | */ |
175 | 144 | |
176 | 145 | i_img * |
177 | i_img_empty_ch(i_img *im,i_img_dim x,i_img_dim y,int ch) { | |
146 | im_img_empty_ch(pIMCTX, i_img *im,i_img_dim x,i_img_dim y,int ch) { | |
178 | 147 | size_t bytes; |
179 | 148 | |
180 | mm_log((1,"i_img_empty_ch(*im %p, x %" i_DF ", y %" i_DF ", ch %d)\n", | |
149 | im_log((aIMCTX, 1,"i_img_empty_ch(*im %p, x %" i_DF ", y %" i_DF ", ch %d)\n", | |
181 | 150 | im, i_DFc(x), i_DFc(y), ch)); |
182 | 151 | |
183 | 152 | if (x < 1 || y < 1) { |
184 | i_push_error(0, "Image sizes must be positive"); | |
153 | im_push_error(aIMCTX, 0, "Image sizes must be positive"); | |
185 | 154 | return NULL; |
186 | 155 | } |
187 | 156 | if (ch < 1 || ch > MAXCHANNELS) { |
188 | i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS); | |
157 | im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS); | |
189 | 158 | return NULL; |
190 | 159 | } |
191 | 160 | /* check this multiplication doesn't overflow */ |
192 | 161 | bytes = x*y*ch; |
193 | 162 | if (bytes / y / ch != x) { |
194 | i_push_errorf(0, "integer overflow calculating image allocation"); | |
163 | im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation"); | |
195 | 164 | return NULL; |
196 | 165 | } |
197 | 166 | |
198 | 167 | if (im == NULL) |
199 | im = i_img_alloc(); | |
168 | im = im_img_alloc(aIMCTX); | |
200 | 169 | |
201 | 170 | memcpy(im, &IIM_base_8bit_direct, sizeof(i_img)); |
202 | 171 | i_tags_new(&im->tags); |
206 | 175 | im->ch_mask = MAXINT; |
207 | 176 | im->bytes=bytes; |
208 | 177 | if ( (im->idata=mymalloc(im->bytes)) == NULL) |
209 | i_fatal(2,"malloc() error\n"); | |
178 | im_fatal(aIMCTX, 2,"malloc() error\n"); | |
210 | 179 | memset(im->idata,0,(size_t)im->bytes); |
211 | 180 | |
212 | 181 | im->ext_data = NULL; |
213 | 182 | |
214 | i_img_init(im); | |
183 | im_img_init(aIMCTX, im); | |
215 | 184 | |
216 | mm_log((1,"(%p) <- i_img_empty_ch\n",im)); | |
185 | im_log((aIMCTX, 1,"(%p) <- i_img_empty_ch\n",im)); | |
217 | 186 | return im; |
218 | 187 | } |
219 | 188 | |
502 | 471 | /* make sure we have good channel numbers */ |
503 | 472 | for (ch = 0; ch < chan_count; ++ch) { |
504 | 473 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
505 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
474 | dIMCTXim(im); | |
475 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
506 | 476 | return 0; |
507 | 477 | } |
508 | 478 | } |
516 | 486 | } |
517 | 487 | else { |
518 | 488 | if (chan_count <= 0 || chan_count > im->channels) { |
519 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
489 | dIMCTXim(im); | |
490 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
520 | 491 | chan_count); |
521 | 492 | return 0; |
522 | 493 | } |
556 | 527 | unsigned char *data; |
557 | 528 | for (ch = 0; ch < chan_count; ++ch) { |
558 | 529 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
559 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
530 | dIMCTXim(im); | |
531 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
560 | 532 | } |
561 | 533 | } |
562 | 534 | if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) { |
570 | 542 | /* make sure we have good channel numbers */ |
571 | 543 | for (ch = 0; ch < chan_count; ++ch) { |
572 | 544 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
573 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
545 | dIMCTXim(im); | |
546 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
574 | 547 | return 0; |
575 | 548 | } |
576 | 549 | } |
584 | 557 | } |
585 | 558 | else { |
586 | 559 | if (chan_count <= 0 || chan_count > im->channels) { |
587 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
560 | dIMCTXim(im); | |
561 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
588 | 562 | chan_count); |
589 | 563 | return 0; |
590 | 564 | } |
637 | 611 | int all_in_mask = 1; |
638 | 612 | for (ch = 0; ch < chan_count; ++ch) { |
639 | 613 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
640 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
614 | dIMCTXim(im); | |
615 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
641 | 616 | return -1; |
642 | 617 | } |
643 | 618 | if (!((1 << chans[ch]) & im->ch_mask)) |
666 | 641 | } |
667 | 642 | else { |
668 | 643 | if (chan_count <= 0 || chan_count > im->channels) { |
669 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
644 | dIMCTXim(im); | |
645 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
670 | 646 | chan_count); |
671 | 647 | return -1; |
672 | 648 | } |
686 | 662 | return count; |
687 | 663 | } |
688 | 664 | else { |
665 | dIMCTXim(im); | |
689 | 666 | i_push_error(0, "Image position outside of image"); |
690 | 667 | return -1; |
691 | 668 | } |
725 | 702 | int all_in_mask = 1; |
726 | 703 | for (ch = 0; ch < chan_count; ++ch) { |
727 | 704 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
728 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
705 | dIMCTXim(im); | |
706 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
729 | 707 | return -1; |
730 | 708 | } |
731 | 709 | if (!((1 << chans[ch]) & im->ch_mask)) |
755 | 733 | } |
756 | 734 | else { |
757 | 735 | if (chan_count <= 0 || chan_count > im->channels) { |
758 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
736 | dIMCTXim(im); | |
737 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
759 | 738 | chan_count); |
760 | 739 | return -1; |
761 | 740 | } |
775 | 754 | return count; |
776 | 755 | } |
777 | 756 | else { |
757 | dIMCTXim(im); | |
778 | 758 | i_push_error(0, "Image position outside of image"); |
779 | 759 | return -1; |
780 | 760 | } |
19 | 19 | =cut |
20 | 20 | */ |
21 | 21 | |
22 | #define IMAGER_NO_CONTEXT | |
22 | 23 | #include "imager.h" |
23 | 24 | #include "imageri.h" |
24 | 25 | |
90 | 91 | }; |
91 | 92 | |
92 | 93 | /* |
93 | =item i_img_double_new(i_img_dim x, i_img_dim y, int ch) | |
94 | =item im_img_double_new(ctx, x, y, ch) | |
95 | X<im_img_double_new API>X<i_img_double_new API> | |
94 | 96 | =category Image creation/destruction |
97 | =synopsis i_img *img = im_img_double_new(aIMCTX, width, height, channels); | |
95 | 98 | =synopsis i_img *img = i_img_double_new(width, height, channels); |
96 | 99 | |
97 | 100 | Creates a new double per sample image. |
101 | ||
102 | Also callable as C<i_img_double_new(width, height, channels)>. | |
98 | 103 | |
99 | 104 | =cut |
100 | 105 | */ |
101 | i_img *i_img_double_new(i_img_dim x, i_img_dim y, int ch) { | |
106 | i_img * | |
107 | im_img_double_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) { | |
102 | 108 | size_t bytes; |
103 | 109 | i_img *im; |
104 | 110 | |
105 | mm_log((1,"i_img_double_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
111 | im_log((aIMCTX, 1,"i_img_double_new(x %" i_DF ", y %" i_DF ", ch %d)\n", | |
106 | 112 | i_DFc(x), i_DFc(y), ch)); |
107 | 113 | |
108 | 114 | if (x < 1 || y < 1) { |
109 | i_push_error(0, "Image sizes must be positive"); | |
115 | im_push_error(aIMCTX, 0, "Image sizes must be positive"); | |
110 | 116 | return NULL; |
111 | 117 | } |
112 | 118 | if (ch < 1 || ch > MAXCHANNELS) { |
113 | i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS); | |
119 | im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS); | |
114 | 120 | return NULL; |
115 | 121 | } |
116 | 122 | bytes = x * y * ch * sizeof(double); |
117 | 123 | if (bytes / y / ch / sizeof(double) != x) { |
118 | i_push_errorf(0, "integer overflow calculating image allocation"); | |
124 | im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation"); | |
119 | 125 | return NULL; |
120 | 126 | } |
121 | 127 | |
122 | im = i_img_alloc(); | |
128 | im = im_img_alloc(aIMCTX); | |
123 | 129 | *im = IIM_base_double_direct; |
124 | 130 | i_tags_new(&im->tags); |
125 | 131 | im->xsize = x; |
129 | 135 | im->ext_data = NULL; |
130 | 136 | im->idata = mymalloc(im->bytes); |
131 | 137 | memset(im->idata, 0, im->bytes); |
132 | i_img_init(im); | |
138 | im_img_init(aIMCTX, im); | |
133 | 139 | |
134 | 140 | return im; |
135 | 141 | } |
331 | 337 | /* make sure we have good channel numbers */ |
332 | 338 | for (ch = 0; ch < chan_count; ++ch) { |
333 | 339 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
334 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
340 | dIMCTXim(im); | |
341 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
335 | 342 | return 0; |
336 | 343 | } |
337 | 344 | } |
345 | 352 | } |
346 | 353 | else { |
347 | 354 | if (chan_count <= 0 || chan_count > im->channels) { |
348 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
355 | dIMCTXim(im); | |
356 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
349 | 357 | chan_count); |
350 | 358 | return 0; |
351 | 359 | } |
382 | 390 | /* make sure we have good channel numbers */ |
383 | 391 | for (ch = 0; ch < chan_count; ++ch) { |
384 | 392 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
385 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
393 | dIMCTXim(im); | |
394 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
386 | 395 | return 0; |
387 | 396 | } |
388 | 397 | } |
396 | 405 | } |
397 | 406 | else { |
398 | 407 | if (chan_count <= 0 || chan_count > im->channels) { |
399 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
408 | dIMCTXim(im); | |
409 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
400 | 410 | chan_count); |
401 | 411 | return 0; |
402 | 412 | } |
450 | 460 | int all_in_mask = 1; |
451 | 461 | for (ch = 0; ch < chan_count; ++ch) { |
452 | 462 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
453 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
463 | dIMCTXim(im); | |
464 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
454 | 465 | return -1; |
455 | 466 | } |
456 | 467 | if (!((1 << chans[ch]) & im->ch_mask)) |
481 | 492 | } |
482 | 493 | else { |
483 | 494 | if (chan_count <= 0 || chan_count > im->channels) { |
484 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
495 | dIMCTXim(im); | |
496 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
485 | 497 | chan_count); |
486 | 498 | return -1; |
487 | 499 | } |
502 | 514 | return count; |
503 | 515 | } |
504 | 516 | else { |
517 | dIMCTXim(im); | |
505 | 518 | i_push_error(0, "Image position outside of image"); |
506 | 519 | return -1; |
507 | 520 | } |
541 | 554 | int all_in_mask = 1; |
542 | 555 | for (ch = 0; ch < chan_count; ++ch) { |
543 | 556 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
544 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
557 | dIMCTXim(im); | |
558 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
545 | 559 | return -1; |
546 | 560 | } |
547 | 561 | if (!((1 << chans[ch]) & im->ch_mask)) |
572 | 586 | } |
573 | 587 | else { |
574 | 588 | if (chan_count <= 0 || chan_count > im->channels) { |
575 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
589 | dIMCTXim(im); | |
590 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
576 | 591 | chan_count); |
577 | 592 | return -1; |
578 | 593 | } |
593 | 608 | return count; |
594 | 609 | } |
595 | 610 | else { |
611 | dIMCTXim(im); | |
596 | 612 | i_push_error(0, "Image position outside of image"); |
597 | 613 | return -1; |
598 | 614 | } |
615 | 631 | i_img *targ; |
616 | 632 | i_fcolor *line; |
617 | 633 | i_img_dim y; |
618 | ||
619 | targ = i_img_double_new(im->xsize, im->ysize, im->channels); | |
634 | dIMCTXim(im); | |
635 | ||
636 | targ = im_img_double_new(aIMCTX, im->xsize, im->ysize, im->channels); | |
620 | 637 | if (!targ) |
621 | 638 | return NULL; |
622 | 639 | line = mymalloc(sizeof(i_fcolor) * im->xsize); |
74 | 74 | #define i_psampf(im, l, r, y, samps, chans, count) \ |
75 | 75 | (((im)->i_f_psampf)((im), (l), (r), (y), (samps), (chans), (count))) |
76 | 76 | |
77 | #ifndef IMAGER_DIRECT_IMAGE_CALLS | |
78 | #define IMAGER_DIRECT_IMAGE_CALLS 1 | |
77 | 79 | #endif |
80 | ||
81 | #if IMAGER_DIRECT_IMAGE_CALLS | |
82 | ||
83 | #define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val))) | |
84 | #define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val))) | |
85 | #define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val))) | |
86 | #define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val))) | |
87 | #define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val)) | |
88 | #define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val)) | |
89 | #define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val)) | |
90 | #define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val)) | |
91 | ||
92 | #define i_gsamp(im, l, r, y, samps, chans, count) \ | |
93 | (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count))) | |
94 | #define i_gsampf(im, l, r, y, samps, chans, count) \ | |
95 | (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count))) | |
96 | ||
97 | #endif | |
98 | ||
99 | #define i_gsamp_bits(im, l, r, y, samps, chans, count, bits) \ | |
100 | (((im)->i_f_gsamp_bits) ? ((im)->i_f_gsamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1) | |
101 | #define i_psamp_bits(im, l, r, y, samps, chans, count, bits) \ | |
102 | (((im)->i_f_psamp_bits) ? ((im)->i_f_psamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1) | |
103 | ||
104 | #define i_findcolor(im, color, entry) \ | |
105 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
106 | ||
107 | #define i_gpal(im, l, r, y, vals) \ | |
108 | (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0) | |
109 | #define i_ppal(im, l, r, y, vals) \ | |
110 | (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0) | |
111 | #define i_addcolors(im, colors, count) \ | |
112 | (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1) | |
113 | #define i_getcolors(im, index, color, count) \ | |
114 | (((im)->i_f_getcolors) ? \ | |
115 | ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0) | |
116 | #define i_setcolors(im, index, color, count) \ | |
117 | (((im)->i_f_setcolors) ? \ | |
118 | ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0) | |
119 | #define i_colorcount(im) \ | |
120 | (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1) | |
121 | #define i_maxcolors(im) \ | |
122 | (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1) | |
123 | #define i_findcolor(im, color, entry) \ | |
124 | (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0) | |
125 | ||
126 | #define i_img_virtual(im) ((im)->virtual) | |
127 | #define i_img_type(im) ((im)->type) | |
128 | #define i_img_bits(im) ((im)->bits) | |
129 | ||
130 | #define pIMCTX im_context_t my_im_ctx | |
131 | ||
132 | #ifdef IMAGER_NO_CONTEXT | |
133 | #define dIMCTXctx(ctx) pIMCTX = (ctx) | |
134 | #define dIMCTX dIMCTXctx(im_get_context()) | |
135 | #define dIMCTXim(im) dIMCTXctx((im)->context) | |
136 | #define dIMCTXio(io) dIMCTXctx((io)->context) | |
137 | #define aIMCTX my_im_ctx | |
138 | #else | |
139 | #define aIMCTX im_get_context() | |
140 | #endif | |
141 | ||
142 | #define i_img_8_new(xsize, ysize, channels) im_img_8_new(aIMCTX, (xsize), (ysize), (channels)) | |
143 | #define i_img_16_new(xsize, ysize, channels) im_img_16_new(aIMCTX, (xsize), (ysize), (channels)) | |
144 | #define i_img_double_new(xsize, ysize, channels) im_img_double_new(aIMCTX, (xsize), (ysize), (channels)) | |
145 | #define i_img_pal_new(xsize, ysize, channels, maxpal) im_img_pal_new(aIMCTX, (xsize), (ysize), (channels), (maxpal)) | |
146 | ||
147 | #define i_img_alloc() im_img_alloc(aIMCTX) | |
148 | #define i_img_init(im) im_img_init(aIMCTX, im) | |
149 | ||
150 | #define i_set_image_file_limits(width, height, bytes) im_set_image_file_limits(aIMCTX, width, height, bytes) | |
151 | #define i_get_image_file_limits(width, height, bytes) im_get_image_file_limits(aIMCTX, width, height, bytes) | |
152 | #define i_int_check_image_file_limits(width, height, channels, sample_size) im_int_check_image_file_limits(aIMCTX, width, height, channels, sample_size) | |
153 | ||
154 | #define i_clear_error() im_clear_error(aIMCTX) | |
155 | #define i_push_errorvf(code, fmt, args) im_push_errorvf(aIMCTX, code, fmt, args) | |
156 | #define i_push_error(code, msg) im_push_error(aIMCTX, code, msg) | |
157 | #define i_errors() im_errors(aIMCTX) | |
158 | ||
159 | #define io_new_fd(fd) im_io_new_fd(aIMCTX, (fd)) | |
160 | #define io_new_bufchain() im_io_new_bufchain(aIMCTX) | |
161 | #define io_new_buffer(data, len, closecb, closectx) im_io_new_buffer(aIMCTX, (data), (len), (closecb), (closectx)) | |
162 | #define io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb) \ | |
163 | im_io_new_cb(aIMCTX, (p), (readcb), (writecb), (seekcb), (closecb), (destroycb)) | |
164 | ||
165 | #endif |
0 | #ifndef IMAGER_IMPERLIO_H | |
1 | #define IMAGER_IMPERLIO_H | |
2 | ||
3 | extern i_io_glue_t * | |
4 | im_io_new_perlio(pTHX_ PerlIO *handle); | |
5 | ||
6 | #endif |
0 | #define IMAGER_NO_CONTEXT | |
0 | 1 | #include "imager.h" |
1 | 2 | #include "iolayer.h" |
2 | 3 | #include "imerror.h" |
112 | 113 | */ |
113 | 114 | |
114 | 115 | 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); | |
116 | i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb, | |
117 | i_io_writep_t writecb, i_io_seekp_t seekcb); | |
117 | 118 | |
118 | 119 | static ssize_t fd_read(io_glue *ig, void *buf, size_t count); |
119 | 120 | static ssize_t fd_write(io_glue *ig, const void *buf, size_t count); |
152 | 153 | */ |
153 | 154 | |
154 | 155 | /* |
155 | =item io_new_bufchain() | |
156 | =item im_io_new_bufchain(ctx) | |
157 | X<im_io_new_bufchain API>X<i_io_new_bufchain API> | |
156 | 158 | =order 10 |
157 | 159 | =category I/O Layers |
158 | 160 | |
159 | returns a new io_glue object that has the 'empty' source and but can | |
161 | Returns a new io_glue object that has the 'empty' source and but can | |
160 | 162 | be written to and read from later (like a pseudo file). |
161 | 163 | |
164 | Also callable as C<io_new_bufchain()>. | |
165 | ||
162 | 166 | =cut |
163 | 167 | */ |
164 | 168 | |
165 | 169 | io_glue * |
166 | io_new_bufchain() { | |
170 | im_io_new_bufchain(pIMCTX) { | |
167 | 171 | io_glue *ig; |
168 | 172 | io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain)); |
169 | 173 | |
170 | mm_log((1, "io_new_bufchain()\n")); | |
174 | im_log((aIMCTX, 1, "io_new_bufchain()\n")); | |
171 | 175 | |
172 | 176 | ig = mymalloc(sizeof(io_glue)); |
173 | 177 | memset(ig, 0, sizeof(*ig)); |
174 | i_io_init(ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek); | |
178 | i_io_init(aIMCTX, ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek); | |
175 | 179 | |
176 | 180 | ieb->offset = 0; |
177 | 181 | ieb->length = 0; |
187 | 191 | ig->closecb = bufchain_close; |
188 | 192 | ig->destroycb = bufchain_destroy; |
189 | 193 | |
194 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); | |
195 | ||
190 | 196 | return ig; |
191 | 197 | } |
192 | 198 | |
193 | 199 | /* |
194 | =item io_new_buffer(data, length) | |
200 | =item im_io_new_buffer(ctx, data, length) | |
201 | X<im_io_new_buffer API>X<io_new_buffer API> | |
195 | 202 | =order 10 |
196 | 203 | =category I/O Layers |
197 | 204 | |
198 | 205 | Returns a new io_glue object that has the source defined as reading |
199 | 206 | from specified buffer. Note that the buffer is not copied. |
200 | 207 | |
208 | ctx - an Imager context object | |
201 | 209 | data - buffer to read from |
202 | 210 | length - length of buffer |
203 | 211 | |
212 | Also callable as C<io_new_buffer(data, length>. | |
213 | ||
204 | 214 | =cut |
205 | 215 | */ |
206 | 216 | |
207 | 217 | io_glue * |
208 | io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { | |
218 | im_io_new_buffer(pIMCTX, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) { | |
209 | 219 | io_buffer *ig; |
210 | 220 | |
211 | mm_log((1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); | |
221 | im_log((aIMCTX, 1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata)); | |
212 | 222 | |
213 | 223 | ig = mymalloc(sizeof(io_buffer)); |
214 | 224 | memset(ig, 0, sizeof(*ig)); |
215 | i_io_init(&ig->base, BUFFER, buffer_read, buffer_write, buffer_seek); | |
225 | i_io_init(aIMCTX, &ig->base, BUFFER, buffer_read, buffer_write, buffer_seek); | |
216 | 226 | ig->data = data; |
217 | 227 | ig->len = len; |
218 | 228 | ig->closecb = closecb; |
223 | 233 | ig->base.closecb = buffer_close; |
224 | 234 | ig->base.destroycb = buffer_destroy; |
225 | 235 | |
236 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); | |
237 | ||
226 | 238 | return (io_glue *)ig; |
227 | 239 | } |
228 | 240 | |
229 | 241 | |
230 | 242 | /* |
231 | =item io_new_fd(fd) | |
243 | =item im_io_new_fd(ctx, file) | |
244 | X<io_new_fd API>X<im_io_new_fd API> | |
232 | 245 | =order 10 |
233 | 246 | =category I/O Layers |
234 | 247 | |
235 | returns a new io_glue object that has the source defined as reading | |
248 | Returns a new io_glue object that has the source defined as reading | |
236 | 249 | from specified file descriptor. Note that the the interface to receiving |
237 | 250 | data from the io_glue callbacks hasn't been done yet. |
238 | 251 | |
239 | fd - file descriptor to read/write from | |
252 | ctx - and Imager context object | |
253 | file - file descriptor to read/write from | |
254 | ||
255 | Also callable as C<io_new_fd(file)>. | |
240 | 256 | |
241 | 257 | =cut |
242 | 258 | */ |
243 | 259 | |
244 | 260 | io_glue * |
245 | io_new_fd(int fd) { | |
261 | im_io_new_fd(pIMCTX, int fd) { | |
246 | 262 | io_fdseek *ig; |
247 | 263 | |
248 | mm_log((1, "io_new_fd(fd %d)\n", fd)); | |
264 | im_log((aIMCTX, 1, "io_new_fd(fd %d)\n", fd)); | |
249 | 265 | |
250 | 266 | ig = mymalloc(sizeof(io_fdseek)); |
251 | 267 | memset(ig, 0, sizeof(*ig)); |
252 | i_io_init(&ig->base, FDSEEK, fd_read, fd_write, fd_seek); | |
268 | i_io_init(aIMCTX, &ig->base, FDSEEK, fd_read, fd_write, fd_seek); | |
253 | 269 | ig->fd = fd; |
254 | 270 | |
255 | 271 | ig->base.closecb = fd_close; |
256 | 272 | ig->base.sizecb = fd_size; |
257 | 273 | ig->base.destroycb = NULL; |
258 | ||
259 | mm_log((1, "(%p) <- io_new_fd\n", ig)); | |
274 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); | |
275 | ||
276 | im_log((aIMCTX, 1, "(%p) <- io_new_fd\n", ig)); | |
260 | 277 | return (io_glue *)ig; |
261 | 278 | } |
262 | 279 | |
263 | 280 | /* |
264 | =item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
281 | =item im_io_new_cb(ctx, p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
282 | X<im_io_new_cb API>X<io_new_cb API> | |
265 | 283 | =category I/O Layers |
266 | 284 | =order 10 |
267 | 285 | |
299 | 317 | |
300 | 318 | =back |
301 | 319 | |
320 | Also callable as C<io_new_cb(p, readcb, writecb, seekcb, closecb, | |
321 | destroycb)>. | |
322 | ||
302 | 323 | =cut |
303 | 324 | */ |
304 | 325 | |
305 | 326 | io_glue * |
306 | io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb, | |
327 | im_io_new_cb(pIMCTX, void *p, i_io_readl_t readcb, i_io_writel_t writecb, | |
307 | 328 | i_io_seekl_t seekcb, i_io_closel_t closecb, |
308 | 329 | i_io_destroyl_t destroycb) { |
309 | 330 | io_cb *ig; |
310 | 331 | |
311 | mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " | |
332 | im_log((aIMCTX, 1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, " | |
312 | 333 | "destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb)); |
313 | 334 | ig = mymalloc(sizeof(io_cb)); |
314 | 335 | 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)); | |
336 | i_io_init(aIMCTX, &ig->base, CBSEEK, realseek_read, realseek_write, realseek_seek); | |
337 | im_log((aIMCTX, 1, "(%p) <- io_new_cb\n", ig)); | |
317 | 338 | |
318 | 339 | ig->base.closecb = realseek_close; |
319 | 340 | ig->base.destroycb = realseek_destroy; |
324 | 345 | ig->seekcb = seekcb; |
325 | 346 | ig->closecb = closecb; |
326 | 347 | ig->destroycb = destroycb; |
348 | ||
349 | im_context_refinc(aIMCTX, "im_io_new_bufchain"); | |
327 | 350 | |
328 | 351 | return (io_glue *)ig; |
329 | 352 | } |
330 | 353 | |
331 | 354 | /* |
332 | 355 | =item io_slurp(ig, c) |
356 | X<io_slurp API> | |
333 | 357 | =category I/O Layers |
334 | 358 | |
335 | 359 | Takes the source that the io_glue is bound to and allocates space for |
360 | 384 | io_type inn = ig->type; |
361 | 385 | |
362 | 386 | if ( inn != BUFCHAIN ) { |
363 | i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n"); | |
387 | dIMCTXio(ig); | |
388 | im_fatal(aIMCTX, 0, "io_slurp: called on a source that is not from a bufchain\n"); | |
364 | 389 | } |
365 | 390 | |
366 | 391 | ieb = ig->exdata; |
372 | 397 | |
373 | 398 | rc = bufchain_read(ig, cc, ieb->length); |
374 | 399 | |
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); | |
400 | if (rc != ieb->length) { | |
401 | dIMCTXio(ig); | |
402 | im_fatal(aIMCTX,1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length); | |
403 | } | |
377 | 404 | |
378 | 405 | return rc; |
379 | 406 | } |
380 | 407 | |
381 | 408 | /* |
382 | 409 | =item io_glue_destroy(ig) |
410 | X<io_glue_destroy API> | |
383 | 411 | =category I/O Layers |
384 | 412 | =order 90 |
385 | 413 | =synopsis io_glue_destroy(ig); |
393 | 421 | |
394 | 422 | void |
395 | 423 | io_glue_destroy(io_glue *ig) { |
396 | mm_log((1, "io_glue_DESTROY(ig %p)\n", ig)); | |
424 | dIMCTXio(ig); | |
425 | im_log((aIMCTX, 1, "io_glue_DESTROY(ig %p)\n", ig)); | |
397 | 426 | |
398 | 427 | if (ig->destroycb) |
399 | 428 | ig->destroycb(ig); |
402 | 431 | myfree(ig->buffer); |
403 | 432 | |
404 | 433 | myfree(ig); |
434 | ||
435 | im_context_refdec(aIMCTX, "io_glue_destroy"); | |
405 | 436 | } |
406 | 437 | |
407 | 438 | /* |
526 | 557 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn(%p, %p, %d)\n", ig, buf, (int)size)); |
527 | 558 | |
528 | 559 | if (size == 0) { |
560 | dIMCTXio(ig); | |
529 | 561 | i_push_error(0, "peekn size must be positive"); |
530 | 562 | IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (zero size)\n")); |
531 | 563 | return -1; |
973 | 1005 | */ |
974 | 1006 | |
975 | 1007 | static void |
976 | i_io_init(io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb, | |
1008 | i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb, | |
977 | 1009 | i_io_seekp_t seekcb) { |
978 | 1010 | ig->type = type; |
979 | 1011 | ig->exdata = NULL; |
983 | 1015 | ig->closecb = NULL; |
984 | 1016 | ig->sizecb = NULL; |
985 | 1017 | ig->destroycb = NULL; |
1018 | ig->context = aIMCTX; | |
986 | 1019 | |
987 | 1020 | ig->buffer = NULL; |
988 | 1021 | ig->read_ptr = NULL; |
1153 | 1186 | work = ig->buffer; |
1154 | 1187 | } |
1155 | 1188 | |
1189 | /* there should always be buffer space the first time around, but | |
1190 | avoid a compiler warning here */ | |
1191 | rc = -1; | |
1156 | 1192 | while (work < buf_end && (rc = i_io_raw_read(ig, work, buf_end - work)) > 0) { |
1157 | 1193 | work += rc; |
1158 | 1194 | good = 1; |
1327 | 1363 | int |
1328 | 1364 | realseek_close(io_glue *igo) { |
1329 | 1365 | io_cb *ig = (io_cb *)igo; |
1366 | dIMCTXio(igo); | |
1330 | 1367 | |
1331 | 1368 | IOL_DEB(fprintf(IOL_DEBs, "realseek_close(%p)\n", ig)); |
1332 | mm_log((1, "realseek_close(ig %p)\n", ig)); | |
1369 | im_log((aIMCTX,1, "realseek_close(ig %p)\n", ig)); | |
1333 | 1370 | if (ig->closecb) |
1334 | 1371 | return ig->closecb(ig->p); |
1335 | 1372 | else |
1396 | 1433 | IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ig->cpos = %ld, buf = %p, count = %u\n", (long) ig->cpos, buf, (unsigned)count) ); |
1397 | 1434 | |
1398 | 1435 | 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)); | |
1436 | dIMCTXio(igo); | |
1437 | im_log((aIMCTX, 1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count)); | |
1400 | 1438 | count = ig->len - ig->cpos; |
1401 | 1439 | } |
1402 | 1440 | |
1422 | 1460 | static |
1423 | 1461 | ssize_t |
1424 | 1462 | buffer_write(io_glue *ig, const void *buf, size_t count) { |
1425 | mm_log((1, "buffer_write called, this method should never be called.\n")); | |
1463 | dIMCTXio(ig); | |
1464 | im_log((aIMCTX, 1, "buffer_write called, this method should never be called.\n")); | |
1426 | 1465 | return -1; |
1427 | 1466 | } |
1428 | 1467 | |
1441 | 1480 | static |
1442 | 1481 | int |
1443 | 1482 | buffer_close(io_glue *ig) { |
1444 | mm_log((1, "buffer_close(ig %p)\n", ig)); | |
1483 | dIMCTXio(ig); | |
1484 | im_log((aIMCTX, 1, "buffer_close(ig %p)\n", ig)); | |
1445 | 1485 | |
1446 | 1486 | return 0; |
1447 | 1487 | } |
1466 | 1506 | calc_seek_offset(ig->cpos, ig->len, offset, whence); |
1467 | 1507 | |
1468 | 1508 | if (reqpos > ig->len) { |
1469 | mm_log((1, "seeking out of readable range\n")); | |
1509 | dIMCTXio(igo); | |
1510 | im_log((aIMCTX, 1, "seeking out of readable range\n")); | |
1470 | 1511 | return (off_t)-1; |
1471 | 1512 | } |
1472 | 1513 | if (reqpos < 0) { |
1514 | dIMCTXio(igo); | |
1473 | 1515 | i_push_error(0, "seek before beginning of file"); |
1474 | 1516 | return (off_t)-1; |
1475 | 1517 | } |
1487 | 1529 | io_buffer *ig = (io_buffer *)igo; |
1488 | 1530 | |
1489 | 1531 | if (ig->closecb) { |
1490 | mm_log((1,"calling close callback %p for io_buffer\n", | |
1532 | dIMCTXio(igo); | |
1533 | im_log((aIMCTX, 1,"calling close callback %p for io_buffer\n", | |
1491 | 1534 | ig->closecb)); |
1492 | 1535 | ig->closecb(ig->closedata); |
1493 | 1536 | } |
1508 | 1551 | io_blink_new(void) { |
1509 | 1552 | io_blink *ib; |
1510 | 1553 | |
1511 | mm_log((1, "io_blink_new()\n")); | |
1554 | #if 0 | |
1555 | im_log((aIMCTX, 1, "io_blink_new()\n")); | |
1556 | #endif | |
1512 | 1557 | |
1513 | 1558 | ib = mymalloc(sizeof(io_blink)); |
1514 | 1559 | |
1560 | 1605 | static void |
1561 | 1606 | io_destroy_bufchain(io_ex_bchain *ieb) { |
1562 | 1607 | io_blink *cp; |
1608 | #if 0 | |
1563 | 1609 | mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb)); |
1610 | #endif | |
1564 | 1611 | cp = ieb->head; |
1565 | 1612 | |
1566 | 1613 | while(cp) { |
1691 | 1738 | size_t scount = count; |
1692 | 1739 | char *cbuf = buf; |
1693 | 1740 | size_t sk; |
1694 | ||
1695 | mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count)); | |
1741 | dIMCTXio(ig); | |
1742 | ||
1743 | im_log((aIMCTX, 1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count)); | |
1696 | 1744 | |
1697 | 1745 | while( scount ) { |
1698 | 1746 | int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len; |
1712 | 1760 | ieb->gpos += sk; |
1713 | 1761 | } |
1714 | 1762 | |
1715 | mm_log((1, "bufchain_read: returning %ld\n", (long)(count-scount))); | |
1763 | im_log((aIMCTX, 1, "bufchain_read: returning %ld\n", (long)(count-scount))); | |
1716 | 1764 | return count-scount; |
1717 | 1765 | } |
1718 | 1766 | |
1739 | 1787 | io_ex_bchain *ieb = ig->exdata; |
1740 | 1788 | size_t ocount = count; |
1741 | 1789 | size_t sk; |
1742 | ||
1743 | mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count)); | |
1790 | dIMCTXio(ig); | |
1791 | ||
1792 | im_log((aIMCTX, 1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count)); | |
1744 | 1793 | |
1745 | 1794 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) ); |
1746 | 1795 | |
1747 | 1796 | while(count) { |
1748 | mm_log((2, "bufchain_write: - looping - count = %ld\n", (long)count)); | |
1797 | im_log((aIMCTX, 2, "bufchain_write: - looping - count = %ld\n", (long)count)); | |
1749 | 1798 | if (ieb->cp->len == ieb->cpos) { |
1750 | mm_log((1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos)); | |
1799 | im_log((aIMCTX, 1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos)); | |
1751 | 1800 | io_bchain_advance(ieb); |
1752 | 1801 | } |
1753 | 1802 | |
1757 | 1806 | |
1758 | 1807 | if (ieb->cp == ieb->tail) { |
1759 | 1808 | int extend = ieb->cpos + sk - ieb->tfill; |
1760 | mm_log((2, "bufchain_write: extending tail by %d\n", extend)); | |
1809 | im_log((aIMCTX, 2, "bufchain_write: extending tail by %d\n", extend)); | |
1761 | 1810 | if (extend > 0) { |
1762 | 1811 | ieb->length += extend; |
1763 | 1812 | ieb->tfill += extend; |
1785 | 1834 | static |
1786 | 1835 | int |
1787 | 1836 | bufchain_close(io_glue *ig) { |
1788 | mm_log((1, "bufchain_close(ig %p)\n",ig)); | |
1837 | dIMCTXio(ig); | |
1838 | im_log((aIMCTX, 1, "bufchain_close(ig %p)\n",ig)); | |
1789 | 1839 | IOL_DEB( fprintf(IOL_DEBs, "bufchain_close(ig %p)\n", ig) ); |
1790 | 1840 | |
1791 | 1841 | return 0; |
1812 | 1862 | |
1813 | 1863 | off_t scount = calc_seek_offset(ieb->gpos, ieb->length, offset, whence); |
1814 | 1864 | off_t sk; |
1815 | ||
1816 | mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence)); | |
1865 | dIMCTXio(ig); | |
1866 | ||
1867 | im_log((aIMCTX, 1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence)); | |
1817 | 1868 | |
1818 | 1869 | if (scount < 0) { |
1819 | 1870 | i_push_error(0, "invalid whence supplied or seek before start of file"); |
1855 | 1906 | |
1856 | 1907 | while(wrlen > 0) { |
1857 | 1908 | ssize_t rc, wl = i_min(wrlen, BBSIZ); |
1858 | mm_log((1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl)); | |
1909 | im_log((aIMCTX, 1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl)); | |
1859 | 1910 | rc = bufchain_write( ig, TB, wl ); |
1860 | if (rc != wl) i_fatal(0, "bufchain_seek: Unable to extend file\n"); | |
1911 | if (rc != wl) im_fatal(aIMCTX, 0, "bufchain_seek: Unable to extend file\n"); | |
1861 | 1912 | wrlen -= rc; |
1862 | 1913 | } |
1863 | 1914 | } |
1864 | 1915 | |
1865 | mm_log((2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos)); | |
1916 | im_log((aIMCTX, 2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos)); | |
1866 | 1917 | return ieb->gpos; |
1867 | 1918 | } |
1868 | 1919 | |
1897 | 1948 | |
1898 | 1949 | /* 0 is valid - means EOF */ |
1899 | 1950 | if (result < 0) { |
1900 | i_push_errorf(0, "read() failure: %s (%d)", my_strerror(errno), errno); | |
1951 | dIMCTXio(igo); | |
1952 | im_push_errorf(aIMCTX, 0, "read() failure: %s (%d)", my_strerror(errno), errno); | |
1901 | 1953 | } |
1902 | 1954 | |
1903 | 1955 | return result; |
1916 | 1968 | (unsigned)count, (int)result)); |
1917 | 1969 | |
1918 | 1970 | if (result <= 0) { |
1919 | i_push_errorf(errno, "write() failure: %s (%d)", my_strerror(errno), errno); | |
1971 | dIMCTXio(igo); | |
1972 | im_push_errorf(aIMCTX, errno, "write() failure: %s (%d)", my_strerror(errno), errno); | |
1920 | 1973 | } |
1921 | 1974 | |
1922 | 1975 | return result; |
1932 | 1985 | #endif |
1933 | 1986 | |
1934 | 1987 | if (result == (off_t)-1) { |
1935 | i_push_errorf(errno, "lseek() failure: %s (%d)", my_strerror(errno), errno); | |
1988 | dIMCTXio(igo); | |
1989 | im_push_errorf(aIMCTX, errno, "lseek() failure: %s (%d)", my_strerror(errno), errno); | |
1936 | 1990 | } |
1937 | 1991 | |
1938 | 1992 | return result; |
1944 | 1998 | } |
1945 | 1999 | |
1946 | 2000 | static ssize_t fd_size(io_glue *ig) { |
1947 | mm_log((1, "fd_size(ig %p) unimplemented\n", ig)); | |
2001 | dIMCTXio(ig); | |
2002 | im_log((aIMCTX, 1, "fd_size(ig %p) unimplemented\n", ig)); | |
1948 | 2003 | |
1949 | 2004 | return -1; |
1950 | 2005 | } |
21 | 21 | void io_glue_gettypes (io_glue *ig, int reqmeth); |
22 | 22 | |
23 | 23 | /* XS functions */ |
24 | io_glue *io_new_fd(int fd); | |
25 | io_glue *io_new_bufchain(void); | |
26 | io_glue *io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
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); | |
24 | io_glue *im_io_new_fd(pIMCTX, int fd); | |
25 | io_glue *im_io_new_bufchain(pIMCTX); | |
26 | io_glue *im_io_new_buffer(pIMCTX, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); | |
27 | io_glue *im_io_new_cb(pIMCTX, 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); | |
28 | 28 | size_t io_slurp(io_glue *ig, unsigned char **c); |
29 | 29 | void io_glue_destroy(io_glue *ig); |
30 | 30 |
69 | 69 | |
70 | 70 | /* if non-zero we do write buffering (enabled by default) */ |
71 | 71 | int buffered; |
72 | ||
73 | im_context_t context; | |
72 | 74 | }; |
73 | 75 | |
74 | 76 | #define I_IO_DUMP_CALLBACKS 1 |
24 | 24 | =for stopwords XS |
25 | 25 | |
26 | 26 | The API allows you to access Imager functions at the C level from XS |
27 | and from Inline::C. | |
27 | and from C<Inline::C>. | |
28 | 28 | |
29 | 29 | The intent is to allow users to: |
30 | 30 | |
33 | 33 | =item * |
34 | 34 | |
35 | 35 | write C code that does Imager operations the user might do from Perl, |
36 | but faster, for example, the Imager::CountColor example. | |
36 | but faster, for example, the L<Imager::CountColor> example. | |
37 | 37 | |
38 | 38 | =item * |
39 | 39 | |
69 | 69 | |
70 | 70 | =item * |
71 | 71 | |
72 | i_img - used to represent an image | |
73 | ||
74 | =item * | |
75 | ||
76 | i_color - used to represent a color with up to 8 bits per sample. | |
77 | ||
78 | =item * | |
79 | ||
80 | i_fcolor - used to represent a color with a double per sample. | |
81 | ||
82 | =item * | |
83 | ||
84 | i_fill_t - an abstract fill | |
72 | L</i_img> - used to represent an image | |
73 | ||
74 | =item * | |
75 | ||
76 | L</i_color> - used to represent a color with up | |
77 | to 8 bits per sample. | |
78 | ||
79 | =item * | |
80 | ||
81 | L</i_fcolor> - used to represent | |
82 | a color with a double per sample. | |
83 | ||
84 | =item * | |
85 | ||
86 | L</i_fill_t> - fill objects>> - an abstract fill | |
87 | ||
88 | =item * | |
89 | ||
90 | L</im_context_t> - Imager's per-thread state. | |
85 | 91 | |
86 | 92 | =back |
87 | 93 | |
88 | 94 | At this point there is no consolidated font object type, and hence the |
89 | 95 | font functions are not visible through Imager's API. |
90 | 96 | |
91 | =head2 i_img - images | |
97 | =head2 i_img | |
92 | 98 | |
93 | 99 | This contains the dimensions of the image (C<xsize>, C<ysize>, |
94 | 100 | C<channels>), image metadata (C<ch_mask>, C<bits>, C<type>, |
95 | C<virtual>), potentially image data (C<idata>) and the a function | |
96 | table, with pointers to functions to perform various low level image | |
101 | C<virtual>), potentially image data (C<idata>) and a function table, | |
102 | with pointers to functions to perform various low level image | |
97 | 103 | operations. |
98 | 104 | |
99 | 105 | The only time you should directly write to any value in this type is |
109 | 115 | Imager return type and a raw image object for an Imager::ImgRaw return |
110 | 116 | type. |
111 | 117 | |
112 | =head2 C<i_color> - 8-bit color | |
118 | =head2 i_color | |
113 | 119 | |
114 | 120 | Represents an 8-bit per sample color. This is a union containing |
115 | 121 | several different structs for access to components of a color: |
134 | 140 | |
135 | 141 | =back |
136 | 142 | |
137 | Use Imager::Color for parameter and return value types. | |
138 | ||
139 | =head2 C<i_fcolor> - floating point color | |
143 | Use C<Imager::Color> for parameter and return value types. | |
144 | ||
145 | =head2 i_fcolor | |
140 | 146 | |
141 | 147 | Similar to C<i_color> except that each component is a double instead of |
142 | 148 | an unsigned char. |
143 | 149 | |
144 | 150 | Use Imager::Color::Float for parameter and return value types. |
145 | 151 | |
146 | =head2 C<i_fill_t> - fill objects | |
152 | =head2 i_fill_t | |
147 | 153 | |
148 | 154 | Abstract type containing pointers called to perform low level fill |
149 | 155 | operations. |
154 | 160 | Use Imager::FillHandle for parameter and return value types. At the |
155 | 161 | Perl level this is stored in the C<fill> member of the Perl level |
156 | 162 | Imager::Fill object. |
163 | ||
164 | =head2 i_io_glue_t | |
165 | ||
166 | C<i_io_glue_t> is Imager's I/O abstraction. | |
167 | ||
168 | Historically named C<io_glue>, and this name is available for backward | |
169 | compatibility. | |
170 | ||
171 | =head2 im_context_t | |
172 | ||
173 | This new type is an opaque type that stores Imager's per-thread state, | |
174 | including the error message stack, the current log file state and | |
175 | image size file limits. | |
176 | ||
177 | While Imager's internal typemap provides a C<T_PTROBJ> mapping and a | |
178 | DESTROY method for this type you B<must> never return objects of this | |
179 | type back to perl. | |
180 | ||
181 | See L</Context objects> for more information. | |
157 | 182 | |
158 | 183 | =head1 Create an XS module using the Imager API |
159 | 184 | |
258 | 283 | |
259 | 284 | =back |
260 | 285 | |
286 | =head1 Context objects | |
287 | ||
288 | Starting with Imager 0.93, Imager keeps some state per-thread rather | |
289 | than storing it in global (or static) variables. The intent is to | |
290 | improve support for multi-threaded perl programs. | |
291 | ||
292 | For the typical XS or Inline::C module using Imager's API this won't | |
293 | matter - the changes are hidden behind macros and rebuilding your | |
294 | module should require no source code changes. | |
295 | ||
296 | Some operations will be slightly slower, these include: | |
297 | ||
298 | =over | |
299 | ||
300 | =item * | |
301 | ||
302 | creating an image | |
303 | ||
304 | =item * | |
305 | ||
306 | reporting errors | |
307 | ||
308 | =item * | |
309 | ||
310 | creating I/O objects | |
311 | ||
312 | =item * | |
313 | ||
314 | setting/getting/testing image file limits | |
315 | ||
316 | =item * | |
317 | ||
318 | logging | |
319 | ||
320 | =back | |
321 | ||
322 | You can avoid this fairly minor overhead by adding a C<#define>: | |
323 | ||
324 | #define IMAGER_NO_CONTEXT | |
325 | ||
326 | before including any Imager header files, but you will need to manage | |
327 | context objects yourself. | |
328 | ||
329 | Some functions and macros that are available without | |
330 | C<IMAGER_NO_CONTEXT> are not available with it defined, these are: | |
331 | ||
332 | =over | |
333 | ||
334 | =item * | |
335 | ||
336 | mm_log() - to avoid using a different context object for the line | |
337 | header and the line text you need to use im_log() instead, with a | |
338 | context object visible in scope. | |
339 | ||
340 | =back | |
341 | ||
342 | =head2 C<aIMCTX> | |
343 | ||
344 | With C<IMAGER_NO_CONTEXT> defined, C<aIMCTX> refers to the locally | |
345 | defined context object, either via one the of the C<dIMCTX> macros or | |
346 | as a parameter with the C<pIMCTX> macro. | |
347 | ||
348 | Without C<IMAGER_NO_CONTEXT>, C<aIMCTX> is a call to | |
349 | C<im_get_context()> which retrieves the context object for the current | |
350 | thread. | |
351 | ||
352 | There is no C<aIMCTX_> macro, any Imager function that can accept a | |
353 | context parameter always accepts it. | |
354 | ||
355 | =head2 C<pIMCTX> | |
356 | ||
357 | This macro declares a variable of type L</im_context_t> that's | |
358 | accessible via the C<aIMCTX> macro. This is intended for use as a | |
359 | parameter declaration for functions: | |
360 | ||
361 | void f(pIMCTX) { | |
362 | ... use aIMCTX here | |
363 | } | |
364 | ||
365 | void g(...) { | |
366 | ... | |
367 | f(aIMCTX); | |
368 | } | |
369 | ||
370 | =head2 C<dIMCTX> | |
371 | ||
372 | Defines a local context variable and initializes it via | |
373 | L<im_get_context()|Imager::APIRef/im_get_context()>. | |
374 | ||
375 | =head2 C<dIMCTXim> | |
376 | ||
377 | Defines a local context variable and initializes it from the context | |
378 | stored in an L<image object|/i_img>, eg: | |
379 | ||
380 | void f(i_img *im) { | |
381 | dIMCTXim(im); | |
382 | ... | |
383 | } | |
384 | ||
385 | =head2 C<dIMCTXio> | |
386 | ||
387 | Defines a local context variable and initializes it from the context | |
388 | stored in an L<I/O object|/i_io_glue_t> object. | |
389 | ||
390 | void f(i_io_glue_t *io) { | |
391 | dIMCTXio(io); | |
392 | ... | |
393 | } | |
394 | ||
395 | =head2 C<dIMCTXctx> | |
396 | ||
397 | Defines a local context variable accessible via C<aIMCTX> in terms of | |
398 | an expression you supply: | |
399 | ||
400 | void f(my_object *p) { | |
401 | dIMCTXctx(p->context); | |
402 | ... | |
403 | } | |
404 | ||
405 | This can be used to define your own local context macro: | |
406 | ||
407 | #define dIMCTXmine(mine) ((mine)->context) | |
408 | ||
409 | void f(my_object *p) { | |
410 | dIMCTXmine(p); | |
411 | ... | |
412 | } | |
413 | ||
414 | =head1 Mutex Functions | |
415 | ||
416 | Since some libraries are not thread safe, Imager's API includes some | |
417 | simple mutex functions. | |
418 | ||
419 | To create a mutex: | |
420 | ||
421 | i_mutex_t m = i_mutex_new(); | |
422 | ||
423 | To control or lock the mutex: | |
424 | ||
425 | i_mutex_lock(m); | |
426 | ||
427 | To release or unlock the mutex: | |
428 | ||
429 | i_mutex_unlock(m); | |
430 | ||
431 | To free any resources used by the mutex: | |
432 | ||
433 | i_mutex_destroy(m); | |
434 | ||
435 | I most cases where you'd use these functions, your code would create | |
436 | the mutex in your BOOT section, then lock and unlock the mutex as | |
437 | needed to control access to the library. | |
438 | ||
439 | =head1 Context slots | |
440 | ||
441 | =for stopwords | |
442 | TLS APIs | |
443 | ||
444 | To avoid abstracting the platform TLS and thread clean up handling, | |
445 | Imager provides simple APIs for storing per-context information. | |
446 | ||
447 | To allocate a slot: | |
448 | ||
449 | im_slot_t slot = im_context_slot_new(callback) | |
450 | ||
451 | where callback is a (possibly NULL) function pointer called when the | |
452 | context object is destroyed. | |
453 | ||
454 | By default, the stored value for a slot is NULL, whether for a new | |
455 | context or for a cloned context. | |
456 | ||
457 | To store a value: | |
458 | ||
459 | im_context_slot_set(aIMCTX, slot, somevalue); | |
460 | ||
461 | where C<somevalue> can be represented as a C<void *>. | |
462 | ||
463 | To retrieve the value: | |
464 | ||
465 | value = im_context_slot_get(aIMCTX, slot); | |
466 | ||
261 | 467 | =head1 AUTHOR |
262 | 468 | |
263 | 469 | Tony Cook <tonyc@cpan.org> |
39 | 39 | i_flood_cfill_border(im, 50, 50, fill, border); |
40 | 40 | |
41 | 41 | # Error handling |
42 | im_clear_error(aIMCTX); | |
42 | 43 | i_clear_error(); |
43 | 44 | i_push_error(0, "Yep, it's broken"); |
44 | 45 | i_push_error(errno, "Error writing"); |
46 | im_push_error(aIMCTX, 0, "Something is wrong"); | |
47 | va_args args; | |
48 | va_start(args, lastarg); | |
49 | im_push_errorvf(ctx, code, format, args); | |
45 | 50 | i_push_errorf(errno, "Cannot open file %s: %d", filename, errno); |
51 | im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno); | |
46 | 52 | |
47 | 53 | # Files |
54 | im_set_image_file_limits(aIMCTX, 500, 500, 1000000); | |
48 | 55 | i_set_image_file_limits(500, 500, 1000000); |
56 | im_get_image_file_limits(aIMCTX, &width, &height, &bytes) | |
49 | 57 | i_get_image_file_limits(&width, &height, &bytes) |
50 | i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
58 | im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t)) | |
59 | i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
51 | 60 | |
52 | 61 | # Fills |
53 | 62 | i_fill_t *fill = i_new_fill_solidf(&fcolor, combine); |
71 | 80 | # Image creation/destruction |
72 | 81 | i_img *img = i_sametype(src, width, height); |
73 | 82 | i_img *img = i_sametype_chans(src, width, height, channels); |
83 | i_img *img = im_img_16_new(aIMCTX, width, height, channels); | |
74 | 84 | i_img *img = i_img_16_new(width, height, channels); |
85 | i_img *img = im_img_8_new(aIMCTX, width, height, channels); | |
75 | 86 | i_img *img = i_img_8_new(width, height, channels); |
87 | i_img *img = im_img_double_new(aIMCTX, width, height, channels); | |
76 | 88 | i_img *img = i_img_double_new(width, height, channels); |
89 | i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size) | |
77 | 90 | i_img *img = i_img_pal_new(width, height, channels, max_palette_size) |
78 | 91 | i_img_destroy(img) |
79 | 92 | |
80 | 93 | # Image Implementation |
94 | i_img *im = im_img_alloc(aIMCTX); | |
95 | i_img *im = i_img_alloc(); | |
96 | im_img_init(aIMCTX, im); | |
97 | i_img_init(im); | |
81 | 98 | |
82 | 99 | # Image Information |
83 | 100 | // only channel 0 writable |
91 | 108 | |
92 | 109 | # Logging |
93 | 110 | |
111 | # Mutex functions | |
112 | i_mutex_t m = i_mutex_new(); | |
113 | i_mutex_destroy(m); | |
114 | i_mutex_lock(m); | |
115 | i_mutex_unlock(m); | |
116 | ||
94 | 117 | # Paletted images |
95 | 118 | |
96 | 119 | # Tags |
263 | 286 | =item * |
264 | 287 | |
265 | 288 | i_f_psampf - implements psamp() for this image. |
289 | ||
290 | =item * | |
291 | ||
292 | C<im_data> - image specific data internal to Imager. | |
293 | ||
294 | =item * | |
295 | ||
296 | C<context> - the Imager API context this image belongs to. | |
266 | 297 | |
267 | 298 | =back |
268 | 299 | |
841 | 872 | |
842 | 873 | =over |
843 | 874 | |
844 | =item i_clear_error() | |
845 | ||
875 | =item i_push_errorf(int code, char const *fmt, ...) | |
876 | ||
877 | i_push_errorf(errno, "Cannot open file %s: %d", filename, errno); | |
878 | ||
879 | A version of i_push_error() that does printf() like formatting. | |
880 | ||
881 | Does not support perl specific format codes. | |
882 | ||
883 | ||
884 | =for comment | |
885 | From: File error.c | |
886 | ||
887 | =item im_clear_error(ctx) | |
888 | X<im_clear_error API>X<i_clear_error API> | |
889 | ||
890 | im_clear_error(aIMCTX); | |
846 | 891 | i_clear_error(); |
847 | 892 | |
848 | 893 | Clears the error stack. |
849 | 894 | |
850 | 895 | Called by any Imager function before doing any other processing. |
851 | 896 | |
897 | Also callable as C<i_clear_error()>. | |
898 | ||
852 | 899 | |
853 | 900 | =for comment |
854 | 901 | From: File error.c |
855 | 902 | |
856 | =item i_push_error(int code, char const *msg) | |
903 | =item im_push_error(ctx, code, message) | |
904 | X<im_push_error API>X<i_push_error API> | |
857 | 905 | |
858 | 906 | i_push_error(0, "Yep, it's broken"); |
859 | 907 | i_push_error(errno, "Error writing"); |
908 | im_push_error(aIMCTX, 0, "Something is wrong"); | |
860 | 909 | |
861 | 910 | Called by an Imager function to push an error message onto the stack. |
862 | 911 | |
868 | 917 | =for comment |
869 | 918 | From: File error.c |
870 | 919 | |
871 | =item i_push_errorf(int code, char const *fmt, ...) | |
872 | ||
873 | i_push_errorf(errno, "Cannot open file %s: %d", filename, errno); | |
874 | ||
875 | A version of i_push_error() that does printf() like formatting. | |
920 | =item im_push_errorf(ctx, code, char const *fmt, ...) | |
921 | ||
922 | im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno); | |
923 | ||
924 | A version of im_push_error() that does printf() like formatting. | |
876 | 925 | |
877 | 926 | Does not support perl specific format codes. |
878 | 927 | |
880 | 929 | =for comment |
881 | 930 | From: File error.c |
882 | 931 | |
883 | =item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>) | |
884 | ||
932 | =item im_push_errorvf(ctx, code, format, args) | |
933 | X<im_push_error_vf API>X<i_push_errorvf API> | |
934 | ||
935 | va_args args; | |
936 | va_start(args, lastarg); | |
937 | im_push_errorvf(ctx, code, format, args); | |
885 | 938 | |
886 | 939 | Intended for use by higher level functions, takes a varargs pointer |
887 | 940 | and a format to produce the finally pushed error message. |
888 | 941 | |
889 | 942 | Does not support perl specific format codes. |
890 | 943 | |
944 | Also callable as C<i_push_errorvf(code, format, args)> | |
945 | ||
891 | 946 | |
892 | 947 | =for comment |
893 | 948 | From: File error.c |
928 | 983 | =for comment |
929 | 984 | From: File image.c |
930 | 985 | |
931 | =item i_get_image_file_limits(&width, &height, &bytes) | |
932 | ||
933 | ||
986 | =item im_get_image_file_limits(ctx, &width, &height, &bytes) | |
987 | X<im_get_image_file_limits API>X<i_get_image_file_limits> | |
988 | ||
989 | im_get_image_file_limits(aIMCTX, &width, &height, &bytes) | |
934 | 990 | i_get_image_file_limits(&width, &height, &bytes) |
935 | 991 | |
936 | 992 | Retrieves the file limits set by i_set_image_file_limits(). |
947 | 1003 | |
948 | 1004 | =back |
949 | 1005 | |
1006 | Also callable as C<i_get_image_file_limits(&width, &height, &bytes)>. | |
1007 | ||
950 | 1008 | |
951 | 1009 | =for comment |
952 | 1010 | From: File limits.c |
953 | 1011 | |
954 | =item i_int_check_image_file_limits(width, height, channels, sample_size) | |
955 | ||
956 | ||
957 | i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
1012 | =item im_int_check_image_file_limits(width, height, channels, sample_size) | |
1013 | X<im_int_check_image_file_limits API>X<i_int_check_image_file_limits> | |
1014 | ||
1015 | im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t)) | |
1016 | i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
958 | 1017 | |
959 | 1018 | Checks the size of a file in memory against the configured image file |
960 | 1019 | limits. |
966 | 1025 | |
967 | 1026 | This function is intended to be called by image file read functions. |
968 | 1027 | |
1028 | Also callable as C<i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)>. | |
1029 | ||
969 | 1030 | |
970 | 1031 | =for comment |
971 | 1032 | From: File limits.c |
972 | 1033 | |
973 | =item i_set_image_file_limits(width, height, bytes) | |
974 | ||
975 | ||
1034 | =item im_set_image_file_limits(ctx, width, height, bytes) | |
1035 | X<im_set_image_file_limits API>X<i_set_image_file_limits API> | |
1036 | ||
1037 | im_set_image_file_limits(aIMCTX, 500, 500, 1000000); | |
976 | 1038 | i_set_image_file_limits(500, 500, 1000000); |
977 | 1039 | |
978 | 1040 | Set limits on the sizes of images read by Imager. |
997 | 1059 | =back |
998 | 1060 | |
999 | 1061 | Returns non-zero on success. |
1062 | ||
1063 | Also callable as C<i_set_image_file_limits(width, height, bytes)>. | |
1000 | 1064 | |
1001 | 1065 | |
1002 | 1066 | =for comment |
1124 | 1188 | |
1125 | 1189 | =over |
1126 | 1190 | |
1127 | =item io_new_bufchain() | |
1128 | ||
1129 | returns a new io_glue object that has the 'empty' source and but can | |
1191 | =item im_io_new_bufchain(ctx) | |
1192 | X<im_io_new_bufchain API>X<i_io_new_bufchain API> | |
1193 | ||
1194 | Returns a new io_glue object that has the 'empty' source and but can | |
1130 | 1195 | be written to and read from later (like a pseudo file). |
1131 | 1196 | |
1197 | Also callable as C<io_new_bufchain()>. | |
1198 | ||
1132 | 1199 | |
1133 | 1200 | =for comment |
1134 | 1201 | From: File iolayer.c |
1135 | 1202 | |
1136 | =item io_new_buffer(data, length) | |
1203 | =item im_io_new_buffer(ctx, data, length) | |
1204 | X<im_io_new_buffer API>X<io_new_buffer API> | |
1137 | 1205 | |
1138 | 1206 | Returns a new io_glue object that has the source defined as reading |
1139 | 1207 | from specified buffer. Note that the buffer is not copied. |
1140 | 1208 | |
1209 | ctx - an Imager context object | |
1141 | 1210 | data - buffer to read from |
1142 | 1211 | length - length of buffer |
1143 | 1212 | |
1213 | Also callable as C<io_new_buffer(data, length>. | |
1214 | ||
1144 | 1215 | |
1145 | 1216 | =for comment |
1146 | 1217 | From: File iolayer.c |
1147 | 1218 | |
1148 | =item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
1219 | =item im_io_new_cb(ctx, p, read_cb, write_cb, seek_cb, close_cb, destroy_cb) | |
1220 | X<im_io_new_cb API>X<io_new_cb API> | |
1149 | 1221 | |
1150 | 1222 | Create a new I/O layer object that calls your supplied callbacks. |
1151 | 1223 | |
1181 | 1253 | |
1182 | 1254 | =back |
1183 | 1255 | |
1256 | Also callable as C<io_new_cb(p, readcb, writecb, seekcb, closecb, | |
1257 | destroycb)>. | |
1258 | ||
1184 | 1259 | |
1185 | 1260 | =for comment |
1186 | 1261 | From: File iolayer.c |
1187 | 1262 | |
1188 | =item io_new_fd(fd) | |
1189 | ||
1190 | returns a new io_glue object that has the source defined as reading | |
1263 | =item im_io_new_fd(ctx, file) | |
1264 | X<io_new_fd API>X<im_io_new_fd API> | |
1265 | ||
1266 | Returns a new io_glue object that has the source defined as reading | |
1191 | 1267 | from specified file descriptor. Note that the the interface to receiving |
1192 | 1268 | data from the io_glue callbacks hasn't been done yet. |
1193 | 1269 | |
1194 | fd - file descriptor to read/write from | |
1270 | ctx - and Imager context object | |
1271 | file - file descriptor to read/write from | |
1272 | ||
1273 | Also callable as C<io_new_fd(file)>. | |
1195 | 1274 | |
1196 | 1275 | |
1197 | 1276 | =for comment |
1348 | 1427 | From: File iolayer.c |
1349 | 1428 | |
1350 | 1429 | =item io_slurp(ig, c) |
1430 | X<io_slurp API> | |
1351 | 1431 | |
1352 | 1432 | Takes the source that the io_glue is bound to and allocates space for |
1353 | 1433 | a return buffer and returns the entire content in a single buffer. |
1370 | 1450 | From: File iolayer.c |
1371 | 1451 | |
1372 | 1452 | =item io_glue_destroy(ig) |
1453 | X<io_glue_destroy API> | |
1373 | 1454 | |
1374 | 1455 | io_glue_destroy(ig); |
1375 | 1456 | |
1467 | 1548 | |
1468 | 1549 | =over |
1469 | 1550 | |
1470 | =item i_img_16_new(x, y, ch) | |
1471 | ||
1472 | ||
1551 | =item i_sametype(C<im>, C<xsize>, C<ysize>) | |
1552 | ||
1553 | ||
1554 | i_img *img = i_sametype(src, width, height); | |
1555 | ||
1556 | Returns an image of the same type (sample size, channels, paletted/direct). | |
1557 | ||
1558 | For paletted images the palette is copied from the source. | |
1559 | ||
1560 | ||
1561 | =for comment | |
1562 | From: File image.c | |
1563 | ||
1564 | =item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<channels>) | |
1565 | ||
1566 | ||
1567 | i_img *img = i_sametype_chans(src, width, height, channels); | |
1568 | ||
1569 | Returns an image of the same type (sample size). | |
1570 | ||
1571 | For paletted images the equivalent direct type is returned. | |
1572 | ||
1573 | ||
1574 | =for comment | |
1575 | From: File image.c | |
1576 | ||
1577 | =item im_img_16_new(ctx, x, y, ch) | |
1578 | X<im_img_16_new API>X<i_img_16_new API> | |
1579 | ||
1580 | i_img *img = im_img_16_new(aIMCTX, width, height, channels); | |
1473 | 1581 | i_img *img = i_img_16_new(width, height, channels); |
1474 | 1582 | |
1475 | 1583 | Create a new 16-bit/sample image. |
1476 | 1584 | |
1477 | 1585 | Returns the image on success, or NULL on failure. |
1478 | 1586 | |
1587 | Also callable as C<i_img_16_new(x, y, ch)> | |
1588 | ||
1479 | 1589 | |
1480 | 1590 | =for comment |
1481 | 1591 | From: File img16.c |
1482 | 1592 | |
1483 | =item i_img_8_new(x, y, ch) | |
1484 | ||
1485 | ||
1486 | ||
1593 | =item im_img_8_new(ctx, x, y, ch) | |
1594 | X<im_img_8_new API>X<i_img_8_new API> | |
1595 | ||
1596 | i_img *img = im_img_8_new(aIMCTX, width, height, channels); | |
1487 | 1597 | i_img *img = i_img_8_new(width, height, channels); |
1488 | 1598 | |
1489 | 1599 | Creates a new image object I<x> pixels wide, and I<y> pixels high with |
1493 | 1603 | =for comment |
1494 | 1604 | From: File img8.c |
1495 | 1605 | |
1496 | =item i_img_double_new(i_img_dim x, i_img_dim y, int ch) | |
1497 | ||
1606 | =item im_img_double_new(ctx, x, y, ch) | |
1607 | X<im_img_double_new API>X<i_img_double_new API> | |
1608 | ||
1609 | i_img *img = im_img_double_new(aIMCTX, width, height, channels); | |
1498 | 1610 | i_img *img = i_img_double_new(width, height, channels); |
1499 | 1611 | |
1500 | 1612 | Creates a new double per sample image. |
1501 | 1613 | |
1614 | Also callable as C<i_img_double_new(width, height, channels)>. | |
1615 | ||
1502 | 1616 | |
1503 | 1617 | =for comment |
1504 | 1618 | From: File imgdouble.c |
1505 | 1619 | |
1506 | =item i_img_pal_new(C<x>, C<y>, C<channels>, C<maxpal>) | |
1507 | ||
1508 | ||
1620 | =item im_img_pal_new(ctx, C<x>, C<y>, C<channels>, C<maxpal>) | |
1621 | X<im_img_pal_new API>X<i_img_pal_new API> | |
1622 | ||
1623 | i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size) | |
1509 | 1624 | i_img *img = i_img_pal_new(width, height, channels, max_palette_size) |
1510 | 1625 | |
1511 | 1626 | Creates a new paletted image of the supplied dimensions. |
1514 | 1629 | |
1515 | 1630 | Returns a new image or NULL on failure. |
1516 | 1631 | |
1632 | Also callable as C<i_img_pal_new(width, height, channels, max_palette_size)>. | |
1633 | ||
1517 | 1634 | |
1518 | 1635 | =for comment |
1519 | 1636 | From: File palimg.c |
1520 | 1637 | |
1521 | =item i_sametype(C<im>, C<xsize>, C<ysize>) | |
1522 | ||
1523 | ||
1524 | i_img *img = i_sametype(src, width, height); | |
1525 | ||
1526 | Returns an image of the same type (sample size, channels, paletted/direct). | |
1527 | ||
1528 | For paletted images the palette is copied from the source. | |
1638 | =item i_img_destroy(C<img>) | |
1639 | ||
1640 | i_img_destroy(img) | |
1641 | ||
1642 | Destroy an image object | |
1529 | 1643 | |
1530 | 1644 | |
1531 | 1645 | =for comment |
1532 | 1646 | From: File image.c |
1533 | 1647 | |
1534 | =item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<channels>) | |
1535 | ||
1536 | ||
1537 | i_img *img = i_sametype_chans(src, width, height, channels); | |
1538 | ||
1539 | Returns an image of the same type (sample size). | |
1540 | ||
1541 | For paletted images the equivalent direct type is returned. | |
1542 | ||
1543 | ||
1544 | =for comment | |
1545 | From: File image.c | |
1546 | ||
1547 | =item i_img_destroy(C<img>) | |
1548 | ||
1549 | i_img_destroy(img) | |
1550 | ||
1551 | Destroy an image object | |
1552 | ||
1553 | ||
1554 | =for comment | |
1555 | From: File image.c | |
1556 | ||
1557 | 1648 | |
1558 | 1649 | =back |
1559 | 1650 | |
1561 | 1652 | |
1562 | 1653 | =over |
1563 | 1654 | |
1564 | =item i_img_alloc() | |
1655 | =item im_img_alloc(aIMCTX) | |
1656 | X<im_img_alloc API>X<i_img_alloc API> | |
1657 | ||
1658 | i_img *im = im_img_alloc(aIMCTX); | |
1659 | i_img *im = i_img_alloc(); | |
1565 | 1660 | |
1566 | 1661 | Allocates a new i_img structure. |
1567 | 1662 | |
1590 | 1685 | =for comment |
1591 | 1686 | From: File image.c |
1592 | 1687 | |
1593 | =item i_img_init(C<img>) | |
1688 | =item im_img_init(aIMCTX, image) | |
1689 | X<im_img_init API>X<i_img_init API> | |
1690 | ||
1691 | im_img_init(aIMCTX, im); | |
1692 | i_img_init(im); | |
1594 | 1693 | |
1595 | 1694 | Imager internal initialization of images. |
1596 | 1695 | |
1597 | Currently this does very little, in the future it may be used to | |
1598 | support threads, or color profiles. | |
1696 | See L</im_img_alloc(aIMCTX)> for more information. | |
1599 | 1697 | |
1600 | 1698 | |
1601 | 1699 | =for comment |
1782 | 1880 | =for comment |
1783 | 1881 | From: File log.c |
1784 | 1882 | |
1785 | =item mm_log((level, format, ...)) | |
1786 | ||
1787 | This is the main entry point to logging. Note that the extra set of | |
1788 | parentheses are required due to limitations in C89 macros. | |
1789 | ||
1790 | This will format a string with the current file and line number to the | |
1791 | log file if logging is enabled. | |
1792 | ||
1793 | ||
1794 | =for comment | |
1795 | From: File log.h | |
1883 | ||
1884 | =back | |
1885 | ||
1886 | =head2 Mutex functions | |
1887 | ||
1888 | =over | |
1889 | ||
1890 | =item i_mutex_new() | |
1891 | ||
1892 | i_mutex_t m = i_mutex_new(); | |
1893 | ||
1894 | Create a mutex. | |
1895 | ||
1896 | If a critical section cannot be created for whatever reason, Imager | |
1897 | will abort. | |
1898 | ||
1899 | ||
1900 | =for comment | |
1901 | From: File mutexwin.c | |
1902 | ||
1903 | =item i_mutex_destroy(m) | |
1904 | ||
1905 | i_mutex_destroy(m); | |
1906 | ||
1907 | Destroy a mutex. | |
1908 | ||
1909 | ||
1910 | =for comment | |
1911 | From: File mutexwin.c | |
1912 | ||
1913 | =item i_mutex_lock(m) | |
1914 | ||
1915 | i_mutex_lock(m); | |
1916 | ||
1917 | Lock the mutex, waiting if another thread has the mutex locked. | |
1918 | ||
1919 | ||
1920 | =for comment | |
1921 | From: File mutexwin.c | |
1922 | ||
1923 | =item i_mutex_unlock(m) | |
1924 | ||
1925 | i_mutex_unlock(m); | |
1926 | ||
1927 | Release the mutex. | |
1928 | ||
1929 | The behavior of releasing a mutex you don't hold is unspecified. | |
1930 | ||
1931 | ||
1932 | =for comment | |
1933 | From: File mutexwin.c | |
1796 | 1934 | |
1797 | 1935 | |
1798 | 1936 | =back |
2129 | 2267 | =for comment |
2130 | 2268 | From: File io.c |
2131 | 2269 | |
2270 | =item im_context_refdec(ctx, where) | |
2271 | X<im_context_refdec API> | |
2272 | =section Context objects | |
2273 | ||
2274 | im_context_refdec(aIMCTX, "a description"); | |
2275 | ||
2276 | Remove a reference to the context, releasing it if all references have | |
2277 | been removed. | |
2278 | ||
2279 | ||
2280 | =for comment | |
2281 | From: File context.c | |
2282 | ||
2283 | =item im_context_refinc(ctx, where) | |
2284 | X<im_context_refinc API> | |
2285 | =section Context objects | |
2286 | ||
2287 | im_context_refinc(aIMCTX, "a description"); | |
2288 | ||
2289 | Add a new reference to the context. | |
2290 | ||
2291 | ||
2292 | =for comment | |
2293 | From: File context.c | |
2294 | ||
2295 | =item im_context_slot_get(ctx, slot) | |
2296 | ||
2297 | Retrieve the value previously stored in the given slot of the context | |
2298 | object. | |
2299 | ||
2300 | ||
2301 | =for comment | |
2302 | From: File context.c | |
2303 | ||
2304 | =item im_context_slot_new(destructor) | |
2305 | ||
2306 | Allocate a new context-local-storage slot. | |
2307 | ||
2308 | C<desctructor> will be called when the context is destroyed if the | |
2309 | corresponding slot is non-NULL. | |
2310 | ||
2311 | ||
2312 | =for comment | |
2313 | From: File context.c | |
2314 | ||
2315 | =item im_context_slot_set(slot, value) | |
2316 | ||
2317 | Set the value of a slot. | |
2318 | ||
2319 | Returns true on success. | |
2320 | ||
2321 | Aborts if the slot supplied is invalid. | |
2322 | ||
2323 | If reallocation of slot storage fails, returns false. | |
2324 | ||
2325 | ||
2326 | =for comment | |
2327 | From: File context.c | |
2328 | ||
2329 | =item im_errors(ctx) | |
2330 | ||
2331 | i_errmsg *errors = im_errors(aIMCTX); | |
2332 | i_errmsg *errors = i_errors(); | |
2333 | ||
2334 | Returns a pointer to the first element of an array of error messages, | |
2335 | terminated by a NULL pointer. The highest level message is first. | |
2336 | ||
2337 | Also callable as C<i_errors()>. | |
2338 | ||
2339 | ||
2340 | =for comment | |
2341 | From: File error.c | |
2342 | ||
2343 | =item im_get_context() | |
2344 | ||
2345 | Retrieve the context object for the current thread. | |
2346 | ||
2347 | Inside Imager itself this is just a function pointer, which the | |
2348 | F<Imager.xs> BOOT handler initializes for use within perl. If you're | |
2349 | taking the Imager code and embedding it elsewhere you need to | |
2350 | initialize the C<im_get_context> pointer at some point. | |
2351 | ||
2352 | ||
2353 | =for comment | |
2354 | From: File imext.c | |
2355 | ||
2356 | ||
2357 | ||
2358 | =back | |
2359 | ||
2360 | ||
2361 | =head1 UNDOCUMENTED | |
2362 | ||
2363 | The following API functions are undocumented so far, hopefully this | |
2364 | will change: | |
2365 | ||
2366 | =over | |
2367 | ||
2368 | =item * | |
2369 | ||
2370 | B<im_lhead> | |
2371 | ||
2372 | =item * | |
2373 | ||
2374 | B<im_loog> | |
2375 | ||
2376 | =item * | |
2377 | ||
2378 | B<mm_log> | |
2379 | ||
2132 | 2380 | |
2133 | 2381 | |
2134 | 2382 | =back |
2140 | 2388 | |
2141 | 2389 | =head1 SEE ALSO |
2142 | 2390 | |
2143 | Imager, Imager::ExtUtils, Imager::Inline | |
2391 | Imager, Imager::API, Imager::ExtUtils, Imager::Inline | |
2144 | 2392 | |
2145 | 2393 | =cut |
590 | 590 | |
591 | 591 | =item * |
592 | 592 | |
593 | x, y - either integers giving the co-ordinates of the pixel to set or | |
593 | C<x>, C<y> - either integers giving the co-ordinates of the pixel to set or | |
594 | 594 | array references containing a set of pixels to be set. |
595 | 595 | |
596 | 596 | =item * |
597 | 597 | |
598 | type - the type of color object to return, either C<'8bit'> for | |
599 | Imager::Color objects or C<'float'> for Imager::Color::Float objects. | |
600 | Default: C<'8bit'>. | |
598 | C<type> - the type of color object to return, either C<'8bit'> for | |
599 | L<Imager::Color> objects or C<'float'> for L<Imager::Color::Float> | |
600 | objects. Default: C<'8bit'>. | |
601 | 601 | |
602 | 602 | =back |
603 | 603 | |
607 | 607 | |
608 | 608 | If a supplied co-ordinate is outside the image then C<undef> is |
609 | 609 | returned for the pixel. |
610 | ||
611 | Each color is returned as an L<Imager::Color> object or as an | |
612 | L<Imager::Color::Float> object if C<type> is set to C<"float">. | |
610 | 613 | |
611 | 614 | Possible errors conditions include: |
612 | 615 |
1377 | 1377 | |
1378 | 1378 | =back |
1379 | 1379 | |
1380 | Each of these tags has a corresponding C< I<base-tag-name>_compressed | |
1380 | Each of these tags has a corresponding C<< I<base-tag-name>_compressed | |
1381 | 1381 | >> tag, eg. C<png_comment_compressed>. When reading, if the PNG chunk |
1382 | 1382 | is compressed this tag will be set to 1, but is otherwise unset. When |
1383 | 1383 | writing, Imager will honor the compression tag if set and non-zero, |
1435 | 1435 | |
1436 | 1436 | =item * |
1437 | 1437 | |
1438 | C<tags, png_gamma>C<png_gamma> - the gamma of the image. This value is | |
1438 | X<tags, png_gamma>C<png_gamma> - the gamma of the image. This value is | |
1439 | 1439 | not currently used by Imager when processing the image, but this may |
1440 | 1440 | change in the future. |
1441 | 1441 | |
1460 | 1460 | |
1461 | 1461 | =item * |
1462 | 1462 | |
1463 | X<tags, png_time>X<png_time> - the creation time of the file formatted | |
1463 | X<tags, png_time>C<png_time> - the creation time of the file formatted | |
1464 | 1464 | as C<< I<year>-I<month>-I<day>TI<hour>:I<minute>:I<second> >>. This |
1465 | 1465 | is stored as time data structure in the file, not a string. If you |
1466 | 1466 | set C<png_time> and it cannot be parsed as above, writing the PNG file |
222 | 222 | |
223 | 223 | fountain (similar to gradients in paint software) |
224 | 224 | |
225 | =item * | |
226 | ||
227 | image - fill with an image, possibly transformed | |
228 | ||
229 | =item * | |
230 | ||
231 | opacity - a lower opacity version of some other fill | |
232 | ||
225 | 233 | =back |
226 | 234 | |
227 | 235 | =head1 Common options |
235 | 243 | |
236 | 244 | =back |
237 | 245 | |
238 | In general colors can be specified as Imager::Color or | |
239 | Imager::Color::Float objects. The fill object will typically store | |
246 | In general colors can be specified as L<Imager::Color> or | |
247 | L<Imager::Color::Float> objects. The fill object will typically store | |
240 | 248 | both types and convert from one to the other. If a fill takes 2 color |
241 | 249 | objects they should have the same type. |
242 | 250 | |
243 | 251 | =head2 Solid fills |
244 | 252 | |
245 | my $fill = Imager::Fill->new(solid=>$color, $combine =>$combine) | |
253 | my $fill = Imager::Fill->new(solid=>$color, combine =>$combine) | |
246 | 254 | |
247 | 255 | Creates a solid fill, the only required parameter is C<solid> which |
248 | 256 | should be the color to fill with. |
249 | 257 | |
258 | A translucent red fill: | |
259 | ||
260 | my $red = Imager::Fill->new(solid => "FF000080", combine => "normal"); | |
261 | ||
250 | 262 | =head2 Hatched fills |
251 | 263 | |
252 | 264 | my $fill = Imager::Fill->new(hatch=>$type, fg=>$fgcolor, bg=>$bgcolor, |
256 | 268 | |
257 | 269 | =over |
258 | 270 | |
259 | =item hatch | |
260 | ||
261 | The type of hatch to perform, this can either be the numeric index of | |
262 | the hatch (not recommended), the symbolic name of the hatch, or an | |
263 | array of 8 integers which specify the pattern of the hatch. | |
271 | =item * | |
272 | ||
273 | C<hatch> - The type of hatch to perform, this can either be the | |
274 | numeric index of the hatch (not recommended), the symbolic name of the | |
275 | hatch, or an array of 8 integers which specify the pattern of the | |
276 | hatch. | |
264 | 277 | |
265 | 278 | Hatches are represented as cells 8x8 arrays of bits, which limits their |
266 | 279 | complexity. |
340 | 353 | |
341 | 354 | =back |
342 | 355 | |
356 | A blue and white 4-pixel check patten: | |
357 | ||
358 | my $fill = Imager::Fill->new(hatch => "check2x2", fg => "blue"); | |
359 | ||
343 | 360 | You can call Imager::Fill->hatches for a list of hatch names. |
344 | 361 | |
345 | 362 | =head2 Fountain fills |
354 | 371 | you are drawing, and the fountain parameter supplies the fill type, |
355 | 372 | and is required. |
356 | 373 | |
374 | A radial fill from white to transparent centered on (50, 50) with a 50 | |
375 | pixel radius: | |
376 | ||
377 | use Imager::Fountain; | |
378 | my $segs = Imager::Fountain->simple(colors => [ "FFFFFF", "FFFFFF00" ], | |
379 | positions => [ 0, 1 ]); | |
380 | my $fill = Imager::Fill->new(fountain => "radial", segments => $segs, | |
381 | xa => 50, ya => 50, xb => 0, yb => 50, | |
382 | combine => "normal"); | |
383 | ||
384 | ||
357 | 385 | =head2 Image Fills |
358 | 386 | |
359 | 387 | my $fill = Imager::Fill->new(image=>$src, xoff=>$xoff, yoff=>$yoff, |
360 | matrix=>$matrix, $combine); | |
388 | matrix=>$matrix, combine => $combine); | |
361 | 389 | |
362 | 390 | Fills the given image with a tiled version of the given image. The |
363 | 391 | first non-zero value of C<xoff> or C<yoff> will provide an offset |
370 | 398 | |
371 | 399 | The matrix parameter will significantly slow down the fill. |
372 | 400 | |
401 | # some image to act as a texture | |
402 | my $txim = Imager->new(...); | |
403 | ||
404 | # simple tiling | |
405 | my $fill = Imager::Fill->new(image => $txim); | |
406 | ||
407 | # tile with a vertical offset | |
408 | my $fill = Imager::Fill->new(image => $txim, yoff => 10); | |
409 | ||
410 | # tile with a horizontal offset | |
411 | my $fill = Imager::Fill->new(image => $txim, xoff => 10); | |
412 | ||
413 | # rotated | |
414 | use Imager::Matrix2d; | |
415 | my $fill = Imager::Fill->new(image => $txim, | |
416 | matrix => Imager::Matrix2d->rotate(degrees => 20)); | |
417 | ||
373 | 418 | =head2 Opacity modification fill |
374 | 419 | |
375 | 420 | my $fill = Imager::Fill->new(type => "opacity", |
400 | 445 | |
401 | 446 | The source fills combine mode is used. |
402 | 447 | |
448 | my $hatch = Imager::Fill->new(hatch => "check4x4", combine => "normal"); | |
449 | my $fill = Imager::Fill->new(type => "opacity", other => $hatch); | |
450 | ||
403 | 451 | =head1 OTHER METHODS |
404 | 452 | |
405 | 453 | =over |
0 | package Imager::Font::Test; | |
1 | use strict; | |
2 | ||
3 | use base 'Imager::Font'; | |
4 | ||
5 | sub new { | |
6 | my ($class, %opts) = @_; | |
7 | ||
8 | bless \%opts, shift; | |
9 | } | |
10 | ||
11 | sub _draw { | |
12 | my ($self, %input) = @_; | |
13 | ||
14 | my $text = $input{string}; | |
15 | ||
16 | my $ppn = int($input{size} * 0.5 + 0.5); | |
17 | my $desc = int($input{size} * 0.3 + 0.5); | |
18 | my $asc = $input{size} - $desc; | |
19 | my $width = $ppn * length $text; | |
20 | my $x = $input{x}; | |
21 | my $y = $input{'y'}; | |
22 | $input{align} and $y -= $asc; | |
23 | ||
24 | $input{image}->box(color => $input{color}, xmin => $x, ymin => $y, | |
25 | xmax => $x + $width-1, ymax => $y + $input{size} - 1); | |
26 | ||
27 | return 1; | |
28 | } | |
29 | ||
30 | sub _bounding_box { | |
31 | my ($self, %input) = @_; | |
32 | ||
33 | my $text = $input{string}; | |
34 | ||
35 | my $ppn = int($input{size} * 0.5 + 0.5); | |
36 | my $desc = int($input{size} * 0.3 + 0.5); | |
37 | my $asc = $input{size} - $desc; | |
38 | ||
39 | return ( 0, -$desc, $ppn * length $text, $asc, -$desc, $asc, $ppn * length $text, 0 ); | |
40 | } | |
41 | ||
42 | sub has_chars { | |
43 | my ($self, %input) = @_; | |
44 | ||
45 | my $text = $input{string}; | |
46 | defined $text | |
47 | or return Imager->_set_error("has_chars: No string parameter supplied"); | |
48 | ||
49 | return (1) x length $text; | |
50 | } | |
51 | ||
52 | sub face_name { | |
53 | "test"; | |
54 | } | |
55 | ||
56 | sub glyph_names { | |
57 | my ($self, %input) = @_; | |
58 | ||
59 | my $text = $input{string}; | |
60 | defined $text | |
61 | or return Imager->_set_error("glyph_names: No string parameter supplied"); | |
62 | ||
63 | return (1) x length $text; | |
64 | } | |
65 | ||
66 | 1; | |
67 | ||
68 | =head1 NAME' | |
69 | ||
70 | Imager::Font::Test - font driver producing consistent output for tests. | |
71 | ||
72 | =head1 SYNOPSIS | |
73 | ||
74 | my $font = Imager::Font::Test->new; | |
75 | ||
76 | # use $font where you use other fonts | |
77 | ||
78 | =head1 DESCRIPTION | |
79 | ||
80 | Imager::Font::Test is intended to produce consistent output without | |
81 | being subject to the inconsistent output produced by different | |
82 | versions of font libraries. | |
83 | ||
84 | The output is simple box for the whole string. | |
85 | ||
86 | =head1 AUTHOR | |
87 | ||
88 | Tony Cook <tonyc@cpan.org> | |
89 | ||
90 | =cut | |
91 |
2 | 2 | use vars qw(@ISA $VERSION); |
3 | 3 | @ISA = qw(Imager::Font); |
4 | 4 | |
5 | $VERSION = "1.011"; | |
5 | $VERSION = "1.012"; | |
6 | 6 | |
7 | 7 | *_first = \&Imager::Font::_first; |
8 | 8 | |
43 | 43 | my $self = shift; |
44 | 44 | my %input = @_; |
45 | 45 | |
46 | # note that the string length parameter is ignored and calculated in | |
47 | # XS with SvPV(), since we want the number of bytes rather than the | |
48 | # number of characters, which is what we'd get in perl for a UTF8 | |
49 | # encoded string in 5.6 and later | |
50 | ||
51 | 46 | if ( exists $input{channel} ) { |
52 | 47 | Imager::i_tt_cp($self->{id},$input{image}{IMG}, |
53 | 48 | $input{'x'}, $input{'y'}, $input{channel}, $input{size}, |
54 | $input{string}, length($input{string}),$input{aa}, | |
49 | $input{string}, $input{aa}, | |
55 | 50 | $input{utf8}, $input{align}); |
56 | 51 | } else { |
57 | 52 | Imager::i_tt_text($self->{id}, $input{image}{IMG}, |
58 | 53 | $input{'x'}, $input{'y'}, $input{color}, |
59 | 54 | $input{size}, $input{string}, |
60 | length($input{string}), $input{aa}, $input{utf8}, | |
55 | $input{aa}, $input{utf8}, | |
61 | 56 | $input{align}); |
62 | 57 | } |
63 | 58 | } |
65 | 60 | sub _bounding_box { |
66 | 61 | my $self = shift; |
67 | 62 | my %input = @_; |
68 | return Imager::i_tt_bbox($self->{id}, $input{size}, | |
69 | $input{string}, length($input{string}), | |
70 | $input{utf8}); | |
63 | my @result = | |
64 | Imager::i_tt_bbox($self->{id}, $input{size}, $input{string}, $input{utf8}); | |
65 | unless (@result) { | |
66 | Imager->_set_error(Imager->_error_as_msg); | |
67 | return; | |
68 | } | |
69 | ||
70 | return @result; | |
71 | 71 | } |
72 | 72 | |
73 | 73 | sub utf8 { 1 } |
76 | 76 | sub has_chars { |
77 | 77 | my ($self, %hsh) = @_; |
78 | 78 | |
79 | unless (defined $hsh{string} && length $hsh{string}) { | |
79 | unless (defined $hsh{string}) { | |
80 | 80 | $Imager::ERRSTR = "No string supplied to \$font->has_chars()"; |
81 | 81 | return; |
82 | 82 | } |
83 | return Imager::i_tt_has_chars($self->{id}, $hsh{string}, | |
84 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
83 | if (wantarray) { | |
84 | my @result = Imager::i_tt_has_chars($self->{id}, $hsh{string}, | |
85 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
86 | unless (@result) { | |
87 | Imager->_set_error(Imager->_error_as_msg); | |
88 | return; | |
89 | } | |
90 | return @result; | |
91 | } | |
92 | else { | |
93 | my $result = Imager::i_tt_has_chars($self->{id}, $hsh{string}, | |
94 | _first($hsh{'utf8'}, $self->{utf8}, 0)); | |
95 | unless (defined $result) { | |
96 | Imager->_set_error(Imager->_error_as_msg); | |
97 | return; | |
98 | } | |
99 | ||
100 | return $result; | |
101 | } | |
85 | 102 | } |
86 | 103 | |
87 | 104 | sub face_name { |
88 | 105 | my ($self) = @_; |
89 | 106 | |
90 | 107 | Imager::i_tt_face_name($self->{id}); |
108 | } | |
109 | ||
110 | sub can_glyph_names { | |
111 | 1; | |
91 | 112 | } |
92 | 113 | |
93 | 114 | sub glyph_names { |
98 | 119 | or return Imager->_set_error("no string parameter passed to glyph_names"); |
99 | 120 | my $utf8 = _first($input{utf8} || 0); |
100 | 121 | |
101 | Imager::i_tt_glyph_name($self->{id}, $string, $utf8); | |
122 | my @names = Imager::i_tt_glyph_name($self->{id}, $string, $utf8); | |
123 | unless (@names) { | |
124 | Imager->_set_error(Imager->_error_as_msg); | |
125 | return; | |
126 | } | |
127 | ||
128 | return @names; | |
102 | 129 | } |
103 | 130 | |
104 | 131 | 1; |
3 | 3 | use strict; |
4 | 4 | use vars qw($VERSION); |
5 | 5 | |
6 | $VERSION = "1.035"; | |
6 | $VERSION = "1.037"; | |
7 | 7 | |
8 | 8 | # the aim here is that we can: |
9 | 9 | # - add file based types in one place: here |
248 | 248 | $input{sizew} = _first($input{sizew}, $self->{sizew}, 0); |
249 | 249 | $input{utf8} = _first($input{utf8}, $self->{utf8}, 0); |
250 | 250 | |
251 | my @box = $self->_bounding_box(%input); | |
251 | my @box = $self->_bounding_box(%input) | |
252 | or return; | |
252 | 253 | |
253 | 254 | if (wantarray) { |
254 | 255 | if(@box && exists $input{'x'} and exists $input{'y'}) { |
543 | 544 | |
544 | 545 | =back |
545 | 546 | |
547 | Returns the new font object on success. Returns C<undef> on failure | |
548 | and sets an error message readable with C<< Imager->errstr >>. | |
549 | ||
546 | 550 | =item bounding_box() |
547 | 551 | |
548 | 552 | Returns the bounding box for the specified string. Example: |
665 | 669 | |
666 | 670 | =back |
667 | 671 | |
672 | On success returns either the list of bounds, or a bounding box object | |
673 | object in scalar context. Returns an empty list or C<undef> on | |
674 | failure and sets an error message readable with C<< Imager->errstr >>. | |
675 | ||
676 | The transformation matrix set by L</transform()> has no effect on the | |
677 | result of this method - the bounds of the untransformed text is | |
678 | returned. | |
679 | ||
668 | 680 | =item string() |
669 | 681 | |
670 | 682 | The $img->string(...) method is now documented in |
746 | 758 | |
747 | 759 | =back |
748 | 760 | |
749 | Returns a list specifying the bounds of the drawn text. | |
761 | Returns a list specifying the bounds of the drawn text on success. | |
762 | Returns an empty list on failure, if an C<image> parameter was | |
763 | supplied the error message can be read with C<< $image->errstr >>, | |
764 | otherwise it's available as C<< Imager->errstr >>. | |
750 | 765 | |
751 | 766 | =item dpi() |
752 | 767 | |
774 | 789 | |
775 | 790 | =back |
776 | 791 | |
777 | Returns a list containing the previous C<xdpi>, C<ydpi> values. | |
792 | Returns a list containing the previous C<xdpi>, C<ydpi> values on | |
793 | success. Returns an empty list on failure, with an error message | |
794 | returned in C<< Imager->errstr >>. | |
778 | 795 | |
779 | 796 | =item transform() |
780 | 797 | |
800 | 817 | Note that the transformation is done in font co-ordinates where y |
801 | 818 | increases as you move up, not image co-ordinates where y decreases as |
802 | 819 | you move up. |
820 | ||
821 | C<transform()> has no effect on the results of L</bounding_box()>. | |
822 | ||
823 | Returns true on success. Returns false on failure with the cause | |
824 | readable from C<< Imager->errstr >>. | |
803 | 825 | |
804 | 826 | =item has_chars(string=>$text) |
805 | 827 | |
812 | 834 | |
813 | 835 | Not all fonts support this method (use $font->can("has_chars") to |
814 | 836 | check.) |
837 | ||
838 | On error, returns an empty list or undef in scalar context, and sets | |
839 | an error message readable with C<< Imager->errstr >>. | |
815 | 840 | |
816 | 841 | =over |
817 | 842 | |
833 | 858 | =item face_name() |
834 | 859 | |
835 | 860 | Returns the internal name of the face. Not all font types support |
836 | this method yet. | |
861 | this method yet, so you should check with C<< $font->can("face_name") | |
862 | >> before calling C<face_name>. | |
837 | 863 | |
838 | 864 | =item glyph_names(string=>$string [, utf8=>$utf8 ][, reliable_only=>0 ] ); |
839 | 865 | |
855 | 881 | Both FreeType 1.x and 2.x allow support for glyph names to not be |
856 | 882 | included. |
857 | 883 | |
884 | If the supplied C<string> is marked as UTF-8 or the C<utf8> parameter | |
885 | is true and the supplied string does not contain valid UTF-8, returns | |
886 | an empty string and set an error message readable from C<< | |
887 | Imager->errstr >>, | |
888 | ||
889 | =item can_glyph_names() | |
890 | ||
891 | As a class method, returns true if the underlying library supports | |
892 | returning glyph names. | |
893 | ||
894 | As an object method, returns true if the supplied font supports | |
895 | returning glyph names. | |
896 | ||
858 | 897 | =item draw |
859 | 898 | |
860 | 899 | This is used by Imager's string() method to implement drawing text. |
966 | 1005 | # at this point $x is has the UTF-8 flag set, but has 5 characters, |
967 | 1006 | # none, of which is the constructed UTF-8 character |
968 | 1007 | |
969 | The test script t/t38ft2font.t has a small example of this after the | |
1008 | The test script t/t38ft2font.t has a small example of this after the | |
970 | 1009 | comment: |
971 | 1010 | |
972 | 1011 | # an attempt using emulation of UTF-8 |
49 | 49 | L<Imager::Files/"I/O Callbacks"> for details on the behavior of |
50 | 50 | the callbacks. |
51 | 51 | |
52 | =item new_fh($fh) | |
53 | ||
54 | Create a new I/O layer based on a perl file handle. | |
55 | ||
52 | 56 | =item new_bufchain() |
53 | 57 | |
54 | 58 | Create a new C<bufchain> based I/O layer. This accumulates the file |
0 | =for stopwords freetype MinGW dfont Redhat SDK IFD GDI TTF preprocessor Redhat-like | |
1 | ||
2 | =head1 NAME | |
3 | ||
4 | Imager::Install - installation notes for Imager | |
5 | ||
6 | =head1 SYNOPSIS | |
7 | ||
8 | perl Makefile.PL | |
9 | make | |
10 | make test | |
11 | make install | |
12 | ||
13 | =head1 CONFIGURATION | |
14 | ||
15 | Assuming you have all of your required libraries in the places Imager | |
16 | looks, you should be able to use the standard mantra: | |
17 | ||
18 | perl Makefile.PL | |
19 | make | |
20 | make test | |
21 | make install | |
22 | ||
23 | to install Imager. | |
24 | ||
25 | If you've installed libraries in places Imager doesn't look, you can | |
26 | supply extra locations either with command-line options: | |
27 | ||
28 | perl Makefile.PL --libpath=/home/tony/local/lib --incpath=/home/tony/local/include | |
29 | ||
30 | or with environment variables: | |
31 | ||
32 | export IM_LIBPATH=/home/tony/local/lib IM_INCPATH=/home/tony/local/include | |
33 | perl Makefile.PL | |
34 | ||
35 | Imager's F<Makefile.PL> produces an epilogue indicating which | |
36 | libraries have and haven't been found, for example: | |
37 | ||
38 | Libraries found: | |
39 | FT2 | |
40 | GIF | |
41 | JPEG | |
42 | PNG | |
43 | T1 | |
44 | TIFF | |
45 | Libraries *not* found: | |
46 | Win32 | |
47 | ||
48 | If a library you expect to be found isn't on this list, use the | |
49 | C<--verbose> or C<-v> option to produce way too much information from | |
50 | Imager's search for the libraries: | |
51 | ||
52 | perl Makefile.PL -v | |
53 | ||
54 | If you can't resolve this, then run | |
55 | ||
56 | perl errep.perl | |
57 | ||
58 | and include the (large) generated F<report.txt> in your email to: | |
59 | ||
60 | bug-Imager@rt.cpan.org | |
61 | ||
62 | There are other options used to configure how Imager is built: | |
63 | ||
64 | =over | |
65 | ||
66 | =item C<--nolog> | |
67 | ||
68 | build Imager without logging support. This will speed up Imager a | |
69 | little. You can also remove logging by setting the C<IMAGER_NOLOG> | |
70 | environment variable to a true value. | |
71 | ||
72 | =item C<--coverage> | |
73 | ||
74 | used to build Imager for C<gcov> coverage testing. This is intended | |
75 | for development and also requires options supplied to C<make>. | |
76 | ||
77 | =item C<--assert> | |
78 | ||
79 | build Imager with assertions enabled. | |
80 | ||
81 | =item C<--tracecontext> | |
82 | ||
83 | build Imager to trace context object management to C<stderr> for | |
84 | debugging. | |
85 | ||
86 | =back | |
87 | ||
88 | =head2 Build time environment variables | |
89 | X<build time environment variables> | |
90 | ||
91 | =over | |
92 | ||
93 | =item * | |
94 | ||
95 | X<< C<IMAGER_NOLOG> >>C<IMAGER_NOLOG> - build Imager with logging disabled. | |
96 | ||
97 | =item * | |
98 | ||
99 | X<< C<IMAGER_DEBUG_MALLOC> >>C<IMAGER_DEBUG_MALLOC> - build Imager with it's | |
100 | debug malloc wrappers. This is I<not> compatible with threaded code. | |
101 | ||
102 | =item * | |
103 | ||
104 | X<< C<IM_INCPATH> >>C<IM_INCPATH> - equivalent to C<--incpath>. | |
105 | ||
106 | =item * | |
107 | ||
108 | X<< C<IM_LIBPATH> >>C<IM_LIBPATH> - equivalent to C<--libpath>. | |
109 | ||
110 | =item * | |
111 | ||
112 | X<< C<IM_VERBOSE> >>C<IM_VERBOSE> - equivalent to C<--verbose> | |
113 | ||
114 | =item * | |
115 | ||
116 | X<< C<IM_CFLAGS> >>C<IM_CFLAGS> - extra C compiler flags. | |
117 | ||
118 | =item * | |
119 | ||
120 | X<< C<IM_LFLAGS> >>C<IM_LFLAGS> - extra linker flags. | |
121 | ||
122 | =item * | |
123 | ||
124 | X<< C<IM_DFLAGS> >>C<IM_DFLAGS> - extra preprocessor flags. | |
125 | ||
126 | =back | |
127 | ||
128 | =head1 EXTERNAL LIBRARIES | |
129 | ||
130 | Some of the file format and font modules included with Imager use | |
131 | external libraries, which should be installed before you try to | |
132 | install Imager itself. | |
133 | ||
134 | If you don't have the libraries installed then Imager itself will | |
135 | install successfully, but the file format or font support module won't | |
136 | be. | |
137 | ||
138 | Preferably the latest version of each library should be used, simple | |
139 | because it has the latest security fixes. | |
140 | ||
141 | =head2 PNG - C<libpng> | |
142 | ||
143 | X<< C<libpng> >>L<Imager::File::PNG> uses L<< C<libpng> | |
144 | |http://www.libpng.org/pub/png/libpng.html >> for PNG image file | |
145 | support. | |
146 | ||
147 | Debian package: C<libpng12-dev> | |
148 | ||
149 | Redhat package: C<libpng-devel> | |
150 | ||
151 | =head2 TIFF - C<libtiff> | |
152 | ||
153 | X<< C<libtiff> >>L<Imager::File::TIFF> uses | |
154 | L<< C<libtiff> |http://www.remotesensing.org/libtiff/ >> for GIF image file | |
155 | support. | |
156 | ||
157 | Version 3.6.0 or later is required to avoid an exploit with infinite | |
158 | IFD loops, though it's possible some distributions have applied the | |
159 | fix to older versions as a security fix. | |
160 | ||
161 | Version 3.9.0 is rejected during the probe process due to a serious | |
162 | bug, fixed in 3.9.1. | |
163 | ||
164 | Debian package: C<libtiff4-dev> | |
165 | ||
166 | Redhat package: C<libtiff-devel> | |
167 | ||
168 | =head2 GIF - C<libgif> | |
169 | ||
170 | X<< C<libgif> >>L<Imager::File::GIF> uses | |
171 | L<< C<libgif> |http://sourceforge.net/projects/giflib/ >> for GIF image file | |
172 | support. | |
173 | ||
174 | C<libgif> releases 4.2.0 and 5.0.0 are specifically not supported, due | |
175 | to bugs in those versions. | |
176 | ||
177 | Release 4.1.4 or later should be used. | |
178 | ||
179 | C<giflib> 3 is no longer supported. | |
180 | ||
181 | C<libungif> is no longer supported as an alternative. | |
182 | ||
183 | Debian package: C<libgif-dev> | |
184 | ||
185 | Redhat package: C<giflib-devel> | |
186 | ||
187 | =head2 JPEG - C<libjpeg> | |
188 | ||
189 | L<Imager::File::JPEG> uses L<< C<libjpeg> |http://www.ijg.org/ >> for JPEG | |
190 | image file support. | |
191 | ||
192 | You may also use | |
193 | L<< C<libjpeg-turbo> |http://sourceforge.net/projects/libjpeg-turbo/ >>. | |
194 | ||
195 | To install older releases of C<libjpeg> from source, you'll need to | |
196 | run: | |
197 | ||
198 | make install-lib | |
199 | ||
200 | to install the libraries. C<make install> only installs the program | |
201 | binaries. | |
202 | ||
203 | Redhat package: C<libjpeg-devel> | |
204 | ||
205 | Debian package: C<libjpeg8-dev> | |
206 | ||
207 | =head2 Freetype 2.x - C<libfreetype> | |
208 | ||
209 | L<Imager::Font::FT2> uses L<< Freetype 2 | |
210 | (C<libfreetype>)|http://www.freetype.org/ >> for font support, supporting | |
211 | too many font formats to mention here. | |
212 | ||
213 | This is the recommended library to use for font support. | |
214 | ||
215 | Debian package: C<libfreetype6-dev> | |
216 | ||
217 | Redhat package: C<freetype-devel> | |
218 | ||
219 | =head2 Win32 GDI fonts | |
220 | ||
221 | L<Imager::Font::W32> uses L<Win32 | |
222 | GDI|http://msdn.microsoft.com/en-us/library/dd145203%28v=vs.85%29.aspx> | |
223 | to render text using installed Windows fonts. | |
224 | ||
225 | This requires Win32 SDK headers and libraries, and is only expected to | |
226 | work on native Win32 or Cygwin. | |
227 | ||
228 | For this to work under Cygwin, install the C<w32api-headers> and | |
229 | C<w32api-runtime> packages. | |
230 | ||
231 | =head2 C<t1lib> | |
232 | ||
233 | L<Imager::Font::T1> uses L<< C<t1lib> |http://www.t1lib.org/ >> for | |
234 | font support, supporting Postscript Type 1 fonts only. | |
235 | ||
236 | Debian package: C<libt1-dev> | |
237 | ||
238 | Redhat package: C<t1lib-devel> | |
239 | ||
240 | =head2 Freetype 1.x - C<libttf> | |
241 | ||
242 | Imager uses L<< Freetype 1 (C<libttf>)|http://www.freetype.org/ >> if | |
243 | available for font support, supporting TTF fonts only. | |
244 | ||
245 | Freetype 1.x is essentially unsupported and shouldn't be used for new | |
246 | code. | |
247 | ||
248 | =head1 PLATFORM SPECIFICS | |
249 | ||
250 | =head2 Linux | |
251 | ||
252 | Several distributions include an Imager package, but they are | |
253 | typically several releases behind due to the nature of release cycles. | |
254 | ||
255 | Imager typically supports the external libraries as packaged with any | |
256 | supported release of Linux. | |
257 | ||
258 | =head3 Debian | |
259 | ||
260 | To install the libraries used by Imager under Debian (or Ubuntu), run | |
261 | as root (or with sudo): | |
262 | ||
263 | apt-get install libgif-dev libjpeg8-dev libtiff4-dev libpng12-dev libfreetype6-dev | |
264 | ||
265 | You may also need to install development tools: | |
266 | ||
267 | apt-get install build-essential | |
268 | ||
269 | =head3 Redhat | |
270 | ||
271 | To install the libraries used by Imager under Redhat and related Linux | |
272 | distributions, run as root (or sudo): | |
273 | ||
274 | yum install giflib-devel libjpeg-devel libtiff-devel libpng-devel freetype-devel | |
275 | ||
276 | To install the development tools needed: | |
277 | ||
278 | yum install gcc | |
279 | ||
280 | (which appears to be enough on a base Redhat-like install) or the more | |
281 | commonly recommended recipe: | |
282 | ||
283 | yum groupinstall "Development Tools" | |
284 | ||
285 | which is massive overkill. | |
286 | ||
287 | =head2 Mac OS X | |
288 | ||
289 | =head3 Building libraries | |
290 | ||
291 | The default perl build in Snow Leopard and Lion is a fat binary, and | |
292 | default builds of C<giflib>, C<libpng> and C<libjpeg> (and maybe other | |
293 | libraries) will produce link failures. | |
294 | ||
295 | To avoid this you need to supply a C<CFLAGS> parameter to the | |
296 | library's configure script, but since the C<-arch> flag conflicts with | |
297 | the options used to build the dependency files, you need to supply | |
298 | another flag to disable dependency tracking. | |
299 | ||
300 | Snow Leopard fat binaries include C<i386>, C<x86_64> and C<PPC> | |
301 | objects, hence you would run configure like: | |
302 | ||
303 | ./configure --disable-dependency-tracking CFLAGS='-arch x86_64 -arch i386 -arch ppc' | |
304 | ||
305 | Lion doesn't support C<PPC>, so there you run configure like: | |
306 | ||
307 | ./configure --disable-dependency-tracking CFLAGS='-arch x86_64 -arch i386' | |
308 | ||
309 | For C<libgif> you might also want to supply the C<--without-x> option: | |
310 | ||
311 | ./configure --disable-dependency-tracking --without-x CFLAGS='-arch x86_64 -arch i386' | |
312 | ||
313 | If you copy library files into place manually, you may need to run | |
314 | C<ranlib> on them in their new location: | |
315 | ||
316 | ranlib /usr/local/lib/libgif.a | |
317 | ||
318 | =head3 Macintosh C<dfont> and suitcase font support | |
319 | ||
320 | Through Freetype 2.1, Imager can use Macintosh C<DFON> (C<.dfont>) | |
321 | fonts and suitcase font files. | |
322 | ||
323 | If you want to be able to use more than just the first face in the | |
324 | font file though, you will need to configure C<freetype2> with the | |
325 | --with-old-mac-fonts option: | |
326 | ||
327 | ./configure --with-old-mac-fonts | |
328 | ||
329 | You can use the index option to get to the other font faces in the | |
330 | file: | |
331 | ||
332 | # get the second face from $file | |
333 | my $font = Imager::Font->new(file=>$file, index=>1) | |
334 | or die Imager->errstr; | |
335 | ||
336 | If you're using a suitcase font, you will also need to force the use | |
337 | of Freetype 2 with the type argument: | |
338 | ||
339 | my $font = Imager::Font->new(file=>$suitcase, type=>'ft2', index=>$index) | |
340 | or die Imager->errstr; | |
341 | ||
342 | =head2 Microsoft Windows | |
343 | ||
344 | The simplest way to install the libraries used by Imager is to install | |
345 | L<Strawberry perl|http://strawberryperl.com/>. | |
346 | ||
347 | You can then use either the bundled Imager, or install from CPAN. | |
348 | ||
349 | If you get errors from your make tool, make sure you're using the same | |
350 | make that was used to build your perl - C<nmake> for Visual C/C++ and | |
351 | C<dmake> for MinGW, run: | |
352 | ||
353 | perl -V:make | |
354 | ||
355 | to see which make was used to build your perl. | |
356 | ||
357 | =head2 Cygwin | |
358 | ||
359 | To build Imager with as much library support as possible on Cygwin, | |
360 | install the following packages: | |
361 | ||
362 | libjpeg-devel libpng-devel libgif-devel libtiff-devel | |
363 | libfreetype-devel t1lib-devel w32api-headers w32api-runtime | |
364 | ||
365 | If you see an error under cygwin during testing along the lines of: | |
366 | ||
367 | C:\cygwin\bin\perl.exe: *** unable to remap C:\cygwin\...some dll to the | |
368 | same address as parent (0x...) != 0x.... | |
369 | ||
370 | you will need to install the cygwin C<rebase> package and run: | |
371 | ||
372 | $ rebaseall -v | |
373 | ||
374 | or possibly, just: | |
375 | ||
376 | $ perlrebase | |
377 | ||
378 | will fix the problem. | |
379 | ||
380 | =head1 Other issues | |
381 | ||
382 | =head2 Freetype 1.x vs Freetype 2.x | |
383 | ||
384 | Freetype 1.x is no longer recommended, is no longer supported | |
385 | upstream, and receives only limited updates in Imager. | |
386 | ||
387 | These two libraries have some conflicting include file names, but as | |
388 | long as you don't put the Freetype 2.x F<freetype.h> directory in the | |
389 | include path it should all work. | |
390 | ||
391 | Put the directory containing F<ft2build.h> in the include path, but | |
392 | not the directory containing the freetype 2.x F<freetype.h>. | |
393 | ||
394 | If you see compilation errors from font.c you've probably made the | |
395 | mistake of putting the Freetype 2.x F<freetype.h> directory into the | |
396 | include path. | |
397 | ||
398 | To see which directories should be in the include path, try: | |
399 | ||
400 | freetype-config --cflags | |
401 | ||
402 | Ideally, C<freetype-config> should be in the PATH when building Imager | |
403 | with freetype 2.x support, in which case L<Imager::Font::FT2> can | |
404 | configure itself. | |
405 | ||
406 | =cut⏎ |
76 | 76 | JPEG 8, 12 8 |
77 | 77 | PBM 1 1 |
78 | 78 | PGM/PPM 1-16 read any, writes 8, 16 |
79 | PNG 1, 2, 4, 8, 16 8 | |
79 | PNG 1, 2, 4, 8, 16 1, 2, 4, 8 paletted | |
80 | 1, 8, 16 gray (1 for is_monochrome() images) | |
81 | 8, 16 RGB | |
80 | 82 | RAW 8 |
81 | 83 | SGI 8, 16 8, 16 |
82 | 84 | TGA 8 8 |
0 | 0 | package Imager::Test; |
1 | 1 | use strict; |
2 | use Test::More; | |
2 | 3 | use Test::Builder; |
3 | 4 | require Exporter; |
4 | 5 | use vars qw(@ISA @EXPORT_OK $VERSION); |
5 | use Carp qw(croak); | |
6 | ||
7 | $VERSION = "1.000"; | |
6 | use Carp qw(croak carp); | |
7 | use Config; | |
8 | ||
9 | $VERSION = "1.002"; | |
8 | 10 | |
9 | 11 | @ISA = qw(Exporter); |
10 | 12 | @EXPORT_OK = |
34 | 36 | mask_tests |
35 | 37 | test_colorf_gpix |
36 | 38 | test_color_gpix |
37 | test_colorf_glin); | |
39 | test_colorf_glin | |
40 | can_test_threads | |
41 | std_font_tests | |
42 | std_font_test_count | |
43 | ); | |
38 | 44 | |
39 | 45 | sub diff_text_with_nul { |
40 | 46 | my ($desc, $text1, $text2, @params) = @_; |
727 | 733 | |
728 | 734 | } |
729 | 735 | |
736 | sub std_font_test_count { | |
737 | return 21; | |
738 | } | |
739 | ||
740 | sub std_font_tests { | |
741 | my ($opts) = @_; | |
742 | ||
743 | my $font = $opts->{font} | |
744 | or carp "Missing font parameter"; | |
745 | ||
746 | my $name_font = $opts->{glyph_name_font} || $font; | |
747 | ||
748 | my $has_chars = $opts->{has_chars} || [ 1, '', 1 ]; | |
749 | ||
750 | my $glyph_names = $opts->{glyph_names} || [ "A", undef, "A" ]; | |
751 | ||
752 | SKIP: | |
753 | { # check magic is handled correctly | |
754 | # https://rt.cpan.org/Ticket/Display.html?id=83438 | |
755 | skip("no native UTF8 support in this version of perl", 11) | |
756 | unless $] >= 5.006; | |
757 | skip("overloading handling of magic is broken in this version of perl", 11) | |
758 | unless $] >= 5.008; | |
759 | Imager->log("utf8 magic tests\n"); | |
760 | my $over = bless {}, "Imager::Test::OverUtf8"; | |
761 | my $text = "A".chr(0x2010)."A"; | |
762 | my $white = Imager::Color->new("#FFF"); | |
763 | my $base_draw = Imager->new(xsize => 80, ysize => 20); | |
764 | ok($base_draw->string(font => $font, | |
765 | text => $text, | |
766 | x => 2, | |
767 | y => 18, | |
768 | size => 15, | |
769 | color => $white, | |
770 | aa => 1), | |
771 | "magic: make a base image"); | |
772 | my $test_draw = Imager->new(xsize => 80, ysize => 20); | |
773 | ok($test_draw->string(font => $font, | |
774 | text => $over, | |
775 | x => 2, | |
776 | y => 18, | |
777 | size => 15, | |
778 | color => $white, | |
779 | aa => 1), | |
780 | "magic: draw with overload"); | |
781 | is_image($base_draw, $test_draw, "check they match"); | |
782 | if ($opts->{files}) { | |
783 | $test_draw->write(file => "testout/utf8tdr.ppm"); | |
784 | $base_draw->write(file => "testout/utf8bdr.ppm"); | |
785 | } | |
786 | ||
787 | my $base_cp = Imager->new(xsize => 80, ysize => 20); | |
788 | $base_cp->box(filled => 1, color => "#808080"); | |
789 | my $test_cp = $base_cp->copy; | |
790 | ok($base_cp->string(font => $font, | |
791 | text => $text, | |
792 | y => 2, | |
793 | y => 18, | |
794 | size => 16, | |
795 | channel => 2, | |
796 | aa => 1), | |
797 | "magic: make a base image (channel)"); | |
798 | Imager->log("magic: draw to channel with overload\n"); | |
799 | ok($test_cp->string(font => $font, | |
800 | text => $over, | |
801 | y => 2, | |
802 | y => 18, | |
803 | size => 16, | |
804 | channel => 2, | |
805 | aa => 1), | |
806 | "magic: draw with overload (channel)"); | |
807 | is_image($test_cp, $base_cp, "check they match"); | |
808 | if ($opts->{files}) { | |
809 | $test_cp->write(file => "testout/utf8tcp.ppm"); | |
810 | $base_cp->write(file => "testout/utf8bcp.ppm"); | |
811 | } | |
812 | ||
813 | SKIP: | |
814 | { | |
815 | Imager->log("magic: has_chars\n"); | |
816 | $font->can("has_chars") | |
817 | or skip "No has_chars aupport", 2; | |
818 | is_deeply([ $font->has_chars(string => $text) ], $has_chars, | |
819 | "magic: has_chars with normal utf8 text"); | |
820 | is_deeply([ $font->has_chars(string => $over) ], $has_chars, | |
821 | "magic: has_chars with magic utf8 text"); | |
822 | } | |
823 | ||
824 | Imager->log("magic: bounding_box\n"); | |
825 | my @base_bb = $font->bounding_box(string => $text, size => 30); | |
826 | is_deeply([ $font->bounding_box(string => $over, size => 30) ], | |
827 | \@base_bb, | |
828 | "check bounding box magic"); | |
829 | ||
830 | SKIP: | |
831 | { | |
832 | $font->can_glyph_names | |
833 | or skip "No glyph_names", 2; | |
834 | Imager->log("magic: glyph_names\n"); | |
835 | my @text_names = $name_font->glyph_names(string => $text, reliable_only => 0); | |
836 | is_deeply(\@text_names, $glyph_names, | |
837 | "magic: glyph_names with normal utf8 text"); | |
838 | my @over_names = $name_font->glyph_names(string => $over, reliable_only => 0); | |
839 | is_deeply(\@over_names, $glyph_names, | |
840 | "magic: glyph_names with magic utf8 text"); | |
841 | } | |
842 | } | |
843 | ||
844 | { # invalid UTF8 handling at the OO level | |
845 | my $im = Imager->new(xsize => 80, ysize => 20); | |
846 | my $bad_utf8 = pack("C", 0xC0); | |
847 | Imager->_set_error(""); | |
848 | ok(!$im->string(font => $font, size => 1, text => $bad_utf8, utf8 => 1, | |
849 | y => 18, x => 2), | |
850 | "drawing invalid utf8 should fail"); | |
851 | is($im->errstr, "invalid UTF8 character", "check error message"); | |
852 | Imager->_set_error(""); | |
853 | ok(!$im->string(font => $font, size => 1, text => $bad_utf8, utf8 => 1, | |
854 | y => 18, x => 2, channel => 1), | |
855 | "drawing invalid utf8 should fail (channel)"); | |
856 | is($im->errstr, "invalid UTF8 character", "check error message"); | |
857 | Imager->_set_error(""); | |
858 | ok(!$font->bounding_box(string => $bad_utf8, size => 30, utf8 => 1), | |
859 | "bounding_box() bad utf8 should fail"); | |
860 | is(Imager->errstr, "invalid UTF8 character", "check error message"); | |
861 | SKIP: | |
862 | { | |
863 | $font->can_glyph_names | |
864 | or skip "No glyph_names support", 2; | |
865 | Imager->_set_error(""); | |
866 | is_deeply([ $font->glyph_names(string => $bad_utf8, utf8 => 1) ], | |
867 | [ ], | |
868 | "glyph_names returns empty list for bad string"); | |
869 | is(Imager->errstr, "invalid UTF8 character", "check error message"); | |
870 | } | |
871 | SKIP: | |
872 | { | |
873 | $font->can("has_chars") | |
874 | or skip "No has_chars support", 2; | |
875 | Imager->_set_error(""); | |
876 | is_deeply([ $font->has_chars(string => $bad_utf8, utf8 => 1) ], | |
877 | [ ], | |
878 | "has_chars returns empty list for bad string"); | |
879 | is(Imager->errstr, "invalid UTF8 character", "check error message"); | |
880 | } | |
881 | } | |
882 | } | |
883 | ||
884 | package Imager::Test::OverUtf8; | |
885 | use overload '""' => sub { "A".chr(0x2010)."A" }; | |
886 | ||
887 | ||
730 | 888 | 1; |
731 | 889 | |
732 | 890 | __END__ |
873 | 1031 | |
874 | 1032 | This was explicitly created for regression tests on #21770. |
875 | 1033 | |
1034 | =item std_font_tests({ font => $font }) | |
1035 | ||
1036 | Perform standard font interface tests. | |
1037 | ||
1038 | =item std_font_test_count() | |
1039 | ||
1040 | The number of tests performed by std_font_tests(). | |
1041 | ||
876 | 1042 | =back |
877 | 1043 | |
878 | 1044 | =head2 Helper functions |
0 | =head1 NAME | |
1 | ||
2 | Imager::Threads - Imager and threads | |
3 | ||
4 | =head1 SYNOPSIS | |
5 | ||
6 | use Imager; | |
7 | use threads; | |
8 | Imager->preload; | |
9 | ||
10 | threads->create(...); | |
11 | ||
12 | =head1 DESCRIPTION | |
13 | ||
14 | Starting from version 0.94 Imager attempts to work safely with perl's | |
15 | C<ithreads>. | |
16 | ||
17 | Previous versions stored some state in global variables, in particular | |
18 | the internal error stack. | |
19 | ||
20 | However there are some limitations: | |
21 | ||
22 | =over | |
23 | ||
24 | =item * | |
25 | ||
26 | Imager's debug malloc isn't thread safe and will never be. Imager's | |
27 | debug malloc is disabled by default. | |
28 | ||
29 | =item * | |
30 | ||
31 | C<libtiff>, which Imager uses for TIFF file support is not thread | |
32 | safe, C<Imager::File::TIFF> works around this by single-threading its | |
33 | access to C<libtiff>. | |
34 | ||
35 | =item * | |
36 | ||
37 | C<giflib>, which Imager uses for GIF support is not thread safe before | |
38 | version 5. C<Imager::File::GIF> works around this by single threading | |
39 | its access to C<giflib>. | |
40 | ||
41 | =item * | |
42 | ||
43 | C<T1Lib>, used by one of Imager's font drivers, is not thread safe. | |
44 | C<Imager::Font::T1> works around this by single threading access. | |
45 | ||
46 | =item * | |
47 | ||
48 | killing a thread reading or writing TIFF or GIF files, or using T1 | |
49 | fonts through C<Imager::Font::T1> may deadlock other threads when they | |
50 | attempt to read or write TIFF or GIF files, or work with Type 1 fonts. | |
51 | ||
52 | =item * | |
53 | ||
54 | Fill, font, color or I/O layer objects created in one thread are not | |
55 | valid for use in child threads. If you manage to duplicate such an | |
56 | object in another thread, you get to keep both pieces when it breaks. | |
57 | ||
58 | =back | |
59 | ||
60 | Note that if you have another module using C<libtiff>, C<giflib> or | |
61 | C<t1lib> it may interact with Imager's use of those libraries in a | |
62 | threaded environment, since there's no way to co-ordinate access to | |
63 | the global information C<libtiff>, C<giflib> and C<t1lib> maintain. | |
64 | ||
65 | Imager currently doesn't use threads itself, except for testing its | |
66 | threads support. | |
67 | ||
68 | =head1 SEE ALSO | |
69 | ||
70 | Imager, C<threads> | |
71 | ||
72 | =head1 AUTHOR | |
73 | ||
74 | Tony Cook <tony@cpan.org> | |
75 | ||
76 | =cut |
23 | 23 | |
24 | 24 | */ |
25 | 25 | |
26 | #define IMAGER_NO_CONTEXT | |
26 | 27 | #include "imageri.h" |
27 | 28 | |
28 | #define DEF_BYTES_LIMIT 0x40000000 | |
29 | ||
30 | static i_img_dim max_width, max_height; | |
31 | static size_t max_bytes = DEF_BYTES_LIMIT; | |
32 | ||
33 | /* | |
34 | =item i_set_image_file_limits(width, height, bytes) | |
35 | ||
29 | /* | |
30 | =item im_set_image_file_limits(ctx, width, height, bytes) | |
31 | X<im_set_image_file_limits API>X<i_set_image_file_limits API> | |
36 | 32 | =category Files |
33 | =synopsis im_set_image_file_limits(aIMCTX, 500, 500, 1000000); | |
37 | 34 | =synopsis i_set_image_file_limits(500, 500, 1000000); |
38 | 35 | |
39 | 36 | Set limits on the sizes of images read by Imager. |
59 | 56 | |
60 | 57 | Returns non-zero on success. |
61 | 58 | |
59 | Also callable as C<i_set_image_file_limits(width, height, bytes)>. | |
60 | ||
62 | 61 | =cut |
63 | 62 | */ |
64 | 63 | |
65 | 64 | int |
66 | i_set_image_file_limits(i_img_dim width, i_img_dim height, size_t bytes) { | |
65 | im_set_image_file_limits(pIMCTX, i_img_dim width, i_img_dim height, size_t bytes) { | |
67 | 66 | i_clear_error(); |
68 | 67 | |
69 | 68 | if (width < 0) { |
79 | 78 | return 0; |
80 | 79 | } |
81 | 80 | |
82 | max_width = width; | |
83 | max_height = height; | |
84 | max_bytes = bytes ? bytes : DEF_BYTES_LIMIT; | |
81 | aIMCTX->max_width = width; | |
82 | aIMCTX->max_height = height; | |
83 | aIMCTX->max_bytes = bytes ? bytes : DEF_BYTES_LIMIT; | |
85 | 84 | |
86 | 85 | return 1; |
87 | 86 | } |
88 | 87 | |
89 | 88 | /* |
90 | =item i_get_image_file_limits(&width, &height, &bytes) | |
91 | ||
89 | =item im_get_image_file_limits(ctx, &width, &height, &bytes) | |
90 | X<im_get_image_file_limits API>X<i_get_image_file_limits> | |
92 | 91 | =category Files |
92 | =synopsis im_get_image_file_limits(aIMCTX, &width, &height, &bytes) | |
93 | 93 | =synopsis i_get_image_file_limits(&width, &height, &bytes) |
94 | 94 | |
95 | 95 | Retrieves the file limits set by i_set_image_file_limits(). |
105 | 105 | size_t *bytes - size in memory of the image in bytes. |
106 | 106 | |
107 | 107 | =back |
108 | ||
109 | Also callable as C<i_get_image_file_limits(&width, &height, &bytes)>. | |
108 | 110 | |
109 | 111 | =cut |
110 | 112 | */ |
111 | 113 | |
112 | 114 | int |
113 | i_get_image_file_limits(i_img_dim *width, i_img_dim *height, size_t *bytes) { | |
114 | i_clear_error(); | |
115 | ||
116 | *width = max_width; | |
117 | *height = max_height; | |
118 | *bytes = max_bytes; | |
115 | im_get_image_file_limits(pIMCTX, i_img_dim *width, i_img_dim *height, size_t *bytes) { | |
116 | im_clear_error(aIMCTX); | |
117 | ||
118 | *width = aIMCTX->max_width; | |
119 | *height = aIMCTX->max_height; | |
120 | *bytes = aIMCTX->max_bytes; | |
119 | 121 | |
120 | 122 | return 1; |
121 | 123 | } |
122 | 124 | |
123 | 125 | /* |
124 | =item i_int_check_image_file_limits(width, height, channels, sample_size) | |
125 | ||
126 | =item im_int_check_image_file_limits(width, height, channels, sample_size) | |
127 | X<im_int_check_image_file_limits API>X<i_int_check_image_file_limits> | |
126 | 128 | =category Files |
127 | =synopsis i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
129 | =synopsis im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t)) | |
130 | =synopsis i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)) | |
128 | 131 | |
129 | 132 | Checks the size of a file in memory against the configured image file |
130 | 133 | limits. |
136 | 139 | |
137 | 140 | This function is intended to be called by image file read functions. |
138 | 141 | |
142 | Also callable as C<i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)>. | |
143 | ||
139 | 144 | =cut |
140 | 145 | */ |
141 | 146 | |
142 | 147 | int |
143 | i_int_check_image_file_limits(i_img_dim width, i_img_dim height, int channels, size_t sample_size) { | |
148 | im_int_check_image_file_limits(pIMCTX, i_img_dim width, i_img_dim height, int channels, size_t sample_size) { | |
144 | 149 | size_t bytes; |
145 | i_clear_error(); | |
150 | im_clear_error(aIMCTX); | |
146 | 151 | |
147 | 152 | if (width <= 0) { |
148 | i_push_errorf(0, "file size limit - image width of %" i_DF " is not positive", | |
153 | im_push_errorf(aIMCTX, 0, "file size limit - image width of %" i_DF " is not positive", | |
149 | 154 | i_DFc(width)); |
150 | 155 | return 0; |
151 | 156 | } |
152 | if (max_width && width > max_width) { | |
153 | i_push_errorf(0, "file size limit - image width of %" i_DF " exceeds limit of %" i_DF, | |
154 | i_DFc(width), i_DFc(max_width)); | |
157 | if (aIMCTX->max_width && width > aIMCTX->max_width) { | |
158 | im_push_errorf(aIMCTX, 0, "file size limit - image width of %" i_DF " exceeds limit of %" i_DF, | |
159 | i_DFc(width), i_DFc(aIMCTX->max_width)); | |
155 | 160 | return 0; |
156 | 161 | } |
157 | 162 | |
158 | 163 | if (height <= 0) { |
159 | i_push_errorf(0, "file size limit - image height of %" i_DF " is not positive", | |
164 | im_push_errorf(aIMCTX, 0, "file size limit - image height of %" i_DF " is not positive", | |
160 | 165 | i_DFc(height)); |
161 | 166 | return 0; |
162 | 167 | } |
163 | 168 | |
164 | if (max_height && height > max_height) { | |
165 | i_push_errorf(0, "file size limit - image height of %" i_DF | |
166 | " exceeds limit of %" i_DF, i_DFc(height), i_DFc(max_height)); | |
169 | if (aIMCTX->max_height && height > aIMCTX->max_height) { | |
170 | im_push_errorf(aIMCTX, 0, "file size limit - image height of %" i_DF | |
171 | " exceeds limit of %" i_DF, i_DFc(height), i_DFc(aIMCTX->max_height)); | |
167 | 172 | return 0; |
168 | 173 | } |
169 | 174 | |
170 | 175 | if (channels < 1 || channels > MAXCHANNELS) { |
171 | i_push_errorf(0, "file size limit - channels %d out of range", | |
176 | im_push_errorf(aIMCTX, 0, "file size limit - channels %d out of range", | |
172 | 177 | channels); |
173 | 178 | return 0; |
174 | 179 | } |
175 | 180 | |
176 | 181 | if (sample_size < 1 || sample_size > sizeof(long double)) { |
177 | i_push_errorf(0, "file size limit - sample_size %ld out of range", | |
182 | im_push_errorf(aIMCTX, 0, "file size limit - sample_size %ld out of range", | |
178 | 183 | (long)sample_size); |
179 | 184 | return 0; |
180 | 185 | } |
186 | 191 | bytes = width * height * channels * sample_size; |
187 | 192 | if (bytes / width != height * channels * sample_size |
188 | 193 | || bytes / height != width * channels * sample_size) { |
189 | i_push_error(0, "file size limit - integer overflow calculating storage"); | |
190 | return 0; | |
191 | } | |
192 | if (max_bytes) { | |
193 | if (bytes > max_bytes) { | |
194 | i_push_errorf(0, "file size limit - storage size of %lu " | |
194 | im_push_error(aIMCTX, 0, "file size limit - integer overflow calculating storage"); | |
195 | return 0; | |
196 | } | |
197 | if (aIMCTX->max_bytes) { | |
198 | if (bytes > aIMCTX->max_bytes) { | |
199 | im_push_errorf(aIMCTX, 0, "file size limit - storage size of %lu " | |
195 | 200 | "exceeds limit of %lu", (unsigned long)bytes, |
196 | (unsigned long)max_bytes); | |
201 | (unsigned long)aIMCTX->max_bytes); | |
197 | 202 | return 0; |
198 | 203 | } |
199 | 204 | } |
0 | #define IMAGER_NO_CONTEXT | |
1 | #include "imageri.h" | |
0 | 2 | #include "imconfig.h" |
1 | 3 | #include "log.h" |
2 | 4 | #include <stdlib.h> |
8 | 10 | #define DTBUFF 50 |
9 | 11 | #define DATABUFF DTBUFF+3+10+1+5+1+1 |
10 | 12 | |
11 | static int log_level = 0; | |
12 | static FILE *lg_file = NULL; | |
13 | static char *date_format = "%Y/%m/%d %H:%M:%S"; | |
14 | static char date_buffer[DTBUFF]; | |
15 | static char data_buffer[DATABUFF]; | |
13 | #define LOG_DATE_FORMAT "%Y/%m/%d %H:%M:%S" | |
16 | 14 | |
15 | static i_mutex_t log_mutex; | |
16 | ||
17 | static void | |
18 | im_vloog(pIMCTX, int level, const char *fmt, va_list ap); | |
17 | 19 | |
18 | 20 | /* |
19 | 21 | * Logging is active |
20 | 22 | */ |
21 | 23 | |
22 | 24 | int |
23 | i_init_log(const char* name,int level) { | |
25 | im_init_log(pIMCTX, const char* name,int level) { | |
24 | 26 | i_clear_error(); |
25 | log_level = level; | |
27 | ||
28 | if (!log_mutex) { | |
29 | log_mutex = i_mutex_new(); | |
30 | } | |
31 | ||
32 | if (aIMCTX->lg_file) { | |
33 | if (aIMCTX->own_log) | |
34 | fclose(aIMCTX->lg_file); | |
35 | aIMCTX->lg_file = NULL; | |
36 | } | |
37 | ||
38 | aIMCTX->log_level = level; | |
26 | 39 | if (level < 0) { |
27 | lg_file = NULL; | |
40 | aIMCTX->lg_file = NULL; | |
28 | 41 | } else { |
29 | 42 | if (name == NULL) { |
30 | lg_file = stderr; | |
43 | aIMCTX->lg_file = stderr; | |
44 | aIMCTX->own_log = 0; | |
31 | 45 | } else { |
32 | if (NULL == (lg_file = fopen(name, "w+")) ) { | |
33 | i_push_errorf(errno, "Cannot open file '%s': (%d)", name, errno); | |
46 | if (NULL == (aIMCTX->lg_file = fopen(name, "w+")) ) { | |
47 | im_push_errorf(aIMCTX, errno, "Cannot open file '%s': (%d)", name, errno); | |
34 | 48 | return 0; |
35 | 49 | } |
50 | aIMCTX->own_log = 1; | |
51 | setvbuf(aIMCTX->lg_file, NULL, _IONBF, BUFSIZ); | |
36 | 52 | } |
37 | 53 | } |
38 | if (lg_file) { | |
39 | setvbuf(lg_file, NULL, _IONBF, BUFSIZ); | |
40 | mm_log((0,"Imager - log started (level = %d)\n", level)); | |
54 | if (aIMCTX->lg_file) { | |
55 | im_log((aIMCTX, 0,"Imager - log started (level = %d)\n", level)); | |
41 | 56 | } |
42 | 57 | |
43 | return lg_file != NULL; | |
58 | return aIMCTX->lg_file != NULL; | |
44 | 59 | } |
45 | 60 | |
46 | 61 | void |
47 | 62 | i_fatal(int exitcode,const char *fmt, ... ) { |
48 | 63 | va_list ap; |
49 | time_t timi; | |
50 | struct tm *str_tm; | |
51 | ||
52 | if (lg_file != NULL) { | |
53 | timi = time(NULL); | |
54 | str_tm = localtime(&timi); | |
55 | if ( strftime(date_buffer, DTBUFF, date_format, str_tm) ) | |
56 | fprintf(lg_file,"[%s] ",date_buffer); | |
64 | dIMCTX; | |
65 | ||
66 | if (aIMCTX->lg_file != NULL) { | |
57 | 67 | va_start(ap,fmt); |
58 | vfprintf(lg_file,fmt,ap); | |
68 | im_vloog(aIMCTX, 0, fmt, ap); | |
59 | 69 | va_end(ap); |
60 | 70 | } |
61 | 71 | exit(exitcode); |
62 | 72 | } |
63 | 73 | |
74 | void | |
75 | im_fatal(pIMCTX, int exitcode,const char *fmt, ... ) { | |
76 | va_list ap; | |
77 | ||
78 | if (aIMCTX->lg_file != NULL) { | |
79 | va_start(ap,fmt); | |
80 | im_vloog(aIMCTX, 0, fmt, ap); | |
81 | va_end(ap); | |
82 | } | |
83 | exit(exitcode); | |
84 | } | |
64 | 85 | |
65 | 86 | /* |
66 | 87 | =item i_loog(level, format, ...) |
71 | 92 | =cut |
72 | 93 | */ |
73 | 94 | |
95 | static void | |
96 | im_vloog(pIMCTX, int level, const char *fmt, va_list ap) { | |
97 | time_t timi; | |
98 | struct tm *str_tm; | |
99 | char date_buffer[DTBUFF]; | |
100 | ||
101 | if (!aIMCTX->lg_file || level > aIMCTX->log_level) | |
102 | return; | |
103 | ||
104 | i_mutex_lock(log_mutex); | |
105 | ||
106 | timi = time(NULL); | |
107 | str_tm = localtime(&timi); | |
108 | strftime(date_buffer, DTBUFF, LOG_DATE_FORMAT, str_tm); | |
109 | fprintf(aIMCTX->lg_file, "[%s] %10s:%-5d %3d: ", date_buffer, | |
110 | aIMCTX->filename, aIMCTX->line, level); | |
111 | vfprintf(aIMCTX->lg_file, fmt, ap); | |
112 | fflush(aIMCTX->lg_file); | |
113 | ||
114 | i_mutex_unlock(log_mutex); | |
115 | } | |
116 | ||
74 | 117 | void |
75 | 118 | i_loog(int level,const char *fmt, ... ) { |
119 | dIMCTX; | |
76 | 120 | va_list ap; |
77 | if (level > log_level) return; | |
78 | if (lg_file != NULL) { | |
79 | fputs(data_buffer, lg_file); | |
80 | fprintf(lg_file, "%3d: ",level); | |
81 | va_start(ap,fmt); | |
82 | vfprintf(lg_file, fmt, ap); | |
83 | fflush(lg_file); | |
84 | va_end(ap); | |
85 | } | |
121 | ||
122 | if (!aIMCTX->lg_file || level > aIMCTX->log_level) | |
123 | return; | |
124 | ||
125 | va_start(ap,fmt); | |
126 | im_vloog(aIMCTX, level, fmt, ap); | |
127 | va_end(ap); | |
128 | } | |
129 | ||
130 | void | |
131 | im_loog(pIMCTX, int level,const char *fmt, ... ) { | |
132 | va_list ap; | |
133 | ||
134 | if (!aIMCTX->lg_file || level > aIMCTX->log_level) | |
135 | return; | |
136 | ||
137 | va_start(ap,fmt); | |
138 | im_vloog(aIMCTX, level, fmt, ap); | |
139 | va_end(ap); | |
86 | 140 | } |
87 | 141 | |
88 | 142 | /* |
95 | 149 | */ |
96 | 150 | |
97 | 151 | void |
98 | i_lhead(const char *file, int line) { | |
99 | time_t timi; | |
100 | struct tm *str_tm; | |
101 | ||
102 | if (lg_file != NULL) { | |
103 | timi = time(NULL); | |
104 | str_tm = localtime(&timi); | |
105 | strftime(date_buffer, DTBUFF, date_format, str_tm); | |
106 | #ifdef IMAGER_SNPRINTF | |
107 | snprintf(data_buffer, sizeof(data_buffer), "[%s] %10s:%-5d ", date_buffer, file, line); | |
108 | #else | |
109 | sprintf(data_buffer, "[%s] %10s:%-5d ", date_buffer, file, line); | |
110 | #endif | |
152 | im_lhead(pIMCTX, const char *file, int line) { | |
153 | if (aIMCTX->lg_file != NULL) { | |
154 | aIMCTX->filename = file; | |
155 | aIMCTX->line = line; | |
111 | 156 | } |
157 | } | |
158 | ||
159 | void i_lhead(const char *file, int line) { | |
160 | dIMCTX; | |
161 | ||
162 | im_lhead(aIMCTX, file, line); | |
112 | 163 | } |
113 | 164 | |
114 | 165 | #else |
117 | 168 | * Logging is inactive - insert dummy functions |
118 | 169 | */ |
119 | 170 | |
120 | int i_init_log(const char* name,int onoff) { | |
171 | int im_init_log(pIMCTX, const char* name,int onoff) { | |
121 | 172 | i_clear_error(); |
122 | 173 | i_push_error(0, "Logging disabled"); |
123 | 174 | return 0; |
124 | 175 | } |
125 | 176 | |
126 | 177 | void i_fatal(int exitcode,const char *fmt, ... ) { exit(exitcode); } |
178 | void im_fatal(pIMCTX, int exitcode,const char *fmt, ... ) { exit(exitcode); } | |
127 | 179 | |
128 | 180 | void |
129 | 181 | i_loog(int level,const char *fmt, ... ) { |
130 | 182 | } |
131 | 183 | |
132 | 184 | void |
185 | im_loog(pIMCTX, int level,const char *fmt, ... ) { | |
186 | } | |
187 | ||
188 | void | |
133 | 189 | i_lhead(const char *file, int line) { } |
134 | 190 | |
191 | void | |
192 | im_lhead(pIMCTX, const char *file, int line) { } | |
193 | ||
135 | 194 | #endif |
10 | 10 | global: creates a global variable FILE* lg_file |
11 | 11 | */ |
12 | 12 | |
13 | int i_init_log( const char *name, int onoff ); | |
13 | int im_init_log(pIMCTX, const char *name, int onoff ); | |
14 | #define i_init_log(name, onoff) im_init_log(aIMCTX, name, onoff) | |
15 | #ifndef IMAGER_NO_CONTEXT | |
14 | 16 | void i_fatal ( int exitcode,const char *fmt, ... ); |
17 | #endif | |
18 | void im_fatal (pIMCTX, int exitcode,const char *fmt, ... ); | |
19 | void im_lhead ( pIMCTX, const char *file, int line ); | |
15 | 20 | void i_lhead ( const char *file, int line ); |
16 | 21 | void i_loog(int level,const char *msg, ... ) I_FORMAT_ATTR(2,3); |
22 | void im_loog(pIMCTX, int level,const char *msg, ... ) I_FORMAT_ATTR(3,4); | |
17 | 23 | |
18 | 24 | /* |
19 | =item mm_log((level, format, ...)) | |
25 | =item im_log((aIMCTX, level, format, ...)) | |
20 | 26 | =category Logging |
21 | 27 | |
22 | 28 | This is the main entry point to logging. Note that the extra set of |
25 | 31 | This will format a string with the current file and line number to the |
26 | 32 | log file if logging is enabled. |
27 | 33 | |
34 | This must be called with a context object defined by one of the | |
35 | C<dIMCTX> macros in scope. | |
36 | ||
37 | This can also be called as C<mm_log((level, format, args))> in which | |
38 | case the currently active context is used and any in scope context is | |
39 | ignored. | |
40 | ||
28 | 41 | =cut |
29 | 42 | */ |
30 | 43 | |
31 | 44 | #ifdef IMAGER_LOG |
45 | #ifndef IMAGER_NO_CONTEXT | |
32 | 46 | #define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; } |
47 | #endif | |
48 | #define im_log(x) { im_lhead(aIMCTX, __FILE__,__LINE__); im_loog x; } | |
33 | 49 | #else |
34 | 50 | #define mm_log(x) |
51 | #define im_log(x) | |
35 | 52 | #endif |
36 | 53 | |
37 | 54 |
9 | 9 | =over |
10 | 10 | =cut |
11 | 11 | */ |
12 | ||
13 | #define IMAGER_NO_CONTEXT | |
12 | 14 | |
13 | 15 | #include "imager.h" |
14 | 16 | #include "imageri.h" |
123 | 125 | =cut |
124 | 126 | */ |
125 | 127 | |
126 | i_img *i_img_masked_new(i_img *targ, i_img *mask, i_img_dim x, i_img_dim y, i_img_dim w, i_img_dim h) { | |
128 | i_img * | |
129 | i_img_masked_new(i_img *targ, i_img *mask, i_img_dim x, i_img_dim y, i_img_dim w, i_img_dim h) { | |
127 | 130 | i_img *im; |
128 | 131 | i_img_mask_ext *ext; |
129 | ||
130 | i_clear_error(); | |
132 | dIMCTXim(targ); | |
133 | ||
134 | im_clear_error(aIMCTX); | |
131 | 135 | if (x >= targ->xsize || y >= targ->ysize) { |
132 | i_push_error(0, "subset outside of target image"); | |
136 | im_push_error(aIMCTX, 0, "subset outside of target image"); | |
133 | 137 | return NULL; |
134 | 138 | } |
135 | 139 | if (mask) { |
143 | 147 | if (y+h > targ->ysize) |
144 | 148 | h = targ->ysize - y; |
145 | 149 | |
146 | im = mymalloc(sizeof(i_img)); | |
150 | im = im_img_alloc(aIMCTX); | |
151 | ||
147 | 152 | memcpy(im, &IIM_base_masked, sizeof(i_img)); |
153 | i_tags_new(&im->tags); | |
148 | 154 | im->xsize = w; |
149 | 155 | im->ysize = h; |
150 | 156 | im->channels = targ->channels; |
157 | 163 | ext->ybase = y; |
158 | 164 | ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize); |
159 | 165 | im->ext_data = ext; |
166 | ||
167 | im_img_init(aIMCTX, im); | |
160 | 168 | |
161 | 169 | return im; |
162 | 170 | } |
584 | 592 | return result; |
585 | 593 | } |
586 | 594 | else { |
595 | dIMCTXim(im); | |
587 | 596 | i_push_error(0, "Image position outside of image"); |
588 | 597 | return -1; |
589 | 598 | } |
651 | 660 | return result; |
652 | 661 | } |
653 | 662 | else { |
663 | dIMCTXim(im); | |
654 | 664 | i_push_error(0, "Image position outside of image"); |
655 | 665 | return -1; |
656 | 666 | } |
0 | /* | |
1 | dummy mutexes, for non-threaded builds | |
2 | */ | |
3 | ||
4 | #include "imageri.h" | |
5 | ||
6 | #include <pthread.h> | |
7 | ||
8 | /* documented in mutexwin.c */ | |
9 | ||
10 | struct i_mutex_tag { | |
11 | int dummy; | |
12 | }; | |
13 | ||
14 | i_mutex_t | |
15 | i_mutex_new(void) { | |
16 | i_mutex_t m; | |
17 | ||
18 | m = malloc(sizeof(*m)); | |
19 | if (!m) | |
20 | i_fatal(3, "Cannot allocate mutex object"); | |
21 | ||
22 | return m; | |
23 | } | |
24 | ||
25 | void | |
26 | i_mutex_destroy(i_mutex_t m) { | |
27 | free(m); | |
28 | } | |
29 | ||
30 | void | |
31 | i_mutex_lock(i_mutex_t m) { | |
32 | (void)m; | |
33 | } | |
34 | ||
35 | void | |
36 | i_mutex_unlock(i_mutex_t m) { | |
37 | (void)m; | |
38 | } |
0 | /* | |
1 | pthreads mutexes | |
2 | */ | |
3 | ||
4 | #include "imageri.h" | |
5 | ||
6 | #include <pthread.h> | |
7 | #include <errno.h> | |
8 | ||
9 | /* documented in mutexwin.c */ | |
10 | ||
11 | struct i_mutex_tag { | |
12 | pthread_mutex_t mutex; | |
13 | }; | |
14 | ||
15 | i_mutex_t | |
16 | i_mutex_new(void) { | |
17 | i_mutex_t m; | |
18 | ||
19 | m = malloc(sizeof(*m)); | |
20 | if (!m) | |
21 | i_fatal(3, "Cannot allocate mutex object"); | |
22 | if (pthread_mutex_init(&m->mutex, NULL) != 0) { | |
23 | i_fatal(3, "Error initializing mutex %d", errno); | |
24 | } | |
25 | ||
26 | return m; | |
27 | } | |
28 | ||
29 | void | |
30 | i_mutex_destroy(i_mutex_t m) { | |
31 | pthread_mutex_destroy(&(m->mutex)); | |
32 | free(m); | |
33 | } | |
34 | ||
35 | void | |
36 | i_mutex_lock(i_mutex_t m) { | |
37 | pthread_mutex_lock(&(m->mutex)); | |
38 | } | |
39 | ||
40 | void | |
41 | i_mutex_unlock(i_mutex_t m) { | |
42 | pthread_mutex_unlock(&m->mutex); | |
43 | } |
0 | /* | |
1 | =head1 NAME | |
2 | ||
3 | mutex.c - Imager's mutex API. | |
4 | ||
5 | =head1 FUNCTIONS | |
6 | ||
7 | =over | |
8 | ||
9 | =cut | |
10 | */ | |
11 | ||
12 | #include "imageri.h" | |
13 | ||
14 | #include <windows.h> | |
15 | ||
16 | struct i_mutex_tag { | |
17 | CRITICAL_SECTION section; | |
18 | }; | |
19 | ||
20 | /* | |
21 | =item i_mutex_new() | |
22 | =category Mutex functions | |
23 | =synopsis i_mutex_t m = i_mutex_new(); | |
24 | =order 10 | |
25 | ||
26 | Create a mutex. | |
27 | ||
28 | If a critical section cannot be created for whatever reason, Imager | |
29 | will abort. | |
30 | ||
31 | =cut | |
32 | */ | |
33 | ||
34 | i_mutex_t | |
35 | i_mutex_new(void) { | |
36 | i_mutex_t m; | |
37 | ||
38 | m = malloc(sizeof(*m)); | |
39 | if (!m) | |
40 | i_fatal(3, "Cannot allocate mutex object"); | |
41 | InitializeCriticalSection(&(m->section)); | |
42 | ||
43 | return m; | |
44 | } | |
45 | ||
46 | /* | |
47 | =item i_mutex_destroy(m) | |
48 | =category Mutex functions | |
49 | =synopsis i_mutex_destroy(m); | |
50 | ||
51 | Destroy a mutex. | |
52 | ||
53 | =cut | |
54 | */ | |
55 | ||
56 | void | |
57 | i_mutex_destroy(i_mutex_t m) { | |
58 | DeleteCriticalSection(&(m->section)); | |
59 | free(m); | |
60 | } | |
61 | ||
62 | /* | |
63 | =item i_mutex_lock(m) | |
64 | =category Mutex functions | |
65 | =synopsis i_mutex_lock(m); | |
66 | ||
67 | Lock the mutex, waiting if another thread has the mutex locked. | |
68 | ||
69 | =cut | |
70 | */ | |
71 | ||
72 | void | |
73 | i_mutex_lock(i_mutex_t m) { | |
74 | EnterCriticalSection(&(m->section)); | |
75 | } | |
76 | ||
77 | /* | |
78 | =item i_mutex_unlock(m) | |
79 | =category Mutex functions | |
80 | =synopsis i_mutex_unlock(m); | |
81 | ||
82 | Release the mutex. | |
83 | ||
84 | The behavior of releasing a mutex you don't hold is unspecified. | |
85 | ||
86 | =cut | |
87 | */ | |
88 | ||
89 | void | |
90 | i_mutex_unlock(i_mutex_t m) { | |
91 | LeaveCriticalSection(&(m->section)); | |
92 | } | |
93 |
16 | 16 | |
17 | 17 | =cut |
18 | 18 | */ |
19 | ||
20 | #define IMAGER_NO_CONTEXT | |
19 | 21 | |
20 | 22 | #include "imager.h" |
21 | 23 | #include "imageri.h" |
83 | 85 | }; |
84 | 86 | |
85 | 87 | /* |
86 | =item i_img_pal_new(C<x>, C<y>, C<channels>, C<maxpal>) | |
87 | ||
88 | =item im_img_pal_new(ctx, C<x>, C<y>, C<channels>, C<maxpal>) | |
89 | X<im_img_pal_new API>X<i_img_pal_new API> | |
88 | 90 | =category Image creation/destruction |
91 | =synopsis i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size) | |
89 | 92 | =synopsis i_img *img = i_img_pal_new(width, height, channels, max_palette_size) |
90 | 93 | |
91 | 94 | Creates a new paletted image of the supplied dimensions. |
94 | 97 | |
95 | 98 | Returns a new image or NULL on failure. |
96 | 99 | |
100 | Also callable as C<i_img_pal_new(width, height, channels, max_palette_size)>. | |
101 | ||
97 | 102 | =cut |
98 | 103 | */ |
99 | 104 | i_img * |
100 | i_img_pal_new(i_img_dim x, i_img_dim y, int channels, int maxpal) { | |
105 | im_img_pal_new(pIMCTX, i_img_dim x, i_img_dim y, int channels, int maxpal) { | |
101 | 106 | i_img *im; |
102 | 107 | i_img_pal_ext *palext; |
103 | 108 | size_t bytes, line_bytes; |
112 | 117 | return NULL; |
113 | 118 | } |
114 | 119 | if (channels < 1 || channels > MAXCHANNELS) { |
115 | i_push_errorf(0, "Channels must be positive and <= %d", MAXCHANNELS); | |
120 | im_push_errorf(aIMCTX, 0, "Channels must be positive and <= %d", MAXCHANNELS); | |
116 | 121 | return NULL; |
117 | 122 | } |
118 | 123 | bytes = sizeof(i_palidx) * x * y; |
184 | 189 | |
185 | 190 | =cut |
186 | 191 | */ |
187 | int i_img_to_rgb_inplace(i_img *im) { | |
192 | int | |
193 | i_img_to_rgb_inplace(i_img *im) { | |
188 | 194 | i_img temp; |
195 | dIMCTXim(im); | |
189 | 196 | |
190 | 197 | if (im->virtual) |
191 | 198 | return 0; |
214 | 221 | i_img *i_img_to_pal(i_img *src, i_quantize *quant) { |
215 | 222 | i_palidx *result; |
216 | 223 | i_img *im; |
224 | dIMCTXim(src); | |
217 | 225 | |
218 | 226 | i_clear_error(); |
219 | 227 | |
243 | 251 | |
244 | 252 | =cut |
245 | 253 | */ |
246 | i_img *i_img_to_rgb(i_img *src) { | |
254 | i_img * | |
255 | i_img_to_rgb(i_img *src) { | |
256 | dIMCTXim(src); | |
247 | 257 | i_img *im = i_img_empty_ch(NULL, src->xsize, src->ysize, src->channels); |
248 | 258 | i_img_rgb_convert(im, src); |
249 | 259 | |
305 | 315 | return 0; |
306 | 316 | } |
307 | 317 | else { |
308 | mm_log((1, "i_ppix: color(%d,%d,%d) not found, converting to rgb\n", | |
318 | dIMCTXim(im); | |
319 | im_log((aIMCTX, 1, "i_ppix: color(%d,%d,%d) not found, converting to rgb\n", | |
309 | 320 | val->channel[0], val->channel[1], val->channel[2])); |
310 | 321 | if (i_img_to_rgb_inplace(im)) { |
311 | 322 | return i_ppix(im, x, y, val); |
422 | 433 | if (chans) { |
423 | 434 | for (ch = 0; ch < chan_count; ++ch) { |
424 | 435 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
425 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
436 | dIMCTXim(im); | |
437 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
426 | 438 | } |
427 | 439 | } |
428 | 440 | |
438 | 450 | } |
439 | 451 | else { |
440 | 452 | if (chan_count <= 0 || chan_count > im->channels) { |
441 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
453 | dIMCTXim(im); | |
454 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
442 | 455 | chan_count); |
443 | 456 | return 0; |
444 | 457 | } |
644 | 657 | /* make sure we have good channel numbers */ |
645 | 658 | for (ch = 0; ch < chan_count; ++ch) { |
646 | 659 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
647 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
660 | dIMCTXim(im); | |
661 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
648 | 662 | return -1; |
649 | 663 | } |
650 | 664 | } |
661 | 675 | } |
662 | 676 | else { |
663 | 677 | if (chan_count <= 0 || chan_count > im->channels) { |
664 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
678 | dIMCTXim(im); | |
679 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
665 | 680 | chan_count); |
666 | 681 | return -1; |
667 | 682 | } |
681 | 696 | return count; |
682 | 697 | } |
683 | 698 | else { |
699 | dIMCTXim(im); | |
684 | 700 | i_push_error(0, "Image position outside of image"); |
685 | 701 | return -1; |
686 | 702 | } |
715 | 731 | /* make sure we have good channel numbers */ |
716 | 732 | for (ch = 0; ch < chan_count; ++ch) { |
717 | 733 | if (chans[ch] < 0 || chans[ch] >= im->channels) { |
718 | i_push_errorf(0, "No channel %d in this image", chans[ch]); | |
734 | dIMCTXim(im); | |
735 | im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]); | |
719 | 736 | return -1; |
720 | 737 | } |
721 | 738 | } |
732 | 749 | } |
733 | 750 | else { |
734 | 751 | if (chan_count <= 0 || chan_count > im->channels) { |
735 | i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", | |
752 | dIMCTXim(im); | |
753 | im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", | |
736 | 754 | chan_count); |
737 | 755 | return -1; |
738 | 756 | } |
752 | 770 | return count; |
753 | 771 | } |
754 | 772 | else { |
773 | dIMCTXim(im); | |
755 | 774 | i_push_error(0, "Image position outside of image"); |
756 | 775 | return -1; |
757 | 776 | } |
0 | /* perlio.c - Imager's interface to PerlIO | |
1 | ||
2 | */ | |
3 | #define IMAGER_NO_CONTEXT | |
4 | #include "imager.h" | |
5 | #include "EXTERN.h" | |
6 | #include "perl.h" | |
7 | #include "imperlio.h" | |
8 | ||
9 | ||
10 | static ssize_t | |
11 | perlio_reader(void *handle, void *buf, size_t count); | |
12 | static ssize_t | |
13 | perlio_writer(void *handle, const void *buf, size_t count); | |
14 | static off_t | |
15 | perlio_seeker(void *handle, off_t offset, int whence); | |
16 | static int | |
17 | perlio_closer(void *handle); | |
18 | static void | |
19 | perlio_destroy(void *handle); | |
20 | static const char *my_strerror(pTHX_ int err); | |
21 | ||
22 | #ifndef tTHX | |
23 | #define tTHX PerlInterpreter * | |
24 | #endif | |
25 | ||
26 | typedef struct { | |
27 | PerlIO *handle; | |
28 | pIMCTX; | |
29 | #ifdef MULTIPLICITY | |
30 | tTHX my_perl; | |
31 | #endif | |
32 | } im_perlio; | |
33 | ||
34 | #define dIMCTXperlio(state) dIMCTXctx(state->aIMCTX) | |
35 | ||
36 | /* | |
37 | =item im_io_new_perlio(PerlIO *) | |
38 | ||
39 | Create a new perl I/O object that reads/writes/seeks on a PerlIO | |
40 | handle. | |
41 | ||
42 | The close() handle flushes output but does not close the handle. | |
43 | ||
44 | =cut | |
45 | */ | |
46 | ||
47 | i_io_glue_t * | |
48 | im_io_new_perlio(pTHX_ PerlIO *handle) { | |
49 | im_perlio *state = mymalloc(sizeof(im_perlio)); | |
50 | dIMCTX; | |
51 | ||
52 | state->handle = handle; | |
53 | #ifdef MULTIPLICITY | |
54 | state->aTHX = aTHX; | |
55 | #endif | |
56 | state->aIMCTX = aIMCTX; | |
57 | ||
58 | return io_new_cb(state, perlio_reader, perlio_writer, | |
59 | perlio_seeker, perlio_closer, perlio_destroy); | |
60 | } | |
61 | ||
62 | static ssize_t | |
63 | perlio_reader(void *ctx, void *buf, size_t count) { | |
64 | im_perlio *state = ctx; | |
65 | dTHXa(state->my_perl); | |
66 | dIMCTXperlio(state); | |
67 | ||
68 | ssize_t result = PerlIO_read(state->handle, buf, count); | |
69 | if (result == 0 && PerlIO_error(state->handle)) { | |
70 | im_push_errorf(aIMCTX, errno, "read() failure (%s)", my_strerror(aTHX_ errno)); | |
71 | return -1; | |
72 | } | |
73 | ||
74 | return result; | |
75 | } | |
76 | ||
77 | static ssize_t | |
78 | perlio_writer(void *ctx, const void *buf, size_t count) { | |
79 | im_perlio *state = ctx; | |
80 | dTHXa(state->my_perl); | |
81 | dIMCTXperlio(state); | |
82 | ssize_t result; | |
83 | ||
84 | result = PerlIO_write(state->handle, buf, count); | |
85 | ||
86 | if (result == 0) { | |
87 | im_push_errorf(aIMCTX, errno, "write() failure (%s)", my_strerror(aTHX_ errno)); | |
88 | } | |
89 | ||
90 | return result; | |
91 | } | |
92 | ||
93 | static off_t | |
94 | perlio_seeker(void *ctx, off_t offset, int whence) { | |
95 | im_perlio *state = ctx; | |
96 | dTHXa(state->my_perl); | |
97 | dIMCTXperlio(state); | |
98 | ||
99 | if (whence != SEEK_CUR || offset != 0) { | |
100 | if (PerlIO_seek(state->handle, offset, whence) < 0) { | |
101 | im_push_errorf(aIMCTX, errno, "seek() failure (%s)", my_strerror(aTHX_ errno)); | |
102 | return -1; | |
103 | } | |
104 | } | |
105 | ||
106 | return PerlIO_tell(state->handle); | |
107 | } | |
108 | ||
109 | static int | |
110 | perlio_closer(void *ctx) { | |
111 | im_perlio *state = ctx; | |
112 | dTHXa(state->my_perl); | |
113 | dIMCTXperlio(state); | |
114 | ||
115 | if (PerlIO_flush(state->handle) < 0) { | |
116 | im_push_errorf(aIMCTX, errno, "flush() failure (%s)", my_strerror(aTHX_ errno)); | |
117 | return -1; | |
118 | } | |
119 | return 0; | |
120 | } | |
121 | ||
122 | static void | |
123 | perlio_destroy(void *ctx) { | |
124 | myfree(ctx); | |
125 | } | |
126 | ||
127 | static | |
128 | const char *my_strerror(pTHX_ int err) { | |
129 | const char *result = strerror(err); | |
130 | ||
131 | if (!result) | |
132 | result = "Unknown error"; | |
133 | ||
134 | return result; | |
135 | } | |
136 |
0 | #include "imager.h" | |
0 | #include "imdatatypes.h" | |
1 | #include "immacros.h" | |
1 | 2 | |
2 | 3 | /* structures for passing data between Imager-plugin and the Imager-module */ |
3 | 4 | |
13 | 14 | #define i_color_set(cl,r,g,b,a) (symbol_table->i_color_set(cl,r,g,b,a)) |
14 | 15 | #define i_color_info(cl) (symbol_table->i_color_info(cl)) |
15 | 16 | |
16 | #define i_img_new() (symbol_table->i_img_new()) | |
17 | #define i_img_empty(im,x,y) ((symbol_table->i_img_empty(im,x,y)) | |
18 | #define i_img_empty_ch(im,x,y,ch) ((symbol_table->i_img_empty_ch(im,x,y,ch)) | |
19 | #define i_img_exorcise(im) (symbol_table->i_img_exorcise(im)) | |
20 | #define i_img_info(im,info) (symbol_table->i_img_info(im,info)) | |
17 | #define im_get_context() (symbol_table->im_get_context_f()) | |
18 | #define i_img_empty_ch(im,x,y,ch) ((symbol_table->i_img_empty_ch_f(im_get_context(), im,x,y,ch)) | |
19 | #define i_img_exorcise(im) (symbol_table->i_img_exorcise_f(im)) | |
20 | #define i_img_info(im,info) (symbol_table->i_img_info_f(im,info)) | |
21 | 21 | |
22 | #define i_img_setmask(im,ch_mask) (symbol_table->i_img_setmask(im,ch_mask)) | |
23 | #define i_img_getmask(im) (symbol_table->i_img_getmask(im)) | |
22 | #define i_img_setmask(im,ch_mask) (symbol_table->i_img_setmask_f(im,ch_mask)) | |
23 | #define i_img_getmask(im) (symbol_table->i_img_getmask_f(im)) | |
24 | 24 | |
25 | 25 | /* |
26 | 26 | Not needed? The i_gpix() macro in image.h will call the right function |
251 | 251 | if (fabs(sz) > 0.0000001 |
252 | 252 | && sx >= -1 && sx < src->xsize |
253 | 253 | && sy >= -1 && sy < src->ysize) { |
254 | i_img_dim bx = floor(sx); | |
255 | i_img_dim by = floor(sy); | |
254 | 256 | |
255 | 257 | ROT_DEBUG(fprintf(stderr, "map " i_DFp " to %g,%g\n", i_DFcp(x, y), sx, sy)); |
256 | if (sx != (i_img_dim)sx) { | |
257 | if (sy != (i_img_dim)sy) { | |
258 | if (sx != bx) { | |
259 | if (sy != by) { | |
258 | 260 | IM_COLOR c[2][2]; |
259 | 261 | IM_COLOR ci2[2]; |
260 | 262 | ROT_DEBUG(fprintf(stderr, " both non-int\n")); |
261 | 263 | for (i = 0; i < 2; ++i) |
262 | 264 | for (j = 0; j < 2; ++j) |
263 | if (IM_GPIX(src, floor(sx)+i, floor(sy)+j, &c[j][i])) | |
265 | if (IM_GPIX(src, bx+i, by+j, &c[j][i])) | |
264 | 266 | c[j][i] = back; |
265 | 267 | for (j = 0; j < 2; ++j) |
266 | 268 | ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels); |
270 | 272 | IM_COLOR ci2[2]; |
271 | 273 | ROT_DEBUG(fprintf(stderr, " y int, x non-int\n")); |
272 | 274 | for (i = 0; i < 2; ++i) |
273 | if (IM_GPIX(src, floor(sx)+i, sy, ci2+i)) | |
275 | if (IM_GPIX(src, bx+i, sy, ci2+i)) | |
274 | 276 | ci2[i] = back; |
275 | 277 | vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels); |
276 | 278 | } |
280 | 282 | IM_COLOR ci2[2]; |
281 | 283 | ROT_DEBUG(fprintf(stderr, " x int, y non-int\n")); |
282 | 284 | for (i = 0; i < 2; ++i) |
283 | if (IM_GPIX(src, sx, floor(sy)+i, ci2+i)) | |
285 | if (IM_GPIX(src, bx, by+i, ci2+i)) | |
284 | 286 | ci2[i] = back; |
285 | 287 | vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels); |
286 | 288 | } |
401 | 403 | |
402 | 404 | #define numfmt "%23g" |
403 | 405 | |
404 | ROT_DEBUG(static dump_mat(const char *name, double *f) { | |
406 | ROT_DEBUG(static void dump_mat(const char *name, double *f) { | |
405 | 407 | fprintf(stderr, "%s:\n " numfmt " " numfmt " " numfmt "\n" |
406 | 408 | " " numfmt " " numfmt " " numfmt "\n" |
407 | 409 | " " numfmt " " numfmt " " numfmt "\n", |
415 | 417 | double xlate2[9] = { 0 }; |
416 | 418 | double temp[9], matrix[9]; |
417 | 419 | i_img_dim x1, x2, y1, y2, newxsize, newysize; |
420 | ||
421 | ROT_DEBUG(fprintf(stderr, "rotate angle %.20g\n", amount)); | |
418 | 422 | |
419 | 423 | /* first translate the centre of the image to (0,0) */ |
420 | 424 | xlate1[0] = 1; |
2 | 2 | # to make sure we get expected values |
3 | 3 | |
4 | 4 | use strict; |
5 | use Test::More tests => 433; | |
5 | use Test::More tests => 466; | |
6 | 6 | |
7 | 7 | BEGIN { use_ok(Imager => qw(:handy :all)) } |
8 | 8 | |
124 | 124 | is_color3($colors[1], 0, 255, 0, "still green"); |
125 | 125 | is_color3($colors[2], 0, 0, 255, "still blue"); |
126 | 126 | is_color3($colors[3], 0, 0, 0, "still black"); |
127 | is_deeply([ unpack("C", Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ])) ], | |
128 | [ unpack("C", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50) ], | |
129 | "colors are still correct"); | |
127 | my @samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]); | |
128 | my @expect = unpack("C*", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50); | |
129 | my $match_list = is_deeply(\@samples, \@expect, "colors are still correct"); | |
130 | my $samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]); | |
131 | my $match_scalar = is_deeply([ unpack("C*", $samples) ], | |
132 | \@expect, "colors are still correct (scalar)"); | |
133 | unless ($match_list && $match_scalar) { | |
134 | # this has been failing on a particular smoker, provide more | |
135 | # diagnostic information | |
136 | print STDERR "Pallete:\n"; | |
137 | print STDERR " $_: ", join(",", $colors[$_]->rgba), "\n" for 0..$#colors; | |
138 | print STDERR "Samples (list): ", join(",", @samples), "\n"; | |
139 | print STDERR "Samples (scalar): ", join(",", unpack("C*", $samples)), "\n"; | |
140 | print STDERR "Indexes: ", join(",", Imager::i_gpal($im_pal2, 0, 100, 0)), "\n"; | |
141 | } | |
130 | 142 | } |
131 | 143 | |
132 | 144 | # test the OO interfaces |
184 | 196 | is($impal3->type, 'paletted', "and is paletted"); |
185 | 197 | } |
186 | 198 | |
187 | { # to_rgb on incomplete image | |
199 | { | |
188 | 200 | my $im = Imager->new; |
189 | 201 | ok($im, "make empty image"); |
190 | 202 | ok(!$im->to_rgb8, "convert to rgb8"); |
191 | is($im->errstr, "empty input image", "check message"); | |
203 | is($im->errstr, "to_rgb8: empty input image", "check message"); | |
204 | is($im->bits, undef, "can't call bits on an empty image"); | |
205 | is($im->errstr, "bits: empty input image", "check message"); | |
206 | is($im->type, undef, "can't call type on an empty image"); | |
207 | is($im->errstr, "type: empty input image", "check message"); | |
208 | is($im->virtual, undef, "can't call virtual on an empty image"); | |
209 | is($im->errstr, "virtual: empty input image", "check message"); | |
210 | is($im->is_bilevel, undef, "can't call virtual on an empty image"); | |
211 | is($im->errstr, "is_bilevel: empty input image", "check message"); | |
212 | ok(!$im->getscanline(y => 0), "can't call getscanline on an empty image"); | |
213 | is($im->errstr, "getscanline: empty input image", "check message"); | |
214 | ok(!$im->setscanline(y => 0, pixels => [ $red, $blue ]), | |
215 | "can't call setscanline on an empty image"); | |
216 | is($im->errstr, "setscanline: empty input image", "check message"); | |
217 | ok(!$im->getsamples(y => 0), "can't call getsamples on an empty image"); | |
218 | is($im->errstr, "getsamples: empty input image", "check message"); | |
219 | is($im->getwidth, undef, "can't get width of empty image"); | |
220 | is($im->errstr, "getwidth: empty input image", "check message"); | |
221 | is($im->getheight, undef, "can't get height of empty image"); | |
222 | is($im->errstr, "getheight: empty input image", "check message"); | |
223 | is($im->getchannels, undef, "can't get channels of empty image"); | |
224 | is($im->errstr, "getchannels: empty input image", "check message"); | |
225 | is($im->getmask, undef, "can't get mask of empty image"); | |
226 | is($im->errstr, "getmask: empty input image", "check message"); | |
227 | is($im->setmask, undef, "can't set mask of empty image"); | |
228 | is($im->errstr, "setmask: empty input image", "check message"); | |
192 | 229 | } |
193 | 230 | |
194 | 231 | { # basic checks, 8-bit direct images |
1063 | 1100 | } |
1064 | 1101 | } |
1065 | 1102 | |
1103 | { | |
1104 | my $empty = Imager->new; | |
1105 | ok(!$empty->addtag(name => "foo", value => 1), | |
1106 | "can't addtag on an empty image"); | |
1107 | is($empty->errstr, "addtag: empty input image", | |
1108 | "check error message"); | |
1109 | ok(!$empty->settag(name => "foo", value => 1), | |
1110 | "can't settag on an empty image"); | |
1111 | is($empty->errstr, "settag: empty input image", | |
1112 | "check error message"); | |
1113 | ok(!$empty->deltag(name => "foo"), "can't deltag on an empty image"); | |
1114 | is($empty->errstr, "deltag: empty input image", | |
1115 | "check error message"); | |
1116 | ok(!$empty->tags(name => "foo"), "can't tags on an empty image"); | |
1117 | is($empty->errstr, "tags: empty input image", | |
1118 | "check error message"); | |
1119 | } | |
1120 | ||
1066 | 1121 | Imager->close_log(); |
1067 | 1122 | |
1068 | 1123 | unless ($ENV{IMAGER_KEEP_FILES}) { |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 242; | |
2 | use Test::More tests => 244; | |
3 | 3 | use Imager qw(:all :handy); |
4 | 4 | use Imager::Test qw(is_color3 is_fcolor3); |
5 | 5 | |
686 | 686 | "check values written"); |
687 | 687 | } |
688 | 688 | |
689 | { | |
690 | my $empty = Imager->new; | |
691 | ok(!$empty->masked, "fail to make a masked image from an empty"); | |
692 | is($empty->errstr, "masked: empty input image", | |
693 | "check error message"); | |
694 | } | |
695 | ||
689 | 696 | Imager->close_log(); |
690 | 697 | |
691 | 698 | unless ($ENV{IMAGER_KEEP_FILES}) { |
209 | 209 | my $im = Imager->new; |
210 | 210 | ok($im, "make empty image"); |
211 | 211 | ok(!$im->to_rgb16, "convert empty image to 16-bit"); |
212 | is($im->errstr, "empty input image", "check message"); | |
212 | is($im->errstr, "to_rgb16: empty input image", "check message"); | |
213 | 213 | } |
214 | 214 | |
215 | 215 | { # bounds checks |
165 | 165 | my $im = Imager->new; |
166 | 166 | ok($im, "make empty image"); |
167 | 167 | ok(!$im->to_rgb_double, "convert empty image to double"); |
168 | is($im->errstr, "empty input image", "check message"); | |
168 | is($im->errstr, "to_rgb_double: empty input image", "check message"); | |
169 | 169 | } |
170 | 170 | |
171 | 171 | my $psamp_outside_error = "Image position outside of image"; |
0 | 0 | #!perl -w |
1 | 1 | # some of this is tested in t01introvert.t too |
2 | 2 | use strict; |
3 | use Test::More tests => 211; | |
3 | use Test::More tests => 226; | |
4 | 4 | BEGIN { use_ok("Imager", ':handy'); } |
5 | 5 | |
6 | 6 | use Imager::Test qw(image_bounds_checks test_image is_color3 isnt_image is_color4 is_fcolor3); |
28 | 28 | print "# blacki $blacki\n"; |
29 | 29 | ok(defined $blacki && $blacki == 0, "we got the first color"); |
30 | 30 | |
31 | ok($img->colorcount() == 4, "should have 4 colors"); | |
31 | is($img->colorcount(), 4, "should have 4 colors"); | |
32 | is($img->maxcolors, 256, "maxcolors always 256"); | |
33 | ||
32 | 34 | my ($redi, $greeni, $bluei) = 1..3; |
33 | 35 | |
34 | 36 | my @all = $img->getcolors; |
589 | 591 | 0, 0, 1.0, "get a pixel in float form, make sure it's blue"); |
590 | 592 | } |
591 | 593 | |
594 | { | |
595 | my $empty = Imager->new; | |
596 | ok(!$empty->to_paletted, "can't convert an empty image"); | |
597 | is($empty->errstr, "to_paletted: empty input image", | |
598 | "check error message"); | |
599 | ||
600 | is($empty->addcolors(colors => [ $black ]), -1, | |
601 | "can't addcolors() to an empty image"); | |
602 | is($empty->errstr, "addcolors: empty input image", | |
603 | "check error message"); | |
604 | ||
605 | ok(!$empty->setcolors(colors => [ $black ]), | |
606 | "can't setcolors() to an empty image"); | |
607 | is($empty->errstr, "setcolors: empty input image", | |
608 | "check error message"); | |
609 | ||
610 | ok(!$empty->getcolors(), | |
611 | "can't getcolors() from an empty image"); | |
612 | is($empty->errstr, "getcolors: empty input image", | |
613 | "check error message"); | |
614 | ||
615 | is($empty->colorcount, -1, "can't colorcount() an empty image"); | |
616 | is($empty->errstr, "colorcount: empty input image", | |
617 | "check error message"); | |
618 | ||
619 | is($empty->maxcolors, -1, "can't maxcolors() an empty image"); | |
620 | is($empty->errstr, "maxcolors: empty input image", | |
621 | "check error message"); | |
622 | ||
623 | is($empty->findcolor(color => $blue), undef, | |
624 | "can't findcolor an empty image"); | |
625 | is($empty->errstr, "findcolor: empty input image", | |
626 | "check error message"); | |
627 | } | |
628 | ||
592 | 629 | Imager->close_log; |
593 | 630 | |
594 | 631 | unless ($ENV{IMAGER_KEEP_FILES}) { |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 252; | |
2 | use Test::More tests => 274; | |
3 | use Imager::Test qw(is_image); | |
3 | 4 | # for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03 |
4 | 5 | use IO::Seekable; |
6 | use Config; | |
5 | 7 | |
6 | 8 | BEGIN { use_ok(Imager => ':all') }; |
7 | 9 | |
226 | 228 | my $result = |
227 | 229 | eval { |
228 | 230 | $io->raw_write($data); |
231 | 1; | |
229 | 232 | }; |
230 | ok($@, "should have croaked") | |
233 | ok(!$result, "should have croaked") | |
231 | 234 | and print "# $@\n"; |
232 | 235 | } |
233 | 236 | |
833 | 836 | } |
834 | 837 | } |
835 | 838 | |
839 | SKIP: | |
840 | { | |
841 | $Config{useperlio} | |
842 | or skip "PerlIO::scalar requires perlio", 13; | |
843 | ||
844 | my $foo; | |
845 | open my $fh, "+<", \$foo; | |
846 | my $io = Imager::IO->_new_perlio($fh); | |
847 | ok($io, "perlio: make a I/O object for a perl scalar fh"); | |
848 | is($io->write("test"), 4, "perlio: check we can write"); | |
849 | is($io->seek(2, SEEK_SET), 2, "perlio: check we can seek"); | |
850 | is($io->write("more"), 4, "perlio: write some more"); | |
851 | is($io->seek(0, SEEK_SET), 0, "perlio: seek back to start"); | |
852 | my $data; | |
853 | is($io->read($data, 10), 6, "perlio: read everything back"); | |
854 | is($data, "temore", "perlio: check we read back what we wrote"); | |
855 | is($io->close, 0, "perlio: close it"); | |
856 | is($foo, "temore", "perlio: check it got to the scalar properly"); | |
857 | ||
858 | my $io2 = Imager::IO->new_fh($fh); | |
859 | ok($io2, "new_fh() can make an I/O layer object from a scalar fh"); | |
860 | close $fh; | |
861 | ||
862 | my $im = Imager->new(xsize => 10, ysize => 10); | |
863 | $foo = ""; | |
864 | open my $fh2, ">", \$foo; | |
865 | ok($im->write(fh => $fh2, type => "pnm"), "can write image to scalar fh") | |
866 | or print "# ", $im->errstr, "\n"; | |
867 | ||
868 | close $fh2; | |
869 | open my $fh3, "<", \$foo; | |
870 | my $im2 = Imager->new(fh => $fh3); | |
871 | ok($im2, "read image from a scalar fh"); | |
872 | is_image($im, $im2, "check they match"); | |
873 | } | |
874 | ||
875 | { | |
876 | tie *FOO, "IO::Tied"; | |
877 | my $io = Imager::IO->new_fh(\*FOO); | |
878 | ok($io, "tied: make a I/O object for a tied fh"); | |
879 | is($io->write("test"), 4, "tied: check we can write"); | |
880 | is($io->seek(2, SEEK_SET), 2, "tied: check we can seek"); | |
881 | is($io->write("more"), 4, "tied: write some more"); | |
882 | is($io->seek(0, SEEK_SET), 0, "tied: seek back to start"); | |
883 | my $data; | |
884 | is($io->read($data, 10), 6, "tied: read everything back"); | |
885 | is($data, "temore", "tied: check we read back what we wrote"); | |
886 | is($io->close, 0, "tied: close it"); | |
887 | is(tied(*FOO)->[0], "temore", "tied: check it got to the output properly"); | |
888 | } | |
889 | ||
836 | 890 | Imager->close_log; |
837 | 891 | |
838 | 892 | unless ($ENV{IMAGER_KEEP_FILES}) { |
867 | 921 | sub fail_seek { |
868 | 922 | return -1; |
869 | 923 | } |
924 | ||
925 | package IO::Tied; | |
926 | use base 'Tie::Handle'; | |
927 | use IO::Seekable; | |
928 | ||
929 | sub TIEHANDLE { | |
930 | return bless [ "", 0 ]; | |
931 | } | |
932 | ||
933 | sub PRINT { | |
934 | for my $entry (@_[1 .. $#_]) { | |
935 | substr($_[0][0], $_[0][1], length $entry, $entry); | |
936 | $_[0][1] += length $entry; | |
937 | } | |
938 | ||
939 | return 1; | |
940 | } | |
941 | ||
942 | sub SEEK { | |
943 | my ($self, $offset, $whence) = @_; | |
944 | ||
945 | my $newpos; | |
946 | if ($whence == SEEK_SET) { | |
947 | $newpos = $offset; | |
948 | } | |
949 | elsif ($whence == SEEK_CUR) { | |
950 | $newpos = $self->[1] + $offset; | |
951 | } | |
952 | elsif ($whence == SEEK_END) { | |
953 | $newpos = length($self->[0]) + $newpos; | |
954 | } | |
955 | else { | |
956 | return -1; | |
957 | } | |
958 | ||
959 | if ($newpos < 0) { | |
960 | return 0; | |
961 | } | |
962 | ||
963 | $self->[1] = $newpos; | |
964 | ||
965 | return 1; | |
966 | } | |
967 | ||
968 | sub TELL { | |
969 | return $_[0][1]; | |
970 | } | |
971 | ||
972 | sub READ { | |
973 | my $self = shift; | |
974 | my $outlen = $_[1]; | |
975 | my $offset = @_ > 2 ? $_[2] : 0; | |
976 | if ($self->[1] + $outlen > length $self->[0]) { | |
977 | $outlen = length($self->[0]) - $self->[1]; | |
978 | $outlen <= 0 | |
979 | and return ""; | |
980 | } | |
981 | defined $_[0] or $_[0] = ""; | |
982 | substr($_[0], $offset, $outlen) = substr($self->[0], $self->[1], $outlen); | |
983 | $self->[1] += $outlen; | |
984 | ||
985 | return $outlen; | |
986 | } |
0 | #!perl -w | |
1 | use strict; | |
2 | ||
3 | # avoiding this prologue would be nice, but it seems to be unavoidable, | |
4 | # see "It is also important to note ..." in perldoc threads | |
5 | use Config; | |
6 | my $loaded_threads; | |
7 | BEGIN { | |
8 | if ($Config{useithreads} && $] > 5.008007) { | |
9 | $loaded_threads = | |
10 | eval { | |
11 | require threads; | |
12 | threads->import; | |
13 | 1; | |
14 | }; | |
15 | } | |
16 | } | |
17 | use Test::More; | |
18 | ||
19 | $Config{useithreads} | |
20 | or plan skip_all => "can't test Imager's threads support with no threads"; | |
21 | $] > 5.008007 | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support"; | |
23 | $loaded_threads | |
24 | or plan skip_all => "couldn't load threads"; | |
25 | ||
26 | $INC{"Devel/Cover.pm"} | |
27 | and plan skip_all => "threads and Devel::Cover don't get along"; | |
28 | ||
29 | use Imager; | |
30 | ||
31 | -d "testout" or mkdir "testout"; | |
32 | ||
33 | Imager->open_log(log => "testout/t080log1.log") | |
34 | or plan skip_all => "Cannot open log file: " . Imager->errstr; | |
35 | ||
36 | plan tests => 3; | |
37 | ||
38 | Imager->log("main thread a\n"); | |
39 | ||
40 | my $t1 = threads->create | |
41 | ( | |
42 | sub { | |
43 | Imager->log("child thread a\n"); | |
44 | Imager->open_log(log => "testout/t080log2.log") | |
45 | or die "Cannot open second log file: ", Imager->errstr; | |
46 | Imager->log("child thread b\n"); | |
47 | sleep(1); | |
48 | Imager->log("child thread c\n"); | |
49 | sleep(1); | |
50 | 1; | |
51 | } | |
52 | ); | |
53 | ||
54 | Imager->log("main thread b\n"); | |
55 | sleep(1); | |
56 | Imager->log("main thread c\n"); | |
57 | ok($t1->join, "join child thread"); | |
58 | Imager->log("main thread d\n"); | |
59 | Imager->close_log(); | |
60 | ||
61 | my %log1 = parse_log("testout/t080log1.log"); | |
62 | my %log2 = parse_log("testout/t080log2.log"); | |
63 | ||
64 | my @log1 = | |
65 | ( | |
66 | "main thread a", | |
67 | "main thread b", | |
68 | "child thread a", | |
69 | "main thread c", | |
70 | "main thread d", | |
71 | ); | |
72 | ||
73 | my @log2 = | |
74 | ( | |
75 | "child thread b", | |
76 | "child thread c", | |
77 | ); | |
78 | ||
79 | is_deeply(\%log1, { map {; $_ => 1 } @log1 }, | |
80 | "check messages in main thread log"); | |
81 | is_deeply(\%log2, { map {; $_ => 1 } @log2 }, | |
82 | "check messages in child thread log"); | |
83 | ||
84 | # grab the messages from the given log | |
85 | sub parse_log { | |
86 | my ($filename) = @_; | |
87 | ||
88 | open my $fh, "<", $filename | |
89 | or die "Cannot open log file $filename: $!"; | |
90 | ||
91 | my %lines; | |
92 | while (<$fh>) { | |
93 | chomp; | |
94 | my ($date, $time, $file_line, $level, $message) = split ' ', $_, 5; | |
95 | $lines{$message} = 1; | |
96 | } | |
97 | ||
98 | delete $lines{"Imager - log started (level = 1)"}; | |
99 | delete $lines{"Imager $Imager::VERSION starting"}; | |
100 | ||
101 | return %lines; | |
102 | } | |
103 | ||
104 | END { | |
105 | unlink "testout/t080log1.log", "testout/t080log2.log" | |
106 | unless $ENV{IMAGER_KEEP_FILES}; | |
107 | } |
0 | #!perl -w | |
1 | use strict; | |
2 | ||
3 | # avoiding this prologue would be nice, but it seems to be unavoidable, | |
4 | # see "It is also important to note ..." in perldoc threads | |
5 | use Config; | |
6 | my $loaded_threads; | |
7 | BEGIN { | |
8 | if ($Config{useithreads} && $] > 5.008007) { | |
9 | $loaded_threads = | |
10 | eval { | |
11 | require threads; | |
12 | threads->import; | |
13 | 1; | |
14 | }; | |
15 | } | |
16 | } | |
17 | use Test::More; | |
18 | ||
19 | $Config{useithreads} | |
20 | or plan skip_all => "can't test Imager's threads support with no threads"; | |
21 | $] > 5.008007 | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support"; | |
23 | $loaded_threads | |
24 | or plan skip_all => "couldn't load threads"; | |
25 | ||
26 | $INC{"Devel/Cover.pm"} | |
27 | and plan skip_all => "threads and Devel::Cover don't get along"; | |
28 | ||
29 | use Imager; | |
30 | ||
31 | # test that the error contexts are separate under threads | |
32 | ||
33 | plan tests => 11; | |
34 | ||
35 | Imager->open_log(log => "testout/t081error.log"); | |
36 | ||
37 | Imager::i_clear_error(); | |
38 | Imager::i_push_error(0, "main thread a"); | |
39 | ||
40 | my @threads; | |
41 | for my $tid (1..5) { | |
42 | my $t1 = threads->create | |
43 | ( | |
44 | sub { | |
45 | my $id = shift; | |
46 | Imager::i_push_error(0, "$id: child thread a"); | |
47 | sleep(1+rand(4)); | |
48 | Imager::i_push_error(1, "$id: child thread b"); | |
49 | ||
50 | is_deeply([ Imager::i_errors() ], | |
51 | [ | |
52 | [ "$id: child thread b", 1 ], | |
53 | [ "$id: child thread a", 0 ], | |
54 | ], "$id: check errors in child"); | |
55 | 1; | |
56 | }, | |
57 | $tid | |
58 | ); | |
59 | push @threads, [ $tid, $t1 ]; | |
60 | } | |
61 | ||
62 | Imager::i_push_error(1, "main thread b"); | |
63 | ||
64 | for my $thread (@threads) { | |
65 | my ($id, $t1) = @$thread; | |
66 | ok($t1->join, "join child $id"); | |
67 | } | |
68 | ||
69 | Imager::i_push_error(2, "main thread c"); | |
70 | ||
71 | is_deeply([ Imager::i_errors() ], | |
72 | [ | |
73 | [ "main thread c", 2 ], | |
74 | [ "main thread b", 1 ], | |
75 | [ "main thread a", 0 ], | |
76 | ], "check errors in parent"); | |
77 |
0 | #!perl -w | |
1 | use strict; | |
2 | ||
3 | # avoiding this prologue would be nice, but it seems to be unavoidable, | |
4 | # see "It is also important to note ..." in perldoc threads | |
5 | use Config; | |
6 | my $loaded_threads; | |
7 | BEGIN { | |
8 | if ($Config{useithreads} && $] > 5.008007) { | |
9 | $loaded_threads = | |
10 | eval { | |
11 | require threads; | |
12 | threads->import; | |
13 | 1; | |
14 | }; | |
15 | } | |
16 | } | |
17 | use Test::More; | |
18 | ||
19 | $Config{useithreads} | |
20 | or plan skip_all => "can't test Imager's threads support with no threads"; | |
21 | $] > 5.008007 | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support"; | |
23 | $loaded_threads | |
24 | or plan skip_all => "couldn't load threads"; | |
25 | ||
26 | $INC{"Devel/Cover.pm"} | |
27 | and plan skip_all => "threads and Devel::Cover don't get along"; | |
28 | ||
29 | use Imager; | |
30 | ||
31 | # test that image file limits are localized to a thread | |
32 | ||
33 | plan tests => 31; | |
34 | ||
35 | Imager->open_log(log => "testout/t082limit.log"); | |
36 | ||
37 | ok(Imager->set_file_limits(width => 10, height => 10, bytes => 300), | |
38 | "set limits to 10, 10, 300"); | |
39 | ||
40 | ok(Imager->check_file_limits(width => 10, height => 10), | |
41 | "successful check limits in parent"); | |
42 | ||
43 | ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2), | |
44 | "failed check limits in parent"); | |
45 | ||
46 | my @threads; | |
47 | for my $tid (1..5) { | |
48 | my $t1 = threads->create | |
49 | ( | |
50 | sub { | |
51 | my $id = shift; | |
52 | my $dlimit = $tid * 5; | |
53 | my $blimit = $dlimit * $dlimit * 3; | |
54 | ok(Imager->set_file_limits(width => $dlimit, height => $dlimit, | |
55 | bytes => $blimit), | |
56 | "$tid: set limits to $dlimit x $dlimit, $blimit bytes"); | |
57 | ok(Imager->check_file_limits(width => $dlimit, height => $dlimit), | |
58 | "$tid: successful check $dlimit x $dlimit"); | |
59 | ok(!Imager->check_file_limits(width => $dlimit, height => $dlimit, sample_size => 2), | |
60 | "$tid: failed check $dlimit x $dlimit, ssize 2"); | |
61 | is_deeply([ Imager->get_file_limits ], [ $dlimit, $dlimit, $blimit ], | |
62 | "check limits are still $dlimit x $dlimit , $blimit bytes"); | |
63 | }, | |
64 | $tid | |
65 | ); | |
66 | push @threads, [ $tid, $t1 ]; | |
67 | } | |
68 | ||
69 | for my $thread (@threads) { | |
70 | my ($id, $t1) = @$thread; | |
71 | ok($t1->join, "join child $id"); | |
72 | } | |
73 | ||
74 | ok(Imager->check_file_limits(width => 10, height => 10), | |
75 | "test we still pass"); | |
76 | ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2), | |
77 | "test we still fail"); | |
78 | is_deeply([ Imager->get_file_limits ], [ 10, 10, 300 ], | |
79 | "check original main thread limits still set"); |
3 | 3 | # the file format |
4 | 4 | |
5 | 5 | use strict; |
6 | use Test::More tests => 85; | |
6 | use Test::More tests => 89; | |
7 | 7 | use Imager; |
8 | 8 | |
9 | 9 | -d "testout" or mkdir "testout"; |
145 | 145 | } |
146 | 146 | } |
147 | 147 | |
148 | { # test empty image handling for write()/write_multi() | |
149 | my $empty = Imager->new; | |
150 | my $data; | |
151 | ok(!$empty->write(data => \$data, type => "pnm"), | |
152 | "fail to write an empty image"); | |
153 | is($empty->errstr, "write: empty input image", "check error message"); | |
154 | my $good = Imager->new(xsize => 1, ysize => 1); | |
155 | ok(!Imager->write_multi({ data => \$data, type => "pnm" }, $good, $empty), | |
156 | "fail to write_multi an empty image"); | |
157 | is(Imager->errstr, "write_multi: empty input image (image 2)"); | |
158 | } | |
159 | ||
148 | 160 | # check file type probe |
149 | 161 | probe_ok("49492A41", undef, "not quite tiff"); |
150 | 162 | probe_ok("4D4D0041", undef, "not quite tiff"); |
175 | 175 | ok(!$im->read(file => 'testout/t103_empty.raw', xsize => 50, ysize=>50, type=>'raw', interleave => 1), |
176 | 176 | 'read an empty file'); |
177 | 177 | is($im->errstr, 'premature end of file', "check message"); |
178 | open RAW, "> testout/t103_empty.raw" | |
179 | or die "Cannot create testout/t103_empty.raw: $!"; | |
180 | ok(!$im->read(fh => \*RAW, , xsize => 50, ysize=>50, type=>'raw', interleave => 1), | |
181 | 'read a file open for write'); | |
182 | cmp_ok($im->errstr, '=~', '^error reading file: read\(\) failure', "check message"); | |
183 | ||
178 | SKIP: | |
179 | { | |
180 | # see 862083f7e40bc2a9e3b94aedce56c1336e7bdb25 in perl5 git | |
181 | $] >= 5.010 | |
182 | or skip "5.8.x and earlier don't treat a read on a WRONLY file as an error", 2; | |
183 | open RAW, "> testout/t103_empty.raw" | |
184 | or die "Cannot create testout/t103_empty.raw: $!"; | |
185 | ok(!$im->read(fh => \*RAW, , xsize => 50, ysize=>50, type=>'raw', interleave => 1), | |
186 | 'read a file open for write'); | |
187 | cmp_ok($im->errstr, '=~', '^error reading file: read\(\) failure', "check message"); | |
188 | } | |
184 | 189 | } |
185 | 190 | |
186 | 191 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 157; | |
2 | use Test::More tests => 165; | |
3 | 3 | |
4 | 4 | use Imager ':handy'; |
5 | 5 | use Imager::Fill; |
665 | 665 | is($im->getcolorcount, 2, "only original and fill color"); |
666 | 666 | } |
667 | 667 | |
668 | SKIP: | |
669 | { | |
670 | # the wrong image dimension was used for adjusting vs yoff, | |
671 | # producing uncovered parts of the output image | |
672 | my $tx = Imager->new(xsize => 30, ysize => 20); | |
673 | ok($tx, "create texture image") | |
674 | or diag "create texture image", Imager->errstr; | |
675 | $tx or skip "no texture image", 7; | |
676 | ok($tx->box(filled => 1, color => "ff0000"), "fill texture image") | |
677 | or diag "fill texture image", $tx->errstr; | |
678 | my $cmp = Imager->new(xsize => 100, ysize => 100); | |
679 | ok($cmp, "create comparison image") | |
680 | or diag "create comparison image: ", Imager->errstr; | |
681 | $cmp or skip "no comparison image", 5; | |
682 | ok($cmp->box(filled => 1, color => "FF0000"), "fill compare image") | |
683 | or diag "fill compare image: ", $cmp->errstr; | |
684 | my $im = Imager->new(xsize => 100, ysize => 100); | |
685 | ok($im, "make test image") | |
686 | or diag "make test image: ", Imager->errstr; | |
687 | $im or skip "no test image", 3; | |
688 | my $fill = Imager::Fill->new(image => $tx, yoff => 10); | |
689 | ok($fill, "make xoff=10 image fill") | |
690 | or diag "make fill: ", Imager->errstr; | |
691 | $fill or skip "no fill", 2; | |
692 | ok($im->box(fill => $fill), "fill test image") | |
693 | or diag "fill test image: ", $im->errstr; | |
694 | is_image($im, $cmp, "check test image"); | |
695 | } | |
696 | ||
668 | 697 | sub color_close { |
669 | 698 | my ($c1, $c2) = @_; |
670 | 699 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 244; | |
2 | use Test::More tests => 256; | |
3 | 3 | use Imager ':all'; |
4 | 4 | use Imager::Test qw(is_color3 is_image); |
5 | 5 | use constant PI => 3.14159265358979; |
296 | 296 | } |
297 | 297 | } |
298 | 298 | |
299 | { | |
300 | my $empty = Imager->new; | |
301 | ok(!$empty->box(), "can't draw box to empty image"); | |
302 | is($empty->errstr, "box: empty input image", "check error message"); | |
303 | ok(!$empty->arc(), "can't draw arc to empty image"); | |
304 | is($empty->errstr, "arc: empty input image", "check error message"); | |
305 | ok(!$empty->line(x1 => 0, y1 => 0, x2 => 10, y2 => 0), | |
306 | "can't draw line to empty image"); | |
307 | is($empty->errstr, "line: empty input image", "check error message"); | |
308 | ok(!$empty->polyline(points => [ [ 0, 0 ], [ 10, 0 ] ]), | |
309 | "can't draw polyline to empty image"); | |
310 | is($empty->errstr, "polyline: empty input image", "check error message"); | |
311 | ok(!$empty->polygon(points => [ [ 0, 0 ], [ 10, 0 ], [ 0, 10 ] ]), | |
312 | "can't draw polygon to empty image"); | |
313 | is($empty->errstr, "polygon: empty input image", "check error message"); | |
314 | ok(!$empty->flood_fill(x => 0, y => 0), "can't flood fill to empty image"); | |
315 | is($empty->errstr, "flood_fill: empty input image", "check error message"); | |
316 | } | |
317 | ||
299 | 318 | |
300 | 319 | malloc_state(); |
301 | 320 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager; |
3 | use Test::More tests => 10; | |
3 | use Test::More tests => 14; | |
4 | 4 | |
5 | 5 | unshift @INC, "t"; |
6 | 6 | |
39 | 39 | or skip("Failed to load", 1); |
40 | 40 | ok($good->isa("GoodTestFont"), "and it's the right type"); |
41 | 41 | } |
42 | ||
43 | ||
44 | use Imager::Font::Test; | |
45 | ||
46 | # check string() and align_string() handle an empty image | |
47 | { | |
48 | my $font = Imager::Font::Test->new; | |
49 | my $empty = Imager->new; | |
50 | ok(!$empty->string(text => "foo", x => 0, y => 10, size => 10, font => $font), | |
51 | "can't draw text on an empty image"); | |
52 | is($empty->errstr, "string: empty input image", | |
53 | "check error message"); | |
54 | ok(!$empty->align_string(text => "foo", x => 0, y => 10, size => 10, font => $font), | |
55 | "can't draw text on an empty image"); | |
56 | is($empty->errstr, "align_string: empty input image", | |
57 | "check error message"); | |
58 | } |
34 | 34 | my $ttraw = Imager::i_tt_new($fontname); |
35 | 35 | ok($ttraw, "create font"); |
36 | 36 | |
37 | my @bbox = i_tt_bbox($ttraw,50.0,'XMCLH',6,0); | |
37 | my @bbox = i_tt_bbox($ttraw,50.0,'XMCLH',0); | |
38 | 38 | is(@bbox, 8, "bounding box"); |
39 | 39 | print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n"; |
40 | 40 | |
76 | 76 | my $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41); |
77 | 77 | my $alttext = "A-A"; |
78 | 78 | |
79 | my @utf8box = i_tt_bbox($ttraw, 50.0, $text, length($text), 1); | |
79 | my @utf8box = i_tt_bbox($ttraw, 50.0, $text, 1); | |
80 | 80 | is(@utf8box, 8, "utf8 bbox element count"); |
81 | my @base = i_tt_bbox($ttraw, 50.0, $alttext, length($alttext), 0); | |
81 | my @base = i_tt_bbox($ttraw, 50.0, $alttext, 0); | |
82 | 82 | is(@base, 8, "alt bbox element count"); |
83 | 83 | my $maxdiff = $fontname eq $deffont ? 0 : $base[2] / 3; |
84 | 84 | print "# (@utf8box vs @base)\n"; |
105 | 105 | "draw UTF8"); |
106 | 106 | ok(i_tt_cp($ttraw, $backgr, 350, 80, 0, 14, $text, 0, 1, 0), |
107 | 107 | "cp UTF8"); |
108 | @utf8box = i_tt_bbox($ttraw, 50.0, $text, length($text), 0); | |
108 | @utf8box = i_tt_bbox($ttraw, 50.0, $text, 0); | |
109 | 109 | is(@utf8box, 8, "native utf8 bbox element count"); |
110 | 110 | ok(abs($utf8box[2] - $base[2]) <= $maxdiff, |
111 | 111 | "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)"); |
0 | #!perl -w | |
1 | use strict; | |
2 | use Imager::Test qw(std_font_tests std_font_test_count); | |
3 | use Imager::Font; | |
4 | use Test::More; | |
5 | ||
6 | $Imager::formats{tt} | |
7 | or plan skip_all => "No tt available"; | |
8 | ||
9 | Imager->open_log(log => "testout/t37std.log"); | |
10 | ||
11 | plan tests => std_font_test_count(); | |
12 | ||
13 | my $font = Imager::Font->new(file => "fontfiles/dodge.ttf", | |
14 | type => "tt"); | |
15 | my $name_font = | |
16 | Imager::Font->new(file => "fontfiles/ImUgly.ttf", | |
17 | type => "tt"); | |
18 | ||
19 | SKIP: | |
20 | { | |
21 | $font | |
22 | or skip "Cannot load font", std_font_test_count(); | |
23 | std_font_tests | |
24 | ({ | |
25 | font => $font, | |
26 | has_chars => [ 1, 1, 1 ], | |
27 | glyph_name_font => $name_font, | |
28 | glyph_names => [ qw(A uni2010 A) ], | |
29 | }); | |
30 | } | |
31 | ||
32 | Imager->close_log; |
102 | 102 | { # error handling - NULL image |
103 | 103 | my $im = Imager->new; |
104 | 104 | ok(!$im->scale(scalefactor => 0.5), "try to scale empty image"); |
105 | is($im->errstr, "empty input image", "check error message"); | |
105 | is($im->errstr, "scale: empty input image", "check error message"); | |
106 | 106 | |
107 | 107 | # scaleX/scaleY |
108 | 108 | ok(!$im->scaleX(scalefactor => 0.5), "try to scaleX empty image"); |
109 | is($im->errstr, "empty input image", "check error message"); | |
109 | is($im->errstr, "scaleX: empty input image", "check error message"); | |
110 | 110 | ok(!$im->scaleY(scalefactor => 0.5), "try to scaleY empty image"); |
111 | is($im->errstr, "empty input image", "check error message"); | |
111 | is($im->errstr, "scaleY: empty input image", "check error message"); | |
112 | 112 | } |
113 | 113 | |
114 | 114 | { # invalid qtype value |
5 | 5 | eval "use Affix::Infix2Postfix; 1;" |
6 | 6 | or plan skip_all => "No Affix::Infix2Postfix"; |
7 | 7 | |
8 | plan tests => 6; | |
8 | plan tests => 8; | |
9 | 9 | |
10 | 10 | #$Imager::DEBUG=1; |
11 | 11 | |
46 | 46 | } |
47 | 47 | } |
48 | 48 | |
49 | ||
49 | { | |
50 | my $empty = Imager->new; | |
51 | ok(!$empty->transform(xexpr => "x", yexpr => "y"), | |
52 | "fail to transform an empty image"); | |
53 | is($empty->errstr, "transform: empty input image", | |
54 | "check error message"); | |
55 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 38; | |
2 | use Test::More tests => 40; | |
3 | 3 | BEGIN { use_ok('Imager'); } |
4 | 4 | use Imager::Test qw(is_color3); |
5 | 5 | |
142 | 142 | 0 0 getp1 sat 255 * 0.01 + 0 0 rgb |
143 | 143 | EOS |
144 | 144 | |
145 | ||
146 | { | |
147 | my $empty = Imager->new; | |
148 | my $good = Imager->new(xsize => 1, ysize => 1); | |
149 | ok(!Imager::transform2({ rpnexpr => "x y getp1" }, $good, $empty), | |
150 | "can't transform an empty image"); | |
151 | is(Imager->errstr, "transform2: empty input image (input image 2)", | |
152 | "check error message"); | |
153 | } | |
154 | ||
145 | 155 | use Imager::Transform; |
146 | 156 | |
147 | 157 | # some simple tests |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager qw(:handy); |
3 | use Test::More tests => 116; | |
3 | use Test::More tests => 122; | |
4 | 4 | |
5 | 5 | -d "testout" or mkdir "testout"; |
6 | 6 | |
423 | 423 | is_image($diff2, $cmp2, "difference() - check image with mindist 1.1 - large samples"); |
424 | 424 | } |
425 | 425 | |
426 | { | |
427 | my $empty = Imager->new; | |
428 | ok(!$empty->filter(type => "hardinvert"), "can't filter an empty image"); | |
429 | is($empty->errstr, "filter: empty input image", | |
430 | "check error message"); | |
431 | ok(!$empty->difference(other => $imbase), "can't difference empty image"); | |
432 | is($empty->errstr, "difference: empty input image", | |
433 | "check error message"); | |
434 | ok(!$imbase->difference(other => $empty), | |
435 | "can't difference against empty image"); | |
436 | is($imbase->errstr, "difference: empty input image (other image)", | |
437 | "check error message"); | |
438 | } | |
439 | ||
426 | 440 | sub test { |
427 | 441 | my ($in, $params, $out) = @_; |
428 | 442 |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager qw(:handy); |
3 | use Test::More tests => 114; | |
3 | use Test::More tests => 120; | |
4 | 4 | use Imager::Test qw(is_image is_imaged); |
5 | 5 | |
6 | 6 | -d "testout" or mkdir "testout"; |
238 | 238 | } |
239 | 239 | } |
240 | 240 | |
241 | { | |
242 | my $empty = Imager->new; | |
243 | my $good = Imager->new(xsize => 1, ysize => 1); | |
244 | ok(!$empty->compose(src => $good), "can't compose to empty image"); | |
245 | is($empty->errstr, "compose: empty input image", | |
246 | "check error message"); | |
247 | ok(!$good->compose(src => $empty), "can't compose from empty image"); | |
248 | is($good->errstr, "compose: empty input image (for src)", | |
249 | "check error message"); | |
250 | ok(!$good->compose(src => $good, mask => $empty), | |
251 | "can't compose with empty mask"); | |
252 | is($good->errstr, "compose: empty input image (for mask)", | |
253 | "check error message"); | |
254 | } | |
255 | ||
241 | 256 | unless ($ENV{IMAGER_KEEP_FILES}) { |
242 | 257 | unlink @files; |
243 | 258 | } |
33 | 33 | # bad image error |
34 | 34 | my $im = Imager->new; |
35 | 35 | ok(!Imager->combine(src => [ $im ]), "empty image"); |
36 | is(Imager->errstr, "empty input image", "check message"); | |
36 | is(Imager->errstr, "combine: empty input image (src->[0])", | |
37 | "check message"); | |
37 | 38 | } |
38 | 39 | |
39 | 40 | { |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 87; | |
2 | use Test::More tests => 95; | |
3 | 3 | use Imager; |
4 | 4 | use Imager::Test qw(is_color3 is_image is_imaged test_image_double test_image isnt_image is_image_similar); |
5 | 5 | |
20 | 20 | my $diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG}); |
21 | 21 | is_image($img, $nimg, "copy matches source"); |
22 | 22 | |
23 | { | |
24 | my $empty = Imager->new; | |
25 | ok(!$empty->copy, "fail to copy an empty image"); | |
26 | is($empty->errstr, "copy: empty input image", "check error message"); | |
27 | } | |
28 | ||
23 | 29 | # test if ->flip(dir=>'h')->flip(dir=>'h') doesn't alter the image |
24 | 30 | $nimg->flip(dir=>"h")->flip(dir=>"h"); |
25 | 31 | is_image($nimg, $img, "double horiz flipped matches original"); |
32 | 38 | # test if ->flip(dir=>'h')->flip(dir=>'v') is same as ->flip(dir=>'hv') |
33 | 39 | $nimg->flip(dir=>"v")->flip(dir=>"h")->flip(dir=>"hv");; |
34 | 40 | is_image($img, $nimg, "check flip with hv matches flip v then flip h"); |
41 | ||
42 | { | |
43 | my $empty = Imager->new; | |
44 | ok(!$empty->flip(dir => "v"), "fail to flip an empty image"); | |
45 | is($empty->errstr, "flip: empty input image", "check error message"); | |
46 | } | |
35 | 47 | |
36 | 48 | { |
37 | 49 | my $imsrc = test_image_double; |
146 | 158 | |
147 | 159 | $trimg->write(file=>"testout/t64_trans_back.ppm") |
148 | 160 | or print "# Cannot save: ",$trimg->errstr,"\n"; |
161 | ||
162 | { | |
163 | my $empty = Imager->new; | |
164 | ok(!$empty->matrix_transform(matrix => [ 1, 0, 0, | |
165 | 0, 1, 0, | |
166 | 0, 0, 1 ]), | |
167 | "can't transform an empty image"); | |
168 | is($empty->errstr, "matrix_transform: empty input image", | |
169 | "check error message"); | |
170 | } | |
149 | 171 | |
150 | 172 | sub rot_test { |
151 | 173 | my ($src, $degrees, $count) = @_; |
246 | 268 | is($deg->getwidth, 150, "check degrees => 270 width"); |
247 | 269 | is($deg->getheight, 150, "check degrees => 270 height"); |
248 | 270 | ok($deg->write(file => "testout/t64rotdeg270.ppm"), "save it"); |
249 | # deg->write(file => "testout/t64rotright270.ppm"); | |
271 | $right->write(file => "testout/t64rotright270.ppm"); | |
250 | 272 | is_image($deg, $right, "check right and degrees result the same"); |
251 | 273 | #$deg = $deg->convert(preset => "addalpha"); |
252 | 274 | # $right = $right->convert(preset => "addalpha"); |
253 | 275 | # my $diff = $right->difference(other => $deg, mindist => 1); |
254 | 276 | # $diff->write(file => "testout/t64rotdiff.png"); |
255 | 277 | } |
278 | ||
279 | { | |
280 | my $empty = Imager->new; | |
281 | ok(!$empty->rotate(degrees => 90), "can't rotate an empty image"); | |
282 | is($empty->errstr, "rotate: empty input image", | |
283 | "check error message"); | |
284 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 64; | |
2 | use Test::More tests => 66; | |
3 | 3 | use Imager; |
4 | 4 | use Imager::Test qw(test_image); |
5 | 5 | |
181 | 181 | "outside of image" ); |
182 | 182 | cmp_ok($src->errstr, '=~', qr/outside of the image/, "and message"); |
183 | 183 | } |
184 | ||
185 | { | |
186 | my $empty = Imager->new; | |
187 | ok(!$empty->crop(left => 10), "can't crop an empty image"); | |
188 | is($empty->errstr, "crop: empty input image", "check message"); | |
189 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 54; | |
2 | use Test::More tests => 60; | |
3 | 3 | |
4 | 4 | use Imager; |
5 | 5 | use Imager::Test qw(is_image); |
23 | 23 | |
24 | 24 | ok($img->write(type=>'pnm',file=>'testout/t66.ppm'), "save it") |
25 | 25 | or print "# ", $img->errstr, "\n"; |
26 | ||
27 | { | |
28 | my $empty = Imager->new; | |
29 | ok(!$empty->paste(src => $nimg), "paste into empty image"); | |
30 | is($empty->errstr, "paste: empty input image", | |
31 | "check error message"); | |
32 | ||
33 | ok(!$img->paste(src => $empty), "paste from empty image"); | |
34 | is($img->errstr, "paste: empty input image (for src)", | |
35 | "check error message"); | |
36 | ||
37 | ok(!$img->paste(), "no source image"); | |
38 | is($img->errstr, "no source image"); | |
39 | } | |
26 | 40 | |
27 | 41 | # more stringent tests |
28 | 42 | { |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | 2 | use Imager qw(:all :handy); |
3 | use Test::More tests => 29; | |
3 | use Test::More tests => 31; | |
4 | 4 | use Imager::Test qw(test_colorf_gpix is_fcolor1 is_fcolor3); |
5 | 5 | |
6 | 6 | -d "testout" or mkdir "testout"; |
148 | 148 | is($im->errstr, "convert: invalid matrix: element 0 is not an array ref", |
149 | 149 | "check the error message"); |
150 | 150 | } |
151 | ||
152 | { | |
153 | my $empty = Imager->new; | |
154 | ok(!$empty->convert(preset => "addalpha"), "can't convert an empty image"); | |
155 | is($empty->errstr, "convert: empty input image", "check error message"); | |
156 | } |
0 | #!perl -w | |
1 | use strict; | |
2 | use Test::More tests => 8; | |
3 | ||
0 | 4 | -d "testout" or mkdir "testout"; |
1 | 5 | |
2 | 6 | Imager::init("log"=>'testout/t68map.log'); |
3 | 7 | |
4 | 8 | use Imager qw(:all :handy); |
5 | 9 | |
6 | print "1..5\n"; | |
7 | ||
8 | 10 | my $imbase = Imager::ImgRaw::new(200,300,3); |
9 | 11 | |
10 | 12 | |
11 | @map1 = map { int($_/2) } 0..255; | |
12 | @map2 = map { 255-int($_/2) } 0..255; | |
13 | @map3 = 0..255; | |
14 | @maps = 0..24; | |
15 | @mapl = 0..400; | |
13 | my @map1 = map { int($_/2) } 0..255; | |
14 | my @map2 = map { 255-int($_/2) } 0..255; | |
15 | my @map3 = 0..255; | |
16 | my @maps = 0..24; | |
17 | my @mapl = 0..400; | |
16 | 18 | |
17 | $tst = 1; | |
19 | my $tst = 1; | |
18 | 20 | |
19 | i_map($imbase, [ [], [], \@map1 ]); | |
20 | print "ok ".$tst++."\n"; | |
21 | i_map($imbase, [ \@map1, \@map1, \@map1 ]); | |
21 | ok(i_map($imbase, [ [], [], \@map1 ]), "map1 in ch 3"); | |
22 | ok(i_map($imbase, [ \@map1, \@map1, \@map1 ]), "map1 in ch1-3"); | |
22 | 23 | |
23 | print "ok ".$tst++."\n"; | |
24 | i_map($imbase, [ \@map1, \@map2, \@map3 ]); | |
24 | ok(i_map($imbase, [ \@map1, \@map2, \@map3 ]), "map1-3 in ch 1-3"); | |
25 | 25 | |
26 | print "ok ".$tst++."\n"; | |
27 | i_map($imbase, [ \@maps, \@mapl, \@map3 ]); | |
26 | ok(i_map($imbase, [ \@maps, \@mapl, \@map3 ]), "incomplete maps"); | |
28 | 27 | |
29 | 28 | # test the highlevel interface |
30 | 29 | # currently this requires visual inspection of the output files |
31 | 30 | |
31 | SKIP: { | |
32 | my $im = Imager->new; | |
33 | $im->read(file=>'testimg/scale.ppm') | |
34 | or skip "Cannot load test image testimg/scale.ppm", 2; | |
32 | 35 | |
33 | my $im = Imager->new; | |
34 | if ($im->read(file=>'testimg/scale.ppm')) { | |
35 | print( ( $im->map(red=>\@map1, green=>\@map2, blue=>\@map3) ? "ok " : "not ok ").$tst++."\n" ); | |
36 | print( ( $im->map(maps=>[\@map1, [], \@map2]) ? "ok " : "not ok "). $tst++."\n"); | |
36 | ok( $im->map(red=>\@map1, green=>\@map2, blue=>\@map3), | |
37 | "test OO interface (maps by color)"); | |
38 | ok( $im->map(maps=>[\@map1, [], \@map2]), | |
39 | "test OO interface (maps by maps)"); | |
37 | 40 | } |
38 | else { | |
39 | die "could not load testout/scale.ppm\n"; | |
41 | ||
42 | { | |
43 | my $empty = Imager->new; | |
44 | ok(!$empty->map(maps => [ \@map1, \@map2, \@map3 ]), | |
45 | "can't map an empty image"); | |
46 | is($empty->errstr, "map: empty input image", "check error message"); | |
40 | 47 | } |
74 | 74 | { # check empty image errors |
75 | 75 | my $empty = Imager->new; |
76 | 76 | ok(!$empty->rubthrough(src => $oosrc), "check empty target"); |
77 | is($empty->errstr, 'empty input image', "check error message"); | |
77 | is($empty->errstr, 'rubthrough: empty input image', "check error message"); | |
78 | 78 | ok(!$oogtarg->rubthrough(src=>$empty), "check empty source"); |
79 | is($oogtarg->errstr, 'empty input image for src', | |
79 | is($oogtarg->errstr, 'rubthrough: empty input image (for src)', | |
80 | 80 | "check error message"); |
81 | 81 | } |
82 | 82 |
18 | 18 | |
19 | 19 | -d "testout" or mkdir "testout"; |
20 | 20 | |
21 | plan tests => 115; | |
21 | print STDERR "Inline version $Inline::VERSION\n"; | |
22 | ||
23 | plan tests => 117; | |
22 | 24 | require Inline; |
23 | 25 | Inline->import(with => 'Imager'); |
24 | 26 | Inline->import("FORCE"); # force rebuild |
409 | 411 | return i_psampf(im, 0, 1, 0, samps, NULL, chan_count); |
410 | 412 | } |
411 | 413 | |
414 | int | |
415 | test_mutex() { | |
416 | i_mutex_t m; | |
417 | ||
418 | m = i_mutex_new(); | |
419 | i_mutex_lock(m); | |
420 | i_mutex_unlock(m); | |
421 | i_mutex_destroy(m); | |
422 | ||
423 | return 1; | |
424 | } | |
425 | ||
426 | int | |
427 | test_slots() { | |
428 | im_slot_t slot = im_context_slot_new(NULL); | |
429 | ||
430 | if (im_context_slot_get(aIMCTX, slot)) { | |
431 | fprintf(stderr, "slots should default to NULL\n"); | |
432 | return 0; | |
433 | } | |
434 | if (!im_context_slot_set(aIMCTX, slot, &slot)) { | |
435 | fprintf(stderr, "set slot failed\n"); | |
436 | return 0; | |
437 | } | |
438 | ||
439 | if (im_context_slot_get(aIMCTX, slot) != &slot) { | |
440 | fprintf(stderr, "get slot didn't match\n"); | |
441 | return 0; | |
442 | } | |
443 | ||
444 | return 1; | |
445 | } | |
446 | ||
412 | 447 | EOS |
413 | 448 | |
414 | 449 | my $im = Imager->new(xsize=>50, ysize=>50); |
625 | 660 | is($im->type, "paletted", "make sure we kept the image type"); |
626 | 661 | } |
627 | 662 | |
663 | ok(test_mutex(), "call mutex APIs"); | |
664 | ||
665 | ok(test_slots(), "call slot APIs"); | |
666 | ||
628 | 667 | sub _get_error { |
629 | 668 | my @errors = Imager::i_errors(); |
630 | 669 | return join(": ", map $_->[0], @errors); |
0 | #!perl -w | |
1 | # | |
2 | # this tests both the Inline interface and the API with IMAGER_NO_CONTEXT | |
3 | use strict; | |
4 | use Test::More; | |
5 | use Imager::Test qw(is_color3 is_color4); | |
6 | eval "require Inline::C;"; | |
7 | plan skip_all => "Inline required for testing API" if $@; | |
8 | ||
9 | eval "require Parse::RecDescent;"; | |
10 | plan skip_all => "Could not load Parse::RecDescent" if $@; | |
11 | ||
12 | use Cwd 'getcwd'; | |
13 | plan skip_all => "Inline won't work in directories with spaces" | |
14 | if getcwd() =~ / /; | |
15 | ||
16 | plan skip_all => "perl 5.005_04, 5.005_05 too buggy" | |
17 | if $] =~ /^5\.005_0[45]$/; | |
18 | ||
19 | -d "testout" or mkdir "testout"; | |
20 | ||
21 | plan tests => 5; | |
22 | require Inline; | |
23 | Inline->import(C => Config => AUTO_INCLUDE => "#define IMAGER_NO_CONTEXT\n"); | |
24 | Inline->import(with => 'Imager'); | |
25 | Inline->import("FORCE"); # force rebuild | |
26 | #Inline->import(C => Config => OPTIMIZE => "-g"); | |
27 | ||
28 | Inline->bind(C => <<'EOS'); | |
29 | #include <math.h> | |
30 | ||
31 | Imager make_10x10() { | |
32 | dIMCTX; | |
33 | i_img *im = i_img_8_new(10, 10, 3); | |
34 | i_color c; | |
35 | c.channel[0] = c.channel[1] = c.channel[2] = 255; | |
36 | i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c); | |
37 | ||
38 | return im; | |
39 | } | |
40 | ||
41 | void error_dIMCTX() { | |
42 | dIMCTX; | |
43 | im_clear_error(aIMCTX); | |
44 | im_push_error(aIMCTX, 0, "test1"); | |
45 | im_push_errorf(aIMCTX, 0, "test%d", 2); | |
46 | ||
47 | im_log((aIMCTX, 0, "test logging\n")); | |
48 | } | |
49 | ||
50 | void error_dIMCTXim(Imager im) { | |
51 | dIMCTXim(im); | |
52 | im_clear_error(aIMCTX); | |
53 | im_push_error(aIMCTX, 0, "test1"); | |
54 | } | |
55 | ||
56 | int context_refs() { | |
57 | dIMCTX; | |
58 | ||
59 | im_context_refinc(aIMCTX, "context_refs"); | |
60 | im_context_refdec(aIMCTX, "context_refs"); | |
61 | ||
62 | return 1; | |
63 | } | |
64 | ||
65 | EOS | |
66 | ||
67 | Imager->open_log(log => "testout/t84inlinectx.log"); | |
68 | ||
69 | my $im2 = make_10x10(); | |
70 | ok($im2, "make an image"); | |
71 | is_color3($im2->getpixel(x => 0, y => 0), 255, 255, 255, | |
72 | "check the colors"); | |
73 | error_dIMCTX(); | |
74 | is(_get_error(), "test2: test1", "check dIMCTX"); | |
75 | ||
76 | my $im = Imager->new(xsize => 1, ysize => 1); | |
77 | error_dIMCTXim($im); | |
78 | is(_get_error(), "test1", "check dIMCTXim"); | |
79 | ||
80 | ok(context_refs(), "check refcount functions"); | |
81 | ||
82 | Imager->close_log(); | |
83 | ||
84 | unless ($ENV{IMAGER_KEEP_FILES}) { | |
85 | unlink "testout/t84inlinectx.log"; | |
86 | } | |
87 | ||
88 | sub _get_error { | |
89 | my @errors = Imager::i_errors(); | |
90 | return join(": ", map $_->[0], @errors); | |
91 | } |
0 | 0 | #!perl -w |
1 | 1 | use strict; |
2 | use Test::More tests => 16; | |
2 | use Test::More tests => 22; | |
3 | 3 | |
4 | 4 | use Imager; |
5 | 5 | |
68 | 68 | $im_g->getcolorusagehash, |
69 | 69 | 'color usage hash (grey)'); |
70 | 70 | } |
71 | ||
72 | { | |
73 | my $empty = Imager->new; | |
74 | is($empty->getcolorcount, undef, "can't getcolorcount an empty image"); | |
75 | is($empty->errstr, "getcolorcount: empty input image", | |
76 | "check error message"); | |
77 | is($empty->getcolorusagehash, undef, "can't getcolorusagehash an empty image"); | |
78 | is($empty->errstr, "getcolorusagehash: empty input image", | |
79 | "check error message"); | |
80 | is($empty->getcolorusage, undef, "can't getcolorusage an empty image"); | |
81 | is($empty->errstr, "getcolorusage: empty input image", | |
82 | "check error message"); | |
83 | } |
17 | 17 | use Test::More; |
18 | 18 | |
19 | 19 | $Config{useithreads} |
20 | or plan skip_all => "can't test Imager's lack of threads support with no threads"; | |
20 | or plan skip_all => "can't test Imager's threads support with no threads"; | |
21 | 21 | $] > 5.008007 |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's lack of threads support"; | |
22 | or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support"; | |
23 | 23 | $loaded_threads |
24 | 24 | or plan skip_all => "couldn't load threads"; |
25 | 25 | |
31 | 31 | $Test::More::VERSION =~ /^2\.00_/ |
32 | 32 | and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*"; |
33 | 33 | |
34 | plan tests => 11; | |
34 | plan tests => 13; | |
35 | 35 | |
36 | 36 | my $thread = threads->create(sub { 1; }); |
37 | 37 | ok($thread->join, "join first thread"); |
38 | 38 | |
39 | # these are all, or contain, XS allocated objects, if we don't | |
40 | # probably handle CLONE requests, or provide a CLONE_SKIP, we'll | |
41 | # probably see a double-free, one from the thread, and the other from | |
42 | # the main line of control. | |
39 | # these are all, or contain, XS allocated objects, if we don't handle | |
40 | # CLONE requests, or provide a CLONE_SKIP, we'll probably see a | |
41 | # double-free, one from the thread, and the other from the main line | |
42 | # of control. | |
43 | # | |
43 | 44 | # So make one of each |
44 | 45 | |
45 | 46 | my $im = Imager->new(xsize => 10, ysize => 10); |
80 | 81 | ( |
81 | 82 | sub { |
82 | 83 | ok(!UNIVERSAL::isa($im->{IMG}, "Imager::ImgRaw"), |
83 | "the low level image object should be undef"); | |
84 | "the low level image object should become unblessed"); | |
85 | ok(!$im->_valid_image, "image no longer considered valid"); | |
86 | is($im->errstr, "images do not cross threads", | |
87 | "check error message"); | |
84 | 88 | 1; |
85 | 89 | } |
86 | 90 | ); |