* Copyright (c) 2022-2026 Huawei Device Co., Ltd.
* 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.
*/
#include "common/qrcodegen/qrcode_mask.h"
#include "securec.h"
#include <cmath>
#include "common/qrcodegen/qrcode_generator.h"
#include "common/qrcodegen/qrcode_version.h"
#include "common/qrcodegen/qrcode_item.h"
#define QR_MASK_NUM (8)
#define N1 (3)
#define N2 (3)
#define N3 (40)
#define N4 (10)
constexpr int32_t QRCODE_INT_MAX = 2147483647;
typedef int32_t (*QrcodeMaskExpressionFunc)(int32_t x, int32_t y);
static int32_t QrcodeMaskCopyWithExpression(int32_t width, uint8_t *src, uint8_t *dest, QrcodeMaskExpressionFunc exp)
{
int32_t blackCount = 0;
if ((src == nullptr) || (dest == nullptr)) {
return blackCount;
}
for (int32_t y = 0; y < width; y++) {
for (int32_t x = 0; x < width; x++) {
if (*src & 0x80) {
*dest = *src;
} else {
*dest = *src ^ ((exp(x, y) == 0) ? 1U : 0U);
}
blackCount += (int)(*dest & 1);
src++;
dest++;
}
}
return blackCount;
}
static int32_t QrcodeMaskFormatInformation(int32_t width, uint8_t *data, int32_t mask)
{
uint32_t i = 0;
int32_t blackCount = 0;
uint8_t value = 0;
if (data == nullptr) {
return blackCount;
}
uint32_t format = QrcodeVersionGetFormatInfo(mask);
for (i = 0; i < QR_BIT_EIGHT; i++) {
if (format & 1) {
blackCount += QR_BIT_TWO;
value = 0x85;
} else {
value = 0x84;
}
data[width * QR_BIT_EIGHT + width - 1 - i] = value;
if (i < QR_BIT_SIX) {
data[width * i + SET_BIT_EIGHT] = value;
} else {
data[width * (i + 1) + SET_BIT_EIGHT] = value;
}
format = format >> 1;
}
for (i = 0; i < QR_BIT_SEVEN; i++) {
if (format & 1) {
blackCount += QR_BIT_TWO;
value = 0x85;
} else {
value = 0x84;
}
data[width * (width - QR_BIT_SEVEN + i) + QR_BIT_EIGHT] = value;
if (i == 0) {
data[width * SET_BIT_EIGHT + SET_BIT_SEVEN] = value;
} else {
data[width * SET_BIT_EIGHT + SET_BIT_SIX - i] = value;
}
format = format >> 1;
}
return blackCount;
}
static int32_t QrcodeMaskExpression0(int32_t x, int32_t y)
{
return ((uint32_t)(x + y)) & 1;
}
static int32_t QrcodeMaskExpression1(int32_t x, int32_t y)
{
return ((uint32_t)y) & 1;
}
static int32_t QrcodeMaskExpression2(int32_t x, int32_t y)
{
return x % QR_BIT_THREE;
}
static int32_t QrcodeMaskExpression3(int32_t x, int32_t y)
{
return (((uint32_t)x) + ((uint32_t)y)) % QR_BIT_THREE;
}
static int32_t QrcodeMaskExpression4(int32_t x, int32_t y)
{
return ((((uint32_t)y) / QR_BIT_TWO) + (((uint32_t)x) / QR_BIT_THREE)) & 1U;
}
static int32_t QrcodeMaskExpression5(int32_t x, int32_t y)
{
return ((((uint32_t)x) * ((uint32_t)y)) & 1U) + (((uint32_t)x) * ((uint32_t)y)) % QR_BIT_THREE;
}
static int32_t QrcodeMaskExpression6(int32_t x, int32_t y)
{
return (((((uint32_t)x) * ((uint32_t)y)) & 1U) + (((uint32_t)x) * ((uint32_t)y)) % QR_BIT_THREE) & 1U;
}
static int32_t QrcodeMaskExpression7(int32_t x, int32_t y)
{
return (((((uint32_t)x) * ((uint32_t)y)) % QR_BIT_THREE) + ((((uint32_t)x) + ((uint32_t)y)) & 1U)) & 1U;
}
static QrcodeMaskExpressionFunc g_qrcodeMaskMakers[QR_MASK_NUM] = {
QrcodeMaskExpression0, QrcodeMaskExpression1, QrcodeMaskExpression2, QrcodeMaskExpression3,
QrcodeMaskExpression4, QrcodeMaskExpression5, QrcodeMaskExpression6, QrcodeMaskExpression7
};
static int32_t QrcodeMaskGetRoleCalcN1N3(uint8_t *buf, int32_t bufLen)
{
int32_t i = 0;
int32_t score = 0;
int32_t continueCount = 1;
int32_t flag = 0;
const uint8_t pattenr1[] = { 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0 };
const uint8_t pattenr2[] = { 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1 };
if (buf == nullptr) {
return -1;
}
for (i = 1; i < bufLen; i++) {
if (buf[i - 1] == buf[i]) {
continueCount++;
} else {
if (continueCount >= QR_BIT_FIVE) {
score += continueCount + N1 - QR_BIT_FIVE;
}
continueCount = 1;
}
}
if (continueCount >= QR_BIT_FIVE) {
score += continueCount + N1 - QR_BIT_FIVE;
}
for (i = 0, flag = 0; i < bufLen; ++i) {
if (pattenr1[flag] == buf[i]) {
if (sizeof(pattenr1) == flag + 1) {
score += N3;
} else {
flag++;
}
} else {
flag = 0;
}
}
for (i = 0, flag = 0; i < bufLen; ++i) {
if (pattenr2[flag] == buf[i]) {
if (sizeof(pattenr2) == (flag + 1)) {
score += N3;
} else {
flag++;
}
} else {
flag = 0;
}
}
return score;
}
static uint8_t QrcodeMaskGetColor(uint8_t *data, int32_t width, int32_t x, int32_t y)
{
if (data == nullptr) {
return 0;
}
uint8_t c = data[x + y * width];
if (c & 1) {
return 1;
}
return 0;
}
static int32_t QrcodeMaskGetRoleCalcN2(int32_t width, uint8_t *data)
{
int32_t score = 0;
uint8_t *p = data;
for (int32_t y = 0; y < width - 1; y++) {
for (int32_t x = 0; x < width - 1; x++) {
uint8_t color11 = QrcodeMaskGetColor(p, width, x, y);
uint8_t color12 = QrcodeMaskGetColor(p, width, x + 1, y);
uint8_t color21 = QrcodeMaskGetColor(p, width, x, y + 1);
uint8_t color22 = QrcodeMaskGetColor(p, width, x + 1, y + 1);
if (color11 == color12 && color12 == color21 && color21 == color22) {
score += N2;
}
}
}
return score;
}
static int32_t QrcodeMaskGetBuffer(uint8_t *buff, uint8_t *src, int32_t width, int32_t isCol)
{
uint8_t *p = src;
if ((p == nullptr) || (buff == nullptr)) {
return 0;
}
int32_t moveLen = (isCol == 0) ? 1 : width;
if (memset_s(buff, width, 0, width) != 0) {
return 0;
}
for (int32_t i = 0; i < width; i++) {
if (p[i * moveLen] & 1) {
buff[i] = 1;
} else {
buff[i] = 0;
}
}
return width;
}
static int32_t QrcodeMaskGetScore(int32_t width, uint8_t *data)
{
int32_t x = 0;
int32_t y = 0;
int32_t score = 0;
uint8_t buffer[QRCODE_VERSION_WIDTH_MAX + 1] = { 0 };
if (data == nullptr) {
return 0;
}
score += QrcodeMaskGetRoleCalcN2(width, data);
for (y = 0; y < width; y++) {
if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != 0) {
return 0;
}
QrcodeMaskGetBuffer(buffer, data + y * width, width, 0);
score += QrcodeMaskGetRoleCalcN1N3(buffer, width);
}
for (x = 0; x < width; x++) {
if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != 0) {
return 0;
}
QrcodeMaskGetBuffer(buffer, data + x, width, 1);
int32_t bufLen = (width > (QRCODE_VERSION_WIDTH_MAX + 1)) ? (QRCODE_VERSION_WIDTH_MAX + 1) : width;
score += QrcodeMaskGetRoleCalcN1N3(buffer, bufLen);
}
return score;
}
uint8_t *QrcodeMaskFindMask(int32_t width, uint8_t *data)
{
int32_t total = width * width;
uint8_t *curMask = (uint8_t *)QrcodeMalloc(total);
if (curMask == nullptr) {
return nullptr;
}
uint8_t *bestMask = nullptr;
int32_t minScore = QRCODE_INT_MAX;
for (int32_t i = 0; i < QR_MASK_NUM; i++) {
int32_t score = 0;
int32_t blackCounts = QrcodeMaskCopyWithExpression(width, data, curMask, g_qrcodeMaskMakers[i]);
blackCounts += QrcodeMaskFormatInformation(width, curMask, i);
int32_t percent = (200 * blackCounts + total) / total / 2;
score = ((percent > 50) ? (percent - 50) : (50 - percent)) / 5 * N4;
score += QrcodeMaskGetScore(width, curMask);
if (score < minScore) {
minScore = score;
if (bestMask != nullptr) {
QrcodeFree(bestMask);
}
bestMask = curMask;
curMask = (unsigned char *)QrcodeMalloc(total);
if (curMask == nullptr) {
break;
}
}
}
if (curMask != nullptr) {
QrcodeFree(curMask);
}
return bestMask;
}