Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
image_codec_jpeg.hpp File Reference

JPEG read/write backend using libjpeg. More...

#include <iostream>
#include <limits>
#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h>
#include "image_data.hpp"

Go to the source code of this file.

Classes

struct  codec_error_mgr
 

Typedefs

typedef struct codec_error_mgr * codec_error_mgr_ptr
 

Functions

static void SaveTextureFileJpeg (rendering_engine::ImageData const &imageData, char const *filename)
 Saves image data to a JPEG file.
 
 codec_error_exit (j_common_ptr cinfo)
 
static bool DoReadJpegFile (struct jpeg_decompress_struct *cinfo, char const *filename, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)
 
static bool DoReadJpegFromMemory (struct jpeg_decompress_struct *cinfo, const unsigned char *memory, size_t memorySize, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)
 
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 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.
 

Detailed Description

JPEG read/write backend using libjpeg.

Provides low-level I/O routines bridging ImageData and the libjpeg API. Supports 8-bit RGB images for loading and saving.

The implementation follows the official libjpeg API workflow:

Custom error handling is implemented via codec_error_mgr to safely recover from libjpeg's internal longjmp behavior.

Note
This file is an internal backend of the Rendering Engine and is not part of the public API.
See also
rendering_engine::ImageData

Definition in file image_codec_jpeg.hpp.

Typedef Documentation

◆ codec_error_mgr_ptr

Definition at line 98 of file image_codec_jpeg.hpp.

Function Documentation

◆ codec_error_exit()

codec_error_exit ( j_common_ptr  cinfo)

Definition at line 105 of file image_codec_jpeg.hpp.

106{
107 /* cinfo->err really points to a codec_error_mgr struct, so coerce pointer */
108 codec_error_mgr_ptr codecErr = (codec_error_mgr_ptr)cinfo->err;
109
110 /* Always display the message. */
111 /* We could postpone this until after returning, if we chose. */
112 (*cinfo->err->output_message) (cinfo);
113
114 /* Return control to the setjmp point */
115 longjmp(codecErr->setjmp_buffer, 1);
116}
struct codec_error_mgr * codec_error_mgr_ptr

◆ DoReadJpegFile()

bool DoReadJpegFile ( struct jpeg_decompress_struct *  cinfo,
char const *  filename,
unsigned int &  width,
unsigned int &  height,
std::vector< std::uint8_t > &  rgbImageDataVector 
)
static

Definition at line 162 of file image_codec_jpeg.hpp.

163{
164 /* We use our private extension JPEG error handler.
165 * Note that this struct must live as long as the main JPEG parameter
166 * struct, to avoid dangling-pointer problems.
167 */
168 struct codec_error_mgr jerr;
169
170 FILE* infile; /* source file */
171 JSAMPARRAY buffer; /* Output row buffer */
172
173 if( (infile = fopen(filename, "rb")) == NULL )
174 {
175 std::cout << "File can not be open." << std::endl;
176 return false;
177 }
178
179 /* Step 1: allocate and initialize JPEG decompression object */
180
181 /* We set up the normal JPEG error routines, then override error_exit. */
182 cinfo->err = jpeg_std_error(&jerr.pub);
183 jerr.pub.error_exit = codec_error_exit;
184 /* Establish the setjmp return context for my_error_exit to use. */
185 if( setjmp(jerr.setjmp_buffer) ) {
186 /* If we get here, the JPEG code has signaled an error.
187 * We need to clean up the JPEG object, close the input file, and return.
188 */
189 jpeg_destroy_decompress(cinfo);
190 fclose(infile);
191 return false;
192 }
193 /* Now we can initialize the JPEG decompression object. */
194 jpeg_create_decompress(cinfo);
195
196 /* Step 2: specify data source (eg, a file) */
197
198 jpeg_stdio_src(cinfo, infile);
199
200 /* Step 3: read file parameters with jpeg_read_header() */
201
202 (void)jpeg_read_header(cinfo, TRUE);
203 /* We can ignore the return value from jpeg_read_header since
204 * (a) suspension is not possible with the stdio data source, and
205 * (b) we passed TRUE to reject a tables-only JPEG file as an error.
206 * See libjpeg.txt for more info.
207 */
208
209 /* Step 4: set parameters for decompression */
210
211 /* In this example, we don't need to change any of the defaults set by
212 * jpeg_read_header(), so we do nothing here.
213 */
214
215 /* Step 5: Start decompressor and fulfill the output data */
216 width = cinfo->image_width;
217 height = cinfo->image_height;
218
219 rgbImageDataVector.clear();
220
221 (void)jpeg_start_decompress(cinfo);
222 /* We can ignore the return value since suspension is not possible
223 * with the stdio data source.
224 */
225
226 /* We may need to do some setup of our own at this point before reading
227 * the data. After jpeg_start_decompress() we have the correct scaled
228 * output image dimensions available, as well as the output colormap
229 * if we asked for color quantization.
230 * In this example, we need to make an output work buffer of the right size.
231 */
232 /* JSAMPLEs per row in output buffer */
233 const JDIMENSION rowStride = cinfo->output_width * static_cast<JDIMENSION>(cinfo->output_components);
234 /* Make a one-row-high sample array that will go away when done with image */
235 buffer = (*cinfo->mem->alloc_sarray)
236 ((j_common_ptr)cinfo, JPOOL_IMAGE, rowStride, 1);
237
238 /* Step 6: while (scan lines remain to be read) */
239 /* jpeg_read_scanlines(...); */
240
241 /* Here we use the library's state variable cinfo->output_scanline as the
242 * loop counter, so that we don't have to keep track ourselves.
243 */
244 ;
245 while( cinfo->output_scanline < cinfo->output_height ) {
246 /* jpeg_read_scanlines expects an array of pointers to scanlines.
247 * Here the array is only one element long, but you could ask for
248 * more than one scanline at a time if that's more convenient.
249 */
250 (void)jpeg_read_scanlines(cinfo, buffer, 1);
251 /* Assume put_scanline_someplace wants a pointer and sample count. */
252 for(JDIMENSION i = 0; i < rowStride; ++i)
253 {
254 rgbImageDataVector.push_back(*(buffer[0] + i));
255 }
256 }
257
258 /* Step 7: Finish decompression */
259
260 (void)jpeg_finish_decompress(cinfo);
261 /* We can ignore the return value since suspension is not possible
262 * with the stdio data source.
263 */
264
265 /* Step 8: Release JPEG decompression object */
266
267 /* This is an important step since it will release a good deal of memory. */
268 jpeg_destroy_decompress(cinfo);
269
270 /* After finish_decompress, we can close the input file.
271 * Here we postpone it until after no more JPEG errors are possible,
272 * so as to simplify the setjmp error logic above. (Actually, I don't
273 * think that jpeg_destroy can do an error exit, but why assume anything...)
274 */
275 fclose(infile);
276
277 /* At this point you may want to check to see whether any corrupt-data
278 * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
279 */
280
281 /* And we're done! */
282 return true;
283}
codec_error_exit(j_common_ptr cinfo)

◆ DoReadJpegFromMemory()

static bool DoReadJpegFromMemory ( struct jpeg_decompress_struct *  cinfo,
const unsigned char *  memory,
size_t  memorySize,
unsigned int &  width,
unsigned int &  height,
std::vector< std::uint8_t > &  rgbImageDataVector 
)
static

Definition at line 339 of file image_codec_jpeg.hpp.

346{
347 struct codec_error_mgr jerr;
348
349 cinfo->err = jpeg_std_error(&jerr.pub);
350 jerr.pub.error_exit = codec_error_exit;
351
352 if (setjmp(jerr.setjmp_buffer))
353 {
354 jpeg_destroy_decompress(cinfo);
355 return false;
356 }
357
358 jpeg_create_decompress(cinfo);
359
360 if (memorySize >
361 static_cast<size_t>(
362 std::numeric_limits<unsigned long>::max()))
363 {
364 jpeg_destroy_decompress(cinfo);
365 return false;
366 }
367
368 jpeg_mem_src(
369 cinfo,
370 memory,
371 static_cast<unsigned long>(memorySize));
372
373 jpeg_read_header(cinfo, TRUE);
374 jpeg_start_decompress(cinfo);
375
376 width = cinfo->output_width;
377 height = cinfo->output_height;
378
379 const size_t components =
380 static_cast<size_t>(cinfo->output_components);
381
382 const size_t rowStride =
383 static_cast<size_t>(width) * components;
384
385 const size_t imageSize =
386 static_cast<size_t>(width) *
387 static_cast<size_t>(height) *
388 components;
389
390 rgbImageDataVector.resize(imageSize);
391
392 while (cinfo->output_scanline < cinfo->output_height)
393 {
394 unsigned char* row =
395 rgbImageDataVector.data() +
396 static_cast<size_t>(cinfo->output_scanline) * rowStride;
397
398 jpeg_read_scanlines(cinfo, &row, 1);
399 }
400
401 jpeg_finish_decompress(cinfo);
402 jpeg_destroy_decompress(cinfo);
403
404 return true;
405}

◆ ReadJpegFile()

static bool ReadJpegFile ( char const *  filename,
unsigned int &  width,
unsigned int &  height,
std::vector< std::uint8_t > &  rgbImageDataVector 
)
static

Reads a JPEG file into an RGB image buffer.

Parameters
filenamePath to input JPEG file.
width[out] Image width in pixels.
height[out] Image height in pixels.
rgbImageDataVector[out] Output vector of RGB pixel data (3 bytes per pixel).
Returns
true on success, false on failure.

Definition at line 143 of file image_codec_jpeg.hpp.

144{
145 /* This struct contains the JPEG decompression parameters and pointers to
146 * working space (which is allocated as needed by the JPEG library).
147 */
148 struct jpeg_decompress_struct cinfo;
149
150 return DoReadJpegFile(&cinfo, filename, width, height, rgbImageDataVector);
151}
static bool DoReadJpegFile(struct jpeg_decompress_struct *cinfo, char const *filename, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)

◆ ReadJpegFromMemory()

static bool ReadJpegFromMemory ( const unsigned char *  memory,
size_t  memorySize,
unsigned int &  width,
unsigned int &  height,
std::vector< std::uint8_t > &  rgbImageDataVector 
)
static

Decode a JPEG image directly from a memory buffer.

This function behaves similarly to ReadJpegFile(), but instead of reading from disk, it reads raw JPEG bytes already loaded in memory.

Useful when assets are stored in:

  • packed archives (Pack.bin)
  • networked streams
  • compressed inline resources

The function expects the buffer to contain valid JPEG-formatted data.

Parameters
memoryPointer to the start of the JPEG byte stream.
memorySizeTotal size of the memory buffer in bytes.
width[out] Decoded image width in pixels.
height[out] Decoded image height in pixels.
rgbImageDataVector[out] Output RGB pixel buffer (3 bytes per pixel).
Returns
  • true → Successful decode.
  • false → Memory buffer was not a valid JPEG stream or decode failed.
Warning
The function does not attempt format detection beyond JPEG signature. Callers must ensure the memory buffer contains JPEG data.

Definition at line 321 of file image_codec_jpeg.hpp.

327{
328 struct jpeg_decompress_struct cinfo;
329
331 &cinfo,
332 memory,
333 memorySize,
334 width,
335 height,
336 rgbImageDataVector);
337}
static bool DoReadJpegFromMemory(struct jpeg_decompress_struct *cinfo, const unsigned char *memory, size_t memorySize, unsigned int &width, unsigned int &height, std::vector< std::uint8_t > &rgbImageDataVector)

◆ SaveTextureFileJpeg()

static void SaveTextureFileJpeg ( rendering_engine::ImageData const &  imageData,
char const *  filename 
)
static

Saves image data to a JPEG file.

Parameters
imageDataSource image in RGB format.
filenamePath to the output file.

Definition at line 39 of file image_codec_jpeg.hpp.

40{
41 auto const imageDataVector = imageData.GetImageDataRGB();
42 JSAMPLE* imageBuffer = new JSAMPLE[imageDataVector.size()];
43 std::copy(imageDataVector.begin(), imageDataVector.end(), imageBuffer);
44 int quality = 100;
45
46 struct jpeg_compress_struct cinfo;
47 struct jpeg_error_mgr jerr;
48
49 FILE* outfile; /* target file */
50 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
51
52 cinfo.err = jpeg_std_error(&jerr);
53 jpeg_create_compress(&cinfo);
54
55 if( (outfile = fopen(filename, "wb")) == NULL ) {
56 fprintf(stderr, "can't open %s\n", filename);
57 exit(1);
58 }
59 jpeg_stdio_dest(&cinfo, outfile);
60
61 cinfo.image_width = imageData.GetWidth(); /* image width and height, in pixels */
62 cinfo.image_height = imageData.GetHeight();
63 cinfo.input_components = 3; /* # of color components per pixel */
64 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
65
66 jpeg_set_defaults(&cinfo);
67
68 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
69
70 jpeg_start_compress(&cinfo, TRUE);
71
72 const size_t rowStride = static_cast<size_t>(imageData.GetWidth()) * 3U; /* JSAMPLEs per row in image_buffer */
73
74 while( cinfo.next_scanline < cinfo.image_height )
75 {
76 /* jpeg_write_scanlines expects an array of pointers to scanlines.
77 * Here the array is only one element long, but you could pass
78 * more than one scanline at a time if that's more convenient.
79 */
80 row_pointer[0] = &imageBuffer[static_cast<size_t>(cinfo.next_scanline) * rowStride];
81 (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
82 }
83
84 jpeg_finish_compress(&cinfo);
85 /* After finish_compress, we can close the output file. */
86 fclose(outfile);
87
88 jpeg_destroy_compress(&cinfo);
89 delete[] imageBuffer;
90}