问题
I'm using zxing to generate barcodes with different types (EAN, 2of5 and DataMatrix). Generating in general works fine. The only problem I currently have is that zxing only generates a 14x14 pixel bitmap which is way too small. But only when using DataMatrix! EAN13, 2of5/ITF and QR-Codes work perfect with the same code.
My code:
BitMatrix bitMatrix = new DataMatrixWriter().encode(message, BarcodeFormat.DATA_MATRIX, 1080, 1080, null);
int height = bitMatrix.getHeight(); //height is always 14, it doesn't matter what value I pass to the encoder
As you can imagine this looks pretty shitty on a 1080p screen like the nexus 5. Am I getting something wrong? Do I have to do some special settings for DataMatrix?
Google and Stackoverflow couldn't help me so far as I can't find any examples for the usage of DataMatrix
Update This is how I convert the bitmatrix to a bitmap
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int[] pixels = new int[width * height];
// All are 0, or black, by default
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = bitMatrix.get(x, y) ? BLACK : WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
If I use any other values for the height I get an OutOfBoundsException which is pretty obvious (I didn't expect anything else)...
When I try to scale the imageview and set a fixed width and height, the barcode is scannable but looks like shit. This is obvious too, as the bitmatrix is only 14x14 instead of the size I specified.
Is there a simple way to somehow scale a bitmatrix? Because it only consists of dots so it should be possible but I don't want to calculate it myself. I couldn't find any documentation for bitmatrix besides stackoverflow and this didn't help me at all.
If I pass a MinWidth or MaxWidth to the encoder via HintMap the app always crashes with an Exception. HintMap (mWidth is the display width of the device but I tried several values): Hashtable hintMap = new Hashtable();
hintMap.put(EncodeHintType.MIN_SIZE, new Dimension(mWidth, mWidth));
hintMap.put(EncodeHintType.MAX_SIZE, new Dimension(mWidth, mWidth));
hintMap.put(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
Exception:
java.lang.IllegalArgumentException: Can't find a symbol arrangement that matches the message. Data codewords: 7
This last issue seems to me like a bug in zxing. I don't get it why the generating doesn't work if I change the size.
回答1:
Here is a small example how you can change your method for the conversion from BitMatrix to Bitmap. The method does the scaling of the BitMatrix.
int BLACK = 0xFF000000;
int WHITE = 0xFFFFFFFF;
// change the values to your needs
int requestedWidth = 300;
int requestedHeight = 300;
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
// calculating the scaling factor
int pixelsize = requestedWidth/width;
if (pixelsize > requestedHeight/height)
{
pixelsize = requestedHeight/height;
}
int[] pixels = new int[requestedWidth * requestedHeight];
// All are 0, or black, by default
for (int y = 0; y < height; y++) {
int offset = y * requestedWidth * pixelsize;
// scaling pixel height
for (int pixelsizeHeight = 0; pixelsizeHeight < pixelsize; pixelsizeHeight++, offset+=requestedWidth) {
for (int x = 0; x < width; x++) {
int color = bitMatrix.get(x, y) ? BLACK : WHITE;
// scaling pixel width
for (int pixelsizeWidth = 0; pixelsizeWidth < pixelsize; pixelsizeWidth++) {
pixels[offset + x * pixelsize + pixelsizeWidth] = color;
}
}
}
}
Bitmap bitmap = Bitmap.createBitmap(requestedWidth, requestedHeight, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, requestedWidth, 0, 0, requestedWidth, requestedHeight);
// I could only test it with BufferedImage and a modified version of the zxing J2SE client
// BufferedImage bitmap = new BufferedImage(requestedWidth, requestedHeight, BufferedImage.TYPE_INT_ARGB);
// bitmap.getRaster().setDataElements(0, 0, requestedWidth, requestedHeight, pixels);
回答2:
As I remember, the Data Matrix encoder is kind of the odd man out since the code came from a different place (barcode4j). It will use the minimum appropriate dimensions and assumes the caller will scale the graphic as desired.
You can do that here -- just set the ImageView
to scale its contents. That shouldn't hurt for any of the encoded barcodes, as long as you do not enable anti-aliasing.
There is also a EncodeHintType.MIN_SIZE
hint which will set the minimum size, just for Data Matrix.
回答3:
I know that this question is about Android, but I think could be classified as generic.
I'm working with Swift and the the wrapper ZXingObjC. I obtain this error using DataMatrix encoding:
'Can't find a symbol arrangement that matches the message. Data codewords: 5'
So, debugging code, I arrived to class ZXDataMatrixSymbolInfo, on method:
+ (ZXDataMatrixSymbolInfo *)lookup:(int)dataCodewords shape:(ZXDataMatrixSymbolShapeHint)shape minSize:(ZXDimension *)minSize
When happens?
I write a custom swift wrapper (CoderEncoder) for my project, with this method:
static func encode(string code: String, encoding: CoderEncorderType, size: CGSize = CGSizeMake(200, 200)) -> UIImage? {
let writer: ZXWriter = ZXMultiFormatWriter.writer() as! ZXWriter
do {
let format = CodeEncoder.getEncoding(type: encoding)
let result = try writer.encode(code, format: format, width: Int32(size.width), height: Int32(size.height))
let cgImage = ZXImage(matrix: result, onColor: UIColor.blackColor().CGColor, offColor: UIColor.clearColor().CGColor).cgimage
return UIImage(CGImage: cgImage)
} catch let error as NSError {
ERLog(what: error)
}
return nil
}
So, when I call:
CoderEncoder.encode("foo", .DataMatrix)
error occurs.
Basically, in ZXDataMatrixWriter maxSize is nil so you need to specify a dimension from 10 to 144.
or!! or specify the shape:
ZXDataMatrixSymbolShapeHintForceSquare
ZXDataMatrixSymbolShapeHintForceRectangle
回答4:
Is there a simple way to somehow scale a bitmatrix?
Yes, there is. Problem is that using Android automatic scaling hits some problems:
- Android uses bi-linear filtering when scaling
- bi-linear filtering cannot be disabled when using Hardware Acceleration
The approach I've taken is to scale manually by an integer factor the Bitmap. Here's how to convert a BitMatrix to an unscaled Bitmap:
/**
* Create an unscaled bitmap (1 square = 1 pixel) from a bitmatrix
*
* @param bitMatrix the bitmatrix to convert
* @return a unscaled bitmap
*/
public Bitmap drawUnscaledBitmap(BitMatrix bitMatrix) {
final int bmWidth = bitMatrix.getWidth();
final int bmHeight = bitMatrix.getHeight();
final Bitmap bitmap = Bitmap.createBitmap(bmWidth, bmHeight, Bitmap.Config.ARGB_8888);
int[] pixels = new int[bmWidth * bmHeight];
for (int y = 0; y < bmHeight; y++) {
int offset = y * bmWidth;
for (int x = 0; x < bmWidth; x++) {
pixels[offset + x] = bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
bitmap.setPixels(pixels, 0, bmWidth, 0, 0, bmWidth, bmHeight);
return bitmap;
}
and here's how to scale up a Bitmap to (almost) fit a desired rectangle size:
/**
* Upscale a bitmap (maintaining ratio) by the biggest integer scaling factor that allows the bitmap to still fit in the given size
*
* @param bitmap the bitmap to upscale
* @param size the size that the output scaled bitmap needs to fit in
* @return the scaled bitmap if the integer scaling factor is > 1; the original bitmap otherwise
*/
public Bitmap drawScaledBitmap(Bitmap bitmap, Size size) {
final int bmWidth = bitmap.getWidth();
final int bmHeight = bitmap.getHeight();
final int wScalingFactor = size.getWidth() / bmWidth;
final int hScalingFactor = size.getHeight() / bmHeight;
final int scalingFactor = Math.min(wScalingFactor, hScalingFactor);
return scalingFactor > 1 ? Bitmap.createScaledBitmap(bitmap, bmWidth * scalingFactor, bmHeight * scalingFactor, false) : bitmap;
}
来源:https://stackoverflow.com/questions/22766017/datamatrix-encoding-with-zxing-only-generates-14px-bitmap