• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 using namespace jpegutil;
31 
32 template <typename T>
safeDelete(T & t)33 void safeDelete(T& t) {
34   if (t != nullptr) {
35     delete t;
36     t = nullptr;
37   }
38 }
39 
40 template <typename T>
safeDeleteArray(T & t)41 void safeDeleteArray(T& t) {
42   if (t != nullptr) {
43     delete[] t;
44     t = nullptr;
45   }
46 }
47 
Transform(int orig_x,int orig_y,int one_x,int one_y)48 jpegutil::Transform::Transform(int orig_x, int orig_y, int one_x, int one_y)
49     : orig_x_(orig_x), orig_y_(orig_y), one_x_(one_x), one_y_(one_y) {
50   if (orig_x == one_x || orig_y == one_y) {
51     // Handle the degenerate case of cropping to a 0x0 rectangle.
52     mat00_ = 0;
53     mat01_ = 0;
54     mat10_ = 0;
55     mat11_ = 0;
56     return;
57   }
58 
59   if (one_x > orig_x && one_y > orig_y) {
60     // 0-degree rotation
61     mat00_ = 1;
62     mat01_ = 0;
63     mat10_ = 0;
64     mat11_ = 1;
65     output_width_ = abs(one_x - orig_x);
66     output_height_ = abs(one_y - orig_y);
67   } else if (one_x < orig_x && one_y > orig_y) {
68     // 90-degree CCW rotation
69     mat00_ = 0;
70     mat01_ = -1;
71     mat10_ = 1;
72     mat11_ = 0;
73     output_width_ = abs(one_y - orig_y);
74     output_height_ = abs(one_x - orig_x);
75   } else if (one_x > orig_x && one_y < orig_y) {
76     // 270-degree CCW rotation
77     mat00_ = 0;
78     mat01_ = 1;
79     mat10_ = -1;
80     mat11_ = 0;
81     output_width_ = abs(one_y - orig_y);
82     output_height_ = abs(one_x - orig_x);
83   } else if (one_x < orig_x && one_y < orig_y) {
84     // 180-degree CCW rotation
85     mat00_ = -1;
86     mat01_ = 0;
87     mat10_ = 0;
88     mat11_ = -1;
89     output_width_ = abs(one_x - orig_x);
90     output_height_ = abs(one_y - orig_y);
91   }
92 }
93 
ForCropFollowedByRotation(int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)94 jpegutil::Transform jpegutil::Transform::ForCropFollowedByRotation(
95     int cropLeft, int cropTop, int cropRight, int cropBottom, int rot90) {
96   // The input crop-region excludes cropRight and cropBottom, so transform the
97   // crop rect such that it defines the entire valid region of pixels
98   // inclusively.
99   cropRight -= 1;
100   cropBottom -= 1;
101 
102   int cropXLow = min(cropLeft, cropRight);
103   int cropYLow = min(cropTop, cropBottom);
104   int cropXHigh = max(cropLeft, cropRight);
105   int cropYHigh = max(cropTop, cropBottom);
106   rot90 %= 4;
107   if (rot90 == 0) {
108     return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
109   } else if (rot90 == 1) {
110     return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1);
111   } else if (rot90 == 2) {
112     return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1);
113   } else if (rot90 == 3) {
114     return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1);
115   }
116   // Impossible case.
117   return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
118 }
119 
operator ==(const Transform & other) const120 bool jpegutil::Transform::operator==(const Transform& other) const {
121   return other.orig_x_ == orig_x_ &&  //
122          other.orig_y_ == orig_y_ &&  //
123          other.one_x_ == one_x_ &&    //
124          other.one_y_ == one_y_;
125 }
126 
127 /**
128  * Transforms the input coordinates.  Coordinates outside the cropped region
129  * are clamped to valid values.
130  */
Map(int x,int y,int * x_out,int * y_out) const131 void jpegutil::Transform::Map(int x, int y, int* x_out, int* y_out) const {
132   x = max(x, 0);
133   y = max(y, 0);
134   x = min(x, output_width() - 1);
135   y = min(y, output_height() - 1);
136   *x_out = x * mat00_ + y * mat01_ + orig_x_;
137   *y_out = x * mat10_ + y * mat11_ + orig_y_;
138 }
139 
Compress(int img_width,int img_height,jpegutil::RowIterator<16> & y_row_generator,jpegutil::RowIterator<8> & cb_row_generator,jpegutil::RowIterator<8> & cr_row_generator,unsigned char * out_buf,size_t out_buf_capacity,std::function<void (size_t)> flush,int quality)140 int jpegutil::Compress(int img_width, int img_height,
141                        jpegutil::RowIterator<16>& y_row_generator,
142                        jpegutil::RowIterator<8>& cb_row_generator,
143                        jpegutil::RowIterator<8>& cr_row_generator,
144                        unsigned char* out_buf, size_t out_buf_capacity,
145                        std::function<void(size_t)> flush, int quality) {
146   // libjpeg requires the use of setjmp/longjmp to recover from errors.  Since
147   // this doesn't play well with RAII, we must use pointers and manually call
148   // delete. See POSIX documentation for longjmp() for details on why the
149   // volatile keyword is necessary.
150   volatile jpeg_compress_struct cinfov;
151 
152   jpeg_compress_struct& cinfo =
153       *const_cast<struct jpeg_compress_struct*>(&cinfov);
154 
155   JSAMPROW* volatile yArr = nullptr;
156   JSAMPROW* volatile cbArr = nullptr;
157   JSAMPROW* volatile crArr = nullptr;
158 
159   JSAMPARRAY imgArr[3];
160 
161   // Error handling
162 
163   struct my_error_mgr {
164     struct jpeg_error_mgr pub;
165     jmp_buf setjmp_buffer;
166   } err;
167 
168   cinfo.err = jpeg_std_error(&err.pub);
169 
170   // Default error_exit will call exit(), so override
171   // to return control via setjmp/longjmp.
172   err.pub.error_exit = [](j_common_ptr cinfo) {
173     my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err);
174 
175     (*cinfo->err->output_message)(cinfo);
176 
177     // Return control to the setjmp point (see call to setjmp()).
178     longjmp(myerr->setjmp_buffer, 1);
179   };
180 
181   cinfo.err = (struct jpeg_error_mgr*)&err;
182 
183   // Set the setjmp point to return to in case of error.
184   if (setjmp(err.setjmp_buffer)) {
185     // If libjpeg hits an error, control will jump to this point (see call to
186     // longjmp()).
187     jpeg_destroy_compress(&cinfo);
188 
189     safeDeleteArray(yArr);
190     safeDeleteArray(cbArr);
191     safeDeleteArray(crArr);
192 
193     return -1;
194   }
195 
196   // Create jpeg compression context
197   jpeg_create_compress(&cinfo);
198 
199   // Stores data needed by our c-style callbacks into libjpeg
200   struct ClientData {
201     unsigned char* out_buf;
202     size_t out_buf_capacity;
203     std::function<void(size_t)> flush;
204     int totalOutputBytes;
205   } clientData{out_buf, out_buf_capacity, flush, 0};
206 
207   cinfo.client_data = &clientData;
208 
209   // Initialize destination manager
210   jpeg_destination_mgr dest;
211 
212   dest.init_destination = [](j_compress_ptr cinfo) {
213     ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
214 
215     cinfo->dest->next_output_byte = cdata.out_buf;
216     cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
217   };
218 
219   dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
220     ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
221 
222     size_t numBytesInBuffer = cdata.out_buf_capacity;
223     cdata.flush(numBytesInBuffer);
224     cdata.totalOutputBytes += numBytesInBuffer;
225 
226     // Reset the buffer
227     cinfo->dest->next_output_byte = cdata.out_buf;
228     cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
229 
230     return true;
231   };
232 
233   dest.term_destination = [](j_compress_ptr cinfo __unused) {
234     // do nothing to terminate the output buffer
235   };
236 
237   cinfo.dest = &dest;
238 
239   // Set jpeg parameters
240   cinfo.image_width = img_width;
241   cinfo.image_height = img_height;
242   cinfo.input_components = 3;
243 
244   // Set defaults based on the above values
245   jpeg_set_defaults(&cinfo);
246 
247   jpeg_set_quality(&cinfo, quality, true);
248 
249   cinfo.dct_method = JDCT_IFAST;
250 
251   cinfo.raw_data_in = true;
252 
253   jpeg_set_colorspace(&cinfo, JCS_YCbCr);
254 
255   cinfo.comp_info[0].h_samp_factor = 2;
256   cinfo.comp_info[0].v_samp_factor = 2;
257   cinfo.comp_info[1].h_samp_factor = 1;
258   cinfo.comp_info[1].v_samp_factor = 1;
259   cinfo.comp_info[2].h_samp_factor = 1;
260   cinfo.comp_info[2].v_samp_factor = 1;
261 
262   jpeg_start_compress(&cinfo, true);
263 
264   yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE];
265   cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE];
266   crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE];
267 
268   imgArr[0] = const_cast<JSAMPARRAY>(yArr);
269   imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
270   imgArr[2] = const_cast<JSAMPARRAY>(crArr);
271 
272   for (int y = 0; y < img_height; y += DCTSIZE * 2) {
273     std::array<unsigned char*, 16> yData = y_row_generator.LoadAt(y);
274     std::array<unsigned char*, 8> cbData = cb_row_generator.LoadAt(y / 2);
275     std::array<unsigned char*, 8> crData = cr_row_generator.LoadAt(y / 2);
276 
277     for (int row = 0; row < DCTSIZE * 2; row++) {
278       yArr[row] = yData[row];
279     }
280     for (int row = 0; row < DCTSIZE; row++) {
281       cbArr[row] = cbData[row];
282       crArr[row] = crData[row];
283     }
284 
285     jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
286   }
287 
288   jpeg_finish_compress(&cinfo);
289 
290   int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf;
291 
292   flush(numBytesInBuffer);
293 
294   clientData.totalOutputBytes += numBytesInBuffer;
295 
296   safeDeleteArray(yArr);
297   safeDeleteArray(cbArr);
298   safeDeleteArray(crArr);
299 
300   jpeg_destroy_compress(&cinfo);
301 
302   return clientData.totalOutputBytes;
303 }
304 
Compress(int width,int height,unsigned char * yBuf,int yPStride,int yRStride,unsigned char * cbBuf,int cbPStride,int cbRStride,unsigned char * crBuf,int crPStride,int crRStride,unsigned char * outBuf,size_t outBufCapacity,int quality,int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)305 int jpegutil::Compress(
306     /** Input image dimensions */
307     int width, int height,
308     /** Y Plane */
309     unsigned char* yBuf, int yPStride, int yRStride,
310     /** Cb Plane */
311     unsigned char* cbBuf, int cbPStride, int cbRStride,
312     /** Cr Plane */
313     unsigned char* crBuf, int crPStride, int crRStride,
314     /** Output */
315     unsigned char* outBuf, size_t outBufCapacity,
316     /** Jpeg compression parameters */
317     int quality,
318     /** Crop */
319     int cropLeft, int cropTop, int cropRight, int cropBottom,
320     /** Rotation (multiple of 90).  For example, rot90 = 1 implies a 90 degree
321      * rotation. */
322     int rot90) {
323   int finalWidth;
324   int finalHeight;
325   finalWidth = cropRight - cropLeft;
326   finalHeight = cropBottom - cropTop;
327 
328   rot90 %= 4;
329   // for 90 and 270-degree rotations, flip the final width and height
330   if (rot90 == 1) {
331     finalWidth = cropBottom - cropTop;
332     finalHeight = cropRight - cropLeft;
333   } else if (rot90 == 3) {
334     finalWidth = cropBottom - cropTop;
335     finalHeight = cropRight - cropLeft;
336   }
337 
338   const Plane yP = {width, height, yBuf, yPStride, yRStride};
339   const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride};
340   const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride};
341 
342   auto flush = [](size_t numBytes __unused) {
343     // do nothing
344   };
345 
346   // Round up to the nearest multiple of 64.
347   int y_row_length = (finalWidth + 16 + 63) & ~63;
348   int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63;
349   int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63;
350 
351   Transform yTrans = Transform::ForCropFollowedByRotation(
352       cropLeft, cropTop, cropRight, cropBottom, rot90);
353 
354   Transform chromaTrans = Transform::ForCropFollowedByRotation(
355       cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90);
356 
357   RowIterator<16> yIter(yP, yTrans, y_row_length);
358   RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length);
359   RowIterator<8> crIter(crP, chromaTrans, cr_row_length);
360 
361   return Compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf,
362                   outBufCapacity, flush, quality);
363 }
364