Home | History | Annotate | Download | only in imagebackend
      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 package com.android.camera.processing.imagebackend;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.BitmapFactory;
     21 import android.graphics.ImageFormat;
     22 import android.graphics.Rect;
     23 import android.graphics.YuvImage;
     24 import android.net.Uri;
     25 
     26 import com.android.camera.debug.Log;
     27 import com.android.camera.one.v2.camera2proxy.ImageProxy;
     28 import com.android.camera.session.CaptureSession;
     29 
     30 import java.io.ByteArrayOutputStream;
     31 import java.io.IOException;
     32 import java.nio.ByteBuffer;
     33 import java.util.List;
     34 import java.util.concurrent.Executor;
     35 
     36 /**
     37  * TaskJpegEncode are the base class of tasks that wish to do JPEG
     38  * encoding/decoding. Various helper functions are held in this class.
     39  */
     40 public abstract class TaskJpegEncode extends TaskImageContainer {
     41 
     42     protected final static Log.Tag TAG = new Log.Tag("TaskJpegEnc");
     43 
     44     /**
     45      * Constructor to use for NOT passing the image reference forward.
     46      *
     47      * @param otherTask Parent task that is spawning this task
     48      * @param processingPriority Preferred processing priority for this task
     49      */
     50     public TaskJpegEncode(TaskImageContainer otherTask, ProcessingPriority processingPriority) {
     51         super(otherTask, processingPriority);
     52     }
     53 
     54     /**
     55      * Constructor to use for initial task definition or complex shared state
     56      * sharing.
     57      *
     58      * @param image Image reference that is required for computation
     59      * @param executor Executor to avoid thread control leakage
     60      * @param imageTaskManager ImageBackend associated with
     61      * @param preferredLane Preferred processing priority for this task
     62      * @param captureSession Session associated for UI handling
     63      */
     64     public TaskJpegEncode(ImageToProcess image, Executor executor,
     65             ImageTaskManager imageTaskManager,
     66             TaskImageContainer.ProcessingPriority preferredLane, CaptureSession captureSession) {
     67         super(image, executor, imageTaskManager, preferredLane, captureSession);
     68     }
     69 
     70     /**
     71      * Converts the YUV420_888 Image into a packed NV21 of a single byte array,
     72      * suitable for JPEG compression by the method convertNv21toJpeg. This
     73      * version will allocate its own byte buffer memory.
     74      *
     75      * @param img image to be converted
     76      * @return byte array of NV21 packed image
     77      */
     78     public byte[] convertYUV420ImageToPackedNV21(ImageProxy img) {
     79         final List<ImageProxy.Plane> planeList = img.getPlanes();
     80 
     81         ByteBuffer y_buffer = planeList.get(0).getBuffer();
     82         ByteBuffer u_buffer = planeList.get(1).getBuffer();
     83         ByteBuffer v_buffer = planeList.get(2).getBuffer();
     84         byte[] dataCopy = new byte[y_buffer.capacity() + u_buffer.capacity() + v_buffer.capacity()];
     85 
     86         return convertYUV420ImageToPackedNV21(img, dataCopy);
     87     }
     88 
     89     /**
     90      * Converts the YUV420_888 Image into a packed NV21 of a single byte array,
     91      * suitable for JPEG compression by the method convertNv21toJpeg. Creates a
     92      * memory block with the y component at the head and interleaves the u,v
     93      * components following the y component. Caller is responsible to allocate a
     94      * large enough buffer for results.
     95      *
     96      * @param img image to be converted
     97      * @param dataCopy buffer to write NV21 packed image
     98      * @return byte array of NV21 packed image
     99      */
    100     public byte[] convertYUV420ImageToPackedNV21(ImageProxy img, byte[] dataCopy) {
    101         // Get all the relevant information and then release the image.
    102         final int w = img.getWidth();
    103         final int h = img.getHeight();
    104         final List<ImageProxy.Plane> planeList = img.getPlanes();
    105 
    106         ByteBuffer y_buffer = planeList.get(0).getBuffer();
    107         ByteBuffer u_buffer = planeList.get(1).getBuffer();
    108         ByteBuffer v_buffer = planeList.get(2).getBuffer();
    109         final int color_pixel_stride = planeList.get(1).getPixelStride();
    110         final int y_size = y_buffer.capacity();
    111         final int u_size = u_buffer.capacity();
    112         final int data_offset = w * h;
    113 
    114         for (int i = 0; i < y_size; i++) {
    115             dataCopy[i] = (byte) (y_buffer.get(i) & 255);
    116         }
    117 
    118         for (int i = 0; i < u_size / color_pixel_stride; i++) {
    119             dataCopy[data_offset + 2 * i] = v_buffer.get(i * color_pixel_stride);
    120             dataCopy[data_offset + 2 * i + 1] = u_buffer.get(i * color_pixel_stride);
    121         }
    122 
    123         return dataCopy;
    124     }
    125 
    126     /**
    127      * Creates a dummy shaded image for testing in packed NV21 format.
    128      *
    129      * @param dataCopy Buffer to contained shaded test image
    130      * @param w Width of image
    131      * @param h Height of Image
    132      */
    133     public void dummyConvertYUV420ImageToPackedNV21(byte[] dataCopy,
    134             final int w, final int h) {
    135         final int y_size = w * h;
    136         final int data_offset = w * h;
    137 
    138         for (int i = 0; i < y_size; i++) {
    139             dataCopy[i] = (byte) ((((i % w) * 255) / w) & 255);
    140             dataCopy[i] = 0;
    141         }
    142 
    143         for (int i = 0; i < h / 2; i++) {
    144             for (int j = 0; j < w / 2; j++) {
    145                 int offset = data_offset + w * i + j * 2;
    146                 dataCopy[offset] = (byte) ((255 * i) / (h / 2) & 255);
    147                 dataCopy[offset + 1] = (byte) ((255 * j) / (w / 2) & 255);
    148             }
    149         }
    150     }
    151 
    152     /**
    153      * Wraps the Android built-in YUV to Jpeg conversion routine. Pass in a
    154      * valid NV21 image and get back a compressed JPEG buffer. A good default
    155      * JPEG compression implementation that should be supported on all
    156      * platforms.
    157      *
    158      * @param data_copy byte buffer that contains the NV21 image
    159      * @param w width of NV21 image
    160      * @param h height of N21 image
    161      * @return byte array of compressed JPEG image
    162      */
    163     public byte[] convertNv21toJpeg(byte[] data_copy, int w, int h, int[] strides) {
    164         Log.e(TAG, "TIMER_BEGIN NV21 to Jpeg Conversion.");
    165         YuvImage yuvImage = new YuvImage(data_copy, ImageFormat.NV21, w, h, strides);
    166 
    167         ByteArrayOutputStream postViewBytes = new ByteArrayOutputStream();
    168 
    169         yuvImage.compressToJpeg(new Rect(0, 0, w, h), 90, postViewBytes);
    170         try {
    171             postViewBytes.flush();
    172         } catch (IOException e) {
    173             e.printStackTrace();
    174         }
    175 
    176         Log.e(TAG, "TIMER_END NV21 to Jpeg Conversion.");
    177         return postViewBytes.toByteArray();
    178     }
    179 
    180     /**
    181      * Implement cropping through the decompression and re-compression of the JPEG using
    182      * the built-in Android bitmap utilities.
    183      *
    184      * @param jpegData Compressed Image to be cropped
    185      * @param crop Crop to be applied
    186      * @param recompressionQuality Recompression quality value for cropped JPEG Image
    187      * @return JPEG compressed byte array representing the cropped image
    188      */
    189     public byte[] decompressCropAndRecompressJpegData(final byte[] jpegData, Rect crop,
    190             int recompressionQuality) {
    191         Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
    192 
    193         final Bitmap croppedResult = Bitmap.createBitmap(original, crop.left, crop.top,
    194                 crop.width(), crop.height());;
    195 
    196         ByteArrayOutputStream stream = new ByteArrayOutputStream();
    197 
    198         croppedResult.compress(Bitmap.CompressFormat.JPEG, recompressionQuality, stream);
    199         return stream.toByteArray();
    200     }
    201 
    202     /**
    203      * Wraps the onResultCompressed listener for ease of use.
    204      *
    205      * @param id Unique content id
    206      * @param input Specification of image input size
    207      * @param result Specification of resultant input size
    208      * @param data Container for uncompressed data that represents image
    209      */
    210     public void onJpegEncodeDone(long id, TaskImage input, TaskImage result, byte[] data,
    211             TaskInfo.Destination aDestination) {
    212         TaskInfo job = new TaskInfo(id, input, result, aDestination);
    213         final ImageProcessorListener listener = mImageTaskManager.getProxyListener();
    214         listener.onResultCompressed(job, new CompressedPayload(data));
    215     }
    216 
    217     /**
    218      * Wraps the onResultUri listener for ease of use.
    219      *
    220      * @param id Unique content id
    221      * @param input Specification of image input size
    222      * @param result Specification of resultant input size
    223      * @param imageUri URI of the saved image.
    224      * @param destination Specifies the purpose of the image artifact
    225      */
    226     public void onUriResolved(long id, TaskImage input, TaskImage result, final Uri imageUri,
    227             TaskInfo.Destination destination) {
    228         final TaskInfo job = new TaskInfo(id, input, result, destination);
    229         final ImageProcessorListener listener = mImageTaskManager.getProxyListener();
    230         listener.onResultUri(job, imageUri);
    231     }
    232 }
    233