Home | History | Annotate | Download | only in utils
      1 /* Copyright (C) 2011 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 
     13 #include <stdint.h>
     14 #include "jinclude.h"
     15 #include "jpeglib.h"
     16 #include "jpeg-compress.h"
     17 #include "panic.h"
     18 
     19 /* Implements JPEG destination manager's init_destination routine. */
     20 static void _on_init_destination(j_compress_ptr cinfo);
     21 /* Implements JPEG destination manager's empty_output_buffer routine. */
     22 static boolean _on_empty_output_buffer(j_compress_ptr cinfo);
     23 /* Implements JPEG destination manager's term_destination routine. */
     24 static void _on_term_destination(j_compress_ptr cinfo);
     25 
     26 /* JPEG compression descriptor. */
     27 struct AJPEGDesc {
     28     /* Common JPEG compression destination manager header. */
     29     struct jpeg_destination_mgr     common;
     30     /* Buffer where to save compressed output. */
     31     uint8_t*                        jpeg_buf;
     32     /* Byte size of the 'jpeg_buf' */
     33     int                             size;
     34     /* Chunk size to increment the 'jpeg_buf' with on each allocation request. */
     35     int                             chunk_size;
     36     /* Size of the header to put in front of the compressed data. */
     37     int                             header_size;
     38 };
     39 
     40 /********************************************************************************
     41  *                      jpeglib callbacks.
     42  *******************************************************************************/
     43 
     44 /* Implements JPEG destination manager's init_destination routine. */
     45 static void
     46 _on_init_destination(j_compress_ptr cinfo)
     47 {
     48     AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest;
     49     if (dst->jpeg_buf == NULL) {
     50         /* This is the first time our destination manager is initialized.
     51          * Allocate minimal buffer. */
     52         dst->size = dst->chunk_size;
     53         dst->jpeg_buf = malloc(dst->size);
     54         if (dst->jpeg_buf == NULL) {
     55             APANIC("Unable to allocate %d bytes for JPEG compression", dst->size);
     56         }
     57     }
     58     /* Initialize common header with entire destination buffer. */
     59     dst->common.next_output_byte = dst->jpeg_buf + dst->header_size;
     60     dst->common.free_in_buffer = dst->size - dst->header_size;
     61 }
     62 
     63 /* Implements JPEG destination manager's empty_output_buffer routine.
     64  * Name is a bit misleading here. This routine is called by the compressor when
     65  * output buffer doesn't have enough free space to contain the next chunk of the
     66  * compressed data. So, here we should reallocate the output buffer, rather than
     67  * "empty" it.
     68  */
     69 static boolean
     70 _on_empty_output_buffer(j_compress_ptr cinfo)
     71 {
     72     AJPEGDesc* const dst = (AJPEGDesc*)cinfo->dest;
     73     /* Save already compressed data size. */
     74     const int accumulated = jpeg_compressor_get_jpeg_size(dst);
     75 
     76     /* Reallocate output buffer. */
     77     dst->size += dst->chunk_size;
     78     dst->jpeg_buf = realloc(dst->jpeg_buf, dst->size);
     79     if (dst->jpeg_buf == NULL) {
     80         APANIC("Unable to allocate %d bytes for JPEG compression", dst->size);
     81     }
     82 
     83     /* Update common header. */
     84     dst->common.next_output_byte = dst->jpeg_buf + accumulated + dst->header_size;
     85     dst->common.free_in_buffer = dst->size - accumulated - dst->header_size;
     86 
     87     return TRUE;
     88 }
     89 
     90 /* Implements JPEG destination manager's term_destination routine.
     91  * We don't do anything here. All the cleanup will be performed when the user
     92  * calls jpeg_compressor_destroy. */
     93 static void
     94 _on_term_destination(j_compress_ptr cinfo)
     95 {
     96 }
     97 
     98 /********************************************************************************
     99  *                      JPEG compressor API.
    100  *******************************************************************************/
    101 
    102 AJPEGDesc*
    103 jpeg_compressor_create(int header_size, int chunk_size)
    104 {
    105     AJPEGDesc* dsc = (AJPEGDesc*)malloc(sizeof(AJPEGDesc));
    106     if (dsc == NULL) {
    107         APANIC("Unable to allocate JPEG compression descriptor.");
    108     }
    109 
    110     dsc->common.next_output_byte    = NULL;
    111     dsc->common.free_in_buffer      = 0;
    112     dsc->common.init_destination    = _on_init_destination;
    113     dsc->common.empty_output_buffer = _on_empty_output_buffer;
    114     dsc->common.term_destination    = _on_term_destination;
    115     dsc->jpeg_buf                   = NULL;
    116     dsc->size                       = 0;
    117     dsc->chunk_size                 = chunk_size;
    118     dsc->header_size                = header_size;
    119     return dsc;
    120 }
    121 
    122 void
    123 jpeg_compressor_destroy(AJPEGDesc* dsc)
    124 {
    125     if (dsc != NULL) {
    126         if (dsc->jpeg_buf != NULL) {
    127             free(dsc->jpeg_buf);
    128         }
    129         free(dsc);
    130     }
    131 }
    132 
    133 int
    134 jpeg_compressor_get_jpeg_size(const AJPEGDesc* dsc)
    135 {
    136     return (dsc->jpeg_buf == NULL) ? 0 :
    137         (uint8_t*)dsc->common.next_output_byte - dsc->jpeg_buf - dsc->header_size;
    138 }
    139 
    140 void*
    141 jpeg_compressor_get_buffer(const AJPEGDesc* dsc)
    142 {
    143      return dsc->jpeg_buf;
    144 }
    145 
    146 int
    147 jpeg_compressor_get_header_size(const AJPEGDesc* dsc)
    148 {
    149      return dsc->header_size;
    150 }
    151 
    152 void
    153 jpeg_compressor_compress_fb(AJPEGDesc* dsc,
    154                             int x, int y, int w, int h, int num_lines,
    155                             int bpp, int bpl,
    156                             const uint8_t* fb,
    157                             int jpeg_quality,
    158                             int ydir){
    159     struct jpeg_compress_struct cinfo = {0};
    160     struct jpeg_error_mgr err_mgr;
    161     const int x_shift = x * bpp;
    162 
    163     /*
    164      * Initialize compressin information structure, and start compression
    165      */
    166 
    167     cinfo.err = jpeg_std_error(&err_mgr);
    168     jpeg_create_compress(&cinfo);
    169     cinfo.dest = &dsc->common;
    170     cinfo.image_width = w;
    171     cinfo.image_height = h;
    172 
    173     /* Decode framebuffer's pixel format. There can be only three:
    174      * - RGB565,
    175      * - RGBA8888,
    176      * - RGBX8888 */
    177     if (bpp == 2) {
    178         /* This is RGB565 - most commonly used pixel format for framebuffer. */
    179         cinfo.input_components = 2;
    180         cinfo.in_color_space = JCS_RGB_565;
    181     } else {
    182         /* RGBA8888, or RGBX8888 - makes no difference here. */
    183         cinfo.input_components = 4;
    184         cinfo.in_color_space = JCS_RGBA_8888;
    185     }
    186     jpeg_set_defaults(&cinfo);
    187     jpeg_set_quality(&cinfo, jpeg_quality, TRUE);
    188     jpeg_start_compress(&cinfo, TRUE);
    189 
    190     /* Line by line compress the region. */
    191     if (ydir >= 0) {
    192         while (cinfo.next_scanline < cinfo.image_height) {
    193             JSAMPROW rgb = (JSAMPROW)(fb + (cinfo.next_scanline + y) * bpl + x_shift);
    194             jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
    195         }
    196     } else {
    197         const int y_shift = num_lines - y - 1;
    198         while (cinfo.next_scanline < cinfo.image_height) {
    199             JSAMPROW rgb = (JSAMPROW)(fb + (y_shift - cinfo.next_scanline) * bpl + x_shift);
    200             jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
    201         }
    202     }
    203 
    204     /* Complete the compression. */
    205     jpeg_finish_compress(&cinfo);
    206     jpeg_destroy_compress(&cinfo);
    207 }
    208