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.
45static 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.
65static 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
85static 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
92const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
93const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
94const 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// ---------------------------------------------------------------
119BTranslator *
120make_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
131BPositionIO *
132tiff_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
142tsize_t
143tiff_read_proc(thandle_t stream, tdata_t buf, tsize_t size)
144{
145	return tiff_get_pio(stream)->Read(buf, size);
146}
147
148tsize_t
149tiff_write_proc(thandle_t stream, tdata_t buf, tsize_t size)
150{
151	return tiff_get_pio(stream)->Write(buf, size);
152}
153
154toff_t
155tiff_seek_proc(thandle_t stream, toff_t off, int whence)
156{
157	return tiff_get_pio(stream)->Seek(off, whence);
158}
159
160int
161tiff_close_proc(thandle_t stream)
162{
163	tiff_get_pio(stream)->Seek(0, SEEK_SET);
164	return 0;
165}
166
167toff_t
168tiff_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
179int
180tiff_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
186void
187tiff_unmap_file_proc(thandle_t stream, tdata_t base, toff_t size)
188{
189	return;
190}
191
192
193status_t
194identify_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
275inline void
276convert_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
292inline void
293convert_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
311inline void
312convert_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
332inline void
333convert_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
353inline void
354convert_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
376inline void
377convert_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
397inline void
398convert_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
411inline void
412convert_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
442inline void
443convert_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
480status_t
481write_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
612TIFFTranslator::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
627TIFFTranslator::~TIFFTranslator()
628{
629}
630
631
632status_t
633TIFFTranslator::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
641status_t
642TIFFTranslator::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;
675switch (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}
695if (compressionString)
696printf("using compression: %s\n", compressionString);
697else
698printf("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
800status_t
801TIFFTranslator::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// ---------------------------------------------------------------
948status_t
949TIFFTranslator::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
965BView *
966TIFFTranslator::NewConfigView(TranslatorSettings *settings)
967{
968	return new TIFFView(B_TRANSLATE("TIFFTranslator Settings"),
969		B_WILL_DRAW, settings);
970}
971