Home | History | Annotate | Download | only in jni
      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>
     33 void safeDelete(T& t) {
     34   if (t != nullptr) {
     35     delete t;
     36     t = nullptr;
     37   }
     38 }
     39 
     40 template <typename T>
     41 void safeDeleteArray(T& t) {
     42   if (t != nullptr) {
     43     delete[] t;
     44     t = nullptr;
     45   }
     46 }
     47 
     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 
     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 
    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_;
    125 }
    126 
    127 /**
    128  * Transforms the input coordinates.  Coordinates outside the cropped region
    129  * are clamped to valid values.
    130  */
    131 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 
    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 
    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