/*****************************************************************************/
// TGATranslator
// Written by Michael Wilber, Haiku Translation Kit Team
//
// TGATranslator.cpp
//
// This BTranslator based object is for opening and writing TGA files.
//
//
// Copyright (c) 2002-2009, Haiku, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/
 
#include <string.h>
#include <stdio.h>
 
#include <Catalog.h>
 
#include "TGATranslator.h"
#include "TGAView.h"
#include "StreamBuffer.h"
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "TGATranslator"
 
// 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 (TGATranslator)"
	},
	{
		B_TGA_FORMAT,
		B_TRANSLATOR_BITMAP,
		TGA_IN_QUALITY,
		TGA_IN_CAPABILITY,
		"image/x-targa",
		"Targa 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 (TGATranslator)"
	},
	{
		B_TGA_FORMAT,
		B_TRANSLATOR_BITMAP,
		TGA_OUT_QUALITY,
		TGA_OUT_CAPABILITY,
		"image/x-targa",
		"Targa 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},
	{TGA_SETTING_RLE, TRAN_SETTING_BOOL, false},
		// RLE compression is off by default
	{TGA_SETTING_IGNORE_ALPHA, TRAN_SETTING_BOOL, false}
		// Don't ignore the alpha channel 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 TGATranslator object to be used by BTranslatorRoster
//
// Preconditions:
//
// Parameters: n,		The translator to return. Since
//						TGATranslator only publishes one
//						translator, it only returns a
//						TGATranslator 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 TGATranslator if n is zero
// ---------------------------------------------------------------
BTranslator *
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
	BTranslator *ptranslator = NULL;
	if (!n)
		ptranslator = new(std::nothrow) TGATranslator();
 
	return ptranslator;
}
 
// ---------------------------------------------------------------
// 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:
// ---------------------------------------------------------------
TGATranslator::TGATranslator()
	: BaseTranslator(B_TRANSLATE("TGA images"),
		B_TRANSLATE("TGA image translator"),
		TGA_TRANSLATOR_VERSION,
		sInputFormats, kNumInputFormats,
		sOutputFormats, kNumOutputFormats,
		"TGATranslator_Settings",
		sDefaultSettings, kNumDefaultSettings,
		B_TRANSLATOR_BITMAP, B_TGA_FORMAT)
{
}
 
// ---------------------------------------------------------------
// Destructor
//
// Does nothing
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
//
// NOTE: It may be the case, that under Be's libtranslation.so,
// that this destructor will never be called
TGATranslator::~TGATranslator()
{
}
 
uint8
TGATranslator::tga_alphabits(TGAFileHeader &filehead, TGAColorMapSpec &mapspec,
	TGAImageSpec &imagespec)
{
	if (fSettings->SetGetBool(TGA_SETTING_IGNORE_ALPHA))
		return 0;
	else {
		uint8 nalpha;
		if (filehead.imagetype == TGA_NOCOMP_COLORMAP ||
			filehead.imagetype == TGA_RLE_COLORMAP) {
			// color mapped images
 
			if (mapspec.entrysize == 32)
				nalpha = 8;
			else if (mapspec.entrysize == 16)
				nalpha = 1;
			else
				nalpha = 0;
 
		} else {
			// non-color mapped images
 
			if (imagespec.depth == 32)
				// Some programs that generate 32-bit TGA files
				// have an alpha channel, but have an incorrect
				// descriptor which says there are no alpha bits.
				// This logic is so that the alpha data can be
				// obtained from TGA files that lie.
				nalpha = 8;
			else
				nalpha = imagespec.descriptor & TGA_DESC_ALPHABITS;
		}
 
		return nalpha;
	}
}
 
// ---------------------------------------------------------------
// identify_tga_header
//
// Determines if the data in inSource is in the TGA format.
// If it is, it returns info about the data in inSource
// to outInfo, pfileheader, pmapspec and pimagespec.
//
// Preconditions:
//
// Parameters:	inSource,	The source of the image data
//
//				outInfo,	Information about the translator
//							is copied here
//
//				pfileheader,	File header info for the TGA is
//								copied here after it is read from
//								the file.
//
//				pmapspec,	color map info for the TGA is copied
//							here after it is read from the file
//
//				pimagespec,	Info about the image width/height etc.
//							is copied here after it is read from
//							the file
//
//
// Postconditions:
//
// Returns: B_NO_TRANSLATOR,	if the data does not look like
//								TGA 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_tga_header(BPositionIO *inSource, translator_info *outInfo,
	TGAFileHeader *pfileheader = NULL, TGAColorMapSpec *pmapspec = NULL,
	TGAImageSpec *pimagespec = NULL)
{
	uint8 buf[TGA_HEADERS_SIZE];
 
	// read in the rest of the TGA headers
	ssize_t size = TGA_HEADERS_SIZE;
	if (size > 0 && inSource->Read(buf, size) != size)
		return B_NO_TRANSLATOR;
 
	// Read in TGA file header
	TGAFileHeader fileheader;
	fileheader.idlength = buf[0];
 
	fileheader.colormaptype = buf[1];
	if (fileheader.colormaptype > 1)
		return B_NO_TRANSLATOR;
 
	fileheader.imagetype = buf[2];
	if ((fileheader.imagetype > 3 && fileheader.imagetype < 9) ||
		fileheader.imagetype > 11)
		return B_NO_TRANSLATOR;
	if ((fileheader.colormaptype == TGA_NO_COLORMAP &&
		fileheader.imagetype == TGA_NOCOMP_COLORMAP) ||
		(fileheader.colormaptype == TGA_COLORMAP &&
			fileheader.imagetype != TGA_NOCOMP_COLORMAP &&
			fileheader.imagetype != TGA_RLE_COLORMAP))
		return B_NO_TRANSLATOR;
 
	// Read in TGA color map spec
	TGAColorMapSpec mapspec;
	memcpy(&mapspec.firstentry, buf + 3, 2);
	mapspec.firstentry = B_LENDIAN_TO_HOST_INT16(mapspec.firstentry);
	if (fileheader.colormaptype == 0 && mapspec.firstentry != 0)
		return B_NO_TRANSLATOR;
 
	memcpy(&mapspec.length, buf + 5, 2);
	mapspec.length = B_LENDIAN_TO_HOST_INT16(mapspec.length);
	if (fileheader.colormaptype == TGA_NO_COLORMAP &&
		mapspec.length != 0)
		return B_NO_TRANSLATOR;
	if (fileheader.colormaptype == TGA_COLORMAP &&
		mapspec.length == 0)
		return B_NO_TRANSLATOR;
 
	mapspec.entrysize = buf[7];
	if (fileheader.colormaptype == TGA_NO_COLORMAP &&
		mapspec.entrysize != 0)
		return B_NO_TRANSLATOR;
	if (fileheader.colormaptype == TGA_COLORMAP &&
		mapspec.entrysize != 15 && mapspec.entrysize != 16 &&
		mapspec.entrysize != 24 && mapspec.entrysize != 32)
		return B_NO_TRANSLATOR;
 
	// Read in TGA image spec
	TGAImageSpec imagespec;
	memcpy(&imagespec.xorigin, buf + 8, 2);
	imagespec.xorigin = B_LENDIAN_TO_HOST_INT16(imagespec.xorigin);
 
	memcpy(&imagespec.yorigin, buf + 10, 2);
	imagespec.yorigin = B_LENDIAN_TO_HOST_INT16(imagespec.yorigin);
 
	memcpy(&imagespec.width, buf + 12, 2);
	imagespec.width = B_LENDIAN_TO_HOST_INT16(imagespec.width);
	if (imagespec.width == 0)
		return B_NO_TRANSLATOR;
 
	memcpy(&imagespec.height, buf + 14, 2);
	imagespec.height = B_LENDIAN_TO_HOST_INT16(imagespec.height);
	if (imagespec.height == 0)
		return B_NO_TRANSLATOR;
 
	imagespec.depth = buf[16];
	if (imagespec.depth < 1 || imagespec.depth > 32)
		return B_NO_TRANSLATOR;
	if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR ||
			fileheader.imagetype == TGA_RLE_TRUECOLOR) &&
		imagespec.depth != 15 && imagespec.depth != 16 &&
		imagespec.depth != 24 && imagespec.depth != 32)
		return B_NO_TRANSLATOR;
	if ((fileheader.imagetype == TGA_NOCOMP_BW ||
			fileheader.imagetype == TGA_RLE_BW) &&
		imagespec.depth != 8)
		return B_NO_TRANSLATOR;
	if (fileheader.colormaptype == TGA_COLORMAP &&
		imagespec.depth != 8)
		return B_NO_TRANSLATOR;
 
	imagespec.descriptor = buf[17];
	// images ordered from Right to Left (rather than Left to Right)
	// are not supported
	if ((imagespec.descriptor & TGA_ORIGIN_HORZ_BIT) != TGA_ORIGIN_LEFT)
		return B_NO_TRANSLATOR;
	// unused descriptor bits, these bits must be zero
	if (imagespec.descriptor & TGA_DESC_BITS76)
		return B_NO_TRANSLATOR;
	if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR ||
		fileheader.imagetype == TGA_RLE_TRUECOLOR) &&
		imagespec.depth == 32 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 8 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 0)
		return B_NO_TRANSLATOR;
	if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR ||
		fileheader.imagetype == TGA_RLE_TRUECOLOR) &&
		imagespec.depth == 24 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 0)
		return B_NO_TRANSLATOR;
	if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR ||
		fileheader.imagetype == TGA_RLE_TRUECOLOR) &&
		imagespec.depth == 16 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 1 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 0)
	if ((fileheader.imagetype == TGA_NOCOMP_TRUECOLOR ||
		fileheader.imagetype == TGA_RLE_TRUECOLOR) &&
		imagespec.depth == 15 &&
		(imagespec.descriptor & TGA_DESC_ALPHABITS) != 0)
		return B_NO_TRANSLATOR;
 
	// Fill in headers passed to this function
	if (pfileheader) {
		pfileheader->idlength = fileheader.idlength;
		pfileheader->colormaptype = fileheader.colormaptype;
		pfileheader->imagetype = fileheader.imagetype;
	}
	if (pmapspec) {
		pmapspec->firstentry = mapspec.firstentry;
		pmapspec->length = mapspec.length;
		pmapspec->entrysize = mapspec.entrysize;
	}
	if (pimagespec) {
		pimagespec->xorigin = imagespec.xorigin;
		pimagespec->yorigin = imagespec.yorigin;
		pimagespec->width = imagespec.width;
		pimagespec->height = imagespec.height;
		pimagespec->depth = imagespec.depth;
		pimagespec->descriptor = imagespec.descriptor;
	}
 
	if (outInfo) {
		outInfo->type = B_TGA_FORMAT;
		outInfo->group = B_TRANSLATOR_BITMAP;
		outInfo->quality = TGA_IN_QUALITY;
		outInfo->capability = TGA_IN_CAPABILITY;
		switch (fileheader.imagetype) {
			case TGA_NOCOMP_COLORMAP:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits colormap)"),
					imagespec.depth);
				break;
			case TGA_NOCOMP_TRUECOLOR:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits truecolor)"),
					imagespec.depth);
				break;
			case TGA_RLE_COLORMAP:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits RLE colormap)"),
					imagespec.depth);
				break;
			case TGA_RLE_TRUECOLOR:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits RLE truecolor)"),
					imagespec.depth);
				break;
			case TGA_RLE_BW:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits RLE gray)"),
					imagespec.depth);
				break;
			case TGA_NOCOMP_BW:
			default:
				snprintf(outInfo->name, sizeof(outInfo->name),
					B_TRANSLATE("Targa image (%d bits gray)"),
					imagespec.depth);
				break;
 
		}
		strcpy(outInfo->MIME, "image/x-targa");
	}
 
	return B_OK;
}
 
status_t
TGATranslator::DerivedIdentify(BPositionIO *inSource,
	const translation_format *inFormat, BMessage *ioExtension,
	translator_info *outInfo, uint32 outType)
{
	return identify_tga_header(inSource, outInfo);
}
 
// Convert width pixels from pbits to TGA format, storing the
// result in ptga
status_t
pix_bits_to_tga(uint8 *pbits, uint8 *ptga, color_space fromspace,
	uint16 width, const color_map *pmap, int32 bitsBytesPerPixel)
{
	status_t bytescopied = 0;
 
	switch (fromspace) {
		case B_RGBA32:
			bytescopied = width * 4;
			memcpy(ptga, pbits, bytescopied);
			break;
 
		case B_RGBA32_BIG:
			bytescopied = width * 4;
			while (width--) {
				ptga[0] = pbits[3];
				ptga[1] = pbits[2];
				ptga[2] = pbits[1];
				ptga[3] = pbits[0];
 
				ptga += 4;
				pbits += 4;
			}
			break;
 
		case B_CMYA32:
			bytescopied = width * 4;
			while (width--) {
				ptga[0] = 255 - pbits[2];
				ptga[1] = 255 - pbits[1];
				ptga[2] = 255 - pbits[0];
				ptga[3] = pbits[3];
 
				ptga += 4;
				pbits += 4;
			}
			break;
 
		case B_RGB32:
		case B_RGB24:
			bytescopied = width * 3;
			while (width--) {
				memcpy(ptga, pbits, 3);
 
				ptga += 3;
				pbits += bitsBytesPerPixel;
			}
			break;
 
		case B_CMYK32:
		{
			int32 comp;
			bytescopied = width * 3;
			while (width--) {
				comp = 255 - pbits[2] - pbits[3];
				ptga[0] = (comp < 0) ? 0 : comp;
 
				comp = 255 - pbits[1] - pbits[3];
				ptga[1] = (comp < 0) ? 0 : comp;
 
				comp = 255 - pbits[0] - pbits[3];
				ptga[2] = (comp < 0) ? 0 : comp;
 
				ptga += 3;
				pbits += 4;
			}
			break;
		}
 
		case B_CMY32:
		case B_CMY24:
			bytescopied = width * 3;
			while (width--) {
				ptga[0] = 255 - pbits[2];
				ptga[1] = 255 - pbits[1];
				ptga[2] = 255 - pbits[0];
 
				ptga += 3;
				pbits += bitsBytesPerPixel;
			}
			break;
 
		case B_RGB16:
		case B_RGB16_BIG:
		{
			// Expand to 24 bit because the TGA format handles
			// 16 bit images differently than the Be Image Format
			// which would cause a loss in quality
			uint16 val;
			bytescopied = width * 3;
			while (width--) {
				if (fromspace == B_RGB16)
					val = pbits[0] + (pbits[1] << 8);
				else
					val = pbits[1] + (pbits[0] << 8);
 
				ptga[0] =
					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
				ptga[1] =
					((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9);
				ptga[2] =
					((val & 0xf800) >> 8) | ((val & 0xf800) >> 13);
 
				ptga += 3;
				pbits += 2;
			}
			break;
		}
 
		case B_RGBA15:
			bytescopied = width * 2;
			memcpy(ptga, pbits, bytescopied);
			break;
 
		case B_RGBA15_BIG:
			bytescopied = width * 2;
			while (width--) {
				ptga[0] = pbits[1];
				ptga[1] = pbits[0];
 
				ptga += 2;
				pbits += 2;
			}
			break;
 
		case B_RGB15:
			bytescopied = width * 2;
			while (width--) {
				ptga[0] = pbits[0];
				ptga[1] = pbits[1] | 0x80;
					// alpha bit is always 1
 
				ptga += 2;
				pbits += 2;
			}
			break;
 
		case B_RGB15_BIG:
			bytescopied = width * 2;
			while (width--) {
				ptga[0] = pbits[1];
				ptga[1] = pbits[0] | 0x80;
					// alpha bit is always 1
 
				ptga += 2;
				pbits += 2;
			}
			break;
 
		case B_RGB32_BIG:
			bytescopied = width * 3;
			while (width--) {
				ptga[0] = pbits[3];
				ptga[1] = pbits[2];
				ptga[2] = pbits[1];
 
				ptga += 3;
				pbits += 4;
			}
			break;
 
		case B_RGB24_BIG:
			bytescopied = width * 3;
			while (width--) {
				ptga[0] = pbits[2];
				ptga[1] = pbits[1];
				ptga[2] = pbits[0];
 
				ptga += 3;
				pbits += 3;
			}
			break;
 
		case B_CMAP8:
		{
			rgb_color c;
			bytescopied = width * 3;
			while (width--) {
				c = pmap->color_list[pbits[0]];
				ptga[0] = c.blue;
				ptga[1] = c.green;
				ptga[2] = c.red;
 
				ptga += 3;
				pbits++;
			}
			break;
		}
 
		case B_GRAY8:
			// NOTE: this code assumes that the
			// destination TGA color space is either
			// 8 bit indexed color or 8 bit grayscale
			bytescopied = width;
			memcpy(ptga, pbits, bytescopied);
			break;
 
		default:
			bytescopied = B_ERROR;
			break;
	} // switch (fromspace)
 
	return bytescopied;
}
 
// create a TGA RLE packet for pixel and copy the
// packet header and pixel data to ptga
status_t
copy_rle_packet(uint8 *ptga, uint32 pixel, uint8 count,
	color_space fromspace, const color_map *pmap,
	int32 bitsBytesPerPixel)
{
	// copy packet header
	// (made of type and count)
	uint8 packethead = (count - 1) | 0x80;
	ptga[0] = packethead;
	ptga++;
 
	return pix_bits_to_tga(reinterpret_cast<uint8 *> (&pixel),
		ptga, fromspace, 1, pmap, bitsBytesPerPixel) + 1;
}
 
// create a TGA raw packet for pixel and copy the
// packet header and pixel data to ptga
status_t
copy_raw_packet(uint8 *ptga, uint8 *praw, uint8 count,
	color_space fromspace, const color_map *pmap,
	int32 bitsBytesPerPixel)
{
	// copy packet header
	// (made of type and count)
	uint8 packethead = count - 1;
	ptga[0] = packethead;
	ptga++;
 
	return pix_bits_to_tga(praw, ptga, fromspace,
		count, pmap, bitsBytesPerPixel) + 1;
}
 
// convert a row of pixel data from pbits to a
// row of pixel data in the TGA format using
// Run Length Encoding
status_t
pix_bits_to_tgarle(uint8 *pbits, uint8 *ptga, color_space fromspace,
	uint16 width, const color_map *pmap, int32 bitsBytesPerPixel)
{
	if (width == 0)
		return B_ERROR;
 
	uint32 current = 0, next = 0, aftnext = 0;
	uint16 nread = 0;
	status_t result, bytescopied = 0;
	uint8 *prawbuf, *praw;
	prawbuf = new(std::nothrow) uint8[bitsBytesPerPixel * 128];
	praw = prawbuf;
	if (!prawbuf)
		return B_ERROR;
 
	uint8 rlecount = 1, rawcount = 0;
	bool bJustWroteRLE = false;
 
	memcpy(&current, pbits, bitsBytesPerPixel);
	pbits += bitsBytesPerPixel;
	if (width == 1) {
		result = copy_raw_packet(ptga,
			reinterpret_cast<uint8 *> (&current), 1,
			fromspace, pmap, bitsBytesPerPixel);
 
		ptga += result;
		bytescopied += result;
		nread++;
			// don't enter the while loop
 
	} else {
		memcpy(&next, pbits, bitsBytesPerPixel);
		pbits += bitsBytesPerPixel;
		nread++;
	}
 
	while (nread < width) {
 
		if (nread < width - 1) {
			memcpy(&aftnext, pbits, bitsBytesPerPixel);
			pbits += bitsBytesPerPixel;
		}
		nread++;
 
		// RLE Packet Creation
		if (current == next && !bJustWroteRLE) {
			rlecount++;
 
			if (next != aftnext || nread == width || rlecount == 128) {
				result = copy_rle_packet(ptga, current, rlecount,
					fromspace, pmap, bitsBytesPerPixel);
 
				ptga += result;
				bytescopied += result;
				rlecount = 1;
				bJustWroteRLE = true;
			}
 
		// RAW Packet Creation
		} else {
 
			if (!bJustWroteRLE) {
				// output the current pixel only if
				// it was not just written out in an RLE packet
				rawcount++;
				memcpy(praw, &current, bitsBytesPerPixel);
				praw += bitsBytesPerPixel;
			}
 
			if (nread == width) {
				// if in the last iteration of the loop,
				// "next" will be the last pixel in the row,
				// and will need to be written out for this
				// special case
 
				if (rawcount == 128) {
					result = copy_raw_packet(ptga, prawbuf, rawcount,
						fromspace, pmap, bitsBytesPerPixel);
 
					ptga += result;
					bytescopied += result;
					praw = prawbuf;
					rawcount = 0;
				}
 
				rawcount++;
				memcpy(praw, &next, bitsBytesPerPixel);
				praw += bitsBytesPerPixel;
			}
 
			if ((!bJustWroteRLE && next == aftnext) ||
				nread == width || rawcount == 128) {
				result = copy_raw_packet(ptga, prawbuf, rawcount,
					fromspace, pmap, bitsBytesPerPixel);
 
				ptga += result;
				bytescopied += result;
				praw = prawbuf;
				rawcount = 0;
			}
 
			bJustWroteRLE = false;
		}
 
		current = next;
		next = aftnext;
	}
 
	delete[] prawbuf;
	prawbuf = NULL;
 
	return bytescopied;
}
 
// ---------------------------------------------------------------
// translate_from_bits_to_tgatc
//
// Converts various varieties of the Be Bitmap format ('bits') to
// the TGA True Color format (RLE or uncompressed)
//
// Preconditions:
//
// Parameters:	inSource,	contains the bits data to convert
//
//				outDestination,	where the TGA data will be written
//
//				fromspace,	the format of the data in inSource
//
//				imagespec,	info about width / height / etc. of
//							the image
//
//				brle,	output using RLE if true, uncompressed
//						if false
//
//
// 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_tgatc(BPositionIO *inSource,
	BPositionIO *outDestination, color_space fromspace,
	TGAImageSpec &imagespec, bool brle)
{
	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 = imagespec.width * bitsBytesPerPixel;
	uint8 tgaBytesPerPixel = (imagespec.depth / 8) +
		((imagespec.depth % 8) ? 1 : 0);
	int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel) +
		(imagespec.width / 2);
	uint32 tgapixrow = 0;
	uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes];
	if (!tgaRowData)
		return B_ERROR;
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData) {
		delete[] tgaRowData;
		tgaRowData = NULL;
		return B_ERROR;
	}
 
	// conversion function pointer, points to either
	// RLE or normal TGA conversion function
	status_t (*convert_to_tga)(uint8 *pbits, uint8 *ptga,
		color_space fromspace, uint16 width, const color_map *pmap,
		int32 bitsBytesPerPixel);
 
	if (brle)
		convert_to_tga = pix_bits_to_tgarle;
	else
		convert_to_tga = pix_bits_to_tga;
 
	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
	const color_map *pmap = NULL;
	if (fromspace == B_CMAP8) {
		pmap = system_colors();
		if (!pmap) {
			delete[] tgaRowData;
			delete[] bitsRowData;
			return B_ERROR;
		}
	}
	while (rd == bitsRowBytes) {
		status_t bytescopied;
		bytescopied = convert_to_tga(bitsRowData, tgaRowData, fromspace,
			imagespec.width, pmap, bitsBytesPerPixel);
 
		outDestination->Write(tgaRowData, bytescopied);
		tgapixrow++;
		// 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 (tgapixrow == imagespec.height)
			break;
 
		rd = inSource->Read(bitsRowData, bitsRowBytes);
	} // while (rd == bitsRowBytes)
 
	delete[] bitsRowData;
	bitsRowData = NULL;
	delete[] tgaRowData;
	tgaRowData = NULL;
 
	return B_OK;
}
 
// ---------------------------------------------------------------
// translate_from_bits1_to_tgabw
//
// Converts 1-bit Be Bitmaps ('bits') to the
// black and white (8-bit grayscale) TGA format
//
// Preconditions:
//
// Parameters:	inSource,	contains the bits data to convert
//
//				outDestination,	where the TGA data will be written
//
//				bitsRowBytes,	number of bytes in one row of
//								bits data
//
//				imagespec,	info about width / height / etc. of
//							the image
//
//				brle,	output using RLE if true, uncompressed
//						if false
//
//
// 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_tgabw(BPositionIO *inSource,
	BPositionIO *outDestination, int32 bitsRowBytes,
	TGAImageSpec &imagespec, bool brle)
{
	uint8 tgaBytesPerPixel = 1;
	int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel) +
		(imagespec.width / 2);
	uint32 tgapixrow = 0;
	uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes];
	if (!tgaRowData)
		return B_ERROR;
 
	uint8 *medRowData = new(std::nothrow) uint8[imagespec.width];
	if (!medRowData) {
		delete[] tgaRowData;
		tgaRowData = NULL;
		return B_ERROR;
	}
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData) {
		delete[] medRowData;
		medRowData = NULL;
		delete[] tgaRowData;
		tgaRowData = NULL;
		return B_ERROR;
	}
 
	// conversion function pointer, points to either
	// RLE or normal TGA conversion function
	status_t (*convert_to_tga)(uint8 *pbits, uint8 *ptga,
		color_space fromspace, uint16 width, const color_map *pmap,
		int32 bitsBytesPerPixel);
 
	if (brle)
		convert_to_tga = pix_bits_to_tgarle;
	else
		convert_to_tga = pix_bits_to_tga;
 
	ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes);
	while (rd == bitsRowBytes) {
		uint32 tgapixcol = 0;
		for (int32 i = 0; (tgapixcol < imagespec.width) &&
			(i < bitsRowBytes); i++) {
			// process each byte in the row
			uint8 pixels = bitsRowData[i];
			for (uint8 compbit = 128; (tgapixcol < imagespec.width) &&
				compbit; compbit >>= 1) {
				// for each bit in the current byte, convert to a TGA palette
				// index and store that in the tgaRowData
				if (pixels & compbit)
					// black
					medRowData[tgapixcol] = 0;
				else
					// white
					medRowData[tgapixcol] = 255;
				tgapixcol++;
			}
		}
 
		status_t bytescopied;
		bytescopied = convert_to_tga(medRowData, tgaRowData, B_GRAY8,
			imagespec.width, NULL, 1);
 
		outDestination->Write(tgaRowData, bytescopied);
		tgapixrow++;
		// 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 (tgapixrow == imagespec.height)
			break;
 
		rd = inSource->Read(bitsRowData, bitsRowBytes);
	} // while (rd == bitsRowBytes)
 
	delete[] bitsRowData;
	bitsRowData = NULL;
	delete[] medRowData;
	medRowData = NULL;
	delete[] tgaRowData;
	tgaRowData = NULL;
 
	return B_OK;
}
 
// ---------------------------------------------------------------
// write_tga_headers
//
// Writes the TGA headers to outDestination.
//
// Preconditions:
//
// Parameters:	outDestination,	where the headers are written to
//
//				fileheader, TGA file header
//
//				mapspec,	color map information
//
//				imagespec,	width / height / etc. info
//
//
// Postconditions:
//
// Returns: B_ERROR, if something went wrong
//
// B_OK, if there were no problems writing out the headers
// ---------------------------------------------------------------
status_t
write_tga_headers(BPositionIO *outDestination, TGAFileHeader &fileheader,
	TGAColorMapSpec &mapspec, TGAImageSpec &imagespec)
{
	uint8 tgaheaders[TGA_HEADERS_SIZE];
 
	// Convert host format headers to Little Endian (Intel) byte order
	TGAFileHeader outFileheader;
	outFileheader.idlength = fileheader.idlength;
	outFileheader.colormaptype = fileheader.colormaptype;
	outFileheader.imagetype = fileheader.imagetype;
 
	TGAColorMapSpec outMapspec;
	outMapspec.firstentry = B_HOST_TO_LENDIAN_INT16(mapspec.firstentry);
	outMapspec.length = B_HOST_TO_LENDIAN_INT16(mapspec.length);
	outMapspec.entrysize = mapspec.entrysize;
 
	TGAImageSpec outImagespec;
	outImagespec.xorigin = B_HOST_TO_LENDIAN_INT16(imagespec.xorigin);
	outImagespec.yorigin = B_HOST_TO_LENDIAN_INT16(imagespec.yorigin);
	outImagespec.width = B_HOST_TO_LENDIAN_INT16(imagespec.width);
	outImagespec.height = B_HOST_TO_LENDIAN_INT16(imagespec.height);
	outImagespec.depth = imagespec.depth;
	outImagespec.descriptor = imagespec.descriptor;
 
	// Copy TGA headers to buffer to be written out
	// all at once
	tgaheaders[0] = outFileheader.idlength;
	tgaheaders[1] = outFileheader.colormaptype;
	tgaheaders[2] = outFileheader.imagetype;
 
	memcpy(tgaheaders + 3, &outMapspec.firstentry, 2);
	memcpy(tgaheaders + 5, &outMapspec.length, 2);
	tgaheaders[7] = outMapspec.entrysize;
 
	memcpy(tgaheaders + 8, &outImagespec.xorigin, 2);
	memcpy(tgaheaders + 10, &outImagespec.yorigin, 2);
	memcpy(tgaheaders + 12, &outImagespec.width, 2);
	memcpy(tgaheaders + 14, &outImagespec.height, 2);
	tgaheaders[16] = outImagespec.depth;
	tgaheaders[17] = outImagespec.descriptor;
 
	ssize_t written;
	written = outDestination->Write(tgaheaders, TGA_HEADERS_SIZE);
 
	if (written == TGA_HEADERS_SIZE)
		return B_OK;
	else
		return B_ERROR;
}
 
// ---------------------------------------------------------------
// write_tga_footer
//
// Writes the TGA footer.  This information is contant in this
// code because this translator does not output the developer
// information section of the TGA format.
//
// Preconditions:
//
// Parameters:	outDestination,	where the headers are written to
//
//
// Postconditions:
//
// Returns: B_ERROR, if something went wrong
//
// B_OK, if there were no problems writing out the headers
// ---------------------------------------------------------------
status_t
write_tga_footer(BPositionIO *outDestination)
{
	const int32 kfootersize = 26;
	uint8 footer[kfootersize];
 
	memset(footer, 0, 8);
		// set the Extension Area Offset and Developer
		// Area Offset to zero (as they are not present)
 
	memcpy(footer + 8, "TRUEVISION-XFILE.", 18);
		// copy the string including the '.' and the '\0'
 
	ssize_t written;
	written = outDestination->Write(footer, kfootersize);
	if (written == kfootersize)
		return B_OK;
	else
		return B_ERROR;
}
 
// ---------------------------------------------------------------
// translate_from_bits
//
// Convert the data in inSource from the Be Bitmap format ('bits')
// to the format specified in outType (either bits or TGA).
//
// Preconditions:
//
// Parameters:	inSource,	the bits data to translate
//
// 				amtread,	the amount of data already read from
//							inSource
//
//				read,		pointer to the data already read from
//							inSource
//
//
//				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
TGATranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
	BPositionIO *outDestination)
{
	TranslatorBitmap bitsHeader;
	bool bheaderonly = false, bdataonly = false, brle;
	brle = fSettings->SetGetBool(TGA_SETTING_RLE);
 
	status_t result;
	result = identify_bits_header(inSource, NULL, &bitsHeader);
	if (result != B_OK)
		return result;
 
	// Translate B_TRANSLATOR_BITMAP to B_TGA_FORMAT
	if (outType == B_TGA_FORMAT) {
		// Set up TGA header
		TGAFileHeader fileheader;
		fileheader.idlength = 0;
		fileheader.colormaptype = TGA_NO_COLORMAP;
		fileheader.imagetype = 0;
 
		TGAColorMapSpec mapspec;
		mapspec.firstentry = 0;
		mapspec.length = 0;
		mapspec.entrysize = 0;
 
		TGAImageSpec imagespec;
		imagespec.xorigin = 0;
		imagespec.yorigin = 0;
		imagespec.width = static_cast<uint16> (bitsHeader.bounds.Width() + 1);
		imagespec.height = static_cast<uint16> (bitsHeader.bounds.Height() + 1);
		imagespec.depth = 0;
		imagespec.descriptor = TGA_ORIGIN_VERT_BIT;
 
		// determine fileSize / imagesize
		switch (bitsHeader.colors) {
 
			// Output to 32-bit True Color TGA (8 bits alpha)
			case B_RGBA32:
			case B_RGBA32_BIG:
			case B_CMYA32:
				if (brle)
					fileheader.imagetype = TGA_RLE_TRUECOLOR;
				else
					fileheader.imagetype = TGA_NOCOMP_TRUECOLOR;
				imagespec.depth = 32;
				imagespec.descriptor |= 8;
					// 8 bits of alpha
				break;
 
			// Output to 24-bit True Color TGA (no alpha)
			case B_RGB32:
			case B_RGB32_BIG:
			case B_RGB24:
			case B_RGB24_BIG:
			case B_CMYK32:
			case B_CMY32:
			case B_CMY24:
				if (brle)
					fileheader.imagetype = TGA_RLE_TRUECOLOR;
				else
					fileheader.imagetype = TGA_NOCOMP_TRUECOLOR;
				imagespec.depth = 24;
				break;
 
			// Output to 16-bit True Color TGA (no alpha)
			// (TGA doesn't see 16 bit images as Be does
			// so converting 16 bit Be Image to 16-bit TGA
			// image would result in loss of quality)
			case B_RGB16:
			case B_RGB16_BIG:
				if (brle)
					fileheader.imagetype = TGA_RLE_TRUECOLOR;
				else
					fileheader.imagetype = TGA_NOCOMP_TRUECOLOR;
				imagespec.depth = 24;
				break;
 
			// Output to 15-bit True Color TGA (1 bit alpha)
			case B_RGB15:
			case B_RGB15_BIG:
				if (brle)
					fileheader.imagetype = TGA_RLE_TRUECOLOR;
				else
					fileheader.imagetype = TGA_NOCOMP_TRUECOLOR;
				imagespec.depth = 16;
				imagespec.descriptor |= 1;
					// 1 bit of alpha (always opaque)
				break;
 
			// Output to 16-bit True Color TGA (1 bit alpha)
			case B_RGBA15:
			case B_RGBA15_BIG:
				if (brle)
					fileheader.imagetype = TGA_RLE_TRUECOLOR;
				else
					fileheader.imagetype = TGA_NOCOMP_TRUECOLOR;
				imagespec.depth = 16;
				imagespec.descriptor |= 1;
					// 1 bit of alpha
				break;
 
			// Output to 8-bit Color Mapped TGA 32 bits per color map entry
			case B_CMAP8:
				fileheader.colormaptype = TGA_COLORMAP;
				if (brle)
					fileheader.imagetype = TGA_RLE_COLORMAP;
				else
					fileheader.imagetype = TGA_NOCOMP_COLORMAP;
				mapspec.firstentry = 0;
				mapspec.length = 256;
				mapspec.entrysize = 32;
				imagespec.depth = 8;
				imagespec.descriptor |= 8;
					// the pixel values contain 8 bits of attribute data
				break;
 
			// Output to 8-bit Black and White TGA
			case B_GRAY8:
			case B_GRAY1:
				if (brle)
					fileheader.imagetype = TGA_RLE_BW;
				else
					fileheader.imagetype = TGA_NOCOMP_BW;
				imagespec.depth = 8;
				break;
 
			default:
				return B_NO_TRANSLATOR;
		}
 
		// write out the TGA headers
		if (bheaderonly || (!bheaderonly && !bdataonly)) {
			result = write_tga_headers(outDestination, fileheader,
				mapspec, imagespec);
			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 TGA 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:
				result = translate_from_bits_to_tgatc(inSource, outDestination,
					bitsHeader.colors, imagespec, brle);
				break;
 
			case B_CMAP8:
			{
				// write Be's system palette to the TGA file
				uint8 pal[1024];
				const color_map *pmap = system_colors();
				if (!pmap)
					return B_ERROR;
				for (int32 i = 0; i < 256; i++) {
					uint8 *palent = pal + (i * 4);
					rgb_color c = pmap->color_list[i];
					palent[0] = c.blue;
					palent[1] = c.green;
					palent[2] = c.red;
					palent[3] = c.alpha;
				}
				if (outDestination->Write(pal, 1024) != 1024)
					return B_ERROR;
 
				result = translate_from_bits_to_tgatc(inSource, outDestination,
					B_GRAY8, imagespec, brle);
				break;
			}
 
			case B_GRAY8:
				result = translate_from_bits_to_tgatc(inSource, outDestination,
					B_GRAY8, imagespec, brle);
				break;
 
			case B_GRAY1:
				result = translate_from_bits1_to_tgabw(inSource, outDestination,
					bitsHeader.rowBytes, imagespec, brle);
				break;
 
			default:
				result = B_NO_TRANSLATOR;
				break;
		}
 
		if (result == B_OK)
			result = write_tga_footer(outDestination);
 
		return result;
 
	} else
		return B_NO_TRANSLATOR;
}
 
// convert a row of uncompressed, non-color mapped
// TGA pixels from ptga to pbits
status_t
pix_tganm_to_bits(uint8 *pbits, uint8 *ptga,
	uint16 width, uint8 depth, uint8 tgaBytesPerPixel,
	uint8 nalpha)
{
	status_t result = B_OK;
 
	switch (depth) {
		case 32:
			if (nalpha == 8 && tgaBytesPerPixel == 4)
				memcpy(pbits, ptga, 4 * width);
			else if (nalpha == 8) {
				// copy the same 32-bit pixel over and over
				while (width--) {
					memcpy(pbits, ptga, 4);
					pbits += 4;
				}
			} else {
				while (width--) {
					memcpy(pbits, ptga, 3);
 
					pbits += 4;
					ptga += tgaBytesPerPixel;
				}
			}
			break;
 
		case 24:
			while (width--) {
				memcpy(pbits, ptga, 3);
 
				pbits += 4;
				ptga += tgaBytesPerPixel;
			}
			break;
 
		case 16:
		{
			uint16 val;
			if (nalpha == 1) {
				while (width--) {
					val = ptga[0] + (ptga[1] << 8);
					pbits[0] =
						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
					pbits[1] =
						((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
					pbits[2] =
						((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
					pbits[3] = (val & 0x8000) ? 255 : 0;
 
					pbits += 4;
					ptga += tgaBytesPerPixel;
				}
			} else {
				while (width--) {
					val = ptga[0] + (ptga[1] << 8);
					pbits[0] =
						((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
					pbits[1] =
						((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
					pbits[2] =
						((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
 
					pbits += 4;
					ptga += tgaBytesPerPixel;
				}
			}
			break;
		}
 
		case 15:
		{
			uint16 val;
			while (width--) {
				val = ptga[0] + (ptga[1] << 8);
				pbits[0] =
					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
				pbits[1] =
					((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
				pbits[2] =
					((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
 
				pbits += 4;
				ptga += tgaBytesPerPixel;
			}
			break;
		}
 
		case 8:
			while (width--) {
				memset(pbits, ptga[0], 3);
 
				pbits += 4;
				ptga += tgaBytesPerPixel;
			}
			break;
 
		default:
			result = B_ERROR;
			break;
	}
 
	return result;
}
 
// ---------------------------------------------------------------
// translate_from_tganm_to_bits
//
// Translates a uncompressed, non-palette TGA from inSource
// to the B_RGB32 or B_RGBA32 bits format.
//
// Preconditions:
//
// Parameters: inSource,	the TGA data to be translated
//
// outDestination,	where the bits data will be written to
//
// filehead, image type info
//
// mapspec, color map info
//
// imagespec, width / height info
//
//
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
TGATranslator::translate_from_tganm_to_bits(BPositionIO *inSource,
	BPositionIO *outDestination, TGAFileHeader &filehead,
	TGAColorMapSpec &mapspec, TGAImageSpec &imagespec)
{
	bool bvflip;
	if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT)
		bvflip = false;
	else
		bvflip = true;
	uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec);
	int32 bitsRowBytes = imagespec.width * 4;
	uint8 tgaBytesPerPixel = (imagespec.depth / 8) +
		((imagespec.depth % 8) ? 1 : 0);
	int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel);
	uint32 tgapixrow = 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
	off_t bitsFileSize = (bitsRowBytes * imagespec.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;
	off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes;
	if (bvflip)
		outDestination->Seek(bitsoffset, SEEK_CUR);
 
	// allocate row buffers
	uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes];
	if (!tgaRowData)
		return B_ERROR;
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData) {
		delete[] tgaRowData;
		tgaRowData = NULL;
		return B_ERROR;
	}
 
	// perform the actual translation
	memset(bitsRowData, 0xff, bitsRowBytes);
	ssize_t rd = inSource->Read(tgaRowData, tgaRowBytes);
	while (rd == tgaRowBytes) {
		pix_tganm_to_bits(bitsRowData, tgaRowData,
			imagespec.width, imagespec.depth,
			tgaBytesPerPixel, nalpha);
 
		outDestination->Write(bitsRowData, bitsRowBytes);
		tgapixrow++;
		// 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 (tgapixrow == imagespec.height)
			break;
 
		if (bvflip)
			outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR);
		rd = inSource->Read(tgaRowData, tgaRowBytes);
	}
 
	delete[] tgaRowData;
	tgaRowData = NULL;
	delete[] bitsRowData;
	bitsRowData = NULL;
 
	return B_OK;
}
 
// ---------------------------------------------------------------
// translate_from_tganmrle_to_bits
//
// Convert non color map, RLE TGA to Be bitmap format
// and write results to outDestination
//
// Preconditions:
//
// Parameters: inSource,	the TGA data to be translated
//
// outDestination,	where the bits data will be written to
//
// filehead, image type info
//
// mapspec, color map info
//
// imagespec, width / height info
//
//
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
TGATranslator::translate_from_tganmrle_to_bits(BPositionIO *inSource,
	BPositionIO *outDestination, TGAFileHeader &filehead,
	TGAColorMapSpec &mapspec, TGAImageSpec &imagespec)
{
	status_t result = B_OK;
 
	bool bvflip;
	if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT)
		bvflip = false;
	else
		bvflip = true;
	uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec);
	int32 bitsRowBytes = imagespec.width * 4;
	uint8 tgaBytesPerPixel = (imagespec.depth / 8) +
		((imagespec.depth % 8) ? 1 : 0);
	uint16 tgapixrow = 0, tgapixcol = 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
	off_t bitsFileSize = (bitsRowBytes * imagespec.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;
	off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes;
	if (bvflip)
		outDestination->Seek(bitsoffset, SEEK_CUR);
 
	// allocate row buffers
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData)
		return B_ERROR;
 
	// perform the actual translation
	memset(bitsRowData, 0xff, bitsRowBytes);
	uint8 *pbitspixel = bitsRowData;
	uint8 packethead;
	StreamBuffer sbuf(inSource, TGA_STREAM_BUFFER_SIZE);
	ssize_t rd = 0;
	if (sbuf.InitCheck() == B_OK)
		rd = sbuf.Read(&packethead, 1);
	while (rd == 1) {
		// Run Length Packet
		if (packethead & TGA_RLE_PACKET_TYPE_BIT) {
			uint8 tgapixel[4], rlecount;
			rlecount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1;
			if (tgapixcol + rlecount > imagespec.width) {
				result = B_NO_TRANSLATOR;
				break;
			}
			rd = sbuf.Read(tgapixel, tgaBytesPerPixel);
			if (rd == tgaBytesPerPixel) {
				pix_tganm_to_bits(pbitspixel, tgapixel,
					rlecount, imagespec.depth, 0, nalpha);
 
				pbitspixel += 4 * rlecount;
				tgapixcol += rlecount;
			} else {
				result = B_NO_TRANSLATOR;
				break; // error
			}
 
		// Raw Packet
		} else {
			uint8 tgaPixelBuf[512], rawcount;
			uint16 rawbytes;
			rawcount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1;
			if (tgapixcol + rawcount > imagespec.width) {
				result = B_NO_TRANSLATOR;
				break;
			}
			rawbytes = tgaBytesPerPixel * rawcount;
			rd = sbuf.Read(tgaPixelBuf, rawbytes);
			if (rd == rawbytes) {
				pix_tganm_to_bits(pbitspixel, tgaPixelBuf,
					rawcount, imagespec.depth, tgaBytesPerPixel, nalpha);
 
				pbitspixel += 4 * rawcount;
				tgapixcol += rawcount;
			} else {
				result = B_NO_TRANSLATOR;
				break;
			}
		}
 
		if (tgapixcol == imagespec.width) {
			outDestination->Write(bitsRowData, bitsRowBytes);
			tgapixcol = 0;
			tgapixrow++;
			if (tgapixrow == imagespec.height)
				break;
			if (bvflip)
				outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR);
			pbitspixel = bitsRowData;
		}
		rd = sbuf.Read(&packethead, 1);
	}
 
	delete[] bitsRowData;
	bitsRowData = NULL;
 
	return result;
}
 
// convert a row of color mapped pixels to pbits
status_t
pix_tgam_to_bits(uint8 *pbits, uint8 *ptgaindices,
	uint16 width, uint8 depth, uint8 *pmap)
{
	status_t result = B_OK;
	uint8 *ptgapixel = NULL;
 
	switch (depth) {
		case 32:
			for (uint16 i = 0; i < width; i++) {
				ptgapixel = pmap +
					(ptgaindices[i] * 4);
 
				memcpy(pbits, ptgapixel, 4);
 
				pbits += 4;
			}
			break;
 
		case 24:
			for (uint16 i = 0; i < width; i++) {
				ptgapixel = pmap +
					(ptgaindices[i] * 3);
 
				memcpy(pbits, ptgapixel, 3);
 
				pbits += 4;
			}
			break;
 
		case 16:
			for (uint16 i = 0; i < width; i++) {
				uint16 val;
 
				ptgapixel = pmap +
					(ptgaindices[i] * 2);
				val = ptgapixel[0] + (ptgapixel[1] << 8);
				pbits[0] =
					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
				pbits[1] =
					((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
				pbits[2] =
					((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
				pbits[3] = (val & 0x8000) ? 255 : 0;
 
				pbits += 4;
			}
			break;
 
		case 15:
			for (uint16 i = 0; i < width; i++) {
				uint16 val;
 
				ptgapixel = pmap +
					(ptgaindices[i] * 2);
				val = ptgapixel[0] + (ptgapixel[1] << 8);
				pbits[0] =
					((val & 0x1f) << 3) | ((val & 0x1f) >> 2);
				pbits[1] =
					((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7);
				pbits[2] =
					((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12);
 
				pbits += 4;
			}
			break;
 
		default:
			result = B_ERROR;
			break;
	}
 
	return result;
}
 
// ---------------------------------------------------------------
// translate_from_tgam_to_bits
//
// Translates a paletted TGA from inSource to the bits format.
//
// Preconditions:
//
// Parameters: inSource,	the TGA data to be translated
//
// outDestination,	where the bits data will be written to
//
// mapspec, info about the color map (palette)
//
// imagespec, width / height info
//
// pmap, color palette
//
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
translate_from_tgam_to_bits(BPositionIO *inSource,
	BPositionIO *outDestination, TGAColorMapSpec &mapspec,
	TGAImageSpec &imagespec, uint8 *pmap)
{
	bool bvflip;
	if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT)
		bvflip = false;
	else
		bvflip = true;
 
	int32 bitsRowBytes = imagespec.width * 4;
	uint8 tgaBytesPerPixel = (imagespec.depth / 8) +
		((imagespec.depth % 8) ? 1 : 0);
	int32 tgaRowBytes = (imagespec.width * tgaBytesPerPixel);
	uint32 tgapixrow = 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
	off_t bitsFileSize = (bitsRowBytes * imagespec.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;
	off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes;
	if (bvflip)
		outDestination->Seek(bitsoffset, SEEK_CUR);
 
	// allocate row buffers
	uint8 *tgaRowData = new(std::nothrow) uint8[tgaRowBytes];
	if (!tgaRowData)
		return B_ERROR;
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData) {
		delete[] tgaRowData;
		tgaRowData = NULL;
		return B_ERROR;
	}
 
	// perform the actual translation
	memset(bitsRowData, 0xff, bitsRowBytes);
	ssize_t rd = inSource->Read(tgaRowData, tgaRowBytes);
	while (rd == tgaRowBytes) {
		pix_tgam_to_bits(bitsRowData, tgaRowData,
			imagespec.width, mapspec.entrysize, pmap);
 
		outDestination->Write(bitsRowData, bitsRowBytes);
		tgapixrow++;
		// 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 (tgapixrow == imagespec.height)
			break;
 
		if (bvflip)
			outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR);
		rd = inSource->Read(tgaRowData, tgaRowBytes);
	}
 
	delete[] tgaRowData;
	tgaRowData = NULL;
	delete[] bitsRowData;
	bitsRowData = NULL;
 
	return B_OK;
}
 
// ---------------------------------------------------------------
// translate_from_tgamrle_to_bits
//
// Translates a color mapped or non color mapped RLE TGA from
// inSource to the bits format.
//
// Preconditions:
//
// Parameters: inSource,	the TGA data to be translated
//
// outDestination,	where the bits data will be written to
//
// filehead, image type info
//
// mapspec, info about the color map (palette)
//
// imagespec, width / height info
//
// pmap, color palette
//
//
// Postconditions:
//
// Returns: B_ERROR, if there is an error allocating memory
//
// B_OK, if all went well
// ---------------------------------------------------------------
status_t
TGATranslator::translate_from_tgamrle_to_bits(BPositionIO *inSource,
	BPositionIO *outDestination, TGAFileHeader &filehead,
	TGAColorMapSpec &mapspec, TGAImageSpec &imagespec, uint8 *pmap)
{
	status_t result = B_OK;
 
	bool bvflip;
	if (imagespec.descriptor & TGA_ORIGIN_VERT_BIT)
		bvflip = false;
	else
		bvflip = true;
	uint8 nalpha = tga_alphabits(filehead, mapspec, imagespec);
	int32 bitsRowBytes = imagespec.width * 4;
	uint8 tgaPalBytesPerPixel = (mapspec.entrysize / 8) +
		((mapspec.entrysize % 8) ? 1 : 0);
	uint8 tgaBytesPerPixel = (imagespec.depth / 8) +
		((imagespec.depth % 8) ? 1 : 0);
	uint16 tgapixrow = 0, tgapixcol = 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
	off_t bitsFileSize = (bitsRowBytes * imagespec.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;
	off_t bitsoffset = (imagespec.height - 1) * bitsRowBytes;
	if (bvflip)
		outDestination->Seek(bitsoffset, SEEK_CUR);
 
	// allocate row buffers
	uint8 *bitsRowData = new(std::nothrow) uint8[bitsRowBytes];
	if (!bitsRowData)
		return B_ERROR;
 
	// perform the actual translation
	memset(bitsRowData, 0xff, bitsRowBytes);
	uint8 *pbitspixel = bitsRowData;
	uint8 packethead;
	StreamBuffer sbuf(inSource, TGA_STREAM_BUFFER_SIZE);
	ssize_t rd = 0;
	if (sbuf.InitCheck() == B_OK)
		rd = sbuf.Read(&packethead, 1);
	while (rd == 1) {
		// Run Length Packet
		if (packethead & TGA_RLE_PACKET_TYPE_BIT) {
			uint8 tgaindex, rlecount;
			rlecount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1;
			if (tgapixcol + rlecount > imagespec.width) {
				result = B_NO_TRANSLATOR;
				break;
			}
			rd = sbuf.Read(&tgaindex, 1);
			if (rd == tgaBytesPerPixel) {
				uint8 *ptgapixel;
				ptgapixel = pmap + (tgaindex * tgaPalBytesPerPixel);
 
				pix_tganm_to_bits(pbitspixel, ptgapixel, rlecount,
					mapspec.entrysize, 0, nalpha);
 
				pbitspixel += 4 * rlecount;
				tgapixcol += rlecount;
			} else {
				result = B_NO_TRANSLATOR;
				break; // error
			}
 
		// Raw Packet
		} else {
			uint8 tgaIndexBuf[128], rawcount;
			rawcount = (packethead & ~TGA_RLE_PACKET_TYPE_BIT) + 1;
			if (tgapixcol + rawcount > imagespec.width) {
				result = B_NO_TRANSLATOR;
				break;
			}
			rd = sbuf.Read(tgaIndexBuf, rawcount);
			if (rd == rawcount) {
				pix_tgam_to_bits(pbitspixel, tgaIndexBuf,
					rawcount, mapspec.entrysize, pmap);
 
				pbitspixel += 4 * rawcount;
				tgapixcol += rawcount;
			} else {
				result = B_NO_TRANSLATOR;
				break;
			}
		}
 
		if (tgapixcol == imagespec.width) {
			outDestination->Write(bitsRowData, bitsRowBytes);
			tgapixcol = 0;
			tgapixrow++;
			if (tgapixrow == imagespec.height)
				break;
			if (bvflip)
				outDestination->Seek(-(bitsRowBytes * 2), SEEK_CUR);
			pbitspixel = bitsRowData;
		}
		rd = sbuf.Read(&packethead, 1);
	}
 
	delete[] bitsRowData;
	bitsRowData = NULL;
 
	return result;
}
 
// ---------------------------------------------------------------
// translate_from_tga
//
// Convert the data in inSource from the TGA format
// to the format specified in outType (either bits or TGA).
//
// Preconditions:
//
// Parameters:	inSource,	the bits data to translate
//
// 				amtread,	the amount of data already read from
//							inSource
//
//				read,		pointer to the data already read from
//							inSource
//
//				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
TGATranslator::translate_from_tga(BPositionIO *inSource, uint32 outType,
	BPositionIO *outDestination)
{
	TGAFileHeader fileheader;
	TGAColorMapSpec mapspec;
	TGAImageSpec imagespec;
	bool bheaderonly = false, bdataonly = false;
 
	status_t result;
	result = identify_tga_header(inSource, NULL, &fileheader, &mapspec,
		&imagespec);
	if (result != B_OK)
		return result;
 
	// if the user wants to translate a TGA to a TGA, easy enough :)
	if (outType == B_TGA_FORMAT) {
		// write out the TGA headers
		if (bheaderonly || (!bheaderonly && !bdataonly)) {
			result = write_tga_headers(outDestination, fileheader,
				mapspec, imagespec);
			if (result != B_OK)
				return result;
		}
		if (bheaderonly)
			// if the user only wants the header,
			// bail before it is written
			return result;
 
		const int32 kbuflen = 1024;
		uint8 buf[kbuflen];
		ssize_t rd = inSource->Read(buf, kbuflen);
		while (rd > 0) {
			outDestination->Write(buf, rd);
			rd = inSource->Read(buf, kbuflen);
		}
		if (rd == 0)
			return B_OK;
		else
			return B_ERROR;
 
	// if translating a TGA 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 = imagespec.width - 1;
		bitsHeader.bounds.bottom = imagespec.height - 1;
 
		// skip over Image ID data (if present)
		if (fileheader.idlength > 0)
			inSource->Seek(fileheader.idlength, SEEK_CUR);
 
		// read in palette and/or skip non-TGA data
		uint8 *ptgapalette = NULL;
		if (fileheader.colormaptype == TGA_COLORMAP) {
			uint32 nentrybytes;
			nentrybytes = mapspec.entrysize / 8;
			if (mapspec.entrysize % 8)
				nentrybytes++;
			ptgapalette = new(std::nothrow) uint8[nentrybytes * mapspec.length];
			inSource->Read(ptgapalette, nentrybytes * mapspec.length);
		}
 
		bitsHeader.rowBytes = imagespec.width * 4;
		if (fileheader.imagetype != TGA_NOCOMP_BW &&
			fileheader.imagetype != TGA_RLE_BW &&
			tga_alphabits(fileheader, mapspec, imagespec))
			bitsHeader.colors = B_RGBA32;
		else
			bitsHeader.colors = B_RGB32;
		int32 datasize = bitsHeader.rowBytes * imagespec.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 (fileheader.imagetype) {
			case TGA_NOCOMP_TRUECOLOR:
			case TGA_NOCOMP_BW:
				result = translate_from_tganm_to_bits(inSource,
					outDestination, fileheader, mapspec, imagespec);
				break;
 
			case TGA_NOCOMP_COLORMAP:
				result = translate_from_tgam_to_bits(inSource,
					outDestination, mapspec, imagespec, ptgapalette);
				break;
 
			case TGA_RLE_TRUECOLOR:
			case TGA_RLE_BW:
				result = translate_from_tganmrle_to_bits(inSource,
					outDestination, fileheader, mapspec, imagespec);
				break;
 
			case TGA_RLE_COLORMAP:
				result = translate_from_tgamrle_to_bits(inSource, outDestination,
					fileheader, mapspec, imagespec, ptgapalette);
				break;
 
			default:
				result = B_NO_TRANSLATOR;
				break;
		}
 
		delete[] ptgapalette;
		ptgapalette = NULL;
 
		return result;
 
	} else
		return B_NO_TRANSLATOR;
}
 
status_t
TGATranslator::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_tga(inSource, outType, outDestination);
	else
		// if BaseTranslator did not properly identify the data as
		// bits or not bits
		return B_NO_TRANSLATOR;
}
 
BView *
TGATranslator::NewConfigView(TranslatorSettings *settings)
{
	return new(std::nothrow) TGAView(B_TRANSLATE("TGATranslator Settings"),
		B_WILL_DRAW, settings);
}
 

V512 A call of the 'memset' function will lead to underflow of the buffer 'footer'.

V570 The 'imagespec.height' variable is assigned to itself.

V570 The 'imagespec.width' variable is assigned to itself.

V570 The 'imagespec.yorigin' variable is assigned to itself.

V570 The 'imagespec.xorigin' variable is assigned to itself.

V570 The 'mapspec.length' variable is assigned to itself.

V570 The 'mapspec.firstentry' variable is assigned to itself.