Ejemplo n.º 1
0
Archivo: jpeg.c Proyecto: Elzair/q3map2
int LoadJPGBuff( void *src_buffer, int src_size, unsigned char **pic, int *width, int *height ) {
	struct jpeg_decompress_struct cinfo;
	struct my_jpeg_error_mgr jerr;
	JSAMPARRAY buffer;
	int row_stride, size;

	cinfo.err = jpeg_std_error( &jerr.pub );
	jerr.pub.error_exit = my_jpeg_error_exit;

	if ( setjmp( jerr.setjmp_buffer ) ) {
		*pic = (unsigned char*)errormsg;
		jpeg_destroy_decompress( &cinfo );
		return -1;
	}

	jpeg_create_decompress( &cinfo );
	jpeg_buffer_src( &cinfo, src_buffer, src_size );
	jpeg_read_header( &cinfo, TRUE );
	jpeg_start_decompress( &cinfo );

	row_stride = cinfo.output_width * cinfo.output_components;

	size = cinfo.output_width * cinfo.output_height * 4;
	*width = cinfo.output_width;
	*height = cinfo.output_height;
	*pic = (unsigned char*)( malloc( size + 1 ) );
	memset( *pic, 0, size + 1 );

	buffer = ( *cinfo.mem->alloc_sarray )( ( j_common_ptr ) & cinfo, JPOOL_IMAGE, row_stride, 1 );

	while ( cinfo.output_scanline < cinfo.output_height )
	{
		jpeg_read_scanlines( &cinfo, buffer, 1 );

		if ( cinfo.out_color_components == 4 ) {
			j_putRGBAScanline( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 );
		}
		else if ( cinfo.out_color_components == 3 ) {
			j_putRGBScanline( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 );
		}
		else if ( cinfo.out_color_components == 1 ) {
			j_putGrayScanlineToRGB( buffer[0], cinfo.output_width, *pic, cinfo.output_scanline - 1 );
		}
	}

	jpeg_finish_decompress( &cinfo );
	jpeg_destroy_decompress( &cinfo );

	return 0;
}
Ejemplo n.º 2
0
bool  JpegDecoder::readHeader()
{
    volatile bool result = false;
    close();

    JpegState* state = new JpegState;
    m_state = state;
    state->cinfo.err = jpeg_std_error(&state->jerr.pub);
    state->jerr.pub.error_exit = error_exit;

    if( setjmp( state->jerr.setjmp_buffer ) == 0 )
    {
        jpeg_create_decompress( &state->cinfo );

        if( !m_buf.empty() )
        {
            jpeg_buffer_src(&state->cinfo, &state->source);
            state->source.pub.next_input_byte = m_buf.ptr();
            state->source.pub.bytes_in_buffer = m_buf.cols*m_buf.rows*m_buf.elemSize();
        }
        else
        {
            m_f = fopen( m_filename.c_str(), "rb" );
            if( m_f )
                jpeg_stdio_src( &state->cinfo, m_f );
        }

        if (state->cinfo.src != 0)
        {
            jpeg_read_header( &state->cinfo, TRUE );

            state->cinfo.scale_num=1;
            state->cinfo.scale_denom = m_scale_denom;
            m_scale_denom=1; // trick! to know which decoder used scale_denom see imread_
            jpeg_calc_output_dimensions(&state->cinfo);
            m_width = state->cinfo.output_width;
            m_height = state->cinfo.output_height;
            m_type = state->cinfo.num_components > 1 ? CV_8UC3 : CV_8UC1;
            result = true;
        }
    }

    m_orientation = getOrientation();

    if( !result )
        close();

    return result;
}
Ejemplo n.º 3
0
static Image* LoadJPGBuff_( const void *src_buffer, int src_size ){
	struct jpeg_decompress_struct cinfo;
	struct my_jpeg_error_mgr jerr;

	cinfo.err = jpeg_std_error( &jerr.pub );
	jerr.pub.error_exit = my_jpeg_error_exit;

	if ( setjmp( jerr.setjmp_buffer ) ) { //< TODO: use c++ exceptions instead of setjmp/longjmp to handle errors
		globalErrorStream() << "WARNING: JPEG library error: " << errormsg << "\n";
		jpeg_destroy_decompress( &cinfo );
		return 0;
	}

	jpeg_create_decompress( &cinfo );
	jpeg_buffer_src( &cinfo, const_cast<void*>( src_buffer ), src_size );
	jpeg_read_header( &cinfo, TRUE );
	jpeg_start_decompress( &cinfo );

	int row_stride = cinfo.output_width * cinfo.output_components;

	RGBAImage* image = new RGBAImage( cinfo.output_width, cinfo.output_height );

	JSAMPARRAY buffer = ( *cinfo.mem->alloc_sarray )( ( j_common_ptr ) & cinfo, JPOOL_IMAGE, row_stride, 1 );

	while ( cinfo.output_scanline < cinfo.output_height )
	{
		jpeg_read_scanlines( &cinfo, buffer, 1 );

		if ( cinfo.out_color_components == 4 ) {
			j_putRGBAScanline( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 );
		}
		else if ( cinfo.out_color_components == 3 ) {
			j_putRGBScanline( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 );
		}
		else if ( cinfo.out_color_components == 1 ) {
			j_putGrayScanlineToRGB( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 );
		}
	}

	jpeg_finish_decompress( &cinfo );
	jpeg_destroy_decompress( &cinfo );

	return image;
}
bool  JpegDecoder::readHeader()
{
    bool result = false;
    close();

    JpegState* state = new JpegState;
    m_state = state;
    state->cinfo.err = jpeg_std_error(&state->jerr.pub);
    state->jerr.pub.error_exit = error_exit;

    if( setjmp( state->jerr.setjmp_buffer ) == 0 )
    {
        jpeg_create_decompress( &state->cinfo );

        if( !m_buf.empty() )
        {
            jpeg_buffer_src(&state->cinfo, &state->source);
            state->source.pub.next_input_byte = m_buf.data;
            state->source.pub.bytes_in_buffer = m_buf.cols*m_buf.rows*m_buf.elemSize();
        }
        else
        {
            m_f = fopen( m_filename.c_str(), "rb" );
            if( m_f )
                jpeg_stdio_src( &state->cinfo, m_f );
        }

        if (state->cinfo.src != 0)
        {
            jpeg_read_header( &state->cinfo, TRUE );

            m_width = state->cinfo.image_width;
            m_height = state->cinfo.image_height;
            m_type = state->cinfo.num_components > 1 ? CV_8UC3 : CV_8UC1;
            result = true;
        }
    }

    if( !result )
        close();

    return result;
}
Ejemplo n.º 5
0
jpeghdr_t *decode_jpeg_raw_hdr(unsigned char *jpeg_data, int len)
{
    struct jpeg_decompress_struct dinfo;
    jpeg_create_decompress(&dinfo);

    jpeg_buffer_src(&dinfo, jpeg_data, len);

    jpeg_read_header(&dinfo, TRUE);
   
	jpeghdr_t *j = (jpeghdr_t*) malloc(sizeof(jpeghdr_t));
	j->jpeg_color_space= dinfo.jpeg_color_space;
	j->width = dinfo.image_width;
	j->height= dinfo.image_height;
	j->num_components = dinfo.num_components;
	j->ccir601 = dinfo.CCIR601_sampling;
	j->version[0] = dinfo.JFIF_major_version;
	j->version[1] = dinfo.JFIF_minor_version;

    jpeg_destroy_decompress(&dinfo);

    return j;
}
Ejemplo n.º 6
0
/*
 * jpeg_data:       Buffer with jpeg data to decode
 * len:             Length of buffer
 * itype:           0: Not interlaced
 *                  1: Interlaced, Top field first
 *                  2: Interlaced, Bottom field first
 * ctype            Chroma format for decompression.
 *                  Currently only Y4M_CHROMA_{420JPEG,422} are available
 * returns:
 *    -1 on fatal error
 *    0 on success
 *    1 if jpeg lib threw a "corrupt jpeg data" warning.
 *        in this case, "a damaged output image is likely."
 *
 */
int decode_jpeg_raw (unsigned char *jpeg_data, int len,
                     int itype, int ctype, unsigned int width,
                     unsigned int height, unsigned char *raw0,
                     unsigned char *raw1, unsigned char *raw2)
{
    int numfields, hsf[3], field, yl, yc;
    int i, xsl, xsc, xs, hdown;
    unsigned int x, y = 0, vsf[3], xd;

    JSAMPROW row0[16] = { buf0[0], buf0[1], buf0[2], buf0[3],
                          buf0[4], buf0[5], buf0[6], buf0[7],
                          buf0[8], buf0[9], buf0[10], buf0[11],
                          buf0[12], buf0[13], buf0[14], buf0[15]};

    JSAMPROW row1[8] = { buf1[0], buf1[1], buf1[2], buf1[3],
                         buf1[4], buf1[5], buf1[6], buf1[7]};

    JSAMPROW row2[16] = { buf2[0], buf2[1], buf2[2], buf2[3],
                          buf2[4], buf2[5], buf2[6], buf2[7]};

    JSAMPROW row1_444[16], row2_444[16];

    JSAMPARRAY scanarray[3] = { row0, row1, row2};

    struct jpeg_decompress_struct dinfo;
    struct my_error_mgr jerr;

    /* We set up the normal JPEG error routines, then override error_exit. */
    dinfo.err = jpeg_std_error (&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    /* Also hook the emit_message routine to note corrupt-data warnings. */
    jerr.original_emit_message = jerr.pub.emit_message;
    jerr.pub.emit_message = my_emit_message;
    jerr.warning_seen = 0;

    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp (jerr.setjmp_buffer)) {
        /* If we get here, the JPEG code has signaled an error. */
        jpeg_destroy_decompress (&dinfo);
        return -1;
    }

    jpeg_create_decompress (&dinfo);

    jpeg_buffer_src (&dinfo, jpeg_data, len);

    /*
     * Read header, make some checks and try to figure out what the
     * user really wants.
     */
    jpeg_read_header (&dinfo, TRUE);
    dinfo.raw_data_out = TRUE;
#if JPEG_LIB_VERSION >= 70    
    dinfo.do_fancy_upsampling = FALSE;
#endif    
    dinfo.out_color_space = JCS_YCbCr;
    dinfo.dct_method = JDCT_IFAST;
    guarantee_huff_tables(&dinfo);
    jpeg_start_decompress (&dinfo);

    if (dinfo.output_components != 3) {
        MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Output components of JPEG image"
                   " = %d, must be 3", dinfo.output_components);
        goto ERR_EXIT;
    }

    for (i = 0; i < 3; i++) {
        hsf[i] = dinfo.comp_info[i].h_samp_factor;
        vsf[i] = dinfo.comp_info[i].v_samp_factor;
    }

    if ((hsf[0] != 2 && hsf[0] != 1) || hsf[1] != 1 || hsf[2] != 1 ||
        (vsf[0] != 1 && vsf[0] != 2) || vsf[1] != 1 || vsf[2] != 1) {
        MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Unsupported sampling factors,"
                   " hsf=(%d, %d, %d) vsf=(%d, %d, %d) !", hsf[0], hsf[1],
                   hsf[2], vsf[0], vsf[1], vsf[2]);
        goto ERR_EXIT;
    }

    if (hsf[0] == 1) {
        if (height % 8 != 0) {
            MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: YUV 4:4:4 sampling, but image"
                       " height %d not dividable by 8 !", height);
            goto ERR_EXIT;
        }

        for (y = 0; y < 16; y++) { // Allocate a special buffer for the extra sampling depth.
            row1_444[y] = (unsigned char *)malloc(dinfo.output_width * sizeof(char));
            row2_444[y] = (unsigned char *)malloc(dinfo.output_width * sizeof(char));
        }
        scanarray[1] = row1_444;
        scanarray[2] = row2_444;
    }

    /* Height match image height or be exact twice the image height. */

    if (dinfo.output_height == height) {
        numfields = 1;
    } else if (2 * dinfo.output_height == height) {
        numfields = 2;
    } else {
        MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Read JPEG: requested height = %d, "
                   "height of image = %d", height, dinfo.output_height);
        goto ERR_EXIT;
    }

    /* Width is more flexible */

    if (dinfo.output_width > MAX_LUMA_WIDTH) {
        MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image width of %d exceeds max",
                   dinfo.output_width);
        goto ERR_EXIT;
    }

    if (width < 2 * dinfo.output_width / 3) {
        /* Downsample 2:1 */
        hdown = 1;
        if (2 * width < dinfo.output_width)
            xsl = (dinfo.output_width - 2 * width) / 2;
        else
            xsl = 0;
    } else if (width == 2 * dinfo.output_width / 3) {
        /* Special case of 3:2 downsampling */
      hdown = 2;
      xsl = 0;
    } else {
        /* No downsampling */
        hdown = 0;
        if (width < dinfo.output_width)
            xsl = (dinfo.output_width - width) / 2;
        else
            xsl = 0;
   }

    /* Make xsl even, calculate xsc */

    xsl = xsl & ~1;
    xsc = xsl / 2;

    yl = yc = 0;

    for (field = 0; field < numfields; field++) {
        if (field > 0) {
            jpeg_read_header (&dinfo, TRUE);
            dinfo.raw_data_out = TRUE;
#if JPEG_LIB_VERSION >= 70            
            dinfo.do_fancy_upsampling = FALSE;
#endif            
            dinfo.out_color_space = JCS_YCbCr;
            dinfo.dct_method = JDCT_IFAST;
            jpeg_start_decompress (&dinfo);
        }

        if (numfields == 2) {
            switch (itype) {
            case Y4M_ILACE_TOP_FIRST:
                yl = yc = field;
                break;
            case Y4M_ILACE_BOTTOM_FIRST:
                yl = yc = (1 - field);
                break;
            default:
                MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Input is interlaced but"
                           " no interlacing set");
                goto ERR_EXIT;
            }
        } else {
            yl = yc = 0;
        }

        while (dinfo.output_scanline < dinfo.output_height) {
            /* Read raw data */
            jpeg_read_raw_data (&dinfo, scanarray, 8 * vsf[0]);

            for (y = 0; y < 8 * vsf[0]; yl += numfields, y++) {
                xd = yl * width;
                xs = xsl;

                if (hdown == 0) {
                    for (x = 0; x < width; x++)
                        raw0[xd++] = row0[y][xs++];
                } else if (hdown == 1) {
                    for (x = 0; x < width; x++, xs += 2)
                        raw0[xd++] = (row0[y][xs] + row0[y][xs + 1]) >> 1;
                } else {
                    for (x = 0; x < width / 2; x++, xd += 2, xs += 3) {
                        raw0[xd] = (2 * row0[y][xs] + row0[y][xs + 1]) / 3;
                        raw0[xd + 1] = (2 * row0[y][xs + 2] + row0[y][xs + 1]) / 3;
                    }
                }
            }

            /* Horizontal downsampling of chroma */

            for (y = 0; y < 8; y++) {
                xs = xsc;

                if (hsf[0] == 1)
                    for (x = 0; x < width / 2; x++, xs++) {
                        row1[y][xs] = (row1_444[y][2*x] + row1_444[y][2*x + 1]) >> 1;
                        row2[y][xs] = (row2_444[y][2*x] + row2_444[y][2*x + 1]) >> 1;
                    }

                xs = xsc;
                if (hdown == 0) {
                    for (x = 0; x < width / 2; x++, xs++) {
                        chr1[y][x] = row1[y][xs];
                        chr2[y][x] = row2[y][xs];
                    }
                } else if (hdown == 1) {
                    for (x = 0; x < width / 2; x++, xs += 2) {
                        chr1[y][x] = (row1[y][xs] + row1[y][xs + 1]) >> 1;
                        chr2[y][x] = (row2[y][xs] + row2[y][xs + 1]) >> 1;
                    }
                } else {
                    for (x = 0; x < width / 2; x += 2, xs += 3) {
                        chr1[y][x] = (2 * row1[y][xs] + row1[y][xs + 1]) / 3;
                        chr1[y][x + 1] = (2 * row1[y][xs + 2] + row1[y][xs + 1]) / 3;
                        chr2[y][x] = (2 * row2[y][xs] + row2[y][xs + 1]) / 3;
                        chr2[y][x + 1] = (2 * row2[y][xs + 2] + row2[y][xs + 1]) / 3;
                    }
                }
            }

            /* Vertical resampling of chroma */

            switch (ctype) {
            case Y4M_CHROMA_422:
                if (vsf[0] == 1) {
                    /* Just copy */
                    for (y = 0; y < 8 /*&& yc < height */; y++, yc += numfields) {
                        xd = yc * width / 2;

                        for (x = 0; x < width / 2; x++, xd++) {
                            raw1[xd] = chr1[y][x];
                            raw2[xd] = chr2[y][x];
                        }
                    }
                } else {
                    /* upsample */
                    for (y = 0; y < 8 /*&& yc < height */; y++) {
                        xd = yc * width / 2;

                        for (x = 0; x < width / 2; x++, xd++) {
                            raw1[xd] = chr1[y][x];
                            raw2[xd] = chr2[y][x];
                        }

                        yc += numfields;
                        xd = yc * width / 2;

                        for (x = 0; x < width / 2; x++, xd++) {
                            raw1[xd] = chr1[y][x];
                            raw2[xd] = chr2[y][x];
                        }

                        yc += numfields;
                    }
                }
                break;
            default:
            /*
             * Should be case Y4M_CHROMA_420JPEG: but use default: for compatibility. Some
             * pass things like '420' in with the expectation that anything other than
             * Y4M_CHROMA_422 will default to 420JPEG.
             */
                if (vsf[0] == 1) {
                    /* Really downsample */
                    for (y = 0; y < 8 /*&& yc < height/2*/; y += 2, yc += numfields) {
                        xd = yc * width / 2;

                        for (x = 0; x < width / 2; x++, xd++) {
                            assert(xd < (width * height / 4));
                            raw1[xd] = (chr1[y][x] + chr1[y + 1][x]) >> 1;
                            raw2[xd] = (chr2[y][x] + chr2[y + 1][x]) >> 1;
                        }
                    }

                } else {
                    /* Just copy */
                    for (y = 0; y < 8 /* && yc < height / 2 */; y++, yc += numfields) {
                        xd = yc * width / 2;

                        for (x = 0; x < width / 2; x++, xd++) {
                            raw1[xd] = chr1[y][x];
                            raw2[xd] = chr2[y][x];
                        }
                    }
                }
                break;
            }
Ejemplo n.º 7
0
int read_JPEG_buffer(struct jpeg_decompress_struct *cinfo, //header
                     unsigned char*jpgbuffer, //src buffer, stores mjpeg
                     long buffersize, //src buffer size
                     unsigned char* destbuffer, //destination buffer, rgb
                     long destbuffersize,//buffer size
                     struct img_info* image_info , //image info
                     int format)
{
    int row_stride;		/* physical row width in output buffer */


    if (DEBUG)printf("come in\n");
    /* Step 2: specify data source (eg, a file) */

    if (DEBUG)printf("----------------jpeg_buffer_src--------------\n");
    jpeg_buffer_src(cinfo, jpgbuffer,buffersize);
    if (DEBUG)printf("----------------after jpeg_buffer_src--------------\n");

    /* Step 3: read file parameters with jpeg_read_header() */
    if (DEBUG)printf("read header...\n");
    int headerret= jpeg_read_header(cinfo, TRUE);
    if (DEBUG)printf("jpeg_read_header %d %d\n",headerret,JPEG_HEADER_OK);
    if (DEBUG)printf("read header end...\n");
    /* We can ignore the return value from jpeg_read_header since
     *   (a) suspension is not possible with the stdio data source, and
     *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
     * See libjpeg.doc for more info.
     */

    //debug info: output header information
    /*
    if(DEBUG)printf("JPEG PARAMETERS:\n");
    if(DEBUG)printf("Width Height %d %d\n",cinfo->image_width,
      cinfo->image_height);
    if(DEBUG)printf("COLOR SPACE %d %d\n",cinfo->jpeg_color_space,JCS_YCbCr);

    if(DEBUG)printf("OUT COLOR SPCAE: %d\n",cinfo->out_color_space);

    if(DEBUG)printf("QUANTIZE COLORS: %d\n",cinfo->quantize_colors);
    */
    if (format==1)
        cinfo->out_color_space=JCS_YCbCr;
    if (image_info!=NULL) {
        image_info->width=cinfo->image_width;
        image_info->height=cinfo->image_height;
        if (DEBUG)printf("Width Height %d %d\n",cinfo->image_width,
                             cinfo->image_height);
    }

    if (fatal_error) {
        if (DEBUG)printf("fatal error %d\n",fatal_error);
        return -1;
    }
    //for mjpeg pictures, huffman table must be added.
    //extracted from libquicktime
    if (DEBUG)printf("guarantee_huff_tables\n");
    guarantee_huff_tables(cinfo);


    /* Step 4: set parameters for decompression */

    /* In this example, we don't need to change any of the defaults set by
     * jpeg_read_header(), so we do nothing here.
     */

    /* Step 5: Start decompressor */

    (void) jpeg_start_decompress(cinfo);
    /* We can ignore the return value since suspension is not possible
     * with the stdio data source.
     */

    /* We may need to do some setup of our own at this point before reading
     * the data.  After jpeg_start_decompress() we have the correct scaled
     * output image dimensions available, as well as the output colormap
     * if we asked for color quantization.
     * In this example, we need to make an output work buffer of the right size.
     */
    /* JSAMPLEs per row in output buffer */
    row_stride = cinfo->output_width * cinfo->output_components;

    //debug info: output_components must be 3, yuv or rgb
    //	if(DEBUG)printf("row_stride: %d output component:%d \n",
    //		row_stride,cinfo->output_components);
    /* Make a one-row-high sample array that will go away when done with image */
    //buffer = (*cinfo->mem->alloc_sarray)
    //		((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, 1);


    //output rgb buffer
    long offset=0;
    //long offset=200;
    //unsigned char* filebuf=malloc(row_stride*cinfo->output_height+offset);
    //sif(DEBUG)printf(filebuf,"%d %d %d\ntype rgb, offset 0, width 1, height 2\n",offset,cinfo->output_width,cinfo->output_height);
    long start=0;
    /* Step 6: while (scan lines remain to be read) */
    /*           jpeg_read_scanlines(...); */

    /* Here we use the library's state variable cinfo.output_scanline as the
     * loop counter, so that we don't have to keep track ourselves.
     */
    int read_line=0;
    while (cinfo->output_scanline < cinfo->output_height) {
        /* jpeg_read_scanlines expects an array of pointers to scanlines.
         * Here the array is only one element long, but you could ask for
         * more than one scanline at a time if that's more convenient.
         */
        read_line=jpeg_read_scanlines(cinfo, buffer,1);// cinfo.image_height);

        //debug info
        //if(DEBUG)printf("output_scan line %d height %d read lines: %d\n",
        //	cinfo->output_scanline,
        //	cinfo->output_height,read_line);

        /* Assume put_scanline_someplace wants a pointer and sample count. */
        //put_scanline_someplace(buffer[0], row_stride);
        memcpy(destbuffer+offset+start,buffer[0],row_stride);
        start+=row_stride;
        if (start>destbuffersize) {
            if (DEBUG)fprintf(stderr,"destination buffer overflow while decompressing jpeg files.\n");
            exit(-1);
        }
    }

    //debug
    //msave("1.tmp",filebuf,start);
    /* Step 7: Finish decompression */

    (void) jpeg_finish_decompress(cinfo);
    /* We can ignore the return value since suspension is not possible
     * with the stdio data source.
     */

    /* And we're done! */
    return 1;
}
Ejemplo n.º 8
0
int decode_jpeg_raw(unsigned char *jpeg_data, int len,
		    int itype, int ctype, int width, int height,
		    unsigned char *raw0, unsigned char *raw1,
		    unsigned char *raw2)           
{
    int numfields, hsf[3], vsf[3], field, yl, yc, x, y =
	0, i, xsl, xsc, xs, xd, hdown;

    JSAMPROW row0[16] = { buf0[0], buf0[1], buf0[2], buf0[3],
	buf0[4], buf0[5], buf0[6], buf0[7],
	buf0[8], buf0[9], buf0[10], buf0[11],
	buf0[12], buf0[13], buf0[14], buf0[15]
    };
    JSAMPROW row1[8] = { buf1[0], buf1[1], buf1[2], buf1[3],
	buf1[4], buf1[5], buf1[6], buf1[7]
    };
    JSAMPROW row2[16] = { buf2[0], buf2[1], buf2[2], buf2[3],
	buf2[4], buf2[5], buf2[6], buf2[7]
    };
    JSAMPROW row1_444[16], row2_444[16];
    JSAMPARRAY scanarray[3] = { row0, row1, row2 };
    struct jpeg_decompress_struct dinfo;
    struct my_error_mgr jerr;

    /* We set up the normal JPEG error routines, then override error_exit. */
    dinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) {
	/* If we get here, the JPEG code has signaled an error. */
	jpeg_destroy_decompress(&dinfo);
	return -1;
    }

    jpeg_create_decompress(&dinfo);

    jpeg_buffer_src(&dinfo, jpeg_data, len);

    /* Read header, make some checks and try to figure out what the
       user really wants */

    jpeg_read_header(&dinfo, TRUE);
    dinfo.raw_data_out = TRUE;
    dinfo.out_color_space = JCS_YCbCr;
//   dinfo.dct_method = (_dct_method == 0 ? JDCT_DEFAULT : JDCT_FLOAT);
    dinfo.dct_method = JDCT_DEFAULT;
    guarantee_huff_tables(&dinfo);
    jpeg_start_decompress(&dinfo);

    if (dinfo.output_components != 3) {
	veejay_msg(0,"Output components of JPEG image = %d, must be 3",
		    dinfo.output_components);
	goto ERR_EXIT;
    }

    for (i = 0; i < 3; i++) {
	hsf[i] = dinfo.comp_info[i].h_samp_factor;
	vsf[i] = dinfo.comp_info[i].v_samp_factor;
    }

    if ((hsf[0] != 2 && hsf[0] != 1) || hsf[1] != 1 || hsf[2] != 1 ||
	(vsf[0] != 1 && vsf[0] != 2) || vsf[1] != 1 || vsf[2] != 1) {
	veejay_msg
	    (0,"Unsupported sampling factors, hsf=(%d, %d, %d) vsf=(%d, %d, %d) !",
	     hsf[0], hsf[1], hsf[2], vsf[0], vsf[1], vsf[2]);
	goto ERR_EXIT;
    }

    if (hsf[0] == 1) {
	if (height % 8 != 0) {
	    veejay_msg
		(0,"YUV 4:4:4 sampling, but image height %d not dividable by 8 !\n",
		 height);
	    goto ERR_EXIT;
	}

	mjpeg_info
	    ("YUV 4:4:4 sampling encountered ! Allocating special row buffer\n");
	for (y = 0; y < 16; y++)	// allocate a special buffer for the extra sampling depth
	{
	    //mjpeg_info("YUV 4:4:4 %d.\n",y);
	    row1_444[y] =
		(unsigned char *) malloc(dinfo.output_width *
					 sizeof(char));
	    row2_444[y] =
		(unsigned char *) malloc(dinfo.output_width *
					 sizeof(char));
	}
	//mjpeg_info("YUV 4:4:4 sampling encountered ! Allocating done.\n");
	scanarray[1] = row1_444;
	scanarray[2] = row2_444;
    }

    /* Height match image height or be exact twice the image height */

    if (dinfo.output_height == height) {
	numfields = 1;
    } else if (2 * dinfo.output_height == height) {
	numfields = 2;
    } else {
	veejay_msg
	    (0,"Read JPEG: requested height = %d, height of image = %d",
	     height, dinfo.output_height);
	goto ERR_EXIT;
    }

    /* Width is more flexible */

    if (dinfo.output_width > MAX_LUMA_WIDTH) {
	veejay_msg(0,"Image width of %d exceeds max", dinfo.output_width);
	goto ERR_EXIT;
    }
    if (width < 2 * dinfo.output_width / 3) {
	/* Downclip 2:1 */

	hdown = 1;
	if (2 * width < dinfo.output_width)
	    xsl = (dinfo.output_width - 2 * width) / 2;
	else
	    xsl = 0;
    } else if (width == 2 * dinfo.output_width / 3) {
	/* special case of 3:2 downsampling */

	hdown = 2;
	xsl = 0;
    } else {
	/* No downsampling */

	hdown = 0;
	if (width < dinfo.output_width)
	    xsl = (dinfo.output_width - width) / 2;
	else
	    xsl = 0;
    }

    /* Make xsl even, calculate xsc */

    xsl = xsl & ~1;
    xsc = xsl / 2;

    yl = yc = 0;

    for (field = 0; field < numfields; field++) {
	if (field > 0) {
	    jpeg_read_header(&dinfo, TRUE);
	    dinfo.raw_data_out = TRUE;
	    dinfo.out_color_space = JCS_YCbCr;
	    dinfo.dct_method = JDCT_FLOAT; //JDCT_DEFAULT;            
	    jpeg_start_decompress(&dinfo);
	}

	if (numfields == 2) {
	    switch (itype) {
	    case LAV_INTER_TOP_FIRST:
		yl = yc = field;
		break;
	    case LAV_INTER_BOTTOM_FIRST:
		yl = yc = (1 - field);
		break;
	    default:
		veejay_msg(0,"Input is interlaced but no interlacing set");
		goto ERR_EXIT;
	    }
	} else
	    yl = yc = 0;

	while (dinfo.output_scanline < dinfo.output_height) {
	    /* read raw data */
	    jpeg_read_raw_data(&dinfo, scanarray, 8 * vsf[0]);

	    for (y = 0; y < 8 * vsf[0]; yl += numfields, y++) {
		xd = yl * width;
		xs = xsl;

		if (hdown == 0)
		    for (x = 0; x < width; x++)
			raw0[xd++] = row0[y][xs++];
		else if (hdown == 1)
		    for (x = 0; x < width; x++, xs += 2)
			raw0[xd++] = (row0[y][xs] + row0[y][xs + 1]) >> 1;
		else
		    for (x = 0; x < width / 2; x++, xd += 2, xs += 3) {
			raw0[xd] = (2 * row0[y][xs] + row0[y][xs + 1]) / 3;
			raw0[xd + 1] =
			    (2 * row0[y][xs + 2] + row0[y][xs + 1]) / 3;
		    }
	    }

	    /* Horizontal downsampling of chroma */

	    for (y = 0; y < 8; y++) {
		xs = xsc;

		if (hsf[0] == 1)
		    for (x = 0; x < width / 2; x++, xs++) {
			row1[y][xs] =
			    (row1_444[y][2 * x] +
			     row1_444[y][2 * x + 1]) >> 1;
			row2[y][xs] =
			    (row2_444[y][2 * x] +
			     row2_444[y][2 * x + 1]) >> 1;
		    }

		xs = xsc;
		if (hdown == 0)
		    for (x = 0; x < width / 2; x++, xs++) {
			chr1[y][x] = row1[y][xs];
			chr2[y][x] = row2[y][xs];
		} else if (hdown == 1)
		    for (x = 0; x < width / 2; x++, xs += 2) {
			chr1[y][x] = (row1[y][xs] + row1[y][xs + 1]) >> 1;
			chr2[y][x] = (row2[y][xs] + row2[y][xs + 1]) >> 1;
		} else
		    for (x = 0; x < width / 2; x += 2, xs += 3) {
			chr1[y][x] =
			    (2 * row1[y][xs] + row1[y][xs + 1]) / 3;
			chr1[y][x + 1] =
			    (2 * row1[y][xs + 2] + row1[y][xs + 1]) / 3;
			chr2[y][x] =
			    (2 * row2[y][xs] + row2[y][xs + 1]) / 3;
			chr2[y][x + 1] =
			    (2 * row2[y][xs + 2] + row2[y][xs + 1]) / 3;
		    }
	    }

	    /* Vertical downsampling of chroma */

	    if (vsf[0] == 1) {
		for (y = 0; y < 8 /*&& yc < height/2 */ ;
		     y += 2, yc += numfields) {
		    xd = yc * width / 2;
		    for (x = 0; x < width / 2; x++, xd++) {
			assert(xd < (width * height / 4));
			raw1[xd] = (chr1[y][x] + chr1[y + 1][x]) >> 1;
			raw2[xd] = (chr2[y][x] + chr2[y + 1][x]) >> 1;
		    }
		}

	    } else {
		/* Just copy */
		for (y = 0; y < 8 /*&& yc < height/2 */ ;
static int
jpeg_image_control(ErlDrvData handle, unsigned int command, 
		   char* buf, int count, 
		   char** res, int res_size)
{
  JSAMPROW row;
  ErlDrvBinary* bin = 0;

  switch (command) {
  case 0: {			/* Read */
    struct jpeg_decompress_struct cinfo;
    int row_stride;		/* physical row width in output buffer */
    unsigned char* rbuf;
    struct my_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) {
      /* If we get here, the JPEG code has signaled an error.
       * We need to clean up the JPEG object, close the input file, and return.
       */
      char buffer[JMSG_LENGTH_MAX];

      /* Create the message */
      (cinfo.err->format_message)((j_common_ptr) &cinfo, buffer);
      jpeg_destroy_decompress(&cinfo);

      bin = driver_alloc_binary(4+strlen(buffer));
      rbuf = bin->orig_bytes;
      ((unsigned *)rbuf)[0] = 0;
      rbuf += 4;
      memcpy(rbuf, buffer, strlen(buffer));
      *res = (void *) bin;
      return 0;
    }

    jpeg_create_decompress(&cinfo);
    jpeg_buffer_src(&cinfo, buf, count);
    (void) jpeg_read_header(&cinfo, TRUE);
    (void) jpeg_start_decompress(&cinfo);

    row_stride = cinfo.output_width * cinfo.output_components;
    res_size = row_stride * cinfo.output_height;
    bin = driver_alloc_binary(res_size+12);
    rbuf = bin->orig_bytes;
    ((unsigned *)rbuf)[0] = cinfo.output_width;
    ((unsigned *)rbuf)[1] = cinfo.output_height;
    ((unsigned *)rbuf)[2] = cinfo.output_components;
    rbuf += 12;
    while (cinfo.output_scanline < cinfo.output_height) {
      row = (JSAMPROW) rbuf;
      (void) jpeg_read_scanlines(&cinfo, &row, 1);
      rbuf += row_stride;
    }
    (void) jpeg_finish_decompress(&cinfo);
    *res = (void *) bin;
    return 0;
  }
  case 1: {			/* Write */
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    int row_stride;		/* physical row width */

    bin = driver_alloc_binary(count);

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

    jpeg_buffer_dest(&cinfo, bin);
    cinfo.image_width = ((unsigned *)buf)[0];
    cinfo.image_height = ((unsigned *)buf)[1];
    cinfo.input_components = ((unsigned *)buf)[2];
    cinfo.in_color_space = JCS_RGB;
    jpeg_set_defaults(&cinfo);
    buf += 12;
    count -= 12;

    jpeg_start_compress(&cinfo, TRUE);
    row_stride = cinfo.input_components * cinfo.image_width;
        
    while (cinfo.next_scanline < cinfo.image_height) {
      row = (JSAMPROW) buf;
      (void) jpeg_write_scanlines(&cinfo, &row, 1);
      buf += row_stride;
    }

    jpeg_finish_compress(&cinfo);
    bin = jpeg_buffer_dest_get_bin(&cinfo);
    jpeg_destroy_compress(&cinfo);
    *res = (void *) bin;
    return 0;
  }
  default:
    return -1;			/* Error return, throws exception in erlang */
  }
}