1 /* 2 * Copyright (C) 2013 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.cts.verifier.camera.fov; 18 19 import android.app.Activity; 20 import android.content.SharedPreferences; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.graphics.Canvas; 24 import android.graphics.Color; 25 import android.graphics.Matrix; 26 import android.graphics.Paint; 27 import android.graphics.RectF; 28 import android.media.ExifInterface; 29 import android.os.Bundle; 30 import android.preference.PreferenceManager; 31 import android.view.SurfaceHolder; 32 import android.view.SurfaceView; 33 import android.view.View; 34 import android.widget.Button; 35 import android.widget.SeekBar; 36 import android.widget.SeekBar.OnSeekBarChangeListener; 37 38 import com.android.cts.verifier.R; 39 40 import java.io.File; 41 import java.io.FileInputStream; 42 import java.io.IOException; 43 44 /** 45 * Shows the picture taken and lets the user specify the field of view (FOV). 46 */ 47 public class DetermineFovActivity extends Activity { 48 49 private static final float FOV_ADJUSTMENT_RANGE = 20; 50 private static final int SEEKBAR_MAX_VALUE = 100; 51 private static final float TEXT_SIZE = 16; 52 private static final float TEXT_PADDING = 0.2f; 53 private static final String DEFAULT_MARKER_DISTANCE = "36.8"; 54 private static final String DEFAULT_TARGET_DISTANCE = "99.7"; 55 56 private float mMarkerDistanceCm; 57 private SurfaceView mSurfaceView; 58 private SurfaceHolder mSurfaceHolder; 59 private Bitmap mPhotoBitmap; 60 private float mFovMinDegrees; 61 private float mFovMaxDegrees; 62 private float mFovDegrees; 63 private float mReportedFovDegrees; 64 private SeekBar mSeekBar; 65 private Button mDoneButton; 66 private float mTargetDistanceCm; 67 private String mMeasuredText; 68 private String mReportedText; 69 70 @Override onCreate(Bundle savedInstanceState)71 protected void onCreate(Bundle savedInstanceState) { 72 super.onCreate(savedInstanceState); 73 setContentView(R.layout.camera_fov_calibration_determine_fov); 74 File pictureFile = PhotoCaptureActivity.getPictureFile(this); 75 try { 76 mPhotoBitmap = 77 BitmapFactory.decodeStream(new FileInputStream(pictureFile)); 78 } catch (IOException e) { 79 e.printStackTrace(); 80 } 81 82 int previewOrientation; 83 try { 84 ExifInterface exif = new ExifInterface(pictureFile); 85 int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 86 ExifInterface.ORIENTATION_NORMAL); 87 switch (exifOrientation) { 88 case ExifInterface.ORIENTATION_ROTATE_90: previewOrientation = 90; break; 89 case ExifInterface.ORIENTATION_ROTATE_180: previewOrientation = 180; break; 90 case ExifInterface.ORIENTATION_ROTATE_270: previewOrientation = 270; break; 91 default: previewOrientation = 0; break; 92 } 93 } catch (IOException e) { 94 previewOrientation = 0; 95 } 96 97 if (previewOrientation != 0) { 98 Matrix transform = new Matrix(); 99 transform.setRotate(previewOrientation); 100 mPhotoBitmap = Bitmap.createBitmap(mPhotoBitmap, 0, 0, mPhotoBitmap.getWidth(), 101 mPhotoBitmap.getHeight(), transform, true); 102 } 103 104 mSurfaceView = (SurfaceView) findViewById(R.id.camera_fov_photo_surface); 105 mSurfaceHolder = mSurfaceView.getHolder(); 106 mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { 107 @Override 108 public void surfaceDestroyed(SurfaceHolder holder) {} 109 110 @Override 111 public void surfaceCreated(SurfaceHolder holder) { 112 drawContents(); 113 } 114 115 @Override 116 public void surfaceChanged( 117 SurfaceHolder holder, int format, int width, int height) { 118 drawContents(); 119 } 120 }); 121 122 mSeekBar = (SeekBar) findViewById(R.id.camera_fov_seekBar); 123 mSeekBar.setMax(SEEKBAR_MAX_VALUE); 124 mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 125 @Override 126 public void onStopTrackingTouch(SeekBar seekBar) {} 127 128 @Override 129 public void onStartTrackingTouch(SeekBar seekBar) {} 130 131 @Override 132 public void onProgressChanged( 133 SeekBar seekBar, int progress, boolean fromUser) { 134 mFovDegrees = seekBarProgressToFovDegrees(progress); 135 drawContents(); 136 } 137 }); 138 139 mDoneButton = (Button) findViewById(R.id.camera_fov_fov_done); 140 mDoneButton.setOnClickListener(new View.OnClickListener() { 141 @Override 142 public void onClick(View v) { 143 setResult(RESULT_OK); 144 CtsTestHelper.storeCtsTestResult(DetermineFovActivity.this, 145 mReportedFovDegrees, mFovDegrees); 146 finish(); 147 } 148 }); 149 } 150 fovToSeekBarProgress(float fovDegrees)151 private int fovToSeekBarProgress(float fovDegrees) { 152 return Math.round((fovDegrees - mFovMinDegrees) 153 / (mFovMaxDegrees - mFovMinDegrees) * SEEKBAR_MAX_VALUE); 154 } 155 seekBarProgressToFovDegrees(int progress)156 private float seekBarProgressToFovDegrees(int progress) { 157 float degrees = mFovMinDegrees + (float) progress / SEEKBAR_MAX_VALUE 158 * (mFovMaxDegrees - mFovMinDegrees); 159 // keep only 2 decimal places. 160 return (int) (degrees * 100) / 100.0f; 161 } 162 163 @Override onResume()164 protected void onResume() { 165 super.onResume(); 166 167 setResult(RESULT_CANCELED); 168 mMarkerDistanceCm = getMarkerDistance(); 169 mTargetDistanceCm = getTargetDistance(); 170 mReportedFovDegrees = PhotoCaptureActivity.getReportedFovDegrees(); 171 172 mFovDegrees = mReportedFovDegrees > 120 ? 60 : mReportedFovDegrees; 173 mFovMaxDegrees = mFovDegrees + FOV_ADJUSTMENT_RANGE / 2; 174 mFovMinDegrees = mFovDegrees - FOV_ADJUSTMENT_RANGE / 2; 175 176 mMeasuredText = getResources().getString(R.string.camera_fov_displayed_fov_label); 177 mReportedText = getResources().getString(R.string.camera_fov_reported_fov_label); 178 179 mSeekBar.setProgress(fovToSeekBarProgress(mFovDegrees)); 180 drawContents(); 181 } 182 getMarkerDistance()183 private float getMarkerDistance() { 184 // Get the marker distance from the preferences. 185 SharedPreferences prefs = 186 PreferenceManager.getDefaultSharedPreferences(this); 187 return Float.parseFloat(prefs.getString( 188 CalibrationPreferenceActivity.OPTION_MARKER_DISTANCE, 189 DEFAULT_MARKER_DISTANCE)); 190 } 191 getTargetDistance()192 private float getTargetDistance() { 193 // Get the marker distance from the preferences. 194 SharedPreferences prefs = 195 PreferenceManager.getDefaultSharedPreferences(this); 196 return Float.parseFloat(prefs.getString( 197 CalibrationPreferenceActivity.OPTION_TARGET_DISTANCE, 198 DEFAULT_TARGET_DISTANCE)); 199 } 200 focalLengthPixels(float fovDegrees, float imageWidth)201 private float focalLengthPixels(float fovDegrees, float imageWidth) { 202 return (float) (imageWidth 203 / (2 * Math.tan(fovDegrees / 2 * Math.PI / 180.0f))); 204 } 205 drawContents()206 private void drawContents() { 207 SurfaceHolder holder = mSurfaceView.getHolder(); 208 Canvas canvas = holder.lockCanvas(); 209 if (canvas == null || mPhotoBitmap == null) { 210 return; 211 } 212 213 int canvasWidth = canvas.getWidth(); 214 int canvasHeight = canvas.getHeight(); 215 int photoWidth = mPhotoBitmap.getWidth(); 216 int photoHeight = mPhotoBitmap.getHeight(); 217 RectF drawRect = new RectF(); 218 219 // Determine if the canvas aspect ratio is larger than that of the photo. 220 float scale = (float) canvasWidth / photoWidth; 221 int scaledHeight = (int) (scale * photoHeight); 222 if (scaledHeight < canvasHeight) { 223 // If the aspect ratio is smaller, set the destination rectangle to pad 224 // vertically. 225 int pad = (canvasHeight - scaledHeight) / 2; 226 drawRect.set(0, pad, canvasWidth, pad + scaledHeight - 1); 227 } else { 228 // Set the destination rectangle to pad horizontally. 229 scale = (float) canvasHeight / photoHeight; 230 float scaledWidth = scale * photoWidth; 231 float pad = (canvasWidth - scaledWidth) / 2; 232 drawRect.set(pad, 0, pad + scaledWidth - 1, canvasHeight); 233 } 234 235 // Draw the photo. 236 canvas.drawColor(Color.BLACK); 237 canvas.drawBitmap(mPhotoBitmap, null, drawRect, null); 238 239 // Draw the fov indicator text. 240 Paint paint = new Paint(); 241 paint.setColor(0xffffffff); 242 float textSize = TEXT_SIZE * DetermineFovActivity.this.getResources() 243 .getDisplayMetrics().scaledDensity; 244 paint.setTextSize(textSize); 245 canvas.drawText(mMeasuredText + " " + mFovDegrees + " degrees.", textSize, 246 2 * textSize * (1.0f + TEXT_PADDING), paint); 247 canvas.drawText(mReportedText + " " + mReportedFovDegrees + " degrees.", 248 textSize, textSize * (1.0f + TEXT_PADDING), paint); 249 250 // Draw the image center circle. 251 paint.setColor(Color.BLACK); 252 paint.setStyle(Paint.Style.STROKE); 253 paint.setStrokeWidth(3); 254 float dstWidth = drawRect.right - drawRect.left + 1; 255 float dstHeight = drawRect.bottom - drawRect.top + 1; 256 float centerX = drawRect.left + dstWidth / 2; 257 canvas.drawLine(centerX, drawRect.top, centerX, drawRect.bottom, paint); 258 259 // Project the markers into the scaled image with the given field of view. 260 float markerX = mMarkerDistanceCm / 2; 261 float markerZ = mTargetDistanceCm; 262 float focalLength = focalLengthPixels(mFovDegrees, dstWidth); 263 float dx = markerX / markerZ * focalLength; 264 float projectedMarkerLeft = dstWidth / 2 - dx; 265 float projectedMarkerRight = dstWidth / 2 + dx; 266 267 // Draw the marker lines over the image. 268 paint.setColor(Color.GREEN); 269 paint.setStrokeWidth(2); 270 float markerImageLeft = projectedMarkerLeft + drawRect.left; 271 canvas.drawLine( 272 markerImageLeft, drawRect.top, markerImageLeft, drawRect.bottom, paint); 273 float markerImageRight = projectedMarkerRight + drawRect.left; 274 canvas.drawLine(markerImageRight, drawRect.top, markerImageRight, 275 drawRect.bottom, paint); 276 277 holder.unlockCanvasAndPost(canvas); 278 } 279 } 280