From f6ee280d4eb22d5167472c4f9772a8be576c9bb7 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 08:37:08 +0000 Subject: [PATCH 01/15] Add CMake configurations for code coverage and analysis tools Introduce settings for lcov, cppcheck, and Doxygen in CMake files. Create necessary configuration files to enable code coverage and static analysis during the Debug build type. This enhances the development workflow by integrating coverage reports and code quality checks. --- .vscode/settings.json | 8 ++++++ CMakeLists.txt | 16 +++--------- cmake/cppcheck.cmake | 22 +++++++++++++++++ cmake/doxy.cmake | 13 ++++++++++ cmake/lcov.cmake | 52 +++++++++++++++++++++++++++++++++++++++ cppcheck_suppressions.txt | 0 6 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 cmake/cppcheck.cmake create mode 100644 cmake/doxy.cmake create mode 100644 cmake/lcov.cmake create mode 100644 cppcheck_suppressions.txt diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3a205fb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "cmake.coverageInfoFiles": [ + "${workspaceFolder}/build/coverage_filtered.info" + ], + "cmake.postRunCoverageTarget": "lcov", + "testing.coverageToolbarEnabled": true, + "testing.defaultGutterClickAction": "runWithCoverage" +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ab8525..14e4c80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,15 +35,7 @@ target_link_libraries(test_runner PRIVATE blit) add_test(NAME pat COMMAND test_runner test/pat) add_test(NAME left_shift_edge COMMAND test_runner test/left_shift_edge) -# https://cmake.org/cmake/help/latest/module/FindDoxygen.html -# https://www.mcternan.me.uk/mscgen/ -find_package(Doxygen OPTIONAL_COMPONENTS dot mscgen dia) -if(DOXYGEN_FOUND) - set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) - set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) - set(DOXYGEN_SOURCE_BROWSER YES) - set(DOXYGEN_EXCLUDE ${CMAKE_BINARY_DIR}) - doxygen_add_docs(doxy ${CMAKE_SOURCE_DIR} - COMMENT "Generating API documentation with Doxygen" - ) -endif() +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +include(lcov) +include(doxy) +include(cppcheck) diff --git a/cmake/cppcheck.cmake b/cmake/cppcheck.cmake new file mode 100644 index 0000000..f85f978 --- /dev/null +++ b/cmake/cppcheck.cmake @@ -0,0 +1,22 @@ +# Add custom commands for code analysis. Enable cppcheck for Debug build type. +# This will run cppcheck only if the build type is Debug. +find_program(CPPCHECK cppcheck) +if(CPPCHECK AND CMAKE_BUILD_TYPE STREQUAL "Debug") + add_custom_target(cppcheck + COMMAND ${CPPCHECK} + --project=${CMAKE_BINARY_DIR}/compile_commands.json + --output-file=${CMAKE_BINARY_DIR}/cppcheck_report.xml + --xml + --enable=all + --enable=warning,style,performance,portability,information + --suppressions-list=${CMAKE_SOURCE_DIR}/cppcheck_suppressions.txt + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running cppcheck for code analysis" + # Use VERBATIM to ensure the command is executed as written. + # This is useful for handling paths with spaces or special characters. + VERBATIM + ) + set_property(TARGET cppcheck PROPERTY FOLDER "Analysis") +else() + message(STATUS "Cppcheck not found or not enabled for Debug build type") +endif() diff --git a/cmake/doxy.cmake b/cmake/doxy.cmake new file mode 100644 index 0000000..4913e71 --- /dev/null +++ b/cmake/doxy.cmake @@ -0,0 +1,13 @@ +# https://cmake.org/cmake/help/latest/module/FindDoxygen.html +# https://www.mcternan.me.uk/mscgen/ +find_package(Doxygen OPTIONAL_COMPONENTS dot mscgen dia) +if(DOXYGEN_FOUND) + set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) + set(DOXYGEN_USE_MATHJAX YES) + set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) + set(DOXYGEN_SOURCE_BROWSER YES) + set(DOXYGEN_EXCLUDE ${CMAKE_BINARY_DIR}) + doxygen_add_docs(doxy ${CMAKE_SOURCE_DIR} + COMMENT "Generating API documentation with Doxygen" + ) +endif() diff --git a/cmake/lcov.cmake b/cmake/lcov.cmake new file mode 100644 index 0000000..7623e21 --- /dev/null +++ b/cmake/lcov.cmake @@ -0,0 +1,52 @@ +# Add custom commands for code coverage analysis. Enable lcov and genhtml if +# they are found. Only enable code coverage analysis for Debug build type, as it +# is typically used for development and testing. Code coverage analysis can add +# overhead to the build and runtime, so it is generally not recommended for +# Release builds. +# +# If lcov is found, add the coverage compiler and linker flags to enable code +# coverage analysis. +# +# Note that find_program will return the path to the executable if found, or +# NOTFOUND if not found. It will also cache the result, so subsequent calls will +# return the cached value. This allows us to check if lcov is available and +# conditionally add code coverage targets. +find_program(LCOV lcov) +if(LCOV AND CMAKE_BUILD_TYPE STREQUAL "Debug") + # Add the necessary compiler and linker flags to enable code coverage analysis. + foreach(target IN ITEMS blit test_runner) + target_compile_options(${target} PRIVATE --coverage) + target_link_options(${target} PRIVATE --coverage) + endforeach() + + # Add custom targets for code coverage analysis using lcov and genhtml. + # These targets will run the tests, capture coverage data, and generate an HTML report. + add_custom_target(ctest + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + DEPENDS test_runner + ) + add_custom_target(lcov + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${LCOV} --capture --directory . --output-file coverage.info + COMMAND ${LCOV} --remove coverage.info '/usr/*' '*/test*' --output-file coverage_filtered.info + ) + set_property(TARGET lcov PROPERTY FOLDER "Analysis") + + # If genhtml is found, add a custom target to generate an HTML report from the + # filtered coverage data. This will create a coverage_report directory with an + # index.html file that can be opened in a web browser to view the coverage report. + find_program(GENHTML genhtml) + if(GENHTML) + message(STATUS "lcov and genhtml found, code coverage targets will be available") + add_custom_target(genhtml + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${GENHTML} coverage_filtered.info --output-directory coverage_report + COMMAND ${CMAKE_COMMAND} -E echo "Coverage report generated in: ${CMAKE_BINARY_DIR}/coverage_report/index.html" + DEPENDS lcov + ) + set_property(TARGET genhtml PROPERTY FOLDER "Analysis") + else() + message(STATUS "genhtml not found, HTML code coverage report target will not be available") + endif() +endif() diff --git a/cppcheck_suppressions.txt b/cppcheck_suppressions.txt new file mode 100644 index 0000000..e69de29 From 2c0f83c7b39428f280c6ae346afe1ba75f476614 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 08:44:44 +0000 Subject: [PATCH 02/15] Fix spelling of "normalize" to "normalise" in documentation Correct the spelling in the function documentation for blit_rgn1_norm to adhere to British English conventions. --- inc/blit/rgn1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/blit/rgn1.h b/inc/blit/rgn1.h index 9818bdf..1e16fb1 100644 --- a/inc/blit/rgn1.h +++ b/inc/blit/rgn1.h @@ -40,7 +40,7 @@ struct blit_rgn1 { /*! * \brief Normalise a one-dimensional region. - * \details This function normalizes a one-dimensional region represented by the + * \details This function normalises a one-dimensional region represented by the * \c blit_rgn1 structure. If the extent of the region is negative, it * adjusts the origin and origin_source accordingly to ensure that the * extent is non-negative. From a2a1fe9d904aad6f30705ac869d154fa02911165 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 08:55:46 +0000 Subject: [PATCH 03/15] Refactor region handling functions to use 'move' terminology Update function names and documentation to replace 'slip' with 'move' for consistency. Adjust comments to reflect the changes in functionality for better understanding of region adjustments. --- inc/blit/rgn1.h | 12 ++++++------ src/blit/rop2.c | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/inc/blit/rgn1.h b/inc/blit/rgn1.h index 1e16fb1..a4a1b3b 100644 --- a/inc/blit/rgn1.h +++ b/inc/blit/rgn1.h @@ -7,7 +7,7 @@ * \brief One-dimensional region structure. * \details This header file defines the \c blit_rgn1 structure, which * represents a one-dimensional region with an origin, extent, and source - * origin. It also provides inline functions for normalising, slipping, and + * origin. It also provides inline functions for normalising, moving, and * clipping the region. */ @@ -62,18 +62,18 @@ static inline void blit_rgn1_norm(struct blit_rgn1 *rgn1) { } /*! - * \brief Slip a one-dimensional region into positive space. + * \brief Move a one-dimensional region into positive space. * \details This function adjusts a one-dimensional region represented by the * \c blit_rgn1 structure to ensure that both the origin and source origin are * non-negative. If either the origin or source origin is negative, the region - * is "slipped" into positive space by adjusting the origins and reducing the + * is "moved" into positive space by adjusting the origins and reducing the * extent accordingly. If the entire region is outside positive space, the * function returns false. - * \param rgn1 Pointer to the \c blit_rgn1 structure to slip. - * \retval true if the region was successfully slipped into positive space. + * \param rgn1 Pointer to the \c blit_rgn1 structure to move. + * \retval true if the region was successfully moved into positive space. * \retval false if the entire region is outside positive space. */ -static inline bool blit_rgn1_slip(struct blit_rgn1 *rgn1) { +static inline bool blit_rgn1_move(struct blit_rgn1 *rgn1) { int offset = rgn1->origin < 0 ? (rgn1->origin < rgn1->origin_source ? -rgn1->origin : -rgn1->origin_source) diff --git a/src/blit/rop2.c b/src/blit/rop2.c index 560a047..9439d52 100644 --- a/src/blit/rop2.c +++ b/src/blit/rop2.c @@ -196,22 +196,22 @@ bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rgn1 *y, const struct blit_scan *source, enum blit_rop2 rop2) { /* - * Normalise, slip, and clip the x region. The regions are first normalised to - * ensure that their extents are non-negative. Then, they are slipped to + * Normalise, move, and clip the x region. The regions are first normalised to + * ensure that their extents are non-negative. Then, they are moved to * ensure that their origins are non-negative. Finally, they are clipped to * ensure that they fit within the bounds of the destination and source scan * structures. */ blit_rgn1_norm(x); - if (!blit_rgn1_slip(x) || !blit_rgn1_clip(x, result->width - x->origin) || + if (!blit_rgn1_move(x) || !blit_rgn1_clip(x, result->width - x->origin) || !blit_rgn1_clip(x, source->width - x->origin_source)) return false; /* - * Normalise, slip, and clip the y region. + * Normalise, move, and clip the y region. */ blit_rgn1_norm(y); - if (!blit_rgn1_slip(y) || !blit_rgn1_clip(y, result->height - y->origin) || + if (!blit_rgn1_move(y) || !blit_rgn1_clip(y, result->height - y->origin) || !blit_rgn1_clip(y, source->height - y->origin_source)) return false; From 880575ec7b2b54536ef604b0c9d46a8e7c5cceb3 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:38:55 +0000 Subject: [PATCH 04/15] Add notes on 'lcov' target availability in settings Clarify that the 'lcov' target is only defined when lcov is installed and the build type is Debug. Mention potential failures in other configurations. --- .vscode/settings.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3a205fb..14701dc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "cmake.coverageInfoFiles": [ "${workspaceFolder}/build/coverage_filtered.info" ], + // NOTE: The 'lcov' target is only defined when lcov is installed and the build type is Debug. + // In other configurations, "Run with coverage" may fail if this target is not available. "cmake.postRunCoverageTarget": "lcov", "testing.coverageToolbarEnabled": true, "testing.defaultGutterClickAction": "runWithCoverage" From 962a6c3b8f9d0569cc9f7997bcce70a282fd92f8 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:39:20 +0000 Subject: [PATCH 05/15] Append to CMAKE_MODULE_PATH for module inclusion Update the CMake configuration to append the current source directory to the CMAKE_MODULE_PATH, ensuring proper module inclusion for lcov, doxy, and cppcheck. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14e4c80..b6d0b36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ target_link_libraries(test_runner PRIVATE blit) add_test(NAME pat COMMAND test_runner test/pat) add_test(NAME left_shift_edge COMMAND test_runner test/left_shift_edge) -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(lcov) include(doxy) include(cppcheck) From dc14015013bc6354b32cfab400741010cf8990f5 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:46:07 +0000 Subject: [PATCH 06/15] Refactor raster operation functions to return operation count Change return type of blit_rgn1_rop2 and blit_rop2 functions from bool to int to indicate the number of logic operations performed. Update documentation to reflect this change and ensure accurate counting of operations, including those that do not alter pixel values. --- inc/blit/rop2.h | 19 ++++++++++++------- src/blit/rop2.c | 26 ++++++++++++-------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/inc/blit/rop2.h b/inc/blit/rop2.h index fca9e2f..d0d2a5e 100644 --- a/inc/blit/rop2.h +++ b/inc/blit/rop2.h @@ -72,11 +72,17 @@ enum blit_rop2 { * \param y Pointer to the one-dimensional region structure for the y-axis. * \param source Pointer to the source scan structure. * \param rop2 The raster operation code. - * \return true if the operation was successful, false otherwise. + * \return The number of logic operations performed. This includes all + * operations performed on the destination scan space, including those + * that may not have resulted in a change to the pixel values (e.g., + * operations that resulted in the same value being written back to the + * destination). It includes partial operations on the first and last + * bytes of each scanline, which may have been masked to only affect + * certain bits. The count reflects the total number of operations + * attempted based on the region and raster operation specified, + * regardless of the actual changes made to the pixel data. */ -bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, - struct blit_rgn1 *y, const struct blit_scan *source, - enum blit_rop2 rop2); +int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rgn1 *y, const struct blit_scan *source, enum blit_rop2 rop2); /*! * \brief Convenience inline function for performing raster operations. @@ -98,10 +104,9 @@ bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, * \param x_source The x-coordinate of the origin of the region in the source. * \param y_source The y-coordinate of the origin of the region in the source. * \param rop2 The raster operation code to apply. + * \return The number of logic operations performed. */ -bool blit_rop2(struct blit_scan *result, const int x, const int y, - const int x_extent, const int y_extent, - const struct blit_scan *source, const int x_source, +int blit_rop2(struct blit_scan *result, const int x, const int y, const int x_extent, const int y_extent, const struct blit_scan *source, const int x_source, const int y_source, enum blit_rop2 rop2); #endif /* __BLIT_ROP2_H__ */ diff --git a/src/blit/rop2.c b/src/blit/rop2.c index 9439d52..67554c9 100644 --- a/src/blit/rop2.c +++ b/src/blit/rop2.c @@ -192,9 +192,7 @@ static void fetch_logic_mask_store(struct blit_phase_align *align, static void fetch_logic_store(struct blit_phase_align *align, enum blit_rop2 rop2, blit_scanline_t *store); -bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, - struct blit_rgn1 *y, const struct blit_scan *source, - enum blit_rop2 rop2) { +int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rgn1 *y, const struct blit_scan *source, enum blit_rop2 rop2) { /* * Normalise, move, and clip the x region. The regions are first normalised to * ensure that their extents are non-negative. Then, they are moved to @@ -203,17 +201,15 @@ bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, * structures. */ blit_rgn1_norm(x); - if (!blit_rgn1_move(x) || !blit_rgn1_clip(x, result->width - x->origin) || - !blit_rgn1_clip(x, source->width - x->origin_source)) - return false; + if (!blit_rgn1_move(x) || !blit_rgn1_clip(x, result->width - x->origin) || !blit_rgn1_clip(x, source->width - x->origin_source)) + return 0; /* * Normalise, move, and clip the y region. */ blit_rgn1_norm(y); - if (!blit_rgn1_move(y) || !blit_rgn1_clip(y, result->height - y->origin) || - !blit_rgn1_clip(y, source->height - y->origin_source)) - return false; + if (!blit_rgn1_move(y) || !blit_rgn1_clip(y, result->height - y->origin) || !blit_rgn1_clip(y, source->height - y->origin_source)) + return 0; /* * Compute some important values up front to avoid doing it inside the bit @@ -272,12 +268,13 @@ bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, * of the phase alignment structure ensures that the source data is correctly * aligned with the destination data during the transfer. */ - int extent = y->extent; + int extent = y->extent, logic_count = 0; if (extra_scan_count == 0) { const blit_scanline_t scan_mask = scan_origin_mask & scan_extent_mask; while (extent--) { blit_phase_align_prefetch(&align); fetch_logic_mask_store(&align, rop2, scan_mask, store++); + logic_count++; store += offset; align.store += offset_source; } @@ -285,21 +282,22 @@ bool blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, while (extent--) { blit_phase_align_prefetch(&align); fetch_logic_mask_store(&align, rop2, scan_origin_mask, store++); + logic_count++; int extra = extra_scan_count; while (--extra) { fetch_logic_store(&align, rop2, store++); + logic_count++; } fetch_logic_mask_store(&align, rop2, scan_extent_mask, store++); + logic_count++; store += offset; align.store += offset_source; } } - return true; + return logic_count; } -bool blit_rop2(struct blit_scan *result, const int x, const int y, - const int x_extent, const int y_extent, - const struct blit_scan *source, const int x_source, +int blit_rop2(struct blit_scan *result, const int x, const int y, const int x_extent, const int y_extent, const struct blit_scan *source, const int x_source, const int y_source, enum blit_rop2 rop2) { /* * Build the one-dimensional region structures for x and y axes from the From ef38e3fac2d28cef29034b57d4d144bc65690207 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:47:15 +0000 Subject: [PATCH 07/15] Refactor blit_rop2 function parameters for improved readability Update the parameter formatting in the blit_rop2 function to clearly distinguish between destination and source regions, as well as the raster operation. This enhances the function's interface for better understanding and usage. --- inc/blit/rop2.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/inc/blit/rop2.h b/inc/blit/rop2.h index d0d2a5e..9ac3360 100644 --- a/inc/blit/rop2.h +++ b/inc/blit/rop2.h @@ -106,7 +106,12 @@ int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rg * \param rop2 The raster operation code to apply. * \return The number of logic operations performed. */ -int blit_rop2(struct blit_scan *result, const int x, const int y, const int x_extent, const int y_extent, const struct blit_scan *source, const int x_source, - const int y_source, enum blit_rop2 rop2); +int blit_rop2(struct blit_scan *result, + /* destination region */ + const int x, const int y, const int x_extent, const int y_extent, + /* source region */ + const struct blit_scan *source, const int x_source, const int y_source, + /* raster operation */ + enum blit_rop2 rop2); #endif /* __BLIT_ROP2_H__ */ From b4bf8e3e230ec02587c2033e60e859768f5bf187 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:49:19 +0000 Subject: [PATCH 08/15] Refactor function signatures for improved readability Adjust function signatures in rop2.c to align parameters on a single line, enhancing readability. This includes typedefs and function declarations for raster operations and logic functions. --- src/blit/rop2.c | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/blit/rop2.c b/src/blit/rop2.c index 67554c9..55f8d4f 100644 --- a/src/blit/rop2.c +++ b/src/blit/rop2.c @@ -32,8 +32,7 @@ * (`fetch`) and the destination operand (`store`). The function returns an * 8-bit result of the raster operation. */ -typedef blit_scanline_t (*blit_rop2_func_t)(blit_scanline_t fetch, - blit_scanline_t store); +typedef blit_scanline_t (*blit_rop2_func_t)(blit_scanline_t fetch, blit_scanline_t store); /*! * \brief Macro to define a raster operation function. @@ -66,13 +65,9 @@ typedef blit_scanline_t (*blit_rop2_func_t)(blit_scanline_t fetch, * \param revPolish The reverse polish notation name of the raster operation. * \param x The expression defining the raster operation using D and S. */ -#define ROP_REV_POLISH(revPolish, x) \ - static blit_scanline_t rop##revPolish(blit_scanline_t fetch, \ - blit_scanline_t store); \ - blit_scanline_t rop##revPolish(blit_scanline_t fetch, \ - blit_scanline_t store) { \ - return x; \ - } +#define ROP_REV_POLISH(revPolish, x) \ + static blit_scanline_t rop##revPolish(blit_scanline_t fetch, blit_scanline_t store); \ + blit_scanline_t rop##revPolish(blit_scanline_t fetch, blit_scanline_t store) { return x; } /*! * \brief Raster operation: 0. @@ -167,10 +162,8 @@ ROP_REV_POLISH(1, 0xffU); * functions. Each function implements a specific raster operation defined * using bitwise operations on the source (S) and destination (D) operands. */ -static blit_rop2_func_t rop2_func[] = {&rop0, &ropDSon, &ropDSna, &ropSn, - &ropSDna, &ropDn, &ropDSx, &ropDSan, - &ropDSa, &ropDSxn, &ropD, &ropDSno, - &ropS, &ropSDno, &ropDSo, &rop1}; +static blit_rop2_func_t rop2_func[] = {&rop0, &ropDSon, &ropDSna, &ropSn, &ropSDna, &ropDn, &ropDSx, &ropDSan, + &ropDSa, &ropDSxn, &ropD, &ropDSno, &ropS, &ropSDno, &ropDSo, &rop1}; /*! * \brief Perform raster operation with masking and store the result. @@ -179,9 +172,7 @@ static blit_rop2_func_t rop2_func[] = {&rop0, &ropDSon, &ropDSna, &ropSn, * \param mask The mask to apply to the operation. * \param store Pointer to the destination operand. */ -static void fetch_logic_mask_store(struct blit_phase_align *align, - enum blit_rop2 rop2, blit_scanline_t mask, - blit_scanline_t *store); +static void fetch_logic_mask_store(struct blit_phase_align *align, enum blit_rop2 rop2, blit_scanline_t mask, blit_scanline_t *store); /*! * \brief Perform raster operation and store the result. @@ -189,8 +180,7 @@ static void fetch_logic_mask_store(struct blit_phase_align *align, * \param rop2 The raster operation code. * \param store Pointer to the destination operand. */ -static void fetch_logic_store(struct blit_phase_align *align, - enum blit_rop2 rop2, blit_scanline_t *store); +static void fetch_logic_store(struct blit_phase_align *align, enum blit_rop2 rop2, blit_scanline_t *store); int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rgn1 *y, const struct blit_scan *source, enum blit_rop2 rop2) { /* @@ -248,9 +238,7 @@ int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rg * get out of sync! Keep them in step! */ struct blit_phase_align align; - blit_phase_align_start( - &align, x->origin, x->origin_source & 7, - blit_scan_find(source, x->origin_source, y->origin_source)); + blit_phase_align_start(&align, x->origin, x->origin_source & 7, blit_scan_find(source, x->origin_source, y->origin_source)); /* * Perform the bit block transfer using the specified raster operation. The @@ -297,8 +285,13 @@ int blit_rgn1_rop2(struct blit_scan *result, struct blit_rgn1 *x, struct blit_rg return logic_count; } -int blit_rop2(struct blit_scan *result, const int x, const int y, const int x_extent, const int y_extent, const struct blit_scan *source, const int x_source, - const int y_source, enum blit_rop2 rop2) { +int blit_rop2(struct blit_scan *result, + /* destination region */ + const int x, const int y, const int x_extent, const int y_extent, + /* source region */ + const struct blit_scan *source, const int x_source, const int y_source, + /* raster operation */ + enum blit_rop2 rop2) { /* * Build the one-dimensional region structures for x and y axes from the * arguments. These structures define the origin and extent of the region to @@ -318,13 +311,10 @@ int blit_rop2(struct blit_scan *result, const int x, const int y, const int x_ex return blit_rgn1_rop2(result, &x_rgn1, &y_rgn1, source, rop2); } -void fetch_logic_mask_store(struct blit_phase_align *align, enum blit_rop2 rop2, - blit_scanline_t mask, blit_scanline_t *store) { - *store = (*store & ~mask) | - (mask & rop2_func[rop2](blit_phase_align_fetch(align), *store)); +void fetch_logic_mask_store(struct blit_phase_align *align, enum blit_rop2 rop2, blit_scanline_t mask, blit_scanline_t *store) { + *store = (*store & ~mask) | (mask & rop2_func[rop2](blit_phase_align_fetch(align), *store)); } -void fetch_logic_store(struct blit_phase_align *align, enum blit_rop2 rop2, - blit_scanline_t *store) { +void fetch_logic_store(struct blit_phase_align *align, enum blit_rop2 rop2, blit_scanline_t *store) { *store = rop2_func[rop2](blit_phase_align_fetch(align), *store); } From fc49ebe0a8c8d06db8553f0dc128ab6aa31f38a3 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:49:35 +0000 Subject: [PATCH 09/15] Fix Doxygen main page reference to use full path for README.md Updated the Doxygen configuration to specify the full path for the main page file, ensuring accurate documentation generation. --- cmake/doxy.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/doxy.cmake b/cmake/doxy.cmake index 4913e71..87655ec 100644 --- a/cmake/doxy.cmake +++ b/cmake/doxy.cmake @@ -2,7 +2,7 @@ # https://www.mcternan.me.uk/mscgen/ find_package(Doxygen OPTIONAL_COMPONENTS dot mscgen dia) if(DOXYGEN_FOUND) - set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md) + set(DOXYGEN_USE_MDFILE_AS_MAINPAGE ${CMAKE_SOURCE_DIR}/README.md) set(DOXYGEN_USE_MATHJAX YES) set(DOXYGEN_OPTIMIZE_OUTPUT_FOR_C YES) set(DOXYGEN_SOURCE_BROWSER YES) From 81d91164eaa0ee3aeb1696268da7def5288bba6a Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:52:48 +0000 Subject: [PATCH 10/15] Refactor cppcheck command for Debug configuration Update cppcheck command to use configuration-aware logic, ensuring it only runs when building the Debug configuration. Improve comments for better understanding of the code analysis process. --- cmake/cppcheck.cmake | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmake/cppcheck.cmake b/cmake/cppcheck.cmake index f85f978..bf67dd2 100644 --- a/cmake/cppcheck.cmake +++ b/cmake/cppcheck.cmake @@ -1,22 +1,22 @@ # Add custom commands for code analysis. Enable cppcheck for Debug build type. -# This will run cppcheck only if the build type is Debug. +# This will run cppcheck only when building the Debug configuration, using configuration-aware logic. find_program(CPPCHECK cppcheck) -if(CPPCHECK AND CMAKE_BUILD_TYPE STREQUAL "Debug") +if(CPPCHECK) add_custom_target(cppcheck - COMMAND ${CPPCHECK} - --project=${CMAKE_BINARY_DIR}/compile_commands.json - --output-file=${CMAKE_BINARY_DIR}/cppcheck_report.xml - --xml - --enable=all - --enable=warning,style,performance,portability,information - --suppressions-list=${CMAKE_SOURCE_DIR}/cppcheck_suppressions.txt + COMMAND $<$:${CPPCHECK}> + $<$:--project=${CMAKE_BINARY_DIR}/compile_commands.json> + $<$:--output-file=${CMAKE_BINARY_DIR}/cppcheck_report.xml> + $<$:--xml> + $<$:--enable=all> + $<$:--enable=warning,style,performance,portability,information> + $<$:--suppressions-list=${CMAKE_SOURCE_DIR}/cppcheck_suppressions.txt> WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMENT "Running cppcheck for code analysis" + COMMENT "Running cppcheck for code analysis (Debug configuration only)" # Use VERBATIM to ensure the command is executed as written. # This is useful for handling paths with spaces or special characters. VERBATIM ) set_property(TARGET cppcheck PROPERTY FOLDER "Analysis") else() - message(STATUS "Cppcheck not found or not enabled for Debug build type") + message(STATUS "Cppcheck not found; code analysis target will not be created") endif() From a79bdd7b70678075b29db0ee08d62b02774b577a Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:54:16 +0000 Subject: [PATCH 11/15] Enable code coverage for all configurations with lcov Refactor CMake configuration to allow code coverage analysis regardless of the build type. Update target_compile_options and target_link_options to use generator expressions for conditional coverage flags. Add dependency for lcov target on ctest to ensure tests are run before coverage is captured. --- cmake/lcov.cmake | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/lcov.cmake b/cmake/lcov.cmake index 7623e21..4bc2b18 100644 --- a/cmake/lcov.cmake +++ b/cmake/lcov.cmake @@ -12,11 +12,13 @@ # return the cached value. This allows us to check if lcov is available and # conditionally add code coverage targets. find_program(LCOV lcov) -if(LCOV AND CMAKE_BUILD_TYPE STREQUAL "Debug") +if(LCOV) # Add the necessary compiler and linker flags to enable code coverage analysis. + # Use generator expressions so coverage is only enabled for Debug configuration, + # which works for both single- and multi-config generators. foreach(target IN ITEMS blit test_runner) - target_compile_options(${target} PRIVATE --coverage) - target_link_options(${target} PRIVATE --coverage) + target_compile_options(${target} PRIVATE $<$:--coverage>) + target_link_options(${target} PRIVATE $<$:--coverage>) endforeach() # Add custom targets for code coverage analysis using lcov and genhtml. @@ -27,9 +29,9 @@ if(LCOV AND CMAKE_BUILD_TYPE STREQUAL "Debug") DEPENDS test_runner ) add_custom_target(lcov - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${LCOV} --capture --directory . --output-file coverage.info COMMAND ${LCOV} --remove coverage.info '/usr/*' '*/test*' --output-file coverage_filtered.info + DEPENDS ctest ) set_property(TARGET lcov PROPERTY FOLDER "Analysis") From 80ef0f3b7c76061fdf2887ae2e3fb3ec2a29c42d Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 10:56:20 +0000 Subject: [PATCH 12/15] Enable code coverage for Debug configuration Add compiler and linker flags for code coverage analysis to the blit and test_runner targets. This ensures that coverage is only enabled for Debug builds, optimising performance for Release builds. --- CMakeLists.txt | 9 +++++++++ cmake/lcov.cmake | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6d0b36..f702cd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,5 +37,14 @@ add_test(NAME left_shift_edge COMMAND test_runner test/left_shift_edge) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(lcov) +if(LCOV) + # Add the necessary compiler and linker flags to enable code coverage analysis. + # Use generator expressions so coverage is only enabled for Debug configuration, + # which works for both single- and multi-config generators. + foreach(target IN ITEMS blit test_runner) + target_compile_options(${target} PRIVATE $<$:--coverage>) + target_link_options(${target} PRIVATE $<$:--coverage>) + endforeach() +endif() include(doxy) include(cppcheck) diff --git a/cmake/lcov.cmake b/cmake/lcov.cmake index 4bc2b18..365a383 100644 --- a/cmake/lcov.cmake +++ b/cmake/lcov.cmake @@ -13,14 +13,6 @@ # conditionally add code coverage targets. find_program(LCOV lcov) if(LCOV) - # Add the necessary compiler and linker flags to enable code coverage analysis. - # Use generator expressions so coverage is only enabled for Debug configuration, - # which works for both single- and multi-config generators. - foreach(target IN ITEMS blit test_runner) - target_compile_options(${target} PRIVATE $<$:--coverage>) - target_link_options(${target} PRIVATE $<$:--coverage>) - endforeach() - # Add custom targets for code coverage analysis using lcov and genhtml. # These targets will run the tests, capture coverage data, and generate an HTML report. add_custom_target(ctest From 22b2f32f1788b102f03b977dbe08332566e1f8c4 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 11:29:13 +0000 Subject: [PATCH 13/15] Add extra_scan_count test to validate scanline behaviour Include a new test for extra_scan_count in the test suite to ensure correct functionality of the scanline operations. This test checks the expected output against the defined patterns. --- CMakeLists.txt | 2 ++ test/extra_scan_count.c | 68 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 test/extra_scan_count.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f702cd3..cc3f553 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ create_test_sourcelist(test_sources test_driver.c test/pat.c test/left_shift_edge.c + test/extra_scan_count.c ) # Add a test executable that links against the library. @@ -34,6 +35,7 @@ target_link_libraries(test_runner PRIVATE blit) add_test(NAME pat COMMAND test_runner test/pat) add_test(NAME left_shift_edge COMMAND test_runner test/left_shift_edge) +add_test(NAME extra_scan_count COMMAND test_runner test/extra_scan_count) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(lcov) diff --git a/test/extra_scan_count.c b/test/extra_scan_count.c new file mode 100644 index 0000000..0a88120 --- /dev/null +++ b/test/extra_scan_count.c @@ -0,0 +1,68 @@ +#include + +#include +#include +#include + +int test_extra_scan_count() { + blit_scanline_t pat_store[] = { + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + 0x55U, 0x55U, 0x55U, // .# (alternating) + 0xAAU, 0xAAU, 0xAAU, // #. (alternating) + }; + struct blit_scan pat = { + .store = pat_store, + .width = 18, + .height = 12, + .stride = 3, + }; + BLIT_SCAN_DEFINE(image, 48, 24); + + for (int y = 0; y < image.height; y += pat.height) { + for (int x = 0; x < image.width; x += pat.width) { + struct blit_rgn1 x_rgn1 = { + .origin = x, + .extent = pat.width, + .origin_source = 0, + }; + struct blit_rgn1 y_rgn1 = { + .origin = y, + .extent = pat.height, + .origin_source = 0, + }; + assert(blit_rgn1_rop2(&image, &x_rgn1, &y_rgn1, &pat, blit_rop2_copy)); + } + } + + for (int x = 0; x < image.width; ++x) { + for (int y = 0; y < image.height; ++y) { + BLIT_SCAN_DEFINE(bit, 1, 1); + struct blit_rgn1 x_rgn1 = { + .origin = 0, + .extent = 1, + .origin_source = x, + }; + struct blit_rgn1 y_rgn1 = { + .origin = 0, + .extent = 1, + .origin_source = y, + }; + assert(blit_rgn1_rop2(&bit, &x_rgn1, &y_rgn1, &image, blit_rop2_copy)); + blit_scanline_t bit_scanline = bit_store[0] >> 7; + (void)printf("%c", bit_scanline ? '#' : '.'); + assert(bit_scanline == ((x & 1U) ^ (y & 1U))); + } + (void)printf("\n"); + } + + return EXIT_SUCCESS; +} From 6a0cbd523e5694a501f1d2515f1abc3a08cd1edd Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 11:30:53 +0000 Subject: [PATCH 14/15] Add gdb launch configuration to launch.json Introduce a new launch configuration for gdb with setup commands for pretty-printing and Intel disassembly flavour. This enhances the debugging experience for users working with gdb. --- .vscode/launch.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index fa5ed59..768448f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,30 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, { "name": "(Windows) Launch", "type": "cppvsdbg", From 4fd765228f6cfc0481ef4f93bff4f91d78afea29 Mon Sep 17 00:00:00 2001 From: Roy Ratcliffe Date: Sun, 1 Mar 2026 11:37:54 +0000 Subject: [PATCH 15/15] Enable compile_commands.json generation for analysis tools This change allows tools like cppcheck and clang-tidy to access the compilation commands and flags used in the project, improving the accuracy of their analysis and reporting. --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc3f553..73a022a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.25) project(blit) +# Enable generation of compile_commands.json for tools like cppcheck and +# clang-tidy. This will allow these tools to understand the compilation +# commands and flags used for the project, which is good for accurate +# analysis and reporting. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + add_library(blit # Source files for the blit library. ${CMAKE_CURRENT_SOURCE_DIR}/src/blit/rop2.c