2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
30 using namespace jpegutil;
33 void safeDelete(T& t) {
41 void safeDeleteArray(T& t) {
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.
59 if (one_x > orig_x && one_y > orig_y) {
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
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
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
89 output_width_ = abs(one_x - orig_x);
90 output_height_ = abs(one_y - orig_y);
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
102 int cropXLow = min(cropLeft, cropRight);
103 int cropYLow = min(cropTop, cropBottom);
104 int cropXHigh = max(cropLeft, cropRight);
105 int cropYHigh = max(cropTop, cropBottom);
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);
117 return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
120 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_;
128 * Transforms the input coordinates. Coordinates outside the cropped region
129 * are clamped to valid values.
131 void jpegutil::Transform::Map(int x, int y, int* x_out, int* y_out) const {
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_;
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;
152 jpeg_compress_struct& cinfo =
153 *const_cast<struct jpeg_compress_struct*>(&cinfov);
155 JSAMPROW* volatile yArr = nullptr;
156 JSAMPROW* volatile cbArr = nullptr;
157 JSAMPROW* volatile crArr = nullptr;
159 JSAMPARRAY imgArr[3];
163 struct my_error_mgr {
164 struct jpeg_error_mgr pub;
165 jmp_buf setjmp_buffer;
168 cinfo.err = jpeg_std_error(&err.pub);
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);
175 (*cinfo->err->output_message)(cinfo);
177 // Return control to the setjmp point (see call to setjmp()).
178 longjmp(myerr->setjmp_buffer, 1);
181 cinfo.err = (struct jpeg_error_mgr*)&err;
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
187 jpeg_destroy_compress(&cinfo);
189 safeDeleteArray(yArr);
190 safeDeleteArray(cbArr);
191 safeDeleteArray(crArr);
196 // Create jpeg compression context
197 jpeg_create_compress(&cinfo);
199 // Stores data needed by our c-style callbacks into libjpeg
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};
207 cinfo.client_data = &clientData;
209 // Initialize destination manager
210 jpeg_destination_mgr dest;
212 dest.init_destination = [](j_compress_ptr cinfo) {
213 ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
215 cinfo->dest->next_output_byte = cdata.out_buf;
216 cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
219 dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
220 ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
222 size_t numBytesInBuffer = cdata.out_buf_capacity;
223 cdata.flush(numBytesInBuffer);
224 cdata.totalOutputBytes += numBytesInBuffer;
227 cinfo->dest->next_output_byte = cdata.out_buf;
228 cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
233 dest.term_destination = [](j_compress_ptr cinfo __unused) {
234 // do nothing to terminate the output buffer
239 // Set jpeg parameters
240 cinfo.image_width = img_width;
241 cinfo.image_height = img_height;
242 cinfo.input_components = 3;
244 // Set defaults based on the above values
245 jpeg_set_defaults(&cinfo);
247 jpeg_set_quality(&cinfo, quality, true);
249 cinfo.dct_method = JDCT_IFAST;
251 cinfo.raw_data_in = true;
253 jpeg_set_colorspace(&cinfo, JCS_YCbCr);
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;
262 jpeg_start_compress(&cinfo, true);
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];
268 imgArr[0] = const_cast<JSAMPARRAY>(yArr);
269 imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
270 imgArr[2] = const_cast<JSAMPARRAY>(crArr);
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);
277 for (int row = 0; row < DCTSIZE * 2; row++) {
278 yArr[row] = yData[row];
280 for (int row = 0; row < DCTSIZE; row++) {
281 cbArr[row] = cbData[row];
282 crArr[row] = crData[row];
285 jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
288 jpeg_finish_compress(&cinfo);
290 int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf;
292 flush(numBytesInBuffer);
294 clientData.totalOutputBytes += numBytesInBuffer;
296 safeDeleteArray(yArr);
297 safeDeleteArray(cbArr);
298 safeDeleteArray(crArr);
300 jpeg_destroy_compress(&cinfo);
302 return clientData.totalOutputBytes;
305 int jpegutil::Compress(
306 /** Input image dimensions */
307 int width, int height,
309 unsigned char* yBuf, int yPStride, int yRStride,
311 unsigned char* cbBuf, int cbPStride, int cbRStride,
313 unsigned char* crBuf, int crPStride, int crRStride,
315 unsigned char* outBuf, size_t outBufCapacity,
316 /** Jpeg compression parameters */
319 int cropLeft, int cropTop, int cropRight, int cropBottom,
320 /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree
325 finalWidth = cropRight - cropLeft;
326 finalHeight = cropBottom - cropTop;
329 // for 90 and 270-degree rotations, flip the final width and height
331 finalWidth = cropBottom - cropTop;
332 finalHeight = cropRight - cropLeft;
333 } else if (rot90 == 3) {
334 finalWidth = cropBottom - cropTop;
335 finalHeight = cropRight - cropLeft;
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};
342 auto flush = [](size_t numBytes __unused) {
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;
351 Transform yTrans = Transform::ForCropFollowedByRotation(
352 cropLeft, cropTop, cropRight, cropBottom, rot90);
354 Transform chromaTrans = Transform::ForCropFollowedByRotation(
355 cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90);
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);
361 return Compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf,
362 outBufCapacity, flush, quality);