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