diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml new file mode 100644 index 00000000..f95a2746 --- /dev/null +++ b/.github/workflows/analysis.yml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright Contributors to the rawtoaces project. + +name: Analysis +permissions: + contents: read + +on: + schedule: + # Run nightly + - cron: "0 8 * * *" + # Run weekly + # - cron: "0 0 * * 0" + push: + # Run on pushes only to main or if the branch name contains "analysis" + branches: + - main + - '*analysis*' + paths: + - '**' + - '!**.md' + - '!**.rst' + - '!**/ci.yml' + - '!**/docs.yml' + - '!**/scorecard.yml' + - '!**/wheel.yml' + - '!**.properties' + - '!docs/**' + pull_request: + branches: + - '*analysis*' + +# Allow subsequent pushes to the same PR or REF to cancel any previous jobs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + analysis: + name: "Static analysis" + if: github.repository == 'AcademySoftwareFoundation/rawtoaces' + runs-on: ubuntu-latest + strategy: + fail-fast: false + env: + CC: clang + CXX: clang++ + steps: + - uses: actions/checkout@v4 + + - name: Dependencies + shell: bash + run: | + build_scripts/install_deps_linux.bash + + - name: Configure CMake + run: > + cmake + -B build + -S . + -D CMAKE_INSTALL_PREFIX=${{ github.workspace }}/install + -D CMAKE_CXX_STANDARD=17 + -D CMAKE_BUILD_TYPE=Debug + -D ENABLE_COVERAGE=ON + -D CMAKE_CXX_FLAGS="-g -O0" + -D CMAKE_EXPORT_COMPILE_COMMANDS=ON + + - uses: whisperity/codechecker-analysis-action@v1 + id: codechecker + with: + logfile: ${{ github.workspace }}/build/compile_commands.json + config: '/home/runner/work/rawtoaces/rawtoaces/tests/static_analysis/codechecker.json' + + - uses: actions/upload-artifact@v4 + if: ${{ steps.codechecker.outputs.warnings == 'true' }} + with: + name: "CodeChecker Bug Reports" + path: ${{ steps.codechecker.outputs.result-html-dir }} + + - name: "Break build if CodeChecker reported any findings" + if: ${{ steps.codechecker.outputs.warnings == 'true' }} + run: exit 1 diff --git a/include/rawtoaces/image_converter.h b/include/rawtoaces/image_converter.h index c7915e40..38888647 100644 --- a/include/rawtoaces/image_converter.h +++ b/include/rawtoaces/image_converter.h @@ -277,9 +277,9 @@ class ImageConverter private: // Solved transform of the current image. - std::vector> _IDT_matrix; - std::vector> _CAT_matrix; - std::vector _WB_multipliers; + std::vector> _idt_matrix; + std::vector> _cat_matrix; + std::vector _wb_multipliers; }; } //namespace util diff --git a/include/rawtoaces/rawtoaces_core.h b/include/rawtoaces/rawtoaces_core.h index 1621efc9..32092ee0 100644 --- a/include/rawtoaces/rawtoaces_core.h +++ b/include/rawtoaces/rawtoaces_core.h @@ -169,8 +169,8 @@ class SpectralSolver std::vector _search_directories; std::vector _all_illuminants; - std::vector _WB_multipliers; - std::vector> _IDT_matrix; + std::vector _wb_multipliers; + std::vector> _idt_matrix; }; /// DNG metadata required to calculate an input transform. diff --git a/src/rawtoaces_core/define.h b/src/rawtoaces_core/define.h index 52dbf677..f93dfafe 100644 --- a/src/rawtoaces_core/define.h +++ b/src/rawtoaces_core/define.h @@ -1,8 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Contributors to the rawtoaces Project. -#ifndef _DEFINE_h__ -#define _DEFINE_h__ +#pragma once #include #include @@ -343,5 +342,3 @@ inline bool isValidCT( std::string str ) } // namespace core } // namespace rta - -#endif diff --git a/src/rawtoaces_core/mathOps.h b/src/rawtoaces_core/mathOps.h index e565fd45..1cacb9c3 100644 --- a/src/rawtoaces_core/mathOps.h +++ b/src/rawtoaces_core/mathOps.h @@ -1,8 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Contributors to the rawtoaces Project. -#ifndef _MATHOPS_h__ -#define _MATHOPS_h__ +#pragma once #include "define.h" @@ -491,5 +490,3 @@ getCalcXYZt( const vector> &RGB, const T beta_params[6] ) } // namespace core } // namespace rta - -#endif diff --git a/src/rawtoaces_core/rawtoaces_core.cpp b/src/rawtoaces_core/rawtoaces_core.cpp index 8a74012e..91c9b913 100644 --- a/src/rawtoaces_core/rawtoaces_core.cpp +++ b/src/rawtoaces_core/rawtoaces_core.cpp @@ -55,10 +55,8 @@ void calculate_daylight_SPD( const int &cct_input, Spectrum &spectrum ) cct = cct_input * 1.0; else { - fprintf( - stderr, - "The range of Correlated Color Temperature for " - "Day Light should be from 4000 to 25000. \n" ); + std::cerr << "The range of Correlated Color Temperature for " + << "Day Light should be from 4000 to 25000." << std::endl; exit( 1 ); } @@ -104,10 +102,8 @@ void calculate_blackbody_SPD( const int &cct, Spectrum &spectrum ) { if ( cct < 1500 || cct >= 4000 ) { - fprintf( - stderr, - "The range of Color Temperature for BlackBody " - "should be from 1500 to 3999. \n" ); + std::cerr << "The range of Color Temperature for BlackBody " + << "should be from 1500 to 3999." << std::endl; exit( 1 ); } @@ -147,10 +143,9 @@ void generate_illuminant( auto &main_spectral_set = main_iter->second; // Add the power channel and get a reference to it - auto &power_spectrum = main_spectral_set - .emplace_back( SpectralData::SpectralChannel( - "power", Spectrum( 0 ) ) ) - .second; + auto &power_data = main_spectral_set.emplace_back( + SpectralData::SpectralChannel( "power", Spectrum( 0 ) ) ); + auto &power_spectrum = power_data.second; illuminant.illuminant = type; if ( is_daylight ) @@ -168,15 +163,15 @@ SpectralSolver::SpectralSolver( : _search_directories( search_directories ) { verbosity = 0; - _IDT_matrix.resize( 3 ); - _WB_multipliers.resize( 3 ); + _idt_matrix.resize( 3 ); + _wb_multipliers.resize( 3 ); for ( int i = 0; i < 3; i++ ) { - _IDT_matrix[i].resize( 3 ); - _WB_multipliers[i] = 1.0; + _idt_matrix[i].resize( 3 ); + _wb_multipliers[i] = 1.0; for ( size_t j = 0; j < 3; j++ ) { - _IDT_matrix[i][j] = neutral3[i][j]; + _idt_matrix[i][j] = neutral3[i][j]; } } } @@ -402,7 +397,7 @@ bool SpectralSolver::find_illuminant( const vector &wb ) { sse = sse_tmp; illuminant = current_illuminant; - _WB_multipliers = wb_tmp; + _wb_multipliers = wb_tmp; } } @@ -431,7 +426,7 @@ bool SpectralSolver::calculate_WB() return false; } - _WB_multipliers = _calculate_WB( camera, illuminant ); + _wb_multipliers = _calculate_WB( camera, illuminant ); return true; } @@ -455,7 +450,7 @@ calculate_CM( const SpectralData &camera, const SpectralData &illuminant ) double g = ( camera_g * illuminant_spectrum ).integrate(); double b = ( camera_b * illuminant_spectrum ).integrate(); - double max = std::max( r, std::max( g, b ) ); + double max = std::max( { r, g, b } ); std::vector result( 3 ); result[0] = max / r; @@ -613,14 +608,14 @@ struct IDTOptimizationCost IDTOptimizationCost( const std::vector> &RGB, const std::vector> &out_LAB ) - : _RGB( RGB ), _outLAB( out_LAB ) + : _in_RGB( RGB ), _out_LAB( out_LAB ) {} template bool operator()( const T *beta_params, T *residuals ) const; - const std::vector> _RGB; - const std::vector> _outLAB; + const std::vector> _in_RGB; + const std::vector> _out_LAB; }; /// Perform curve fitting optimization to find optimal IDT matrix parameters. @@ -746,10 +741,10 @@ bool SpectralSolver::calculate_IDT_matrix() double beta_params_start[6] = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }; auto TI = calculate_TI( illuminant, training_data ); - auto RGB = calculate_RGB( camera, _WB_multipliers, TI ); + auto RGB = calculate_RGB( camera, _wb_multipliers, TI ); auto XYZ = calculate_XYZ( observer, illuminant, TI ); - return curveFit( RGB, XYZ, beta_params_start, verbosity, _IDT_matrix ); + return curveFit( RGB, XYZ, beta_params_start, verbosity, _idt_matrix ); } // ===================================================================== @@ -763,12 +758,12 @@ bool SpectralSolver::calculate_IDT_matrix() const vector> &SpectralSolver::get_IDT_matrix() const { - return _IDT_matrix; + return _idt_matrix; } const vector &SpectralSolver::get_WB_multipliers() const { - return _WB_multipliers; + return _wb_multipliers; } MetadataSolver::MetadataSolver( const core::Metadata &metadata ) @@ -953,13 +948,13 @@ vector find_XYZ_to_camera_matrix( if ( metadata.calibration[0].illuminant == 0 ) { - fprintf( stderr, " No calibration illuminants were found. \n " ); + std::cerr << "No calibration illuminants were found." << std::endl; return metadata.calibration[0].XYZ_to_RGB_matrix; } if ( neutral_RGB.size() == 0 ) { - fprintf( stderr, " no neutral RGB values were found. \n " ); + std::cerr << "No neutral RGB values were found." << std::endl; return metadata.calibration[0].XYZ_to_RGB_matrix; } @@ -980,16 +975,16 @@ vector find_XYZ_to_camera_matrix( metadata.calibration[1].XYZ_to_RGB_matrix; double low_mired = - std::max( min_mired, std::min( max_mired, std::min( mir1, mir2 ) ) ); + std::clamp( std::min( mir1, mir2 ), min_mired, max_mired ); double high_mired = - std::max( min_mired, std::min( max_mired, std::max( mir1, mir2 ) ) ); - double mirStep = std::max( 5.0, ( high_mired - low_mired ) / 50.0 ); + std::clamp( std::max( mir1, mir2 ), min_mired, max_mired ); + double mired_step = std::max( 5.0, ( high_mired - low_mired ) / 50.0 ); - double current_mired = 0.0, last_mired = 0.0, estimated_mired = 0.0, - current_error = 0.0, last_error = 0.0, smallest_error = 0.0; + double last_mired = 0.0, estimated_mired = 0.0, current_error = 0.0, + last_error = 0.0, smallest_error = 0.0; - for ( current_mired = low_mired; current_mired < high_mired; - current_mired += mirStep ) + double current_mired = low_mired; + while ( current_mired < high_mired ) { current_error = current_mired - @@ -1020,6 +1015,8 @@ vector find_XYZ_to_camera_matrix( last_error = current_error; last_mired = current_mired; + + current_mired += mired_step; } return XYZ_to_camera_weighted_matrix( @@ -1232,19 +1229,19 @@ vector> MetadataSolver::calculate_IDT_matrix() template bool IDTOptimizationCost::operator()( const T *beta_params, T *residuals ) const { - vector> RGB_copy( _RGB.size(), vector( 3 ) ); - for ( size_t i = 0; i < _RGB.size(); i++ ) + vector> RGB_copy( _in_RGB.size(), vector( 3 ) ); + for ( size_t i = 0; i < _in_RGB.size(); i++ ) for ( size_t j = 0; j < 3; j++ ) - RGB_copy[i][j] = T( _RGB[i][j] ); + RGB_copy[i][j] = T( _in_RGB[i][j] ); vector> out_calc_LAB = XYZ_to_LAB( getCalcXYZt( RGB_copy, beta_params ) ); - for ( size_t i = 0; i < _RGB.size(); i++ ) + for ( size_t i = 0; i < _in_RGB.size(); i++ ) for ( size_t j = 0; j < 3; j++ ) - residuals[i * 3 + j] = _outLAB[i][j] - out_calc_LAB[i][j]; + residuals[i * 3 + j] = _out_LAB[i][j] - out_calc_LAB[i][j]; return true; } } // namespace core -} // namespace rta \ No newline at end of file +} // namespace rta diff --git a/src/rawtoaces_core/spectral_data.cpp b/src/rawtoaces_core/spectral_data.cpp index cdf04793..534cdfe7 100644 --- a/src/rawtoaces_core/spectral_data.cpp +++ b/src/rawtoaces_core/spectral_data.cpp @@ -87,17 +87,25 @@ void Spectrum::reshape() return; std::vector temp; - size_t src = 0; - float wl_src = shape.first; - float wl_dst = ReferenceShape.first; + size_t src = 0; - while ( wl_dst <= ReferenceShape.last ) + double wl_src_first = static_cast( shape.first ); + double wl_src_step = static_cast( shape.step ); + + double wl_dst_first = static_cast( ReferenceShape.first ); + double wl_dst_last = static_cast( ReferenceShape.last ); + double wl_dst_step = static_cast( ReferenceShape.step ); + + double wl_src = wl_src_first; + double wl_dst = wl_dst_first; + + while ( wl_dst <= wl_dst_last ) { if ( wl_src < wl_dst ) { if ( src < values.size() - 1 ) { - float next_wl_src = shape.first + shape.step * ( src + 1 ); + double next_wl_src = wl_src_first + wl_src_step * ( src + 1 ); if ( next_wl_src <= wl_dst ) { // The next source wavelength is still not big enough, @@ -114,8 +122,7 @@ void Spectrum::reshape() double vv = values[src] * ( 1.0 - ratio ) + values[src + 1] * ratio; temp.push_back( vv ); - wl_dst = ReferenceShape.first + - ReferenceShape.step * temp.size(); + wl_dst = wl_dst_first + wl_dst_step * temp.size(); } } else @@ -123,21 +130,20 @@ void Spectrum::reshape() // We have passed all available source samples, // copying the last sample. temp.push_back( values[src] ); - wl_dst = - ReferenceShape.first + ReferenceShape.step * temp.size(); + wl_dst = wl_dst_first + wl_dst_step * temp.size(); } } else if ( wl_src == wl_dst ) { // Found an exact match, just copy it over. temp.push_back( values[src] ); - wl_dst = ReferenceShape.first + ReferenceShape.step * temp.size(); + wl_dst = wl_dst_first + wl_dst_step * temp.size(); } else { // Haven't reached the available source range yet, advancing. temp.push_back( values[src] ); - wl_dst = ReferenceShape.first + ReferenceShape.step * temp.size(); + wl_dst = wl_dst_first + wl_dst_step * temp.size(); } } diff --git a/src/rawtoaces_util/image_converter.cpp b/src/rawtoaces_util/image_converter.cpp index 589b0a3c..aea59124 100644 --- a/src/rawtoaces_util/image_converter.cpp +++ b/src/rawtoaces_util/image_converter.cpp @@ -317,7 +317,8 @@ bool prepare_transform_spectral( if ( attr ) { for ( int i = 0; i < 4; i++ ) - tmp_wb_multipliers[i] = attr->get_float_indexed( i ); + tmp_wb_multipliers[i] = + static_cast( attr->get_float_indexed( i ) ); } } @@ -428,8 +429,8 @@ bool prepare_transform_DNG( // Step 1: Extract basic DNG metadata core::Metadata metadata; - metadata.baseline_exposure = - image_spec.get_float_attribute( "raw:dng:baseline_exposure" ); + metadata.baseline_exposure = static_cast( + image_spec.get_float_attribute( "raw:dng:baseline_exposure" ) ); // Step 2: Extract neutral RGB values from camera multipliers metadata.neutral_RGB.resize( 3 ); @@ -439,7 +440,8 @@ bool prepare_transform_DNG( if ( attr ) { for ( int i = 0; i < 3; i++ ) - metadata.neutral_RGB[i] = 1.0 / attr->get_float_indexed( i ); + metadata.neutral_RGB[i] = + 1.0 / static_cast( attr->get_float_indexed( i ) ); } // Step 3: Extract calibration data for two illuminants @@ -467,7 +469,8 @@ bool prepare_transform_DNG( for ( int j = 0; j < 3; j++ ) { calibration.XYZ_to_RGB_matrix[i * 3 + j] = - matrix1_attr->get_float_indexed( i * 3 + j ); + static_cast( + matrix1_attr->get_float_indexed( i * 3 + j ) ); } } } @@ -483,7 +486,8 @@ bool prepare_transform_DNG( for ( int j = 0; j < 3; j++ ) { calibration.camera_calibration_matrix[i * 3 + j] = - matrix2_attr->get_float_indexed( i * 4 + j ); + static_cast( + matrix2_attr->get_float_indexed( i * 4 + j ) ); } } } @@ -1296,8 +1300,8 @@ bool ImageConverter::configure( settings.crop_box ); } - if ( settings.chromatic_aberration[0] != 1.0 && - settings.chromatic_aberration[1] != 1.0 ) + if ( settings.chromatic_aberration[0] != 1.0f && + settings.chromatic_aberration[1] != 1.0f ) { options.attribute( "raw:aber", @@ -1328,9 +1332,9 @@ bool ImageConverter::configure( OIIO::TypeDesc( OIIO::TypeDesc::FLOAT, 4 ), custom_WB ); - _WB_multipliers.resize( 4 ); + _wb_multipliers.resize( 4 ); for ( size_t i = 0; i < 4; i++ ) - _WB_multipliers[i] = custom_WB[i]; + _wb_multipliers[i] = static_cast( custom_WB[i] ); } break; } @@ -1366,9 +1370,10 @@ bool ImageConverter::configure( OIIO::TypeDesc( OIIO::TypeDesc::FLOAT, 4 ), settings.custom_WB ); - _WB_multipliers.resize( 4 ); + _wb_multipliers.resize( 4 ); for ( size_t i = 0; i < 4; i++ ) - _WB_multipliers[i] = settings.custom_WB[i]; + _wb_multipliers[i] = + static_cast( settings.custom_WB[i] ); break; default: @@ -1422,13 +1427,14 @@ bool ImageConverter::configure( options["raw:ColorSpace"] = "raw"; options["raw:use_camera_matrix"] = 0; - _IDT_matrix.resize( 3 ); + _idt_matrix.resize( 3 ); for ( int i = 0; i < 3; i++ ) { - _IDT_matrix[i].resize( 3 ); + _idt_matrix[i].resize( 3 ); for ( int j = 0; j < 3; j++ ) { - _IDT_matrix[i][j] = settings.custom_matrix[i][j]; + _idt_matrix[i][j] = + static_cast( settings.custom_matrix[i][j] ); } } break; @@ -1448,9 +1454,9 @@ bool ImageConverter::configure( if ( !prepare_transform_spectral( image_spec, settings, - _WB_multipliers, - _IDT_matrix, - _CAT_matrix ) ) + _wb_multipliers, + _idt_matrix, + _cat_matrix ) ) { std::cerr << "ERROR: the colour space transform has not been " << "configured properly (spectral mode)." << std::endl; @@ -1461,12 +1467,12 @@ bool ImageConverter::configure( { float custom_WB[4]; - for ( size_t i = 0; i < _WB_multipliers.size(); i++ ) + for ( size_t i = 0; i < _wb_multipliers.size(); i++ ) { - custom_WB[i] = static_cast( _WB_multipliers[i] ); + custom_WB[i] = static_cast( _wb_multipliers[i] ); } - if ( _WB_multipliers.size() == 3 ) - custom_WB[3] = static_cast( _WB_multipliers[1] ); + if ( _wb_multipliers.size() == 3 ) + custom_WB[3] = static_cast( _wb_multipliers[1] ); options.attribute( "raw:user_mul", @@ -1483,7 +1489,7 @@ bool ImageConverter::configure( options["raw:use_camera_wb"] = 1; if ( !prepare_transform_DNG( - image_spec, settings, _IDT_matrix, _CAT_matrix ) ) + image_spec, settings, _idt_matrix, _cat_matrix ) ) { std::cerr << "ERROR: the colour space transform has not been " << "configured properly (metadata mode)." @@ -1493,12 +1499,12 @@ bool ImageConverter::configure( } else { - prepare_transform_nonDNG( _IDT_matrix, _CAT_matrix ); + prepare_transform_nonDNG( _idt_matrix, _cat_matrix ); } } else if ( matrix_method == Settings::MatrixMethod::Adobe ) { - prepare_transform_nonDNG( _IDT_matrix, _CAT_matrix ); + prepare_transform_nonDNG( _idt_matrix, _cat_matrix ); } if ( settings.verbosity > 1 ) @@ -1660,16 +1666,16 @@ bool ImageConverter::apply_matrix( if ( !roi.defined() ) roi = dst.roi(); - if ( _IDT_matrix.size() ) + if ( _idt_matrix.size() ) { - success = rta::util::apply_matrix( _IDT_matrix, dst, src, roi ); + success = rta::util::apply_matrix( _idt_matrix, dst, src, roi ); if ( !success ) return false; } - if ( _CAT_matrix.size() ) + if ( _cat_matrix.size() ) { - success = rta::util::apply_matrix( _CAT_matrix, dst, dst, roi ); + success = rta::util::apply_matrix( _cat_matrix, dst, dst, roi ); if ( !success ) return false; @@ -1917,17 +1923,17 @@ bool ImageConverter::process_image( const std::string &input_filename ) const std::vector &ImageConverter::get_WB_multipliers() { - return _WB_multipliers; + return _wb_multipliers; } const std::vector> &ImageConverter::get_IDT_matrix() { - return _IDT_matrix; + return _idt_matrix; } const std::vector> &ImageConverter::get_CAT_matrix() { - return _CAT_matrix; + return _cat_matrix; } } //namespace util diff --git a/tests/static_analysis/codechecker.json b/tests/static_analysis/codechecker.json new file mode 100644 index 00000000..c9777d0f --- /dev/null +++ b/tests/static_analysis/codechecker.json @@ -0,0 +1,8 @@ +{ + "parse": [ + "--skip=/home/runner/work/rawtoaces/rawtoaces/tests/static_analysis/codechecker_skipfile.txt" + ], + "store": [ + "--skip=/home/runner/work/rawtoaces/rawtoaces/tests/static_analysis/codechecker_skipfile.txt" + ] +} diff --git a/tests/static_analysis/codechecker_skipfile.txt b/tests/static_analysis/codechecker_skipfile.txt new file mode 100644 index 00000000..8a960ee8 --- /dev/null +++ b/tests/static_analysis/codechecker_skipfile.txt @@ -0,0 +1,3 @@ +# The paths are based on Ubuntu builds +-/usr/include/OpenImageIO/detail/fmt/format.h +-/usr/lib/llvm-22/lib/clang/22/include/cetintrin.h \ No newline at end of file