Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
image_data.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Alexander Obzherin � Licensed under the zlib License. See LICENSE.md.
2
3#include "image_data.hpp"
5#include "image_codec_png.hpp"
6#include "logger.hpp"
7#include <filesystem>
8#include <stdexcept>
9
10namespace rendering_engine
11{
13 :
14 ImageData::ImageData(0U, 0U)
15{
16}
17
18ImageData::ImageData( unsigned int width, unsigned int height )
19 :
20 mWidth( width ),
21 mHeight( height )
22{
23 AllocateMemory( mWidth, mHeight );
24}
25
26ImageData::ImageData(std::string filepath)
27 :
29{
30 auto const pathToTexture = std::filesystem::path(filepath);
31 if(std::filesystem::exists(pathToTexture) && std::filesystem::is_regular_file(pathToTexture) )
32 {
33 size_t const dot = pathToTexture.string().find_last_of(".");
34 std::string const fileExtension = pathToTexture.string().substr(dot + 1);
35
36 if( std::string{ "jpg" } == fileExtension )
37 {
38 LoadTextureJpegFile(pathToTexture.string().c_str());
39 }
40 else
41 {
42 if( std::string{ "png" } == fileExtension )
43 {
44 LoadTexturePngFile(pathToTexture.string().c_str());
45 }
46 else
47 {
48 throw std::runtime_error("Unsupported texture file format!");
49 }
50 }
51 }
52 else
53 {
54 throw std::runtime_error("Path to texture file is incorrect.");
55 }
56}
57
58ImageData::ImageData(std::vector<uint8_t> const& fileBytes)
59{
60 if (fileBytes.size() < 4)
61 throw std::runtime_error("Invalid image buffer");
62
63 // PNG signature
64 if (fileBytes[0] == 0x89 &&
65 fileBytes[1] == 'P' &&
66 fileBytes[2] == 'N' &&
67 fileBytes[3] == 'G')
68 {
69 std::vector<std::uint8_t> pixelsRGBA;
70
71 if (!ReadPngFromMemory(fileBytes.data(), fileBytes.size(), mWidth, mHeight, pixelsRGBA))
72 throw std::runtime_error("PNG memory decode failed");
73
74 AllocateMemory(mWidth, mHeight);
75 Fill(Color(0, 0, 0, 255));
76 LoadImageDataRGBA(pixelsRGBA);
77 return;
78 }
79
80 // JPEG signature: FF D8 FF
81 if (fileBytes[0] == 0xFF &&
82 fileBytes[1] == 0xD8 &&
83 fileBytes[2] == 0xFF)
84 {
85 std::vector<std::uint8_t> pixelsRGB;
86
87 if (!ReadJpegFromMemory(fileBytes.data(), fileBytes.size(), mWidth, mHeight, pixelsRGB))
88 throw std::runtime_error("JPEG memory decode failed");
89
90 AllocateMemory(mWidth, mHeight);
91 LoadImageDataRGB(pixelsRGB);
92
93 return;
94 }
95
96 throw std::runtime_error("Unsupported image format in memory buffer");
97}
98
99ImageData::ImageData(unsigned int width, unsigned int height, std::vector<std::uint8_t> const& pixelsRGBA)
100 :
101ImageData::ImageData(width, height)
102{
103 Fill(Color(255, 255, 255, 255));
104 LoadImageDataRGBA( pixelsRGBA );
105}
106
111
113 :
114 mWidth(src.mWidth),
115 mHeight(src.mHeight),
116 mData(src.mData)
117{
118}
119
121{
122 if (this == &rhs)
123 {
124 return *this;
125 }
126
127 mWidth = rhs.mWidth;
128 mHeight = rhs.mHeight;
129 mData = rhs.mData;
130 return *this;
131}
132
134{
135 std::fill(mData.begin(), mData.end(), color);
136}
137
138void ImageData::SetPixel( unsigned int x, unsigned int y, Color color )
139{
140 if (x < mWidth && y < mHeight)
141 {
142 PixelRef(x, y) = color;
143 }
144}
145
146const Color ImageData::GetPixel( unsigned int x, unsigned int y ) const
147{
148 if (x >= mWidth || y >= mHeight)
149 {
150 LOG_ERROR("Out of bounds access in GetPixel");
151 return Color{};
152 }
153
154 return PixelRef(x, y);
155}
156
157void ImageData::DrawImageOnImageAtPos(unsigned int const x, unsigned int const y, ImageData& toImage, ImageData& fromImage)
158{
159 // If fromImage can not be placed entirely, it will be cropped on right and bottom side
160
161 // Source image draw from top left pixel
162
163 // At first check if point pos is inside toImage
164 if (x >= toImage.GetWidth() || y >= toImage.GetHeight())
165 {
166 return;
167 }
168
169 if (toImage.GetWidth() == 0U || toImage.GetHeight() == 0U ||
170 fromImage.GetWidth() == 0U || fromImage.GetHeight() == 0U)
171 {
172 return;
173 }
174
175 // Crop
176 unsigned int left;
177 if ((toImage.GetWidth() - x) < fromImage.GetWidth())
178 {
179 left = toImage.GetWidth() - x;
180 }
181 else
182 {
183 left = fromImage.GetWidth();
184 }
185
186 unsigned int bottom;
187 if ((toImage.GetHeight() - y) < fromImage.GetHeight())
188 {
189 bottom = toImage.GetHeight() - y;
190 }
191 else
192 {
193 bottom = fromImage.GetHeight();
194 }
195
196 if (left == 0U || bottom == 0U)
197 {
198 return;
199 }
200
201 for (unsigned int deltaY = 0; deltaY < bottom; ++deltaY)
202 {
203 const size_t srcRowOffset =
204 static_cast<size_t>(deltaY) * static_cast<size_t>(fromImage.GetWidth());
205
206 const size_t dstRowOffset =
207 static_cast<size_t>(y + deltaY) * static_cast<size_t>(toImage.GetWidth()) +
208 static_cast<size_t>(x);
209
210 std::memcpy(
211 toImage.mData.data() + dstRowOffset,
212 fromImage.mData.data() + srcRowOffset,
213 static_cast<size_t>(left) * sizeof(Color));
214 }
215}
216
217std::vector<uint8_t> ImageData::GetImageDataRGBA() const
218{
219 if (mWidth == 0 || mHeight == 0)
220 {
221 return {};
222 }
223
224 const size_t totalBytes =
225 static_cast<size_t>(mWidth) * static_cast<size_t>(mHeight) * 4;
226
227 std::vector<uint8_t> result(totalBytes);
228 std::memcpy(result.data(), mData.data(), totalBytes);
229
230 return result;
231}
232
233std::vector<uint8_t> ImageData::GetImageDataRGB() const
234{
235 if (mWidth == 0 || mHeight == 0)
236 {
237 return {};
238 }
239
240 const size_t pixelCount =
241 static_cast<size_t>(mWidth) * static_cast<size_t>(mHeight);
242
243 std::vector<uint8_t> result(pixelCount * 3);
244
245 const Color* src = mData.data();
246 uint8_t* dst = result.data();
247
248 for (size_t i = 0; i < pixelCount; ++i)
249 {
250 dst[i * 3 + 0] = src[i].r;
251 dst[i * 3 + 1] = src[i].g;
252 dst[i * 3 + 2] = src[i].b;
253 }
254
255 return result;
256}
257
258void ImageData::WriteJpegFile(char const* filename)
259{
260 SaveTextureFileJpeg(*this, filename);
261}
262
263bool ImageData::LoadTextureJpegFile(char const* filename)
264{
266 unsigned int width = 0;
267 unsigned int height = 0;
268 std::vector<std::uint8_t> rgbImageDataVector;
269
270 bool result = ReadJpegFile(filename, width, height, rgbImageDataVector);
271 const size_t expectedSize =
272 static_cast<size_t>(width) * static_cast<size_t>(height) * 3U;
273
274 if (result && rgbImageDataVector.size() == expectedSize)
275 {
276 mWidth = width;
277 mHeight = height;
278 AllocateMemory(width, height);
279 LoadImageDataRGB(rgbImageDataVector);
280 }
281
282 return result;
283}
284
285void ImageData::WritePngFile(char const* filename)
286{
287 SaveTextureFilePng(*this, filename);
288}
289
290bool ImageData::LoadTexturePngFile(char const* filename)
291{
293 unsigned int width = 0;
294 unsigned int height = 0;
295 std::vector<std::uint8_t> rgbaImageDataVector;
296
297 bool result = ReadPngFile(filename, width, height, rgbaImageDataVector);
298
299 const size_t expectedSize = static_cast<size_t>(width) * static_cast<size_t>(height) * 4U;
300
301 if (result && rgbaImageDataVector.size() == expectedSize)
302 {
303 mWidth = width;
304 mHeight = height;
305 AllocateMemory(width, height);
306 LoadImageDataRGBA(rgbaImageDataVector);
307 }
308 return true;
309}
310
311void ImageData::AllocateMemory( unsigned int width, unsigned int height )
312{
313 mWidth = width;
314 mHeight = height;
315 mData.resize(static_cast<size_t>(mWidth) * static_cast<size_t>(mHeight));
316}
317
319{
320 mData.clear();
321
322 mWidth = 0;
323 mHeight = 0;
324}
325
326void ImageData::LoadImageDataRGBA(const std::vector<uint8_t>& pixels)
327{
328 const size_t pixelCount = static_cast<size_t>(mWidth) * static_cast<size_t>(mHeight);
329
330 const size_t expectedSize = pixelCount * 4U;
331
332 if (pixels.size() != expectedSize)
333 {
334 throw std::runtime_error("Array data size doesn't match image dimension.");
335 }
336
337 mData.resize(pixelCount);
338
339 std::memcpy(mData.data(), pixels.data(), expectedSize);
340}
341
342void ImageData::LoadImageDataRGB(std::vector<std::uint8_t> const& pixels)
343{
344 const size_t pixelCount =
345 static_cast<size_t>(mWidth) * static_cast<size_t>(mHeight);
346
347 const size_t expectedSize = pixelCount * 3;
348 if (pixels.size() != expectedSize)
349 {
350 throw std::runtime_error("Array data size doesn't match image dimension.");
351 }
352
353 mData.resize(pixelCount);
354
355 const uint8_t* src = pixels.data();
356 Color* dst = mData.data();
357
358 for (size_t i = 0; i < pixelCount; ++i)
359 {
360 dst[i].r = src[i * 3 + 0];
361 dst[i].g = src[i * 3 + 1];
362 dst[i].b = src[i * 3 + 2];
363 dst[i].a = 255;
364 }
365}
366
367
368} //namespace rendering_engine
Represents raw 2D image data stored in memory.
void CleanAllocatedMemory()
Frees allocated memory.
~ImageData()
Destructor that frees memory.
void SetPixel(unsigned int x, unsigned int y, Color color)
Sets the color of a specific pixel.
void AllocateMemory(unsigned int width, unsigned int height)
Allocates memory for internal pixel storage.
unsigned int GetWidth() const
Returns the image width.
std::vector< uint8_t > GetImageDataRGB() const
Gets raw image data in RGB format.
ImageData()
Constructs an empty image.
void LoadImageDataRGBA(std::vector< std::uint8_t > const &pixels)
Loads image data from a 32-bit RGBA buffer.
bool LoadTextureJpegFile(char const *filename)
Loads image from a JPEG file.
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.
ImageData & operator=(const ImageData &rhs)
Copy assignment operator.
unsigned int GetHeight() const
Returns the image height.
Color & PixelRef(unsigned int x, unsigned int y)
void LoadImageDataRGB(std::vector< std::uint8_t > const &pixels)
Loads image data from a 24-bit RGB buffer.
void WritePngFile(char const *filename)
Writes the image data to a PNG file.
void WriteJpegFile(char const *filename)
Writes the image data to a JPEG file.
const Color GetPixel(unsigned int x, unsigned int y) const
Retrieves the color of a specific pixel.
bool LoadTexturePngFile(char const *filename)
Loads image from a PNG file.
JPEG read/write backend using libjpeg.
static bool ReadJpegFromMemory(const unsigned char *memory, size_t memorySize, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)
Decode a JPEG image directly from a memory buffer.
static bool ReadJpegFile(char const *filename, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)
Reads a JPEG file into an RGB image buffer.
static void SaveTextureFileJpeg(rendering_engine::ImageData const &imageData, char const *filename)
Saves image data to a JPEG file.
PNG read/write backend using libpng.
static bool ReadPngFromMemory(const unsigned char *memory, size_t memorySize, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbaImageDataVector)
Reads a PNG image from memory into RGBA image data.
static void SaveTextureFilePng(rendering_engine::ImageData const &imageData, char const *filename)
Saves image data to a PNG file.
static bool ReadPngFile(char const *filename, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbaImageDataVector)
Reads a PNG file into RGBA image data.
Engine-wide logging system for runtime diagnostics and performance tracking.
#define LOG_ERROR(msg)
Definition logger.hpp:41
Represents a color with red, green, blue, and alpha channels.