Rendering Engine 0.2.9
Modular Graphics Rendering Engine | v0.2.9
image_codec_png.hpp
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/**
7 * @file image_codec_png.hpp
8 * @brief PNG read/write backend using libpng.
9 *
10 * This module bridges the Rendering Engine's `ImageData` abstraction and the libpng C API.
11 * It provides direct read/write routines for 8-bit RGBA images.
12 *
13 * @details
14 * The implementation follows the official libpng examples and reference documentation:
15 * - https://www.libpng.org/pub/png/libpng.html
16 * - See "Simplified API for Reading and Writing" for modern usage examples.
17 *
18 * The function sequence mirrors the standard libpng workflow:
19 * 1. Create and initialize `png_structp` and `png_infop`.
20 * 2. Set up `setjmp` error handling.
21 * 3. Configure image metadata with `png_set_IHDR`.
22 * 4. Write/read rows with `png_write_row` / `png_image_finish_read`.
23 * 5. Clean up with `png_destroy_write_struct`.
24 *
25 * @note
26 * This file is considered an engine backend implementation detail.
27 * It is not intended for use outside `ImageData` and `ImageDataGpu`.
28 *
29 * @see rendering_engine::ImageData
30 */
31#pragma once
32
33#include <cstring>
34#include <vector>
35#include <stdlib.h>
36#include <png.h>
37
38/**
39 * @brief Saves image data to a PNG file.
40 *
41 * Uses libpng to write RGBA data from ImageData into a PNG file.
42 * The function performs full file initialization, metadata setup, and cleanup.
43 *
44 * @param imageData Source image to save.
45 * @param filename Path to the output PNG file.
46 */
47static void SaveTextureFilePng(rendering_engine::ImageData const& imageData, char const* filename)
48{
49 FILE* fp = fopen(filename, "wb");
50 if (fp == NULL)
51 {
52 fprintf(stderr, "can't open %s\n", filename);
53 exit(1);
54 }
55
56 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
57 if (png_ptr == NULL)
58 {
59 fclose(fp);
60 return;
61 }
62
63 png_infop info_ptr = png_create_info_struct(png_ptr);
64 if (info_ptr == NULL)
65 {
66 fclose(fp);
67 png_destroy_write_struct(&png_ptr, NULL);
68 return;
69 }
70
71 if (setjmp(png_jmpbuf(png_ptr)))
72 {
73 fclose(fp);
74 png_destroy_write_struct(&png_ptr, &info_ptr);
75 return;
76 }
77
78 png_init_io(png_ptr, fp);
79
80 int const bit_depth = 8;
81 png_set_IHDR(png_ptr,
82 info_ptr,
83 imageData.GetWidth(),
84 imageData.GetHeight(),
85 bit_depth,
86 PNG_COLOR_TYPE_RGB_ALPHA,
87 PNG_INTERLACE_NONE,
88 PNG_COMPRESSION_TYPE_BASE,
89 PNG_FILTER_TYPE_BASE);
90
91 png_colorp palette =
92 static_cast<png_colorp>(png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)));
93 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
94
95 png_color_8 sig_bit;
96 sig_bit.red = 8;
97 sig_bit.green = 8;
98 sig_bit.blue = 8;
99 sig_bit.alpha = 8;
100 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
101
102 png_write_info(png_ptr, info_ptr);
103
104 auto const imageDataVector = imageData.GetImageDataRGBA();
105 size_t const rowLength = static_cast<size_t>(imageData.GetWidth()) * 4U;
106
107 for (unsigned int row = 0; row < imageData.GetHeight(); ++row)
108 {
109 png_bytep row_pointer = const_cast<png_bytep>(
110 reinterpret_cast<png_const_bytep>(imageDataVector.data() + static_cast<size_t>(row) * rowLength));
111
112 png_write_row(png_ptr, row_pointer);
113 }
114
115 png_write_end(png_ptr, info_ptr);
116
117 png_free(png_ptr, palette);
118 palette = NULL;
119
120 png_destroy_write_struct(&png_ptr, &info_ptr);
121 fclose(fp);
122}
123
124/**
125 * @brief Reads a PNG file into RGBA image data.
126 *
127 * Loads an image using libpng's simplified API and converts it to 8-bit RGBA format.
128 *
129 * @param filename Path to the PNG file.
130 * @param width [out] Image width.
131 * @param height [out] Image height.
132 * @param rgbaImageDataVector [out] Filled with 4-byte-per-pixel RGBA data.
133 * @return true if the image was successfully read, false otherwise.
134 */
135static bool ReadPngFile(char const* filename, unsigned int& width, unsigned int& height, std::vector<std::uint8_t>& rgbaImageDataVector)
136{
137 png_image image;
138 memset(&image, 0, sizeof(image));
139 image.version = PNG_IMAGE_VERSION;
140
141 if (png_image_begin_read_from_file(&image, filename) == 0)
142 {
143 return false;
144 }
145
146 image.format = PNG_FORMAT_RGBA;
147
148 size_t const imageSize = PNG_IMAGE_SIZE(image);
149 png_bytep buffer = new png_byte[imageSize];
150
151 if (buffer == NULL)
152 {
153 png_image_free(&image);
154 return false;
155 }
156
157 if (png_image_finish_read(&image,
158 NULL /* background */,
159 buffer,
160 0 /* row_stride */,
161 NULL /* colormap */) == 0)
162 {
163 delete[] buffer;
164 png_image_free(&image);
165 return false;
166 }
167
168 width = image.width;
169 height = image.height;
170
171 rgbaImageDataVector.resize(imageSize);
172 std::memcpy(rgbaImageDataVector.data(), buffer, imageSize);
173
174 delete[] buffer;
175 png_image_free(&image);
176
177 return true;
178}
179
180/**
181 * @brief Reads a PNG image from memory into RGBA image data.
182 *
183 * Works similarly to ReadPngFile(), but instead of loading from disk it
184 * accepts a raw memory buffer containing a complete PNG file. The data
185 * is decoded with libpng’s simplified API and converted to 8-bit RGBA format.
186 *
187 * @param memory Pointer to the PNG file data in memory.
188 * @param memorySize Size of the memory buffer in bytes.
189 * @param width [out] Image width in pixels.
190 * @param height [out] Image height in pixels.
191 * @param rgbaImageDataVector [out] Output buffer filled with RGBA pixel data
192 * (4 bytes per pixel, stored in a vector of 32-bit unsigned integers).
193 * @return true if decoding succeeded, false otherwise.
194 */
196 const unsigned char* memory,
197 size_t memorySize,
198 unsigned int& width,
199 unsigned int& height,
200 std::vector<std::uint8_t>& rgbaImageDataVector)
201{
202 png_image image;
203 memset(&image, 0, sizeof(image));
204 image.version = PNG_IMAGE_VERSION;
205
206 if (png_image_begin_read_from_memory(&image, memory, memorySize) == 0)
207 {
208 return false;
209 }
210
211 image.format = PNG_FORMAT_RGBA;
212
213 size_t const imageSize = PNG_IMAGE_SIZE(image);
214 png_bytep buffer = new png_byte[imageSize];
215
216 if (buffer == NULL)
217 {
218 png_image_free(&image);
219 return false;
220 }
221
222 if (png_image_finish_read(&image, nullptr, buffer, 0, nullptr) == 0)
223 {
224 delete[] buffer;
225 png_image_free(&image);
226 return false;
227 }
228
229 width = image.width;
230 height = image.height;
231
232 rgbaImageDataVector.resize(imageSize);
233 std::memcpy(rgbaImageDataVector.data(), buffer, imageSize);
234
235 delete[] buffer;
236 png_image_free(&image);
237
238 return true;
239}
Represents raw 2D image data stored in memory.
Definition: image_data.hpp:80
unsigned int GetWidth() const
Returns the image width.
Definition: image_data.hpp:137
std::vector< uint8_t > GetImageDataRGBA() const
Gets raw image data in RGBA format.
Definition: image_data.cpp:219
unsigned int GetHeight() const
Returns the image height.
Definition: image_data.hpp:145
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.