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 */
16package com.android.camera.util;
17
18import android.graphics.ImageFormat;
19import android.media.Image;
20import android.media.Image.Plane;
21
22import java.nio.ByteBuffer;
23
24/**
25 * Provides direct access to libjpeg-turbo via the NDK.
26 */
27public class JpegUtilNative {
28    static {
29        System.loadLibrary("jni_jpegutil");
30    }
31
32    public static final int ERROR_OUT_BUF_TOO_SMALL = -1;
33
34    /**
35     * Compresses an image from YUV422 format to jpeg.
36     *
37     * @param yBuf the buffer containing the Y component of the image
38     * @param yPStride the stride between adjacent pixels in the same row in yBuf
39     * @param yRStride the stride between adjacent rows in yBuf
40     * @param cbBuf the buffer containing the Cb component of the image
41     * @param cbPStride the stride between adjacent pixels in the same row in cbBuf
42     * @param cbRStride the stride between adjacent rows in cbBuf
43     * @param crBuf the buffer containing the Cr component of the image
44     * @param crPStride the stride between adjacent pixels in the same row in crBuf
45     * @param crRStride the stride between adjacent rows in crBuf
46     * @param quality the quality level (0 to 100) to use
47     * @return The number of bytes written, or a negative value indicating an error
48     */
49    private static native int compressJpegFromYUV420pNative(
50            int width, int height,
51            Object yBuf, int yPStride, int yRStride,
52            Object cbBuf, int cbPStride, int cbRStride,
53            Object crBuf, int crPStride, int crRStride,
54            Object outBuf, int outBufCapacity, int quality);
55
56    /**
57     * @see JpegUtilNative#compressJpegFromYUV420pNative(int, int, java.lang.Object, int, int,
58     *      java.lang.Object, int, int, java.lang.Object, int, int, java.lang.Object, int, int)
59     */
60    public static int compressJpegFromYUV420p(
61            int width, int height,
62            ByteBuffer yBuf, int yPStride, int yRStride,
63            ByteBuffer cbBuf, int cbPStride, int cbRStride,
64            ByteBuffer crBuf, int crPStride, int crRStride,
65            ByteBuffer outBuf, int quality) {
66        return compressJpegFromYUV420pNative(width, height, yBuf, yPStride, yRStride, cbBuf,
67                cbPStride, cbRStride, crBuf, crPStride, crRStride, outBuf, outBuf.capacity(), quality);
68    }
69
70    /**
71     * Compresses the given image to jpeg. Note that only ImageFormat.YUV_420_888 is currently
72     * supported. Furthermore, all planes must use direct byte buffers.
73     *
74     * @param img the image to compress
75     * @param outBuf a direct byte buffer to hold the output jpeg.
76     * @return The number of bytes written to outBuf
77     */
78    public static int compressJpegFromYUV420Image(Image img, ByteBuffer outBuf, int quality) {
79        if (img.getFormat() != ImageFormat.YUV_420_888) {
80            throw new RuntimeException("Unsupported Image Format.");
81        }
82
83        final int NUM_PLANES = 3;
84
85        if (img.getPlanes().length != NUM_PLANES) {
86            throw new RuntimeException("Output buffer must be direct.");
87        }
88
89        if (!outBuf.isDirect()) {
90            throw new RuntimeException("Output buffer must be direct.");
91        }
92
93        ByteBuffer[] planeBuf = new ByteBuffer[NUM_PLANES];
94        int[] pixelStride = new int[NUM_PLANES];
95        int[] rowStride = new int[NUM_PLANES];
96
97        for (int i = 0; i < NUM_PLANES; i++) {
98            Plane plane = img.getPlanes()[i];
99
100            if (!plane.getBuffer().isDirect()) {
101                return -1;
102            }
103
104            planeBuf[i] = plane.getBuffer();
105            pixelStride[i] = plane.getPixelStride();
106            rowStride[i] = plane.getRowStride();
107        }
108
109        outBuf.clear();
110
111        int numBytesWritten = compressJpegFromYUV420p(
112                img.getWidth(), img.getHeight(),
113                planeBuf[0], pixelStride[0], rowStride[0],
114                planeBuf[1], pixelStride[1], rowStride[1],
115                planeBuf[2], pixelStride[2], rowStride[2],
116                outBuf, quality);
117
118        outBuf.limit(numBytesWritten);
119
120        return numBytesWritten;
121    }
122}
123