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 17#include <jni.h> 18#include <math.h> 19#include <android/bitmap.h> 20 21#include "jpegutil.h" 22 23using namespace jpegutil; 24 25/** 26 * Compresses a YCbCr image to jpeg, applying a crop and rotation. 27 * 28 * The input is defined as a set of 3 planes of 8-bit samples, one plane for 29 each channel of Y, Cb, Cr. 30 * The Y plane is assumed to have the same width and height of the entire image. 31 * The Cb and Cr planes are assumed to be downsampled by a factor of 2, to have 32 * dimensions (floor(width / 2), floor(height / 2)). 33 * Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride, and 34 * a row-stride. So, the sample at coordinate (x, y) can be retrieved from 35 * byteBuffer[x * pixel_stride + y * row_stride]. 36 * 37 * The pre-compression transformation is applied as follows: 38 * 1. The image is cropped to the rectangle from (cropLeft, cropTop) to 39 * (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) - 40 * (width, height) is a no-op. 41 * 2. The rotation is applied counter-clockwise relative to the coordinate 42 * space of the image, so a CCW rotation will appear CW when the image is 43 * rendered in scanline order. Only rotations which are multiples of 44 * 90-degrees are suppored, so the parameter 'rot90' specifies which multiple 45 * of 90 to rotate the image. 46 * 47 * @param env the JNI environment 48 * @param width the width of the image to compress 49 * @param height the height of the image to compress 50 * @param yBuf the buffer containing the Y component of the image 51 * @param yPStride the stride between adjacent pixels in the same row in yBuf 52 * @param yRStride the stride between adjacent rows in yBuf 53 * @param cbBuf the buffer containing the Cb component of the image 54 * @param cbPStride the stride between adjacent pixels in the same row in cbBuf 55 * @param cbRStride the stride between adjacent rows in cbBuf 56 * @param crBuf the buffer containing the Cr component of the image 57 * @param crPStride the stride between adjacent pixels in the same row in crBuf 58 * @param crRStride the stride between adjacent rows in crBuf 59 * @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg. This 60 * must have enough capacity to store the result, or an error code will be 61 * returned. 62 * @param outBufCapacity the capacity of outBuf 63 * @param quality the jpeg-quality (1-100) to use 64 * @param crop[Left|Top|Right|Bottom] the bounds of the image to crop to before 65 * rotation 66 * @param rot90 the multiple of 90 to rotate by 67 */ 68extern "C" JNIEXPORT jint JNICALL 69Java_com_android_camera_util_JpegUtilNative_compressJpegFromYUV420pNative( 70 JNIEnv* env, jclass clazz, 71 /** Input image dimensions */ 72 jint width, jint height, 73 /** Y Plane */ 74 jobject yBuf, jint yPStride, jint yRStride, 75 /** Cb Plane */ 76 jobject cbBuf, jint cbPStride, jint cbRStride, 77 /** Cr Plane */ 78 jobject crBuf, jint crPStride, jint crRStride, 79 /** Output */ 80 jobject outBuf, jint outBufCapacity, 81 /** Jpeg compression parameters */ 82 jint quality, 83 /** Crop */ 84 jint cropLeft, jint cropTop, jint cropRight, jint cropBottom, 85 /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree 86 * rotation. */ 87 jint rot90) { 88 jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf); 89 jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf); 90 jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf); 91 jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf); 92 93 return Compress(width, height, // 94 (unsigned char*)y, yPStride, yRStride, // 95 (unsigned char*)cb, cbPStride, cbRStride, // 96 (unsigned char*)cr, crPStride, crRStride, // 97 (unsigned char*)out, (size_t)outBufCapacity, // 98 quality, // 99 cropLeft, cropTop, cropRight, cropBottom, // 100 rot90); 101} 102 103/** 104 * Copies the Image.Plane specified by planeBuf, pStride, and rStride to the 105 * Bitmap. 106 * 107 * @param env the JNI environment 108 * @param clazz the java class 109 * @param width the width of the output image 110 * @param height the height of the output image 111 * @param planeBuf the native ByteBuffer containing the image plane data 112 * @param pStride the stride between adjacent pixels in the same row of 113 *planeBuf 114 * @param rStride the stride between adjacent rows in planeBuf 115 * @param rot90 the multiple of 90 degrees to rotate, one of {0, 1, 2, 3}. 116 */ 117extern "C" JNIEXPORT void JNICALL 118Java_com_android_camera_util_JpegUtilNative_copyImagePlaneToBitmap( 119 JNIEnv* env, jclass clazz, jint width, jint height, jobject planeBuf, 120 jint pStride, jint rStride, jobject outBitmap, jint rot90) { 121 jbyte* src = (jbyte*)env->GetDirectBufferAddress(planeBuf); 122 123 char* dst = 0; 124 AndroidBitmap_lockPixels(env, outBitmap, (void**)&dst); 125 126 if (rot90 == 0) { 127 // No rotation 128 for (int y = 0; y < height; y++) { 129 char* srcPtr = reinterpret_cast<char*>(&src[y * rStride]); 130 char* dstPtr = &dst[y * width]; 131 for (int x = 0; x < width; x++) { 132 *dstPtr = *srcPtr; 133 srcPtr += pStride; 134 dstPtr++; 135 } 136 } 137 } else if (rot90 == 1) { 138 // 90-degree rotation 139 for (int y = 0; y < height; y++) { 140 for (int x = 0; x < width; x++) { 141 int srcX = height - 1 - y; 142 int srcY = x; 143 dst[y * width + x] = src[srcX * pStride + rStride * srcY]; 144 } 145 } 146 } else if (rot90 == 2) { 147 // 180-degree rotation 148 for (int y = 0; y < height; y++) { 149 for (int x = 0; x < width; x++) { 150 int srcX = width - 1 - x; 151 int srcY = height - 1 - y; 152 dst[y * width + x] = src[srcX * pStride + rStride * srcY]; 153 } 154 } 155 } else if (rot90 == 3) { 156 // 270-degree rotation 157 for (int y = 0; y < height; y++) { 158 for (int x = 0; x < width; x++) { 159 int srcX = y; 160 int srcY = width - 1 - x; 161 dst[y * width + x] = src[srcX * pStride + rStride * srcY]; 162 } 163 } 164 } 165 166 AndroidBitmap_unlockPixels(env, outBitmap); 167} 168