BitMatrix là thành phần nền tảng trong thư viện xử lý mã vạch ZXing, đóng vai trò trung tâm trong cả quá trình sinh mã (encoding) và giải mã (decoding). Khác với các cấu trúc mảng nhị phân thông thường, BitMatrix được thiết kế đặc biệt để cân bằng giữa dung lượng bộ nhớ, tốc độ truy cập và khả năng mở rộng — điều kiện tiên quyết khi làm việc với các ma trận lớn như QR Code có kích thước lên đến 177×177 module.
Cấu trúc lưu trữ thông minh
Lớp BitMatrix không sử dụng mảng hai chiều boolean[][] hay byte[][], mà thay vào đó khai thác cơ chế đóng gói bit trên kiểu dữ liệu int. Mỗi phần tử int trong mảng nội bộ bits[] biểu diễn trạng thái của 32 pixel liên tiếp theo chiều ngang. Các thuộc tính chính bao gồm:
width,height: Kích thước logic của ma trậnrowSize: Số lượngintcần thiết cho mỗi hàng, được tính bằng(width + 31) >> 5bits: Mảng số nguyên chứa toàn bộ dữ liệu nhị phân
Cơ chế truy cập bit hiệu quả
Hàm get(x, y) thực hiện truy xuất O(1) thông qua chuỗi phép toán bit:
public boolean get(int x, int y) {
final int offset = y * rowSize + (x >> 5);
return (bits[offset] & (1 << (x & 31))) != 0;
}
Ở đây, x >> 5 tương đương với x / 32, còn x & 31 thay thế cho x % 32 — cả hai đều nhanh hơn phép chia và chia lấy dư thông thường. Việc kiểm tra bit được thực hiện bằng phép AND với mask 1 << position, loại bỏ hoàn toàn chi phí chuyển đổi kiểu hoặc gọi hàm phụ trợ.
Quy trình sinh mã QR từ dữ liệu thô
Khi tạo QR Code, luồng xử lý đi qua các giai đoạn sau:
- Chuỗi đầu vào được mã hóa sang dạng bit theo chế độ ký tự (numeric, alphanumeric, byte…)
- Thêm khối dữ liệu kiểm soát lỗi (Reed-Solomon) dựa trên mức độ sửa lỗi chọn trước
- Đổ dữ liệu đã mã hóa vào ma trận theo thứ tự xoáy (boustrophedon), tránh vùng định vị và mẫu tìm kiếm
- Chèn các mẫu cố định: ba góc vuông định vị (finder patterns), đường dò (timing patterns), và vùng dành riêng (format & version info)
- Áp dụng mặt nạ (masking) để giảm thiểu các mẫu gây nhiễu cho thuật toán quét
Các thao tác phổ biến trên BitMatrix
Ngoài các phương thức cơ bản như set(), unset(), flip(), lớp này hỗ trợ nhiều thao tác cấp cao nhằm tăng hiệu suất:
setRegion(x, y, w, h): Đặt toàn bộ vùng hình chữ nhật về giá trị 1 — hữu ích khi vẽ khung, đường viền hoặc khối dữ liệu liền kềclear(): Đặt lại toàn bộ ma trận về 0 bằng một lệnhArrays.fill()duy nhấtrotate90(): Xoay ma trận 90 độ theo chiều kim đồng hồ bằng cách hoán đổi trục và đảo ngược chỉ số — không cần tạo mảng mớigetEnclosingRectangle(): Trả về bounding box nhỏ nhất bao quanh tất cả các điểm đen, giúp cắt vùng quan tâm trước khi giải mã
Chuyển đổi sang ảnh trực quan
Một ví dụ minh họa cách render BitMatrix thành BufferedImage với hệ số phóng đại linh hoạt:
public static BufferedImage toImage(BitMatrix matrix, int scale) {
final int width = matrix.getWidth() * scale;
final int height = matrix.getHeight() * scale;
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
final int color = matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF;
for (int dy = 0; dy < scale; dy++) {
for (int dx = 0; dx < scale; dx++) {
image.setRGB(x * scale + dx, y * scale + dy, color);
}
}
}
}
return image;
}
Tối ưu hiệu năng thực tế
Một số chiến lược hiệu quả khi làm việc với BitMatrix trong môi trường sản xuất:
- Giới hạn kích thước ma trận: Với QR Code cấp L (7%), kích thước tối đa là 177×177 → ~2.5KB bộ nhớ; tránh khởi tạo ma trận lớn hơn mức cần thiết
- Tái sử dụng đối tượng: Gọi
matrix.clear()thay vì tạo mới mỗi lần, đặc biệt trong vòng lặp xử lý hàng loạt - Xử lý theo hàng: Dùng
getRow(y, reuseArray)để trích xuất từng dòng dưới dạngBitArray, thuận tiện cho các thuật toán phát hiện cạnh hoặc phân tích thống kê - Loại bỏ vùng dư thừa sớm: Áp dụng
getEnclosingRectangle()sau bước nhị phân hóa để giảm diện tích xử lý cho các bước tiếp theo
Ví dụ ứng dụng: Tạo và đọc QR Code
Khởi tạo QR Code với tùy chọn nâng cao:
public static BitMatrix createQR(String data, int size, ErrorCorrectionLevel ecLevel)
throws WriterException {
final QRCodeWriter writer = new QRCodeWriter();
final HashMap<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ecLevel);
hints.put(EncodeHintType.MARGIN, 0); // Loại bỏ lề mặc định nếu cần
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
return writer.encode(data, BarcodeFormat.QR_CODE, size, size, hints);
}
Trích xuất BitMatrix từ ảnh đầu vào:
public static BitMatrix decodeFromImage(BufferedImage img) throws Exception {
final LuminanceSource source = new BufferedImageLuminanceSource(img);
final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
final Result result = new MultiFormatReader().decode(bitmap);
return bitmap.getBlackMatrix(); // Ma trận nhị phân gốc trước khi giải mã
}