• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <log/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     return compressData(data, exifData);
37 }
38 
getCompressedData() const39 const std::vector<uint8_t>& Compressor::getCompressedData() const {
40     return mDestManager.mBuffer;
41 }
42 
configureCompressor(int width,int height,int quality)43 bool Compressor::configureCompressor(int width, int height, int quality) {
44     mCompressInfo.err = jpeg_std_error(&mErrorManager);
45     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
46     // The compiler will not generate code to destroy them during the return
47     // below so they will leak. Additionally, do not place any calls to libjpeg
48     // that can fail above this line or any error will cause undefined behavior.
49     if (setjmp(mErrorManager.mJumpBuffer)) {
50         // This is where the error handler will jump in case setup fails
51         // The error manager will ALOG an appropriate error message
52         return false;
53     }
54 
55     jpeg_create_compress(&mCompressInfo);
56 
57     mCompressInfo.image_width = width;
58     mCompressInfo.image_height = height;
59     mCompressInfo.input_components = 3;
60     mCompressInfo.in_color_space = JCS_YCbCr;
61     jpeg_set_defaults(&mCompressInfo);
62 
63     jpeg_set_quality(&mCompressInfo, quality, TRUE);
64     // It may seem weird to set color space here again but this will also set
65     // other fields. These fields might be overwritten by jpeg_set_defaults
66     jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr);
67     mCompressInfo.raw_data_in = TRUE;
68     mCompressInfo.dct_method = JDCT_IFAST;
69     // Set sampling factors
70     mCompressInfo.comp_info[0].h_samp_factor = 2;
71     mCompressInfo.comp_info[0].v_samp_factor = 2;
72     mCompressInfo.comp_info[1].h_samp_factor = 1;
73     mCompressInfo.comp_info[1].v_samp_factor = 1;
74     mCompressInfo.comp_info[2].h_samp_factor = 1;
75     mCompressInfo.comp_info[2].v_samp_factor = 1;
76 
77     mCompressInfo.dest = &mDestManager;
78 
79     return true;
80 }
81 
compressData(const unsigned char * data,ExifData * exifData)82 bool Compressor::compressData(const unsigned char* data, ExifData* exifData) {
83     const uint8_t* y[16];
84     const uint8_t* cb[8];
85     const uint8_t* cr[8];
86     const uint8_t** planes[3] = { y, cb, cr };
87 
88     int i;
89     int width = mCompressInfo.image_width;
90     int height = mCompressInfo.image_height;
91     const uint8_t* yPlanar = data;
92     const uint8_t* uPlanar = yPlanar + width * height;
93     const uint8_t* vPlanar = uPlanar + width * height / 4;
94 
95     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
96     // The compiler will not generate code to destroy them during the return
97     // below so they will leak. Additionally, do not place any calls to libjpeg
98     // that can fail above this line or any error will cause undefined behavior.
99     if (setjmp(mErrorManager.mJumpBuffer)) {
100         // This is where the error handler will jump in case compression fails
101         // The error manager will ALOG an appropriate error message
102         return false;
103     }
104 
105     jpeg_start_compress(&mCompressInfo, TRUE);
106 
107     attachExifData(exifData);
108 
109     // process 16 lines of Y and 8 lines of U/V each time.
110     while (mCompressInfo.next_scanline < mCompressInfo.image_height) {
111         // Jpeg library ignores the rows whose indices are greater than height.
112         for (i = 0; i < 16; i++) {
113             // y row
114             y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width;
115 
116             // construct u row and v row
117             if ((i & 1) == 0) {
118                 // height and width are both halved because of downsampling
119                 cb[i/2] = uPlanar + (mCompressInfo.next_scanline + i) * width / 4;
120                 cr[i/2] = vPlanar + (mCompressInfo.next_scanline + i) * width / 4;
121             }
122         }
123         jpeg_write_raw_data(&mCompressInfo, const_cast<JSAMPIMAGE>(planes), 16);
124     }
125 
126     jpeg_finish_compress(&mCompressInfo);
127     jpeg_destroy_compress(&mCompressInfo);
128 
129     return true;
130 }
131 
attachExifData(ExifData * exifData)132 bool Compressor::attachExifData(ExifData* exifData) {
133     if (exifData == nullptr) {
134         // This is not an error, we don't require EXIF data
135         return true;
136     }
137 
138     // Save the EXIF data to memory
139     unsigned char* rawData = nullptr;
140     unsigned int size = 0;
141     exif_data_save_data(exifData, &rawData, &size);
142     if (rawData == nullptr) {
143         ALOGE("Failed to create EXIF data block");
144         return false;
145     }
146 
147     jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size);
148     free(rawData);
149     return true;
150 }
151 
ErrorManager()152 Compressor::ErrorManager::ErrorManager() {
153     error_exit = &onJpegError;
154 }
155 
onJpegError(j_common_ptr cinfo)156 void Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) {
157     // NOTE! Do not construct any non-trivial objects in this method at the top
158     // scope. Their destructors will not be called. If you do need such an
159     // object create a local scope that does not include the longjmp call,
160     // that ensures the object is destroyed before longjmp is called.
161     ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(cinfo->err);
162 
163     // Format and log error message
164     char errorMessage[JMSG_LENGTH_MAX];
165     (*errorManager->format_message)(cinfo, errorMessage);
166     errorMessage[sizeof(errorMessage) - 1] = '\0';
167     ALOGE("JPEG compression error: %s", errorMessage);
168     jpeg_destroy(cinfo);
169 
170     // And through the looking glass we go
171     longjmp(errorManager->mJumpBuffer, 1);
172 }
173 
DestinationManager()174 Compressor::DestinationManager::DestinationManager() {
175     init_destination = &initDestination;
176     empty_output_buffer = &emptyOutputBuffer;
177     term_destination = &termDestination;
178 }
179 
initDestination(j_compress_ptr cinfo)180 void Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) {
181     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
182 
183     // Start out with some arbitrary but not too large buffer size
184     manager->mBuffer.resize(16 * 1024);
185     manager->next_output_byte = &manager->mBuffer[0];
186     manager->free_in_buffer = manager->mBuffer.size();
187 }
188 
emptyOutputBuffer(j_compress_ptr cinfo)189 boolean Compressor::DestinationManager::emptyOutputBuffer(
190         j_compress_ptr cinfo) {
191     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
192 
193     // Keep doubling the size of the buffer for a very low, amortized
194     // performance cost of the allocations
195     size_t oldSize = manager->mBuffer.size();
196     manager->mBuffer.resize(oldSize * 2);
197     manager->next_output_byte = &manager->mBuffer[oldSize];
198     manager->free_in_buffer = manager->mBuffer.size() - oldSize;
199     return manager->free_in_buffer != 0;
200 }
201 
termDestination(j_compress_ptr cinfo)202 void Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) {
203     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
204 
205     // Resize down to the exact size of the output, that is remove as many
206     // bytes as there are left in the buffer
207     manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer);
208 }
209 
210