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