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