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