Add decoding through LibRaw

Decode raw files with LibRaw and fall back to dcraw if LibRaw is unable
to read the file.
This commit is contained in:
Lawrence Lee 2023-11-18 21:23:55 -08:00
parent 3f5c3988e5
commit 20d3311931
No known key found for this signature in database
GPG Key ID: 048FF2B76A63895F
11 changed files with 669 additions and 101 deletions

View File

@ -22,7 +22,7 @@ jobs:
mkdir build
date +%s > build/stamp
brew uninstall --ignore-dependencies libtiff
brew install libtiff gtk+3 gtkmm3 gtk-mac-integration adwaita-icon-theme libsigc++@2 little-cms2 libiptcdata fftw lensfun expat pkgconfig llvm shared-mime-info exiv2 | tee -a depslog
brew install libtiff gtk+3 gtkmm3 gtk-mac-integration adwaita-icon-theme libsigc++@2 little-cms2 libiptcdata fftw lensfun expat pkgconfig llvm shared-mime-info exiv2 automake | tee -a depslog
date -u
echo "----====Pourage====----"
cat depslog | grep Pouring
@ -42,6 +42,7 @@ jobs:
export REF=${GITHUB_REF##*/}
export C_FLAGS=$(echo -e $C_FLAGS | tr -d '\n')
cd build && date -u && date +%s > configstamp
curl -L https://github.com/Homebrew/homebrew-core/raw/679923b4eb48a8dc7ecc1f05d06063cd79b3fc00/Formula/libomp.rb -o libomp.rb && brew install --formula libomp.rb
cmake \
-DCMAKE_BUILD_TYPE="Release" \
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
@ -66,7 +67,6 @@ jobs:
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
-DOSX_CONTINUOUS=ON \
..
curl -L https://github.com/Homebrew/homebrew-core/raw/679923b4eb48a8dc7ecc1f05d06063cd79b3fc00/Formula/libomp.rb -o libomp.rb && brew install --formula libomp.rb
zsh -c 'echo "Configured in $(printf "%0.2f" $(($[$(date +%s)-$(cat configstamp)]/$((60.))))) minutes"'
- name: Compile RawTherapee
run: |

View File

@ -43,6 +43,7 @@ jobs:
cc:p
pkgconf:p
cmake:p
autotools:p
ninja:p
gtkmm3:p
lcms2:p

View File

@ -226,6 +226,7 @@ option(WITH_LTO "Build with link-time optimizations" OFF)
option(WITH_SAN "Build with run-time sanitizer" OFF)
option(WITH_PROF "Build with profiling instrumentation" OFF)
option(WITH_SYSTEM_KLT "Build using system KLT library." OFF)
option(WITH_SYSTEM_LIBRAW "Build using system LibRaw library." OFF)
option(OPTION_OMP "Build with OpenMP support" ON)
option(
STRICT_MUTEX
@ -522,6 +523,13 @@ foreach(l ${_exiv2_libs})
set(EXIV2_LIBRARIES ${EXIV2_LIBRARIES} ${_el})
endforeach()
if(NOT WITH_SYSTEM_LIBRAW)
set(LIBRAW_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/rtengine/libraw/lib/.libs/libraw_r.a")
if(WIN32)
set(LIBRAW_LIBRARIES ${LIBRAW_LIBRARIES} -lws2_32)
endif()
endif()
if(WIN32)
add_definitions(-DWIN32)
add_definitions(-D_WIN32)
@ -551,6 +559,9 @@ find_package(ZLIB REQUIRED)
if(WITH_SYSTEM_KLT)
find_package(KLT REQUIRED)
endif()
if(WITH_SYSTEM_LIBRAW)
pkg_check_modules(LIBRAW REQUIRED libraw_r>=0.21)
endif()
# Check for libcanberra-gtk3 (sound events on Linux):
if(UNIX AND (NOT APPLE))

View File

@ -45,6 +45,11 @@ endif()
if(EXIV2_INCLUDE_DIRS)
include_directories("${EXIV2_INCLUDE_DIRS}")
endif()
if(NOT WITH_SYSTEM_LIBRAW)
include_directories("${CMAKE_SOURCE_DIR}/rtengine/libraw")
else()
include_directories("${LIBRAW_INCLUDE_DIRS}")
endif()
link_directories(
"${EXPAT_LIBRARY_DIRS}"
@ -245,10 +250,16 @@ target_link_libraries(rtengine
${RSVG_LIBRARIES}
${KLT_LIBRARIES}
${EXIV2_LIBRARIES}
${LIBRAW_LIBRARIES}
)
if(OpenMP_FOUND)
target_link_libraries(rtengine ${OpenMP_CXX_LIBRARIES})
endif()
# Configure LibRaw
if(NOT WITH_SYSTEM_LIBRAW)
include(LibRaw.cmake)
endif()
install(FILES ${CAMCONSTSFILE} DESTINATION "${DATADIR}" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)

124
rtengine/LibRaw.cmake Normal file
View File

@ -0,0 +1,124 @@
# LibRaw has its own configuration script and uses make to build. Here we add
# the LibRaw configuration commands so they run during the CMake configuration.
# Also, add a target which always runs make.
set(LIBRAW_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraw")
set(LIBRAW_LIB_DIR "${LIBRAW_DIR}/lib")
set(LIBRAW_PHANTOM_FILE "${LIBRAW_LIB_DIR}/phantom_file")
if(DEFINED ENV{SHELL})
set(SHELL "$ENV{SHELL}")
else()
set(SHELL "sh")
endif()
add_custom_target(
LibRaw ALL
DEPENDS ${LIBRAW_PHANTOM_FILE} # Ensures target always executes.
)
# Configuration flags.
set(CONFIGURE_FLAGS "--disable-examples")
set(LIBRAW_CXX_FLAGS "${CXX_FLAGS} -std=gnu++11 -Wno-error=unknown-pragmas")
# Let the configure script handle OpenMP flags.
string(REPLACE "${OpenMP_CXX_FLAGS}" "" LIBRAW_CXX_FLAGS "${LIBRAW_CXX_FLAGS}")
if(OPTION_OMP)
set(CONFIGURE_FLAGS "${CONFIGURE_FLAGS} --enable-openmp")
else()
set(CONFIGURE_FLAGS "${CONFIGURE_FLAGS} --disable-openmp")
endif()
set(CONFIGURE_FLAGS "${CONFIGURE_FLAGS} CC=\"${CMAKE_C_COMPILER}\"")
set(CONFIGURE_FLAGS "${CONFIGURE_FLAGS} CXX=\"${CMAKE_CXX_COMPILER}\"")
set(CONFIGURE_FLAGS "${CONFIGURE_FLAGS} CXXFLAGS=\"${LIBRAW_CXX_FLAGS}\"")
# Configuration commands.
message(STATUS "Configuring LibRaw")
execute_process(
COMMAND cp -p -R "${CMAKE_CURRENT_SOURCE_DIR}/libraw" .
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
RESULT_VARIABLE PROCESS_RESULT
COMMAND_ECHO STDOUT
)
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
message(FATAL_ERROR "Could not copy LibRaw files into build directory")
endif()
execute_process(
COMMAND "${SHELL}" -l -c "autoreconf -v --install"
WORKING_DIRECTORY "${LIBRAW_DIR}"
RESULT_VARIABLE PROCESS_RESULT
COMMAND_ECHO STDOUT
)
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
message(FATAL_ERROR "Could not generate LibRaw configuration script")
endif()
execute_process(
COMMAND "${SHELL}" -l -c "./configure ${CONFIGURE_FLAGS}"
WORKING_DIRECTORY "${LIBRAW_DIR}"
RESULT_VARIABLE PROCESS_RESULT
COMMAND_ECHO STDOUT
)
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
execute_process(
COMMAND cat config.log
WORKING_DIRECTORY "${LIBRAW_DIR}"
COMMAND_ECHO STDOUT
)
message(FATAL_ERROR "LibRaw configure failed")
endif()
# Build flags.
set(LIBRAW_MAKE_FLAGS "")
if(CMAKE_GENERATOR MATCHES ".*Makefiles.*")
set(LIBRAW_MAKE_COMMAND "$(MAKE)")
else()
# If not using Makefiles, set number of jobs equal to logical processors
# count. Not necessary for make because of the jobserver.
execute_process(
COMMAND "${SHELL}" -l -c "nproc"
OUTPUT_VARIABLE LOGICAL_PROCESSORS
RESULT_VARIABLE PROCESS_RESULT
ERROR_QUIET
)
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
execute_process(
COMMAND "${SHELL}" -l -c "sysctl -n hw.ncpu"
OUTPUT_VARIABLE LOGICAL_PROCESSORS
RESULT_VARIABLE PROCESS_RESULT
ERROR_QUIET
)
endif()
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
execute_process(
COMMAND "${SHELL}" -l -c "getconf _NPROCESSORS_ONLN"
OUTPUT_VARIABLE LOGICAL_PROCESSORS
RESULT_VARIABLE PROCESS_RESULT
ERROR_QUIET
)
endif()
if(PROCESS_RESULT AND NOT PROCESS_RESULT EQUAL 0)
set(LOGICAL_PROCESSORS "1")
endif()
string(STRIP "${LOGICAL_PROCESSORS}" LOGICAL_PROCESSORS)
set(LIBRAW_MAKE_FLAGS "${LIBRAW_MAKE_FLAGS} -j${LOGICAL_PROCESSORS}")
set(LIBRAW_MAKE_COMMAND "make")
endif()
# Build commands.
add_custom_command(
OUTPUT "${LIBRAW_PHANTOM_FILE}" "${LIBRAW_LIB_DIR}/.libs/libraw_r.a"
COMMAND cp -p -R "${CMAKE_CURRENT_SOURCE_DIR}/libraw" ..
COMMAND "${SHELL}" -l -c "${LIBRAW_MAKE_COMMAND} ${LIBRAW_MAKE_FLAGS}"
COMMENT "Building LibRaw"
WORKING_DIRECTORY libraw
VERBATIM
)
# Add a `make clean-libraw` command because there's no good way to automatically
# clean the LibRaw build with `make`clean`.
add_custom_target(
clean-libraw
COMMAND make clean
COMMAND rm -rf lib
WORKING_DIRECTORY libraw
)

View File

@ -67,6 +67,7 @@ public:
,getbithuff(this,ifp,zero_after_ff)
,nikbithuff(ifp)
{
shrink=0;
memset(&hbd, 0, sizeof(hbd));
aber[0]=aber[1]=aber[2]=aber[3]=1;
gamm[0]=0.45;gamm[1]=4.5;gamm[2]=gamm[3]=gamm[4]=gamm[5]=0;

View File

@ -44,6 +44,7 @@ const Settings* settings;
MyMutex* lcmsMutex = nullptr;
MyMutex *fftwMutex = nullptr;
MyMutex *librawMutex = nullptr;
int init (const Settings* s, const Glib::ustring& baseDir, const Glib::ustring& userSettingsDir, bool loadAll)
{
@ -120,6 +121,8 @@ int init (const Settings* s, const Glib::ustring& baseDir, const Glib::ustring&
delete lcmsMutex;
lcmsMutex = new MyMutex;
fftwMutex = new MyMutex;
delete librawMutex;
librawMutex = new MyMutex;
return 0;
}

View File

@ -58,6 +58,9 @@ unsigned DCraw::pana_bits_t::operator() (int nbits, unsigned *bytes)
}
}
namespace
{
class pana_cs6_page_decoder
{
unsigned int pixelbuffer[14], lastoffset, maxoffset;
@ -99,6 +102,8 @@ void pana_cs6_page_decoder::read_page()
}
#undef wbuffer
}
void DCraw::panasonic_load_raw()
{
int enc_blck_size = RT_pana_info.bpp == 12 ? 10 : 9;

View File

@ -1,6 +1,8 @@
/*
* This file is part of RawTherapee.
*
* LibRaw integration adapted from ART.
*
* Created on: 20/nov/2010
*/
@ -11,17 +13,24 @@
#include <netinet/in.h>
#endif
#include "image8.h"
#include "rawimage.h"
#include "settings.h"
#include "camconst.h"
#include "utils.h"
#include "rtengine.h"
#include "libraw/libraw/libraw.h"
namespace rtengine
{
extern MyMutex *librawMutex;
RawImage::RawImage(const Glib::ustring &name)
: data(nullptr)
: DCraw()
, data(nullptr)
, prefilters(0)
, filename(name)
, rotate_deg(0)
@ -29,9 +38,12 @@ RawImage::RawImage(const Glib::ustring &name)
, allocation(nullptr)
{
memset(maximum_c4, 0, sizeof(maximum_c4));
memset(white, 0, sizeof(white));
RT_matrix_from_constant = ThreeValBool::X;
RT_blacklevel_from_constant = ThreeValBool::X;
RT_whitelevel_from_constant = ThreeValBool::X;
memset(make, 0, sizeof(make));
memset(model, 0, sizeof(model));
}
RawImage::~RawImage()
@ -41,7 +53,7 @@ RawImage::~RawImage()
ifp = nullptr;
}
if (image) {
if (image && decoder == Decoder::DCRAW) {
free(image);
}
@ -66,6 +78,18 @@ RawImage::~RawImage()
}
}
void RawImage::pre_interpolate()
{
int w = width, h = height;
if (decoder == Decoder::LIBRAW) {
width = iwidth;
height = iheight;
}
DCraw::pre_interpolate();
width = w;
height = h;
}
eSensorType RawImage::getSensorType() const
{
if (isBayer()) {
@ -467,7 +491,176 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
// set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly
shot_select = imageNum;
libraw.reset(new LibRaw());
int libraw_error = [&]() -> int {
libraw->imgdata.params.use_camera_wb = 1;
int err = libraw->open_buffer(ifp->data, ifp->size);
if (err == LIBRAW_FILE_UNSUPPORTED || err == LIBRAW_TOO_BIG) {
// fallback to the internal one
return err;
} else if (err != LIBRAW_SUCCESS && strncmp(libraw->imgdata.idata.software, "make_arq", 8) == 0) {
return err;
} else if (err == LIBRAW_FILE_UNSUPPORTED && (strncmp(libraw->unpack_function_name(), "sony_arq_load_raw", 17) == 0 || strncmp(libraw->imgdata.idata.software, "HDRMerge", 8) == 0)) {
return err;
} else if (err != LIBRAW_SUCCESS) {
decoder = Decoder::LIBRAW;
return err;
} else if (libraw->is_floating_point() && libraw->imgdata.idata.dng_version) {
return err;
}
auto &d = libraw->imgdata.idata;
is_raw = d.raw_count;
strncpy(make, d.normalized_make, sizeof(make)-1);
make[sizeof(make)-1] = 0;
strncpy(model, d.normalized_model, sizeof(model)-1);
model[sizeof(model)-1] = 0;
RT_software = d.software;
dng_version = d.dng_version;
filters = d.filters;
is_foveon = d.is_foveon;
colors = d.colors;
tiff_bps = 0;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 6; ++j) {
xtrans[i][j] = d.xtrans[i][j];
xtrans_abs[i][j] = d.xtrans_abs[i][j];
}
}
auto &s = libraw->imgdata.sizes;
raw_width = s.raw_width;
raw_height = s.raw_height;
width = s.width;
height = s.height;
top_margin = s.top_margin;
left_margin = s.left_margin;
iheight = s.iheight;
iwidth = s.iwidth;
flip = s.flip;
auto &o = libraw->imgdata.other;
iso_speed = o.iso_speed;
shutter = o.shutter;
aperture = o.aperture;
focal_len = o.focal_len;
timestamp = o.timestamp;
shot_order = o.shot_order;
auto &io = libraw->imgdata.rawdata.ioparams;
shrink = io.shrink;
zero_is_bad = io.zero_is_bad;
fuji_width = io.fuji_width;
raw_color = io.raw_color;
mix_green = io.mix_green;
auto &cd = libraw->imgdata.color;
black = cd.black;
maximum = cd.maximum;
tiff_bps = cd.raw_bps;
for (size_t i = 0; i < sizeof(cblack)/sizeof(unsigned); ++i) {
cblack[i] = cd.cblack[i];
}
for (int i = 0; i < 4; ++i) {
cam_mul[i] = cd.cam_mul[i];
pre_mul[i] = cd.pre_mul[i];
}
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
cmatrix[i][j] = cd.cmatrix[i][j];
rgb_cam[i][j] = cd.rgb_cam[i][j];
}
}
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 8; ++j) {
white[i][j] = cd.white[i][j];
}
}
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 4; ++j) {
mask[i][j] = s.mask[i][j];
}
}
auto &mkn = libraw->imgdata.makernotes;
// if (!strcmp(make, "Panasonic") && mkn.panasonic.BlackLevelDim > 0) {
// memset(cblack, 0, sizeof(cblack));
// if (mkn.panasonic.BlackLevelDim >= 4) {
// for (size_t i = 0; i < 4; ++i) {
// cblack[i] = mkn.panasonic.BlackLevel[i];
// }
// black = 0;
// } else {
// black = mkn.panasonic.BlackLevel[0];
// }
// } else
if (!strcmp(make, "Canon") && isBayer() && !dng_version) {
if (mkn.canon.AverageBlackLevel) {
memset(cblack, 0, sizeof(cblack));
for (size_t i = 0; i < 4; ++i) {
cblack[i] = mkn.canon.ChannelBlackLevel[i];
}
black = 0;
}
if (mkn.canon.SpecularWhiteLevel) {
maximum = mkn.canon.SpecularWhiteLevel;
} else if (mkn.canon.NormalWhiteLevel) {
maximum = mkn.canon.NormalWhiteLevel;
}
}
while (tiff_bps < 16 && (size_t(1) << size_t(tiff_bps)) < maximum) {
++tiff_bps;
}
if (dng_version) {
RT_whitelevel_from_constant = ThreeValBool::F;
RT_blacklevel_from_constant = ThreeValBool::F;
if (!isBayer() && !isXtrans()) {
RT_matrix_from_constant = ThreeValBool::F;
}
} else if (strcmp(make, "Panasonic") != 0) {
RT_whitelevel_from_constant = ThreeValBool::T;
RT_blacklevel_from_constant = ThreeValBool::T;
}
if (is_foveon) {
raw_width = width;
raw_height = height;
top_margin = 0;
left_margin = 0;
}
decoder = Decoder::LIBRAW;
return err;
}();
if (libraw_error && verbose) {
printf("LibRaw could not load image.");
if (decoder == Decoder::DCRAW) {
printf(" Falling back to dcraw.");
}
printf("\n");
}
if (decoder == Decoder::LIBRAW) {
if (libraw_error) {
return libraw_error;
}
} else {
libraw->recycle();
}
if (decoder == Decoder::DCRAW) {
identify();
}
// in case dcraw didn't handle the above mentioned case...
shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1);
@ -482,7 +675,7 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
return 2;
}
if (!strcmp(make, "Fujifilm") && raw_height * raw_width * 2u != raw_size) {
if (decoder == Decoder::DCRAW && !strcmp(make, "Fujifilm") && raw_height * raw_width * 2u != raw_size) {
if (raw_width * raw_height * 7u / 4u == raw_size) {
load_raw = &RawImage::fuji_14bit_load_raw;
} else {
@ -514,6 +707,7 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
iheight = height;
iwidth = width;
if (decoder == Decoder::DCRAW) {
if (filters || colors == 1) {
raw_image = (ushort *) calloc ((static_cast<unsigned int>(raw_height) + 7u) * static_cast<unsigned int>(raw_width), 2);
merror(raw_image, "main()");
@ -537,6 +731,63 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
// Load raw pixels data
fseek(ifp, data_offset, SEEK_SET);
(this->*load_raw)();
} else if (decoder == Decoder::LIBRAW) {
libraw->imgdata.rawparams.shot_select = shot_select;
int err = libraw->open_buffer(ifp->data, ifp->size);
if (err) {
return err;
}
{
#ifdef LIBRAW_USE_OPENMP
MyMutex::MyLock lock(*librawMutex);
#endif
err = libraw->unpack();
}
if (err) {
return err;
}
auto &rd = libraw->imgdata.rawdata;
raw_image = rd.raw_image;
if (rd.float_image) {
float_raw_image = new float[raw_width * raw_height];
for (int y = 0; y < raw_height; ++y) {
for (int x = 0; x < raw_width; ++x) {
size_t idx = y * raw_width + x;
float_raw_image[idx] = rd.float_image[idx];
}
}
} else {
#ifdef LIBRAW_USE_OPENMP
MyMutex::MyLock lock(*librawMutex);
#endif
float_raw_image = nullptr;
err = libraw->raw2image();
if (err) {
return err;
}
image = libraw->imgdata.image;
}
// get our custom camera matrices, but don't mess with black/white levels yet
// (if we have custom levels in json files, we will get them later)
auto bl = RT_blacklevel_from_constant;
auto wl = RT_whitelevel_from_constant;
RT_blacklevel_from_constant = ThreeValBool::F;
RT_whitelevel_from_constant = ThreeValBool::F;
adobe_coeff(make, model);
RT_blacklevel_from_constant = bl;
RT_whitelevel_from_constant = wl;
if (libraw->imgdata.color.profile_length) {
profile_length = libraw->imgdata.color.profile_length;
profile_data = new char[profile_length];
memcpy(profile_data, libraw->imgdata.color.profile, profile_length);
}
}
if (!float_raw_image) { // apply baseline exposure only for float DNGs
RT_baseline_exposure = 0;
@ -551,6 +802,10 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
bool raw_crop_cc = false;
int orig_raw_width = width;
int orig_raw_height = height;
// For raw crop when using LibRaw.
int raw_top_margin = 0;
int raw_left_margin = 0;
bool adjust_margins = false;
if (raw_image) {
orig_raw_width = raw_width;
@ -561,6 +816,18 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
int lm, tm, w, h;
cc->get_rawCrop(raw_width, raw_height, lm, tm, w, h);
if ((w < 0 || h < 0) && decoder != Decoder::DCRAW) {
raw_crop_cc = false;
} else {
// protect against DNG files that are already cropped
if (int(raw_width) <= w+lm) {
lm = max(int(raw_width) - w, 0);
}
if (int(raw_height) <= h+tm) {
tm = max(int(raw_height) - h, 0);
}
if (decoder == Decoder::DCRAW) {
if (isXtrans()) {
shiftXtransMatrix(6 - ((top_margin - tm) % 6), 6 - ((left_margin - lm) % 6));
} else {
@ -571,6 +838,32 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
left_margin = lm;
top_margin = tm;
} else {
if (lm < left_margin) {
lm = left_margin;
}
if (tm < top_margin) {
tm = top_margin;
}
// make sure we do not rotate filters
if (isXtrans()) {
if ((tm - top_margin) % 6) {
tm = top_margin;
}
if ((lm - left_margin) % 6) {
lm = left_margin;
}
} else {
if ((tm - top_margin) & 1) {
tm = top_margin;
}
if ((lm - left_margin) & 1) {
lm = left_margin;
}
}
raw_left_margin = lm - left_margin;
raw_top_margin = tm - top_margin;
}
if (w < 0) {
iwidth += w;
@ -578,7 +871,12 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
width += w;
width -= left_margin;
} else if (w > 0) {
iwidth = width = min((int)width, w);
width = min((int)width, w);
if (decoder == Decoder::DCRAW) {
iwidth = width;
} else if (width > iwidth) {
width = iwidth;
}
}
if (h < 0) {
@ -587,7 +885,13 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
height += h;
height -= top_margin;
} else if (h > 0) {
iheight = height = min((int)height, h);
height = min((int)height, h);
if (decoder == Decoder::DCRAW) {
iheight = height;
} else if (height > iheight) {
height = iheight;
}
}
}
}
@ -597,11 +901,14 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
}
}
if (decoder == Decoder::DCRAW) {
crop_masked_pixels();
free(raw_image);
}
raw_image = nullptr;
adjust_margins = !float_raw_image; //true;
} else {
if (get_maker() == "Sigma" && cc && cc->has_rawCrop(width, height)) { // foveon images
if (decoder == Decoder::DCRAW && get_maker() == "Sigma" && cc && cc->has_rawCrop(width, height)) { // foveon images
raw_crop_cc = true;
int lm, tm, w, h;
cc->get_rawCrop(width, height, lm, tm, w, h);
@ -611,21 +918,27 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
if (w < 0) {
width += w;
width -= left_margin;
iwidth += w;
iwidth -= left_margin;
} else if (w > 0) {
width = min((int)width, w);
iwidth = width;
}
if (h < 0) {
height += h;
height -= top_margin;
iheight += h;
iheight -= top_margin;
} else if (h > 0) {
height = min((int)height, h);
iheight = height;
}
}
}
// Load embedded profile
if (profile_length) {
if (decoder == Decoder::DCRAW && profile_length) {
profile_data = new char[profile_length];
fseek(ifp, profile_offset, SEEK_SET);
fread(profile_data, 1, profile_length, ifp);
@ -690,23 +1003,30 @@ int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, Prog
for (int c = 0; c < 4; c++) {
if (static_cast<int>(cblack[c]) < black_c4[c]) {
cblack[c] = black_c4[c];
cblack[4] = cblack[5] = 0;
}
}
if (settings->verbose) {
const char *decoder_name = decoder == Decoder::DCRAW ? "dcraw" : decoder == Decoder::LIBRAW ? "libraw" : "unknown";
if (cc) {
printf("constants exists for \"%s %s\" in camconst.json\n", make, model);
} else {
printf("no constants in camconst.json exists for \"%s %s\" (relying only on dcraw defaults)\n", make, model);
printf("no constants in camconst.json exists for \"%s %s\" (relying only on %s defaults)\n", make, model, decoder_name);
}
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, raw_crop_cc ? "camconst.json" : "dcraw");
printf("color matrix provided by %s\n", (cc && cc->has_dcrawMatrix()) ? "camconst.json" : "dcraw");
printf("black levels: R:%d G1:%d B:%d G2:%d (provided by %s)\n", get_cblack(0), get_cblack(1), get_cblack(2), get_cblack(3),
black_from_cc ? "camconst.json" : decoder_name);
printf("white levels: R:%d G1:%d B:%d G2:%d (provided by %s)\n", get_white(0), get_white(1), get_white(2), get_white(3),
white_from_cc ? "camconst.json" : decoder_name);
printf("raw crop: %d %d %d %d (provided by %s)\n", left_margin, top_margin, iwidth, iheight, raw_crop_cc ? "camconst.json" : decoder_name);
printf("color matrix provided by %s\n", (cc && cc->has_dcrawMatrix()) ? "camconst.json" : decoder_name);
}
if (adjust_margins) {
top_margin = raw_top_margin;
left_margin = raw_left_margin;
}
}
@ -779,7 +1099,7 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage)
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
this->data[row][col] = image[row * width + col][FC(row, col)];
this->data[row][col] = image[(row + top_margin) * iwidth + col + left_margin][FC(row, col)];
}
} else if (isXtrans()) {
#ifdef _OPENMP
@ -788,7 +1108,7 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage)
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
this->data[row][col] = image[row * width + col][XTRANSFC(row, col)];
this->data[row][col] = image[(row + top_margin) * iwidth + col + left_margin][XTRANSFC(row, col)];
}
} else if (colors == 1) {
#ifdef _OPENMP
@ -797,7 +1117,7 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage)
for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++) {
this->data[row][col] = image[row * width + col][0];
this->data[row][col] = image[row * iwidth + col][0];
}
} else {
if((get_maker() == "Sigma" || get_maker() == "Pentax" || get_maker() == "Sony") && dng_version) { // Hack to prevent sigma dng files and dng files from PixelShift2DNG from crashing
@ -817,7 +1137,11 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage)
}
if (freeImage) {
if (decoder == Decoder::DCRAW) {
free(image); // we don't need this anymore
} else if (decoder == Decoder::LIBRAW) {
libraw->recycle();
}
image = nullptr;
}
@ -872,6 +1196,89 @@ RawImage::get_thumbSwap() const
return (order == 0x4949) == (ntohs(0x1234) == 0x1234);
}
bool RawImage::checkThumbOk() const
{
if (!is_supportedThumb()) {
return false;
}
if (get_thumbOffset() >= get_file()->size) {
return false;
}
const ssize_t length =
fdata (get_thumbOffset(), get_file())[1] != 0xD8 && is_ppmThumb()
? get_thumbWidth() * get_thumbHeight() * (get_thumbBPS() / 8) * 3
: get_thumbLength();
return get_thumbOffset() + length <= get_file()->size;
}
Image8 *RawImage::getThumbnail() const
{
if (decoder == Decoder::DCRAW) {
if (!checkThumbOk()) {
return nullptr;
}
Image8 *img = new Image8();
img->setSampleFormat(IIOSF_UNSIGNED_CHAR);
img->setSampleArrangement(IIOSA_CHUNKY);
const char *data = reinterpret_cast<const char *>(fdata(get_thumbOffset(), get_file()));
int err = 1;
if ((unsigned char)data[1] == 0xd8) {
err = img->loadJPEGFromMemory(data, get_thumbLength());
} else if (is_ppmThumb()) {
err = img->loadPPMFromMemory(data, get_thumbWidth(), get_thumbHeight(), get_thumbSwap(), get_thumbBPS());
}
// did we succeed?
if (err) {
delete img;
img = nullptr;
}
return img;
}
if (!ifp) {
return nullptr;
} else {
int err = libraw->unpack_thumb();
if (err) {
return nullptr;
}
auto &t = libraw->imgdata.thumbnail;
if (!t.thumb) {
return nullptr;
} else if (t.tformat != LIBRAW_THUMBNAIL_JPEG && t.tformat != LIBRAW_THUMBNAIL_BITMAP) {
return nullptr;
} else {
Image8 *img = new Image8();
img->setSampleFormat(IIOSF_UNSIGNED_CHAR);
img->setSampleArrangement(IIOSA_CHUNKY);
if (t.tformat == LIBRAW_THUMBNAIL_JPEG) {
err = img->loadJPEGFromMemory(t.thumb, t.tlength);
} else {
err = img->loadPPMFromMemory(t.thumb, t.twidth, t.theight, false, 8);
}
if (err) {
delete img;
return nullptr;
} else {
return img;
}
}
}
return nullptr;
}
} //namespace rtengine
bool

View File

@ -21,14 +21,23 @@
#include <ctime>
#include <cmath>
#include <iostream>
#include <memory>
#include <glibmm/ustring.h>
#include "dcraw.h"
#include "imageformat.h"
class LibRaw;
namespace rtengine
{
class Image8;
class RawImage: public DCraw
{
public:
@ -57,11 +66,18 @@ public:
double getBaselineExposure() const { return RT_baseline_exposure; }
protected:
enum class Decoder {
DCRAW,
LIBRAW,
};
Glib::ustring filename; // complete filename
int rotate_deg; // 0,90,180,270 degree of rotation: info taken by dcraw from exif
char* profile_data; // Embedded ICC color profile
float* allocation; // pointer to allocated memory
int maximum_c4[4];
Decoder decoder{Decoder::DCRAW};
std::unique_ptr<LibRaw> libraw;
bool isFoveon() const
{
return is_foveon;
@ -261,10 +277,7 @@ public:
public:
// dcraw functions
void pre_interpolate()
{
DCraw::pre_interpolate();
}
void pre_interpolate();
public:
bool ISRED(unsigned row, unsigned col) const
@ -304,6 +317,11 @@ public:
{
return dng_version;
}
public:
bool checkThumbOk() const;
Image8 *getThumbnail() const;
};
}

View File

@ -495,32 +495,14 @@ Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, eSensorType
sensorType = ri->getSensorType();
Image8* img = new Image8 ();
// No sample format detection occurred earlier, so we set them here,
// as they are mandatory for the setScanline method
img->setSampleFormat (IIOSF_UNSIGNED_CHAR);
img->setSampleArrangement (IIOSA_CHUNKY);
int err = 1;
// See if it is something we support
if (checkRawImageThumb (*ri)) {
const char* data ((const char*)fdata (ri->get_thumbOffset(), ri->get_file()));
if ( (unsigned char)data[1] == 0xd8 ) {
err = img->loadJPEGFromMemory (data, ri->get_thumbLength());
} else if (ri->is_ppmThumb()) {
err = img->loadPPMFromMemory (data, ri->get_thumbWidth(), ri->get_thumbHeight(), ri->get_thumbSwap(), ri->get_thumbBPS());
}
}
Image8 *img = ri->getThumbnail();
// did we succeed?
if ( err ) {
if (!img) {
if (settings->verbose) {
std::cout << "Could not extract thumb from " << fname.c_str() << std::endl;
}
delete tpp;
delete img;
delete ri;
return nullptr;
}
@ -627,6 +609,11 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, eSensorType &sens
int width = ri->get_width();
int height = ri->get_height();
int iwidth = ri->get_iwidth();
int iheight = ri->get_iheight();
int left_margin = ri->get_leftmargin();
int top_margin = ri->get_topmargin();
rtengine::Thumbnail* tpp = new rtengine::Thumbnail;
tpp->isRaw = true;
@ -717,19 +704,19 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, eSensorType &sens
if (ri->getSensorType() == ST_BAYER) {
// demosaicing! (sort of)
for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
rofs = row * width;
rofs = (row + top_margin) * iwidth;
for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col += hskip, x++) {
int ofs = rofs + col;
int ofs = rofs + col + left_margin;
int g = image[ofs][1];
int r, b;
if (FISRED (filter, row, col + 1)) {
r = (image[ofs + 1 ][0] + image[ofs - 1 ][0]) >> 1;
b = (image[ofs + width][2] + image[ofs - width][2]) >> 1;
b = (image[ofs + iwidth][2] + image[ofs - iwidth][2]) >> 1;
} else {
b = (image[ofs + 1 ][2] + image[ofs - 1 ][2]) >> 1;
r = (image[ofs + width][0] + image[ofs - width][0]) >> 1;
r = (image[ofs + iwidth][0] + image[ofs - iwidth][0]) >> 1;
}
tmpImg->r (y, x) = r;
@ -739,28 +726,28 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, eSensorType &sens
}
} else if (ri->get_colors() == 1) {
for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
rofs = row * width;
rofs = (row + top_margin) * iwidth;
for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col
+= hskip, x++) {
int ofs = rofs + col;
int ofs = rofs + col + left_margin;
tmpImg->r (y, x) = tmpImg->g (y, x) = tmpImg->b (y, x) = image[ofs][0];
}
}
} else {
if (ri->getSensorType() == ST_FUJI_XTRANS) {
for ( int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
rofs = row * width;
rofs = (row + top_margin) * iwidth;
for ( int col = 1, x = 0; col < width - 1 && x < tmpw; col += hskip, x++ ) {
int ofs = rofs + col;
int ofs = rofs + col + left_margin;
float sum[3] = {};
int c;
for (int v = -1; v <= 1; v++) {
for (int h = -1; h <= 1; h++) {
c = ri->XTRANSFC (row + v, col + h);
sum[c] += image[ofs + v * width + h][c];
sum[c] += image[ofs + v * iwidth + h][c];
}
}
@ -788,11 +775,11 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, eSensorType &sens
}
}
} else {
int iwidth = ri->get_iwidth();
int iheight = ri->get_iheight();
int left_margin = ri->get_leftmargin();
// int iwidth = ri->get_iwidth();
// int iheight = ri->get_iheight();
// int left_margin = ri->get_leftmargin();
firstgreen += left_margin;
int top_margin = ri->get_topmargin();
// int top_margin = ri->get_topmargin();
int wmax = tmpw;
int hmax = tmph;