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