their original size for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20409 a95241bf-73f2-0310-859d-f6bbb57e9c96
3350 lines
85 KiB
C++
3350 lines
85 KiB
C++
/*
|
|
* Copyright 2007, Axel Dörfler, [email protected]. All rights reserved.
|
|
* 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) dprintf x
|
|
#define TRACE(x)
|
|
//#define TAG(x) dprintf 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 d65_white[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;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
DCRaw::DCRaw(BPositionIO& stream)
|
|
:
|
|
fRead(stream),
|
|
fNumImages(0),
|
|
fRawIndex(-1),
|
|
fThumbIndex(-1),
|
|
fDNGVersion(0),
|
|
fIsTIFF(false),
|
|
fThreshold(0),
|
|
fHalfSize(true),
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
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 (flip & 2)
|
|
row = fOutputHeight - 1 - row;
|
|
if (flip & 1)
|
|
col = fOutputWidth - 1 - col;
|
|
|
|
return row * fOutputWidth + col;
|
|
}
|
|
|
|
|
|
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::_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - Image Conversion
|
|
|
|
|
|
void
|
|
DCRaw::_ScaleColors()
|
|
{
|
|
int dblack, 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;
|
|
|
|
dblack = fMeta.black;
|
|
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 1
|
|
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()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
DCRaw::_PreInterpolate()
|
|
{
|
|
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] / d65_white[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 cam_xyz[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] += cam_xyz[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(char *make, 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 cam_xyz[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++) {
|
|
cam_xyz[0][j] = table[i].trans[j] / 10000.0;
|
|
}
|
|
_CameraXYZCoefficients(cam_xyz);
|
|
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()
|
|
{
|
|
#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);
|
|
|
|
for (top = 0; top < fInputHeight; top += TS - 6) {
|
|
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()
|
|
{
|
|
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] = htonl(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*/) {
|
|
fprintf(stderr, fRawColor ? "Building histograms...\n"
|
|
: "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
|
|
|
|
|
|
/*!
|
|
This is, for example, used in PENTAX RAW images
|
|
*/
|
|
void
|
|
DCRaw::_LoadRAWPacked12(const image_data_info& image)
|
|
{
|
|
uint32 rawWidth = _Raw().width;
|
|
uint32 column, row;
|
|
|
|
_InitDecodeBits();
|
|
|
|
for (row = 0; row < fInputHeight; row++) {
|
|
for (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 (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
|
|
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);
|
|
}
|
|
|
|
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 (_IsCanon()) {
|
|
if (fIsTIFF)
|
|
_LoadRAWLosslessJPEG(image);
|
|
else
|
|
_LoadRAWCanonCompressed(image);
|
|
} else {
|
|
switch (image.compression) {
|
|
case 32773:
|
|
_LoadRAWPacked12(image);
|
|
break;
|
|
|
|
default:
|
|
printf("unknown compression: %ld\n", image.compression);
|
|
throw (status_t)B_NO_TRANSLATOR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - Image writers
|
|
|
|
|
|
void
|
|
DCRaw::_WriteRGB32(image_data_info& image, uint8* outputBuffer)
|
|
{
|
|
fOutputWidth = fInputWidth;
|
|
fOutputHeight = fInputHeight;
|
|
|
|
if (image.flip & 4)
|
|
SWAP(fOutputHeight, fOutputWidth);
|
|
|
|
uint8* line, lookUpTable[0x10000];
|
|
|
|
uint32 outputRow = (4 * fOutputBitsPerSample / 8) * fOutputWidth;
|
|
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, fOutputWidth, 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, fOutputWidth, fOutputHeight));
|
|
|
|
if (fOutputBitsPerSample == 8) {
|
|
for (uint32 row = 0; row < fOutputHeight; row++, sourceOffset += rowStep) {
|
|
for (uint32 col = 0; col < fOutputWidth; 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], cam_xyz[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 = "50132467"[fRead.Next<uint16>() & 7] - '0';
|
|
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);
|
|
_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] /= d65_white[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++) {
|
|
cam_xyz[c][i] = 0;
|
|
for (uint32 j = 0; j < fColors; j++) {
|
|
cam_xyz[c][i] += cameraCalibration[c][j]
|
|
* colorMatrix[j][i] * xyz[i];
|
|
}
|
|
}
|
|
}
|
|
_CameraXYZCoefficients(cam_xyz);
|
|
}
|
|
|
|
#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 != 6 || fImages[i].samples != 3) {
|
|
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 == ~0UL)
|
|
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;
|
|
|
|
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;
|
|
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)
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|