More minor javadoc, code tweaks
[zxing.git] / android / src / com / google / zxing / client / android / CameraSurfaceView.java
1 /*
2  * Copyright (C) 2008 Google Inc.
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.google.zxing.client.android;
18
19 import com.google.zxing.ResultPoint;
20
21 import android.content.Context;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.Paint;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.view.SurfaceHolder;
28 import android.view.SurfaceView;
29
30 /**
31  * @author dswitkin@google.com (Daniel Switkin)
32  */
33 final class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
34
35   private static int[] SCANNER_ALPHA = { 0, 64, 128, 192, 255, 192, 128, 64 };
36   
37   private CameraManager cameraManager;
38   private SurfaceHolder surfaceHolder;
39   private boolean hasSurface;
40   private int scannerAlpha;
41   
42         CameraSurfaceView(Context context, CameraManager cameraManager) {
43                 super(context);
44                 this.cameraManager = cameraManager;
45
46                 // Install a SurfaceHolder.Callback so we get notified when the underlying surface is created
47                 // and destroyed.
48                 surfaceHolder = getHolder();
49                 surfaceHolder.setCallback(this);
50                 hasSurface = false;
51                 scannerAlpha = 0;
52
53                 // FIXME(dswitkin): This resolution needs to be made dynamic to handle different devices and
54                 // orientations.
55                 surfaceHolder.setFixedSize(320, 240);
56         }
57
58         public boolean surfaceCreated(SurfaceHolder holder) {
59                 hasSurface = true;
60
61                 // Tell the system that we filled the surface in this call. This is a lie to prevent the system
62                 // from filling the surface for us automatically. THIS IS REQUIRED because otherwise we'll
63                 // access the Surface object from 2 different threads which is not allowed.
64                 return true;
65         }
66
67         public void surfaceDestroyed(SurfaceHolder holder) {
68                 // FIXME(dswitkin): The docs say this surface will be destroyed when this method returns. In
69           // practice this has not been a problem so far. I need to investigate.
70                 hasSurface = false;
71         }
72
73         public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
74                 // Surface size or format has changed. This won't happen because of the  setFixedSize() call.
75         }
76         
77         /**
78          * This method is only called from the WorkerThread. It's job is to grab the next preview frame
79          * from the camera, draw the framing rectangle, and blit everything to the screen.
80          */
81         public void capturePreviewAndDraw() {
82     if (hasSurface) {
83       Canvas canvas = surfaceHolder.lockCanvas();
84       cameraManager.capturePreview(canvas);
85       Rect frame = cameraManager.getFramingRect();
86       int width = canvas.getBitmapWidth();
87       int height = canvas.getBitmapHeight();
88       
89       // Draw the exterior (i.e. outside the framing rect) as half darkened
90       Paint paint = new Paint();
91       paint.setColor(Color.BLACK);
92       paint.setAlpha(96);
93       Rect box = new Rect(0, 0, width, frame.top);
94       canvas.drawRect(box, paint);
95       box.set(0, frame.top, frame.left, frame.bottom + 1);
96       canvas.drawRect(box, paint);
97       box.set(frame.right + 1, frame.top, width, frame.bottom + 1);
98       canvas.drawRect(box, paint);
99       box.set(0, frame.bottom + 1, width, height);
100       canvas.drawRect(box, paint);
101       
102       // Draw a two pixel solid black border inside the framing rect
103       paint.setAlpha(255);
104       box.set(frame.left, frame.top, frame.right + 1, frame.top + 2);
105       canvas.drawRect(box, paint);
106       box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1);
107       canvas.drawRect(box, paint);
108       box.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1);
109       canvas.drawRect(box, paint);
110       box.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1);
111       canvas.drawRect(box, paint);
112       
113       // Draw a red "laser scanner" line through the middle
114       paint.setColor(Color.RED);
115       paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
116       int middle = frame.height() / 2 + frame.top;
117       box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2);
118       canvas.drawRect(box, paint);
119       
120       surfaceHolder.unlockCanvasAndPost(canvas);
121       
122       // This cheap animation is tied to the rate at which we pull previews from the camera.
123       scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
124     }
125         }
126         
127         /**
128          * Draw a line for 1D barcodes (which return two points) or otherwise a set of points returned
129          * from the decoder to indicate what we found.
130          * TODO(dswitkin): It might be nice to clear the framing rect and zoom in on the actual still that
131          * was captured, then paint the green points on it. This would also clear the red scanner line
132          * which doesn't make sense after the capture.
133          * 
134          * @param resultPoints An array of points from the decoder, whose coordinates are expressed
135          *                     relative to the still image from the camera.
136          */
137         public void drawResultPoints(ResultPoint[] resultPoints) {
138     if (hasSurface) {
139       Canvas canvas = surfaceHolder.lockCanvas();
140       Paint paint = new Paint();
141       paint.setColor(Color.GREEN);
142       paint.setAlpha(128);
143       
144       Point[] points = cameraManager.convertResultPoints(resultPoints);
145       if (points.length == 2) {
146         paint.setStrokeWidth(4);
147         canvas.drawLine(points[0].x, points[0].y, points[1].x, points[1].y, paint);
148       } else {
149         paint.setStrokeWidth(10);
150         for (int x = 0; x < points.length; x++) {
151           canvas.drawPoint(points[x].x, points[x].y, paint);
152         }
153       }
154       
155       surfaceHolder.unlockCanvasAndPost(canvas);
156     }
157         }
158         
159 }