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.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 
     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 
     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 
     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 
    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      */
    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      */
    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      */
    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 
    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      */
    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 
    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      */
    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      */
    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      */
    302     public ProcessingPriority getProcessingPriority() {
    303         return mProcessingPriority;
    304     }
    305 }
    306