1/*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "components/ui_qrcode.h"
17#include "qrcodegen.hpp"
18#include "gfx_utils/graphic_log.h"
19#include "securec.h"
20
21using qrcodegen::QrCode;
22namespace OHOS {
23UIQrcode::UIQrcode()
24    : width_(0), needDraw_(false), backgroundColor_(Color::White()), qrColor_(Color::Black()), qrcodeVal_(nullptr)
25{
26    style_ = &(StyleDefault::GetBackgroundTransparentStyle());
27    imageInfo_ = {{0}};
28}
29
30UIQrcode::~UIQrcode()
31{
32    if (qrcodeVal_ != nullptr) {
33        UIFree(qrcodeVal_);
34        qrcodeVal_ = nullptr;
35    }
36
37    if (imageInfo_.data != nullptr) {
38        ImageCacheFree(imageInfo_);
39        imageInfo_.data = nullptr;
40    }
41}
42
43void UIQrcode::SetQrcodeInfo(const char* val, ColorType backgroundColor, ColorType qrColor)
44{
45    if (val == nullptr) {
46        GRAPHIC_LOGE("UIQrcode::SetQrcodeInfo val is null!\n");
47        return;
48    }
49    uint32_t length = static_cast<uint32_t>(strlen(val));
50    if ((length > QRCODE_VAL_MAX) || (length == 0)) {
51        GRAPHIC_LOGE("UIQrcode::SetQrcodeInfo val length is equal 0 or greater than QRCODE_VAL_MAX!\n");
52        return;
53    }
54    backgroundColor_ = backgroundColor;
55    qrColor_ = qrColor;
56    SetQrcodeVal(val, length);
57    RefreshQrcode();
58}
59
60void UIQrcode::RefreshQrcode()
61{
62    Invalidate();
63    if (!needDraw_) {
64        needDraw_ = true;
65    }
66}
67
68void UIQrcode::SetWidth(int16_t width)
69{
70    if (GetWidth() != width) {
71        UIView::SetWidth(width);
72        RefreshQrcode();
73    }
74}
75
76void UIQrcode::SetHeight(int16_t height)
77{
78    if (GetHeight() != height) {
79        UIView::SetHeight(height);
80        RefreshQrcode();
81    }
82}
83
84void UIQrcode::ReMeasure()
85{
86    if (!needDraw_) {
87        return;
88    }
89    needDraw_ = false;
90    if (qrcodeVal_ == nullptr) {
91        GRAPHIC_LOGE("UIQrcode::ReMeasure qrcodeVal_ is null!\n");
92        return;
93    }
94    QrCode qr = QrCode::encodeText(qrcodeVal_, QrCode::Ecc::LOW);
95    SetImageInfo(qr);
96    SetSrc(&imageInfo_);
97}
98
99void UIQrcode::SetQrcodeVal(const char* qrcodeVal, uint32_t length)
100{
101    if (qrcodeVal_ != nullptr) {
102        UIFree(qrcodeVal_);
103        qrcodeVal_ = nullptr;
104    }
105
106    uint32_t len = static_cast<uint32_t>(length + 1);
107    qrcodeVal_ = static_cast<char*>(UIMalloc(len));
108    if (qrcodeVal_ != nullptr) {
109        if (memcpy_s(qrcodeVal_, len, qrcodeVal, len) != EOK) {
110            UIFree(reinterpret_cast<void*>(qrcodeVal_));
111            qrcodeVal_ = nullptr;
112        }
113    }
114}
115
116void UIQrcode::SetImageInfo(qrcodegen::QrCode& qrcode)
117{
118    int16_t width = GetWidth();
119    int16_t height = GetHeight();
120    width_ = (width >= height) ? height : width;
121    if (width_ < qrcode.getSize()) {
122        GRAPHIC_LOGE("UIQrcode::SetImageInfo width is less than the minimum qrcode width!\n");
123        return;
124    }
125    imageInfo_.header.width = width;
126    imageInfo_.header.height = height;
127    imageInfo_.header.colorMode = ARGB8888;
128    width = UI_ALIGN_UP(width);
129    imageInfo_.dataSize = width * imageInfo_.header.height * QRCODE_FACTOR_NUM;
130    if (imageInfo_.data != nullptr) {
131        ImageCacheFree(imageInfo_);
132        imageInfo_.data = nullptr;
133    }
134    imageInfo_.data = reinterpret_cast<uint8_t*>(ImageCacheMalloc(imageInfo_));
135    if (imageInfo_.data == nullptr) {
136        GRAPHIC_LOGE("UIQrcode::SetImageInfo imageInfo_.data is null!\n");
137        return;
138    }
139    GenerateQrCode(qrcode);
140}
141
142void UIQrcode::GenerateQrCode(qrcodegen::QrCode& qrcode)
143{
144    FillQrCodeBackgroundColor();
145
146    FillQrCodeColor(qrcode);
147}
148
149void UIQrcode::FillQrCodeColor(qrcodegen::QrCode& qrcode)
150{
151    int32_t qrWidth = qrcode.getSize();
152    if (qrWidth <= 0) {
153        GRAPHIC_LOGE("UIQrcode::FillQrCodeColor generated qrcode size is less or equal 0!\n");
154        return;
155    }
156    int16_t width = imageInfo_.header.width;
157    int16_t height = imageInfo_.header.height;
158    uint16_t outFilePixelPrescaler = width_ / qrWidth;
159    int32_t offsetX = (width - outFilePixelPrescaler * qrWidth) / 2;    // 2: half
160    int32_t offsetY = (height - outFilePixelPrescaler * qrWidth) / 2;   // 2: half
161
162    width = UI_ALIGN_UP(width);
163    uint8_t* destData = nullptr;
164    int64_t oneLinePixel = width * QRCODE_FACTOR_NUM * outFilePixelPrescaler;
165    int64_t oneLineOffsetPixel = (offsetY * width * QRCODE_FACTOR_NUM) + (offsetX * QRCODE_FACTOR_NUM);
166    for (int32_t y = 0; y < qrWidth; ++y) {
167        destData = const_cast<uint8_t*>(imageInfo_.data) + (oneLinePixel * y) + oneLineOffsetPixel;
168        for (int32_t x = 0; x < qrWidth; ++x) {
169            if (qrcode.getModule(x, y)) {
170                GetDestData(destData, outFilePixelPrescaler);
171            }
172            destData += QRCODE_FACTOR_NUM * outFilePixelPrescaler;
173        }
174    }
175}
176
177void UIQrcode::FillQrCodeBackgroundColor()
178{
179    uint8_t* initColorData = const_cast<uint8_t*>(imageInfo_.data);
180    *(initColorData + 0) = backgroundColor_.blue;  // 0: B channel
181    *(initColorData + 1) = backgroundColor_.green; // 1: G channel
182    *(initColorData + 2) = backgroundColor_.red;   // 2: R channel
183    *(initColorData + 3) = OPA_OPAQUE;             // 3: Alpha channel
184
185    uint32_t width = imageInfo_.header.width;
186    width = UI_ALIGN_UP(width);
187
188    uint8_t* tempColorData = initColorData;
189    for (int16_t col = 1; col < width; ++col) {
190        initColorData += QRCODE_FACTOR_NUM;
191        if (memcpy_s(initColorData, QRCODE_FACTOR_NUM, tempColorData, QRCODE_FACTOR_NUM) != EOK) {
192            GRAPHIC_LOGE("UIQrcode::FillQrCodeBackgroundColor memcpy_s failed!\n");
193            return;
194        }
195    }
196    initColorData = tempColorData;
197    int32_t deltaWidth = QRCODE_FACTOR_NUM * width;
198    for (int16_t row = 1; row < imageInfo_.header.height; ++row) {
199        initColorData += deltaWidth;
200        if (memcpy_s(initColorData, deltaWidth, tempColorData, deltaWidth) != EOK) {
201            GRAPHIC_LOGE("UIQrcode::FillQrCodeBackgroundColor memcpy_s failed!\n");
202            return;
203        }
204    }
205}
206
207void UIQrcode::GetDestData(uint8_t* destData, int32_t outFilePixelPrescaler)
208{
209    uint32_t width = imageInfo_.header.width;
210    width = UI_ALIGN_UP(width);
211
212    for (int32_t x = 0; x < outFilePixelPrescaler; ++x) {
213        uint8_t* tempData = destData + width * QRCODE_FACTOR_NUM * x;
214        for (int32_t y = 0; y < outFilePixelPrescaler; ++y) {
215            *(tempData + 0) = qrColor_.blue;  // 0: B channel
216            *(tempData + 1) = qrColor_.green; // 1: G channel
217            *(tempData + 2) = qrColor_.red;   // 2: R channel
218            *(tempData + 3) = OPA_OPAQUE;     // 3: Alpha channel
219            tempData += QRCODE_FACTOR_NUM;
220        }
221    }
222}
223} // namespace OHOS
224