root/src/add-ons/translators/tiff/TIFFTranslator.cpp
/*
 * Copyright 2003-2009, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Wilber
 *              Stephan Aßmus <stippi@yellowbites.com> (write support)
 */


#include "TIFFTranslator.h"
#include "TIFFView.h"

#define TIFF_DISABLE_DEPRECATED
#include "tiffio.h"

#if __GNUC__ == 2
#define TIFF_UINT32_TYPE uint32
#else
#define TIFF_UINT32_TYPE uint32_t
#endif

#include <Catalog.h>
#include <stdio.h>
#include <string.h>


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "TIFFTranslator"


/*!
        How this works:

        libtiff has a special version of TIFFOpen() that gets passed custom
        functions for reading writing etc. and a handle. This handle in our case
        is a BPositionIO object, which libtiff passes on to the functions for reading
        writing etc. So when operations are performed on the TIFF* handle that is
        returned by TIFFOpen(), libtiff uses the special reading writing etc
        functions so that all stream io happens on the BPositionIO object.
*/


// The input formats that this translator supports.
static const translation_format sInputFormats[] = {
        {
                B_TRANSLATOR_BITMAP,
                B_TRANSLATOR_BITMAP,
                BBT_IN_QUALITY,
                BBT_IN_CAPABILITY,
                "image/x-be-bitmap",
                "Be Bitmap Format (TIFFTranslator)"
        },
        {
                B_TIFF_FORMAT,
                B_TRANSLATOR_BITMAP,
                TIFF_IN_QUALITY,
                TIFF_IN_CAPABILITY,
                "image/tiff",
                "TIFF image"
        }
};

// The output formats that this translator supports.
static const translation_format sOutputFormats[] = {
        {
                B_TRANSLATOR_BITMAP,
                B_TRANSLATOR_BITMAP,
                BBT_OUT_QUALITY,
                BBT_OUT_CAPABILITY,
                "image/x-be-bitmap",
                "Be Bitmap Format (TIFFTranslator)"
        },
        {
                B_TIFF_FORMAT,
                B_TRANSLATOR_BITMAP,
                TIFF_OUT_QUALITY,
                TIFF_OUT_CAPABILITY,
                "image/tiff",
                "TIFF image"
        }
};

// Default settings for the Translator
static const TranSetting sDefaultSettings[] = {
        {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
        {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false},
        {TIFF_SETTING_COMPRESSION, TRAN_SETTING_INT32, COMPRESSION_LZW}
                // Compression is LZW by default
};

const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);


// ---------------------------------------------------------------
// make_nth_translator
//
// Creates a TIFFTranslator object to be used by BTranslatorRoster
//
// Preconditions:
//
// Parameters: n,               The translator to return. Since
//                                              TIFFTranslator only publishes one
//                                              translator, it only returns a
//                                              TIFFTranslator if n == 0
//
//             you,     The image_id of the add-on that
//                                              contains code (not used).
//
//             flags,   Has no meaning yet, should be 0.
//
// Postconditions:
//
// Returns: NULL if n is not zero,
//          a new TIFFTranslator if n is zero
// ---------------------------------------------------------------
BTranslator *
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
        if (!n)
                return new TIFFTranslator();
        else
                return NULL;
}


//// libtiff Callback functions!

BPositionIO *
tiff_get_pio(thandle_t stream)
{
        BPositionIO *pio = NULL;
        pio = static_cast<BPositionIO *>(stream);
        if (!pio)
                debugger("pio is NULL");

        return pio;
}

tsize_t
tiff_read_proc(thandle_t stream, tdata_t buf, tsize_t size)
{
        return tiff_get_pio(stream)->Read(buf, size);
}

tsize_t
tiff_write_proc(thandle_t stream, tdata_t buf, tsize_t size)
{
        return tiff_get_pio(stream)->Write(buf, size);
}

toff_t
tiff_seek_proc(thandle_t stream, toff_t off, int whence)
{
        return tiff_get_pio(stream)->Seek(off, whence);
}

int
tiff_close_proc(thandle_t stream)
{
        tiff_get_pio(stream)->Seek(0, SEEK_SET);
        return 0;
}

toff_t
tiff_size_proc(thandle_t stream)
{
        BPositionIO *pio = tiff_get_pio(stream);
        off_t cur, end;
        cur = pio->Position();
        end = pio->Seek(0, SEEK_END);
        pio->Seek(cur, SEEK_SET);

        return end;
}

int
tiff_map_file_proc(thandle_t stream, tdata_t *pbase, toff_t *psize)
{
        // BeOS doesn't support mmap() so just return 0
        return 0;
}

void
tiff_unmap_file_proc(thandle_t stream, tdata_t base, toff_t size)
{
        return;
}


status_t
identify_tiff_header(BPositionIO *inSource, BMessage *ioExtension,
        translator_info *outInfo, uint32 outType, TIFF **poutTIFF = NULL)
{
        // get TIFF handle
        TIFF* tif = TIFFClientOpen("TIFFTranslator", "r", inSource,
                tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc,
                tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc);
        if (!tif)
                return B_NO_TRANSLATOR;

        // count number of documents
        int32 documentCount = 0, documentIndex = 1;
        do {
                documentCount++;
        } while (TIFFReadDirectory(tif));

        if (ioExtension) {
                // Check if a document index has been specified
                if (ioExtension->FindInt32(DOCUMENT_INDEX, &documentIndex) != B_OK)
                        documentIndex = 1;

                if (documentIndex < 1 || documentIndex > documentCount) {
                        // document index is invalid
                        fputs(B_TRANSLATE("identify_tiff_header: invalid "
                                "document index\n"), stderr);
                        return B_NO_TRANSLATOR;
                }
        }

        // identify the document the user specified or the first document
        // if the user did not specify which document they wanted to identify
        if (!TIFFSetDirectory(tif, documentIndex - 1)) {
                fputs(B_TRANSLATE("identify_tiff_header: couldn't set "
                        "directory\n"), stderr);
                return B_NO_TRANSLATOR;
        }

        if (ioExtension) {
                // add page count to ioExtension
                ioExtension->RemoveName(DOCUMENT_COUNT);
                ioExtension->AddInt32(DOCUMENT_COUNT, documentCount);
        }

        if (outInfo) {
                outInfo->type = B_TIFF_FORMAT;
                outInfo->group = B_TRANSLATOR_BITMAP;
                outInfo->quality = TIFF_IN_QUALITY;
                outInfo->capability = TIFF_IN_CAPABILITY;
                strcpy(outInfo->MIME, "image/tiff");
                strlcpy(outInfo->name, B_TRANSLATE("TIFF image"),
                        sizeof(outInfo->name));
        }

        if (!poutTIFF) {
                // close TIFF if caller is not interested in TIFF handle
                TIFFClose(tif);
        } else {
                // leave TIFF open and return handle if caller needs it
                *poutTIFF = tif;
        }

        return B_OK;
}


// How this works:
// Following are a couple of functions,
//
// convert_buffer_*   to convert a buffer in place to the TIFF native format
//
// convert_buffers_*  to convert from one buffer to another to the TIFF
//                    native format, additionally compensating for padding bytes
//                    I don't know if libTIFF can be set up to respect padding bytes,
//                    otherwise this whole thing could be simplified a bit.
//
// Additionally, there are two functions convert_buffer() and convert_buffers() that take
// a color_space as one of the arguments and pick the correct worker functions from there.
// This way I don't write any code more than once, for easier debugging and maintainance.


// convert_buffer_bgra_rgba
inline void
convert_buffer_bgra_rgba(uint8* buffer, uint32 rows, uint32 width,
        uint32 bytesPerRow)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* handle = buffer;
                for (uint32 x = 0; x < width; x++) {
                        uint8 temp = handle[0];
                        handle[0] = handle[2];
                        handle[2] = temp;
                        handle += 4;
                }
                buffer += bytesPerRow;
        }
}

// convert_buffer_argb_rgba
inline void
convert_buffer_argb_rgba(uint8* buffer, uint32 rows, uint32 width,
        uint32 bytesPerRow)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* handle = buffer;
                for (uint32 x = 0; x < width; x++) {
                        uint8 temp = handle[0];
                        handle[0] = handle[1];
                        handle[1] = handle[2];
                        handle[2] = handle[3];
                        handle[3] = temp;
                        handle += 4;
                }
                buffer += bytesPerRow;
        }
}

// convert_buffers_bgra_rgba
inline void
convert_buffers_bgra_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows,
        uint32 width, uint32 bytesPerRow)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* inHandle = inBuffer;
                uint8* outHandle = outBuffer;
                for (uint32 x = 0; x < width; x++) {
                        outHandle[0] = inHandle[2];
                        outHandle[1] = inHandle[1];
                        outHandle[2] = inHandle[0];
                        outHandle[3] = inHandle[3];
                        inHandle += 4;
                        outHandle += 4;
                }
                inBuffer += bytesPerRow;
                outBuffer += width * 4;
        }
}

// convert_buffers_argb_rgba
inline void
convert_buffers_argb_rgba(uint8* inBuffer, uint8* outBuffer, uint32 rows,
        uint32 width, uint32 bytesPerRow)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* inHandle = inBuffer;
                uint8* outHandle = outBuffer;
                for (uint32 x = 0; x < width; x++) {
                        outHandle[0] = inHandle[1];
                        outHandle[1] = inHandle[2];
                        outHandle[2] = inHandle[3];
                        outHandle[3] = inHandle[0];
                        inHandle += 4;
                        outHandle += 4;
                }
                inBuffer += bytesPerRow;
                outBuffer += width * 4;
        }
}

// convert_buffers_bgrX_rgb
inline void
convert_buffers_bgrX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows,
        uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* inHandle = inBuffer;
                uint8* outHandle = outBuffer;
                for (uint32 x = 0; x < width; x++) {
                        // the usage of temp is just in case inBuffer == outBuffer
                        // (see convert_buffer() for B_RGB24)
                        uint8 temp = inHandle[0];
                        outHandle[0] = inHandle[2];
                        outHandle[1] = inHandle[1];
                        outHandle[2] = temp;
                        inHandle += samplesPerPixel;
                        outHandle += 3;
                }
                inBuffer += bytesPerRow;
                outBuffer += width * 3;
        }
}

// convert_buffers_rgbX_rgb
inline void
convert_buffers_rgbX_rgb(uint8* inBuffer, uint8* outBuffer, uint32 rows,
        uint32 width, uint32 bytesPerRow, uint32 samplesPerPixel)
{
        for (uint32 y = 0; y < rows; y++) {
                uint8* inHandle = inBuffer;
                uint8* outHandle = outBuffer;
                for (uint32 x = 0; x < width; x++) {
                        outHandle[0] = inHandle[0];
                        outHandle[1] = inHandle[1];
                        outHandle[2] = inHandle[2];
                        inHandle += samplesPerPixel;
                        outHandle += 3;
                }
                inBuffer += bytesPerRow;
                outBuffer += width * 3;
        }
}


// convert_buffers_cmap
inline void
convert_buffers_cmap(uint8* inBuffer, uint8* outBuffer, uint32 rows,
        uint32 width, uint32 bytesPerRow)
{
        // compensate for bytesPerRow != width (padding bytes)
        // this function will not be called if bytesPerRow == width, btw
        for (uint32 y = 0; y < rows; y++) {
                _TIFFmemcpy(outBuffer, inBuffer, width);
                inBuffer += bytesPerRow;
                outBuffer += width;
        }
}

// convert_buffer
inline void
convert_buffer(color_space format, uint8* buffer, uint32 rows, uint32 width,
        uint32 bytesPerRow)
{
        switch (format) {
                case B_RGBA32:
                        convert_buffer_bgra_rgba(buffer, rows, width, bytesPerRow);
                        break;
                case B_RGBA32_BIG:
                        convert_buffer_argb_rgba(buffer, rows, width, bytesPerRow);
                        break;
//              case B_RGB32:
//              case B_RGB32_BIG:
//                      these two cannot be encountered, since inBufferSize != bytesPerStrip
//                      (we're stripping the unused "alpha" channel 32->24 bits)
                case B_RGB24:
                        convert_buffers_bgrX_rgb(buffer, buffer, rows, width, bytesPerRow, 3);
                        break;
//              case B_RGB24_BIG:
                        // buffer already has the correct format
                        break;
//              case B_CMAP8:
//              case B_GRAY8:
                        // buffer already has the correct format
                        break;
                default:
                        break;
        }
}

// convert_buffers
inline void
convert_buffers(color_space format, uint8* inBuffer, uint8* outBuffer,
        uint32 rows, uint32 width, uint32 bytesPerRow)
{
        switch (format) {
                case B_RGBA32:
                        convert_buffers_bgra_rgba(inBuffer, outBuffer, rows, width, bytesPerRow);
                        break;
                case B_RGBA32_BIG:
                        convert_buffers_argb_rgba(inBuffer, outBuffer, rows, width, bytesPerRow);
                        break;
                case B_RGB32:
                        convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4);
                        break;
                case B_RGB32_BIG:
                        convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 4);
                        break;
                case B_RGB24:
                        convert_buffers_bgrX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3);
                        break;
                case B_RGB24_BIG:
                        convert_buffers_rgbX_rgb(inBuffer, outBuffer, rows, width, bytesPerRow, 3);
                        break;
                case B_CMAP8:
                case B_GRAY8:
                        convert_buffers_cmap(inBuffer, outBuffer, rows, width, bytesPerRow);
                        break;
                default:
                        break;
        }
}

// Sets up any additional TIFF fields for the color spaces it supports,
// determines if it needs one or two buffers to carry out any conversions,
// uses the various convert routines above to do the actual conversion,
// writes complete strips of data plus one strip of remaining data.
//
// write_tif_stream
status_t
write_tif_stream(TIFF* tif, BPositionIO* inSource, color_space format,
                                 uint32 width, uint32 height, uint32 bytesPerRow,
                                 uint32 rowsPerStrip, uint32 dataSize)
{
        uint32 bytesPerStrip = 0;

        // set up the TIFF fields about what channels we write
        switch (format) {
                case B_RGBA32:
                case B_RGBA32_BIG:
                        uint16 extraSamples[1];
                        extraSamples[0] = EXTRASAMPLE_UNASSALPHA;
                        TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, extraSamples);
                        TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
                        // going to write rgb + alpha channels
                        bytesPerStrip = width * 4 * rowsPerStrip;
                        break;
                case B_RGB32:
                case B_RGB32_BIG:
                case B_RGB24:
                case B_RGB24_BIG:
                        TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
                        // going to write just the rgb channels
                        bytesPerStrip = width * 3 * rowsPerStrip;
                        break;
                case B_CMAP8:
                case B_GRAY8:
                        bytesPerStrip = width * rowsPerStrip;
                        break;
                default:
                        return B_BAD_VALUE;
        }

        uint32 remaining = dataSize;
        status_t ret = B_OK;
        // Write the information to the stream
        uint32 inBufferSize = bytesPerRow * rowsPerStrip;
        // allocate intermediate input buffer
        uint8* inBuffer = (uint8*)_TIFFmalloc(inBufferSize);
        ssize_t read, written = B_ERROR;
        // bytesPerStrip is the size of the buffer that libtiff expects to write per strip
        // it might be different to the size of the input buffer,
        // if that one contains padding bytes at the end of each row
        if (inBufferSize != bytesPerStrip) {
                // allocate a second buffer
                // (two buffers are needed since padding bytes have to be compensated for)
                uint8* outBuffer = (uint8*)_TIFFmalloc(bytesPerStrip);
                if (inBuffer && outBuffer) {
//printf("using two buffers\n");
                        read = inSource->Read(inBuffer, inBufferSize);
                        uint32 stripIndex = 0;
                        while (read == (ssize_t)inBufferSize) {
//printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex);
                                // convert the buffers (channel order) and compensate
                                // for bytesPerRow != samplesPerRow (padding bytes)
                                convert_buffers(format, inBuffer, outBuffer,
                                                                rowsPerStrip, width, bytesPerRow);
                                // let libtiff write the encoded strip to the BPositionIO
                                written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, bytesPerStrip);
                                stripIndex++;
                                if (written < B_OK)
                                        break;
                                remaining -= inBufferSize;
                                read = inSource->Read(inBuffer, min_c(inBufferSize, remaining));
                        }
                        // write the rest of the remaining rows
                        if (read < (ssize_t)inBufferSize && read > 0) {
//printf("writing remaining bytes: %ld\n", read);
                                // convert the buffers (channel order) and compensate
                                // for bytesPerRow != samplesPerRow (padding bytes)
                                convert_buffers(format, inBuffer, outBuffer,
                                                                read / bytesPerRow, width, bytesPerRow);
                                // let libtiff write the encoded strip to the BPositionIO
                                written = TIFFWriteEncodedStrip(tif, stripIndex, outBuffer, read);
                                remaining -= read;
                        }
                } else
                        ret = B_NO_MEMORY;
                // clean up output buffer
                if (outBuffer)
                        _TIFFfree(outBuffer);
        } else {
//printf("using one buffer\n");
                // the input buffer is all we need, we convert it in place
                if (inBuffer) {
                        read = inSource->Read(inBuffer, inBufferSize);
                        uint32 stripIndex = 0;
                        while (read == (ssize_t)inBufferSize) {
//printf("writing bytes: %ld (strip: %ld)\n", read, stripIndex);
                                // convert the buffer (channel order)
                                convert_buffer(format, inBuffer,
                                                           rowsPerStrip, width, bytesPerRow);
                                // let libtiff write the encoded strip to the BPositionIO
                                written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, bytesPerStrip);
                                stripIndex++;
                                if (written < 0)
                                        break;
                                remaining -= inBufferSize;
                                read = inSource->Read(inBuffer, min_c(inBufferSize, remaining));
                        }
                        // write the rest of the remaining rows
                        if (read < (ssize_t)inBufferSize && read > 0) {
//printf("writing remaining bytes: %ld (strip: %ld)\n", read, stripIndex);
                                // convert the buffers (channel order) and compensate
                                // for bytesPerRow != samplesPerRow (padding bytes)
                                convert_buffer(format, inBuffer,
                                                           read / bytesPerRow, width, bytesPerRow);
                                // let libtiff write the encoded strip to the BPositionIO
                                written = TIFFWriteEncodedStrip(tif, stripIndex, inBuffer, read);
                                remaining -= read;
                        }
                } else
                        ret = B_NO_MEMORY;
        }
        // clean up input buffer
        if (inBuffer)
                _TIFFfree(inBuffer);
        // see if there was an error reading or writing the streams
        if (remaining > 0)
                // "written" may contain a more specific error
                ret = written < 0 ? written : B_ERROR;
        else
                ret = B_OK;

        return ret;
}


//      #pragma mark -


TIFFTranslator::TIFFTranslator()
        : BaseTranslator(B_TRANSLATE("TIFF images"),
                B_TRANSLATE("TIFF image translator"),
                TIFF_TRANSLATOR_VERSION,
                sInputFormats, kNumInputFormats,
                sOutputFormats, kNumOutputFormats,
                "TIFFTranslator_Settings",
                sDefaultSettings, kNumDefaultSettings,
                B_TRANSLATOR_BITMAP, B_TIFF_FORMAT)
{
        // TODO: for now!
        TIFFSetErrorHandler(NULL);
}


TIFFTranslator::~TIFFTranslator()
{
}


status_t
TIFFTranslator::DerivedIdentify(BPositionIO *inSource,
        const translation_format *inFormat, BMessage *ioExtension,
        translator_info *outInfo, uint32 outType)
{
        return identify_tiff_header(inSource, ioExtension, outInfo, outType);
}


status_t
TIFFTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
        BPositionIO *outDestination)
{
        TranslatorBitmap bitsHeader;

        uint32 compression = fSettings->SetGetInt32(TIFF_SETTING_COMPRESSION);

        status_t result;
        result = identify_bits_header(inSource, NULL, &bitsHeader);
        if (result != B_OK)
                return result;

        // Translate B_TRANSLATOR_BITMAP to B_TIFF_FORMAT
        if (outType == B_TIFF_FORMAT) {
                // Set up TIFF header

                // get TIFF handle
                TIFF* tif = TIFFClientOpen("TIFFTranslator", "w", outDestination,
                        tiff_read_proc, tiff_write_proc, tiff_seek_proc, tiff_close_proc,
                        tiff_size_proc, tiff_map_file_proc, tiff_unmap_file_proc);
                if (!tif)
                        return B_NO_TRANSLATOR;

                // common fields which are independent of the bitmap format
                uint32 width = bitsHeader.bounds.IntegerWidth() + 1;
                uint32 height = bitsHeader.bounds.IntegerHeight() + 1;
                uint32 dataSize = bitsHeader.dataSize;
                uint32 bytesPerRow = bitsHeader.rowBytes;

                TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
                TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
                TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
/*const char* compressionString = NULL;
switch (compression) {
        case COMPRESSION_NONE:
                compressionString = "None";
                break;
        case COMPRESSION_PACKBITS:
                compressionString = "RLE";
                break;
        case COMPRESSION_DEFLATE:
                compressionString = "Deflate";
                break;
        case COMPRESSION_LZW:
                compressionString = "LZW";
                break;
        case COMPRESSION_JPEG:
                compressionString = "JPEG";
                break;
        case COMPRESSION_JP2000:
                compressionString = "JPEG2000";
                break;
}
if (compressionString)
printf("using compression: %s\n", compressionString);
else
printf("using unkown compression (%ld).\n", compression);
*/
                TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);

                // TODO: some extra fields that should also get some special attention
                TIFFSetField(tif, TIFFTAG_XRESOLUTION, 150.0);
                TIFFSetField(tif, TIFFTAG_YRESOLUTION, 150.0);
                TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);

                // we are going to write XX row(s) of pixels (lines) per strip
                uint32 rowsPerStrip = TIFFDefaultStripSize(tif, 0);
//printf("recommended rows per strip: %ld\n", TIFFDefaultStripSize(tif, 0));
                TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);

                status_t ret = B_OK;
                // set the rest of the fields according to the bitmap format
                switch (bitsHeader.colors) {

                        // Output to 32-bit True Color TIFF (8 bits alpha)
                        case B_RGBA32:
                        case B_RGB32:
                        case B_RGB24:
                        case B_RGBA32_BIG:
                        case B_RGB32_BIG:
                        case B_RGB24_BIG:
                                // set the fields specific to this color space
                                TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
                                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
//                              TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
                                // write the tiff stream
                                ret = write_tif_stream(tif, inSource, bitsHeader.colors,
                                                                           width, height, bytesPerRow,
                                                                           rowsPerStrip, dataSize);
                                break;
/*
                        case B_CMYA32:
                                break;

                        // Output to 15-bit True Color TIFF
                        case B_RGB15:
                        case B_RGB15_BIG:
                                TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 5);
                                TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
                                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
                                bytesPerStrip = width * 2 * rowsPerStrip;
                                break;
*/
                        // Output to 8-bit Color Mapped TIFF 32 bits per color map entry
                        case B_CMAP8: {
                                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE);
                                TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
                                TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
                                // convert the system palette to 16 bit values for libtiff
                                const color_map *map = system_colors();
                                if (map) {
                                        uint16 red[256];
                                        uint16 green[256];
                                        uint16 blue[256];
                                        for (uint32 i = 0; i < 256; i++) {
                                                // scale 8 bits to 16 bits
                                                red[i] = map->color_list[i].red * 256 + map->color_list[i].red;
                                                green[i] = map->color_list[i].green * 256 + map->color_list[i].green;
                                                blue[i] = map->color_list[i].blue * 256 + map->color_list[i].blue;
                                        }
                                        TIFFSetField(tif, TIFFTAG_COLORMAP, &red, &green, &blue);
                                        // write the tiff stream
                                        ret = write_tif_stream(tif, inSource, bitsHeader.colors,
                                                                                   width, height, bytesPerRow,
                                                                                   rowsPerStrip, dataSize);
                                } else
                                        ret = B_ERROR;
                                break;
                        }
                        // Output to 8-bit Black and White TIFF
                        case B_GRAY8:
                                TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
                                TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
                                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
                                ret = write_tif_stream(tif, inSource, bitsHeader.colors,
                                                                           width, height, bytesPerRow,
                                                                           rowsPerStrip, dataSize);
                                break;

/*                      // Output to 1-bit Black and White TIFF
                        case B_GRAY1:
                                TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1);
                                TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 8);
                                TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
                                bytesPerStrip = ((width + 7) / 8) * rowsPerStrip;
                                break;
*/
                        default:
                                ret = B_NO_TRANSLATOR;
                }
                // Close the handle
                TIFFClose(tif);
                return ret;

        } else
                return B_NO_TRANSLATOR;
}

status_t
TIFFTranslator::translate_from_tiff(BPositionIO *inSource, BMessage *ioExtension,
        uint32 outType, BPositionIO *outDestination)
{
        status_t result = B_NO_TRANSLATOR;

        bool bheaderonly = false, bdataonly = false;
                // Always write out the entire image. Some programs
                // fail when given "headerOnly", even though they requested it.
                // These settings are not applicable when outputting TIFFs

        // variables needing cleanup
        TIFF *ptif = NULL;
        uint32 *praster = NULL;

        status_t ret;
        ret = identify_tiff_header(inSource, ioExtension, NULL, outType, &ptif);

        if (outType == B_TIFF_FORMAT && ret == B_OK && ptif) {
                // if translating from TIFF to TIFF,
                // just write out the entire TIFF
                TIFFClose(ptif);
                translate_direct_copy(inSource, outDestination);
                return B_OK;
        }

        while (ret == B_OK && ptif) {
                // use while / break not for looping, but for
                // cleaner goto like capability

                ret = B_ERROR;
                        // make certain there is no looping

                uint32 width = 0, height = 0;
                if (!TIFFGetField(ptif, TIFFTAG_IMAGEWIDTH, &width)) {
                        result = B_NO_TRANSLATOR;
                        break;
                }
                if (!TIFFGetField(ptif, TIFFTAG_IMAGELENGTH, &height)) {
                        result = B_NO_TRANSLATOR;
                        break;
                }
                size_t npixels = 0;
                npixels = width * height;
                praster = static_cast<uint32 *>(_TIFFmalloc(npixels * 4));
                if (praster && TIFFReadRGBAImage(ptif, width, height, (TIFF_UINT32_TYPE*)praster, 0)) {
                        if (!bdataonly) {
                                // Construct and write Be bitmap header
                                TranslatorBitmap bitsHeader;
                                bitsHeader.magic = B_TRANSLATOR_BITMAP;
                                bitsHeader.bounds.left = 0;
                                bitsHeader.bounds.top = 0;
                                bitsHeader.bounds.right = width - 1;
                                bitsHeader.bounds.bottom = height - 1;
                                bitsHeader.rowBytes = 4 * width;
                                bitsHeader.colors = B_RGBA32;
                                bitsHeader.dataSize = bitsHeader.rowBytes * height;
                                if (swap_data(B_UINT32_TYPE, &bitsHeader,
                                        sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
                                        result = B_ERROR;
                                        break;
                                }
                                outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
                        }

                        if (!bheaderonly) {
                                // Convert raw RGBA data to B_RGBA32 colorspace
                                // and write out the results
                                uint8 *pbitsrow = new uint8[width * 4];
                                if (!pbitsrow) {
                                        result = B_NO_MEMORY;
                                        break;
                                }
                                uint8 *pras8 = reinterpret_cast<uint8 *>(praster);
                                for (uint32 i = 0; i < height; i++) {
                                        uint8 *pbits, *prgba;
                                        pbits = pbitsrow;
                                        prgba = pras8 + ((height - (i + 1)) * width * 4);

                                        for (uint32 k = 0; k < width; k++) {
                                                pbits[0] = prgba[2];
                                                pbits[1] = prgba[1];
                                                pbits[2] = prgba[0];
                                                pbits[3] = prgba[3];
                                                pbits += 4;
                                                prgba += 4;
                                        }

                                        outDestination->Write(pbitsrow, width * 4);
                                }
                                delete[] pbitsrow;
                                pbitsrow = NULL;
                        }

                        result = B_OK;
                        break;

                } // if (praster && TIFFReadRGBAImage(ptif, width, height, praster, 0))

        } // while (ret == B_OK && ptif)

        if (praster) {
                _TIFFfree(praster);
                praster = NULL;
        }
        if (ptif) {
                TIFFClose(ptif);
                ptif = NULL;
        }

        return result;
}

// ---------------------------------------------------------------
// DerivedTranslate
//
// Translates the data in inSource to the type outType and stores
// the translated data in outDestination.
//
// Preconditions:
//
// Parameters:  inSource,       the data to be translated
//
//                              inInfo, hint about the data in inSource (not used)
//
//                              ioExtension,    configuration options for the
//                                                              translator
//
//                              outType,        the type to convert inSource to
//
//                              outDestination, where the translated data is
//                                                              put
//
//                              baseType, indicates whether inSource is in the
//                                        bits format, not in the bits format or
//                                        is unknown
//
// Postconditions:
//
// Returns: B_BAD_VALUE, if the options in ioExtension are bad
//
// B_NO_TRANSLATOR, if this translator doesn't understand the data
//
// B_ERROR, if there was an error allocating memory or converting
//          data
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
TIFFTranslator::DerivedTranslate(BPositionIO *inSource,
                const translator_info *inInfo, BMessage *ioExtension,
                uint32 outType, BPositionIO *outDestination, int32 baseType)
{
        if (baseType == 1)
                // if inSource is in bits format
                return translate_from_bits(inSource, outType, outDestination);
        else if (baseType == 0)
                // if inSource is NOT in bits format
                return translate_from_tiff(inSource, ioExtension, outType, outDestination);
        else
                // if BaseTranslator did not properly identify the data as
                // bits or not bits
                return B_NO_TRANSLATOR;
}

BView *
TIFFTranslator::NewConfigView(TranslatorSettings *settings)
{
        return new TIFFView(B_TRANSLATE("TIFFTranslator Settings"),
                B_WILL_DRAW, settings);
}