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. */
20static void _on_init_destination(j_compress_ptr cinfo);
21/* Implements JPEG destination manager's empty_output_buffer routine. */
22static boolean _on_empty_output_buffer(j_compress_ptr cinfo);
23/* Implements JPEG destination manager's term_destination routine. */
24static void _on_term_destination(j_compress_ptr cinfo);
25
26/* JPEG compression descriptor. */
27struct 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. */
45static 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 */
69static 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. */
93static void
94_on_term_destination(j_compress_ptr cinfo)
95{
96}
97
98/********************************************************************************
99 *                      JPEG compressor API.
100 *******************************************************************************/
101
102AJPEGDesc*
103jpeg_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
122void
123jpeg_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
133int
134jpeg_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
140void*
141jpeg_compressor_get_buffer(const AJPEGDesc* dsc)
142{
143     return dsc->jpeg_buf;
144}
145
146int
147jpeg_compressor_get_header_size(const AJPEGDesc* dsc)
148{
149     return dsc->header_size;
150}
151
152void
153jpeg_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