MozJPEG’s API is built around the concept of destination and source managers. As their respective name implies, destination and source managers tell MozJPEG where to read/write data before/after compression. The default ones are jpeg_stdio_src/jpeg_stdio_dest which read/write data from a file stream, but there is also the jpeg_mem_src/jpeg_mem_dest that uses memory.

In one of my previous posts (in my personal blog), I presented a full example of how to do image compression using MozJPEG’s API. The example can be found in this gist. The examples uses jpeg_stdio_src/jpeg_stdio_dest as destination managers. In this post, I would like to build on that example to demonstrate the usage of jpeg_mem_src and jpeg_mem_dest. Some benefits of using a memory buffer over a file stream are reducing CPU cycles, and increasing read/write speed.

Step 1: reading the whole file into a buffer:

There are different ways to read a whole file into memory in C language, but in our case we can get some inspiration from the already existing djpeg code here:

    unsigned char *inbuffer = NULL;
    unsigned long insize = 0;
    size_t nbytes;

    do {
      inbuffer = (unsigned char *)realloc(inbuffer, insize + INPUT_BUF_SIZE);
      if (inbuffer == NULL) {
        fprintf(stderr, "memory allocation failure\n");
        exit(EXIT_FAILURE);
      }
      nbytes = fread(&inbuffer[insize], 1, INPUT_BUF_SIZE, input_file);
      if (nbytes < INPUT_BUF_SIZE && ferror(input_file)) {
          fprintf(stderr, "can't read from %s\n", filename);
      }
      insize += (unsigned long)nbytes;
    } while (nbytes == INPUT_BUF_SIZE);

     //we initialize our source using the decompress struct we already created, the input buffer, and the buffer size
     jpeg_mem_src(&dinfo, inbuffer, insize);

The code goes through the input image data and reads chunk by chunk with each chunk having a maximum size of INPUT_BUF_SIZE(which is a constant that has 4096 as value). With each loop, our input buffer is resized using realloc to accomodate more bytes. Once our read data size is less than the INPUT_BUF_SIZE, we know that we have reached the end and we escape the loop. Finally, we call jpeg_mem_src with our buffer to initialize the source of the image data.

Step 2: preparing our output buffer:

The next step is to configure the output buffer to which data will be written after compression:

 unsigned char *outbuffer = NULL;
 unsigned long outsize = insize;
 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);

As simple as that. jpeg_mem_dest takes care of doing the memory allocation (as you can see in the source code) for the buffer so we can provide a null buffer without worry.

Full source code:

https://gist.github.com/zak905/ea18633a210cde8adb3cc03e6a8ec92f