/* * 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; using System.Drawing.Imaging; using System.Drawing; using System.Runtime.InteropServices; namespace ZXing { public partial class BitmapLuminanceSource : BaseLuminanceSource { /// /// Initializes a new instance of the class. /// /// The width. /// The height. protected BitmapLuminanceSource(int width, int height) : base(width, height) { } /// /// Initializes a new instance of the class /// with the image of a Bitmap instance /// /// The bitmap. public BitmapLuminanceSource(Bitmap bitmap) : base(bitmap.Width, bitmap.Height) { var height = bitmap.Height; var width = bitmap.Width; // In order to measure pure decoding speed, we convert the entire image to a greyscale array // The underlying raster of image consists of bytes with the luminance values #if WindowsCE var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); #else var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); #endif try { var stride = Math.Abs(data.Stride); var pixelWidth = stride/width; if (pixelWidth > 4) { // old slow way for unsupported bit depth Color c; for (int y = 0; y < height; y++) { int offset = y*width; for (int x = 0; x < width; x++) { c = bitmap.GetPixel(x, y); luminances[offset + x] = (byte)((RChannelWeight * c.R + GChannelWeight * c.G + BChannelWeight * c.B) >> ChannelWeight); } } } else { var strideStep = data.Stride; var buffer = new byte[stride]; var ptrInBitmap = data.Scan0; #if !WindowsCE // prepare palette for 1 and 8 bit indexed bitmaps var luminancePalette = new byte[bitmap.Palette.Entries.Length]; for (var index = 0; index < bitmap.Palette.Entries.Length; index++) { var color = bitmap.Palette.Entries[index]; luminancePalette[index] = (byte) ((RChannelWeight*color.R + GChannelWeight*color.G + BChannelWeight*color.B) >> ChannelWeight); } if (bitmap.PixelFormat == PixelFormat.Format32bppArgb || bitmap.PixelFormat == PixelFormat.Format32bppPArgb) { pixelWidth = 40; } if ((int)bitmap.PixelFormat == 8207 || (bitmap.Flags & (int)ImageFlags.ColorSpaceCmyk) == (int)ImageFlags.ColorSpaceCmyk) { pixelWidth = 41; } #endif for (int y = 0; y < height; y++) { // copy a scanline not the whole bitmap because of memory usage Marshal.Copy(ptrInBitmap, buffer, 0, stride); #if NET40 ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep); #else ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep); #endif var offset = y*width; switch (pixelWidth) { #if !WindowsCE case 0: for (int x = 0; x*8 < width; x++) { for (int subX = 0; subX < 8 && 8*x + subX < width; subX++) { var index = (buffer[x] >> (7 - subX)) & 1; luminances[offset + 8*x + subX] = luminancePalette[index]; } } break; case 1: for (int x = 0; x < width; x++) { luminances[offset + x] = luminancePalette[buffer[x]]; } break; #endif case 2: // should be RGB565 or RGB555, assume RGB565 { var maxIndex = 2*width; for (int index = 0; index < maxIndex; index += 2) { var byte1 = buffer[index]; var byte2 = buffer[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; luminances[offset] = (byte)((RChannelWeight * r8 + GChannelWeight * g8 + BChannelWeight * b8) >> ChannelWeight); offset++; } } break; case 3: { var maxIndex = width*3; for (int x = 0; x < maxIndex; x += 3) { var luminance = (byte) ((BChannelWeight*buffer[x] + GChannelWeight*buffer[x + 1] + RChannelWeight*buffer[x + 2]) >> ChannelWeight); luminances[offset] = luminance; offset++; } } break; case 4: // 4 bytes without alpha channel value { var maxIndex = 4*width; for (int x = 0; x < maxIndex; x += 4) { var luminance = (byte) ((BChannelWeight*buffer[x] + GChannelWeight*buffer[x + 1] + RChannelWeight*buffer[x + 2]) >> ChannelWeight); luminances[offset] = luminance; offset++; } } break; case 40: // with alpha channel; some barcodes are completely black if you // only look at the r, g and b channel but the alpha channel controls // the view { var maxIndex = 4*width; for (int x = 0; x < maxIndex; x += 4) { var luminance = (byte) ((BChannelWeight*buffer[x] + GChannelWeight*buffer[x + 1] + RChannelWeight*buffer[x + 2]) >> ChannelWeight); // calculating the resulting luminance based upon a white background // var alpha = buffer[x * pixelWidth + 3] / 255.0; // luminance = (byte)(luminance * alpha + 255 * (1 - alpha)); var alpha = buffer[x + 3]; luminance = (byte) (((luminance*alpha) >> 8) + (255*(255 - alpha) >> 8) + 1); luminances[offset] = luminance; offset++; } } break; case 41: // CMYK color space { var maxIndex = 4 * width; for (int x = 0; x < maxIndex; x += 4) { var luminance = (byte) (255 - ((BChannelWeight*buffer[x] + GChannelWeight*buffer[x + 1] + RChannelWeight*buffer[x + 2]) >> ChannelWeight)); // Ignore value of k at the moment luminances[offset] = luminance; offset++; } } break; default: throw new NotSupportedException(); } } } } finally { bitmap.UnlockBits(data); } } /// /// 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 BitmapLuminanceSource(width, height) { luminances = newLuminances }; } } }