/*
* Copyright 2012 ZXing.Net 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.
*/
using System;
namespace ZXing
{
///
/// Luminance source class which support different formats of images.
///
public partial class RGBLuminanceSource : BaseLuminanceSource
{
///
/// enumeration of supported bitmap format which the RGBLuminanceSource can process
///
public enum BitmapFormat
{
///
/// format of the byte[] isn't known. RGBLuminanceSource tries to determine the best possible value
///
Unknown,
///
/// grayscale array, the byte array is a luminance array with 1 byte per pixel
///
Gray8,
///
/// 3 bytes per pixel with the channels red, green and blue
///
RGB24,
///
/// 4 bytes per pixel with the channels red, green and blue
///
RGB32,
///
/// 4 bytes per pixel with the channels alpha, red, green and blue
///
ARGB32,
///
/// 3 bytes per pixel with the channels blue, green and red
///
BGR24,
///
/// 4 bytes per pixel with the channels blue, green and red
///
BGR32,
///
/// 4 bytes per pixel with the channels blue, green, red and alpha
///
BGRA32,
///
/// 2 bytes per pixel, 5 bit red, 6 bits green and 5 bits blue
///
RGB565,
///
/// 4 bytes per pixel with the channels red, green, blue and alpha
///
RGBA32,
}
///
/// Initializes a new instance of the class.
///
/// The width.
/// The height.
protected RGBLuminanceSource(int width, int height)
: base(width, height)
{
}
///
/// Initializes a new instance of the class.
/// It supports a byte array with 3 bytes per pixel (RGB24).
///
/// The RGB raw bytes.
/// The width.
/// The height.
public RGBLuminanceSource(byte[] rgbRawBytes, int width, int height)
: this(rgbRawBytes, width, height, BitmapFormat.RGB24)
{
}
///
/// Initializes a new instance of the class.
/// It supports a byte array with 1 byte per pixel (Gray8).
/// That means the whole array consists of the luminance values (grayscale).
///
/// The luminance array.
/// The width.
/// The height.
/// if set to true [is8 bit].
[Obsolete("Use RGBLuminanceSource(luminanceArray, width, height, BitmapFormat.Gray8)")]
public RGBLuminanceSource(byte[] luminanceArray, int width, int height, bool is8Bit)
: this(luminanceArray, width, height, BitmapFormat.Gray8)
{
}
///
/// Initializes a new instance of the class.
/// It supports a byte array with 3 bytes per pixel (RGB24).
///
/// The RGB raw bytes.
/// The width.
/// The height.
/// The bitmap format.
public RGBLuminanceSource(byte[] rgbRawBytes, int width, int height, BitmapFormat bitmapFormat)
: base(width, height)
{
CalculateLuminance(rgbRawBytes, bitmapFormat);
}
///
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
///
/// The new luminances.
/// The width.
/// The height.
///
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
{
return new RGBLuminanceSource(width, height) { luminances = newLuminances };
}
private static BitmapFormat DetermineBitmapFormat(byte[] rgbRawBytes, int width, int height)
{
var square = width*height;
var byteperpixel = rgbRawBytes.Length/square;
switch (byteperpixel)
{
case 1:
return BitmapFormat.Gray8;
case 2:
return BitmapFormat.RGB565;
case 3:
return BitmapFormat.RGB24;
case 4:
return BitmapFormat.RGB32;
default:
throw new ArgumentException("The bitmap format could not be determined. Please specify the correct value.");
}
}
protected void CalculateLuminance(byte[] rgbRawBytes, BitmapFormat bitmapFormat)
{
if (bitmapFormat == BitmapFormat.Unknown)
{
bitmapFormat = DetermineBitmapFormat(rgbRawBytes, Width, Height);
}
switch (bitmapFormat)
{
case BitmapFormat.Gray8:
Buffer.BlockCopy(rgbRawBytes, 0, luminances, 0, rgbRawBytes.Length < luminances.Length ? rgbRawBytes.Length : luminances.Length);
break;
case BitmapFormat.RGB24:
CalculateLuminanceRGB24(rgbRawBytes);
break;
case BitmapFormat.BGR24:
CalculateLuminanceBGR24(rgbRawBytes);
break;
case BitmapFormat.RGB32:
CalculateLuminanceRGB32(rgbRawBytes);
break;
case BitmapFormat.BGR32:
CalculateLuminanceBGR32(rgbRawBytes);
break;
case BitmapFormat.RGBA32:
CalculateLuminanceRGBA32(rgbRawBytes);
break;
case BitmapFormat.ARGB32:
CalculateLuminanceARGB32(rgbRawBytes);
break;
case BitmapFormat.BGRA32:
CalculateLuminanceBGRA32(rgbRawBytes);
break;
case BitmapFormat.RGB565:
CalculateLuminanceRGB565(rgbRawBytes);
break;
default:
throw new ArgumentException("The bitmap format isn't supported.", bitmapFormat.ToString());
}
}
private void CalculateLuminanceRGB565(byte[] rgb565RawData)
{
var luminanceIndex = 0;
for (var index = 0; index < rgb565RawData.Length && luminanceIndex < luminances.Length; index += 2, luminanceIndex++)
{
var byte1 = rgb565RawData[index];
var byte2 = rgb565RawData[index + 1];
var b5 = byte1 & 0x1F;
var g5 = (((byte1 & 0xE0) >> 5) | ((byte2 & 0x03) << 3)) & 0x1F;
var r5 = (byte2 >> 2) & 0x1F;
var r8 = (r5 * 527 + 23) >> 6;
var g8 = (g5 * 527 + 23) >> 6;
var b8 = (b5 * 527 + 23) >> 6;
// cheap, not fully accurate conversion
//var pixel = (byte2 << 8) | byte1;
//b8 = (((pixel) & 0x001F) << 3);
//g8 = (((pixel) & 0x07E0) >> 2) & 0xFF;
//r8 = (((pixel) & 0xF800) >> 8);
luminances[luminanceIndex] = (byte)((RChannelWeight * r8 + GChannelWeight * g8 + BChannelWeight * b8) >> ChannelWeight);
}
}
private void CalculateLuminanceRGB24(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int r = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int b = rgbRawBytes[rgbIndex++];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGR24(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int b = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int r = rgbRawBytes[rgbIndex++];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceRGB32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int r = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int b = rgbRawBytes[rgbIndex++];
rgbIndex++;
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGR32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int b = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int r = rgbRawBytes[rgbIndex++];
rgbIndex++;
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGRA32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var b = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var r = rgbRawBytes[rgbIndex++];
var alpha = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
private void CalculateLuminanceRGBA32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var r = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var b = rgbRawBytes[rgbIndex++];
var alpha = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
private void CalculateLuminanceARGB32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var alpha = rgbRawBytes[rgbIndex++];
var r = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var b = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
}
}