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 17package com.android.camera.one.v2.camera2proxy; 18 19import android.graphics.Rect; 20import android.media.Image; 21 22import com.google.common.base.Objects; 23import com.google.common.collect.ImmutableList; 24 25import java.nio.ByteBuffer; 26import java.util.ArrayList; 27import java.util.List; 28 29import javax.annotation.concurrent.GuardedBy; 30import javax.annotation.concurrent.ThreadSafe; 31 32/** 33 * An {@link ImageProxy} backed by an {@link android.media.Image}. 34 */ 35@ThreadSafe 36public class AndroidImageProxy implements ImageProxy { 37 38 /** 39 * An {@link ImageProxy.Plane} backed by an 40 * {@link android.media.Image.Plane}. 41 */ 42 public class Plane implements ImageProxy.Plane { 43 private final int mPixelStride; 44 private final int mRowStride; 45 private final ByteBuffer mBuffer; 46 47 public Plane(Image.Plane imagePlane) { 48 // Copying out the contents of the Image.Plane means that this Plane 49 // implementation can be thread-safe (without requiring any locking) 50 // and can have getters which do not throw a RuntimeException if 51 // the underlying Image is closed. 52 mPixelStride = imagePlane.getPixelStride(); 53 mRowStride = imagePlane.getRowStride(); 54 mBuffer = imagePlane.getBuffer(); 55 } 56 57 /** 58 * @see {@link android.media.Image.Plane#getRowStride} 59 */ 60 @Override 61 public int getRowStride() { 62 return mRowStride; 63 } 64 65 /** 66 * @see {@link android.media.Image.Plane#getPixelStride} 67 */ 68 @Override 69 public int getPixelStride() { 70 return mPixelStride; 71 } 72 73 /** 74 * @see {@link android.media.Image.Plane#getBuffer} 75 */ 76 @Override 77 public ByteBuffer getBuffer() { 78 return mBuffer; 79 } 80 } 81 82 private final Object mLock; 83 /** 84 * {@link android.media.Image} is not thread-safe, so all interactions must 85 * be guarded by {@link #mLock}. 86 */ 87 @GuardedBy("mLock") 88 private final android.media.Image mImage; 89 private final int mFormat; 90 private final int mWidth; 91 private final int mHeight; 92 private final long mTimestamp; 93 private final ImmutableList<ImageProxy.Plane> mPlanes; 94 @GuardedBy("mLock") 95 private Rect mCropRect; 96 97 public AndroidImageProxy(android.media.Image image) { 98 mLock = new Object(); 99 100 mImage = image; 101 // Copying out the contents of the Image means that this Image 102 // implementation can be thread-safe (without requiring any locking) 103 // and can have getters which do not throw a RuntimeException if 104 // the underlying Image is closed. 105 mFormat = mImage.getFormat(); 106 mWidth = mImage.getWidth(); 107 mHeight = mImage.getHeight(); 108 mTimestamp = mImage.getTimestamp(); 109 110 Image.Plane[] planes; 111 planes = mImage.getPlanes(); 112 if (planes == null) { 113 mPlanes = ImmutableList.of(); 114 } else { 115 List<ImageProxy.Plane> wrappedPlanes = new ArrayList<>(planes.length); 116 for (int i = 0; i < planes.length; i++) { 117 wrappedPlanes.add(new Plane(planes[i])); 118 } 119 mPlanes = ImmutableList.copyOf(wrappedPlanes); 120 } 121 } 122 123 /** 124 * @see {@link android.media.Image#getCropRect} 125 */ 126 @Override 127 public Rect getCropRect() { 128 synchronized (mLock) { 129 try { 130 mCropRect = mImage.getCropRect(); 131 } catch (IllegalStateException imageClosedException) { 132 // If the image is closed, then just return the cached CropRect. 133 return mCropRect; 134 } 135 return mCropRect; 136 } 137 } 138 139 /** 140 * @see {@link android.media.Image#setCropRect} 141 */ 142 @Override 143 public void setCropRect(Rect cropRect) { 144 synchronized (mLock) { 145 mCropRect = cropRect; 146 try { 147 mImage.setCropRect(cropRect); 148 } catch (IllegalStateException imageClosedException) { 149 // Ignore. 150 } 151 } 152 } 153 154 /** 155 * @see {@link android.media.Image#getFormat} 156 */ 157 @Override 158 public int getFormat() { 159 return mFormat; 160 } 161 162 /** 163 * @see {@link android.media.Image#getHeight} 164 */ 165 @Override 166 public int getHeight() { 167 return mHeight; 168 } 169 170 /** 171 * @see {@link android.media.Image#getPlanes} 172 */ 173 @Override 174 public List<ImageProxy.Plane> getPlanes() { 175 return mPlanes; 176 } 177 178 /** 179 * @see {@link android.media.Image#getTimestamp} 180 */ 181 @Override 182 public long getTimestamp() { 183 return mTimestamp; 184 } 185 186 /** 187 * @see {@link android.media.Image#getWidth} 188 */ 189 @Override 190 public int getWidth() { 191 return mWidth; 192 } 193 194 /** 195 * @see {@link android.media.Image#close} 196 */ 197 @Override 198 public void close() { 199 synchronized (mLock) { 200 mImage.close(); 201 } 202 } 203 204 @Override 205 public String toString() { 206 return Objects.toStringHelper(this) 207 .add("format", getFormat()) 208 .add("timestamp", getTimestamp()) 209 .add("width", getWidth()) 210 .add("height", getHeight()) 211 .toString(); 212 } 213 214 @Override 215 public boolean equals(Object other) { 216 if (other == null) { 217 return false; 218 } 219 if (!(other instanceof ImageProxy)) { 220 return false; 221 } 222 ImageProxy otherImage = (ImageProxy) other; 223 return otherImage.getFormat() == getFormat() && 224 otherImage.getWidth() == getWidth() && 225 otherImage.getHeight() == getHeight() && 226 otherImage.getTimestamp() == getTimestamp(); 227 } 228 229 @Override 230 public int hashCode() { 231 return Objects.hashCode(getFormat(), getWidth(), getHeight(), getTimestamp()); 232 } 233} 234