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