| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/qr_code_generator/qr_code_generator.h" |
| |
| #include <math.h> |
| #include <string.h> |
| |
| #include <ostream> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/notreached.h" |
| |
| // kMaxVersionWithSmallLengths is the maximum QR version that uses the smaller |
| // length fields, i.e. that is |VersionClass::SMALL|. See table 3. |
| static constexpr int kMaxVersionWithSmallLengths = 9; |
| |
| // A structure containing QR version-specific constants and data. |
| // All versions currently use error correction at level M. |
| struct QRVersionInfo { |
| enum class ECC { |
| kLow = 0, |
| kMedium = 1, |
| }; |
| |
| constexpr QRVersionInfo(const int version, |
| const ECC ecc, |
| const uint32_t encoded_version, |
| const int size, |
| const size_t group1_bytes, |
| const size_t group1_num_blocks, |
| const size_t group1_block_data_bytes, |
| const size_t group2_bytes, |
| const size_t group2_num_blocks, |
| const size_t group2_block_data_bytes, |
| const std::array<int, 3> alignment_locations) |
| : version(version), |
| ecc(ecc), |
| encoded_version(encoded_version), |
| size(size), |
| group1_bytes(group1_bytes), |
| group1_num_blocks(group1_num_blocks), |
| group1_block_data_bytes(group1_block_data_bytes), |
| group2_bytes(group2_bytes), |
| group2_num_blocks(group2_num_blocks), |
| group2_block_data_bytes(group2_block_data_bytes), |
| alignment_locations(alignment_locations) { |
| if (version < 1 || version > 40 || size < 0 || |
| !CheckBlockGroupParameters(group1_bytes, group1_num_blocks, |
| group1_block_data_bytes) || |
| (group2_num_blocks != 0 && |
| !CheckBlockGroupParameters(group2_bytes, group2_num_blocks, |
| group2_block_data_bytes)) || |
| (version < 7 && encoded_version != 0) || |
| (version >= 7 && |
| encoded_version >> 12 != static_cast<uint32_t>(version)) || |
| (group2_num_blocks != 0 && |
| group2_block_ec_bytes() != group1_block_ec_bytes())) { |
| __builtin_unreachable(); |
| } |
| } |
| QRVersionInfo(const QRVersionInfo&) = delete; |
| QRVersionInfo& operator=(const QRVersionInfo&) = delete; |
| |
| // The version of the QR code. |
| const int version; |
| |
| // The error correction level. |
| const ECC ecc; |
| |
| // An 18-bit value that contains the version, BCH (18,6)-encoded. Only valid |
| // for versions seven and above. See table D.1 for values. |
| const uint32_t encoded_version; |
| |
| // The number of "tiles" in each dimension for a QR code of |version|. See |
| // table 1. (The colored squares in in QR codes are called tiles in the |
| // spec.) |
| const int size; |
| |
| // Values taken from Table 9, page 38, for a QR code of version |version|. |
| const size_t group1_bytes; |
| const size_t group1_num_blocks; |
| const size_t group1_block_data_bytes; |
| const size_t group2_bytes; |
| const size_t group2_num_blocks; |
| const size_t group2_block_data_bytes; |
| |
| const std::array<int, 3> alignment_locations; |
| |
| // Total number of tiles for the QR code, size*size. |
| constexpr int total_size() const { return size * size; } |
| |
| constexpr size_t total_bytes() const { return group1_bytes + group2_bytes; } |
| |
| constexpr size_t group1_block_bytes() const { |
| return group1_bytes / group1_num_blocks; |
| } |
| |
| constexpr size_t group1_block_ec_bytes() const { |
| return group1_block_bytes() - group1_block_data_bytes; |
| } |
| |
| constexpr size_t group1_data_bytes() const { |
| return group1_block_data_bytes * group1_num_blocks; |
| } |
| |
| constexpr size_t group2_block_bytes() const { |
| if (group2_num_blocks == 0) |
| return 0; |
| return group2_bytes / group2_num_blocks; |
| } |
| |
| constexpr size_t group2_block_ec_bytes() const { |
| return group2_block_bytes() - group2_block_data_bytes; |
| } |
| |
| constexpr size_t group2_data_bytes() const { |
| return group2_block_data_bytes * group2_num_blocks; |
| } |
| |
| // input_bytes is the maximum number of data bytes, including framing bytes. |
| constexpr size_t input_bytes() const { |
| return group1_data_bytes() + group2_data_bytes(); |
| } |
| |
| private: |
| static constexpr bool CheckBlockGroupParameters( |
| const size_t bytes, |
| const size_t num_blocks, |
| const size_t block_data_bytes) { |
| if (num_blocks == 0 || bytes % num_blocks != 0 || block_data_bytes == 0 || |
| block_data_bytes * num_blocks > bytes || |
| (bytes - block_data_bytes * num_blocks) % num_blocks != 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| }; |
| |
| namespace { |
| |
| constexpr QRVersionInfo version_infos[] = { |
| // See table 9 in the spec for the source of these numbers. |
| |
| // 2-L |
| // 44 bytes in a single block. |
| { |
| 2, // version |
| QRVersionInfo::ECC::kLow, |
| 0, // encoded version (not included in this version) |
| 25, // size (num tiles in each axis) |
| |
| // Block group 1: |
| 44, // Total bytes in group |
| 1, // Number of blocks |
| 34, // Data bytes per block |
| |
| // Block group 2: |
| 0, |
| 0, |
| 0, |
| |
| // Alignment locations |
| {6, 18, 0}, |
| }, |
| |
| // 5-M |
| // 134 bytes, as 2 blocks of 67. |
| { |
| 5, // version |
| QRVersionInfo::ECC::kMedium, |
| 0, // encoded version (not included in this version) |
| 37, // size (num tiles in each axis) |
| |
| // Block group 1: |
| 134, // Total bytes in group |
| 2, // Number of blocks |
| 43, // Data bytes per block |
| |
| // Block group 2: |
| 0, |
| 0, |
| 0, |
| |
| // Alignment locations |
| {6, 30, 0}, |
| }, |
| |
| // 7-M |
| // 196 bytes, as 4 blocks of 49. |
| { |
| 7, // version |
| QRVersionInfo::ECC::kMedium, |
| 0b000111110010010100, // encoded version |
| 45, // size (num tiles in each axis) |
| |
| // Block group 1: |
| 196, // Total bytes in group |
| 4, // Number of blocks |
| 31, // Data bytes per block |
| |
| // Block group 2: |
| 0, |
| 0, |
| 0, |
| |
| // Alignment locations |
| {6, 22, 38}, |
| }, |
| |
| // 9-M |
| // 292 bytes, as 3 blocks of 58 plus 2 blocks of 59. |
| { |
| 9, // version |
| QRVersionInfo::ECC::kMedium, |
| 0b001001101010011001, // encoded version |
| 53, // size (num tiles in each axis) |
| |
| // Block group 1: |
| 174, // Total bytes in group |
| 3, // Number of blocks |
| 36, // Data bytes per block |
| |
| // Block group 2: |
| 118, |
| 2, |
| 37, |
| |
| // Alignment locations |
| {6, 26, 46}, |
| }, |
| |
| // 12-M |
| // 466 bytes, as 6 blocks of 58 and 2 blocks of 59. |
| { |
| 12, // version |
| QRVersionInfo::ECC::kMedium, |
| 0b001100011101100010, // encoded version |
| 65, // size (num tiles in each axis) |
| |
| // Block group 1: |
| 348, // Total bytes in group |
| 6, // Number of blocks |
| 36, // Data bytes per block |
| |
| // Block group 2: |
| 118, |
| 2, |
| 37, |
| |
| // Alignment locations |
| {6, 32, 58}, |
| }, |
| }; |
| |
| const QRVersionInfo* GetVersionForDataSize(size_t num_data_bytes, |
| absl::optional<int> min_version) { |
| for (const auto& version : version_infos) { |
| if (version.input_bytes() >= num_data_bytes && |
| (!min_version || *min_version <= version.version)) { |
| return &version; |
| } |
| } |
| return nullptr; |
| } |
| |
| // kMaxMask is the maximum masking function number. See table 10. |
| constexpr uint8_t kMaxMask = 7; |
| |
| // The following functions implement the masks specified in table 10. |
| |
| uint8_t MaskFunction0(int x, int y) { |
| return (x + y) % 2 == 0; |
| } |
| uint8_t MaskFunction1(int x, int y) { |
| return y % 2 == 0; |
| } |
| uint8_t MaskFunction2(int x, int y) { |
| return x % 3 == 0; |
| } |
| uint8_t MaskFunction3(int x, int y) { |
| return (x + y) % 3 == 0; |
| } |
| uint8_t MaskFunction4(int x, int y) { |
| return ((y / 2) + (x / 3)) % 2 == 0; |
| } |
| uint8_t MaskFunction5(int x, int y) { |
| return ((x * y) % 2) + ((x * y) % 3) == 0; |
| } |
| uint8_t MaskFunction6(int x, int y) { |
| return (((x * y) % 2) + ((x * y) % 3)) % 2 == 0; |
| } |
| uint8_t MaskFunction7(int x, int y) { |
| return (((x + y) % 2) + ((x * y) % 3)) % 2 == 0; |
| } |
| |
| static uint8_t (*const kMaskFunctions[kMaxMask + 1])(int x, int y) = { |
| MaskFunction0, MaskFunction1, MaskFunction2, MaskFunction3, |
| MaskFunction4, MaskFunction5, MaskFunction6, MaskFunction7, |
| }; |
| |
| // FormatInformationForECC returns an array of eight format values (one for each |
| // mask) for the given ECC level. |
| base::span<const uint16_t, 8> FormatInformationForECC(QRVersionInfo::ECC ecc) { |
| // kFormatInformation is taken from table C.1 on page 80 and specifies the |
| // format value for each masking function. The first eight values assume ECC |
| // level 'L' and the rest assume ECC level 'M'. |
| static const uint16_t kFormatInformation[2 * (kMaxMask + 1)] = { |
| 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, |
| 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, |
| }; |
| |
| switch (ecc) { |
| case QRVersionInfo::ECC::kLow: |
| return base::span<const uint16_t, 8>(kFormatInformation, 8); |
| case QRVersionInfo::ECC::kMedium: |
| return base::span<const uint16_t, 8>(&kFormatInformation[8], 8); |
| } |
| } |
| |
| // kAlphanumValue maps from the beginning of the ASCII codespace to the value |
| // of a character in QR's alphanumeric mode, or 255 if the ASCII byte isn't |
| // represented. This is taken from table five of ISO 18004 (2015 edition). |
| static constexpr uint8_t kAlphanumValue[91] = { |
| 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
| 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, |
| 255, 255, 255, 255, 255, 255, 36, 255, 255, 255, 37, 38, 255, |
| 255, 255, 255, 39, 40, 255, 41, 42, 43, 0, 1, 2, 3, |
| 4, 5, 6, 7, 8, 9, 44, 255, 255, 255, 255, 255, 255, |
| 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
| 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, |
| }; |
| |
| // These assertions are spot checks of a few values from the table. |
| static_assert(kAlphanumValue[static_cast<int>('0')] == 0, ""); |
| static_assert(kAlphanumValue[static_cast<int>('A')] == 10, ""); |
| static_assert(kAlphanumValue[static_cast<int>('Z')] == 35, ""); |
| static_assert(kAlphanumValue[static_cast<int>(' ')] == 36, ""); |
| static_assert(kAlphanumValue[static_cast<int>(':')] == 44, ""); |
| static_assert(kAlphanumValue[static_cast<int>('"')] == 255, ""); |
| |
| // LengthBits returns the number of bits used to encode the length of a given |
| // segment type. See table 3. |
| constexpr size_t LengthBits(QRCodeGenerator::VersionClass vclass, |
| QRCodeGenerator::SegmentType type) { |
| switch (type) { |
| case QRCodeGenerator::SegmentType::DIGIT: |
| switch (vclass) { |
| case QRCodeGenerator::VersionClass::SMALL: |
| return 10; |
| case QRCodeGenerator::VersionClass::LARGE: |
| return 12; |
| } |
| |
| case QRCodeGenerator::SegmentType::ALPHANUM: |
| switch (vclass) { |
| case QRCodeGenerator::VersionClass::SMALL: |
| return 9; |
| case QRCodeGenerator::VersionClass::LARGE: |
| return 11; |
| } |
| |
| case QRCodeGenerator::SegmentType::BINARY: |
| switch (vclass) { |
| case QRCodeGenerator::VersionClass::SMALL: |
| return 8; |
| case QRCodeGenerator::VersionClass::LARGE: |
| return 16; |
| } |
| } |
| } |
| |
| // BitPacker appends bits to |output|, packing them from most-significant place |
| // to least significant. |
| class BitPacker { |
| public: |
| explicit BitPacker(std::vector<uint8_t>* output) : out_(output) {} |
| |
| // AppendSegments adds the contents of |input| using the segmentation from |
| // |segments|. |
| void AppendSegments(const std::vector<QRCodeGenerator::Segment>& segments, |
| QRCodeGenerator::VersionClass vclass, |
| base::span<const uint8_t> input) { |
| size_t offset = 0; |
| for (const auto& segment : segments) { |
| base::span<const uint8_t> segment_data = |
| input.subspan(offset, segment.length); |
| switch (segment.type) { |
| case QRCodeGenerator::SegmentType::DIGIT: |
| AppendDigits(segment_data, vclass); |
| break; |
| case QRCodeGenerator::SegmentType::ALPHANUM: |
| AppendAlphanum(segment_data, vclass); |
| break; |
| case QRCodeGenerator::SegmentType::BINARY: |
| AppendBinary(segment_data, vclass); |
| break; |
| } |
| |
| offset += segment.length; |
| } |
| } |
| |
| void AppendTerminator() { AppendBitsFromByte(0, 4); } |
| |
| private: |
| // ModeNumber returns the 4-bit identifier for the given mode. See table 2. |
| static constexpr uint8_t ModeNumber(QRCodeGenerator::SegmentType stype) { |
| switch (stype) { |
| case QRCodeGenerator::SegmentType::DIGIT: |
| return 1; |
| case QRCodeGenerator::SegmentType::ALPHANUM: |
| return 2; |
| case QRCodeGenerator::SegmentType::BINARY: |
| return 4; |
| } |
| } |
| |
| // AppendBitsFromByte appends |num_bits| (which must be <= 8) from |v|. |
| void AppendBitsFromByte(uint8_t v, int num_bits) { |
| DCHECK_LE(num_bits, 8); |
| |
| v <<= 8 - num_bits; |
| |
| if (bits_remaining_in_final_byte_ > 0) { |
| out_->back() |= v >> (8 - bits_remaining_in_final_byte_); |
| } |
| bits_remaining_in_final_byte_ -= num_bits; |
| if (bits_remaining_in_final_byte_ < 0) { |
| out_->push_back(v << (num_bits + bits_remaining_in_final_byte_)); |
| bits_remaining_in_final_byte_ += 8; |
| } |
| } |
| |
| // AppendBits appends |num_bits| (which must be <= 16) from |v|. |
| void AppendBits(const uint16_t v, const int num_bits) { |
| DCHECK_LE(num_bits, 16); |
| DCHECK_LT(v, 1u << num_bits); |
| |
| if (num_bits <= 8) { |
| AppendBitsFromByte(static_cast<uint8_t>(v), num_bits); |
| return; |
| } |
| |
| const int n = num_bits - 8; |
| AppendBitsFromByte(static_cast<uint8_t>(v >> n), 8); |
| AppendBitsFromByte(static_cast<uint8_t>(v & ((1u << n) - 1)), n); |
| } |
| |
| static unsigned DigitValue(uint8_t digit) { |
| DCHECK('0' <= digit && digit <= '9'); |
| return digit - '0'; |
| } |
| |
| void AppendDigits(base::span<const uint8_t> in, |
| QRCodeGenerator::VersionClass vclass) { |
| const auto stype = QRCodeGenerator::SegmentType::DIGIT; |
| AppendBitsFromByte(ModeNumber(stype), 4); |
| AppendBits(in.size(), LengthBits(vclass, stype)); |
| |
| while (in.size() >= 3) { |
| // Triples of digits are encoded as 10-bit values. |
| AppendBits( |
| DigitValue(in[0]) * 100 + DigitValue(in[1]) * 10 + DigitValue(in[2]), |
| 10); |
| in = in.subspan(3); |
| } |
| |
| // Any remaining digits are encoded using the minimal number of bits. |
| if (in.size() == 2) { |
| AppendBitsFromByte(DigitValue(in[0]) * 10 + DigitValue(in[1]), 7); |
| } else if (in.size() == 1) { |
| AppendBitsFromByte(DigitValue(in[0]), 4); |
| } |
| } |
| |
| void AppendAlphanum(base::span<const uint8_t> in, |
| QRCodeGenerator::VersionClass vclass) { |
| const auto stype = QRCodeGenerator::SegmentType::ALPHANUM; |
| AppendBitsFromByte(ModeNumber(stype), 4); |
| AppendBits(in.size(), LengthBits(vclass, stype)); |
| |
| while (in.size() >= 2) { |
| DCHECK(in[0] < sizeof(kAlphanumValue) && kAlphanumValue[in[0]] != 0xff) |
| << in[0]; |
| DCHECK(in[1] < sizeof(kAlphanumValue) && kAlphanumValue[in[1]] != 0xff) |
| << in[1]; |
| AppendBits(kAlphanumValue[in[0]] * 45 + kAlphanumValue[in[1]], 11); |
| in = in.subspan(2); |
| } |
| |
| if (!in.empty()) { |
| AppendBitsFromByte(kAlphanumValue[in[0]], 6); |
| } |
| } |
| |
| void AppendBinary(base::span<const uint8_t> in, |
| QRCodeGenerator::VersionClass vclass) { |
| const auto stype = QRCodeGenerator::SegmentType::BINARY; |
| AppendBitsFromByte(ModeNumber(stype), 4); |
| AppendBits(in.size(), LengthBits(vclass, stype)); |
| |
| for (const uint8_t b : in) { |
| AppendBitsFromByte(b, 8); |
| } |
| } |
| |
| const raw_ptr<std::vector<uint8_t>> out_; |
| int bits_remaining_in_final_byte_ = 0; |
| }; |
| |
| // VersionClassForVersion translates a QR code version number into whether it |
| // uses small or large lengths. |
| QRCodeGenerator::VersionClass VersionClassForVersion(int version) { |
| return version <= kMaxVersionWithSmallLengths |
| ? QRCodeGenerator::VersionClass::SMALL |
| : QRCodeGenerator::VersionClass::LARGE; |
| } |
| |
| // SegmentBits returns the number of bits needed to encode the given segment. |
| size_t SegmentBits(QRCodeGenerator::VersionClass vclass, |
| const QRCodeGenerator::Segment& segment) { |
| static const uint8_t kDigitTrailingBits[3] = {0, 4, 7}; |
| const size_t header_bits = 4 + LengthBits(vclass, segment.type); |
| |
| switch (segment.type) { |
| case QRCodeGenerator::SegmentType::DIGIT: |
| return header_bits + (10 * (segment.length / 3)) + |
| kDigitTrailingBits[segment.length % 3]; |
| |
| case QRCodeGenerator::SegmentType::ALPHANUM: |
| return header_bits + (11 * (segment.length / 2)) + |
| (segment.length & 1 ? 6 : 0); |
| |
| case QRCodeGenerator::SegmentType::BINARY: |
| return header_bits + 8 * segment.length; |
| } |
| } |
| |
| size_t SegmentBits(QRCodeGenerator::VersionClass vclass, |
| base::span<const QRCodeGenerator::Segment> segments) { |
| size_t sum = 0; |
| for (const auto& segment : segments) { |
| sum += SegmentBits(vclass, segment); |
| } |
| |
| return sum; |
| } |
| |
| // SegmentSpanLength returns the total length of |segments|. |
| size_t SegmentSpanLength(base::span<const QRCodeGenerator::Segment> segments) { |
| size_t sum = 0; |
| for (const auto& segment : segments) { |
| sum += segment.length; |
| } |
| |
| return sum; |
| } |
| |
| } // namespace |
| |
| QRCodeGenerator::QRCodeGenerator() = default; |
| |
| QRCodeGenerator::~QRCodeGenerator() = default; |
| |
| QRCodeGenerator::GeneratedCode::GeneratedCode() = default; |
| QRCodeGenerator::GeneratedCode::GeneratedCode( |
| QRCodeGenerator::GeneratedCode&&) = default; |
| QRCodeGenerator::GeneratedCode::~GeneratedCode() = default; |
| |
| absl::optional<QRCodeGenerator::GeneratedCode> QRCodeGenerator::Generate( |
| base::span<const uint8_t> in, |
| absl::optional<int> min_version, |
| absl::optional<uint8_t> mask) { |
| CHECK(!mask || *mask <= kMaxMask); |
| |
| std::vector<Segment> segments; |
| const QRVersionInfo* version_info = nullptr; |
| |
| // First assume that we can use |SMALL| QR codes. If the input is too large |
| // for that than recalculate for |LARGE| codes. |
| for (VersionClass version_class : |
| {VersionClass::SMALL, VersionClass::LARGE}) { |
| segments = SegmentInput(version_class, in); |
| DCHECK(IsValidSegmentation(segments, in)); |
| |
| const size_t length_bits = |
| SegmentBits(version_class, segments) + 4 /* for the terminator */; |
| const size_t length_bytes = (length_bits + 7) / 8; |
| version_info = GetVersionForDataSize(length_bytes, min_version); |
| if (!version_info) { |
| return absl::nullopt; |
| } |
| |
| if (VersionClassForVersion(version_info->version) == version_class) { |
| break; |
| } |
| } |
| |
| const VersionClass version_class = |
| VersionClassForVersion(version_info->version); |
| if (version_info != version_info_) { |
| version_info_ = version_info; |
| d_.resize(version_info_->total_size()); |
| } |
| // Previous data and "set" bits must be cleared. |
| memset(&d_[0], 0, version_info_->total_size()); |
| |
| const size_t framed_input_size = |
| version_info_->group1_data_bytes() + version_info_->group2_data_bytes(); |
| std::vector<uint8_t> prefixed_data; |
| prefixed_data.reserve(framed_input_size); |
| BitPacker packer(&prefixed_data); |
| |
| packer.AppendSegments(segments, version_class, in); |
| packer.AppendTerminator(); |
| |
| // The remainder of the space is filled with alternatining 0xec and 0x11 |
| // bytes, as per the standard. (Although the contents of this padding do not, |
| // in practice, matter.) |
| bool padding_phase = false; |
| while (prefixed_data.size() < framed_input_size) { |
| prefixed_data.push_back(padding_phase ? 0x11 : 0xec); |
| padding_phase = !padding_phase; |
| } |
| |
| PutVerticalTiming(6); |
| PutHorizontalTiming(6); |
| PutFinder(3, 3); |
| PutFinder(3, version_info_->size - 4); |
| PutFinder(version_info_->size - 4, 3); |
| |
| const auto& alignment_locations = version_info_->alignment_locations; |
| size_t num_alignment_locations = 0; |
| for (size_t i = 0; i < alignment_locations.size(); i++) { |
| if (alignment_locations[i] == 0) { |
| break; |
| } |
| num_alignment_locations++; |
| } |
| |
| for (size_t i = 0; i < num_alignment_locations; i++) { |
| for (size_t j = 0; j < num_alignment_locations; j++) { |
| // Three of the corners already have finder symbols. |
| if ((i == 0 && j == 0) || (i == 0 && j == num_alignment_locations - 1) || |
| (i == num_alignment_locations - 1 && j == 0)) { |
| continue; |
| } |
| |
| PutAlignment(alignment_locations[i], alignment_locations[j]); |
| } |
| } |
| |
| if (version_info_->encoded_version != 0) { |
| PutVersionBlocks(version_info_->encoded_version); |
| } |
| |
| // Each block of input data is expanded with error correcting |
| // information and then interleaved. |
| |
| // Error Correction for Group 1, present for all versions. |
| const size_t group1_num_blocks = version_info_->group1_num_blocks; |
| const size_t group1_block_bytes = version_info_->group1_block_bytes(); |
| const size_t group1_block_data_bytes = version_info_->group1_block_data_bytes; |
| const size_t group1_block_ec_bytes = version_info_->group1_block_ec_bytes(); |
| std::vector<std::vector<uint8_t>> expanded_blocks(group1_num_blocks); |
| for (size_t i = 0; i < group1_num_blocks; i++) { |
| expanded_blocks[i].resize(group1_block_bytes); |
| AddErrorCorrection( |
| expanded_blocks[i], |
| base::span<const uint8_t>(&prefixed_data[group1_block_data_bytes * i], |
| group1_block_data_bytes), |
| group1_block_bytes, group1_block_ec_bytes); |
| } |
| |
| // Error Correction for Group 2, present for some versions. |
| // Factor out the number of bytes written by the prior group. |
| const size_t group_data_offset = version_info_->group1_data_bytes(); |
| const size_t group2_num_blocks = version_info_->group2_num_blocks; |
| const size_t group2_block_bytes = version_info_->group2_block_bytes(); |
| const size_t group2_block_data_bytes = version_info_->group2_block_data_bytes; |
| const size_t group2_block_ec_bytes = version_info_->group2_block_ec_bytes(); |
| |
| std::vector<std::vector<uint8_t>> expanded_blocks_2; |
| if (group2_num_blocks > 0) { |
| expanded_blocks_2.resize(group2_num_blocks); |
| for (size_t i = 0; i < group2_num_blocks; i++) { |
| expanded_blocks_2[i].resize(group2_block_bytes); |
| AddErrorCorrection( |
| expanded_blocks_2[i], |
| base::span<const uint8_t>( |
| &prefixed_data[group_data_offset + group2_block_data_bytes * i], |
| group2_block_data_bytes), |
| group2_block_bytes, group2_block_ec_bytes); |
| } |
| } |
| |
| const size_t total_bytes = version_info_->total_bytes(); |
| uint8_t interleaved_data[total_bytes]; |
| CHECK(total_bytes == group1_block_bytes * group1_num_blocks + |
| group2_block_bytes * group2_num_blocks) |
| << "internal error"; |
| |
| size_t k = 0; |
| // Interleave data from all blocks. All non-ECC data is written before any ECC |
| // data. The group two blocks, if any, will be longer than the group one |
| // blocks. Once group one is exhausted then the interleave considers only |
| // group two. |
| DCHECK(group2_num_blocks == 0 || version_info_->group2_block_data_bytes > |
| version_info_->group1_block_data_bytes); |
| |
| size_t j = 0; |
| for (; j < version_info_->group1_block_data_bytes; j++) { |
| for (size_t i = 0; i < group1_num_blocks; i++) { |
| interleaved_data[k++] = expanded_blocks[i][j]; |
| } |
| for (size_t i = 0; i < group2_num_blocks; i++) { |
| interleaved_data[k++] = expanded_blocks_2[i][j]; |
| } |
| } |
| if (group2_num_blocks > 0) { |
| for (; j < version_info_->group2_block_data_bytes; j++) { |
| for (size_t i = 0; i < group2_num_blocks; i++) { |
| interleaved_data[k++] = expanded_blocks_2[i][j]; |
| } |
| } |
| } |
| |
| // The number of ECC bytes in each group is the same so the interleave |
| // considers them uniformly. |
| DCHECK(version_info_->group2_num_blocks == 0 || |
| version_info_->group2_block_ec_bytes() == |
| version_info_->group1_block_ec_bytes()); |
| for (j = 0; j < version_info_->group1_block_ec_bytes(); j++) { |
| for (size_t i = 0; i < group1_num_blocks; i++) { |
| interleaved_data[k++] = |
| expanded_blocks[i][version_info_->group1_block_data_bytes + j]; |
| } |
| for (size_t i = 0; i < group2_num_blocks; i++) { |
| interleaved_data[k++] = |
| expanded_blocks_2[i][version_info_->group2_block_data_bytes + j]; |
| } |
| } |
| DCHECK_EQ(k, total_bytes); |
| |
| uint8_t best_mask = mask.value_or(0); |
| absl::optional<unsigned> lowest_penalty; |
| |
| // If |mask| was not specified, then evaluate each masking function to find |
| // the one with the lowest penalty score. |
| for (uint8_t mask_num = 0; !mask && mask_num <= kMaxMask; mask_num++) { |
| // FormatInformationForECC returns an array of encoded formatting words for |
| // the QR code that this code generates. See tables 10 and 12. For example: |
| // 00 011 |
| // --|--- |
| // error correction M | Mask pattern 3 |
| // |
| // It's translated into a 15-bit value using the table on page 80. |
| PutFormatBits(FormatInformationForECC(version_info_->ecc)[mask_num]); |
| |
| PutBits(interleaved_data, sizeof(interleaved_data), |
| kMaskFunctions[mask_num]); |
| |
| const unsigned penalty = CountPenaltyPoints(); |
| if (!lowest_penalty || *lowest_penalty > penalty) { |
| lowest_penalty = penalty; |
| best_mask = mask_num; |
| } |
| } |
| |
| // Repaint with the best mask function. |
| PutFormatBits(FormatInformationForECC(version_info_->ecc)[best_mask]); |
| PutBits(interleaved_data, sizeof(interleaved_data), |
| kMaskFunctions[best_mask]); |
| |
| GeneratedCode code; |
| code.data = base::span<uint8_t>(&d_[0], version_info_->total_size()); |
| code.qr_size = version_info_->size; |
| return code; |
| } |
| |
| // PutFinder paints a finder symbol at the given coordinates. |
| void QRCodeGenerator::PutFinder(int x, int y) { |
| DCHECK_GE(x, 3); |
| DCHECK_GE(y, 3); |
| fillAt(x - 3, y - 3, 7, 0b11); |
| fillAt(x - 2, y - 2, 5, 0b10); |
| fillAt(x - 2, y + 2, 5, 0b10); |
| fillAt(x - 3, y + 3, 7, 0b11); |
| |
| static constexpr uint8_t kLine[7] = {0b11, 0b10, 0b11, 0b11, |
| 0b11, 0b10, 0b11}; |
| copyTo(x - 3, y - 1, kLine, sizeof(kLine)); |
| copyTo(x - 3, y, kLine, sizeof(kLine)); |
| copyTo(x - 3, y + 1, kLine, sizeof(kLine)); |
| |
| at(x - 3, y - 2) = 0b11; |
| at(x + 3, y - 2) = 0b11; |
| at(x - 3, y + 2) = 0b11; |
| at(x + 3, y + 2) = 0b11; |
| |
| for (int xx = x - 4; xx <= x + 4; xx++) { |
| clipped(xx, y - 4) = 0b10; |
| clipped(xx, y + 4) = 0b10; |
| } |
| for (int yy = y - 3; yy <= y + 3; yy++) { |
| clipped(x - 4, yy) = 0b10; |
| clipped(x + 4, yy) = 0b10; |
| } |
| } |
| |
| // PutAlignment paints an alignment symbol centered at the given coordinates. |
| void QRCodeGenerator::PutAlignment(int x, int y) { |
| fillAt(x - 2, y - 2, 5, 0b11); |
| fillAt(x - 2, y + 2, 5, 0b11); |
| static constexpr uint8_t kLine[5] = {0b11, 0b10, 0b10, 0b10, 0b11}; |
| copyTo(x - 2, y - 1, kLine, sizeof(kLine)); |
| copyTo(x - 2, y, kLine, sizeof(kLine)); |
| copyTo(x - 2, y + 1, kLine, sizeof(kLine)); |
| at(x, y) = 0b11; |
| } |
| |
| // PutVerticalTiming paints the vertical timing signal. |
| void QRCodeGenerator::PutVerticalTiming(int x) { |
| for (int y = 0; y < version_info_->size; y++) { |
| at(x, y) = 0b10 | (1 ^ (y & 1)); |
| } |
| } |
| |
| // PutVerticalTiming paints the horizontal timing signal. |
| void QRCodeGenerator::PutHorizontalTiming(int y) { |
| for (int x = 0; x < version_info_->size; x++) { |
| at(x, y) = 0b10 | (1 ^ (x & 1)); |
| } |
| } |
| |
| // PutFormatBits paints the 15-bit, pre-encoded format metadata. See page 56 |
| // for the location of the format bits. |
| void QRCodeGenerator::PutFormatBits(const uint16_t format) { |
| // kRun1 is the location of the initial format bits, where the upper nibble |
| // is the x coordinate and the lower the y. |
| static constexpr uint8_t kRun1[15] = { |
| 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, |
| 0x78, 0x58, 0x48, 0x38, 0x28, 0x18, 0x08, |
| }; |
| |
| uint16_t v = format; |
| for (size_t i = 0; i < sizeof(kRun1); i++) { |
| const uint8_t location = kRun1[i]; |
| at(location >> 4, location & 15) = 0b10 | (v & 1); |
| v >>= 1; |
| } |
| |
| const int size = version_info_->size; |
| v = format; |
| for (int x = size - 1; x >= size - 1 - 7; x--) { |
| at(x, 8) = 0b10 | (v & 1); |
| v >>= 1; |
| } |
| |
| at(8, size - 1 - 7) = 0b11; |
| for (int y = size - 1 - 6; y <= size - 1; y++) { |
| at(8, y) = 0b10 | (v & 1); |
| v >>= 1; |
| } |
| } |
| |
| void QRCodeGenerator::PutVersionBlocks(uint32_t encoded_version) { |
| // Version 7 and larger require 18-bit version information taking the form |
| // of 6x3 rectangles above the bottom-left locator and to the left of the |
| // top-right locator. |
| const int size = version_info_->size; |
| |
| for (int i = 0; i < 6; i++) { |
| for (int j = 0; j < 3; j++) { |
| // Bottom-left rectangle is top-to-bottom, left-to-right |
| at(i, size - 8 - 3 + j) = 0b10 | (encoded_version & 1); |
| // Top-right rectangle is left-to-right, top-to-bottom |
| at(size - 8 - 3 + j, i) = 0b10 | (encoded_version & 1); |
| // Shift to consider the next bit. |
| encoded_version >>= 1; |
| } |
| } |
| } |
| |
| // PutBits writes the given data into the QR code in correct order, avoiding |
| // structural elements that must have already been painted. See section 7.7.3 |
| // about the placement algorithm. |
| void QRCodeGenerator::PutBits(const uint8_t* data, |
| size_t data_len, |
| uint8_t (*mask_func)(int, int)) { |
| // BitStream vends bits from |data| on demand, in the order that QR codes |
| // expect them. |
| class BitStream { |
| public: |
| BitStream(const uint8_t* data, size_t data_len) |
| : data_(data), data_len_(data_len), i_(0), bits_in_current_byte_(0) {} |
| |
| uint8_t Next() { |
| if (bits_in_current_byte_ == 0) { |
| if (i_ >= data_len_) { |
| byte_ = 0; |
| } else { |
| byte_ = data_[i_++]; |
| } |
| bits_in_current_byte_ = 8; |
| } |
| |
| const uint8_t ret = byte_ >> 7; |
| byte_ <<= 1; |
| bits_in_current_byte_--; |
| return ret; |
| } |
| |
| private: |
| const uint8_t* const data_; |
| const size_t data_len_; |
| size_t i_; |
| unsigned bits_in_current_byte_; |
| uint8_t byte_; |
| }; |
| |
| BitStream stream(data, data_len); |
| |
| bool going_up = true; |
| int x = version_info_->size - 1; |
| int y = version_info_->size - 1; |
| |
| for (;;) { |
| uint8_t& right = at(x, y); |
| // Test the current value in the QR code to avoid painting over any |
| // existing structural elements. |
| if ((right & 2) == 0) { |
| right = stream.Next() ^ mask_func(x, y); |
| } |
| |
| uint8_t& left = at(x - 1, y); |
| if ((left & 2) == 0) { |
| left = stream.Next() ^ mask_func(x - 1, y); |
| } |
| |
| if ((going_up && y == 0) || (!going_up && y == version_info_->size - 1)) { |
| if (x == 1) { |
| break; |
| } |
| x -= 2; |
| // The vertical timing column is skipped over. |
| if (x == 6) { |
| x--; |
| } |
| going_up = !going_up; |
| } else { |
| if (going_up) { |
| y--; |
| } else { |
| y++; |
| } |
| } |
| } |
| } |
| |
| // at returns a reference to the given element of |d_|. |
| uint8_t& QRCodeGenerator::at(int x, int y) { |
| DCHECK_LE(0, x); |
| DCHECK_LT(x, version_info_->size); |
| DCHECK_LE(0, y); |
| DCHECK_LT(y, version_info_->size); |
| return d_[version_info_->size * y + x]; |
| } |
| |
| // fillAt sets the |len| elements at (x, y) to |value|. |
| void QRCodeGenerator::fillAt(int x, int y, size_t len, uint8_t value) { |
| DCHECK_LE(0, x); |
| DCHECK_LE(static_cast<int>(x + len), version_info_->size); |
| DCHECK_LE(0, y); |
| DCHECK_LT(y, version_info_->size); |
| memset(&d_[version_info_->size * y + x], value, len); |
| } |
| |
| // copyTo copies |len| elements from |data| to the elements at (x, y). |
| void QRCodeGenerator::copyTo(int x, int y, const uint8_t* data, size_t len) { |
| DCHECK_LE(0, x); |
| DCHECK_LE(static_cast<int>(x + len), version_info_->size); |
| DCHECK_LE(0, y); |
| DCHECK_LT(y, version_info_->size); |
| memcpy(&d_[version_info_->size * y + x], data, len); |
| } |
| |
| // clipped returns a reference to the given element of |d_|, or to |
| // |clip_dump_| if the coordinates are out of bounds. |
| uint8_t& QRCodeGenerator::clipped(int x, int y) { |
| if (0 <= x && x < version_info_->size && 0 <= y && y < version_info_->size) { |
| return d_[version_info_->size * y + x]; |
| } |
| return clip_dump_; |
| } |
| |
| // GF28Mul returns the product of |a| and |b| (which must be field elements, |
| // i.e. < 256) in the field GF(2^8) mod x^8 + x^4 + x^3 + x^2 + 1. |
| // static |
| uint8_t QRCodeGenerator::GF28Mul(uint16_t a, uint16_t b) { |
| uint16_t acc = 0; |
| |
| // Perform 8-bit, carry-less multiplication of |a| and |b|. |
| for (int i = 0; i < 8; i++) { |
| const uint16_t mask = ~((b & 1) - 1); |
| acc ^= a & mask; |
| b >>= 1; |
| a <<= 1; |
| } |
| |
| // Add multiples of the modulus to eliminate all bits past a byte. Note that |
| // the bits in |modulus| have a one where there's a non-zero power of |x| in |
| // the field modulus. |
| uint16_t modulus = 0b100011101 << 7; |
| for (int i = 15; i >= 8; i--) { |
| const uint16_t mask = ~((acc >> i) - 1); |
| acc ^= modulus & mask; |
| modulus >>= 1; |
| } |
| |
| return acc; |
| } |
| |
| // AddErrorCorrection writes the Reed-Solomon expanded version of |in| to |
| // |out|. |
| // |out| should have length block_bytes for the code's version. |
| // |in| should have length block_data_bytes for the code's version. |
| void QRCodeGenerator::AddErrorCorrection(base::span<uint8_t> out, |
| base::span<const uint8_t> in, |
| size_t block_bytes, |
| size_t block_ec_bytes) { |
| // kGenerator is the product of (z - x^i) for 0 <= i < |block_ec_bytes|, |
| // where x is the term of GF(2^8) and z is the term of a polynomial ring |
| // over GF(2^8). It's generated with the following Sage script: |
| // |
| // F.<x> = GF(2^8, modulus = x^8 + x^4 + x^3 + x^2 + 1) |
| // R.<z> = PolynomialRing(F, 'z') |
| // |
| // def toByte(p): |
| // return sum([(1<<i) * int(term) for (i, term) in |
| // enumerate(p.polynomial())]) |
| // |
| // def generatorPoly(n): |
| // acc = (z - F(1)) |
| // for i in range(1,n): |
| // acc *= (z - x^i) |
| // return acc |
| // |
| // gen = generatorPoly(24) |
| // coeffs = list(gen) |
| // gen = [toByte(x) for x in coeffs] |
| // print 'uint8_t kGenerator[' + str(len(gen)) + '] = {' + str(gen) + '}' |
| |
| // Used for 2-L: 10 error correction codewords per block. |
| static const uint8_t kGenerator10[] = { |
| 193, 157, 113, 95, 94, 199, 111, 159, 194, 216, 1, |
| }; |
| |
| // Used for 7-M: 18 error correction codewords per block. |
| static const uint8_t kGenerator18[] = { |
| 146, 217, 67, 32, 75, 173, 82, 73, 220, 240, |
| 215, 199, 175, 149, 113, 183, 251, 239, 1, |
| }; |
| |
| // Used for 9-M and 12-M; 22 error correction codewords per block. |
| static const uint8_t kGenerator22[] = { |
| 245, 145, 26, 230, 218, 86, 253, 67, 123, 29, 137, 28, |
| 40, 69, 189, 19, 244, 182, 176, 131, 179, 89, 1, |
| }; |
| |
| // Used for 5-M, 24 error correction codewords per block. |
| static const uint8_t kGenerator24[] = { |
| 117, 144, 217, 127, 247, 237, 1, 206, 43, 61, 72, 130, 73, |
| 229, 150, 115, 102, 216, 237, 178, 70, 169, 118, 122, 1, |
| }; |
| |
| const uint8_t* generator = nullptr; |
| switch (block_ec_bytes) { |
| case 10: |
| generator = kGenerator10; |
| break; |
| case 18: |
| generator = kGenerator18; |
| break; |
| case 22: |
| generator = kGenerator22; |
| break; |
| case 24: |
| generator = kGenerator24; |
| break; |
| default: { |
| NOTREACHED() << "Unsupported Generator Polynomial for block_ec_bytes: " |
| << block_ec_bytes; |
| return; |
| } |
| } |
| |
| // The error-correction bytes are the remainder of dividing |in| * x^k by |
| // |kGenerator|, where |k| is the number of EC codewords. Polynomials here |
| // are represented in little-endian order, i.e. the value at index |i| is |
| // the coefficient of z^i. |
| |
| // Multiplication of |in| by x^k thus just involves moving it up. |
| uint8_t remainder[block_bytes]; |
| DCHECK_LE(block_ec_bytes, block_bytes); |
| memset(remainder, 0, block_ec_bytes); |
| size_t block_data_bytes = block_bytes - block_ec_bytes; |
| // Reed-Solomon input is backwards. See section 7.5.2. |
| for (size_t i = 0; i < block_data_bytes; i++) { |
| remainder[block_ec_bytes + i] = in[block_data_bytes - 1 - i]; |
| } |
| |
| // Progressively eliminate the leading coefficient by subtracting some |
| // multiple of |generator| until we have a value smaller than |generator|. |
| for (size_t i = block_bytes - 1; i >= block_ec_bytes; i--) { |
| // The leading coefficient of |generator| is 1, so the multiple to |
| // subtract to eliminate the leading term of |remainder| is the value of |
| // that leading term. The polynomial ring is characteristic two, so |
| // subtraction is the same as addition, which is XOR. |
| for (size_t j = 0; j < block_ec_bytes; j++) { |
| remainder[i - block_ec_bytes + j] ^= GF28Mul(generator[j], remainder[i]); |
| } |
| } |
| |
| memmove(&out[0], &in[0], block_data_bytes); |
| // Remove the Reed-Solomon remainder again to match QR's convention. |
| for (size_t i = 0; i < block_ec_bytes; i++) { |
| out[block_data_bytes + i] = remainder[block_ec_bytes - 1 - i]; |
| } |
| } |
| |
| unsigned QRCodeGenerator::CountPenaltyPoints() const { |
| const int size = version_info_->size; |
| unsigned penalty = 0; |
| |
| // The spec penalises the pattern X.XXX.X with four unpainted tiles to |
| // the left or right. These are "finder-like" patterns. To catch them, a |
| // sliding window of 11 tiles is used. |
| static const unsigned k11Bits = 0x7ff; |
| static const unsigned kFinderLeft = 0b00001011101; |
| static const unsigned kFinderRight = 0b10111010000; |
| |
| // Count: |
| // * Horizontal runs of the same color, at least five tiles in a row. |
| // * The number of horizontal finder-like patterns. |
| // * Total number of painted tiles, which is used later. |
| unsigned current_run_length; |
| int current_color; |
| unsigned total_painted_tiles = 0; |
| unsigned window = 0; |
| |
| size_t i = 0; |
| for (int y = 0; y < size; y++) { |
| current_color = d_[i++] & 1; |
| current_run_length = 0; |
| window = current_color; |
| total_painted_tiles += current_color; |
| |
| for (int x = 1; x < size; x++) { |
| const int color = d_[i++] & 1; |
| |
| window = k11Bits & ((window << 1) | color); |
| if (window == kFinderLeft || window == kFinderRight) { |
| penalty += 40; |
| } |
| |
| total_painted_tiles += color; |
| |
| if (color == current_color) { |
| current_run_length++; |
| continue; |
| } |
| |
| if (current_run_length >= 5) { |
| penalty += current_run_length - 2; |
| } |
| current_run_length = 0; |
| current_color = color; |
| } |
| |
| if (current_run_length >= 5) { |
| penalty += current_run_length - 2; |
| } |
| |
| window = k11Bits & (window << 4); |
| if (window == kFinderRight) { |
| penalty += 40; |
| } |
| } |
| DCHECK_EQ(i, static_cast<size_t>(size * size)); |
| |
| // Count: |
| // * Vertical runs of the same color, at least five tiles in a row. |
| // * The number of vertical finder-like patterns. |
| for (int x = 0; x < size; x++) { |
| i = x; |
| current_run_length = 0; |
| current_color = d_[i] & 1; |
| i += size; |
| window = current_color; |
| |
| for (int y = 1; y < size; y++, i += size) { |
| const int color = d_[i] & 1; |
| window = k11Bits & ((window << 1) | color); |
| if (window == kFinderLeft || window == kFinderRight) { |
| penalty += 40; |
| } |
| |
| if (color == current_color) { |
| current_run_length++; |
| continue; |
| } |
| |
| if (current_run_length >= 5) { |
| penalty += current_run_length - 2; |
| } |
| current_run_length = 0; |
| current_color = color; |
| } |
| |
| if (current_run_length >= 5) { |
| penalty += current_run_length - 2; |
| } |
| |
| window = k11Bits & (window << 4); |
| if (window == kFinderRight) { |
| penalty += 40; |
| } |
| } |
| DCHECK_EQ(i, static_cast<size_t>(size * size + size - 1)); |
| |
| // Count 2x2 blocks of the same color. |
| i = 0; |
| for (int y = 0; y < size - 1; y++) { |
| for (int x = 0; x < size - 1; x++) { |
| const int color = d_[i++] & 1; |
| if ((d_[i + 1] & 1) == color && (d_[i + size] & 1) == color && |
| (d_[i + size + 1] & 1) == color) { |
| penalty += 3; |
| } |
| } |
| } |
| |
| // Each deviation of 5% away from 50%-painted costs five points. |
| DCHECK_LE(total_painted_tiles, static_cast<unsigned>(size) * size); |
| double painted_fraction = static_cast<double>(total_painted_tiles) / |
| (static_cast<double>(size) * size); |
| if (painted_fraction < 0.5) { |
| painted_fraction = 1.0 - painted_fraction; |
| } |
| const double deviation = (painted_fraction - 0.5) / 0.05; |
| penalty += 5 * static_cast<unsigned>(floor(deviation)); |
| |
| return penalty; |
| } |
| |
| // Segmentation |
| // |
| // The data in a QR code is a series of "segments". Each segment has a type, |
| // a length, and encoding rules. The set of bytes that can be encoded varies |
| // between segment types. A DIGIT segment can only encode '0'<=x<='9' but it |
| // encodes them more efficiently than ASCII. |
| // |
| // For the segment types that we support here, there is a super-set relation: |
| // BINARY can encode everything that ALPHANUM can, which can encode everything |
| // that DIGIT can. |
| // |
| // To perform segmentation we first need a function that returns the minimum |
| // segment type for a given byte. "Minimum" here is defined using the super-set |
| // relation, so DIGIT < ALPHANUM < BINARY. |
| |
| // static |
| QRCodeGenerator::SegmentType QRCodeGenerator::ClassifyByte(uint8_t byte) { |
| if ('0' <= byte && byte <= '9') { |
| return QRCodeGenerator::SegmentType::DIGIT; |
| } else if (byte < sizeof(kAlphanumValue) && kAlphanumValue[byte] != 0xff) { |
| return QRCodeGenerator::SegmentType::ALPHANUM; |
| } else { |
| return QRCodeGenerator::SegmentType::BINARY; |
| } |
| } |
| |
| // A "valid" segmentation is one where: |
| // |
| // 1. The sum of the segment lengths is equal to the input length. |
| // 2. No byte of input is in a segment that cannot encode it. |
| |
| // static |
| bool QRCodeGenerator::IsValidSegmentation(const std::vector<Segment>& segments, |
| base::span<const uint8_t> input) { |
| size_t offset = 0; |
| for (const auto& segment : segments) { |
| for (size_t i = offset; i < offset + segment.length; i++) { |
| const auto min_type = ClassifyByte(input[i]); |
| if (static_cast<int>(segment.type) < static_cast<int>(min_type)) { |
| return false; |
| } |
| } |
| offset += segment.length; |
| } |
| |
| return offset == input.size(); |
| } |
| |
| // Segmentation is the problem of finding a valid segmentation for an input and, |
| // preferably, one that is optimal. Segments have headers that take space so, |
| // for example, it's not optimal to use a DIGIT segment to encode a single digit |
| // in a span of otherwise BINARY data. |
| // |
| // To start, we define an additional invariant that we will always maintain: |
| // |
| // 3. No two consecutive segments have the same type. |
| // |
| // (I.e. consecutive segments with the same type must immediately be merged |
| // together.) |
| |
| bool QRCodeGenerator::NoSuperfluousSegments( |
| const std::vector<Segment>& segments) { |
| for (size_t i = 1; i < segments.size(); i++) { |
| if (segments[i - 1].type == segments[i].type) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Given a classification for each byte of an input, the "initial segmentation" |
| // is defined as a segmentation that puts each byte of the input into a segment |
| // of minimal type. |
| |
| // static |
| std::vector<QRCodeGenerator::Segment> QRCodeGenerator::InitialSegmentation( |
| base::span<const uint8_t> input) { |
| std::vector<Segment> segments; |
| absl::optional<Segment> current; |
| |
| for (const uint8_t b : input) { |
| const SegmentType type = ClassifyByte(b); |
| if (current.has_value() && type == current->type) { |
| current->length++; |
| } else { |
| if (current.has_value()) { |
| segments.push_back(*current); |
| } else { |
| current.emplace(); |
| } |
| |
| current->type = type; |
| current->length = 1; |
| } |
| } |
| |
| if (current.has_value()) { |
| segments.push_back(*current); |
| } |
| |
| DCHECK(IsValidSegmentation(segments, input)); |
| DCHECK(NoSuperfluousSegments(segments)); |
| |
| return segments; |
| } |
| |
| // In order to improve on the initial segmentation we need the ability to |
| // merge segments. Spans of segments are specified by (start, end) indexes where |
| // start is the index of the first segment of the span and end is the index of |
| // the last. |
| // |
| // |MergeSegments| replaces a span of segments with a given segment and returns |
| // the index of the replacement. |
| |
| // static |
| size_t QRCodeGenerator::MergeSegments(std::vector<Segment>* segments, |
| size_t start, |
| size_t end, |
| Segment replacement) { |
| DCHECK_LT(start, segments->size()); |
| DCHECK_LT(end, segments->size()); |
| DCHECK_LT(start, end); |
| |
| segments->at(start) = replacement; |
| segments->erase(segments->begin() + start + 1, segments->begin() + end + 1); |
| return start; |
| } |
| |
| // Segments should only be merged when the replacement is smaller than the |
| // separate segments. |MaybeMerge| will replace a span of segments with a |
| // single segment of |merged_type| if the merged form is equal or smaller than |
| // the span. It returns the index of the right-edge of the span, whether merged |
| // or not. |
| // |
| // Callers of this function must ensure that merging a span doesn't violate |
| // invariant three. |
| |
| // static |
| size_t QRCodeGenerator::MaybeMerge(VersionClass vclass, |
| std::vector<Segment>* segments, |
| size_t start, |
| size_t end, |
| SegmentType merged_type) { |
| auto s = base::span<const Segment>(&segments->at(start), (end - start) + 1); |
| const size_t unmerged_cost = SegmentBits(vclass, s); |
| const Segment merged = { |
| .type = merged_type, |
| .length = SegmentSpanLength(s), |
| }; |
| const size_t merged_cost = SegmentBits(vclass, merged); |
| |
| if (merged_cost <= unmerged_cost) { |
| return MergeSegments(segments, start, end, merged); |
| } |
| return end; |
| } |
| |
| // If there were only two classes then there would be an obvious solution for |
| // merging: for each transition to the smaller class, consider whether the |
| // overhead of the segment was worth the more compact encoding and promote the |
| // segment if not. |
| // |
| // There are three classes here but we deal with that by using the two-class |
| // solution twice. Runs of DIGIT and ALPHANUM segments are considered to be |
| // subproblems and solved using the two-class pattern. Then those runs are |
| // considered to be a single segment for the purpose of applying the two-class |
| // solution again for BINARY and DIGIT/ALPHANUM runs. |
| // |
| // It is not clear that is gives an optimal segmentation, but it does a pretty |
| // good job in linear time. ("Sort of" linear: since the segments are kept in |
| // a vector and moved down when merging, it's probably quadratic. But I |
| // suspect the fixed overhead of a linked list would make it slower in practice |
| // for the sizes of problems that we care about.) |
| // |
| // Since the size of a segment is monotonic with number of input bytes |
| // contained, and the edge cases (where an additional input element can cost |
| // different numbers of bits depending on the length mod 3 or 2) differ only by |
| // one bit, I _suspect_ that this algorithm might be optimal. It would be a fun |
| // formal verification problem had I a couple of months to spare. But I don't, |
| // so pressing on: |
| // |
| // First we define a function, |SegmentDigitAlphaSpan|, which performs the |
| // two-class solution on a span of segments that are all DIGIT or ALPHANUM |
| // segments. |
| |
| // static |
| size_t QRCodeGenerator::SegmentDigitAlphaSpan(VersionClass vclass, |
| std::vector<Segment>* segments, |
| size_t start, |
| size_t end) { |
| // |start| and |end| are the indexes of the first and last segment of the |
| // span. |
| DCHECK_LE(start, end); |
| for (size_t i = start; i <= end; i++) { |
| DCHECK(segments->at(i).type == SegmentType::DIGIT || |
| segments->at(i).type == SegmentType::ALPHANUM); |
| } |
| |
| // A single segment span is trivially optimal. |
| if (end - start == 0) { |
| return end; |
| } |
| |
| // Merging segments invalidates |end| (but not |start|). The distance from the |
| // end of |segments| to |end| is constant, however, and used to fix up |end| |
| // after merging. |
| const size_t distance_from_vector_end = segments->size() - end; |
| |
| for (size_t i = start + 1; i <= end; i++) { |
| // Find an ALPHANUM segment. |
| if (segments->at(i).type != SegmentType::ALPHANUM) { |
| continue; |
| } |
| |
| // |segments[i]| is ALPHANUM and, because of invariant three, |i-1| must |
| // be DIGIT. |
| DCHECK_EQ(segments->at(i - 1).type, SegmentType::DIGIT); |
| DCHECK_EQ(segments->at(i).type, SegmentType::ALPHANUM); |
| |
| // If there's another ALPHANUM segment at |i-2| then we must include it in |
| // any merge decisions in order to maintain invariant three. Thus figure |
| // out if segment |i-2| exists. |
| const bool have_left = i >= 2 && i - 2 >= start; |
| |
| if (!have_left) { |
| i = MaybeMerge(vclass, segments, i - 1, i, SegmentType::ALPHANUM); |
| } else { |
| DCHECK_EQ(segments->at(i - 2).type, SegmentType::ALPHANUM); |
| i = MaybeMerge(vclass, segments, i - 2, i, SegmentType::ALPHANUM); |
| } |
| |
| end = segments->size() - distance_from_vector_end; |
| } |
| |
| if (segments->at(end).type == SegmentType::DIGIT) { |
| // If the last segment was DIGIT then it won't have been considered for |
| // merging. So do that. |
| DCHECK_EQ(segments->at(end - 1).type, SegmentType::ALPHANUM); |
| DCHECK_EQ(segments->at(end).type, SegmentType::DIGIT); |
| return MaybeMerge(vclass, segments, end - 1, end, SegmentType::ALPHANUM); |
| } |
| |
| return end; |
| } |
| |
| // Having seen the two-class solution for the subproblem of DIGIT/ALPHANUM |
| // segments we use the same pattern to cover BINARY segments too. Spans of |
| // DIGIT/ALPHANUM segments are found and optimised using the previous function. |
| // Then those spans are treated like a single segment for the purposes of |
| // merging with BINARY segments. |
| |
| // static |
| std::vector<QRCodeGenerator::Segment> QRCodeGenerator::SegmentInput( |
| VersionClass vclass, |
| base::span<const uint8_t> input) { |
| std::vector<Segment> segments = InitialSegmentation(input); |
| |
| // Zero or one segments are trivially optimal. |
| if (segments.size() < 2) { |
| return segments; |
| } |
| |
| // digit_alpha_start_idx is the index of the start of the current span of |
| // DIGIT/ALPHANUM segments. |
| absl::optional<size_t> digit_alpha_start_idx; |
| |
| for (size_t i = 0; i < segments.size(); i++) { |
| // Invariants must always be maintained. |
| DCHECK(IsValidSegmentation(segments, input)); |
| DCHECK(NoSuperfluousSegments(segments)); |
| |
| const bool is_digit_alpha = segments[i].type == SegmentType::DIGIT || |
| segments[i].type == SegmentType::ALPHANUM; |
| if (is_digit_alpha) { |
| if (!digit_alpha_start_idx) { |
| digit_alpha_start_idx = i; |
| } |
| |
| continue; |
| } |
| |
| if (!digit_alpha_start_idx) { |
| continue; |
| } |
| |
| // |segment[i]| is BINARY and |*digit_alpha_start_idx| to i-1 are all |
| // DIGIT/ALPHANUM segments. |
| DCHECK_EQ(segments[i].type, SegmentType::BINARY); |
| |
| // Optimise the span of DIGIT/ALPHANUM segments. This returns the index of |
| // the right edge of the span after merging, i.e. the translation of |i-1|. |
| // Thus |i| is adjusted to be that plus one. |
| i = SegmentDigitAlphaSpan(vclass, &segments, *digit_alpha_start_idx, |
| i - 1) + |
| 1; |
| |
| // Is there also a BINARY segment on the left of the DIGIT/ALPHANUM span? If |
| // so it must be included in merge decisions in order to maintain invariant |
| // three. |
| const bool has_left = *digit_alpha_start_idx != 0; |
| DCHECK(!has_left || |
| segments[*digit_alpha_start_idx - 1].type == SegmentType::BINARY); |
| if (!has_left) { |
| i = MaybeMerge(vclass, &segments, *digit_alpha_start_idx, i, |
| SegmentType::BINARY); |
| } else { |
| i = MaybeMerge(vclass, &segments, *digit_alpha_start_idx - 1, i, |
| SegmentType::BINARY); |
| } |
| |
| digit_alpha_start_idx.reset(); |
| } |
| |
| if (digit_alpha_start_idx) { |
| // There was a trailing span of DIGIT/ALPHANUM segments that wasn't |
| // considered for merging. So we do that now. |
| SegmentDigitAlphaSpan(vclass, &segments, *digit_alpha_start_idx, |
| segments.size() - 1); |
| if (*digit_alpha_start_idx != 0) { |
| DCHECK_EQ(segments[*digit_alpha_start_idx - 1].type, SegmentType::BINARY); |
| MaybeMerge(vclass, &segments, *digit_alpha_start_idx - 1, |
| segments.size() - 1, SegmentType::BINARY); |
| } |
| } |
| |
| return segments; |
| } |