Merge branch 'dev' into multi-external-editor

This commit is contained in:
Lawrence Lee 2023-01-02 15:06:33 -08:00
commit 4d287b4cdf
No known key found for this signature in database
GPG Key ID: 048FF2B76A63895F
53 changed files with 1268 additions and 199 deletions

View File

@ -159,7 +159,7 @@ jobs:
"libstdc++-6.dll" \
"libsystre-0.dll" \
"libthai-0.dll" \
"libtiff-5.dll" \
"libtiff-6.dll" \
"libtre-5.dll" \
"libwebp-7.dll" \
"libwinpthread-1.dll" \

View File

@ -1,4 +1,6 @@
![RawTherapee logo](https://www.rawtherapee.com/images/logos/rawtherapee_logo_discuss.png)
![RawTherapee logo](https://raw.githubusercontent.com/Beep6581/RawTherapee/dev/rtdata/images/rt-logo-text-black.svg)
![RawTherapee screenshot](http://rawtherapee.com/images/carousel/100_rt59_provence_local_maskxxx.jpg)
RawTherapee is a powerful, cross-platform raw photo processing program, released as [libre software](https://en.wikipedia.org/wiki/Free_software) under the [GNU General Public License Version 3](https://opensource.org/licenses/gpl-3.0.html). It is written mostly in C++ using a [GTK+](https://www.gtk.org) front-end. It uses a patched version of [dcraw](https://www.dechifro.org/dcraw/) for reading raw files, with an in-house solution which adds the highest quality support for certain camera models unsupported by dcraw and enhances the accuracy of certain raw files already supported by dcraw. It is notable for the advanced control it gives the user over the demosaicing and development process.

Binary file not shown.

View File

@ -61,6 +61,7 @@ EXIFFILTER_IMAGETYPE;Image type
EXIFFILTER_ISO;ISO
EXIFFILTER_LENS;Lens
EXIFFILTER_METADATAFILTER;Enable metadata filters
EXIFFILTER_PATH;File path
EXIFFILTER_SHUTTER;Shutter
EXIFPANEL_ADDEDIT;Add/Edit
EXIFPANEL_ADDEDITHINT;Add new tag or edit tag.
@ -165,6 +166,7 @@ FILEBROWSER_POPUPREMOVE;Delete permanently
FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version
FILEBROWSER_POPUPRENAME;Rename
FILEBROWSER_POPUPSELECTALL;Select all
FILEBROWSER_POPUPSORTBY;Sort Files
FILEBROWSER_POPUPTRASH;Move to trash
FILEBROWSER_POPUPUNRANK;Unrank
FILEBROWSER_POPUPUNTRASH;Remove from trash
@ -1406,6 +1408,7 @@ HISTORY_MSG_DEHAZE_STRENGTH;Dehaze - Strength
HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold
HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold
HISTORY_MSG_EDGEFFECT;Edge Attenuation response
HISTORY_MSG_FF_FROMMETADATA;Flat-Field - From Metadata
HISTORY_MSG_FILMNEGATIVE_BALANCE;FN - Reference output
HISTORY_MSG_FILMNEGATIVE_COLORSPACE;Film negative color space
HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative
@ -1736,6 +1739,7 @@ PARTIALPASTE_EXPOSURE;Exposure
PARTIALPASTE_FILMNEGATIVE;Film negative
PARTIALPASTE_FILMSIMULATION;Film simulation
PARTIALPASTE_FLATFIELDAUTOSELECT;Flat-field auto-selection
PARTIALPASTE_FLATFIELDFROMMETADATA;Flat-field from Metadata
PARTIALPASTE_FLATFIELDBLURRADIUS;Flat-field blur radius
PARTIALPASTE_FLATFIELDBLURTYPE;Flat-field blur type
PARTIALPASTE_FLATFIELDCLIPCONTROL;Flat-field clip control
@ -2065,6 +2069,13 @@ SAVEDLG_WARNFILENAME;File will be named
SHCSELECTOR_TOOLTIP;Click right mouse button to reset the position of those 3 sliders.
SOFTPROOF_GAMUTCHECK_TOOLTIP;Highlight pixels with out-of-gamut colors with respect to:\n- the printer profile, if one is set and soft-proofing is enabled,\n- the output profile, if a printer profile is not set and soft-proofing is enabled,\n- the monitor profile, if soft-proofing is disabled.
SOFTPROOF_TOOLTIP;Soft-proofing simulates the appearance of the image:\n- when printed, if a printer profile is set in Preferences > Color Management,\n- when viewed on a display that uses the current output profile, if a printer profile is not set.
SORT_ASCENDING;Ascending
SORT_BY_NAME;By Name
SORT_BY_DATE;By Date
SORT_BY_EXIF;By EXIF
SORT_BY_RANK;By Rank
SORT_BY_LABEL;By Color Label
SORT_DESCENDING;Descending
TC_PRIM_BLUX;Bx
TC_PRIM_BLUY;By
TC_PRIM_GREX;Gx
@ -2478,6 +2489,7 @@ TP_FLATFIELD_BT_VERTHORIZ;Vertical + Horizontal
TP_FLATFIELD_BT_VERTICAL;Vertical
TP_FLATFIELD_CLIPCONTROL;Clip control
TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Clip control avoids clipped highlights caused by applying the flat field. If there are already clipped highlights before applying the flat field, value 0 is used.
TP_FLATFIELD_FROMMETADATA;From Metadata
TP_FLATFIELD_LABEL;Flat-Field
TP_GENERAL_11SCALE_TOOLTIP;The effects of this tool are only visible or only accurate at a preview scale of 1:1.
TP_GRADIENT_CENTER;Center

View File

@ -176,6 +176,18 @@ if(LENSFUN_HAS_LOAD_DIRECTORY)
set_source_files_properties(rtlensfun.cc PROPERTIES COMPILE_DEFINITIONS RT_LENSFUN_HAS_LOAD_DIRECTORY)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.0")
# procparams.cc takes a long time to compile with optimizations starting
# with GCC 12.1 due to PTA (see issue #6548)
get_source_file_property(PROCPARAMS_COMPILE_OPTIONS procparams.cc COMPILE_OPTIONS)
if(PROCPARAMS_COMPILE_OPTIONS STREQUAL "NOTFOUND")
set(PROCPARAMS_COMPILE_OPTIONS "")
else()
set(PROCPARAMS_COMPILE_OPTIONS "${PROCPARAMS_COMPILE_OPTIONS};")
endif()
set(PROCPARAMS_COMPILE_OPTIONS "${PROCPARAMS_COMPILE_OPTIONS}-fno-tree-pta")
set_source_files_properties(procparams.cc PROPERTIES COMPILE_OPTIONS ${PROCPARAMS_COMPILE_OPTIONS})
endif()
if(WITH_BENCHMARK)
add_definitions(-DBENCHMARK)

View File

@ -248,6 +248,14 @@ public:
return *this;
}
// import from flat data
void operator()(std::size_t w, std::size_t h, const T* const copy)
{
ar_realloc(w, h);
for (std::size_t y = 0; y < h; ++y) {
std::copy(copy + y * w, copy + y * w + w, rows.data()[y]);
}
}
int getWidth() const
{

View File

@ -28,8 +28,6 @@ namespace rtengine
CameraConst::CameraConst() : pdafOffset(0)
{
memset(dcraw_matrix, 0, sizeof(dcraw_matrix));
memset(raw_crop, 0, sizeof(raw_crop));
memset(raw_mask, 0, sizeof(raw_mask));
white_max = 0;
globalGreenEquilibration = -1;
}
@ -192,6 +190,68 @@ CameraConst* CameraConst::parseEntry(const void *cJSON_, const char *make_model)
std::unique_ptr<CameraConst> cc(new CameraConst);
cc->make_model = make_model;
const auto get_raw_crop =
[](int w, int h, const cJSON *ji, CameraConst *cc) -> bool
{
std::array<int, 4> rc;
if (ji->type != cJSON_Array) {
//fprintf(stderr, "\"raw_crop\" must be an array\n");
return false;
}
int i;
for (i = 0, ji = ji->child; i < 4 && ji != nullptr; i++, ji = ji->next) {
if (ji->type != cJSON_Number) {
//fprintf(stderr, "\"raw_crop\" array must contain numbers\n");
return false;
}
//cc->raw_crop[i] = ji->valueint;
rc[i] = ji->valueint;
}
if (i != 4 || ji != nullptr) {
//fprintf(stderr, "\"raw_crop\" must contain 4 numbers\n");
return false;
}
cc->raw_crop[std::make_pair(w, h)] = rc;
return true;
};
const auto get_masked_areas =
[](int w, int h, const cJSON *ji, CameraConst *cc) -> bool
{
std::array<std::array<int, 4>, 2> rm;
if (ji->type != cJSON_Array) {
//fprintf(stderr, "\"masked_areas\" must be an array\n");
return false;
}
int i;
for (i = 0, ji = ji->child; i < 2 * 4 && ji != nullptr; i++, ji = ji->next) {
if (ji->type != cJSON_Number) {
//fprintf(stderr, "\"masked_areas\" array must contain numbers\n");
return false;
}
//cc->raw_mask[i / 4][i % 4] = ji->valueint;
rm[i / 4][i % 4] = ji->valueint;
}
if (i % 4 != 0) {
//fprintf(stderr, "\"masked_areas\" array length must be divisable by 4\n");
return false;
}
cc->raw_mask[std::make_pair(w, h)] = rm;
return true;
};
const cJSON *ji = cJSON_GetObjectItem(js, "dcraw_matrix");
if (ji) {
@ -216,24 +276,32 @@ CameraConst* CameraConst::parseEntry(const void *cJSON_, const char *make_model)
if (ji) {
if (ji->type != cJSON_Array) {
fprintf(stderr, "\"raw_crop\" must be an array\n");
fprintf(stderr, "invalid entry for raw_crop.\n");
return nullptr;
}
int i;
for (i = 0, ji = ji->child; i < 4 && ji; i++, ji = ji->next) {
if (ji->type != cJSON_Number) {
fprintf(stderr, "\"raw_crop\" array must contain numbers\n");
return nullptr;
} else if (!get_raw_crop(0, 0, ji, cc.get())) {
cJSON *je;
cJSON_ArrayForEach(je, ji) {
if (!cJSON_IsObject(je)) {
fprintf(stderr, "invalid entry for raw_crop.\n");
return nullptr;
} else {
auto js = cJSON_GetObjectItem(je, "frame");
if (!js || js->type != cJSON_Array ||
cJSON_GetArraySize(js) != 2 ||
!cJSON_IsNumber(cJSON_GetArrayItem(js, 0)) ||
!cJSON_IsNumber(cJSON_GetArrayItem(js, 1))) {
fprintf(stderr, "invalid entry for raw_crop.\n");
return nullptr;
}
int w = cJSON_GetArrayItem(js, 0)->valueint;
int h = cJSON_GetArrayItem(js, 1)->valueint;
js = cJSON_GetObjectItem(je, "crop");
if (!js || !get_raw_crop(w, h, js, cc.get())) {
fprintf(stderr, "invalid entry for raw_crop.\n");
return nullptr;
}
}
}
cc->raw_crop[i] = ji->valueint;
}
if (i != 4 || ji) {
fprintf(stderr, "\"raw_crop\" must contain 4 numbers\n");
return nullptr;
}
}
@ -241,24 +309,32 @@ CameraConst* CameraConst::parseEntry(const void *cJSON_, const char *make_model)
if (ji) {
if (ji->type != cJSON_Array) {
fprintf(stderr, "\"masked_areas\" must be an array\n");
fprintf(stderr, "invalid entry for masked_areas.\n");
return nullptr;
}
int i;
for (i = 0, ji = ji->child; i < 2 * 4 && ji; i++, ji = ji->next) {
if (ji->type != cJSON_Number) {
fprintf(stderr, "\"masked_areas\" array must contain numbers\n");
return nullptr;
} else if (!get_masked_areas(0, 0, ji, cc.get())) {
cJSON *je;
cJSON_ArrayForEach(je, ji) {
if (!cJSON_IsObject(je)) {
fprintf(stderr, "invalid entry for masked_areas.\n");
return nullptr;
} else {
auto js = cJSON_GetObjectItem(je, "frame");
if (!js || js->type != cJSON_Array ||
cJSON_GetArraySize(js) != 2 ||
!cJSON_IsNumber(cJSON_GetArrayItem(js, 0)) ||
!cJSON_IsNumber(cJSON_GetArrayItem(js, 1))) {
fprintf(stderr, "invalid entry for masked_areas.\n");
return nullptr;
}
int w = cJSON_GetArrayItem(js, 0)->valueint;
int h = cJSON_GetArrayItem(js, 1)->valueint;
js = cJSON_GetObjectItem(je, "areas");
if (!js || !get_masked_areas(w, h, js, cc.get())) {
fprintf(stderr, "invalid entry for masked_areas.\n");
return nullptr;
}
}
}
cc->raw_mask[i / 4][i % 4] = ji->valueint;
}
if (i % 4 != 0) {
fprintf(stderr, "\"masked_areas\" array length must be divisible by 4\n");
return nullptr;
}
}
@ -399,29 +475,41 @@ void CameraConst::update_pdafOffset(int other)
pdafOffset = other;
}
bool CameraConst::has_rawCrop() const
bool CameraConst::has_rawCrop(int raw_width, int raw_height) const
{
return raw_crop[0] != 0 || raw_crop[1] != 0 || raw_crop[2] != 0 || raw_crop[3] != 0;
return raw_crop.find(std::make_pair(raw_width, raw_height)) != raw_crop.end() || raw_crop.find(std::make_pair(0, 0)) != raw_crop.end();
}
void CameraConst::get_rawCrop(int& left_margin, int& top_margin, int& width, int& height) const
void CameraConst::get_rawCrop(int raw_width, int raw_height, int &left_margin, int &top_margin, int &width, int &height) const
{
left_margin = raw_crop[0];
top_margin = raw_crop[1];
width = raw_crop[2];
height = raw_crop[3];
auto it = raw_crop.find(std::make_pair(raw_width, raw_height));
if (it == raw_crop.end()) {
it = raw_crop.find(std::make_pair(0, 0));
}
if (it != raw_crop.end()) {
left_margin = it->second[0];
top_margin = it->second[1];
width = it->second[2];
height = it->second[3];
} else {
left_margin = top_margin = width = height = 0;
}
}
bool CameraConst::has_rawMask(int idx) const
bool CameraConst::has_rawMask(int raw_width, int raw_height, int idx) const
{
if (idx < 0 || idx > 1) {
return false;
}
return (raw_mask[idx][0] | raw_mask[idx][1] | raw_mask[idx][2] | raw_mask[idx][3]) != 0;
return raw_mask.find(std::make_pair(raw_width, raw_height)) != raw_mask.end() || raw_mask.find(std::make_pair(0, 0)) != raw_mask.end();
}
void CameraConst::get_rawMask(int idx, int& top, int& left, int& bottom, int& right) const
void CameraConst::get_rawMask(int raw_width, int raw_height, int idx, int &top, int &left, int &bottom, int &right) const
{
top = left = bottom = right = 0;
@ -429,10 +517,17 @@ void CameraConst::get_rawMask(int idx, int& top, int& left, int& bottom, int& ri
return;
}
top = raw_mask[idx][0];
left = raw_mask[idx][1];
bottom = raw_mask[idx][2];
right = raw_mask[idx][3];
auto it = raw_mask.find(std::make_pair(raw_width, raw_height));
if (it == raw_mask.end()) {
it = raw_mask.find(std::make_pair(0, 0));
}
if (it != raw_mask.end()) {
top = it->second[idx][0];
left = it->second[idx][1];
bottom = it->second[idx][2];
right = it->second[idx][3];
}
}
void CameraConst::update_Levels(const CameraConst *other)
@ -464,9 +559,7 @@ void CameraConst::update_Crop(CameraConst *other)
return;
}
if (other->has_rawCrop()) {
other->get_rawCrop(raw_crop[0], raw_crop[1], raw_crop[2], raw_crop[3]);
}
raw_crop.insert(other->raw_crop.begin(), other->raw_crop.end());
}
bool CameraConst::get_Levels(camera_const_levels & lvl, int bw, int iso, float fnumber) const

View File

@ -1,9 +1,11 @@
/*
/* -*- C++ -*-
*
* This file is part of RawTherapee.
*/
#pragma once
#include <map>
#include <array>
#include <string>
#include <vector>
@ -17,17 +19,17 @@ class ustring;
namespace rtengine
{
struct camera_const_levels {
int levels[4];
};
class CameraConst final
{
private:
struct camera_const_levels {
int levels[4];
};
std::string make_model;
short dcraw_matrix[12];
int raw_crop[4];
int raw_mask[2][4];
std::map<std::pair<int, int>, std::array<int, 4>> raw_crop;
std::map<std::pair<int, int>, std::array<std::array<int, 4>, 2>> raw_mask;
int white_max;
std::map<int, camera_const_levels> mLevels[2];
std::map<float, float> mApertureScaling;
@ -47,10 +49,10 @@ public:
const short *get_dcrawMatrix(void) const;
const std::vector<int>& get_pdafPattern() const;
int get_pdafOffset() const {return pdafOffset;};
bool has_rawCrop(void) const;
void get_rawCrop(int& left_margin, int& top_margin, int& width, int& height) const;
bool has_rawMask(int idx) const;
void get_rawMask(int idx, int& top, int& left, int& bottom, int& right) const;
bool has_rawCrop(int raw_width, int raw_height) const;
void get_rawCrop(int raw_width, int raw_height, int& left_margin, int& top_margin, int& width, int& height) const;
bool has_rawMask(int raw_width, int raw_height, int idx) const;
void get_rawMask(int raw_width, int raw_height, int idx, int& top, int& left, int& bottom, int& right) const;
int get_BlackLevel(int idx, int iso_speed) const;
int get_WhiteLevel(int idx, int iso_speed, float fnumber) const;
bool has_globalGreenEquilibration() const;
@ -77,4 +79,5 @@ public:
const CameraConst *get(const char make[], const char model[]) const;
};
}
} // namespace rtengine

View File

@ -70,6 +70,14 @@ Examples:
// cropped so the "negative number" way is not totally safe.
"raw_crop": [ 10, 20, 4000, 3000 ],
// multi-aspect support (added 2020-12-03)
// "frame" defines the full dimensions the crop applies to
// (with [0, 0] being the fallback crop if none of the other applies)
"raw_crop" : [
{ "frame" : [4100, 3050], "crop": [10, 20, 4050, 3020] },
{ "frame" : [0, 0], "crop": [10, 20, 4000, 3000] }
]
// Almost same as MaskedAreas DNG tag, used for black level measuring. Here up to two areas can be defined
// by tetrads of numbers:
"masked_areas": [ 51, 2, 3804, 156, 51, 5794, 3804, 5792 ],
@ -84,6 +92,14 @@ Examples:
// instead, to take care of possible light leaks from the light sensing area to the optically black (masked)
// area or sensor imperfections at the outer borders.
// multi-aspect support (added 2020-12-03)
// "frame" defines the full dimensions the masked areas apply to
// (with [0, 0] being the fallback crop if none of the other applies)
"masked_areas" : [
{ "frame" : [4100, 3050], "areas": [10, 20, 4050, 3020] },
{ "frame" : [0, 0], "areas": [10, 20, 4000, 3000] }
]
// list of indices of the rows with on-sensor PDAF pixels, for cameras that have such features. The indices here form a pattern that is repeated for the whole height of the sensor. The values are relative to the "pdaf_offset" value (see below)
"pdaf_pattern" : [ 0,12,36,54,72,90,114,126,144,162,180,204,216,240,252,270,294,306,324,342,366,384,396,414,432,450,474,492,504,522,540,564,576,594,606,630 ],
// index of the first row of the PDAF pattern in the sensor (0 is the topmost row). Allowed to be negative for convenience (this means that the first repetition of the pattern doesn't start from the first row)
@ -1210,33 +1226,50 @@ Camera constants:
{ // Quality C
"make_model": "Canon EOS R3",
"dcraw_matrix" : [9423,-2839,-1195,-4532,12377,2415,-483,1374,5276]
"dcraw_matrix" : [ 9423, -2839, -1195, -4532, 12377, 2415, -483, 1374, 5276 ],
"raw_crop": [ 160, 120, 6024, 4024 ]
},
{ // Quality C
"make_model": "Canon EOS R5",
"dcraw_matrix" : [9766, -2953, -1254, -4276, 12116, 2433, -437, 1336, 5131],
"raw_crop" : [ 128, 96, 8224, 5490 ],
"masked_areas" : [ 94, 20, 5578, 122 ],
"raw_crop" : [
{ "frame" : [ 8352, 5586 ], "crop" : [ 128, 96, 8224, 5490 ] },
{ "frame" : [ 5248, 3510 ], "crop" : [ 128, 96, 5120, 3382 ] }
],
"masked_areas" : [
{ "frame" : [ 8352, 5586 ], "areas": [ 94, 20, 5578, 122 ] },
{ "frame" : [ 5248, 3510 ], "areas": [ 94, 20, 3510, 122 ] }
],
"ranges" : { "white" : 16382 }
},
{ // Quality C
"make_model": "Canon EOS R6",
"dcraw_matrix" : [8293, -1611, -1132, -4759, 12710, 2275, -1013, 2415, 5508],
"raw_crop": [ 72, 38, 5496, 3670 ],
"masked_areas" : [ 40, 10, 5534, 70 ],
"raw_crop": [
{ "frame": [5568, 3708], "crop" : [ 72, 38, 5496, 3670 ] },
{ "frame": [3584, 2386], "crop" : [ 156, 108, 3404, 2270 ] }
],
"masked_areas" : [
{ "frame": [5568, 3708], "areas": [ 40, 10, 5534, 70 ] },
{ "frame": [3584, 2386], "areas": [ 40, 10, 2374, 110 ] }
],
"ranges" : { "white" : 16382 }
},
{ // Quality C
"make_model": "Canon EOS R7",
"dcraw_matrix" : [10424, -3138, -1300, -4221, 11938, 2584, -547, 1658, 6183]
"dcraw_matrix" : [10424, -3138, -1300, -4221, 11938, 2584, -547, 1658, 6183],
"raw_crop": [ 144, 72, 6984, 4660 ],
"masked_areas" : [ 70, 20, 4724, 138 ]
},
{ // Quality C
"make_model": "Canon EOS R10",
"dcraw_matrix" : [9269, -2012, -1107, -3990, 11762, 2527, -569, 2093, 4913]
"dcraw_matrix" : [9269, -2012, -1107, -3990, 11762, 2527, -569, 2093, 4913],
"raw_crop": [ 144, 40, 6048, 4020 ],
"masked_areas" : [ 38, 20, 4052, 138 ]
},
{ // Quality C, CHDK DNGs, raw frame correction
@ -1377,7 +1410,11 @@ Camera constants:
{ // Quality C
"make_model": [ "FUJIFILM GFX 100", "FUJIFILM GFX100S" ],
"dcraw_matrix" : [ 16212, -8423, -1583, -4336, 12583, 1937, -195, 726, 6199 ], // taken from ART
"raw_crop": [ 0, 2, 11664, 8734 ]
"raw_crop": [
// multi-aspect crop to account for 16-shot pixel shift images
{ "frame" : [11808, 8754], "crop" : [ 0, 2, 11664, 8734 ] },
{ "frame" : [23616, 17508], "crop" : [ 0, 4, 23328, 17468 ] }
]
},
{ // Quality B
@ -1549,6 +1586,12 @@ Camera constants:
"raw_crop": [ 0, 5, 7752, 5184 ]
},
{ // Quality C
"make_model": "FUJIFILM DBP for GX680",
"dcraw_matrix": [ 12741, -4916, -1420, -8510, 16791, 1715, -1767, 2302, 7771 ], // same as S2Pro as per LibRaw
"ranges": { "white": 4096, "black": 132 }
},
{ // Quality C, Leica C-Lux names can differ?
"make_model" : [ "LEICA C-LUX", "LEICA CAM-DC25" ],
"dcraw_matrix" : [7790, -2736, -755, -3452, 11870, 1769, -628, 1647, 4898]
@ -2988,7 +3031,10 @@ Camera constants:
{ // Quality B, correction for frame width
"make_model": [ "Sony ILCE-7S", "Sony ILCE-7SM2" ],
"dcraw_matrix": [ 5838,-1430,-246,-3497,11477,2297,-748,1885,5778 ], // DNG_v9.2 D65
"raw_crop": [ 0, 0, 4254, 2848 ],
"raw_crop" : [
{ "frame" : [ 2816, 1872 ], "crop" : [ 0, 0, 2792, 1872 ] },
{ "frame" : [ 4254, 2848 ], "crop" : [ 0, 0, 4254, 2848 ] }
],
"ranges": { "black": 512, "white": 16300 }
},

View File

@ -2464,6 +2464,30 @@ void CLASS unpacked_load_raw()
}
}
// RT - from LibRaw
void CLASS unpacked_load_raw_FujiDBP()
/*
for Fuji DBP for GX680, aka DX-2000
DBP_tile_width = 688;
DBP_tile_height = 3856;
DBP_n_tiles = 8;
*/
{
int scan_line, tile_n;
int nTiles = 8;
tile_width = raw_width / nTiles;
ushort *tile;
tile = (ushort *) calloc(raw_height, tile_width * 2);
for (tile_n = 0; tile_n < nTiles; tile_n++) {
read_shorts(tile, tile_width * raw_height);
for (scan_line = 0; scan_line < raw_height; scan_line++) {
memcpy(&raw_image[scan_line * raw_width + tile_n * tile_width],
&tile[scan_line * tile_width], tile_width * 2);
}
}
free(tile);
fseek(ifp, -2, SEEK_CUR); // avoid EOF error
}
// RT
void CLASS sony_arq_load_raw()
@ -6319,12 +6343,13 @@ void CLASS parse_mos (int offset)
void CLASS linear_table (unsigned len)
{
int i;
if (len > 0x1000) len = 0x1000;
read_shorts (curve, len);
for (i=len; i < 0x1000; i++)
curve[i] = curve[i-1];
maximum = curve[0xfff];
const unsigned maxLen = std::min(0x10000ull, 1ull << tiff_bps);
len = std::min(len, maxLen);
read_shorts(curve, len);
maximum = curve[len - 1];
for (std::size_t i = len; i < maxLen; ++i) {
curve[i] = maximum;
}
}
void CLASS parse_kodak_ifd (int base)
@ -6913,7 +6938,6 @@ it under the terms of the one of two licenses as you choose:
unsigned oldOrder = order;
order = 0x4d4d; // always big endian per definition in https://www.adobe.com/content/dam/acom/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf chapter 7
unsigned ntags = get4(); // read the number of opcodes
if (ntags < ifp->size / 12) { // rough check for wrong value (happens for example with DNG files from DJI FC6310)
while (ntags-- && !ifp->eof) {
unsigned opcode = get4();
@ -6932,8 +6956,48 @@ it under the terms of the one of two licenses as you choose:
break;
}
case 51009: /* OpcodeList2 */
meta_offset = ftell(ifp);
break;
{
meta_offset = ftell(ifp);
const unsigned oldOrder = order;
order = 0x4d4d; // always big endian per definition in https://www.adobe.com/content/dam/acom/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf chapter 7
unsigned ntags = get4(); // read the number of opcodes
if (ntags < ifp->size / 12) { // rough check for wrong value (happens for example with DNG files from DJI FC6310)
while (ntags-- && !ifp->eof) {
unsigned opcode = get4();
if (opcode == 9 && gainMaps.size() < 4) {
fseek(ifp, 4, SEEK_CUR); // skip 4 bytes as we know that the opcode 4 takes 4 byte
fseek(ifp, 8, SEEK_CUR); // skip 8 bytes as they don't interest us currently
GainMap gainMap;
gainMap.Top = get4();
gainMap.Left = get4();
gainMap.Bottom = get4();
gainMap.Right = get4();
gainMap.Plane = get4();
gainMap.Planes = get4();
gainMap.RowPitch = get4();
gainMap.ColPitch = get4();
gainMap.MapPointsV = get4();
gainMap.MapPointsH = get4();
gainMap.MapSpacingV = getreal(12);
gainMap.MapSpacingH = getreal(12);
gainMap.MapOriginV = getreal(12);
gainMap.MapOriginH = getreal(12);
gainMap.MapPlanes = get4();
const std::size_t n = static_cast<std::size_t>(gainMap.MapPointsV) * static_cast<std::size_t>(gainMap.MapPointsH) * static_cast<std::size_t>(gainMap.MapPlanes);
gainMap.MapGain.reserve(n);
for (std::size_t i = 0; i < n; ++i) {
gainMap.MapGain.push_back(getreal(11));
}
gainMaps.push_back(std::move(gainMap));
} else {
fseek(ifp, 8, SEEK_CUR); // skip 8 bytes as they don't interest us currently
fseek(ifp, get4(), SEEK_CUR);
}
}
}
order = oldOrder;
break;
}
case 64772: /* Kodak P-series */
if (len < 13) break;
fseek (ifp, 16, SEEK_CUR);
@ -10066,6 +10130,9 @@ canon_a5:
} else if (!strcmp(model, "X-Pro3") || !strcmp(model, "X-T3") || !strcmp(model, "X-T30") || !strcmp(model, "X-T4") || !strcmp(model, "X100V") || !strcmp(model, "X-S10")) {
width = raw_width = 6384;
height = raw_height = 4182;
} else if (!strcmp(model, "DBP for GX680")) { // Special case for #4204
width = raw_width = 5504;
height = raw_height = 3856;
}
top_margin = (raw_height - height) >> 2 << 1;
left_margin = (raw_width - width ) >> 2 << 1;
@ -10073,6 +10140,16 @@ canon_a5:
if (width == 4032 || width == 4952 || width == 6032 || width == 8280) left_margin = 0;
if (width == 3328 && (width -= 66)) left_margin = 34;
if (width == 4936) left_margin = 4;
if (width == 5504) { // #4204, taken from LibRaw
left_margin = 32;
top_margin = 8;
width = raw_width - 2*left_margin;
height = raw_height - 2*top_margin;
load_raw = &CLASS unpacked_load_raw_FujiDBP;
filters = 0x16161616;
load_flags = 0;
flip = 6;
}
if (!strcmp(model,"HS50EXR") ||
!strcmp(model,"F900EXR")) {
width += 2;
@ -11041,6 +11118,70 @@ void CLASS nikon_14bit_load_raw()
free(buf);
}
bool CLASS isGainMapSupported() const {
if (!(dng_version && isBayer())) {
return false;
}
const auto n = gainMaps.size();
if (n != 4) { // we need 4 gainmaps for bayer files
if (rtengine::settings->verbose) {
std::cout << "GainMap has " << n << " maps, but 4 are needed" << std::endl;
}
return false;
}
unsigned int check = 0;
bool noOp = true;
for (const auto &m : gainMaps) {
if (m.MapGain.size() < 1) {
if (rtengine::settings->verbose) {
std::cout << "GainMap has invalid size of " << m.MapGain.size() << std::endl;
}
return false;
}
if (m.MapGain.size() != static_cast<std::size_t>(m.MapPointsV) * static_cast<std::size_t>(m.MapPointsH) * static_cast<std::size_t>(m.MapPlanes)) {
if (rtengine::settings->verbose) {
std::cout << "GainMap has size of " << m.MapGain.size() << ", but needs " << m.MapPointsV * m.MapPointsH * m.MapPlanes << std::endl;
}
return false;
}
if (m.RowPitch != 2 || m.ColPitch != 2) {
if (rtengine::settings->verbose) {
std::cout << "GainMap needs Row/ColPitch of 2/2, but has " << m.RowPitch << "/" << m.ColPitch << std::endl;
}
return false;
}
if (m.Top == 0){
if (m.Left == 0) {
check += 1;
} else if (m.Left == 1) {
check += 2;
}
} else if (m.Top == 1) {
if (m.Left == 0) {
check += 4;
} else if (m.Left == 1) {
check += 8;
}
}
for (size_t i = 0; noOp && i < m.MapGain.size(); ++i) {
if (m.MapGain[i] != 1.f) { // we have at least one value != 1.f => map is not a nop
noOp = false;
}
}
}
if (noOp || check != 15) { // all maps are nops or the structure of the combination of 4 maps is not correct
if (rtengine::settings->verbose) {
if (noOp) {
std::cout << "GainMap is a nop" << std::endl;
} else {
std::cout << "GainMap has unsupported type : " << check << std::endl;
}
}
return false;
}
return true;
}
/* RT: Delete from here */
/*RT*/#undef SQR
/*RT*/#undef MAX

View File

@ -19,9 +19,12 @@
#pragma once
#include <iostream>
#include "myfile.h"
#include <csetjmp>
#include "dnggainmap.h"
#include "settings.h"
class DCraw
{
@ -165,6 +168,8 @@ protected:
PanasonicRW2Info(): bpp(0), encoding(0) {}
};
PanasonicRW2Info RT_pana_info;
std::vector<GainMap> gainMaps;
public:
struct CanonCR3Data {
// contents of tag CMP1 for relevant track in CR3 file
@ -193,6 +198,18 @@ public:
int crx_track_selected;
short CR3_CTMDtag;
};
bool isBayer() const
{
return (filters != 0 && filters != 9);
}
const std::vector<GainMap>& getGainMaps() const {
return gainMaps;
}
bool isGainMapSupported() const;
struct CanonLevelsData {
unsigned cblack[4];
unsigned white;
@ -200,6 +217,7 @@ public:
bool white_ok;
CanonLevelsData(): cblack{0}, white{0}, black_ok(false), white_ok(false) {}
};
protected:
CanonCR3Data RT_canon_CR3_data;
@ -432,6 +450,7 @@ void parse_hasselblad_gain();
void hasselblad_load_raw();
void leaf_hdr_load_raw();
void unpacked_load_raw();
void unpacked_load_raw_FujiDBP();
void sinar_4shot_load_raw();
void imacon_full_load_raw();
void packed_load_raw();

43
rtengine/dnggainmap.h Normal file
View File

@ -0,0 +1,43 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2021 Ingo Weyrich <heckflosse67@gmx.de>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <vector>
struct GainMap
{
std::uint32_t Top;
std::uint32_t Left;
std::uint32_t Bottom;
std::uint32_t Right;
std::uint32_t Plane;
std::uint32_t Planes;
std::uint32_t RowPitch;
std::uint32_t ColPitch;
std::uint32_t MapPointsV;
std::uint32_t MapPointsH;
double MapSpacingV;
double MapSpacingH;
double MapOriginV;
double MapOriginH;
std::uint32_t MapPlanes;
std::vector<float> MapGain;
};

View File

@ -77,7 +77,7 @@ bool DynamicProfileRule::operator< (const DynamicProfileRule &other) const
}
bool DynamicProfileRule::matches (const rtengine::FramesMetaData *im) const
bool DynamicProfileRule::matches (const rtengine::FramesMetaData *im, const Glib::ustring& filename) const
{
return (iso (im->getISOSpeed())
&& fnumber (im->getFNumber())
@ -86,6 +86,7 @@ bool DynamicProfileRule::matches (const rtengine::FramesMetaData *im) const
&& expcomp (im->getExpComp())
&& camera (im->getCamera())
&& lens (im->getLens())
&& path (filename)
&& imagetype(im->getImageType(0)));
}
@ -214,6 +215,7 @@ bool DynamicProfileRules::loadRules()
get_double_range (rule.expcomp, kf, group, "expcomp");
get_optional (rule.camera, kf, group, "camera");
get_optional (rule.lens, kf, group, "lens");
get_optional (rule.path, kf, group, "path");
get_optional (rule.imagetype, kf, group, "imagetype");
try {
@ -247,6 +249,7 @@ bool DynamicProfileRules::storeRules()
set_double_range (kf, group, "expcomp", rule.expcomp);
set_optional (kf, group, "camera", rule.camera);
set_optional (kf, group, "lens", rule.lens);
set_optional (kf, group, "path", rule.path);
set_optional (kf, group, "imagetype", rule.imagetype);
kf.set_string (group, "profilepath", rule.profilepath);
}

View File

@ -51,7 +51,7 @@ public:
};
DynamicProfileRule();
bool matches (const rtengine::FramesMetaData *im) const;
bool matches (const rtengine::FramesMetaData *im, const Glib::ustring& filename) const;
bool operator< (const DynamicProfileRule &other) const;
int serial_number;
@ -62,6 +62,7 @@ public:
Range<double> expcomp;
Optional camera;
Optional lens;
Optional path;
Optional imagetype;
Glib::ustring profilepath;
};

View File

@ -19,6 +19,7 @@
#include <functional>
#include <strings.h>
#include <time.h>
#include <tiff.h>
@ -57,7 +58,8 @@ template<typename T>
T getFromFrame(
const std::vector<std::unique_ptr<FrameData>>& frames,
std::size_t frame,
const std::function<T (const FrameData&)>& function
const std::function<T (const FrameData&)>& function,
T defval = {}
)
{
if (frame < frames.size()) {
@ -66,7 +68,7 @@ T getFromFrame(
if (!frames.empty()) {
return function(*frames[0]);
}
return {};
return defval;
}
const std::string& validateUft8(const std::string& str, const std::string& on_error = "???")
@ -85,11 +87,21 @@ FramesMetaData* FramesMetaData::fromFile(const Glib::ustring& fname, std::unique
return new FramesData(fname, std::move(rml), firstFrameOnly);
}
FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir) :
static struct tm timeFromTS(const time_t ts)
{
#if !defined(WIN32)
struct tm tm;
return *gmtime_r(&ts, &tm);
#else
return *gmtime(&ts);
#endif
}
FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir, time_t ts) :
frameRootDir(frameRootDir_),
iptc(nullptr),
time{},
timeStamp{},
time(timeFromTS(ts)),
timeStamp(ts),
iso_speed(0),
aperture(0.),
focal_len(0.),
@ -1068,7 +1080,8 @@ tm FramesData::getDateTime(unsigned int frame) const
[](const FrameData& frame_data)
{
return frame_data.getDateTime();
}
},
modTime
);
}
@ -1080,7 +1093,8 @@ time_t FramesData::getDateTimeAsTS(unsigned int frame) const
[](const FrameData& frame_data)
{
return frame_data.getDateTimeAsTS();
}
},
modTimeStamp
);
}
@ -1366,6 +1380,11 @@ failure:
FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml, bool firstFrameOnly) :
iptc(nullptr), dcrawFrameCount(0)
{
GStatBuf statbuf = {};
g_stat(fname.c_str(), &statbuf);
modTimeStamp = statbuf.st_mtime;
modTime = timeFromTS(modTimeStamp);
if (rml && (rml->exifBase >= 0 || rml->ciffBase >= 0)) {
FILE* f = g_fopen(fname.c_str(), "rb");
@ -1384,7 +1403,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
// creating FrameData
for (auto currFrame : exifManager.frames) {
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
}
for (auto currRoot : roots) {
@ -1410,7 +1429,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
roots = exifManager.roots;
for (auto currFrame : exifManager.frames) {
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
}
rewind(exifManager.f); // Not sure this is necessary
@ -1430,7 +1449,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
// creating FrameData
for (auto currFrame : exifManager.frames) {
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
}
for (auto currRoot : roots) {

View File

@ -72,7 +72,7 @@ protected:
public:
FrameData (rtexif::TagDirectory* frameRootDir, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir);
FrameData (rtexif::TagDirectory* frameRootDir, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir, time_t ts = 0);
virtual ~FrameData ();
bool getPixelShift () const;
@ -109,6 +109,8 @@ private:
std::vector<rtexif::TagDirectory*> roots;
IptcData* iptc;
unsigned int dcrawFrameCount;
struct tm modTime;
time_t modTimeStamp;
public:
explicit FramesData (const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml = nullptr, bool firstFrameOnly = false);

View File

@ -137,6 +137,7 @@ public:
virtual ImageMatrices* getImageMatrices () = 0;
virtual bool isRAW () const = 0;
virtual bool isGainMapSupported () const = 0;
virtual DCPProfile* getDCP (const procparams::ColorManagementParams &cmp, DCPProfileApplyState &as)
{
return nullptr;

View File

@ -407,7 +407,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
// If high detail (=100%) is newly selected, do a demosaic update, since the last was just with FAST
if (imageTypeListener) {
imageTypeListener->imageTypeChanged(imgsrc->isRAW(), imgsrc->getSensorType() == ST_BAYER, imgsrc->getSensorType() == ST_FUJI_XTRANS, imgsrc->isMono());
imageTypeListener->imageTypeChanged(imgsrc->isRAW(), imgsrc->getSensorType() == ST_BAYER, imgsrc->getSensorType() == ST_FUJI_XTRANS, imgsrc->isMono(), imgsrc->isGainMapSupported());
}
if ((todo & M_RAW)

View File

@ -5633,6 +5633,7 @@ bool RAWParams::PreprocessWB::operator !=(const PreprocessWB& other) const
RAWParams::RAWParams() :
df_autoselect(false),
ff_AutoSelect(false),
ff_FromMetaData(false),
ff_BlurRadius(32),
ff_BlurType(getFlatFieldBlurTypeString(FlatFieldBlurType::AREA)),
ff_AutoClipControl(false),
@ -5658,6 +5659,7 @@ bool RAWParams::operator ==(const RAWParams& other) const
&& df_autoselect == other.df_autoselect
&& ff_file == other.ff_file
&& ff_AutoSelect == other.ff_AutoSelect
&& ff_FromMetaData == other.ff_FromMetaData
&& ff_BlurRadius == other.ff_BlurRadius
&& ff_BlurType == other.ff_BlurType
&& ff_AutoClipControl == other.ff_AutoClipControl
@ -7484,6 +7486,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->raw.df_autoselect, "RAW", "DarkFrameAuto", raw.df_autoselect, keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_file, "RAW", "FlatFieldFile", relativePathIfInside(fname, fnameAbsolute, raw.ff_file), keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_AutoSelect, "RAW", "FlatFieldAutoSelect", raw.ff_AutoSelect, keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_FromMetaData, "RAW", "FlatFieldFromMetaData", raw.ff_FromMetaData, keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_BlurRadius, "RAW", "FlatFieldBlurRadius", raw.ff_BlurRadius, keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_BlurType, "RAW", "FlatFieldBlurType", raw.ff_BlurType, keyFile);
saveToKeyfile(!pedited || pedited->raw.ff_AutoClipControl, "RAW", "FlatFieldAutoClipControl", raw.ff_AutoClipControl, keyFile);
@ -10130,6 +10133,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
}
assignFromKeyfile(keyFile, "RAW", "FlatFieldAutoSelect", pedited, raw.ff_AutoSelect, pedited->raw.ff_AutoSelect);
assignFromKeyfile(keyFile, "RAW", "FlatFieldFromMetaData", pedited, raw.ff_FromMetaData, pedited->raw.ff_FromMetaData);
assignFromKeyfile(keyFile, "RAW", "FlatFieldBlurRadius", pedited, raw.ff_BlurRadius, pedited->raw.ff_BlurRadius);
assignFromKeyfile(keyFile, "RAW", "FlatFieldBlurType", pedited, raw.ff_BlurType, pedited->raw.ff_BlurType);
assignFromKeyfile(keyFile, "RAW", "FlatFieldAutoClipControl", pedited, raw.ff_AutoClipControl, pedited->raw.ff_AutoClipControl);

View File

@ -2440,6 +2440,7 @@ struct RAWParams {
Glib::ustring ff_file;
bool ff_AutoSelect;
bool ff_FromMetaData;
int ff_BlurRadius;
Glib::ustring ff_BlurType;
bool ff_AutoClipControl;

View File

@ -508,7 +508,7 @@ void ProfileStore::dumpFolderList()
printf ("\n");
}
PartialProfile *ProfileStore::loadDynamicProfile (const FramesMetaData *im)
PartialProfile *ProfileStore::loadDynamicProfile (const FramesMetaData *im, const Glib::ustring& filename)
{
if (storeState == STORESTATE_NOTINITIALIZED) {
parseProfilesOnce();
@ -521,7 +521,7 @@ PartialProfile *ProfileStore::loadDynamicProfile (const FramesMetaData *im)
}
for (auto rule : dynamicRules) {
if (rule.matches (im)) {
if (rule.matches (im, filename)) {
if (settings->verbose) {
printf ("found matching profile %s\n", rule.profilepath.c_str());
}

View File

@ -209,7 +209,7 @@ public:
void addListener (ProfileStoreListener *listener);
void removeListener (ProfileStoreListener *listener);
rtengine::procparams::PartialProfile* loadDynamicProfile (const rtengine::FramesMetaData *im);
rtengine::procparams::PartialProfile* loadDynamicProfile (const rtengine::FramesMetaData *im, const Glib::ustring& filename);
void dumpFolderList();
};

View File

@ -548,11 +548,18 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
CameraConstantsStore* ccs = CameraConstantsStore::getInstance();
const CameraConst *cc = ccs->get(make, model);
bool raw_crop_cc = false;
int orig_raw_width = width;
int orig_raw_height = height;
if (raw_image) {
if (cc && cc->has_rawCrop()) {
orig_raw_width = raw_width;
orig_raw_height = raw_height;
if (cc && cc->has_rawCrop(raw_width, raw_height)) {
raw_crop_cc = true;
int lm, tm, w, h;
cc->get_rawCrop(lm, tm, w, h);
cc->get_rawCrop(raw_width, raw_height, lm, tm, w, h);
if (isXtrans()) {
shiftXtransMatrix(6 - ((top_margin - tm) % 6), 6 - ((left_margin - lm) % 6));
@ -584,9 +591,9 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
}
}
if (cc && cc->has_rawMask(0)) {
for (int i = 0; i < 8 && cc->has_rawMask(i); i++) {
cc->get_rawMask(i, mask[i][0], mask[i][1], mask[i][2], mask[i][3]);
if (cc && cc->has_rawMask(orig_raw_width, orig_raw_height, 0)) {
for (int i = 0; i < 2 && cc->has_rawMask(orig_raw_width, orig_raw_height, i); i++) {
cc->get_rawMask(orig_raw_width, orig_raw_height, i, mask[i][0], mask[i][1], mask[i][2], mask[i][3]);
}
}
@ -594,9 +601,10 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
free(raw_image);
raw_image = nullptr;
} else {
if (get_maker() == "Sigma" && cc && cc->has_rawCrop()) { // foveon images
if (get_maker() == "Sigma" && cc && cc->has_rawCrop(width, height)) { // foveon images
raw_crop_cc = true;
int lm, tm, w, h;
cc->get_rawCrop(lm, tm, w, h);
cc->get_rawCrop(width, height, lm, tm, w, h);
left_margin = lm;
top_margin = tm;
@ -692,11 +700,12 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
printf("no constants in camconst.json exists for \"%s %s\" (relying only on dcraw defaults)\n", make, model);
}
printf("raw dimensions: %d x %d\n", orig_raw_width, orig_raw_height);
printf("black levels: R:%d G1:%d B:%d G2:%d (%s)\n", get_cblack(0), get_cblack(1), get_cblack(2), get_cblack(3),
black_from_cc ? "provided by camconst.json" : "provided by dcraw");
printf("white levels: R:%d G1:%d B:%d G2:%d (%s)\n", get_white(0), get_white(1), get_white(2), get_white(3),
white_from_cc ? "provided by camconst.json" : "provided by dcraw");
printf("raw crop: %d %d %d %d (provided by %s)\n", left_margin, top_margin, iwidth, iheight, (cc && cc->has_rawCrop()) ? "camconst.json" : "dcraw");
printf("raw crop: %d %d %d %d (provided by %s)\n", left_margin, top_margin, iwidth, iheight, raw_crop_cc ? "camconst.json" : "dcraw");
printf("color matrix provided by %s\n", (cc && cc->has_dcrawMatrix()) ? "camconst.json" : "dcraw");
}
}

View File

@ -245,11 +245,6 @@ public:
return zero_is_bad == 1;
}
bool isBayer() const
{
return (filters != 0 && filters != 9);
}
bool isXtrans() const
{
return filters == 9;

View File

@ -39,6 +39,7 @@
#include "rawimage.h"
#include "rawimagesource_i.h"
#include "rawimagesource.h"
#include "rescale.h"
#include "rt_math.h"
#include "rtengine.h"
#include "rtlensfun.h"
@ -1347,7 +1348,6 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
rif = ffm.searchFlatField(idata->getMake(), idata->getModel(), idata->getLens(), idata->getFocalLen(), idata->getFNumber(), idata->getDateTimeAsTS());
}
bool hasFlatField = (rif != nullptr);
if (hasFlatField && settings->verbose) {
@ -1387,6 +1387,9 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
}
//FLATFIELD end
if (raw.ff_FromMetaData && isGainMapSupported()) {
applyDngGainMap(c_black, ri->getGainMaps());
}
// Always correct camera badpixels from .badpixels file
const std::vector<badPix> *bp = DFManager::getInstance().getBadPixels(ri->get_maker(), ri->get_model(), idata->getSerialNumber());
@ -6259,6 +6262,36 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int
}
}
bool RawImageSource::isGainMapSupported() const {
return ri->isGainMapSupported();
}
void RawImageSource::applyDngGainMap(const float black[4], const std::vector<GainMap> &gainMaps) {
// now we can apply each gain map to raw_data
array2D<float> mvals[2][2];
for (auto &m : gainMaps) {
mvals[m.Top & 1][m.Left & 1](m.MapPointsH, m.MapPointsV, m.MapGain.data());
}
// now we assume, col_scale and row scale is the same for all maps
const float col_scale = float(gainMaps[0].MapPointsH-1) / float(W);
const float row_scale = float(gainMaps[0].MapPointsV-1) / float(H);
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (std::size_t y = 0; y < static_cast<size_t>(H); ++y) {
const float rowBlack[2] = {black[FC(y,0)], black[FC(y,1)]};
const float ys = y * row_scale;
float xs = 0.f;
for (std::size_t x = 0; x < static_cast<std::size_t>(W); ++x, xs += col_scale) {
const float f = getBilinearValue(mvals[y & 1][x & 1], xs, ys);
const float b = rowBlack[x & 1];
rawData[y][x] = rtengine::max((rawData[y][x] - b) * f + b, 0.f);
}
}
}
void RawImageSource::cleanup ()
{
delete phaseOneIccCurve;

View File

@ -24,6 +24,7 @@
#include "array2D.h"
#include "colortemp.h"
#include "dnggainmap.h"
#include "iimage.h"
#include "imagesource.h"
#include "procparams.h"
@ -177,6 +178,8 @@ public:
return true;
}
bool isGainMapSupported() const override;
void setProgressListener (ProgressListener* pl) override
{
plistener = pl;
@ -304,6 +307,7 @@ protected:
void vflip (Imagefloat* im);
void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) override;
void captureSharpening(const procparams::CaptureSharpeningParams &sharpeningParams, bool showMask, double &conrastThreshold, double &radius) override;
void applyDngGainMap(const float black[4], const std::vector<GainMap> &gainMaps);
};
}

View File

@ -493,7 +493,7 @@ class ImageTypeListener
{
public:
virtual ~ImageTypeListener() = default;
virtual void imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool is_Mono = false) = 0;
virtual void imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool is_Mono = false, bool isGainMapSupported = false) = 0;
};
class AutoContrastListener

View File

@ -100,6 +100,11 @@ public:
return false;
}
bool isGainMapSupported() const override
{
return false;
}
void setProgressListener (ProgressListener* pl) override
{
plistener = pl;

124
rtengine/winutils.h Normal file
View File

@ -0,0 +1,124 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2021 Lawrence Lee
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef WIN32
#include <aclapi.h>
#include <windows.h>
#include "noncopyable.h"
/**
* Wrapper for pointers to memory allocated by HeapAlloc.
*
* Memory is automatically freed when the object goes out of scope.
*/
template <typename T>
class WinHeapPtr : public rtengine::NonCopyable
{
private:
const T ptr;
public:
WinHeapPtr() = delete;
/** Allocates the specified number of bytes in the process heap. */
explicit WinHeapPtr(SIZE_T bytes): ptr(static_cast<T>(HeapAlloc(GetProcessHeap(), 0, bytes))) {};
~WinHeapPtr()
{
// HeapFree does a null check.
HeapFree(GetProcessHeap(), 0, static_cast<LPVOID>(ptr));
}
T operator ->() const
{
return ptr;
}
operator T() const
{
return ptr;
}
};
/**
* Wrapper for HLOCAL pointers to memory allocated by LocalAlloc.
*
* Memory is automatically freed when the object goes out of scope.
*/
template <typename T>
class WinLocalPtr : public rtengine::NonCopyable
{
private:
const T ptr;
public:
WinLocalPtr() = delete;
/** Wraps a raw pointer. */
WinLocalPtr(T pointer): ptr(pointer) {};
~WinLocalPtr()
{
// LocalFree does a null check.
LocalFree(static_cast<HLOCAL>(ptr));
}
T operator ->() const
{
return ptr;
}
operator T() const
{
return ptr;
}
};
/**
* Wrapper for HANDLEs.
*
* Handles are automatically closed when the object goes out of scope.
*/
class WinHandle : public rtengine::NonCopyable
{
private:
const HANDLE handle;
public:
WinHandle() = delete;
/** Wraps a HANDLE. */
WinHandle(HANDLE handle): handle(handle) {};
~WinHandle()
{
CloseHandle(handle);
}
operator HANDLE() const
{
return handle;
}
};
#endif

View File

@ -34,7 +34,7 @@ bool BatchQueueEntry::iconsLoaded(false);
Glib::RefPtr<Gdk::Pixbuf> BatchQueueEntry::savedAsIcon;
BatchQueueEntry::BatchQueueEntry (rtengine::ProcessingJob* pjob, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, int prevw, int prevh, Thumbnail* thm, bool overwrite) :
ThumbBrowserEntryBase(fname),
ThumbBrowserEntryBase(fname, thm),
opreview(nullptr),
origpw(prevw),
origph(prevh),

View File

@ -42,6 +42,7 @@ DynamicProfilePanel::EditDialog::EditDialog (const Glib::ustring &title, Gtk::Wi
add_optional (M ("EXIFFILTER_CAMERA"), has_camera_, camera_);
add_optional (M ("EXIFFILTER_LENS"), has_lens_, lens_);
add_optional (M ("EXIFFILTER_PATH"), has_path_, path_);
imagetype_ = Gtk::manage (new MyComboBoxText());
imagetype_->append(Glib::ustring("(") + M("DYNPROFILEEDITOR_IMGTYPE_ANY") + ")");
@ -93,6 +94,9 @@ void DynamicProfilePanel::EditDialog::set_rule (
has_lens_->set_active (rule.lens.enabled);
lens_->set_text (rule.lens.value);
has_path_->set_active (rule.path.enabled);
path_->set_text (rule.path.value);
if (!rule.imagetype.enabled) {
imagetype_->set_active(0);
} else if (rule.imagetype.value == "STD") {
@ -136,6 +140,9 @@ DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule()
ret.lens.enabled = has_lens_->get_active();
ret.lens.value = lens_->get_text();
ret.path.enabled = has_path_->get_active();
ret.path.value = path_->get_text();
ret.imagetype.enabled = imagetype_->get_active_row_number() > 0;
switch (imagetype_->get_active_row_number()) {
case 1:
@ -296,6 +303,16 @@ DynamicProfilePanel::DynamicProfilePanel():
*this, &DynamicProfilePanel::render_lens));
}
cell = Gtk::manage (new Gtk::CellRendererText());
cols_count = treeview_.append_column (M ("EXIFFILTER_PATH"), *cell);
col = treeview_.get_column (cols_count - 1);
if (col) {
col->set_cell_data_func (
*cell, sigc::mem_fun (
*this, &DynamicProfilePanel::render_path));
}
cell = Gtk::manage (new Gtk::CellRendererText());
cols_count = treeview_.append_column (M ("EXIFFILTER_IMAGETYPE"), *cell);
col = treeview_.get_column (cols_count - 1);
@ -375,6 +392,7 @@ void DynamicProfilePanel::update_rule (Gtk::TreeModel::Row row,
row[columns_.expcomp] = rule.expcomp;
row[columns_.camera] = rule.camera;
row[columns_.lens] = rule.lens;
row[columns_.path] = rule.path;
row[columns_.imagetype] = rule.imagetype;
row[columns_.profilepath] = rule.profilepath;
}
@ -398,6 +416,7 @@ DynamicProfileRule DynamicProfilePanel::to_rule (Gtk::TreeModel::Row row,
ret.expcomp = row[columns_.expcomp];
ret.camera = row[columns_.camera];
ret.lens = row[columns_.lens];
ret.path = row[columns_.path];
ret.profilepath = row[columns_.profilepath];
ret.imagetype = row[columns_.imagetype];
return ret;
@ -510,6 +529,12 @@ void DynamicProfilePanel::render_lens (
RENDER_OPTIONAL_ (lens);
}
void DynamicProfilePanel::render_path (
Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
{
RENDER_OPTIONAL_ (path);
}
void DynamicProfilePanel::render_imagetype (
Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
{

View File

@ -55,6 +55,7 @@ private:
add (expcomp);
add (camera);
add (lens);
add (path);
add (profilepath);
add (imagetype);
}
@ -66,6 +67,7 @@ private:
Gtk::TreeModelColumn<DynamicProfileRule::Range<double>> expcomp;
Gtk::TreeModelColumn<DynamicProfileRule::Optional> camera;
Gtk::TreeModelColumn<DynamicProfileRule::Optional> lens;
Gtk::TreeModelColumn<DynamicProfileRule::Optional> path;
Gtk::TreeModelColumn<DynamicProfileRule::Optional> imagetype;
Gtk::TreeModelColumn<Glib::ustring> profilepath;
};
@ -78,6 +80,7 @@ private:
void render_expcomp (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
void render_camera (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
void render_lens (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
void render_path (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
void render_imagetype (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
void render_profilepath (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter);
@ -114,6 +117,9 @@ private:
Gtk::CheckButton *has_lens_;
Gtk::Entry *lens_;
Gtk::CheckButton *has_path_;
Gtk::Entry *path_;
MyComboBoxText *imagetype_;
ProfileStoreComboBox *profilepath_;

View File

@ -45,6 +45,8 @@
#ifdef WIN32
#include "windows.h"
#include "../rtengine/winutils.h"
#endif
using namespace rtengine::procparams;
@ -135,6 +137,235 @@ bool find_default_monitor_profile (GdkWindow *rootwin, Glib::ustring &defprof, G
}
#endif
bool hasUserOnlyPermission(const Glib::ustring &dirname)
{
#if defined(__linux__) || defined(__APPLE__)
const Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dirname);
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("owner::user,unix::mode");
if (!file_info) {
return false;
}
const Glib::ustring owner = file_info->get_attribute_string("owner::user");
const guint32 mode = file_info->get_attribute_uint32("unix::mode");
return (mode & 0777) == 0700 && owner == Glib::get_user_name();
#elif defined(WIN32)
const Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dirname);
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("owner::user");
if (!file_info) {
return false;
}
// Current user must be the owner.
const Glib::ustring user_name = Glib::get_user_name();
const Glib::ustring owner = file_info->get_attribute_string("owner::user");
if (user_name != owner) {
return false;
}
// Get security descriptor and discretionary access control list.
PACL dacl = nullptr;
PSECURITY_DESCRIPTOR sec_desc_raw_ptr = nullptr;
auto win_error = GetNamedSecurityInfo(
dirname.c_str(),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
nullptr,
nullptr,
&dacl,
nullptr,
&sec_desc_raw_ptr
);
const WinLocalPtr<PSECURITY_DESCRIPTOR> sec_desc_ptr(sec_desc_raw_ptr);
if (win_error != ERROR_SUCCESS) {
return false;
}
// Must not inherit permissions.
SECURITY_DESCRIPTOR_CONTROL sec_desc_control;
DWORD revision;
if (!(
GetSecurityDescriptorControl(sec_desc_ptr, &sec_desc_control, &revision)
&& sec_desc_control & SE_DACL_PROTECTED
)) {
return false;
}
// Check that there is one entry allowing full access.
ULONG acl_entry_count;
PEXPLICIT_ACCESS acl_entry_list_raw = nullptr;
win_error = GetExplicitEntriesFromAcl(dacl, &acl_entry_count, &acl_entry_list_raw);
const WinLocalPtr<PEXPLICIT_ACCESS> acl_entry_list(acl_entry_list_raw);
if (win_error != ERROR_SUCCESS || acl_entry_count != 1) {
return false;
}
const EXPLICIT_ACCESS &ace = acl_entry_list[0];
if (
ace.grfAccessMode != GRANT_ACCESS
|| (ace.grfAccessPermissions & FILE_ALL_ACCESS) != FILE_ALL_ACCESS
|| ace.Trustee.TrusteeForm != TRUSTEE_IS_SID // Should already be SID, but double check.
) {
return false;
}
// ACE must be for the current user.
HANDLE process_token_raw;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &process_token_raw)) {
return false;
}
const WinHandle process_token(process_token_raw);
DWORD actual_token_info_size = 0;
GetTokenInformation(process_token, TokenUser, nullptr, 0, &actual_token_info_size);
if (!actual_token_info_size) {
return false;
}
const WinHeapPtr<PTOKEN_USER> user_token_ptr(actual_token_info_size);
if (!user_token_ptr || !GetTokenInformation(
process_token,
TokenUser,
user_token_ptr,
actual_token_info_size,
&actual_token_info_size
)) {
return false;
}
return EqualSid(ace.Trustee.ptstrName, user_token_ptr->User.Sid);
#endif
return false;
}
/**
* Sets read and write permissions, and optionally the execute permission, for
* the user and no permissions for others.
*/
void setUserOnlyPermission(const Glib::RefPtr<Gio::File> file, bool execute)
{
#if defined(__linux__) || defined(__APPLE__)
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("unix::mode");
if (!file_info) {
return;
}
guint32 mode = file_info->get_attribute_uint32("unix::mode");
mode = (mode & ~0777) | (execute ? 0700 : 0600);
try {
file->set_attribute_uint32("unix::mode", mode, Gio::FILE_QUERY_INFO_NONE);
} catch (Gio::Error &) {
}
#elif defined(WIN32)
// Get the current user's SID.
HANDLE process_token_raw;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &process_token_raw)) {
return;
}
const WinHandle process_token(process_token_raw);
DWORD actual_token_info_size = 0;
GetTokenInformation(process_token, TokenUser, nullptr, 0, &actual_token_info_size);
if (!actual_token_info_size) {
return;
}
const WinHeapPtr<PTOKEN_USER> user_token_ptr(actual_token_info_size);
if (!user_token_ptr || !GetTokenInformation(
process_token,
TokenUser,
user_token_ptr,
actual_token_info_size,
&actual_token_info_size
)) {
return;
}
const PSID user_sid = user_token_ptr->User.Sid;
// Get a handle to the file.
const Glib::ustring filename = file->get_path();
const HANDLE file_handle_raw = CreateFile(
filename.c_str(),
READ_CONTROL | WRITE_DAC,
0,
nullptr,
OPEN_EXISTING,
execute ? FILE_FLAG_BACKUP_SEMANTICS : FILE_ATTRIBUTE_NORMAL,
nullptr
);
if (file_handle_raw == INVALID_HANDLE_VALUE) {
return;
}
const WinHandle file_handle(file_handle_raw);
// Create the user-only permission and set it.
EXPLICIT_ACCESS ea = {
.grfAccessPermissions = FILE_ALL_ACCESS,
.grfAccessMode = GRANT_ACCESS,
.grfInheritance = NO_INHERITANCE,
};
BuildTrusteeWithSid(&(ea.Trustee), user_sid);
PACL new_dacl_raw = nullptr;
auto win_error = SetEntriesInAcl(1, &ea, nullptr, &new_dacl_raw);
if (win_error != ERROR_SUCCESS) {
return;
}
const WinLocalPtr<PACL> new_dacl(new_dacl_raw);
SetSecurityInfo(
file_handle,
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
nullptr,
nullptr,
new_dacl,
nullptr
);
#endif
}
/**
* Gets the path to the temp directory, creating it if necessary.
*/
Glib::ustring getTmpDirectory()
{
#if defined(__linux__) || defined(__APPLE__) || defined(WIN32)
static Glib::ustring recent_dir = "";
const Glib::ustring tmp_dir_root = Glib::get_tmp_dir();
const Glib::ustring subdir_base =
Glib::ustring::compose("rawtherapee-%1", Glib::get_user_name());
Glib::ustring dir = Glib::build_filename(tmp_dir_root, subdir_base);
// Returns true if the directory doesn't exist or has the right permissions.
auto is_usable_dir = [](const Glib::ustring &dir_path) {
return !Glib::file_test(dir_path, Glib::FILE_TEST_EXISTS) || (Glib::file_test(dir_path, Glib::FILE_TEST_IS_DIR) && hasUserOnlyPermission(dir_path));
};
if (!(is_usable_dir(dir) || recent_dir.empty())) {
// Try to reuse the random suffix directory.
dir = recent_dir;
}
if (!is_usable_dir(dir)) {
// Create new directory with random suffix.
gchar *const rand_dir = g_dir_make_tmp((subdir_base + "-XXXXXX").c_str(), nullptr);
if (!rand_dir) {
return tmp_dir_root;
}
dir = recent_dir = rand_dir;
g_free(rand_dir);
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dir);
setUserOnlyPermission(file, true);
} else if (!Glib::file_test(dir, Glib::FILE_TEST_EXISTS)) {
// Create the directory.
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dir);
bool dir_created = file->make_directory();
if (!dir_created) {
return tmp_dir_root;
}
setUserOnlyPermission(file, true);
}
return dir;
#else
return Glib::get_tmp_dir();
#endif
}
}
class EditorPanel::ColorManagementToolbar
@ -2116,7 +2347,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *p
dirname = options.editor_custom_out_dir;
break;
default: // Options::EDITOR_OUT_DIR_TEMP
dirname = Glib::get_tmp_dir();
dirname = getTmpDirectory();
break;
}
Glib::ustring fullFileName = Glib::build_filename(dirname, shortname);
@ -2183,6 +2414,8 @@ bool EditorPanel::idle_sentToGimp (ProgressConnector<int> *pc, rtengine::IImagef
parent->setProgress (0.);
bool success = false;
setUserOnlyPermission(Gio::File::create_for_path(filename), false);
success = ExtProgStore::openInExternalEditor(filename, external_editor_info);
if (!success) {

View File

@ -167,6 +167,41 @@ FileBrowser::FileBrowser () :
pmenu->attach (*Gtk::manage(selall = new Gtk::MenuItem (M("FILEBROWSER_POPUPSELECTALL"))), 0, 1, p, p + 1);
p++;
/***********************
* sort
***********************/
const std::array<std::string, 2> cnameSortOrders = {
M("SORT_ASCENDING"),
M("SORT_DESCENDING"),
};
const std::array<std::string, Options::SORT_METHOD_COUNT> cnameSortMethods = {
M("SORT_BY_NAME"),
M("SORT_BY_DATE"),
M("SORT_BY_EXIF"),
M("SORT_BY_RANK"),
M("SORT_BY_LABEL"),
};
pmenu->attach (*Gtk::manage(menuSort = new Gtk::MenuItem (M("FILEBROWSER_POPUPSORTBY"))), 0, 1, p, p + 1);
p++;
Gtk::Menu* submenuSort = Gtk::manage (new Gtk::Menu ());
Gtk::RadioButtonGroup sortOrderGroup, sortMethodGroup;
for (size_t i = 0; i < cnameSortOrders.size(); i++) {
submenuSort->attach (*Gtk::manage(sortOrder[i] = new Gtk::RadioMenuItem (sortOrderGroup, cnameSortOrders[i])), 0, 1, p, p + 1);
p++;
sortOrder[i]->set_active (i == options.sortDescending);
}
submenuSort->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
p++;
for (size_t i = 0; i < cnameSortMethods.size(); i++) {
submenuSort->attach (*Gtk::manage(sortMethod[i] = new Gtk::RadioMenuItem (sortMethodGroup, cnameSortMethods[i])), 0, 1, p, p + 1);
p++;
sortMethod[i]->set_active (i == options.sortMethod);
}
submenuSort->show_all ();
menuSort->set_submenu (*submenuSort);
/***********************
* rank
***********************/
@ -427,6 +462,14 @@ FileBrowser::FileBrowser () :
inspect->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), inspect));
}
for (int i = 0; i < 2; i++) {
sortOrder[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortOrder[i]));
}
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++) {
sortMethod[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortMethod[i]));
}
for (int i = 0; i < 6; i++) {
rank[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rank[i]));
}
@ -610,27 +653,7 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry)
entry->getThumbButtonSet()->setButtonListener(this);
entry->resize(getThumbnailHeight());
entry->filtered = !checkFilter(entry);
// find place in abc order
{
MYWRITERLOCK(l, entryRW);
fd.insert(
std::lower_bound(
fd.begin(),
fd.end(),
entry,
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
{
return *a < *b;
}
),
entry
);
initEntry(entry);
}
redraw(entry);
insertEntry(entry);
}
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname)
@ -724,6 +747,18 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m)
return;
}
for (int i = 0; i < 2; i++)
if (m == sortOrder[i]) {
sortOrderRequested (i);
return;
}
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++)
if (m == sortMethod[i]) {
sortMethodRequested (i);
return;
}
for (int i = 0; i < 6; i++)
if (m == rank[i]) {
rankingRequested (mselected, i);
@ -1632,6 +1667,18 @@ void FileBrowser::fromTrashRequested (std::vector<FileBrowserEntry*> tbe)
applyFilter (filter);
}
void FileBrowser::sortMethodRequested (int method)
{
options.sortMethod = Options::SortMethod(method);
resort ();
}
void FileBrowser::sortOrderRequested (int order)
{
options.sortDescending = !!order;
resort ();
}
void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank)
{

View File

@ -83,9 +83,12 @@ protected:
Gtk::MenuItem* open;
Gtk::MenuItem* inspect;
Gtk::MenuItem* selall;
Gtk::RadioMenuItem* sortMethod[Options::SORT_METHOD_COUNT];
Gtk::RadioMenuItem* sortOrder[2];
Gtk::MenuItem* copyTo;
Gtk::MenuItem* moveTo;
Gtk::MenuItem* menuSort;
Gtk::MenuItem* menuRank;
Gtk::MenuItem* menuLabel;
Gtk::MenuItem* menuFileOperations;
@ -131,6 +134,8 @@ protected:
void toTrashRequested (std::vector<FileBrowserEntry*> tbe);
void fromTrashRequested (std::vector<FileBrowserEntry*> tbe);
void sortMethodRequested (int method);
void sortOrderRequested (int order);
void rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank);
void colorlabelRequested (std::vector<FileBrowserEntry*> tbe, int colorlabel);
void requestRanking (int rank);

View File

@ -45,10 +45,8 @@ Glib::RefPtr<Gdk::Pixbuf> FileBrowserEntry::hdr;
Glib::RefPtr<Gdk::Pixbuf> FileBrowserEntry::ps;
FileBrowserEntry::FileBrowserEntry (Thumbnail* thm, const Glib::ustring& fname)
: ThumbBrowserEntryBase (fname), wasInside(false), iatlistener(nullptr), press_x(0), press_y(0), action_x(0), action_y(0), rot_deg(0.0), landscape(true), cropParams(new rtengine::procparams::CropParams), cropgl(nullptr), state(SNormal), crop_custom_ratio(0.f)
: ThumbBrowserEntryBase (fname, thm), wasInside(false), iatlistener(nullptr), press_x(0), press_y(0), action_x(0), action_y(0), rot_deg(0.0), landscape(true), cropParams(new rtengine::procparams::CropParams), cropgl(nullptr), state(SNormal), crop_custom_ratio(0.f)
{
thumbnail = thm;
feih = new FileBrowserEntryIdleHelper;
feih->fbentry = this;
feih->destroyed = false;

View File

@ -18,6 +18,7 @@
*/
#include <sstream>
#include "eventmapper.h"
#include "flatfield.h"
#include "guiutils.h"
@ -32,6 +33,9 @@ using namespace rtengine::procparams;
FlatField::FlatField () : FoldableToolPanel(this, "flatfield", M("TP_FLATFIELD_LABEL"))
{
auto m = ProcEventMapper::getInstance();
EvFlatFieldFromMetaData = m->newEvent(DARKFRAME, "HISTORY_MSG_FF_FROMMETADATA");
hbff = Gtk::manage(new Gtk::Box());
flatFieldFile = Gtk::manage(new MyFileChooserButton(M("TP_FLATFIELD_LABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN));
bindCurrentFolder (*flatFieldFile, options.lastFlatfieldDir);
@ -42,6 +46,8 @@ FlatField::FlatField () : FoldableToolPanel(this, "flatfield", M("TP_FLATFIELD_L
hbff->pack_start(*flatFieldFile);
hbff->pack_start(*flatFieldFileReset, Gtk::PACK_SHRINK);
flatFieldAutoSelect = Gtk::manage(new Gtk::CheckButton((M("TP_FLATFIELD_AUTOSELECT"))));
flatFieldFromMetaData = Gtk::manage(new CheckBox((M("TP_FLATFIELD_FROMMETADATA")), multiImage));
flatFieldFromMetaData->setCheckBoxListener (this);
ffInfo = Gtk::manage(new Gtk::Label("-"));
setExpandAlignProperties(ffInfo, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
flatFieldBlurRadius = Gtk::manage(new Adjuster (M("TP_FLATFIELD_BLURRADIUS"), 0, 200, 2, 32));
@ -70,8 +76,10 @@ FlatField::FlatField () : FoldableToolPanel(this, "flatfield", M("TP_FLATFIELD_L
flatFieldClipControl->show();
flatFieldClipControl->set_tooltip_markup (M("TP_FLATFIELD_CLIPCONTROL_TOOLTIP"));
pack_start( *hbff, Gtk::PACK_SHRINK);
pack_start( *flatFieldFromMetaData, Gtk::PACK_SHRINK);
pack_start( *Gtk::manage( new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_SHRINK, 0 );
pack_start( *flatFieldAutoSelect, Gtk::PACK_SHRINK);
pack_start( *hbff, Gtk::PACK_SHRINK);
pack_start( *ffInfo, Gtk::PACK_SHRINK);
pack_start( *hbffbt, Gtk::PACK_SHRINK);
pack_start( *flatFieldBlurRadius, Gtk::PACK_SHRINK);
@ -128,12 +136,14 @@ void FlatField::read(const rtengine::procparams::ProcParams* pp, const ParamsEdi
}
flatFieldAutoSelect->set_active (pp->raw.ff_AutoSelect);
flatFieldFromMetaData->set_active (pp->raw.ff_FromMetaData);
flatFieldBlurRadius->setValue (pp->raw.ff_BlurRadius);
flatFieldClipControl->setValue (pp->raw.ff_clipControl);
flatFieldClipControl->setAutoValue (pp->raw.ff_AutoClipControl);
if(pedited ) {
flatFieldAutoSelect->set_inconsistent (!pedited->raw.ff_AutoSelect);
flatFieldFromMetaData->set_inconsistent (!pedited->raw.ff_FromMetaData);
flatFieldBlurRadius->setEditedState( pedited->raw.ff_BlurRadius ? Edited : UnEdited );
flatFieldClipControl->setEditedState( pedited->raw.ff_clipControl ? Edited : UnEdited );
flatFieldClipControl->setAutoInconsistent(multiImage && !pedited->raw.ff_AutoClipControl);
@ -214,6 +224,7 @@ void FlatField::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedit
{
pp->raw.ff_file = flatFieldFile->get_filename();
pp->raw.ff_AutoSelect = flatFieldAutoSelect->get_active();
pp->raw.ff_FromMetaData = flatFieldFromMetaData->get_active();
pp->raw.ff_BlurRadius = flatFieldBlurRadius->getIntValue();
pp->raw.ff_clipControl = flatFieldClipControl->getIntValue();
pp->raw.ff_AutoClipControl = flatFieldClipControl->getAutoValue();
@ -227,6 +238,7 @@ void FlatField::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedit
if (pedited) {
pedited->raw.ff_file = ffChanged;
pedited->raw.ff_AutoSelect = !flatFieldAutoSelect->get_inconsistent();
pedited->raw.ff_FromMetaData = !flatFieldFromMetaData->get_inconsistent();
pedited->raw.ff_BlurRadius = flatFieldBlurRadius->getEditedState ();
pedited->raw.ff_clipControl = flatFieldClipControl->getEditedState ();
pedited->raw.ff_AutoClipControl = !flatFieldClipControl->getAutoInconsistent();
@ -352,6 +364,13 @@ void FlatField::flatFieldBlurTypeChanged ()
}
}
void FlatField::checkBoxToggled (CheckBox* c, CheckValue newval)
{
if (listener && c == flatFieldFromMetaData) {
listener->panelChanged (EvFlatFieldFromMetaData, flatFieldFromMetaData->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
}
}
void FlatField::flatFieldAutoSelectChanged()
{
if (batchMode) {
@ -419,3 +438,18 @@ void FlatField::flatFieldAutoClipValueChanged(int n)
}
);
}
void FlatField::setGainMap(bool enabled) {
flatFieldFromMetaData->set_sensitive(enabled);
if (!enabled) {
idle_register.add(
[this, enabled]() -> bool
{
disableListener();
flatFieldFromMetaData->setValue(false);
enableListener();
return false;
}
);
}
}

View File

@ -23,6 +23,7 @@
#include <gtkmm.h>
#include "adjuster.h"
#include "checkbox.h"
#include "guiutils.h"
#include "toolpanel.h"
@ -42,7 +43,7 @@ public:
// add other info here
};
class FlatField final : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::FlatFieldAutoClipListener
class FlatField final : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel, public rtengine::FlatFieldAutoClipListener
{
protected:
@ -52,6 +53,7 @@ protected:
Gtk::Label *ffInfo;
Gtk::Button *flatFieldFileReset;
Gtk::CheckButton* flatFieldAutoSelect;
CheckBox* flatFieldFromMetaData;
Adjuster* flatFieldClipControl;
Adjuster* flatFieldBlurRadius;
MyComboBoxText* flatFieldBlurType;
@ -64,8 +66,10 @@ protected:
Glib::ustring lastShortcutPath;
bool b_filter_asCurrent;
bool israw;
rtengine::ProcEvent EvFlatFieldFromMetaData;
IdleRegister idle_register;
public:
FlatField ();
@ -90,4 +94,6 @@ public:
ffp = p;
};
void flatFieldAutoClipValueChanged(int n = 0) override;
void checkBoxToggled(CheckBox* c, CheckValue newval) override;
void setGainMap(bool enabled);
};

View File

@ -743,7 +743,7 @@ int processLineParams ( int argc, char **argv )
if (options.defProfRaw == DEFPROFILE_DYNAMIC) {
rawParams->deleteInstance();
delete rawParams;
rawParams = ProfileStore::getInstance()->loadDynamicProfile (ii->getMetaData());
rawParams = ProfileStore::getInstance()->loadDynamicProfile (ii->getMetaData(), inputFile);
}
std::cout << " Merging default raw processing profile." << std::endl;
@ -752,7 +752,7 @@ int processLineParams ( int argc, char **argv )
if (options.defProfImg == DEFPROFILE_DYNAMIC) {
imgParams->deleteInstance();
delete imgParams;
imgParams = ProfileStore::getInstance()->loadDynamicProfile (ii->getMetaData());
imgParams = ProfileStore::getInstance()->loadDynamicProfile (ii->getMetaData(), inputFile);
}
std::cout << " Merging default non-raw processing profile." << std::endl;

View File

@ -687,6 +687,8 @@ void Options::setDefaults()
lastICCProfCreatorDir = "";
gimpPluginShowInfoDialog = true;
maxRecentFolders = 15;
sortMethod = SORT_BY_NAME;
sortDescending = false;
rtSettings.lensfunDbDirectory = ""; // set also in main.cc and main-cli.cc
cropGuides = CROP_GUIDE_FULL;
cropAutoFit = false;
@ -1303,6 +1305,19 @@ void Options::readFromFile(Glib::ustring fname)
if (keyFile.has_key("File Browser", "RecentFolders")) {
recentFolders = keyFile.get_string_list("File Browser", "RecentFolders");
}
if (keyFile.has_key("File Browser", "SortMethod")) {
int v = keyFile.get_integer("File Browser", "SortMethod");
if (v < int(0) || v >= int(SORT_METHOD_COUNT)) {
sortMethod = SORT_BY_NAME;
} else {
sortMethod = SortMethod(v);
}
}
if (keyFile.has_key("File Browser", "SortDescending")) {
sortDescending = keyFile.get_boolean("File Browser", "SortDescending");
}
}
if (keyFile.has_group("Clipping Indication")) {
@ -2389,6 +2404,8 @@ void Options::saveToFile(Glib::ustring fname)
keyFile.set_string_list("File Browser", "RecentFolders", temp);
}
keyFile.set_integer("File Browser", "SortMethod", sortMethod);
keyFile.set_boolean("File Browser", "SortDescending", sortDescending);
keyFile.set_integer("Clipping Indication", "HighlightThreshold", highlightThreshold);
keyFile.set_integer("Clipping Indication", "ShadowThreshold", shadowThreshold);
keyFile.set_boolean("Clipping Indication", "BlinkClipped", blinkClipped);

View File

@ -465,6 +465,17 @@ public:
size_t maxRecentFolders; // max. number of recent folders stored in options file
std::vector<Glib::ustring> recentFolders; // List containing all recent folders
enum SortMethod {
SORT_BY_NAME,
SORT_BY_DATE,
SORT_BY_EXIF,
SORT_BY_RANK,
SORT_BY_LABEL,
SORT_METHOD_COUNT,
};
SortMethod sortMethod; // remembers current state of file browser
bool sortDescending;
Options ();

View File

@ -519,6 +519,7 @@ void ParamsEdited::set(bool v)
raw.df_autoselect = v;
raw.ff_file = v;
raw.ff_AutoSelect = v;
raw.ff_FromMetaData = v;
raw.ff_BlurRadius = v;
raw.ff_BlurType = v;
raw.ff_AutoClipControl = v;
@ -1930,6 +1931,7 @@ void ParamsEdited::initFrom(const std::vector<rtengine::procparams::ProcParams>&
raw.df_autoselect = raw.df_autoselect && p.raw.df_autoselect == other.raw.df_autoselect;
raw.ff_file = raw.ff_file && p.raw.ff_file == other.raw.ff_file;
raw.ff_AutoSelect = raw.ff_AutoSelect && p.raw.ff_AutoSelect == other.raw.ff_AutoSelect;
raw.ff_FromMetaData = raw.ff_FromMetaData && p.raw.ff_FromMetaData == other.raw.ff_FromMetaData;
raw.ff_BlurRadius = raw.ff_BlurRadius && p.raw.ff_BlurRadius == other.raw.ff_BlurRadius;
raw.ff_BlurType = raw.ff_BlurType && p.raw.ff_BlurType == other.raw.ff_BlurType;
raw.ff_AutoClipControl = raw.ff_AutoClipControl && p.raw.ff_AutoClipControl == other.raw.ff_AutoClipControl;
@ -6644,6 +6646,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
toEdit.raw.ff_AutoSelect = mods.raw.ff_AutoSelect;
}
if (raw.ff_FromMetaData) {
toEdit.raw.ff_FromMetaData = mods.raw.ff_FromMetaData;
}
if (raw.ff_BlurRadius) {
toEdit.raw.ff_BlurRadius = mods.raw.ff_BlurRadius;
}
@ -7375,7 +7381,7 @@ bool RAWParamsEdited::XTransSensor::isUnchanged() const
bool RAWParamsEdited::isUnchanged() const
{
return bayersensor.isUnchanged() && xtranssensor.isUnchanged() && ca_autocorrect && ca_avoidcolourshift && caautoiterations && cared && cablue && hotPixelFilter && deadPixelFilter && hotdeadpix_thresh && darkFrame
&& df_autoselect && ff_file && ff_AutoSelect && ff_BlurRadius && ff_BlurType && exPos && ff_AutoClipControl && ff_clipControl;
&& df_autoselect && ff_file && ff_AutoSelect && ff_FromMetaData && ff_BlurRadius && ff_BlurType && exPos && ff_AutoClipControl && ff_clipControl;
}
bool LensProfParamsEdited::isUnchanged() const

View File

@ -1489,6 +1489,7 @@ struct RAWParamsEdited {
bool df_autoselect;
bool ff_file;
bool ff_AutoSelect;
bool ff_FromMetaData;
bool ff_BlurRadius;
bool ff_BlurType;
bool ff_AutoClipControl;

View File

@ -301,6 +301,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
//---
ff_file = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDFILE")));
ff_AutoSelect = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDAUTOSELECT")));
ff_FromMetaData = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDFROMMETADATA")));
ff_BlurType = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDBLURTYPE")));
ff_BlurRadius = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDBLURRADIUS")));
ff_ClipControl = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FLATFIELDCLIPCONTROL")));
@ -423,6 +424,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
vboxes[8]->pack_start (*Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_SHRINK, 0);
vboxes[8]->pack_start (*ff_file, Gtk::PACK_SHRINK, 2);
vboxes[8]->pack_start (*ff_AutoSelect, Gtk::PACK_SHRINK, 2);
vboxes[8]->pack_start (*ff_FromMetaData, Gtk::PACK_SHRINK, 2);
vboxes[8]->pack_start (*ff_BlurType, Gtk::PACK_SHRINK, 2);
vboxes[8]->pack_start (*ff_BlurRadius, Gtk::PACK_SHRINK, 2);
vboxes[8]->pack_start (*ff_ClipControl, Gtk::PACK_SHRINK, 2);
@ -574,6 +576,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
//---
ff_fileConn = ff_file->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
ff_AutoSelectConn = ff_AutoSelect->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
ff_FromMetaDataConn = ff_FromMetaData->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
ff_BlurTypeConn = ff_BlurType->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
ff_BlurRadiusConn = ff_BlurRadius->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
ff_ClipControlConn = ff_ClipControl->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
@ -655,6 +658,7 @@ void PartialPasteDlg::rawToggled ()
ConnectionBlocker df_AutoSelectBlocker(df_AutoSelectConn);
ConnectionBlocker ff_fileBlocker(ff_fileConn);
ConnectionBlocker ff_AutoSelectBlocker(ff_AutoSelectConn);
ConnectionBlocker ff_FromMetaDataBlocker(ff_FromMetaDataConn);
ConnectionBlocker ff_BlurTypeBlocker(ff_BlurTypeConn);
ConnectionBlocker ff_BlurRadiusBlocker(ff_BlurRadiusConn);
ConnectionBlocker ff_ClipControlBlocker(ff_ClipControlConn);
@ -685,6 +689,7 @@ void PartialPasteDlg::rawToggled ()
df_AutoSelect->set_active (raw->get_active ());
ff_file->set_active (raw->get_active ());
ff_AutoSelect->set_active (raw->get_active ());
ff_FromMetaData->set_active (raw->get_active ());
ff_BlurType->set_active (raw->get_active ());
ff_BlurRadius->set_active (raw->get_active ());
ff_ClipControl->set_active (raw->get_active ());
@ -1173,6 +1178,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
filterPE.raw.ff_AutoSelect = falsePE.raw.ff_AutoSelect;
}
if (!ff_FromMetaData->get_active ()) {
filterPE.raw.ff_FromMetaData = falsePE.raw.ff_FromMetaData;
}
if (!ff_BlurRadius->get_active ()) {
filterPE.raw.ff_BlurRadius = falsePE.raw.ff_BlurRadius;
}

View File

@ -214,6 +214,7 @@ public:
Gtk::CheckButton* df_AutoSelect;
Gtk::CheckButton* ff_file;
Gtk::CheckButton* ff_AutoSelect;
Gtk::CheckButton* ff_FromMetaData;
Gtk::CheckButton* ff_BlurRadius;
Gtk::CheckButton* ff_BlurType;
Gtk::CheckButton* ff_ClipControl;
@ -230,7 +231,7 @@ public:
sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn;
sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn;
sigc::connection metadataConn, exifchConn, iptcConn, icmConn;
sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_BlurRadiusConn, ff_BlurTypeConn, ff_ClipControlConn;
sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_FromMetaDataConn, ff_BlurRadiusConn, ff_BlurTypeConn, ff_ClipControlConn;
sigc::connection raw_caredblueConn, raw_ca_autocorrectConn, raw_ca_avoid_colourshiftconn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_pdaf_lines_filterConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_borderConn, raw_imagenumConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_pixelshiftConn, raw_dcb_enhanceConn, raw_exposConn, raw_blackConn;
sigc::connection filmNegativeConn;
sigc::connection captureSharpeningConn;

View File

@ -1091,6 +1091,25 @@ bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event)
}
void ThumbBrowserBase::resort ()
{
{
MYWRITERLOCK(l, entryRW);
std::sort(
fd.begin(),
fd.end(),
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
{
bool lt = a->compare(*b, options.sortMethod);
return options.sortDescending ? !lt : lt;
}
);
}
redraw ();
}
void ThumbBrowserBase::redraw (ThumbBrowserEntryBase* entry)
{
@ -1218,9 +1237,30 @@ void ThumbBrowserBase::enableTabMode(bool enable)
}
}
void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry)
void ThumbBrowserBase::insertEntry (ThumbBrowserEntryBase* entry)
{
entry->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
// find place in sort order
{
MYWRITERLOCK(l, entryRW);
fd.insert(
std::lower_bound(
fd.begin(),
fd.end(),
entry,
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
{
bool lt = a->compare(*b, options.sortMethod);
return options.sortDescending ? !lt : lt;
}
),
entry
);
entry->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
}
redraw ();
}
void ThumbBrowserBase::getScrollPosition (double& h, double& v)

View File

@ -208,12 +208,13 @@ public:
return fd;
}
void on_style_updated () override;
void resort (); // re-apply sort method
void redraw (ThumbBrowserEntryBase* entry = nullptr); // arrange files and draw area
void refreshThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
void refreshQuickThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
void refreshEditedState (const std::set<Glib::ustring>& efiles);
void initEntry (ThumbBrowserEntryBase* entry);
void insertEntry (ThumbBrowserEntryBase* entry);
void getScrollPosition (double& h, double& v);
void setScrollPosition (double h, double v);

View File

@ -119,7 +119,7 @@ Glib::ustring getPaddedName(const Glib::ustring& name)
}
ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname) :
ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname, Thumbnail *thm) :
fnlabw(0),
fnlabh(0),
dtlabw(0),
@ -153,7 +153,8 @@ ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname) :
bbPreview(nullptr),
cursor_type(CSUndefined),
collate_name(getPaddedName(dispname).casefold_collate_key()),
thumbnail(nullptr),
collate_exif(getPaddedName(thm->getExifString()).casefold_collate_key()),
thumbnail(thm),
filename(fname),
selected(false),
drawable(false),

View File

@ -26,6 +26,8 @@
#include "guiutils.h"
#include "lwbuttonset.h"
#include "threadutils.h"
#include "options.h"
#include "thumbnail.h"
#include "../rtengine/coord2d.h"
@ -95,6 +97,7 @@ protected:
private:
const std::string collate_name;
const std::string collate_exif;
public:
@ -117,7 +120,7 @@ public:
bool updatepriority;
eWithFilename withFilename;
explicit ThumbBrowserEntryBase (const Glib::ustring& fname);
explicit ThumbBrowserEntryBase (const Glib::ustring& fname, Thumbnail *thm);
virtual ~ThumbBrowserEntryBase ();
void setParent (ThumbBrowserBase* l)
@ -174,9 +177,32 @@ public:
void setPosition (int x, int y, int w, int h);
void setOffset (int x, int y);
bool operator <(const ThumbBrowserEntryBase& other) const
bool compare (const ThumbBrowserEntryBase& other, Options::SortMethod method) const
{
return collate_name < other.collate_name;
int cmp = 0;
switch (method){
case Options::SORT_BY_NAME:
return collate_name < other.collate_name;
case Options::SORT_BY_DATE:
cmp = thumbnail->getDateTime().compare(other.thumbnail->getDateTime());
break;
case Options::SORT_BY_EXIF:
cmp = collate_exif.compare(other.collate_exif);
break;
case Options::SORT_BY_RANK:
cmp = thumbnail->getRank() - other.thumbnail->getRank();
break;
case Options::SORT_BY_LABEL:
cmp = thumbnail->getColorLabel() - other.thumbnail->getColorLabel();
break;
case Options::SORT_METHOD_COUNT: abort();
}
// Always fall back to sorting by name
if (!cmp)
cmp = collate_name.compare(other.collate_name);
return cmp < 0;
}
virtual void refreshThumbnailImage () = 0;

View File

@ -31,6 +31,7 @@
#include "../rtengine/procparams.h"
#include "../rtengine/rtthumbnail.h"
#include <glib/gstdio.h>
#include <glibmm/timezone.h>
#include "../rtengine/dynamicprofile.h"
#include "../rtengine/profilestore.h"
@ -266,7 +267,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu
// Should we ask all frame's MetaData ?
imageMetaData = rtengine::FramesMetaData::fromFile (fname, nullptr, true);
}
PartialProfile *pp = ProfileStore::getInstance()->loadDynamicProfile(imageMetaData);
PartialProfile *pp = ProfileStore::getInstance()->loadDynamicProfile(imageMetaData, fname);
delete imageMetaData;
int err = pp->pparams->save(outFName);
pp->deleteInstance();
@ -718,11 +719,44 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro
void Thumbnail::generateExifDateTimeStrings ()
{
if (cfs.timeValid) {
std::string dateFormat = options.dateFormat;
std::ostringstream ostr;
bool spec = false;
exifString = "";
dateTimeString = "";
for (size_t i = 0; i < dateFormat.size(); i++)
if (spec && dateFormat[i] == 'y') {
ostr << cfs.year;
spec = false;
} else if (spec && dateFormat[i] == 'm') {
ostr << (int)cfs.month;
spec = false;
} else if (spec && dateFormat[i] == 'd') {
ostr << (int)cfs.day;
spec = false;
} else if (dateFormat[i] == '%') {
spec = true;
} else {
ostr << (char)dateFormat[i];
spec = false;
}
ostr << " " << (int)cfs.hour;
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.min;
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.sec;
dateTimeString = ostr.str ();
dateTime = Glib::DateTime::create_local(cfs.year, cfs.month, cfs.day,
cfs.hour, cfs.min, cfs.sec);
}
if (!dateTime.gobj() || !cfs.timeValid) {
dateTimeString = "";
dateTime = Glib::DateTime::create_now_utc(0);
}
if (!cfs.exifValid) {
exifString = "";
return;
}
@ -731,33 +765,6 @@ void Thumbnail::generateExifDateTimeStrings ()
if (options.fbShowExpComp && cfs.expcomp != "0.00" && !cfs.expcomp.empty()) { // don't show exposure compensation if it is 0.00EV;old cache files do not have ExpComp, so value will not be displayed.
exifString = Glib::ustring::compose ("%1 %2EV", exifString, cfs.expcomp); // append exposure compensation to exifString
}
std::string dateFormat = options.dateFormat;
std::ostringstream ostr;
bool spec = false;
for (size_t i = 0; i < dateFormat.size(); i++)
if (spec && dateFormat[i] == 'y') {
ostr << cfs.year;
spec = false;
} else if (spec && dateFormat[i] == 'm') {
ostr << (int)cfs.month;
spec = false;
} else if (spec && dateFormat[i] == 'd') {
ostr << (int)cfs.day;
spec = false;
} else if (dateFormat[i] == '%') {
spec = true;
} else {
ostr << (char)dateFormat[i];
spec = false;
}
ostr << " " << (int)cfs.hour;
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.min;
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.sec;
dateTimeString = ostr.str ();
}
const Glib::ustring& Thumbnail::getExifString () const
@ -772,6 +779,12 @@ const Glib::ustring& Thumbnail::getDateTimeString () const
return dateTimeString;
}
const Glib::DateTime& Thumbnail::getDateTime () const
{
return dateTime;
}
void Thumbnail::getAutoWB (double& temp, double& green, double equal, double tempBias)
{
if (cfs.redAWBMul != -1.0) {
@ -802,6 +815,16 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, std::unique_ptr<rtengi
cfs.timeValid = false;
cfs.exifValid = false;
if (idata->getDateTimeAsTS() > 0) {
cfs.year = 1900 + idata->getDateTime().tm_year;
cfs.month = idata->getDateTime().tm_mon + 1;
cfs.day = idata->getDateTime().tm_mday;
cfs.hour = idata->getDateTime().tm_hour;
cfs.min = idata->getDateTime().tm_min;
cfs.sec = idata->getDateTime().tm_sec;
cfs.timeValid = true;
}
if (idata->hasExif()) {
cfs.shutter = idata->getShutterSpeed ();
cfs.fnumber = idata->getFNumber ();
@ -814,18 +837,11 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, std::unique_ptr<rtengi
cfs.isPixelShift = idata->getPixelShift ();
cfs.frameCount = idata->getFrameCount ();
cfs.sampleFormat = idata->getSampleFormat ();
cfs.year = 1900 + idata->getDateTime().tm_year;
cfs.month = idata->getDateTime().tm_mon + 1;
cfs.day = idata->getDateTime().tm_mday;
cfs.hour = idata->getDateTime().tm_hour;
cfs.min = idata->getDateTime().tm_min;
cfs.sec = idata->getDateTime().tm_sec;
cfs.timeValid = true;
cfs.exifValid = true;
cfs.lens = idata->getLens();
cfs.camMake = idata->getMake();
cfs.camModel = idata->getModel();
cfs.rating = idata->getRating();
cfs.exifValid = true;
if (idata->getOrientation() == "Rotate 90 CW") {
deg = 90;

View File

@ -22,6 +22,7 @@
#include <string>
#include <glibmm/ustring.h>
#include <glibmm/datetime.h>
#include "cacheimagedata.h"
#include "threadutils.h"
@ -73,6 +74,7 @@ class Thumbnail
// exif & date/time strings
Glib::ustring exifString;
Glib::ustring dateTimeString;
Glib::DateTime dateTime;
bool initial_;
@ -124,6 +126,7 @@ public:
const Glib::ustring& getExifString () const;
const Glib::ustring& getDateTimeString () const;
const Glib::DateTime& getDateTime () const;
void getCamWB (double& temp, double& green) const;
void getAutoWB (double& temp, double& green, double equal, double tempBias);
void getSpotWB (int x, int y, int rect, double& temp, double& green);

View File

@ -343,12 +343,12 @@ ToolPanelCoordinator::~ToolPanelCoordinator ()
delete toolBar;
}
void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool isMono)
void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool isMono, bool isGainMapSupported)
{
if (isRaw) {
if (isBayer) {
idle_register.add(
[this]() -> bool
[this, isGainMapSupported]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorxtrans->FoldableToolPanel::hide();
@ -362,6 +362,7 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
preprocessWB->FoldableToolPanel::show();
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
flatfield->setGainMap(isGainMapSupported);
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@ -369,7 +370,7 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
);
} else if (isXtrans) {
idle_register.add(
[this]() -> bool
[this, isGainMapSupported]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorxtrans->FoldableToolPanel::show();
@ -383,6 +384,7 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
preprocessWB->FoldableToolPanel::show();
preprocess->FoldableToolPanel::show();
flatfield->FoldableToolPanel::show();
flatfield->setGainMap(isGainMapSupported);
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;
@ -390,7 +392,7 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
);
} else if (isMono) {
idle_register.add(
[this]() -> bool
[this, isGainMapSupported]() -> bool
{
rawPanelSW->set_sensitive(true);
sensorbayer->FoldableToolPanel::hide();
@ -403,6 +405,7 @@ void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtr
preprocessWB->FoldableToolPanel::hide();
preprocess->FoldableToolPanel::hide();
flatfield->FoldableToolPanel::show();
flatfield->setGainMap(isGainMapSupported);
pdSharpening->FoldableToolPanel::show();
retinex->FoldableToolPanel::setGrayedOut(false);
return false;

View File

@ -260,7 +260,7 @@ public:
void unsetTweakOperator (rtengine::TweakOperator *tOperator) override;
// FilmNegProvider interface
void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false) override;
void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false, bool isGainMapSupported = false) override;
// profilechangelistener interface
void profileChange(