Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ All notable changes to this project will be documented in this file.

### Changed
- The `bitmap` library now exposes the `BmpTool` API via `include/bitmap.hpp` for simplified bitmap operations.
- **Documentation**:
- Reviewed and significantly updated `README.md` for accuracy regarding features, API examples (old vs. new), build instructions, and added "Code Documentation" and "Contributing" sections.
- Reviewed and extensively updated `src/matrix/Documentation/Matrix.MD` to align with the current `src/matrix/matrix.h` implementation, including documenting move semantics, new functions, and correcting outdated information.

## [0.2.0] - 2024-07-27

Expand Down
220 changes: 138 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ The library supports the following image manipulation functions:

* **Color Adjustments:**
* Greyscale Conversion
* Brightness Adjustment (Overall and per R,G,B,Magenta,Yellow,Cyan channel)
* Contrast Adjustment (Overall and per R,G,B,Magenta,Yellow,Cyan channel)
* Brightness Adjustment (Overall)
* Contrast Adjustment (Overall)
* Saturation Adjustment (Overall and per R,G,B,Magenta,Yellow,Cyan channel)
* Luminance Adjustment (per R,G,B,Magenta,Yellow,Cyan channel)
* Invert Colors
Expand All @@ -19,125 +19,149 @@ The library supports the following image manipulation functions:
* Box Blur
* **Geometric Transformations:**
* Shrink Image (reduce size)
* Rotate Image (Clockwise and Counter-Clockwise)
* Rotate Image (90 degrees Clockwise and Counter-Clockwise)
* Mirror Image (horizontal flip)
* Flip Image (vertical flip)
* **Utility:**
* **Utility (Old API - `src/bitmap/bitmap.h`):**
* Screen Capture (Windows only: `ScreenShotWindow`)
* Load BMP from file
* Save BMP to file
* Load BMP from file (`Bitmap::File::Open`)
* Save BMP to file (`Bitmap::File::SaveAs`)
* **Utility (New API - `include/bitmap.hpp`):**
* Load BMP from memory (`BmpTool::load`)
* Save BMP to memory (`BmpTool::save`)

Note: The old API (`src/bitmap/bitmap.h` and `src/bitmapfile/bitmap_file.h`) provides file-based loading/saving and additional pixel-level operations. The new `BmpTool` API (`include/bitmap.hpp`) focuses on memory span-based operations and provides a more modern interface for whole-image manipulations.

## Dependencies

This library utilizes:
* A simple Matrix library (from `src/matrix`)
* A BMP file handling library (from `src/bitmapfile`)
These are now part of the main source tree under the `src/` directory.
This library has internal dependencies:
* A simple Matrix library (from `src/matrix`) used by the old API.
* BMP file handling structures (from `src/bitmapfile`) used by both APIs.
These are part of the main source tree under the `src/` directory. No external libraries are required for core functionality.

## Core Modules
* **Bitmap Library (`src/bitmap`)**: Provides core image processing functions.
* **Bitmap File Handler (`src/bitmapfile`)**: Handles loading and saving of BMP files.
* **Matrix Library (`src/matrix`)**: A generic matrix manipulation library used by the bitmap processing functions.
* **New Bitmap API (`include/bitmap.hpp`)**: Provides the `BmpTool` namespace for modern, span-based image processing. This is the recommended API for new projects.
* **Old Bitmap Library (`src/bitmap`)**: Provides older image processing functions using the `Bitmap::File` class.
* **Bitmap File Handler (`src/bitmapfile`)**: Contains structures and functions for handling BMP file headers and low-level data, used by both APIs.
* **Matrix Library (`src/matrix`)**: A generic matrix manipulation library, primarily used by the old bitmap library.

## New Span-Based API (`BmpTool`)

For more direct memory-based operations and a stable interface, the `BmpTool` API is provided.
For more direct memory-based operations and a stable interface, the `BmpTool` API is provided. This API operates on image data in memory buffers (`std::span`).

The main header for this API is `include/bitmap.hpp`.

Core components include:
* `BmpTool::Bitmap`: A struct holding image dimensions (width, height, bits-per-pixel) and a `std::vector<uint8_t>` for RGBA pixel data.
* `BmpTool::load()`: Loads BMP data from a `std::span<const uint8_t>` into a `BmpTool::Bitmap`.
* `BmpTool::save()`: Saves a `BmpTool::Bitmap` to a `std::span<uint8_t>`.
* `BmpTool::Result<T, E>`: Used for functions that can return a value or an error, with `BmpTool::BitmapError` providing specific error codes.
* `BmpTool::Bitmap`: A struct holding image dimensions (width, height, bits-per-pixel) and a `std::vector<uint8_t>` for pixel data (typically RGBA).
* `BmpTool::load(std::span<const uint8_t> bmp_data)`: Loads BMP data from a memory span into a `BmpTool::Bitmap`.
* `BmpTool::save(const BmpTool::Bitmap& bitmap, std::span<uint8_t> out_bmp_buffer)`: Saves a `BmpTool::Bitmap` to a memory span. The size of the output BMP data can be determined from the `bfSize` field of the `BITMAPFILEHEADER` written to the beginning of the `out_bmp_buffer`.
* `BmpTool::Result<T, BmpTool::BitmapError>`: Used by functions to return either a value `T` or a `BmpTool::BitmapError` enum, indicating the outcome of the operation. For functions that don't return a value on success (like `save`), `BmpTool::Result<void, BmpTool::BitmapError>` is used (internally represented as `Result<Success, BitmapError>`).

All image manipulation functions in the `BmpTool` namespace (e.g., `BmpTool::greyscale`, `BmpTool::shrink`) take a `const BmpTool::Bitmap&` as input and return a `BmpTool::Result<BmpTool::Bitmap, BmpTool::BitmapError>`.

### `BmpTool` API Usage Example

The following example demonstrates loading a BMP file into a buffer, processing it using `BmpTool`, and saving the result. This example is a simplified version of `main.cpp`.

```cpp
#include "include/bitmap.hpp" // Public API
#include "include/bitmap.hpp" // Public API for BmpTool
#include "src/bitmapfile/bitmap_file.h" // For BITMAPFILEHEADER definition (to get actual size)

#include <vector>
#include <fstream> // For reading/writing files to/from buffer for example
#include <iostream>
#include <fstream> // For std::ifstream, std::ofstream
#include <iostream> // For std::cerr, std::cout
#include <cstring> // For std::memcpy if reading header manually

// Helper to read a file into a vector
std::vector<uint8_t> read_file_to_buffer(const std::string& filepath) {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) return {};
if (!file) {
std::cerr << "Failed to open " << filepath << std::endl;
return {};
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(size);
std::vector<uint8_t> buffer(static_cast<size_t>(size));
if (file.read(reinterpret_cast<char*>(buffer.data()), size)) {
return buffer;
}
std::cerr << "Failed to read " << filepath << std::endl;
return {};
}

// Helper to write a buffer to a file
// Helper to write a buffer of a specific size to a file
bool write_buffer_to_file(const std::string& filepath, const std::vector<uint8_t>& buffer, size_t actual_size) {
std::ofstream file(filepath, std::ios::binary);
if (!file) return false;
if (!file) {
std::cerr << "Failed to open " << filepath << " for writing." << std::endl;
return false;
}
file.write(reinterpret_cast<const char*>(buffer.data()), actual_size);
return file.good();
}

int main() {
// Load an existing BMP into a buffer
std::vector<uint8_t> input_bmp_data = read_file_to_buffer("input.bmp");
const std::string input_filename = "input.bmp";
const std::string output_filename = "output_processed.bmp";

// 1. Load an existing BMP into a buffer
std::vector<uint8_t> input_bmp_data = read_file_to_buffer(input_filename);
if (input_bmp_data.empty()) {
std::cerr << "Failed to read input.bmp into buffer." << std::endl;
return 1;
}

// Use BmpTool::load
// 2. Use BmpTool::load to parse the buffer
BmpTool::Result<BmpTool::Bitmap, BmpTool::BitmapError> load_result = BmpTool::load(input_bmp_data);

if (load_result.isError()) {
std::cerr << "BmpTool::load failed: " << static_cast<int>(load_result.error()) << std::endl;
return 1;
}
BmpTool::Bitmap my_bitmap = load_result.value();
std::cout << "Loaded input.bmp into BmpTool::Bitmap: "
std::cout << "Loaded '" << input_filename << "': "
<< my_bitmap.w << "x" << my_bitmap.h << " @ " << my_bitmap.bpp << "bpp" << std::endl;

// (Perform some manipulation on my_bitmap.data if desired)
// For example, invert the red channel for all pixels:
// for (size_t i = 0; i < my_bitmap.data.size(); i += 4) {
// my_bitmap.data[i] = 255 - my_bitmap.data[i]; // Invert Red
// }
// 3. Perform some manipulation (e.g., greyscale)
BmpTool::Result<BmpTool::Bitmap, BmpTool::BitmapError> greyscale_result = BmpTool::greyscale(my_bitmap);
if(greyscale_result.isError()){
std::cerr << "BmpTool::greyscale failed: " << static_cast<int>(greyscale_result.error()) << std::endl;
return 1;
}
my_bitmap = greyscale_result.value(); // Update bitmap with processed one
std::cout << "Applied greyscale." << std::endl;

// Prepare a buffer for saving
// Estimate required size: headers (54) + data (W*H*4)
size_t estimated_output_size = 54 + my_bitmap.w * my_bitmap.h * 4;
std::vector<uint8_t> output_bmp_buffer(estimated_output_size);
// 4. Prepare a buffer for saving. It must be large enough.
// Max possible size: BMP headers (54 bytes) + data (Width * Height * 4 bytes/pixel for 32bpp RGBA)
size_t max_output_size = 54 + my_bitmap.w * my_bitmap.h * 4;
std::vector<uint8_t> output_bmp_buffer(max_output_size);

// Use BmpTool::save
// 5. Use BmpTool::save
BmpTool::Result<void, BmpTool::BitmapError> save_result = BmpTool::save(my_bitmap, output_bmp_buffer);

if (save_result.isError()) { // For Result<void,E>, isError() or checking error() != E::Ok
if (save_result.isError()) {
std::cerr << "BmpTool::save failed: " << static_cast<int>(save_result.error()) << std::endl;
return 1;
}
std::cout << "BmpTool::Bitmap saved to buffer." << std::endl;

// To get the actual size of the BMP written to the buffer (needed for writing to file):
// One way is to read bfSize from the header in output_bmp_buffer
// For example:
// #include "src/bitmapfile/bitmap_file.h" // For BITMAPFILEHEADER definition
// BITMAPFILEHEADER* fh = reinterpret_cast<BITMAPFILEHEADER*>(output_bmp_buffer.data());
// size_t actual_written_size = fh->bfSize;
// This requires including the BITMAPFILEHEADER definition.
// The save function itself doesn't return it, so this is a known aspect of the API.
// For this example, we'll use estimated_output_size, but actual_written_size is more robust.
// A more robust approach would be to parse bfSize or ensure save guarantees fitting within the span and updating its size.
// For this example, we assume the buffer is large enough and we write what's estimated.
// If the actual BMP is smaller, this might write extra uninitialized bytes from the buffer.
// If actual is larger (should not happen with correct estimation and save), it's a problem.
// The best is to parse bfSize from output_bmp_buffer.data().

if (write_buffer_to_file("output_new_api.bmp", output_bmp_buffer, estimated_output_size /* ideally actual_written_size from parsed header */)) {
std::cout << "Output buffer saved to output_new_api.bmp" << std::endl;
std::cout << "BmpTool::Bitmap saved to output buffer." << std::endl;

// 6. Determine actual size of BMP written to buffer and write to file
// The BMP File Header (BITMAPFILEHEADER) is at the start of the buffer.
// Its bfSize field contains the total size of the BMP file.
BITMAPFILEHEADER* fh = reinterpret_cast<BITMAPFILEHEADER*>(output_bmp_buffer.data());
size_t actual_written_size = fh->bfSize;

// Ensure actual_written_size is not greater than our buffer to prevent reading out of bounds.
if (actual_written_size > max_output_size) {
std::cerr << "Error: saved BMP size (" << actual_written_size
<< ") exceeds buffer capacity (" << max_output_size << ")." << std::endl;
return 1;
}

if (write_buffer_to_file(output_filename, output_bmp_buffer, actual_written_size)) {
std::cout << "Processed image saved to '" << output_filename << "'" << std::endl;
} else {
std::cerr << "Failed to write output_new_api.bmp." << std::endl;
std::cerr << "Failed to write output to '" << output_filename << "'." << std::endl;
return 1;
}

return 0;
Expand All @@ -146,56 +170,88 @@ int main() {

## Building the Project

The project uses CMake for building.
The project uses CMake for building. Ensure you have CMake (version 3.10 or newer) and a C++20 compatible compiler installed.

```bash
# Create a build directory
# Create a build directory (out-of-source build is recommended)
mkdir build
cd build

# Configure the project
# Configure the project (from the build directory)
cmake ..

# Build the library (if configured as a library) and executables (like main example and tests)
# Build the library and executables (e.g., testexe, tests)
# On Linux/macOS with Makefiles (default)
make
# or use your specific build system command e.g., mingw32-make
# On Windows with Visual Studio, after cmake .. you might open the .sln or use:
# cmake --build . --config Release
# For MinGW users:
# mingw32-make

# Run tests (if configured)
# Run tests (if configured and built)
# From the build directory
ctest
# or directly run the test executable: ./bitmap_tests (or tests\bitmap_tests.exe on Windows)
# or directly run the test executable (e.g., ./tests/bitmap_tests or build\tests\Debug\bitmap_tests.exe)
```

## Basic Usage Example
## Basic Usage Example (Old API)

The following example demonstrates basic usage of the older, file-based API found in `src/bitmap/bitmap.h` and `src/bitmapfile/bitmap_file.h`.

```cpp
#include "bitmap/bitmap.h" // Adjust path if necessary, assumes src/ is an include dir
#include "bitmap/bitmap.h" // Old API (assumes src/ is an include directory or path adjusted)
#include <iostream>

int main() {
Bitmap::File myBitmap;
Bitmap::File myBitmap; // Uses the old Bitmap::File class

// Load an image
// Load an image directly from a file path
if (!myBitmap.Open("input.bmp")) {
std::cerr << "Error opening input.bmp" << std::endl;
std::cerr << "Error opening input.bmp using old API" << std::endl;
return 1;
}
std::cout << "Loaded input.bmp using old API." << std::endl;

// Apply some manipulations
myBitmap = GreyscaleImage(myBitmap);
myBitmap = ChangeImageBrightness(myBitmap, 1.2f); // Increase brightness by 20%
myBitmap = ApplyBoxBlur(myBitmap, 2); // Apply box blur with radius 2
// Apply some manipulations (old API functions often return a new Bitmap::File)
Bitmap::File processedBitmap = GreyscaleImage(myBitmap);
processedBitmap = ChangeImageBrightness(processedBitmap, 1.2f); // Increase brightness by 20%
processedBitmap = ApplyBoxBlur(processedBitmap, 2); // Apply box blur with radius 2
std::cout << "Applied manipulations using old API." << std::endl;

// Save the result
if (!myBitmap.SaveAs("output.bmp")) {
std::cerr << "Error saving output.bmp" << std::endl;
// Save the result to a new file path
if (!processedBitmap.SaveAs("output_old_api.bmp")) {
std::cerr << "Error saving output_old_api.bmp using old API" << std::endl;
return 1;
}

std::cout << "Image processed and saved as output.bmp" << std::endl;
std::cout << "Image processed and saved as output_old_api.bmp using old API" << std::endl;
return 0;
}
```
Refer to `main.cpp` for more examples.
The `main.cpp` in the root of the repository demonstrates usage of the **new `BmpTool` API**.

## Code Documentation

The public API for `BmpTool` in `include/bitmap.hpp` is documented using Doxygen-style comments.

If Doxygen is installed, you can generate the full HTML documentation by:
1. Installing Doxygen.
2. Creating a Doxyfile configuration (e.g., `doxygen -g Doxyfile` in the project root).
3. Customizing the `Doxyfile` (e.g., set `INPUT` to `include/`, `RECURSIVE` to `YES`).
4. Running `doxygen Doxyfile` from the project root.
The documentation will then be available in the specified output directory (e.g., `html/`).

## Contributing

Contributions to this project are welcome! Please follow these basic guidelines:

1. **Fork the repository:** Create your own fork of the project on GitHub.
2. **Create a branch:** Make a new branch in your fork for your feature or bug fix (e.g., `feature/new-filter` or `fix/brightness-bug`).
3. **Make your changes:** Implement your changes, ensuring code is clear and follows existing style where possible.
4. **Add tests:** If you're adding a new feature or fixing a bug, please add appropriate unit tests in the `tests/` directory. Ensure all tests pass by running `ctest` or the test executable.
5. **Commit your changes:** Make clear, concise commit messages.
6. **Push to your fork:** Push your changes to your branch on your fork.
7. **Submit a Pull Request (PR):** Open a PR from your branch to the main repository's `main` branch. Provide a clear description of your changes in the PR.

## Fuzz Testing

Expand Down
Loading
Loading