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
25extern "C" {
26#include "jpeglib.h"
27}
28
29using namespace std;
30using namespace jpegutil;
31
32template <typename T>
33void safeDelete(T& t) {
34  if (t != nullptr) {
35    delete t;
36    t = nullptr;
37  }
38}
39
40template <typename T>
41void safeDeleteArray(T& t) {
42  if (t != nullptr) {
43    delete[] t;
44    t = nullptr;
45  }
46}
47
48jpegutil::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
94jpegutil::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
120bool 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 */
131void 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
140int 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
305int 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