/*****************************************************************************/
// SGITranslator
// Written by Stephan Aßmus
// based on TIFFTranslator written mostly by
// Michael Wilber
//
// SGITranslator.cpp
//
// This BTranslator based object is for opening and writing
// SGI images.
//
//
// Copyright (c) 2003-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.
/*****************************************************************************/
//
// How this works:
//
// libtiff has a special version of SGIOpen() that gets passed custom
// functions for reading writing etc. and a handle. This handle in our case
// is a BPositionIO object, which libtiff passes on to the functions for reading
// writing etc. So when operations are performed on the SGI* handle that is
// returned by SGIOpen(), libtiff uses the special reading writing etc
// functions so that all stream io happens on the BPositionIO object.
 
#include <new>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
 
#include <Catalog.h>
#include <OS.h>
 
#include "SGIImage.h"
#include "SGITranslator.h"
#include "SGIView.h"
 
using std::nothrow;
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "SGITranslator"
 
 
// 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 (SGITranslator)"
	},
	{
		SGI_FORMAT,
		B_TRANSLATOR_BITMAP,
		SGI_IN_QUALITY,
		SGI_IN_CAPABILITY,
		"image/sgi",
		"SGI 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 (SGITranslator)"
	},
	{
		SGI_FORMAT,
		B_TRANSLATOR_BITMAP,
		SGI_OUT_QUALITY,
		SGI_OUT_CAPABILITY,
		"image/sgi",
		"SGI 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},
	{SGI_SETTING_COMPRESSION, TRAN_SETTING_INT32, SGI_COMP_RLE}
		// compression is set to RLE 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 SGITranslator object to be used by BTranslatorRoster
//
// Preconditions:
//
// Parameters: n,		The translator to return. Since
//						SGITranslator only publishes one
//						translator, it only returns a
//						SGITranslator 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 SGITranslator if n is zero
// ---------------------------------------------------------------
BTranslator *
make_nth_translator(int32 n, image_id you, uint32 flags, ...)
{
	if (!n)
		return new SGITranslator();
	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:
// ---------------------------------------------------------------
SGITranslator::SGITranslator()
	:
	BaseTranslator(B_TRANSLATE("SGI images"), 
		B_TRANSLATE("SGI image translator"),
		SGI_TRANSLATOR_VERSION,
		sInputFormats, kNumInputFormats,
		sOutputFormats, kNumOutputFormats,
		"SGITranslator_Settings",
		sDefaultSettings, kNumDefaultSettings,
		B_TRANSLATOR_BITMAP, SGI_FORMAT)
{
}
 
// ---------------------------------------------------------------
// Destructor
//
// Does nothing
//
// Preconditions:
//
// Parameters:
//
// Postconditions:
//
// Returns:
// ---------------------------------------------------------------
SGITranslator::~SGITranslator()
{
}
 
status_t
identify_sgi_header(BPositionIO *inSource, translator_info *outInfo, uint32 outType,
	SGIImage **poutSGIImage = NULL)
{
	status_t status = B_NO_MEMORY;
	// construct new SGIImage object and set it to the provided BPositionIO
	SGIImage* sgiImage = new(nothrow) SGIImage();
	if (sgiImage)
		status = sgiImage->SetTo(inSource);
 
	if (status >= B_OK) {
		if (outInfo) {
			outInfo->type = SGI_FORMAT;
			outInfo->group = B_TRANSLATOR_BITMAP;
			outInfo->quality = SGI_IN_QUALITY;
			outInfo->capability = SGI_IN_CAPABILITY;
			strcpy(outInfo->MIME, "image/sgi");
			strlcpy(outInfo->name, B_TRANSLATE("SGI image"),
				sizeof(outInfo->name));
		}
	} else {
		delete sgiImage;
		sgiImage = NULL;
	}
	if (!poutSGIImage)
		// close SGIImage if caller is not interested in SGIImage handle
		delete sgiImage;
	else
		// leave SGIImage open (if it is) and return handle if caller needs it
		*poutSGIImage = sgiImage;
 
	return status;
}
 
// ---------------------------------------------------------------
// 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 (not used)
//
//				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 understood the data and there were
//			no errors found
//
// Other errors if BPositionIO::Read() returned an error value
// ---------------------------------------------------------------
status_t
SGITranslator::DerivedIdentify(BPositionIO *inSource,
	const translation_format *inFormat, BMessage *ioExtension,
	translator_info *outInfo, uint32 outType)
{
	return identify_sgi_header(inSource, outInfo, outType);
}
 
// translate_from_bits
status_t
SGITranslator::translate_from_bits(BPositionIO *inSource, uint32 outType,
	BPositionIO *outDestination)
{
	TranslatorBitmap bitsHeader;
 
	uint32 compression = fSettings->SetGetInt32(SGI_SETTING_COMPRESSION);
 
	status_t ret = identify_bits_header(inSource, NULL, &bitsHeader);
	if (ret < B_OK)
		return ret;
 
	// Translate B_TRANSLATOR_BITMAP to SGI_FORMAT
	if (outType == SGI_FORMAT) {
 
		// common fields which are independent of the bitmap format
		uint32 width = bitsHeader.bounds.IntegerWidth() + 1;
		uint32 height = bitsHeader.bounds.IntegerHeight() + 1;
		uint32 bytesPerRow = bitsHeader.rowBytes;
		uint32 bytesPerChannel = 1;
		color_space format = bitsHeader.colors;
 
		uint32 channelCount;
		switch (format) {
			case B_GRAY8:
				channelCount = 1;
				break;
			case B_RGB32:
			case B_RGB32_BIG:
			case B_RGB24:
			case B_RGB24_BIG:
				channelCount = 3;
				break;
			case B_RGBA32:
			case B_RGBA32_BIG:
				channelCount = 4;
				break;
			default:
				return B_NO_TRANSLATOR;
		}
 
		// Set up SGI header
		SGIImage* sgiImage = new SGIImage();
		status_t ret = sgiImage->SetTo(outDestination, width, height,
									   channelCount, bytesPerChannel, compression);
		if (ret >= B_OK) {
			// read one row at a time,
			// convert to the correct format
			// and write out the results
 
			// SGI Images store each channel separately
			// a buffer is allocated big enough to hold all channels
			// then the pointers are assigned with offsets into that buffer
			uint8** rows = new(nothrow) uint8*[channelCount];
			if (rows)
				rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel];
			// rowBuffer is going to hold the converted data
			uint8* rowBuffer = new(nothrow) uint8[bytesPerRow];
			if (rows && rows[0] && rowBuffer) {
				// assign the other pointers (channel offsets in row buffer)
				for (uint32 i = 1; i < channelCount; i++)
					rows[i] = rows[0] + i * width;
				// loop through all lines of the image
				for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) {
 
					ret = inSource->Read(rowBuffer, bytesPerRow);
					// see if an error happened while reading
					if (ret < B_OK)
						break;
					// convert to native format (big endian)
					switch (format) {
						case B_GRAY8: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[0];
								src += 1;
							}
							break;
						}
						case B_RGB24: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[2];
								rows[1][x] = src[1];
								rows[2][x] = src[0];
								src += 3;
							}
							break;
						}
						case B_RGB24_BIG: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[0];
								rows[1][x] = src[1];
								rows[2][x] = src[2];
								src += 3;
							}
							break;
						}
						case B_RGB32: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[2];
								rows[1][x] = src[1];
								rows[2][x] = src[0];
								// ignore src[3]
								src += 4;
							}
							break;
						}
						case B_RGB32_BIG: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[1];
								rows[1][x] = src[2];
								rows[2][x] = src[3];
								// ignore src[0]
								src += 4;
							}
							break;
						}
						case B_RGBA32: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[2];
								rows[1][x] = src[1];
								rows[2][x] = src[0];
								rows[3][x] = src[3];
								src += 4;
							}
							break;
						}
						case B_RGBA32_BIG: {
							uint8* src = rowBuffer;
							for (uint32 x = 0; x < width; x++) {
								rows[0][x] = src[1];
								rows[1][x] = src[2];
								rows[2][x] = src[3];
								rows[3][x] = src[0];
								src += 4;
							}
							break;
						}
						default:
							// cannot be here
							break;
					} // switch (format)
 
					// for each channel, write a row buffer
					for (uint32 z = 0; z < channelCount; z++) {
						ret = sgiImage->WriteRow(rows[z], y, z);
						if (ret < B_OK) {
							syslog(LOG_ERR,
								"WriteRow() returned %s!\n"), strerror(ret);
							break;
						}
					}
 
				} // for (uint32 y = 0; y < height && ret >= B_OK; y++)
				if (ret >= B_OK)
					ret = B_OK;
			} else // if (rows && rows[0] && rowBuffer)
				ret = B_NO_MEMORY;
 
			delete[] rows[0];
			delete[] rows;
			delete[] rowBuffer;
		}
 
		// done with the SGIImage object
		delete sgiImage;
 
		return ret;
	}
	return B_NO_TRANSLATOR;
}
 
// translate_from_sgi
status_t
SGITranslator::translate_from_sgi(BPositionIO *inSource, uint32 outType,
	BPositionIO *outDestination)
{
	status_t ret = B_NO_TRANSLATOR;
 
	// if copying SGI_FORMAT to SGI_FORMAT
	if (outType == SGI_FORMAT) {
		translate_direct_copy(inSource, outDestination);
		return B_OK;
	}
 
	// variables needing cleanup
	SGIImage* sgiImage = NULL;
 
	ret = identify_sgi_header(inSource, NULL, outType, &sgiImage);
 
	if (ret >= B_OK) {
 
		bool bheaderonly = false, bdataonly = false;
 
		uint32 width = sgiImage->Width();
		uint32 height = sgiImage->Height();
		uint32 channelCount = sgiImage->CountChannels();
		color_space format = B_RGBA32;
		uint32 bytesPerRow = 0;
		uint32 bytesPerChannel = sgiImage->BytesPerChannel();
 
		if (channelCount == 1) {
//			format = B_GRAY8;	// this format is not supported by most applications
//			bytesPerRow = width;
			format = B_RGB32;
			bytesPerRow = width * 4;
		} else if (channelCount == 2) {
			// means gray (luminance) + alpha, we convert that to B_RGBA32
			format = B_RGBA32;
			bytesPerRow = width * 4;
		} else if (channelCount == 3) {
			format = B_RGB32; // should be B_RGB24, but let's not push it too hard...
			bytesPerRow = width * 4;
		} else if (channelCount == 4) {
			format = B_RGBA32;
			bytesPerRow = width * 4;
		} else
			ret = B_NO_TRANSLATOR; // we cannot handle this image
 
		if (ret >= B_OK && !bdataonly) {
			// Construct and write Be bitmap header
			TranslatorBitmap bitsHeader;
			bitsHeader.magic = B_TRANSLATOR_BITMAP;
			bitsHeader.bounds.left = 0;
			bitsHeader.bounds.top = 0;
			bitsHeader.bounds.right = width - 1;
			bitsHeader.bounds.bottom = height - 1;
			bitsHeader.rowBytes = bytesPerRow;
			bitsHeader.colors = format;
			bitsHeader.dataSize = bitsHeader.rowBytes * height;
			if ((ret = swap_data(B_UINT32_TYPE, &bitsHeader,
				sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN)) < B_OK) {
				return ret;
			} else
				ret = outDestination->Write(&bitsHeader, 
					sizeof(TranslatorBitmap));
		}
		if (ret < B_OK)
			syslog(LOG_ERR, "error writing bits header: %s\n", strerror(ret));
		if (ret >= B_OK && !bheaderonly) {
			// read one row at a time,
			// convert to the correct format
			// and write out the results
 
			// SGI Images store each channel separately
			// a buffer is allocated big enough to hold all channels
			// then the pointers are assigned with offsets into that buffer
			uint8** rows = new(nothrow) uint8*[channelCount];
			if (rows)
				rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel];
			// rowBuffer is going to hold the converted data
			uint8* rowBuffer = new(nothrow) uint8[bytesPerRow];
			if (rows && rows[0] && rowBuffer) {
				// assign the other pointers (channel offsets in row buffer)
				for (uint32 i = 1; i < channelCount; i++)
					rows[i] = rows[0] + i * width * bytesPerChannel;
				// loop through all lines of the image
				for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) {
					// fill the row buffer with each channel
					for (uint32 z = 0; z < channelCount; z++) {
						ret = sgiImage->ReadRow(rows[z], y, z);
						if (ret < B_OK)
							break;
					}
					// see if an error happened while reading
					if (ret < B_OK)
						break;
					// convert to native format (big endian)
					if (bytesPerChannel == 1) {
						switch (format) {
							case B_GRAY8: {
								uint8* dst = rowBuffer;
								for (uint32 x = 0; x < width; x++) {
									dst[0] = rows[0][x];
									dst += 1;
								}
								break;
							}
							case B_RGB24: {
								uint8* dst = rowBuffer;
								for (uint32 x = 0; x < width; x++) {
									dst[0] = rows[2][x];
									dst[1] = rows[1][x];
									dst[2] = rows[0][x];
									dst += 3;
								}
								break;
							}
							case B_RGB32: {
								uint8* dst = rowBuffer;
								if (channelCount == 1) {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows[0][x];
										dst[1] = rows[0][x];
										dst[2] = rows[0][x];
										dst[3] = 255;
										dst += 4;
									}
								} else {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows[2][x];
										dst[1] = rows[1][x];
										dst[2] = rows[0][x];
										dst[3] = 255;
										dst += 4;
									}
								}
								break;
							}
							case B_RGBA32: {
								uint8* dst = rowBuffer;
								if (channelCount == 2) {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows[0][x];
										dst[1] = rows[0][x];
										dst[2] = rows[0][x];
										dst[3] = rows[1][x];
										dst += 4;
									}
								} else {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows[2][x];
										dst[1] = rows[1][x];
										dst[2] = rows[0][x];
										dst[3] = rows[3][x];
										dst += 4;
									}
								}
								break;
							}
							default:
								// cannot be here
								break;
						} // switch (format)
						ret = outDestination->Write(rowBuffer, bytesPerRow);
					} else {
						// support for 16 bits per channel images
						uint16** rows16 = (uint16**)rows;
						switch (format) {
							case B_GRAY8: {
								uint8* dst = rowBuffer;
								for (uint32 x = 0; x < width; x++) {
									dst[0] = rows16[0][x] >> 8;
									dst += 1;
								}
								break;
							}
							case B_RGB24: {
								uint8* dst = rowBuffer;
								for (uint32 x = 0; x < width; x++) {
									dst[0] = rows16[2][x] >> 8;
									dst[1] = rows16[1][x] >> 8;
									dst[2] = rows16[0][x] >> 8;
									dst += 3;
								}
								break;
							}
							case B_RGB32: {
								uint8* dst = rowBuffer;
								if (channelCount == 1) {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows16[0][x] >> 8;
										dst[1] = rows16[0][x] >> 8;
										dst[2] = rows16[0][x] >> 8;
										dst[3] = 255;
										dst += 4;
									}
								} else {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows16[2][x] >> 8;
										dst[1] = rows16[1][x] >> 8;
										dst[2] = rows16[0][x] >> 8;
										dst[3] = 255;
										dst += 4;
									}
								}
								break;
							}
							case B_RGBA32: {
								uint8* dst = rowBuffer;
								if (channelCount == 2) {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows16[0][x] >> 8;
										dst[1] = rows16[0][x] >> 8;
										dst[2] = rows16[0][x] >> 8;
										dst[3] = rows16[1][x] >> 8;
										dst += 4;
									}
								} else {
									for (uint32 x = 0; x < width; x++) {
										dst[0] = rows16[2][x] >> 8;
										dst[1] = rows16[1][x] >> 8;
										dst[2] = rows16[0][x] >> 8;
										dst[3] = rows16[3][x] >> 8;
										dst += 4;
									}
								}
								break;
							}
							default:
								// cannot be here
								break;
						} // switch (format)
						ret = outDestination->Write(rowBuffer, bytesPerRow);
					} // 16 bit version
				} // for (uint32 y = 0; y < height && ret >= B_OK; y++)
				if (ret >= B_OK)
					ret = B_OK;
			} else // if (rows && rows[0] && rowBuffer)
				ret = B_NO_MEMORY;
			delete[] rows[0];
			delete[] rows;
			delete[] rowBuffer;
		} // if (ret >= B_OK && !bheaderonly)
	} // if (ret >= B_OK)
	delete sgiImage;
 
	return ret;
}
 
// ---------------------------------------------------------------
// 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
SGITranslator::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_sgi(inSource, outType, outDestination);
	else
		// if BaseTranslator did not properly identify the data as
		// bits or not bits
		return B_NO_TRANSLATOR;
}
 
BView *
SGITranslator::NewConfigView(TranslatorSettings *settings)
{
	return new SGIView(B_TRANSLATE("SGITranslator Settings"), B_WILL_DRAW, 
		settings);
}

V576 Incorrect format. A different number of actual arguments is expected while calling 'syslog' function. Expected: 3. Present: 2.