root/src/add-ons/translators/bmp/BMPTranslator.cpp
/*
 * Copyright 2002-2009, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Wilber <mwilber@users.berlios.de>
 */

#include "BMPTranslator.h"

#include <algorithm>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Catalog.h>

#include "BMPView.h"


using std::nothrow;
using std::min;


//#define INFO(x) printf(x);
#define INFO(x)
//#define ERROR(x) printf(x);
#define ERROR(x)

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "BMPTranslator"


// 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 (BMPTranslator)"
        },
        {
                B_BMP_FORMAT,
                B_TRANSLATOR_BITMAP,
                BMP_IN_QUALITY,
                BMP_IN_CAPABILITY,
                "image/bmp",
                "BMP 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 (BMPTranslator)"
        },
        {
                B_BMP_FORMAT,
                B_TRANSLATOR_BITMAP,
                BMP_OUT_QUALITY,
                BMP_OUT_CAPABILITY,
                "image/bmp",
                "BMP 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}
};

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 BMPTranslator object to be used by BTranslatorRoster
//
// Preconditions:
//
// Parameters: n,               The translator to return. Since
//                                              BMPTranslator only publishes one
//                                              translator, it only returns a
//                                              BMPTranslator 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 BMPTranslator if n is zero
// ---------------------------------------------------------------
BTranslator *
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
        if (!n)
                return new BMPTranslator();
        else
                return NULL;
}

// ---------------------------------------------------------------
// Constructor
//
// Sets up the version info and the name of the translator so that
// these values can be returned when they are requested.
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
BMPTranslator::BMPTranslator()
        : BaseTranslator(B_TRANSLATE("BMP images"),
                B_TRANSLATE("BMP image translator"),
                BMP_TRANSLATOR_VERSION,
                sInputFormats, kNumInputFormats,
                sOutputFormats, kNumOutputFormats,
                "BMPTranslator_Settings",
                sDefaultSettings, kNumDefaultSettings,
                B_TRANSLATOR_BITMAP, B_BMP_FORMAT)
{
}

// ---------------------------------------------------------------
// Destructor
//
// Does nothing
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
BMPTranslator::~BMPTranslator()
{
}

// ---------------------------------------------------------------
// get_padding
//
// Returns number of bytes of padding required at the end of
// the row by the BMP format
//
//
// Preconditions: If bitsperpixel is zero, a division by zero
//                will occur, which is bad
//
// Parameters:  width, width of the row, in pixels
//
//                              bitsperpixel, bitdepth of the image
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
int32
get_padding(uint32 width, uint16 bitsperpixel)
{
        int32 padding = 0;

        if (bitsperpixel > 8) {
                uint8 bytesPerPixel = bitsperpixel / 8;
                padding = (width * bytesPerPixel) % 4;
        } else {
                uint8 pixelsPerByte = 8 / bitsperpixel;
                if (!(width % pixelsPerByte))
                        padding = (width / pixelsPerByte) % 4;
                else
                        padding = ((width + pixelsPerByte -
                                (width % pixelsPerByte)) /
                                        pixelsPerByte) % 4;
        }

        if (padding)
                padding = 4 - padding;

        return padding;
}

// ---------------------------------------------------------------
// get_rowbytes
//
// Returns number of bytes required to store a row of BMP pixels
// with a width of width and a bit depth of bitsperpixel.
//
//
// Preconditions: If bitsperpixel is zero, a division by zero
//                will occur, which is bad
//
// Parameters:  width, width of the row, in pixels
//
//                              bitsperpixel, bitdepth of the image
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
int32
get_rowbytes(uint32 width, uint16 bitsperpixel)
{
        int32 rowbytes = 0;
        int32 padding = get_padding(width, bitsperpixel);

        if (bitsperpixel > 8) {
                uint8 bytesPerPixel = bitsperpixel / 8;
                rowbytes = (width * bytesPerPixel) + padding;
        } else {
                uint8 pixelsPerByte = 8 / bitsperpixel;
                rowbytes = (width / pixelsPerByte) +
                        ((width % pixelsPerByte) ? 1 : 0) + padding;
        }

        return rowbytes;
}

// ---------------------------------------------------------------
// identify_bmp_header
//
// Determines if the data in inSource is in the MS or OS/2 BMP
// format. If it is, it returns info about the data in inSource
// to outInfo, pfileheader, pmsheader, pfrommsformat and os2skip.
//
// Preconditions:
//
// Parameters:  inSource,       The source of the image data
//
//                              outInfo,        Information about the translator
//                                                      is copied here
//
//                              amtread,        Amount of data read from inSource
//                                                      before this function was called
//
//                              read,           Pointer to the data that was read
//                                                      in before this function was called
//
//                              pfileheader,    File header info for the BMP is
//                                                              copied here after it is read from
//                                                              the file.
//
//                              pmsheader,              BMP header info read in from the
//                                                              BMP file
//
//                              pfrommsformat,  Set to true if BMP data is BMP
//                                                              format, false if BMP data is OS/2
//                                                              format.
//
//                              pos2skip,       If data is in OS/2 format, the number
//                                                      of bytes to skip between the header
//                                                      data and image data is stored here
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,    if the data does not look like
//                                                              BMP format data
//
// B_ERROR,     if the header data could not be converted to host
//                      format
//
// B_OK,        if the data looks like bits data and no errors were
//                      encountered
// ---------------------------------------------------------------
status_t
identify_bmp_header(BPositionIO *inSource, translator_info *outInfo,
        BMPFileHeader *pfileheader = NULL, MSInfoHeader *pmsheader = NULL,
        bool *pfrommsformat = NULL, off_t *pos2skip = NULL)
{
        // read in the fileHeader
        uint8 buf[40];
        BMPFileHeader fileHeader;
        ssize_t size = 14;
        if (inSource->Read(buf, size) != size)
                return B_NO_TRANSLATOR;

        // check BMP magic number
        const uint16 kBmpMagic = B_HOST_TO_LENDIAN_INT16('MB');
        uint16 sourceMagic;
        memcpy(&sourceMagic, buf, sizeof(uint16));
        if (sourceMagic != kBmpMagic)
                return B_NO_TRANSLATOR;

        // convert fileHeader to host byte order
        memcpy(&fileHeader.magic, buf, 2);
        memcpy(&fileHeader.fileSize, buf + 2, 4);
        memcpy(&fileHeader.reserved, buf + 6, 4);
        memcpy(&fileHeader.dataOffset, buf + 10, 4);
        if (swap_data(B_UINT16_TYPE, &fileHeader.magic, sizeof(uint16),
                B_SWAP_LENDIAN_TO_HOST) != B_OK)
                return B_ERROR;
        if (swap_data(B_UINT32_TYPE,
                (reinterpret_cast<uint8 *> (&fileHeader)) + 2, 12,
                B_SWAP_LENDIAN_TO_HOST) != B_OK)
                return B_ERROR;

        if (fileHeader.reserved != 0)
                return B_NO_TRANSLATOR;

        uint32 headersize = 0;
        if (inSource->Read(&headersize, 4) != 4)
                return B_NO_TRANSLATOR;
        if (swap_data(B_UINT32_TYPE, &headersize, 4,
                B_SWAP_LENDIAN_TO_HOST) != B_OK)
                return B_ERROR;

        if (headersize == sizeof(MSInfoHeader)) {
                // MS format

                if (fileHeader.dataOffset < 54)
                        return B_NO_TRANSLATOR;

                MSInfoHeader msheader;
                msheader.size = headersize;
                if (inSource->Read(
                        reinterpret_cast<uint8 *> (&msheader) + 4, 36) != 36)
                        return B_NO_TRANSLATOR;

                // convert msheader to host byte order
                if (swap_data(B_UINT32_TYPE,
                        reinterpret_cast<uint8 *> (&msheader) + 4, 36,
                        B_SWAP_LENDIAN_TO_HOST) != B_OK)
                        return B_ERROR;

                // check if msheader is valid
                if (msheader.width == 0 || msheader.height == 0)
                        return B_NO_TRANSLATOR;
                if (msheader.planes != 1)
                        return B_NO_TRANSLATOR;
                if ((msheader.bitsperpixel != 1 ||
                                msheader.compression != BMP_NO_COMPRESS) &&
                        (msheader.bitsperpixel != 4 ||
                                msheader.compression != BMP_NO_COMPRESS) &&
                        (msheader.bitsperpixel != 4 ||
                                msheader.compression != BMP_RLE4_COMPRESS) &&
                        (msheader.bitsperpixel != 8 ||
                                msheader.compression != BMP_NO_COMPRESS) &&
                        (msheader.bitsperpixel != 8 ||
                                msheader.compression != BMP_RLE8_COMPRESS) &&
                        (msheader.bitsperpixel != 24 ||
                                msheader.compression != BMP_NO_COMPRESS) &&
                        (msheader.bitsperpixel != 32 ||
                                msheader.compression != BMP_NO_COMPRESS))
                        return B_NO_TRANSLATOR;
                if (!msheader.imagesize && msheader.compression)
                        return B_NO_TRANSLATOR;
                if (msheader.colorsimportant > msheader.colorsused)
                        return B_NO_TRANSLATOR;

                if (outInfo) {
                        outInfo->type = B_BMP_FORMAT;
                        outInfo->group = B_TRANSLATOR_BITMAP;
                        outInfo->quality = BMP_IN_QUALITY;
                        outInfo->capability = BMP_IN_CAPABILITY;
                        sprintf(outInfo->name, 
                                B_TRANSLATE_COMMENT("BMP image (MS format, %d bits", 
                                "Ignore missing closing round bracket"), 
                                msheader.bitsperpixel);
                        if (msheader.compression)
                                strcat(outInfo->name, ", RLE)");
                        else
                                strcat(outInfo->name, ")");
                        strcpy(outInfo->MIME, "image/x-bmp");
                }

                if (pfileheader) {
                        pfileheader->magic = fileHeader.magic;
                        pfileheader->fileSize = fileHeader.fileSize;
                        pfileheader->reserved = fileHeader.reserved;
                        pfileheader->dataOffset = fileHeader.dataOffset;
                }
                if (pmsheader) {
                        pmsheader->size = msheader.size;
                        pmsheader->width = abs(msheader.width);
                        pmsheader->height = msheader.height;
                        pmsheader->planes = msheader.planes;
                        pmsheader->bitsperpixel = msheader.bitsperpixel;
                        pmsheader->compression = msheader.compression;
                        pmsheader->imagesize = msheader.imagesize;
                        pmsheader->xpixperm = msheader.xpixperm;
                        pmsheader->ypixperm = msheader.ypixperm;
                        pmsheader->colorsused = msheader.colorsused;
                        pmsheader->colorsimportant = msheader.colorsimportant;
                }
                if (pfrommsformat)
                        (*pfrommsformat) = true;

                return B_OK;

        } else if (headersize == sizeof(OS2InfoHeader)) {
                // OS/2 format

                if (fileHeader.dataOffset < 26)
                        return B_NO_TRANSLATOR;

                OS2InfoHeader os2header;
                os2header.size = headersize;
                if (inSource->Read(
                        reinterpret_cast<uint8 *> (&os2header) + 4, 8) != 8)
                        return B_NO_TRANSLATOR;

                // convert msheader to host byte order
                if (swap_data(B_UINT32_TYPE,
                        reinterpret_cast<uint8 *> (&os2header) + 4, 8,
                        B_SWAP_LENDIAN_TO_HOST) != B_OK)
                        return B_ERROR;

                // check if msheader is valid
                if (os2header.width == 0 || os2header.height == 0)
                        return B_NO_TRANSLATOR;
                if (os2header.planes != 1)
                        return B_NO_TRANSLATOR;
                if (os2header.bitsperpixel != 1 &&
                        os2header.bitsperpixel != 4 &&
                        os2header.bitsperpixel != 8 &&
                        os2header.bitsperpixel != 24)
                        return B_NO_TRANSLATOR;

                if (outInfo) {
                        outInfo->type = B_BMP_FORMAT;
                        outInfo->group = B_TRANSLATOR_BITMAP;
                        outInfo->quality = BMP_IN_QUALITY;
                        outInfo->capability = BMP_IN_CAPABILITY;
                        sprintf(outInfo->name, B_TRANSLATE("BMP image (OS/2 format, "
                                "%d bits)"), os2header.bitsperpixel);
                        strcpy(outInfo->MIME, "image/x-bmp");
                }
                if (pfileheader && pmsheader) {
                        pfileheader->magic = 'MB';
                        pfileheader->fileSize = 0;
                        pfileheader->reserved = 0;
                        pfileheader->dataOffset = 0;

                        pmsheader->size = 40;
                        pmsheader->width = os2header.width;
                        pmsheader->height = os2header.height;
                        pmsheader->planes = 1;
                        pmsheader->bitsperpixel = os2header.bitsperpixel;
                        pmsheader->compression = BMP_NO_COMPRESS;
                        pmsheader->imagesize = 0;
                        pmsheader->xpixperm = 2835; // 72 dpi horizontal
                        pmsheader->ypixperm = 2835; // 72 dpi vertical
                        pmsheader->colorsused = 0;
                        pmsheader->colorsimportant = 0;

                        // determine fileSize / imagesize
                        switch (pmsheader->bitsperpixel) {
                                case 24:
                                        if (pos2skip && fileHeader.dataOffset > 26)
                                                (*pos2skip) = fileHeader.dataOffset - 26;

                                        pfileheader->dataOffset = 54;
                                        pmsheader->imagesize = get_rowbytes(pmsheader->width,
                                                pmsheader->bitsperpixel) * abs(pmsheader->height);
                                        pfileheader->fileSize = pfileheader->dataOffset +
                                                pmsheader->imagesize;

                                        break;

                                case 8:
                                case 4:
                                case 1:
                                {
                                        uint16 ncolors = 1 << pmsheader->bitsperpixel;
                                        pmsheader->colorsused = ncolors;
                                        pmsheader->colorsimportant = ncolors;
                                        if (pos2skip && fileHeader.dataOffset >
                                                static_cast<uint32> (26 + (ncolors * 3)))
                                                        (*pos2skip) = fileHeader.dataOffset -
                                                                (26 + (ncolors * 3));

                                        pfileheader->dataOffset = 54 + (ncolors * 4);
                                        pmsheader->imagesize = get_rowbytes(pmsheader->width,
                                                pmsheader->bitsperpixel) * abs(pmsheader->height);
                                        pfileheader->fileSize = pfileheader->dataOffset +
                                                pmsheader->imagesize;

                                        break;
                                }

                                default:
                                        break;
                        }
                }
                if (pfrommsformat)
                        (*pfrommsformat) = false;

                return B_OK;

        } else
                return B_NO_TRANSLATOR;
}

// ---------------------------------------------------------------
// DerivedIdentify
//
// Examines the data from inSource and determines if it is in a
// format that this translator knows how to work with.
//
// Preconditions:
//
// Parameters:  inSource,       where the data to examine is
//
//                              inFormat,       a hint about the data in inSource,
//                                                      it is ignored since it is only a hint
//
//                              ioExtension,    configuration settings for the
//                                                              translator
//
//                              outInfo,        information about what data is in
//                                                      inSource and how well this translator
//                                                      can handle that data is stored here
//
//                              outType,        The format that the user wants
//                                                      the data in inSource to be
//                                                      converted to
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,    if this translator can't handle
//                                                              the data in inSource
//
// B_ERROR,     if there was an error converting the data to the host
//                      format
//
// B_BAD_VALUE, if the settings in ioExtension are bad
//
// B_OK,        if this translator understand the data and there were
//                      no errors found
// ---------------------------------------------------------------
status_t
BMPTranslator::DerivedIdentify(BPositionIO *inSource,
        const translation_format *inFormat, BMessage *ioExtension,
        translator_info *outInfo, uint32 outType)
{
        return identify_bmp_header(inSource, outInfo);
}

// ---------------------------------------------------------------
// translate_from_bits_to_bmp24
//
// Converts various varieties of the Be Bitmap format ('bits') to
// the MS BMP 24-bit format.
//
// Preconditions:
//
// Parameters:  inSource,       contains the bits data to convert
//
//                              outDestination, where the BMP data will be written
//
//                              fromspace,      the format of the data in inSource
//
//                              msheader,       contains information about the BMP
//                                                      dimensions and filesize
//
// Postconditions:
//
// Returns: B_ERROR,    if memory couldn't be allocated or another
//                                              error occured
//
// B_OK,        if no errors occurred
// ---------------------------------------------------------------
status_t
translate_from_bits_to_bmp24(BPositionIO *inSource,
BPositionIO *outDestination, color_space fromspace, MSInfoHeader &msheader)
{
        // TODO: WHOHA! big switch statement for the innermost loop!
        // make a loop per colorspace and put the switch outside!!!
        // remove memcpy() to copy 3 bytes
        int32 bitsBytesPerPixel = 0;
        switch (fromspace) {
                case B_RGB32:
                case B_RGB32_BIG:
                case B_RGBA32:
                case B_RGBA32_BIG:
                case B_CMY32:
                case B_CMYA32:
                case B_CMYK32:
                        bitsBytesPerPixel = 4;
                        break;

                case B_RGB24:
                case B_RGB24_BIG:
                case B_CMY24:
                        bitsBytesPerPixel = 3;
                        break;

                case B_RGB16:
                case B_RGB16_BIG:
                case B_RGBA15:
                case B_RGBA15_BIG:
                case B_RGB15:
                case B_RGB15_BIG:
                        bitsBytesPerPixel = 2;
                        break;

                case B_CMAP8:
                case B_GRAY8:
                        bitsBytesPerPixel = 1;
                        break;

                default:
                        return B_ERROR;
        }
        int32 bitsRowBytes = msheader.width * bitsBytesPerPixel;
        int32 padding = get_padding(msheader.width, msheader.bitsperpixel);
        int32 bmpRowBytes =
                get_rowbytes(msheader.width, msheader.bitsperpixel);
        int32 bmppixrow = 0;
        if (msheader.height > 0)
                inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);
        uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
        if (!bmpRowData)
                return B_NO_MEMORY;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData) {
                delete[] bmpRowData;
                return B_NO_MEMORY;
        }
        memset(bmpRowData + (bmpRowBytes - padding), 0, padding);
        ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
        const color_map *pmap = NULL;
        if (fromspace == B_CMAP8) {
                pmap = system_colors();
                if (!pmap) {
                        delete [] bmpRowData;
                        delete [] bitsRowData;
                        return B_ERROR;
                }
        }
        while (rd == static_cast<ssize_t>(bitsRowBytes)) {
                for (int32 i = 0; i < msheader.width; i++) {
                        uint8 *bitspixel, *bmppixel;
                        uint16 val;
                        switch (fromspace) {
                                case B_RGB32:
                                case B_RGBA32:
                                case B_RGB24:
                                        memcpy(bmpRowData + (i * 3),
                                                bitsRowData + (i * bitsBytesPerPixel), 3);
                                        break;

                                case B_RGB16:
                                case B_RGB16_BIG:
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        if (fromspace == B_RGB16)
                                                val = bitspixel[0] + (bitspixel[1] << 8);
                                        else
                                                val = bitspixel[1] + (bitspixel[0] << 8);
                                        bmppixel[0] =
                                                ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
                                        bmppixel[1] =
                                                ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
                                        bmppixel[2] =
                                                ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
                                        break;

                                case B_RGB15:
                                case B_RGB15_BIG:
                                case B_RGBA15:
                                case B_RGBA15_BIG:
                                        // NOTE: the alpha data for B_RGBA15* is not used
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        if (fromspace == B_RGB15 || fromspace == B_RGBA15)
                                                val = bitspixel[0] + (bitspixel[1] << 8);
                                        else
                                                val = bitspixel[1] + (bitspixel[0] << 8);
                                        bmppixel[0] =
                                                ((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
                                        bmppixel[1] =
                                                ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
                                        bmppixel[2] =
                                                ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
                                        break;

                                case B_RGB32_BIG:
                                case B_RGBA32_BIG:
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        bmppixel[0] = bitspixel[3];
                                        bmppixel[1] = bitspixel[2];
                                        bmppixel[2] = bitspixel[1];
                                        break;

                                case B_RGB24_BIG:
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        bmppixel[0] = bitspixel[2];
                                        bmppixel[1] = bitspixel[1];
                                        bmppixel[2] = bitspixel[0];
                                        break;

                                case B_CMAP8:
                                {
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        rgb_color c = pmap->color_list[bitspixel[0]];
                                        bmppixel[0] = c.blue;
                                        bmppixel[1] = c.green;
                                        bmppixel[2] = c.red;
                                        break;
                                }

                                case B_GRAY8:
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        bmppixel[0] = bitspixel[0];
                                        bmppixel[1] = bitspixel[0];
                                        bmppixel[2] = bitspixel[0];
                                        break;

                                case B_CMYK32:
                                {
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);

                                        int32 comp = 255 - bitspixel[2] - bitspixel[3];
                                        bmppixel[0] = (comp < 0) ? 0 : comp;

                                        comp = 255 - bitspixel[1] - bitspixel[3];
                                        bmppixel[1] = (comp < 0) ? 0 : comp;

                                        comp = 255 - bitspixel[0] - bitspixel[3];
                                        bmppixel[2] = (comp < 0) ? 0 : comp;
                                        break;
                                }

                                case B_CMY32:
                                case B_CMYA32:
                                case B_CMY24:
                                        bitspixel = bitsRowData + (i * bitsBytesPerPixel);
                                        bmppixel = bmpRowData + (i * 3);
                                        bmppixel[0] = 255 - bitspixel[2];
                                        bmppixel[1] = 255 - bitspixel[1];
                                        bmppixel[2] = 255 - bitspixel[0];
                                        break;

                                default:
                                        break;
                        } // switch (fromspace)
                } // for for (uint32 i = 0; i < msheader.width; i++)

                outDestination->Write(bmpRowData, bmpRowBytes);
                bmppixrow++;
                // if I've read all of the pixel data, break
                // out of the loop so I don't try to read
                // non-pixel data
                if (bmppixrow == abs(msheader.height))
                        break;

                if (msheader.height > 0)
                        inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
                rd = inSource->Read(bitsRowData, bitsRowBytes);
        } // while (rd == bitsRowBytes)

        delete[] bmpRowData;
        delete[] bitsRowData;

        return B_OK;
}

// ---------------------------------------------------------------
// translate_from_bits8_to_bmp8
//
// Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format
//
// Preconditions:
//
// Parameters:  inSource,       contains the bits data to convert
//
//                              outDestination, where the BMP data will be written
//
//                              bitsRowBytes,   number of bytes in one row of
//                                                              bits data
//
//                              msheader,       contains information about the BMP
//                                                      dimensions and filesize
//
// Postconditions:
//
// Returns: B_ERROR,    if memory couldn't be allocated or another
//                                              error occured
//
// B_OK,        if no errors occurred
// ---------------------------------------------------------------
status_t
translate_from_bits8_to_bmp8(BPositionIO *inSource,
        BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
{
        int32 padding = get_padding(msheader.width, msheader.bitsperpixel);
        int32 bmpRowBytes =
                get_rowbytes(msheader.width, msheader.bitsperpixel);
        int32 bmppixrow = 0;
        if (msheader.height > 0)
                inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);
        uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
        if (!bmpRowData)
                return B_NO_MEMORY;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData) {
                delete[] bmpRowData;
                return B_NO_MEMORY;
        }
        memset(bmpRowData + (bmpRowBytes - padding), 0, padding);
        ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
        while (rd == bitsRowBytes) {
                memcpy(bmpRowData, bitsRowData, msheader.width);
                outDestination->Write(bmpRowData, bmpRowBytes);
                bmppixrow++;
                // if I've read all of the pixel data, break
                // out of the loop so I don't try to read
                // non-pixel data
                if (bmppixrow == abs(msheader.height))
                        break;

                if (msheader.height > 0)
                        inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
                rd = inSource->Read(bitsRowData, bitsRowBytes);
        } // while (rd == bitsRowBytes)

        delete[] bmpRowData;
        delete[] bitsRowData;

        return B_OK;
}

// ---------------------------------------------------------------
// translate_from_bits1_to_bmp1
//
// Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format
//
// Preconditions:
//
// Parameters:  inSource,       contains the bits data to convert
//
//                              outDestination, where the BMP data will be written
//
//                              bitsRowBytes,   number of bytes in one row of
//                                                              bits data
//
//                              msheader,       contains information about the BMP
//                                                      dimensions and filesize
//
// Postconditions:
//
// Returns: B_ERROR,    if memory couldn't be allocated or another
//                                              error occured
//
// B_OK,        if no errors occurred
// ---------------------------------------------------------------
status_t
translate_from_bits1_to_bmp1(BPositionIO *inSource,
        BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader)
{
        uint8 pixelsPerByte = 8 / msheader.bitsperpixel;
        int32 bmpRowBytes =
                get_rowbytes(msheader.width, msheader.bitsperpixel);
        int32 bmppixrow = 0;
        if (msheader.height > 0)
                inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);
        uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
        if (!bmpRowData)
                return B_NO_MEMORY;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData) {
                delete[] bmpRowData;
                return B_NO_MEMORY;
        }
        ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
        while (rd == bitsRowBytes) {
                int32 bmppixcol = 0;
                memset(bmpRowData, 0, bmpRowBytes);
                for (int32 i = 0; (bmppixcol < msheader.width) &&
                        (i < bitsRowBytes); i++) {
                        // process each byte in the row
                        uint8 pixels = bitsRowData[i];
                        for (uint8 compbit = 128; (bmppixcol < msheader.width) &&
                                compbit; compbit >>= 1) {
                                // for each bit in the current byte, convert to a BMP palette
                                // index and store that in the bmpRowData
                                uint8 index;
                                if (pixels & compbit)
                                        // 1 == black
                                        index = 1;
                                else
                                        // 0 == white
                                        index = 0;
                                bmpRowData[bmppixcol / pixelsPerByte] |=
                                        index << (7 - (bmppixcol % pixelsPerByte));
                                bmppixcol++;
                        }
                }

                outDestination->Write(bmpRowData, bmpRowBytes);
                bmppixrow++;
                // if I've read all of the pixel data, break
                // out of the loop so I don't try to read
                // non-pixel data
                if (bmppixrow == abs(msheader.height))
                        break;

                if (msheader.height > 0)
                        inSource->Seek(bitsRowBytes * -2, SEEK_CUR);
                rd = inSource->Read(bitsRowData, bitsRowBytes);
        } // while (rd == bitsRowBytes)

        delete[] bmpRowData;
        delete[] bitsRowData;

        return B_OK;
}

// ---------------------------------------------------------------
// write_bmp_headers
//
// Writes the MS BMP headers (fileHeader and msheader)
// to outDestination.
//
// Preconditions:
//
// Parameters:  outDestination, where the headers are written to
//
//                              fileHeader, BMP file header data
//
//                              msheader, BMP info header data
//
// Postconditions:
//
// Returns: B_ERROR, if something went wrong
//
// B_OK, if there were no problems writing out the headers
// ---------------------------------------------------------------
status_t
write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader,
        MSInfoHeader &msheader)
{
        uint8 bmpheaders[54];
        memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16));
        memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32));
        memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32));
        memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32));
        memcpy(bmpheaders + 14, &msheader, sizeof(msheader));
        if (swap_data(B_UINT16_TYPE, bmpheaders, 2,
                B_SWAP_HOST_TO_LENDIAN) != B_OK)
                return B_ERROR;
        if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12,
                B_SWAP_HOST_TO_LENDIAN) != B_OK)
                return B_ERROR;
        if (swap_data(B_UINT32_TYPE, bmpheaders + 14,
                sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK)
                return B_ERROR;
        if (outDestination->Write(bmpheaders, 54) != 54)
                return B_ERROR;

        return B_OK;
}

// ---------------------------------------------------------------
// translate_from_bits
//
// Convert the data in inSource from the Be Bitmap format ('bits')
// to the format specified in outType (either bits or BMP).
//
// Preconditions:
//
// Parameters:  inSource,       the bits data to translate
//
//                              outType,        the type of data to convert to
//
//                              outDestination, where the output is written to
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,    if the data is not in a supported
//                                                              format
//
// B_ERROR, if there was an error allocating memory or some other
//                      error
//
// B_OK, if successfully translated the data from the bits format
// ---------------------------------------------------------------
status_t
BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
        BPositionIO *outDestination)
{
        bool bheaderonly, bdataonly;
        bheaderonly = bdataonly = false;

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

        // Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT
        if (outType == B_BMP_FORMAT) {
                // Set up BMP header
                BMPFileHeader fileHeader;
                fileHeader.magic = 'MB';
                fileHeader.reserved = 0;

                MSInfoHeader msheader;
                msheader.size = 40;
                msheader.width =
                        static_cast<uint32> (bitsHeader.bounds.Width() + 1);
                msheader.height =
                        static_cast<int32> (bitsHeader.bounds.Height() + 1);
                msheader.planes = 1;
                msheader.xpixperm = 2835; // 72 dpi horizontal
                msheader.ypixperm = 2835; // 72 dpi vertical
                msheader.colorsused = 0;
                msheader.colorsimportant = 0;

                // determine fileSize / imagesize
                switch (bitsHeader.colors) {
                        case B_RGB32:
                        case B_RGB32_BIG:
                        case B_RGBA32:
                        case B_RGBA32_BIG:
                        case B_RGB24:
                        case B_RGB24_BIG:
                        case B_RGB16:
                        case B_RGB16_BIG:
                        case B_RGB15:
                        case B_RGB15_BIG:
                        case B_RGBA15:
                        case B_RGBA15_BIG:
                        case B_CMYK32:
                        case B_CMY32:
                        case B_CMYA32:
                        case B_CMY24:

                                fileHeader.dataOffset = 54;
                                msheader.bitsperpixel = 24;
                                msheader.compression = BMP_NO_COMPRESS;
                                msheader.imagesize = get_rowbytes(msheader.width, 24) *
                                        msheader.height;
                                fileHeader.fileSize = fileHeader.dataOffset +
                                        msheader.imagesize;

                                break;

                        case B_CMAP8:
                        case B_GRAY8:

                                msheader.colorsused = 256;
                                msheader.colorsimportant = 256;
                                fileHeader.dataOffset = 54 + (4 * 256);
                                msheader.bitsperpixel = 8;
                                msheader.compression = BMP_NO_COMPRESS;
                                msheader.imagesize = get_rowbytes(msheader.width,
                                        msheader.bitsperpixel) * msheader.height;
                                fileHeader.fileSize = fileHeader.dataOffset +
                                        msheader.imagesize;

                                break;

                        case B_GRAY1:

                                msheader.colorsused = 2;
                                msheader.colorsimportant = 2;
                                fileHeader.dataOffset = 62;
                                msheader.bitsperpixel = 1;
                                msheader.compression = BMP_NO_COMPRESS;
                                msheader.imagesize = get_rowbytes(msheader.width,
                                        msheader.bitsperpixel) * msheader.height;
                                fileHeader.fileSize = fileHeader.dataOffset +
                                        msheader.imagesize;

                                break;

                        default:
                                return B_NO_TRANSLATOR;
                }

                // write out the BMP headers
                if (bheaderonly || (!bheaderonly && !bdataonly)) {
                        result = write_bmp_headers(outDestination, fileHeader, msheader);
                        if (result != B_OK)
                                return result;
                }
                if (bheaderonly)
                        // if user only wants the header, bail out
                        // before the data is written
                        return result;

                // write out the BMP pixel data
                switch (bitsHeader.colors) {
                        case B_RGB32:
                        case B_RGB32_BIG:
                        case B_RGBA32:
                        case B_RGBA32_BIG:
                        case B_RGB24:
                        case B_RGB24_BIG:
                        case B_RGB16:
                        case B_RGB16_BIG:
                        case B_RGB15:
                        case B_RGB15_BIG:
                        case B_RGBA15:
                        case B_RGBA15_BIG:
                        case B_CMYK32:
                        case B_CMY32:
                        case B_CMYA32:
                        case B_CMY24:
                                return translate_from_bits_to_bmp24(inSource, outDestination,
                                        bitsHeader.colors, msheader);

                        case B_CMAP8:
                        case B_GRAY8:
                        {
                                // write palette to BMP file
                                uint8 pal[1024];
                                uint8* palHandle = pal;
                                if (bitsHeader.colors == B_CMAP8) {
                                        // write system palette
                                        const color_map *pmap = system_colors();
                                        if (!pmap)
                                                return B_ERROR;
                                        for (int32 i = 0; i < 256; i++) {
                                                rgb_color c = pmap->color_list[i];
                                                palHandle[0] = c.blue;
                                                palHandle[1] = c.green;
                                                palHandle[2] = c.red;
                                                palHandle[3] = c.alpha;
                                                palHandle += 4;
                                        }
                                } else {
                                        // write gray palette
                                        for (int32 i = 0; i < 256; i++) {
                                                palHandle[0] = i;
                                                palHandle[1] = i;
                                                palHandle[2] = i;
                                                palHandle[3] = 255;
                                                palHandle += 4;
                                        }
                                }
                                ssize_t written = outDestination->Write(pal, 1024);
                                if (written < 0)
                                        return written;
                                if (written != 1024)
                                        return B_ERROR;

                                return translate_from_bits8_to_bmp8(inSource, outDestination,
                                        bitsHeader.rowBytes, msheader);
                        }

                        case B_GRAY1:
                        {
                                // write monochrome palette to the BMP file
                                const uint32 monopal[] = { 0x00ffffff, 0x00000000 };
                                ssize_t written = outDestination->Write(monopal, 8);
                                if (written < 0)
                                        return written;
                                if (written != 8)
                                        return B_ERROR;

                                return translate_from_bits1_to_bmp1(inSource, outDestination,
                                        bitsHeader.rowBytes, msheader);
                        }

                        default:
                                return B_NO_TRANSLATOR;
                }
        } else
                return B_NO_TRANSLATOR;
}

// ---------------------------------------------------------------
// translate_from_bmpnpal_to_bits
//
// Translates a non-palette BMP from inSource to the B_RGB32
// bits format.
//
// Preconditions:
//
// Parameters: inSource,        the BMP data to be translated
//
// outDestination,      where the bits data will be written to
//
// msheader,    header information about the BMP to be written
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
translate_from_bmpnpal_to_bits(BPositionIO *inSource,
        BPositionIO *outDestination, MSInfoHeader &msheader)
{
        int32 bitsRowBytes = msheader.width * 4;
        int32 bmpBytesPerPixel = msheader.bitsperpixel / 8;
        int32 bmpRowBytes =
                get_rowbytes(msheader.width, msheader.bitsperpixel);

        // Setup outDestination so that it can be written to
        // from the end of the file to the beginning instead of
        // the other way around
        off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) +
                sizeof(TranslatorBitmap);
        if (outDestination->SetSize(bitsFileSize) != B_OK) {
                // This call should work for BFile and BMallocIO objects,
                // but may not work for other BPositionIO based types
                ERROR("BMPTranslator::translate_from_bmpnpal_to_bits() - "
                        "failed to SetSize()\n");
                return B_ERROR;
        }
        if (msheader.height > 0)
                outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);

        // allocate row buffers
        uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
        if (!bmpRowData)
                return B_NO_MEMORY;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData) {
                delete[] bmpRowData;
                return B_NO_MEMORY;
        }

        // perform the actual translation
        if (bmpBytesPerPixel != 4) {
                // clean out buffer so that we don't have to write
                // alpha for each row
                memset(bitsRowData, 0xff, bitsRowBytes);
        }

        status_t ret = B_OK;

        uint32 rowCount = abs(msheader.height);
        for (uint32 y = 0; y < rowCount; y++) {
                ssize_t read = inSource->Read(bmpRowData, bmpRowBytes);
                if (read != bmpRowBytes) {
                        // break on read error
                        if (read >= 0)
                                ret = B_ERROR;
                        else
                                ret = read;
                        break;
                }

                if (bmpBytesPerPixel == 4) {
                        memcpy(bitsRowData, bmpRowData, bmpRowBytes);
                } else {
                        uint8 *pBitsPixel = bitsRowData;
                        uint8 *pBmpPixel = bmpRowData;
                        for (int32 i = 0; i < msheader.width; i++) {
                                pBitsPixel[0] = pBmpPixel[0];
                                pBitsPixel[1] = pBmpPixel[1];
                                pBitsPixel[2] = pBmpPixel[2];
                                pBitsPixel += 4;
                                pBmpPixel += bmpBytesPerPixel;
                        }
                }
                // write row and seek backward by two rows
                ssize_t written = outDestination->Write(bitsRowData, bitsRowBytes);
                if (msheader.height > 0)
                        outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);

                if (written != bitsRowBytes) {
                        // break on write error
                        if (written >= 0)
                                ret = B_ERROR;
                        else
                                ret = read;
                        break;
                }
        }

        delete[] bmpRowData;
        delete[] bitsRowData;

        return ret;
}

// ---------------------------------------------------------------
// translate_from_bmppal_to_bits
//
// Translates an uncompressed, palette BMP from inSource to
// the B_RGB32 bits format.
//
// Preconditions:
//
// Parameters: inSource,        the BMP data to be translated
//
// outDestination,      where the bits data will be written to
//
// msheader,    header information about the BMP to be written
//
// palette,     BMP palette for the BMP image
//
// frommsformat, true if BMP in inSource is in MS format,
//                               false if it is in OS/2 format
//
// Postconditions:
//
// Returns: B_NO_MEMORY, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
translate_from_bmppal_to_bits(BPositionIO *inSource,
        BPositionIO *outDestination, MSInfoHeader &msheader,
        const uint8 *palette, bool frommsformat)
{
        uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
        uint16 bitsPerPixel = msheader.bitsperpixel;
        uint8 palBytesPerPixel;
        if (frommsformat)
                palBytesPerPixel = 4;
        else
                palBytesPerPixel = 3;

        uint8 mask = 1;
        mask = (mask << bitsPerPixel) - 1;

        int32 bmpRowBytes =
                get_rowbytes(msheader.width, msheader.bitsperpixel);
        int32 bmppixrow = 0;

        // Setup outDestination so that it can be written to
        // from the end of the file to the beginning instead of
        // the other way around
        int32 bitsRowBytes = msheader.width * 4;
        off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) +
                sizeof(TranslatorBitmap);
        if (outDestination->SetSize(bitsFileSize) != B_OK)
                // This call should work for BFile and BMallocIO objects,
                // but may not work for other BPositionIO based types
                return B_ERROR;
        if (msheader.height > 0)
                outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);

        // allocate row buffers
        uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes];
        if (!bmpRowData)
                return B_NO_MEMORY;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData) {
                delete[] bmpRowData;
                return B_NO_MEMORY;
        }
        memset(bitsRowData, 0xff, bitsRowBytes);
        ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes);
        while (rd == static_cast<ssize_t>(bmpRowBytes)) {
                for (int32 i = 0; i < msheader.width; i++) {
                        uint8 indices = (bmpRowData + (i / pixelsPerByte))[0];
                        uint8 index;
                        index = (indices >>
                                (bitsPerPixel * ((pixelsPerByte - 1) -
                                        (i % pixelsPerByte)))) & mask;
                        memcpy(bitsRowData + (i * 4),
                                palette + (index * palBytesPerPixel), 3);
                }

                outDestination->Write(bitsRowData, bitsRowBytes);
                bmppixrow++;
                // if I've read all of the pixel data, break
                // out of the loop so I don't try to read
                // non-pixel data
                if (bmppixrow == abs(msheader.height))
                        break;

                if (msheader.height > 0)
                        outDestination->Seek(bitsRowBytes * -2, SEEK_CUR);
                rd = inSource->Read(bmpRowData, bmpRowBytes);
        }

        delete[] bmpRowData;
        delete[] bitsRowData;

        return B_OK;
}


// ---------------------------------------------------------------
// pixelcpy
//
// Copies count 32-bit pixels with a color value of pixel to dest.
//
// Preconditions:
//
// Parameters:  dest,   where the pixel data will be copied to
//
//                              pixel,  the 32-bit color value to copy to dest
//                                              count times
//
//                              count,  the number of times pixel is copied to
//                                              dest
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
void
pixelcpy(uint8 *dest, uint32 pixel, uint32 count)
{
        for (uint32 i = 0; i < count; i++) {
                memcpy(dest, &pixel, 3);
                dest += 4;
        }
}

// ---------------------------------------------------------------
// translate_from_bmppalr_to_bits
//
// Translates an RLE compressed, palette BMP from inSource to
// the B_RGB32 bits format. Currently, this code is not as
// memory effcient as it could be. It assumes that the BMP
// from inSource is relatively small.
//
// Preconditions:
//
// Parameters: inSource,        the BMP data to be translated
//
// outDestination,      where the bits data will be written to
//
// datasize, number of bytes of data needed for the bits output
//
// msheader,    header information about the BMP to be written
//
// palette,     BMP palette for data in inSource
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
translate_from_bmppalr_to_bits(BPositionIO *inSource,
        BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader,
        const uint8 *palette)
{
        uint16 pixelsPerByte = 8 / msheader.bitsperpixel;
        uint16 bitsPerPixel = msheader.bitsperpixel;
        uint8 mask = (1 << bitsPerPixel) - 1;

        uint8 count, indices, index;
        // Setup outDestination so that it can be written to
        // from the end of the file to the beginning instead of
        // the other way around
        int32 rowCount = abs(msheader.height);
        int32 bitsRowBytes = msheader.width * 4;
        off_t bitsFileSize = (bitsRowBytes * rowCount) +
                sizeof(TranslatorBitmap);
        if (outDestination->SetSize(bitsFileSize) != B_OK)
                // This call should work for BFile and BMallocIO objects,
                // but may not work for other BPositionIO based types
                return B_ERROR;
        uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes];
        if (!bitsRowData)
                return B_NO_MEMORY;
        memset(bitsRowData, 0xff, bitsRowBytes);
        int32 bmppixcol = 0, bmppixrow = 0;
        uint32 defaultcolor = *(uint32*)palette;
        off_t rowOffset = msheader.height > 0 ? bitsRowBytes * -2 : 0;
        // set bits output to last row in the image
        if (msheader.height > 0)
                outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR);
        ssize_t rd = inSource->Read(&count, 1);
        while (rd > 0) {
                // repeated color
                if (count) {
                        // abort if all of the pixels in the row
                        // have already been drawn to
                        if (bmppixcol == msheader.width) {
                                rd = -1;
                                break;
                        }
                        // if count is greater than the number of
                        // pixels remaining in the current row,
                        // only process the correct number of pixels
                        // remaining in the row
                        if (count + bmppixcol > msheader.width)
                                count = msheader.width - bmppixcol;

                        rd = inSource->Read(&indices, 1);
                        if (rd != 1) {
                                rd = -1;
                                break;
                        }
                        for (uint8 i = 0; i < count; i++) {
                                index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) -
                                        (i % pixelsPerByte)))) & mask;
                                memcpy(bitsRowData + (bmppixcol*4), palette + (index*4), 3);
                                bmppixcol++;
                        }
                // special code
                } else {
                        uint8 code;
                        rd = inSource->Read(&code, 1);
                        if (rd != 1) {
                                rd = -1;
                                break;
                        }
                        switch (code) {
                                // end of line
                                case 0:
                                        // if there are columns remaing on this
                                        // line, set them to the color at index zero
                                        if (bmppixcol < msheader.width)
                                                pixelcpy(bitsRowData + (bmppixcol * 4),
                                                        defaultcolor, msheader.width - bmppixcol);
                                        outDestination->Write(bitsRowData, bitsRowBytes);
                                        bmppixcol = 0;
                                        bmppixrow++;
                                        if (bmppixrow < rowCount)
                                                outDestination->Seek(rowOffset, SEEK_CUR);
                                        break;

                                // end of bitmap
                                case 1:
                                        // if at the end of a row
                                        if (bmppixcol == msheader.width) {
                                                outDestination->Write(bitsRowData, bitsRowBytes);
                                                bmppixcol = 0;
                                                bmppixrow++;
                                                if (bmppixrow < rowCount)
                                                        outDestination->Seek(rowOffset, SEEK_CUR);
                                        }

                                        while (bmppixrow < rowCount) {
                                                pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
                                                        msheader.width - bmppixcol);
                                                outDestination->Write(bitsRowData, bitsRowBytes);
                                                bmppixcol = 0;
                                                bmppixrow++;
                                                if (bmppixrow < rowCount)
                                                        outDestination->Seek(rowOffset, SEEK_CUR);
                                        }
                                        rd = 0;
                                                // break out of while loop
                                        break;

                                // delta, skip several rows and/or columns and
                                // fill the skipped pixels with the default color
                                case 2:
                                {
                                        uint8 da[2], lastcol, dx, dy;
                                        rd = inSource->Read(da, 2);
                                        if (rd != 2) {
                                                rd = -1;
                                                break;
                                        }
                                        dx = da[0];
                                        dy = da[1];

                                        // abort if dx or dy is too large
                                        if ((dx + bmppixcol >= msheader.width) ||
                                                (dy + bmppixrow >= rowCount)) {
                                                rd = -1;
                                                break;
                                        }

                                        lastcol = bmppixcol;

                                        // set all pixels to the first entry in
                                        // the palette, for the number of rows skipped
                                        while (dy > 0) {
                                                pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
                                                        msheader.width - bmppixcol);
                                                outDestination->Write(bitsRowData, bitsRowBytes);
                                                bmppixcol = 0;
                                                bmppixrow++;
                                                dy--;
                                                outDestination->Seek(rowOffset, SEEK_CUR);
                                        }

                                        if (bmppixcol < static_cast<int32>(lastcol + dx)) {
                                                pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor,
                                                        dx + lastcol - bmppixcol);
                                                bmppixcol = dx + lastcol;
                                        }

                                        break;
                                }

                                // code >= 3
                                // read code uncompressed indices
                                default:
                                        // abort if all of the pixels in the row
                                        // have already been drawn to
                                        if (bmppixcol == msheader.width) {
                                                rd = -1;
                                                break;
                                        }
                                        // if code is greater than the number of
                                        // pixels remaining in the current row,
                                        // only process the correct number of pixels
                                        // remaining in the row
                                        if (code + bmppixcol > msheader.width)
                                                code = msheader.width - bmppixcol;

                                        uint8 uncomp[256];
                                        int32 padding;
                                        if (!(code % pixelsPerByte))
                                                padding = (code / pixelsPerByte) % 2;
                                        else
                                                padding = ((code + pixelsPerByte -
                                                        (code % pixelsPerByte)) / pixelsPerByte) % 2;
                                        int32 uncompBytes = (code / pixelsPerByte) +
                                                ((code % pixelsPerByte) ? 1 : 0) + padding;
                                        rd = inSource->Read(uncomp, uncompBytes);
                                        if (rd != uncompBytes) {
                                                rd = -1;
                                                break;
                                        }
                                        for (uint8 i = 0; i < code; i++) {
                                                indices = (uncomp + (i / pixelsPerByte))[0];
                                                index = (indices >>
                                                        (bitsPerPixel * ((pixelsPerByte - 1) -
                                                                (i % pixelsPerByte)))) & mask;
                                                memcpy(bitsRowData + (bmppixcol * 4),
                                                        palette + (index * 4), 3);
                                                bmppixcol++;
                                        }

                                        break;
                        }
                }
                if (rd > 0)
                        rd = inSource->Read(&count, 1);
        }

        delete[] bitsRowData;

        if (!rd)
                return B_OK;
        else
                return B_NO_TRANSLATOR;
}

// ---------------------------------------------------------------
// translate_from_bmp
//
// Convert the data in inSource from the BMP format
// to the format specified in outType (either bits or BMP).
//
// Preconditions:
//
// Parameters:  inSource,       the bits data to translate
//
//                              outType,        the type of data to convert to
//
//                              outDestination, where the output is written to
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,    if the data is not in a supported
//                                                              format
//
// B_ERROR, if there was an error allocating memory or some other
//                      error
//
// B_OK, if successfully translated the data from the bits format
// ---------------------------------------------------------------
status_t
BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType,
        BPositionIO *outDestination)
{
        bool bheaderonly, bdataonly;
        bheaderonly = bdataonly = false;

        BMPFileHeader fileHeader;
        MSInfoHeader msheader;
        bool frommsformat;
        off_t os2skip = 0;

        status_t result;
        result = identify_bmp_header(inSource, NULL, &fileHeader, &msheader,
                &frommsformat, &os2skip);
        if (result != B_OK) {
                INFO("BMPTranslator::translate_from_bmp() - identify_bmp_header failed\n");
                return result;
        }

        // if the user wants to translate a BMP to a BMP, easy enough :)
        if (outType == B_BMP_FORMAT) {
                // write out the BMP headers
                if (bheaderonly || (!bheaderonly && !bdataonly)) {
                        result = write_bmp_headers(outDestination, fileHeader, msheader);
                        if (result != B_OK)
                                return result;
                }
                if (bheaderonly)
                        // if the user only wants the header,
                        // bail before it is written
                        return result;

                uint8 buf[1024];
                ssize_t rd;
                uint32 rdtotal = 54;
                if (!frommsformat && (msheader.bitsperpixel == 1 ||
                        msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) {
                        // if OS/2 paletted format, convert palette to MS format
                        uint16 ncolors = 1 << msheader.bitsperpixel;
                        rd = inSource->Read(buf, ncolors * 3);
                        if (rd != ncolors * 3)
                                return B_NO_TRANSLATOR;
                        uint8 mspalent[4] = {0, 0, 0, 0};
                        for (uint16 i = 0; i < ncolors; i++) {
                                memcpy(mspalent, buf + (i * 3), 3);
                                outDestination->Write(mspalent, 4);
                        }
                        rdtotal = fileHeader.dataOffset;
                }
                // if there is junk between the OS/2 headers and
                // the actual data, skip it
                if (!frommsformat && os2skip)
                        inSource->Seek(os2skip, SEEK_CUR);

                rd = min((uint32)1024, fileHeader.fileSize - rdtotal);
                rd = inSource->Read(buf, rd);
                while (rd > 0) {
                        outDestination->Write(buf, rd);
                        rdtotal += rd;
                        rd = min((uint32)1024, fileHeader.fileSize - rdtotal);
                        rd = inSource->Read(buf, rd);
                }
                if (rd == 0)
                        return B_OK;
                else
                        return B_ERROR;

        // if translating a BMP to a Be Bitmap
        } else if (outType == B_TRANSLATOR_BITMAP) {
                TranslatorBitmap bitsHeader;
                bitsHeader.magic = B_TRANSLATOR_BITMAP;
                bitsHeader.bounds.left = 0;
                bitsHeader.bounds.top = 0;
                bitsHeader.bounds.right = msheader.width - 1;
                bitsHeader.bounds.bottom = abs(msheader.height) - 1;

                // read in palette and/or skip non-BMP data
                uint8 bmppalette[1024];
                off_t nskip = 0;
                if (msheader.bitsperpixel == 1 ||
                        msheader.bitsperpixel == 4 ||
                        msheader.bitsperpixel == 8) {

                        uint8 palBytesPerPixel;
                        if (frommsformat)
                                palBytesPerPixel = 4;
                        else
                                palBytesPerPixel = 3;

                        if (!msheader.colorsused)
                                msheader.colorsused = 1 << msheader.bitsperpixel;

                        if (inSource->Read(bmppalette, msheader.colorsused *
                                palBytesPerPixel) !=
                                        (off_t) msheader.colorsused * palBytesPerPixel)
                                return B_NO_TRANSLATOR;

                        // skip over non-BMP data
                        if (frommsformat) {
                                if (fileHeader.dataOffset > (msheader.colorsused *
                                        palBytesPerPixel) + 54)
                                        nskip = fileHeader.dataOffset -
                                                ((msheader.colorsused * palBytesPerPixel) + 54);
                        } else
                                nskip = os2skip;
                } else if (fileHeader.dataOffset > 54)
                        // skip over non-BMP data
                        nskip = fileHeader.dataOffset - 54;

                if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0)
                        return B_NO_TRANSLATOR;

                bitsHeader.rowBytes = msheader.width * 4;
                bitsHeader.colors = B_RGB32;
                int32 datasize = bitsHeader.rowBytes * abs(msheader.height);
                bitsHeader.dataSize = datasize;

                // write out Be's Bitmap header
                if (bheaderonly || (!bheaderonly && !bdataonly)) {
                        if (swap_data(B_UINT32_TYPE, &bitsHeader,
                                sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK)
                                return B_ERROR;
                        outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap));
                }
                if (bheaderonly)
                        // if the user only wants the header,
                        // bail before the data is written
                        return B_OK;

                // write out the actual image data
                switch (msheader.bitsperpixel) {
                        case 32:
                        case 24:
                                return translate_from_bmpnpal_to_bits(inSource,
                                        outDestination, msheader);

                        case 8:
                                // 8 bit BMP with NO compression
                                if (msheader.compression == BMP_NO_COMPRESS)
                                        return translate_from_bmppal_to_bits(inSource,
                                                outDestination, msheader, bmppalette, frommsformat);

                                // 8 bit RLE compressed BMP
                                else if (msheader.compression == BMP_RLE8_COMPRESS)
                                        return translate_from_bmppalr_to_bits(inSource,
                                                outDestination, datasize, msheader, bmppalette);
                                else
                                        return B_NO_TRANSLATOR;

                        case 4:
                                // 4 bit BMP with NO compression
                                if (!msheader.compression)
                                        return translate_from_bmppal_to_bits(inSource,
                                                outDestination, msheader, bmppalette, frommsformat);

                                // 4 bit RLE compressed BMP
                                else if (msheader.compression == BMP_RLE4_COMPRESS)
                                        return translate_from_bmppalr_to_bits(inSource,
                                                outDestination, datasize, msheader, bmppalette);
                                else
                                        return B_NO_TRANSLATOR;

                        case 1:
                                return translate_from_bmppal_to_bits(inSource,
                                        outDestination, msheader, bmppalette, frommsformat);

                        default:
                                return B_NO_TRANSLATOR;
                }

        } else
                return B_NO_TRANSLATOR;
}

// ---------------------------------------------------------------
// 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
BMPTranslator::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_bmp(inSource, outType, outDestination);
        else
                return B_NO_TRANSLATOR;
}

BView *
BMPTranslator::NewConfigView(TranslatorSettings *settings)
{
        return new BMPView(BRect(0, 0, 225, 175), 
                B_TRANSLATE("BMPTranslator Settings"), B_FOLLOW_ALL, B_WILL_DRAW, 
                settings);
}