YuvImageBackendImageSaver.java revision f68a39a0ad362ff0543e11e3f0ce11741a7de48c
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.net.Uri;
22
23import com.android.camera.app.OrientationManager;
24import com.android.camera.async.MainThread;
25import com.android.camera.one.OneCamera;
26import com.android.camera.one.v2.camera2proxy.ImageProxy;
27import com.android.camera.one.v2.photo.ImageRotationCalculator;
28import com.android.camera.processing.imagebackend.ImageBackend;
29import com.android.camera.processing.imagebackend.ImageConsumer;
30import com.android.camera.processing.imagebackend.ImageProcessorListener;
31import com.android.camera.processing.imagebackend.ImageProcessorProxyListener;
32import com.android.camera.processing.imagebackend.ImageToProcess;
33import com.android.camera.processing.imagebackend.TaskImageContainer;
34import com.android.camera.session.CaptureSession;
35
36import com.google.common.base.Optional;
37
38import java.util.HashSet;
39import java.util.Set;
40
41import javax.annotation.Nonnull;
42import javax.annotation.ParametersAreNonnullByDefault;
43
44/**
45 * Wires up the ImageBackend task submission process to save Yuv images.
46 */
47public class YuvImageBackendImageSaver implements ImageSaver.Builder {
48    @ParametersAreNonnullByDefault
49    private static class ImageSaverImpl implements SingleImageSaver {
50        private final MainThread mExecutor;
51        private final CaptureSession mSession;
52        private final OrientationManager.DeviceOrientation mImageRotation;
53        private final ImageBackend mImageBackend;
54        private final ImageProcessorListener mPreviewListener;
55
56        public ImageSaverImpl(MainThread executor,
57                CaptureSession session, OrientationManager.DeviceOrientation imageRotation,
58                ImageBackend imageBackend, ImageProcessorListener previewListener) {
59            mExecutor = executor;
60            mSession = session;
61            mImageRotation = imageRotation;
62            mImageBackend = imageBackend;
63            mPreviewListener = previewListener;
64        }
65
66        @Override
67        public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail) {
68            // TODO Use thumbnail to speedup RGB thumbnail creation whenever
69            // possible.
70            if (thumbnail.isPresent()) {
71                thumbnail.get().close();
72            }
73            final ImageProcessorProxyListener listenerProxy = mImageBackend.getProxyListener();
74
75            listenerProxy.registerListener(mPreviewListener, image);
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),
85                        mExecutor, taskFlagsSet, mSession);
86            } catch (InterruptedException e) {
87                // Impossible exception because receiveImage is nonblocking
88                throw new RuntimeException(e);
89            }
90        }
91    }
92
93    private static class PreviewListener implements ImageProcessorListener {
94        private final MainThread mExecutor;
95        private final ImageProcessorProxyListener mListenerProxy;
96        private final CaptureSession mSession;
97        private final OrientationManager.DeviceOrientation mImageRotation;
98        private final OneCamera.PictureSaverCallback mPictureSaverCallback;
99
100        private PreviewListener(MainThread executor,
101                ImageProcessorProxyListener listenerProxy, CaptureSession session,
102                OrientationManager.DeviceOrientation imageRotation,
103                OneCamera.PictureSaverCallback pictureSaverCallback) {
104            mExecutor = executor;
105            mListenerProxy = listenerProxy;
106            mSession = session;
107            mImageRotation = imageRotation;
108            mPictureSaverCallback = pictureSaverCallback;
109        }
110
111        @Override
112        public void onStart(TaskImageContainer.TaskInfo task) {
113            switch (task.destination) {
114                case FAST_THUMBNAIL:
115                    // Signal start of processing
116                    break;
117                case INTERMEDIATE_THUMBNAIL:
118                    // Do nothing
119                    break;
120            }
121        }
122
123        @Override
124        public void onResultCompressed(TaskImageContainer.TaskInfo task,
125                TaskImageContainer.CompressedPayload payload) {
126            if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
127                mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data);
128            }
129        }
130
131        @Override
132        public void onResultUncompressed(TaskImageContainer.TaskInfo task,
133                TaskImageContainer.UncompressedPayload payload) {
134            // Load bitmap into CameraAppUI
135            switch (task.destination) {
136                case FAST_THUMBNAIL:
137                    final Bitmap bitmap = Bitmap.createBitmap(payload.data,
138                            task.result.width,
139                            task.result.height, Bitmap.Config.ARGB_8888);
140                    mPictureSaverCallback.onThumbnailAvailable(bitmap, mImageRotation.getDegrees());
141                    break;
142                case INTERMEDIATE_THUMBNAIL:
143                    final Bitmap bitmapIntermediate = Bitmap.createBitmap(payload.data,
144                            task.result.width,
145                            task.result.height, Bitmap.Config.ARGB_8888);
146                    Matrix matrix = new Matrix();
147                    matrix.postRotate(mImageRotation.getDegrees());
148                    final Bitmap bitmapIntermediateRotated = Bitmap.createBitmap(
149                            bitmapIntermediate, 0, 0, bitmapIntermediate.getWidth(),
150                            bitmapIntermediate.getHeight(), matrix, true);
151                    mExecutor.execute(new Runnable() {
152                        @Override
153                        public void run() {
154                            // TODO: Finalize and I18N string.
155                            mSession.updateThumbnail(bitmapIntermediateRotated);
156                            mSession.setProgressMessage("Saving image ...");
157                        }
158                    });
159                    break;
160            }
161        }
162
163        @Override
164        public void onResultUri(TaskImageContainer.TaskInfo task, Uri uri) {
165            // Remove yourself from the listener after JPEG save.
166            // TODO: This should really be done by the ImageBackend to guarantee
167            // ordering, since technically this could happen out of order.
168            mListenerProxy.unregisterListener(this);
169        }
170    }
171
172    private final MainThread mExecutor;
173    private final ImageRotationCalculator mImageRotationCalculator;
174    private final ImageBackend mImageBackend;
175
176    /**
177     * Constructor
178     *
179     * @param executor Executor to run listener events on the ImageBackend
180     * @param imageRotationCalculator the image rotation calculator to determine
181     */
182    public YuvImageBackendImageSaver(MainThread executor,
183            ImageRotationCalculator imageRotationCalculator,
184            ImageBackend imageBackend) {
185        mExecutor = executor;
186        mImageRotationCalculator = imageRotationCalculator;
187        mImageBackend = imageBackend;
188    }
189
190    /**
191     * Builder for the Zsl/ImageBackend Interface
192     *
193     * @return Instantiated interface object
194     */
195    @Override
196    public ImageSaver build(
197            @Nonnull OneCamera.PictureSaverCallback pictureSaverCallback,
198            @Nonnull OrientationManager.DeviceOrientation orientation,
199            @Nonnull CaptureSession session) {
200        final OrientationManager.DeviceOrientation imageRotation = mImageRotationCalculator
201                .toImageRotation();
202
203        ImageProcessorProxyListener proxyListener = mImageBackend.getProxyListener();
204
205        PreviewListener previewListener = new PreviewListener(mExecutor,
206                proxyListener, session, imageRotation, pictureSaverCallback);
207        return new MostRecentImageSaver(new ImageSaverImpl(mExecutor, session,
208                imageRotation, mImageBackend, previewListener));
209    }
210}
211