1/* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { Constants, Log, MediaItem } from '@ohos/common'; 17import { PhotoEditCrop } from './crop/PhotoEditCrop'; 18import { PhotoEditBase } from './base/PhotoEditBase'; 19import { PhotoEditMode } from './base/PhotoEditType'; 20import { PixelMapWrapper } from './base/PixelMapWrapper'; 21import { ImageFilterStack } from './ImageFilterStack'; 22import { Loader } from './Loader'; 23import { Save } from './Save'; 24import image from '@ohos.multimedia.image'; 25 26const TAG: string = 'editor_PhotoEditorManager'; 27 28export class PhotoEditorManager { 29 private currentMode: PhotoEditMode = PhotoEditMode.EDIT_MODE_MAIN; 30 private origin: PixelMapWrapper = undefined; 31 private item: MediaItem = undefined; 32 private editors: Array<PhotoEditBase> = undefined; 33 private historyManager: ImageFilterStack = undefined; 34 private albumUri: string = ''; 35 36 private constructor() { 37 this.historyManager = new ImageFilterStack(); 38 this.editors = []; 39 this.editors[PhotoEditMode.EDIT_MODE_CROP] = new PhotoEditCrop(); 40 } 41 42 static getInstance(): PhotoEditorManager { 43 if (AppStorage.get(Constants.PHOTO_EDITOR_MANAGER) == null) { 44 AppStorage.setOrCreate(Constants.PHOTO_EDITOR_MANAGER, new PhotoEditorManager()); 45 } 46 return AppStorage.get(Constants.PHOTO_EDITOR_MANAGER); 47 } 48 49 initialize(item: MediaItem, albumUri: string, mode: PhotoEditMode, errCallback?: Function): void { 50 Log.info(TAG, `initialize mode[${mode}], item[${item.uri}] albumUri = ${albumUri}`); 51 this.item = item; 52 this.albumUri = albumUri; 53 Loader.loadPixelMapWrapper(item, true).then((pixelMap) => { 54 if (pixelMap) { 55 this.origin = pixelMap; 56 (this.historyManager as ImageFilterStack).setOriginPixelMap(this.origin); 57 this.switchMode(mode); 58 } else { 59 Log.error(TAG, 'initialize loadPixelMapWrapper failed'); 60 errCallback && errCallback(); 61 } 62 }); 63 } 64 65 clear(): void { 66 Log.debug(TAG, 'clear'); 67 this.item = undefined; 68 if (this.origin) { 69 this.origin.release(); 70 this.origin = undefined; 71 } 72 this.historyManager.setOriginPixelMap(undefined); 73 this.historyManager.releaseAll(); 74 if (this.editors[this.currentMode]) { 75 this.editors[this.currentMode].clearCanvas(); 76 } 77 this.currentMode = PhotoEditMode.EDIT_MODE_MAIN; 78 } 79 80 getPhotoEditBaseInstance(mode: PhotoEditMode): PhotoEditBase { 81 return this.editors[mode]; 82 } 83 84 getLastestPixelMap(): PixelMapWrapper { 85 let position = this.historyManager.getPosition(); 86 Log.debug(TAG, `getLastestPixelMap position = ${position}`); 87 if (position < 0) { 88 return this.origin; 89 } else { 90 return this.historyManager.at(position).getCache(); 91 } 92 } 93 94 switchMode(mode: PhotoEditMode): PhotoEditMode { 95 Log.info(TAG, `switchMode: currentMode[${this.currentMode}] mode[${mode}]`); 96 if (this.currentMode == mode) { 97 return mode; 98 } 99 100 // exit current edit mode 101 if (this.editors[this.currentMode] != undefined) { 102 const filter = this.editors[this.currentMode].exit(); 103 if (filter != undefined) { 104 // save cache 105 if (!filter.isCached()) { 106 const prePixelMap = this.getLastestPixelMap(); 107 filter.setCache(filter.render(prePixelMap)); 108 } 109 this.historyManager.push(filter); 110 } 111 } 112 113 // entry next edit mode 114 let input = this.getLastestPixelMap(); 115 if (input && this.editors[mode] != undefined) { 116 this.editors[mode].entry(input); 117 // update current edit mode 118 this.currentMode = mode; 119 } 120 return this.currentMode; 121 } 122 123 isRedoValid(): boolean { 124 return this.historyManager.isRedoValid(); 125 } 126 127 isUndoValid(): boolean { 128 return this.historyManager.isUndoValid(); 129 } 130 131 redo(): boolean { 132 if (this.isRedoValid() && this.editors[this.currentMode] != undefined) { 133 let newPixel = this.historyManager.doRedo(); 134 this.editors[this.currentMode].entry(newPixel); 135 return true; 136 } 137 138 return false; 139 } 140 141 undo(): boolean { 142 if (this.isUndoValid() && this.editors[this.currentMode] != undefined) { 143 let newPixel = this.historyManager.doUndo(); 144 this.editors[this.currentMode].entry(newPixel); 145 return true; 146 } 147 148 return false; 149 } 150 151 save(isReplace: boolean, callback: Function): void { 152 if (this.editors[this.currentMode] != undefined) { 153 Log.info(TAG, `save enter isReplace = ${isReplace}`); 154 AppStorage.setOrCreate('isReplace', isReplace); 155 AppStorage.setOrCreate('isEditFunc', true); 156 const filter = this.editors[this.currentMode].exit(); 157 if (filter != undefined) { 158 this.historyManager.push(filter); 159 } 160 Save.save(this.item as MediaItem, this.albumUri, this.historyManager as ImageFilterStack, isReplace, callback); 161 } else { 162 Log.warn(TAG, `editor is undefined, currentMode = ${this.currentMode}`); 163 } 164 } 165}