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