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 
     31 template <typename T>
     32 void safeDelete(T& t) {
     33   if (t != nullptr) {
     34     delete t;
     35     t = nullptr;
     36   }
     37 }
     38 
     39 template <typename T>
     40 void safeDeleteArray(T& t) {
     41   if (t != nullptr) {
     42     delete[] t;
     43     t = nullptr;
     44   }
     45 }
     46 
     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 
     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 
    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 
    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