diff --git a/clean.bat b/clean.bat index 52e77f954..7196af4b0 100644 --- a/clean.bat +++ b/clean.bat @@ -20,4 +20,3 @@ del .\rtdata\Makefile del .\rtengine\librtengine.so del .\rtengine\librtengine.a del .\rtgui\rawtherapee -del .\rtexif\librtexif.a diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index a4ab82f3d..43d658e66 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -1,4 +1,3 @@ -<<<<<<< HEAD if(EXTRA_INCDIR) include_directories("${EXTRA_INCDIR}") endif() diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc index 3c20ea0c1..7d9295c71 100644 --- a/rtengine/dcp.cc +++ b/rtengine/dcp.cc @@ -21,9 +21,7 @@ #include #include #include -#include -#include -#include +#include #include "dcp.h" @@ -35,9 +33,15 @@ #include "rawimagesource.h" #include "rt_math.h" #include "utils.h" -#include "../rtexif/rtexif.h" #include "../rtgui/options.h" +namespace rtengine +{ + +extern const Settings* settings; + +} + using namespace rtengine; namespace @@ -48,7 +52,7 @@ namespace DCPProfile::Matrix invert3x3(const DCPProfile::Matrix& a) { DCPProfile::Matrix res = a; - if (!invertMatrix(a, res)) { + if (!invertMatrix(a, res) && settings->verbose) { std::cerr << "DCP matrix cannot be inverted! Expect weird output." << std::endl; } return res; @@ -333,6 +337,8 @@ double xyCoordToTemperature(const std::array& white_xy) // Search for line pair coordinate is between. double last_dt = 0.0; + double last_dv = 0.0; + double last_du = 0.0; for (uint32_t index = 1; index <= 30; ++index) { // Convert slope to delta-u and delta-v, with length 1. @@ -368,11 +374,23 @@ double xyCoordToTemperature(const std::array& white_xy) // Interpolate the temperature. res = 1.0e6 / (temp_table[index - 1].r * f + temp_table[index].r * (1.0 - f)); + + // Find delta from black body point to test coordinate. + uu = u - (temp_table [index - 1].u * f + temp_table [index].u * (1.0 - f)); + vv = v - (temp_table [index - 1].v * f + temp_table [index].v * (1.0 - f)); + // Interpolate vectors along slope. + du = du * (1.0 - f) + last_du * f; + dv = dv * (1.0 - f) + last_dv * f; + len = sqrt (du * du + dv * dv); + du /= len; + dv /= len; break; } // Try next line pair. last_dt = dt; + last_du = du; + last_dv = dv; } return res; @@ -431,7 +449,6 @@ std::map getAliases(const Glib::ustring& profile_dir) class DCPMetadata { -private: enum TagType { INVALID = 0, BYTE = 1, @@ -455,17 +472,17 @@ private: }; public: - explicit DCPMetadata(FILE* file) : - file_(file), - order_(UNKNOWN) - { - } + explicit DCPMetadata(FILE *file): order_(UNKNOWN), file_(file) {} bool parse() { - if (!file_) { + int offset = 0; + FILE *f = file_; + if (!f) { #ifndef NDEBUG - std::cerr << "ERROR: No file opened." << std::endl; + if (settings->verbose) { + std::cerr << "ERROR : no file opened !" << std::endl; + } #endif return false; } @@ -473,28 +490,31 @@ public: setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf // read tiff header - std::fseek(file_, 0, SEEK_SET); - std::uint16_t bo; - std::fread(&bo, 1, 2, file_); - order_ = ByteOrder(bo); + fseek(f, 0, SEEK_SET); + unsigned short bo; + fread(&bo, 1, 2, f); + order_ = ByteOrder(int(bo)); + + get2(f, order_); + if (!offset) { + offset = get4(f, order_); + } - get2(); // Skip + // seek to IFD + fseek(f, offset, SEEK_SET); - // Seek to IFD - const std::size_t offset = get4(); - std::fseek(file_, offset, SEEK_SET); + // first read the IFD directory + int numtags = get2(f, order_); - // First read the IFD directory - const std::uint16_t numtags = get2(); - - if (numtags > 1000) { // KodakIfd has lots of tags, thus 1000 as the limit + if (numtags <= 0 || numtags > 1000) { // KodakIfd has lots of tags, thus 1000 as the limit return false; } - for (std::uint16_t i = 0; i < numtags; ++i) { - Tag tag; - if (parseTag(tag)) { - tags_[tag.id] = std::move(tag); + int base = 0; + for (int i = 0; i < numtags; i++) { + Tag t; + if (parse_tag(t, f, base, order_)) { + tags_[t.id] = std::move(t); } } @@ -505,304 +525,221 @@ public: { return tags_.find(id) != tags_.end(); } - - std::string toString(int id) const + + std::string toString(int id) { - const Tags::const_iterator tag = tags_.find(id); - if (tag != tags_.end()) { - if (tag->second.type == ASCII) { - return std::string(tag->second.value.begin(), tag->second.value.end()).c_str(); + auto it = tags_.find(id); + if (it != tags_.end()) { + auto &t = it->second; + if (t.type == ASCII) { + std::ostringstream buf; + unsigned char *value = &(t.value[0]); + buf << value; + return buf.str(); } } - return {}; + return ""; } - std::int32_t toInt(int id, std::size_t offset = 0, TagType as_type = INVALID) const + int toInt(int id, int ofs=0, TagType astype=INVALID) { - const Tags::const_iterator tag = tags_.find(id); - if (tag == tags_.end()) { + auto it = tags_.find(id); + if (it == tags_.end()) { return 0; } - if (as_type == INVALID) { - as_type = tag->second.type; + auto &t = it->second; + int a; + unsigned char *value = &(t.value[0]); + + if (astype == INVALID) { + astype = t.type; } - switch (as_type) { - case SBYTE: { - if (offset < tag->second.value.size()) { - return static_cast(tag->second.value[offset]); - } - return 0; - } - - case BYTE: { - if (offset < tag->second.value.size()) { - return tag->second.value[offset]; - } - return 0; - } - - case SSHORT: { - if (offset + 1 < tag->second.value.size()) { - return static_cast(sget2(tag->second.value.data() + offset)); - } - return 0; - } - - case SHORT: { - if (offset + 1 < tag->second.value.size()) { - return sget2(tag->second.value.data() + offset); - } - return 0; - } - - case SLONG: - case LONG: { - if (offset + 3 < tag->second.value.size()) { - return sget4(tag->second.value.data() + offset); - } - return 0; - } - - case SRATIONAL: - case RATIONAL: { - if (offset + 7 < tag->second.value.size()) { - const std::uint32_t denominator = sget4(tag->second.value.data() + offset + 4); - return - denominator == 0 - ? 0 - : static_cast(sget4(tag->second.value.data() + offset)) / denominator; - } - return 0; - } - - case FLOAT: { - return toDouble(id, offset); - } - - default: { - return 0; - } + switch (astype) { + case SBYTE: + return reinterpret_cast(value)[ofs]; + case BYTE: + return value[ofs]; + case SSHORT: + return int2_to_signed(sget2(value + ofs, order_)); + case SHORT: + return sget2(value + ofs, order_); + case SLONG: + case LONG: + return sget4 (value + ofs, order_); + case SRATIONAL: + case RATIONAL: + a = sget4(value + ofs + 4, order_); + return a == 0 ? 0 : int(sget4(value + ofs, order_)) / a; + case FLOAT: + return toDouble(id, ofs); + default: + return 0; + } } - - int toShort(int id, std::size_t offset = 0) const + + int toShort(int id, int ofs=0) { - return toInt(id, offset, SHORT); + return toInt(id, ofs, SHORT); } - - double toDouble(int id, std::size_t offset = 0) const + + double toDouble(int id, int ofs=0) { - const Tags::const_iterator tag = tags_.find(id); - if (tag == tags_.end()) { + auto it = tags_.find(id); + if (it == tags_.end()) { return 0.0; } - switch (tag->second.type) { - case SBYTE: { - if (offset < tag->second.value.size()) { - return static_cast(tag->second.value[offset]); - } - return 0.0; - } + auto &t = it->second; + + union IntFloat { + uint32_t i; + float f; + } conv; - case BYTE: { - if (offset < tag->second.value.size()) { - return tag->second.value[offset]; - } - return 0.0; - } - - case SSHORT: { - if (offset + 1 < tag->second.value.size()) { - return static_cast(sget2(tag->second.value.data() + offset)); - } - return 0.0; - } - - case SHORT: { - if (offset + 1 < tag->second.value.size()) { - return sget2(tag->second.value.data() + offset); - } - return 0.0; - } - - case SLONG: - case LONG: { - if (offset + 3 < tag->second.value.size()) { - return sget4(tag->second.value.data() + offset); - } - return 0.0; - } - - case SRATIONAL: - case RATIONAL: { - if (offset + 7 < tag->second.value.size()) { - const std::int32_t numerator = sget4(tag->second.value.data() + offset); - const std::int32_t denominator = sget4(tag->second.value.data() + offset + 4); - return - denominator == 0 - ? 0.0 - : static_cast(numerator) / static_cast(denominator); - } - return 0.0; - } - - case FLOAT: { - union IntFloat { - std::uint32_t i; - float f; - } conv; - - conv.i = sget4(tag->second.value.data() + offset); - return conv.f; // IEEE FLOATs are already C format, they just need a recast - } - - default: { - return 0.0; - } + int ud, dd; + unsigned char *value = &(t.value[0]); + + switch (t.type) { + case SBYTE: + return int((reinterpret_cast (value))[ofs]); + case BYTE: + return int(value[ofs]); + case SSHORT: + return int2_to_signed(sget2(value + ofs, order_)); + case SHORT: + return sget2(value + ofs, order_); + case SLONG: + case LONG: + return sget4(value + ofs, order_); + case SRATIONAL: + case RATIONAL: + ud = sget4(value + ofs, order_); + dd = sget4(value + ofs + 4, order_); + return (dd ? double(ud)/double(dd) : 0.0); + case FLOAT: + conv.i = sget4(value + ofs, order_); + return conv.f; // IEEE FLOATs are already C format, they just need a recast + default: + return 0.; + } } - unsigned int getCount(int id) const + unsigned int getCount(int id) { - const Tags::const_iterator tag = tags_.find(id); - if (tag != tags_.end()) { - return tag->second.count; + auto it = tags_.find(id); + if (it != tags_.end()) { + return it->second.count; } return 0; } private: - struct Tag { - int id; - std::vector value; - TagType type; - unsigned int count; - }; - - using Tags = std::unordered_map; - - std::uint16_t sget2(const std::uint8_t* s) const + static unsigned short sget2(unsigned char *s, ByteOrder order) { - if (order_ == INTEL) { + if (order == INTEL) { return s[0] | s[1] << 8; } else { return s[0] << 8 | s[1]; } } - - std::uint32_t sget4(const std::uint8_t* s) const + + static int sget4(unsigned char *s, ByteOrder order) { - if (order_ == INTEL) { + if (order == INTEL) { return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; } else { return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; } } - - std::uint16_t get2() - { - std::uint16_t res = std::numeric_limits::max(); - std::fread(&res, 1, 2, file_); - return sget2(reinterpret_cast(&res)); + + static unsigned short get2(FILE* f, ByteOrder order) + { + unsigned char str[2] = { 0xff, 0xff }; + fread (str, 1, 2, f); + return sget2(str, order); } - - std::uint32_t get4() + + static int get4(FILE *f, ByteOrder order) + { + unsigned char str[4] = { 0xff, 0xff, 0xff, 0xff }; + fread (str, 1, 4, f); + return sget4 (str, order); + } + + static short int int2_to_signed(short unsigned int i) { - std::uint32_t res = std::numeric_limits::max(); - std::fread(&res, 1, 4, file_); - return sget4(reinterpret_cast(&res)); + union { + short unsigned int i; + short int s; + } u; + u.i = i; + return u.s; } static int getTypeSize(TagType type) { - switch (type) { - case INVALID: - case BYTE: - case ASCII: - case SBYTE: - case UNDEFINED: { - return 1; - } - - case SHORT: - case SSHORT: { - return 2; - } - - case LONG: - case SLONG: - case FLOAT: { - return 4; - } - - case RATIONAL: - case SRATIONAL: - case DOUBLE: { - return 8; - } - } - - return 1; + return ("11124811248484"[type < 14 ? type : 0] - '0'); } - bool parseTag(Tag& tag) + struct Tag { + int id; + std::vector value; + TagType type; + unsigned int count; + + Tag(): id(0), value(), type(INVALID), count(0) {} + }; + + bool parse_tag(Tag &t, FILE *f, int base, ByteOrder order) { - tag.id = get2(); - tag.type = TagType(get2()); - tag.count = std::max(1U, get4()); + t.id = get2(f, order); + t.type = (TagType)get2(f, order); + t.count = get4(f, order); - // Filter out invalid tags - // Note: The large count is to be able to pass LeafData ASCII tag which can be up to almost 10 megabytes, + if (!t.count) { + t.count = 1; + } + + // filter out invalid tags + // note the large count is to be able to pass LeafData ASCII tag which can be up to almost 10 megabytes, // (only a small part of it will actually be parsed though) - if ( - tag.type == INVALID - || tag.type > DOUBLE - || tag.count > 10 * 1024 * 1024 - ) { - tag.type = INVALID; + if ((int)t.type < 1 || (int)t.type > 12 || t.count > 10 * 1024 * 1024) { + t.type = INVALID; return false; } - // Store next Tag's position in file - const std::size_t saved_position = std::ftell(file_) + 4; + // store next Tag's position in file + int save = ftell(f) + 4; - // Load value field (possibly seek before) - const std::size_t value_size = tag.count * getTypeSize(tag.type); + // load value field (possibly seek before) + int valuesize = t.count * getTypeSize(t.type); - if (value_size > 4) { - if (std::fseek(file_, get4(), SEEK_SET) == -1) { - tag.type = INVALID; - return false; - } + if (valuesize > 4) { + fseek(f, get4(f, order) + base, SEEK_SET); } - // Read value - tag.value.resize(value_size + 1); - const std::size_t read = std::fread(tag.value.data(), 1, value_size, file_); - if (read != value_size) { - tag.type = INVALID; - return false; - } - tag.value[read] = '\0'; - - // Seek back to the saved position - std::fseek(file_, saved_position, SEEK_SET); + // read value + t.value.resize(valuesize + 1); + auto readSize = fread(&(t.value[0]), 1, valuesize, f); + t.value[readSize] = '\0'; + // seek back to the saved position + fseek(f, save, SEEK_SET); return true; } - FILE* const file_; - - Tags tags_; + std::unordered_map tags_; ByteOrder order_; + FILE *file_; }; } // namespace -struct DCPProfileApplyState::Data { +struct DCPProfile::ApplyState::Data { float pro_photo[3][3]; float work[3][3]; bool already_pro_photo; @@ -811,12 +748,14 @@ struct DCPProfileApplyState::Data { float bl_scale; }; -DCPProfileApplyState::DCPProfileApplyState() : +DCPProfile::ApplyState::ApplyState() : data(new Data{}) { } -DCPProfileApplyState::~DCPProfileApplyState() = default; +DCPProfile::ApplyState::~ApplyState() +{ +} DCPProfile::DCPProfile(const Glib::ustring& filename) : has_color_matrix_1(false), @@ -833,22 +772,22 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : constexpr int tiff_float_size = 4; enum TagKey { - TAG_KEY_COLOR_MATRIX_1 = 50721, - TAG_KEY_COLOR_MATRIX_2 = 50722, - TAG_KEY_PROFILE_HUE_SAT_MAP_DIMS = 50937, - TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_1 = 50938, - TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_2 = 50939, - TAG_KEY_PROFILE_TONE_CURVE = 50940, - TAG_KEY_PROFILE_TONE_COPYRIGHT = 50942, - TAG_KEY_CALIBRATION_ILLUMINANT_1 = 50778, - TAG_KEY_CALIBRATION_ILLUMINANT_2 = 50779, - TAG_KEY_FORWARD_MATRIX_1 = 50964, - TAG_KEY_FORWARD_MATRIX_2 = 50965, - TAG_KEY_PROFILE_LOOK_TABLE_DIMS = 50981, // ProfileLookup is the low quality variant - TAG_KEY_PROFILE_LOOK_TABLE_DATA = 50982, - TAG_KEY_PROFILE_HUE_SAT_MAP_ENCODING = 51107, - TAG_KEY_PROFILE_LOOK_TABLE_ENCODING = 51108, - TAG_KEY_BASELINE_EXPOSURE_OFFSET = 51109 + COLOR_MATRIX_1 = 50721, + COLOR_MATRIX_2 = 50722, + PROFILE_HUE_SAT_MAP_DIMS = 50937, + PROFILE_HUE_SAT_MAP_DATA_1 = 50938, + PROFILE_HUE_SAT_MAP_DATA_2 = 50939, + PROFILE_TONE_CURVE = 50940, + PROFILE_TONE_COPYRIGHT = 50942, + CALIBRATION_ILLUMINANT_1 = 50778, + CALIBRATION_ILLUMINANT_2 = 50779, + FORWARD_MATRIX_1 = 50964, + FORWARD_MATRIX_2 = 50965, + PROFILE_LOOK_TABLE_DIMS = 50981, // ProfileLookup is the low quality variant + PROFILE_LOOK_TABLE_DATA = 50982, + PROFILE_HUE_SAT_MAP_ENCODING = 51107, + PROFILE_LOOK_TABLE_ENCODING = 51108, + BASELINE_EXPOSURE_OFFSET = 51109 }; static const float adobe_camera_raw_default_curve[] = { @@ -1114,53 +1053,55 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : FILE* const file = g_fopen(filename.c_str(), "rb"); if (file == nullptr) { - printf ("Unable to load DCP profile '%s' !", filename.c_str()); + //printf ("Unable to load DCP profile '%s' !", filename.c_str()); return; } DCPMetadata md(file); if (!md.parse()) { - printf ("Unable to load DCP profile '%s'.", filename.c_str()); + //printf ("Unable to load DCP profile '%s'.", filename.c_str()); return; } light_source_1 = - md.find(TAG_KEY_CALIBRATION_ILLUMINANT_1) - ? md.toShort(TAG_KEY_CALIBRATION_ILLUMINANT_1) - : -1; + md.find(CALIBRATION_ILLUMINANT_1) ? + md.toShort(CALIBRATION_ILLUMINANT_1) : + -1; light_source_2 = - md.find(TAG_KEY_CALIBRATION_ILLUMINANT_2) - ? md.toShort(TAG_KEY_CALIBRATION_ILLUMINANT_2) - : -1; + md.find(CALIBRATION_ILLUMINANT_2) ? + md.toShort(CALIBRATION_ILLUMINANT_2) : + -1; temperature_1 = calibrationIlluminantToTemperature(light_source_1); temperature_2 = calibrationIlluminantToTemperature(light_source_2); - const bool has_second_hue_sat = md.find(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_2); // Some profiles have two matrices, but just one huesat + const bool has_second_hue_sat = md.find(PROFILE_HUE_SAT_MAP_DATA_2); // Some profiles have two matrices, but just one huesat // Fetch Forward Matrices, if any - has_forward_matrix_1 = md.find(TAG_KEY_FORWARD_MATRIX_1); + has_forward_matrix_1 = md.find(FORWARD_MATRIX_1); if (has_forward_matrix_1) { for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { - forward_matrix_1[row][col] = md.toDouble(TAG_KEY_FORWARD_MATRIX_1, (col + row * 3) * 8); + forward_matrix_1[row][col] = md.toDouble(FORWARD_MATRIX_1, (col + row * 3) * 8); } } } - has_forward_matrix_2 = md.find(TAG_KEY_FORWARD_MATRIX_2); + has_forward_matrix_2 = md.find(FORWARD_MATRIX_2); if (has_forward_matrix_2) { for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { - forward_matrix_2[row][col] = md.toDouble(TAG_KEY_FORWARD_MATRIX_2, (col + row * 3) * 8); + forward_matrix_2[row][col] = md.toDouble(FORWARD_MATRIX_2, (col + row * 3) * 8); } } } // Color Matrix (one is always there) - if (!md.find(TAG_KEY_COLOR_MATRIX_1)) { - std::cerr << "DCP '" << filename << "' is missing 'ColorMatrix1'. Skipped." << std::endl; + if (!md.find(COLOR_MATRIX_1)) { + if (settings->verbose) { + std::cerr << "DCP '" << filename << "' is missing 'ColorMatrix1'. Skipped." << std::endl; + } fclose(file); return; } @@ -1169,24 +1110,24 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { - color_matrix_1[row][col] = md.toDouble(TAG_KEY_COLOR_MATRIX_1, (col + row * 3) * 8); + color_matrix_1[row][col] = md.toDouble(COLOR_MATRIX_1, (col + row * 3) * 8); } } - if (md.find(TAG_KEY_PROFILE_LOOK_TABLE_DIMS)) { - look_info.hue_divisions = md.toInt(TAG_KEY_PROFILE_LOOK_TABLE_DIMS, 0); - look_info.sat_divisions = md.toInt(TAG_KEY_PROFILE_LOOK_TABLE_DIMS, 4); - look_info.val_divisions = md.toInt(TAG_KEY_PROFILE_LOOK_TABLE_DIMS, 8); + if (md.find(PROFILE_LOOK_TABLE_DIMS)) { + look_info.hue_divisions = md.toInt(PROFILE_LOOK_TABLE_DIMS, 0); + look_info.sat_divisions = md.toInt(PROFILE_LOOK_TABLE_DIMS, 4); + look_info.val_divisions = md.toInt(PROFILE_LOOK_TABLE_DIMS, 8); - look_info.srgb_gamma = md.find(TAG_KEY_PROFILE_LOOK_TABLE_ENCODING) && md.toInt(TAG_KEY_PROFILE_LOOK_TABLE_ENCODING); + look_info.srgb_gamma = md.find(PROFILE_LOOK_TABLE_ENCODING) && md.toInt(PROFILE_LOOK_TABLE_ENCODING); - look_info.array_count = md.getCount(TAG_KEY_PROFILE_LOOK_TABLE_DATA) / 3; + look_info.array_count = md.getCount(PROFILE_LOOK_TABLE_DATA) / 3; look_table.resize(look_info.array_count); for (unsigned int i = 0; i < look_info.array_count; i++) { - look_table[i].hue_shift = md.toDouble(TAG_KEY_PROFILE_LOOK_TABLE_DATA, (i * 3) * tiff_float_size); - look_table[i].sat_scale = md.toDouble(TAG_KEY_PROFILE_LOOK_TABLE_DATA, (i * 3 + 1) * tiff_float_size); - look_table[i].val_scale = md.toDouble(TAG_KEY_PROFILE_LOOK_TABLE_DATA, (i * 3 + 2) * tiff_float_size); + look_table[i].hue_shift = md.toDouble(PROFILE_LOOK_TABLE_DATA, (i * 3) * tiff_float_size); + look_table[i].sat_scale = md.toDouble(PROFILE_LOOK_TABLE_DATA, (i * 3 + 1) * tiff_float_size); + look_table[i].val_scale = md.toDouble(PROFILE_LOOK_TABLE_DATA, (i * 3 + 2) * tiff_float_size); } // Precalculated constants for table application @@ -1203,20 +1144,20 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : look_info.pc.val_step = look_info.hue_divisions * look_info.pc.hue_step; } - if (md.find(TAG_KEY_PROFILE_HUE_SAT_MAP_DIMS)) { - delta_info.hue_divisions = md.toInt(TAG_KEY_PROFILE_HUE_SAT_MAP_DIMS, 0); - delta_info.sat_divisions = md.toInt(TAG_KEY_PROFILE_HUE_SAT_MAP_DIMS, 4); - delta_info.val_divisions = md.toInt(TAG_KEY_PROFILE_HUE_SAT_MAP_DIMS, 8); + if (md.find(PROFILE_HUE_SAT_MAP_DIMS)) { + delta_info.hue_divisions = md.toInt(PROFILE_HUE_SAT_MAP_DIMS, 0); + delta_info.sat_divisions = md.toInt(PROFILE_HUE_SAT_MAP_DIMS, 4); + delta_info.val_divisions = md.toInt(PROFILE_HUE_SAT_MAP_DIMS, 8); - delta_info.srgb_gamma = md.find(TAG_KEY_PROFILE_HUE_SAT_MAP_ENCODING) && md.toInt(TAG_KEY_PROFILE_HUE_SAT_MAP_ENCODING); + delta_info.srgb_gamma = md.find(PROFILE_HUE_SAT_MAP_ENCODING) && md.toInt(PROFILE_HUE_SAT_MAP_ENCODING); - delta_info.array_count = md.getCount(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_1) / 3; + delta_info.array_count = md.getCount(PROFILE_HUE_SAT_MAP_DATA_1) / 3; deltas_1.resize(delta_info.array_count); for (unsigned int i = 0; i < delta_info.array_count; ++i) { - deltas_1[i].hue_shift = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_1, (i * 3) * tiff_float_size); - deltas_1[i].sat_scale = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_1, (i * 3 + 1) * tiff_float_size); - deltas_1[i].val_scale = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_1, (i * 3 + 2) * tiff_float_size); + deltas_1[i].hue_shift = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_1, (i * 3) * tiff_float_size); + deltas_1[i].sat_scale = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_1, (i * 3 + 1) * tiff_float_size); + deltas_1[i].val_scale = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_1, (i * 3 + 2) * tiff_float_size); } delta_info.pc.h_scale = @@ -1236,14 +1177,14 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : // Second matrix has_color_matrix_2 = true; - const bool cm2 = md.find(TAG_KEY_COLOR_MATRIX_2); + bool cm2 = md.find(COLOR_MATRIX_2); for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { color_matrix_2[row][col] = cm2 - ? md.toDouble(TAG_KEY_COLOR_MATRIX_2, (col + row * 3) * 8) - : color_matrix_1[row][col]; + ? md.toDouble(COLOR_MATRIX_2, (col + row * 3) * 8) + : color_matrix_1[row][col]; } } @@ -1253,20 +1194,20 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : // Saturation maps. Need to be unwinded. for (unsigned int i = 0; i < delta_info.array_count; ++i) { - deltas_2[i].hue_shift = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_2, (i * 3) * tiff_float_size); - deltas_2[i].sat_scale = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_2, (i * 3 + 1) * tiff_float_size); - deltas_2[i].val_scale = md.toDouble(TAG_KEY_PROFILE_HUE_SAT_MAP_DATA_2, (i * 3 + 2) * tiff_float_size); + deltas_2[i].hue_shift = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_2, (i * 3) * tiff_float_size); + deltas_2[i].sat_scale = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_2, (i * 3 + 1) * tiff_float_size); + deltas_2[i].val_scale = md.toDouble(PROFILE_HUE_SAT_MAP_DATA_2, (i * 3 + 2) * tiff_float_size); } } } - has_baseline_exposure_offset = md.find(TAG_KEY_BASELINE_EXPOSURE_OFFSET); + has_baseline_exposure_offset = md.find(BASELINE_EXPOSURE_OFFSET); if (has_baseline_exposure_offset) { - baseline_exposure_offset = md.toDouble(TAG_KEY_BASELINE_EXPOSURE_OFFSET); + baseline_exposure_offset = md.toDouble(BASELINE_EXPOSURE_OFFSET); } // Read tone curve points, if any, but disable to RTs own profiles - if (md.find(TAG_KEY_PROFILE_TONE_CURVE)) { + if (md.find(PROFILE_TONE_CURVE)) { std::vector curve_points = { static_cast(DCT_Spline) // The first value is the curve type }; @@ -1274,9 +1215,9 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : // Push back each X/Y coordinates in a loop bool curve_is_linear = true; - for (unsigned int i = 0, n = md.getCount(TAG_KEY_PROFILE_TONE_CURVE); i < n; i += 2) { - const double x = md.toDouble(TAG_KEY_PROFILE_TONE_CURVE, (i + 0) * tiff_float_size); - const double y = md.toDouble(TAG_KEY_PROFILE_TONE_CURVE, (i + 1) * tiff_float_size); + for (unsigned int i = 0, n = md.getCount(PROFILE_TONE_CURVE); i < n; i += 2) { + const double x = md.toDouble(PROFILE_TONE_CURVE, (i + 0) * tiff_float_size); + const double y = md.toDouble(PROFILE_TONE_CURVE, (i + 1) * tiff_float_size); if (x != y) { curve_is_linear = false; @@ -1292,7 +1233,7 @@ DCPProfile::DCPProfile(const Glib::ustring& filename) : tone_curve.Set(DiagonalCurve(curve_points, CURVES_MIN_POLY_POINTS)); } } else { - if (md.find(TAG_KEY_PROFILE_TONE_COPYRIGHT) && md.toString(TAG_KEY_PROFILE_TONE_COPYRIGHT).find("Adobe Systems") != std::string::npos) { + if (md.find(PROFILE_TONE_COPYRIGHT) && md.toString(PROFILE_TONE_COPYRIGHT).find("Adobe Systems") != std::string::npos) { // An Adobe profile without tone curve is expected to have the Adobe Default Curve, we add that std::vector curve_points = { static_cast(DCT_Spline) @@ -1390,31 +1331,32 @@ void DCPProfile::apply( const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, - bool apply_hue_sat_map + bool apply_hue_sat_map, + bool apply_look_table ) const { const TMatrix work_matrix = ICCStore::getInstance()->workingSpaceInverseMatrix(working_space); - const Matrix xyz_cam = makeXyzCam(white_balance, pre_mul, cam_wb_matrix, preferred_illuminant); // Camera RGB to XYZ D50 matrix - const std::vector delta_base = makeHueSatMap(white_balance, preferred_illuminant); if (delta_base.empty()) { apply_hue_sat_map = false; } + const Matrix xyz_cam = makeXyzCam(white_balance, pre_mul, cam_wb_matrix, preferred_illuminant, apply_hue_sat_map || apply_look_table); // Camera RGB to XYZ D50 matrix + if (!apply_hue_sat_map) { // The fast path: No LUT --> Calculate matrix for direct conversion raw -> working space float mat[3][3] = {}; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - double temp = 0.0; + for (int k = 0; k < 3; ++k) { - temp += work_matrix[i][k] * xyz_cam[k][j]; + mat[i][j] += work_matrix[i][k] * xyz_cam[k][j]; } - mat[i][j] = temp; + } } @@ -1440,11 +1382,11 @@ void DCPProfile::apply( for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - double temp = 0.0; + for (int k = 0; k < 3; ++k) { - temp += prophoto_xyz[i][k] * xyz_cam[k][j]; + pro_photo[i][j] += prophoto_xyz[i][k] * xyz_cam[k][j]; } - pro_photo[i][j] = temp; + } } @@ -1452,11 +1394,11 @@ void DCPProfile::apply( for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - double temp = 0.0; + for (int k = 0; k < 3; ++k) { - temp += work_matrix[i][k] * xyz_prophoto[k][j]; + work[i][j] += work_matrix[i][k] * xyz_prophoto[k][j]; } - work[i][j] = temp; + } } @@ -1498,7 +1440,7 @@ void DCPProfile::apply( } } -void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use_tone_curve, bool apply_look_table, bool apply_baseline_exposure, DCPProfileApplyState& as_out) +void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use_tone_curve, bool apply_look_table, bool apply_baseline_exposure, ApplyState& as_out) { as_out.data->use_tone_curve = use_tone_curve; as_out.data->apply_look_table = apply_look_table; @@ -1527,11 +1469,9 @@ void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - double temp = 0.0; for (int k = 0; k < 3; k++) { - temp += prophoto_xyz[i][k] * mWork[k][j]; + as_out.data->pro_photo[i][j] += prophoto_xyz[i][k] * mWork[k][j]; } - as_out.data->pro_photo[i][j] = temp; } } @@ -1540,20 +1480,18 @@ void DCPProfile::setStep2ApplyState(const Glib::ustring& working_space, bool use for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - double temp = 0.0; for (int k = 0; k < 3; k++) { - temp += mWork[i][k] * xyz_prophoto[k][j]; + as_out.data->work[i][j] += mWork[i][k] * xyz_prophoto[k][j]; } - as_out.data->work[i][j] = temp; } } } } -void DCPProfile::step2ApplyTile(float* rc, float* gc, float* bc, int width, int height, int tile_width, const DCPProfileApplyState& as_in) const +void DCPProfile::step2ApplyTile(float* rc, float* gc, float* bc, int width, int height, int tile_width, const ApplyState& as_in) const { -#define FCLIP(a) ((a)>0.f?((a)<65535.5f?(a):65535.5f):0.f) +#define FCLIP(a) ((a)>0.0?((a)<65535.5?(a):65535.5):0.0) #define CLIP01(a) ((a)>0?((a)<1?(a):1):0) float exp_scale = as_in.data->bl_scale; @@ -1992,7 +1930,7 @@ std::vector DCPProfile::makeHueSatMap(const ColorTemp& wh return res; } -inline void DCPProfile::hsdApply(const HsdTableInfo& table_info, const std::vector& table_base, float& h, float& s, float& v) const +void DCPProfile::hsdApply(const HsdTableInfo& table_info, const std::vector& table_base, float& h, float& s, float& v) const { // Apply the HueSatMap. Ported from Adobes reference implementation. float hue_shift; @@ -2177,7 +2115,8 @@ void DCPStore::init(const Glib::ustring& rt_profile_dir, bool loadAll) && lastdot <= sname.size() - 4 && !sname.casefold().compare(lastdot, 4, ".dcp") ) { - file_std_profiles[sname.substr(0, lastdot).casefold_collate_key()] = fname; // They will be loaded and cached on demand + const Glib::ustring cam_short_name = sname.substr(0, lastdot).uppercase(); + file_std_profiles[cam_short_name] = fname; // They will be loaded and cached on demand } } else { // Directory @@ -2188,10 +2127,11 @@ void DCPStore::init(const Glib::ustring& rt_profile_dir, bool loadAll) for (const auto& alias : getAliases(rt_profile_dir)) { const Glib::ustring alias_name = Glib::ustring(alias.first).uppercase(); - const std::map::const_iterator real = file_std_profiles.find(Glib::ustring(alias.second).casefold_collate_key()); + const Glib::ustring real_name = Glib::ustring(alias.second).uppercase(); + const std::map::const_iterator real = file_std_profiles.find(real_name); if (real != file_std_profiles.end()) { - file_std_profiles[alias_name.casefold_collate_key()] = real->second; + file_std_profiles[alias_name] = real->second; } } } @@ -2213,20 +2153,20 @@ bool DCPStore::isValidDCPFileName(const Glib::ustring& filename) const DCPProfile* DCPStore::getProfile(const Glib::ustring& filename) const { - const auto key = filename.casefold_collate_key(); MyMutex::MyLock lock(mutex); - const std::map::const_iterator iter = profile_cache.find(key); - if (iter != profile_cache.end()) { - return iter->second; + const std::map::iterator r = profile_cache.find(filename); + + if (r != profile_cache.end()) { + return r->second; } DCPProfile* const res = new DCPProfile(filename); if (res->isValid()) { // Add profile - profile_cache[key] = res; - if (settings->verbose) { + profile_cache[filename] = res; + if (options.rtSettings.verbose) { printf("DCP profile '%s' loaded from disk\n", filename.c_str()); } return res; @@ -2238,9 +2178,13 @@ DCPProfile* DCPStore::getProfile(const Glib::ustring& filename) const DCPProfile* DCPStore::getStdProfile(const Glib::ustring& requested_cam_short_name) const { - const std::map::const_iterator iter = file_std_profiles.find(requested_cam_short_name.casefold_collate_key()); - if (iter != file_std_profiles.end()) { - return getProfile(iter->second); + const Glib::ustring name = requested_cam_short_name.uppercase(); + + // Warning: do NOT use map.find(), since it does not seem to work reliably here + for (const auto& file_std_profile : file_std_profiles) { + if (file_std_profile.first == name) { + return getProfile(file_std_profile.second); + } } // profile not found, looking if we're in loadAll=false mode diff --git a/rtengine/dcp.h b/rtengine/dcp.h index 573349348..c818d9b6c 100644 --- a/rtengine/dcp.h +++ b/rtengine/dcp.h @@ -41,6 +41,20 @@ class DCPProfileApplyState; class DCPProfile final { public: + class ApplyState final + { + public: + ApplyState(); + ~ApplyState(); + + private: + struct Data; + + std::unique_ptr data; + + friend class DCPProfile; + }; + struct Illuminants { short light_source_1; short light_source_2; @@ -72,10 +86,11 @@ public: const ColorTemp& white_balance, const Triple& pre_mul, const Matrix& cam_wb_matrix, - bool apply_hue_sat_map = true + bool apply_hue_sat_map, + bool apply_look_table ) const; - void setStep2ApplyState(const Glib::ustring& working_space, bool use_tone_curve, bool apply_look_table, bool apply_baseline_exposure, DCPProfileApplyState& as_out); - void step2ApplyTile(float* r, float* g, float* b, int width, int height, int tile_width, const DCPProfileApplyState& as_in) const; + void setStep2ApplyState(const Glib::ustring& working_space, bool use_tone_curve, bool apply_look_table, bool apply_baseline_exposure, ApplyState& as_out); + void step2ApplyTile(float* r, float* g, float* b, int width, int height, int tile_width, const ApplyState& as_in) const; private: struct HsbModify { @@ -136,20 +151,6 @@ private: AdobeToneCurve tone_curve; }; -class DCPProfileApplyState final -{ -public: - DCPProfileApplyState(); - ~DCPProfileApplyState(); - -private: - struct Data; - - const std::unique_ptr data; - - friend class DCPProfile; -}; - class DCPStore final : public NonCopyable { @@ -171,10 +172,10 @@ private: std::vector profileDir; // these contain standard profiles from RT. keys are all in uppercase, file path is value - std::map file_std_profiles; + std::map file_std_profiles; // Maps file name to profile as cache - mutable std::map profile_cache; + mutable std::map profile_cache; }; } diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index 37ab1f6b4..4975a6854 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -29,7 +29,6 @@ #include "utils.h" #include "../rtgui/options.h" #include "../rtgui/version.h" -#include "../rtexif/rtexif.h" #ifdef WIN32 #include @@ -1177,6 +1176,10 @@ int ImageIO::saveTIFF (const Glib::ustring &fname, int bps, bool isFloat, bool u TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_ADOBE_DEFLATE); TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, (bps == 16 || bps == 32) && isFloat ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); + /* + + TODO: Re-apply fix from #5787 + [out]() { const std::vector default_tags = rtexif::ExifManager::getDefaultTIFFTags(nullptr); @@ -1188,7 +1191,7 @@ int ImageIO::saveTIFF (const Glib::ustring &fname, int bps, bool isFloat, bool u for (auto default_tag : default_tags) { delete default_tag; } - }(); + }();*/ if (!uncompressed) { TIFFSetField (out, TIFFTAG_PREDICTOR, (bps == 16 || bps == 32) && isFloat ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index b56e3b17f..a63e52ab7 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -47,12 +47,6 @@ class LUT; using LUTu = LUT; class EditDataProvider; -namespace rtexif -{ - -class TagDirectory; - -} namespace rtengine { diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index e1ebf2893..698b8f411 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -24,7 +24,6 @@ #include "image8.h" #include "imagefloat.h" #include "LUT.h" -#include "rawmetadatalocation.h" #include "../rtgui/threadutils.h" diff --git a/rtgui/exifpanel.h b/rtgui/exifpanel.h index ce6a6ba42..fc0cad111 100644 --- a/rtgui/exifpanel.h +++ b/rtgui/exifpanel.h @@ -23,7 +23,6 @@ #include #include "toolpanel.h" -#include "../rtexif/rtexif.h" namespace rtengine {