Skip to content

Instantly share code, notes, and snippets.

@PhirePhly
Created July 10, 2012 02:33
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save PhirePhly/3080633 to your computer and use it in GitHub Desktop.
Save PhirePhly/3080633 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link

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

@dacre-denny
Copy link

Very helpful - thank you!

@goodmattg
Copy link

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

@kuoyliu
Copy link

kuoyliu commented Apr 12, 2017

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

@lilith
Copy link

lilith commented Aug 21, 2017

Sounds like you have a mismatched header and library.

Copy link

ghost 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!

@birgersp
Copy link

Can you please elaborate some more on what you mean in line 65-67?

@PhirePhly
Copy link
Author

PhirePhly commented Jun 24, 2019

Can you please elaborate some more on what you mean in line 65-67?

See line 114 where I used the jpeg_mem_src() API call with the jpg_buffer. If you're reading a file, you would normally just want to use the jpeg_stdio_src() call, so you don't need to manually read the whole file into memory first before passing it to the library.

@AysegulYANIK
Copy link

/tmp/ccVcUUF0.o: In function main': read_JPEG.cc:(.text+0x1b0): undefined reference to jpeg_std_error'
read_JPEG.cc:(.text+0x1d0): undefined reference to jpeg_CreateDecompress' read_JPEG.cc:(.text+0x200): undefined reference to jpeg_mem_src'
read_JPEG.cc:(.text+0x22a): undefined reference to jpeg_read_header' read_JPEG.cc:(.text+0x278): undefined reference to jpeg_start_decompress'
read_JPEG.cc:(.text+0x344): undefined reference to jpeg_read_scanlines' read_JPEG.cc:(.text+0x36b): undefined reference to jpeg_finish_decompress'
read_JPEG.cc:(.text+0x37a): undefined reference to `jpeg_destroy_decompress'
collect2: error: ld returned 1 exit status

I am getting this error, could you pls what it is?

@ArthurRyan0803
Copy link

Helpful!
But whether it's necessary to care about 4 bytes align of image row stride?
row_stride = width * pixel_size;

@manulari
Copy link

Hi Kenneth!
Would you mind giving permission to use this under MIT license / BSD license or similar?

@tlehman
Copy link

tlehman commented Oct 29, 2021

Does anyone know why there's a magic number 100 appended to jpg_size on line 74?

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