-
-
Save PhirePhly/3080633 to your computer and use it in GitHub Desktop.
| // 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; | |
| } |
Thanks for this code. Was looking to decompress not from a file but direct from a mmap() buffer.
Very helpful - thank you!
Thank you! Struggled to find complete example using the jpeglib library.
How can I resolve "Wrong JPEG library version: library is 80, caller expects 90"?
Sounds like you have a mismatched header and library.
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!
Can you please elaborate some more on what you mean in line 65-67?
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.
/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?
Helpful!
But whether it's necessary to care about 4 bytes align of image row stride?
row_stride = width * pixel_size;
Hi Kenneth!
Would you mind giving permission to use this under MIT license / BSD license or similar?
Does anyone know why there's a magic number 100 appended to jpg_size on line 74?
Thanks! This was very helpful. Previously, I was struggling with unsigned char *buffer_array and making it decode into a pre-existing buffer. Sorted.