1/*
2 * Copyright (c) 2022-2023 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 { Log } from './Log';
17import { Constants } from '../model/common/Constants';
18import display from '@ohos.display';
19import image from '@ohos.multimedia.image';
20
21const TAG: string = 'common_ImageUtil';
22
23type ImageSize = {
24  width: number;
25  height: number;
26};
27
28export class ImageUtil {
29  // 默认缩略图大小,用于大图跳转
30  static readonly DEFAULT_THUMBNAIL_SIZE: number = 640;
31
32  static computeSampleSize(width: number, height: number, minSideLength: number, maxNumOfPixels: number): number {
33    if (width == 0 || height == 0 || minSideLength == 0 || maxNumOfPixels == 0) {
34      return 2;
35    }
36    let initialSize = ImageUtil.computeInitialSampleSize(width, height, minSideLength, maxNumOfPixels);
37    Log.debug(TAG, `initialSize:  ${initialSize}`);
38    return initialSize <= 8 ? ImageUtil.nextPowerOf2(initialSize) : Math.floor((initialSize + 8 - 1) / 8) * 8;
39  }
40
41  static getScreenNailSize(size: image.Size): void {
42    let displayClass: display.Display = display.getDefaultDisplaySync();
43    // 屏幕最长边px值
44    let screenWidth: number = displayClass.width;
45    let screenHeight: number = displayClass.height;
46    let maxScreenSideLength = Math.max(screenWidth, screenHeight);
47    Log.info(TAG, `getScreenNailSize maxSideLength: ${maxScreenSideLength}`);
48    let targetSize = maxScreenSideLength * Constants.NUMBER_1 / Constants.NUMBER_4;
49    let sizeWidth: number = size.width;
50    let sizeHeight: number = size.height;
51    let maxImageSideLength: number = Math.max(sizeWidth, sizeHeight);
52    if (maxImageSideLength === 0) {
53      Log.error(TAG, 'screen max length is zero');
54      return;
55    }
56    let scale = targetSize / maxImageSideLength;
57    size.width = Math.ceil(size.width * scale);
58    size.height = Math.ceil(size.height * scale);
59    Log.info(TAG, `getScreenNailSize scale: ${scale}, target height ${size.height}`);
60  }
61
62  static nextPowerOf2(value: number): number {
63    let useValue = value;
64    if (useValue <= 0 || useValue > (1 << 30)) {
65      Log.warn(TAG, `invalid sample value ${useValue}!`);
66      return 1;
67    }
68    useValue -= 1;
69    useValue |= useValue >> 16;
70    useValue |= useValue >> 8;
71    useValue |= useValue >> 4;
72    useValue |= useValue >> 2;
73    useValue |= useValue >> 1;
74    Log.debug(TAG, `nextPowerOf2:${useValue}`);
75    return useValue + 1;
76  }
77
78  /**
79   * Calculate the aspect ratio, considering the direction of rotation
80   *
81   * @param info include orientation width height
82   * @return the aspect ratio
83   */
84  static calcRatio(info: ImageSize): number {
85    if (info == null || info == undefined) {
86      return 1;
87    }
88    if (info.width == 0 || info.height == 0) {
89      return 1;
90    }
91    return info.width / info.height;
92  }
93
94  /**
95   * 计算缩略图大小生成对应路径
96   * 计算缩略图大小规格:短边等于不小于给定大小;如果原图短边小于给定大小则返回原图大小
97   *
98   * @param uri 图片媒体路径
99   * @param imageHeight 图片原高
100   * @param imageWidth 图片原宽
101   */
102  static calcThumbnail(uri: string, imageHeight: number, imageWidth: number, orientation?: number): string {
103    if (uri == null) {
104      return '';
105    }
106    if (!imageWidth || !imageHeight) {
107      // 长宽无效返回缩略图默认值256 * 256
108      return `${uri}/thumbnail/256/256`
109    }
110    if (imageHeight < this.DEFAULT_THUMBNAIL_SIZE || imageWidth < this.DEFAULT_THUMBNAIL_SIZE) {
111      return `${uri}/thumbnail/${imageWidth}/${imageHeight}`
112    }
113
114    let width = 0;
115    let height = 0;
116    if (orientation != null && (orientation == Constants.ANGLE_90 || orientation == Constants.ANGLE_270)) {
117      let temp = imageWidth;
118      imageWidth = imageHeight;
119      imageHeight = temp;
120    }
121    if (imageHeight < imageWidth) {
122      height = this.DEFAULT_THUMBNAIL_SIZE;
123      width = Math.round(height * imageWidth / imageHeight)
124    } else {
125      width = this.DEFAULT_THUMBNAIL_SIZE;
126      height = Math.round(width * imageHeight / imageWidth);
127    }
128    return `${uri}/thumbnail/${width}/${height}`
129  }
130
131  private static computeInitialSampleSize(width: number, height: number,
132                                          minSideLength: number, maxNumOfPixels: number): number {
133    if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
134      return 1;
135    }
136
137    let lowerBound: number = (maxNumOfPixels == -1) ? 1 : Math.ceil(Math.sqrt((width * height) / maxNumOfPixels));
138    Log.debug(TAG, `lowerBound: ${lowerBound}`);
139    if (minSideLength == -1) {
140      return lowerBound;
141    } else {
142      let sampleSize = Math.min(width / minSideLength, height / minSideLength);
143      return Math.max(sampleSize, lowerBound);
144    }
145  }
146}