1/*
2 * Copyright (c) 2020-2022 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 "common/image.h"
17#include "common/image_decode_ability.h"
18#include "draw/draw_image.h"
19#include "gfx_utils/file.h"
20#include "gfx_utils/graphic_log.h"
21#include "imgdecode/cache_manager.h"
22#if ENABLE_JPEG
23#include "jpeglib.h"
24#endif
25#if ENABLE_PNG
26#include "png.h"
27#endif
28#include "securec.h"
29
30namespace OHOS {
31Image::Image() : imageInfo_(nullptr), path_(nullptr), srcType_(IMG_SRC_UNKNOWN), mallocFlag_(false) {}
32
33Image::~Image()
34{
35    if (srcType_ == IMG_SRC_FILE) {
36        CacheManager::GetInstance().Close(path_);
37    }
38
39    ReInitImageInfo(nullptr, false);
40
41    if (path_ != nullptr) {
42        UIFree(reinterpret_cast<void*>(path_));
43        path_ = nullptr;
44    }
45    srcType_ = IMG_SRC_UNKNOWN;
46}
47
48void Image::GetHeader(ImageHeader& header) const
49{
50    if ((srcType_ == IMG_SRC_VARIABLE) && (imageInfo_ != nullptr)) {
51        header = imageInfo_->header;
52    } else if ((srcType_ == IMG_SRC_FILE) && (path_ != nullptr)) {
53        CacheManager::GetInstance().GetImageHeader(path_, header);
54    }
55}
56
57#if ENABLE_JPEG || ENABLE_PNG
58OHOS::Image::ImageType Image::CheckImgType(const char* src)
59{
60    char buf[IMG_BYTES_TO_CHECK] = {0};
61#ifdef _WIN32
62    int32_t fd = open(src, O_RDONLY | O_BINARY);
63#else
64    int32_t fd = open(src, O_RDONLY);
65#endif
66    if (fd < 0) {
67        GRAPHIC_LOGE("can't open %s\n", src);
68        return IMG_UNKNOWN;
69    }
70    if (read(fd, buf, IMG_BYTES_TO_CHECK) != IMG_BYTES_TO_CHECK) {
71        close(fd);
72        return IMG_UNKNOWN;
73    }
74    close(fd);
75#if ENABLE_PNG
76    if (!png_sig_cmp(reinterpret_cast<png_const_bytep>(buf), 0, IMG_BYTES_TO_CHECK)) {
77        return IMG_PNG;
78    }
79#endif
80#if ENABLE_JPEG
81    // 0xFF 0xD8: JPEG file's header
82    if ((static_cast<uint8_t>(buf[0]) == 0xFF) && (static_cast<uint8_t>(buf[1]) == 0xD8)) {
83        return IMG_JPEG;
84    }
85#endif
86    if ((static_cast<uint8_t>(buf[0]) == 0x47) && (static_cast<uint8_t>(buf[1]) == 0x49) &&
87          (static_cast<uint8_t>(buf[2]) == 0x46)) { // 2: array index of GIF file's header
88        return IMG_GIF;
89    }
90    return IMG_UNKNOWN;
91}
92#endif
93
94bool Image::SetStandardSrc(const char* src)
95{
96    if (src == nullptr) {
97        return false;
98    }
99    srcType_ = IMG_SRC_UNKNOWN;
100
101    const char* ptr = strrchr(src, '.');
102    if (ptr == nullptr) {
103        return false;
104    }
105
106#if ENABLE_JPEG || ENABLE_PNG
107    ImageType imageType = CheckImgType(src);
108#if ENABLE_PNG
109    if (imageType == IMG_PNG) {
110        return SetPNGSrc(src);
111    }
112#endif
113#if ENABLE_JPEG
114    if (imageType == IMG_JPEG) {
115        return SetJPEGSrc(src);
116    }
117#endif
118#endif
119
120    size_t strLen = strlen(src) + 1;
121    char* imagePath = static_cast<char*>(UIMalloc(static_cast<uint32_t>(strLen)));
122    if (imagePath == nullptr) {
123        return false;
124    }
125
126    if (strcpy_s(imagePath, strLen, src) != EOK) {
127        UIFree(reinterpret_cast<void*>(imagePath));
128        imagePath = nullptr;
129        return false;
130    }
131    path_ = imagePath;
132    srcType_ = IMG_SRC_FILE;
133    return true;
134}
135
136bool Image::SetLiteSrc(const char* src)
137{
138    if (src == nullptr) {
139        return false;
140    }
141    srcType_ = IMG_SRC_UNKNOWN;
142
143    const char* ptr = strrchr(src, '.');
144    if (ptr == nullptr) {
145        return false;
146    }
147
148    const char* suffixName = ".bin";
149    size_t strLen = strlen(src) + strlen(suffixName) + 1;
150    char* imagePath = static_cast<char*>(UIMalloc(static_cast<uint32_t>(strLen)));
151    if (imagePath == nullptr) {
152        return false;
153    }
154    if (IsImgValid(ptr)) {
155        if (memcpy_s(imagePath, strLen, src, strLen) != EOK) {
156            UIFree(reinterpret_cast<void*>(imagePath));
157            imagePath = nullptr;
158            return false;
159        }
160        if (strcat_s(imagePath, strLen, suffixName) != EOK) {        // The format is xxx.xxx.bin
161            UIFree(reinterpret_cast<void*>(imagePath));
162            imagePath = nullptr;
163            return false;
164        }
165        if (access(imagePath, F_OK) != EOK) {                        // Check whether the xxx.xxx.bin file exists
166            if (memcpy_s(imagePath, strLen, src, strLen) != EOK) {
167                UIFree(reinterpret_cast<void*>(imagePath));
168                imagePath = nullptr;
169                return false;
170            }
171            (ptr - src + imagePath)[0] = '\0'; // remove suffix
172            if (strcat_s(imagePath, strLen, suffixName) != EOK) {    // The format is xxx.bin
173                UIFree(reinterpret_cast<void*>(imagePath));
174                imagePath = nullptr;
175                return false;
176            }
177        }
178    } else {
179        if (memcpy_s(imagePath, strLen, src, strLen) != EOK) {
180            UIFree(reinterpret_cast<void*>(imagePath));
181            imagePath = nullptr;
182            return false;
183        }
184    }
185    path_ = imagePath;
186    srcType_ = IMG_SRC_FILE;
187    return true;
188}
189
190bool Image::SetSrc(const char* src)
191{
192    if (path_ != nullptr) {
193        UIFree(reinterpret_cast<void*>(path_));
194        path_ = nullptr;
195    }
196
197    if (src != nullptr) {
198        uint32_t imageType = ImageDecodeAbility::GetInstance().GetImageDecodeAbility();
199        if (((imageType & IMG_SUPPORT_JPEG) == IMG_SUPPORT_JPEG) ||
200            ((imageType & IMG_SUPPORT_PNG) == IMG_SUPPORT_PNG)) {
201            return SetStandardSrc(src);
202        }
203        return SetLiteSrc(src);
204    }
205    srcType_ = IMG_SRC_UNKNOWN;
206    return true;
207}
208
209bool Image::SetSrc(const ImageInfo* src)
210{
211    ReInitImageInfo(nullptr, false);
212    srcType_ = IMG_SRC_UNKNOWN;
213    imageInfo_ = nullptr;
214
215    if (src != nullptr) {
216        imageInfo_ = static_cast<ImageInfo*>(UIMalloc(static_cast<uint32_t>(sizeof(ImageInfo))));
217        if (imageInfo_ == nullptr) {
218            return false;
219        }
220
221        if (memcpy_s(const_cast<ImageInfo*>(imageInfo_), sizeof(ImageInfo), src, sizeof(ImageInfo)) != EOK) {
222            return false;
223        }
224
225        srcType_ = IMG_SRC_VARIABLE;
226    }
227
228    return true;
229}
230
231bool Image::PreParse(const char *src)
232{
233    if (src == nullptr) {
234        return false;
235    }
236    const char* ptr = strrchr(src, '.');
237    if (ptr == nullptr) {
238        srcType_ = IMG_SRC_UNKNOWN;
239        return false;
240    }
241    if (path_ != nullptr) {
242        UIFree(reinterpret_cast<void*>(path_));
243    }
244    size_t strLen = strlen(src) + 1;
245    char* path = static_cast<char*>(UIMalloc(static_cast<uint32_t>(strLen)));
246    if (strcpy_s(path, strLen, src) != EOK) {
247        UIFree(reinterpret_cast<void*>(path));
248        return false;
249    }
250    path_ = path;
251    bool isSucess = true;
252#if ENABLE_JPEG || ENABLE_PNG
253    ImageType imageType = CheckImgType(src);
254    if (imageType == IMG_PNG) {
255#if ENABLE_PNG
256        isSucess = SetPNGSrc(src);
257#endif
258    } else if (imageType == IMG_JPEG) {
259#if ENABLE_JPEG
260        isSucess = SetJPEGSrc(src);
261#endif
262    } else if (imageType == IMG_GIF) {
263        isSucess = true;
264    } else {
265        srcType_ = IMG_SRC_UNKNOWN;
266        return false;
267    }
268#endif
269    return isSucess;
270}
271
272void Image::DrawImage(BufferInfo& gfxDstBuffer,
273                      const Rect& coords,
274                      const Rect& mask,
275                      const Style& style,
276                      uint8_t opaScale) const
277{
278    if (srcType_ == IMG_SRC_VARIABLE) {
279        DrawImage::DrawCommon(gfxDstBuffer, coords, mask, imageInfo_, style, opaScale);
280    } else if (srcType_ == IMG_SRC_FILE) {
281        DrawImage::DrawCommon(gfxDstBuffer, coords, mask, path_, style, opaScale);
282    } else {
283        GRAPHIC_LOGE("Image::DrawImage:: failed with error srctype!\n");
284    }
285}
286
287#if ENABLE_PNG
288static inline void FreePngBytep(png_bytep** rowPointer, uint16_t size)
289{
290    png_bytep* tmpRowPointer = *rowPointer;
291    for (uint16_t i = 0; i < size; i++) {
292        UIFree(tmpRowPointer[i]);
293        tmpRowPointer[i] = nullptr;
294    }
295    UIFree(*rowPointer);
296    *rowPointer = nullptr;
297}
298
299static inline png_bytep* MallocPngBytep(uint16_t height, uint32_t rowBytes)
300{
301    png_bytep* rowPointer = static_cast<png_bytep*>(UIMalloc(sizeof(png_bytep) * height));
302    if (rowPointer == nullptr) {
303        return nullptr;
304    }
305    for (uint16_t y = 0; y < height; y++) {
306        rowPointer[y] = static_cast<png_byte*>(UIMalloc(rowBytes));
307        if (rowPointer[y] == nullptr) {
308            FreePngBytep(&rowPointer, y);
309            return nullptr;
310        }
311    }
312    return rowPointer;
313}
314
315bool Image::SetPNGSrc(const char* src)
316{
317    srcType_ = IMG_SRC_UNKNOWN;
318    png_bytep* rowPointer = nullptr;
319    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
320    if (png == nullptr) {
321        return false;
322    }
323    png_infop info = png_create_info_struct(png);
324    if (info == nullptr) {
325        png_destroy_read_struct(&png, &info, nullptr);
326        return false;
327    }
328    FILE* infile = fopen(src, "rb");
329    if (infile == nullptr) {
330        GRAPHIC_LOGE("can't open %s\n", src);
331        png_destroy_read_struct(&png, &info, nullptr);
332        return false;
333    }
334    png_init_io(png, infile);
335    png_read_info(png, info);
336
337    uint8_t pixelByteSize = DrawUtils::GetPxSizeByColorMode(ARGB8888) >> 3; // 3: Shift right 3 bits
338    uint16_t width = png_get_image_width(png, info);
339    uint16_t height = png_get_image_height(png, info);
340    uint8_t colorType = png_get_color_type(png, info);
341    uint8_t bitDepth = png_get_bit_depth(png, info);
342    uint32_t dataSize = height * width * pixelByteSize;
343
344    if ((colorType == PNG_COLOR_TYPE_GRAY) && (bitDepth < 8)) { // 8: Expand grayscale images to the full 8 bits
345        png_set_expand_gray_1_2_4_to_8(png);
346    }
347    if ((colorType == PNG_COLOR_TYPE_GRAY) || (colorType == PNG_COLOR_TYPE_GRAY_ALPHA)) {
348        png_set_gray_to_rgb(png);
349    }
350    if (colorType == PNG_COLOR_TYPE_PALETTE) {
351        png_set_palette_to_rgb(png);
352    }
353    if (bitDepth == 16) { // 16: Chop 16-bit depth images to 8-bit depth
354        png_set_strip_16(png);
355    }
356    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
357        png_set_tRNS_to_alpha(png);
358    }
359    if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
360        png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
361    }
362    png_set_interlace_handling(png);
363    png_read_update_info(png, info);
364
365    rowPointer = MallocPngBytep(height, png_get_rowbytes(png, info));
366    if (rowPointer == nullptr) {
367        fclose(infile);
368        png_destroy_read_struct(&png, &info, nullptr);
369        return false;
370    }
371
372    png_read_image(png, rowPointer);
373    fclose(infile);
374    png_destroy_read_struct(&png, &info, nullptr);
375
376    ImageInfo* imgInfo = static_cast<ImageInfo*>(UIMalloc(sizeof(ImageInfo)));
377    if (imgInfo == nullptr) {
378        FreePngBytep(&rowPointer, height);
379        return false;
380    }
381    uint8_t* srcData = static_cast<uint8_t*>(UIMalloc(dataSize));
382    if (srcData == nullptr) {
383        FreePngBytep(&rowPointer, height);
384        UIFree(imgInfo);
385        return false;
386    }
387    uint32_t n = 0;
388    for (uint16_t y = 0; y < height; y++) {
389        png_bytep row = rowPointer[y];
390        for (uint16_t x = 0; x < width * pixelByteSize; x += pixelByteSize) {
391            srcData[n++] = row[x + 2]; // 2: B channel
392            srcData[n++] = row[x + 1]; // 1: G channel
393            srcData[n++] = row[x + 0]; // 0: R channel
394            srcData[n++] = row[x + 3]; // 3: Alpha channel
395        }
396    }
397    FreePngBytep(&rowPointer, height);
398
399    imgInfo->header.width = width;
400    imgInfo->header.height = height;
401    imgInfo->header.colorMode = ARGB8888;
402    imgInfo->dataSize = dataSize;
403    imgInfo->data = srcData;
404
405    ReInitImageInfo(imgInfo, true);
406    srcType_ = IMG_SRC_VARIABLE;
407    return true;
408}
409#endif
410
411#if ENABLE_JPEG
412bool Image::SetJPEGSrc(const char* src)
413{
414    struct jpeg_decompress_struct cinfo;
415    struct jpeg_error_mgr jerr;
416    srcType_ = IMG_SRC_UNKNOWN;
417
418    FILE* infile = fopen(src, "rb");
419    if (infile == nullptr) {
420        GRAPHIC_LOGE("can't open %s\n", src);
421        return false;
422    }
423    cinfo.err = jpeg_std_error(&jerr);
424    jpeg_create_decompress(&cinfo);
425    jpeg_stdio_src(&cinfo, infile);
426    jpeg_read_header(&cinfo, TRUE);
427    jpeg_start_decompress(&cinfo);
428
429    uint8_t pixelByteSize = DrawUtils::GetPxSizeByColorMode(ARGB8888) >> 3; // 3: Shift right 3 bits
430    uint16_t width = cinfo.output_width;
431    uint16_t height = cinfo.output_height;
432    uint32_t dataSize = width * height * pixelByteSize;
433    uint16_t rowStride = cinfo.output_width * pixelByteSize;
434    JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(&cinfo), JPOOL_IMAGE, rowStride,
435                                                   1); // 1: one-row-high array
436    ImageInfo* imgInfo = static_cast<ImageInfo*>(UIMalloc(sizeof(ImageInfo)));
437    if (imgInfo == nullptr) {
438        jpeg_finish_decompress(&cinfo);
439        jpeg_destroy_decompress(&cinfo);
440        fclose(infile);
441        return false;
442    }
443    uint8_t* srcData = static_cast<uint8_t*>(UIMalloc(dataSize));
444    if (srcData == nullptr) {
445        jpeg_finish_decompress(&cinfo);
446        jpeg_destroy_decompress(&cinfo);
447        fclose(infile);
448        UIFree(imgInfo);
449        return false;
450    }
451    uint32_t n = 0;
452    while (cinfo.output_scanline < cinfo.output_height) {
453        jpeg_read_scanlines(&cinfo, buffer, 1);       // 1: read one line each time
454        for (uint16_t x = 0; x < width * 3; x += 3) { // 3: color components per pixel
455            srcData[n++] = buffer[0][x + 2];          // 2: B channel
456            srcData[n++] = buffer[0][x + 1];          // 1: G channel
457            srcData[n++] = buffer[0][x + 0];          // 0: R channel
458            srcData[n++] = 255;                       // 255: set alpha channel
459        }
460    }
461    jpeg_finish_decompress(&cinfo);
462    jpeg_destroy_decompress(&cinfo);
463    fclose(infile);
464
465    imgInfo->header.width = width;
466    imgInfo->header.height = height;
467    imgInfo->header.colorMode = ARGB8888;
468    imgInfo->dataSize = dataSize;
469    imgInfo->data = srcData;
470
471    ReInitImageInfo(imgInfo, true);
472    srcType_ = IMG_SRC_VARIABLE;
473    return true;
474}
475#endif
476
477void Image::ReInitImageInfo(ImageInfo* imgInfo, bool mallocFlag)
478{
479    if (mallocFlag_) {
480        if (imageInfo_->data != nullptr) {
481            UIFree(reinterpret_cast<void*>(const_cast<uint8_t*>(imageInfo_->data)));
482        }
483    }
484    UIFree(reinterpret_cast<void*>(const_cast<ImageInfo*>(imageInfo_)));
485
486    imageInfo_ = imgInfo;
487    mallocFlag_ = mallocFlag;
488}
489} // namespace OHOS
490