diff --git a/core/src/com/google/zxing/EncodeHintType.java b/core/src/com/google/zxing/EncodeHintType.java new file mode 100644 index 000000000..8074c2ff7 --- /dev/null +++ b/core/src/com/google/zxing/EncodeHintType.java @@ -0,0 +1,34 @@ +/* + * Copyright 2008 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; + +/** + * These are a set of hints that you may pass to Writers to specify their behavior. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +public final class EncodeHintType { + + /** + * Specifies what degree of error correction to use, for example in QR Codes (type Integer). + */ + public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType(); + + private EncodeHintType() { + } + +} diff --git a/core/src/com/google/zxing/qrcode/QRCodeWriter.java b/core/src/com/google/zxing/qrcode/QRCodeWriter.java index 15211979d..2446b30c7 100644 --- a/core/src/com/google/zxing/qrcode/QRCodeWriter.java +++ b/core/src/com/google/zxing/qrcode/QRCodeWriter.java @@ -17,10 +17,11 @@ package com.google.zxing.qrcode; import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; import com.google.zxing.Writer; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.ByteArray; +import com.google.zxing.common.ByteMatrix; import com.google.zxing.qrcode.encoder.Encoder; import com.google.zxing.qrcode.encoder.QRCode; @@ -57,8 +58,14 @@ public final class QRCodeWriter implements Writer { height); } - // TODO: Check hints for error correction level instead of hardcoding int errorCorrectionLevel = QRCode.EC_LEVEL_L; + if (hints != null) { + Integer requestedECLevel = (Integer) hints.get(EncodeHintType.ERROR_CORRECTION); + if (requestedECLevel != null) { + errorCorrectionLevel = requestedECLevel.intValue(); + } + } + QRCode code = new QRCode(); Encoder.Encode(new ByteArray(contents), errorCorrectionLevel, code); return renderResult(code, width, height); diff --git a/core/test/data/qrcode_encode/renderer-test-01.png b/core/test/data/golden/qrcode/renderer-test-01.png similarity index 100% rename from core/test/data/qrcode_encode/renderer-test-01.png rename to core/test/data/golden/qrcode/renderer-test-01.png diff --git a/core/test/data/qrcode_encode/renderer-test-02.png b/core/test/data/golden/qrcode/renderer-test-02.png similarity index 100% rename from core/test/data/qrcode_encode/renderer-test-02.png rename to core/test/data/golden/qrcode/renderer-test-02.png diff --git a/core/test/data/qrcode_encode/renderer-test-03.png b/core/test/data/golden/qrcode/renderer-test-03.png similarity index 100% rename from core/test/data/qrcode_encode/renderer-test-03.png rename to core/test/data/golden/qrcode/renderer-test-03.png diff --git a/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java b/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java new file mode 100644 index 000000000..eec335713 --- /dev/null +++ b/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java @@ -0,0 +1,139 @@ +/* + * Copyright 2008 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.qrcode; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.ByteMatrix; +import com.google.zxing.qrcode.encoder.QRCode; +import junit.framework.TestCase; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Hashtable; + +/** + * @author satorux@google.com (Satoru Takabayashi) - creator + * @author dswitkin@google.com (Daniel Switkin) - ported and expanded from C++ + */ +public final class QRCodeWriterTestCase extends TestCase { + + private static final String BASE_IMAGE_PATH = "test/data/golden/qrcode/"; + + private static BufferedImage loadImage(String fileName) { + try { + File file = new File(BASE_IMAGE_PATH + fileName); + assertTrue("Please run from the 'core' directory", file.exists()); + return ImageIO.read(file); + } catch (IOException e) { + return null; + } + } + + // In case the golden images are not monochromatic, convert the RGB values to greyscale. + private static ByteMatrix createMatrixFromImage(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + int[] pixels = new int[width * height]; + image.getRGB(0, 0, width, height, pixels, 0, width); + + ByteMatrix matrix = new ByteMatrix(height, width); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixels[y * width + x]; + int luminance = (306 * ((pixel >> 16) & 0xFF) + + 601 * ((pixel >> 8) & 0xFF) + + 117 * (pixel & 0xFF)) >> 10; + matrix.set(y, x, luminance); + } + } + return matrix; + } + + public void testQRCodeWriter() throws WriterException { + // The QR should be multiplied up to fit, with extra padding if necessary + final int bigEnough = 256; + QRCodeWriter writer = new QRCodeWriter(); + ByteMatrix matrix = writer.encode("http://www.google.com/", BarcodeFormat.QR_CODE, bigEnough, + bigEnough, null); + assertTrue(matrix != null); + assertEquals(bigEnough, matrix.width()); + assertEquals(bigEnough, matrix.height()); + + // The QR will not fit in this size, so the matrix should come back bigger + final int tooSmall = 20; + matrix = writer.encode("http://www.google.com/", BarcodeFormat.QR_CODE, tooSmall, + tooSmall, null); + assertTrue(matrix != null); + assertTrue(tooSmall < matrix.width()); + assertTrue(tooSmall < matrix.height()); + + // We should also be able to handle non-square requests by padding them + final int strangeWidth = 500; + final int strangeHeight = 100; + matrix = writer.encode("http://www.google.com/", BarcodeFormat.QR_CODE, strangeWidth, + strangeHeight, null); + assertTrue(matrix != null); + assertEquals(strangeWidth, matrix.width()); + assertEquals(strangeHeight, matrix.height()); + } + + private static boolean compareToGoldenFile(final String contents, final int ecLevel, + final int resolution, final String fileName) throws WriterException { + + BufferedImage image = loadImage(fileName); + assertNotNull(image); + ByteMatrix goldenResult = createMatrixFromImage(image); + assertNotNull(goldenResult); + + QRCodeWriter writer = new QRCodeWriter(); + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.ERROR_CORRECTION, new Integer(ecLevel)); + ByteMatrix generatedResult = writer.encode(contents, BarcodeFormat.QR_CODE, resolution, + resolution, hints); + + assertEquals("Width should be " + resolution + ", but was " + generatedResult.width(), + resolution, generatedResult.width()); + assertEquals("Height should be " + resolution + ", but was " + generatedResult.height(), + resolution, generatedResult.height()); + assertTrue("Expected " + goldenResult.toString() + " but got " + generatedResult.toString(), + Arrays.deepEquals(goldenResult.getArray(), generatedResult.getArray())); + return true; + } + + // Golden images are generated with "qrcode_sample.cc". The images are checked with both eye balls + // and cell phones. We expect pixel-perfect results, because the error correction level is known, + // and the pixel dimensions matches exactly. + public void testRegressionTest() throws WriterException { + assertTrue(compareToGoldenFile("http://www.google.com/", QRCode.EC_LEVEL_M, 99, + "renderer-test-01.png")); + + assertTrue(compareToGoldenFile("12345", QRCode.EC_LEVEL_L, 58, "renderer-test-02.png")); + + // Test in Katakana in Shift_JIS. + final byte[] KATAKANA_INPUT = { + (byte)0x83, 0x65, (byte)0x83, 0x58, (byte)0x83, 0x67 + }; + assertTrue(compareToGoldenFile(new String(KATAKANA_INPUT), QRCode.EC_LEVEL_H, 145, + "renderer-test-03.png")); + } + +}