Home | History | Annotate | Download | only in camera
      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 "Thumbnail.h"
     18 
     19 #define LOG_NDEBUG 0
     20 #define LOG_TAG "EmulatedCamera_Thumbnail"
     21 #include <log/log.h>
     22 #include <libexif/exif-data.h>
     23 #include <libyuv.h>
     24 
     25 #include "JpegCompressor.h"
     26 
     27 #include <vector>
     28 
     29 /*
     30  * The NV21 format is a YUV format with an 8-bit Y-component and the U and V
     31  * components are stored as 8 bits each but they are shared between a block of
     32  * 2x2 pixels. So when calculating bits per pixel the 16 bits of U and V are
     33  * shared between 4 pixels leading to 4 bits of U and V per pixel. Together
     34  * with the 8 bits of Y this gives us 12 bits per pixel..
     35  *
     36  * The components are not grouped by pixels but separated into one Y-plane and
     37  * one interleaved U and V-plane. The first half of the byte sequence is all of
     38  * the Y data laid out in a linear fashion. After that the interleaved U and V-
     39  * plane starts with one byte of V followed by one byte of U followed by one
     40  * byte of V and so on. Each byte of U or V is associated with a 2x2 pixel block
     41  * in a linear fashion.
     42  *
     43  * For an 8 by 4 pixel image the layout would be:
     44  *
     45  * +-----+-----+-----+-----+-----+-----+-----+-----+
     46  * | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  |
     47  * +-----+-----+-----+-----+-----+-----+-----+-----+
     48  * | Y8  | Y9  | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
     49  * +-----+-----+-----+-----+-----+-----+-----+-----+
     50  * | Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
     51  * +-----+-----+-----+-----+-----+-----+-----+-----+
     52  * | Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
     53  * +-----+-----+-----+-----+-----+-----+-----+-----+
     54  * | V0  | U0  | V1  | U1  | V2  | U2  | V3  | U3  |
     55  * +-----+-----+-----+-----+-----+-----+-----+-----+
     56  * | V4  | U4  | V5  | U5  | V6  | U6  | V7  | U7  |
     57  * +-----+-----+-----+-----+-----+-----+-----+-----+
     58  *
     59  * In this image V0 and U0 are the V and U components for the 2x2 block of
     60  * pixels whose Y components are Y0, Y1, Y8 and Y9. V1 and U1 are matched with
     61  * the Y components Y2, Y3, Y10, Y11, and so on for that row. For the next row
     62  * of V and U the V4 and U4 components would be paired with Y16, Y17, Y24 and
     63  * Y25.
     64  */
     65 
     66 namespace android {
     67 
     68 static bool createRawThumbnail(const unsigned char* sourceImage,
     69                                int sourceWidth, int sourceHeight,
     70                                int thumbnailWidth, int thumbnailHeight,
     71                                std::vector<unsigned char>* thumbnail) {
     72     // Deinterleave the U and V planes into separate planes, this is because
     73     // libyuv requires the planes to be separate when scaling
     74     const size_t sourceUVPlaneSize = (sourceWidth * sourceHeight) / 4;
     75     // Put both U and V planes in one buffer, one after the other, to reduce
     76     // memory fragmentation and number of allocations
     77     std::vector<unsigned char> sourcePlanes(sourceUVPlaneSize * 2);
     78     const unsigned char* ySourcePlane = sourceImage;
     79     unsigned char* uSourcePlane = &sourcePlanes[0];
     80     unsigned char* vSourcePlane = &sourcePlanes[sourceUVPlaneSize];
     81 
     82     for (size_t i = 0; i < sourceUVPlaneSize; ++i) {
     83         vSourcePlane[i] = sourceImage[sourceWidth * sourceHeight + i * 2 + 0];
     84         uSourcePlane[i] = sourceImage[sourceWidth * sourceHeight + i * 2 + 1];
     85     }
     86 
     87     // Create enough space in the output vector for the result
     88     thumbnail->resize((thumbnailWidth * thumbnailHeight * 12) / 8);
     89 
     90     // The downscaled U and V planes will also be linear instead of interleaved,
     91     // allocate space for them here
     92     const size_t destUVPlaneSize = (thumbnailWidth * thumbnailHeight) / 4;
     93     std::vector<unsigned char> destPlanes(destUVPlaneSize * 2);
     94     unsigned char* yDestPlane = &(*thumbnail)[0];
     95     unsigned char* uDestPlane = &destPlanes[0];
     96     unsigned char* vDestPlane = &destPlanes[destUVPlaneSize];
     97 
     98     // The strides for the U and V planes are half the width because the U and V
     99     // components are common to 2x2 pixel blocks
    100     int result = libyuv::I420Scale(ySourcePlane, sourceWidth,
    101                                    uSourcePlane, sourceWidth / 2,
    102                                    vSourcePlane, sourceWidth / 2,
    103                                    sourceWidth, sourceHeight,
    104                                    yDestPlane, thumbnailWidth,
    105                                    uDestPlane, thumbnailWidth / 2,
    106                                    vDestPlane, thumbnailWidth / 2,
    107                                    thumbnailWidth, thumbnailHeight,
    108                                    libyuv::kFilterBilinear);
    109     if (result != 0) {
    110         ALOGE("Unable to create thumbnail, downscaling failed with error: %d",
    111               result);
    112         return false;
    113     }
    114 
    115     // Now we need to interleave the downscaled U and V planes into the
    116     // output buffer to make it NV21 encoded
    117     const size_t uvPlanesOffset = thumbnailWidth * thumbnailHeight;
    118     for (size_t i = 0; i < destUVPlaneSize; ++i) {
    119         (*thumbnail)[uvPlanesOffset + i * 2 + 0] = vDestPlane[i];
    120         (*thumbnail)[uvPlanesOffset + i * 2 + 1] = uDestPlane[i];
    121     }
    122 
    123     return true;
    124 }
    125 
    126 bool createThumbnail(const unsigned char* sourceImage,
    127                      int sourceWidth, int sourceHeight,
    128                      int thumbWidth, int thumbHeight, int quality,
    129                      ExifData* exifData) {
    130     if (thumbWidth <= 0 || thumbHeight <= 0) {
    131         ALOGE("%s: Invalid thumbnail width=%d or height=%d, must be > 0",
    132               __FUNCTION__, thumbWidth, thumbHeight);
    133         return false;
    134     }
    135 
    136     // First downscale the source image into a thumbnail-sized raw image
    137     std::vector<unsigned char> rawThumbnail;
    138     if (!createRawThumbnail(sourceImage, sourceWidth, sourceHeight,
    139                             thumbWidth, thumbHeight, &rawThumbnail)) {
    140         // The thumbnail function will log an appropriate error if needed
    141         return false;
    142     }
    143 
    144     // And then compress it into JPEG format without any EXIF data
    145     NV21JpegCompressor compressor;
    146     status_t result = compressor.compressRawImage(&rawThumbnail[0],
    147                                                   nullptr /* EXIF */,
    148                                                   quality, thumbWidth, thumbHeight);
    149     if (result != NO_ERROR) {
    150         ALOGE("%s: Unable to compress thumbnail", __FUNCTION__);
    151         return false;
    152     }
    153 
    154     // And finally put it in the EXIF data. This transfers ownership of the
    155     // malloc'd memory to the EXIF data structure. As long as the EXIF data
    156     // structure is free'd using the EXIF library this memory will be free'd.
    157     exifData->size = compressor.getCompressedSize();
    158     exifData->data = reinterpret_cast<unsigned char*>(malloc(exifData->size));
    159     if (exifData->data == nullptr) {
    160         ALOGE("%s: Unable to allocate %u bytes of memory for thumbnail",
    161               __FUNCTION__, exifData->size);
    162         exifData->size = 0;
    163         return false;
    164     }
    165     compressor.getCompressedImage(exifData->data);
    166     return true;
    167 }
    168 
    169 }  // namespace android
    170 
    171