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.Rect; 20 import com.android.camera.app.OrientationManager; 21 import com.android.camera.debug.Log; 22 import com.android.camera.one.v2.camera2proxy.ImageProxy; 23 import com.android.camera.session.CaptureSession; 24 25 import java.util.concurrent.Executor; 26 27 import javax.annotation.Nullable; 28 29 /** 30 * TaskImageContainer are the base class of tasks that wish to run with the 31 * ImageBackend class. It contains the basic information required to interact 32 * with the ImageBackend class and the ability to identify itself to the UI 33 * backend for updates on its progress. 34 */ 35 public abstract class TaskImageContainer implements Runnable { 36 37 /** 38 * Simple helper class to encapsulate uncompressed payloads. Could be more 39 * complex in the future. 40 */ 41 static public class UncompressedPayload { 42 final public int[] data; 43 UncompressedPayload(int[] passData)44 UncompressedPayload(int[] passData) { 45 data = passData; 46 } 47 } 48 49 /** 50 * Simple helper class to encapsulate compressed payloads. Could be more 51 * complex in the future. 52 */ 53 static public class CompressedPayload { 54 final public byte[] data; 55 CompressedPayload(byte[] passData)56 CompressedPayload(byte[] passData) { 57 data = passData; 58 } 59 } 60 61 /** 62 * Simple helper class to encapsulate all necessary image information that 63 * is carried with the data to processing, so that tasks derived off of 64 * TaskImageContainer can properly coordinate and optimize its computation. 65 */ 66 static public class TaskImage { 67 // Addendum to Android-defined image-format 68 public final static int EXTRA_USER_DEFINED_FORMAT_ARGB_8888 = -1; 69 70 // Minimal required knowledge for the image specification. 71 public final OrientationManager.DeviceOrientation orientation; 72 73 public final int height; 74 public final int width; 75 public final int format; 76 public final Rect cropApplied; 77 TaskImage(OrientationManager.DeviceOrientation anOrientation, int aWidth, int aHeight, int aFormat, Rect crop)78 TaskImage(OrientationManager.DeviceOrientation anOrientation, int aWidth, int aHeight, 79 int aFormat, Rect crop) { 80 orientation = anOrientation; 81 height = aHeight; 82 width = aWidth; 83 format = aFormat; 84 cropApplied = crop; 85 } 86 87 } 88 89 /** 90 * Simple helper class to encapsulate input and resultant image 91 * specification. TasksImageContainer classes can be uniquely identified by 92 * triplet of its content (currently, the global timestamp of when the 93 * object was taken), the image specification of the input and the desired 94 * output image specification. Added a field to specify the destination of 95 * the image artifact, since spawn tasks may created multiple un/compressed 96 * artifacts of different size that need to be routed to different 97 * components. 98 */ 99 static public class TaskInfo { 100 101 /** 102 * A single task graph can often create multiple imaging processing 103 * artifacts and the listener needs to distinguish an uncompressed image 104 * meant for image destinations. The different destinations are as 105 * follows: 106 * <ul> 107 * <li>FAST_THUMBNAIL: Small image required as soon as possible</li> 108 * <li>INTERMEDIATE_THUMBNAIL: Mid-sized image required for filmstrips 109 * at approximately 100-500ms latency</li> 110 * <li>FINAL_IMAGE: Full-resolution image artifact where latency > 500 111 * ms</li> 112 * </ul> 113 */ 114 public enum Destination { 115 FAST_THUMBNAIL, 116 INTERMEDIATE_THUMBNAIL, 117 FINAL_IMAGE 118 } 119 120 public final Destination destination; 121 // The unique Id of the image being processed. 122 public final long contentId; 123 124 public final TaskImage input; 125 126 public final TaskImage result; 127 TaskInfo(long aContentId, TaskImage inputSpec, TaskImage outputSpec, Destination aDestination)128 TaskInfo(long aContentId, TaskImage inputSpec, TaskImage outputSpec, 129 Destination aDestination) { 130 contentId = aContentId; 131 input = inputSpec; 132 result = outputSpec; 133 destination = aDestination; 134 } 135 136 } 137 138 public enum ProcessingPriority { 139 FAST, AVERAGE, SLOW 140 } 141 142 protected final static Log.Tag TAG = new Log.Tag("TaskImgContain"); 143 144 final protected ImageTaskManager mImageTaskManager; 145 146 final protected Executor mExecutor; 147 148 final protected long mId; 149 150 final protected ProcessingPriority mProcessingPriority; 151 152 final protected ImageToProcess mImage; 153 154 final protected CaptureSession mSession; 155 156 /** 157 * Constructor when releasing the image reference. 158 * 159 * @param otherTask the original task that is spawning this task. 160 * @param processingPriority Priority that the derived task will run at. 161 */ TaskImageContainer(TaskImageContainer otherTask, ProcessingPriority processingPriority)162 public TaskImageContainer(TaskImageContainer otherTask, ProcessingPriority processingPriority) { 163 mId = otherTask.mId; 164 mExecutor = otherTask.mExecutor; 165 mImageTaskManager = otherTask.mImageTaskManager; 166 mProcessingPriority = processingPriority; 167 mSession = otherTask.mSession; 168 mImage = null; 169 } 170 171 /** 172 * Constructor to use when keeping the image reference. 173 * 174 * @param image Image reference that needs to be released. 175 * @param Executor Executor to run the event handling, if required. 176 * @param imageTaskManager a reference to the ImageBackend, in case, you 177 * need to spawn other tasks 178 * @param preferredLane Priority that the derived task will run at 179 * @param captureSession Session that handles image processing events 180 */ TaskImageContainer(ImageToProcess image, @Nullable Executor Executor, ImageTaskManager imageTaskManager, ProcessingPriority preferredLane, CaptureSession captureSession)181 public TaskImageContainer(ImageToProcess image, @Nullable Executor Executor, 182 ImageTaskManager imageTaskManager, 183 ProcessingPriority preferredLane, CaptureSession captureSession) { 184 mImage = image; 185 mId = mImage.proxy.getTimestamp(); 186 mExecutor = Executor; 187 mImageTaskManager = imageTaskManager; 188 mProcessingPriority = preferredLane; 189 mSession = captureSession; 190 } 191 192 /** 193 * Returns rotated crop rectangle in terms of absolute sensor crop 194 * 195 */ rotateBoundingBox(Rect box, OrientationManager.DeviceOrientation orientation)196 protected Rect rotateBoundingBox(Rect box, OrientationManager.DeviceOrientation orientation) { 197 if(orientation == OrientationManager.DeviceOrientation.CLOCKWISE_0 || 198 orientation == OrientationManager.DeviceOrientation.CLOCKWISE_180) { 199 return new Rect(box); 200 } else { 201 // Switch x/y coordinates. 202 return new Rect(box.top, box.left, box.bottom, box.right); 203 } 204 } 205 addOrientation( OrientationManager.DeviceOrientation orientation1, OrientationManager.DeviceOrientation orientation2)206 protected OrientationManager.DeviceOrientation addOrientation( 207 OrientationManager.DeviceOrientation orientation1, 208 OrientationManager.DeviceOrientation orientation2) { 209 return OrientationManager.DeviceOrientation.from(orientation1.getDegrees() 210 + orientation2.getDegrees()); 211 } 212 213 /** 214 * Returns a crop rectangle whose points are a strict subset of the points 215 * specified by image rectangle. A Null Intersection returns 216 * Rectangle(0,0,0,0). 217 * 218 * @param image image to be cropped 219 * @param crop an arbitrary crop rectangle; if null, the crop is assumed to 220 * be set of all points. 221 * @return the rectangle produced by the intersection of the image rectangle 222 * with passed-in crop rectangle; a null intersection returns 223 * Rect(0,0,0,0) 224 */ guaranteedSafeCrop(ImageProxy image, @Nullable Rect crop)225 public Rect guaranteedSafeCrop(ImageProxy image, @Nullable Rect crop) { 226 return guaranteedSafeCrop(image.getWidth(), image.getHeight(), crop); 227 } 228 229 /** 230 * Returns a crop rectangle whose points are a strict subset of the points 231 * specified by image rectangle. A Null Intersection returns Rectangle(0,0,0,0). 232 * Since sometimes the ImageProxy doesn't take into account rotation. The Image 233 * is assumed to have its top-left corner at (0,0). 234 * 235 * @param width image width 236 * @param height image height 237 * @param crop an arbitrary crop rectangle; if null, the crop is assumed to 238 * be set of all points. 239 * @return the rectangle produced by the intersection of the image rectangle 240 * with passed-in crop rectangle; a null intersection returns 241 * Rect(0,0,0,0) 242 */ 243 guaranteedSafeCrop(int width, int height, @Nullable Rect crop)244 public Rect guaranteedSafeCrop(int width, int height, @Nullable Rect crop) { 245 if (crop == null) { 246 return new Rect(0, 0, width, height); 247 } 248 Rect safeCrop = new Rect(crop); 249 if (crop.top > crop.bottom || crop.left > crop.right || crop.width() <= 0 250 || crop.height() <= 0) { 251 return new Rect(0, 0, 0, 0); 252 } 253 254 safeCrop.left = Math.max(safeCrop.left, 0); 255 safeCrop.top = Math.max(safeCrop.top, 0); 256 safeCrop.right = Math.max(Math.min(safeCrop.right, width), safeCrop.left); 257 safeCrop.bottom = Math.max(Math.min(safeCrop.bottom, height), safeCrop.top); 258 259 if (safeCrop.width() <= 0 || safeCrop.height() <= 0) { 260 return new Rect(0, 0, 0, 0); 261 } 262 263 return safeCrop; 264 } 265 266 /** 267 * Returns whether the crop operation is required. 268 * 269 * @param image Image to be cropped 270 * @param crop Crop region 271 * @return whether the image needs any more processing to be cropped 272 * properly. 273 */ requiresCropOperation(ImageProxy image, @Nullable Rect crop)274 public boolean requiresCropOperation(ImageProxy image, @Nullable Rect crop) { 275 if (crop == null) { 276 return false; 277 } 278 279 return !(crop.equals(new Rect(0, 0, image.getWidth(), image.getHeight()))); 280 } 281 282 /** 283 * Basic listener function to signal ImageBackend that task has started. 284 * 285 * @param id Id for image content 286 * @param input Image specification for task input 287 * @param result Image specification for task result 288 * @param aDestination Purpose of image processing artifact 289 */ onStart(long id, TaskImage input, TaskImage result, TaskInfo.Destination aDestination)290 public void onStart(long id, TaskImage input, TaskImage result, 291 TaskInfo.Destination aDestination) { 292 TaskInfo job = new TaskInfo(id, input, result, aDestination); 293 final ImageProcessorListener listener = mImageTaskManager.getProxyListener(); 294 listener.onStart(job); 295 } 296 297 /** 298 * Getter for Processing Priority 299 * 300 * @return Processing Priority associated with the task. 301 */ getProcessingPriority()302 public ProcessingPriority getProcessingPriority() { 303 return mProcessingPriority; 304 } 305 } 306