From 8260e407c5b00b7931e542fe54604ebd92b42ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 22 Mar 2007 18:43:45 +0000 Subject: [PATCH] * Initial and very basic version of a RAW image translator. * Based on Dave Coffin's fabuluous dcraw - it's basically the same thing in C++, but follows common sense programming rules a bit more :-). * Current version probably only supports PENTAX RAW format, though. * Still lots of places left to clean up. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20404 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/translators/Jamfile | 1 + src/add-ons/translators/raw/ConfigView.cpp | 77 + src/add-ons/translators/raw/ConfigView.h | 19 + src/add-ons/translators/raw/Jamfile | 26 + src/add-ons/translators/raw/RAW.cpp | 2859 +++++++++++++++++ src/add-ons/translators/raw/RAW.h | 170 + src/add-ons/translators/raw/RAWTranslator.cpp | 225 ++ src/add-ons/translators/raw/RAWTranslator.h | 51 + src/add-ons/translators/raw/ReadHelper.h | 179 ++ src/add-ons/translators/raw/TIFF.h | 29 + src/add-ons/translators/raw/main.cpp | 147 + 11 files changed, 3783 insertions(+) create mode 100644 src/add-ons/translators/raw/ConfigView.cpp create mode 100644 src/add-ons/translators/raw/ConfigView.h create mode 100644 src/add-ons/translators/raw/Jamfile create mode 100644 src/add-ons/translators/raw/RAW.cpp create mode 100644 src/add-ons/translators/raw/RAW.h create mode 100644 src/add-ons/translators/raw/RAWTranslator.cpp create mode 100644 src/add-ons/translators/raw/RAWTranslator.h create mode 100644 src/add-ons/translators/raw/ReadHelper.h create mode 100644 src/add-ons/translators/raw/TIFF.h create mode 100644 src/add-ons/translators/raw/main.cpp diff --git a/src/add-ons/translators/Jamfile b/src/add-ons/translators/Jamfile index df0e22d6fc..006ae5ccf8 100644 --- a/src/add-ons/translators/Jamfile +++ b/src/add-ons/translators/Jamfile @@ -8,6 +8,7 @@ SubInclude HAIKU_TOP src add-ons translators jpeg2000 ; SubInclude HAIKU_TOP src add-ons translators libtiff ; SubInclude HAIKU_TOP src add-ons translators png ; SubInclude HAIKU_TOP src add-ons translators ppm ; +SubInclude HAIKU_TOP src add-ons translators raw ; SubInclude HAIKU_TOP src add-ons translators rtf ; SubInclude HAIKU_TOP src add-ons translators sgi ; SubInclude HAIKU_TOP src add-ons translators stxt ; diff --git a/src/add-ons/translators/raw/ConfigView.cpp b/src/add-ons/translators/raw/ConfigView.cpp new file mode 100644 index 0000000000..0d511ec730 --- /dev/null +++ b/src/add-ons/translators/raw/ConfigView.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "ConfigView.h" +#include "RAWTranslator.h" + +#include +#include + +#include +#include + + +ConfigView::ConfigView(const BRect &frame, uint32 resize, uint32 flags) + : BView(frame, "RAWTranslator Settings", resize, flags) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + font_height fontHeight; + be_bold_font->GetHeight(&fontHeight); + float height = fontHeight.descent + fontHeight.ascent + fontHeight.leading; + + BRect rect(10, 10, 200, 10 + height); + BStringView *stringView = new BStringView(rect, "title", "RAW Images"); + stringView->SetFont(be_bold_font); + stringView->ResizeToPreferred(); + AddChild(stringView); + + rect.OffsetBy(0, height + 10); + char version[256]; + sprintf(version, "Version %d.%d.%d, %s", + int(B_TRANSLATION_MAJOR_VERSION(RAW_TRANSLATOR_VERSION)), + int(B_TRANSLATION_MINOR_VERSION(RAW_TRANSLATOR_VERSION)), + int(B_TRANSLATION_REVISION_VERSION(RAW_TRANSLATOR_VERSION)), + __DATE__); + stringView = new BStringView(rect, "version", version); + stringView->ResizeToPreferred(); + AddChild(stringView); + + GetFontHeight(&fontHeight); + height = fontHeight.descent + fontHeight.ascent + fontHeight.leading; + + rect.OffsetBy(0, height + 5); + stringView = new BStringView(rect, "copyright", B_UTF8_COPYRIGHT "2007 Haiku Inc."); + stringView->ResizeToPreferred(); + AddChild(stringView); + + rect.OffsetBy(0, height + 20); + BCheckBox *checkBox = new BCheckBox(rect, "color", "Write 32 bit images on true color input", NULL); + checkBox->ResizeToPreferred(); + AddChild(checkBox); + + rect.OffsetBy(0, height + 10); + checkBox = new BCheckBox(rect, "size", "Enforce valid icon sizes", NULL); + checkBox->ResizeToPreferred(); + checkBox->SetValue(1); + AddChild(checkBox); + + rect.OffsetBy(0, height + 15); + stringView = new BStringView(rect, "valid1", "Valid icon sizes are 16, 32, or 48"); + stringView->ResizeToPreferred(); + AddChild(stringView); + + rect.OffsetBy(0, height + 5); + stringView = new BStringView(rect, "valid2", "pixel in either direction."); + stringView->ResizeToPreferred(); + AddChild(stringView); +} + + +ConfigView::~ConfigView() +{ +} + diff --git a/src/add-ons/translators/raw/ConfigView.h b/src/add-ons/translators/raw/ConfigView.h new file mode 100644 index 0000000000..dfb0abf5fb --- /dev/null +++ b/src/add-ons/translators/raw/ConfigView.h @@ -0,0 +1,19 @@ +/* + * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef CONFIG_VIEW_H +#define CONFIG_VIEW_H + + +#include + + +class ConfigView : public BView { + public: + ConfigView(const BRect &frame, uint32 resize = B_FOLLOW_ALL, + uint32 flags = B_WILL_DRAW); + virtual ~ConfigView(); +}; + +#endif /* CONFIG_VIEW_H */ diff --git a/src/add-ons/translators/raw/Jamfile b/src/add-ons/translators/raw/Jamfile new file mode 100644 index 0000000000..70edbd1584 --- /dev/null +++ b/src/add-ons/translators/raw/Jamfile @@ -0,0 +1,26 @@ +SubDir HAIKU_TOP src add-ons translators raw ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +# Include code from shared translator directory +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons translators shared ] ; + +Translator RAWTranslator : + # RawTranslator classes + main.cpp + RAWTranslator.cpp + ConfigView.cpp + RAW.cpp + + # shared classes + BaseTranslator.cpp + TranslatorSettings.cpp + TranslatorWindow.cpp + + : be translation +; + +Package haiku-translationkit-cvs : + RAWTranslator + : boot home config add-ons Translators + ; diff --git a/src/add-ons/translators/raw/RAW.cpp b/src/add-ons/translators/raw/RAW.cpp new file mode 100644 index 0000000000..315f02ec27 --- /dev/null +++ b/src/add-ons/translators/raw/RAW.cpp @@ -0,0 +1,2859 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. 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 +#include + +#include +#include +#include +#include + + +#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 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), + 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]; + + 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; +} + + +DCRaw::~DCRaw() +{ + delete[] fImages; + delete[] fDecodeBuffer; + + 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; +} + + +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(); + if (tag.tag == lengthTag) + _Thumb().bytes = fRead.Next(); + + 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(); + 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() != 42) + goto quit; + + uint32 offset = fRead.Next(); + 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); + //printf("got 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(); + // ignored + fMeta.iso_speed = fRead.Next(); + } + + if (tag.tag == 4 && tag.length == 27) { + fRead.Next(); + // ignored + fMeta.iso_speed = 50 * pow(2, fRead.Next() / 32.0 - 4); + fRead.Next(); + // ignored + fMeta.aperture = pow(2, fRead.Next() / 64.0); + fMeta.shutter = pow(2, fRead.Next() / -32.0); + } + if (tag.tag == 8 && tag.type == 4) + fMeta.shot_order = fRead.Next(); + 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(); + if (tag.tag == 0x11) { + if (_ParseTIFFImageFileDirectory(baseOffset, fRead.Next()) == 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()) && c != EOF) { + serial = serial * 10 + (isdigit(c) ? c - '0' : c % 10); + } + } + if (tag.tag == 0x81 && tag.type == 4) { + _Raw().data_offset = fRead.Next(); + fRead.Seek(_Raw().data_offset + 41, SEEK_SET); + _Raw().height = fRead.Next() * 2; + _Raw().width = fRead.Next(); + 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())) { + _Thumb().data_offset += baseOffset; + } + if (tag.tag == 0x89 && tag.type == 4) + _Thumb().bytes = fRead.Next(); + 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() - '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(); + } + 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(); + } + 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() ^ fRead.Next() + ^ fRead.Next() ^ fRead.Next()]; + 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() + fRead.Next() + + fRead.Next() + fRead.Next()) / 4; + } + if (tag.tag == 0x201 && tag.length == 4) + goto get2_rggb; + if (tag.tag == 0x401 && tag.length == 4) { + fMeta.black = (fRead.Next() + fRead.Next() + + fRead.Next() + fRead.Next()) / 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(); + fRead.Seek(14, SEEK_CUR); + i = fRead.Next() - 4; + if (tag.tag == 0x76a43207) + fMeta.flip = fRead.Next(); + 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() * 508 * 1.078 / 0x10000; + fMeta.camera_multipliers[2] = fRead.Next() * 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() / 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() << 2; + } + } + if (tag.tag == 0x1017) + fMeta.camera_multipliers[0] = fRead.Next() / 256.0; + if (tag.tag == 0x1018) + fMeta.camera_multipliers[2] = fRead.Next() / 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() / 256.0; + fMeta.camera_multipliers[2] = fRead.Next() / 256.0; + + fRead.SetSwap(previousSwap); + } + + if (tag.tag == 0x2020) + _ParseThumbTag(baseOffset, 257, 258); + if (tag.tag == 0xb028) { + fRead.Seek(fRead.Next(), 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(); + } + } + +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); +//printf("got EXIF tag %u (type %u, length %lu)\n", tag.tag, tag.type, tag.length); + + switch (tag.tag) { + 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(); + 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(); + break; + case 40963: + if (kodak) + _Raw().height = fRead.Next(); + break; + case 41730: + if (fRead.Next() == 0x20002) { + fEXIFFilters = 0; + for (uint32 c = 0; c < 8; c += 2) { + fEXIFFilters |= fRead.Next() * 0x01010101 << c; + } + } + break; + } + + fRead.Seek(nextOffset, SEEK_SET); + } +} + + +// #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(); + if ((fDecodeBitsReset = fDecodeBitsZeroAfterMax && c == 0xff && fRead.Next())) + 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()); + } while (c != EOF && mark >> 4 != 0xffd); + } + _InitDecodeBits(); + } + + uint16* outp = jh->row; + + for (int32 col = 0; col < jh->wide; col++) { + for (int32 c = 0; c < jh->clrs; c++) { + int32 diff = _LosslessJPEGDiff(jh->huff[c]); + *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff); + outp++; + } + } +} + + +// #pragma mark - RAW loaders + + +void +DCRaw::_LoadRAWPacked12() +{ + 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); + } + } +} + + +// #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); + + 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(); + } + } 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); +//printf("%u tags in file\n", 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); + +//printf("got tag: %u\n", tag.tag); + switch (tag.tag) { + default: + //printf("tag %ld NOT HANDLED!\n", tag.tag); + break; + + case 17: + case 18: + if (tag.type == 3 && tag.length == 1) + fMeta.camera_multipliers[(tag.tag - 17) * 2] = fRead.Next() / 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(); + 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(); + } + 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(); + break; + + case 259: // compression + image.compression = fRead.Next(); + break; + + case 262: // Photometric Interpretation + image.photometric_interpretation = fRead.Next(); + 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(); + 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() & 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(); + 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(); + 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() + baseOffset; + break; + } + + while (tag.length--) { + off_t nextOffset = fRead.Position() + sizeof(uint32); + + fRead.Seek(fRead.Next() + 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(); + } + 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(); + } + 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() + 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(); + 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(); + } + 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 + +#if 0 + case 291: // Linearization Table + case 50712: + linear_table(tag.length); + break; +#endif + + 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; + case 50752: + read_shorts (cr2_slice, 3); + break; +#endif + 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); + + 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 + + if ((_Raw().width | _Raw().height) < 0) + _Raw().width = _Raw().height = 0; + if (fMeta.maximum == 0) + fMeta.maximum = (1 << _Raw().bits_per_sample) - 1; + if (fInputWidth == 0) + fInputWidth = _Raw().width; + if (fInputHeight == 0) + fInputHeight = _Raw().height; + + if (fInputWidth == 3936 && fInputHeight == 2624) { + // Pentax K10D and Samsumg GX10 + fInputWidth = 3896; + fInputHeight = 2616; + } + + 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 = (image.width + fShrink) >> fShrink; + fOutputHeight = (image.height + 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) { + switch (image.compression) { + case 32773: + _LoadRAWPacked12(); + break; + + default: + printf("unknown compression: %ld\n", image.compression); + break; + } + + //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); + } + + bufferSize = fOutputWidth * 4 * fOutputHeight; + + 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; +} + diff --git a/src/add-ons/translators/raw/RAW.h b/src/add-ons/translators/raw/RAW.h new file mode 100644 index 0000000000..8db7676ec5 --- /dev/null +++ b/src/add-ons/translators/raw/RAW.h @@ -0,0 +1,170 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef RAW_H +#define RAW_H + + +#include "ReadHelper.h" + +struct jhead; +struct tiff_tag; + + +struct image_meta_info { + char manufacturer[64]; + char model[128]; + char software[64]; + float flash_used; + float iso_speed; + float shutter; + float aperture; + float focal_length; + double pixel_aspect; + uint32 raw_width; + uint32 raw_height; + int flip; + uint32 dng_version; + uint32 shot_order; + int32 black; + double maximum; + float camera_multipliers[4]; + float pre_multipliers[4]; + float rgb_camera[3][4]; /* RGB from camera color */ + time_t timestamp; +}; + +struct image_data_info { + uint32 width; + uint32 height; + uint32 output_width; + uint32 output_height; + uint32 bits_per_sample; + uint32 compression; + uint32 photometric_interpretation; + uint32 flip; + uint32 samples; + uint32 bytes; + off_t data_offset; + bool is_raw; +}; + +class DCRaw { + public: + DCRaw(BPositionIO& stream); + ~DCRaw(); + + status_t Identify(); + status_t ReadImageAt(uint32 index, uint8*& outputBuffer, + size_t& bufferSize); + + void GetMetaInfo(image_meta_info& metaInfo) const; + uint32 CountImages() const; + status_t ImageAt(uint32 index, image_data_info& info) const; + + private: + int32 _AllocateImage(); + image_data_info& _Raw(); + image_data_info& _Thumb(); + void _CorrectIndex(uint32& index) const; + uint16& _Bayer(int32 column, int32 row); + int32 _FilterCoefficient(int32 column, int32 row); + int32 _FlipIndex(uint32 row, uint32 col, uint32 flip); + + // image manipulation and conversion + void _ScaleColors(); + void _WaveletDenoise(); + void _PreInterpolate(); + void _CameraToCIELab(ushort cam[4], float lab[3]); + void _CameraXYZCoefficients(double cam_xyz[4][3]); + void _AdobeCoefficients(char* manufacturer, char* model); + void _BorderInterpolate(uint32 border); + void _AHDInterpolate(); + void _PseudoInverse(double (*in)[3], double (*out)[3], uint32 size); + void _ConvertToRGB(); + void _GammaLookUpTable(uchar* lut); + + void _ParseThumbTag(off_t baseOffset, uint32 offsetTag, uint32 lengthTag); + void _ParseManufacturerTag(off_t baseOffset); + void _ParseEXIF(off_t baseOffset); + + // Lossless JPEG + void _InitDecoder(); + uchar *_MakeDecoder(const uchar* source, int level); + void _InitDecodeBits(); + uint32 _GetDecodeBits(uint32 numBits); + status_t _LosslessJPEGInit(struct jhead* jh, bool infoOnly); + int _LosslessJPEGDiff(struct decode *dindex); + void _LosslessJPEGRow(struct jhead *jh, int jrow); + + // RAW Loader + void _LoadRAWPacked12(); + + // Image writers + void _WriteJPEG(image_data_info& image, uint8* outputBuffer); + void _WriteRGB32(image_data_info& image, uint8* outputBuffer); + + // TIFF + time_t _ParseTIFFTimestamp(bool reversed); + void _ParseTIFFTag(off_t baseOffset, tiff_tag& tag, off_t& offset); + status_t _ParseTIFFImageFileDirectory(off_t baseOffset, uint32 offset); + status_t _ParseTIFFImageFileDirectory(off_t baseOffset); + status_t _ParseTIFF(off_t baseOffset); + + TReadHelper fRead; + image_meta_info fMeta; + image_data_info* fImages; + uint32 fNumImages; + + int32 fRawIndex; + int32 fThumbIndex; + uint32 fDNGVersion; + + uint16 (*fImageData)[4]; + // output image data + int32 fThreshold; + int32 fShrink; + bool fHalfSize; + bool fUseCameraWhiteBalance; + bool fUseAutoWhiteBalance; + bool fRawColor; + bool fUseGamma; + float fBrightness; + int32 fOutputColor; + int32 fHighlight; + int32 fDocumentMode; + uint32 fOutputWidth; + uint32 fOutputHeight; + uint32 fInputWidth; + uint32 fInputHeight; + int32 fTopMargin; + int32 fLeftMargin; + uint32 fUniqueID; + uint32 fColors; + uint16 fWhite[8][8]; + float fUserMultipliers[4]; + uint32* fOutputProfile; + uint32 fOutputBitsPerSample; + int32 (*fHistogram)[4]; + float* cbrt; + float xyz_cam[3][4]; + // TODO: find better names for these... + + // Lossless JPEG + decode* fDecodeBuffer; + decode* fSecondDecode; + decode* fFreeDecode; + int fDecodeLeaf; + uint32 fDecodeBits; + uint32 fDecodeBitsRead; + bool fDecodeBitsReset; + bool fDecodeBitsZeroAfterMax; + + uint32 fFilters; + uint32 fEXIFFilters; + + off_t fCurveOffset; +}; + +#endif // RAW_H diff --git a/src/add-ons/translators/raw/RAWTranslator.cpp b/src/add-ons/translators/raw/RAWTranslator.cpp new file mode 100644 index 0000000000..8a5d1e1832 --- /dev/null +++ b/src/add-ons/translators/raw/RAWTranslator.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RAWTranslator.h" +#include "ConfigView.h" +#include "RAW.h" + +#include +#include +#include + + +// Extensions that ShowImage supports +const char *kDocumentCount = "/documentCount"; +const char *kDocumentIndex = "/documentIndex"; + +// The input formats that this translator supports. +translation_format sInputFormats[] = { + { + RAW_IMAGE_FORMAT, + B_TRANSLATOR_BITMAP, + RAW_IN_QUALITY, + RAW_IN_CAPABILITY, + "image/x-vnd.adobe-dng", + "Adobe Digital Negative" + }, + { + RAW_IMAGE_FORMAT, + B_TRANSLATOR_BITMAP, + RAW_IN_QUALITY, + RAW_IN_CAPABILITY, + "image/x-vnd.photo-raw", + "Digital Photo RAW image" + }, +}; + +// The output formats that this translator supports. +translation_format sOutputFormats[] = { + { + B_TRANSLATOR_BITMAP, + B_TRANSLATOR_BITMAP, + BITS_OUT_QUALITY, + BITS_OUT_CAPABILITY, + "x-be-bitmap", + "Be Bitmap image" + }, +}; + +// Default settings for the Translator +static TranSetting sDefaultSettings[] = { + {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false}, + {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false} +}; + +const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format); +const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format); +const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting); + + +RAWTranslator::RAWTranslator() + : BaseTranslator("Raw Images", "Raw Image Translator", + RAW_TRANSLATOR_VERSION, + sInputFormats, kNumInputFormats, + sOutputFormats, kNumOutputFormats, + "RawTranslator_Settings", + sDefaultSettings, kNumDefaultSettings, + B_TRANSLATOR_BITMAP, RAW_IMAGE_FORMAT) +{ +} + + +RAWTranslator::~RAWTranslator() +{ +} + + +status_t +RAWTranslator::DerivedIdentify(BPositionIO *stream, + const translation_format *format, BMessage *settings, + translator_info *info, uint32 outType) +{ + if (!outType) + outType = B_TRANSLATOR_BITMAP; + if (outType != B_TRANSLATOR_BITMAP) + return B_NO_TRANSLATOR; + + DCRaw raw(*stream); + status_t status; + + try { + status = raw.Identify(); + } catch (status_t error) { + status = error; + } + + if (status < B_OK) + return B_NO_TRANSLATOR; + + image_meta_info meta; + raw.GetMetaInfo(meta); + + if (settings) { + int32 count = raw.CountImages(); + + // Add page count to ioExtension + settings->RemoveName(kDocumentCount); + settings->AddInt32(kDocumentCount, count); + + // Check if a document index has been specified + int32 index; + if (settings->FindInt32(kDocumentIndex, &index) == B_OK) + index--; + else + index = 0; + + if (index < 0 || index >= count) + return B_NO_TRANSLATOR; + } + + info->type = RAW_IMAGE_FORMAT; + info->group = B_TRANSLATOR_BITMAP; + info->quality = RAW_IN_QUALITY; + info->capability = RAW_IN_CAPABILITY; + snprintf(info->name, sizeof(info->name), "%s RAW image", meta.manufacturer); + strcpy(info->MIME, "image/x-vnd.photo-raw"); + + return B_OK; +} + + +status_t +RAWTranslator::DerivedTranslate(BPositionIO* source, + const translator_info* info, BMessage* settings, + uint32 outType, BPositionIO* target, int32 baseType) +{ + if (!outType) + outType = B_TRANSLATOR_BITMAP; + if (outType != B_TRANSLATOR_BITMAP || baseType != 0) + return B_NO_TRANSLATOR; + + DCRaw raw(*source); + + int32 imageIndex = 0; + uint8* buffer = NULL; + size_t bufferSize; + status_t status; + + try { + status = raw.Identify(); + + if (status == B_OK && settings) { + // Check if a document index has been specified + if (settings->FindInt32(kDocumentIndex, &imageIndex) == B_OK) + imageIndex--; + else + imageIndex = 0; + + if (imageIndex < 0 || imageIndex >= (int32)raw.CountImages()) + status = B_BAD_VALUE; + } + if (status == B_OK) + status = raw.ReadImageAt(imageIndex, buffer, bufferSize); + } catch (status_t error) { + status = error; + } + + if (status < B_OK) + return B_NO_TRANSLATOR; + + image_meta_info meta; + raw.GetMetaInfo(meta); + + image_data_info data; + raw.ImageAt(imageIndex, data); + + if (!data.is_raw) + return B_NO_TRANSLATOR; + + uint32 dataSize = data.output_width * 4 * data.output_height; + + TranslatorBitmap header; + header.magic = B_TRANSLATOR_BITMAP; + header.bounds.Set(0, 0, data.output_width - 1, data.output_height - 1); + header.rowBytes = data.output_width * 4; + header.colors = B_RGB32; + header.dataSize = dataSize; + + // write out Be's Bitmap header + swap_data(B_UINT32_TYPE, &header, sizeof(TranslatorBitmap), + B_SWAP_HOST_TO_BENDIAN); + target->Write(&header, sizeof(TranslatorBitmap)); + + ssize_t bytesWritten = target->Write(buffer, dataSize); + if (bytesWritten < B_OK) + return bytesWritten; + + if ((size_t)bytesWritten != dataSize) + return B_IO_ERROR; + + return B_OK; +} + + +BView * +RAWTranslator::NewConfigView(TranslatorSettings *settings) +{ + return new ConfigView(BRect(0, 0, 225, 175)); +} + + +// #pragma mark - + + +BTranslator * +make_nth_translator(int32 n, image_id you, uint32 flags, ...) +{ + if (n != 0) + return NULL; + + return new RAWTranslator(); +} + diff --git a/src/add-ons/translators/raw/RAWTranslator.h b/src/add-ons/translators/raw/RAWTranslator.h new file mode 100644 index 0000000000..32dba0a45b --- /dev/null +++ b/src/add-ons/translators/raw/RAWTranslator.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef RAW_TRANSLATOR_H +#define RAW_TRANSLATOR_H + + +#include "BaseTranslator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define RAW_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(0, 1, 0) +#define RAW_IMAGE_FORMAT 'RAWI' + +#define RAW_IN_QUALITY 0.95 +#define RAW_IN_CAPABILITY 0.95 +#define BITS_IN_QUALITY 1 +#define BITS_IN_CAPABILITY 1 + +#define RAW_OUT_QUALITY 0.8 +#define RAW_OUT_CAPABILITY 0.8 +#define BITS_OUT_QUALITY 1 +#define BITS_OUT_CAPABILITY 0.9 + + +class RAWTranslator : public BaseTranslator { + public: + RAWTranslator(); + virtual ~RAWTranslator(); + + virtual status_t DerivedIdentify(BPositionIO *inSource, + const translation_format *inFormat, BMessage *ioExtension, + translator_info *outInfo, uint32 outType); + + virtual status_t DerivedTranslate(BPositionIO *inSource, + const translator_info *inInfo, BMessage *ioExtension, + uint32 outType, BPositionIO *outDestination, int32 baseType); + + virtual BView *NewConfigView(TranslatorSettings *settings); +}; + +#endif // RAW_TRANSLATOR_H diff --git a/src/add-ons/translators/raw/ReadHelper.h b/src/add-ons/translators/raw/ReadHelper.h new file mode 100644 index 0000000000..6f6bca52c4 --- /dev/null +++ b/src/add-ons/translators/raw/ReadHelper.h @@ -0,0 +1,179 @@ +/* + * Copyright 2004-2007, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef READ_HELPER_H +#define READ_HELPER_H + + +#include "TIFF.h" + +#include +#include + + +template +inline void +byte_swap(T &/*data*/) +{ + // Specialize for data types which actually swap +// printf("DEAD MEAT!\n"); +// exit(1); +} + + +template<> +inline void +byte_swap(float &data) +{ + data = __swap_float(data); +} + + +template<> +inline void +byte_swap(int32 &data) +{ + data = __swap_int32(data); +} + + +template<> +inline void +byte_swap(uint32 &data) +{ + data = __swap_int32(data); +} + + +template<> +inline void +byte_swap(int16 &data) +{ + data = __swap_int16(data); +} + + +template<> +inline void +byte_swap(uint16 &data) +{ + data = __swap_int16(data); +} + + +class TReadHelper { + public: + TReadHelper(BPositionIO& stream) + : + fStream(&stream, 65536, false), + fError(B_OK), + fSwap(false) + { + } + + template inline void + operator()(T &data) + { + fError = fStream.Read((void *)&data, sizeof(T)); + if (fError > B_OK) { + if (IsSwapping()) + byte_swap(data); + return; + } + + if (fError == 0) + fError = B_ERROR; + throw fError; + } + + template inline void + operator()(T data, size_t length) + { + fError = fStream.Read((void *)data, length); + if (fError < (ssize_t)length) + fError = B_ERROR; + + if (fError >= B_OK) + return; + + throw fError; + } + + template inline T + Next() + { + T value; + fError = fStream.Read((void *)&value, sizeof(T)); + if (fError > B_OK) { + if (IsSwapping()) + byte_swap(value); + return value; + } + + if (fError == 0) + fError = B_ERROR; + throw fError; + } + + inline uint32 + Next(uint16 type) + { + if (type == TIFF_UINT16_TYPE || type == TIFF_INT16_TYPE) + return Next(); + + return Next(); + } + + inline double + NextDouble(uint16 type) + { + switch (type) { + case TIFF_UINT16_TYPE: + return Next(); + case TIFF_UINT32_TYPE: + return Next(); + case TIFF_UFRACTION_TYPE: + { + double value = Next(); + return value / Next(); + } + case TIFF_INT16_TYPE: + return Next(); + case TIFF_INT32_TYPE: + return Next(); + case TIFF_FRACTION_TYPE: + { + double value = Next(); + return value / Next(); + } + case TIFF_FLOAT_TYPE: + return Next(); + case TIFF_DOUBLE_TYPE: + return Next(); + + default: + return Next(); + } + } + + status_t Status() + { return fError >= B_OK ? B_OK : fError; }; + + off_t Seek(off_t offset, int32 mode) + { return fStream.Seek(offset, mode); } + off_t Position() + { return fStream.Position(); } + + void SetSwap(bool yesNo) { fSwap = yesNo; }; + bool IsSwapping() { return fSwap; }; + + BPositionIO& Stream() { return fStream; } + + private: + BBufferIO fStream; + status_t fError; + bool fSwap; +}; + +#endif // READ_HELPER_H diff --git a/src/add-ons/translators/raw/TIFF.h b/src/add-ons/translators/raw/TIFF.h new file mode 100644 index 0000000000..fcb0552c42 --- /dev/null +++ b/src/add-ons/translators/raw/TIFF.h @@ -0,0 +1,29 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef TIFF_H +#define TIFF_H + + +#include + + +enum types { + TIFF_UINT16_TYPE = 3, + TIFF_UINT32_TYPE, + TIFF_UFRACTION_TYPE, + TIFF_INT16_TYPE = 8, + TIFF_INT32_TYPE, + TIFF_FRACTION_TYPE, + TIFF_FLOAT_TYPE, + TIFF_DOUBLE_TYPE, +}; + +struct tiff_tag { + uint16 tag; + uint16 type; + uint32 length; +}; + +#endif // TIFF_H diff --git a/src/add-ons/translators/raw/main.cpp b/src/add-ons/translators/raw/main.cpp new file mode 100644 index 0000000000..df6c9880cb --- /dev/null +++ b/src/add-ons/translators/raw/main.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RAWTranslator.h" +#include "RAW.h" +#include "TranslatorWindow.h" + +#include + +#define TEST_MODE 0 +#if TEST_MODE +# include +# include +# include +# include +#endif + +#include +#include + + +int +main(int argc, char **argv) +{ + BApplication app("application/x-vnd.haiku-raw-translator"); + +#if TEST_MODE + if (argc > 1) { + for (int i = 1; i < argc; i++) { + BFile file; + status_t status = file.SetTo(argv[i], B_READ_ONLY); + if (status != B_OK) { + fprintf(stderr, "Cannot read file %s: %s\n", argv[i], + strerror(status)); + continue; + } + + DCRaw raw(file); + + try { + status = raw.Identify(); + } catch (status_t error) { + status = error; + } + + if (status < B_OK) { + fprintf(stderr, "Could not identify file %s: %s\n", + argv[i], strerror(status)); + continue; + } + + image_meta_info meta; + raw.GetMetaInfo(meta); + + printf("manufacturer: %s\n", meta.manufacturer); + printf("model: %s\n", meta.model); + printf("software: %s\n", meta.software); + printf("flash used: %g\n", meta.flash_used); + printf("ISO speed: %g\n", meta.iso_speed); + printf("shutter: "); + if (meta.shutter >= 1) + printf("%g sec\n", meta.shutter); + else + printf("1/%g sec\n", 1 / meta.shutter); + printf("aperture: %g\n", meta.aperture); + printf("focal length: %g mm\n", meta.focal_length); + printf("pixel aspect: %g\n", meta.pixel_aspect); + printf("flip: %d\n", meta.flip); + printf("shot order: %ld\n", meta.shot_order); + printf("DNG version: %ld\n", meta.dng_version); + printf("Camera White Balance:"); + for (int32 i = 0; i < 4; i++) { + printf(" %g", meta.camera_multipliers[i]); + } + putchar('\n'); + + for (int32 i = 0; i < (int32)raw.CountImages(); i++) { + image_data_info data; + raw.ImageAt(i, data); + + printf(" [%ld] %s %lu x %lu (%ld bits per sample, compression %ld)\n", + i, data.is_raw ? "RAW " : "JPEG", + data.width, data.height, data.bits_per_sample, data.compression); + + if (!data.is_raw) { + // write data to file + uint8* buffer; + size_t bufferSize; + try { + status = raw.ReadImageAt(i, buffer, bufferSize); + } catch (status_t error) { + status = error; + } + if (status == B_OK) { + BString name = "/tmp/output"; + name << i << ".jpg"; + BFile output(name.String(), + B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); + output.Write(buffer, bufferSize); + } + } else { + RAWTranslator translator; + BBitmapStream output; + BBitmap* bitmap; + + status_t status = translator.DerivedTranslate(&file, NULL, + NULL, B_TRANSLATOR_BITMAP, &output, 0); + if (status == B_OK) + status = output.DetachBitmap(&bitmap); + if (status == B_OK) { + BWindow* window = new BWindow(BRect(50, 50, + bitmap->Bounds().Width() + 50, + bitmap->Bounds().Height() + 50), + "RAW", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS + | B_NOT_RESIZABLE); + BView* view = new BView(window->Bounds(), NULL, + B_WILL_DRAW, B_FOLLOW_NONE); + window->AddChild(view); + window->Show(); + snooze(300000); + window->Lock(); + view->DrawBitmap(bitmap, window->Bounds()); + view->Sync(); + window->Unlock(); + delete bitmap; + + wait_for_thread(window->Thread(), &status); + } + } + } + } + return 0; + } +#endif + + status_t status = LaunchTranslatorWindow(new RAWTranslator, "RAW Settings", + BRect(0, 0, 225, 175)); + if (status != B_OK) + return 1; + + app.Run(); + return 0; +} +