/*
    Robot Interface
    (C) 2006 Jason Hunt
    nulluser@gmail.com
    
    File: compress.h
    
    You have to wrap jpeglib.h in extern "C" { } 
*/

#include <stdio.h>
#include <jpeglib.h>
#include <jconfig.h>
#include <jerror.h>

#include "camera.h"

unsigned int max_buffer;        // The size of the buffer that the caller is maintaining
char *c_buffer;                 // Pointer to the compressed buffer
unsigned int out_size;          // Size of the compressed object

typedef struct {                // Simple destination manager
  struct jpeg_destination_mgr pub;    
} buffer_mgr;

typedef buffer_mgr * dest_ptr;



/* Called on startup to setup destination*/
METHODDEF(void) buffer_init_destination (j_compress_ptr cinfo)
{
    dest_ptr dest = (dest_ptr) cinfo->dest;

    // Point to start of buffer
    dest->pub.next_output_byte = (JOCTET *) c_buffer;

    // Set buffer size
    dest->pub.free_in_buffer = max_buffer;
}
/* End of buffer_init_destination */


/* Write the compressed data to the buffer */
METHODDEF(boolean) buffer_empty_output_buffer (j_compress_ptr cinfo)
{
    // Not needed, we are writing directly to a buffer
    return(TRUE);
}
/* End of buffer_empty_output_buffer */


/* Called for cleanup */
METHODDEF(void) buffer_term_destination (j_compress_ptr cinfo)
{
    dest_ptr dest = (dest_ptr) cinfo->dest;
    out_size +=  max_buffer - dest->pub.free_in_buffer;
}
/* End of buffer_term_destination */


/* Eetup for buffer compress */
GLOBAL(void) jpeg_buffer_dest (j_compress_ptr cinfo, char *buffer, unsigned int max)
{
    dest_ptr dest;              // Pointer to destination structure
    out_size = 0;               // Reset the outsize
    
    max_buffer = max;           // Set max buffer size
    c_buffer = buffer;          // Link the buffer
   
    /* Create the structure */
    if (cinfo->dest == NULL) 
    {	
        cinfo->dest = (struct jpeg_destination_mgr *)
                      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(buffer_mgr));
    }

    dest = (dest_ptr) cinfo->dest;

    dest->pub.init_destination = buffer_init_destination;
    dest->pub.empty_output_buffer = buffer_empty_output_buffer;
    dest->pub.term_destination = buffer_term_destination;
}
/* End of buffer_dest */


/* Transcode the image into 3 byte configuration */ 
void convert_4byte_to_3byte(unsigned char *in, unsigned char *out, unsigned int size)
{
    unsigned int out_index = 0, in_index = 0;

    for (unsigned int i = 0; i < size; i++)
    {
        out[out_index++] = in[in_index++];
        out[out_index++] = in[in_index++];
        out[out_index++] = in[in_index++];

        in_index++;
    }
}
/* End of convert_4byte_to_3byte */


/* Use ligjpeg to compress the image */
void compress_frame(byte *image, 
                    byte *compressed, 
                    byte *transcode_buffer, 
                    int max, unsigned int *the_out_size, int x_size, int y_size)
{
    if (x_size == 0 || y_size == 0) return;

    convert_4byte_to_3byte(image, (byte *)transcode_buffer, x_size * y_size);

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    JSAMPROW row_pointer[1];	// Pointer to JSAMPLE rows
    int row_stride;		        // Physical row width in image buffer 

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    jpeg_buffer_dest(&cinfo, (char *)compressed, max);

    cinfo.image_width = x_size; 	// Set image size
    cinfo.image_height = y_size;

    cinfo.input_components = 3;		// Number of color components per pixel 
    cinfo.in_color_space = JCS_RGB;	// RGB

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 80, TRUE ); // 80 percent quality

    jpeg_start_compress(&cinfo, TRUE);

    row_stride = x_size * cinfo.input_components;	// JSAMPLEs per row in image_buffer 

    // Core compression loop
    while (cinfo.next_scanline < cinfo.image_height) 
    {
        row_pointer[0] = (JSAMPLE *) &transcode_buffer[cinfo.next_scanline * row_stride];

        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
  
    // Set actual output size
    *the_out_size = out_size;   
  
    jpeg_destroy_compress(&cinfo);/**/
 }
/* End of compress_frame */


