1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Compressor.h"
18
19 #define LOG_NDEBUG 0
20 #define LOG_TAG "EmulatedCamera_JPEGStub_Compressor"
21 #include <cutils/log.h>
22 #include <libexif/exif-data.h>
23
Compressor()24 Compressor::Compressor() {
25
26 }
27
compress(const unsigned char * data,int width,int height,int quality,ExifData * exifData)28 bool Compressor::compress(const unsigned char* data,
29 int width, int height, int quality,
30 ExifData* exifData) {
31 if (!configureCompressor(width, height, quality)) {
32 // The method will have logged a more detailed error message than we can
33 // provide here so just return.
34 return false;
35 }
36
37 return compressData(data, exifData);
38 }
39
getCompressedData() const40 const std::vector<uint8_t>& Compressor::getCompressedData() const {
41 return mDestManager.mBuffer;
42 }
43
configureCompressor(int width,int height,int quality)44 bool Compressor::configureCompressor(int width, int height, int quality) {
45 mCompressInfo.err = jpeg_std_error(&mErrorManager);
46 // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
47 // The compiler will not generate code to destroy them during the return
48 // below so they will leak. Additionally, do not place any calls to libjpeg
49 // that can fail above this line or any error will cause undefined behavior.
50 if (setjmp(mErrorManager.mJumpBuffer)) {
51 // This is where the error handler will jump in case setup fails
52 // The error manager will ALOG an appropriate error message
53 return false;
54 }
55
56 jpeg_create_compress(&mCompressInfo);
57
58 mCompressInfo.image_width = width;
59 mCompressInfo.image_height = height;
60 mCompressInfo.input_components = 3;
61 mCompressInfo.in_color_space = JCS_YCbCr;
62 jpeg_set_defaults(&mCompressInfo);
63
64 jpeg_set_quality(&mCompressInfo, quality, TRUE);
65 // It may seem weird to set color space here again but this will also set
66 // other fields. These fields might be overwritten by jpeg_set_defaults
67 jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr);
68 mCompressInfo.raw_data_in = TRUE;
69 mCompressInfo.dct_method = JDCT_IFAST;
70 // Set sampling factors
71 mCompressInfo.comp_info[0].h_samp_factor = 2;
72 mCompressInfo.comp_info[0].v_samp_factor = 2;
73 mCompressInfo.comp_info[1].h_samp_factor = 1;
74 mCompressInfo.comp_info[1].v_samp_factor = 1;
75 mCompressInfo.comp_info[2].h_samp_factor = 1;
76 mCompressInfo.comp_info[2].v_samp_factor = 1;
77
78 mCompressInfo.dest = &mDestManager;
79
80 return true;
81 }
82
deinterleave(const uint8_t * vuPlanar,std::vector<uint8_t> & uRows,std::vector<uint8_t> & vRows,int rowIndex,int width,int height,int stride)83 static void deinterleave(const uint8_t* vuPlanar, std::vector<uint8_t>& uRows,
84 std::vector<uint8_t>& vRows, int rowIndex, int width,
85 int height, int stride) {
86 int numRows = (height - rowIndex) / 2;
87 if (numRows > 8) numRows = 8;
88 for (int row = 0; row < numRows; ++row) {
89 int offset = ((rowIndex >> 1) + row) * stride;
90 const uint8_t* vu = vuPlanar + offset;
91 for (int i = 0; i < (width >> 1); ++i) {
92 int index = row * (width >> 1) + i;
93 uRows[index] = vu[1];
94 vRows[index] = vu[0];
95 vu += 2;
96 }
97 }
98 }
99
100
compressData(const unsigned char * data,ExifData * exifData)101 bool Compressor::compressData(const unsigned char* data, ExifData* exifData) {
102 const uint8_t* y[16];
103 const uint8_t* cb[8];
104 const uint8_t* cr[8];
105 const uint8_t** planes[3] = { y, cb, cr };
106
107 int i, offset;
108 int width = mCompressInfo.image_width;
109 int height = mCompressInfo.image_height;
110 const uint8_t* yPlanar = data;
111 const uint8_t* vuPlanar = data + (width * height);
112 std::vector<uint8_t> uRows(8 * (width >> 1));
113 std::vector<uint8_t> vRows(8 * (width >> 1));
114
115 // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
116 // The compiler will not generate code to destroy them during the return
117 // below so they will leak. Additionally, do not place any calls to libjpeg
118 // that can fail above this line or any error will cause undefined behavior.
119 if (setjmp(mErrorManager.mJumpBuffer)) {
120 // This is where the error handler will jump in case compression fails
121 // The error manager will ALOG an appropriate error message
122 return false;
123 }
124
125 jpeg_start_compress(&mCompressInfo, TRUE);
126
127 attachExifData(exifData);
128
129 // process 16 lines of Y and 8 lines of U/V each time.
130 while (mCompressInfo.next_scanline < mCompressInfo.image_height) {
131 //deinterleave u and v
132 deinterleave(vuPlanar, uRows, vRows, mCompressInfo.next_scanline,
133 width, height, width);
134
135 // Jpeg library ignores the rows whose indices are greater than height.
136 for (i = 0; i < 16; i++) {
137 // y row
138 y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width;
139
140 // construct u row and v row
141 if ((i & 1) == 0) {
142 // height and width are both halved because of downsampling
143 offset = (i >> 1) * (width >> 1);
144 cb[i/2] = &uRows[offset];
145 cr[i/2] = &vRows[offset];
146 }
147 }
148 jpeg_write_raw_data(&mCompressInfo, const_cast<JSAMPIMAGE>(planes), 16);
149 }
150
151 jpeg_finish_compress(&mCompressInfo);
152 jpeg_destroy_compress(&mCompressInfo);
153
154 return true;
155 }
156
attachExifData(ExifData * exifData)157 bool Compressor::attachExifData(ExifData* exifData) {
158 if (exifData == nullptr) {
159 // This is not an error, we don't require EXIF data
160 return true;
161 }
162
163 // Save the EXIF data to memory
164 unsigned char* rawData = nullptr;
165 unsigned int size = 0;
166 exif_data_save_data(exifData, &rawData, &size);
167 if (rawData == nullptr) {
168 ALOGE("Failed to create EXIF data block");
169 return false;
170 }
171
172 jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size);
173 free(rawData);
174 return true;
175 }
176
ErrorManager()177 Compressor::ErrorManager::ErrorManager() {
178 error_exit = &onJpegError;
179 }
180
onJpegError(j_common_ptr cinfo)181 void Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) {
182 // NOTE! Do not construct any non-trivial objects in this method at the top
183 // scope. Their destructors will not be called. If you do need such an
184 // object create a local scope that does not include the longjmp call,
185 // that ensures the object is destroyed before longjmp is called.
186 ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(cinfo->err);
187
188 // Format and log error message
189 char errorMessage[JMSG_LENGTH_MAX];
190 (*errorManager->format_message)(cinfo, errorMessage);
191 errorMessage[sizeof(errorMessage) - 1] = '\0';
192 ALOGE("JPEG compression error: %s", errorMessage);
193 jpeg_destroy(cinfo);
194
195 // And through the looking glass we go
196 longjmp(errorManager->mJumpBuffer, 1);
197 }
198
DestinationManager()199 Compressor::DestinationManager::DestinationManager() {
200 init_destination = &initDestination;
201 empty_output_buffer = &emptyOutputBuffer;
202 term_destination = &termDestination;
203 }
204
initDestination(j_compress_ptr cinfo)205 void Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) {
206 auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
207
208 // Start out with some arbitrary but not too large buffer size
209 manager->mBuffer.resize(16 * 1024);
210 manager->next_output_byte = &manager->mBuffer[0];
211 manager->free_in_buffer = manager->mBuffer.size();
212 }
213
emptyOutputBuffer(j_compress_ptr cinfo)214 boolean Compressor::DestinationManager::emptyOutputBuffer(
215 j_compress_ptr cinfo) {
216 auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
217
218 // Keep doubling the size of the buffer for a very low, amortized
219 // performance cost of the allocations
220 size_t oldSize = manager->mBuffer.size();
221 manager->mBuffer.resize(oldSize * 2);
222 manager->next_output_byte = &manager->mBuffer[oldSize];
223 manager->free_in_buffer = manager->mBuffer.size() - oldSize;
224 return manager->free_in_buffer != 0;
225 }
226
termDestination(j_compress_ptr cinfo)227 void Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) {
228 auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
229
230 // Resize down to the exact size of the output, that is remove as many
231 // bytes as there are left in the buffer
232 manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer);
233 }
234
235