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