1/*
2 * Copyright (c) 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
16import type { MediaItem, Size } from '@ohos/common';
17import { BrowserDataFactory, ImageUtil, Log, ScreenManager } from '@ohos/common';
18import { PixelMapWrapper } from './base/PixelMapWrapper';
19import { CropAngle } from './crop/CropType';
20import userFileManager from '@ohos.filemanagement.userFileManager';
21import image from '@ohos.multimedia.image';
22
23const TAG: string = 'editor_Loader';
24
25export class Loader {
26  private static readonly MIN_PIXEL_MAP_SIZE: number = 1024;
27
28  static async loadPixelMapWrapper(mediaItem: MediaItem, isPreview: boolean = false): Promise<PixelMapWrapper> {
29    Log.debug(TAG, `Photo: loadPixelMap uri = ${mediaItem.uri}`);
30    let dataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO);
31
32    let result = await dataImpl.getDataByUri(mediaItem.uri);
33    if (!result) {
34      Log.error(TAG, 'get file asset failed.');
35      return null;
36    }
37
38    let size = {
39      width: result.get(userFileManager.ImageVideoKey.WIDTH.toString()) as number,
40      height: result.get(userFileManager.ImageVideoKey.HEIGHT.toString()) as number
41    };
42
43    let thumbnail = undefined;
44    if (isPreview) {
45      Loader.getPixelMapPreviewSize(size);
46      thumbnail = await result.getThumbnail(size);
47    } else {
48      let fd = undefined;
49      let imageSourceApi: image.ImageSource = undefined;
50      try {
51        fd = await result.open('rw');
52        imageSourceApi = image.createImageSource(fd);
53        thumbnail = await imageSourceApi.createPixelMap();
54      } catch (e) {
55        Log.error(TAG, "loadPixelMapWrapper: sth wrong, " + e);
56      } finally {
57        await result.close(fd);
58      }
59    }
60
61    let wrapper = new PixelMapWrapper(thumbnail, px2vp(size.width), px2vp(size.height));
62    Log.info(TAG, `Photo: loadPixelMap: size[${JSON.stringify(size)}] wrapper[${JSON.stringify(wrapper)}]`);
63
64    let orientation = mediaItem.orientation || 0;
65    Loader.translatePixelMap(wrapper, orientation);
66    Log.info(TAG, `Photo: loadPixelMap: final wrapper[${JSON.stringify(wrapper)}]`);
67    return wrapper;
68  }
69
70  private static getPixelMapPreviewSize(size: Size) {
71    let width = ScreenManager.getInstance().getWinWidth();
72    let height = ScreenManager.getInstance().getWinHeight();
73    Log.debug(TAG, `picture real size: ${size.width} ${size.height}`);
74    let scale = ImageUtil.computeSampleSize(size.width, size.height, Loader.MIN_PIXEL_MAP_SIZE, width * height * 2);
75    size.width = Math.ceil(size.width / scale);
76    size.height = Math.ceil(size.height / scale);
77    Log.debug(TAG, `picture scale: ${scale} size: ${JSON.stringify(size)}`);
78  }
79
80  private static translatePixelMap(image: PixelMapWrapper, orientation: number) {
81    if (0 == orientation % CropAngle.CIRCLE_ANGLE) {
82      return image;
83    }
84
85    let size = Loader.swapSize(image, orientation);
86    let offWidth = size.width;
87    let offHeight = size.height;
88    let setting = new RenderingContextSettings(true)
89    let context = new OffscreenCanvasRenderingContext2D(offWidth, offHeight, setting);
90
91    context.save();
92    context.drawImage(image.pixelMap, 0, 0, offWidth, offHeight);
93    context.restore();
94
95    image.pixelMap && image.pixelMap.release();
96    image.pixelMap = context.getPixelMap(0, 0, offWidth, offHeight);
97    image.width = size.width;
98    image.height = size.height;
99  }
100
101  private static swapSize(image: PixelMapWrapper, orientation: number): Size {
102    let angle = orientation % CropAngle.HALF_CIRCLE_ANGLE;
103    let size = {
104      width: image.width,
105      height: image.height
106    };
107    if (0 != angle) {
108      size.width = image.height;
109      size.height = image.width;
110    }
111    return size;
112  }
113
114  static async getCompressedBitsPerPixel(mediaItem: MediaItem): Promise<number> {
115    return new Promise(async (resolve, reject)=> {
116      let dataImpl = BrowserDataFactory.getFeature(BrowserDataFactory.TYPE_PHOTO);
117      let result = await dataImpl.getDataByUri(mediaItem.uri);
118      if (!result) {
119        Log.error(TAG, 'getCompressedBitsPerPixel: get file asset failed.');
120        reject();
121      }
122
123      let fd = await result.open('rw');
124      let imageSourceApi: image.ImageSource = image.createImageSource(fd);
125      imageSourceApi = image.createImageSource(fd);
126      imageSourceApi.getImageProperty("CompressedBitsPerPixel", (error, data) => {
127        if (error) {
128          Log.error(TAG, 'Failed to get the value of the specified attribute key of the image.');
129          result.close(fd);
130          reject();
131        } else {
132          Log.error(TAG, 'Succeeded in getting the value of the specified attribute key of the image. data is ' + data);
133          result.close(fd);
134          resolve(Number(data));
135        }
136      });
137    });
138  }
139}