* Imaging library, but which may not be available in other environments such as J2ME, and vice
* versa.
*
- * The implementation used can be controlled by calling {@link #setGridSamplerClassName(String)}
- * with the name of a class which implements this interface.
+ * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
+ * with an instance of a class which implements this interface.
*
* @author srowen@google.com (Sean Owen)
*/
public abstract class GridSampler {
- private static final String DEFAULT_IMPL_CLASS =
- "com.google.zxing.qrcode.detector.DefaultGridSampler";
-
- private static String gridSamplerClassName = DEFAULT_IMPL_CLASS;
private static GridSampler gridSampler;
- public static void setGridSamplerClassName(String className) {
- if (className == null) {
+ /**
+ * Sets the implementation of {@link GridSampler} used by the library. One global
+ * instance is stored, which may sound problematic. But, the implementation provided
+ * ought to be appropriate for the entire platform, and all uses of this library
+ * in the whole lifetime of the JVM. For instance, an Android activity can swap in
+ * an implementation that takes advantage of native platform libraries.
+ *
+ * @param newGridSampler
+ */
+ public static void setGridSampler(GridSampler newGridSampler) {
+ if (newGridSampler == null) {
throw new IllegalArgumentException();
}
- gridSamplerClassName = className;
+ gridSampler = newGridSampler;
}
+ /**
+ * @return the current implementation of {@link GridSampler}
+ */
public static GridSampler getInstance() {
if (gridSampler == null) {
- try {
- Class gridSamplerClass = Class.forName(gridSamplerClassName);
- gridSampler = (GridSampler) gridSamplerClass.newInstance();
- } catch (ClassNotFoundException cnfe) {
- throw new RuntimeException(cnfe.toString());
- } catch (IllegalAccessException iae) {
- throw new RuntimeException(iae.toString());
- } catch (InstantiationException ie) {
- throw new RuntimeException(ie.toString());
- }
+ gridSampler = new DefaultGridSampler();
}
return gridSampler;
}
+ /**
+ * <p>Given an image, locations of a QR Code's finder patterns and bottom-right alignment pattern,
+ * and the presumed dimension in modules of the QR Code, implemntations of this method extract
+ * the QR Code from the image by sampling the points in the image which should correspond to the
+ * modules of the QR Code.</p>
+ *
+ * @param image image to sample
+ * @param topLeft top-left finder pattern location
+ * @param topRight top-right finder pattern location
+ * @param bottomLeft bottom-left finder pattern location
+ * @param alignmentPattern bottom-right alignment pattern location
+ * @param dimension dimension of QR Code
+ * @return {@link BitMatrix} representing QR Code's modules
+ * @throws ReaderException if QR Code cannot be reasonably sampled -- for example if the location
+ * of the finder patterns imply a transformation that would require sampling off the image
+ */
protected abstract BitMatrix sampleGrid(MonochromeBitmapSource image,
FinderPattern topLeft,
FinderPattern topRight,
AlignmentPattern alignmentPattern,
int dimension) throws ReaderException;
- protected static void checkEndpoint(MonochromeBitmapSource image, float[] points)
- throws ReaderException {
- int x = (int) points[0];
- int y = (int) points[1];
- if (x < 0 || x >= image.getWidth() || y < 0 || y >= image.getHeight()) {
+ /**
+ * <p>Checks a set of points that have been transformed to sample points on an image against
+ * the image's dimensions to see if the endpoints are even within the image.
+ * This method actually only checks the endpoints since the points are assumed to lie
+ * on a line.</p>
+ *
+ * <p>This method will actually "nudge" the endpoints back onto the image if they are found to be barely
+ * (less than 1 pixel) off the image. This accounts for imperfect detection of finder patterns in an image
+ * where the QR Code runs all the way to the image border.</p>
+ *
+ * @param image image into which the points should map
+ * @param points actual points in x1,y1,...,xn,yn form
+ * @throws ReaderException if an endpoint is lies outside the image boundaries
+ */
+ protected static void checkEndpoint(MonochromeBitmapSource image, float[] points) throws ReaderException {
+ int width = image.getWidth();
+ int height = image.getHeight();
+ checkOneEndpoint(points, (int) points[0], (int) points[1], width, height);
+ checkOneEndpoint(points, (int) points[points.length - 2], (int) points[points.length - 1], width, height);
+ }
+
+ private static void checkOneEndpoint(float[] points, int x, int y, int width, int height) throws ReaderException {
+ if (x < -1 || x > width || y < -1 || y > height) {
throw new ReaderException("Transformed point out of bounds at " + x + ',' + y);
}
- x = (int) points[points.length - 2];
- y = (int) points[points.length - 1];
- if (x < 0 || x >= image.getWidth() || y < 0 || y >= image.getHeight()) {
- throw new ReaderException("Transformed point out of bounds at " + x + ',' + y);
+ if (x == -1) {
+ points[0] = 0.0f;
+ }
+ if (y == -1) {
+ points[1] = 0.0f;
+ }
+ if (x == width) {
+ points[0] = width - 1;
+ }
+ if (y == height) {
+ points[1] = height - 1;
}
}