Skip to content

Commit

Permalink
const_image_span::subspan()
Browse files Browse the repository at this point in the history
  • Loading branch information
igagis committed Oct 31, 2024
1 parent dfa6a11 commit ded428a
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 13 deletions.
17 changes: 14 additions & 3 deletions src/rasterimage/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class image : public dimensioned
public:
static const size_t num_channels = number_of_channels;

using image_span_type = image_span<channel_type, num_channels>;
using const_image_span_type = const_image_span<channel_type, num_channels>;
using pixel_type = r4::vector<channel_type, num_channels>;
using value_type = typename pixel_type::value_type;

Expand Down Expand Up @@ -91,14 +93,14 @@ class image : public dimensioned
});
}

image_span<channel_type, number_of_channels> span() noexcept
image_span_type span() noexcept
{
return *this;
}

image_span<const channel_type, number_of_channels> span() const noexcept
const_image_span_type span() const noexcept
{
return *this;
return const_image_span_type(*this);
}

bool empty() const noexcept
Expand Down Expand Up @@ -173,4 +175,13 @@ image_span<channel_type, number_of_channels, is_const_span>::image_span(image<ch
buffer(&im.pixels().front())
{}

template <typename channel_type, size_t number_of_channels, bool is_const_span>
image_span<channel_type, number_of_channels, is_const_span>::image_span(
const image<channel_type, number_of_channels>& im
) :
dimensioned(im.dims()),
stride_px(im.dims().x()),
buffer(&im.pixels().front())
{}

} // namespace rasterimage
42 changes: 32 additions & 10 deletions src/rasterimage/image_span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,19 @@ class image_span : public dimensioned
{}

public:
using iterator = iterator_internal<false>;
using iterator = iterator_internal<is_const_span>;
using const_iterator = iterator_internal<true>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;

image_span(image<channel_type, number_of_channels>& img);

/**
* @brief Conversion constructor from image.
* Constructor for automatic conversion to const_image_span or span of another convertible channel type.
*/
image_span(const image<channel_type, number_of_channels>& img);

/**
* @brief Conversion constructor.
* Constructor for automatic conversion to const_image_span or span of another convertible channel type.
Expand All @@ -264,22 +270,22 @@ class image_span : public dimensioned
)
{}

pixel_type* data()
pixel_type* data() noexcept
{
return this->buffer;
}

const pixel_type* data() const
const pixel_type* data() const noexcept
{
return this->buffer;
}

unsigned stride_pixels() const
unsigned stride_pixels() const noexcept
{
return this->stride_px;
}

size_t stride_bytes() const
size_t stride_bytes() const noexcept
{
return this->stride_pixels() * sizeof(pixel_type);
}
Expand Down Expand Up @@ -339,24 +345,35 @@ class image_span : public dimensioned
return *utki::next(this->begin(), line_index);
}

image_span subspan(r4::rectangle<uint32_t> rect)
image_span subspan(r4::rectangle<uint32_t> rect) noexcept
{
ASSERT(r4::rectangle<uint32_t>({0, 0}, this->dims()).contains(rect), [&](auto& o) {
o << "requested subspan is out of the span, this->dims() = " << this->dims() << ", rect = " << rect;
})

image_span
ret( //
return image_span( //
rect.d,
this->stride_px,
&(*this)[rect.p.y()][rect.p.x()]
);
}

const_image_span_type subspan(r4::rectangle<uint32_t> rect) const noexcept
{
ASSERT(r4::rectangle<uint32_t>({0, 0}, this->dims()).contains(rect), [&](auto& o) {
o << "requested subspan is out of the span, this->dims() = " << this->dims() << ", rect = " << rect;
})

return ret;
return const_image_span_type( //
rect.d,
this->stride_px,
&(*this)[rect.p.y()][rect.p.x()]
);
}

void clear(pixel_type val)
void clear(pixel_type val) noexcept
{
static_assert(!is_const_span, "image_span is const, cannot clear");
for (auto l : *this) {
for (auto& p : l) {
p = val;
Expand All @@ -366,6 +383,8 @@ class image_span : public dimensioned

void swap_red_blue() noexcept
{
static_assert(!is_const_span, "image_span is const, cannot swap red and blue");

using std::swap;
for (auto l : *this) {
for (auto& p : l) {
Expand All @@ -376,6 +395,7 @@ class image_span : public dimensioned

void unpremultiply_alpha() noexcept
{
static_assert(!is_const_span, "image_span is const, cannot unpremultiply alpha");
for (auto l : *this) {
for (auto& p : l) {
p = rasterimage::unpremultiply_alpha(p);
Expand All @@ -385,6 +405,8 @@ class image_span : public dimensioned

void flip_vertical() noexcept
{
static_assert(!is_const_span, "image_span is const, cannot flip vetical");

// NOTE: the std::prev(this->end()) cannot be used here because iterator_category is std::input_iterator_tag,
// while std::prev() requires at least std::bidirectional_iterator_tag.
for (auto upper = this->begin(), lower = --this->end(); upper < lower; ++upper, --lower) {
Expand Down
45 changes: 45 additions & 0 deletions tests/unit/src/image_span.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@

namespace {
const tst::set set("image_span", [](tst::suite& suite) {
suite.add("constructor_from_image", []() {
rasterimage::image<uint8_t, 4> img(rasterimage::dimensioned::dimensions_type{100, 200});
img.span().clear(10);

decltype(img)::image_span_type im(img);
tst::check_eq(im[0][0], decltype(im)::pixel_type{10, 10, 10, 10}, SL);
});

suite.add("constructor_from_const_image", []() {
rasterimage::image<uint8_t, 4> img(rasterimage::dimensioned::dimensions_type{100, 200});
img.span().clear(10);

const auto& cimg = img;

decltype(img)::const_image_span_type im(cimg);
tst::check_eq(im[0][0], decltype(im)::pixel_type{10, 10, 10, 10}, SL);
});

suite.add("clear", []() {
rasterimage::image<uint8_t, 4> img(rasterimage::dimensioned::dimensions_type{100, 200});

Expand Down Expand Up @@ -374,6 +392,33 @@ const tst::set set("image_span", [](tst::suite& suite) {
tst::check_eq(im[5][3], expected1, SL);
});

suite.add("subspan__rectangle_const", []() {
rasterimage::image<uint8_t, 4> img(rasterimage::dimensioned::dimensions_type{100, 200});

auto im = img.span();

decltype(im)::pixel_type expected1 = {10, 20, 30, 40};
im.clear(expected1);

auto subim = im.subspan({1, 2, 2, 3});

decltype(im)::pixel_type expected2(0);
subim.clear(expected2);

const auto& cimg = img;
auto cim = cimg.span();
auto csubim = cim.subspan({1, 2, 2, 3});

tst::check_eq(csubim[0][0], expected2, SL);
tst::check_eq(csubim[0][1], expected2, SL);

tst::check_eq(csubim[1][0], expected2, SL);
tst::check_eq(csubim[1][1], expected2, SL);

tst::check_eq(csubim[2][0], expected2, SL);
tst::check_eq(csubim[2][1], expected2, SL);
});

suite.add<unsigned>("flip_vertical", {1, 2, 3, 4, 5, 10, 13}, [](const auto& p) {
rasterimage::image<int, 4> img(rasterimage::dimensioned::dimensions_type{2, p});

Expand Down

0 comments on commit ded428a

Please sign in to comment.