6 Responses to “Very Simple JPEG writer in C/C++”

  1. virgoptrex says:

    This one is a piece of cake! What about compressinng to memory buffer? You need to replace jpeg_stdio_dest(&cinfo, outfile);
    with your own manager!

  2. aewhite says:

    I remember looking into this at one time and I can’t remember exactly what I came up with. I think there is a easy way to do what you want. If I have time, I’ll see what I can dig up. If you find an answer please let me know.

  3. virgoptrex says:

    I have tried various methods ……. I am able to successfully decompress from memory with my own manager………..however compressing and writing to the memory is giving errors and seg faults! tried using Valgrind and ddd debugger. Also had coredump and back traced the problem in jmarker.c. This occurs at the point jpeg_start_compress(&cinfo, true);. I am pretty sure there is a problem with my destination manager……

    Here is what I have.

    /* Expanded data destination object for stdio output */

    typedef struct {

    struct jpeg_destination_mgr pub; /* public fields */

    // char * destbufferholder; /* target stream */
    int destbufferlen;

    JOCTET *destbuffer; /* start of buffer */
    size_t bufsize;
    size_t jpegsize;

    } destbuffer_mgr;

    typedef destbuffer_mgr * destbuffer_ptr;

    #define DESTBUFFER_OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite’able size */

    /*

    * Initialize destination — called by jpeg_start_compress

    * before any data is actually written.

    */

    void destbuffer_init_destination (j_compress_ptr cinfo)

    {

    destbuffer_ptr dest = (destbuffer_ptr) cinfo->dest;

    /* Allocate the output buffer — it will be released when done with image */
    dest->destbuffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, (dest->bufsize)* sizeof(JOCTET));

    dest->pub.next_output_byte = dest->destbuffer;
    dest->pub.free_in_buffer = dest->bufsize;//DESTBUFFER_OUTPUT_BUF_SIZE;
    dest->jpegsize = 0;

    }

    /*

    * Empty the output buffer — called whenever buffer fills up.

    *

    * In typical applications, this should write the entire output buffer

    * (ignoring the current state of next_output_byte & free_in_buffer),

    * reset the pointer & count to the start of the buffer, and return TRUE

    * indicating that the buffer has been dumped.

    *

    * In applications that need to be able to suspend compression due to output

    * overrun, a FALSE return indicates that the buffer cannot be emptied now.

    * In this situation, the compressor will return to its caller (possibly with

    * an indication that it has not accepted all the supplied scanlines). The

    * application should resume compression after it has made more room in the

    * output buffer. Note that there are substantial restrictions on the use of

    * suspension — see the documentation.

    *

    * When suspending, the compressor will back up to a convenient restart point

    * (typically the start of the current MCU). next_output_byte & free_in_buffer

    * indicate where the restart point will be if the current call returns FALSE.

    * Data beyond this point will be regenerated after resumption, so do not

    * write it out when emptying the buffer externally.

    */

    boolean destbuffer_empty_output_buffer (j_compress_ptr cinfo)

    {

    destbuffer_ptr dest = (destbuffer_ptr) cinfo->dest;

    dest->pub.next_output_byte = dest->destbuffer;
    dest->pub.free_in_buffer = dest->bufsize; //DESTBUFFER_OUTPUT_BUF_SIZE;

    /* Treat empty input file as fatal error */

    // ERREXIT(cinfo, JERR_INPUT_EMPTY);

    return TRUE;

    }

    /*

    * Terminate destination — called by jpeg_finish_compress

    * after all data has been written. Usually needs to flush buffer.

    *

    * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding

    * application must deal with any cleanup that should happen even

    * for error exit.

    */

    void destbuffer_term_destination (j_compress_ptr cinfo)

    {

    destbuffer_ptr dest = (destbuffer_ptr) cinfo->dest;
    dest->jpegsize = dest->bufsize – dest->pub.free_in_buffer;

    }

    /*

    * Prepare for output to a stdio stream.

    * The caller must have already opened the stream, and is responsible

    * for closing it after finishing compression.

    */

    void jpeg_mem_dest (j_compress_ptr cinfo, char * destbufferholder, size_t image_size)

    {

    destbuffer_ptr dest;

    /* The destination object is made permanent so that multiple JPEG images

    * can be written to the same file without re-executing jpeg_stdio_dest.

    * This makes it dangerous to use this manager and a different destination

    * manager serially with the same JPEG object, because their private object

    * sizes may be different. Caveat programmer.

    */

    if (cinfo->dest == NULL) { /* first time for this JPEG object? */

    cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(destbuffer_mgr));

    }

    dest = (destbuffer_ptr ) cinfo->dest;

    dest->pub.init_destination = destbuffer_init_destination;

    dest->pub.empty_output_buffer = destbuffer_empty_output_buffer;

    dest->pub.term_destination = destbuffer_term_destination;

    dest->destbuffer = (JOCTET *)destbufferholder;
    dest->bufsize = image_size;
    dest->jpegsize = 0;

    Could you replace this in your code upon reading any bmp file, jpeg file getting raw_image with this manager and let me know what happens?

  4. virgoptrex says:

    The function in the main looks like

    char* write_jpeg_file_mem( char *outbuffer, int size )
    {
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    /* this is a pointer to one row of image data */
    JSAMPROW row_pointer[1];
    //FILE *outfile = fopen( “gyg.jpg”, “wb” );

    cinfo.err = jpeg_std_error( &jerr );
    jpeg_create_compress(&cinfo);
    //jpeg_stdio_dest(&cinfo, outfile);
    jpeg_mem_dest(&cinfo, outbuffer, (size_t)size);

    /* Setting the parameters of the output file here */
    cinfo.image_width = width;
    cinfo.image_height = height;
    cinfo.input_components = 3;
    cinfo.in_color_space = color_space;
    /* default compression parameters, we shouldn’t be worried about these */
    jpeg_set_defaults( &cinfo );
    // jpeg_set_quality(&cinfo, 5, TRUE /* limit to baseline-JPEG values */);
    /* Now do the compression .. */
    jpeg_start_compress( &cinfo, TRUE );
    /* like reading a file, this time write one row at a time */
    while( cinfo.next_scanline widthStep = (int)cinfo.image_width*3;
    // hello->imageData = (char *)raw_image;

    // cvSaveImage(“hellower.bmp”,raw_image);

    /* similar to read file, clean up after we’re done compressing */
    jpeg_finish_compress( &cinfo );
    jpeg_destroy_compress( &cinfo );

    //fclose( outfile );
    /* success code is 1! */
    return (char*)raw_image;
    }

  5. aewhite says:

    I changed references of char * to JOCTET * and I at least got to scanline input. I don’t think I’ll be able to go much deeper into this problem right now though, sorry.

  6. virgoptrex says:

    I got in-memory compression to work. See the following

    #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite’able size */

    /* Expanded data destination object for memory output */

    typedef struct {
    struct jpeg_destination_mgr pub; /* public fields */

    unsigned char ** outbuffer; /* target buffer */
    unsigned long * outsize;
    unsigned char * newbuffer; /* newly allocated buffer */
    JOCTET * buffer; /* start of buffer */
    size_t bufsize;
    } my_mem_destination_mgr;

    typedef my_mem_destination_mgr * my_mem_dest_ptr;

    void
    init_mem_destination (j_compress_ptr cinfo)
    {
    /* no work necessary here */
    }

    boolean
    empty_mem_output_buffer (j_compress_ptr cinfo)
    {
    size_t nextsize;
    JOCTET * nextbuffer;
    my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest;

    /* Try to allocate new buffer with double size */
    nextsize = dest->bufsize * 2;
    nextbuffer = (JOCTET *)malloc(nextsize);

    if (nextbuffer == NULL)
    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);

    memcpy(nextbuffer, dest->buffer, dest->bufsize);

    if (dest->newbuffer != NULL)
    free(dest->newbuffer);

    dest->newbuffer = nextbuffer;

    dest->pub.next_output_byte = nextbuffer + dest->bufsize;
    dest->pub.free_in_buffer = dest->bufsize;

    dest->buffer = nextbuffer;
    dest->bufsize = nextsize;

    return TRUE;
    }

    void
    term_mem_destination (j_compress_ptr cinfo)
    {
    my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest;

    *dest->outbuffer = dest->buffer;
    *dest->outsize = dest->bufsize – dest->pub.free_in_buffer;
    }

    void
    jpeg_mem_dest (j_compress_ptr cinfo,
    unsigned char ** outbuffer, unsigned long * outsize)
    {
    my_mem_dest_ptr dest;

    if (outbuffer == NULL || outsize == NULL) /* sanity check */
    ERREXIT(cinfo, JERR_BUFFER_SIZE);

    /* The destination object is made permanent so that multiple JPEG images
    * can be written to the same buffer without re-executing jpeg_mem_dest.
    */
    if (cinfo->dest == NULL) { /* first time for this JPEG object? */
    cinfo->dest = (struct jpeg_destination_mgr *)
    (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
    sizeof(my_mem_destination_mgr));
    }

    dest = (my_mem_dest_ptr) cinfo->dest;
    dest->pub.init_destination = init_mem_destination;
    dest->pub.empty_output_buffer = empty_mem_output_buffer;
    dest->pub.term_destination = term_mem_destination;
    dest->outbuffer = outbuffer;
    dest->outsize = outsize;
    dest->newbuffer = NULL;

    if (*outbuffer == NULL || *outsize == 0) {
    /* Allocate initial buffer */
    dest->newbuffer = *outbuffer = (unsigned char*)malloc(OUTPUT_BUF_SIZE);
    if (dest->newbuffer == NULL)
    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
    *outsize = OUTPUT_BUF_SIZE;
    }

    dest->pub.next_output_byte = dest->buffer = *outbuffer;
    dest->pub.free_in_buffer = dest->bufsize = *outsize;
    }
    //*******************************************************************************************

    To use this do something like this in the main

    /************/

    unsigned long outlen;
    unsigned char *outbuffer;

    jpeg_mem_dest (&cinfo,&outbuffer,&outlen );
    printf(“outlen is %lu\n”,(long unsigned int)outlen);

    ——————hope that helps ———————- if this works for you like me then if you could make tutorial on this that would help many!