diff --git a/ImageEditTaskPool/AppScope/app.json5 b/ImageEditTaskPool/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d835af8bcb5ed37242286222c9aab43b7f59e6e2 --- /dev/null +++ b/ImageEditTaskPool/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.imagetestharmony", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/ImageEditTaskPool/AppScope/resources/base/element/string.json b/ImageEditTaskPool/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..d03b1b5998deb75c49920c57e54bd359339b2ea2 --- /dev/null +++ b/ImageEditTaskPool/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ImageEdit" + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/AppScope/resources/base/media/app_icon.png b/ImageEditTaskPool/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/ImageEditTaskPool/AppScope/resources/base/media/app_icon.png differ diff --git a/ImageEditTaskPool/LICENSE b/ImageEditTaskPool/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..338e5b0bc22082e0ffcc7121c2ed3897a3ddccb0 --- /dev/null +++ b/ImageEditTaskPool/LICENSE @@ -0,0 +1,78 @@ + Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Apache License, Version 2.0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and +2.You must cause any modified files to carry prominent notices stating that You changed the files; and +3.You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +4.If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/ImageEditTaskPool/README.md b/ImageEditTaskPool/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ca7368520c0298a2564a62954abc108ddd1eb04b --- /dev/null +++ b/ImageEditTaskPool/README.md @@ -0,0 +1,56 @@ +# 基于TaskPool实现图片编辑功能 + +### 简介 +本示例基于TaskPool多线程并发方案,实现图片编辑功能,包含图片裁剪、图片旋转、色域调节(亮度、透明度、饱和度)、图片重置,图片保存功能,开发者使用TaskPool线程接口,把任务方法、参数传入execute接口,系统会自动根据任务量进行扩容及缩容,进行线程调度,提升代码运行效率,降低任务运行耗时。 + +### 效果预览 +![image](screenshots/device/ImageEditTaskPool_CN.gif) + +### 工程目录 +``` +├──entry/src/main/ets // 代码区 +│ ├──common +│ │ └──constant +│ │ └──CommonConstants.ets // 公共常量类 +│ ├──entryability +│ │ └──EntryAbility.ets // 本地启动ability +│ ├──pages +│ │ └──HomePage.ets // 本地主页面 +│ ├──utils +│ │ ├──AdjustUtil.ts // 调节工具类 +│ │ ├──CropUtil.ets // 裁剪工具类 +│ │ ├──DecodeUtil.ets // 解码工具类 +│ │ ├──EncodeUtil.ets // 编码工具类 +│ │ └──OpacityUtil.ets // 透明度调节工具类 +│ ├──view +│ │ ├──AdjustContentView.ets // 色域调整视图 +│ │ ├──CommBackgroundIcon.ets // 公共带背景色icon +│ │ └──TitleBar.ets // 顶部工具栏 +│ └──viewmodel +│ ├──IconListViewModel.ets // icon数据 +│ ├──MessageItem.ets // 多线程封装消息 +│ ├──OptionViewModel.ts // 图片处理封装类 +│ └──RegionItem.ets // 区域封装类 +└──entry/src/main/resources // 资源文件目录 +``` + +### 使用说明 +1. 按照个人需求点击按钮进行裁剪及调节。 +2. 完成之后可按需保存图片。 +3. 说明: 当前亮度调节、透明度调节、饱和度调节是在UI层面实现的,未实现细节优化算法,只做简单示例。调节后的图片会有色彩上的失真。 + +### 具体实现 +1. 图片裁剪具体实现步骤:通过pixelMap获取图片尺寸,为后续裁剪做准备。 确定裁剪的方式,当前裁剪默认有原图、1:1、4:3、16:9。 最后通过pixelMap调用接口crop()进行裁剪操作。 +2. 图片旋转具体实现步骤:确定旋转方向,当前支持顺时针和逆时针旋转。 最后通过pixelMap调用接口rotate()进行旋转操作。 +3. 亮度调节具体实现步骤:先将pixelMap转换成ArrayBuffer,把生成好的ArrayBuffer发送到worker线程,再计算好的ArrayBuffer发送回主线程。最后把ArrayBuffer写入pixelMap,刷新UI。 +4. 透明度调节具体实现步骤:先获取pixelMap,再调用接口opacity()进行透明度调节。 +5. 饱和度调节具体实现步骤:将pixelMap转换成ArrayBuffer,把生成好的ArrayBuffer发送到worker线程,对每一个像素点的饱和度按倍率计算。然后再把计算好的ArrayBuffer发送回主线程。 最后将ArrayBuffer写入pixelMap,刷新UI。 + +### 相关权限 +- ohos.permission.WRITE_IMAGEVIDEO + +### 约束与限制 +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS NEXT Release及以上。 +3. DevEco Studio版本:DevEco Studio NEXT Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS NEXT Release SDK及以上。 \ No newline at end of file diff --git a/ImageEditTaskPool/build-profile.json5 b/ImageEditTaskPool/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d12140d202702d7c73d64f1b291fe5c45a660ce --- /dev/null +++ b/ImageEditTaskPool/build-profile.json5 @@ -0,0 +1,27 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/build-profile.json5 b/ImageEditTaskPool/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..befa10141dfc5999e35f60a36a9f948e664732b1 --- /dev/null +++ b/ImageEditTaskPool/entry/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/hvigorfile.ts b/ImageEditTaskPool/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..80e4ec5b81689f238c34614b167a0b9e9c83e8d9 --- /dev/null +++ b/ImageEditTaskPool/entry/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/ImageEditTaskPool/entry/oh-package.json5 b/ImageEditTaskPool/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..225946cb11a2c405c8dc81eea89c22f923556638 --- /dev/null +++ b/ImageEditTaskPool/entry/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "license": "", + "devDependencies": {}, + "author": "", + "name": "entry", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} diff --git a/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets b/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..26db7d1f23ae8c6464d450488f09fd78131eea4b --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class CommonConstants { + /** + * Adjust slider value. + */ + static readonly ADJUST_SLIDER_VALUE: number[] = [100, 100, 100]; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/entryability/EntryAbility.ets b/ImageEditTaskPool/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..277bacf40cb22d616fdb360e04391f054d057b9c --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { UIAbility, abilityAccessCtrl, Permissions, Want, AbilityConstant } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const TAG: string = '[EntryAbility]'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onCreate'); + hilog.info(0x0000, TAG, '%{public}s', `want: ${JSON.stringify(want)}`); + hilog.info(0x0000, TAG, '%{public}s', `launchParam: ${JSON.stringify(launchParam)}`); + } + + onDestroy(): void { + hilog.info(0x0000, TAG, '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, TAG, '%{public}s', 'Ability onWindowStageCreate'); + const permissions: Array = [ + 'ohos.permission.WRITE_IMAGEVIDEO' + ]; + const atManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(this.context, permissions, (err, data) => { + if (err) { + hilog.error(0x0000, TAG, 'Failed to requestPermission. Cause: %{public}s', + JSON.stringify(err) ?? ''); + } else { + hilog.info(0x0000, TAG, 'Succeeded in requestPermission. Data: %{public}s', + JSON.stringify(data) ?? ''); + } + }); + + let windowClass: window.Window = windowStage.getMainWindowSync(); + windowClass.setWindowLayoutFullScreen(true); + + let SystemBarProperties: window.SystemBarProperties = { + statusBarContentColor: '#FFFFFF' + }; + let promise = windowClass.setWindowSystemBarProperties(SystemBarProperties); + promise.then(() => { + hilog.info(0x0000, TAG, 'Succeeded in setting the system bar properties.'); + }).catch((err: BusinessError) => { + hilog.error(0x0000, TAG, + `Failed to set the system bar properties. Cause code: ${err.code}, message: ${err.message}`); + }); + + let navigationBarArea: window.AvoidArea = + windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); + let area: window.AvoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + AppStorage.setOrCreate('naviIndicatorHeight', px2vp(navigationBarArea.bottomRect.height)); + AppStorage.setOrCreate('statusBarHeight', px2vp(area.topRect.height)); + + windowStage.loadContent('pages/HomePage', (err, data) => { + if (err.code) { + hilog.error(0x0000, TAG, 'Failed to load the content. Cause: %{public}s', + JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, TAG, 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, TAG, '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, TAG, '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, TAG, '%{public}s', 'Ability onBackground'); + } +} diff --git a/ImageEditTaskPool/entry/src/main/ets/pages/HomePage.ets b/ImageEditTaskPool/entry/src/main/ets/pages/HomePage.ets new file mode 100644 index 0000000000000000000000000000000000000000..5f5efb6794087b88aa0a5483847c888bf0611505 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/pages/HomePage.ets @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { AlertDialog } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; +import { router } from '@kit.ArkUI'; +import { cropIconChangeList, menuIconList } from '../viewModel/IconListViewModel'; +import { RotateType, CropType, MainTabId } from '../viewModel/OptionViewModel'; +import { CommonConstants } from '../common/constant/CommonConstants'; +import { square, banner, rectangle } from '../utils/CropUtil'; +import { IconStatus } from '../viewModel/IconListViewModel'; +import { encode } from '../utils/EncodeUtil'; +import AdjustContentView from '../view/AdjustContentView'; +import getPixelMap from '../utils/DecodeUtil'; +import TitleBar from '../view/TitleBar'; + +const TAG: string = '[HomePage]'; + +@Entry +@Component +struct HomePage { + @State currentIndex: number = 0; + @State currentCropIndex: number = 0; + @Provide('imageInfo') imageInfo: image.ImageInfo = { + size: { height: 0, width: 0 }, + density: 0, + stride: 0, + alphaType: 0, + pixelFormat: 0, + mimeType: '', + isHdr: false + }; + @Provide('currentAdjustData') currentAdjustData: Array = + CommonConstants.ADJUST_SLIDER_VALUE.map((item: number) => item); + @Provide('isPixelMapChange') @Watch('flushPixelMap') isPixelMapChange: boolean = false; + @Provide('pixelMap') pixelMap?: image.PixelMap = undefined; + menuIconChangeList = menuIconList; + cropIconChange: Array = cropIconChangeList; + dialogControllerConfirm: CustomDialogController = new CustomDialogController({ + builder: AlertDialog({ + primaryTitle: $r('app.string.save_image'), + content: $r('app.string.confirm_save'), + primaryButton: { + value: $r('app.string.cancel'), + action: () => { + hilog.info(0x0000, TAG, '%{public}s', 'cancel'); + } + }, + secondaryButton: { + value: $r('app.string.ok'), + action: () => { + if (this.pixelMap) { + encode(this, this.pixelMap); + } + } + } + }) + }); + + @Builder + TabBuilderMenu(index: number, name: string | Resource) { + Column() { + Image(this.currentIndex === index ? this.menuIconChangeList[index]?.chosen : + this.menuIconChangeList[index]?.normal) + .width(24) + .height(24) + + Text(name) + .fontColor(this.currentIndex === index ? '#006CDE' : Color.White) + .fontSize(10) + .margin({ top: 8 }) + } + .width('100%') + } + + async cropImage(proportion: CropType): Promise { + if (!this.pixelMap) { + return; + } + const imageInfo = await this.pixelMap.getImageInfo(); + const size = imageInfo.size; + const imageWidth = size?.width; + const imageHeight = size?.height; + switch (proportion) { + case CropType.ORIGINAL_IMAGE: + this.pixelInit(); + break; + case CropType.SQUARE: + if (this.pixelMap) { + square(this.pixelMap, imageWidth, imageHeight).then(() => { + this.flushPixelMapChange(); + }); + } + break; + case CropType.BANNER: + if (this.pixelMap) { + banner(this.pixelMap, imageWidth, imageHeight).then(() => { + this.flushPixelMapChange(); + }); + } + break; + case CropType.RECTANGLE: + if (this.pixelMap) { + rectangle(this.pixelMap, imageWidth, imageHeight).then(() => { + this.flushPixelMapChange(); + }); + } + break; + default: + break; + } + } + + rotateImage(rotateType: RotateType): void { + if (rotateType === RotateType.CLOCKWISE) { + if (!this.pixelMap) { + return; + } + try { + this.pixelMap.rotate(90) + .then(() => { + this.flushPixelMapChange(); + }) + } catch (error) { + hilog.error(0x0000, TAG, '%{public}s', `there is a error in rotate process with ${error?.code}`); + } + } + if (rotateType === RotateType.ANTI_CLOCK) { + if (!this.pixelMap) { + return; + } + try { + this.pixelMap.rotate(-90) + .then(() => { + this.flushPixelMapChange(); + }) + } catch (error) { + hilog.error(0x0000, TAG, '%{public}s', `there is a error in rotate process with ${error?.code}`); + } + } + } + + flushPixelMapChange(): void { + this.isPixelMapChange = !this.isPixelMapChange; + } + + flushPixelMap(): void { + const temp = this.pixelMap; + this.pixelMap = undefined; + this.pixelMap = temp; + } + + pixelInit(): void { + getPixelMap(this) + .then((pixelMap?: image.PixelMap) => { + if (pixelMap) { + this.isPixelMapChange = !this.isPixelMapChange; + this.pixelMap = pixelMap; + } + this.currentCropIndex = 0; + this.currentAdjustData = CommonConstants.ADJUST_SLIDER_VALUE.map((item: number) => item); + }) + } + + aboutToAppear() { + this.pixelInit(); + } + + @Builder + TitleNavigation() { + TitleBar({ + onBack: () => { + router.back(); + }, + onReset: () => { + this.pixelInit(); + }, + onSave: () => { + this.dialogControllerConfirm.open(); + } + }) + } + + build() { + Navigation() { + Column() { + Column() { + this.TitleNavigation() + if (this.isPixelMapChange) { + Image(this.pixelMap) + .objectFit(ImageFit.None) + } else { + Image(this.pixelMap) + .objectFit(ImageFit.None) + } + } + .justifyContent(FlexAlign.Start) + .padding({ top: 8 }) + .width('100%') + .height('70%') + + Column() { + Tabs({ barPosition: BarPosition.End }) { + TabContent() { + Row() { + ForEach(this.cropIconChange, (item: IconStatus, index?: number | undefined) => { + Image(this.currentCropIndex === index ? item?.chosen : item?.normal) + .width(36) + .height(36) + .onClick(() => { + if (typeof (index) === 'number') { + this.currentCropIndex = index; + this.cropImage(index); + } + }) + }, (item: IconStatus) => JSON.stringify(item)) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.SpaceEvenly) + } + .tabBar(this.TabBuilderMenu(MainTabId.CROP, $r('app.string.crop'))) + + TabContent() { + Row() { + Image($r('app.media.ic_clockwise')) + .width(30) + .height(30) + .onClick(() => { + this.rotateImage(RotateType.CLOCKWISE); + }) + + Image($r('app.media.ic_anti_clockwise')) + .width(30) + .height(30) + .onClick(() => { + this.rotateImage(RotateType.ANTI_CLOCK); + }) + } + .justifyContent(FlexAlign.SpaceEvenly) + .width('100%') + .height('100%') + } + .tabBar(this.TabBuilderMenu(MainTabId.ROTATE, $r('app.string.rotate'))) + + TabContent() { + AdjustContentView() + } + .tabBar(this.TabBuilderMenu(MainTabId.ADJUST, $r('app.string.adjust'))) + } + .scrollable(false) + .onChange((index: number) => { + this.currentIndex = index; + }) + } + .padding({ bottom: 16 }) + .width('100%') + .height('30%') + } + .width('100%') + .height('100%') + } + .padding({ + top: (AppStorage.get('statusBarHeight') ?? 0), + bottom: (AppStorage.get('naviIndicatorHeight') ?? 0) + }) + .hideToolBar(true) + .hideTitleBar(true) + .width('100%') + .height('100%') + .backgroundColor(Color.Black) + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts b/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..b952c3593834ddd41ce206e0cd2e8f1c338fb129 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RGBIndex, HSVIndex, AngelRange } from '../viewModel/OptionViewModel'; + +/** + * Saturation adjust. + * + * @param pixelMap. + * @param value saturation's value. + * @return arrayBuffer. + */ +export function adjustSaturation(bufferArray: ArrayBuffer, last: number, cur: number): ArrayBuffer | undefined { + return execColorInfo(bufferArray, last, cur, HSVIndex.SATURATION); +} + +/** + * Image brightness adjust. + * + * @param pixelMap. + * @param value image's brigtness. + * @return arrayBuffer. + */ +export function adjustImageValue(bufferArray: ArrayBuffer, last: number, cur: number): ArrayBuffer | undefined { + return execColorInfo(bufferArray, last, cur, HSVIndex.VALUE); +} + +/** + * Exec color transform. + * + * @param bufferArray. + * @param last. + * @param cur. + * @param hsvIndex. + * @return arrayBuffer. + */ +export function execColorInfo(bufferArray: ArrayBuffer, last: number, cur: number, + hsvIndex: number): ArrayBuffer | undefined { + if (!bufferArray) { + return; + } + const newBufferArr = bufferArray; + let colorInfo = new Uint8Array(newBufferArr); + for (let i = 0; i < colorInfo?.length; i += 4) { + const hsv = rgb2hsv(colorInfo[i + RGBIndex.RED], colorInfo[i + RGBIndex.GREEN], colorInfo[i + RGBIndex.BLUE]); + let rate = cur / last; + hsv[hsvIndex] *= rate; + const rgb = hsv2rgb(hsv[HSVIndex.HUE], hsv[HSVIndex.SATURATION], hsv[HSVIndex.VALUE]); + colorInfo[i + RGBIndex.RED] = rgb[RGBIndex.RED]; + colorInfo[i + RGBIndex.GREEN] = rgb[RGBIndex.GREEN]; + colorInfo[i + RGBIndex.BLUE] = rgb[RGBIndex.BLUE]; + } + return newBufferArr; +} + +/** + * Color transform. + * + * @param rgbValue 0 - 255. + * @return 0 - 1. + */ +function colorTransform(rgbValue: number): number { + return Number((rgbValue / 255).toFixed(2)); +} + +/** + * RGB transform to HSV. + * + * @param red 0- 255. + * @param green 0- 255. + * @param blue 0- 255. + * @return h (0 - 360) s(0 - 100) v (0 - 100). + */ +function rgb2hsv(red: number, green: number, blue: number): number[] { + let hsvH: number = 0, hsvS: number = 0, hsvV: number = 0; + const rgbR: number = colorTransform(red); + const rgbG: number = colorTransform(green); + const rgbB: number = colorTransform(blue); + const maxValue = Math.max(rgbR, Math.max(rgbG, rgbB)); + const minValue = Math.min(rgbR, Math.min(rgbG, rgbB)); + hsvV = maxValue * 100; + if (maxValue === 0) { + hsvS = 0; + } else { + hsvS = Number((1 - minValue / maxValue).toFixed(2)) * 100; + } + if (maxValue === minValue) { + hsvH = 0; + } + if (maxValue === rgbR && rgbG >= rgbB) { + hsvH = Math.floor(60 * ((rgbG - rgbB) / (maxValue - minValue))); + } + if (maxValue === rgbR && rgbG < rgbB) { + hsvH = Math.floor(60 * ((rgbG - rgbB) / (maxValue - minValue)) + 360); + } + if (maxValue === rgbG) { + hsvH = Math.floor(60 * ((rgbB - rgbR) / (maxValue - minValue)) + 120); + } + if (maxValue === rgbB) { + hsvH = Math.floor(60 * ((rgbR - rgbG) / (maxValue - minValue)) + 240); + } + return [hsvH, hsvS, hsvV]; +} + +/** + * HSV to RGB conversion formula: + * When 0 <= H <= 360, 0 <= S <= 1 and 0 <= V <= 1: + * C = V * S + * X = C * (1 - Math.abs((H / 60) mod 2 - 1)) + * m = V - C + * | (C, X ,0), 0 <= H < 60 + * | (X, C, 0), 60 <= H < 120 + * | (0, C, X), 120 <= H < 180 + * (R', G', B') = | (0, X, C), 180 <= H < 240 + * | (X, 0, C), 240 <= H < 300 + * | (C, 0, X), 300 <= H < 360 + * + * (R, G, B) = ((R' + m) * 255, (G' + m) * 255, (B' + m) * 255) + * + * @param h hue 0 ~ 360. + * @param s saturation 0 ~ 100. + * @param v value 0 ~ 100. + * @return rgb value. + */ +function hsv2rgb(hue: number, saturation: number, value: number) { + let rgbR: number = 0, rgbG: number = 0, rgbB: number = 0; + if (saturation === 0) { + rgbR = rgbG = rgbB = Math.round((value * 255) / 100); + return { rgbR, rgbG, rgbB }; + } + const cxmC = (value * saturation) / (100 * 100); + const cxmX = cxmC * (1 - Math.abs((hue / 60) % 2 - 1)); + const cxmM = (value - cxmC * 100) / 100; + const hsvHRange = Math.floor(hue / 60); + switch (hsvHRange) { + case AngelRange.ANGEL_0_60: + rgbR = (cxmC + cxmM) * 255; + rgbG = (cxmX + cxmM) * 255; + rgbB = (0 + cxmM) * 255; + break; + case AngelRange.ANGEL_60_120: + rgbR = (cxmX + cxmM) * 255; + rgbG = (cxmC + cxmM) * 255; + rgbB = (0 + cxmM) * 255; + break; + case AngelRange.ANGEL_120_180: + rgbR = (0 + cxmM) * 255; + rgbG = (cxmC + cxmM) * 255; + rgbB = (cxmX + cxmM) * 255; + break; + case AngelRange.ANGEL_180_240: + rgbR = (0 + cxmM) * 255; + rgbG = (cxmX + cxmM) * 255; + rgbB = (cxmC + cxmM) * 255; + break; + case AngelRange.ANGEL_240_300: + rgbR = (cxmX + cxmM) * 255; + rgbG = (0 + cxmM) * 255; + rgbB = (cxmC + cxmM) * 255; + break; + case AngelRange.ANGEL_300_360: + rgbR = (cxmC + cxmM) * 255; + rgbG = (0 + cxmM) * 255; + rgbB = (cxmX + cxmM) * 255; + break; + default: + break; + } + return [ + Math.round(rgbR), + Math.round(rgbG), + Math.round(rgbB) + ]; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/CropUtil.ets b/ImageEditTaskPool/entry/src/main/ets/utils/CropUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..9de8b9e5cecb3e534fa1500a19080aa2f8b05ecf --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/utils/CropUtil.ets @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RegionItem } from '../viewModel/RegionItem'; + +/** + * Crop 1:1. + * + * @param pixelMap. + * @param width. + * @param height. + */ +export async function square(pixelMap: PixelMap, width: number, height: number): Promise { + if (width < height) { + await pixelMap.crop({ + size: { + width: width, + height: width + }, + x: 0, + y: Math.round((height - width) / 2) + }); + } else { + await pixelMap.crop({ + size: { + width: height, + height: height + }, + x: Math.round((width - height) / 2), + y: 0 + }); + } +} + +/** + * Common crop function. + * + * @param pixelMap. + * @param cropWidth. + * @param cropHeight. + * @param cropPosition. + */ +export async function cropCommon(pixelMap: PixelMap, cropWidth: number, cropHeight: number, + cropPosition: RegionItem): Promise { + await pixelMap.crop({ + size: { + width: cropWidth, + height: cropHeight + }, + x: cropPosition.x, + y: cropPosition.y + }); +} + +/** + * Crop 4:3. + * + * @param pixelMap. + * @param width. + * @param height. + */ +export async function banner(pixelMap: PixelMap, width: number, height: number): Promise { + if (width <= height) { + const cropWidth = width; + const cropHeight = Math.floor(width * 0.75); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + if (width * 0.75 >= height) { + const cropWidth = Math.floor(height / 0.75); + const cropHeight = height; + const cropPosition = new RegionItem(Math.floor((width - cropWidth) / 2), 0); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + + const cropWidth = width; + const cropHeight = Math.floor(width * 0.75); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); +} + +/** + * Crop 16:9. + * + * @param pixelMap. + * @param width. + * @param height. + */ +export async function rectangle(pixelMap: PixelMap, width: number, height: number): Promise { + if (width <= height) { + const cropWidth = width; + const cropHeight = Math.floor(width * (9 / 16)); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + if (width * (9 / 16) >= height) { + const cropWidth = Math.floor(height / (9 / 16)); + const cropHeight = height; + const cropPosition = new RegionItem(Math.floor((width - cropWidth) / 2), 0); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); + return; + } + + const cropWidth = width; + const cropHeight = Math.floor(width * (9 / 16)); + const cropPosition = new RegionItem(0, Math.floor((height - cropHeight) / 2)); + await cropCommon(pixelMap, cropWidth, cropHeight, cropPosition); +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/DecodeUtil.ets b/ImageEditTaskPool/entry/src/main/ets/utils/DecodeUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..528ab8f6c9fdd4fd7f0063add6dc8d0c5a66162a --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/utils/DecodeUtil.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { fileIo } from '@kit.CoreFileKit'; +import { image } from '@kit.ImageKit'; + +const TAG: string = '[DecodeUtil]'; + +/** + * Async get resource fd. + * + * @return file fd. + */ +async function getResourceFd(component: Object): Promise { + const context = getContext(component); + const resourceMgr = context.resourceManager; + let imageBuffer = await resourceMgr.getMediaContent($r('app.media.ic_low')); + let filePath = context.cacheDir + '/low.jpg'; + let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); + fileIo.writeSync(file.fd, imageBuffer.buffer); + return file.fd; +} + +/** + * Async create pixel map. + * + * @return pixelMa. + */ +export default async function getPixelMap(component: Object): Promise { + const fd = await getResourceFd(component); + const imageSourceApi = image.createImageSource(fd); + if (!imageSourceApi) { + hilog.error(0x0000, TAG, '%{public}s', 'imageSourceAPI created failed!'); + return; + } + const pixelMap = await imageSourceApi.createPixelMap({ + editable: true + }); + return pixelMap; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/EncodeUtil.ets b/ImageEditTaskPool/entry/src/main/ets/utils/EncodeUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..088c94e89dd512fdd692bf848b6e51e7ca9942f2 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/utils/EncodeUtil.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { promptAction } from '@kit.ArkUI'; +import { fileIo } from '@kit.CoreFileKit'; +import { image } from '@kit.ImageKit'; + +const TAG: string = '[EncodeUtil]'; +const context = getContext(this); +let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); + +/** + * Pack the image. + * + * @param pixelMap. + */ +export async function encode(component: Object, pixelMap: PixelMap) { + hilog.info(0x0000, TAG, '%{public}s', `component is ${component}`); + const newPixelMap = pixelMap; + // Packing image. + const imagePackerApi = image.createImagePacker(); + const packOptions: image.PackingOption = { + format: 'image/jpeg', + quality: 100 + } + const imageData = await imagePackerApi.packing(newPixelMap, packOptions); + hilog.info(0x0000, TAG, '%{public}s', `imageData's length is ${imageData.byteLength}`); + // Create image asset. + let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE; + let extension: string = 'jpg'; + phAccessHelper.createAsset(photoType, extension, (err, uri) => { + if (err) { + hilog.error(0x0000, TAG, 'createAsset ', JSON.stringify(err) ?? ''); + } + if (uri != undefined) { + let file = fileIo.openSync(uri, fileIo.OpenMode.READ_WRITE); + fileIo.writeSync(file.fd, imageData); + fileIo.close(file.fd); + promptAction.showToast({ message: $r('app.string.image_save') }); + } + }); +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/OpacityUtil.ets b/ImageEditTaskPool/entry/src/main/ets/utils/OpacityUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..ccc04bdda80387a96bd969fc63294f692b404317 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/utils/OpacityUtil.ets @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BusinessError } from '@kit.BasicServicesKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { image } from '@kit.ImageKit'; + +const TAG = '[OpacityUtil]'; + +/** + * Opacity adjust. + * + * @param pixelMap. + * @param value. + * @return pixelMap. + */ +export async function adjustOpacity(pixelMap: image.PixelMap, value: number): Promise { + if (!pixelMap) { + return; + } + const newPixelMap = pixelMap; + await newPixelMap.opacity(value / 100).then(() => { + hilog.info(0x0000, TAG, '%{public}s', 'Success in setting opacity.'); + }).catch((err: BusinessError) => { + hilog.error(0x0000, TAG, '%{public}s', `Failed to set opacity: ${JSON.stringify(err.message)}`); + }) + + return newPixelMap; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets b/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets new file mode 100644 index 0000000000000000000000000000000000000000..2dfc096810ae1c0800372b410c35d75dc17df270 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { taskpool } from '@kit.ArkTS'; +import { image } from '@kit.ImageKit'; +import { adjustIconList, IconStatus } from '../viewModel/IconListViewModel'; +import { adjustImageValue, adjustSaturation } from '../utils/AdjustUtil'; +import { AdjustId } from '../viewModel/OptionViewModel'; +import { adjustOpacity } from '../utils/OpacityUtil'; + +@Component +export default struct AdjustContentView { + @State currentAdjustIndex: number = 0; + @Consume('currentAdjustData') currentAdjustData: Array; + adjustIconList: Array = adjustIconList; + + @Builder + TabBuilder(index: number, name: ResourceStr) { + Column() { + Row() { + Image(this.currentAdjustIndex === index ? this.adjustIconList[index]?.chosen : + this.adjustIconList[index]?.normal) + .width(24) + .height(24) + } + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + .backgroundColor('#333333') + .borderRadius(20) + .width(40) + .height(40) + + Text(name) + .fontColor(this.currentAdjustIndex === index ? '#006CDE' : Color.White) + .fontSize(10) + .padding({ top: 8 }) + } + .width('100%') + } + + build() { + Tabs({ barPosition: BarPosition.End }) { + TabContent() { + Column() { + SliderCustom({ + currentIndex: AdjustId.BRIGHTNESS.valueOf(), + min: 1, + max: 100, + currentAdjustData: this.currentAdjustData + }) + } + .justifyContent(FlexAlign.End) + .height('100%') + .padding({ + bottom: 24 + }) + } + .tabBar(this.TabBuilder(AdjustId.BRIGHTNESS, $r('app.string.brightness'))) + + TabContent() { + Column() { + SliderCustom({ + currentIndex: AdjustId.TRANSPARENCY.valueOf(), + min: 1, + max: 100, + currentAdjustData: this.currentAdjustData + }) + } + .justifyContent(FlexAlign.End) + .height('100%') + .padding({ + bottom: 24 + }) + } + .tabBar(this.TabBuilder(AdjustId.TRANSPARENCY, $r('app.string.transparency'))) + + TabContent() { + Column() { + SliderCustom({ + currentIndex: AdjustId.SATURATION.valueOf(), + min: 1, + max: 100, + currentAdjustData: this.currentAdjustData + }) + } + .justifyContent(FlexAlign.End) + .height('100%') + .padding({ + bottom: 24 + }) + } + .tabBar(this.TabBuilder(AdjustId.SATURATION, $r('app.string.saturation'))) + } + .barHeight(60) + .padding({ bottom: 24 }) + .onChange((index: number) => { + this.currentAdjustIndex = index; + }) + } +} + +@Component +struct SliderCustom { + @Prop currentIndex: number; + @Link currentAdjustData: number[]; + @Prop min: number; + @Prop max: number; + @Consume('pixelMap') pixelMap?: image.PixelMap; + @Consume('isPixelMapChange') isPixelMapChange: boolean; + private postState: boolean = true; + saturationLastSlider: number = 100; + brightnessLastSlider: number = 100; + deviceListDialogController: CustomDialogController = new CustomDialogController({ + builder: Dialog(), + alignment: DialogAlignment.Center, + autoCancel: false, + customStyle: true + }); + + build() { + Column() { + Text(`${this.currentAdjustData[this.currentIndex]}`) + .fontColor(Color.White) + .margin({ top: -24 }) + .fontSize(16) + Row() { + Slider({ + value: this.currentAdjustData[this.currentIndex], + step: 10, + min: this.min, + max: this.max + }) + .height(20) + .blockColor(Color.White) + .selectedColor('#E6E6E6') + .trackColor('#333333') + .width('100%') + .showSteps(true) + .padding({ + right: 60, + left: 60 + }) + .onChange((value: number, mode: SliderChangeMode) => { + this.sliderChange(value > this.max ? this.max : value, mode); + }) + } + .width('100%') + .justifyContent(FlexAlign.End) + } + } + + sliderChange(value: number, mode: SliderChangeMode): void { + if ((mode === SliderChangeMode.End) && (value !== this.currentAdjustData[this.currentIndex])) { + this.currentAdjustData[this.currentIndex] = Math.round(value); + switch (this.currentIndex) { + case AdjustId.BRIGHTNESS: + this.postProcess(AdjustId.BRIGHTNESS, value); + break; + case AdjustId.TRANSPARENCY: + if (this.pixelMap) { + adjustOpacity(this.pixelMap, Math.round(value)) + .then((pixelMap?: image.PixelMap) => { + if (pixelMap) { + this.pixelMap = pixelMap; + this.isPixelMapChange = !this.isPixelMapChange; + } + }); + } + break; + case AdjustId.SATURATION: + this.postProcess(AdjustId.SATURATION, value); + break; + default: + break; + } + } + } + + async postProcess(type: AdjustId, value: number) { + if (!this.pixelMap) { + return; + } + let sliderValue = type === AdjustId.BRIGHTNESS ? this.brightnessLastSlider : this.saturationLastSlider; + const bufferArray = new ArrayBuffer(this.pixelMap.getPixelBytesNumber()); + this.pixelMap.readPixelsToBuffer(bufferArray) + .then(() => { + const buffers: ArrayBuffer[] = splitArrayBuffer(bufferArray, 240); + const group = splitTask(buffers, type, sliderValue, value); + taskpool.execute(group, taskpool.Priority.HIGH).then((ret) => { + // Combine the results of each task execution + const entireArrayBuffer = mergeArrayBuffers(ret); + // Update the UI based on the calculation results + this.updatePixelMap(entireArrayBuffer); + }); + if (this.postState) { + this.deviceListDialogController.open(); + } + this.postState = false; + if (type === AdjustId.BRIGHTNESS) { + this.brightnessLastSlider = Math.round(value); + } else { + this.saturationLastSlider = Math.round(value); + } + }) + } + + updatePixelMap(ret: ArrayBuffer) { + const newPixel = this.pixelMap as image.PixelMap; + newPixel.writeBufferToPixels(ret); + this.pixelMap = newPixel; + this.isPixelMapChange = !this.isPixelMapChange; + this.deviceListDialogController.close(); + this.postState = true; + } +} + +@CustomDialog +export struct Dialog { + controller?: CustomDialogController; + + build() { + Column() { + LoadingProgress() + .color(Color.White) + .width('30%') + .height('30%') + } + } +} + + +/** + * Each task processes a portion of the pixel data and adds the task to the task group. + * + */ +function splitTask(buffers: ArrayBuffer[], type: AdjustId, sliderValue: number, value: number): taskpool.TaskGroup { + // Creating a Task Group + let group: taskpool.TaskGroup = new taskpool.TaskGroup(); + for (const buffer of buffers) { + group.addTask(imageProcessing, { + // Add a task to a task group + type, + bufferArray: buffer, + sliderValue, + value + }); + } + return group; +} + +@Concurrent +async function imageProcessing(args: ImageProcessing) { + const type: AdjustId = args.type; + const bufferArray: ArrayBuffer = args.bufferArray; + const value: number = args.value; + const sliderValue: number = args.sliderValue; + if (type === AdjustId.BRIGHTNESS) { + return adjustImageValue(bufferArray, sliderValue, value); + } else if (type === AdjustId.SATURATION) { + return adjustSaturation(bufferArray, sliderValue, value); + } else { + return undefined; + } +} + +interface ImageProcessing { + type: AdjustId; + bufferArray: ArrayBuffer; + sliderValue: number; + value: number; +} + +function splitArrayBuffer(buffer: ArrayBuffer, n: number): ArrayBuffer[] { + let num = Math.floor(buffer.byteLength / n); + while (num % 4 !== 0) { + num += 1; + } + let result: ArrayBuffer[] = []; + for (let index = 0; index < n; index++) { + if (index === n - 1) { + result[index] = buffer.slice(index * num); + } else { + result[index] = buffer.slice(index * num, (index + 1) * num); + } + } + return result; +} + +function mergeArrayBuffers(buffers: Object[]) { + let thisBuffers = buffers as ArrayBuffer[]; + // Calculate the combined total length + let totalLength = thisBuffers.reduce((length, buffer) => { + length += buffer.byteLength; + return length; + }, 0); + // Create a new ArrayBuffer + let mergedBuffer = new ArrayBuffer(totalLength); + // Create a Uint8Array to operate the new Uint8Array + let mergedArray = new Uint8Array(mergedBuffer); + let offset = 0; + for (let buffer of thisBuffers) { + let array = new Uint8Array(buffer); + mergedArray.set(array, offset); + offset += array.length; + } + return mergedBuffer; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/view/CommBackgroundIcon.ets b/ImageEditTaskPool/entry/src/main/ets/view/CommBackgroundIcon.ets new file mode 100644 index 0000000000000000000000000000000000000000..3df69270e540b7740b005e943c1ecb8345f80c85 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/view/CommBackgroundIcon.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@Component +export struct CommBackgroundIcon { + @Prop icon: Resource; + private onClickImage: () => void = () => { + }; + + build() { + Row() { + Image(this.icon) + .width(24) + .height(24) + } + .onClick(() => { + this.onClickImage(); + }) + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + .backgroundColor('#333333') + .borderRadius(20) + .width(40) + .height(40) + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/view/TitleBar.ets b/ImageEditTaskPool/entry/src/main/ets/view/TitleBar.ets new file mode 100644 index 0000000000000000000000000000000000000000..5777aec071cd02fcef9c9658292bd912c2c6d3d9 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/view/TitleBar.ets @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommBackgroundIcon } from './CommBackgroundIcon'; + +@Component +export default struct TitleBar { + private onBack: () => void = () => { + }; + private onReset: () => void = () => { + }; + private onSave: () => void = () => { + }; + + build() { + Row() { + Row() { + CommBackgroundIcon({ + icon: $r('app.media.ic_backward'), + onClickImage: () => { + this.onBack(); + } + }) + Text($r('app.string.image_edit')) + .fontSize(20) + .fontColor(Color.White) + .margin({ left: 8 }) + } + + Row() { + CommBackgroundIcon({ + icon: $r('app.media.ic_reset'), + onClickImage: () => { + this.onReset(); + } + }) + Blank().width(8) + CommBackgroundIcon({ + icon: $r('app.media.ic_save'), + onClickImage: () => { + this.onSave(); + } + }) + } + } + .padding({ + right: 16, + left: 16 + }) + .justifyContent(FlexAlign.SpaceBetween) + .width('100%') + .backgroundColor(Color.Black) + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/viewModel/IconListViewModel.ets b/ImageEditTaskPool/entry/src/main/ets/viewModel/IconListViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..88b3bb11e7a2a769c52a6b868b146e157baba737 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/viewModel/IconListViewModel.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Icon status. + */ +export class IconStatus { + normal: Resource; + chosen: Resource; + + constructor(normal: Resource, chosen: Resource) { + this.normal = normal; + this.chosen = chosen; + } +} + +/** + * Bottom menu icon. + */ +export const menuIconList: Array = [ + new IconStatus($r('app.media.ic_crop_rotate_white'), $r('app.media.ic_crop_rotate_blue')), + new IconStatus($r('app.media.ic_rotate'), $r('app.media.ic_rotate_filled')), + new IconStatus($r('app.media.ic_adjust'), $r('app.media.ic_adjust_filled')) +] + +/** + * Crop icon. + */ +export const cropIconChangeList: Array = [ + new IconStatus($r('app.media.ic_original'), $r('app.media.ic_original_filled')), + new IconStatus($r('app.media.ic_one2one'), $r('app.media.ic_one2one_filled')), + new IconStatus($r('app.media.ic_four2three'), $r('app.media.ic_four2three_filled')), + new IconStatus($r('app.media.ic_sixteen2nine'), $r('app.media.ic_sixteen2nine_filled')) +] + +/** + * Adjust icon. + */ +export const adjustIconList: Array = [ + new IconStatus($r('app.media.ic_brightness'), $r('app.media.ic_brightness_filled')), + new IconStatus($r('app.media.ic_transparency_lock_white'), $r('app.media.ic_transparency_lock_blue')), + new IconStatus($r('app.media.ic_saturation'), $r('app.media.ic_saturation_filled')) +] \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/viewModel/MessageItem.ets b/ImageEditTaskPool/entry/src/main/ets/viewModel/MessageItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..b8a8f97706a5a54d3f44317080d05d7b1d976e53 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/viewModel/MessageItem.ets @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Multithreading transmission message. + */ +export class MessageItem { + constructor(buf: ArrayBuffer, last: number, cur: number) { + this.buf = buf; + this.last = last; + this.cur = cur; + } + + /** + * Send buffers. + */ + buf: ArrayBuffer; + + /** + * Last slider value. + */ + last: number; + + /** + * Current slider value. + */ + cur: number; +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/viewModel/OptionViewModel.ts b/ImageEditTaskPool/entry/src/main/ets/viewModel/OptionViewModel.ts new file mode 100644 index 0000000000000000000000000000000000000000..795289e77ae460d6670c54e81bdb2c8958f09e9a --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/viewModel/OptionViewModel.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Crop type. + */ +export enum CropType { + ORIGINAL_IMAGE, + SQUARE, + BANNER, + RECTANGLE +} + +/** + * Rotate type + */ +export enum RotateType { + CLOCKWISE, + ANTI_CLOCK +} + +/** + * Adjust type. + */ +export enum AdjustId { + BRIGHTNESS, + TRANSPARENCY, + SATURATION +} + +/** + * Main page tab type. + */ +export enum MainTabId { + CROP, + ROTATE, + ADJUST +} + +/** + * RGB color, red,green and blue. + */ +export enum RGBIndex { + RED, + GREEN, + BLUE +} + +/** + * HSV type. + */ +export enum HSVIndex { + HUE, + SATURATION, + VALUE +} + +/** + * Angel range. + */ +export enum AngelRange { + ANGEL_0_60, + ANGEL_60_120, + ANGEL_120_180, + ANGEL_180_240, + ANGEL_240_300, + ANGEL_300_360 +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/viewModel/RegionItem.ets b/ImageEditTaskPool/entry/src/main/ets/viewModel/RegionItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..dd0e30be03c753c69cf6bc5186c8bd7e28e27449 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/ets/viewModel/RegionItem.ets @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License,Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class RegionItem { + /** + * width coordinate. + */ + x: number; + + /** + * height coordinate. + */ + y: number; + + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/module.json5 b/ImageEditTaskPool/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0381c7f68e0691d7ac52e929074c612d5a3c9798 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/module.json5 @@ -0,0 +1,55 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "metadata": [ + { + "name": "ArkTSPartialUpdate", + "value": "true" + } + ], + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "orientation": "portrait", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.WRITE_IMAGEVIDEO", + "reason": "$string:reason", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/element/color.json b/ImageEditTaskPool/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/element/float.json b/ImageEditTaskPool/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..4a8a9740066889e7056295def412eaf37c355f51 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/element/float.json @@ -0,0 +1,76 @@ +{ + "float": [ + { + "name": "title_font_size", + "value": "20fp" + }, + { + "name": "category_font_size", + "value": "16fp" + }, + { + "name": "category_margin_top", + "value": "8vp" + }, + { + "name": "title_image_width", + "value": "24vp" + }, + { + "name": "title_image_height", + "value": "24vp" + }, + { + "name": "crop_image_width", + "value": "48vp" + }, + { + "name": "crop_image_height", + "value": "48vp" + }, + { + "name": "rotate_image_width", + "value": "36vp" + }, + { + "name": "rotate_image_height", + "value": "36vp" + }, + { + "name": "adjust_icon_width", + "value": "20vp" + }, + { + "name": "adjust_icon_height", + "value": "20vp" + }, + { + "name": "adjust_font_size", + "value": "12fp" + }, + { + "name": "adjust_margin_top", + "value": "8vp" + }, + { + "name": "adjust_margin_bottom", + "value": "10vp" + }, + { + "name": "slider_font_size", + "value": "16fp" + }, + { + "name": "slider_margin_top", + "value": "12vp" + }, + { + "name": "title_margin_top", + "value": "15vp" + }, + { + "name": "title_margin_left", + "value": "20vp" + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/element/string.json b/ImageEditTaskPool/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..51963c0cb86f7e6583e0b8d0850e3c747a2fc301 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/element/string.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "ImageEdit" + }, + { + "name": "reason", + "value": "Click the Save button in the upper right corner of the page to save the currently edited picture to the album." + }, + { + "name": "image_edit", + "value": "imageEdit" + }, + { + "name": "crop", + "value": "crop" + }, + { + "name": "rotate", + "value": "rotate" + }, + { + "name": "adjust", + "value": "adjust" + }, + { + "name": "brightness", + "value": "brightness" + }, + { + "name": "transparency", + "value": "transparency" + }, + { + "name": "saturation", + "value": "saturation" + }, + { + "name": "save_image", + "value": "saveImage" + }, + { + "name": "confirm_save", + "value": "are you sure to save image" + }, + { + "name": "save", + "value": "save" + }, + { + "name": "cancel", + "value": "cancel" + }, + { + "name": "exec_edit", + "value": "running exec edit." + }, + { + "name": "ok", + "value": "ok" + }, + { + "name": "image_save", + "value": "The picture has been saved to the album." + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust.png new file mode 100644 index 0000000000000000000000000000000000000000..0c925713c760486019aebb0700f6c1df5ddb2e36 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..34f56f2a40980db139d5669fcec4fabbf552bce9 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_adjust_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_anti_clockwise.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_anti_clockwise.png new file mode 100644 index 0000000000000000000000000000000000000000..262594ad4b902e5810a1bda18f04ae1c63170b8b Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_anti_clockwise.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_backward.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_backward.png new file mode 100644 index 0000000000000000000000000000000000000000..406c2c5241504d28ba90a2bd8d019468335186e1 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_backward.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness.png new file mode 100644 index 0000000000000000000000000000000000000000..79c5ff0f9f4ccc73f7f629f31e3fe1aea7469723 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..41777d105cd8d57c87c2f787bd4da5bb167efcb5 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_brightness_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_clockwise.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_clockwise.png new file mode 100644 index 0000000000000000000000000000000000000000..0b739c4ccea379a18704e849519b74bc685aec6a Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_clockwise.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_blue.svg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_blue.svg new file mode 100644 index 0000000000000000000000000000000000000000..0b090773db6a2f26834d74c2e7f8bc93d55aeb81 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_blue.svg @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_white.svg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..e880b2a25890a8306dcc2463c7d0b6b1705f0451 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_crop_rotate_white.svg @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three.png new file mode 100644 index 0000000000000000000000000000000000000000..8a5c7f27f01100535e13585ffd58c6add77bb94d Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..f52ebeefdaa8ac7289c787cbcff6e1256981d1bd Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_four2three_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_low.jpg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_low.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65d34e3b43b750029cb6d66c7cf392aa500a2b90 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_low.jpg differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one.png new file mode 100644 index 0000000000000000000000000000000000000000..ed831b5419c1cab025b66acad8275e9cb0df4f40 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..1af69bf4201819505459932f8f88312abe9c305b Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_one2one_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original.png new file mode 100644 index 0000000000000000000000000000000000000000..5a742e402154f9931370e343d29979ef3846b999 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..96a4f39bc742f4ee9a99ef8938bfb6e78395b476 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_original_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_reset.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_reset.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac9af2768caf6d9b79da3bf798d01e153e9820f Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_reset.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..9620f9967a3b95d0884499e6c71dd49bcbb8684a Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..acea9c87a0960d5d2d00ffb8c84e86da4add2d2e Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_rotate_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation.png new file mode 100644 index 0000000000000000000000000000000000000000..157bf72c4f3931170252f31c667580a91bb197ee Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..ea1aa3f4e29af6a8097bf9b2f9e6d72aed3ce4ff Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_saturation_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_save.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_save.png new file mode 100644 index 0000000000000000000000000000000000000000..0f6599c303b99a0e3cf0078d802068b7d5f33255 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_save.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine.png new file mode 100644 index 0000000000000000000000000000000000000000..c74c7ed34801825647ebaefabb46b75d7dfc9d62 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..6b686b9203ce2290d7210101aa4ef816544c188f Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_sixteen2nine_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency.png new file mode 100644 index 0000000000000000000000000000000000000000..c3c52ab3823bf80d8312f416b4f5f5b9c39f5129 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_filled.png b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..88b01b99f0af1156d24280be7aa9b7f246c17e3c Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_filled.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock.svg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock.svg new file mode 100644 index 0000000000000000000000000000000000000000..9ab3b02d259e740fb4d450dd5f591fde2fd87157 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock.svg @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_blue.svg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_blue.svg new file mode 100644 index 0000000000000000000000000000000000000000..873e75cfecba8a5936a562261aa9296fc0e77662 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_blue.svg @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_white.svg b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..2870aa88fae69f11289b093f7d435fb9b8b145ed --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/media/ic_transparency_lock_white.svg @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/base/media/icon.png b/ImageEditTaskPool/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/base/media/icon.png differ diff --git a/ImageEditTaskPool/entry/src/main/resources/base/profile/main_pages.json b/ImageEditTaskPool/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b16dfa5fe61f19a4e4fc6f1e25708dda21e01ac5 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/HomePage" + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/en_US/element/string.json b/ImageEditTaskPool/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..51963c0cb86f7e6583e0b8d0850e3c747a2fc301 --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "ImageEdit" + }, + { + "name": "reason", + "value": "Click the Save button in the upper right corner of the page to save the currently edited picture to the album." + }, + { + "name": "image_edit", + "value": "imageEdit" + }, + { + "name": "crop", + "value": "crop" + }, + { + "name": "rotate", + "value": "rotate" + }, + { + "name": "adjust", + "value": "adjust" + }, + { + "name": "brightness", + "value": "brightness" + }, + { + "name": "transparency", + "value": "transparency" + }, + { + "name": "saturation", + "value": "saturation" + }, + { + "name": "save_image", + "value": "saveImage" + }, + { + "name": "confirm_save", + "value": "are you sure to save image" + }, + { + "name": "save", + "value": "save" + }, + { + "name": "cancel", + "value": "cancel" + }, + { + "name": "exec_edit", + "value": "running exec edit." + }, + { + "name": "ok", + "value": "ok" + }, + { + "name": "image_save", + "value": "The picture has been saved to the album." + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/resources/rawfile/low.jpg b/ImageEditTaskPool/entry/src/main/resources/rawfile/low.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65d34e3b43b750029cb6d66c7cf392aa500a2b90 Binary files /dev/null and b/ImageEditTaskPool/entry/src/main/resources/rawfile/low.jpg differ diff --git a/ImageEditTaskPool/entry/src/main/resources/zh_CN/element/string.json b/ImageEditTaskPool/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0b49559fa78365f932c8cb1bcd8443078b15441d --- /dev/null +++ b/ImageEditTaskPool/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "ImageEdit" + }, + { + "name": "reason", + "value": "点击页面右上角的保存按钮,把当前编辑的图片保存至相册。" + }, + { + "name": "image_edit", + "value": "图片编辑" + }, + { + "name": "crop", + "value": "裁剪" + }, + { + "name": "rotate", + "value": "旋转" + }, + { + "name": "adjust", + "value": "调节" + }, + { + "name": "brightness", + "value": "亮度" + }, + { + "name": "transparency", + "value": "透明度" + }, + { + "name": "saturation", + "value": "饱和度" + }, + { + "name": "save_image", + "value": "保存图片" + }, + { + "name": "confirm_save", + "value": "确定要保存图片吗" + }, + { + "name": "save", + "value": "保存" + }, + { + "name": "cancel", + "value": "取消" + }, + { + "name": "exec_edit", + "value": "正在执行编辑" + }, + { + "name": "ok", + "value": "确认" + }, + { + "name": "image_save", + "value": "图片已保存至相册" + } + ] +} \ No newline at end of file diff --git a/ImageEditTaskPool/hvigor/hvigor-config.json5 b/ImageEditTaskPool/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f70ecd4112d94f9aa555adf898d53f18bf58f3e9 --- /dev/null +++ b/ImageEditTaskPool/hvigor/hvigor-config.json5 @@ -0,0 +1,5 @@ +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} \ No newline at end of file diff --git a/ImageEditTaskPool/hvigorfile.ts b/ImageEditTaskPool/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..6478186902c0c1ad7c966a929c7d6b7d8ae7a9f3 --- /dev/null +++ b/ImageEditTaskPool/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { appTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/ImageEditTaskPool/oh-package.json5 b/ImageEditTaskPool/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0102ccfffc04d7eb3cf77190f9a71a15f28b9765 --- /dev/null +++ b/ImageEditTaskPool/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "modelVersion": "5.0.0", + "license": "", + "devDependencies": { + }, + "author": "", + "name": "imageedit", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/ImageEditTaskPool/screenshots/device/ImageEditTaskPool_CN.gif b/ImageEditTaskPool/screenshots/device/ImageEditTaskPool_CN.gif new file mode 100644 index 0000000000000000000000000000000000000000..e622d909a17c9c1a1002dc6f6c25078c8d7bdb17 Binary files /dev/null and b/ImageEditTaskPool/screenshots/device/ImageEditTaskPool_CN.gif differ