Home | History | Annotate | Download | only in graphics
      1 #include "CreateJavaOutputStreamAdaptor.h"
      2 #include "SkJPEGWriteUtility.h"
      3 #include "YuvToJpegEncoder.h"
      4 #include <ui/PixelFormat.h>
      5 #include <hardware/hardware.h>
      6 
      7 #include "core_jni_helpers.h"
      8 
      9 #include <jni.h>
     10 
     11 YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
     12     // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
     13     // for now.
     14     if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
     15         return new Yuv420SpToJpegEncoder(strides);
     16     } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) {
     17         return new Yuv422IToJpegEncoder(strides);
     18     } else {
     19       return NULL;
     20     }
     21 }
     22 
     23 YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
     24 }
     25 
     26 struct ErrorMgr {
     27     struct jpeg_error_mgr pub;
     28     jmp_buf jmp;
     29 };
     30 
     31 void error_exit(j_common_ptr cinfo) {
     32     ErrorMgr* err = (ErrorMgr*) cinfo->err;
     33     (*cinfo->err->output_message) (cinfo);
     34     longjmp(err->jmp, 1);
     35 }
     36 
     37 bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
     38         int height, int* offsets, int jpegQuality) {
     39     jpeg_compress_struct    cinfo;
     40     ErrorMgr                err;
     41     skjpeg_destination_mgr  sk_wstream(stream);
     42 
     43     cinfo.err = jpeg_std_error(&err.pub);
     44     err.pub.error_exit = error_exit;
     45 
     46     if (setjmp(err.jmp)) {
     47         jpeg_destroy_compress(&cinfo);
     48         return false;
     49     }
     50     jpeg_create_compress(&cinfo);
     51 
     52     cinfo.dest = &sk_wstream;
     53 
     54     setJpegCompressStruct(&cinfo, width, height, jpegQuality);
     55 
     56     jpeg_start_compress(&cinfo, TRUE);
     57 
     58     compress(&cinfo, (uint8_t*) inYuv, offsets);
     59 
     60     jpeg_finish_compress(&cinfo);
     61 
     62     jpeg_destroy_compress(&cinfo);
     63 
     64     return true;
     65 }
     66 
     67 void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo,
     68         int width, int height, int quality) {
     69     cinfo->image_width = width;
     70     cinfo->image_height = height;
     71     cinfo->input_components = 3;
     72     cinfo->in_color_space = JCS_YCbCr;
     73     jpeg_set_defaults(cinfo);
     74 
     75     jpeg_set_quality(cinfo, quality, TRUE);
     76     jpeg_set_colorspace(cinfo, JCS_YCbCr);
     77     cinfo->raw_data_in = TRUE;
     78     cinfo->dct_method = JDCT_IFAST;
     79     configSamplingFactors(cinfo);
     80 }
     81 
     82 ///////////////////////////////////////////////////////////////////
     83 Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
     84         YuvToJpegEncoder(strides) {
     85     fNumPlanes = 2;
     86 }
     87 
     88 void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
     89         uint8_t* yuv, int* offsets) {
     90     SkDebugf("onFlyCompress");
     91     JSAMPROW y[16];
     92     JSAMPROW cb[8];
     93     JSAMPROW cr[8];
     94     JSAMPARRAY planes[3];
     95     planes[0] = y;
     96     planes[1] = cb;
     97     planes[2] = cr;
     98 
     99     int width = cinfo->image_width;
    100     int height = cinfo->image_height;
    101     uint8_t* yPlanar = yuv + offsets[0];
    102     uint8_t* vuPlanar = yuv + offsets[1]; //width * height;
    103     uint8_t* uRows = new uint8_t [8 * (width >> 1)];
    104     uint8_t* vRows = new uint8_t [8 * (width >> 1)];
    105 
    106 
    107     // process 16 lines of Y and 8 lines of U/V each time.
    108     while (cinfo->next_scanline < cinfo->image_height) {
    109         //deitnerleave u and v
    110         deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height);
    111 
    112         // Jpeg library ignores the rows whose indices are greater than height.
    113         for (int i = 0; i < 16; i++) {
    114             // y row
    115             y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0];
    116 
    117             // construct u row and v row
    118             if ((i & 1) == 0) {
    119                 // height and width are both halved because of downsampling
    120                 int offset = (i >> 1) * (width >> 1);
    121                 cb[i/2] = uRows + offset;
    122                 cr[i/2] = vRows + offset;
    123             }
    124           }
    125         jpeg_write_raw_data(cinfo, planes, 16);
    126     }
    127     delete [] uRows;
    128     delete [] vRows;
    129 
    130 }
    131 
    132 void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows,
    133         uint8_t* vRows, int rowIndex, int width, int height) {
    134     int numRows = (height - rowIndex) / 2;
    135     if (numRows > 8) numRows = 8;
    136     for (int row = 0; row < numRows; ++row) {
    137         int offset = ((rowIndex >> 1) + row) * fStrides[1];
    138         uint8_t* vu = vuPlanar + offset;
    139         for (int i = 0; i < (width >> 1); ++i) {
    140             int index = row * (width >> 1) + i;
    141             uRows[index] = vu[1];
    142             vRows[index] = vu[0];
    143             vu += 2;
    144         }
    145     }
    146 }
    147 
    148 void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
    149     // cb and cr are horizontally downsampled and vertically downsampled as well.
    150     cinfo->comp_info[0].h_samp_factor = 2;
    151     cinfo->comp_info[0].v_samp_factor = 2;
    152     cinfo->comp_info[1].h_samp_factor = 1;
    153     cinfo->comp_info[1].v_samp_factor = 1;
    154     cinfo->comp_info[2].h_samp_factor = 1;
    155     cinfo->comp_info[2].v_samp_factor = 1;
    156 }
    157 
    158 ///////////////////////////////////////////////////////////////////////////////
    159 Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
    160         YuvToJpegEncoder(strides) {
    161     fNumPlanes = 1;
    162 }
    163 
    164 void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
    165         uint8_t* yuv, int* offsets) {
    166     SkDebugf("onFlyCompress_422");
    167     JSAMPROW y[16];
    168     JSAMPROW cb[16];
    169     JSAMPROW cr[16];
    170     JSAMPARRAY planes[3];
    171     planes[0] = y;
    172     planes[1] = cb;
    173     planes[2] = cr;
    174 
    175     int width = cinfo->image_width;
    176     int height = cinfo->image_height;
    177     uint8_t* yRows = new uint8_t [16 * width];
    178     uint8_t* uRows = new uint8_t [16 * (width >> 1)];
    179     uint8_t* vRows = new uint8_t [16 * (width >> 1)];
    180 
    181     uint8_t* yuvOffset = yuv + offsets[0];
    182 
    183     // process 16 lines of Y and 16 lines of U/V each time.
    184     while (cinfo->next_scanline < cinfo->image_height) {
    185         deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height);
    186 
    187         // Jpeg library ignores the rows whose indices are greater than height.
    188         for (int i = 0; i < 16; i++) {
    189             // y row
    190             y[i] = yRows + i * width;
    191 
    192             // construct u row and v row
    193             // width is halved because of downsampling
    194             int offset = i * (width >> 1);
    195             cb[i] = uRows + offset;
    196             cr[i] = vRows + offset;
    197         }
    198 
    199         jpeg_write_raw_data(cinfo, planes, 16);
    200     }
    201     delete [] yRows;
    202     delete [] uRows;
    203     delete [] vRows;
    204 }
    205 
    206 
    207 void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
    208         uint8_t* vRows, int rowIndex, int width, int height) {
    209     int numRows = height - rowIndex;
    210     if (numRows > 16) numRows = 16;
    211     for (int row = 0; row < numRows; ++row) {
    212         uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0];
    213         for (int i = 0; i < (width >> 1); ++i) {
    214             int indexY = row * width + (i << 1);
    215             int indexU = row * (width >> 1) + i;
    216             yRows[indexY] = yuvSeg[0];
    217             yRows[indexY + 1] = yuvSeg[2];
    218             uRows[indexU] = yuvSeg[1];
    219             vRows[indexU] = yuvSeg[3];
    220             yuvSeg += 4;
    221         }
    222     }
    223 }
    224 
    225 void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
    226     // cb and cr are horizontally downsampled and vertically downsampled as well.
    227     cinfo->comp_info[0].h_samp_factor = 2;
    228     cinfo->comp_info[0].v_samp_factor = 2;
    229     cinfo->comp_info[1].h_samp_factor = 1;
    230     cinfo->comp_info[1].v_samp_factor = 2;
    231     cinfo->comp_info[2].h_samp_factor = 1;
    232     cinfo->comp_info[2].v_samp_factor = 2;
    233 }
    234 ///////////////////////////////////////////////////////////////////////////////
    235 
    236 static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
    237         jint format, jint width, jint height, jintArray offsets,
    238         jintArray strides, jint jpegQuality, jobject jstream,
    239         jbyteArray jstorage) {
    240     jbyte* yuv = env->GetByteArrayElements(inYuv, NULL);
    241     SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
    242 
    243     jint* imgOffsets = env->GetIntArrayElements(offsets, NULL);
    244     jint* imgStrides = env->GetIntArrayElements(strides, NULL);
    245     YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides);
    246     jboolean result = JNI_FALSE;
    247     if (encoder != NULL) {
    248         encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
    249         delete encoder;
    250         result = JNI_TRUE;
    251     }
    252 
    253     env->ReleaseByteArrayElements(inYuv, yuv, 0);
    254     env->ReleaseIntArrayElements(offsets, imgOffsets, 0);
    255     env->ReleaseIntArrayElements(strides, imgStrides, 0);
    256     delete strm;
    257     return result;
    258 }
    259 ///////////////////////////////////////////////////////////////////////////////
    260 
    261 static const JNINativeMethod gYuvImageMethods[] = {
    262     {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z",
    263         (void*)YuvImage_compressToJpeg }
    264 };
    265 
    266 int register_android_graphics_YuvImage(JNIEnv* env)
    267 {
    268     return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods,
    269                                          NELEM(gYuvImageMethods));
    270 }
    271