Skip to content

fix(ImageIO): image input robustness and faster image IO - #227

Merged
BenPinet merged 13 commits into
nextfrom
fix/image-io-robustness
Oct 22, 2026
Merged

BenPinet merged 13 commits into
nextfrom
fix/image-io-robustness

Conversation

@BotellaA

Copy link
Copy Markdown
Member

Summary

Review of the image library, focused on robustness and performance. One commit per fix, each one with a test that fails before it.

Depends on

Replaces #225, which was closed automatically when its base branch fix/vtk-output-robustness (#219) was merged and deleted.

Robustness

Commit Before
fix(VTKOutput): write to a temporary file, then rename A failed write (exception, full disk) left an empty or truncated file and lost the previous one. Symbolic links are followed; special files such as /dev/full are still written directly.
fix(BMPInput): same row order as the other formats BMP images were loaded upside down.
fix(ImageInput): bands not tagged as RGB 3/4-band images without R/G/B color interpretation (e.g. MINISBLACK TIFF) used band 0: segfault.
fix(ImageInput): more than four bands 5+ band images were silently loaded all black.
fix(ImageInput): palette images 8-bit palette PNG/BMP/TIFF were loaded as grey levels of their indices.
fix(ImageInput): 16-bit and floating point images GDAL clamped values to 8 bits: 16-bit images came out all white, float images all black. Values are now linearly rescaled from the value range; nodata and NaN pixels are black.
fix(GDALFile): additional files The main file was returned as an additional file, and every file was flagged missing.
fix(ImageInput): image drivers only Any GDAL driver could open an image: a VRT named .png was followed to arbitrary files or URLs. Now only PNG, JPEG, BMP and GTiff are accepted, so misnamed images still load.
fix(ImageInput): GDAL errors is_loadable() threw on unreadable files. GDAL errors went to stderr and were missing from the exceptions.

Performance (64 Mpx RGB image, compared with next)

before after
PNG load 1.01 s 0.47 s (≈ GDAL decoding time)
TIFF load 0.70 s 0.26 s
VTI save 0.65 s 0.08 s (includes the streaming from #219)
  • perf(ImageInput): GDAL decodes straight into the raster color storage, rows reversed through a negative line spacing. Previously there was one buffer per band plus one set_color call per pixel. Peak memory is halved.
  • perf(VTIRasterImageOutput): the color storage is written as is, with no copy.

Tests

New 4×3 test images in tests/data/image_*, checked pixel by pixel in test-raster-image. All OpenGeode-IO tests pass locally.

🤖 Generated with Claude Code

BotellaA and others added 13 commits October 21, 2026 09:21
The output file was truncated as soon as the writer was created, so any
failure (exception, full disk) left an empty or partial file and lost
the previous one. The file is now written in a temporary file which
replaces the output only once complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Colors were copied pixel by pixel through RasterImage::color() into a
temporary buffer. The VTK writer can now reference external data, so the
contiguous color storage is written as is, without any copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GDAL returns the top row first for every format, but unlike the PNG, JPG
and TIFF inputs the BMP input did not reverse the rows: BMP images were
loaded upside down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each band was read in its own buffer, then every pixel was copied with
one RasterImage::set_color call. GDAL now writes the pixels interleaved
straight into the color storage, rows reversed through a negative line
spacing. Loading a 64 Mpx image goes from 1.0s to 0.47s (PNG) and from
0.70s to 0.26s (TIFF), at half the peak memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Images with 3 or 4 bands without red/green/blue color interpretation
(e.g. TIFF with MINISBLACK photometric) used band 0, which does not
exist: the load crashed. The first three bands are now used, with a
warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Images with 5 bands or more (e.g. multispectral TIFF) were silently
loaded all black. Their red, green and blue bands, or their first three
bands, are now used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Palette images (8-bit PNG, BMP, TIFF) were loaded as grey levels of
their palette indices. The RGB or grey palette colors are now used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pixels were converted to 8-bit by GDAL with clamping: 16-bit images were
loaded all white and floating point images all black. Values are now
linearly rescaled from the range of the image values, nodata and NaN
pixels being black.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The main file was returned as an additional file and every file was
flagged as missing, although GDAL only lists existing files: any image
was reported with missing additional files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Image files were opened by any GDAL driver: a VRT file named .png was
loaded and could make GDAL read any other local file or URL. Image
inputs now only accept the PNG, JPEG, BMP and GTiff drivers, so a
misnamed image of these formats still loads. GDALFile accepts a list of
allowed drivers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
is_loadable() threw on files GDAL cannot open instead of returning 0.
GDAL errors were printed on stderr and missing from the exceptions,
which only said the file failed to open. GDAL warnings are now logged
and GDAL error messages are part of the exceptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image test uses the VTK writer header, which needs pugixml. When the
tests are configured on their own against an installed OpenGeode-IO, the
pugixml target was not defined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-tidy (v21.1.8) reports: 136 concern(s)
  • include/geode/io/image/detail/gdal_file.hpp:40:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       40 | namespace geode
          | ^~~~~~~~~~~~~~~
       41 | {
          | ~
       42 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • include/geode/io/image/detail/gdal_file.hpp:58:13: warning: [modernize-use-nodiscard]

    function 'last_error' should be marked [[nodiscard]]

       58 |             std::string last_error() const;
          |             ^
          |             [[nodiscard]] 
  • include/geode/io/image/detail/gdal_file.hpp:61:38: warning: [cppcoreguidelines-special-member-functions]

    class 'GDALFile' defines a destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator

       61 |         class opengeode_io_image_api GDALFile
          |                                      ^
  • include/geode/io/image/detail/vtk_output.hpp:52:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       52 | namespace geode
          | ^~~~~~~~~~~~~~~
       53 | {
          | ~
       54 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • include/geode/io/image/detail/vtk_output.hpp:60:13: warning: [cppcoreguidelines-pro-type-member-init]

    uninitialized record type: 'buffer'

       60 |             std::array< char, 32 > buffer;
          |             ^                            
          |                                          {}
  • include/geode/io/image/detail/vtk_output.hpp:60:31: warning: [cppcoreguidelines-avoid-magic-numbers]

    32 is a magic number; consider replacing it with a named constant

       60 |             std::array< char, 32 > buffer;
          |                               ^
  • include/geode/io/image/detail/vtk_output.hpp:62:46: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

       62 |                 buffer.data(), buffer.data() + buffer.size(), value );
          |                                              ^
  • include/geode/io/image/detail/vtk_output.hpp:78:56: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'names'

       78 |                 constexpr std::array< const char*, 9 > names{ "", "Int8",
          |                                                        ^~~~~
          |                                                        NAMES
       79 |                     "Int16", "", "Int32", "", "", "", "Int64" };
       80 |                 return names[sizeof( T )];
          |                        ~~~~~
          |                        NAMES
  • include/geode/io/image/detail/vtk_output.hpp:84:56: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'names'

       84 |                 constexpr std::array< const char*, 9 > names{ "", "UInt8",
          |                                                        ^~~~~
          |                                                        NAMES
       85 |                     "UInt16", "", "UInt32", "", "", "", "UInt64" };
       86 |                 return names[sizeof( T )];
          |                        ~~~~~
          |                        NAMES
  • include/geode/io/image/detail/vtk_output.hpp:100:44: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'nb_components'

      100 |             static constexpr local_index_t nb_components = 1;
          |                                            ^~~~~~~~~~~~~
          |                                            NB_COMPONENTS
  • include/geode/io/image/detail/vtk_output.hpp:112:44: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'nb_components'

      112 |             static constexpr local_index_t nb_components = size;
          |                                            ^~~~~~~~~~~~~
          |                                            NB_COMPONENTS
  • include/geode/io/image/detail/vtk_output.hpp:114:71: warning: [readability-identifier-length]

    parameter name 'c' is too short, expected at least 3 characters

      114 |                 const std::array< Value, size >& value, local_index_t c )
          |                                                                       ^
  • include/geode/io/image/detail/vtk_output.hpp:124:44: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'nb_components'

      124 |             static constexpr local_index_t nb_components = dimension;
          |                                            ^~~~~~~~~~~~~
          |                                            NB_COMPONENTS
  • include/geode/io/image/detail/vtk_output.hpp:126:64: warning: [readability-identifier-length]

    parameter name 'c' is too short, expected at least 3 characters

      126 |                 const Point< dimension >& value, local_index_t c )
          |                                                                ^
  • include/geode/io/image/detail/vtk_output.hpp:136:44: warning: [readability-identifier-naming]

    invalid case style for constexpr variable 'nb_components'

      136 |             static constexpr local_index_t nb_components = dimension;
          |                                            ^~~~~~~~~~~~~
          |                                            NB_COMPONENTS
  • include/geode/io/image/detail/vtk_output.hpp:138:65: warning: [readability-identifier-length]

    parameter name 'c' is too short, expected at least 3 characters

      138 |                 const Vector< dimension >& value, local_index_t c )
          |                                                                 ^
  • include/geode/io/image/detail/vtk_output.hpp:156:39: warning: [cppcoreguidelines-avoid-magic-numbers]

    9 is a magic number; consider replacing it with a named constant

      156 |             std::make_index_sequence< 9 >{} ) );
          |                                       ^
  • include/geode/io/image/detail/vtk_output.hpp:163:13: warning: [google-runtime-int]

    consider replacing 'short' with 'int16'

      163 |             short,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:164:13: warning: [google-runtime-int]

    consider replacing 'unsigned short' with 'uint16'

      164 |             unsigned short,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:167:13: warning: [google-runtime-int]

    consider replacing 'long' with 'int64'

      167 |             long,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:168:13: warning: [google-runtime-int]

    consider replacing 'unsigned long' with 'uint64'

      168 |             unsigned long,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:169:13: warning: [google-runtime-int]

    consider replacing 'long long' with 'int64'

      169 |             long long,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:170:13: warning: [google-runtime-int]

    consider replacing 'unsigned long long' with 'uint64'

      170 |             unsigned long long,
          |             ^
  • include/geode/io/image/detail/vtk_output.hpp:179:15: warning: [cppcoreguidelines-special-member-functions]

    class 'VTKOutputImpl' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator

      179 |         class VTKOutputImpl
          |               ^
  • include/geode/io/image/detail/vtk_output.hpp:303:34: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      303 |                 for( const auto& id : manager.attribute_ids() )
          |                                  ^
  • include/geode/io/image/detail/vtk_output.hpp:411:49: warning: [bugprone-narrowing-conversions]

    narrowing conversion from 'size_type' (aka 'unsigned long') to signed type 'streamsize' (aka 'long') is implementation-defined

      411 |                 file_.write( xml_string.data(), split );
          |                                                 ^
  • include/geode/io/image/detail/vtk_output.hpp:416:25: warning: [cppcoreguidelines-pro-type-reinterpret-cast]

    do not use reinterpret_cast

      416 |                         reinterpret_cast< const char* >( &block.nb_bytes ),
          |                         ^
  • include/geode/io/image/detail/vtk_output.hpp:423:39: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      423 |                     xml_string.data() + split, xml_string.size() - split );
          |                                       ^
  • include/geode/io/image/detail/vtk_output.hpp:423:48: warning: [bugprone-narrowing-conversions]

    narrowing conversion from 'size_type' (aka 'unsigned long') to signed type 'streamsize' (aka 'long') is implementation-defined

      423 |                     xml_string.data() + split, xml_string.size() - split );
          |                                                ^
  • include/geode/io/image/detail/vtk_output.hpp:433:17: warning: [readability-function-cognitive-complexity]

    function 'compute_attribute_names' has cognitive complexity of 22 (threshold 10)

      433 |                 compute_attribute_names( const AttributeManager& manager )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:436:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      436 |                 for( const auto& series_name : manager.time_series_names() )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:439:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      439 |                     for( const auto step : Indices{ time_steps } )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:447:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      447 |                 for( const auto& id : manager.attribute_ids() )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:449:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      449 |                     if( names.contains( id ) )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:454:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      454 |                     if( !should_be_written( attribute ) )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:461:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      461 |                 for( const auto& [id, name] : names )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:465:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      465 |                 for( const auto& [name, ids] : same_name_ids )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:467:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      467 |                     if( ids.size() == 1 && !used_names.contains( name ) )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:467:41: note: +1
      467 |                     if( ids.size() == 1 && !used_names.contains( name ) )
          |                                         ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:473:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      473 |                 for( const auto& [name, ids] : same_name_ids )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:475:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      475 |                     if( ids.size() == 1 && names.contains( ids.front() ) )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:475:41: note: +1
      475 |                     if( ids.size() == 1 && names.contains( ids.front() ) )
          |                                         ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:481:21: note: +2, including nesting penalty of 1, nesting level increased to 2
      481 |                     for( const auto& id : ids )
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:484:25: note: +3, including nesting penalty of 2, nesting level increased to 3
      484 |                         while( used_names.contains( unique_name ) )
          |                         ^
  • include/geode/io/image/detail/vtk_output.hpp:447:34: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      447 |                 for( const auto& id : manager.attribute_ids() )
          |                                  ^
  • include/geode/io/image/detail/vtk_output.hpp:461:35: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      461 |                 for( const auto& [id, name] : names )
          |                                   ^
  • include/geode/io/image/detail/vtk_output.hpp:481:38: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      481 |                     for( const auto& id : ids )
          |                                      ^
  • include/geode/io/image/detail/vtk_output.hpp:496:18: warning: [readability-function-size]

    function 'write_typed_attribute' exceeds recommended size/complexity thresholds

      496 |             bool write_typed_attribute( pugi::xml_node& attribute_node,
          |                  ^
    /__w/OpenGeode-IO/OpenGeode-IO/include/geode/io/image/detail/vtk_output.hpp:496:18: note: 5 parameters (threshold 4)
  • include/geode/io/image/detail/vtk_output.hpp:628:50: warning: [google-runtime-int]

    consider replacing 'long long' with 'int64'

      628 |                         .set_value( static_cast< long long >( min ) );
          |                                                  ^
  • include/geode/io/image/detail/vtk_output.hpp:630:50: warning: [google-runtime-int]

    consider replacing 'long long' with 'int64'

      630 |                         .set_value( static_cast< long long >( max ) );
          |                                                  ^
  • include/geode/io/image/detail/vtk_output.hpp:635:50: warning: [google-runtime-int]

    consider replacing 'unsigned long long' with 'uint64'

      635 |                         .set_value( static_cast< unsigned long long >( min ) );
          |                                                  ^
  • include/geode/io/image/detail/vtk_output.hpp:637:50: warning: [google-runtime-int]

    consider replacing 'unsigned long long' with 'uint64'

      637 |                         .set_value( static_cast< unsigned long long >( max ) );
          |                                                  ^
  • include/geode/io/image/internal/raster_image_input.hpp:36:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       36 | namespace geode
          | ^~~~~~~~~~~~~~~
       37 | {
          | ~
       38 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
       39 |     {
       40 |         class ImageInputImpl : public detail::GDALFile
       41 |         {
       42 |         public:
       43 |             explicit ImageInputImpl( std::string_view filename );
       44 | 
       45 |             RasterImage2D read_file();
       46 | 
       47 |             // Unreadable files are not loadable
       48 |             static Percentage is_loadable( std::string_view filename );
       49 |         };
       50 |     } // namespace internal
          |     ~~~~~~~~~~~~~~~~~~~~~~~
          |     } // namespace geode::internal
       51 | } // namespace geode
          | ~~~~~~~~~~~~~~~~~~~~
  • src/geode/io/image/bmp_input.cpp:30:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       30 | namespace geode
          | ^~~~~~~~~~~~~~~
       31 | {
          | ~
       32 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
  • src/geode/io/image/gdal_file.cpp:47:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       47 | namespace geode
          | ^~~~~~~~~~~~~~~
       48 | {
          | ~
       49 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • src/geode/io/image/gdal_file.cpp:88:17: warning: [cppcoreguidelines-pro-type-member-init]

    uninitialized record type: 'geo_transform'

       88 |                 std::array< double, 6 > geo_transform;
          |                 ^                                    
          |                                                      {}
  • src/geode/io/image/gdal_file.cpp:88:37: warning: [cppcoreguidelines-avoid-magic-numbers]

    6 is a magic number; consider replacing it with a named constant

       88 |                 std::array< double, 6 > geo_transform;
          |                                     ^
  • src/geode/io/image/gdal_file.cpp:99:59: warning: [bugprone-implicit-widening-of-multiplication-result]

    performing an implicit widening conversion to type 'size_type' (aka 'unsigned long') of a multiplication performed in type 'int'

       99 |                     origin.set_value( axis, geo_transform[3 * axis] );
          |                                                           ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/gdal_file.cpp:99:59: note: make conversion explicit to silence this warning
       24 |                     origin.set_value( axis, geo_transform[3 * axis] );
          |                                                           ^~~~~~~~
          |                                                           static_cast<size_type>( )
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/gdal_file.cpp:99:59: note: perform multiplication in a wider type
       99 |                     origin.set_value( axis, geo_transform[3 * axis] );
          |                                                           ^
          |                                                           static_cast<long>( )
  • src/geode/io/image/gdal_file.cpp:100:64: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations

      100 |                     x_direction.set_value( axis, geo_transform[3 * axis + 1] );
          |                                                                ^~~~~~~~
          |                                                                (       )
  • src/geode/io/image/gdal_file.cpp:101:64: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations

      101 |                     y_direction.set_value( axis, geo_transform[3 * axis + 2] );
          |                                                                ^~~~~~~~
          |                                                                (       )
  • src/geode/io/image/gdal_file.cpp:108:17: warning: [cppcoreguidelines-pro-type-member-init]

    uninitialized record type: 'geo_transform'

      108 |                 std::array< double, 6 > geo_transform;
          |                 ^                                    
          |                                                      {}
  • src/geode/io/image/gdal_file.cpp:108:37: warning: [cppcoreguidelines-avoid-magic-numbers]

    6 is a magic number; consider replacing it with a named constant

      108 |                 std::array< double, 6 > geo_transform;
          |                                     ^
  • src/geode/io/image/gdal_file.cpp:116:24: warning: [readability-identifier-naming]

    invalid case style for variable 'papszFileList'

      116 |                 char** papszFileList = dataset_->GetFileList();
          |                        ^~~~~~~~~~~~~
          |                        papsz_file_list
      117 |                 if( !papszFileList )
          |                      ~~~~~~~~~~~~~
          |                      papsz_file_list
      118 |                 {
      119 |                     return {};
      120 |                 }
      121 |                 // The list starts with the main file, which is not associated
      122 |                 const std::filesystem::path main_file{
      123 |                     dataset_->GetDescription()
      124 |                 };
      125 |                 std::vector< std::string > files;
      126 |                 for( int i = 0; papszFileList[i] != nullptr; i++ )
          |                                 ~~~~~~~~~~~~~
          |                                 papsz_file_list
      127 |                 {
      128 |                     std::error_code error;
      129 |                     if( papszFileList[i] == main_file
          |                         ~~~~~~~~~~~~~
          |                         papsz_file_list
      130 |                         || std::filesystem::equivalent(
      131 |                             papszFileList[i], main_file, error ) )
          |                             ~~~~~~~~~~~~~
          |                             papsz_file_list
  • src/geode/io/image/gdal_file.cpp:117:22: warning: [readability-implicit-bool-conversion]

    implicit conversion 'char **' -> 'bool'

      117 |                 if( !papszFileList )
          |                     ~^
          |                                    == nullptr
  • src/geode/io/image/gdal_file.cpp:126:33: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      126 |                 for( int i = 0; papszFileList[i] != nullptr; i++ )
          |                                 ^
  • src/geode/io/image/gdal_file.cpp:129:25: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      129 |                     if( papszFileList[i] == main_file
          |                         ^
  • src/geode/io/image/gdal_file.cpp:131:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      131 |                             papszFileList[i], main_file, error ) )
          |                             ^
  • src/geode/io/image/gdal_file.cpp:135:41: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      135 |                     files.emplace_back( papszFileList[i] );
          |                                         ^
  • src/geode/io/image/jpg_input.cpp:30:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       30 | namespace geode
          | ^~~~~~~~~~~~~~~
       31 | {
          | ~
       32 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
  • src/geode/io/image/png_input.cpp:30:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       30 | namespace geode
          | ^~~~~~~~~~~~~~~
       31 | {
          | ~
       32 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
  • src/geode/io/image/raster_image_input.cpp:58:58: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

       58 |         return reinterpret_cast< GByte* >( colors.data() + colors.size() )
          |                                                          ^
  • src/geode/io/image/raster_image_input.cpp:59:16: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

       59 |                - static_cast< std::ptrdiff_t >( 3 ) * width;
          |                ^
  • src/geode/io/image/raster_image_input.cpp:59:18: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '-'; add parentheses to explicitly specify the order of operations

       59 |                - static_cast< std::ptrdiff_t >( 3 ) * width;
          |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                  (                                         )
  • src/geode/io/image/raster_image_input.cpp:124:10: warning: [readability-function-size]

    function 'read_palette_band' exceeds recommended size/complexity thresholds

      124 |     void read_palette_band( GDALRasterBand& band,
          |          ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:124:10: note: 5 parameters (threshold 4)
  • src/geode/io/image/raster_image_input.cpp:134:28: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      134 |             indices.data() + indices.size() - width, width, height, GDT_UInt16,
          |                            ^
  • src/geode/io/image/raster_image_input.cpp:134:45: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      134 |             indices.data() + indices.size() - width, width, height, GDT_UInt16,
          |                                             ^
  • src/geode/io/image/raster_image_input.cpp:145:10: warning: [readability-function-size]

    function 'read_rgb_bands' exceeds recommended size/complexity thresholds

      145 |     void read_rgb_bands( GDALDataset& dataset,
          |          ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:145:10: note: 5 parameters (threshold 4)
  • src/geode/io/image/raster_image_input.cpp:178:13: warning: [cppcoreguidelines-pro-type-member-init]

    uninitialized record type: 'min_max'

      178 |             std::array< double, 2 > min_max;
          |             ^                              
          |                                            {}
  • src/geode/io/image/raster_image_input.cpp:180:21: warning: [readability-implicit-bool-conversion]

    implicit conversion 'bool' -> 'int'

      180 |                     false, min_max.data() )
          |                     ^~~~~
          |                     0
  • src/geode/io/image/raster_image_input.cpp:191:33: warning: [bugprone-easily-swappable-parameters]

    2 adjacent parameters of 'set_channel' of convertible types are easily swapped by mistake

      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:191:40: note: the first parameter in the range is 'channel'
      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                        ^~~~~~~
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:191:70: note: the last parameter in the range is 'value'
      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                                                      ^~~~~
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:191:33: note: 
      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:191:49: note: 'size_t' and 'geode::local_index_t' may be implicitly converted: 'size_t' (as 'unsigned long') -> 'geode::local_index_t' (as 'unsigned char'), 'geode::local_index_t' (as 'unsigned char') -> 'size_t' (as 'unsigned long')
      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                                 ^
  • src/geode/io/image/raster_image_input.cpp:209:10: warning: [readability-function-cognitive-complexity]

    function 'read_rescaled_bands' has cognitive complexity of 18 (threshold 10)

      209 |     void read_rescaled_bands( GDALDataset& dataset,
          |          ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:218:17: note: +1, including nesting penalty of 0, nesting level increased to 1
      218 |                 ? std::numeric_limits< geode::local_index_t >::max()
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:223:9: note: +1, including nesting penalty of 0, nesting level increased to 1
      223 |         for( const auto channel : geode::Indices{ bands } )
          |         ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:225:13: note: +2, including nesting penalty of 1, nesting level increased to 2
      225 |             if( absl::c_find( bands, bands[channel] )
          |             ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:237:13: note: +2, including nesting penalty of 1, nesting level increased to 2
      237 |             for( const auto pixel : geode::Indices{ values } )
          |             ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:241:41: note: +1
      241 |                     std::isnan( value ) || ( has_nodata && value == nodata );
          |                                         ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:241:57: note: +1
      241 |                     std::isnan( value ) || ( has_nodata && value == nodata );
          |                                                         ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:243:31: note: +3, including nesting penalty of 2, nesting level increased to 3
      243 |                     is_nodata ? geode::local_index_t{ 0 }
          |                               ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:246:17: note: +3, including nesting penalty of 2, nesting level increased to 3
      246 |                 for( const auto other : geode::Indices{ bands } )
          |                 ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:248:21: note: +4, including nesting penalty of 3, nesting level increased to 4
      248 |                     if( bands[other] == bands[channel] )
          |                     ^
  • src/geode/io/image/raster_image_input.cpp:209:10: warning: [readability-function-size]

    function 'read_rescaled_bands' exceeds recommended size/complexity thresholds

      209 |     void read_rescaled_bands( GDALDataset& dataset,
          |          ^
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:209:10: note: 5 parameters (threshold 4)
  • src/geode/io/image/raster_image_input.cpp:226:34: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      226 |                 != bands.begin() + channel )
          |                                  ^
  • src/geode/io/image/raster_image_input.cpp:233:31: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      233 |                 values.data() + values.size() - width, width, height,
          |                               ^
  • src/geode/io/image/raster_image_input.cpp:233:47: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      233 |                 values.data() + values.size() - width, width, height,
          |                                               ^
  • src/geode/io/image/raster_image_input.cpp:241:46: warning: [readability-implicit-bool-conversion]

    implicit conversion 'int' -> 'bool'

      241 |                     std::isnan( value ) || ( has_nodata && value == nodata );
          |                                              ^
          |                                              (          != 0)
  • src/geode/io/image/raster_image_input.cpp:250:25: warning: [readability-suspicious-call-argument]

    2nd argument 'other' (passed to 'channel') looks like it might be swapped with the 3rd, 'channel_value' (passed to 'value')

      250 |                         set_channel( colors[pixel], other, channel_value );
          |                         ^                           ~~~~~  ~~~~~~~~~~~~~
    /__w/OpenGeode-IO/OpenGeode-IO/src/geode/io/image/raster_image_input.cpp:190:10: note: in the call to 'set_channel', declared here
      190 |     void set_channel(
          |          ^
      191 |         geode::RGBColor& color, size_t channel, geode::local_index_t value )
          |                                        ~~~~~~~                       ~~~~~
  • src/geode/io/image/raster_image_input.cpp:263:29: warning: [readability-identifier-naming]

    invalid case style for variable 'colorInterp'

      263 |             GDALColorInterp colorInterp =
          |                             ^~~~~~~~~~~
          |                             color_interp
      264 |                 gdal_data.GetRasterBand( band_id )->GetColorInterpretation();
      265 |             if( colorInterp == GCI_RedBand )
          |                 ~~~~~~~~~~~
          |                 color_interp
      266 |             {
      267 |                 rgb_indices[0] = band_id;
      268 |             }
      269 |             else if( colorInterp == GCI_GreenBand )
          |                      ~~~~~~~~~~~
          |                      color_interp
      270 |             {
      271 |                 rgb_indices[1] = band_id;
      272 |             }
      273 |             else if( colorInterp == GCI_BlueBand )
          |                      ~~~~~~~~~~~
          |                      color_interp
  • src/geode/io/image/raster_image_input.cpp:289:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

      289 | namespace geode
          | ^~~~~~~~~~~~~~~
      290 | {
          | ~
      291 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
  • src/geode/io/image/tiff_input.cpp:30:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       30 | namespace geode
          | ^~~~~~~~~~~~~~~
       31 | {
          | ~
       32 |     namespace internal
          |     ~~~~~~~~~~~~~~~~~~
          |     namespace geode::internal
  • src/geode/io/image/vti_raster_image_output.cpp:52:13: warning: [cppcoreguidelines-pro-type-member-init]

    uninitialized record type: 'extent'

       52 |             std::array< geode::index_t, dimension > extent;
          |             ^                                             
          |                                                           {}
  • src/geode/io/image/vti_raster_image_output.cpp:71:17: warning: [cppcoreguidelines-pro-type-reinterpret-cast]

    do not use reinterpret_cast

       71 |                 reinterpret_cast< const uint8_t* >( colors.data() );
          |                 ^
  • src/geode/io/image/vti_raster_image_output.cpp:78:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       78 | namespace geode
          | ^~~~~~~~~~~~~~~
       79 | {
          | ~
       80 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
       81 |     {
       82 |         template < index_t dimension >
       83 |         std::vector< std::string > VTIRasterImageOutput< dimension >::write(
       84 |             const RasterImage< dimension >& raster ) const
       85 |         {
       86 |             ::VTIOutputImpl< dimension > impl{ raster, this->filename() };
       87 |             impl.write_file();
       88 |             return { to_string( this->filename() ) };
       89 |         }
       90 | 
       91 |         template class VTIRasterImageOutput< 2 >;
       92 |         template class VTIRasterImageOutput< 3 >;
       93 |     } // namespace detail
          |     ~~~~~~~~~~~~~~~~~~~~~
          |     } // namespace geode::detail
  • tests/image/test-raster-image.cpp:52:6: warning: [misc-use-internal-linkage]

    function 'test_jpg_from_gimp_input' can be made static or moved into an anonymous namespace to enforce internal linkage

       52 | void test_jpg_from_gimp_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:67:17: warning: [bugprone-unchecked-optional-access]

    unchecked access to optional value

       67 |                 attribute_ids.value().at( 0 ) );
          |                 ^~~~~~~~~~~~~
  • tests/image/test-raster-image.cpp:79:6: warning: [misc-use-internal-linkage]

    function 'test_jpg_from_paraview_input' can be made static or moved into an anonymous namespace to enforce internal linkage

       79 | void test_jpg_from_paraview_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:94:17: warning: [bugprone-unchecked-optional-access]

    unchecked access to optional value

       94 |                 attribute_ids.value().at( 0 ) );
          |                 ^~~~~~~~~~~~~
  • tests/image/test-raster-image.cpp:107:6: warning: [misc-use-internal-linkage]

    function 'test_png_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      107 | void test_png_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:122:17: warning: [bugprone-unchecked-optional-access]

    unchecked access to optional value

      122 |                 attribute_ids.value().at( 0 ) );
          |                 ^~~~~~~~~~~~~
  • tests/image/test-raster-image.cpp:134:6: warning: [misc-use-internal-linkage]

    function 'test_tiff_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      134 | void test_tiff_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:158:33: warning: [misc-use-internal-linkage]

    function 'test_image_pixel' can be made static or moved into an anonymous namespace to enforce internal linkage

      158 | std::array< geode::index_t, 2 > test_image_pixel( geode::index_t cell )
          |                                 ^
          | static 
  • tests/image/test-raster-image.cpp:161:33: warning: [readability-math-missing-parentheses]

    '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations

      161 |         TEST_IMAGE_HEIGHT - 1 - cell / TEST_IMAGE_WIDTH };
          |                                 ^~~~~~~~~~~~~~~~~~~~~~~
          |                                 (                      )
  • tests/image/test-raster-image.cpp:164:22: warning: [misc-use-internal-linkage]

    function 'load_test_image' can be made static or moved into an anonymous namespace to enforce internal linkage

      164 | geode::RasterImage2D load_test_image( std::string_view filename )
          |                      ^
          | static 
  • tests/image/test-raster-image.cpp:176:17: warning: [misc-use-internal-linkage]

    function 'test_image_rgb' can be made static or moved into an anonymous namespace to enforce internal linkage

      176 | geode::RGBColor test_image_rgb( geode::index_t cell )
          |                 ^
          | static 
  • tests/image/test-raster-image.cpp:179:51: warning: [cppcoreguidelines-avoid-magic-numbers]

    20 is a magic number; consider replacing it with a named constant

      179 |     return { static_cast< geode::local_index_t >( 20 * pixel[0] ),
          |                                                   ^
  • tests/image/test-raster-image.cpp:180:46: warning: [cppcoreguidelines-avoid-magic-numbers]

    40 is a magic number; consider replacing it with a named constant

      180 |         static_cast< geode::local_index_t >( 40 * pixel[1] ), 130 };
          |                                              ^
  • tests/image/test-raster-image.cpp:180:63: warning: [cppcoreguidelines-avoid-magic-numbers]

    130 is a magic number; consider replacing it with a named constant

      180 |         static_cast< geode::local_index_t >( 40 * pixel[1] ), 130 };
          |                                                               ^
  • tests/image/test-raster-image.cpp:183:6: warning: [misc-use-internal-linkage]

    function 'check_test_image_colors' can be made static or moved into an anonymous namespace to enforce internal linkage

      183 | void check_test_image_colors( std::string_view filename,
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:199:6: warning: [misc-use-internal-linkage]

    function 'test_bmp_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      199 | void test_bmp_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:205:6: warning: [misc-use-internal-linkage]

    function 'test_untagged_bands_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      205 | void test_untagged_bands_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:210:6: warning: [misc-use-internal-linkage]

    function 'test_five_bands_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      210 | void test_five_bands_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:215:6: warning: [misc-use-internal-linkage]

    function 'test_palette_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      215 | void test_palette_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:219:39: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations

      219 |         const auto index = pixel[0] + TEST_IMAGE_WIDTH * pixel[1];
          |                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                                       (                          )
  • tests/image/test-raster-image.cpp:221:37: warning: [cppcoreguidelines-avoid-magic-numbers]

    20 is a magic number; consider replacing it with a named constant

      221 |                                     20 * index ),
          |                                     ^
  • tests/image/test-raster-image.cpp:222:50: warning: [cppcoreguidelines-avoid-magic-numbers]

    255 is a magic number; consider replacing it with a named constant

      222 |             static_cast< geode::local_index_t >( 255 - 20 * index ),
          |                                                  ^
  • tests/image/test-raster-image.cpp:222:56: warning: [cppcoreguidelines-avoid-magic-numbers]

    20 is a magic number; consider replacing it with a named constant

      222 |             static_cast< geode::local_index_t >( 255 - 20 * index ),
          |                                                        ^
  • tests/image/test-raster-image.cpp:222:56: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '-'; add parentheses to explicitly specify the order of operations

      222 |             static_cast< geode::local_index_t >( 255 - 20 * index ),
          |                                                        ^~~~~~~~~~
          |                                                        (         )
  • tests/image/test-raster-image.cpp:223:50: warning: [cppcoreguidelines-avoid-magic-numbers]

    5 is a magic number; consider replacing it with a named constant

      223 |             static_cast< geode::local_index_t >( 5 * index ) };
          |                                                  ^
  • tests/image/test-raster-image.cpp:227:22: warning: [misc-use-internal-linkage]

    function 'rescaled' can be made static or moved into an anonymous namespace to enforce internal linkage

      227 | geode::local_index_t rescaled( double value, double min, double max )
          |                      ^
          | static 
  • tests/image/test-raster-image.cpp:230:39: warning: [cppcoreguidelines-avoid-magic-numbers]

    1. is a magic number; consider replacing it with a named constant
      230 |         std::round( ( value - min ) * 255. / ( max - min ) ) );
          |                                       ^
  • tests/image/test-raster-image.cpp:233:6: warning: [misc-use-internal-linkage]

    function 'test_non_8_bit_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      233 | void test_non_8_bit_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:237:27: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations

      237 |         return pixel[0] + TEST_IMAGE_WIDTH * pixel[1];
          |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
          |                           (                          )
  • tests/image/test-raster-image.cpp:241:45: warning: [readability-math-missing-parentheses]

    '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations

      241 |         const auto grey = rescaled( 1000. + 100. * index( cell ), 1000, 2100 );
          |                                             ^~~~~~~~~~~~~~~~~~~~~
          |                                             (                   )
  • tests/image/test-raster-image.cpp:247:47: warning: [cppcoreguidelines-avoid-magic-numbers]

    1. is a magic number; consider replacing it with a named constant
      247 |             return geode::RGBColor{ rescaled( 1000. + value, 1000, 4100 ),
          |                                               ^
  • tests/image/test-raster-image.cpp:247:62: warning: [cppcoreguidelines-avoid-magic-numbers]

    1000 is a magic number; consider replacing it with a named constant

      247 |             return geode::RGBColor{ rescaled( 1000. + value, 1000, 4100 ),
          |                                                              ^
  • tests/image/test-raster-image.cpp:247:68: warning: [cppcoreguidelines-avoid-magic-numbers]

    4100 is a magic number; consider replacing it with a named constant

      247 |             return geode::RGBColor{ rescaled( 1000. + value, 1000, 4100 ),
          |                                                                    ^
  • tests/image/test-raster-image.cpp:248:27: warning: [cppcoreguidelines-avoid-magic-numbers]

    1. is a magic number; consider replacing it with a named constant
      248 |                 rescaled( 2000. + value, 1000, 4100 ),
          |                           ^
  • tests/image/test-raster-image.cpp:248:42: warning: [cppcoreguidelines-avoid-magic-numbers]

    1000 is a magic number; consider replacing it with a named constant

      248 |                 rescaled( 2000. + value, 1000, 4100 ),
          |                                          ^
  • tests/image/test-raster-image.cpp:248:48: warning: [cppcoreguidelines-avoid-magic-numbers]

    4100 is a magic number; consider replacing it with a named constant

      248 |                 rescaled( 2000. + value, 1000, 4100 ),
          |                                                ^
  • tests/image/test-raster-image.cpp:249:27: warning: [cppcoreguidelines-avoid-magic-numbers]

    1. is a magic number; consider replacing it with a named constant
      249 |                 rescaled( 3000. + value, 1000, 4100 ) };
          |                           ^
  • tests/image/test-raster-image.cpp:249:42: warning: [cppcoreguidelines-avoid-magic-numbers]

    1000 is a magic number; consider replacing it with a named constant

      249 |                 rescaled( 3000. + value, 1000, 4100 ) };
          |                                          ^
  • tests/image/test-raster-image.cpp:249:48: warning: [cppcoreguidelines-avoid-magic-numbers]

    4100 is a magic number; consider replacing it with a named constant

      249 |                 rescaled( 3000. + value, 1000, 4100 ) };
          |                                                ^
  • tests/image/test-raster-image.cpp:262:6: warning: [misc-use-internal-linkage]

    function 'test_additional_files' can be made static or moved into an anonymous namespace to enforce internal linkage

      262 | void test_additional_files()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:281:6: warning: [misc-use-internal-linkage]

    function 'test_non_image_format_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      281 | void test_non_image_format_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:298:6: warning: [misc-use-internal-linkage]

    function 'test_unreadable_input' can be made static or moved into an anonymous namespace to enforce internal linkage

      298 | void test_unreadable_input()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:318:6: warning: [misc-use-internal-linkage]

    function 'test_empty_image_output' can be made static or moved into an anonymous namespace to enforce internal linkage

      318 | void test_empty_image_output()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:330:6: warning: [misc-use-internal-linkage]

    function 'test_vti_colors_output' can be made static or moved into an anonymous namespace to enforce internal linkage

      330 | void test_vti_colors_output()
          |      ^
          | static 
  • tests/image/test-raster-image.cpp:349:44: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      349 |     std::memcpy( &nb_bytes, content.data() + data, sizeof( nb_bytes ) );
          |                                            ^
  • tests/image/test-raster-image.cpp:351:21: warning: [bugprone-implicit-widening-of-multiplication-result]

    performing an implicit widening conversion to type 'std::uint64_t' (aka 'unsigned long') of a multiplication performed in type 'index_t' (aka 'unsigned int')

      351 |         nb_bytes == 3 * raster.nb_cells(), "[TEST] Wrong VTI colors size" );
          |                     ^
    /__w/OpenGeode-IO/OpenGeode-IO/tests/image/test-raster-image.cpp:351:21: note: make conversion explicit to silence this warning
       24 |         nb_bytes == 3 * raster.nb_cells(), "[TEST] Wrong VTI colors size" );
          |                     ^~~~~~~~~~~~~~~~~~~~~
          |                     static_cast<std::uint64_t>( )
    /__w/OpenGeode-IO/OpenGeode-IO/tests/image/test-raster-image.cpp:351:21: note: perform multiplication in a wider type
      351 |         nb_bytes == 3 * raster.nb_cells(), "[TEST] Wrong VTI colors size" );
          |                     ^
          |                     static_cast<std::uint64_t>( )

Have any feedback or feature suggestions? Share it here.

@BotellaA
BotellaA requested review from BenPinet and panquez October 21, 2026 19:12
@BenPinet
BenPinet merged commit cf67868 into next Oct 22, 2026
36 checks passed
@BenPinet
BenPinet deleted the fix/image-io-robustness branch October 22, 2026 12:17
@geode-ci

geode-ci Bot commented Oct 22, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 7.12.1-rc.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants