• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 */
15import router from '@ohos.router';
16import image from '@ohos.multimedia.image';
17import effectKit from '@ohos.effectKit';
18import { logger } from '../util/Logger';
19import { copyPixelMap } from '../util/CopyObj';
20import { adjustDatas, cropTaskDatas, markDatas } from '../model/AdjustModels';
21import { CropTasks, TaskData, Tasks } from '../model/AdjustData';
22import { ScalePhotoPage } from '../pages/ScalePhotoPage';
23import MaterialEdit from './MaterialEdit';
24import InputTextDialog from './InputTextDialog';
25import LoadingDialog from './LoadingDialog';
26import PixelMapQueue from '../feature/PixelMapQueue';
27import { readImage } from '../util/MediaUtil';
28import { FontColorData, MaterialData } from '../model/MaterialData';
29import { fontColors, stickers } from '../model/MaterialModels';
30import { getContainSize, Size } from '../util/ImageUtil';
31import { savePixelMap } from '../util/FileUtil';
32import { BusinessError } from '@ohos.base';
33
34const TAG: string = '[Sample_EditImage]';
35
36@Component
37export struct EditImage {
38  @State mediaUris: string = (router.getParams() as Record<string, Object>)['mediaUris'] as string;
39  @State pixelMap: PixelMap | undefined | null = undefined;
40  @State adjustMarkJudg: boolean = false;
41  @State currentTask: number = Tasks.ADJUST;
42  @State currentCropTask: number = CropTasks.NONE;
43  @State tempPixelMap: image.PixelMap | undefined | null = undefined;
44  @State cancelOkText: Resource | null = null;
45  @State brightnessValue: number = 0;
46  @State outSetValueOne: number = 0;
47  @State cropFlag: boolean = false;
48  @State originBM: PixelMap | null = null;
49  @State cropIndex: number = 0;
50  @State canClick: boolean = true;
51  @State loading: boolean = false;
52  @State isCancelQuit: boolean = false;
53  // 选择资源素材index
54  @State resourceIndex: number = 0;
55  @State isTextMaterial: boolean = true;
56  selectedResource: Resource | undefined = $r('app.color.material_font_color_one');
57  scroller: Scroller = new Scroller();
58  brightnessOriginBM: PixelMap | null | undefined = null;
59  @State resolution: string = '';
60  @State inputValue: string = '';
61  // 分辨率的坐标
62  @State dpiX: number = 0;
63  @State dpiY: number = 0;
64  // 分辨率
65  @State dpi: string = '';
66  // 图片父容器大小
67  containerArea: Area | null = null;
68  private pixelMapQueue: PixelMapQueue = new PixelMapQueue();
69
70  onOpen(): void {
71    if (this.loading) {
72      return;
73    }
74    this.loading = true;
75    this.loadingController.open();
76  }
77
78  onClose(): void {
79    this.loading = false;
80    this.loadingController.close();
81  }
82
83
84  /**
85   * 输入文字素材弹窗
86   */
87  dialogController: CustomDialogController | undefined = new CustomDialogController({
88    builder: InputTextDialog({
89      cancel: (): void => this.onCancel(),
90      confirm: (): void => this.onAccept(),
91      inputValue: $inputValue
92    }),
93    cancel: (): void => this.existApp(),
94    autoCancel: true,
95    alignment: DialogAlignment.Bottom,
96    offset: { dx: 0, dy: -20 },
97    gridCount: 4,
98    customStyle: false
99  });
100
101  /**
102   * 加载弹窗
103   */
104  loadingController: CustomDialogController = new CustomDialogController({ builder: LoadingDialog() });
105
106  onCancel(): void {
107    console.info('Callback when the first button is clicked');
108  }
109
110  onAccept(): void {
111    if (this.inputValue !== null && this.inputValue.length > 0) {
112      this.currentTask = Tasks.TEXT;
113    }
114    this.onSelectItem(markDatas[0], true);
115  }
116
117  async onEditCancel(): Promise<void> {
118    if (this.currentTask === Tasks.CROP || this.currentTask === Tasks.SCALE ||
119    this.currentTask === Tasks.ROTATE || this.currentTask === Tasks.TONING) {
120      this.currentTask = Tasks.ADJUST;
121      this.cropIndex = 0;
122    }
123    if (this.currentTask === Tasks.TEXT || this.currentTask === Tasks.STICKER) {
124      this.currentTask = Tasks.MARK;
125      this.cropIndex = 0;
126    }
127    if (this.currentTask === Tasks.CROP) {
128      this.cropIndex = 0;
129    }
130    this.isCancelQuit = true;
131    this.outSetValueOne = 0;
132    if (this.originBM !== null && this.originBM !== undefined) {
133      this.pixelMap = await copyPixelMap(this.originBM); // 拷贝
134    }
135    // 移出一个缓存
136    const pm: image.PixelMap|undefined = this.pixelMapQueue.pop();
137    this.releasePm(pm);
138  }
139
140  existApp(): void {
141    console.info('Click the callback in the blank area')
142  }
143
144  async aboutToAppear(): Promise<void> {
145    this.pixelMap = await readImage(this.mediaUris);
146    if (this.pixelMap !== null) {
147      this.originBM = await copyPixelMap(this.pixelMap);
148    }
149  }
150
151  aboutToDisappear(): void {
152    this.dialogController = undefined
153  }
154
155  /**
156   * 选择子菜单
157   * @param item
158   * @param hasInputText
159   */
160  async onSelectItem(item: TaskData, hasInputText: boolean = false): Promise<void> {
161    if (hasInputText || item.task !== Tasks.TEXT) {
162      // 只有hasInputText,才是图片素材模式,其他都是贴纸
163      this.isTextMaterial = hasInputText;
164      this.resourceIndex = 0;
165      this.isCancelQuit = false;
166      if (item.task !== undefined) {
167        this.currentTask = item.task;
168      }
169
170      if (item.text !== undefined) {
171        this.cancelOkText = item.text;
172      }
173
174      if (this.pixelMap) {
175        this.originBM = await copyPixelMap(this.pixelMap);
176      }
177      // 保存到队列
178      if (this.originBM !== null && this.originBM !== undefined) {
179        this.pixelMapQueue.push(this.originBM);
180      }
181    } else if (item.task === Tasks.TEXT) {
182      this.dialogController?.open();
183    }
184  }
185
186  /**
187   * 撤销功能
188   */
189  async repeal(): Promise<void> {
190    const pm: image.PixelMap | undefined = this.pixelMapQueue.pop();
191    if (pm !== null && pm !== undefined) {
192      this.pixelMap = await copyPixelMap(pm);
193    }
194  }
195
196  /**
197   * 刷新图层显示
198   */
199  flushPage(): void {
200    this.tempPixelMap = this.pixelMap;
201    this.pixelMap = null;
202    this.pixelMap = this.tempPixelMap;
203  }
204
205  /**
206   * 亮度调节
207   */
208  async brightChange(): Promise<void> {
209    let headFilter: effectKit.Filter = effectKit.createEffect(this.brightnessOriginBM);
210    if (headFilter !== null) {
211      headFilter.brightness(this.brightnessValue);
212      this.pixelMap = headFilter.getPixelMap();
213      this.flushPage();
214    }
215  }
216
217  /**
218   * 图片裁剪
219   * @param proportion
220   */
221  async cropImage(proportion: number): Promise<void> {
222    if (!this.pixelMap) {
223      return;
224    }
225    let imageInfo: image.ImageInfo = await this.pixelMap.getImageInfo();
226    if (!imageInfo) {
227      return;
228    }
229    let imageHeight: number = imageInfo.size.height;
230    let imageWith: number = imageInfo.size.width;
231    logger.info(TAG, `imageInfo = ${JSON.stringify(imageInfo)}`);
232    if (proportion === 1) {
233      if (imageHeight > imageWith) {
234        imageHeight = imageWith;
235      } else {
236        imageWith = imageHeight;
237      }
238      logger.info(TAG, `imageHeight = ${JSON.stringify(imageHeight)},imageWith = ${JSON.stringify(imageWith)}`);
239    }
240    try {
241      await this.pixelMap.crop({
242        size: { height: imageHeight / proportion, width: imageWith },
243        x: 0,
244        y: 0
245      });
246    } catch (error) {
247      logger.info(TAG, `crop error = ${JSON.stringify(error)}`);
248    }
249    this.flushPage();
250  }
251
252  /**
253   * 底部一级菜单
254   */
255  @Builder
256  getFirstLvMenu() {
257    Row() {
258      Column() {
259        Image($r('app.media.ic_adjust'))
260          .width($r('app.float.size_30'))
261          .height($r('app.float.size_30'))
262        Text($r('app.string.edit_image_adjust'))
263          .fontColor(Color.White)
264          .fontSize($r('app.float.size_16'))
265      }
266      .justifyContent(FlexAlign.Center)
267      .height('100%')
268      .width('40%')
269      .margin({ left: '10%' })
270      .backgroundColor(this.adjustMarkJudg ? Color.Black : $r('app.color.edit_image_adjust_selected'))
271      .onClick(async () => {
272        this.adjustMarkJudg = false;
273        this.currentTask = Tasks.ADJUST;
274      })
275
276      Column() {
277        Image($r('app.media.ic_mark'))
278          .width($r('app.float.size_30'))
279          .height($r('app.float.size_30'))
280        Text($r('app.string.edit_image_mark'))
281          .fontColor(Color.White)
282          .fontSize($r('app.float.size_16'))
283      }
284      .justifyContent(FlexAlign.Center)
285      .onClick(() => {
286        this.adjustMarkJudg = true;
287        this.currentTask = Tasks.MARK;
288      })
289      .backgroundColor(this.adjustMarkJudg ? $r('app.color.edit_image_adjust_selected') : Color.Black)
290      .height('100%')
291      .width('40%')
292      .margin({ right: '10%' })
293    }
294    .height('9%')
295    .width('100%')
296  }
297
298  releasePm(pm: PixelMap | undefined): void {
299    if(!pm){
300      return
301    }
302
303    pm.release().catch((err:BusinessError) => {
304      logger.error('pm release异常:' + JSON.stringify(err));
305    });
306  }
307
308  @Builder
309  CancelOrOk(text: string | Resource) {
310    Row() {
311      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {
312        Image($r('app.media.ic_cancel'))
313          .width($r('app.float.size_30'))
314          .height($r('app.float.size_30'))
315          .onClick(async () => {
316            await this.onEditCancel();
317          })
318          .margin({ left: '10%' })
319          .id('Cancel')
320
321        Text(text)
322          .fontColor(Color.White)
323          .fontSize($r('app.float.size_18'))
324          .width($r('app.float.size_40'))
325          .height($r('app.float.size_30'))
326
327        Image($r('app.media.ic_ok'))
328          .width($r('app.float.size_30'))
329          .height($r('app.float.size_30'))
330          .margin({ right: '10%' })
331          .onClick(() => {
332            if (this.currentTask === Tasks.CROP || this.currentTask === Tasks.SCALE ||
333            this.currentTask === Tasks.ROTATE || this.currentTask === Tasks.TONING) {
334              this.currentTask = Tasks.ADJUST;
335              this.cropIndex = 0;
336            }
337            if (this.currentTask === Tasks.TEXT || this.currentTask === Tasks.STICKER) {
338              this.currentTask = Tasks.MARK;
339              this.cropIndex = 0;
340            }
341            this.outSetValueOne = 0;
342          })
343          .id('Ok')
344      }
345    }
346    .backgroundColor($r('app.color.edit_image_public_background'))
347    .height('9%')
348    .width('100%')
349  }
350
351  @Builder
352  getMarkTool() {
353    Row() {
354      List() {
355        ForEach(markDatas, (item: TaskData, index) => {
356          ListItem() {
357            Column() {
358              Image(item.image)
359                .width($r('app.float.size_30'))
360                .height($r('app.float.size_30'))
361              Text(item.text)
362                .fontColor(Color.White)
363                .fontSize($r('app.float.size_15'))
364                .margin({ top: $r('app.float.size_5') })
365            }
366            .justifyContent(FlexAlign.Center)
367            .height('100%')
368            .width('40%')
369            .margin(index === 0 ? { left: '10%' } : { right: '10%' })
370            .onClick(async () => {
371              this.onSelectItem(item);
372            })
373          }
374        })
375      }
376      .listDirection(Axis.Horizontal) // 排列方向
377      .width('100%')
378      .height('14%')
379    }
380    .backgroundColor($r('app.color.edit_image_public_background'))
381    .width('100%')
382    .justifyContent(FlexAlign.SpaceAround)
383    .alignItems(VerticalAlign.Center)
384  }
385
386  @Builder
387  getCropTool() {
388    Row() {
389      List() {
390        ForEach(cropTaskDatas, (item: TaskData, index: number) => {
391          ListItem() {
392            Column() {
393              Image(item.image)
394                .width($r('app.float.size_30'))
395                .height($r('app.float.size_30'))
396              Text(item.text)
397                .fontColor(Color.White)
398                .fontSize($r('app.float.size_14'))
399                .margin({ top: $r('app.float.size_5') })
400            }
401            .backgroundColor(this.cropIndex === index ? $r('app.color.edit_image_public_background') : $r('app.color.edit_image_crop_select'))
402            .justifyContent(FlexAlign.Center)
403            .height('100%')
404            .width('25%')
405            .onClick(async () => {
406              if (item.task !== undefined) {
407                this.currentCropTask = item.task;
408              }
409              this.pixelMap = await copyPixelMap(this.originBM); // 拷贝
410              this.flushPage();
411              this.cropIndex = index;
412              if (this.currentCropTask === CropTasks.ORIGIN) { // 原图
413                this.pixelMap = await readImage(this.mediaUris);
414              } else if (this.currentCropTask === CropTasks.ONE_ONE) {
415                await this.cropImage(1);
416                console.log(TAG + 'CropTasks  this.cropImage(1)' + this.currentCropTask)
417              } else if (this.currentCropTask === CropTasks.THREE_FOUR) {
418                await this.cropImage(4 / 3);
419                console.log(TAG + 'CropTasks  cropImage(4 / 3)==' + this.currentTask)
420              } else if (this.currentCropTask === CropTasks.NINE_SIXTH) {
421                await this.cropImage(16 / 9);
422                console.log(TAG + 'CropTasks  (16 / 9)==' + this.currentTask);
423              }
424            })
425          }
426        })
427      }
428      .listDirection(Axis.Horizontal) // 排列方向
429      .height('14%')
430    }
431    .width('100%')
432  }
433
434  @Builder
435  getAdjustTool() {
436    Row() {
437      List() {
438        ForEach(adjustDatas, (item: TaskData, index) => {
439          ListItem() {
440            Column() {
441              Image(item.image)
442                .width($r('app.float.size_30'))
443                .height($r('app.float.size_30'))
444              Text(item.text)
445                .fontColor(Color.White)
446                .fontSize($r('app.float.size_15'))
447                .margin({ top: $r('app.float.size_5') })
448            }
449            .justifyContent(FlexAlign.Center)
450            .height('100%')
451            .width('25%')
452            .onClick(async () => {
453              if (item.task !== undefined) {
454                this.currentTask = item.task;
455              }
456              if (this.currentTask === Tasks.SCALE) {
457                this.computeDpiPosition();
458              }
459              if (item.task === Tasks.TONING) {
460                this.brightnessOriginBM = this.pixelMap;
461              }
462              if (item.text !== undefined) {
463                this.cancelOkText = item.text
464              }
465              this.originBM = await copyPixelMap(this.pixelMap) // 拷贝
466              if (item.task === Tasks.ROTATE) {
467                this.pixelMap = await copyPixelMap(this.originBM);
468              }
469              // 保存到队列
470              this.pixelMapQueue.push(this.originBM);
471            })
472          }
473        })
474      }
475      .listDirection(Axis.Horizontal) // 排列方向
476      .height('14%')
477    }
478    .backgroundColor($r('app.color.edit_image_public_background'))
479    .width('100%')
480    .justifyContent(FlexAlign.SpaceAround)
481    .alignItems(VerticalAlign.Center)
482  }
483
484  @Builder
485  getScaleTool() {
486    ScalePhotoPage({ pixelMap: $pixelMap, dpi: $dpi })
487      .backgroundColor($r('app.color.edit_image_public_background'))
488      .height('14%')
489      .width('100%')
490      .padding({ top: $r('app.float.size_10') })
491  }
492
493  @Builder
494  getRotateTool() {
495    Row() {
496      Column() {
497        Image($r('app.media.ic_rotateto90'))
498          .width($r('app.float.size_30'))
499          .height($r('app.float.size_30'))
500        Text($r('app.string.edit_image_rotate_90'))
501          .margin({ top: $r('app.float.size_5') })
502          .fontSize($r('app.float.size_14'))
503          .fontColor(Color.White)
504      }
505      .id('90')
506      .onClick(async () => {
507        if (this.canClick) {
508          this.canClick = false;
509          await this.pixelMap?.rotate(90);
510          setTimeout(() => {
511            this.canClick = true;
512            this.flushPage();
513          }, 300)
514        }
515      })
516
517      Column() {
518        Image($r('app.media.ic_rotate'))
519          .width($r('app.float.size_30'))
520          .height($r('app.float.size_30'))
521        Text($r('app.string.edit_image_rotate_down_90'))
522          .margin({ top: $r('app.float.size_5') })
523          .fontColor(Color.White)
524          .fontSize($r('app.float.size_14'))
525      }
526      .id('-90')
527      .onClick(async () => {
528        if (this.canClick) {
529          this.canClick = false;
530          await this.pixelMap?.rotate(-90);
531          setTimeout(() => {
532            this.canClick = true;
533            this.flushPage();
534          }, 300);
535        }
536      })
537    }
538    .justifyContent(FlexAlign.SpaceAround)
539    .backgroundColor($r('app.color.edit_image_public_background'))
540    .height('14%')
541    .width('100%')
542  }
543
544  @Builder
545  getToningTool() {
546    Row() {
547      Row() {
548        Slider({
549          value: this.outSetValueOne,
550          min: 0,
551          max: 30,
552          style: SliderStyle.OutSet,
553        })
554          .id('Slider')
555          .trackThickness($r('app.float.size_5'))
556          .trackColor($r('app.color.edit_image_slider_trackColor'))
557          .selectedColor($r('app.color.edit_image_slider_selected'))
558          .onChange(async (value: number, mode: SliderChangeMode) => {
559            this.outSetValueOne = value;
560            this.brightnessValue = Number((value / 100).toFixed(2));
561            await this.brightChange();
562          })
563      }
564      .height('14%')
565      .width('96%')
566    }
567    .justifyContent(FlexAlign.Center)
568    .width('100%')
569    .backgroundColor($r('app.color.edit_image_public_background'))
570  }
571
572  @Builder
573  getMaterialTool(materials: MaterialData[]) {
574    this.TextOrStickerScroll(materials)
575  }
576
577  async onSave(): Promise<void> {
578    if (this.loading) {
579      return;
580    }
581    this.loading = true;
582    this.loadingController.open();
583
584    if (this.pixelMap !== undefined && this.pixelMap !== null) {
585      const uri: string = await savePixelMap(getContext(this), this.pixelMap);
586      logger.debug('保存图片地址为:' + uri);
587      router.pushUrl({
588        url: 'pages/Index',
589        params: { isShowCamera: true }
590      });
591    }
592
593  }
594
595  @Builder
596  TextOrStickerScroll(materials: MaterialData[]) {
597    Row() {
598      Scroll() {
599        List({ scroller: this.scroller }) {
600          ForEach(materials, (item: MaterialData, index:number) => {
601            ListItem() {
602              Column() {
603                if (item instanceof FontColorData) {
604                  Text(item.getResource())
605                    .visibility(Visibility.Hidden)
606                    .width($r('app.float.size_40'))
607                    .height($r('app.float.size_40'))
608                } else {
609                  Image(item.getResource())
610                    .width($r('app.float.size_40'))
611                    .height($r('app.float.size_40'))
612                }
613              }
614              .justifyContent(FlexAlign.Center)
615              .borderRadius($r('app.float.size_10'))
616              .border(this.resourceIndex === index ?
617                {
618                  width: $r('app.float.size_3'),
619                  color: $r('app.color.edit_image_mark_scroll_selected'),
620                  radius: $r('app.float.size_10')
621                } : { width: $r('app.float.size_0'), color: $r('app.color.edit_image_mark_scroll') })
622              .backgroundColor(this.currentTask === Tasks.STICKER ? $r('app.color.edit_image_mark_scroll') : item.getResource())
623              .width($r('app.float.size_45'))
624              .height($r('app.float.size_45'))
625              .onClick(() => {
626                this.resourceIndex = index;
627                this.selectedResource = item.getResource();
628              })
629            }
630            .height('100%')
631            .width('17%')
632          })
633        }
634        .listDirection(Axis.Horizontal) // 排列方向
635        .height('14%')
636      }
637      .padding({ left: $r('app.float.size_30'), right: $r('app.float.size_30') })
638      .scrollBar(BarState.Off)
639      .scrollable(ScrollDirection.Horizontal)
640    }
641    .alignItems(VerticalAlign.Center)
642    .backgroundColor($r('app.color.edit_image_public_background'))
643    .width('100%')
644  }
645
646  @Builder
647  getTopBar() {
648    Row() {
649      Image($r("app.media.ic_public_back"))
650        .fillColor(Color.White)
651        .width($r('app.float.size_32'))
652        .height($r('app.float.size_32'))
653        .onClick(() => {
654          router.back();
655        })
656      Blank()
657      // 非编辑模式,展示撤回和保存功能
658      if (this.currentTask === Tasks.ADJUST || this.currentTask === Tasks.MARK) {
659        Row({ space: 24 }) {
660          Image($r('app.media.ic_public_tosmall'))
661            .height($r('app.float.size_32'))
662            .width($r('app.float.size_32'))
663            .id('Repeal')
664            .onClick(async () => {
665              this.repeal();
666            })
667          Image($r('app.media.ic_public_save'))
668            .height($r('app.float.size_32'))
669            .width($r('app.float.size_32'))
670            .id('Save')
671            .onClick(() => {
672              this.onSave();
673            })
674        }.margin({ right: $r('app.float.size_10') })
675      }
676    }
677    .width('100%')
678    .padding({ left: $r('app.float.size_14') })
679    .margin({ top: $r('app.float.size_20') });
680  }
681
682  async computeDpiPosition(): Promise<void> {
683    if (this.containerArea === null || this.pixelMap === null || this.pixelMap === undefined) {
684      return;
685    }
686    let imageInfo: image.ImageInfo = await this.pixelMap.getImageInfo()
687    if (!imageInfo) {
688      return;
689    }
690    const imageHeight: number = imageInfo.size.height;
691    const imageWith: number = imageInfo.size.width;
692    this.dpi = imageWith + "*" + imageHeight;
693    // 计算容器宽高
694    const containerHeight: number = Number(this.containerArea.height);
695    const containerWidth: number = Number(this.containerArea.width);
696    const size: Size = getContainSize(containerWidth * 0.9, containerHeight * 0.9, imageWith, imageHeight);
697    // 计算左上角的坐标
698    const x: number = containerWidth / 2 + size.width / 2;
699    const y: number = containerHeight / 2 - size.height / 2;
700    // 最终坐标还要除去控件宽高
701    this.dpiX = x - 100;
702    this.dpiY = y;
703  }
704
705  getResolutionText(): string {
706    const tip: string = getContext(this).resourceManager.getStringSync($r('app.string.edit_image_resolution'));
707    return tip + this.dpi;
708  }
709
710  build() {
711    Column() {
712      // 顶部功能区
713      this.getTopBar()
714      // 中间操作区
715      if (this.currentTask === Tasks.TEXT || this.currentTask === Tasks.STICKER) {
716        Stack() {
717          MaterialEdit({
718            pixelMap: $pixelMap,
719            text: this.inputValue,
720            isCancelQuit: this.isCancelQuit,
721            selectedMaterialIndex: this.resourceIndex,
722            isTextMaterial: this.isTextMaterial,
723            onCancel: (): Promise<void> => this.onEditCancel(),
724            onOpen: (): void => this.onOpen(),
725            onClose: (): void => this.onClose()
726          }).width('90%')
727            .height('90%')
728        }.layoutWeight(1)
729      } else {
730        Stack({ alignContent: Alignment.Center }) {
731          Image(this.pixelMap)
732            .objectFit(ImageFit.Contain)
733            .width('90%')
734            .height('90%')
735            .backgroundColor($r('app.color.edit_image_stack_image'))
736            .alignRules(
737              {
738                middle: { anchor: '__container__', align: HorizontalAlign.Center },
739                center: { anchor: '__container__', align: VerticalAlign.Center }
740              }
741            )
742            .id('image')
743
744          if (this.currentTask === Tasks.SCALE) {
745            Stack() {
746              Text(this.getResolutionText())
747                .fontSize($r('app.float.size_14'))
748                .fontColor(Color.White)
749            }
750            .position({ x: this.dpiX, y: this.dpiY })
751            .backgroundColor($r('app.color.edit_image_stack_resolution'))
752            .id('dpi')
753            .padding($r('app.float.size_10'))
754            .width($r('app.float.size_100'))
755            .height($r('app.float.size_80'))
756            .border({ radius: $r('app.float.size_20') })
757          }
758        }.layoutWeight(1)
759        .onAreaChange((oldValue: Area, newValue: Area) => {
760          this.containerArea = newValue;
761        })
762      }
763      // 底部菜单栏
764      Column() {
765        if (this.currentTask === Tasks.MARK) {
766          this.getMarkTool();
767        } else if (this.currentTask === Tasks.ADJUST) {
768          this.getAdjustTool();
769        } else if (this.currentTask === Tasks.CROP) {
770          this.getCropTool();
771        } else if (this.currentTask === Tasks.SCALE) {
772          this.getScaleTool();
773        } else if (this.currentTask === Tasks.ROTATE) {
774          this.getRotateTool();
775        } else if (this.currentTask === Tasks.TONING) {
776          this.getToningTool();
777        } else if (this.currentTask === Tasks.TEXT) {
778          this.getMaterialTool(fontColors);
779        } else if (this.currentTask === Tasks.STICKER) {
780          this.getMaterialTool(stickers);
781        }
782        if (this.currentTask === Tasks.MARK || this.currentTask === Tasks.ADJUST) {
783          this.getFirstLvMenu();
784        } else {
785          this.CancelOrOk(this.cancelOkText);
786        }
787      }
788      .width('100%')
789    }
790    .backgroundColor(Color.Black)
791    .width('100%')
792    .height('100%')
793  }
794}