guacamole-spice-protocol/src/guacenc/jpeg.c
2016-03-28 20:39:19 -07:00

151 lines
4.4 KiB
C

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "jpeg.h"
#include "log.h"
#include <stdio.h>
#include <unistd.h>
#include <cairo/cairo.h>
#include <jpeglib.h>
#include <stdlib.h>
/**
* Translates libjpeg's 24-bit RGB format into Cairo's 32-bit ARGB32 / RGB24
* format. The red, green, and blue components from the libjpeg pixel are
* copied verbatim, while the extra high byte used within Cairo is set to 0xFF.
*
* @param src
* A pointer to the first byte of the 24-bit RGB pixel within a libjpeg
* scanline buffer.
*
* @return
* A 32-bit Cairo ARGB32 / RGB24 pixel value equivalent to the libjpeg
* pixel at the given pointer.
*/
static uint32_t guacenc_jpeg_translate_rgb(const unsigned char* src) {
/* Pull components from source */
int r = *(src++);
int g = *(src++);
int b = *(src++);
/* Translate to 32-bit integer compatible with Cairo */
return 0xFF000000 | (r << 16) | (g << 8) | b;
}
/**
* Copies the data from a libjpeg scanline buffer into a row of image data
* within a Cairo surface, translating each pixel as necessary.
*
* @param dst
* The destination buffer into which the scanline should be copied.
*
* @param src
* The libjpeg scanline buffer that should be copied into the
* destination buffer.
*
* @param width
* The number of pixels available within both the scanline buffer and the
* destination buffer.
*/
static void guacenc_jpeg_copy_scanline(unsigned char* dst,
const unsigned char* src, int width) {
uint32_t* current = (uint32_t*) dst;
/* Copy all pixels from source to destination, translating for Cairo */
for (; width > 0; width--, src += 3) {
*(current++) = guacenc_jpeg_translate_rgb(src);
}
}
cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* Create decompressor with standard error handling */
jpeg_create_decompress(&cinfo);
cinfo.err = jpeg_std_error(&jerr);
/* Read JPEG directly from memory buffer */
jpeg_mem_src(&cinfo, data, length);
/* Read and validate JPEG header */
if (!jpeg_read_header(&cinfo, TRUE)) {
guacenc_log(GUAC_LOG_WARNING, "Invalid JPEG data");
jpeg_destroy_decompress(&cinfo);
return NULL;
}
/* Begin decompression */
cinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&cinfo);
/* Pull JPEG dimensions from decompressor */
int width = cinfo.output_width;
int height = cinfo.output_height;
/* Allocate sufficient buffer space for one JPEG scanline */
unsigned char* jpeg_scanline = malloc(width * 3);
/* Create blank Cairo surface (no transparency in JPEG) */
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
width, height);
/* Pull underlying buffer and its stride */
int stride = cairo_image_surface_get_stride(surface);
unsigned char* row = cairo_image_surface_get_data(surface);
/* Read JPEG into surface */
while (cinfo.output_scanline < height) {
/* Read single scanline */
unsigned char* buffers[1] = { jpeg_scanline };
jpeg_read_scanlines(&cinfo, buffers, 1);
/* Copy scanline to Cairo surface */
guacenc_jpeg_copy_scanline(row, jpeg_scanline, width);
/* Advance to next row of Cairo surface */
row += stride;
}
/* Scanline buffer is no longer needed */
free(jpeg_scanline);
/* End decompression */
jpeg_finish_decompress(&cinfo);
/* Free decompressor */
jpeg_destroy_decompress(&cinfo);
/* JPEG was read successfully */
return surface;
}