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