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      */
TaskJpegEncode(TaskImageContainer otherTask, ProcessingPriority processingPriority)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      */
TaskJpegEncode(ImageToProcess image, Executor executor, ImageTaskManager imageTaskManager, TaskImageContainer.ProcessingPriority preferredLane, CaptureSession captureSession)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      */
convertYUV420ImageToPackedNV21(ImageProxy img)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      */
convertYUV420ImageToPackedNV21(ImageProxy img, byte[] dataCopy)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      */
dummyConvertYUV420ImageToPackedNV21(byte[] dataCopy, final int w, final int h)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      */
convertNv21toJpeg(byte[] data_copy, int w, int h, int[] strides)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      */
decompressCropAndRecompressJpegData(final byte[] jpegData, Rect crop, int recompressionQuality)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      */
onJpegEncodeDone(long id, TaskImage input, TaskImage result, byte[] data, TaskInfo.Destination aDestination)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      */
onUriResolved(long id, TaskImage input, TaskImage result, final Uri imageUri, TaskInfo.Destination destination)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