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