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