]> Pileus Git - ~andy/freeotp/blob - src/com/google/zxing/PlanarYUVLuminanceSource.java
Add native camera support
[~andy/freeotp] / src / com / google / zxing / PlanarYUVLuminanceSource.java
1 /*
2  * Copyright 2009 ZXing authors
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;
18
19 /**
20  * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
21  * with the option to crop to a rectangle within the full data. This can be used to exclude
22  * superfluous pixels around the perimeter and speed up decoding.
23  *
24  * It works for any pixel format where the Y channel is planar and appears first, including
25  * YCbCr_420_SP and YCbCr_422_SP.
26  *
27  * @author dswitkin@google.com (Daniel Switkin)
28  */
29 public final class PlanarYUVLuminanceSource extends LuminanceSource {
30
31   private static final int THUMBNAIL_SCALE_FACTOR = 2;
32   
33   private final byte[] yuvData;
34   private final int dataWidth;
35   private final int dataHeight;
36   private final int left;
37   private final int top;
38
39   public PlanarYUVLuminanceSource(byte[] yuvData,
40                                   int dataWidth,
41                                   int dataHeight,
42                                   int left,
43                                   int top,
44                                   int width,
45                                   int height,
46                                   boolean reverseHorizontal) {
47     super(width, height);
48
49     if (left + width > dataWidth || top + height > dataHeight) {
50       throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
51     }
52
53     this.yuvData = yuvData;
54     this.dataWidth = dataWidth;
55     this.dataHeight = dataHeight;
56     this.left = left;
57     this.top = top;
58     if (reverseHorizontal) {
59       reverseHorizontal(width, height);
60     }
61   }
62
63   @Override
64   public byte[] getRow(int y, byte[] row) {
65     if (y < 0 || y >= getHeight()) {
66       throw new IllegalArgumentException("Requested row is outside the image: " + y);
67     }
68     int width = getWidth();
69     if (row == null || row.length < width) {
70       row = new byte[width];
71     }
72     int offset = (y + top) * dataWidth + left;
73     System.arraycopy(yuvData, offset, row, 0, width);
74     return row;
75   }
76
77   @Override
78   public byte[] getMatrix() {
79     int width = getWidth();
80     int height = getHeight();
81
82     // If the caller asks for the entire underlying image, save the copy and give them the
83     // original data. The docs specifically warn that result.length must be ignored.
84     if (width == dataWidth && height == dataHeight) {
85       return yuvData;
86     }
87
88     int area = width * height;
89     byte[] matrix = new byte[area];
90     int inputOffset = top * dataWidth + left;
91
92     // If the width matches the full width of the underlying data, perform a single copy.
93     if (width == dataWidth) {
94       System.arraycopy(yuvData, inputOffset, matrix, 0, area);
95       return matrix;
96     }
97
98     // Otherwise copy one cropped row at a time.
99     byte[] yuv = yuvData;
100     for (int y = 0; y < height; y++) {
101       int outputOffset = y * width;
102       System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
103       inputOffset += dataWidth;
104     }
105     return matrix;
106   }
107
108   @Override
109   public boolean isCropSupported() {
110     return true;
111   }
112
113   @Override
114   public LuminanceSource crop(int left, int top, int width, int height) {
115     return new PlanarYUVLuminanceSource(yuvData,
116                                         dataWidth,
117                                         dataHeight,
118                                         this.left + left,
119                                         this.top + top,
120                                         width,
121                                         height,
122                                         false);
123   }
124
125   public int[] renderThumbnail() {
126     int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
127     int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
128     int[] pixels = new int[width * height];
129     byte[] yuv = yuvData;
130     int inputOffset = top * dataWidth + left;
131
132     for (int y = 0; y < height; y++) {
133       int outputOffset = y * width;
134       for (int x = 0; x < width; x++) {
135         int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
136         pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
137       }
138       inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
139     }
140     return pixels;
141   }
142   
143   /**
144    * @return width of image from {@link #renderThumbnail()}
145    */
146   public int getThumbnailWidth() {
147     return getWidth() / THUMBNAIL_SCALE_FACTOR;
148   }
149   
150   /**
151    * @return height of image from {@link #renderThumbnail()}
152    */  
153   public int getThumbnailHeight() {
154     return getHeight() / THUMBNAIL_SCALE_FACTOR;
155   }
156
157   private void reverseHorizontal(int width, int height) {
158     byte[] yuvData = this.yuvData;
159     for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
160       int middle = rowStart + width / 2;
161       for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
162         byte temp = yuvData[x1];
163         yuvData[x1] = yuvData[x2];
164         yuvData[x2] = temp;
165       }
166     }
167   }
168
169 }