]>
Commit | Line | Data |
---|---|---|
92bda632 | 1 | #include "imager.h" |
02d1d628 AMH |
2 | #include "tiffio.h" |
3 | #include "iolayer.h" | |
92bda632 | 4 | #include "imageri.h" |
5c829fcf | 5 | |
02d1d628 AMH |
6 | /* |
7 | =head1 NAME | |
8 | ||
9 | tiff.c - implements reading and writing tiff files, uses io layer. | |
10 | ||
11 | =head1 SYNOPSIS | |
12 | ||
13 | io_glue *ig = io_new_fd( fd ); | |
14 | i_img *im = i_readtiff_wiol(ig, -1); // no limit on how much is read | |
15 | // or | |
16 | io_glue *ig = io_new_fd( fd ); | |
17 | return_code = i_writetiff_wiol(im, ig); | |
18 | ||
19 | =head1 DESCRIPTION | |
20 | ||
21 | tiff.c implements the basic functions to read and write tiff files. | |
22 | It uses the iolayer and needs either a seekable source or an entire | |
23 | memory mapped buffer. | |
24 | ||
25 | =head1 FUNCTION REFERENCE | |
26 | ||
27 | Some of these functions are internal. | |
28 | ||
b8c2033e | 29 | =over |
02d1d628 AMH |
30 | |
31 | =cut | |
32 | */ | |
33 | ||
5c829fcf AMH |
34 | #define byteswap_macro(x) \ |
35 | ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ | |
36 | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) | |
37 | ||
fd9a31d2 TC |
38 | struct tag_name { |
39 | char *name; | |
40 | uint32 tag; | |
41 | }; | |
42 | ||
43 | static struct tag_name text_tag_names[] = | |
44 | { | |
45 | { "tiff_documentname", TIFFTAG_DOCUMENTNAME, }, | |
46 | { "tiff_imagedescription", TIFFTAG_IMAGEDESCRIPTION, }, | |
47 | { "tiff_make", TIFFTAG_MAKE, }, | |
48 | { "tiff_model", TIFFTAG_MODEL, }, | |
49 | { "tiff_pagename", TIFFTAG_PAGENAME, }, | |
50 | { "tiff_software", TIFFTAG_SOFTWARE, }, | |
51 | { "tiff_datetime", TIFFTAG_DATETIME, }, | |
52 | { "tiff_artist", TIFFTAG_ARTIST, }, | |
53 | { "tiff_hostcomputer", TIFFTAG_HOSTCOMPUTER, }, | |
54 | }; | |
55 | ||
56 | static const int text_tag_count = | |
57 | sizeof(text_tag_names) / sizeof(*text_tag_names); | |
5c829fcf | 58 | |
5bb828f1 TC |
59 | static void error_handler(char const *module, char const *fmt, va_list ap) { |
60 | i_push_errorvf(0, fmt, ap); | |
61 | } | |
62 | ||
ffeb4a67 TC |
63 | #define WARN_BUFFER_LIMIT 10000 |
64 | static char *warn_buffer = NULL; | |
65 | static int warn_buffer_size = 0; | |
66 | ||
be371490 | 67 | static void warn_handler(char const *module, char const *fmt, va_list ap) { |
ffeb4a67 TC |
68 | char buf[1000]; |
69 | ||
70 | buf[0] = '\0'; | |
71 | #ifdef HAVE_SNPRINTF | |
72 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
73 | #else | |
74 | vsprintf(buf, fmt, ap); | |
75 | #endif | |
76 | if (!warn_buffer || strlen(warn_buffer)+strlen(buf)+2 > warn_buffer_size) { | |
77 | int new_size = warn_buffer_size + strlen(buf) + 2; | |
78 | char *old_buffer = warn_buffer; | |
79 | if (new_size > WARN_BUFFER_LIMIT) { | |
80 | new_size = WARN_BUFFER_LIMIT; | |
81 | } | |
82 | warn_buffer = myrealloc(warn_buffer, new_size); | |
83 | if (!old_buffer) *warn_buffer = '\0'; | |
84 | warn_buffer_size = new_size; | |
85 | } | |
86 | if (strlen(warn_buffer)+strlen(buf)+2 <= warn_buffer_size) { | |
87 | strcat(warn_buffer, buf); | |
88 | strcat(warn_buffer, "\n"); | |
89 | } | |
be371490 TC |
90 | } |
91 | ||
5bb828f1 TC |
92 | static int save_tiff_tags(TIFF *tif, i_img *im); |
93 | ||
94 | static void expand_4bit_hl(unsigned char *buf, int count); | |
95 | ||
f62b2d84 TC |
96 | static void pack_4bit_hl(unsigned char *buf, int count); |
97 | ||
caa833d5 AMH |
98 | |
99 | static toff_t sizeproc(thandle_t x) { | |
100 | return 0; | |
101 | } | |
102 | ||
103 | ||
02d1d628 AMH |
104 | /* |
105 | =item comp_seek(h, o, w) | |
106 | ||
107 | Compatability for 64 bit systems like latest freebsd (internal) | |
108 | ||
109 | h - tiff handle, cast an io_glue object | |
110 | o - offset | |
111 | w - whence | |
112 | ||
113 | =cut | |
114 | */ | |
115 | ||
116 | static | |
117 | toff_t | |
118 | comp_seek(thandle_t h, toff_t o, int w) { | |
119 | io_glue *ig = (io_glue*)h; | |
120 | return (toff_t) ig->seekcb(ig, o, w); | |
121 | } | |
122 | ||
e18f39b3 TC |
123 | /* |
124 | =item comp_mmap(thandle_t, tdata_t*, toff_t*) | |
125 | ||
126 | Dummy mmap stub. | |
127 | ||
128 | This shouldn't ever be called but newer tifflibs want it anyway. | |
129 | ||
130 | =cut | |
131 | */ | |
132 | ||
133 | static | |
134 | int | |
135 | comp_mmap(thandle_t h, tdata_t*p, toff_t*off) { | |
136 | return -1; | |
137 | } | |
138 | ||
139 | /* | |
140 | =item comp_munmap(thandle_t h, tdata_t p, toff_t off) | |
141 | ||
142 | Dummy munmap stub. | |
143 | ||
144 | This shouldn't ever be called but newer tifflibs want it anyway. | |
145 | ||
146 | =cut | |
147 | */ | |
148 | ||
149 | static void | |
150 | comp_munmap(thandle_t h, tdata_t p, toff_t off) { | |
151 | /* do nothing */ | |
152 | } | |
153 | ||
d87dc9a4 | 154 | static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) { |
02d1d628 AMH |
155 | i_img *im; |
156 | uint32 width, height; | |
157 | uint16 channels; | |
5bb828f1 | 158 | uint32* raster = NULL; |
02d1d628 | 159 | int tiled, error; |
faa9b3e7 TC |
160 | float xres, yres; |
161 | uint16 resunit; | |
162 | int gotXres, gotYres; | |
fd9a31d2 | 163 | uint16 photometric; |
5bb828f1 | 164 | uint16 bits_per_sample; |
fd9a31d2 | 165 | int i; |
5bb828f1 | 166 | int ch; |
02d1d628 AMH |
167 | |
168 | error = 0; | |
169 | ||
02d1d628 AMH |
170 | TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); |
171 | TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); | |
5bb828f1 | 172 | TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels); |
02d1d628 | 173 | tiled = TIFFIsTiled(tif); |
5bb828f1 TC |
174 | TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric); |
175 | TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); | |
02d1d628 AMH |
176 | |
177 | mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels)); | |
178 | mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not ")); | |
179 | mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not ")); | |
faa9b3e7 | 180 | |
a50608d2 TC |
181 | /* separated defaults to CMYK, but if the user is using some strange |
182 | ink system we can't work out the color anyway */ | |
183 | if (photometric == PHOTOMETRIC_SEPARATED && channels >= 4) { | |
184 | /* TIFF can have more than one alpha channel on an image, | |
185 | but Imager can't, only store the first one */ | |
186 | ||
187 | channels = channels == 4 ? 3 : 4; | |
188 | ||
189 | /* unfortunately the RGBA functions don't try to deal with the alpha | |
190 | channel on CMYK images, at some point I'm planning on expanding | |
191 | TIFF support to handle 16-bit/sample images and I'll deal with | |
192 | it then */ | |
193 | } | |
194 | ||
195 | /* TIFF images can have more than one alpha channel, but Imager can't | |
196 | this ignores the possibility of 2 channel images with 2 alpha, | |
197 | but there's not much I can do about that */ | |
198 | if (channels > 4) | |
199 | channels = 4; | |
200 | ||
5bb828f1 TC |
201 | if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) { |
202 | channels = 3; | |
203 | im = i_img_pal_new(width, height, channels, 256); | |
204 | } | |
205 | else { | |
206 | im = i_img_empty_ch(NULL, width, height, channels); | |
207 | } | |
8c3af7b3 TC |
208 | |
209 | if (!im) | |
210 | return NULL; | |
f00e06a0 TC |
211 | |
212 | /* general metadata */ | |
213 | i_tags_addn(&im->tags, "tiff_bitspersample", 0, bits_per_sample); | |
214 | i_tags_addn(&im->tags, "tiff_photometric", 0, photometric); | |
5bb828f1 | 215 | |
fd9a31d2 | 216 | /* resolution tags */ |
5bb828f1 | 217 | TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit); |
faa9b3e7 TC |
218 | gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres); |
219 | gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres); | |
220 | if (gotXres || gotYres) { | |
221 | if (!gotXres) | |
222 | xres = yres; | |
223 | else if (!gotYres) | |
224 | yres = xres; | |
3cff89e2 | 225 | i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit); |
faa9b3e7 TC |
226 | if (resunit == RESUNIT_CENTIMETER) { |
227 | /* from dots per cm to dpi */ | |
228 | xres *= 2.54; | |
229 | yres *= 2.54; | |
616b2541 | 230 | i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "centimeter", -1, 0); |
faa9b3e7 | 231 | } |
3cff89e2 | 232 | else if (resunit == RESUNIT_NONE) { |
faa9b3e7 | 233 | i_tags_addn(&im->tags, "i_aspect_only", 0, 1); |
3cff89e2 TC |
234 | i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "none", -1, 0); |
235 | } | |
236 | else if (resunit == RESUNIT_INCH) { | |
237 | i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "inch", -1, 0); | |
238 | } | |
239 | else { | |
240 | i_tags_add(&im->tags, "tiff_resolutionunit_name", 0, "unknown", -1, 0); | |
241 | } | |
2e41e30b TC |
242 | /* tifflib doesn't seem to provide a way to get to the original rational |
243 | value of these, which would let me provide a more reasonable | |
244 | precision. So make up a number. */ | |
245 | i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6); | |
246 | i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6); | |
faa9b3e7 | 247 | } |
fd9a31d2 TC |
248 | |
249 | /* Text tags */ | |
250 | for (i = 0; i < text_tag_count; ++i) { | |
251 | char *data; | |
252 | if (TIFFGetField(tif, text_tag_names[i].tag, &data)) { | |
253 | mm_log((1, "i_readtiff_wiol: tag %d has value %s\n", | |
254 | text_tag_names[i].tag, data)); | |
255 | i_tags_add(&im->tags, text_tag_names[i].name, 0, data, | |
256 | strlen(data), 0); | |
257 | } | |
258 | } | |
8c3af7b3 TC |
259 | |
260 | i_tags_add(&im->tags, "i_format", 0, "tiff", -1, 0); | |
ffeb4a67 TC |
261 | if (warn_buffer && *warn_buffer) { |
262 | i_tags_add(&im->tags, "i_warning", 0, warn_buffer, -1, 0); | |
263 | *warn_buffer = '\0'; | |
264 | } | |
02d1d628 AMH |
265 | |
266 | /* TIFFPrintDirectory(tif, stdout, 0); good for debugging */ | |
02d1d628 | 267 | |
5bb828f1 TC |
268 | if (photometric == PHOTOMETRIC_PALETTE && |
269 | (bits_per_sample == 4 || bits_per_sample == 8)) { | |
270 | uint16 *maps[3]; | |
271 | char used[256]; | |
272 | int maxused; | |
273 | uint32 row, col; | |
274 | unsigned char *buffer; | |
02d1d628 | 275 | |
5bb828f1 TC |
276 | if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) { |
277 | i_push_error(0, "Cannot get colormap for paletted image"); | |
5bb828f1 | 278 | i_img_destroy(im); |
02d1d628 AMH |
279 | return NULL; |
280 | } | |
5bb828f1 TC |
281 | buffer = (unsigned char *)_TIFFmalloc(width+2); |
282 | if (!buffer) { | |
283 | i_push_error(0, "out of memory"); | |
5bb828f1 | 284 | i_img_destroy(im); |
5bb828f1 TC |
285 | return NULL; |
286 | } | |
287 | row = 0; | |
288 | memset(used, 0, sizeof(used)); | |
289 | while (row < height && TIFFReadScanline(tif, buffer, row, 0) > 0) { | |
290 | if (bits_per_sample == 4) | |
291 | expand_4bit_hl(buffer, (width+1)/2); | |
292 | for (col = 0; col < width; ++col) { | |
293 | used[buffer[col]] = 1; | |
02d1d628 | 294 | } |
5bb828f1 TC |
295 | i_ppal(im, 0, width, row, buffer); |
296 | ++row; | |
02d1d628 | 297 | } |
5bb828f1 | 298 | if (row < height) { |
d87dc9a4 | 299 | if (allow_incomplete) { |
9c106321 TC |
300 | i_tags_setn(&im->tags, "i_lines_read", row); |
301 | } | |
302 | else { | |
303 | i_img_destroy(im); | |
304 | _TIFFfree(buffer); | |
305 | return NULL; | |
306 | } | |
5bb828f1 | 307 | error = 1; |
02d1d628 | 308 | } |
5bb828f1 TC |
309 | /* Ideally we'd optimize the palette, but that could be expensive |
310 | since we'd have to re-index every pixel. | |
311 | ||
312 | Optimizing the palette (even at this level) might not | |
313 | be what the user wants, so I don't do it. | |
314 | ||
315 | We'll add a function to optimize a paletted image instead. | |
316 | */ | |
317 | maxused = (1 << bits_per_sample)-1; | |
318 | if (!error) { | |
319 | while (maxused >= 0 && !used[maxused]) | |
320 | --maxused; | |
321 | } | |
322 | for (i = 0; i < 1 << bits_per_sample; ++i) { | |
323 | i_color c; | |
324 | for (ch = 0; ch < 3; ++ch) { | |
325 | c.channel[ch] = Sample16To8(maps[ch][i]); | |
326 | } | |
327 | i_addcolors(im, &c, 1); | |
328 | } | |
329 | _TIFFfree(buffer); | |
330 | } | |
331 | else { | |
332 | if (tiled) { | |
333 | int ok = 1; | |
334 | uint32 row, col; | |
335 | uint32 tile_width, tile_height; | |
336 | ||
337 | TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width); | |
338 | TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height); | |
339 | mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height)); | |
02d1d628 | 340 | |
5bb828f1 TC |
341 | raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32)); |
342 | if (!raster) { | |
343 | i_img_destroy(im); | |
344 | i_push_error(0, "No space for raster buffer"); | |
5bb828f1 | 345 | return NULL; |
02d1d628 AMH |
346 | } |
347 | ||
5bb828f1 TC |
348 | for( row = 0; row < height; row += tile_height ) { |
349 | for( col = 0; ok && col < width; col += tile_width ) { | |
350 | uint32 i_row, x, newrows, newcols; | |
351 | ||
352 | /* Read the tile into an RGBA array */ | |
353 | if (!TIFFReadRGBATile(tif, col, row, raster)) { | |
354 | ok = 0; | |
355 | break; | |
356 | } | |
357 | newrows = (row+tile_height > height) ? height-row : tile_height; | |
358 | mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows)); | |
359 | newcols = (col+tile_width > width ) ? width-row : tile_width; | |
360 | for( i_row = 0; i_row < tile_height; i_row++ ) { | |
361 | for(x = 0; x < newcols; x++) { | |
362 | i_color val; | |
363 | uint32 temp = raster[x+tile_width*(tile_height-i_row-1)]; | |
364 | val.rgba.r = TIFFGetR(temp); | |
365 | val.rgba.g = TIFFGetG(temp); | |
366 | val.rgba.b = TIFFGetB(temp); | |
367 | val.rgba.a = TIFFGetA(temp); | |
368 | i_ppix(im, col+x, row+i_row, &val); | |
369 | } | |
370 | } | |
371 | } | |
372 | } | |
373 | } else { | |
374 | uint32 rowsperstrip, row; | |
1b0554d1 AMH |
375 | int rc = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); |
376 | mm_log((1, "i_readtiff_wiol: rowsperstrip=%d rc = %d\n", rowsperstrip, rc)); | |
377 | ||
378 | if (rc != 1 || rowsperstrip==-1) { | |
379 | rowsperstrip = height; | |
380 | } | |
381 | ||
5bb828f1 TC |
382 | raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32)); |
383 | if (!raster) { | |
384 | i_img_destroy(im); | |
385 | i_push_error(0, "No space for raster buffer"); | |
5bb828f1 TC |
386 | return NULL; |
387 | } | |
02d1d628 | 388 | |
5bb828f1 TC |
389 | for( row = 0; row < height; row += rowsperstrip ) { |
390 | uint32 newrows, i_row; | |
391 | ||
392 | if (!TIFFReadRGBAStrip(tif, row, raster)) { | |
d87dc9a4 | 393 | if (allow_incomplete) { |
9c106321 TC |
394 | i_tags_setn(&im->tags, "i_lines_read", row); |
395 | error++; | |
396 | break; | |
397 | } | |
398 | else { | |
399 | i_push_error(0, "could not read TIFF image strip"); | |
400 | _TIFFfree(raster); | |
401 | i_img_destroy(im); | |
402 | return NULL; | |
403 | } | |
5bb828f1 TC |
404 | } |
405 | ||
406 | newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip; | |
407 | mm_log((1, "newrows=%d\n", newrows)); | |
408 | ||
409 | for( i_row = 0; i_row < newrows; i_row++ ) { | |
410 | uint32 x; | |
411 | for(x = 0; x<width; x++) { | |
412 | i_color val; | |
413 | uint32 temp = raster[x+width*(newrows-i_row-1)]; | |
414 | val.rgba.r = TIFFGetR(temp); | |
415 | val.rgba.g = TIFFGetG(temp); | |
416 | val.rgba.b = TIFFGetB(temp); | |
417 | val.rgba.a = TIFFGetA(temp); | |
418 | i_ppix(im, x, i_row+row, &val); | |
419 | } | |
420 | } | |
02d1d628 AMH |
421 | } |
422 | } | |
02d1d628 AMH |
423 | } |
424 | if (error) { | |
425 | mm_log((1, "i_readtiff_wiol: error during reading\n")); | |
9c106321 | 426 | i_tags_setn(&im->tags, "i_incomplete", 1); |
02d1d628 | 427 | } |
5bb828f1 TC |
428 | if (raster) |
429 | _TIFFfree( raster ); | |
10461f9a TC |
430 | |
431 | return im; | |
432 | } | |
433 | ||
434 | /* | |
435 | =item i_readtiff_wiol(im, ig) | |
436 | ||
437 | =cut | |
438 | */ | |
439 | i_img* | |
d87dc9a4 | 440 | i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) { |
10461f9a TC |
441 | TIFF* tif; |
442 | TIFFErrorHandler old_handler; | |
be371490 | 443 | TIFFErrorHandler old_warn_handler; |
10461f9a TC |
444 | i_img *im; |
445 | ||
446 | i_clear_error(); | |
447 | old_handler = TIFFSetErrorHandler(error_handler); | |
be371490 | 448 | old_warn_handler = TIFFSetWarningHandler(warn_handler); |
ffeb4a67 TC |
449 | if (warn_buffer) |
450 | *warn_buffer = '\0'; | |
10461f9a TC |
451 | |
452 | /* Add code to get the filename info from the iolayer */ | |
453 | /* Also add code to check for mmapped code */ | |
454 | ||
455 | io_glue_commit_types(ig); | |
d87dc9a4 | 456 | mm_log((1, "i_readtiff_wiol(ig %p, allow_incomplete %d, page %d)\n", ig, allow_incomplete, page)); |
10461f9a TC |
457 | |
458 | tif = TIFFClientOpen("(Iolayer)", | |
459 | "rm", | |
460 | (thandle_t) ig, | |
461 | (TIFFReadWriteProc) ig->readcb, | |
462 | (TIFFReadWriteProc) ig->writecb, | |
463 | (TIFFSeekProc) comp_seek, | |
464 | (TIFFCloseProc) ig->closecb, | |
caa833d5 | 465 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
e18f39b3 TC |
466 | (TIFFMapFileProc) comp_mmap, |
467 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
468 | |
469 | if (!tif) { | |
470 | mm_log((1, "i_readtiff_wiol: Unable to open tif file\n")); | |
2691d220 | 471 | i_push_error(0, "Error opening file"); |
10461f9a | 472 | TIFFSetErrorHandler(old_handler); |
be371490 | 473 | TIFFSetWarningHandler(old_warn_handler); |
10461f9a TC |
474 | return NULL; |
475 | } | |
476 | ||
8f8bd9aa TC |
477 | if (page != 0) { |
478 | if (!TIFFSetDirectory(tif, page)) { | |
479 | mm_log((1, "i_readtiff_wiol: Unable to switch to directory %d\n", page)); | |
480 | i_push_errorf(0, "could not switch to page %d", page); | |
481 | TIFFSetErrorHandler(old_handler); | |
482 | TIFFSetWarningHandler(old_warn_handler); | |
01f2d357 | 483 | TIFFClose(tif); |
8f8bd9aa TC |
484 | return NULL; |
485 | } | |
486 | } | |
487 | ||
d87dc9a4 | 488 | im = read_one_tiff(tif, allow_incomplete); |
10461f9a | 489 | |
02d1d628 | 490 | if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n")); |
5bb828f1 | 491 | TIFFSetErrorHandler(old_handler); |
be371490 | 492 | TIFFSetWarningHandler(old_warn_handler); |
5bb828f1 | 493 | TIFFClose(tif); |
02d1d628 AMH |
494 | return im; |
495 | } | |
496 | ||
10461f9a TC |
497 | /* |
498 | =item i_readtiff_multi_wiol(ig, length, *count) | |
02d1d628 | 499 | |
10461f9a | 500 | Reads multiple images from a TIFF. |
02d1d628 | 501 | |
10461f9a TC |
502 | =cut |
503 | */ | |
504 | i_img** | |
505 | i_readtiff_multi_wiol(io_glue *ig, int length, int *count) { | |
506 | TIFF* tif; | |
507 | TIFFErrorHandler old_handler; | |
be371490 | 508 | TIFFErrorHandler old_warn_handler; |
10461f9a TC |
509 | i_img **results = NULL; |
510 | int result_alloc = 0; | |
511 | int dirnum = 0; | |
02d1d628 | 512 | |
10461f9a TC |
513 | i_clear_error(); |
514 | old_handler = TIFFSetErrorHandler(error_handler); | |
be371490 | 515 | old_warn_handler = TIFFSetWarningHandler(warn_handler); |
ffeb4a67 TC |
516 | if (warn_buffer) |
517 | *warn_buffer = '\0'; | |
02d1d628 | 518 | |
10461f9a TC |
519 | /* Add code to get the filename info from the iolayer */ |
520 | /* Also add code to check for mmapped code */ | |
02d1d628 | 521 | |
10461f9a TC |
522 | io_glue_commit_types(ig); |
523 | mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length)); | |
524 | ||
525 | tif = TIFFClientOpen("(Iolayer)", | |
526 | "rm", | |
527 | (thandle_t) ig, | |
528 | (TIFFReadWriteProc) ig->readcb, | |
529 | (TIFFReadWriteProc) ig->writecb, | |
530 | (TIFFSeekProc) comp_seek, | |
531 | (TIFFCloseProc) ig->closecb, | |
e18f39b3 TC |
532 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
533 | (TIFFMapFileProc) comp_mmap, | |
534 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
535 | |
536 | if (!tif) { | |
537 | mm_log((1, "i_readtiff_wiol: Unable to open tif file\n")); | |
2691d220 | 538 | i_push_error(0, "Error opening file"); |
10461f9a | 539 | TIFFSetErrorHandler(old_handler); |
be371490 | 540 | TIFFSetWarningHandler(old_warn_handler); |
10461f9a TC |
541 | return NULL; |
542 | } | |
02d1d628 | 543 | |
10461f9a TC |
544 | *count = 0; |
545 | do { | |
9c106321 | 546 | i_img *im = read_one_tiff(tif, 0); |
10461f9a TC |
547 | if (!im) |
548 | break; | |
549 | if (++*count > result_alloc) { | |
550 | if (result_alloc == 0) { | |
551 | result_alloc = 5; | |
552 | results = mymalloc(result_alloc * sizeof(i_img *)); | |
553 | } | |
554 | else { | |
555 | i_img **newresults; | |
556 | result_alloc *= 2; | |
557 | newresults = myrealloc(results, result_alloc * sizeof(i_img *)); | |
be371490 TC |
558 | if (!newresults) { |
559 | i_img_destroy(im); /* don't leak it */ | |
560 | break; | |
561 | } | |
562 | results = newresults; | |
10461f9a TC |
563 | } |
564 | } | |
565 | results[*count-1] = im; | |
566 | } while (TIFFSetDirectory(tif, ++dirnum)); | |
567 | ||
be371490 | 568 | TIFFSetWarningHandler(old_warn_handler); |
10461f9a TC |
569 | TIFFSetErrorHandler(old_handler); |
570 | TIFFClose(tif); | |
571 | return results; | |
572 | } | |
02d1d628 AMH |
573 | |
574 | undef_int | |
10461f9a TC |
575 | i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) { |
576 | uint32 width, height; | |
577 | unsigned char *linebuf = NULL; | |
578 | uint32 y; | |
579 | int rc; | |
580 | uint32 x; | |
10461f9a TC |
581 | uint32 rowsperstrip; |
582 | float vres = fine ? 196 : 98; | |
583 | int luma_chan; | |
584 | ||
585 | width = im->xsize; | |
586 | height = im->ysize; | |
587 | ||
588 | switch (im->channels) { | |
589 | case 1: | |
590 | case 2: | |
591 | luma_chan = 0; | |
592 | break; | |
593 | case 3: | |
594 | case 4: | |
595 | luma_chan = 1; | |
596 | break; | |
597 | default: | |
598 | /* This means a colorspace we don't handle yet */ | |
599 | mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels)); | |
600 | return 0; | |
601 | } | |
602 | ||
603 | /* Add code to get the filename info from the iolayer */ | |
604 | /* Also add code to check for mmapped code */ | |
605 | ||
606 | ||
607 | mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels)); | |
608 | ||
609 | if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) | |
610 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; } | |
611 | if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) | |
612 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; } | |
613 | if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1)) | |
614 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; } | |
615 | if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) | |
616 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; } | |
617 | if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1) ) | |
618 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; } | |
619 | if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) | |
620 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; } | |
799d55a6 | 621 | if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE)) |
10461f9a TC |
622 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; } |
623 | if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3)) | |
624 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; } | |
625 | ||
626 | linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) ); | |
627 | ||
628 | if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) { | |
629 | mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; } | |
630 | ||
631 | TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); | |
632 | TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc); | |
633 | ||
634 | mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip)); | |
635 | mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) )); | |
636 | mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG)); | |
637 | ||
638 | if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204)) | |
639 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; } | |
640 | if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres)) | |
641 | { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; } | |
642 | if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) { | |
643 | mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0; | |
644 | } | |
645 | ||
646 | if (!save_tiff_tags(tif, im)) { | |
647 | return 0; | |
648 | } | |
649 | ||
650 | for (y=0; y<height; y++) { | |
651 | int linebufpos=0; | |
652 | for(x=0; x<width; x+=8) { | |
653 | int bits; | |
654 | int bitpos; | |
655 | i_sample_t luma[8]; | |
656 | uint8 bitval = 128; | |
657 | linebuf[linebufpos]=0; | |
658 | bits = width-x; if(bits>8) bits=8; | |
659 | i_gsamp(im, x, x+8, y, luma, &luma_chan, 1); | |
660 | for(bitpos=0;bitpos<bits;bitpos++) { | |
799d55a6 | 661 | linebuf[linebufpos] |= ((luma[bitpos] < 128) ? bitval : 0); |
10461f9a TC |
662 | bitval >>= 1; |
663 | } | |
664 | linebufpos++; | |
665 | } | |
666 | if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) { | |
667 | mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n")); | |
668 | break; | |
669 | } | |
670 | } | |
671 | if (linebuf) _TIFFfree(linebuf); | |
672 | ||
673 | return 1; | |
674 | } | |
675 | ||
676 | undef_int | |
677 | i_writetiff_low(TIFF *tif, i_img *im) { | |
02d1d628 AMH |
678 | uint32 width, height; |
679 | uint16 channels; | |
680 | uint16 predictor = 0; | |
681 | int quality = 75; | |
682 | int jpegcolormode = JPEGCOLORMODE_RGB; | |
683 | uint16 compression = COMPRESSION_PACKBITS; | |
684 | i_color val; | |
685 | uint16 photometric; | |
686 | uint32 rowsperstrip = (uint32) -1; /* Let library pick default */ | |
02d1d628 AMH |
687 | unsigned char *linebuf = NULL; |
688 | uint32 y; | |
689 | tsize_t linebytes; | |
690 | int ch, ci, rc; | |
691 | uint32 x; | |
a659442a | 692 | int got_xres, got_yres, aspect_only, resunit; |
faa9b3e7 | 693 | double xres, yres; |
f62b2d84 TC |
694 | uint16 bitspersample = 8; |
695 | uint16 samplesperpixel; | |
696 | uint16 *colors = NULL; | |
02d1d628 | 697 | |
02d1d628 AMH |
698 | width = im->xsize; |
699 | height = im->ysize; | |
700 | channels = im->channels; | |
701 | ||
702 | switch (channels) { | |
703 | case 1: | |
704 | photometric = PHOTOMETRIC_MINISBLACK; | |
705 | break; | |
706 | case 3: | |
707 | photometric = PHOTOMETRIC_RGB; | |
708 | if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR; | |
f62b2d84 TC |
709 | else if (im->type == i_palette_type) { |
710 | photometric = PHOTOMETRIC_PALETTE; | |
711 | } | |
02d1d628 AMH |
712 | break; |
713 | default: | |
714 | /* This means a colorspace we don't handle yet */ | |
715 | mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels)); | |
716 | return 0; | |
717 | } | |
718 | ||
719 | /* Add code to get the filename info from the iolayer */ | |
720 | /* Also add code to check for mmapped code */ | |
721 | ||
10461f9a TC |
722 | /*io_glue_commit_types(ig);*/ |
723 | /*mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));*/ | |
02d1d628 | 724 | |
10461f9a | 725 | mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d\n", width, height, channels)); |
02d1d628 | 726 | |
10461f9a TC |
727 | if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) { |
728 | mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); | |
729 | return 0; | |
730 | } | |
731 | if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) { | |
732 | mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); | |
733 | return 0; | |
734 | } | |
735 | if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) { | |
736 | mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); | |
737 | return 0; | |
738 | } | |
739 | if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { | |
740 | mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); | |
741 | return 0; | |
742 | } | |
743 | if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) { | |
744 | mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); | |
745 | return 0; | |
746 | } | |
747 | if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) { | |
748 | mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); | |
749 | return 0; | |
02d1d628 | 750 | } |
f62b2d84 TC |
751 | samplesperpixel = channels; |
752 | if (photometric == PHOTOMETRIC_PALETTE) { | |
753 | uint16 *out[3]; | |
754 | i_color c; | |
755 | int count = i_colorcount(im); | |
756 | int size; | |
f62b2d84 TC |
757 | int ch, i; |
758 | ||
759 | samplesperpixel = 1; | |
760 | if (count > 16) | |
761 | bitspersample = 8; | |
762 | else | |
763 | bitspersample = 4; | |
764 | size = 1 << bitspersample; | |
765 | colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size); | |
766 | out[0] = colors; | |
767 | out[1] = colors + size; | |
768 | out[2] = colors + 2 * size; | |
769 | ||
770 | for (i = 0; i < count; ++i) { | |
771 | i_getcolors(im, i, &c, 1); | |
772 | for (ch = 0; ch < 3; ++ch) | |
773 | out[ch][i] = c.channel[ch] * 257; | |
774 | } | |
775 | for (; i < size; ++i) { | |
776 | for (ch = 0; ch < 3; ++ch) | |
777 | out[ch][i] = 0; | |
778 | } | |
779 | if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) { | |
780 | mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n", | |
781 | bitspersample)); | |
782 | return 0; | |
783 | } | |
784 | if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) { | |
785 | mm_log((1, "i_writetiff_wiol: TIFFSetField colormap\n")); | |
786 | return 0; | |
787 | } | |
788 | } | |
789 | else { | |
790 | if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) { | |
791 | mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n", | |
792 | bitspersample)); | |
793 | return 0; | |
794 | } | |
795 | } | |
796 | if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) { | |
797 | mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel)); | |
798 | return 0; | |
799 | } | |
02d1d628 AMH |
800 | |
801 | switch (compression) { | |
802 | case COMPRESSION_JPEG: | |
803 | mm_log((1, "i_writetiff_wiol: jpeg compression\n")); | |
10461f9a TC |
804 | if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) { |
805 | mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); | |
806 | return 0; | |
807 | } | |
808 | if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { | |
809 | mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); | |
810 | return 0; | |
811 | } | |
02d1d628 AMH |
812 | break; |
813 | case COMPRESSION_LZW: | |
814 | mm_log((1, "i_writetiff_wiol: lzw compression\n")); | |
815 | break; | |
816 | case COMPRESSION_DEFLATE: | |
817 | mm_log((1, "i_writetiff_wiol: deflate compression\n")); | |
818 | if (predictor != 0) | |
10461f9a TC |
819 | if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { |
820 | mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); | |
821 | return 0; | |
822 | } | |
02d1d628 AMH |
823 | break; |
824 | case COMPRESSION_PACKBITS: | |
825 | mm_log((1, "i_writetiff_wiol: packbits compression\n")); | |
826 | break; | |
827 | default: | |
828 | mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression)); | |
829 | return 0; | |
830 | } | |
831 | ||
832 | linebytes = channels * width; | |
f62b2d84 TC |
833 | linebytes = TIFFScanlineSize(tif) > linebytes ? linebytes |
834 | : TIFFScanlineSize(tif); | |
835 | /* working space for the scanlines - we go from 8-bit/pixel to 4 */ | |
836 | if (photometric == PHOTOMETRIC_PALETTE && bitspersample == 4) | |
837 | linebytes += linebytes + 1; | |
838 | linebuf = (unsigned char *)_TIFFmalloc(linebytes); | |
02d1d628 AMH |
839 | |
840 | if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) { | |
841 | mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; } | |
842 | ||
843 | TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); | |
844 | TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc); | |
845 | ||
846 | mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip)); | |
847 | mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) )); | |
848 | mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG)); | |
230e675b | 849 | mm_log((1, "i_writetiff_wiol: bitspersample = %d\n", bitspersample)); |
02d1d628 | 850 | |
faa9b3e7 TC |
851 | got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres); |
852 | got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres); | |
853 | if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only)) | |
854 | aspect_only = 0; | |
855 | if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit)) | |
856 | resunit = RESUNIT_INCH; | |
857 | if (got_xres || got_yres) { | |
858 | if (!got_xres) | |
859 | xres = yres; | |
860 | else if (!got_yres) | |
861 | yres = xres; | |
862 | if (aspect_only) { | |
863 | resunit = RESUNIT_NONE; | |
864 | } | |
865 | else { | |
866 | if (resunit == RESUNIT_CENTIMETER) { | |
867 | xres /= 2.54; | |
868 | yres /= 2.54; | |
869 | } | |
870 | else { | |
871 | resunit = RESUNIT_INCH; | |
872 | } | |
873 | } | |
874 | if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) { | |
faa9b3e7 TC |
875 | i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag"); |
876 | return 0; | |
877 | } | |
878 | if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) { | |
faa9b3e7 TC |
879 | i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag"); |
880 | return 0; | |
881 | } | |
882 | if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) { | |
faa9b3e7 TC |
883 | i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag"); |
884 | return 0; | |
02d1d628 AMH |
885 | } |
886 | } | |
fd9a31d2 TC |
887 | |
888 | if (!save_tiff_tags(tif, im)) { | |
fd9a31d2 TC |
889 | return 0; |
890 | } | |
891 | ||
f62b2d84 TC |
892 | if (photometric == PHOTOMETRIC_PALETTE) { |
893 | for (y = 0; y < height; ++y) { | |
894 | i_gpal(im, 0, width, y, linebuf); | |
895 | if (bitspersample == 4) | |
896 | pack_4bit_hl(linebuf, width); | |
897 | if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) { | |
898 | mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n")); | |
10461f9a TC |
899 | if (linebuf) _TIFFfree(linebuf); |
900 | if (colors) _TIFFfree(colors); | |
901 | return 0; | |
f62b2d84 | 902 | } |
02d1d628 | 903 | } |
f62b2d84 TC |
904 | } |
905 | else { | |
906 | for (y=0; y<height; y++) { | |
907 | ci = 0; | |
908 | for(x=0; x<width; x++) { | |
909 | (void) i_gpix(im, x, y,&val); | |
910 | for(ch=0; ch<channels; ch++) | |
911 | linebuf[ci++] = val.channel[ch]; | |
912 | } | |
913 | if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) { | |
914 | mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n")); | |
10461f9a TC |
915 | if (linebuf) _TIFFfree(linebuf); |
916 | if (colors) _TIFFfree(colors); | |
917 | return 0; | |
f62b2d84 | 918 | } |
02d1d628 AMH |
919 | } |
920 | } | |
02d1d628 | 921 | if (linebuf) _TIFFfree(linebuf); |
f62b2d84 | 922 | if (colors) _TIFFfree(colors); |
02d1d628 AMH |
923 | return 1; |
924 | } | |
925 | ||
d2dfdcc9 | 926 | /* |
10461f9a | 927 | =item i_writetiff_multi_wiol(ig, imgs, count, fine_mode) |
d2dfdcc9 | 928 | |
10461f9a | 929 | Stores an image in the iolayer object. |
d2dfdcc9 | 930 | |
d2dfdcc9 | 931 | ig - io_object that defines source to write to |
10461f9a | 932 | imgs,count - the images to write |
d2dfdcc9 | 933 | |
10461f9a | 934 | =cut |
d2dfdcc9 TC |
935 | */ |
936 | ||
937 | undef_int | |
10461f9a | 938 | i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) { |
d2dfdcc9 | 939 | TIFF* tif; |
2691d220 | 940 | TIFFErrorHandler old_handler; |
10461f9a | 941 | int i; |
d2dfdcc9 | 942 | |
2691d220 TC |
943 | old_handler = TIFFSetErrorHandler(error_handler); |
944 | ||
10461f9a TC |
945 | io_glue_commit_types(ig); |
946 | i_clear_error(); | |
947 | mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n", | |
948 | ig, imgs, count)); | |
d2dfdcc9 | 949 | |
10461f9a TC |
950 | /* FIXME: Enable the mmap interface */ |
951 | ||
952 | tif = TIFFClientOpen("No name", | |
953 | "wm", | |
954 | (thandle_t) ig, | |
955 | (TIFFReadWriteProc) ig->readcb, | |
956 | (TIFFReadWriteProc) ig->writecb, | |
957 | (TIFFSeekProc) comp_seek, | |
958 | (TIFFCloseProc) ig->closecb, | |
e18f39b3 TC |
959 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
960 | (TIFFMapFileProc) comp_mmap, | |
961 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
962 | |
963 | ||
964 | ||
965 | if (!tif) { | |
2691d220 TC |
966 | mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n")); |
967 | i_push_error(0, "Could not create TIFF object"); | |
968 | TIFFSetErrorHandler(old_handler); | |
d2dfdcc9 TC |
969 | return 0; |
970 | } | |
971 | ||
10461f9a TC |
972 | for (i = 0; i < count; ++i) { |
973 | if (!i_writetiff_low(tif, imgs[i])) { | |
974 | TIFFClose(tif); | |
2691d220 | 975 | TIFFSetErrorHandler(old_handler); |
10461f9a TC |
976 | return 0; |
977 | } | |
978 | ||
979 | if (!TIFFWriteDirectory(tif)) { | |
980 | i_push_error(0, "Cannot write TIFF directory"); | |
981 | TIFFClose(tif); | |
2691d220 | 982 | TIFFSetErrorHandler(old_handler); |
10461f9a TC |
983 | return 0; |
984 | } | |
985 | } | |
986 | ||
2691d220 | 987 | TIFFSetErrorHandler(old_handler); |
10461f9a | 988 | (void) TIFFClose(tif); |
2691d220 | 989 | |
10461f9a TC |
990 | return 1; |
991 | } | |
992 | ||
993 | /* | |
994 | =item i_writetiff_multi_wiol_faxable(ig, imgs, count, fine_mode) | |
995 | ||
996 | Stores an image in the iolayer object. | |
997 | ||
998 | ig - io_object that defines source to write to | |
999 | imgs,count - the images to write | |
1000 | fine_mode - select fine or normal mode fax images | |
1001 | ||
1002 | =cut | |
1003 | */ | |
1004 | ||
1005 | ||
1006 | undef_int | |
1007 | i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) { | |
1008 | TIFF* tif; | |
1009 | int i; | |
2691d220 TC |
1010 | TIFFErrorHandler old_handler; |
1011 | ||
1012 | old_handler = TIFFSetErrorHandler(error_handler); | |
d2dfdcc9 TC |
1013 | |
1014 | io_glue_commit_types(ig); | |
10461f9a TC |
1015 | i_clear_error(); |
1016 | mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n", | |
1017 | ig, imgs, count)); | |
d2dfdcc9 TC |
1018 | |
1019 | /* FIXME: Enable the mmap interface */ | |
1020 | ||
1021 | tif = TIFFClientOpen("No name", | |
1022 | "wm", | |
1023 | (thandle_t) ig, | |
1024 | (TIFFReadWriteProc) ig->readcb, | |
1025 | (TIFFReadWriteProc) ig->writecb, | |
1026 | (TIFFSeekProc) comp_seek, | |
1027 | (TIFFCloseProc) ig->closecb, | |
e18f39b3 TC |
1028 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
1029 | (TIFFMapFileProc) comp_mmap, | |
1030 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
1031 | |
1032 | ||
d2dfdcc9 TC |
1033 | |
1034 | if (!tif) { | |
10461f9a | 1035 | mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n")); |
2691d220 TC |
1036 | i_push_error(0, "Could not create TIFF object"); |
1037 | TIFFSetErrorHandler(old_handler); | |
d2dfdcc9 TC |
1038 | return 0; |
1039 | } | |
1040 | ||
10461f9a TC |
1041 | for (i = 0; i < count; ++i) { |
1042 | if (!i_writetiff_low_faxable(tif, imgs[i], fine)) { | |
1043 | TIFFClose(tif); | |
2691d220 | 1044 | TIFFSetErrorHandler(old_handler); |
10461f9a TC |
1045 | return 0; |
1046 | } | |
d2dfdcc9 | 1047 | |
10461f9a TC |
1048 | if (!TIFFWriteDirectory(tif)) { |
1049 | i_push_error(0, "Cannot write TIFF directory"); | |
1050 | TIFFClose(tif); | |
2691d220 | 1051 | TIFFSetErrorHandler(old_handler); |
10461f9a TC |
1052 | return 0; |
1053 | } | |
1054 | } | |
d2dfdcc9 | 1055 | |
10461f9a | 1056 | (void) TIFFClose(tif); |
2691d220 TC |
1057 | TIFFSetErrorHandler(old_handler); |
1058 | ||
10461f9a TC |
1059 | return 1; |
1060 | } | |
d2dfdcc9 | 1061 | |
10461f9a TC |
1062 | /* |
1063 | =item i_writetiff_wiol(im, ig) | |
d2dfdcc9 | 1064 | |
10461f9a TC |
1065 | Stores an image in the iolayer object. |
1066 | ||
1067 | im - image object to write out | |
1068 | ig - io_object that defines source to write to | |
1069 | ||
1070 | =cut | |
1071 | */ | |
1072 | undef_int | |
1073 | i_writetiff_wiol(i_img *img, io_glue *ig) { | |
1074 | TIFF* tif; | |
2691d220 TC |
1075 | TIFFErrorHandler old_handler; |
1076 | ||
1077 | old_handler = TIFFSetErrorHandler(error_handler); | |
10461f9a TC |
1078 | |
1079 | io_glue_commit_types(ig); | |
1080 | i_clear_error(); | |
1081 | mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig)); | |
1082 | ||
1083 | /* FIXME: Enable the mmap interface */ | |
2691d220 | 1084 | |
10461f9a TC |
1085 | tif = TIFFClientOpen("No name", |
1086 | "wm", | |
1087 | (thandle_t) ig, | |
1088 | (TIFFReadWriteProc) ig->readcb, | |
1089 | (TIFFReadWriteProc) ig->writecb, | |
1090 | (TIFFSeekProc) comp_seek, | |
1091 | (TIFFCloseProc) ig->closecb, | |
e18f39b3 TC |
1092 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
1093 | (TIFFMapFileProc) comp_mmap, | |
1094 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
1095 | |
1096 | ||
1097 | ||
1098 | if (!tif) { | |
1099 | mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n")); | |
2691d220 TC |
1100 | i_push_error(0, "Could not create TIFF object"); |
1101 | TIFFSetErrorHandler(old_handler); | |
10461f9a | 1102 | return 0; |
d2dfdcc9 | 1103 | } |
d2dfdcc9 | 1104 | |
10461f9a | 1105 | if (!i_writetiff_low(tif, img)) { |
fd9a31d2 | 1106 | TIFFClose(tif); |
2691d220 | 1107 | TIFFSetErrorHandler(old_handler); |
fd9a31d2 TC |
1108 | return 0; |
1109 | } | |
1110 | ||
10461f9a | 1111 | (void) TIFFClose(tif); |
2691d220 TC |
1112 | TIFFSetErrorHandler(old_handler); |
1113 | ||
10461f9a TC |
1114 | return 1; |
1115 | } | |
1116 | ||
1117 | ||
1118 | ||
1119 | /* | |
1120 | =item i_writetiff_wiol_faxable(i_img *, io_glue *) | |
1121 | ||
1122 | Stores an image in the iolayer object in faxable tiff format. | |
1123 | ||
1124 | im - image object to write out | |
1125 | ig - io_object that defines source to write to | |
1126 | ||
1127 | Note, this may be rewritten to use to simply be a call to a | |
1128 | lower-level function that gives more options for writing tiff at some | |
1129 | point. | |
1130 | ||
1131 | =cut | |
1132 | */ | |
1133 | ||
1134 | undef_int | |
1135 | i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) { | |
1136 | TIFF* tif; | |
2691d220 TC |
1137 | TIFFErrorHandler old_handler; |
1138 | ||
1139 | old_handler = TIFFSetErrorHandler(error_handler); | |
10461f9a TC |
1140 | |
1141 | io_glue_commit_types(ig); | |
1142 | i_clear_error(); | |
1143 | mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", im, ig)); | |
1144 | ||
1145 | /* FIXME: Enable the mmap interface */ | |
1146 | ||
1147 | tif = TIFFClientOpen("No name", | |
1148 | "wm", | |
1149 | (thandle_t) ig, | |
1150 | (TIFFReadWriteProc) ig->readcb, | |
1151 | (TIFFReadWriteProc) ig->writecb, | |
1152 | (TIFFSeekProc) comp_seek, | |
1153 | (TIFFCloseProc) ig->closecb, | |
e18f39b3 TC |
1154 | ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc, |
1155 | (TIFFMapFileProc) comp_mmap, | |
1156 | (TIFFUnmapFileProc) comp_munmap); | |
10461f9a TC |
1157 | |
1158 | ||
1159 | ||
1160 | if (!tif) { | |
1161 | mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n")); | |
2691d220 TC |
1162 | i_push_error(0, "Could not create TIFF object"); |
1163 | TIFFSetErrorHandler(old_handler); | |
10461f9a | 1164 | return 0; |
d2dfdcc9 | 1165 | } |
10461f9a TC |
1166 | |
1167 | if (!i_writetiff_low_faxable(tif, im, fine)) { | |
1168 | TIFFClose(tif); | |
2691d220 | 1169 | TIFFSetErrorHandler(old_handler); |
10461f9a TC |
1170 | return 0; |
1171 | } | |
1172 | ||
d2dfdcc9 | 1173 | (void) TIFFClose(tif); |
2691d220 TC |
1174 | TIFFSetErrorHandler(old_handler); |
1175 | ||
d2dfdcc9 TC |
1176 | return 1; |
1177 | } | |
1178 | ||
fd9a31d2 TC |
1179 | static int save_tiff_tags(TIFF *tif, i_img *im) { |
1180 | int i; | |
10461f9a | 1181 | |
fd9a31d2 TC |
1182 | for (i = 0; i < text_tag_count; ++i) { |
1183 | int entry; | |
1184 | if (i_tags_find(&im->tags, text_tag_names[i].name, 0, &entry)) { | |
1185 | if (!TIFFSetField(tif, text_tag_names[i].tag, | |
10461f9a TC |
1186 | im->tags.tags[entry].data)) { |
1187 | i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name); | |
1188 | return 0; | |
fd9a31d2 TC |
1189 | } |
1190 | } | |
1191 | } | |
10461f9a | 1192 | |
fd9a31d2 TC |
1193 | return 1; |
1194 | } | |
b8c2033e | 1195 | |
10461f9a | 1196 | |
5bb828f1 TC |
1197 | /* |
1198 | =item expand_4bit_hl(buf, count) | |
1199 | ||
1200 | Expands 4-bit/entry packed data into 1 byte/entry. | |
1201 | ||
1202 | buf must contain count bytes to be expanded and have 2*count bytes total | |
1203 | space. | |
1204 | ||
1205 | The data is expanded in place. | |
1206 | ||
1207 | =cut | |
1208 | */ | |
1209 | ||
1210 | static void expand_4bit_hl(unsigned char *buf, int count) { | |
1211 | while (--count >= 0) { | |
1212 | buf[count*2+1] = buf[count] & 0xF; | |
1213 | buf[count*2] = buf[count] >> 4; | |
1214 | } | |
1215 | } | |
1216 | ||
f62b2d84 | 1217 | static void pack_4bit_hl(unsigned char *buf, int count) { |
230e675b | 1218 | int i = 0; |
f62b2d84 TC |
1219 | while (i < count) { |
1220 | buf[i/2] = (buf[i] << 4) + buf[i+1]; | |
1221 | i += 2; | |
1222 | } | |
1223 | } | |
5bb828f1 | 1224 | |
b8c2033e AMH |
1225 | /* |
1226 | =back | |
1227 | ||
1228 | =head1 AUTHOR | |
1229 | ||
1230 | Arnar M. Hrafnkelsson <addi@umich.edu> | |
1231 | ||
1232 | =head1 SEE ALSO | |
1233 | ||
1234 | Imager(3) | |
1235 | ||
1236 | =cut | |
1237 | */ |