• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
_on_init_destination(j_compress_ptr cinfo)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
_on_empty_output_buffer(j_compress_ptr cinfo)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
_on_term_destination(j_compress_ptr cinfo)94 _on_term_destination(j_compress_ptr cinfo)
95 {
96 }
97 
98 /********************************************************************************
99  *                      JPEG compressor API.
100  *******************************************************************************/
101 
102 AJPEGDesc*
jpeg_compressor_create(int header_size,int chunk_size)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
jpeg_compressor_destroy(AJPEGDesc * dsc)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
jpeg_compressor_get_jpeg_size(const AJPEGDesc * dsc)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*
jpeg_compressor_get_buffer(const AJPEGDesc * dsc)141 jpeg_compressor_get_buffer(const AJPEGDesc* dsc)
142 {
143      return dsc->jpeg_buf;
144 }
145 
146 int
jpeg_compressor_get_header_size(const AJPEGDesc * dsc)147 jpeg_compressor_get_header_size(const AJPEGDesc* dsc)
148 {
149      return dsc->header_size;
150 }
151 
152 void
jpeg_compressor_compress_fb(AJPEGDesc * dsc,int x,int y,int w,int h,int num_lines,int bpp,int bpl,const uint8_t * fb,int jpeg_quality,int ydir)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