Merge branch 'dev' into multi-external-editor
This commit is contained in:
commit
4d287b4cdf
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@ -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" \
|
||||
|
@ -1,4 +1,6 @@
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
|
BIN
rtdata/dcpprofiles/FUJIFILM DBP for GX680.dcp
Normal file
BIN
rtdata/dcpprofiles/FUJIFILM DBP for GX680.dcp
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 }
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
43
rtengine/dnggainmap.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -245,11 +245,6 @@ public:
|
||||
return zero_is_bad == 1;
|
||||
}
|
||||
|
||||
bool isBayer() const
|
||||
{
|
||||
return (filters != 0 && filters != 9);
|
||||
}
|
||||
|
||||
bool isXtrans() const
|
||||
{
|
||||
return filters == 9;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
124
rtengine/winutils.h
Normal 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
|
@ -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),
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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_;
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user