Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 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 | |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 23 | using namespace jpegutil; |
| 24 | |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 25 | /** |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 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 | * |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 47 | * @param env the JNI environment |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 48 | * @param width the width of the image to compress |
| 49 | * @param height the height of the image to compress |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 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 |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 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 |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 67 | */ |
| 68 | extern "C" JNIEXPORT jint JNICALL |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 69 | Java_com_android_camera_util_JpegUtilNative_compressJpegFromYUV420pNative( |
Tomasz Wasilczyk | a5aa010 | 2017-04-07 14:54:24 -0700 | [diff] [blame] | 70 | JNIEnv* env, jclass clazz __unused, |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 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) { |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 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 | |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 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); |
Puneet Lall | 967b782 | 2014-08-07 17:05:38 -0700 | [diff] [blame] | 101 | } |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 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 |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 112 | * @param pStride the stride between adjacent pixels in the same row of |
| 113 | *planeBuf |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 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 | */ |
| 117 | extern "C" JNIEXPORT void JNICALL |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 118 | Java_com_android_camera_util_JpegUtilNative_copyImagePlaneToBitmap( |
Tomasz Wasilczyk | a5aa010 | 2017-04-07 14:54:24 -0700 | [diff] [blame] | 119 | JNIEnv* env, jclass clazz __unused, jint width, jint height, jobject planeBuf, |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 120 | jint pStride, jint rStride, jobject outBitmap, jint rot90) { |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 121 | jbyte* src = (jbyte*)env->GetDirectBufferAddress(planeBuf); |
| 122 | |
| 123 | char* dst = 0; |
Puneet Lall | 7e744b1 | 2015-02-09 17:45:44 -0800 | [diff] [blame] | 124 | AndroidBitmap_lockPixels(env, outBitmap, (void**)&dst); |
Puneet Lall | 0ad7ab0 | 2014-09-05 10:38:39 -0700 | [diff] [blame] | 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 | } |