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 "imgdecode/image_load.h"
17#include "gfx_utils/file.h"
18#include "gfx_utils/graphic_log.h"
19#include "gfx_utils/mem_api.h"
20
21namespace {
22const uint8_t BITMAP_ZIP_LEN = 3;
23const uint8_t BITMAP_MID_BIT = 1;
24const uint8_t BITMAP_LOW_BIT = 2;
25const uint32_t BITMAP_ZIP24_FLAG = 0x456789;
26const uint32_t BITMAP_ZIP_FLAG = 0x23456789;
27const uint32_t BITMAP_ALPHA_MASK = 0xFF000000;
28const uint32_t BITMAP_MAXCON_PIXNUM = 0xCB100;
29const uint32_t MOVE_HIGH = 16;
30const uint32_t MOVE_LOW = 8;
31} // namespace
32
33namespace OHOS {
34bool ImageLoad::CreateImage(ImageInfo& imageInfo)
35{
36    uint32_t bytePerPixel = 4;
37    ImageHeader& imageHeader = imageInfo.header;
38
39    switch (imageHeader.colorMode) {
40        case ARGB8888:
41            bytePerPixel = 4; // 4 bytes per pixel
42            break;
43        case RGB888:
44            bytePerPixel = 3; // 3 bytes per pixel
45            break;
46        case RGB565:
47            bytePerPixel = 2; // 2 bytes per pixel
48            break;
49        default:
50            GRAPHIC_LOGE("CreateImage invalid colorMode.");
51            return false;
52    }
53
54    imageInfo.dataSize = imageHeader.width * imageHeader.height * bytePerPixel;
55    imageInfo.data = static_cast<uint8_t*>(ImageCacheMalloc(imageInfo));
56    if (imageInfo.data == nullptr) {
57        GRAPHIC_LOGE("ImageCacheMalloc error.");
58        return false;
59    }
60
61    return true;
62}
63
64bool ImageLoad::UncompressImageInZip(ImageInfo& imageInfo, uint8_t* buffer, uint32_t size)
65{
66    if (!CreateImage(imageInfo)) {
67        GRAPHIC_LOGE("Create image error.");
68        return false;
69    }
70
71    if (imageInfo.header.colorMode == RGB888) {
72        return Unzip24Image(buffer, size, imageInfo);
73    } else {
74        return UnzipImage(buffer, size, imageInfo);
75    }
76}
77
78bool ImageLoad::UnzipImage(uint8_t* imageBuffer, uint32_t size, ImageInfo& imageInfo)
79{
80    if ((imageBuffer == nullptr) || (size == 0)) {
81        GRAPHIC_LOGE("imageHeader is null.");
82        return false;
83    }
84
85    uint32_t* source = reinterpret_cast<uint32_t*>(imageBuffer);
86    uint32_t* sourceEnd = reinterpret_cast<uint32_t*>(imageBuffer + size);
87    uint32_t* dest = nullptr;
88    uint32_t* destEnd = nullptr;
89
90    dest = reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(imageInfo.data));
91    destEnd = reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(imageInfo.data) + imageInfo.dataSize);
92
93    while ((source < sourceEnd) && (dest < destEnd)) {
94        if (*source != BITMAP_ZIP_FLAG) {
95            *dest++ = *source++;
96        } else {
97            source++;
98            uint32_t value = *source++;
99            uint32_t count = *source++;
100            if (destEnd < count + dest) {
101                break;
102            }
103
104            while (count--) {
105                *dest++ = value;
106            }
107        }
108    }
109
110    if (dest == destEnd) {
111        return true;
112    }
113    ImageCacheFree(imageInfo);
114    imageInfo.data = nullptr;
115    return false;
116}
117
118bool ImageLoad::Unzip24Image(uint8_t* imageBuffer, uint32_t size, ImageInfo& imageInfo)
119{
120    if ((imageBuffer == nullptr) || (size == 0)) {
121        GRAPHIC_LOGE("imageHeader is null.");
122        return false;
123    }
124
125    uint8_t* source = reinterpret_cast<uint8_t*>(imageBuffer);
126    uint8_t* sourceEnd = reinterpret_cast<uint8_t*>(imageBuffer + size);
127    uint32_t* dest = reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(imageInfo.data));
128    uint32_t* destEnd = reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(imageInfo.data) + imageInfo.dataSize);
129    while ((source < sourceEnd) && (dest < destEnd)) {
130        // Little endian
131        uint32_t value = ((*source)) + (*(source + BITMAP_MID_BIT) << MOVE_LOW)
132                         + (*(source + BITMAP_LOW_BIT) << MOVE_HIGH);
133        source = source + BITMAP_ZIP_LEN;
134        if (value != BITMAP_ZIP24_FLAG) {
135            *dest = value | BITMAP_ALPHA_MASK;
136            dest++;
137        } else {
138            value = ((*source)) + (*(source + BITMAP_MID_BIT) << MOVE_LOW) + (*(source + BITMAP_LOW_BIT) << MOVE_HIGH);
139            source = source + BITMAP_ZIP_LEN;
140
141            uint32_t count = ((*source)) + (*(source + BITMAP_MID_BIT) << MOVE_LOW)
142                             + (*(source + BITMAP_LOW_BIT) << MOVE_HIGH);
143            source = source + BITMAP_ZIP_LEN;
144
145            if (count > BITMAP_MAXCON_PIXNUM) {
146                *dest = BITMAP_ZIP24_FLAG | BITMAP_ALPHA_MASK;
147                dest++;
148                *dest = value | BITMAP_ALPHA_MASK;
149                dest++;
150                *dest = count | BITMAP_ALPHA_MASK;
151                dest++;
152                continue;
153            }
154            if (static_cast<uintptr_t>(destEnd - dest) < static_cast<uintptr_t>(count)) {
155                break;
156            }
157            while (count--) {
158                *dest = value | BITMAP_ALPHA_MASK;
159                dest++;
160            }
161        }
162    }
163
164    if (dest == destEnd) {
165        return true;
166    }
167    ImageCacheFree(imageInfo);
168    imageInfo.data = nullptr;
169    return false;
170}
171
172bool ImageLoad::UnZip2ImageInfo(ImageInfo& imageInfo, uint8_t* buffer, uint32_t size)
173{
174    switch (imageInfo.header.compressMode) {
175        case COMPRESS_MODE__ZIP_ALG:
176            return UncompressImageInZip(imageInfo, buffer, size);
177        default:
178            return false;
179    }
180}
181
182bool ImageLoad::GetImageInfo(int32_t fd, uint32_t size, ImageInfo& imageInfo)
183{
184    if (size == 0) {
185        return false;
186    }
187
188    uint8_t* buffer = reinterpret_cast<uint8_t*>(UIMalloc(size));
189    if (buffer == nullptr) {
190        return false;
191    }
192
193    if (read(fd, buffer, size) != static_cast<int32_t>(size)) {
194        UIFree(buffer);
195        GRAPHIC_LOGE("SeekImageFile error.");
196        return false;
197    }
198    bool ret = UnZip2ImageInfo(imageInfo, buffer, size);
199    UIFree(buffer);
200    return ret;
201}
202} // namespace OHOS
203