Committed C# port from Mohamad
[zxing.git] / csharp / common / BaseMonochromeBitmapSource.cs
1 /*\r
2 * Licensed under the Apache License, Version 2.0 (the "License");\r
3 * you may not use this file except in compliance with the License.\r
4 * You may obtain a copy of the License at\r
5 *\r
6 *      http://www.apache.org/licenses/LICENSE-2.0\r
7 *\r
8 * Unless required by applicable law or agreed to in writing, software\r
9 * distributed under the License is distributed on an "AS IS" BASIS,\r
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
11 * See the License for the specific language governing permissions and\r
12 * limitations under the License.\r
13 */\r
14 \r
15 using System;\r
16 using BitArray = com.google.zxing.common.BitArray;\r
17 using com.google.zxing.common;\r
18 namespace com.google.zxing\r
19 {\r
20      /**\r
21      * @author dswitkin@google.com (Daniel Switkin)\r
22      */\r
23     public abstract class BaseMonochromeBitmapSource: MonochromeBitmapSource \r
24     {\r
25       private static int LUMINANCE_BITS = 5;\r
26       private static int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;\r
27       private static int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;\r
28 \r
29       private int blackPoint;\r
30       private BlackPointEstimationMethod lastMethod;\r
31       private int lastArgument;\r
32       private int[] luminances;\r
33 \r
34       protected BaseMonochromeBitmapSource() {\r
35         blackPoint = 0x7F;\r
36         lastMethod = null;\r
37         lastArgument = 0;\r
38       }\r
39 \r
40       private void initLuminances() {\r
41         if (luminances == null) {\r
42           int width = getWidth();\r
43           int height = getHeight();\r
44           int max = width > height ? width : height;\r
45           luminances = new int[max];\r
46         }\r
47       }\r
48 \r
49       public bool isBlack(int x, int y) {\r
50         return getLuminance(x, y) < blackPoint;\r
51       }\r
52 \r
53       public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {\r
54         if (row == null || row.getSize() < getWidth) {\r
55           row = new BitArray(getWidth);\r
56         } else {\r
57           row.clear();\r
58         }\r
59 \r
60         // Reuse the same int array each time\r
61         initLuminances();\r
62         luminances = getLuminanceRow(y, luminances);\r
63 \r
64         // If the current decoder calculated the blackPoint based on one row, assume we're trying to\r
65         // decode a 1D barcode, and apply some sharpening.\r
66         if (lastMethod.Equals(BlackPointEstimationMethod.ROW_SAMPLING)) {\r
67           int left = luminances[startX];\r
68           int center = luminances[startX + 1];\r
69           for (int x = 1; x < getWidth - 1; x++) {\r
70             int right = luminances[startX + x + 1];\r
71             // Simple -1 4 -1 box filter with a weight of 2\r
72             int luminance = ((center << 2) - left - right) >> 1;\r
73             if (luminance < blackPoint) {\r
74               row.set(x);\r
75             }\r
76             left = center;\r
77             center = right;\r
78           }\r
79         } else {\r
80           for (int x = 0; x < getWidth; x++) {\r
81             if (luminances[startX + x] < blackPoint) {\r
82               row.set(x);\r
83             }\r
84           }\r
85         }\r
86         return row;\r
87       }\r
88 \r
89       public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) {\r
90         if (column == null || column.getSize() < getHeight) {\r
91           column = new BitArray(getHeight);\r
92         } else {\r
93           column.clear();\r
94         }\r
95 \r
96         // Reuse the same int array each time\r
97         initLuminances();\r
98         luminances = getLuminanceColumn(x, luminances);\r
99 \r
100         // We don't handle "row sampling" specially here\r
101         for (int y = 0; y < getHeight; y++) {\r
102           if (luminances[startY + y] < blackPoint) {\r
103             column.set(y);\r
104           }\r
105         }\r
106         return column;\r
107       }\r
108 \r
109       public void estimateBlackPoint(BlackPointEstimationMethod method, int argument){\r
110         if (!method.Equals(lastMethod) || argument != lastArgument) {\r
111           int width = getWidth();\r
112           int height = getHeight();\r
113           int[] histogram = new int[LUMINANCE_BUCKETS];\r
114           if (method.Equals(BlackPointEstimationMethod.TWO_D_SAMPLING)) {\r
115             int minDimension = width < height ? width : height;\r
116             int startX = (width - minDimension) >> 1;\r
117             int startY = (height - minDimension) >> 1;\r
118             for (int n = 0; n < minDimension; n++) {\r
119               int luminance = getLuminance(startX + n, startY + n);\r
120               histogram[luminance >> LUMINANCE_SHIFT]++;\r
121             }\r
122           } else if (method.Equals(BlackPointEstimationMethod.ROW_SAMPLING)) {\r
123             if (argument < 0 || argument >= height) {\r
124               throw new Exception("Row is not within the image: " + argument);\r
125             }\r
126             initLuminances();\r
127             luminances = getLuminanceRow(argument, luminances);\r
128             for (int x = 0; x < width; x++) {\r
129               histogram[luminances[x] >> LUMINANCE_SHIFT]++;\r
130             }\r
131           } else {\r
132               throw new Exception("Unknown method: " + method);\r
133           }\r
134           blackPoint = BlackPointEstimator.estimate(histogram) << LUMINANCE_SHIFT;\r
135           lastMethod = method;\r
136           lastArgument = argument;\r
137         }\r
138       }\r
139 \r
140       public BlackPointEstimationMethod getLastEstimationMethod() {\r
141         return lastMethod;\r
142       }\r
143 \r
144       public MonochromeBitmapSource rotateCounterClockwise() {\r
145         throw new Exception("Rotate not supported");\r
146       }\r
147 \r
148       public bool isRotateSupported() {\r
149         return false;\r
150       }\r
151 \r
152       // These two methods should not need to exist because they are defined in the interface that\r
153       // this abstract class implements. However this seems to cause problems on some Nokias. \r
154       // So we write these redundant declarations.\r
155 \r
156       public abstract int getHeight();\r
157 \r
158       public abstract int getWidth();\r
159 \r
160       /**\r
161        * Retrieves the luminance at the pixel x,y in the bitmap. This method is only used for estimating\r
162        * the black point and implementing getBlackRow() - it is not meant for decoding, hence it is not\r
163        * part of MonochromeBitmapSource itself, and is protected.\r
164        *\r
165        * @param x The x coordinate in the image.\r
166        * @param y The y coordinate in the image.\r
167        * @return The luminance value between 0 and 255.\r
168        */\r
169       protected abstract int getLuminance(int x, int y);\r
170 \r
171       /**\r
172        * This is the main mechanism for retrieving luminance data. It is dramatically more efficient\r
173        * than repeatedly calling getLuminance(). As above, this is not meant for decoders.\r
174        *\r
175        * @param y The row to fetch\r
176        * @param row The array to write luminance values into. It is <b>strongly</b> suggested that you\r
177        *            allocate this yourself, making sure row.length >= getWidth(), and reuse the same\r
178        *            array on subsequent calls for performance. If you pass null, you will be flogged,\r
179        *            but then I will take pity on you and allocate a sufficient array internally.\r
180        * @return The array containing the luminance data. This is the same as row if it was usable.\r
181        */\r
182       protected abstract int[] getLuminanceRow(int y, int[] row);\r
183 \r
184       /**\r
185        * The same as getLuminanceRow(), but for columns.\r
186        *\r
187        * @param x The column to fetch\r
188        * @param column The array to write luminance values into. See above.\r
189        * @return The array containing the luminance data.\r
190        */\r
191       protected abstract int[] getLuminanceColumn(int x, int[] column);\r
192 \r
193     }\r
194 }\r
195 \r
196 \r