summaryrefslogtreecommitdiff
path: root/src/com/google/zxing/client/android/CameraSurfaceView.java
blob: 06132cd6da2de6a0d1e994f2139e7502768ba954 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.zxing.client.android;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.google.zxing.ResultPoint;

/**
 * @author dswitkin@google.com (Daniel Switkin)
 */
final class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

  private static int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};

  private CameraManager cameraManager;
  private SurfaceHolder surfaceHolder;
  private boolean hasSurface;
  private int scannerAlpha;

  CameraSurfaceView(Context context, CameraManager cameraManager) {
    super(context);
    this.cameraManager = cameraManager;

    // Install a SurfaceHolder.Callback so we get notified when the underlying surface is created
    // and destroyed.
    surfaceHolder = getHolder();
    surfaceHolder.setCallback(this);
    hasSurface = false;
    scannerAlpha = 0;

    // FIXME(dswitkin): This resolution needs to be made dynamic to handle different devices and
    // orientations.
    surfaceHolder.setFixedSize(320, 240);
  }

  public boolean surfaceCreated(SurfaceHolder holder) {
    hasSurface = true;

    // Tell the system that we filled the surface in this call. This is a lie to prevent the system
    // from filling the surface for us automatically. THIS IS REQUIRED because otherwise we'll
    // access the Surface object from 2 different threads which is not allowed.
    return true;
  }

  public void surfaceDestroyed(SurfaceHolder holder) {
    // FIXME(dswitkin): The docs say this surface will be destroyed when this method returns. In
    // practice this has not been a problem so far. I need to investigate.
    hasSurface = false;
  }

  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Surface size or format has changed. This won't happen because of the  setFixedSize() call.
  }

  /**
   * This method is only called from the WorkerThread. It's job is to grab the next preview frame
   * from the camera, draw the framing rectangle, and blit everything to the screen.
   */
  public void capturePreviewAndDraw() {
    if (hasSurface) {
      Canvas canvas = surfaceHolder.lockCanvas();
      cameraManager.capturePreview(canvas);
      Rect frame = cameraManager.getFramingRect();
      int width = canvas.getBitmapWidth();
      int height = canvas.getBitmapHeight();

      // Draw the exterior (i.e. outside the framing rect) as half darkened
      Paint paint = new Paint();
      paint.setColor(Color.BLACK);
      paint.setAlpha(96);
      Rect box = new Rect(0, 0, width, frame.top);
      canvas.drawRect(box, paint);
      box.set(0, frame.top, frame.left, frame.bottom + 1);
      canvas.drawRect(box, paint);
      box.set(frame.right + 1, frame.top, width, frame.bottom + 1);
      canvas.drawRect(box, paint);
      box.set(0, frame.bottom + 1, width, height);
      canvas.drawRect(box, paint);

      // Draw a two pixel solid black border inside the framing rect
      paint.setAlpha(255);
      box.set(frame.left, frame.top, frame.right + 1, frame.top + 2);
      canvas.drawRect(box, paint);
      box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1);
      canvas.drawRect(box, paint);
      box.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1);
      canvas.drawRect(box, paint);
      box.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1);
      canvas.drawRect(box, paint);

      // Draw a red "laser scanner" line through the middle
      paint.setColor(Color.RED);
      paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
      int middle = frame.height() / 2 + frame.top;
      box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2);
      canvas.drawRect(box, paint);

      surfaceHolder.unlockCanvasAndPost(canvas);

      // This cheap animation is tied to the rate at which we pull previews from the camera.
      scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
    }
  }

  /**
   * Draw a line for 1D barcodes (which return two points) or otherwise a set of points returned
   * from the decoder to indicate what we found.
   * TODO(dswitkin): It might be nice to clear the framing rect and zoom in on the actual still that
   * was captured, then paint the green points on it. This would also clear the red scanner line
   * which doesn't make sense after the capture.
   *
   * @param resultPoints An array of points from the decoder, whose coordinates are expressed
   * relative to the still image from the camera.
   */
  public void drawResultPoints(ResultPoint[] resultPoints) {
    if (hasSurface) {
      Canvas canvas = surfaceHolder.lockCanvas();
      Paint paint = new Paint();
      paint.setColor(Color.GREEN);
      paint.setAlpha(128);

      Point[] points = cameraManager.convertResultPoints(resultPoints);
      if (points.length == 2) {
        paint.setStrokeWidth(4);
        canvas.drawLine(points[0].x, points[0].y, points[1].x, points[1].y, paint);
      } else {
        paint.setStrokeWidth(10);
        for (int x = 0; x < points.length; x++) {
          canvas.drawPoint(points[x].x, points[x].y, paint);
        }
      }

      surfaceHolder.unlockCanvasAndPost(canvas);
    }
	}
	
}