HarmonyOS 自定义相机demo

有没有自定义相机的demo。

HarmonyOS
3天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
zbw_apple

​这边提供一个完整的demo,包括图片裁剪,相机拍照,已经相册选图功能。

本项目基于OpenHarmony三方库 [ImageKnife] 进行场景开发使用:

支持不同类型的本地与网络图片展示。

支持磁拍照展示与图库照片选择展示。

支持进行图片变换: 支持图像像素源图片变换效果。

需要权限

1、ohos.permission.INTERNET

2、ohos.permission.MEDIA_LOCATION

3、ohos.permission.READ_MEDIA

4、ohos.permission.WRITE_MEDIA

下载安装

ohpm install @ohos/imageknife

moudle.json 权限代码​:

"requestPermissions": [ 
{ 
  "name": "ohos.permission.INTERNET", 
"usedScene": { 
  "abilities": [ 
  "EntryAbility" 
  ], 
  "when": "always" 
} 
}, 
{ 
  "name": "ohos.permission.MEDIA_LOCATION", 
"reason": "$string:app_permission_MEDIA_LOCATION", 
"usedScene": { 
  "abilities": [ 
  "EntryAbility" 
  ], 
  "when": "always" 
} 
}, 
{ 
  "name": "ohos.permission.READ_MEDIA", 
"reason": "$string:app_permission_READ_MEDIA", 
"usedScene": { 
  "abilities": [ 
  "EntryAbility" 
  ], 
  "when": "always" 
} 
}, 
{ 
  "name": "ohos.permission.WRITE_MEDIA", 
"reason": "$string:app_permission_WRITE_MEDIA", 
"usedScene": { 
  "abilities": [ 
  "EntryAbility" 
  ], 
  "when": "inuse" 
} 
} 
]

entryability页面代码:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 
import { hilog } from '@kit.PerformanceAnalysisKit'; 
import { window } from '@kit.ArkUI'; 
import { ImageKnife } from '@ohos/imageknife' 
 
export default class EntryAbility extends UIAbility { 
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onCreate'); 
    AppStorage.setOrCreate('bundleName',this.context.abilityInfo.bundleName) 
  } 
 
  onDestroy(): void { 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onDestroy'); 
  } 
 
  onWindowStageCreate(windowStage: window.WindowStage): void { 
    // Main window is created, set main page for this ability 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onWindowStageCreate'); 
 
    windowStage.loadContent('pages/Index', (err) => { 
      if (err.code) { 
        hilog.error(0x1314, 'ImageDemo', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 
        return; 
      } 
      hilog.info(0x1314, 'ImageDemo', 'Succeeded in loading the content.'); 
    }); 
 
    // 初始化全局ImageKnife 
    ImageKnife.with(this.context); 
  } 
 
  onWindowStageDestroy(): void { 
    // Main window is destroyed, release UI related resources 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onWindowStageDestroy'); 
  } 
 
  onForeground(): void { 
    // Ability has brought to foreground 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onForeground'); 
  } 
 
  onBackground(): void { 
    // Ability has back to background 
    hilog.info(0x1314, 'ImageDemo', '%{public}s', 'Ability onBackground'); 
  } 
}

ets下common文件夹下Constants代码1:

import { HashMap } from '@kit.ArkTS' 
import { Tabar } from '../model/Tabar' 
 
export const localImageResource: HashMap<number, ResourceStr> = new HashMap() 
localImageResource.set(0, $r('app.media.jpgSample')) 
localImageResource.set(1, $r('app.media.pngSample')) 
localImageResource.set(2, $r('app.media.svgSample')) 
localImageResource.set(3, $r('app.media.gifSample')) 
localImageResource.set(4, $r('app.media.bmpSample')) 
localImageResource.set(5, $r('app.media.webpSample')) 
 
export const netImageResource: HashMap<number, ResourceStr> = new HashMap() 
netImageResource.set(0, 'https://img-home.csdnimg.cn/images/20240327093300.jpg') 
netImageResource.set(1, 'https://img-blog.csdnimg.cn/20191215043500229.png') 
netImageResource.set(2, 'https://img13.360buyimg.com/n1/jfs/t1/220646/38/10395/30916/61d6e061E1a6d91c8/c0a9a67e726dd7a4.jpg.dpg') 
netImageResource.set(3, 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658') 
netImageResource.set(4, 'https://img-blog.csdn.net/20140514114029140') 
netImageResource.set(5, 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp') 
 
export const BUNDLE_NAME: string = 'com.harmonyos.image' 
 
export const TAG: string = 'ImageDemo'; 
 
export const tabars: Array<Tabar> = [ 
  { 
    name: '裁剪', 
    selectN: $r('app.media.ic_pc_cut'), 
    selectY: $r('app.media.ic_pc_cut_select') 
  }, 
  { 
    name: '亮度', 
    selectN: $r('app.media.ic_public_brightness'), 
    selectY: $r('app.media.ic_public_brightness_filled') 
  }, 
  { 
    name: '对比度', 
    selectN: $r('app.media.contrast_ratio'), 
    selectY: $r('app.media.contrast_ratio_selected') 
  }, 
  { 
    name: '旋转', 
    selectN: $r('app.media.ic_switch'), 
    selectY: $r('app.media.ic_switch_selected') 
  } 
] 
 
export class Constants { 
  /** 
   * Title row width. 
   */ 
  static readonly TITLE_ROW_WEIGHT: string = '50%'; 
 
  /** 
   * Layout full screen. 
   */ 
  static readonly LAYOUT_FULL_SCREEN: string = '100%'; 
 
  /** 
   * Edit page height. 
   */ 
  static readonly EDIT_PAGE_HEIGHT: string = '26%'; 
 
  /** 
   * Image show height. 
   */ 
  static readonly IMAGE_SHOW_HEIGHT: string = '68%'; 
 
  /** 
   * Slider width. 
   */ 
  static readonly SLIDER_WIDTH: string = '80%'; 
 
  /** 
   * Loading width and height. 
   */ 
  static readonly LOADING_WH: string = '30%'; 
  /** 
   * Clock wise. 
   */ 
  static readonly CLOCK_WISE: number = 90; 
 
  /** 
   * Anti clock. 
   */ 
  static readonly ANTI_CLOCK: number = -90; 
 
  /** 
   * Tab menu width. 
   */ 
  static readonly TAB_MENU_WIDTH: number = 18; 
 
  /** 
   * Navigation height. 
   */ 
  static readonly NAVIGATION_HEIGHT: number = 56; 
 
  /** 
   * Adjust slider value. 
   */ 
  static readonly ADJUST_SLIDER_VALUE: number[] = [100, 100, 100]; 
 
  /** 
   * Slider min. 
   */ 
  static readonly SLIDER_MIN: number = 1; 
 
  /** 
   * Slider step. 
   */ 
  static readonly SLIDER_MAX: number = 100; 
 
  /** 
   * Slider step. 
   */ 
  static readonly SLIDER_STEP: number = 1; 
 
  /** 
   * Pixel step. 
   */ 
  static readonly PIXEL_STEP: number = 4; 
 
  /** 
   * Decimal two. 
   */ 
  static readonly DECIMAL_TWO: number = 2; 
 
  /** 
   * Color level max. 
   */ 
  static readonly COLOR_LEVEL_MAX: number = 255; 
 
  /** 
   * Convert int. 
   */ 
  static readonly CONVERT_INT: number = 100; 
 
  /** 
   * Angle 60. 
   */ 
  static readonly ANGLE_60: number = 60; 
 
  /** 
   * Angle 120. 
   */ 
  static readonly ANGLE_120: number = 120; 
 
  /** 
   * Angle 240. 
   */ 
  static readonly ANGLE_240: number = 240; 
 
  /** 
   * Angle 300. 
   */ 
  static readonly ANGLE_360: number = 360; 
 
  /** 
   * Angle 360. 
   */ 
  static readonly MOD_2: number = 2; 
 
  /** 
   * Average height and width. 
   */ 
  static readonly AVERAGE_WEIGHT_WIDTH: number = 2; 
 
  /** 
   * Crop rate 4:3. 
   */ 
  static readonly CROP_RATE_4_3: number = 0.75; 
 
  /** 
   * Crop rate 16:9. 
   */ 
  static readonly CROP_RATE_9_16: number = 9 / 16; 
 
  /** 
   * Encode quality. 
   */ 
  static readonly ENCODE_QUALITY: number = 100; 
 
  /** 
   * Title space. 
   */ 
  static readonly TITLE_SPACE: number = 0; 
 
  /** 
   * Slider mode click. 
   */ 
  static readonly SLIDER_CLICK_MODE: number = 3; 
 
  /** 
   * Encode format. 
   */ 
  static readonly ENCODE_FORMAT: string = 'image/jpeg'; 
 
  /** 
   * Encode file permission. 
   */ 
  static readonly ENCODE_FILE_PERMISSION: string = 'rw'; 
 
  /** 
   * Brightness worker file. 
   */ 
  static readonly BRIGHTNESS_WORKER_FILE = 'entry/ets/workers/AdjustBrightnessWork.ts'; 
 
  /** 
   * Brightness worker file. 
   */ 
  static readonly SATURATION_WORKER_FILE = 'entry/ets/workers/AdjustSaturationWork.ts'; 
 
  /** 
   * Image name. 
   */ 
  static readonly IMAGE_PREFIX = 'image'; 
 
  /** 
   * Image format. 
   */ 
  static readonly IMAGE_FORMAT = '.jpg'; 
 
  /** 
   * Rawfile name. 
   */ 
  static readonly RAW_FILE_NAME = 'low.jpg'; 
 
  /** 
   * Cache dir file name. 
   */ 
  static readonly RAW_FILE_NAME_TEST = 'low_test.jpg'; 
 
  /** 
   * Free ratio. 
   */ 
  static readonly DEFAULT_RATIO: number = -1; 
 
  /** 
   * Ratio 1:1. 
   */ 
  static readonly RATIO_1_1: number = 1; 
 
  /** 
   * Ratio 16:9. 
   */ 
  static readonly RATIO_16_9: number[] = [16, 9]; 
 
  /** 
   * Ratio 9:16. 
   */ 
  static readonly RATIO_9_16: number[] = [9, 16]; 
 
  /** 
   * Ratio 4:3. 
   */ 
  static readonly RATIO_4_3: number[] = [4, 3]; 
 
  /** 
   * Ratio 3:4. 
   */ 
  static readonly RATIO_3_4: number[] = [3, 4]; 
 
  /** 
   * Ratio 3:2. 
   */ 
  static readonly RATIO_3_2: number[] = [3, 2]; 
 
  /** 
   * Ratio 2:3. 
   */ 
  static readonly RATIO_2_3: number[] = [2, 3]; 
 
  /** 
   * Screen display margin. 
   */ 
  static readonly SCREEN_DISPLAY_MARGIN: number = 15; 
 
  /** 
   * Double. 
   */ 
  static readonly DOUBLE: number = 2; 
 
  /** 
   * Edit screen can use scope. 
   */ 
  static readonly EDIT_SCREEN_USAGE: number = 0.68; 
 
  /** 
   * Title height. 
   */ 
  static readonly TITLE_HEIGHT: number = 56; 
 
  /** 
   * Screen manager key. 
   */ 
  static readonly APP_KEY_SCREEN_MANAGER: string = 'app_key_screen_manager'; 
 
  /** 
   * Whether full screen. 
   */ 
  static readonly IS_FULL_SCREEN_KEY: string = 'isFullScreen'; 
 
  /** 
   * Device type. 
   */ 
  static readonly DEFAULT_DEVICE_TYPE: string = 'phone'; 
 
  /** 
   * Status bar color. 
   */ 
  static readonly STATUS_BAR_BACKGROUND_COLOR: string = '#F1F3F5'; 
 
  /** 
   * Status bar text color. 
   */ 
  static readonly STATUS_BAR_CONTENT_COLOR: string = '#000000'; 
 
  /** 
   * Navigation height. 
   */ 
  static readonly TOP_BAR_SIZE: number = 56; 
 
  /** 
   * Tool bar size. 
   */ 
  static readonly TOOL_BAR_SIZE: number = 72; 
 
  /** 
   * Time out. 
   */ 
  static readonly TIMEOUT: number = 50; 
 
  /** 
   * Rect line width inner. 
   */ 
  static readonly DEFAULT_LINE_WIDTH: number = 0.4; 
 
  /** 
   * Rect line width outer. 
   */ 
  static readonly DEFAULT_LINE_RECT_WIDTH: number = 0.8; 
 
  /** 
   * Rect Button line width. 
   */ 
  static readonly DEFAULT_BUTTON_WIDTH: number = 2.3; 
  /** 
   * Rect Button line padding. 
   */ 
  static readonly DEFAULT_BUTTON_PADDING: number = 1; 
 
  /** 
   * Rect Button line length. 
   */ 
  static readonly DEFAULT_BUTTON_LENGTH: number = 20; 
 
  /** 
   * Rect line color. 
   */ 
  static readonly DEFAULT_LINE_COLOR: string = '#80FFFFFF'; 
 
  /** 
   * Rect Button line color outer. 
   */ 
  static readonly DEFAULT_RECT_LINE_COLOR: string = '#FFFFFFFF'; 
 
  /** 
   * Rect Button line color. 
   */ 
  static readonly DEFAULT_BUTTON_COLOR: string = 'white'; 
 
  /** 
   * Mask style. 
   */ 
  static readonly DEFAULT_MASK_STYLE: string = 'rgba(0, 0, 0, 0.3)'; 
 
  /** 
   * Equality threshold. 
   */ 
  static readonly EQUALITY_THRESHOLD = 0.0001; 
 
  /** 
   * Min side length. 
   */ 
  static readonly DEFAULT_MIN_SIDE_LENGTH: number = 90; 
 
  /** 
   * Touch move identification range. 
   */ 
  static readonly DEFAULT_TOUCH_BOUND: number = 20; 
 
  /** 
   * Base scale value. 
   */ 
  static readonly BASE_SCALE_VALUE: number = 1.0; 
 
  /** 
   * Default image ratio. 
   */ 
  static readonly DEFAULT_IMAGE_RATIO: number = 1.0; 
 
  /** 
   * Rect min side length. 
   */ 
  static readonly DEFAULT_MIN_SIDE_LENGTH_EDIT: number = 32; 
 
  /** 
   * Default margin length. 
   */ 
  static readonly DEFAULT_MARGIN_LENGTH: number = 20; 
 
  /** 
   * Time out. 
   */ 
  static readonly DEFAULT_TIMEOUT_MILLISECOND_1000: number = 1000; 
 
  /** 
   * Default split fraction. 
   */ 
  static readonly DEFAULT_SPLIT_FRACTION: number = 3; 
}

ets文件夹下common文件夹下BrightNess页面代码:

import { 
  BrightnessFilterTransformation, 
  ImageKnifeComponent, 
  ScaleType, 
  ImageKnifeData, 
  ImageKnifeGlobal, 
  RequestOption 
} from '@ohos/imageknife'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
import PictureUtil from '../utils/PictureUtil'; 
 
/** 
 * 亮度调节 
 */ 
@Component 
export struct BrightNess { 
  private ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); 
  private mUrl = $r('app.media.pngSample'); 
  @State mBrightnessPixelMap?: PixelMap = undefined; 
  @State @Watch('brightnessPixelMap') brightNessValue: number = 0; 
  @State showUI: boolean = false 
  textTimerController: TextTimerController = new TextTimerController() 
  @State format: string = 'mm:ss.SS' 
 
  async aboutToAppear(): Promise<void> { 
    this.mBrightnessPixelMap = await PictureUtil.getResourcePixelMap($r('app.media.pngSample')) 
  } 
 
  build() { 
    Column() { 
      Stack() { 
        ImageKnifeComponent({ 
          imageKnifeOption: { 
            loadSrc: this.mBrightnessPixelMap, 
            mainScaleType: ScaleType.FIT_CENTER, 
          } 
        }) 
          .width('100%') 
          .height(400) 
 
        Text(this.brightNessValue.toFixed(0)) 
          .fontSize(48) 
          .fontColor(Color.White) 
          .fontWeight(FontWeight.Bold) 
          .visibility(this.showUI ? Visibility.Visible : Visibility.Hidden) 
        TextTimer({ isCountDown: true, count: 1500, controller: this.textTimerController }) 
          .format(this.format) 
          .fontColor(Color.Black) 
          .fontSize(50) 
          .onTimer((utc: number, elapsedTime: number) => { 
            console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime) 
            if (elapsedTime >= 1500) { 
              this.showUI = false; 
            } 
          }) 
          .visibility(Visibility.Hidden) 
      } 
 
      Slider({ 
        value: this.brightNessValue, 
        min: -100, 
        max: 100, 
        style: SliderStyle.OutSet, 
      }) 
        .blockColor(Color.White) 
        .trackColor(Color.Gray) 
        .selectedColor(Color.Gray) 
        .onChange((value: number, mode: SliderChangeMode) => { 
          this.brightNessValue = value 
          this.showUI = true; 
          if (mode === SliderChangeMode.End) { 
            this.textTimerController.reset() 
            this.textTimerController.start() 
          } 
          console.info('value:' + value + 'mode:' + mode.toString()) 
        }) 
 
      Text('参考"ets/component/BrightNess.ets"') 
    } 
    .height('100%') 
    .width('100%') 
    .padding(15) 
    .justifyContent(FlexAlign.SpaceAround) 
  } 
 
  /** 
   *亮度b 
   */ 
  brightnessPixelMap() { 
    let brightness = this.brightNessValue / 100; 
    let imageKnifeOption = new RequestOption(); 
    new BrightnessFilterTransformation(brightness); 
    imageKnifeOption.load(this.mUrl) 
      .addListener({ 
        callback: (err: BusinessError | string, data: ImageKnifeData) => { 
          this.mBrightnessPixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; 
          return false; 
        } 
      }) 
      .setImageViewSize({ width: vp2px(200), height: vp2px(200) }) 
      .skipMemoryCache(true) 
      .enableGPU() 
      .brightnessFilter(brightness) 
    this.ImageKnife?.call(imageKnifeOption); 
  } 
}

ets文件夹下common文件夹下 Contrast页面代码:

import { 
  ContrastFilterTransformation, 
  ImageKnifeComponent, 
  ScaleType, 
  ImageKnifeData, 
  ImageKnifeGlobal, 
  RequestOption 
} from '@ohos/imageknife'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
import PictureUtil from '../utils/PictureUtil'; 
 
/** 
 * 对比度调整 
 */ 
@Component 
export struct Contrast { 
  private ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); 
  private mUrl = $r('app.media.pngSample'); 
  @State mBrightnessPixelMap?: PixelMap = undefined; 
  @State @Watch('contrastPixelMap') contrastValue: number = 0; 
  @State showUI: boolean = false 
  textTimerController: TextTimerController = new TextTimerController() 
  @State format: string = 'mm:ss.SS' 
 
  async aboutToAppear(): Promise<void> { 
    this.mBrightnessPixelMap = await PictureUtil.getResourcePixelMap($r('app.media.pngSample')) 
  } 
 
  build() { 
    Column() { 
      Stack() { 
        ImageKnifeComponent({ 
          imageKnifeOption: { 
            loadSrc: this.mBrightnessPixelMap, 
            mainScaleType: ScaleType.FIT_CENTER, 
          } 
        }) 
          .width('100%') 
          .height(400) 
 
        Text(this.contrastValue.toFixed(0)) 
          .fontSize(48) 
          .fontColor(Color.White) 
          .fontWeight(FontWeight.Bold) 
          .visibility(this.showUI ? Visibility.Visible : Visibility.Hidden) 
        TextTimer({ isCountDown: true, count: 1500, controller: this.textTimerController }) 
          .format(this.format) 
          .fontColor(Color.Black) 
          .fontSize(50) 
          .onTimer((utc: number, elapsedTime: number) => { 
            console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime) 
            if (elapsedTime >= 1500) { 
              this.showUI = false; 
            } 
          }) 
          .visibility(Visibility.Hidden) 
      } 
 
      Slider({ 
        value: this.contrastValue, 
        min: -100, 
        max: 100, 
        style: SliderStyle.OutSet, 
      }) 
        .blockColor(Color.White) 
        .trackColor(Color.Gray) 
        .selectedColor(Color.Gray) 
        .onChange((value: number, mode: SliderChangeMode) => { 
          this.contrastValue = value 
          this.showUI = true; 
          if (mode === SliderChangeMode.End) { 
            this.textTimerController.reset() 
            this.textTimerController.start() 
          } 
          console.info('value:' + value + 'mode:' + mode.toString()) 
        }) 
 
      Text('参考"ets/component/Contrast.ets"') 
    } 
    .height('100%') 
    .width('100%') 
    .padding(15) 
    .justifyContent(FlexAlign.SpaceAround) 
  } 
 
  /** 
   *对比度 
   */ 
  contrastPixelMap() { 
    let imageKnifeOption = new RequestOption(); 
    let transformation = new ContrastFilterTransformation(this.contrastValue); 
    imageKnifeOption.load(this.mUrl) 
      .addListener({ 
        callback: (err: BusinessError | string, data: ImageKnifeData) => { 
          this.mBrightnessPixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; 
          return false; 
        } 
      }) 
      .setImageViewSize({ width: vp2px(200), height: vp2px(200) }) 
      .skipMemoryCache(true) 
      .enableGPU() 
      .contrastFilter(this.contrastValue) 
    this.ImageKnife?.call(imageKnifeOption); 
  } 
}

CropImageComponent组件代码:

import { 
  CropOptions, 
  FileUtils, 
  ImageKnifeComponent, 
  ImageKnifeGlobal, 
  Options, 
  PixelMapCrop, 
  ScaleType 
} from '@ohos/imageknife'; 
import { Constants, TAG } from '../common/Constants'; 
import { common } from '@kit.AbilityKit'; 
import { resourceManager } from '@kit.LocalizationKit'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
import Pictures from '../utils/PictureUtil'; 
import { image } from '@kit.ImageKit'; 
 
/** 
 * 图片裁剪 
 */ 
@Component 
export struct CropImageComponent { 
  private settings: RenderingContextSettings = new RenderingContextSettings(true) 
  private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) 
  @State cropImage: ResourceStr = $r('app.media.pngSample') 
  @State options1: Options = new Options(); 
  @State cropTap: boolean = false; 
  @State _rotate: number = 0; 
  @State _scale: number = 1; 
  @State cropOptions: CropOptions = { 
    src: this.cropImage, 
    size: { 
      width: 300, 
      height: 300 
    } 
  }; 
  private context: common.UIAbilityContext = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext; 
 
  /** 
   * 初始化一个默认图片显示 
   * @returns 
   */ 
  async aboutToAppear(): Promise<void> { 
    const resourceMgr = this.context.resourceManager as resourceManager.ResourceManager; 
    let imageBuffer = await resourceMgr.getMediaContent($r("app.media.pngSample").id); 
    let arrayBuffer = FileUtils.getInstance().uint8ArrayToBuffer(imageBuffer); 
    this.setOptions(arrayBuffer) 
  } 
 
  build() { 
    Scroll() { 
      Column({ space: 10 }) { 
        PixelMapCrop({ options: $options1, cropTap: this.cropTap }) 
 
        Canvas(this.canvasContext) 
          .width(this.cropOptions.size.width) 
          .height(this.cropOptions.size.height) 
          .rotate({ 
            z: 1, 
            centerX: this.cropOptions.size.width / 2, 
            centerY: this.cropOptions.size.height / 2, 
            angle: this._rotate 
          }) 
          .scale({ x: this._scale, y: this._scale, z: 1.0 }) 
          .gesture(GestureGroup(GestureMode.Parallel, 
            RotationGesture({ fingers: 2 }).onActionUpdate((event?: GestureEvent) => { 
              if (event != undefined) { 
                this._rotate = event.angle; 
              } 
            }), PinchGesture({ fingers: 2 }).onActionUpdate((event?: GestureEvent) => { 
              if (event != undefined) { 
                this._scale = event.scale; 
              } 
            }))) 
 
        Row() { 
          Row() { 
            Button('点击裁剪图片').onClick(() => { 
              this.cropTap = !this.cropTap; 
            }) 
              .width('49%') 
              .type(ButtonType.Normal) 
            Button('图库选图').onClick(async (event: ClickEvent) => { 
              let uri = await Pictures.getImageFromGallery() 
              if (uri) { 
                Pictures.getPixelMap(uri).then(async pixelMap => { 
                  if (pixelMap) { 
                    let packOpts : image.PackingOption = { format:"image/png", quality:100 }; 
                    const imagePackerApi = image.createImagePacker(); 
                    imagePackerApi.packing(pixelMap, packOpts).then( (data : ArrayBuffer) => { 
                      this.setOptions(data) 
                    }).catch((error : BusinessError) => { 
                      console.error('Failed to pack the image. And the error is: ' + error); 
                    }) 
                  } 
                }) 
 
              } 
            }) 
              .width('49%') 
              .type(ButtonType.Normal) 
          } 
          .width('100%') 
          .justifyContent(FlexAlign.SpaceBetween) 
        } 
 
        Text('参考"ets/component/CropImage.ets"') 
      } 
      .width('100%') 
    } 
    .height('100%') 
    .padding(15) 
  } 
 
  setOptions(arrayBuffer: ArrayBuffer) { 
    try { 
      let optionx = new Options(); 
      optionx.setWidth(800).setHeight(800) 
        .setCropFunction((err: BusinessError | string, pixelmap: PixelMap | null, sx: number, sy: number) => { 
          console.log('PMC setCropFunction callback') 
          if (err) { 
            console.error('PMC crop err =' + err) 
          } else { 
            this.cropOptions.size.width = sx * px2vp(1); 
            this.cropOptions.size.height = sy * px2vp(1); 
            if (pixelmap != null) { 
              this.cropOptions.src = pixelmap; 
              this.canvasContext.drawImage(pixelmap, 0, 0, this.cropOptions.size.width, this.cropOptions.size.height) 
            } 
          } 
        }) 
      optionx.loadBuffer(arrayBuffer, () => { 
        this.options1 = optionx; 
      }) 
    } catch (err) { 
      console.log(err) 
    } 
  } 
}

TransformRotate 组件代码:

import { 
  ImageKnifeComponent, 
  ScaleType, 
  ImageKnifeData, 
  ImageKnifeGlobal, 
  RequestOption, 
  RotateImageTransformation 
} from '@ohos/imageknife'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
import PictureUtil from '../utils/PictureUtil'; 
 
/** 
 * 图片旋转 
 */ 
@Component 
export struct TransformRotate { 
  private ImageKnife = ImageKnifeGlobal.getInstance().getImageKnife(); 
  private mUrl = $r('app.media.pngSample'); 
  @State mRotatePixelMap?: PixelMap = undefined; 
  @State @Watch('transformRotate') mRotate: number = 0; 
  @State showUI: boolean = false 
  textTimerController: TextTimerController = new TextTimerController() 
  @State format: string = 'mm:ss.SS' 
  @State savePath: string = '' 
 
  async aboutToAppear(): Promise<void> { 
    this.mRotatePixelMap = await PictureUtil.getResourcePixelMap($r('app.media.pngSample')) 
  } 
 
  build() { 
    Scroll() { 
      Column() { 
        Stack() { 
          ImageKnifeComponent({ 
            imageKnifeOption: { 
              loadSrc: this.mRotatePixelMap, 
              mainScaleType: ScaleType.FIT_CENTER, 
            } 
          }) 
            .width('100%') 
            .height(400) 
 
 
          Text(this.mRotate.toFixed(0)) 
            .fontSize(48) 
            .fontColor(Color.White) 
            .fontWeight(FontWeight.Bold) 
            .visibility(this.showUI ? Visibility.Visible : Visibility.Hidden) 
          TextTimer({ isCountDown: true, count: 1500, controller: this.textTimerController }) 
            .format(this.format) 
            .fontColor(Color.Black) 
            .fontSize(50) 
            .onTimer((utc: number, elapsedTime: number) => { 
              console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime) 
              if (elapsedTime >= 1500) { 
                this.showUI = false; 
              } 
            }) 
            .visibility(Visibility.Hidden) 
        } 
 
        Column() { 
          Text('固定角度') 
          Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceEvenly }) { 
            Button('左转45°') 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(ButtonStyleMode.EMPHASIZED) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .onClick(() => { 
                this.mRotate = this.mRotate >= 45 ? this.mRotate - 45 : this.mRotate + 360 - 45; 
              }) 
 
            Button('左转90°') 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(ButtonStyleMode.EMPHASIZED) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .onClick(() => { 
                this.mRotate = this.mRotate >= 90 ? this.mRotate - 90 : this.mRotate + 360 - 90; 
              }) 
 
            Button('旋转180°') 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(ButtonStyleMode.EMPHASIZED) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .onClick(() => { 
                this.mRotate = this.mRotate + 180 >= 360 ? this.mRotate - 180 : this.mRotate + 180; 
              }) 
 
            Button('右转45°') 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(ButtonStyleMode.EMPHASIZED) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .onClick(() => { 
                this.mRotate = this.mRotate + 45 >= 360 ? this.mRotate + 45 - 360 : this.mRotate + 45; 
              }) 
 
            Button('右转90°') 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(ButtonStyleMode.EMPHASIZED) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .onClick(() => { 
                this.mRotate = this.mRotate + 90 >= 360 ? this.mRotate + 90 - 360 : this.mRotate + 90; 
              }) 
          } 
        } 
        .borderWidth(1) 
        .borderColor('#E5E5E5') 
 
        Slider({ 
          value: this.mRotate, 
          min: 0, 
          max: 360, 
          style: SliderStyle.OutSet, 
        }) 
          .blockColor(Color.White) 
          .trackColor(Color.Gray) 
          .selectedColor(Color.Gray) 
          .margin({ top: 15 }) 
          .onChange((value: number, mode: SliderChangeMode) => { 
            this.mRotate = value 
            this.showUI = true; 
            if (mode === SliderChangeMode.End) { 
              this.textTimerController.reset() 
              this.textTimerController.start() 
            } 
            console.info('value:' + value + 'mode:' + mode.toString()) 
          }) 
 
        Button('保存') 
          .onClick(async (event: ClickEvent) => { 
            if (this.mRotatePixelMap) { 
              this.savePath = await PictureUtil.savePixelMap(this.mRotatePixelMap) 
            } 
          }) 
          .width('100%') 
          .type(ButtonType.Normal) 
          .borderRadius(5) 
          .margin({ top: 15 }) 
 
        if (this.savePath) { 
          Text('保存后沙箱路径:' + this.savePath).margin({ top: 15 }) 
        } 
 
        Text('参考"ets/component/TransformRotate.ets"').margin({ top: 15 }) 
      } 
      .width('100%') 
      .padding(15) 
      .justifyContent(FlexAlign.SpaceAround) 
    } 
    .height('100%') 
    .scrollBar(BarState.Off) 
  } 
 
  /** 
   * 旋转 
   */ 
  transformRotate() { 
    let imageKnifeOption = new RequestOption(); 
    let transformation = new RotateImageTransformation(this.mRotate); 
    imageKnifeOption.load(this.mUrl) 
      .addListener({ 
        callback: (err: BusinessError | string, data: ImageKnifeData) => { 
          this.mRotatePixelMap = data.drawPixelMap?.imagePixelMap as PixelMap; 
          return false; 
        } 
      }) 
      .setImageViewSize({ width: vp2px(200), height: vp2px(200) }) 
      .skipMemoryCache(true) 
      .rotateImage(this.mRotate) 
    this.ImageKnife?.call(imageKnifeOption); 
  } 
}

model文件夹下pages代码:

export class Pages { 
  names: string = "" 
  values: NavPathStack | null = null 
}

ets文件夹下model文件夹下tabar代码:

export class Tabar { 
  name: string = ''; 
  selectN: ResourceStr = ''; 
  selectY: ResourceStr = ''; 
}

pages文件夹下CapabilityList代码:

import { Tabar } from '../model/Tabar' 
import { tabars } from '../common/Constants'; 
import { CropImageComponent } from '../component/CropImage'; 
import { BrightNess } from '../component/BrightNess'; 
import { Contrast } from '../component/Contrast'; 
import { TransformRotate } from '../component/TransformRotate'; 
 
@Entry 
@Component 
struct CapabilityList { 
  @State fontColor: string = '#182431' 
  @State selectedFontColor: string = '#007DFF' 
  @State currentIndex: number = 0 
  private controller: TabsController = new TabsController() 
 
  build() { 
    Column() { 
      Tabs({ barPosition: BarPosition.End, controller: this.controller }) { 
        TabContent() { 
          CropImageComponent() 
        } 
        .tabBar(this.tabBuilder(0, tabars[0])) 
        .backgroundColor(Color.White) 
 
        TabContent() { 
          BrightNess() 
        } 
        .tabBar(this.tabBuilder(1, tabars[1])) 
        .backgroundColor(Color.White) 
 
        TabContent() { 
          Contrast() 
        } 
        .tabBar(this.tabBuilder(2, tabars[2])) 
        .backgroundColor(Color.White) 
 
        TabContent() { 
          TransformRotate() 
        } 
        .tabBar(this.tabBuilder(3, tabars[3])) 
        .backgroundColor(Color.White) 
      } 
      .onChange((index: number) => { 
        this.currentIndex = index 
      }) 
      .backgroundColor('#F1F3F5') 
      .scrollable(false) 
    } 
  } 
 
  @Builder 
  tabBuilder(index: number, tabar: Tabar) { 
    Column() { 
      Image(this.currentIndex === index ? tabar.selectY : tabar.selectN) 
        .width(24) 
        .height(24) 
        .margin({ bottom: 4 }) 
        .objectFit(ImageFit.Contain) 
      Text(tabar.name) 
        .fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor) 
        .fontSize(10) 
        .fontWeight(500) 
        .lineHeight(14) 
    }.width('100%').height('100%').justifyContent(FlexAlign.Center) 
  } 
}

pages文件夹下index页面代码:

import { ImageKnifeComponent, ScaleType } from '@ohos/imageknife'; 
import { router } from '@kit.ArkUI'; 
import { localImageResource, netImageResource } from '../common/Constants'; 
import PictureUtil from '../utils/PictureUtil'; 
 
@Entry 
@Component 
struct Index { 
  @State imageData: ResourceStr = $r('app.media.jpgSample'); 
  @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack() 
  @State @Watch('localChange') btLIndex: number = 0; 
  @State @Watch('netChange') btNIndex: number = -1; 
  private btLNames: Array<string> = ['JPG', 'PNG', 'SVG', 'GIF', 'BMP', 'WEBP']; 
  private btNNames: Array<string> = ['JPG', 'PNG', 'DPG', 'GIF', 'BMP', 'WEBP'] 
 
  build() { 
    Column({ space: 15 }) { 
      Text('图片展示') 
        .fontSize(18) 
        .fontWeight(FontWeight.Medium) 
 
      ImageKnifeComponent({ 
        imageKnifeOption: { 
          loadSrc: this.imageData, 
          mainScaleType: ScaleType.FIT_CENTER, 
          placeholderSrc: $r('app.media.ic_loading'), 
          errorholderSrc: $r('app.media.ic_public_fail_filled'), 
          displayProgress:true, 
        } 
      }) 
        .width(300) 
        .height(300) 
        .borderWidth(1) 
        .borderColor('#E5E5E5') 
 
      Column() { 
        Text('本地图片') 
        Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceEvenly }) { 
          ForEach(this.btLNames, (item: string, index: number) => { 
            Button(item) 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(this.btLIndex === index ? ButtonStyleMode.EMPHASIZED : ButtonStyleMode.NORMAL) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .fontColor(this.btLIndex === index ? null : '#99000000') 
              .onClick(() => { 
                this.btLIndex = index 
              }) 
          }) 
        } 
      } 
      .borderWidth(1) 
      .borderColor('#E5E5E5') 
 
      Column() { 
        Text('网络图片') 
        Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceEvenly }) { 
          ForEach(this.btNNames, (item: string, index: number) => { 
            Button(item) 
              .controlSize(ControlSize.NORMAL) 
              .buttonStyle(this.btNIndex === index ? ButtonStyleMode.EMPHASIZED : ButtonStyleMode.NORMAL) 
              .type(ButtonType.Normal) 
              .width('30%') 
              .margin({ top: 5, bottom: 5 }) 
              .borderRadius(5) 
              .fontColor(this.btNIndex === index ? null : '#99000000') 
              .onClick(() => { 
                this.btNIndex = index 
              }) 
          }) 
        } 
      } 
      .borderWidth(1) 
      .borderColor('#E5E5E5') 
 
      Row() { 
        Button('拍照展示').onClick(async (event: ClickEvent) => { 
          let uri = await PictureUtil.takePhoto() 
          if (uri) { 
            this.imageData = uri; 
          } 
        }) 
          .width('49%') 
          .type(ButtonType.Normal) 
          .borderRadius(5) 
 
        Button('图库选图').onClick(async (event: ClickEvent) => { 
          let uri = await PictureUtil.getImageFromGallery() 
          if (uri) { 
            this.imageData = uri; 
          } 
        }) 
          .width('49%') 
          .type(ButtonType.Normal) 
          .borderRadius(5) 
      } 
      .width('100%') 
      .justifyContent(FlexAlign.SpaceBetween) 
 
      Button('更多能力').onClick((event: ClickEvent) => { 
        router.pushUrl({ url: 'pages/CapabilityList' }) 
      }) 
        .width('100%') 
        .type(ButtonType.Normal) 
        .borderRadius(5) 
    } 
    .width('100%') 
    .height('100%') 
    .padding(15) 
    .backgroundColor(Color.White) 
  } 
 
  localChange() { 
    if (this.btLIndex != -1) { 
      this.btNIndex = -1 
    } 
    console.log('lindex = ' + this.btLIndex) 
    this.imageData = localImageResource.get(this.btLIndex) 
  } 
 
  netChange() { 
    if (this.btNIndex != -1) { 
      this.btLIndex = -1 
    } 
    console.log('nindex = ' + this.btNIndex) 
    this.imageData = netImageResource.get(this.btNIndex) 
  } 
}

ets文件夹下utils文件夹下PermissionsUtil代码:

import { abilityAccessCtrl, bundleManager, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
 
/** 
 * 权限管理工具类 
 * 能力:检查权限是否已存在、请求用户授权 
 */ 
class PermissionsUtil { 
 
  /** 
   * 校验应用是否被授予定位权限 
   * @param permissions 
   * @returns 
   */ 
  async checkPermissions(permissions: Array<Permissions>): Promise<void> { 
    let applyResult: boolean = false; 
    for (let permission of permissions) { 
      let grantStatus: abilityAccessCtrl.GrantStatus = await this.checkAccessToken(permission); 
      if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 
        applyResult = true; 
      } else { 
        applyResult = false; 
      } 
    } 
    if (!applyResult) { 
      this.requestPermissions(permissions); 
    } 
  } 
 
  async checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> { 
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); 
    let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED; 
 
    // 获取应用程序的accessTokenID 
    let tokenId: number = 0; 
    try { 
      let bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); 
      let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo; 
      tokenId = appInfo.accessTokenId; 
    } catch (error) { 
      let err: BusinessError = error as BusinessError; 
      console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`); 
    } 
 
    // 校验应用是否被授予权限 
    try { 
      grantStatus = await atManager.checkAccessToken(tokenId, permission); 
    } catch (error) { 
      let err: BusinessError = error as BusinessError; 
      console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`); 
    } 
 
    return grantStatus; 
  } 
 
  /** 
   * 申请用户授权 
   * @param permissions 
   */ 
  requestPermissions(permissions: Array<Permissions>): void { 
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); 
    atManager.requestPermissionsFromUser(getContext() as common.UIAbilityContext, permissions) 
      .then((data: PermissionRequestResult) => { 
        console.info('request Permissions success') 
      }) 
      .catch((err: BusinessError) => { 
        console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); 
      }) 
  } 
} 
 
export default new PermissionsUtil();

util文件夹下pcitureutil代码:

import { common, Want } from '@kit.AbilityKit'; 
import { fileIo, fileUri } from '@kit.CoreFileKit'; 
import { BusinessError } from '@kit.BasicServicesKit'; 
import { BUNDLE_NAME, TAG } from '../common/Constants'; 
import { image } from '@kit.ImageKit'; 
import fs from '@ohos.file.fs'; 
import { resourceManager } from '@kit.LocalizationKit'; 
import { FileUtils } from '@ohos/imageknife'; 
 
class PictureUtil { 
  private LENGTH: number = 1; 
  private mContext: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; 
 
  /** 
   * 从图库选取一张图片 
   * @returns 
   */ 
  async getImageFromGallery(): Promise<string | undefined> { 
    let wantParam: Want = { 
      bundleName: 'com.huawei.hmos.photos', 
      abilityName: 'com.huawei.hmos.photos.MainAbility', 
      parameters: { 
        uri: 'multipleselect', 
        filterMediaType: 'FILTER_MEDIA_TYPE_IMAGE', 
        maxSelectCount: this.LENGTH 
      } 
    } 
 
    let abilityResult: common.AbilityResult = await this.mContext.startAbilityForResult(wantParam) 
    let wantResult: Want | undefined = abilityResult.want; 
    if (!wantResult || !wantResult.parameters) { 
      return undefined 
    } 
 
    let result: Array<string> = wantResult.parameters['select-item-list'] as Array<string>; 
    return result.length > 0 ? result[0] : undefined 
  } 
 
  /** 
   * 拍照并拿到照片uri 
   * @returns 
   */ 
  async takePhoto(): Promise<string | undefined> { 
    let bundleName: string = AppStorage.get('bundleName') ? AppStorage.get('bundleName') as string : BUNDLE_NAME 
    let wantParam: Want = { 
      action: 'ohos.want.action.imageCapture', 
      parameters: { 
        // 拍照完成后返回的应用BundleName 
        callBundleName: bundleName, 
        supportMultiMode: false 
      } 
    }; 
 
    let abilityResult: common.AbilityResult = await this.mContext.startAbilityForResult(wantParam) 
    let wantResult: Want | undefined = abilityResult.want; 
    if (!wantResult || !wantResult.parameters) { 
      return undefined 
    } 
 
    let uri: string = wantResult.parameters.resourceUri as string; 
    if (!uri) { 
      return undefined 
    } 
 
    return await this.saveImage(uri) 
  } 
 
  /** 
   * 保存照片并返回uri 
   * @param uri 
   * @returns 
   */ 
  async saveImage(uri: string): Promise<string | undefined> { 
    try { 
      let file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY); 
      let prefix = uri.substring(uri.lastIndexOf('.') + 1); 
      let tempFileName = getContext(this).filesDir + '/' + new Date().getTime() + '.' + prefix; 
      let tempFile = fileIo.openSync(tempFileName, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); 
      fileIo.copyFileSync(file.fd, tempFile.fd); 
      let imageData = fileUri.getUriFromPath(tempFileName); 
      console.info(TAG, `resolve2Sandbox successful.` + imageData); 
      return imageData; 
    } catch (err) { 
      let error = err as BusinessError; 
      console.error(TAG, `resolve2Sandbox failed, err is ${JSON.stringify(error)}`); 
    } 
    return undefined 
  } 
 
  /** 
   * Async create pixel map. 
   * 
   * @return pixelMa. 
   */ 
  async getPixelMap(fileName: string) { 
    const fd = this.readImage(fileName); 
    const imageSourceApi = image.createImageSource(fd); 
    if (!imageSourceApi) { 
      console.error(TAG, 'imageSourceAPI created failed!'); 
      return; 
    } 
    const pixelMap = await imageSourceApi.createPixelMap({ 
      editable: true 
    }); 
    return pixelMap; 
  } 
 
  readImage(uri: string) { 
    const prefix: string = 'file:/'; 
    if (uri.startsWith('file://media')) { 
    } else if (prefix) { 
      uri = uri.substring(prefix.length); 
    } 
    // path参数为文件的应用沙箱路径或文件URI,所以使用沙箱路径时,需要去掉前缀 
    const file: fs.File = fs.openSync(uri, fs.OpenMode.READ_ONLY); 
    return file.fd; 
  } 
 
  /** 
   * 获取内置图片pixelMap 
   * @param fileName 
   * @returns 
   */ 
  async getResourcePixelMap(fileName: Resource) { 
    let resourceMgr = this.mContext.resourceManager as resourceManager.ResourceManager; 
    let imageBuffer = await resourceMgr.getMediaContent(fileName.id); 
    let arrayBuffer = FileUtils.getInstance().uint8ArrayToBuffer(imageBuffer); 
    let imageSourceApi: image.ImageSource = image.createImageSource(arrayBuffer); 
    const pixelMap = await imageSourceApi.createPixelMap({ 
      editable: true 
    }); 
    return pixelMap; 
  } 
 
  /** 
   * 保存pixelMap,返回路径 
   * @param pm 
   * @returns 
   */ 
  async savePixelMap(pm: PixelMap): Promise<string> { 
    if (pm === null) { 
      console.error(TAG, '传入的pm为空'); 
      return ''; 
    } 
    const imagePackerApi: image.ImagePacker = image.createImagePacker(); 
    let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 30 }; 
    try { 
      this.packToFile(pm); 
      const data: ArrayBuffer = await imagePackerApi.packing(pm, packOpts); 
      return await this.saveFile(data); 
    } catch (err) { 
      console.error(TAG, '保存文件失败,err=' + JSON.stringify(err)); 
      return ''; 
    } 
  } 
 
  async packToFile(pixelMap: PixelMap) { 
    let fPath: string = this.mContext.cacheDir + '/' + this.getTimeStr() + '.jpg'; 
    let writeFd: fs.File = await fs.open(fPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE); 
 
    let opts : image.PackingOption = { format: "image/jpeg", quality: 100}; 
    const imagePacker = image.createImagePacker(); 
    await imagePacker.packToFile(pixelMap, writeFd.fd, opts); 
    fs.closeSync(writeFd.fd); 
  } 
 
  async saveFile(data: ArrayBuffer): Promise<string> { 
    let uri: string = this.mContext.filesDir + '/' + this.getTimeStr() + '.jpg'; 
    let file: fileIo.File = fs.openSync(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 
    fs.writeSync(file.fd, data); 
    fs.closeSync(file); 
    // /data/storage/.... 加上file://前缀 
    uri = 'file:/' + uri; 
    return uri; 
  } 
 
  getTimeStr() { 
    const now: Date = new Date(); 
    const year: number = now.getFullYear(); 
    const month: number = now.getMonth() + 1; 
    const day: number = now.getDate(); 
    const hours: number = now.getHours(); 
    const minutes: number = now.getMinutes(); 
    const seconds: number = now.getSeconds(); 
    return `${year}${month}${day}_${hours}${minutes}${seconds}`; 
  } 
} 
export default new PictureUtil()
分享
微博
QQ
微信
回复
3天前
相关问题
HarmonyOS 自定义相机演示demo
53浏览 • 1回复 待解决
HarmonyOS 全局自定义弹窗demo
186浏览 • 1回复 待解决
hvigor自定义扩展demo
881浏览 • 1回复 待解决
能够提供HarmonyOS自定义相机案例吗?
243浏览 • 1回复 待解决
HarmonyOS 自定义相机拍照后数据展示
552浏览 • 1回复 待解决
HarmonyOS 请提供自定义组件封装demo
283浏览 • 2回复 待解决
【求助】自定义相机Camera2焦距异常
7855浏览 • 1回复 待解决
自定义弹窗自定义转场动画
892浏览 • 1回复 待解决
HarmonyOS 自定义键盘
141浏览 • 1回复 待解决
HarmonyOS 使用自定义字体
110浏览 • 1回复 待解决
HarmonyOS 自定义弹窗选择
245浏览 • 1回复 待解决
HarmonyOS TextInput自定义键盘
224浏览 • 1回复 待解决
HarmonyOS 自定义Slider样式
218浏览 • 1回复 待解决
HarmonyOS 如何自定义注解?
101浏览 • 1回复 待解决
HarmonyOS 自定义组件问题
251浏览 • 1回复 待解决
HarmonyOS 如何自定义tab
277浏览 • 2回复 待解决
HarmonyOS 如何自定义BuildMode?
373浏览 • 1回复 待解决