Showing
3 changed files
with
454 additions
and
1 deletions
| @@ -9,6 +9,10 @@ import { SingleColumn999Component } from './SingleColumn999Component'; | @@ -9,6 +9,10 @@ import { SingleColumn999Component } from './SingleColumn999Component'; | ||
| 9 | import { topicInfoView } from './topicInfoView'; | 9 | import { topicInfoView } from './topicInfoView'; |
| 10 | import { DateFormatUtil, PlayerConstants, WDPlayerController } from 'wdPlayer'; | 10 | import { DateFormatUtil, PlayerConstants, WDPlayerController } from 'wdPlayer'; |
| 11 | import { AudioDataList } from 'wdBean/src/main/ets/bean/morningevening/AudioDataList'; | 11 | import { AudioDataList } from 'wdBean/src/main/ets/bean/morningevening/AudioDataList'; |
| 12 | +import { image } from '@kit.ImageKit'; | ||
| 13 | +import { getPicture, imageNet2PixelMap } from '../../utils/ImageUtils'; | ||
| 14 | +import { effectKit } from '@kit.ArkGraphics2D'; | ||
| 15 | +import { window } from '@kit.ArkUI'; | ||
| 12 | 16 | ||
| 13 | const TAG = 'MorningEveningPaperComponent'; | 17 | const TAG = 'MorningEveningPaperComponent'; |
| 14 | 18 | ||
| @@ -47,6 +51,9 @@ export struct MorningEveningPaperComponent { | @@ -47,6 +51,9 @@ export struct MorningEveningPaperComponent { | ||
| 47 | @Provide currentTime: string = "00:00"; | 51 | @Provide currentTime: string = "00:00"; |
| 48 | @Provide totalTime: string = "00:00"; | 52 | @Provide totalTime: string = "00:00"; |
| 49 | @Provide progressVal: number = 0; | 53 | @Provide progressVal: number = 0; |
| 54 | + @State mixedBgColor: string = '' | ||
| 55 | + // 顶部安全高度赋值 | ||
| 56 | + @State topSafeHeight: number = 0; | ||
| 50 | private audioDataList: AudioDataList[] = [] | 57 | private audioDataList: AudioDataList[] = [] |
| 51 | private playerController: WDPlayerController = new WDPlayerController(); | 58 | private playerController: WDPlayerController = new WDPlayerController(); |
| 52 | simpleAudioDialog: CustomDialogController = new CustomDialogController({ | 59 | simpleAudioDialog: CustomDialogController = new CustomDialogController({ |
| @@ -93,6 +100,10 @@ export struct MorningEveningPaperComponent { | @@ -93,6 +100,10 @@ export struct MorningEveningPaperComponent { | ||
| 93 | } | 100 | } |
| 94 | 101 | ||
| 95 | async aboutToAppear() { | 102 | async aboutToAppear() { |
| 103 | + let windowHight: window.Window = await window.getLastWindow(getContext(this)); | ||
| 104 | + await windowHight.setWindowLayoutFullScreen(true); | ||
| 105 | + this.topSafeHeight = px2vp(windowHight.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect.height) | ||
| 106 | + | ||
| 96 | let dailyPaperTopicPageId = await SPHelper.default.getSync('dailyPaperTopicPageId', "") as String | 107 | let dailyPaperTopicPageId = await SPHelper.default.getSync('dailyPaperTopicPageId', "") as String |
| 97 | console.info(TAG, `aboutToAppear = ` + dailyPaperTopicPageId) | 108 | console.info(TAG, `aboutToAppear = ` + dailyPaperTopicPageId) |
| 98 | const currentTime = new Date().getTime() | 109 | const currentTime = new Date().getTime() |
| @@ -110,6 +121,7 @@ export struct MorningEveningPaperComponent { | @@ -110,6 +121,7 @@ export struct MorningEveningPaperComponent { | ||
| 110 | 121 | ||
| 111 | Logger.info(TAG, "pageInfoBean dateTime = " + dateTime) | 122 | Logger.info(TAG, "pageInfoBean dateTime = " + dateTime) |
| 112 | Logger.info(TAG, "pageInfoBean subTitle = " + this.subTitle) | 123 | Logger.info(TAG, "pageInfoBean subTitle = " + this.subTitle) |
| 124 | + this.setComponentBgColor(pageInfoBean.backgroundImgUrl) | ||
| 113 | 125 | ||
| 114 | let compInfoBean = await MorningEveningViewModel.getMorningEveningCompInfo(pageInfoBean?.id, pageInfoBean?.groups[0]?.id, currentTime + "", pageInfoBean?.topicInfo?.topicId) | 126 | let compInfoBean = await MorningEveningViewModel.getMorningEveningCompInfo(pageInfoBean?.id, pageInfoBean?.groups[0]?.id, currentTime + "", pageInfoBean?.topicInfo?.topicId) |
| 115 | // this.compInfoBean = compInfoBean | 127 | // this.compInfoBean = compInfoBean |
| @@ -130,6 +142,30 @@ export struct MorningEveningPaperComponent { | @@ -130,6 +142,30 @@ export struct MorningEveningPaperComponent { | ||
| 130 | } catch (exception) { | 142 | } catch (exception) { |
| 131 | 143 | ||
| 132 | } | 144 | } |
| 145 | + | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + async setComponentBgColor(imageUrl: string) { | ||
| 149 | + // 图片转换为PixelMap对象 | ||
| 150 | + // const pixelMap: image.PixelMap = await image2PixelMap(item.icon); | ||
| 151 | + const imageSource: image.ImageSource | undefined = await getPicture(imageUrl); | ||
| 152 | + if (imageSource) { | ||
| 153 | + this.pickColor(imageSource) | ||
| 154 | + | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + private async pickColor(imageSource: image.ImageSource | undefined) { | ||
| 160 | + if (imageSource) { | ||
| 161 | + const pixelMap: image.PixelMap = await imageNet2PixelMap(imageSource); | ||
| 162 | + effectKit.createColorPicker(pixelMap, (err, colorPicker) => { | ||
| 163 | + let color = colorPicker.getMainColorSync(); | ||
| 164 | + // 将取色器选取的color示例转换为十六进制颜色代码 | ||
| 165 | + this.mixedBgColor = "#" + color.alpha.toString(16) + color.red.toString(16) + color.green.toString(16) + color.blue.toString(16); | ||
| 166 | + }); | ||
| 167 | + | ||
| 168 | + } | ||
| 133 | } | 169 | } |
| 134 | 170 | ||
| 135 | onPageHide() { | 171 | onPageHide() { |
| @@ -175,7 +211,9 @@ export struct MorningEveningPaperComponent { | @@ -175,7 +211,9 @@ export struct MorningEveningPaperComponent { | ||
| 175 | .width('100%') | 211 | .width('100%') |
| 176 | // .backgroundColor('#000080') | 212 | // .backgroundColor('#000080') |
| 177 | // .backgroundColor(Color.Black) | 213 | // .backgroundColor(Color.Black) |
| 178 | - .backgroundColor(this.pageInfoBean?.backgroundColor ?? Color.Black) | 214 | + // .backgroundColor(this.pageInfoBean?.backgroundColor ?? Color.Black) |
| 215 | + .backgroundColor(this.mixedBgColor ?? Color.Black) | ||
| 216 | + .padding({ top: this.topSafeHeight }) | ||
| 179 | } | 217 | } |
| 180 | 218 | ||
| 181 | @Builder | 219 | @Builder |
| 1 | +/* | ||
| 2 | + * Copyright (c) 2024 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 | +/** | ||
| 17 | + * RGB颜色类型 | ||
| 18 | + */ | ||
| 19 | +export interface ColorRgb { | ||
| 20 | + // 红色(Red) | ||
| 21 | + red: number; | ||
| 22 | + | ||
| 23 | + // 绿色(Green) | ||
| 24 | + green: number; | ||
| 25 | + | ||
| 26 | + // 蓝色(Blue) | ||
| 27 | + blue: number; | ||
| 28 | + | ||
| 29 | + // 透明度 | ||
| 30 | + alpha: number; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * HSV颜色类型 | ||
| 35 | + */ | ||
| 36 | +export interface ColorHsv { | ||
| 37 | + // 色调 | ||
| 38 | + hue: number; | ||
| 39 | + | ||
| 40 | + // 饱和度 | ||
| 41 | + saturation: number; | ||
| 42 | + | ||
| 43 | + // 明度 | ||
| 44 | + value: number; | ||
| 45 | + | ||
| 46 | + // 透明度 | ||
| 47 | + alpha: number; | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +// RGB颜色最大值 | ||
| 51 | +const MAX_RGB_VALUE: number = 255.0; | ||
| 52 | + | ||
| 53 | +export class ColorUtils { | ||
| 54 | + /** | ||
| 55 | + * RGB格式转换为HSV格式,公式参考resources/base/media/rgb_to_hsv.png | ||
| 56 | + * @param color RGB像素值 | ||
| 57 | + * @returns HSV像素值 | ||
| 58 | + */ | ||
| 59 | + public static rgb2hsv(color: ColorRgb): ColorHsv { | ||
| 60 | + // RGB颜色取值范围是0~255,需要转换为0~1的浮点数 | ||
| 61 | + const red: number = color.red / MAX_RGB_VALUE; | ||
| 62 | + const green: number = color.green / MAX_RGB_VALUE; | ||
| 63 | + const blue: number = color.blue / MAX_RGB_VALUE; | ||
| 64 | + | ||
| 65 | + const max: number = Math.max(red, green, blue); | ||
| 66 | + const min: number = Math.min(red, green, blue); | ||
| 67 | + const delta: number = max - min; | ||
| 68 | + | ||
| 69 | + // 色调 | ||
| 70 | + let hue: number = 0; | ||
| 71 | + // 饱和度 | ||
| 72 | + let saturation: number = 0; | ||
| 73 | + // 明度 | ||
| 74 | + let value: number = 0; | ||
| 75 | + // 计算hue值 | ||
| 76 | + if (max === min) { | ||
| 77 | + hue = 0; | ||
| 78 | + } else if (Math.abs(max - red) < Number.EPSILON) { | ||
| 79 | + hue = (green >= blue ? ((green - blue) / delta) * 60 : ((green - blue) / delta) * 60 + 360); | ||
| 80 | + } else if (Math.abs(max - green) < Number.EPSILON) { | ||
| 81 | + hue = (((blue - red) / delta) + 2) * 60; | ||
| 82 | + } else if (Math.abs(max - blue) < Number.EPSILON) { | ||
| 83 | + hue = (((red - green) / delta) + 4) * 60; | ||
| 84 | + } | ||
| 85 | + // 计算saturation值 | ||
| 86 | + saturation = (max === 0 ? 0 : delta / max); | ||
| 87 | + // 计算value值 | ||
| 88 | + value = max; | ||
| 89 | + | ||
| 90 | + return { | ||
| 91 | + hue: hue, | ||
| 92 | + saturation: saturation, | ||
| 93 | + value: value, | ||
| 94 | + alpha: color.alpha | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + * HSV格式转换为RGB格式,公式可参考resources/base/media/hsv_to_rgb.png | ||
| 100 | + * @param color HSV像素值 | ||
| 101 | + * @returns RGB像素值 | ||
| 102 | + */ | ||
| 103 | + public static hsv2rgb(color: ColorHsv): ColorRgb { | ||
| 104 | + // hue取值范围是0~360°,每60°为一个区间,每个区间中RGB计算方式不同 | ||
| 105 | + const h60: number = color.hue / 60; | ||
| 106 | + // 向下取整 | ||
| 107 | + const h60f: number = Math.floor(h60); | ||
| 108 | + const hi: number = h60f % 6; | ||
| 109 | + const f: number = h60 - h60f; | ||
| 110 | + // 计算不同hue区间里面的RGB值 | ||
| 111 | + const p: number = color.value * (1 - color.saturation); | ||
| 112 | + const q: number = color.value * (1 - f * color.saturation); | ||
| 113 | + const t: number = color.value * (1 - (1 - f) * color.saturation); | ||
| 114 | + | ||
| 115 | + let red: number = 0.0; | ||
| 116 | + let green: number = 0.0; | ||
| 117 | + let blue: number = 0.0; | ||
| 118 | + // 根据区间,计算RGB的值 | ||
| 119 | + if (hi === 0) { | ||
| 120 | + red = color.value; | ||
| 121 | + green = t; | ||
| 122 | + blue = p; | ||
| 123 | + } else if (hi === 1) { | ||
| 124 | + red = q; | ||
| 125 | + green = color.value; | ||
| 126 | + blue = p; | ||
| 127 | + } else if (hi === 2) { | ||
| 128 | + red = p; | ||
| 129 | + green = color.value; | ||
| 130 | + blue = t; | ||
| 131 | + } else if (hi === 3) { | ||
| 132 | + red = p; | ||
| 133 | + green = q; | ||
| 134 | + blue = color.value; | ||
| 135 | + } else if (hi === 4) { | ||
| 136 | + red = t; | ||
| 137 | + green = p; | ||
| 138 | + blue = color.value; | ||
| 139 | + } else if (hi === 5) { | ||
| 140 | + red = color.value; | ||
| 141 | + green = p; | ||
| 142 | + blue = q; | ||
| 143 | + } | ||
| 144 | + // RGB取值范围是0~255,需要通过计算 | ||
| 145 | + return { | ||
| 146 | + red: Math.floor(red * MAX_RGB_VALUE), | ||
| 147 | + green: Math.floor(green * MAX_RGB_VALUE), | ||
| 148 | + blue: Math.floor(blue * MAX_RGB_VALUE), | ||
| 149 | + alpha: color.alpha | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + /** | ||
| 154 | + * RGB颜色转为整数值 | ||
| 155 | + * @param color RGB像素值 | ||
| 156 | + * @returns 整数 | ||
| 157 | + */ | ||
| 158 | + public static rgbToNumber(color: ColorRgb): number { | ||
| 159 | + const tmpColor = ((color.alpha << 24) | (color.red << 16) | (color.green << 8) | color.blue) | ||
| 160 | + return tmpColor; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + /** | ||
| 164 | + * 整数值转为RGB颜色值 | ||
| 165 | + * @param color 整数 | ||
| 166 | + * @returns RGB格式像素 | ||
| 167 | + */ | ||
| 168 | + public static numberToRgb(color: number): ColorRgb { | ||
| 169 | + return { | ||
| 170 | + red: (color & 0xFF0000) >> 16, | ||
| 171 | + green: (color & 0xFF00) >> 8, | ||
| 172 | + blue: (color & 0xFF), | ||
| 173 | + alpha: (color & 0xFF000000) >> 24 | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | +} |
| 1 | +/* | ||
| 2 | + * Copyright (c) 2024 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 | +import { resourceManager } from '@kit.LocalizationKit'; | ||
| 17 | +import { image } from '@kit.ImageKit'; | ||
| 18 | +import { ColorHsv, ColorRgb, ColorUtils } from './ColorUtils'; | ||
| 19 | +import { http } from '@kit.NetworkKit'; | ||
| 20 | +import { BusinessError } from '@kit.BasicServicesKit'; | ||
| 21 | +import ResponseCode from '@ohos.net.http'; | ||
| 22 | + | ||
| 23 | +const resourceMgs: resourceManager.ResourceManager = getContext(this).resourceManager; | ||
| 24 | +const PIXEL_MAP_SIZE_WIDTH: number = 40; | ||
| 25 | +const PIXEL_MAP_SIZE_HEIGHT: number = 40; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 图片转换为PixelMap对象 | ||
| 29 | + * @param icon 图片地址,模拟数据存放于rawfile文件夹中 | ||
| 30 | + * @returns 图片转换的PixelMap对象 | ||
| 31 | + */ | ||
| 32 | +export async function image2PixelMap(icon: string): Promise<image.PixelMap> { | ||
| 33 | + // 读取rawfile文件夹下的文件 | ||
| 34 | + const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon); | ||
| 35 | + const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor); | ||
| 36 | + // 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素 | ||
| 37 | + const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({ | ||
| 38 | + editable: false, | ||
| 39 | + desiredPixelFormat: image.PixelMapFormat.BGRA_8888, | ||
| 40 | + // 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。 | ||
| 41 | + desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT } | ||
| 42 | + }) | ||
| 43 | + return pixelMap; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +/** | ||
| 47 | + * 通过http的request方法从网络下载图片资源 | ||
| 48 | + */ | ||
| 49 | +// export async function getPicture() { | ||
| 50 | +// const url:string='https://rmrbcmsonline.peopleapp.com/upload/image/202312/rmrb_86691703594454.png' | ||
| 51 | +// http.createHttp() | ||
| 52 | +// .request(url, | ||
| 53 | +// (error: BusinessError, data: http.HttpResponse) => { | ||
| 54 | +// // if (error) { | ||
| 55 | +// // // 下载失败时弹窗提示检查网络,不执行后续逻辑 | ||
| 56 | +// // promptAction.showToast({ | ||
| 57 | +// // message: $r('app.string.image_request_fail'), | ||
| 58 | +// // duration: 2000 | ||
| 59 | +// // }) | ||
| 60 | +// // return; | ||
| 61 | +// // } | ||
| 62 | +// // transcodePixelMap(data); | ||
| 63 | +// // 判断网络获取到的资源是否为ArrayBuffer类型 | ||
| 64 | +// // if (data.result instanceof ArrayBuffer) { | ||
| 65 | +// // this.imageBuffer = data.result as ArrayBuffer; | ||
| 66 | +// // } | ||
| 67 | +// if (ResponseCode.ResponseCode.OK === data.responseCode) { | ||
| 68 | +// const imageData: ArrayBuffer = data.result as ArrayBuffer; | ||
| 69 | +// // 通过ArrayBuffer创建图片源实例。 | ||
| 70 | +// const imageSource: image.ImageSource = image.createImageSource(imageData); | ||
| 71 | +// return imageSource | ||
| 72 | +// // const options: image.InitializationOptions = { | ||
| 73 | +// // 'alphaType': 0, // 透明度 | ||
| 74 | +// // 'editable': false, // 是否可编辑 | ||
| 75 | +// // 'pixelFormat': 3, // 像素格式 | ||
| 76 | +// // 'scaleMode': 1, // 缩略值 | ||
| 77 | +// // 'size': { height: 100, width: 100 } | ||
| 78 | +// // }; // 创建图片大小 | ||
| 79 | +// | ||
| 80 | +// // 通过属性创建PixelMap | ||
| 81 | +// // imageSource.createPixelMap(options).then((pixelMap: PixelMap) => { | ||
| 82 | +// // // this.image = pixelMap; | ||
| 83 | +// // }); | ||
| 84 | +// // imageNet2PixelMap(imageSource) | ||
| 85 | +// } | ||
| 86 | +// } | ||
| 87 | +// ) | ||
| 88 | +// } | ||
| 89 | + | ||
| 90 | +// 假设http和image是之前正确导入或定义的模块 | ||
| 91 | +export async function getPicture(imageUrl: string): Promise<image.ImageSource | undefined> { | ||
| 92 | + // const url: string = 'https://rmrbcmsonline.peopleapp.com/upload/image/202312/rmrb_86691703594454.png'; | ||
| 93 | + const url: string = 'https://rmrbcmsonline.peopleapp.com/upload/image/201912/rmrb_24141576767688.png?x-oss-process=image/resize,w_550/quality,q_90/format,jpg'; | ||
| 94 | + | ||
| 95 | + return new Promise((resolve, reject) => { | ||
| 96 | + http.createHttp().request(url, (error: BusinessError, data: http.HttpResponse) => { | ||
| 97 | + if (error) { | ||
| 98 | + // 处理错误情况,比如网络错误 | ||
| 99 | + console.error("Download error", error); | ||
| 100 | + // 你可以选择reject(error)或者根据需要调整 | ||
| 101 | + reject(error); | ||
| 102 | + return; | ||
| 103 | + } | ||
| 104 | + if (ResponseCode.ResponseCode.OK === data.responseCode) { | ||
| 105 | + const imageData: ArrayBuffer = data.result as ArrayBuffer; | ||
| 106 | + // 通过ArrayBuffer创建图片源实例 | ||
| 107 | + const imageSource: image.ImageSource = image.createImageSource(imageData); | ||
| 108 | + resolve(imageSource); // 成功时解析Promise | ||
| 109 | + } else { | ||
| 110 | + // 处理其他响应码 | ||
| 111 | + reject(new Error('Invalid response code')); | ||
| 112 | + } | ||
| 113 | + }); | ||
| 114 | + }); | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | + | ||
| 118 | +/** | ||
| 119 | + * 使用createPixelMap将ArrayBuffer类型的图片装换为PixelMap类型 | ||
| 120 | + * @param data:网络获取到的资源 | ||
| 121 | + */ | ||
| 122 | +function transcodePixelMap(data: http.HttpResponse) { | ||
| 123 | + if (ResponseCode.ResponseCode.OK === data.responseCode) { | ||
| 124 | + const imageData: ArrayBuffer = data.result as ArrayBuffer; | ||
| 125 | + // 通过ArrayBuffer创建图片源实例。 | ||
| 126 | + const imageSource: image.ImageSource = image.createImageSource(imageData); | ||
| 127 | + // const options: image.InitializationOptions = { | ||
| 128 | + // 'alphaType': 0, // 透明度 | ||
| 129 | + // 'editable': false, // 是否可编辑 | ||
| 130 | + // 'pixelFormat': 3, // 像素格式 | ||
| 131 | + // 'scaleMode': 1, // 缩略值 | ||
| 132 | + // 'size': { height: 100, width: 100 } | ||
| 133 | + // }; // 创建图片大小 | ||
| 134 | + | ||
| 135 | + // 通过属性创建PixelMap | ||
| 136 | + // imageSource.createPixelMap(options).then((pixelMap: PixelMap) => { | ||
| 137 | + // // this.image = pixelMap; | ||
| 138 | + // }); | ||
| 139 | + // imageNet2PixelMap(imageSource) | ||
| 140 | + } | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +/** | ||
| 144 | + * 图片转换为PixelMap对象 | ||
| 145 | + * @param icon 图片地址,模拟数据存放于rawfile文件夹中 | ||
| 146 | + * @returns 图片转换的PixelMap对象 | ||
| 147 | + */ | ||
| 148 | +export async function imageNet2PixelMap(imageSource: image.ImageSource): Promise<image.PixelMap> { | ||
| 149 | + // 读取rawfile文件夹下的文件 | ||
| 150 | + // const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon); | ||
| 151 | + // const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor); | ||
| 152 | + // 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素 | ||
| 153 | + const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({ | ||
| 154 | + editable: false, | ||
| 155 | + desiredPixelFormat: image.PixelMapFormat.BGRA_8888, | ||
| 156 | + // 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。 | ||
| 157 | + desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT } | ||
| 158 | + }) | ||
| 159 | + return pixelMap; | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +/** | ||
| 163 | + * 查找数组中出现次数最多的像素 | ||
| 164 | + * @param allPixels 像素数组 | ||
| 165 | + * @returns 出现次数最多的像素 | ||
| 166 | + */ | ||
| 167 | +export function findMaxPixel(allPixels: number[]): number { | ||
| 168 | + // 遍历数组,将像素放到一个Map中,key是像素值,value是该像素值出现的次数 | ||
| 169 | + const map: Map<number, number> = new Map(); | ||
| 170 | + allPixels.forEach((pixel: number) => { | ||
| 171 | + if (map.has(pixel)) { | ||
| 172 | + map.set(pixel, map.get(pixel)! + 1); | ||
| 173 | + } else { | ||
| 174 | + map.set(pixel, 1); | ||
| 175 | + } | ||
| 176 | + }) | ||
| 177 | + // 查找出现次数最多的像素 | ||
| 178 | + let maxPixel: number = 0; | ||
| 179 | + let maxTimes: number = 0; | ||
| 180 | + map.forEach((value: number, key: number) => { | ||
| 181 | + if (value >= maxTimes) { | ||
| 182 | + maxTimes = value; | ||
| 183 | + maxPixel = key; | ||
| 184 | + } | ||
| 185 | + }) | ||
| 186 | + return maxPixel; | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +/** | ||
| 190 | + * 修改HSV格式的颜色值,可根据业务需求或者UI设计自行修改S和V的值,此处只举例进行说明 | ||
| 191 | + * @param color HSV格式的颜色 | ||
| 192 | + */ | ||
| 193 | +export function modifySVValue(color: ColorHsv): void { | ||
| 194 | + if (color.hue > 0 && color.hue <= 60) { | ||
| 195 | + color.saturation = 0.12; | ||
| 196 | + color.value = 0.93; | ||
| 197 | + } else if (color.hue > 60 && color.hue <= 190) { | ||
| 198 | + color.saturation = 0.08; | ||
| 199 | + color.value = 0.91; | ||
| 200 | + } else if (color.hue > 190 && color.hue <= 270) { | ||
| 201 | + color.saturation = 0.1; | ||
| 202 | + color.value = 0.93; | ||
| 203 | + } else { | ||
| 204 | + color.saturation = 0.08; | ||
| 205 | + color.value = 0.93; | ||
| 206 | + } | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +/** | ||
| 210 | + * 遍历所有像素,并放到一个数组中 | ||
| 211 | + * @param pixelMap 图片的PixelMap对象 | ||
| 212 | + * @returns 包含图片所有像素的数组 | ||
| 213 | + */ | ||
| 214 | +export async function traverseAllPixel(pixelMap: image.PixelMap): Promise<number[]> { | ||
| 215 | + // PixelMap对象使用BGRA_8888格式,由4个字节表示一个像素,并且宽和高都设置为40,所以此处ArrayBuffer的length就是40*40*4 | ||
| 216 | + const pixelArrayBuffer: ArrayBuffer = new ArrayBuffer(40 * 40 * 4); | ||
| 217 | + // 将PixelMap对象读入ArrayBuffer中 | ||
| 218 | + await pixelMap.readPixelsToBuffer(pixelArrayBuffer); | ||
| 219 | + const allPixels: number[] = []; | ||
| 220 | + // ArrayBuffer是一个二进制的数组,无法直接读取内容,需要转换为整型数组才可以正常使用,此处使用Uint8Array(8 位无符号整型数组) | ||
| 221 | + const unit8Pixels: Uint8Array = new Uint8Array(pixelArrayBuffer); | ||
| 222 | + // 遍历unit8Pixels,取出每个像素的red、green、blue、alpha颜色值,放到一个ColorRgb对象中,然后将ColorRgb转换为数字,放入数组中 | ||
| 223 | + for (let i = 0; i < unit8Pixels.length; i += 4) { | ||
| 224 | + // 如果是透明色,则不放入数组中,否则可能导致计算出来的出现次数最多的像素是透明色 | ||
| 225 | + if (unit8Pixels[i] === 0 && unit8Pixels[i+1] === 0 && unit8Pixels[i+2] === 0 && unit8Pixels[i+3] === 0) { | ||
| 226 | + continue; | ||
| 227 | + } | ||
| 228 | + // BGRA_8888格式,B表示Blue,G表示Green,R表示Red,A表示透明度,此处根据顺序提取B、R、G、A | ||
| 229 | + const rgb: ColorRgb = { | ||
| 230 | + red: unit8Pixels[i+2], | ||
| 231 | + green: unit8Pixels[i+1], | ||
| 232 | + blue: unit8Pixels[i], | ||
| 233 | + alpha: unit8Pixels[i+3] | ||
| 234 | + } | ||
| 235 | + // 高性能知识点:直接将ColorRgb放入数组或者Map中会影响计算出现次数最多的像素的速度,所以需要使用rgbToNumber方法转换为整数 | ||
| 236 | + allPixels.push(ColorUtils.rgbToNumber(rgb)); | ||
| 237 | + } | ||
| 238 | + return allPixels; | ||
| 239 | +} |
-
Please register or login to post a comment