int guac_jpeg_write()

in src/libguac/encode-jpeg.c [168:266]


int guac_jpeg_write(guac_socket* socket, guac_stream* stream,
        cairo_surface_t* surface, int quality) {

    /* Get image surface properties and data */
    cairo_format_t format = cairo_image_surface_get_format(surface);

    if (format != CAIRO_FORMAT_RGB24) {
        guac_error = GUAC_STATUS_INTERNAL_ERROR;
        guac_error_message =
            "Invalid Cairo image format. Unable to create JPEG.";
        return -1;
    }

    int width = cairo_image_surface_get_width(surface);
    int height = cairo_image_surface_get_height(surface);
    int stride = cairo_image_surface_get_stride(surface);
    unsigned char* data = cairo_image_surface_get_data(surface);

    /* Flush pending operations to surface */
    cairo_surface_flush(surface);

    /* Prepare JPEG bits */
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    /* Write JPEG directly to given stream */
    jpeg_guac_dest(&cinfo, socket, stream);

    cinfo.image_width = width; /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.arith_code = TRUE;

#ifdef JCS_EXTENSIONS
    /* The Turbo JPEG extensions allows us to use the Cairo surface
     * (BGRx) as input without converting it */
    cinfo.input_components = 4;
    cinfo.in_color_space = JCS_EXT_BGRX;
#else
    /* Standard JPEG supports RGB as input so we will have to convert
     * the contents of the Cairo surface from (BGRx) to RGB */
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    /* Create a buffer for the write scan line which is where we will
     * put the converted pixels (BGRx -> RGB) */
    unsigned char *scanline_data = guac_mem_zalloc(cinfo.image_width, cinfo.input_components);
#endif

    /* Initialize the JPEG compressor */
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW row_pointer[1]; /* pointer to a single row */

    /* Write scanlines to be used in JPEG compression */
    while (cinfo.next_scanline < cinfo.image_height) {

        int row_offset = stride * cinfo.next_scanline;

#ifdef JCS_EXTENSIONS
        /* In Turbo JPEG we can use the raw BGRx scanline  */
        row_pointer[0] = &data[row_offset];
#else
        /* For standard JPEG libraries we have to convert the
         * scanline from 24 bit (4 byte) BGRx to 24 bit (3 byte) RGB */
        unsigned char *inptr = data + row_offset;
        unsigned char *outptr = scanline_data;

        for (int x = 0; x < width; ++x) {

            outptr[2] = *inptr++; /* B */
            outptr[1] = *inptr++; /* G */
            outptr[0] = *inptr++; /* R */
            inptr++; /* skip the upper byte (x/A) */
            outptr += 3;

        }

        row_pointer[0] = scanline_data;
#endif

        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

#ifndef JCS_EXTENSIONS
    guac_mem_free(scanline_data);
#endif

    /* Finalize compression */
    jpeg_finish_compress(&cinfo);

    /* Clean up */
    jpeg_destroy_compress(&cinfo);
    return 0;

}