CaptureMoment 0.1.0
Free and open-source non-destructive photo editor
Loading...
Searching...
No Matches
image_region.h
Go to the documentation of this file.
1
12#pragma once
13
14#include "pixel_format.h"
15#include "types/image_types.h"
16
17#include <vector>
18#include <cstddef>
19#include <concepts>
20#include <span>
21#include <cassert>
22#include <limits>
23#include <cstdint>
24#include <mdspan>
25namespace CaptureMoment::Core {
26
27namespace Common {
28
48
49 // ============================================================
50 // Dimensions & Meta-data
51 // ============================================================
52
57
62
67
72
77
82
88 std::vector<float> m_data;
89
90 // ============================================================
91 // Constructors
92 // ============================================================
93
98 ImageRegion() = default;
99
105 : m_width(w)
106 , m_height(h)
107 , m_channels(ch)
108 , m_format(fmt)
109 , m_data(static_cast<std::size_t>(w) * h * ch) // Safe multiplication
110 {}
111
112
137 ImageRegion(std::vector<float>&& data, ImageDim w, ImageDim h, ImageChan ch,
139 : m_x(x)
140 , m_y(y)
141 , m_width(w)
142 , m_height(h)
143 , m_channels(ch)
144 , m_format(fmt)
145 , m_data(std::move(data))
146 {}
147
168 ImageRegion(std::span<const float> data_span, ImageDim w, ImageDim h, ImageChan ch,
170 : m_x(x)
171 , m_y(y)
172 , m_width(w)
173 , m_height(h)
174 , m_channels(ch)
175 , m_format(fmt)
176 , m_data(data_span.begin(), data_span.end())
177 {}
178
179 // ============================================================
180 // Accessors & Utilities
181 // ============================================================
182
183 /*
184 * @brief Accessor for width.
185 * @return The width of the image region in pixels.
186 */
187 [[nodiscard]] constexpr ImageDim width() const noexcept { return m_width; }
188
189 /*
190 * @brief Accessor for height.
191 * @return The height of the image region in pixels.
192 */
193 [[nodiscard]] constexpr ImageDim height() const noexcept { return m_height; }
194
195 /*
196 * @brief Accessor for channels.
197 * @return The number of color channels per pixel.
198 */
199 [[nodiscard]] constexpr ImageChan channels() const noexcept { return m_channels; }
200
201 /*
202 * @brief Accessor for pixel format.
203 * @return The pixel format of the image region.
204 */
205 [[nodiscard]] constexpr PixelFormat format() const noexcept { return m_format; }
206
207 /*
208 * @brief Accessor for x-coordinate.
209 * @return The x-coordinate of the image region in the source image.
210 */
211 [[nodiscard]] constexpr ImageCoord x() const noexcept { return m_x; }
212
213 /*
214 * @brief Accessor for y-coordinate.
215 * @return The y-coordinate of the image region in the source image.
216 */
217 [[nodiscard]] constexpr ImageCoord y() const noexcept { return m_y; }
218
229 [[nodiscard]] constexpr bool isValid() const noexcept
230 {
231 // 1. Basic sanity checks
232 if (m_width == 0 || m_height == 0 || m_channels == 0) {
233 return false;
234 }
235
236 // Optional: Reject unreasonable channel counts (e.g. > 8 for typical RGB/CMYK)
237 // Prevents logic errors where dimensions are small but channels are huge causing overflow.
238 if (m_channels > 8) {
239 return false;
240 }
241
242 // 2. Safe Calculation Helper (Prevents Overflow)
243 // Using a lambda to keep the logic local and constexpr-friendly.
244 auto safe_multiply = [](std::size_t a, std::size_t b, std::size_t& out_result) constexpr -> bool {
245 if (a != 0 && b > std::numeric_limits<std::size_t>::max() / a) {
246 // b > MAX / a implies a * b > MAX (Integer Overflow)
247 return false;
248 }
249 out_result = a * b;
250 return true;
251 };
252
253 // 3. Calculate pixel count safely (width * height)
254 std::size_t pixel_count = 0;
255 if (!safe_multiply(m_width, m_height, pixel_count)) {
256 return false; // Overflow detected in width * height
257 }
258
259 // 4. Calculate total elements safely (pixel_count * channels)
260 std::size_t expected_size = 0;
261 if (!safe_multiply(pixel_count, m_channels, expected_size)) {
262 return false; // Overflow detected in total elements
263 }
264
265 // 5. Consistency Check
266 // Ensure the vector holds exactly the amount of data expected.
267 return m_data.size() == expected_size;
268 }
269
273 [[nodiscard]] constexpr ImageSize sizeInBytes() const noexcept {
274 return m_data.size() * sizeof(float);
275 }
276
285 [[nodiscard]] constexpr ImageSize getDataSize() const noexcept {
286 return m_data.size();
287 }
288
292 template <typename Self>
293 [[nodiscard]] constexpr auto getBuffer(this Self&& self) noexcept {
294 return std::span{self.m_data};
295 }
296
305 template <typename Self>
306 [[nodiscard]] auto& operator()(this Self&& self, std::size_t y, std::size_t x, std::size_t c) noexcept {
307 assert(y < self.m_height);
308 assert(x < self.m_width);
309 assert(c < self.m_channels);
310 const std::size_t idx = (y * self.m_width + x) * self.m_channels + c;
311 return self.m_data[idx];
312 }
313
320 template <typename Self>
321 [[nodiscard]] auto getMdSpan(this Self&& self) noexcept {
322 using ValueType = std::remove_reference_t<decltype(self.m_data[0])>;
323 return std::mdspan<ValueType, std::dextents<std::size_t, 3>>(
324 self.m_data.data(), self.m_height, self.m_width, self.m_channels
325 );
326 }
327};
328
329// ============================================================
330// C++23 Concepts
331// ============================================================
332
342template<typename T>
343concept ImageLike = requires(const T& t)
344{
345 // Must have dimensions
346 { t.m_width } -> std::convertible_to<Common::ImageDim>;
347 { t.m_height } -> std::convertible_to<Common::ImageDim>;
348 { t.m_channels } -> std::convertible_to<Common::ImageChan>;
349
350 // Must have a validity check
351 { t.isValid() } -> std::same_as<bool>;
352
353 // Must support read-only access via getBuffer() or similar
354 // (We check if it provides a view into contiguous float data)
355 { t.getBuffer() } -> std::convertible_to<std::span<const float>>;
356};
357
362template<typename T>
363concept MutableImageLike = ImageLike<T> && requires(T& t)
364{
365 { t.getBuffer() } -> std::convertible_to<std::span<float>>;
366};
367
368} // namespace Common
369
370} // namespace CaptureMoment::Core
A concept that constrains a type to behave like an image container.
Extends ImageLike to require read/write access.
Common type definitions for all image types in CaptureMoment.
std::size_t ImageSize
Type for data size (element count).
Definition image_types.h:38
std::uint8_t ImageChan
Type for channel count.
Definition image_types.h:26
std::int32_t ImageCoord
Type for pixel coordinates (x, y).
Definition image_types.h:32
PixelFormat
Enumerates the supported pixel storage formats in memory.
@ RGBA_F32
4-channel format: Red, Green, Blue, Alpha in 32-bit float.
std::size_t ImageDim
Type for image dimensions (width, height).
Definition image_types.h:20
Manages the lifecycle of a single IWorkingImage instance.
Defines the pixel storage formats used throughout the CaptureMoment Core library.
Represents a rectangular region of an image and its pixel data.
constexpr ImageCoord x() const noexcept
constexpr ImageDim height() const noexcept
ImageRegion()=default
Default constructor. Creates an empty, invalid ImageRegion.
constexpr ImageChan channels() const noexcept
constexpr ImageCoord y() const noexcept
constexpr PixelFormat format() const noexcept
ImageDim m_width
Width of this image region in pixels.
ImageDim m_height
Height of this image region in pixels.
constexpr ImageDim width() const noexcept
constexpr ImageSize getDataSize() const noexcept
Returns the total number of float elements in the pixel data buffer.
constexpr auto getBuffer(this Self &&self) noexcept
Provides read-only access to the pixel data buffer as a std::span.
std::vector< float > m_data
Pixel data (row-major layout).
ImageRegion(ImageDim w, ImageDim h, ImageChan ch, PixelFormat fmt=PixelFormat::RGBA_F32)
Constructs an ImageRegion and allocates uninitialized memory.
ImageRegion(std::span< const float > data_span, ImageDim w, ImageDim h, ImageChan ch, ImageCoord x=0, ImageCoord y=0, PixelFormat fmt=PixelFormat::RGBA_F32)
Constructs an ImageRegion by copying data from a span (Deep Copy).
ImageCoord m_x
X-coordinate of the top-left corner of this region in the full source image.
ImageCoord m_y
Y-coordinate of the top-left corner of this region in the full source image.
ImageRegion(std::vector< float > &&data, ImageDim w, ImageDim h, ImageChan ch, ImageCoord x=0, ImageCoord y=0, PixelFormat fmt=PixelFormat::RGBA_F32)
Constructs an ImageRegion by moving existing pixel data (Zero-Copy).
auto & operator()(this Self &&self, std::size_t y, std::size_t x, std::size_t c) noexcept
Provides unchecked access to a specific pixel's channel value.
auto getMdSpan(this Self &&self) noexcept
Returns std::mdspan (3D: Height, Width, Channels).
PixelFormat m_format
Format specifying how pixels are stored.
constexpr bool isValid() const noexcept
Validates the integrity of the ImageRegion (Overflow-Safe Version).
ImageChan m_channels
Number of color channels per pixel.
constexpr ImageSize sizeInBytes() const noexcept
Calculates the total size in bytes of the pixel data buffer.
CaptureMoment — footer