blob: f3eb7eab07562acd018df55834cce7a1632dbb1e [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.extensions.impl.advanced;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageWriter;
import android.util.Log;
import java.nio.ByteBuffer;
// Jpeg compress input YUV and queue back in the client target surface.
public class JpegEncoder {
public final static int JPEG_DEFAULT_QUALITY = 100;
public final static int JPEG_DEFAULT_ROTATION = 0;
public static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
/**
* Compresses a YCbCr image to jpeg, applying a crop and rotation.
* <p>
* The input is defined as a set of 3 planes of 8-bit samples, one plane for
* each channel of Y, Cb, Cr.<br>
* The Y plane is assumed to have the same width and height of the entire
* image.<br>
* The Cb and Cr planes are assumed to be downsampled by a factor of 2, to
* have dimensions (floor(width / 2), floor(height / 2)).<br>
* Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride,
* and a row-stride. So, the sample at coordinate (x, y) can be retrieved
* from byteBuffer[x * pixel_stride + y * row_stride].
* <p>
* The pre-compression transformation is applied as follows:
* <ol>
* <li>The image is cropped to the rectangle from (cropLeft, cropTop) to
* (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) -
* (width, height) is a no-op.</li>
* <li>The rotation is applied counter-clockwise relative to the coordinate
* space of the image, so a CCW rotation will appear CW when the image is
* rendered in scanline order. Only rotations which are multiples of
* 90-degrees are suppored, so the parameter 'rot90' specifies which
* multiple of 90 to rotate the image.</li>
* </ol>
*
* @param width the width of the image to compress
* @param height the height of the image to compress
* @param yBuf the buffer containing the Y component of the image
* @param yPStride the stride between adjacent pixels in the same row in
* yBuf
* @param yRStride the stride between adjacent rows in yBuf
* @param cbBuf the buffer containing the Cb component of the image
* @param cbPStride the stride between adjacent pixels in the same row in
* cbBuf
* @param cbRStride the stride between adjacent rows in cbBuf
* @param crBuf the buffer containing the Cr component of the image
* @param crPStride the stride between adjacent pixels in the same row in
* crBuf
* @param crRStride the stride between adjacent rows in crBuf
* @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg.
* This must have enough capacity to store the result, or an
* error code will be returned.
* @param outBufCapacity the capacity of outBuf
* @param quality the jpeg-quality (1-100) to use
* @param cropLeft left-edge of the bounds of the image to crop to before
* rotation
* @param cropTop top-edge of the bounds of the image to crop to before
* rotation
* @param cropRight right-edge of the bounds of the image to crop to before
* rotation
* @param cropBottom bottom-edge of the bounds of the image to crop to
* before rotation
* @param rot90 the multiple of 90 to rotate the image CCW (after cropping)
*/
public static native int compressJpegFromYUV420pNative(
int width, int height,
ByteBuffer yBuf, int yPStride, int yRStride,
ByteBuffer cbBuf, int cbPStride, int cbRStride,
ByteBuffer crBuf, int crPStride, int crRStride,
ByteBuffer outBuf, int outBufCapacity,
int quality,
int cropLeft, int cropTop, int cropRight, int cropBottom,
int rot90);
public static void encodeToJpeg(Image yuvImage, Image jpegImage,
int jpegOrientation, int jpegQuality) {
jpegOrientation = (360 - (jpegOrientation % 360)) / 90;
ByteBuffer jpegBuffer = jpegImage.getPlanes()[0].getBuffer();
jpegBuffer.clear();
int jpegCapacity = jpegImage.getWidth();
Plane lumaPlane = yuvImage.getPlanes()[0];
Plane crPlane = yuvImage.getPlanes()[1];
Plane cbPlane = yuvImage.getPlanes()[2];
JpegEncoder.compressJpegFromYUV420pNative(
yuvImage.getWidth(), yuvImage.getHeight(),
lumaPlane.getBuffer(), lumaPlane.getPixelStride(), lumaPlane.getRowStride(),
crPlane.getBuffer(), crPlane.getPixelStride(), crPlane.getRowStride(),
cbPlane.getBuffer(), cbPlane.getPixelStride(), cbPlane.getRowStride(),
jpegBuffer, jpegCapacity, jpegQuality,
0, 0, yuvImage.getWidth(), yuvImage.getHeight(),
jpegOrientation);
}
public static int imageFormatToPublic(int format) {
switch (format) {
case HAL_PIXEL_FORMAT_BLOB:
return ImageFormat.JPEG;
case ImageFormat.JPEG:
throw new IllegalArgumentException(
"ImageFormat.JPEG is an unknown internal format");
default:
return format;
}
}
}