/*
 * Copyright 2007-2008, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 *
 * Copyright 1997-2007, Dave Coffin, dcoffin a cybercom o net
 * This code is based on Dave Coffin's dcraw 8.63 - it's basically the same
 * thing in C++, but follows common sense programming rules a bit more :-)
 * Except the Fovean functions, dcraw is public domain.
 */
 
 
#include "RAW.h"
#include "ReadHelper.h"
 
#include <Message.h>
#include <TranslationErrors.h>
 
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
 
//#define TRACE(x) printf x
#define TRACE(x)
//#define TAG(x...) printf(x)
#define TAG(x...)
 
#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31))
#define LIM(x,min,max) MAX(min,MIN(x,max))
#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y))
#define CLIP(x) LIM(x,0,65535)
#define SWAP(a,b) { a ^= b; a ^= (b ^= a); }
 
#define FC(row,col) \
	(fFilters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
 
 
static const uint32 kImageBufferCount = 10;
static const uint32 kDecodeBufferCount = 2048;
 
const double xyz_rgb[3][3] = {			/* XYZ from RGB */
  { 0.412453, 0.357580, 0.180423 },
  { 0.212671, 0.715160, 0.072169 },
  { 0.019334, 0.119193, 0.950227 } };
const float kD65White[3] = { 0.950456, 1, 1.088754 };
 
struct decode {
	struct decode *branch[2];
	int32	leaf;
};
 
struct jhead {
	int		bits, high, wide, clrs, restart, vpred[4];
	struct decode *huff[4];
	uint16*	row;
};
 
struct tiff_header {
	uint16	order, magic;
	int32	image_file_directory;
	uint16	pad, ntag;
	struct tiff_tag tag[15];
	int32	next_image_file_directory;
	uint16	pad2, nexif;
	struct tiff_tag exif[4];
	int16	bps[4];
	int32	rat[6];
	char make[64], model[64], soft[32], date[20];
};
 
 
template<class T> inline T
square(const T& value)
{
	return value * value;
}
 
 
static inline bool
x_flipped(int32 orientation)
{
	return orientation == 2 || orientation == 3
		|| orientation == 7 || orientation == 8;
}
 
 
static inline bool
y_flipped(int32 orientation)
{
	return orientation == 3 || orientation == 4
		|| orientation == 6 || orientation == 7;
}
 
 
#if 0
void
dump_to_disk(void* data, size_t length)
{
	FILE* file = fopen("/tmp/RAW.out", "wb");
	if (file == NULL)
		return;
 
	fwrite(data, length, 1, file);
	fclose(file);
}
#endif
 
 
//	#pragma mark -
 
 
DCRaw::DCRaw(BPositionIO& stream)
	:
	fRead(stream),
	fNumImages(0),
	fRawIndex(-1),
	fThumbIndex(-1),
	fDNGVersion(0),
	fIsTIFF(false),
	fImageData(NULL),
	fThreshold(0.0f),
	fHalfSize(false),
	fUseCameraWhiteBalance(true),
	fUseAutoWhiteBalance(true),
	fRawColor(true),
	fUseGamma(true),
	fBrightness(1.0f),
	fOutputColor(1),
	fHighlight(0),
	fDocumentMode(0),
	fOutputWidth(0),
	fOutputHeight(0),
	fInputWidth(0),
	fInputHeight(0),
	fTopMargin(0),
	fLeftMargin(0),
	fColors(3),
	fOutputProfile(NULL),
	fOutputBitsPerSample(8),
	fDecodeLeaf(0),
	fDecodeBitsZeroAfterMax(false),
	fFilters(~0),
	fEXIFOffset(-1),
	fProgressMonitor(NULL)
{
	fImages = new image_data_info[kImageBufferCount];
	fDecodeBuffer = new decode[kDecodeBufferCount];
	fCurve = new uint16[0x1000];
	for (uint32 i = 0; i < 0x1000; i++) {
		fCurve[i] = i;
	}
 
	cbrt = new float[0x10000];
	fHistogram = (int32 (*)[4])calloc(sizeof(int32) * 0x2000 * 4, 1);
 
	memset(fImages, 0, sizeof(image_data_info) * kImageBufferCount);
	memset(&fMeta, 0, sizeof(image_meta_info));
	memset(fUserMultipliers, 0, sizeof(fUserMultipliers));
	memset(fWhite, 0, sizeof(fWhite));
 
	fMeta.camera_multipliers[0] = -1;
	fCR2Slice[0] = 0;
}
 
 
DCRaw::~DCRaw()
{
	delete[] fImages;
	delete[] fDecodeBuffer;
	delete[] fCurve;
 
	delete[] cbrt;
 
	free(fHistogram);
	free(fImageData);
}
 
 
int32
DCRaw::_AllocateImage()
{
	if (fNumImages + 1 == kImageBufferCount)
		throw (status_t)B_ERROR;
 
	return fNumImages++;
}
 
 
image_data_info&
DCRaw::_Raw()
{
	if (fRawIndex < 0)
		fRawIndex = _AllocateImage();
	if (fRawIndex < 0)
		throw (status_t)B_ERROR;
 
	return fImages[fRawIndex];
}
 
 
image_data_info&
DCRaw::_Thumb()
{
	if (fThumbIndex < 0)
		fThumbIndex = _AllocateImage();
	if (fThumbIndex < 0)
		throw (status_t)B_ERROR;
 
	return fImages[fThumbIndex];
}
 
 
//! Make sure that the raw image always comes first
void
DCRaw::_CorrectIndex(uint32& index) const
{
	if (fRawIndex > 0) {
		if (index == 0)
			index = fRawIndex;
		else if (index <= (uint32)fRawIndex)
			index--;
	}
}
 
 
inline uint16&
DCRaw::_Bayer(int32 column, int32 row)
{
	return fImageData[((row) >> fShrink) * fOutputWidth
		+ ((column) >> fShrink)][FC(row, column)];
}
 
 
inline int32
DCRaw::_FilterCoefficient(int32 x, int32 y)
{
	static const char filter[16][16] = {
		{ 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
		{ 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 },
		{ 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 },
		{ 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 },
		{ 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 },
		{ 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 },
		{ 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 },
		{ 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 },
		{ 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 },
		{ 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 },
		{ 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 },
		{ 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 },
		{ 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 },
		{ 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 },
		{ 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
		{ 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 }
	};
 
	if (fFilters != 1)
		return FC(y, x);
 
	return filter[(y + fTopMargin) & 15][(x + fLeftMargin) & 15];
}
 
 
inline int32
DCRaw::_FlipIndex(uint32 row, uint32 col, uint32 flip)
{
	if (flip > 4)
		SWAP(row, col);
	if (y_flipped(flip))
		row = fInputHeight - 1 - row;
	if (x_flipped(flip))
		col = fInputWidth - 1 - col;
 
	return row * fInputWidth + col;
}
 
 
bool
DCRaw::_SupportsCompression(image_data_info& image) const
{
	switch (image.compression) {
		//case COMPRESSION_NONE:
		case COMPRESSION_OLD_JPEG:
		case COMPRESSION_PACKBITS:
			return true;
 
		default:
			return false;
	}
}
 
 
bool
DCRaw::_IsCanon() const
{
	return !strncasecmp(fMeta.manufacturer, "Canon", 5);
}
 
 
bool
DCRaw::_IsKodak() const
{
	return !strncasecmp(fMeta.manufacturer, "Kodak", 5);
}
 
 
bool
DCRaw::_IsNikon() const
{
	return !strncasecmp(fMeta.manufacturer, "Nikon", 5);
}
 
 
bool
DCRaw::_IsOlympus() const
{
	return !strncasecmp(fMeta.manufacturer, "Olympus", 7);
}
 
 
bool
DCRaw::_IsPentax() const
{
	return !strncasecmp(fMeta.manufacturer, "Pentax", 6);
}
 
 
bool
DCRaw::_IsSamsung() const
{
	return !strncasecmp(fMeta.manufacturer, "Samsung", 7);
}
 
 
void
DCRaw::_ParseThumbTag(off_t baseOffset, uint32 offsetTag, uint32 lengthTag)
{
	uint16 entries;
	fRead(entries);
 
	while (entries--) {
		off_t nextOffset;
		tiff_tag tag;
		_ParseTIFFTag(baseOffset, tag, nextOffset);
 
		if (tag.tag == offsetTag)
			_Thumb().data_offset = fRead.Next<uint32>();
		if (tag.tag == lengthTag)
			_Thumb().bytes = fRead.Next<uint32>();
 
		fRead.Seek(nextOffset, SEEK_SET);
	}
}
 
 
void
DCRaw::_ParseManufacturerTag(off_t baseOffset)
{
	static const uchar xlat[2][256] = {
		{
			0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d,
			0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d,
			0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f,
			0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f,
			0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1,
			0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17,
			0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89,
			0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f,
			0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b,
			0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb,
			0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3,
			0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f,
			0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35,
			0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43,
			0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5,
			0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7
		},
		{
			0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c,
			0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34,
			0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad,
			0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05,
			0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee,
			0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d,
			0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b,
			0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b,
			0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc,
			0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33,
			0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8,
			0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6,
			0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c,
			0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49,
			0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb,
			0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f
		}
	};
 
	uint32 ver97 = 0, serial = 0;
	uchar buf97[324], ci, cj, ck;
	bool originalSwap = fRead.IsSwapping();
	image_data_info& image = fImages[fNumImages];
 
	// The MakerNote might have its own TIFF header (possibly with
	// its own byte-order!), or it might just be a table.
 
	char type[10];
	fRead(type, sizeof(type));
 
	if (!strncmp(type, "KDK", 3)
		|| !strncmp(type, "VER", 3)
		|| !strncmp(type, "IIII", 4)
		|| !strncmp(type, "MMMM", 4)) {
		// these aren't TIFF tables
		return;
	}
	if (!strncmp(type, "KC", 2)			// Konica KD-400Z, KD-510Z
		|| !strncmp(type, "MLY", 3)) {	// Minolta DiMAGE G series
		fRead.SetSwap(B_HOST_IS_LENDIAN != 0);
			// this chunk is always in big endian
 
		uint32 whiteBalance[4] = {0, 0, 0, 0};
 
		off_t offset;
	    while ((offset = fRead.Position()) < image.data_offset
	    	&& offset < 16384) {
			whiteBalance[0] = whiteBalance[2];
			whiteBalance[2] = whiteBalance[1];
			whiteBalance[1] = whiteBalance[3];
 
			whiteBalance[3] = fRead.Next<uint16>();
			if (whiteBalance[1] == 256 && whiteBalance[3] == 256
				&& whiteBalance[0] > 256 && whiteBalance[0] < 640
				&& whiteBalance[2] > 256 && whiteBalance[2] < 640) {
				for (uint32 i = 0; i < 4; i++) {
					fMeta.camera_multipliers[i] = whiteBalance[i];
				}
			}
		}
		goto quit;
	}
	if (!strcmp(type, "Nikon")) {
		baseOffset = fRead.Position();
 
		uint16 endian;
		fRead(endian);
 
#if B_HOST_IS_LENDIAN
		fRead.SetSwap(endian == 'MM');
#else
		fRead.SetSwap(endian == 'II');
#endif
 
		if (fRead.Next<uint16>() != 42)
			goto quit;
 
		uint32 offset = fRead.Next<uint32>();
		fRead.Seek(offset - 8, SEEK_CUR);
	} else if (!strncmp(type, "FUJIFILM", 8)
		|| !strncmp(type, "SONY", 4)
		|| !strcmp(type, "Panasonic")) {
		fRead.SetSwap(B_HOST_IS_BENDIAN != 0);
			// this chunk is always in little endian
		fRead.Seek(2, SEEK_CUR);
	} else if (!strcmp(type, "OLYMP")
		|| !strcmp(type, "LEICA")
		|| !strcmp(type, "Ricoh")
		|| !strcmp(type, "EPSON"))
		fRead.Seek(-2, SEEK_CUR);
	else if (!strcmp(type, "AOC") || !strcmp(type, "QVC"))
		fRead.Seek(-4, SEEK_CUR);
	else
		fRead.Seek(-10, SEEK_CUR);
 
	uint16 entries;
	fRead(entries);
	if (entries > 1000)
		return;
 
	while (entries--) {
		off_t nextOffset;
		tiff_tag tag;
		_ParseTIFFTag(baseOffset, tag, nextOffset);
		TAG("Manufacturer tag %u (type %u, length %lu)\n", tag.tag, tag.type,
			tag.length);
 
		if (strstr(fMeta.manufacturer, "PENTAX")) {
			if (tag.tag == 0x1b)
				tag.tag = 0x1018;
			if (tag.tag == 0x1c)
				tag.tag = 0x1017;
		} else if (tag.tag == 2 && strstr(fMeta.manufacturer, "NIKON")) {
			fRead.Next<uint16>();
				// ignored
			fMeta.iso_speed = fRead.Next<uint16>();
		}
 
		if (tag.tag == 4 && tag.length == 27) {
			fRead.Next<uint32>();
				// ignored
			fMeta.iso_speed = 50 * pow(2, fRead.Next<uint16>() / 32.0 - 4);
			fRead.Next<uint16>();
				// ignored
			fMeta.aperture = pow(2, fRead.Next<uint16>() / 64.0);
			fMeta.shutter = pow(2, fRead.Next<int16>() / -32.0);
		}
		if (tag.tag == 8 && tag.type == 4)
			fMeta.shot_order = fRead.Next<uint32>();
		if (tag.tag == 0xc && tag.length == 4) {
			fMeta.camera_multipliers[0] = fRead.NextDouble(TIFF_FRACTION_TYPE);
			fMeta.camera_multipliers[2] = fRead.NextDouble(TIFF_FRACTION_TYPE);
		}
		if (tag.tag == 0x10 && tag.type == 4)
			fUniqueID = fRead.Next<uint32>();
		if (tag.tag == 0x11) {
			if (_ParseTIFFImageFileDirectory(baseOffset, fRead.Next<uint32>())
					== B_OK)
				fNumImages++;
		}
		if (tag.tag == 0x14 && tag.length == 2560 && tag.type == 7) {
			fRead.Seek(1248, SEEK_CUR);
			goto get2_256;
		}
		if (tag.tag == 0x1d) {
			int c;
			while ((c = fRead.Next<uint8>()) && c != EOF) {
				serial = serial * 10 + (isdigit(c) ? c - '0' : c % 10);
			}
		}
		if (tag.tag == 0x81 && tag.type == 4) {
			_Raw().data_offset = fRead.Next<uint32>();
			fRead.Seek(_Raw().data_offset + 41, SEEK_SET);
			_Raw().height = fRead.Next<uint16>() * 2;
			_Raw().width  = fRead.Next<uint16>();
			fFilters = 0x61616161;
		}
		if ((tag.tag == 0x81 && tag.type == 7)
			|| (tag.tag == 0x100 && tag.type == 7)
			|| (tag.tag == 0x280 && tag.type == 1)) {
			_Thumb().data_offset = fRead.Position();
			_Thumb().bytes = tag.length;
		}
		if (tag.tag == 0x88 && tag.type == 4
			&& (_Thumb().data_offset = fRead.Next<uint32>())) {
			_Thumb().data_offset += baseOffset;
		}
		if (tag.tag == 0x89 && tag.type == 4)
			_Thumb().bytes = fRead.Next<uint32>();
		if (tag.tag == 0x8c)
			fCurveOffset = fRead.Position() + 2112;
		if (tag.tag == 0x96)
			fCurveOffset = fRead.Position() + 2;
		if (tag.tag == 0x97) {
			for (uint32 i = 0; i < 4; i++) {
				ver97 = (ver97 << 4) + fRead.Next<uint8>() - '0';
			}
			switch (ver97) {
				case 0x100:
					fRead.Seek(68, SEEK_CUR);
					for (uint32 i = 0; i < 4; i++) {
						fMeta.camera_multipliers[(i >> 1) | ((i & 1) << 1)]
							= fRead.Next<uint16>();
					}
					break;
				case 0x102:
					fRead.Seek(6, SEEK_CUR);
					goto get2_rggb;
				case 0x103:
					fRead.Seek(16, SEEK_CUR);
					for (uint32 i = 0; i < 4; i++) {
						fMeta.camera_multipliers[i] = fRead.Next<uint16>();
					}
					break;
			}
			if (ver97 >> 8 == 2) {
				if (ver97 != 0x205)
					fRead.Seek(280, SEEK_CUR);
				fRead(buf97, sizeof(buf97));
			}
		}
		if (tag.tag == 0xa7 && ver97 >> 8 == 2) {
			ci = xlat[0][serial & 0xff];
			cj = xlat[1][fRead.Next<uint8>() ^ fRead.Next<uint8>()
				^ fRead.Next<uint8>() ^ fRead.Next<uint8>()];
			ck = 0x60;
			for (uint32 i = 0; i < 324; i++) {
				buf97[i] ^= (cj += ci * ck++);
			}
			for (uint32 i = 0; i < 4; i++) {
				uint16* data = (uint16*)(buf97
					+ (ver97 == 0x205 ? 14 : 6) + i * 2);
 
				if (fRead.IsSwapping()) {
					fMeta.camera_multipliers[i ^ (i >> 1)]
						= __swap_int16(*data);
				} else {
					fMeta.camera_multipliers[i ^ (i >> 1)] = *data;
				}
			}
		}
		if (tag.tag == 0x200 && tag.length == 4) {
			fMeta.black = (fRead.Next<uint16>() + fRead.Next<uint16>()
				+ fRead.Next<uint16>() + fRead.Next<uint16>()) / 4;
		}
		if (tag.tag == 0x201 && tag.length == 4)
			goto get2_rggb;
		if (tag.tag == 0x401 && tag.length == 4) {
			fMeta.black = (fRead.Next<uint32>() + fRead.Next<uint32>()
				+ fRead.Next<uint32>() + fRead.Next<uint32>()) / 4;
		}
		if (tag.tag == 0xe01) {
			// Nikon Capture Note
			bool previousSwap = fRead.IsSwapping();
			fRead.SetSwap(B_HOST_IS_BENDIAN != 0);
				// this chunk is always in little endian
 
			off_t offset = 22;
			fRead.Seek(offset, SEEK_CUR);
 
			int32 i = 0;
 
			for (; offset + 22 < tag.length; offset += 22 + i) {
				tag.tag = fRead.Next<uint32>();
				fRead.Seek(14, SEEK_CUR);
				i = fRead.Next<uint32>() - 4;
				if (tag.tag == 0x76a43207)
					fMeta.flip = fRead.Next<uint16>();
				else
					fRead.Seek(i, SEEK_CUR);
			}
 
			fRead.SetSwap(previousSwap);
		}
		if (tag.tag == 0xe80 && tag.length == 256 && tag.type == 7) {
			fRead.Seek(48, SEEK_CUR);
			fMeta.camera_multipliers[0]
				= fRead.Next<uint16>() * 508 * 1.078 / 0x10000;
			fMeta.camera_multipliers[2]
				= fRead.Next<uint16>() * 382 * 1.173 / 0x10000;
		}
		if (tag.tag == 0xf00 && tag.type == 7) {
			if (tag.length == 614)
				fRead.Seek(176, SEEK_CUR);
			else if (tag.length == 734 || tag.length == 1502)
				fRead.Seek(148, SEEK_CUR);
			else
				goto next;
			goto get2_256;
		}
		if (tag.tag == 0x1011 && tag.length == 9 && fUseCameraWhiteBalance) {
			for (uint32 i = 0; i < 3; i++) {
				for (uint32 j = 0; j < 3; j++) {
					fMeta.rgb_camera[i][j] = fRead.Next<int16>() / 256.0;
				}
			}
			fRawColor = fMeta.rgb_camera[0][0] < 1;
		}
		if (tag.tag == 0x1012 && tag.length == 4) {
			fMeta.black = 0;
			for (uint32 i = 0; i < 4; i++) {
				fMeta.black += fRead.Next<uint16>() << 2;
			}
		}
		if (tag.tag == 0x1017)
			fMeta.camera_multipliers[0] = fRead.Next<uint16>() / 256.0;
		if (tag.tag == 0x1018)
			fMeta.camera_multipliers[2] = fRead.Next<uint16>() / 256.0;
 
		if (tag.tag == 0x2011 && tag.length == 2) {
get2_256:
			bool previousSwap = fRead.IsSwapping();
			fRead.SetSwap(B_HOST_IS_LENDIAN != 0);
				// this chunk is always in big endian
 
			fMeta.camera_multipliers[0] = fRead.Next<uint16>() / 256.0;
			fMeta.camera_multipliers[2] = fRead.Next<uint16>() / 256.0;
 
			fRead.SetSwap(previousSwap);
		}
 
		if (tag.tag == 0x2020)
			_ParseThumbTag(baseOffset, 257, 258);
		if (tag.tag == 0xb028) {
			fRead.Seek(fRead.Next<uint32>(), SEEK_SET);
			_ParseThumbTag(baseOffset, 136, 137);
		}
 
		if (tag.tag == 0x4001) {
			{
				off_t offset = tag.length == 582 ? 50 : tag.length == 653
					? 68 : 126;
				fRead.Seek(offset, SEEK_CUR);
			}
get2_rggb:
			for (uint32 i = 0; i < 4; i++) {
				fMeta.camera_multipliers[i ^ (i >> 1)] = fRead.Next<uint16>();
			}
		}
 
next:
    	fRead.Seek(nextOffset, SEEK_SET);
	}
 
quit:
	fRead.SetSwap(originalSwap);
}
 
 
void
DCRaw::_ParseEXIF(off_t baseOffset)
{
	bool kodak = !strncmp(fMeta.manufacturer, "EASTMAN", 7);
 
	uint16 entries;
	fRead(entries);
 
	while (entries--) {
		off_t nextOffset;
		tiff_tag tag;
		_ParseTIFFTag(baseOffset, tag, nextOffset);
		TAG("EXIF tag %u (type %u, length %lu)\n", tag.tag, tag.type,
			tag.length);
 
		switch (tag.tag) {
#if 0
			default:
				printf("  unhandled EXIF tag %u\n", tag.tag);
				break;
#endif
			case 33434:
				fMeta.shutter = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
			case 33437:
				fMeta.aperture = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
			case 34855:
				fMeta.iso_speed = fRead.Next<uint16>();
				break;
			case 36867:
			case 36868:
				fMeta.timestamp = _ParseTIFFTimestamp(false);
				break;
			case 37377:
			{
				double expo;
				if ((expo = -fRead.NextDouble(TIFF_FRACTION_TYPE)) < 128)
					fMeta.shutter = pow(2, expo);
				break;
			}
			case 37378:
				fMeta.aperture
					= pow(2, fRead.NextDouble(TIFF_FRACTION_TYPE) / 2);
				break;
			case 37386:
				fMeta.focal_length = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
			case 37500:
				_ParseManufacturerTag(baseOffset);
				break;
			case 40962:
				if (kodak)
					_Raw().width = fRead.Next<uint32>();
				break;
			case 40963:
				if (kodak)
					_Raw().height = fRead.Next<uint32>();
				break;
			case 41730:
				if (fRead.Next<uint32>() == 0x20002) {
					fEXIFFilters = 0;
					for (uint32 c = 0; c < 8; c += 2) {
						fEXIFFilters |= fRead.Next<uint8>() * 0x01010101 << c;
					}
				}
				break;
		}
 
		fRead.Seek(nextOffset, SEEK_SET);
	}
}
 
 
void
DCRaw::_ParseLinearTable(uint32 length)
{
	if (length > 0x1000)
		length = 0x1000;
 
	fRead.NextShorts(fCurve, length);
 
	for (uint32 i = length; i < 0x1000; i++) {
		fCurve[i] = fCurve[i - 1];
	}
 
	fMeta.maximum = fCurve[0xfff];
}
 
 
/*!	This (lengthy) method contains fixes for the values in the image data to
	be able to actually read the image data correctly.
*/
void
DCRaw::_FixupValues()
{
	// PENTAX and SAMSUNG section
	// (Samsung sells rebranded Pentax cameras)
 
	if (_IsPentax() || _IsSamsung()) {
		if (fInputWidth == 3936 && fInputHeight == 2624) {
			// Pentax K10D and Samsumg GX10
			fInputWidth = 3896;
			fInputHeight = 2616;
		}
	}
 
	// CANON
 
	if (_IsCanon()) {
		bool isCR2 = false;
		if (strstr(fMeta.model, "EOS D2000C")) {
			fFilters = 0x61616161;
			fMeta.black = fCurve[200];
		}
 
		switch (_Raw().width) {
			case 2144:
				fInputHeight = 1550;
				fInputWidth = 2088;
				fTopMargin = 8;
				fLeftMargin = 4;
				if (!strcmp(fMeta.model, "PowerShot G1")) {
					fColors = 4;
					fFilters = 0xb4b4b4b4;
				}
				break;
 
			case 2224:
				fInputHeight = 1448;
				fInputWidth = 2176;
				fTopMargin = 6;
				fLeftMargin = 48;
				break;
 
			case 2376:
				fInputHeight = 1720;
				fInputWidth = 2312;
				fTopMargin = 6;
				fLeftMargin = 12;
				break;
 
			case 2672:
				fInputHeight = 1960;
				fInputWidth = 2616;
				fTopMargin = 6;
				fLeftMargin = 12;
				break;
 
			case 3152:
				fInputHeight = 2056;
				fInputWidth = 3088;
				fTopMargin = 12;
				fLeftMargin = 64;
				if (fUniqueID == 0x80000170)
					_AdobeCoefficients("Canon", "EOS 300D");
				fMeta.maximum = 0xfa0;
				break;
 
			case 3160:
				fInputHeight = 2328;
				fInputWidth = 3112;
				fTopMargin = 12;
				fLeftMargin = 44;
				break;
 
			case 3344:
				fInputHeight = 2472;
				fInputWidth = 3288;
				fTopMargin = 6;
				fLeftMargin = 4;
				break;
 
			case 3516:
				fTopMargin = 14;
				fLeftMargin = 42;
				if (fUniqueID == 0x80000189)
					_AdobeCoefficients("Canon", "EOS 350D");
				isCR2 = true;
				break;
 
			case 3596:
				fTopMargin = 12;
				fLeftMargin = 74;
				isCR2 = true;
				break;
 
			case 3948:
				fTopMargin = 18;
				fLeftMargin = 42;
				fInputHeight -= 2;
				if (fUniqueID == 0x80000236)
					_AdobeCoefficients("Canon", "EOS 400D");
				isCR2 = true;
				break;
 
			case 3984:
				fTopMargin  = 20;
				fLeftMargin = 76;
				fInputHeight -= 2;
				fMeta.maximum = 0x3bb0;
				isCR2 = true;
				break;
 
			case 4476:
				fTopMargin  = 34;
				fLeftMargin = 90;
				fMeta.maximum = 0xe6c;
				isCR2 = true;
				break;
 
			case 5108:
				fTopMargin  = 13;
				fLeftMargin = 98;
				fMeta.maximum = 0xe80;
				isCR2 = true;
				break;
		}
 
		if (isCR2) {
			fInputHeight -= fTopMargin;
			fInputWidth -= fLeftMargin;
		}
	}
 
	// Olympus
 
	if (_IsOlympus()) {
		if (!strcmp(fMeta.model,"E-300") || !strcmp(fMeta.model,"E-500")) {
			fInputWidth -= 20;
			fMeta.maximum = 0xfc30;
			//if (load_raw == &CLASS unpacked_load_raw) black = 0;
		}
	}
}
 
 
//	#pragma mark - Image Conversion
 
 
void
DCRaw::_ScaleColors()
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Scale Colors", 5, fProgressData);
 
	int c, val, sum[8];
	uint32 row, col, x, y;
	double dsum[8], dmin, dmax;
	float scale_mul[4];
 
	if (fUseCameraWhiteBalance && fMeta.camera_multipliers[0] != -1) {
		memset(sum, 0, sizeof(sum));
		for (row = 0; row < 8; row++) {
			for (col = 0; col < 8; col++) {
				c = FC(row, col);
				if ((val = fWhite[row][col] - fMeta.black) > 0)
					sum[c] += val;
				sum[c + 4]++;
			}
		}
 
		if (sum[0] && sum[1] && sum[2] && sum[3]) {
			for (int c = 0; c < 4; c++) {
				fMeta.pre_multipliers[c] = (float)sum[c+4] / sum[c];
			}
		} else if (fMeta.camera_multipliers[0] && fMeta.camera_multipliers[2]) {
			memcpy(fMeta.pre_multipliers, fMeta.camera_multipliers,
				sizeof(fMeta.pre_multipliers));
		} else
			fprintf(stderr, "Cannot use camera white balance.\n");
	} else if (fUseAutoWhiteBalance) {
		memset(dsum, 0, sizeof(dsum));
		for (row = 0; row < fOutputHeight - 7; row += 8) {
			for (col = 0; col < fOutputWidth - 7; col += 8) {
				memset(sum, 0, sizeof(sum));
				for (y = row; y < row + 8; y++) {
					for (x = col; x < col + 8; x++) {
						for (int c = 0; c < 4; c++) {
							val = fImageData[y * fOutputWidth + x][c];
							if (!val)
								continue;
							if (val > fMeta.maximum - 25)
								goto skip_block;
							val -= fMeta.black;
							if (val < 0)
								val = 0;
							sum[c] += val;
							sum[c+4]++;
						}
					}
				}
 
				for (c=0; c < 8; c++) {
					dsum[c] += sum[c];
				}
 
			skip_block:
				continue;
			}
		}
		for (int c = 0; c < 4; c++) {
			if (dsum[c])
				fMeta.pre_multipliers[c] = dsum[c + 4] / dsum[c];
		}
	}
 
 
	if (fUserMultipliers[0]) {
		memcpy(fMeta.pre_multipliers, fUserMultipliers,
			sizeof(fMeta.pre_multipliers));
	}
	if (fMeta.pre_multipliers[3] == 0)
		fMeta.pre_multipliers[3] = fColors < 4 ? fMeta.pre_multipliers[1] : 1;
 
#if 0
	int dblack = fMeta.black;
#endif
	if (fThreshold)
		_WaveletDenoise();
 
	fMeta.maximum -= fMeta.black;
	for (dmin = DBL_MAX, dmax = c = 0; c < 4; c++) {
		if (dmin > fMeta.pre_multipliers[c])
			dmin = fMeta.pre_multipliers[c];
		if (dmax < fMeta.pre_multipliers[c])
			dmax = fMeta.pre_multipliers[c];
	}
 
	if (!fHighlight)
		dmax = dmin;
 
	for (int c = 0; c < 4; c++) {
		scale_mul[c] = (fMeta.pre_multipliers[c] /= dmax) * 65535.0
			/ fMeta.maximum;
	}
 
#if 0
	if (1/*verbose*/) {
		fprintf(stderr, "Scaling with black %d, multipliers", dblack);
		for (int c = 0; c < 4; c++) {
			fprintf(stderr, " %f", fMeta.pre_multipliers[c]);
		}
		fputc('\n', stderr);
	}
#endif
 
	for (row = 0; row < fOutputHeight; row++) {
		for (col = 0; col < fOutputWidth; col++) {
			for (int c = 0; c < 4; c++) {
				val = fImageData[row * fOutputWidth + col][c];
				if (!val)
					continue;
				val -= fMeta.black;
				val = int(val * scale_mul[c]);
				fImageData[row * fOutputWidth + col][c] = CLIP(val);
			}
		}
	}
}
 
 
void
DCRaw::_WaveletDenoise()
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Wavelet Denoise", 8, fProgressData);
 
	float *fimg, *temp, mul[2], avg, diff;
	int32 scale = 1, sh, c, i, j, k, m, row, col, size, numColors, dim = 0;
	int32 wlast;
	ushort *window[4];
	// Daubechies 9-tap/7-tap filter
	static const float wlet[] =	{ 1.149604398, -1.586134342,
		-0.05298011854, 0.8829110762, 0.4435068522 };
 
	while ((fMeta.maximum << scale) < 0x10000) {
		scale++;
	}
	fMeta.maximum <<= fMeta.maximum << --scale;
	fMeta.black <<= scale;
 
	while ((1UL << dim) < fOutputWidth || (1UL << dim) < fOutputHeight) {
		dim++;
	}
 
	fimg = (float *)calloc((1UL << dim*2) + (1UL << dim) + 2, sizeof *fimg);
	if (fimg == NULL)
		return;
 
	temp = fimg + (1 << dim * 2) + 1;
	numColors = fColors;
	if (numColors == 3 && fFilters)
		numColors++;
 
	for (c = 0; c < numColors; c++) {
		// denoise R,G1,B,G3 individually
		for (row = 0; row < (int32)fOutputHeight; row++) {
			for (col = 0; col < (int32)fOutputWidth; col++) {
				fimg[(row << dim) + col]
					= fImageData[row * fOutputWidth + col][c] << scale;
			}
		}
		for (size = 1UL << dim; size > 1; size >>= 1) {
			for (sh = 0; sh <= dim; sh += dim) {
				for (i = 0; i < size; i++) {
					for (j = 0; j < size; j++) {
						temp[j] = fimg[(i << (dim - sh)) + (j << sh)];
					}
					for (k = 1; k < 5; k += 2) {
						temp[size] = temp[size - 2];
						for (m = 1; m < size; m += 2) {
							temp[m] += wlet[k] * (temp[m - 1] + temp[m + 1]);
						}
						temp[-1] = temp[1];
						for (m = 0; m < size; m += 2) {
							temp[m] += wlet[k + 1]
								* (temp[m - 1] + temp[m + 1]);
						}
					}
					for (m = 0; m < size; m++) {
						temp[m] *= (m & 1) ? 1 / wlet[0] : wlet[0];
					}
					for (j = k = 0; j < size; j++, k += 2) {
						if (k == size)
							k = 1;
						fimg[(i << (dim - sh)) + (j << sh)] = temp[k];
					}
				}
			}
		}
 
		for (i = 0; i < (1 << dim * 2); i++) {
			if (fimg[i] < -fThreshold)
				fimg[i] += fThreshold;
			else if (fimg[i] > fThreshold)
				fimg[i] -= fThreshold;
			else
				fimg[i] = 0;
		}
 
		for (size = 2; size <= (1 << dim); size <<= 1) {
			for (sh = dim; sh >= 0; sh -= dim) {
				for (i = 0; i < size; i++) {
					for (j = k = 0; j < size; j++, k += 2) {
						if (k == size)
							k = 1;
						temp[k] = fimg[(i << (dim - sh)) + (j << sh)];
					}
					for (m = 0; m < size; m++) {
						temp[m] *= (m & 1) ? wlet[0] : 1 / wlet[0];
					}
					for (k = 3; k > 0; k -= 2) {
						temp[-1] = temp[1];
						for (m = 0; m < size; m += 2) {
							temp[m] -= wlet[k + 1]
								* (temp[m - 1] + temp[m + 1]);
						}
						temp[size] = temp[size - 2];
						for (m = 1; m < size; m += 2) {
							temp[m] -= wlet[k] * (temp[m - 1] + temp[m + 1]);
						}
					}
					for (j = 0; j < size; j++) {
						fimg[(i << (dim - sh)) + (j << sh)] = temp[j];
					}
				}
			}
		}
 
		for (row = 0; row < (int32)fOutputHeight; row++) {
			for (col = 0; col < (int32)fOutputWidth; col++) {
				fImageData[row * fOutputWidth + col][c]
					= (uint16)CLIP(fimg[(row << dim) + col] + 0.5);
			}
		}
	}
 
	if (fFilters && fColors == 3) {
		// pull G1 and G3 closer together
		for (row = 0; row < 2; row++) {
			mul[row] = 0.125 * fMeta.pre_multipliers[FC(row + 1, 0) | 1]
				/ fMeta.pre_multipliers[FC(row, 0) | 1];
		}
		for (i = 0; i < 4; i++) {
			window[i] = (ushort *)fimg + fInputWidth * i;
		}
		for (wlast = -1, row = 1; row < (int32)fInputHeight - 1; row++) {
			while (wlast < (int32)row + 1) {
				for (wlast++, i = 0; i < 4; i++) {
					window[(i + 3) & 3] = window[i];
				}
				for (col = FC(wlast, 1) & 1; col < (int32)fInputWidth;
						col += 2) {
					window[2][col] = _Bayer(col, wlast);
				}
			}
 
			for (col = (FC(row, 0) & 1) + 1; col < (int32)fInputWidth - 1;
					col += 2) {
				avg = ( window[0][col - 1] + window[0][col + 1]
					+ window[2][col - 1] + window[2][col + 1] - fMeta.black * 4)
					* mul[row & 1] + (window[1][col] - fMeta.black) * 0.5
					+ fMeta.black;
				diff = _Bayer(col, row) - avg;
 
				if (diff < -fThreshold / M_SQRT2)
					diff += fThreshold / M_SQRT2;
				else if (diff > fThreshold / M_SQRT2)
					diff -= fThreshold / M_SQRT2;
				else
					diff = 0;
				_Bayer(col, row) = (uint16)CLIP(avg + diff + 0.5);
			}
		}
	}
 
	free(fimg);
}
 
 
void
DCRaw::_PreInterpolate()
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Pre-Interpolate", 10, fProgressData);
 
	uint32 row, col;
 
	if (fShrink) {
		if (fHalfSize) {
			fInputHeight = fOutputHeight;
			fInputWidth = fOutputWidth;
			fFilters = 0;
		} else {
			uint16 (*data)[4] = (uint16 (*)[4])calloc(fInputHeight
				* fInputWidth, sizeof(*data));
			if (data == NULL)
				throw (status_t)B_NO_MEMORY;
 
			for (row = 0; row < fInputHeight; row++) {
				for (col = 0; col < fInputWidth; col++) {
					data[row * fInputWidth + col][FC(row, col)]
						= _Bayer(col, row);
				}
			}
 
			free(fImageData);
			fImageData = data;
			fShrink = 0;
		}
	}
 
	if (fFilters && fColors == 3) {
//		if ((mix_green = four_color_rgb))
//			fColors++;
//		else
		{
			for (row = FC(1, 0) >> 1; row < fInputHeight; row += 2) {
				for (col = FC(row, 1) & 1; col < fInputWidth; col += 2) {
					fImageData[row * fInputWidth + col][1]
						= fImageData[row * fInputWidth + col][3];
				}
			}
			fFilters &= ~((fFilters & 0x55555555) << 1);
		}
	}
}
 
 
void
DCRaw::_CameraToCIELab(ushort cam[4], float lab[3])
{
	if (cam == NULL) {
		for (uint32 i = 0; i < 0x10000; i++) {
			float r = i / 65535.0;
			cbrt[i] = r > 0.008856 ? pow(r, 1 / 3.0) : 7.787 * r + 16 / 116.0;
		}
		for (uint32 i = 0; i < 3; i++) {
			for (uint32 j = 0; j < fColors; j++) {
				xyz_cam[i][j] = 0;
				for (uint32 k = 0; k < 3; k++) {
					xyz_cam[i][j] += xyz_rgb[i][k] * fMeta.rgb_camera[k][j]
						/ kD65White[i];
				}
			}
		}
	} else {
		float xyz[3];
		xyz[0] = xyz[1] = xyz[2] = 0.5;
		for (uint32 c = 0; c < fColors; c++) {
			xyz[0] += xyz_cam[0][c] * cam[c];
			xyz[1] += xyz_cam[1][c] * cam[c];
			xyz[2] += xyz_cam[2][c] * cam[c];
		}
		xyz[0] = cbrt[CLIP((int) xyz[0])];
		xyz[1] = cbrt[CLIP((int) xyz[1])];
		xyz[2] = cbrt[CLIP((int) xyz[2])];
		lab[0] = 116 * xyz[1] - 16;
		lab[1] = 500 * (xyz[0] - xyz[1]);
		lab[2] = 200 * (xyz[1] - xyz[2]);
	}
}
 
 
void
DCRaw::_CameraXYZCoefficients(double cameraXYZ[4][3])
{
	double cam_rgb[4][3], inverse[4][3], num;
	uint32 i, j, k;
 
	// Multiply out XYZ colorspace
	for (i = 0; i < fColors; i++)	{
		for (j = 0; j < 3; j++) {
			for (cam_rgb[i][j] = k = 0; k < 3; k++) {
				cam_rgb[i][j] += cameraXYZ[i][k] * xyz_rgb[k][j];
			}
		}
	}
 
	// Normalize cam_rgb so that cam_rgb * (1,1,1) is (1,1,1,1)
	for (i = 0; i < fColors; i++) {
		for (num = j = 0; j < 3; j++) {
			num += cam_rgb[i][j];
		}
		for (j = 0; j < 3; j++) {
			cam_rgb[i][j] /= num;
		}
		fMeta.pre_multipliers[i] = 1 / num;
	}
 
	_PseudoInverse(cam_rgb, inverse, fColors);
 
	fRawColor = false;
	for (i = 0; i < 3; i++) {
		for (j=0; j < fColors; j++) {
			fMeta.rgb_camera[i][j] = inverse[j][i];
		}
	}
}
 
 
/*!	Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
*/
void
DCRaw::_AdobeCoefficients(const char *make, const char *model)
{
	static const struct {
		const char *prefix;
		short black, trans[12];
	} table[] = {
		{ "Canon EOS D2000", 0,
			{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 }},
		{ "Canon EOS D6000", 0,
			{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 }},
		{ "Canon EOS D30", 0,
			{ 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 }},
		{ "Canon EOS D60", 0,
			{ 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 }},
		{ "Canon EOS 5D", 0,
			{ 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 }},
		{ "Canon EOS 20Da", 0,
			{ 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 }},
		{ "Canon EOS 20D", 0,
			{ 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 }},
		{ "Canon EOS 30D", 0,
			{ 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 }},
		{ "Canon EOS 350D", 0,
			{ 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 }},
		{ "Canon EOS 400D", 0,
			{ 7054,-1501,-990,-8156,15544,2812,-1278,1414,7796 }},
		{ "Canon EOS-1Ds Mark II", 0,
			{ 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 }},
		{ "Canon EOS-1D Mark II N", 0,
			{ 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 }},
		{ "Canon EOS-1D Mark II", 0,
			{ 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 }},
		{ "Canon EOS-1DS", 0,
			{ 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 }},
		{ "Canon EOS-1D", 0,
			{ 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 }},
		{ "Canon EOS", 0,
			{ 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 }},
		{ "Canon PowerShot A50", 0,
			{ -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 }},
		{ "Canon PowerShot A5", 0,
			{ -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 }},
		{ "Canon PowerShot G1", 0,
			{ -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 }},
		{ "Canon PowerShot G2", 0,
			{ 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 }},
		{ "Canon PowerShot G3", 0,
			{ 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 }},
		{ "Canon PowerShot G5", 0,
			{ 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 }},
		{ "Canon PowerShot G6", 0,
			{ 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 }},
		{ "Canon PowerShot Pro1", 0,
			{ 10062,-3522,-999,-7643,15117,2730,-765,817,7323 }},
		{ "Canon PowerShot Pro70", 34,
			{ -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 }},
		{ "Canon PowerShot Pro90", 0,
			{ -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 }},
		{ "Canon PowerShot S30", 0,
			{ 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 }},
		{ "Canon PowerShot S40", 0,
			{ 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 }},
		{ "Canon PowerShot S45", 0,
			{ 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 }},
		{ "Canon PowerShot S50", 0,
			{ 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 }},
		{ "Canon PowerShot S60", 0,
			{ 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 }},
		{ "Canon PowerShot S70", 0,
			{ 9976,-3810,-832,-7115,14463,2906,-901,989,7889 }},
		{ "Canon PowerShot A610", 0, /* DJC */
			{ 15591,-6402,-1592,-5365,13198,2168,-1300,1824,5075 }},
		{ "Canon PowerShot A620", 0, /* DJC */
			{ 15265,-6193,-1558,-4125,12116,2010,-888,1639,5220 }},
		{ "Canon PowerShot S3 IS", 0, /* DJC */
			{ 14062,-5199,-1446,-4712,12470,2243,-1286,2028,4836 }},
		{ "Contax N Digital", 0,
			{ 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 }},
		{ "EPSON R-D1", 0,
			{ 6827,-1878,-732,-8429,16012,2564,-704,592,7145 }},
		{ "FUJIFILM FinePix E550", 0,
			{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 }},
		{ "FUJIFILM FinePix E900", 0,
			{ 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 }},
		{ "FUJIFILM FinePix F8", 0,
			{ 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 }},
		{ "FUJIFILM FinePix F7", 0,
			{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 }},
		{ "FUJIFILM FinePix S20Pro", 0,
			{ 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 }},
		{ "FUJIFILM FinePix S2Pro", 128,
			{ 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 }},
		{ "FUJIFILM FinePix S3Pro", 0,
			{ 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 }},
		{ "FUJIFILM FinePix S5000", 0,
			{ 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 }},
		{ "FUJIFILM FinePix S5100", 0,
			{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 }},
		{ "FUJIFILM FinePix S5500", 0,
			{ 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 }},
		{ "FUJIFILM FinePix S5200", 0,
			{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 }},
		{ "FUJIFILM FinePix S5600", 0,
			{ 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 }},
		{ "FUJIFILM FinePix S6", 0,
			{ 12628,-4887,-1401,-6861,14996,1962,-2198,2782,7091 }},
		{ "FUJIFILM FinePix S7000", 0,
			{ 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 }},
		{ "FUJIFILM FinePix S9000", 0,
			{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 }},
		{ "FUJIFILM FinePix S9500", 0,
			{ 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 }},
		{ "FUJIFILM FinePix S9100", 0,
			{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 }},
		{ "FUJIFILM FinePix S9600", 0,
			{ 12343,-4515,-1285,-7165,14899,2435,-1895,2496,8800 }},
		{ "Imacon Ixpress", 0,	/* DJC */
			{ 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 }},
		{ "KODAK NC2000", 0,	/* DJC */
			{ 16475,-6903,-1218,-851,10375,477,2505,-7,1020 }},
		{ "Kodak DCS315C", 8,
			{ 17523,-4827,-2510,756,8546,-137,6113,1649,2250 }},
		{ "Kodak DCS330C", 8,
			{ 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 }},
		{ "KODAK DCS420", 0,
			{ 10868,-1852,-644,-1537,11083,484,2343,628,2216 }},
		{ "KODAK DCS460", 0,
			{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 }},
		{ "KODAK EOSDCS1", 0,
			{ 10592,-2206,-967,-1944,11685,230,2206,670,1273 }},
		{ "KODAK EOSDCS3B", 0,
			{ 9898,-2700,-940,-2478,12219,206,1985,634,1031 }},
		{ "Kodak DCS520C", 180,
			{ 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 }},
		{ "Kodak DCS560C", 188,
			{ 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 }},
		{ "Kodak DCS620C", 180,
			{ 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 }},
		{ "Kodak DCS620X", 185,
			{ 13095,-6231,154,12221,-21,-2137,895,4602,2258 }},
		{ "Kodak DCS660C", 214,
			{ 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 }},
		{ "Kodak DCS720X", 0,
			{ 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 }},
		{ "Kodak DCS760C", 0,
			{ 16623,-6309,-1411,-4344,13923,323,2285,274,2926 }},
		{ "Kodak DCS Pro SLR", 0,
			{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 }},
		{ "Kodak DCS Pro 14nx", 0,
			{ 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 }},
		{ "Kodak DCS Pro 14", 0,
			{ 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 }},
		{ "Kodak ProBack645", 0,
			{ 16414,-6060,-1470,-3555,13037,473,2545,122,4948 }},
		{ "Kodak ProBack", 0,
			{ 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 }},
		{ "KODAK P712", 0,
			{ 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 }},
		{ "KODAK P850", 0,
			{ 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 }},
		{ "KODAK P880", 0,
			{ 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 }},
		{ "Leaf CMost", 0,
			{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 }},
		{ "Leaf Valeo 6", 0,
			{ 3952,2189,449,-6701,14585,2275,-4536,7349,6536 }},
		{ "Leaf Aptus 65", 0,
			{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 }},
		{ "Leaf Aptus 75", 0,
			{ 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 }},
		{ "Leaf", 0,
			{ 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 }},
		{ "Micron 2010", 110,	/* DJC */
			{ 16695,-3761,-2151,155,9682,163,3433,951,4904 }},
		{ "Minolta DiMAGE 5", 0,
			{ 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 }},
		{ "Minolta DiMAGE 7Hi", 0,
			{ 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 }},
		{ "Minolta DiMAGE 7", 0,
			{ 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 }},
		{ "Minolta DiMAGE A1", 0,
			{ 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 }},
		{ "MINOLTA DiMAGE A200", 0,
			{ 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 }},
		{ "Minolta DiMAGE A2", 0,
			{ 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 }},
		{ "Minolta DiMAGE Z2", 0,	/* DJC */
			{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 }},
		{ "MINOLTA DYNAX 5", 0,
			{ 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 }},
		{ "MINOLTA DYNAX 7", 0,
			{ 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 }},
		{ "NIKON D100", 0,
			{ 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 }},
		{ "NIKON D1H", 0,
			{ 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 }},
		{ "NIKON D1X", 0,
			{ 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 }},
		{ "NIKON D1", 0,	/* multiplied by 2.218750, 1.0, 1.148438 */
			{ 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 }},
		{ "NIKON D2H", 0,
			{ 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 }},
		{ "NIKON D2X", 0,
			{ 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 }},
		{ "NIKON D40", 0,
			{ 6992,-1668,-806,-8138,15748,2543,-874,850,7897 }},
		{ "NIKON D50", 0,
			{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 }},
		{ "NIKON D70", 0,
			{ 7732,-2422,-789,-8238,15884,2498,-859,783,7330 }},
		{ "NIKON D80", 0,
			{ 8629,-2410,-883,-9055,16940,2171,-1490,1363,8520 }},
		{ "NIKON D200", 0,
			{ 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 }},
		{ "NIKON E950", 0,		/* DJC */
			{ -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 }},
		{ "NIKON E995", 0,	/* copied from E5000 */
			{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 }},
		{ "NIKON E2500", 0,
			{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 }},
		{ "NIKON E4300", 0, /* copied from Minolta DiMAGE Z2 */
			{ 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 }},
		{ "NIKON E4500", 0,
			{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 }},
		{ "NIKON E5000", 0,
			{ -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 }},
		{ "NIKON E5400", 0,
			{ 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 }},
		{ "NIKON E5700", 0,
			{ -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 }},
		{ "NIKON E8400", 0,
			{ 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 }},
		{ "NIKON E8700", 0,
			{ 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 }},
		{ "NIKON E8800", 0,
			{ 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 }},
		{ "OLYMPUS C5050", 0,
			{ 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 }},
		{ "OLYMPUS C5060", 0,
			{ 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 }},
		{ "OLYMPUS C7070", 0,
			{ 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 }},
		{ "OLYMPUS C70", 0,
			{ 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 }},
		{ "OLYMPUS C80", 0,
			{ 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 }},
		{ "OLYMPUS E-10", 0,
			{ 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 }},
		{ "OLYMPUS E-1", 0,
			{ 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 }},
		{ "OLYMPUS E-20", 0,
			{ 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 }},
		{ "OLYMPUS E-300", 0,
			{ 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 }},
		{ "OLYMPUS E-330", 0,
			{ 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 }},
		{ "OLYMPUS E-400", 0,
			{ 6169,-1483,-21,-7107,14761,2536,-2904,3580,8568 }},
		{ "OLYMPUS E-500", 0,
			{ 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 }},
		{ "OLYMPUS SP350", 0,
			{ 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 }},
		{ "OLYMPUS SP3", 0,
			{ 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 }},
		{ "OLYMPUS SP500UZ", 0,
			{ 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 }},
		{ "OLYMPUS SP510UZ", 0,
			{ 10593,-3607,-1010,-5881,13127,3084,-1200,1805,6721 }},
		{ "PENTAX *ist DL2", 0,
			{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 }},
		{ "PENTAX *ist DL", 0,
			{ 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 }},
		{ "PENTAX *ist DS2", 0,
			{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 }},
		{ "PENTAX *ist DS", 0,
			{ 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 }},
		{ "PENTAX *ist D", 0,
			{ 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 }},
		{ "PENTAX K10D", 0,
			{ 9566,-2863,-803,-7170,15172,2112,-818,803,9705 }},
		{ "PENTAX K1", 0,
			{ 11095,-3157,-1324,-8377,15834,2720,-1108,947,11688 }},
		{ "Panasonic DMC-FZ30", 0,
			{ 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 }},
		{ "Panasonic DMC-FZ50", 0,	/* aka "LEICA V-LUX1" */
			{ 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 }},
		{ "Panasonic DMC-L1", 0,	/* aka "LEICA DIGILUX 3" */
			{ 8054,-1885,-1025,-8349,16367,2040,-2805,3542,7629 }},
		{ "Panasonic DMC-LC1", 0,	/* aka "LEICA DIGILUX 2" */
			{ 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 }},
		{ "Panasonic DMC-LX1", 0,	/* aka "LEICA D-LUX2" */
			{ 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 }},
		{ "Panasonic DMC-LX2", 0,	/* aka "LEICA D-LUX3" */
			{ 8048,-2810,-623,-6450,13519,3272,-1700,2146,7049 }},
		{ "SAMSUNG GX-1", 0,
			{ 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 }},
		{ "Sinar", 0,		/* DJC */
			{ 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 }},
		{ "SONY DSC-F828", 491,
			{ 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 }},
		{ "SONY DSC-R1", 512,
			{ 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 }},
		{ "SONY DSC-V3", 0,
			{ 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 }},
		{ "SONY DSLR-A100", 0,
			{ 9437,-2811,-774,-8405,16215,2290,-710,596,7181 }}
	};
	double cameraXYZ[4][3];
 
	for (uint32 i = 0; i < sizeof table / sizeof *table; i++) {
		if (!strncasecmp(model, table[i].prefix, strlen(table[i].prefix))) {
			if (table[i].black)
				fMeta.black = table[i].black;
			for (uint32 j = 0; j < 12; j++) {
				((double**)cameraXYZ)[0][j] = table[i].trans[j] / 10000.0;
			}
			_CameraXYZCoefficients(cameraXYZ);
			break;
		}
	}
}
 
 
void
DCRaw::_BorderInterpolate(uint32 border)
{
	uint32 row, col, y, x, f, c, sum[8];
 
	for (row = 0; row < fInputHeight; row++) {
		for (col = 0; col < fInputWidth; col++) {
			if (col == border && row >= border && row < fInputHeight - border)
				col = fInputWidth - border;
 
			memset(sum, 0, sizeof(sum));
 
			for (y = row - 1; y != row + 2; y++) {
				for (x = col - 1; x != col + 2; x++) {
					if (y < fInputHeight && x < fInputWidth) {
						f = _FilterCoefficient(x, y);
						sum[f] += fImageData[y * fInputWidth + x][f];
						sum[f + 4]++;
					}
				}
			}
 
			f = _FilterCoefficient(col, row);
 
			for (c = 0; c < fColors; c++) {
				if (c != f && sum[c + 4]) {
					fImageData[row * fInputWidth + col][c]
						= sum[c] / sum[c + 4];
				}
			}
		}
	}
}
 
 
/*!	Adaptive Homogeneity-Directed interpolation is based on
	the work of Keigo Hirakawa, Thomas Parks, and Paul Lee.
*/
void
DCRaw::_AHDInterpolate()
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Interpolate", 20, fProgressData);
 
#define TS 256		/* Tile Size */
 
	int i, j, tr, tc, fc, c, d, val, hm[2];
	uint32 top, left, row, col;
	ushort (*pix)[4], (*rix)[3];
	static const int dir[4] = { -1, 1, -TS, TS };
	unsigned ldiff[2][4], abdiff[2][4], leps, abeps;
	float flab[3];
	ushort (*rgb)[TS][TS][3];
	short (*lab)[TS][TS][3];
	char (*homo)[TS][TS], *buffer;
 
	_BorderInterpolate(3);
	buffer = (char *)malloc(26 * TS * TS);		/* 1664 kB */
	if (buffer == NULL)
		throw (status_t)B_NO_MEMORY;
 
	rgb = (ushort(*)[TS][TS][3])buffer;
	lab = (short (*)[TS][TS][3])(buffer + 12 * TS * TS);
	homo = (char (*)[TS][TS])(buffer + 24 * TS * TS);
	float percentage = 20;
	float percentageStep = 70.0f / (fInputHeight / (TS - 6));
 
	for (top = 0; top < fInputHeight; top += TS - 6) {
		if (fProgressMonitor) {
			fProgressMonitor("Interpolate", percentage, fProgressData);
			percentage += percentageStep;
		}
 
		for (left = 0; left < fInputWidth; left += TS - 6) {
			memset(rgb, 0, 12 * TS * TS);
 
			/* Interpolate green horizontally and vertically: */
			for (row = top < 2 ? 2 : top; row < top + TS
					&& row < fInputHeight - 2; row++) {
				col = left + (FC(row, left) == 1);
				if (col < 2)
					col += 2;
				for (fc = FC(row, col); col < left + TS
						&& col < fInputWidth - 2; col += 2) {
					pix = fImageData + row * fInputWidth + col;
					val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2
						- pix[-2][fc] - pix[2][fc]) >> 2;
					rgb[0][row - top][col - left][1]
						= ULIM(val, pix[-1][1], pix[1][1]);
					val = ((pix[-fInputWidth][1] + pix[0][fc]
							+ pix[fInputWidth][1]) * 2
						- pix[-2 * fInputWidth][fc] - pix[2 * fInputWidth][fc])
							>> 2;
					rgb[1][row - top][col - left][1] = ULIM(val,
						pix[-fInputWidth][1], pix[fInputWidth][1]);
				}
			}
 
			/* Interpolate red and blue, and convert to CIELab: */
			for (d = 0; d < 2; d++) {
				for (row = top + 1; row < top + TS - 1
						&& row < fInputHeight - 1; row++) {
					for (col = left + 1; col < left + TS - 1
							&& col < fInputWidth - 1; col++) {
						pix = fImageData + row * fInputWidth + col;
						rix = &rgb[d][row - top][col - left];
						if ((c = 2 - FC(row, col)) == 1) {
							c = FC(row + 1,col);
							val = pix[0][1] + ((pix[-1][2-c] + pix[1][2 - c]
								- rix[-1][1] - rix[1][1] ) >> 1);
							rix[0][2-c] = CLIP(val);
							val = pix[0][1] + ((pix[-fInputWidth][c]
								+ pix[fInputWidth][c]
								- rix[-TS][1] - rix[TS][1] ) >> 1);
						} else {
							val = rix[0][1] + ((pix[-fInputWidth - 1][c]
								+ pix[-fInputWidth + 1][c]
								+ pix[fInputWidth - 1][c]
								+ pix[fInputWidth + 1][c]
								- rix[-TS - 1][1] - rix[-TS + 1][1]
								- rix[TS - 1][1] - rix[TS + 1][1] + 1) >> 2);
						}
						rix[0][c] = CLIP(val);
						c = FC(row, col);
						rix[0][c] = pix[0][c];
						_CameraToCIELab(rix[0], flab);
						for (c = 0; c < 3; c++) {
							lab[d][row - top][col - left][c]
								= int16(64 * flab[c]);
						}
					}
				}
			}
 
			/* Build homogeneity maps from the CIELab images: */
			memset(homo, 0, 2 * TS * TS);
			for (row = top + 2; row < top+TS-2 && row < fInputHeight; row++) {
				tr = row - top;
				for (col = left + 2; col < left + TS - 2
						&& col < fInputWidth; col++) {
					tc = col - left;
					for (d = 0; d < 2; d++) {
						for (i = 0; i < 4; i++) {
							ldiff[d][i] = ABS(lab[d][tr][tc][0]
									- lab[d][tr][tc+dir[i]][0]);
						}
					}
 
					leps = MIN(MAX(ldiff[0][0],ldiff[0][1]),
						MAX(ldiff[1][2],ldiff[1][3]));
 
					for (d = 0; d < 2; d++) {
						for (i = 0; i < 4; i++) {
							if (i >> 1 == d || ldiff[d][i] <= leps) {
								abdiff[d][i] = square(lab[d][tr][tc][1]
										- lab[d][tr][tc+dir[i]][1])
									+ square(lab[d][tr][tc][2]
										- lab[d][tr][tc+dir[i]][2]);
							}
						}
					}
 
					abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]),
						MAX(abdiff[1][2],abdiff[1][3]));
 
					for (d=0; d < 2; d++) {
						for (i=0; i < 4; i++) {
							if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps)
								homo[d][tr][tc]++;
						}
					}
				}
			}
 
			/* Combine the most homogenous pixels for the final result: */
			for (row = top + 3; row < top + TS - 3 && row < fInputHeight - 3;
					row++) {
				tr = row - top;
				for (col = left + 3; col < left + TS - 3
						&& col < fInputWidth - 3; col++) {
					tc = col - left;
					for (d = 0; d < 2; d++) {
						for (hm[d] = 0, i = tr - 1; i <= tr + 1; i++) {
							for (j = tc - 1; j <= tc + 1; j++) {
								hm[d] += homo[d][i][j];
							}
						}
					}
					if (hm[0] != hm[1]) {
						for (c = 0; c < 3; c++) {
							fImageData[row * fInputWidth + col][c]
								= rgb[hm[1] > hm[0]][tr][tc][c];
						}
					} else {
						for (c = 0; c < 3; c++) {
							fImageData[row * fInputWidth + col][c]
								= (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1;
						}
					}
				}
			}
		}
	}
	free(buffer);
#undef TS
}
 
 
void
DCRaw::_PseudoInverse(double (*in)[3], double (*out)[3], uint32 size)
{
	double work[3][6], num;
	uint32 i, j, k;
 
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 6; j++) {
			work[i][j] = j == i + 3;
		}
		for (j = 0; j < 3; j++) {
			for (k = 0; k < size; k++) {
				work[i][j] += in[k][i] * in[k][j];
			}
		}
	}
 
	for (i = 0; i < 3; i++) {
		num = work[i][i];
		for (j = 0; j < 6; j++) {
			work[i][j] /= num;
		}
		for (k = 0; k < 3; k++) {
			if (k == i)
				continue;
 
			num = work[k][i];
 
			for (j = 0; j < 6; j++) {
				work[k][j] -= work[i][j] * num;
			}
		}
	}
 
	for (i = 0; i < size; i++) {
		for (j = 0; j < 3; j++) {
			for (out[i][j] = k =0; k < 3; k++) {
				out[i][j] += work[j][k+3] * in[i][k];
			}
		}
	}
}
 
 
void
DCRaw::_ConvertToRGB()
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Convert to RGB", 90, fProgressData);
 
	uint32 row, col, c, i, j, k;
	float out[3], out_cam[3][4];
	double num, inverse[3][3];
	static const double xyzd50_srgb[3][3] = {
		{ 0.436083, 0.385083, 0.143055 },
		{ 0.222507, 0.716888, 0.060608 },
		{ 0.013930, 0.097097, 0.714022 }};
	static const double rgb_rgb[3][3] = {
		{ 1,0,0 }, { 0,1,0 }, { 0,0,1 }};
	static const double adobe_rgb[3][3] = {
		{ 0.715146, 0.284856, 0.000000 },
		{ 0.000000, 1.000000, 0.000000 },
		{ 0.000000, 0.041166, 0.958839 }};
	static const double wide_rgb[3][3] = {
		{ 0.593087, 0.404710, 0.002206 },
		{ 0.095413, 0.843149, 0.061439 },
		{ 0.011621, 0.069091, 0.919288 }};
	static const double prophoto_rgb[3][3] = {
		{ 0.529317, 0.330092, 0.140588 },
		{ 0.098368, 0.873465, 0.028169 },
		{ 0.016879, 0.117663, 0.865457 }};
	static const double (*out_rgb[])[3]
		= { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb };
	static const char *name[] = { "sRGB", "Adobe RGB (1998)", "WideGamut D65",
		"ProPhoto D65", "XYZ" };
	static const unsigned phead[] = { 1024, 0, 0x2100000, 0x6d6e7472,
		0x52474220, 0x58595a20, 0, 0, 0, 0x61637370, 0, 0, 0x6e6f6e65,
		0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d };
	unsigned pbody[] = { 10,
		0x63707274, 0, 36,	/* cprt */
		0x64657363, 0, 40,	/* desc */
		0x77747074, 0, 20,	/* wtpt */
		0x626b7074, 0, 20,	/* bkpt */
		0x72545243, 0, 14,	/* rTRC */
		0x67545243, 0, 14,	/* gTRC */
		0x62545243, 0, 14,	/* bTRC */
		0x7258595a, 0, 20,	/* rXYZ */
		0x6758595a, 0, 20,	/* gXYZ */
		0x6258595a, 0, 20 };	/* bXYZ */
	static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc };
	unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 };
 
	memcpy(out_cam, fMeta.rgb_camera, sizeof(out_cam));
	fRawColor |= fColors == 1 || fDocumentMode
		|| fOutputColor < 1 || fOutputColor > 5;
	if (!fRawColor) {
		fOutputProfile = (uint32 *)calloc(phead[0], 1);
		if (fOutputProfile == NULL)
			throw (status_t)B_NO_MEMORY;
 
		memcpy(fOutputProfile, phead, sizeof(phead));
		if (fOutputColor == 5)
			fOutputProfile[4] = fOutputProfile[5];
 
		fOutputProfile[0] = 132 + 12 * pbody[0];
		for (i = 0; i < pbody[0]; i++) {
			fOutputProfile[fOutputProfile[0] / 4]
				= i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874;
			pbody[i*3+2] = fOutputProfile[0];
			fOutputProfile[0] += (pbody[i*3+3] + 3) & -4;
		}
 
		memcpy(fOutputProfile + 32, pbody, sizeof(pbody));
		fOutputProfile[pbody[5] / 4 + 2] = strlen(name[fOutputColor - 1]) + 1;
		memcpy((char *)fOutputProfile + pbody[8] + 8, pwhite, sizeof(pwhite));
		if (fOutputBitsPerSample == 8) {
#ifdef SRGB_GAMMA
			pcurve[3] = 0x2330000;
#else
			pcurve[3] = 0x1f00000;
#endif
		}
 
		for (i = 4; i < 7; i++) {
			memcpy((char *)fOutputProfile + pbody[i * 3 + 2], pcurve,
				sizeof(pcurve));
		}
 
		_PseudoInverse((double (*)[3])out_rgb[fOutputColor - 1], inverse, 3);
 
		for (i = 0; i < 3; i++) {
			for (j = 0; j < 3; j++) {
				for (num = k=0; k < 3; k++) {
					num += xyzd50_srgb[i][k] * inverse[j][k];
				}
				fOutputProfile[pbody[j * 3 + 23] / 4 + i + 2]
					= uint32(num * 0x10000 + 0.5);
			}
		}
		for (i = 0; i < phead[0]/4; i++) {
			fOutputProfile[i] = B_HOST_TO_BENDIAN_INT32(fOutputProfile[i]);
		}
		strcpy((char *)fOutputProfile + pbody[2] + 8,
			"auto-generated by dcraw");
		strcpy((char *)fOutputProfile + pbody[5] + 12, name[fOutputColor - 1]);
 
		for (i = 0; i < 3; i++) {
			for (j = 0; j < fColors; j++) {
				for (out_cam[i][j] = k = 0; k < 3; k++) {
					out_cam[i][j] += out_rgb[fOutputColor-1][i][k]
						* fMeta.rgb_camera[k][j];
				}
			}
		}
	}
 
	if (1/*verbose*/) {
		if (fRawColor)
			fprintf(stderr, "Building histograms...\n");
		else {
			fprintf(stderr, "Converting to %s colorspace...\n",
				name[fOutputColor - 1]);
		}
	}
 
	ushort* img = fImageData[0];
	memset(fHistogram, 0, sizeof(int32) * 0x2000 * 4);
 
	for (row = 0; row < fInputHeight; row++) {
		for (col = 0; col < fInputWidth; col++, img += 4) {
			if (!fRawColor) {
				out[0] = out[1] = out[2] = 0;
				for (c = 0; c < fColors; c++) {
					out[0] += out_cam[0][c] * img[c];
					out[1] += out_cam[1][c] * img[c];
					out[2] += out_cam[2][c] * img[c];
				}
				for (c = 0; c < 3; c++) {
					img[c] = CLIP((int)out[c]);
				}
			} else if (fDocumentMode)
				img[0] = img[FC(row, col)];
 
			for (c = 0; c < fColors; c++) {
				fHistogram[img[c] >> 3][c]++;
			}
		}
	}
 
	if (fColors == 4 && fOutputColor)
		fColors = 3;
	if (fDocumentMode && fFilters)
		fColors = 1;
}
 
 
void
DCRaw::_GammaLookUpTable(uchar* lut)
{
	int32 percent, val, total, i;
	float white = 0, r;
 
	percent = int32(fInputWidth * fInputHeight * 0.01);
		// 99th percentile white point
 
	//  if (fuji_width) perc /= 2;
	if (fHighlight)
		percent = 0;
 
	for (uint32 c = 0; c < fColors; c++) {
		for (val = 0x2000, total = 0; --val > 32;) {
			if ((total += fHistogram[val][c]) > percent)
				break;
		}
		if (white < val)
			white = val;
	}
 
	white *= 8 / fBrightness;
 
	for (i = 0; i < 0x10000; i++) {
		r = i / white;
		val = int32(256 * (!fUseGamma ? r :
#ifdef SRGB_GAMMA
			r <= 0.00304 ? r*12.92 : pow(r,2.5/6)*1.055-0.055));
#else
			r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099));
#endif
		if (val > 255)
			val = 255;
		lut[i] = val;
	}
}
 
 
//	#pragma mark - Lossless JPEG
 
 
void
DCRaw::_InitDecoder()
{
	memset(fDecodeBuffer, 0, sizeof(decode) * kDecodeBufferCount);
	fFreeDecode = fDecodeBuffer;
}
 
 
/*!	Construct a decode tree according the specification in *source.
	The first 16 bytes specify how many codes should be 1-bit, 2-bit
	3-bit, etc.  Bytes after that are the leaf values.
 
	For example, if the source is
 
	{ 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
	  0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff  },
 
	then the code is
 
	00		0x04
	010		0x03
	011		0x05
	100		0x06
	101		0x02
	1100		0x07
	1101		0x01
	11100		0x08
	11101		0x09
	11110		0x00
	111110		0x0a
	1111110		0x0b
	1111111		0xff
*/
uchar *
DCRaw::_MakeDecoder(const uchar* source, int level)
{
	if (level == 0)
		fDecodeLeaf = 0;
 
	if ((uint8*)fFreeDecode > (uint8*)fDecodeBuffer
			+ sizeof(decode) * kDecodeBufferCount) {
		fprintf(stderr, "decoder table overflow\n");
		throw (status_t)B_ERROR;
	}
 
	struct decode* current = fFreeDecode++;
 
	int i, next;
	for (i = next = 0; i <= fDecodeLeaf && next < 16; ) {
		i += source[next++];
	}
 
	if (i > fDecodeLeaf) {
		if (level < next) {
			current->branch[0] = fFreeDecode;
			_MakeDecoder(source, level + 1);
			current->branch[1] = fFreeDecode;
			_MakeDecoder(source, level + 1);
		} else
			current->leaf = source[16 + fDecodeLeaf++];
	}
 
	return (uchar*)source + 16 + fDecodeLeaf;
}
 
 
/*!	Not a full implementation of Lossless JPEG, just
	enough to decode Canon, Kodak and Adobe DNG images.
*/
void
DCRaw::_InitDecodeBits()
{
	fDecodeBits = fDecodeBitsRead = 0;
	fDecodeBitsReset = false;
}
 
 
/*!	_GetDecodeBits(n) where 0 <= n <= 25 returns an n-bit integer
*/
uint32
DCRaw::_GetDecodeBits(uint32 numBits)
{
	if (numBits == 0 || fDecodeBitsReset)
		return 0;
 
	while (fDecodeBitsRead < numBits) {
		uint8 c = fRead.Next<uint8>();
		if ((fDecodeBitsReset = fDecodeBitsZeroAfterMax
				&& c == 0xff && fRead.Next<uint8>()))
			return 0;
		fDecodeBits = (fDecodeBits << 8) + c;
		fDecodeBitsRead += 8;
	}
 
	fDecodeBitsRead -= numBits;
 
	return fDecodeBits << (32 - numBits - fDecodeBitsRead) >> (32 - numBits);
}
 
 
status_t
DCRaw::_LosslessJPEGInit(struct jhead* jh, bool infoOnly)
{
	int i, tag, len;
 
	_InitDecoder();
 
	for (i = 0; i < 4; i++) {
		jh->huff[i] = fFreeDecode;
	}
 
	jh->restart = INT_MAX;
 
	uchar data[0x10000], *dp;
	fRead(data, 2);
	if (data[1] != 0xd8)
		return B_ERROR;
 
	do {
		fRead(data, 4);
		tag = data[0] << 8 | data[1];
		len = (data[2] << 8 | data[3]) - 2;
		if (tag <= 0xff00)
			return B_ERROR;
 
		fRead(data, len);
		switch (tag) {
			case 0xffc0:
			case 0xffc3:
				jh->bits = data[0];
				jh->high = data[1] << 8 | data[2];
				jh->wide = data[3] << 8 | data[4];
				jh->clrs = data[5];
				break;
			case 0xffc4:
				if (infoOnly)
					break;
 
				for (dp = data; dp < data+len && *dp < 4; ) {
					jh->huff[*dp] = fFreeDecode;
					dp = _MakeDecoder(++dp, 0);
				}
				break;
			case 0xffdd:
				jh->restart = data[0] << 8 | data[1];
				break;
		}
	} while (tag != 0xffda);
 
	if (infoOnly)
		return B_OK;
 
	jh->row = (ushort *)calloc(jh->wide*jh->clrs, 2);
	if (jh->row == NULL)
		throw (status_t)B_NO_MEMORY;
 
	fDecodeBitsZeroAfterMax = true;
	return B_OK;
}
 
 
int
DCRaw::_LosslessJPEGDiff(struct decode *dindex)
{
	while (dindex->branch[0]) {
		dindex = dindex->branch[_GetDecodeBits(1)];
	}
 
	int length = dindex->leaf;
	if (length == 16 && (!fDNGVersion || fDNGVersion >= 0x1010000))
		return -32768;
 
	int diff = _GetDecodeBits(length);
	if ((diff & (1 << (length - 1))) == 0)
		diff -= (1 << length) - 1;
 
	return diff;
}
 
 
void
DCRaw::_LosslessJPEGRow(struct jhead *jh, int jrow)
{
	if (jrow * jh->wide % jh->restart == 0) {
		for (uint32 i = 0; i < 4; i++) {
			jh->vpred[i] = 1 << (jh->bits - 1);
		}
		if (jrow) {
			uint16 mark = 0;
			int c;
			do {
				mark = (mark << 8) + (c = fRead.Next<uint8>());
			} while (c != EOF && mark >> 4 != 0xffd);
		}
		_InitDecodeBits();
	}
 
	uint16* outp = jh->row;
 
	for (int32 col = 0; col < jh->wide; col++) {
		for (int32 c = 0; c < jh->clrs; c++) {
			int32 diff = _LosslessJPEGDiff(jh->huff[c]);
			*outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff);
			outp++;
		}
	}
}
 
 
//	#pragma mark - RAW loaders
 
 
void
DCRaw::_LoadRAWUnpacked(const image_data_info& image)
{
	uint32 rawWidth = _Raw().width;
 
	uint16* pixel = (uint16*)calloc(rawWidth, sizeof(uint16));
	if (pixel == NULL)
		return;
 
	fRead.Seek((fTopMargin * rawWidth + fLeftMargin) * sizeof(uint16),
		SEEK_CUR);
 
	for (uint32 row = 0; row < fInputHeight; row++) {
		fRead.NextShorts(pixel, rawWidth);
		for (uint32 column = 0; column < fInputWidth; column++) {
			_Bayer(column, row) = pixel[column];
		}
	}
 
	free(pixel);
}
 
 
/*!	This is, for example, used in PENTAX RAW images
*/
void
DCRaw::_LoadRAWPacked12(const image_data_info& image)
{
	uint32 rawWidth = _Raw().width;
 
	_InitDecodeBits();
 
	for (uint32 row = 0; row < fInputHeight; row++) {
		for (uint32 column = 0; column < fInputWidth; column++) {
			//uint16 bits = _GetDecodeBits(12);
			_Bayer(column, row) = _GetDecodeBits(12);
			//fImageData[((row) >> fShrink)*fOutputWidth + ((column) >> fShrink)][FC(row,column)] = bits;
		}
		for (uint32 column = fInputWidth * 3 / 2; column < rawWidth; column++) {
			_GetDecodeBits(8);
		}
	}
}
 
 
void
DCRaw::_MakeCanonDecoder(uint32 table)
{
	static const uchar kFirstTree[3][29] = {
		{ 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
			0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff },
		{ 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
			0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff },
		{ 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
			0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff },
	};
	static const uchar kSecondTree[3][180] = {
		{ 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139,
			0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
			0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0,
			0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
			0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
			0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9,
			0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98,
			0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6,
			0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4,
			0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7,
			0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1,
			0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64,
			0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba,
			0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4,
			0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff },
		{ 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140,
			0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
			0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
			0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
			0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
			0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59,
			0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9,
			0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99,
			0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85,
			0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8,
			0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a,
			0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9,
			0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8,
			0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8,
			0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff },
		{ 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
			0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
			0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22,
			0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
			0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
			0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48,
			0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69,
			0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8,
			0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94,
			0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a,
			0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6,
			0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62,
			0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5,
			0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3,
			0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff }
	};
 
	if (table > 2)
		table = 2;
 
	_InitDecoder();
 
	_MakeDecoder(kFirstTree[table], 0);
	fSecondDecode = fFreeDecode;
	_MakeDecoder(kSecondTree[table], 0);
}
 
 
/*!	Return 0 if the image starts with compressed data,
	1 if it starts with uncompressed low-order bits.
 
	In Canon compressed data, 0xff is always followed by 0x00.
*/
bool
DCRaw::_CanonHasLowBits()
{
	bool hasLowBits = true;
	uchar test[0x4000 - 540];
 
	fRead.Seek(540, SEEK_SET);
	fRead(test, sizeof(test));
 
	for (uint32 i = 0; i < sizeof(test) - 1; i++)
		if (test[i] == 0xff) {
			if (test[i + 1])
				return 1;
			hasLowBits = 0;
    }
 
	return hasLowBits;
}
 
 
void
DCRaw::_LoadRAWCanonCompressed(const image_data_info& image)
{
	uint32 rawWidth = _Raw().width;
	int carry = 0, pnum = 0, base[2];
 
	_MakeCanonDecoder(image.compression);
 
	uint16* pixel = (uint16 *)calloc(rawWidth * 8, sizeof(*pixel));
	if (pixel == NULL)
		throw (status_t)B_NO_MEMORY;
 
	bool hasLowBits = _CanonHasLowBits();
	if (!hasLowBits)
		fMeta.maximum = 0x3ff;
 
	fRead.Seek(540 + (hasLowBits ? _Raw().height * rawWidth / 4 : 0),
		SEEK_SET);
 
	fDecodeBitsZeroAfterMax = true;
	_InitDecodeBits();
 
	for (uint32 row = 0; row < _Raw().height; row += 8) {
		for (uint32 block = 0; block < rawWidth >> 3; block++) {
			int diffbuf[64];
			memset(diffbuf, 0, sizeof diffbuf);
			struct decode* decode = fDecodeBuffer;
 
			for (uint32 i = 0; i < 64; i++) {
				struct decode* dindex = decode;
				while (dindex->branch[0]) {
					dindex = dindex->branch[_GetDecodeBits(1)];
				}
				int leaf = dindex->leaf;
				decode = fSecondDecode;
				if (leaf == 0 && i)
					break;
				if (leaf == 0xff)
					continue;
				i += leaf >> 4;
 
				int len = leaf & 15;
				if (len == 0)
					continue;
				int diff = _GetDecodeBits(len);
				if ((diff & (1 << (len-1))) == 0)
					diff -= (1 << len) - 1;
				if (i < 64)
					diffbuf[i] = diff;
			}
 
			diffbuf[0] += carry;
			carry = diffbuf[0];
 
			for (uint32 i = 0; i < 64; i++) {
				if (pnum++ % _Raw().width == 0)
					base[0] = base[1] = 512;
				pixel[(block << 6) + i] = (base[i & 1] += diffbuf[i]);
			}
		}
 
		if (hasLowBits) {
			off_t savedOffset = fRead.Position();
			fRead.Seek(26 + row * _Raw().width / 4, SEEK_SET);
 
			uint16* pixelRow = pixel;
			for (uint32 i = 0; i < rawWidth * 2; i++) {
				uint8 c = fRead.Next<uint8>();
 
				for (uint32 r = 0; r < 8; r += 2, pixelRow++) {
					uint32 val = (*pixelRow << 2) + ((c >> r) & 3);
					if (rawWidth == 2672 && val < 512)
						val += 2;
					*pixelRow = val;
				}
			}
 
			fRead.Seek(savedOffset, SEEK_SET);
		}
 
		for (uint32 r = 0; r < 8; r++) {
			uint32 irow = row - fTopMargin + r;
			if (irow >= fInputHeight)
				continue;
 
			for (uint32 col = 0; col < rawWidth; col++) {
				uint32 icol = col - fLeftMargin;
				if (icol < fInputWidth)
					_Bayer(icol, irow) = pixel[r * rawWidth + col];
				else
					fMeta.black += pixel[r * rawWidth + col];
			}
		}
	}
 
	free(pixel);
 
	if (rawWidth > fInputWidth)
		fMeta.black /= (rawWidth - fInputWidth) * fInputHeight;
}
 
 
void
DCRaw::_LoadRAWLosslessJPEG(const image_data_info& image)
{
	int jwide, jrow, jcol, val, jidx, i, j, row = 0, col = 0;
	uint32 rawWidth = _Raw().width;
	int min = INT_MAX;
 
	struct jhead jh;
	if (_LosslessJPEGInit(&jh, false) != B_OK)
		throw (status_t)B_NO_TRANSLATOR;
 
	jwide = jh.wide * jh.clrs;
 
	for (jrow = 0; jrow < jh.high; jrow++) {
		_LosslessJPEGRow(&jh, jrow);
 
		for (jcol = 0; jcol < jwide; jcol++) {
			val = jh.row[jcol];
			if (jh.bits <= 12)
				val = fCurve[val];
 
			if (fCR2Slice[0]) {
				jidx = jrow * jwide + jcol;
				i = jidx / (fCR2Slice[1] * jh.high);
				if ((j = i >= fCR2Slice[0]))
					i  = fCR2Slice[0];
				jidx -= i * (fCR2Slice[1] * jh.high);
				row = jidx / fCR2Slice[1 + j];
				col = jidx % fCR2Slice[1 + j] + i * fCR2Slice[1];
			}
 
			if (_Raw().width == 3984 && (col -= 2) < 0) {
				col += rawWidth;
				row--;
			}
 
			if (uint32(row - fTopMargin) < fInputHeight) {
				if (uint32(col - fLeftMargin) < fInputWidth) {
					_Bayer(col - fLeftMargin, row - fTopMargin) = val;
					if (min > val)
						min = val;
				} else
					fMeta.black += val;
			}
			if (++col >= (int32)rawWidth) {
				col = 0;
				row++;
			}
		}
	}
 
	//dump_to_disk(fImageData, fInputWidth * fColors * 100);
	free(jh.row);
 
	if (rawWidth > fInputWidth)
		fMeta.black /= (rawWidth - fInputWidth) * fInputHeight;
	if (_IsKodak())
		fMeta.black = min;
}
 
 
void
DCRaw::_LoadRAW(const image_data_info& image)
{
#if 0
	if (_IsCanon()) {
		if (fIsTIFF)
		else
			_LoadRAWCanonCompressed(image);
	} else
#endif
	{
		switch (image.compression) {
			case COMPRESSION_NONE:
				_LoadRAWUnpacked(image);
				break;
			case COMPRESSION_OLD_JPEG:
				_LoadRAWLosslessJPEG(image);
				//_LoadRAWCanonCompressed(image);
				break;
			case COMPRESSION_PACKBITS:
				_LoadRAWPacked12(image);
				break;
 
			default:
				fprintf(stderr, "DCRaw: unknown compression: %" B_PRId32 "\n",
					image.compression);
				throw (status_t)B_NO_TRANSLATOR;
				break;
		}
	}
}
 
 
//	#pragma mark - Image writers
 
 
void
DCRaw::_WriteRGB32(image_data_info& image, uint8* outputBuffer)
{
	if (fProgressMonitor != NULL)
		fProgressMonitor("Write RGB", 95, fProgressData);
 
	uint8* line, lookUpTable[0x10000];
 
	uint32 width = image.flip > 4 ? fOutputHeight : fOutputWidth;
	uint32 height = image.flip > 4 ? fOutputWidth : fOutputHeight;
	uint32 outputRow = (4 * fOutputBitsPerSample / 8) * width;
	uint32 outputOffset = 0;
 
	line = (uint8 *)malloc(outputRow);
	if (line == NULL)
		throw (status_t)B_NO_MEMORY;
 
	memset(line, 0, outputRow);
 
	if (fOutputBitsPerSample == 8)
		_GammaLookUpTable(lookUpTable);
 
	int32 sourceOffset = _FlipIndex(0, 0, image.flip);
	int32 colStep = _FlipIndex(0, 1, image.flip) - sourceOffset;
	int32 rowStep = _FlipIndex(1, 0, image.flip)
		- _FlipIndex(0, width, image.flip);
 
	TRACE(("flip = %ld, sourceOffset = %ld, colStep = %ld, rowStep = %ld, "
		"input: %lu x %lu, output: %lu x %lu\n", image.flip, sourceOffset,
		colStep, rowStep, fInputWidth, fInputHeight, width,
		height));
 
	if (fOutputBitsPerSample == 8) {
		for (uint32 row = 0; row < height; row++, sourceOffset += rowStep) {
			for (uint32 col = 0; col < width; col++, sourceOffset += colStep) {
				line[col * 4 + 2] = lookUpTable[fImageData[sourceOffset][0]];
				line[col * 4 + 1] = lookUpTable[fImageData[sourceOffset][1]];
				line[col * 4 + 0] = lookUpTable[fImageData[sourceOffset][2]];
			}
 
			memcpy(&outputBuffer[outputOffset], line, outputRow);
			outputOffset += outputRow;
		}
	} else {
#if 0
		uint16* ppm2 = (uint16*)line;
		for (row = 0; row < fOutputHeight; row++, soff += rstep) {
			for (col = 0; col < fOutputWidth; col++, soff += cstep) {
				FORCC ppm2[col*colors+c] =     image[soff][c];
			}
			if (!output_tiff && htons(0x55aa) != 0x55aa)
				swab (ppm2, ppm2, width*colors*2);
			fwrite (ppm, colors*output_bps/8, width, ofp);
		}
#endif
	}
 
	free(line);
}
 
 
void
DCRaw::_WriteJPEG(image_data_info& image, uint8* outputBuffer)
{
	fRead(outputBuffer, image.bytes);
 
	if (outputBuffer[0] != 0xff || outputBuffer[1] != 0xd8)
		throw (status_t)B_NO_TRANSLATOR;
 
#if 0
	uint8* thumb = (uint8*)malloc(image.bytes);
	if (thumb == NULL)
		throw (status_t)B_NO_MEMORY;
 
	fRead(thumb, image.bytes);
 
	uint8* data = (uint8*)fImageData;
	data[0] = 0xff;
	data[1] = 0xd8;
 
	if (strcmp((char *)thumb + 6, "Exif")) {
		// TODO: no EXIF data - write them ourselves
	}
 
	memcpy(&data[2], thumb + 2, image.bytes - 2);
	free(thumb);
#endif
}
 
 
//	#pragma mark - TIFF
 
 
time_t
DCRaw::_ParseTIFFTimestamp(bool reversed)
{
	char str[20];
	str[19] = 0;
 
	if (reversed) {
		for (int i = 19; i--; ) {
			str[i] = fRead.Next<uint8>();
		}
	} else
		fRead(str, 19);
 
	struct tm t;
	memset(&t, 0, sizeof t);
 
	if (sscanf(str, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon,
			&t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6)
		return 0;
 
	t.tm_year -= 1900;
	t.tm_mon -= 1;
 
	return mktime(&t);
}
 
 
/*!	Reads a TIFF tag and positions the file stream to its data section
*/
void
DCRaw::_ParseTIFFTag(off_t baseOffset, tiff_tag& tag, off_t& offset)
{
	fRead(tag.tag);
	fRead(tag.type);
	fRead(tag.length);
 
	offset = fRead.Position() + 4;
 
	uint32 length = tag.length;
 
	switch (tag.type) {
		case TIFF_UINT16_TYPE:
		case TIFF_INT16_TYPE:
			length *= 2;
			break;
 
		case TIFF_UINT32_TYPE:
		case TIFF_INT32_TYPE:
		case TIFF_FLOAT_TYPE:
			length *= 4;
			break;
 
		case TIFF_UFRACTION_TYPE:
		case TIFF_FRACTION_TYPE:
		case TIFF_DOUBLE_TYPE:
			length *= 8;
			break;
 
		default:
			break;
	}
 
	if (length > 4) {
		uint32 position;
		fRead(position);
 
		fRead.Seek(baseOffset + position, SEEK_SET);
	}
}
 
 
status_t
DCRaw::_ParseTIFFImageFileDirectory(off_t baseOffset, uint32 offset)
{
	double analogBalance[] = {1, 1, 1, 1};
	double xyz[] = {1, 1, 1, 1};
	bool useColorMatrix = false;
	double cameraCalibration[4][4], colorMatrix[4][3], cameraXYZ[4][3];
 
	for (int32 j = 0; j < 4; j++) {
		for (int32 i = 0; i < 4; i++) {
			cameraCalibration[j][i] = i == j;
		}
	}
 
	fRead.Seek(baseOffset + offset, SEEK_SET);
 
	uint16 tags;
	fRead(tags);
	if (tags > 512)
		return B_BAD_DATA;
 
	image_data_info& image = fImages[fNumImages];
 
	while (tags--) {
		off_t nextOffset;
		tiff_tag tag;
		_ParseTIFFTag(baseOffset, tag, nextOffset);
		TAG("TIFF tag: %u\n", tag.tag);
 
		switch (tag.tag) {
#if 0
			default:
				printf("tag %u NOT HANDLED!\n", tag.tag);
				break;
#endif
 
			case 17:
			case 18:
				if (tag.type == 3 && tag.length == 1) {
					fMeta.camera_multipliers[(tag.tag - 17) * 2]
						= fRead.Next<uint16>() / 256.0;
				}
				break;
 
			case 23:	// ISO speed
				fMeta.iso_speed = fRead.Next(tag.type);
				break;
 
			case 36:
			case 37:
			case 38:
				fMeta.camera_multipliers[tag.tag - 0x24] = fRead.Next<uint16>();
				break;
 
			case 39:
				if (tag.length < 50 || fMeta.camera_multipliers[0])
					break;
 
				fRead.Stream().Seek(12, SEEK_CUR);
				for (uint32 i = 0; i < 3; i++) {
					fMeta.camera_multipliers[i] = fRead.Next<uint16>();
				}
				break;
 
			case 2:		// image width
			case 256:
				image.width = fRead.Next(tag.type);
				break;
 
			case 3:		// image height
			case 257:
				image.height = fRead.Next(tag.type);
				break;
 
			case 258:	// bits per sample
				image.samples = tag.length;
				image.bits_per_sample = fRead.Next<uint16>();
				break;
 
			case 259:	// compression
				image.compression = fRead.Next<uint16>();
				break;
 
			case 262:	// Photometric Interpretation
				image.photometric_interpretation = fRead.Next<uint16>();
				break;
 
			case 271:	// manufacturer
				fRead(fMeta.manufacturer, 64);
				break;
 
			case 272:	// model
				fRead(fMeta.model, 64);
				break;
 
			case 273:	// Strip Offset
			case 513:
				image.data_offset = baseOffset + fRead.Next<uint32>();
				if (!image.bits_per_sample) {
					fRead.Stream().Seek(image.data_offset, SEEK_SET);
					jhead jh;
					if (_LosslessJPEGInit(&jh, true) == B_OK) {
						image.compression = 6;
						image.width = jh.wide << (jh.clrs == 2);
						image.height = jh.high;
						image.bits_per_sample = jh.bits;
						image.samples = jh.clrs;
					}
				}
				break;
 
			case 274:	// Orientation
				image.flip = fRead.Next<uint16>();
				break;
 
			case 277:	// Samples Per Pixel
				image.samples = fRead.Next(tag.type);
				break;
 
			case 279:	// Strip Byte Counts
			case 514:
				image.bytes = fRead.Next<uint32>();
				break;
 
			case 305:	// Software
				fRead(fMeta.software, 64);
				if (!strncmp(fMeta.software, "Adobe", 5)
					|| !strncmp(fMeta.software, "dcraw", 5)
					|| !strncmp(fMeta.software, "Bibble", 6)
					|| !strncmp(fMeta.software, "Nikon Scan", 10)
					|| !strcmp(fMeta.software,"Digital Photo Professional"))
					throw (status_t)B_NO_TRANSLATOR;
				break;
 
			case 306:	// Date/Time
				fMeta.timestamp = _ParseTIFFTimestamp(false);
				break;
 
#if 0
			case 323:	// Tile Length
				tile_length = fRead.Next(type);
				break;
 
			case 324:	// Tile Offsets
				image.data_offset = tag.length > 1
					? fRead.Stream().Position() : fRead.Next<uint32>();
				if (tag.length == 4)
					load_raw = &CLASS sinar_4shot_load_raw;
				break;
#endif
 
			case 330:	// Sub IFDs
				if (!strcmp(fMeta.model, "DSLR-A100") && image.width == 3872) {
					// TODO: this might no longer work!
					image.data_offset = fRead.Next<uint32>() + baseOffset;
					break;
				}
 
				while (tag.length--) {
					off_t nextOffset = fRead.Position() + sizeof(uint32);
 
					fRead.Seek(fRead.Next<uint32>() + baseOffset, SEEK_SET);
					if (_ParseTIFFImageFileDirectory(baseOffset) != B_OK)
						break;
 
					fNumImages++;
					fRead.Seek(nextOffset, SEEK_SET);
				}
				break;
 
#if 0
			case 400:
				strcpy(fMeta.manufacturer, "Sarnoff");
				maximum = 0xfff;
				break;
#endif
 
#if 0
			case 29184:
				sony_offset = get4();
				break;
			case 29185:
				sony_length = get4();
				break;
			case 29217:
				sony_key = get4();
				break;
#endif
 
			case 29443:
				for (uint32 i = 0; i < 4; i++) {
					fMeta.camera_multipliers[i ^ (i < 2)] = fRead.Next<uint16>();
				}
				break;
 
			case 33405:	// Model 2
				fRead(fMeta.model + 64, 64);
				break;
 
#if 0
			case 33422:	// CFA Pattern
			case 64777:	// Kodak P-series
			{
				if ((plen=len) > 16) plen = 16;
				fread (cfa_pat, 1, plen, ifp);
				for (colors=cfa=i=0; i < plen; i++) {
				  colors += !(cfa & (1 << cfa_pat[i]));
				  cfa |= 1 << cfa_pat[i];
				}
				if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3);	/* CMY */
				if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4);	/* GMCY */
				goto guess_cfa_pc;
				break;
			}
 
			case 33424:
				fseek(ifp, get4()+base, SEEK_SET);
				parse_kodak_ifd (base);
				break;
#endif
 
			case 33434:	// Exposure Time
				fMeta.shutter = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
 
			case 33437:	// Aperture
				fMeta.aperture = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
 
			case 34306:	// Leaf white balance
				for (uint32 i = 0; i < 4; i++) {
					fMeta.camera_multipliers[i ^ 1] = 4096.0 / fRead.Next<uint16>();
				}
				break;
 
#if 0
			case 34307:	// Leaf Catch Light color matrix
				fread (software, 1, 7, ifp);
				if (strncmp(software,"MATRIX",6))
					break;
				colors = 4;
				for (fRawColor = i=0; i < 3; i++) {
					FORC4 fscanf (ifp, "%f", &rgb_cam[i][c^1]);
					if (!use_camera_wb)
						continue;
					num = 0;
					FORC4 num += rgb_cam[i][c];
					FORC4 rgb_cam[i][c] /= num;
				}
				break;
			case 34310:	// Leaf metadata
				parse_mos (ftell(ifp));
			case 34303:
				strcpy(image.manufacturer, "Leaf");
				break;
#endif
 
			case 34665:	// EXIF tag
				fRead.Seek(fRead.Next<uint32>() + baseOffset, SEEK_SET);
 
				fEXIFOffset = fRead.Position();
				fEXIFLength = tag.length;
 
				_ParseEXIF(baseOffset);
				break;
 
#if 0
			case 34675:	// InterColorProfile
			case 50831:	// AsShotICCProfile
				profile_offset = fRead.Stream().Position();
				profile_length = tag.length;
				break;
 
			case 37122:	// Compressed Bits Per Pixel
				kodak_cbpp = fRead.Next<uint32>();
				break;
#endif
 
			case 37386:	// Focal Length
				fMeta.focal_length = fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
 
			case 37393:	// Image Number
				fMeta.shot_order = fRead.Next(tag.type);
				break;
 
#if 0
			case 37400:	// old Kodak KDC tag
				for (fRawColor = i=0; i < 3; i++) {
					getrat();
					FORC3 rgb_cam[i][c] = getrat();
				}
				break;
 
			case 46275:	// Imacon tags
				strcpy (make, "Imacon");
				data_offset = ftell(ifp);
				ima_len = len;
				break;
      case 46279:
	fseek (ifp, 78, SEEK_CUR);
	raw_width  = get4();
	raw_height = get4();
	left_margin = get4() & 7;
	width = raw_width - left_margin - (get4() & 7);
	top_margin = get4() & 7;
	height = raw_height - top_margin - (get4() & 7);
	fseek (ifp, 52, SEEK_CUR);
	FORC3 cam_multipliers[c] = getreal(11);
	fseek (ifp, 114, SEEK_CUR);
	flip = (get2() >> 7) * 90;
	if (width * height * 6 == ima_len) {
	  if (flip % 180 == 90) SWAP(width,height);
	  filters = flip = 0;
	}
	break;
      case 50454:			/* Sinar tag */
      case 50455:
	if (!(cbuf = (char *) malloc(len))) break;
	fread (cbuf, 1, len, ifp);
	for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n'))
	  if (!strncmp (++cp,"Neutral ",8))
	    sscanf (cp+8, "%f %f %f", cam_multipliers, cam_multipliers+1, cam_multipliers+2);
	free (cbuf);
	break;
#endif
 
			case 50706:	// DNG Version
				for (int32 i = 0; i < 4; i++) {
					fDNGVersion = (fDNGVersion << 8) + fRead.Next<uint8>();
				}
				break;
 
#if 0
			case 50710:	// CFAPlaneColor
				if (len > 4)
					len = 4;
				colors = len;
				fread(cfa_pc, 1, colors, ifp);
			guess_cfa_pc:
				FORCC tab[cfa_pc[c]] = c;
				cdesc[c] = 0;
				for (i=16; i--; )
					filters = filters << 2 | tab[cfa_pat[i % plen]];
				break;
			case 50711:	// CFALayout
				if (get2() == 2) {
					fuji_width = 1;
					filters = 0x49494949;
				}
				break;
#endif
 
			case 291:	// Linearization Table
			case 50712:
				_ParseLinearTable(tag.length);
				break;
 
			case 50714:			/* BlackLevel */
			case 50715:			/* BlackLevelDeltaH */
			case 50716:			/* BlackLevelDeltaV */
			{
				double black = 0.0;
				for (uint32 i = 0; i < tag.length; i++) {
					black += fRead.NextDouble(tag.type);
				}
				fMeta.black += int32(black / tag.length + 0.5);
				break;
			}
 
			case 50717:	// White Level
				fMeta.maximum = fRead.Next(tag.type);
				break;
 
			case 50718:	// Default Scale
				fMeta.pixel_aspect = fRead.NextDouble(TIFF_FRACTION_TYPE);
				fMeta.pixel_aspect /= fRead.NextDouble(TIFF_FRACTION_TYPE);
				break;
 
			case 50721:	// Color Matrix
			case 50722:
				for (uint32 c = 0; c < fColors; c++) {
					for (uint32 j = 0; j < 3; j++) {
						colorMatrix[c][j] = fRead.NextDouble(TIFF_FRACTION_TYPE);
					}
				}
				useColorMatrix = true;
				break;
 
			case 50723:	// Camera Calibration
			case 50724:
				for (uint32 i = 0; i < fColors; i++) {
					for (uint32 c = 0; c < fColors; c++) {
						cameraCalibration[i][c] = fRead.NextDouble(
							TIFF_FRACTION_TYPE);
					}
				}
				//break;
			case 50727:	// Analog Balance
				for (uint32 c = 0; c < fColors; c++) {
					analogBalance[c] = fRead.NextDouble(TIFF_FRACTION_TYPE);
					//printf("ab: %g\n", analogBalance[c]);
				}
				break;
#if 0
      case 50728:			/* AsShotNeutral */
	FORCC asn[c] = getreal(type);
	break;
      case 50729:			/* AsShotWhiteXY */
	xyz[0] = getrat();
	xyz[1] = getrat();
	xyz[2] = 1 - xyz[0] - xyz[1];
	FORC3 xyz[c] /= kD65White[c];
	break;
      case 50740:			/* DNGPrivateData */
	if (dng_version) break;
	i = order;
	parse_minolta (j = get4()+base);
	order = i;
	fseek (ifp, j, SEEK_SET);
	parse_tiff_ifd (base);
	break;
#endif
			case 50752:
				fRead.NextShorts(fCR2Slice, 3);
				break;
 
			case 50829:	// Active Area
				fTopMargin = fRead.Next(tag.type);
				fLeftMargin = fRead.Next(tag.type);
				fInputHeight = fRead.Next(tag.type) - fTopMargin;
				fInputWidth = fRead.Next(tag.type) - fLeftMargin;
				break;
#if 0
      case 64772:			/* Kodak P-series */
	fseek (ifp, 16, SEEK_CUR);
	data_offset = get4();
	fseek (ifp, 28, SEEK_CUR);
	data_offset += get4();
	load_raw = &CLASS packed_12_load_raw;
#endif
		}
		fRead.Seek(nextOffset, SEEK_SET);
	}
 
	// handle SONY tags
 
#if 0
	if (sony_length && (buf = (unsigned *) malloc(sony_length))) {
		fseek(ifp, sony_offset, SEEK_SET);
		fread(buf, sony_length, 1, ifp);
		sony_decrypt(buf, sony_length / 4, 1, sony_key);
		sfp = ifp;
		if ((ifp = tmpfile())) {
			fwrite(buf, sony_length, 1, ifp);
			fseek(ifp, 0, SEEK_SET);
			parse_tiff_ifd(-sony_offset);
			fclose(ifp);
		}
		ifp = sfp;
		free(buf);
	}
#endif
 
	for (uint32 i = 0; i < fColors; i++) {
		for (uint32 c = 0; c < fColors; c++) {
			cameraCalibration[i][c] *= analogBalance[i];
		}
	}
 
	if (useColorMatrix) {
		for (uint32 c = 0; c < fColors; c++) {
			for (uint32 i = 0; i < 3; i++) {
				cameraXYZ[c][i] = 0;
				for (uint32 j = 0; j < fColors; j++) {
					cameraXYZ[c][i] += cameraCalibration[c][j]
						* colorMatrix[j][i] * xyz[i];
				}
			}
		}
		_CameraXYZCoefficients(cameraXYZ);
	}
 
#if 0
	if (asn[0])
    	FORCC pre_multipliers[c] = 1 / asn[c];
#endif
	if (!useColorMatrix) {
		for (uint32 c = 0; c < fColors; c++) {
			fMeta.pre_multipliers[c] /= cameraCalibration[c][c];
		}
	}
 
	return B_OK;
}
 
 
status_t
DCRaw::_ParseTIFFImageFileDirectory(off_t baseOffset)
{
	while (fNumImages < kImageBufferCount) {
		int32 offset;
		fRead(offset);
		if (offset == 0)
			break;
 
		status_t status = _ParseTIFFImageFileDirectory(baseOffset, offset);
		if (status < B_OK)
			return status;
 
		fNumImages++;
	}
 
	return B_OK;
}
 
 
status_t
DCRaw::_ParseTIFF(off_t baseOffset)
{
	fRead.Stream().Seek(baseOffset, SEEK_SET);
 
	uint16 endian;
	fRead(endian);
	if (endian != 'MM' && endian != 'II')
		return B_NO_TRANSLATOR;
 
#if B_HOST_IS_LENDIAN
	fRead.SetSwap(endian == 'MM');
#else
	fRead.SetSwap(endian == 'II');
#endif
 
	fRead(endian);
		// dummy, not used, should be 42 for actual TIFF images,
		// but may vary for RAW images
 
	_ParseTIFFImageFileDirectory(baseOffset);
	fIsTIFF = true;
 
	uint32 maxSamples = 0;
 
	if (fThumbIndex >= 0 && _Thumb().data_offset) {
		fRead.Seek(_Thumb().data_offset, SEEK_SET);
 
		jhead jh;
		if (_LosslessJPEGInit(&jh, true)) {
			_Thumb().bits_per_sample = jh.bits;
			_Thumb().width = jh.wide;
			_Thumb().height = jh.high;
			_Thumb().bits_per_sample = 16;
		}
	}
 
	// identify RAW image in list of images retrieved
 
	for (uint32 i = 0; i < fNumImages; i++) {
		if (maxSamples < fImages[i].samples)
			maxSamples = fImages[i].samples;
 
		if ((fImages[i].compression != COMPRESSION_OLD_JPEG
				|| fImages[i].samples != 3)
			&& _SupportsCompression(fImages[i])) {
			fImages[i].is_raw = true;
 
			if (fRawIndex < 0 || fImages[i].width * fImages[i].height
					> _Raw().width * _Raw().height) {
				fRawIndex = i;
				//fuji_secondary = _Raw().samples == 2;
			}
		}
	}
 
	if (fRawIndex < 0
		|| (!fDNGVersion && _Raw().samples == 3 && _Raw().bits_per_sample == 8))
		throw (status_t)B_NO_TRANSLATOR;
 
	if (fRawIndex >= 0) {
		fMeta.raw_width = _Raw().width;
		fMeta.raw_height = _Raw().height;
	}
 
#if 0
  fuji_width *= (raw_width+1)/2;
  if (tiff_ifd[0].flip) tiff_flip = tiff_ifd[0].flip;
  if (raw >= 0 && !load_raw)
    switch (tiff_compress) {
      case 0:  case 1:
	load_raw = tiff_bps > 8 ?
	  &CLASS unpacked_load_raw : &CLASS eight_bit_load_raw;
	if (tiff_ifd[raw].bytes * 5 == raw_width * raw_height * 8)
	  load_raw = &CLASS olympus_e300_load_raw;
	if (tiff_bps == 12 && tiff_ifd[raw].phint == 2)
	  load_raw = &CLASS olympus_cseries_load_raw;
	break;
      case 6:  case 7:  case 99:
	load_raw = &CLASS lossless_jpeg_load_raw;		break;
      case 262:
	load_raw = &CLASS kodak_262_load_raw;			break;
      case 32773:
	load_raw = &CLASS packed_12_load_raw;			break;
      case 65535:
	load_raw = &CLASS pentax_k10_load_raw;			break;
      case 65000:
	switch (tiff_ifd[raw].phint) {
	  case 2: load_raw = &CLASS kodak_rgb_load_raw;   fFilters = 0;  break;
	  case 6: load_raw = &CLASS kodak_ycbcr_load_raw; fFilters = 0;  break;
	  case 32803: load_raw = &CLASS kodak_65000_load_raw;
	}
    }
  if (tiff_samples == 3 && tiff_bps == 8)
    if (!dng_version) is_raw = 0;
#endif
 
#if 0
  if (thm >= 0) {
    thumb_misc |= tiff_ifd[thm].samples << 5;
    switch (tiff_ifd[thm].comp) {
      case 0:
	write_thumb = &CLASS layer_thumb;
	break;
      case 1:
	if (tiff_ifd[thm].bps > 8)
	  thumb_load_raw = &CLASS kodak_thumb_load_raw;
	else
	  write_thumb = &CLASS ppm_thumb;
	break;
      case 65000:
	thumb_load_raw = tiff_ifd[thm].phint == 6 ?
		&CLASS kodak_ycbcr_load_raw : &CLASS kodak_rgb_load_raw;
    }
  }
#endif
	return B_OK;
}
 
 
//	#pragma mark -
 
 
status_t
DCRaw::Identify()
{
	fRead.Seek(0, SEEK_SET);
 
	status_t status = B_NO_TRANSLATOR;
	char header[32];
	fRead(header, sizeof(header));
 
	// check for TIFF-like files first
 
	uint16 endian = *(uint16*)&header;
	if (endian == 'II' || endian == 'MM')
		status = _ParseTIFF(0);
 
	if (status < B_OK)
		return status;
 
	// brush up some variables for later use
 
	fInputWidth = _Raw().width;
	fInputHeight = _Raw().height;
 
	_FixupValues();
 
	if ((_Raw().width | _Raw().height) < 0)
		_Raw().width = _Raw().height = 0;
	if (fMeta.maximum == 0)
		fMeta.maximum = (1 << _Raw().bits_per_sample) - 1;
 
	if (fFilters == ~(uint32)0)
		fFilters = 0x94949494;
	if (fFilters && fColors == 3) {
		for (int32 i = 0; i < 32; i += 4) {
			if ((fFilters >> i & 15) == 9)
				fFilters |= 2 << i;
			if ((fFilters >> i & 15) == 6)
				fFilters |= 8 << i;
		}
	}
 
	if (fRawColor)
		_AdobeCoefficients(fMeta.manufacturer, fMeta.model);
 
	// remove invalid images
 
	int32 rawCount = 0;
 
	for (int32 i = 0; i < (int32)fNumImages; i++) {
		if (fImages[i].width == 0 || fImages[i].height == 0
			|| fImages[i].data_offset == 0) {
			fNumImages--;
			if (i == fRawIndex)
				fRawIndex = -1;
			else if (i < fRawIndex)
				fRawIndex--;
			if (i == fThumbIndex)
				fThumbIndex = -1;
			else if (i < fThumbIndex)
				fThumbIndex--;
 
			if (i < (int32)fNumImages) {
				memmove(&fImages[i], &fImages[i + 1],
					sizeof(image_data_info) * (fNumImages - i));
			}
			i--;
		} else if (fImages[i].is_raw)
			rawCount++;
	}
 
	// This is to prevent us from identifying TIFF images
	if (rawCount == 0)
		return B_NO_TRANSLATOR;
 
	fMeta.flip = _Raw().flip;
	return B_OK;
}
 
 
status_t
DCRaw::ReadImageAt(uint32 index, uint8*& outputBuffer, size_t& bufferSize)
{
	if (index >= fNumImages)
		return B_BAD_VALUE;
 
	_CorrectIndex(index);
 
	image_data_info& image = fImages[index];
 
	fShrink = (fHalfSize || fThreshold) && fFilters;
	fOutputWidth = (fInputWidth + fShrink) >> fShrink;
	fOutputHeight = (fInputHeight + fShrink) >> fShrink;
 
	if (image.flip > 4) {
		// image is rotated
		image.output_width = fOutputHeight;
		image.output_height = fOutputWidth;
	} else {
		image.output_width = fOutputWidth;
		image.output_height = fOutputHeight;
	}
 
	if (image.is_raw) {
		bufferSize = fOutputWidth * 4 * fOutputHeight;
 
		fImageData = (uint16 (*)[4])calloc(fOutputWidth * fOutputHeight
			* sizeof(*fImageData) + 0, 1); //meta_length, 1);
		if (fImageData == NULL)
			throw (status_t)B_NO_MEMORY;
	} else {
		bufferSize = image.bytes + sizeof(tiff_header) + 10;
			// TIFF header plus EXIF identifier
	}
 
	outputBuffer = (uint8*)malloc(bufferSize);
	if (outputBuffer == NULL) {
		free(fImageData);
		fImageData = NULL;
		throw (status_t)B_NO_MEMORY;
	}
 
	fRead.Seek(image.data_offset, SEEK_SET);
 
	if (image.is_raw) {
		_LoadRAW(image);
 
		//bad_pixels();
		//if (dark_frame) subtract (dark_frame);
		//quality = 2 + !fuji_width;
 
		if (fDocumentMode < 2)
			_ScaleColors();
		_PreInterpolate();
		_CameraToCIELab(NULL, NULL);
 
		if (fFilters && !fDocumentMode) {
#if 0
			if (quality == 0)
				lin_interpolate();
			else if (quality < 3 || colors > 3)
				vng_interpolate();
#endif
			_AHDInterpolate();
		}
 
#if 0
		if (fHightlight > 1)
			_RecoverHighlights();
		if (use_fuji_rotate) fuji_rotate();
		if (mix_green && (colors = 3))
			for (i=0; i < height*width; i++)
				image[i][1] = (image[i][1] + image[i][3]) >> 1;
#endif
 
		_ConvertToRGB();
		//if (use_fuji_rotate) stretch();
 
		_WriteRGB32(image, outputBuffer);
	} else {
		_WriteJPEG(image, outputBuffer);
	}
 
	free(fImageData);
	fImageData = NULL;
 
	return B_OK;
}
 
 
void
DCRaw::GetMetaInfo(image_meta_info& metaInfo) const
{
	metaInfo = fMeta;
}
 
 
uint32
DCRaw::CountImages() const
{
	return fNumImages;
}
 
 
status_t
DCRaw::ImageAt(uint32 index, image_data_info& info) const
{
	if (index >= fNumImages)
		return B_BAD_VALUE;
 
	_CorrectIndex(index);
 
	info = fImages[index];
	return B_OK;
}
 
 
status_t
DCRaw::GetEXIFTag(off_t& offset, size_t& length, bool& bigEndian) const
{
	if (fEXIFOffset < 0)
		return B_ENTRY_NOT_FOUND;
 
	offset = fEXIFOffset;
	length = fEXIFLength;
 
#if B_HOST_IS_LENDIAN
	bigEndian = fRead.IsSwapping();
#else
	bigEndian = !fRead.IsSwapping();
#endif
	return B_OK;
}
 
 
void
DCRaw::SetProgressMonitor(monitor_hook hook, void* data)
{
	fProgressMonitor = hook;
	fProgressData = data;
}
 
 
void
DCRaw::SetHalfSize(bool half)
{
	fHalfSize = half;
}

V557 Array overrun is possible. The '256 - 1' index is pointing beyond array bound.

V557 Array overrun is possible. The '256 + 1' index is pointing beyond array bound.

V557 Array overrun is possible. The '1' index is pointing beyond array bound.

V548 Consider reviewing type casting. TYPE X[][] is not equivalent to TYPE **X.

V593 Consider reviewing the expression of the 'A = B >= C' kind. The expression is calculated as following: 'A = (B >= C)'.

V547 Expression '(_Raw().width | _Raw().height) < 0' is always false. Unsigned type value is never < 0.

V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression.

V557 Array overrun is possible. The '256' index is pointing beyond array bound.

V634 The priority of the '*' operation is higher than that of the '<<' operation. It's possible that parentheses should be used in the expression.