diff --git a/core/src/com/google/zxing/BinaryBitmap.java b/core/src/com/google/zxing/BinaryBitmap.java index da5df5fbe..c75eb0405 100644 --- a/core/src/com/google/zxing/BinaryBitmap.java +++ b/core/src/com/google/zxing/BinaryBitmap.java @@ -115,7 +115,8 @@ public final class BinaryBitmap { } /** - * Returns a new object with rotated image data. Only callable if isRotateSupported() is true. + * Returns a new object with rotated image data by 90 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. * * @return A rotated version of this object. */ @@ -124,4 +125,15 @@ public final class BinaryBitmap { return new BinaryBitmap(binarizer.createBinarizer(newSource)); } + /** + * Returns a new object with rotated image data by 45 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return A rotated version of this object. + */ + public BinaryBitmap rotateCounterClockwise45() { + LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45(); + return new BinaryBitmap(binarizer.createBinarizer(newSource)); + } + } diff --git a/core/src/com/google/zxing/LuminanceSource.java b/core/src/com/google/zxing/LuminanceSource.java index bde1e64b8..329763f44 100644 --- a/core/src/com/google/zxing/LuminanceSource.java +++ b/core/src/com/google/zxing/LuminanceSource.java @@ -102,12 +102,23 @@ public abstract class LuminanceSource { } /** - * Returns a new object with rotated image data. Only callable if isRotateSupported() is true. + * Returns a new object with rotated image data by 90 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. * * @return A rotated version of this object. */ public LuminanceSource rotateCounterClockwise() { - throw new UnsupportedOperationException("This luminance source does not support rotation."); + throw new UnsupportedOperationException("This luminance source does not support rotation by 90 degrees."); + } + + /** + * Returns a new object with rotated image data by 45 degrees counterclockwise. + * Only callable if {@link #isRotateSupported()} is true. + * + * @return A rotated version of this object. + */ + public LuminanceSource rotateCounterClockwise45() { + throw new UnsupportedOperationException("This luminance source does not support rotation by 45 degrees."); } @Override diff --git a/core/test/src/com/google/zxing/BufferedImageLuminanceSource.java b/core/test/src/com/google/zxing/BufferedImageLuminanceSource.java index 50f64f26b..f22268674 100644 --- a/core/test/src/com/google/zxing/BufferedImageLuminanceSource.java +++ b/core/test/src/com/google/zxing/BufferedImageLuminanceSource.java @@ -45,6 +45,18 @@ public final class BufferedImageLuminanceSource extends LuminanceSource { if (left + width > sourceWidth || top + height > sourceHeight) { throw new IllegalArgumentException("Crop rectangle does not fit within image data."); } + + // The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent + // black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a + // barcode image. Force any such pixel to be white: + for (int y = top; y < top + height; y++) { + for (int x = left; x < left + width; x++) { + if ((image.getRGB(x, y) & 0xFF000000) == 0) { + image.setRGB(x, y, 0xFFFFFFFF); // = white + } + } + } + // Create a grayscale copy, no need to calculate the luminance manually this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY); this.image.getGraphics().drawImage(image, 0, 0, null); @@ -99,9 +111,6 @@ public final class BufferedImageLuminanceSource extends LuminanceSource { @Override public LuminanceSource rotateCounterClockwise() { - //if (!isRotateSupported()) { - // throw new IllegalStateException("Rotate not supported"); - //} int sourceWidth = image.getWidth(); int sourceHeight = image.getHeight(); @@ -121,4 +130,32 @@ public final class BufferedImageLuminanceSource extends LuminanceSource { return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width); } + @Override + public LuminanceSource rotateCounterClockwise45() { + int width = getWidth(); + int height = getHeight(); + + int oldCenterX = left + width / 2; + int oldCenterY = top + height / 2; + + // Rotate 45 degrees counterclockwise. + AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(-45.0), oldCenterX, oldCenterY); + + int sourceDimension = Math.max(image.getWidth(), image.getHeight()); + BufferedImage rotatedImage = new BufferedImage(sourceDimension, sourceDimension, BufferedImage.TYPE_BYTE_GRAY); + + // Draw the original image into rotated, via transformation + Graphics2D g = rotatedImage.createGraphics(); + g.drawImage(image, transform, null); + g.dispose(); + + int halfDimension = Math.max(width, height) / 2; + int newLeft = Math.max(0, oldCenterX - halfDimension); + int newTop = Math.max(0, oldCenterY - halfDimension); + int newRight = Math.min(sourceDimension - 1, oldCenterX + halfDimension); + int newBottom = Math.min(sourceDimension - 1, oldCenterY + halfDimension); + + return new BufferedImageLuminanceSource(rotatedImage, newLeft, newTop, newRight - newLeft, newBottom - newTop); + } + } diff --git a/javase/src/com/google/zxing/client/j2se/BufferedImageLuminanceSource.java b/javase/src/com/google/zxing/client/j2se/BufferedImageLuminanceSource.java index 18392a8e8..0775703ab 100644 --- a/javase/src/com/google/zxing/client/j2se/BufferedImageLuminanceSource.java +++ b/javase/src/com/google/zxing/client/j2se/BufferedImageLuminanceSource.java @@ -113,9 +113,6 @@ public final class BufferedImageLuminanceSource extends LuminanceSource { @Override public LuminanceSource rotateCounterClockwise() { - //if (!isRotateSupported()) { - // throw new IllegalStateException("Rotate not supported"); - //} int sourceWidth = image.getWidth(); int sourceHeight = image.getHeight(); @@ -135,4 +132,32 @@ public final class BufferedImageLuminanceSource extends LuminanceSource { return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width); } + @Override + public LuminanceSource rotateCounterClockwise45() { + int width = getWidth(); + int height = getHeight(); + + int oldCenterX = left + width / 2; + int oldCenterY = top + height / 2; + + // Rotate 45 degrees counterclockwise. + AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(-45.0), oldCenterX, oldCenterY); + + int sourceDimension = Math.max(image.getWidth(), image.getHeight()); + BufferedImage rotatedImage = new BufferedImage(sourceDimension, sourceDimension, BufferedImage.TYPE_BYTE_GRAY); + + // Draw the original image into rotated, via transformation + Graphics2D g = rotatedImage.createGraphics(); + g.drawImage(image, transform, null); + g.dispose(); + + int halfDimension = Math.max(width, height) / 2; + int newLeft = Math.max(0, oldCenterX - halfDimension); + int newTop = Math.max(0, oldCenterY - halfDimension); + int newRight = Math.min(sourceDimension - 1, oldCenterX + halfDimension); + int newBottom = Math.min(sourceDimension - 1, oldCenterY + halfDimension); + + return new BufferedImageLuminanceSource(rotatedImage, newLeft, newTop, newRight - newLeft, newBottom - newTop); + } + }