1 /*
2  * Copyright (C) 2014 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 #include "jpegutil.h"
17 #include <memory.h>
18 #include <array>
19 #include <vector>
20 #include <cstring>
21 #include <cstdio>
22 
23 #include <setjmp.h>
24 
25 extern "C" {
26 #include "jpeglib.h"
27 }
28 
29 using namespace std;
30 
31 template <typename T>
safeDelete(T & t)32 void safeDelete(T& t) {
33   if (t != nullptr) {
34     delete t;
35     t = nullptr;
36   }
37 }
38 
39 template <typename T>
safeDeleteArray(T & t)40 void safeDeleteArray(T& t) {
41   if (t != nullptr) {
42     delete[] t;
43     t = nullptr;
44   }
45 }
46 
RowIterator(const Plane * plane)47 jpegutil::Plane::RowIterator::RowIterator(const Plane* plane) : plane_(plane) {
48   // We must be able to supply up to 8 * 2 lines at a time to libjpeg.
49   // 8 = vertical size of blocks transformed with DCT.
50   // 2 = scaling factor for Y vs UV planes.
51   bufRowCount_ = 16;
52 
53   // Rows must be padded to the next multiple of 16
54   // TODO OPTIMIZE Cb and Cr components only need to be padded to a multiple of
55   // 8.
56   rowPadding_ = (16 - (plane_->planeWidth_ % 16)) % 16;
57   bufRowStride_ = plane_->planeWidth_ + rowPadding_;
58 
59   // Round up to the nearest multiple of 64 for cache alignment
60   bufRowStride_ = (bufRowStride_ + 63) & ~63;
61 
62   // Allocate an extra 64 bytes to allow for cache alignment
63   size_t bufSize = bufRowStride_ * bufRowCount_ + 64;
64 
65   // TODO OPTIMIZE if the underlying data has a pixel-stride of 1, and an image
66   // width which is a multiple of 16, we can avoid this allocation and simply
67   // return pointers into the underlying data in operator()(int) instead of
68   // copying the data.
69   buffer_ = unique_ptr<unsigned char[]>(new unsigned char[bufSize]);
70 
71   // Find the start of the 64-byte aligned buffer we allocated.
72   size_t bufStart = reinterpret_cast<size_t>(&buffer_[0]);
73   size_t alignedBufStart = (bufStart + 63) & ~63;
74   alignedBuffer_ = reinterpret_cast<unsigned char*>(alignedBufStart);
75 
76   bufCurRow_ = 0;
77 }
78 
operator ()(int y)79 unsigned char* jpegutil::Plane::RowIterator::operator()(int y) {
80   unsigned char* bufCurRowPtr = alignedBuffer_ + bufRowStride_ * bufCurRow_;
81 
82   unsigned char* srcPtr = &plane_->data_[y * plane_->rowStride_];
83   unsigned char* dstPtr = bufCurRowPtr;
84 
85   // Use memcpy when possible.
86   if (plane_->pixelStride_ == 1) {
87     memcpy(dstPtr, srcPtr, plane_->planeWidth_);
88   } else {
89     int pixelStride = plane_->pixelStride_;
90 
91     for (int i = 0; i < plane_->planeWidth_; i++) {
92       *dstPtr = *srcPtr;
93 
94       srcPtr += pixelStride;
95       dstPtr++;
96     }
97   }
98 
99   // Add padding to the right side by replicating the rightmost column of
100   // (actual) image values into the padding bytes.
101   memset(&bufCurRowPtr[plane_->planeWidth_],
102          bufCurRowPtr[plane_->planeWidth_ - 1], rowPadding_);
103 
104   bufCurRow_++;
105   // Wrap within ring buffer.
106   bufCurRow_ %= bufRowCount_;
107 
108   return bufCurRowPtr;
109 }
110 
Plane(int imgWidth,int imgHeight,int planeWidth,int planeHeight,unsigned char * data,int pixelStride,int rowStride)111 jpegutil::Plane::Plane(int imgWidth, int imgHeight, int planeWidth,
112                        int planeHeight, unsigned char* data, int pixelStride,
113                        int rowStride)
114     : imgWidth_(imgWidth),
115       imgHeight_(imgHeight),
116       planeWidth_(planeWidth),
117       planeHeight_(planeHeight),
118       data_(data),
119       rowStride_(rowStride),
120       pixelStride_(pixelStride) {}
121 
compress(const Plane & yPlane,const Plane & cbPlane,const Plane & crPlane,unsigned char * outBuf,size_t outBufCapacity,std::function<void (size_t)> flush,int quality)122 int jpegutil::compress(const Plane& yPlane, const Plane& cbPlane,
123                        const Plane& crPlane, unsigned char* outBuf,
124                        size_t outBufCapacity, std::function<void(size_t)> flush,
125                        int quality) {
126   int imgWidth = yPlane.imgWidth();
127   int imgHeight = yPlane.imgHeight();
128 
129   // libjpeg requires the use of setjmp/longjmp to recover from errors.  Since
130   // this doesn't play well with RAII, we must use pointers and manually call
131   // delete. See POSIX documentation for longjmp() for details on why the
132   // volatile keyword is necessary.
133   volatile jpeg_compress_struct cinfov;
134 
135   jpeg_compress_struct& cinfo =
136       *const_cast<struct jpeg_compress_struct*>(&cinfov);
137 
138   JSAMPROW* volatile yArr = nullptr;
139   JSAMPROW* volatile cbArr = nullptr;
140   JSAMPROW* volatile crArr = nullptr;
141 
142   Plane::RowIterator* volatile yRowGenerator = nullptr;
143   Plane::RowIterator* volatile cbRowGenerator = nullptr;
144   Plane::RowIterator* volatile crRowGenerator = nullptr;
145 
146   JSAMPARRAY imgArr[3];
147 
148   // Error handling
149 
150   struct my_error_mgr {
151     struct jpeg_error_mgr pub;
152     jmp_buf setjmp_buffer;
153   } err;
154 
155   cinfo.err = jpeg_std_error(&err.pub);
156 
157   // Default error_exit will call exit(), so override
158   // to return control via setjmp/longjmp.
159   err.pub.error_exit = [](j_common_ptr cinfo) {
160     my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err);
161 
162     (*cinfo->err->output_message)(cinfo);
163 
164     // Return control to the setjmp point (see call to setjmp()).
165     longjmp(myerr->setjmp_buffer, 1);
166   };
167 
168   cinfo.err = (struct jpeg_error_mgr*)&err;
169 
170   // Set the setjmp point to return to in case of error.
171   if (setjmp(err.setjmp_buffer)) {
172     // If libjpeg hits an error, control will jump to this point (see call to
173     // longjmp()).
174     jpeg_destroy_compress(&cinfo);
175 
176     safeDeleteArray(yArr);
177     safeDeleteArray(cbArr);
178     safeDeleteArray(crArr);
179     safeDelete(yRowGenerator);
180     safeDelete(cbRowGenerator);
181     safeDelete(crRowGenerator);
182 
183     return -1;
184   }
185 
186   // Create jpeg compression context
187   jpeg_create_compress(&cinfo);
188 
189   // Stores data needed by our c-style callbacks into libjpeg
190   struct ClientData {
191     unsigned char* outBuf;
192     size_t outBufCapacity;
193     std::function<void(size_t)> flush;
194     int totalOutputBytes;
195   } clientData{outBuf, outBufCapacity, flush, 0};
196 
197   cinfo.client_data = &clientData;
198 
199   // Initialize destination manager
200   jpeg_destination_mgr dest;
201 
202   dest.init_destination = [](j_compress_ptr cinfo) {
203     ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
204 
205     cinfo->dest->next_output_byte = cdata.outBuf;
206     cinfo->dest->free_in_buffer = cdata.outBufCapacity;
207   };
208 
209   dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
210     ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
211 
212     size_t numBytesInBuffer = cdata.outBufCapacity;
213     cdata.flush(numBytesInBuffer);
214     cdata.totalOutputBytes += numBytesInBuffer;
215 
216     // Reset the buffer
217     cinfo->dest->next_output_byte = cdata.outBuf;
218     cinfo->dest->free_in_buffer = cdata.outBufCapacity;
219 
220     return true;
221   };
222 
223   dest.term_destination = [](j_compress_ptr cinfo) {
224     // do nothing to terminate the output buffer
225   };
226 
227   cinfo.dest = &dest;
228 
229   // Set jpeg parameters
230   cinfo.image_width = imgWidth;
231   cinfo.image_height = imgHeight;
232   cinfo.input_components = 3;
233 
234   // Set defaults based on the above values
235   jpeg_set_defaults(&cinfo);
236 
237   jpeg_set_quality(&cinfo, quality, true);
238 
239   cinfo.dct_method = JDCT_IFAST;
240 
241   cinfo.raw_data_in = true;
242 
243   jpeg_set_colorspace(&cinfo, JCS_YCbCr);
244 
245   cinfo.comp_info[0].h_samp_factor = 2;
246   cinfo.comp_info[0].v_samp_factor = 2;
247   cinfo.comp_info[1].h_samp_factor = 1;
248   cinfo.comp_info[1].v_samp_factor = 1;
249   cinfo.comp_info[2].h_samp_factor = 1;
250   cinfo.comp_info[2].v_samp_factor = 1;
251 
252   jpeg_start_compress(&cinfo, true);
253 
254   yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE];
255   cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE];
256   crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE];
257 
258   imgArr[0] = const_cast<JSAMPARRAY>(yArr);
259   imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
260   imgArr[2] = const_cast<JSAMPARRAY>(crArr);
261 
262   yRowGenerator = new Plane::RowIterator(&yPlane);
263   cbRowGenerator = new Plane::RowIterator(&cbPlane);
264   crRowGenerator = new Plane::RowIterator(&crPlane);
265 
266   Plane::RowIterator& yRG = *const_cast<Plane::RowIterator*>(yRowGenerator);
267   Plane::RowIterator& cbRG = *const_cast<Plane::RowIterator*>(cbRowGenerator);
268   Plane::RowIterator& crRG = *const_cast<Plane::RowIterator*>(crRowGenerator);
269 
270   for (int y = 0; y < imgHeight; y += DCTSIZE * 2) {
271     for (int row = 0; row < DCTSIZE * 2; row++) {
272       yArr[row] = yRG(y + row);
273     }
274 
275     for (int row = 0; row < DCTSIZE; row++) {
276       // The y-index within the subsampled chroma planes to send to libjpeg.
277       const int chY = y / 2 + row;
278 
279       if (chY < imgHeight / 2) {
280         cbArr[row] = cbRG(chY);
281         crArr[row] = crRG(chY);
282       } else {
283         // When we have run out of rows in the chroma planes to compress, send
284         // the last row as padding.
285         cbArr[row] = cbRG(imgHeight / 2 - 1);
286         crArr[row] = crRG(imgHeight / 2 - 1);
287       }
288     }
289 
290     jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
291   }
292 
293   jpeg_finish_compress(&cinfo);
294 
295   int numBytesInBuffer = cinfo.dest->next_output_byte - outBuf;
296 
297   flush(numBytesInBuffer);
298 
299   clientData.totalOutputBytes += numBytesInBuffer;
300 
301   safeDeleteArray(yArr);
302   safeDeleteArray(cbArr);
303   safeDeleteArray(crArr);
304   safeDelete(yRowGenerator);
305   safeDelete(cbRowGenerator);
306   safeDelete(crRowGenerator);
307 
308   return clientData.totalOutputBytes;
309 }
310