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