YuvImageBackendImageSaver.java revision 0f0329889f69182648fe8f535335e48978d63cc0
1/*
2 * Copyright (C) 2015 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
17package com.android.camera.one.v2.imagesaver;
18
19import android.graphics.Bitmap;
20import android.graphics.Matrix;
21import android.graphics.Rect;
22import android.net.Uri;
23
24import com.android.camera.app.OrientationManager;
25import com.android.camera.one.OneCamera;
26import com.android.camera.one.v2.camera2proxy.ImageProxy;
27import com.android.camera.one.v2.camera2proxy.TotalCaptureResultProxy;
28import com.android.camera.one.v2.photo.ImageRotationCalculator;
29import com.android.camera.processing.imagebackend.ImageBackend;
30import com.android.camera.processing.imagebackend.ImageConsumer;
31import com.android.camera.processing.imagebackend.ImageProcessorListener;
32import com.android.camera.processing.imagebackend.ImageToProcess;
33import com.android.camera.processing.imagebackend.TaskImageContainer;
34import com.android.camera.session.CaptureSession;
35import com.android.camera2.R;
36
37import com.google.common.annotations.VisibleForTesting;
38import com.google.common.base.Optional;
39import com.google.common.util.concurrent.ListenableFuture;
40
41import java.util.HashSet;
42import java.util.Set;
43import java.util.concurrent.Executor;
44import java.util.concurrent.Executors;
45
46import javax.annotation.Nonnull;
47import javax.annotation.ParametersAreNonnullByDefault;
48
49/**
50 * Wires up the ImageBackend task submission process to save Yuv images.
51 */
52public class YuvImageBackendImageSaver implements ImageSaver.Builder {
53
54    @ParametersAreNonnullByDefault
55    private final class ImageSaverImpl implements SingleImageSaver {
56        private final CaptureSession mSession;
57        private final OrientationManager.DeviceOrientation mImageRotation;
58        private final ImageProcessorListener mImageProcessorListener;
59
60        public ImageSaverImpl(CaptureSession session,
61                OrientationManager.DeviceOrientation imageRotation,
62                ImageProcessorListener imageProcessorListener) {
63            mSession = session;
64            mImageRotation = imageRotation;
65            mImageProcessorListener = imageProcessorListener;
66        }
67
68        @Override
69        public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail,
70                ListenableFuture<TotalCaptureResultProxy> metadata) {
71            // TODO Use thumbnail to speedup RGB thumbnail creation whenever
72            // possible.
73            if (thumbnail.isPresent()) {
74                thumbnail.get().close();
75            }
76
77            Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>();
78            taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW);
79            taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CONVERT_TO_RGB_PREVIEW);
80            taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK);
81            taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE);
82
83            try {
84                mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation, metadata,
85                        mCrop), mExecutor, taskFlagsSet, mSession,
86                        Optional.of(mImageProcessorListener));
87            } catch (InterruptedException e) {
88                // Impossible exception because receiveImage is nonblocking
89                throw new RuntimeException(e);
90            }
91        }
92    }
93
94    private static class YuvImageProcessorListener implements ImageProcessorListener {
95        private final CaptureSession mSession;
96        private final OrientationManager.DeviceOrientation mImageRotation;
97        private final OneCamera.PictureSaverCallback mPictureSaverCallback;
98
99        private YuvImageProcessorListener(CaptureSession session,
100                OrientationManager.DeviceOrientation imageRotation,
101                OneCamera.PictureSaverCallback pictureSaverCallback) {
102            mSession = session;
103            mImageRotation = imageRotation;
104            mPictureSaverCallback = pictureSaverCallback;
105        }
106
107        @Override
108        public void onStart(TaskImageContainer.TaskInfo task) {
109            switch (task.destination) {
110                case FAST_THUMBNAIL:
111                    // Signal start of processing
112                    break;
113                case INTERMEDIATE_THUMBNAIL:
114                    // Do nothing
115                    break;
116            }
117        }
118
119        @Override
120        public void onResultCompressed(TaskImageContainer.TaskInfo task,
121                TaskImageContainer.CompressedPayload payload) {
122            if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
123                mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data);
124            }
125        }
126
127        @Override
128        public void onResultUncompressed(TaskImageContainer.TaskInfo task,
129                TaskImageContainer.UncompressedPayload payload) {
130            // Load bitmap into CameraAppUI
131            switch (task.destination) {
132                case FAST_THUMBNAIL:
133                    final Bitmap bitmap = Bitmap.createBitmap(payload.data,
134                            task.result.width,
135                            task.result.height, Bitmap.Config.ARGB_8888);
136                    mSession.updateCaptureIndicatorThumbnail(bitmap, mImageRotation.getDegrees());
137                    break;
138                case INTERMEDIATE_THUMBNAIL:
139                    final Bitmap bitmapIntermediate = Bitmap.createBitmap(payload.data,
140                            task.result.width,
141                            task.result.height, Bitmap.Config.ARGB_8888);
142                    Matrix matrix = new Matrix();
143                    matrix.postRotate(mImageRotation.getDegrees());
144                    final Bitmap bitmapIntermediateRotated = Bitmap.createBitmap(
145                            bitmapIntermediate, 0, 0, bitmapIntermediate.getWidth(),
146                            bitmapIntermediate.getHeight(), matrix, true);
147                    mSession.updateThumbnail(bitmapIntermediateRotated);
148                    mSession.setProgressMessage(R.string.session_saving_image);
149                    break;
150            }
151        }
152
153        @Override
154        public void onResultUri(TaskImageContainer.TaskInfo task, Uri uri) {
155            // Do Nothing
156        }
157    }
158
159    private final ImageRotationCalculator mImageRotationCalculator;
160    private final ImageBackend mImageBackend;
161    private final Rect mCrop;
162    private final Executor mExecutor;
163
164    /**
165     * Constructor
166     *
167     * @param imageRotationCalculator the image rotation calculator to determine
168     * @param imageBackend ImageBackend to run the image tasks
169     * @param crop the crop to apply. Note that crop must be done *before* any
170     *            rotation of the images.
171     */
172    public YuvImageBackendImageSaver(ImageRotationCalculator imageRotationCalculator,
173            ImageBackend imageBackend, Rect crop) {
174        mImageRotationCalculator = imageRotationCalculator;
175        mImageBackend = imageBackend;
176        mCrop = crop;
177        mExecutor = Executors.newSingleThreadExecutor();
178    }
179
180    /**
181     * Constructor for dependency injection/ testing.
182     *
183     * @param imageRotationCalculator the image rotation calculator to determine
184     * @param imageBackend ImageBackend to run the image tasks
185     * @param crop the crop to apply. Note that crop must be done *before* any
186     *            rotation of the images.
187     * @param executor Executor to be used for listener events in ImageBackend.
188     */
189    @VisibleForTesting
190    public YuvImageBackendImageSaver(ImageRotationCalculator imageRotationCalculator,
191            ImageBackend imageBackend, Rect crop, Executor executor) {
192        mImageRotationCalculator = imageRotationCalculator;
193        mImageBackend = imageBackend;
194        mCrop = crop;
195        mExecutor = executor;
196    }
197
198    /**
199     * Builder for the Zsl/ImageBackend Interface
200     *
201     * @return Instantiated interface object
202     */
203    @Override
204    public ImageSaver build(
205            @Nonnull OneCamera.PictureSaverCallback pictureSaverCallback,
206            @Nonnull OrientationManager.DeviceOrientation orientation,
207            @Nonnull CaptureSession session) {
208        final OrientationManager.DeviceOrientation imageRotation = mImageRotationCalculator
209                .toImageRotation();
210
211        YuvImageProcessorListener yuvImageProcessorListener = new YuvImageProcessorListener(
212                session, imageRotation, pictureSaverCallback);
213        return new MostRecentImageSaver(new ImageSaverImpl(session, imageRotation,
214                yuvImageProcessorListener));
215    }
216}
217