1 /*
2 * Copyright 2003-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Michael Wilber
7 * Stephan Aßmus <stippi@yellowbites.com> (write support)
8 */
9
10
11 #include "TIFFTranslator.h"
12 #include "TIFFView.h"
13
14 #define TIFF_DISABLE_DEPRECATED
15 #include "tiffio.h"
16
17 #if __GNUC__ == 2
18 #define TIFF_UINT32_TYPE uint32
19 #else
20 #define TIFF_UINT32_TYPE uint32_t
21 #endif
22
23 #include <Catalog.h>
24 #include <stdio.h>
25 #include <string.h>
26
27
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "TIFFTranslator"
30
31
32 /*!
33 How this works:
34
35 libtiff has a special version of TIFFOpen() that gets passed custom
36 functions for reading writing etc. and a handle. This handle in our case
37 is a BPositionIO object, which libtiff passes on to the functions for reading
38 writing etc. So when operations are performed on the TIFF* handle that is
39 returned by TIFFOpen(), libtiff uses the special reading writing etc
40 functions so that all stream io happens on the BPositionIO object.
41 */
42
43
44 // The input formats that this translator supports.
45 static const translation_format sInputFormats[] = {
46 {
47 B_TRANSLATOR_BITMAP,
48 B_TRANSLATOR_BITMAP,
49 BBT_IN_QUALITY,
50 BBT_IN_CAPABILITY,
51 "image/x-be-bitmap",
52 "Be Bitmap Format (TIFFTranslator)"
53 },
54 {
55 B_TIFF_FORMAT,
56 B_TRANSLATOR_BITMAP,
57 TIFF_IN_QUALITY,
58 TIFF_IN_CAPABILITY,
59 "image/tiff",
60 "TIFF image"
61 }
62 };
63
64 // The output formats that this translator supports.
65 static const translation_format sOutputFormats[] = {
66 {
67 B_TRANSLATOR_BITMAP,
68 B_TRANSLATOR_BITMAP,
69 BBT_OUT_QUALITY,
70 BBT_OUT_CAPABILITY,
71 "image/x-be-bitmap",
72 "Be Bitmap Format (TIFFTranslator)"
73 },
74 {
75 B_TIFF_FORMAT,
76 B_TRANSLATOR_BITMAP,
77 TIFF_OUT_QUALITY,
78 TIFF_OUT_CAPABILITY,
79 "image/tiff",
80 "TIFF image"
81 }
82 };
83
84 // Default settings for the Translator
85 static const TranSetting sDefaultSettings[] = {
86 {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
87 {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false},
88 {TIFF_SETTING_COMPRESSION, TRAN_SETTING_INT32, COMPRESSION_LZW}
89 // Compression is LZW by default
90 };
91
92 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
93 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
94 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
95
96
97 // ---------------------------------------------------------------
98 // make_nth_translator
99 //
100 // Creates a TIFFTranslator object to be used by BTranslatorRoster
101 //
102 // Preconditions:
103 //
104 // Parameters: n, The translator to return. Since
105 // TIFFTranslator only publishes one
106 // translator, it only returns a
107 // TIFFTranslator if n == 0
108 //
109 // you, The image_id of the add-on that
110 // contains code (not used).
111 //
112 // flags, Has no meaning yet, should be 0.
113 //
114 // Postconditions:
115 //
116 // Returns: NULL if n is not zero,
117 // a new TIFFTranslator if n is zero
118 // ---------------------------------------------------------------
119 BTranslator *
make_nth_translator(int32 n,image_id you,uint32 flags,...)120 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
121 {
122 if (!n)
123 return new TIFFTranslator();
124 else
125 return NULL;
126 }
127
128
129 //// libtiff Callback functions!
130
131 BPositionIO *
tiff_get_pio(thandle_t stream)132 tiff_get_pio(thandle_t stream)
133 {
134 BPositionIO *pio = NULL;
135 pio = static_cast<BPositionIO *>(stream);
136 if (!pio)
137 debugger("pio is NULL");
138
139 return pio;
140 }
141
142 tsize_t
tiff_read_proc(thandle_t stream,tdata_t buf,tsize_t size)143 tiff_read_proc(thandle_t stream, tdata_t buf, tsize_t size)
144 {
145 return tiff_get_pio(stream)->Read(buf, size);
146 }
147
148 tsize_t
tiff_write_proc(thandle_t stream,tdata_t buf,tsize_t size)149 tiff_write_proc(thandle_t stream, tdata_t buf, tsize_t size)
150 {
151 return tiff_get_pio(stream)->Write(buf, size);
152 }
153
154 toff_t
tiff_seek_proc(thandle_t stream,toff_t off,int whence)155 tiff_seek_proc(thandle_t stream, toff_t off, int whence)
156 {
157 return tiff_get_pio(stream)->Seek(off, whence);
158 }
159
160 int
tiff_close_proc(thandle_t stream)161 tiff_close_proc(thandle_t stream)
162 {
163 tiff_get_pio(stream)->Seek(0, SEEK_SET);
164 return 0;
165 }
166
167 toff_t
tiff_size_proc(thandle_t stream)168 tiff_size_proc(thandle_t stream)
169 {
170 BPositionIO *pio = tiff_get_pio(stream);
171 off_t cur, end;
172 cur = pio->Position();
173 end = pio->Seek(0, SEEK_END);
174 pio->Seek(cur, SEEK_SET);
175
176 return end;
177 }
178
179 int
tiff_map_file_proc(thandle_t stream,tdata_t * pbase,toff_t * psize)180 tiff_map_file_proc(thandle_t stream, tdata_t *pbase, toff_t *psize)
181 {
182 // BeOS doesn't support mmap() so just return 0
183 return 0;
184 }
185
186 void
tiff_unmap_file_proc(thandle_t stream,tdata_t base,toff_t size)187 tiff_unmap_file_proc(thandle_t stream, tdata_t base, toff_t size)
188 {
189 return;
190 }
191
192
193 status_t
identify_tiff_header(BPositionIO * inSource,BMessage * ioExtension,translator_info * outInfo,uint32 outType,TIFF ** poutTIFF=NULL)194 identify_tiff_header(BPositionIO *inSource, BMessage *ioExtension,
195 translator_info *outInfo, uint32 outType, TIFF **poutTIFF = NULL)
196 {
197 // get TIFF handle
198 TIFF* tif = TIFFClientOpen("TIFFTranslator", "r", inSource,
199 tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc,
200 tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc);
201 if (!tif)
202 return B_NO_TRANSLATOR;
203
204 // count number of documents
205 int32 documentCount = 0, documentIndex = 1;
206 do {
207 documentCount++;
208 } while (TIFFReadDirectory(tif));
209
210 if (ioExtension) {
211 // Check if a document index has been specified
212 if (ioExtension->FindInt32(DOCUMENT_INDEX, &documentIndex) != B_OK)
213 documentIndex = 1;
214
215 if (documentIndex < 1 || documentIndex > documentCount) {
216 // document index is invalid
217 fputs(B_TRANSLATE("identify_tiff_header: invalid "
218 "document index\n"), stderr);
219 return B_NO_TRANSLATOR;
220 }
221 }
222
223 // identify the document the user specified or the first document
224 // if the user did not specify which document they wanted to identify
225 if (!TIFFSetDirectory(tif, documentIndex - 1)) {
226 fputs(B_TRANSLATE("identify_tiff_header: couldn't set "
227 "directory\n"), stderr);
228 return B_NO_TRANSLATOR;
229 }
230
231 if (ioExtension) {
232 // add page count to ioExtension
233 ioExtension->RemoveName(DOCUMENT_COUNT);
234 ioExtension->AddInt32(DOCUMENT_COUNT, documentCount);
235 }
236
237 if (outInfo) {
238 outInfo->type = B_TIFF_FORMAT;
239 outInfo->group = B_TRANSLATOR_BITMAP;
240 outInfo->quality = TIFF_IN_QUALITY;
241 outInfo->capability = TIFF_IN_CAPABILITY;
242 strcpy(outInfo->MIME, "image/tiff");
243 strlcpy(outInfo->name, B_TRANSLATE("TIFF image"),
244 sizeof(outInfo->name));
245 }
246
247 if (!poutTIFF) {
248 // close TIFF if caller is not interested in TIFF handle
249 TIFFClose(tif);
250 } else {
251 // leave TIFF open and return handle if caller needs it
252 *poutTIFF = tif;
253 }
254
255 return B_OK;
256 }
257
258
259 // How this works:
260 // Following are a couple of functions,
261 //
262 // convert_buffer_* to convert a buffer in place to the TIFF native format
263 //
264 // convert_buffers_* to convert from one buffer to another to the TIFF
265 // native format, additionally compensating for padding bytes
266 // I don't know if libTIFF can be set up to respect padding bytes,
267 // otherwise this whole thing could be simplified a bit.
268 //
269 // Additionally, there are two functions convert_buffer() and convert_buffers() that take
270 // a color_space as one of the arguments and pick the correct worker functions from there.
271 // This way I don't write any code more than once, for easier debugging and maintainance.
272
273
274 // convert_buffer_bgra_rgba
275 inline void
convert_buffer_bgra_rgba(uint8 * buffer,uint32 rows,uint32 width,uint32 bytesPerRow)276 convert_buffer_bgra_rgba(uint8* buffer, uint32 rows, uint32 width,
277 uint32 bytesPerRow)
278 {
279 for (uint32 y = 0; y < rows; y++) {
280 uint8* handle = buffer;
281 for (uint32 x = 0; x < width; x++) {
282 uint8 temp = handle[0];
283 handle[0] = handle[2];
284 handle[2] = temp;
285 handle += 4;
286 }
287 buffer += bytesPerRow;
288 }
289 }
290
291 // convert_buffer_argb_rgba
292 inline void
convert_buffer_argb_rgba(uint8 * buffer,uint32 rows,uint32 width,uint32 bytesPerRow)293 convert_buffer_argb_rgba(uint8* buffer, uint32 rows, uint32 width,
294 uint32 bytesPerRow)
295 {
296 for (uint32 y = 0; y < rows; y++) {
297 uint8* handle = buffer;
298 for (uint32 x = 0; x < width; x++) {
299 uint8 temp = handle[0];
300 handle[0] = handle[1];
301 handle[1] = handle[2];
302 handle[2] = handle[3];
303 handle[3] = temp;
304 handle += 4;
305 }
306 buffer += bytesPerRow;
307 }
308 }
309
310 // convert_buffers_bgra_rgba
311 inline void
convert_buffers_bgra_rgba(uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow)312 convert_buffers_bgra_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows,
313 uint32 width, uint32 bytesPerRow)
314 {
315 for (uint32 y = 0; y < rows; y++) {
316 uint8* inHandle = inBuffer;
317 uint8* outHandle = outBuffer;
318 for (uint32 x = 0; x < width; x++) {
319 outHandle[0] = inHandle[2];
320 outHandle[1] = inHandle[1];
321 outHandle[2] = inHandle[0];
322 outHandle[3] = inHandle[3];
323 inHandle += 4;
324 outHandle += 4;
325 }
326 inBuffer += bytesPerRow;
327 outBuffer += width * 4;
328 }
329 }
330
331 // convert_buffers_argb_rgba
332 inline void
convert_buffers_argb_rgba(uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow)333 convert_buffers_argb_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows,
334 uint32 width, uint32 bytesPerRow)
335 {
336 for (uint32 y = 0; y < rows; y++) {
337 uint8* inHandle = inBuffer;
338 uint8* outHandle = outBuffer;
339 for (uint32 x = 0; x < width; x++) {
340 outHandle[0] = inHandle[1];
341 outHandle[1] = inHandle[2];
342 outHandle[2] = inHandle[3];
343 outHandle[3] = inHandle[0];
344 inHandle += 4;
345 outHandle += 4;
346 }
347 inBuffer += bytesPerRow;
348 outBuffer += width * 4;
349 }
350 }
351
352 // convert_buffers_bgrX_rgb
353 inline void
convert_buffers_bgrX_rgb(uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow,uint32 samplesPerPixel)354 convert_buffers_bgrX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows,
355 uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel)
356 {
357 for (uint32 y = 0; y < rows; y++) {
358 uint8* inHandle = inBuffer;
359 uint8* outHandle = outBuffer;
360 for (uint32 x = 0; x < width; x++) {
361 // the usage of temp is just in case inBuffer == outBuffer
362 // (see convert_buffer() for B_RGB24)
363 uint8 temp = inHandle[0];
364 outHandle[0] = inHandle[2];
365 outHandle[1] = inHandle[1];
366 outHandle[2] = temp;
367 inHandle += samplesPerPixel;
368 outHandle += 3;
369 }
370 inBuffer += bytesPerRow;
371 outBuffer += width * 3;
372 }
373 }
374
375 // convert_buffers_rgbX_rgb
376 inline void
convert_buffers_rgbX_rgb(uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow,uint32 samplesPerPixel)377 convert_buffers_rgbX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows,
378 uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel)
379 {
380 for (uint32 y = 0; y < rows; y++) {
381 uint8* inHandle = inBuffer;
382 uint8* outHandle = outBuffer;
383 for (uint32 x = 0; x < width; x++) {
384 outHandle[0] = inHandle[0];
385 outHandle[1] = inHandle[1];
386 outHandle[2] = inHandle[2];
387 inHandle += samplesPerPixel;
388 outHandle += 3;
389 }
390 inBuffer += bytesPerRow;
391 outBuffer += width * 3;
392 }
393 }
394
395
396 // convert_buffers_cmap
397 inline void
convert_buffers_cmap(uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow)398 convert_buffers_cmap(uint8* inBuffer, uint8* outBuffer, uint32 rows,
399 uint32 width, uint32 bytesPerRow)
400 {
401 // compensate for bytesPerRow != width (padding bytes)
402 // this function will not be called if bytesPerRow == width, btw
403 for (uint32 y = 0; y < rows; y++) {
404 _TIFFmemcpy(outBuffer, inBuffer, width);
405 inBuffer += bytesPerRow;
406 outBuffer += width;
407 }
408 }
409
410 // convert_buffer
411 inline void
convert_buffer(color_space format,uint8 * buffer,uint32 rows,uint32 width,uint32 bytesPerRow)412 convert_buffer(color_space format, uint8* buffer, uint32 rows, uint32 width,
413 uint32 bytesPerRow)
414 {
415 switch (format) {
416 case B_RGBA32:
417 convert_buffer_bgra_rgba(buffer, rows, width, bytesPerRow);
418 break;
419 case B_RGBA32_BIG:
420 convert_buffer_argb_rgba(buffer, rows, width, bytesPerRow);
421 break;
422 // case B_RGB32:
423 // case B_RGB32_BIG:
424 // these two cannot be encountered, since inBufferSize != bytesPerStrip
425 // (we're stripping the unused "alpha" channel 32->24 bits)
426 case B_RGB24:
427 convert_buffers_bgrX_rgb(buffer, buffer, rows, width, bytesPerRow, 3);
428 break;
429 // case B_RGB24_BIG:
430 // buffer already has the correct format
431 break;
432 // case B_CMAP8:
433 // case B_GRAY8:
434 // buffer already has the correct format
435 break;
436 default:
437 break;
438 }
439 }
440
441 // convert_buffers
442 inline void
convert_buffers(color_space format,uint8 * inBuffer,uint8 * outBuffer,uint32 rows,uint32 width,uint32 bytesPerRow)443 convert_buffers(color_space format, uint8* inBuffer, uint8* outBuffer,
444 uint32 rows, uint32 width, uint32 bytesPerRow)
445 {
446 switch (format) {
447 case B_RGBA32:
448 convert_buffers_bgra_rgba(inBuffer, outBuffer, rows, width, bytesPerRow);
449 break;
450 case B_RGBA32_BIG:
451 convert_buffers_argb_rgba(inBuffer, outBuffer, rows, width, bytesPerRow);
452 break;
453 case B_RGB32:
454 convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4);
455 break;
456 case B_RGB32_BIG:
457 convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4);
458 break;
459 case B_RGB24:
460 convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3);
461 break;
462 case B_RGB24_BIG:
463 convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3);
464 break;
465 case B_CMAP8:
466 case B_GRAY8:
467 convert_buffers_cmap(inBuffer, outBuffer, rows, width, bytesPerRow);
468 break;
469 default:
470 break;
471 }
472 }
473
474 // Sets up any additional TIFF fields for the color spaces it supports,
475 // determines if it needs one or two buffers to carry out any conversions,
476 // uses the various convert routines above to do the actual conversion,
477 // writes complete strips of data plus one strip of remaining data.
478 //
479 // write_tif_stream
480 status_t
write_tif_stream(TIFF * tif,BPositionIO * inSource,color_space format,uint32 width,uint32 height,uint32 bytesPerRow,uint32 rowsPerStrip,uint32 dataSize)481 write_tif_stream(TIFF* tif, BPositionIO* inSource, color_space format,
482 uint32 width, uint32 height, uint32 bytesPerRow,
483 uint32 rowsPerStrip, uint32 dataSize)
484 {
485 uint32 bytesPerStrip = 0;
486
487 // set up the TIFF fields about what channels we write
488 switch (format) {
489 case B_RGBA32:
490 case B_RGBA32_BIG:
491 uint16 extraSamples[1];
492 extraSamples[0] = EXTRASAMPLE_UNASSALPHA;
493 TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, extraSamples);
494 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
495 // going to write rgb + alpha channels
496 bytesPerStrip = width * 4 * rowsPerStrip;
497 break;
498 case B_RGB32:
499 case B_RGB32_BIG:
500 case B_RGB24:
501 case B_RGB24_BIG:
502 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
503 // going to write just the rgb channels
504 bytesPerStrip = width * 3 * rowsPerStrip;
505 break;
506 case B_CMAP8:
507 case B_GRAY8:
508 bytesPerStrip = width * rowsPerStrip;
509 break;
510 default:
511 return B_BAD_VALUE;
512 }
513
514 uint32 remaining = dataSize;
515 status_t ret = B_OK;
516 // Write the information to the stream
517 uint32 inBufferSize = bytesPerRow * rowsPerStrip;
518 // allocate intermediate input buffer
519 uint8* inBuffer = (uint8*)_TIFFmalloc(inBufferSize);
520 ssize_t read, written = B_ERROR;
521 // bytesPerStrip is the size of the buffer that libtiff expects to write per strip
522 // it might be different to the size of the input buffer,
523 // if that one contains padding bytes at the end of each row
524 if (inBufferSize != bytesPerStrip) {
525 // allocate a second buffer
526 // (two buffers are needed since padding bytes have to be compensated for)
527 uint8* outBuffer = (uint8*)_TIFFmalloc(bytesPerStrip);
528 if (inBuffer && outBuffer) {
529 //printf("using two buffers\n");
530 read = inSource->Read(inBuffer, inBufferSize);
531 uint32 stripIndex = 0;
532 while (read == (ssize_t)inBufferSize) {
533 //printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex);
534 // convert the buffers (channel order) and compensate
535 // for bytesPerRow != samplesPerRow (padding bytes)
536 convert_buffers(format, inBuffer, outBuffer,
537 rowsPerStrip, width, bytesPerRow);
538 // let libtiff write the encoded strip to the BPositionIO
539 written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, bytesPerStrip);
540 stripIndex++;
541 if (written < B_OK)
542 break;
543 remaining -= inBufferSize;
544 read = inSource->Read(inBuffer, min_c(inBufferSize, remaining));
545 }
546 // write the rest of the remaining rows
547 if (read < (ssize_t)inBufferSize && read > 0) {
548 //printf("writing remaining bytes: %ld\n", read);
549 // convert the buffers (channel order) and compensate
550 // for bytesPerRow != samplesPerRow (padding bytes)
551 convert_buffers(format, inBuffer, outBuffer,
552 read / bytesPerRow, width, bytesPerRow);
553 // let libtiff write the encoded strip to the BPositionIO
554 written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, read);
555 remaining -= read;
556 }
557 } else
558 ret = B_NO_MEMORY;
559 // clean up output buffer
560 if (outBuffer)
561 _TIFFfree(outBuffer);
562 } else {
563 //printf("using one buffer\n");
564 // the input buffer is all we need, we convert it in place
565 if (inBuffer) {
566 read = inSource->Read(inBuffer, inBufferSize);
567 uint32 stripIndex = 0;
568 while (read == (ssize_t)inBufferSize) {
569 //printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex);
570 // convert the buffer (channel order)
571 convert_buffer(format, inBuffer,
572 rowsPerStrip, width, bytesPerRow);
573 // let libtiff write the encoded strip to the BPositionIO
574 written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, bytesPerStrip);
575 stripIndex++;
576 if (written < 0)
577 break;
578 remaining -= inBufferSize;
579 read = inSource->Read(inBuffer, min_c(inBufferSize, remaining));
580 }
581 // write the rest of the remaining rows
582 if (read < (ssize_t)inBufferSize && read > 0) {
583 //printf("writing remaining bytes: %ld (strip: %ld)\n", read, stripIndex);
584 // convert the buffers (channel order) and compensate
585 // for bytesPerRow != samplesPerRow (padding bytes)
586 convert_buffer(format, inBuffer,
587 read / bytesPerRow, width, bytesPerRow);
588 // let libtiff write the encoded strip to the BPositionIO
589 written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, read);
590 remaining -= read;
591 }
592 } else
593 ret = B_NO_MEMORY;
594 }
595 // clean up input buffer
596 if (inBuffer)
597 _TIFFfree(inBuffer);
598 // see if there was an error reading or writing the streams
599 if (remaining > 0)
600 // "written" may contain a more specific error
601 ret = written < 0 ? written : B_ERROR;
602 else
603 ret = B_OK;
604
605 return ret;
606 }
607
608
609 // #pragma mark -
610
611
TIFFTranslator()612 TIFFTranslator::TIFFTranslator()
613 : BaseTranslator(B_TRANSLATE("TIFF images"),
614 B_TRANSLATE("TIFF image translator"),
615 TIFF_TRANSLATOR_VERSION,
616 sInputFormats, kNumInputFormats,
617 sOutputFormats, kNumOutputFormats,
618 "TIFFTranslator_Settings",
619 sDefaultSettings, kNumDefaultSettings,
620 B_TRANSLATOR_BITMAP, B_TIFF_FORMAT)
621 {
622 // TODO: for now!
623 TIFFSetErrorHandler(NULL);
624 }
625
626
~TIFFTranslator()627 TIFFTranslator::~TIFFTranslator()
628 {
629 }
630
631
632 status_t
DerivedIdentify(BPositionIO * inSource,const translation_format * inFormat,BMessage * ioExtension,translator_info * outInfo,uint32 outType)633 TIFFTranslator::DerivedIdentify(BPositionIO *inSource,
634 const translation_format *inFormat, BMessage *ioExtension,
635 translator_info *outInfo, uint32 outType)
636 {
637 return identify_tiff_header(inSource, ioExtension, outInfo, outType);
638 }
639
640
641 status_t
translate_from_bits(BPositionIO * inSource,uint32 outType,BPositionIO * outDestination)642 TIFFTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
643 BPositionIO *outDestination)
644 {
645 TranslatorBitmap bitsHeader;
646
647 uint32 compression = fSettings->SetGetInt32(TIFF_SETTING_COMPRESSION);
648
649 status_t result;
650 result = identify_bits_header(inSource, NULL, &bitsHeader);
651 if (result != B_OK)
652 return result;
653
654 // Translate B_TRANSLATOR_BITMAP to B_TIFF_FORMAT
655 if (outType == B_TIFF_FORMAT) {
656 // Set up TIFF header
657
658 // get TIFF handle
659 TIFF* tif = TIFFClientOpen("TIFFTranslator", "w", outDestination,
660 tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc,
661 tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc);
662 if (!tif)
663 return B_NO_TRANSLATOR;
664
665 // common fields which are independent of the bitmap format
666 uint32 width = bitsHeader.bounds.IntegerWidth() + 1;
667 uint32 height = bitsHeader.bounds.IntegerHeight() + 1;
668 uint32 dataSize = bitsHeader.dataSize;
669 uint32 bytesPerRow = bitsHeader.rowBytes;
670
671 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
672 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
673 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
674 /*const char* compressionString = NULL;
675 switch (compression) {
676 case COMPRESSION_NONE:
677 compressionString = "None";
678 break;
679 case COMPRESSION_PACKBITS:
680 compressionString = "RLE";
681 break;
682 case COMPRESSION_DEFLATE:
683 compressionString = "Deflate";
684 break;
685 case COMPRESSION_LZW:
686 compressionString = "LZW";
687 break;
688 case COMPRESSION_JPEG:
689 compressionString = "JPEG";
690 break;
691 case COMPRESSION_JP2000:
692 compressionString = "JPEG2000";
693 break;
694 }
695 if (compressionString)
696 printf("using compression: %s\n", compressionString);
697 else
698 printf("using unkown compression (%ld).\n", compression);
699 */
700 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
701
702 // TODO: some extra fields that should also get some special attention
703 TIFFSetField(tif, TIFFTAG_XRESOLUTION, 150.0);
704 TIFFSetField(tif, TIFFTAG_YRESOLUTION, 150.0);
705 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
706
707 // we are going to write XX row(s) of pixels (lines) per strip
708 uint32 rowsPerStrip = TIFFDefaultStripSize(tif, 0);
709 //printf("recommended rows per strip: %ld\n", TIFFDefaultStripSize(tif, 0));
710 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
711
712 status_t ret = B_OK;
713 // set the rest of the fields according to the bitmap format
714 switch (bitsHeader.colors) {
715
716 // Output to 32-bit True Color TIFF (8 bits alpha)
717 case B_RGBA32:
718 case B_RGB32:
719 case B_RGB24:
720 case B_RGBA32_BIG:
721 case B_RGB32_BIG:
722 case B_RGB24_BIG:
723 // set the fields specific to this color space
724 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
725 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
726 // TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
727 // write the tiff stream
728 ret = write_tif_stream(tif, inSource, bitsHeader.colors,
729 width, height, bytesPerRow,
730 rowsPerStrip, dataSize);
731 break;
732 /*
733 case B_CMYA32:
734 break;
735
736 // Output to 15-bit True Color TIFF
737 case B_RGB15:
738 case B_RGB15_BIG:
739 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 5);
740 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
741 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
742 bytesPerStrip = width * 2 * rowsPerStrip;
743 break;
744 */
745 // Output to 8-bit Color Mapped TIFF 32 bits per color map entry
746 case B_CMAP8: {
747 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
748 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
749 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
750 // convert the system palette to 16 bit values for libtiff
751 const color_map *map = system_colors();
752 if (map) {
753 uint16 red[256];
754 uint16 green[256];
755 uint16 blue[256];
756 for (uint32 i = 0; i < 256; i++) {
757 // scale 8 bits to 16 bits
758 red[i] = map->color_list[i].red * 256 + map->color_list[i].red;
759 green[i] = map->color_list[i].green * 256 + map->color_list[i].green;
760 blue[i] = map->color_list[i].blue * 256 + map->color_list[i].blue;
761 }
762 TIFFSetField(tif, TIFFTAG_COLORMAP, &red, &green, &blue);
763 // write the tiff stream
764 ret = write_tif_stream(tif, inSource, bitsHeader.colors,
765 width, height, bytesPerRow,
766 rowsPerStrip, dataSize);
767 } else
768 ret = B_ERROR;
769 break;
770 }
771 // Output to 8-bit Black and White TIFF
772 case B_GRAY8:
773 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
774 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
775 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
776 ret = write_tif_stream(tif, inSource, bitsHeader.colors,
777 width, height, bytesPerRow,
778 rowsPerStrip, dataSize);
779 break;
780
781 /* // Output to 1-bit Black and White TIFF
782 case B_GRAY1:
783 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1);
784 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 8);
785 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
786 bytesPerStrip = ((width + 7) / 8) * rowsPerStrip;
787 break;
788 */
789 default:
790 ret = B_NO_TRANSLATOR;
791 }
792 // Close the handle
793 TIFFClose(tif);
794 return ret;
795
796 } else
797 return B_NO_TRANSLATOR;
798 }
799
800 status_t
translate_from_tiff(BPositionIO * inSource,BMessage * ioExtension,uint32 outType,BPositionIO * outDestination)801 TIFFTranslator::translate_from_tiff(BPositionIO *inSource, BMessage *ioExtension,
802 uint32 outType, BPositionIO *outDestination)
803 {
804 status_t result = B_NO_TRANSLATOR;
805
806 bool bheaderonly = false, bdataonly = false;
807 // Always write out the entire image. Some programs
808 // fail when given "headerOnly", even though they requested it.
809 // These settings are not applicable when outputting TIFFs
810
811 // variables needing cleanup
812 TIFF *ptif = NULL;
813 uint32 *praster = NULL;
814
815 status_t ret;
816 ret = identify_tiff_header(inSource, ioExtension, NULL, outType, &ptif);
817
818 if (outType == B_TIFF_FORMAT && ret == B_OK && ptif) {
819 // if translating from TIFF to TIFF,
820 // just write out the entire TIFF
821 TIFFClose(ptif);
822 translate_direct_copy(inSource, outDestination);
823 return B_OK;
824 }
825
826 while (ret == B_OK && ptif) {
827 // use while / break not for looping, but for
828 // cleaner goto like capability
829
830 ret = B_ERROR;
831 // make certain there is no looping
832
833 uint32 width = 0, height = 0;
834 if (!TIFFGetField(ptif, TIFFTAG_IMAGEWIDTH, &width)) {
835 result = B_NO_TRANSLATOR;
836 break;
837 }
838 if (!TIFFGetField(ptif, TIFFTAG_IMAGELENGTH, &height)) {
839 result = B_NO_TRANSLATOR;
840 break;
841 }
842 size_t npixels = 0;
843 npixels = width * height;
844 praster = static_cast<uint32 *>(_TIFFmalloc(npixels * 4));
845 if (praster && TIFFReadRGBAImage(ptif, width, height, (TIFF_UINT32_TYPE*)praster, 0)) {
846 if (!bdataonly) {
847 // Construct and write Be bitmap header
848 TranslatorBitmap bitsHeader;
849 bitsHeader.magic = B_TRANSLATOR_BITMAP;
850 bitsHeader.bounds.left = 0;
851 bitsHeader.bounds.top = 0;
852 bitsHeader.bounds.right = width - 1;
853 bitsHeader.bounds.bottom = height - 1;
854 bitsHeader.rowBytes = 4 * width;
855 bitsHeader.colors = B_RGBA32;
856 bitsHeader.dataSize = bitsHeader.rowBytes * height;
857 if (swap_data(B_UINT32_TYPE, &bitsHeader,
858 sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
859 result = B_ERROR;
860 break;
861 }
862 outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
863 }
864
865 if (!bheaderonly) {
866 // Convert raw RGBA data to B_RGBA32 colorspace
867 // and write out the results
868 uint8 *pbitsrow = new uint8[width * 4];
869 if (!pbitsrow) {
870 result = B_NO_MEMORY;
871 break;
872 }
873 uint8 *pras8 = reinterpret_cast<uint8 *>(praster);
874 for (uint32 i = 0; i < height; i++) {
875 uint8 *pbits, *prgba;
876 pbits = pbitsrow;
877 prgba = pras8 + ((height - (i + 1)) * width * 4);
878
879 for (uint32 k = 0; k < width; k++) {
880 pbits[0] = prgba[2];
881 pbits[1] = prgba[1];
882 pbits[2] = prgba[0];
883 pbits[3] = prgba[3];
884 pbits += 4;
885 prgba += 4;
886 }
887
888 outDestination->Write(pbitsrow, width * 4);
889 }
890 delete[] pbitsrow;
891 pbitsrow = NULL;
892 }
893
894 result = B_OK;
895 break;
896
897 } // if (praster && TIFFReadRGBAImage(ptif, width, height, praster, 0))
898
899 } // while (ret == B_OK && ptif)
900
901 if (praster) {
902 _TIFFfree(praster);
903 praster = NULL;
904 }
905 if (ptif) {
906 TIFFClose(ptif);
907 ptif = NULL;
908 }
909
910 return result;
911 }
912
913 // ---------------------------------------------------------------
914 // DerivedTranslate
915 //
916 // Translates the data in inSource to the type outType and stores
917 // the translated data in outDestination.
918 //
919 // Preconditions:
920 //
921 // Parameters: inSource, the data to be translated
922 //
923 // inInfo, hint about the data in inSource (not used)
924 //
925 // ioExtension, configuration options for the
926 // translator
927 //
928 // outType, the type to convert inSource to
929 //
930 // outDestination, where the translated data is
931 // put
932 //
933 // baseType, indicates whether inSource is in the
934 // bits format, not in the bits format or
935 // is unknown
936 //
937 // Postconditions:
938 //
939 // Returns: B_BAD_VALUE, if the options in ioExtension are bad
940 //
941 // B_NO_TRANSLATOR, if this translator doesn't understand the data
942 //
943 // B_ERROR, if there was an error allocating memory or converting
944 // data
945 //
946 // B_OK, if all went well
947 // ---------------------------------------------------------------
948 status_t
DerivedTranslate(BPositionIO * inSource,const translator_info * inInfo,BMessage * ioExtension,uint32 outType,BPositionIO * outDestination,int32 baseType)949 TIFFTranslator::DerivedTranslate(BPositionIO *inSource,
950 const translator_info *inInfo, BMessage *ioExtension,
951 uint32 outType, BPositionIO *outDestination, int32 baseType)
952 {
953 if (baseType == 1)
954 // if inSource is in bits format
955 return translate_from_bits(inSource, outType, outDestination);
956 else if (baseType == 0)
957 // if inSource is NOT in bits format
958 return translate_from_tiff(inSource, ioExtension, outType, outDestination);
959 else
960 // if BaseTranslator did not properly identify the data as
961 // bits or not bits
962 return B_NO_TRANSLATOR;
963 }
964
965 BView *
NewConfigView(TranslatorSettings * settings)966 TIFFTranslator::NewConfigView(TranslatorSettings *settings)
967 {
968 return new TIFFView(B_TRANSLATE("TIFFTranslator Settings"),
969 B_WILL_DRAW, settings);
970 }
971