Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
image_data_tests.cpp
Go to the documentation of this file.
1// This file is part of the Rendering Engine project.
2// Author: Alexander Obzherin <alexanderobzherin@gmail.com>
3// Copyright (c) 2026 Alexander Obzherin
4// Distributed under the terms of the zlib License. See LICENSE.md for details.
5
6#include "image_data.hpp"
7#include <gtest/gtest.h>
8#include <limits>
9#include <stdexcept>
10
11using namespace rendering_engine;
12
13TEST(Color, ConstructorsAndEqualityIncludeEveryChannel)
14{
15 const Color black;
16 EXPECT_EQ(black.r, 0);
17 EXPECT_EQ(black.g, 0);
18 EXPECT_EQ(black.b, 0);
19 EXPECT_EQ(black.a, 255);
20 const Color rgb(12, 34, 56);
21 EXPECT_EQ(rgb.r, 12);
22 EXPECT_EQ(rgb.g, 34);
23 EXPECT_EQ(rgb.b, 56);
24 EXPECT_EQ(rgb.a, 255);
25 const Color rgba(12, 34, 56, 78);
26 EXPECT_EQ(rgba.r, 12);
27 EXPECT_EQ(rgba.g, 34);
28 EXPECT_EQ(rgba.b, 56);
29 EXPECT_EQ(rgba.a, 78);
30 EXPECT_TRUE(rgba == Color(12, 34, 56, 78));
31 EXPECT_FALSE(rgba == Color(13, 34, 56, 78));
32 EXPECT_FALSE(rgba == Color(12, 35, 56, 78));
33 EXPECT_FALSE(rgba == Color(12, 34, 57, 78));
34 EXPECT_FALSE(rgba == rgb);
35}
36
37TEST(ImageData, EmptyImageHasNoPixelsOrBytes)
38{
39 ImageData image;
40 EXPECT_EQ(image.GetWidth(), 0U);
41 EXPECT_EQ(image.GetHeight(), 0U);
42 EXPECT_EQ(image.GetSizeInBytes(), 0U);
43 EXPECT_TRUE(image.GetImageDataRGBA().empty());
44 EXPECT_TRUE(image.GetImageDataRGB().empty());
45}
46
47TEST(ImageData, SizedImageInitializesPixelsAndFillReplacesAllChannels)
48{
49 ImageData image(3, 2);
50 EXPECT_EQ(image.GetWidth(), 3U);
51 EXPECT_EQ(image.GetHeight(), 2U);
52 EXPECT_EQ(image.GetSizeInBytes(), 24U);
53 for (unsigned y = 0; y < 2; ++y)
54 for (unsigned x = 0; x < 3; ++x)
55 EXPECT_EQ(image.GetPixel(x, y), Color(0, 0, 0, 255));
56 const Color fill(10, 20, 30, 40);
57 image.Fill(fill);
58 for (unsigned y = 0; y < 2; ++y)
59 for (unsigned x = 0; x < 3; ++x)
60 EXPECT_EQ(image.GetPixel(x, y), fill);
61}
62
63TEST(ImageData, PixelWritesAreIsolatedAndBoundsAreChecked)
64{
65 ImageData image(3, 2);
66 const Color background(1, 2, 3, 4), foreground(5, 6, 7, 8);
67 image.Fill(background);
68 image.SetPixel(1, 1, foreground);
69 for (unsigned y = 0; y < 2; ++y)
70 for (unsigned x = 0; x < 3; ++x)
71 EXPECT_EQ(image.GetPixel(x, y), (x == 1 && y == 1) ? foreground : background);
72 const auto before = image.GetImageDataRGBA();
73 image.SetPixel(3, 0, foreground);
74 image.SetPixel(0, 2, foreground);
75 image.SetPixel(std::numeric_limits<unsigned>::max(), 0, foreground);
76 EXPECT_EQ(image.GetImageDataRGBA(), before);
77 EXPECT_EQ(image.GetPixel(3, 0), Color());
78 EXPECT_EQ(image.GetPixel(0, 2), Color());
79}
80
81TEST(ImageData, RawRGBAAndRGBPreserveRowAndChannelOrder)
82{
83 const std::vector<std::uint8_t> rgba = {1, 2, 3, 4, 5, 6, 7, 8,
84 9, 10, 11, 12, 13, 14, 15, 16};
85 const ImageData image(2, 2, rgba);
86 EXPECT_EQ(image.GetWidth(), 2U);
87 EXPECT_EQ(image.GetHeight(), 2U);
88 EXPECT_EQ(image.GetSizeInBytes(), 16U);
89 EXPECT_EQ(image.GetPixel(0, 0), Color(1, 2, 3, 4));
90 EXPECT_EQ(image.GetPixel(1, 0), Color(5, 6, 7, 8));
91 EXPECT_EQ(image.GetPixel(0, 1), Color(9, 10, 11, 12));
92 EXPECT_EQ(image.GetPixel(1, 1), Color(13, 14, 15, 16));
93 EXPECT_EQ(image.GetImageDataRGBA(), rgba);
94 const std::vector<std::uint8_t> rgb = {1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15};
95 EXPECT_EQ(image.GetImageDataRGB(), rgb);
96 EXPECT_EQ(image.GetImageDataRGB().size(), 2U * 2U * 3U);
97}
98
99TEST(ImageData, RawRGBARejectsBothShortAndLongBuffers)
100{
101 EXPECT_THROW((ImageData(2, 2, std::vector<std::uint8_t>(15))), std::runtime_error);
102 EXPECT_THROW((ImageData(2, 2, std::vector<std::uint8_t>(17))), std::runtime_error);
103}
104
105TEST(ImageData, CopyConstructionOwnsIndependentStorage)
106{
107 ImageData original(2, 3);
108 original.Fill(Color(1, 2, 3, 4));
109 ImageData copy(original);
110 EXPECT_EQ(copy.GetWidth(), 2U);
111 EXPECT_EQ(copy.GetHeight(), 3U);
112 EXPECT_EQ(copy.GetImageDataRGBA(), original.GetImageDataRGBA());
113 copy.SetPixel(1, 2, Color(9, 8, 7, 6));
114 EXPECT_EQ(copy.GetPixel(1, 2), Color(9, 8, 7, 6));
115 EXPECT_EQ(original.GetPixel(1, 2), Color(1, 2, 3, 4));
116}
117
118TEST(ImageData, CopyAssignmentResizesAndOwnsIndependentStorage)
119{
120 ImageData original(2, 3), copy(1, 1);
121 original.Fill(Color(1, 2, 3, 4));
122 EXPECT_EQ(&(copy = original), &copy);
123 EXPECT_EQ(copy.GetWidth(), 2U);
124 EXPECT_EQ(copy.GetHeight(), 3U);
125 EXPECT_EQ(copy.GetImageDataRGBA(), original.GetImageDataRGBA());
126 copy.SetPixel(1, 2, Color(9, 8, 7, 6));
127 EXPECT_EQ(original.GetPixel(1, 2), Color(1, 2, 3, 4));
128 const auto before = copy.GetImageDataRGBA();
129 const ImageData& same = copy;
130 copy = same;
131 EXPECT_EQ(copy.GetImageDataRGBA(), before);
132}
133
134namespace
135{
136struct OverlayCase
137{
138 unsigned x;
139 unsigned y;
140 const char* name;
141};
142
143class ImageOverlay : public ::testing::TestWithParam<OverlayCase>
144{
145
146};
147}
148
149TEST_P(ImageOverlay, CopiesSourcePixelsAndPreservesEverythingOutsideIntersection)
150{
151 ImageData destination(3, 3);
152 const Color background(90, 80, 70, 60);
153 destination.Fill(background);
154 ImageData source(2, 2, std::vector<std::uint8_t>{
155 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
156 const auto sourceBefore = source.GetImageDataRGBA();
157 const auto placement = GetParam();
158 ImageData::DrawImageOnImageAtPos(placement.x, placement.y, destination, source);
159 for (unsigned y = 0; y < 3; ++y)
160 {
161 for (unsigned x = 0; x < 3; ++x)
162 {
163 SCOPED_TRACE(::testing::Message() << "pixel " << x << ", " << y);
164 const bool covered = x >= placement.x && y >= placement.y &&
165 x - placement.x < 2 && y - placement.y < 2;
166 EXPECT_EQ(destination.GetPixel(x, y), covered ?
167 source.GetPixel(x - placement.x, y - placement.y) : background);
168 }
169 }
170 EXPECT_EQ(source.GetImageDataRGBA(), sourceBefore);
171}
172
173INSTANTIATE_TEST_SUITE_P(Placements, ImageOverlay, ::testing::Values(
174 OverlayCase{0, 0, "Origin"}, OverlayCase{1, 1, "Offset"},
175 OverlayCase{2, 0, "RightClip"}, OverlayCase{0, 2, "BottomClip"},
176 OverlayCase{2, 2, "BothClip"}, OverlayCase{3, 0, "OutsideRight"},
177 OverlayCase{0, 3, "OutsideBottom"},
178 OverlayCase{std::numeric_limits<unsigned>::max(), 0, "FarOutside"}),
179 [](const ::testing::TestParamInfo<OverlayCase>& info) { return info.param.name; });
180
181TEST(ImageData, ZeroSizedOverlaysAreNoOps)
182{
183 ImageData destination(2, 2);
184 destination.Fill(Color(1, 2, 3, 4));
185 const auto before = destination.GetImageDataRGBA();
186 ImageData empty, zeroWidth(0, 2), zeroHeight(2, 0);
187 for (ImageData* source : {&empty, &zeroWidth, &zeroHeight})
188 {
189 ImageData::DrawImageOnImageAtPos(0, 0, destination, *source);
190 EXPECT_EQ(destination.GetImageDataRGBA(), before);
191 ImageData::DrawImageOnImageAtPos(0, 0, *source, destination);
192 EXPECT_TRUE(source->GetImageDataRGBA().empty());
193 EXPECT_TRUE(source->GetImageDataRGB().empty());
194 EXPECT_EQ(source->GetSizeInBytes(), 0U);
195 }
196}
197
Represents raw 2D image data stored in memory.
void SetPixel(unsigned int x, unsigned int y, Color color)
Sets the color of a specific pixel.
unsigned int GetWidth() const
Returns the image width.
std::vector< uint8_t > GetImageDataRGB() const
Gets raw image data in RGB format.
static void DrawImageOnImageAtPos(unsigned int const x, unsigned int const y, ImageData &toImage, ImageData &fromImage)
Overlays one image on top of another at a given position.
void Fill(Color color)
Fills the image with a solid color.
std::vector< uint8_t > GetImageDataRGBA() const
Gets raw image data in RGBA format.
unsigned int GetHeight() const
Returns the image height.
size_t GetSizeInBytes() const
Gets the total size of the image in memory.
const Color GetPixel(unsigned int x, unsigned int y) const
Retrieves the color of a specific pixel.
INSTANTIATE_TEST_SUITE_P(Placements, ImageOverlay, ::testing::Values(OverlayCase{0, 0, "Origin"}, OverlayCase{1, 1, "Offset"}, OverlayCase{2, 0, "RightClip"}, OverlayCase{0, 2, "BottomClip"}, OverlayCase{2, 2, "BothClip"}, OverlayCase{3, 0, "OutsideRight"}, OverlayCase{0, 3, "OutsideBottom"}, OverlayCase{std::numeric_limits< unsigned >::max(), 0, "FarOutside"}), [](const ::testing::TestParamInfo< OverlayCase > &info) { return info.param.name;})
TEST(Color, ConstructorsAndEqualityIncludeEveryChannel)
TEST_P(ImageOverlay, CopiesSourcePixelsAndPreservesEverythingOutsideIntersection)
Represents a color with red, green, blue, and alpha channels.