diff options
Diffstat (limited to 'android/utils/jpeg-compress.c')
-rw-r--r-- | android/utils/jpeg-compress.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/android/utils/jpeg-compress.c b/android/utils/jpeg-compress.c new file mode 100644 index 0000000..2b42a00 --- /dev/null +++ b/android/utils/jpeg-compress.c @@ -0,0 +1,197 @@ +/* Copyright (C) 2011 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#include <stdint.h> +#include "jinclude.h" +#include "jpeglib.h" +#include "jpeg-compress.h" +#include "panic.h" + +/* Implements JPEG destination manager's init_destination routine. */ +static void _on_init_destination(j_compress_ptr cinfo); +/* Implements JPEG destination manager's empty_output_buffer routine. */ +static boolean _on_empty_output_buffer(j_compress_ptr cinfo); +/* Implements JPEG destination manager's term_destination routine. */ +static void _on_term_destination(j_compress_ptr cinfo); + +/* JPEG compression descriptor. */ +struct AJPEGDesc { + /* Common JPEG compression destination manager header. */ + struct jpeg_destination_mgr common; + /* Buffer where to save compressed output. */ + uint8_t* jpeg_buf; + /* Byte size of the 'jpeg_buf' */ + int size; + /* Chunk size to increment the 'jpeg_buf' with on each allocation request. */ + int chunk_size; + /* Size of the header to put in front of the compressed data. */ + int header_size; +}; + +/******************************************************************************** + * jpeglib callbacks. + *******************************************************************************/ + +/* Implements JPEG destination manager's init_destination routine. */ +static void +_on_init_destination(j_compress_ptr cinfo) +{ + AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest; + if (dst->jpeg_buf == NULL) { + /* This is the first time our destination manager is initialized. + * Allocate minimal buffer. */ + dst->size = dst->chunk_size; + dst->jpeg_buf = malloc(dst->size); + if (dst->jpeg_buf == NULL) { + APANIC("Unable to allocate %d bytes for JPEG compression", dst->size); + } + } + /* Initialize common header with entire destination buffer. */ + dst->common.next_output_byte = dst->jpeg_buf + dst->header_size; + dst->common.free_in_buffer = dst->size - dst->header_size; +} + +/* Implements JPEG destination manager's empty_output_buffer routine. + * Name is a bit misleading here. This routine is called by the compressor when + * output buffer doesn't have enough free space to contain the next chunk of the + * compressed data. So, here we should reallocate the output buffer, rather than + * "empty" it. + */ +static boolean +_on_empty_output_buffer(j_compress_ptr cinfo) +{ + AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest; + /* Save already compressed data size. */ + const int accumulated = jpeg_compressor_get_jpeg_size(dst); + + /* Reallocate output buffer. */ + dst->size += dst->chunk_size; + dst->jpeg_buf = realloc(dst->jpeg_buf, dst->size); + if (dst->jpeg_buf == NULL) { + APANIC("Unable to allocate %d bytes for JPEG compression", dst->size); + } + + /* Update common header. */ + dst->common.next_output_byte = dst->jpeg_buf + accumulated + dst->header_size; + dst->common.free_in_buffer = dst->size - accumulated - dst->header_size; + + return TRUE; +} + +/* Implements JPEG destination manager's term_destination routine. + * We don't do anything here. All the cleanup will be performed when the user + * calls jpeg_compressor_destroy. */ +static void +_on_term_destination(j_compress_ptr cinfo) +{ +} + +/******************************************************************************** + * JPEG compressor API. + *******************************************************************************/ + +AJPEGDesc* +jpeg_compressor_create(int header_size, int chunk_size) +{ + AJPEGDesc* dsc = (AJPEGDesc*)malloc(sizeof(AJPEGDesc)); + if (dsc == NULL) { + APANIC("Unable to allocate JPEG compression descriptor."); + } + + dsc->common.next_output_byte = NULL; + dsc->common.free_in_buffer = 0; + dsc->common.init_destination = _on_init_destination; + dsc->common.empty_output_buffer = _on_empty_output_buffer; + dsc->common.term_destination = _on_term_destination; + dsc->jpeg_buf = NULL; + dsc->size = 0; + dsc->chunk_size = chunk_size; + dsc->header_size = header_size; + return dsc; +} + +void +jpeg_compressor_destroy(AJPEGDesc* dsc) +{ + if (dsc != NULL) { + if (dsc->jpeg_buf != NULL) { + free(dsc->jpeg_buf); + } + free(dsc); + } +} + +int +jpeg_compressor_get_jpeg_size(const AJPEGDesc* dsc) +{ + return (dsc->jpeg_buf == NULL) ? 0 : + (uint8_t*)dsc->common.next_output_byte - dsc->jpeg_buf - dsc->header_size; +} + +void* +jpeg_compressor_get_buffer(const AJPEGDesc* dsc) +{ + return dsc->jpeg_buf; +} + +int +jpeg_compressor_get_header_size(const AJPEGDesc* dsc) +{ + return dsc->header_size; +} + +void +jpeg_compressor_compress_fb(AJPEGDesc* dsc, + int x, int y, int w, int h, + int bpp, int bpl, + const uint8_t* fb, + int jpeg_quality){ + struct jpeg_compress_struct cinfo = {0}; + struct jpeg_error_mgr err_mgr; + + /* + * Initialize compressin information structure, and start compression + */ + + cinfo.err = jpeg_std_error(&err_mgr); + jpeg_create_compress(&cinfo); + cinfo.dest = &dsc->common; + cinfo.image_width = w; + cinfo.image_height = h; + + /* Decode framebuffer's pixel format. There can be only three: + * - RGB565, + * - RGBA8888, + * - RGBX8888 */ + if (bpp == 2) { + /* This is RGB565 - most commonly used pixel format for framebuffer. */ + cinfo.input_components = 2; + cinfo.in_color_space = JCS_RGB_565; + } else { + /* RGBA8888, or RGBX8888 - makes no difference here. */ + cinfo.input_components = 4; + cinfo.in_color_space = JCS_RGBA_8888; + } + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, jpeg_quality, TRUE); + jpeg_start_compress(&cinfo, TRUE); + + /* Line by line compress the region. */ + while (cinfo.next_scanline < cinfo.image_height) { + JSAMPROW rgb = (JSAMPROW)(fb + (cinfo.next_scanline + y) * bpl + x * bpp); + jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1); + } + + /* Complete the compression. */ + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); +} |