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