I am trying to use libjpeg to save a screenshot from an opengl buffer.
The function I use is this one
void OnKeyPress(unsigned char key, int x, int y) {
if (key != static_cast<unsigned char>('p'))
return;
int width = g_current_width;
int height = g_current_height;
boost::scoped_array<boost::uint8_t> buffer(new boost::uint8_t[3 * width * height]);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
reinterpret_cast<GLvoid *>(buffer.get()));
glReadBuffer(GL_BACK);
FlipImage(buffer.get(), width, height);
// Generate a BMP files for testing purposes
SaveRGB("screenshot.bmp", buffer.get(), width, height);
boost::shared_ptr<FILE> outfile(fopen("screenshot.jpeg", "wb"), fclose);
if (!outfile)
return;
jpeg_compress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jerr.trace_level = 10;
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, outfile.get());
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 100, true);
jpeg_start_compress(&cinfo, true);
int row_stride = width * 3;
JSAMPROW row_pointer[1];
int counter = 0;
std::cout << boost::format("height: %d\n") % height;
boost::uint8_t *r_buffer = buffer.get();
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &r_buffer[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
std::cout << boost::format("current line: %d\n") % (counter++);
}
jpeg_finish_compress(&cinfo); // never reaches this point
outfile.reset();
jpeg_destroy_compress(&cinfo);
}
This function starts, but after some iterations (approximately 100 – 150) the function returns without writing anything in the file, nor generating a warning or an error.
If I use memory encoding (which is actually what I need), the function finishes, but the result make no sense. Moreover, any attempt to free the buffer ends up in error. Here is the memory destination version
void OnKeyPress(unsigned char key, int x, int y) {
if (key != static_cast<unsigned char>('p'))
return;
int width = g_current_width;
int height = g_current_height;
boost::scoped_array<boost::uint8_t> buffer(new boost::uint8_t[3 * width * height]);
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
reinterpret_cast<GLvoid *>(buffer.get()));
glReadBuffer(GL_BACK);
FlipImage(buffer.get(), width, height);
// Generate a BMP files for testing purposes
SaveRGB("screenshot.bmp", buffer.get(), width, height);
jpeg_compress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jerr.trace_level = 10;
jpeg_create_compress(&cinfo);
boost::uint8_t *jpeg_buffer_raw = NULL;
unsigned long outbuffer_size = 0;
jpeg_mem_dest(&cinfo, &jpeg_buffer_raw, &outbuffer_size);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 100, true);
jpeg_start_compress(&cinfo, true);
int row_stride = width * 3;
JSAMPROW row_pointer[1];
int counter = 0;
std::cout << boost::format("height: %d\n") % height;
boost::uint8_t *r_buffer = buffer.get();
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &r_buffer[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
std::cout << boost::format("current line: %d\n") % (counter++);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
std::ofstream jpegfile("screenshot.jpg");
jpegfile.write(reinterpret_cast<const char*>(jpeg_buffer_raw), outbuffer_size);
jpegfile.flush();
// calling free(jpeg_buffer_raw); or delete[] jpeg_buffer_raw; generates an error
}
This function generates the following jpeg image:
By comparison, the line that saves the bitmap files generates this one:
The problem, I found out, is that FILE *
handlers are not portable across dll's. Using a static version of library solves the problem.
The in memory version can be solved by opening the file like this:
std::ofstream jpegfile("screenshot.jpg", std::ios_base::out | std::ios_base::binary);