Instantly share code, notes, and snippets.

Embed
What would you like to do?
A bare-bones example of how to use jpeglib to decompress a jpg in memory.
// memdjpeg - A super simple example of how to decode a jpeg in memory
// Kenneth Finnegan, 2012
// blog.thelifeofkenneth.com
//
// After installing jpeglib, compile with:
// cc memdjpeg.c -ljpeg -o memdjpeg
//
// Run with:
// ./memdjpeg filename.jpg
//
// Version Date Time By
// ------- ---------- ----- ---------
// 0.01 2012-07-09 11:18 Kenneth Finnegan
//
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <jpeglib.h>
int main (int argc, char *argv[]) {
int rc, i, j;
char *syslog_prefix = (char*) malloc(1024);
sprintf(syslog_prefix, "%s", argv[0]);
openlog(syslog_prefix, LOG_PERROR | LOG_PID, LOG_USER);
if (argc != 2) {
fprintf(stderr, "USAGE: %s filename.jpg\n", argv[0]);
exit(EXIT_FAILURE);
}
// SSS EEEEEEE TTTTTTT U U PPPP
// SS SS E T U U P PP
// S E T U U P PP
// SS E T U U P PP
// SSS EEEE T U U PPPP
// SS E T U U P
// S E T U U P
// SS SS E T U U P
// SSS EEEEEEE T UUU P
// Variables for the source jpg
struct stat file_info;
unsigned long jpg_size;
unsigned char *jpg_buffer;
// Variables for the decompressor itself
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
// Variables for the output buffer, and how long each row is
unsigned long bmp_size;
unsigned char *bmp_buffer;
int row_stride, width, height, pixel_size;
// Load the jpeg data from a file into a memory buffer for
// the purpose of this demonstration.
// Normally, if it's a file, you'd use jpeg_stdio_src, but just
// imagine that this was instead being downloaded from the Internet
// or otherwise not coming from disk
rc = stat(argv[1], &file_info);
if (rc) {
syslog(LOG_ERR, "FAILED to stat source jpg");
exit(EXIT_FAILURE);
}
jpg_size = file_info.st_size;
jpg_buffer = (unsigned char*) malloc(jpg_size + 100);
int fd = open(argv[1], O_RDONLY);
i = 0;
while (i < jpg_size) {
rc = read(fd, jpg_buffer + i, jpg_size - i);
syslog(LOG_INFO, "Input: Read %d/%lu bytes", rc, jpg_size-i);
i += rc;
}
close(fd);
// SSS TTTTTTT A RRRR TTTTTTT
// SS SS T A A R RR T
// S T A A R RR T
// SS T A A R RR T
// SSS T AAAAAAA RRRR T
// SS T A A R RR T
// S T A A R R T
// SS SS T A A R R T
// SSS T A A R R T
syslog(LOG_INFO, "Proc: Create Decompress struct");
// Allocate a new decompress struct, with the default error handler.
// The default error handler will exit() on pretty much any issue,
// so it's likely you'll want to replace it or supplement it with
// your own.
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
syslog(LOG_INFO, "Proc: Set memory buffer as source");
// Configure this decompressor to read its data from a memory
// buffer starting at unsigned char *jpg_buffer, which is jpg_size
// long, and which must contain a complete jpg already.
//
// If you need something fancier than this, you must write your
// own data source manager, which shouldn't be too hard if you know
// what it is you need it to do. See jpeg-8d/jdatasrc.c for the
// implementation of the standard jpeg_mem_src and jpeg_stdio_src
// managers as examples to work from.
jpeg_mem_src(&cinfo, jpg_buffer, jpg_size);
syslog(LOG_INFO, "Proc: Read the JPEG header");
// Have the decompressor scan the jpeg header. This won't populate
// the cinfo struct output fields, but will indicate if the
// jpeg is valid.
rc = jpeg_read_header(&cinfo, TRUE);
if (rc != 1) {
syslog(LOG_ERR, "File does not seem to be a normal JPEG");
exit(EXIT_FAILURE);
}
syslog(LOG_INFO, "Proc: Initiate JPEG decompression");
// By calling jpeg_start_decompress, you populate cinfo
// and can then allocate your output bitmap buffers for
// each scanline.
jpeg_start_decompress(&cinfo);
width = cinfo.output_width;
height = cinfo.output_height;
pixel_size = cinfo.output_components;
syslog(LOG_INFO, "Proc: Image is %d by %d with %d components",
width, height, pixel_size);
bmp_size = width * height * pixel_size;
bmp_buffer = (unsigned char*) malloc(bmp_size);
// The row_stride is the total number of bytes it takes to store an
// entire scanline (row).
row_stride = width * pixel_size;
syslog(LOG_INFO, "Proc: Start reading scanlines");
//
// Now that you have the decompressor entirely configured, it's time
// to read out all of the scanlines of the jpeg.
//
// By default, scanlines will come out in RGBRGBRGB... order,
// but this can be changed by setting cinfo.out_color_space
//
// jpeg_read_scanlines takes an array of buffers, one for each scanline.
// Even if you give it a complete set of buffers for the whole image,
// it will only ever decompress a few lines at a time. For best
// performance, you should pass it an array with cinfo.rec_outbuf_height
// scanline buffers. rec_outbuf_height is typically 1, 2, or 4, and
// at the default high quality decompression setting is always 1.
while (cinfo.output_scanline < cinfo.output_height) {
unsigned char *buffer_array[1];
buffer_array[0] = bmp_buffer + \
(cinfo.output_scanline) * row_stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
syslog(LOG_INFO, "Proc: Done reading scanlines");
// Once done reading *all* scanlines, release all internal buffers,
// etc by calling jpeg_finish_decompress. This lets you go back and
// reuse the same cinfo object with the same settings, if you
// want to decompress several jpegs in a row.
//
// If you didn't read all the scanlines, but want to stop early,
// you instead need to call jpeg_abort_decompress(&cinfo)
jpeg_finish_decompress(&cinfo);
// At this point, optionally go back and either load a new jpg into
// the jpg_buffer, or define a new jpeg_mem_src, and then start
// another decompress operation.
// Once you're really really done, destroy the object to free everything
jpeg_destroy_decompress(&cinfo);
// And free the input buffer
free(jpg_buffer);
// DDDD OOO N N EEEEEEE
// D DDD O O NN N E
// D DD O O N N N E
// D D O O N N N E
// D D O O N N N EEEE
// D D O O N N N E
// D DD O O N N N E
// D DDD O O N NN E
// DDDD OOO N N EEEEEEE
// Write the decompressed bitmap out to a ppm file, just to make sure
// it worked.
fd = open("output.ppm", O_CREAT | O_WRONLY, 0666);
char buf[1024];
rc = sprintf(buf, "P6 %d %d 255\n", width, height);
write(fd, buf, rc); // Write the PPM image header before data
write(fd, bmp_buffer, bmp_size); // Write out all RGB pixel data
close(fd);
free(bmp_buffer);
syslog(LOG_INFO, "End of decompression");
return EXIT_SUCCESS;
}
@TimSC

This comment has been minimized.

Show comment
Hide comment
@TimSC

TimSC Jan 2, 2014

Thanks! This was very helpful. Previously, I was struggling with unsigned char *buffer_array and making it decode into a pre-existing buffer. Sorted.

TimSC commented Jan 2, 2014

Thanks! This was very helpful. Previously, I was struggling with unsigned char *buffer_array and making it decode into a pre-existing buffer. Sorted.

@redingen

This comment has been minimized.

Show comment
Hide comment
@redingen

redingen Oct 14, 2014

Thanks for this code. Was looking to decompress not from a file but direct from a mmap() buffer.

redingen commented Oct 14, 2014

Thanks for this code. Was looking to decompress not from a file but direct from a mmap() buffer.

@dacredenny

This comment has been minimized.

Show comment
Hide comment
@dacredenny

dacredenny Mar 3, 2015

Very helpful - thank you!

dacredenny commented Mar 3, 2015

Very helpful - thank you!

@goodmattg

This comment has been minimized.

Show comment
Hide comment
@goodmattg

goodmattg Jan 5, 2017

Thank you! Struggled to find complete example using the jpeglib library.

goodmattg commented Jan 5, 2017

Thank you! Struggled to find complete example using the jpeglib library.

@kuoyliu

This comment has been minimized.

Show comment
Hide comment
@kuoyliu

kuoyliu Apr 12, 2017

How can I resolve "Wrong JPEG library version: library is 80, caller expects 90"?

kuoyliu commented Apr 12, 2017

How can I resolve "Wrong JPEG library version: library is 80, caller expects 90"?

@lilith

This comment has been minimized.

Show comment
Hide comment
@lilith

lilith Aug 21, 2017

Sounds like you have a mismatched header and library.

lilith commented Aug 21, 2017

Sounds like you have a mismatched header and library.

@NemaOnGit

This comment has been minimized.

Show comment
Hide comment
@NemaOnGit

NemaOnGit Nov 20, 2017

Warning for all Windows coders about the following:

rc = sprintf(buf, "P6 %d %d 255\n", width, height);
write(fd, buf, rc); // Write the PPM image header before data

The \n resulted in hex 0D 0A in my environment (CLion + gcc), whereas it should only be 0A. Therefore, the generated test output file looked corrupt until I figured this out!

NemaOnGit commented Nov 20, 2017

Warning for all Windows coders about the following:

rc = sprintf(buf, "P6 %d %d 255\n", width, height);
write(fd, buf, rc); // Write the PPM image header before data

The \n resulted in hex 0D 0A in my environment (CLion + gcc), whereas it should only be 0A. Therefore, the generated test output file looked corrupt until I figured this out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment