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