/*
 * Copyright 2009 ZXing authors
 *
 * 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;

/**
 * This class is used to help decode images from files which arrive as RGB data from
 * an ARGB pixel array. It does not support rotation.
 *
 * @author dswitkin@google.com (Daniel Switkin)
 * @author Betaminos
 */
public final class RGBLuminanceSource extends LuminanceSource {

  private final byte[] luminances;
  private final int dataWidth;
  private final int dataHeight;
  private final int left;
  private final int top;

  public RGBLuminanceSource(int width, int height, int[] pixels) {
    super(width, height);

    dataWidth = width;
    dataHeight = height;
    left = 0;
    top = 0;

    // In order to measure pure decoding speed, we convert the entire image to a greyscale array
    // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
    luminances = new byte[width * height];
    for (int y = 0; y < height; y++) {
      int offset = y * width;
      for (int x = 0; x < width; x++) {
        int pixel = pixels[offset + x];
        int r = (pixel >> 16) & 0xff;
        int g = (pixel >> 8) & 0xff;
        int b = pixel & 0xff;
        if (r == g && g == b) {
          // Image is already greyscale, so pick any channel.
          luminances[offset + x] = (byte) r;
        } else {
          // Calculate luminance cheaply, favoring green.
          luminances[offset + x] = (byte) ((r + 2 * g + b) / 4);
        }
      }
    }
  }
  
  private RGBLuminanceSource(byte[] pixels,
                             int dataWidth,
                             int dataHeight,
                             int left,
                             int top,
                             int width,
                             int height) {
    super(width, height);
    if (left + width > dataWidth || top + height > dataHeight) {
      throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
    }
    this.luminances = pixels;
    this.dataWidth = dataWidth;
    this.dataHeight = dataHeight;
    this.left = left;
    this.top = top;
  }

  @Override
  public byte[] getRow(int y, byte[] row) {
    if (y < 0 || y >= getHeight()) {
      throw new IllegalArgumentException("Requested row is outside the image: " + y);
    }
    int width = getWidth();
    if (row == null || row.length < width) {
      row = new byte[width];
    }
    int offset = (y + top) * dataWidth + left;
    System.arraycopy(luminances, offset, row, 0, width);
    return row;
  }

  @Override
  public byte[] getMatrix() {
    int width = getWidth();
    int height = getHeight();

    // If the caller asks for the entire underlying image, save the copy and give them the
    // original data. The docs specifically warn that result.length must be ignored.
    if (width == dataWidth && height == dataHeight) {
      return luminances;
    }

    int area = width * height;
    byte[] matrix = new byte[area];
    int inputOffset = top * dataWidth + left;

    // If the width matches the full width of the underlying data, perform a single copy.
    if (width == dataWidth) {
      System.arraycopy(luminances, inputOffset, matrix, 0, area);
      return matrix;
    }

    // Otherwise copy one cropped row at a time.
    byte[] rgb = luminances;
    for (int y = 0; y < height; y++) {
      int outputOffset = y * width;
      System.arraycopy(rgb, inputOffset, matrix, outputOffset, width);
      inputOffset += dataWidth;
    }
    return matrix;
  }
  
  @Override
  public boolean isCropSupported() {
    return true;
  }

  @Override
  public LuminanceSource crop(int left, int top, int width, int height) {
    return new RGBLuminanceSource(luminances,
                                  dataWidth,
                                  dataHeight,
                                  this.left + left,
                                  this.top + top,
                                  width,
                                  height);
  }

}

@@ -1423,158 +1423,184 @@
     
-luminances = new byte%5Bwidth * height%5D;%0A    for (int y = 0; y %3C height; y++) %7B%0A      int offset = y * width;%0A      for (int x = 0; x %3C width; x
+//%0A    // Total number of pixels suffices, can ignore shape%0A    int size = width * height;%0A    luminances = new byte%5Bsize%5D;%0A    for (int offset = 0; offset %3C size; offset
 ++) %7B%0A
-  
     
@@ -1630,17 +1630,11 @@
 fset
- + x
 %5D;%0A
-  
     
@@ -1656,35 +1656,40 @@
 l %3E%3E 16) & 0xff;
-%0A  
+ // red%0A
       int g = (p
@@ -1683,16 +1683,17 @@
    int g
+2
  = (pixe
@@ -1701,21 +1701,33 @@
  %3E%3E 
-8
+7
 ) & 0x
-ff;%0A  
+1fe; // 2 * green%0A
     
@@ -1753,227 +1753,68 @@
 xff;
-%0A        if (r == g && g == b) %7B%0A          // Image is already greyscale, so pick any channel.%0A          luminances%5Boffset + x%5D = (byte) r;%0A        %7D else %7B%0A          // Calculate luminance cheaply, 
+ // blue%0A      // Calculate green-
 favo
+u
 ring 
-green.%0A    
+average cheaply%0A
     
@@ -1836,12 +1836,8 @@
 fset
- + x
 %5D = 
@@ -1853,14 +1853,11 @@
 r + 
+g
 2 
-* g 
 + b)
@@ -1867,26 +1867,8 @@
 4);%0A
-        %7D%0A      %7D%0A
     

Simplify conversion RGB -> luminance