Home | History | Annotate | Download | only in jpeg-stub
      1 /*
      2 * Copyright (C) 2016 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 
     17 #include "Compressor.h"
     18 
     19 #define LOG_NDEBUG 0
     20 #define LOG_TAG "EmulatedCamera_JPEGStub_Compressor"
     21 #include <cutils/log.h>
     22 #include <libexif/exif-data.h>
     23 
     24 Compressor::Compressor() {
     25 
     26 }
     27 
     28 bool Compressor::compress(const unsigned char* data,
     29                           int width, int height, int quality,
     30                           ExifData* exifData) {
     31     if (!configureCompressor(width, height, quality)) {
     32         // The method will have logged a more detailed error message than we can
     33         // provide here so just return.
     34         return false;
     35     }
     36 
     37     return compressData(data, exifData);
     38 }
     39 
     40 const std::vector<uint8_t>& Compressor::getCompressedData() const {
     41     return mDestManager.mBuffer;
     42 }
     43 
     44 bool Compressor::configureCompressor(int width, int height, int quality) {
     45     mCompressInfo.err = jpeg_std_error(&mErrorManager);
     46     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
     47     // The compiler will not generate code to destroy them during the return
     48     // below so they will leak. Additionally, do not place any calls to libjpeg
     49     // that can fail above this line or any error will cause undefined behavior.
     50     if (setjmp(mErrorManager.mJumpBuffer)) {
     51         // This is where the error handler will jump in case setup fails
     52         // The error manager will ALOG an appropriate error message
     53         return false;
     54     }
     55 
     56     jpeg_create_compress(&mCompressInfo);
     57 
     58     mCompressInfo.image_width = width;
     59     mCompressInfo.image_height = height;
     60     mCompressInfo.input_components = 3;
     61     mCompressInfo.in_color_space = JCS_YCbCr;
     62     jpeg_set_defaults(&mCompressInfo);
     63 
     64     jpeg_set_quality(&mCompressInfo, quality, TRUE);
     65     // It may seem weird to set color space here again but this will also set
     66     // other fields. These fields might be overwritten by jpeg_set_defaults
     67     jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr);
     68     mCompressInfo.raw_data_in = TRUE;
     69     mCompressInfo.dct_method = JDCT_IFAST;
     70     // Set sampling factors
     71     mCompressInfo.comp_info[0].h_samp_factor = 2;
     72     mCompressInfo.comp_info[0].v_samp_factor = 2;
     73     mCompressInfo.comp_info[1].h_samp_factor = 1;
     74     mCompressInfo.comp_info[1].v_samp_factor = 1;
     75     mCompressInfo.comp_info[2].h_samp_factor = 1;
     76     mCompressInfo.comp_info[2].v_samp_factor = 1;
     77 
     78     mCompressInfo.dest = &mDestManager;
     79 
     80     return true;
     81 }
     82 
     83 static void deinterleave(const uint8_t* vuPlanar, std::vector<uint8_t>& uRows,
     84                          std::vector<uint8_t>& vRows, int rowIndex, int width,
     85                          int height, int stride) {
     86     int numRows = (height - rowIndex) / 2;
     87     if (numRows > 8) numRows = 8;
     88     for (int row = 0; row < numRows; ++row) {
     89         int offset = ((rowIndex >> 1) + row) * stride;
     90         const uint8_t* vu = vuPlanar + offset;
     91         for (int i = 0; i < (width >> 1); ++i) {
     92             int index = row * (width >> 1) + i;
     93             uRows[index] = vu[1];
     94             vRows[index] = vu[0];
     95             vu += 2;
     96         }
     97     }
     98 }
     99 
    100 
    101 bool Compressor::compressData(const unsigned char* data, ExifData* exifData) {
    102     const uint8_t* y[16];
    103     const uint8_t* cb[8];
    104     const uint8_t* cr[8];
    105     const uint8_t** planes[3] = { y, cb, cr };
    106 
    107     int i, offset;
    108     int width = mCompressInfo.image_width;
    109     int height = mCompressInfo.image_height;
    110     const uint8_t* yPlanar = data;
    111     const uint8_t* vuPlanar = data + (width * height);
    112     std::vector<uint8_t> uRows(8 * (width >> 1));
    113     std::vector<uint8_t> vRows(8 * (width >> 1));
    114 
    115     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
    116     // The compiler will not generate code to destroy them during the return
    117     // below so they will leak. Additionally, do not place any calls to libjpeg
    118     // that can fail above this line or any error will cause undefined behavior.
    119     if (setjmp(mErrorManager.mJumpBuffer)) {
    120         // This is where the error handler will jump in case compression fails
    121         // The error manager will ALOG an appropriate error message
    122         return false;
    123     }
    124 
    125     jpeg_start_compress(&mCompressInfo, TRUE);
    126 
    127     attachExifData(exifData);
    128 
    129     // process 16 lines of Y and 8 lines of U/V each time.
    130     while (mCompressInfo.next_scanline < mCompressInfo.image_height) {
    131         //deinterleave u and v
    132         deinterleave(vuPlanar, uRows, vRows, mCompressInfo.next_scanline,
    133                      width, height, width);
    134 
    135         // Jpeg library ignores the rows whose indices are greater than height.
    136         for (i = 0; i < 16; i++) {
    137             // y row
    138             y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width;
    139 
    140             // construct u row and v row
    141             if ((i & 1) == 0) {
    142                 // height and width are both halved because of downsampling
    143                 offset = (i >> 1) * (width >> 1);
    144                 cb[i/2] = &uRows[offset];
    145                 cr[i/2] = &vRows[offset];
    146             }
    147           }
    148         jpeg_write_raw_data(&mCompressInfo, const_cast<JSAMPIMAGE>(planes), 16);
    149     }
    150 
    151     jpeg_finish_compress(&mCompressInfo);
    152     jpeg_destroy_compress(&mCompressInfo);
    153 
    154     return true;
    155 }
    156 
    157 bool Compressor::attachExifData(ExifData* exifData) {
    158     if (exifData == nullptr) {
    159         // This is not an error, we don't require EXIF data
    160         return true;
    161     }
    162 
    163     // Save the EXIF data to memory
    164     unsigned char* rawData = nullptr;
    165     unsigned int size = 0;
    166     exif_data_save_data(exifData, &rawData, &size);
    167     if (rawData == nullptr) {
    168         ALOGE("Failed to create EXIF data block");
    169         return false;
    170     }
    171 
    172     jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size);
    173     free(rawData);
    174     return true;
    175 }
    176 
    177 Compressor::ErrorManager::ErrorManager() {
    178     error_exit = &onJpegError;
    179 }
    180 
    181 void Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) {
    182     // NOTE! Do not construct any non-trivial objects in this method at the top
    183     // scope. Their destructors will not be called. If you do need such an
    184     // object create a local scope that does not include the longjmp call,
    185     // that ensures the object is destroyed before longjmp is called.
    186     ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(cinfo->err);
    187 
    188     // Format and log error message
    189     char errorMessage[JMSG_LENGTH_MAX];
    190     (*errorManager->format_message)(cinfo, errorMessage);
    191     errorMessage[sizeof(errorMessage) - 1] = '\0';
    192     ALOGE("JPEG compression error: %s", errorMessage);
    193     jpeg_destroy(cinfo);
    194 
    195     // And through the looking glass we go
    196     longjmp(errorManager->mJumpBuffer, 1);
    197 }
    198 
    199 Compressor::DestinationManager::DestinationManager() {
    200     init_destination = &initDestination;
    201     empty_output_buffer = &emptyOutputBuffer;
    202     term_destination = &termDestination;
    203 }
    204 
    205 void Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) {
    206     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
    207 
    208     // Start out with some arbitrary but not too large buffer size
    209     manager->mBuffer.resize(16 * 1024);
    210     manager->next_output_byte = &manager->mBuffer[0];
    211     manager->free_in_buffer = manager->mBuffer.size();
    212 }
    213 
    214 boolean Compressor::DestinationManager::emptyOutputBuffer(
    215         j_compress_ptr cinfo) {
    216     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
    217 
    218     // Keep doubling the size of the buffer for a very low, amortized
    219     // performance cost of the allocations
    220     size_t oldSize = manager->mBuffer.size();
    221     manager->mBuffer.resize(oldSize * 2);
    222     manager->next_output_byte = &manager->mBuffer[oldSize];
    223     manager->free_in_buffer = manager->mBuffer.size() - oldSize;
    224     return manager->free_in_buffer != 0;
    225 }
    226 
    227 void Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) {
    228     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
    229 
    230     // Resize down to the exact size of the output, that is remove as many
    231     // bytes as there are left in the buffer
    232     manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer);
    233 }
    234 
    235