首页 > 其他分享 >Flutter OHOS flutter_gpu_image(图片视频添加滤镜)

Flutter OHOS flutter_gpu_image(图片视频添加滤镜)

时间:2024-12-18 09:31:25浏览次数:4  
标签:1.0 0.0 image private 滤镜 let null Flutter

GPUImage for Flutter

Flutter中相机、照片、视频添加各种滤镜效果。

本地环境

  • [✓] Flutter (Channel stable, 3.0.0, on macOS 12.3.1 21E258 darwin-x64, locale zh-Hans-CN)
  • [✓] Android toolchain develop for Android devices (Android SDK version 33.0.0-rc1)
  • [✓] Xcode develop for iOS and macOS (Xcode 13.3.1)
  • [✓] Chrome develop for the web
  • [✓] Android Studio (version 2021.1)
  • [✓] VS Code (version 1.66.2)
  • [✓] Connected device (4 available)
  • [✓] HTTP Host Availability

集成步骤

1、pubspec.yaml

gpu_image: ^1.0.0

2、引入

import 'package:gpu_image/gpu_image.dart';

相机

final GlobalKey<GPUCameraWidgetStatecameraKey = GlobalKey();
//相机widget
GPUCameraWidget(
key: cameraKey,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
cameraCallBack: GPUCameraCallBack(
  recordPhoto: (path){
print("拍照保存地址 $path");
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =ImagePage(path: path)));
  }
),
  ),

//拍照
cameraKey.currentState?.recordPhoto();
//切换摄像头
cameraKey.currentState?.switchCamera();
//设置滤镜
cameraKey.currentState?.setFilter(filter);

图片

final GlobalKey<GPUImageWidgetState> imageKey = GlobalKey();
GPUImageWidget(
key: imageKey,
width: 400,
height: 600,
path: widget.path,
callBack: GPUImageCallBack(
saveImage: (path){
  print("保存图片地址 $path")
Navigator.of(context).push(MaterialPageRoute(
  builder: (context) => ImagePage(path: path)));
}
),
),
//设置滤镜
imageKey.currentState?.setFilter(filter);
//保存图片
imageKey.currentState?.saveImage();

滤镜

鸿蒙OS关键代码

相机 CameraView.ets

@Component
struct XComponentView {
  @Prop params: Params
  @StorageLink('width') w: number = 1080
  @StorageLink('Height') h: number = 1920
  @StorageLink('id') idx: number = 1
  cameraView: CameraView = this.params.platformView as CameraView;
  build() {
Column() {
  XComponent({id:'camera_' + this.idx.toString(), type:'surface',libraryname:'gpuimagenative'})
.width(this.w.toString() + 'px')
.height(this.h.toString() + 'px')
.onLoad(() => {
  /*OpenGL的离屏渲染线程在XComponent的OnSurfaceCreated回调创建,
  * 相机启动预览流需要NativeImage的Surface,所以在onLoad中调用startCameraInView启动相机
  */
  this.cameraView.startCameraInView();
})
}
  }

  aboutToAppear(): void {
  }

  aboutToDisappear(): void {
  }

  aboutToRecycle(): void {
  }
}

@Builder
function XComponentViewBuilder(params: Params) {
  XComponentView({ params: params})
}

AppStorage.setOrCreate('width', 1080)
AppStorage.setOrCreate('Height', 1920)
AppStorage.setOrCreate('id', 1)

@Observed
export class CameraView extends PlatformView implements MethodCallHandler {
  private context: common.Context | null = null;
  private uiAbility: UIAbility | null = null;
  private width: number = 0;
  private height: number = 0;
  private id: number = 0;
  private channel: MethodChannel | null = null;
  private isBackCamera: boolean = true;
  private offScreenSurface: string = '0';
  private cameraSession: camera.PhotoSession | null = null;
  private previewOutput: camera.PreviewOutput | null = null;
  private cameraInput: camera.CameraInput | null = null;

  constructor(ctx: common.Context, uiAbility: UIAbility,
messenger: BinaryMessenger, id: number, params: Map<string, ESObject>) {
super();
this.id = id;
this.width = params.get("width") as number;
this.height = params.get("height") as number;
let widthLink: SubscribedAbstractProperty<number> = AppStorage.link("width");
widthLink.set(this.width);
let heightLink: SubscribedAbstractProperty<number> = AppStorage.link("Height");
heightLink.set(this.height);
let idLink: SubscribedAbstractProperty<number> = AppStorage.link("id");
idLink.set(this.id);
this.channel = new MethodChannel(messenger, "com.gstory.gpu_image/camera_" + this.id.toString());
this.channel.setMethodCallHandler(this);
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
  case "setFilter":
let args: Map<string, ESObject> = call.args as Map<string, ESObject>;
FilterTools.switchFilter(args);
break;
  case "switchCamera":
this.isBackCamera = !this.isBackCamera;
this.startCameraIfReady();
break;
  case "recordPhoto":
let folderName: string = "GPUImage";
let fileName: string = systemDateTime.getTime(false).toString() + ".jpg";
GpuImageNative.savePixelMap((err: BusinessError, pixelMap: image.PixelMap) => {
  const ctx = getContext(this);
  let folderPath = ctx.filesDir + "/" + folderName;
  if (!fs.accessSync(folderPath)) {
fs.mkdirSync(folderPath);
  }
  let imagePath = folderPath + "/" + fileName;
  let imageFile = fs.openSync(imagePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
  const imagePacker = image.createImagePacker();
  const packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
  imagePacker.packToFile(pixelMap, imageFile.fd, packOpts).then(() => {
let args: Map<string, ESObject> = new Map();
args.set("path", imagePath);
this.channel!.invokeMethod("recordPhoto", args);
  });
});
break;
  case "closeCamera":
//PlatformView的原生Component生命周期不会触发aboutToDisappear
//根据Flutter页面的dispose回调释放相机和渲染线程资源
this.stopCamera();
GpuImageNative.releaseRenderThread();
break;
  default:
result.notImplemented();
break;
}
  }

  getView(): WrappedBuilder<[Params]> {
return new WrappedBuilder(XComponentViewBuilder);
  }

  dispose(): void {

  }

  private async startCameraIfReady(): Promise<void> {
checkPermissions(permissions).then((hasPermission: boolean) => {
  if (hasPermission) {
this.startCamera();
  } else {
reqPermissionsFromUser(permissions, getContext(this) as Context).then((isPermit: boolean) => {
  if (isPermit) {
this.startCamera();
  } else {
Log.e(TAG, "get permission failed!");
  }
})
  }
})

  }

  private async startCamera(): Promise<void> {
await this.stopCamera();
let cameraManager = CameraUtil.getCameraManager(this.context!);
let inputCamera: camera.CameraDevice | null = null;
if (this.isBackCamera) {
  inputCamera = CameraUtil.getCameraDevice(cameraManager, camera.CameraPosition.CAMERA_POSITION_BACK);
  GpuImageNative.setRotation(Rotation.ROTATION_0, false, false);
} else {
  inputCamera = CameraUtil.getCameraDevice(cameraManager, camera.CameraPosition.CAMERA_POSITION_FRONT);
  GpuImageNative.setRotation(Rotation.ROTATION_0, false, true);
}
this.offScreenSurface = GpuImageNative.getSurfaceId().toString();
if (this.offScreenSurface == '0') {
  Log.e(TAG, "getSurfaceId error!");
  return;
}
await this.startPreviewOutput(cameraManager, inputCamera!, this.offScreenSurface, this.height / this.width);
  }

  private async startPreviewOutput(cameraManager: camera.CameraManager, inputCamera: camera.CameraDevice,
surfaceId: string, scale: number): Promise<void> {
try {
  let profiles: camera.CameraOutputCapability =
cameraManager.getSupportedOutputCapability(inputCamera, camera.SceneMode.NORMAL_PHOTO);
  let bestProfile =
CameraUtil.getSuitableProfile(camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP, scale, profiles.previewProfiles);
  this.previewOutput = cameraManager.createPreviewOutput(bestProfile, surfaceId);
  this.cameraInput = cameraManager.createCameraInput(inputCamera);
  await this.cameraInput.open();
  this.cameraSession =
cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
  this.setSessionListener();
  this.cameraSession.beginConfig();
  this.cameraSession.addInput(this.cameraInput);
  this.cameraSession.addOutput(this.previewOutput);
  await this.cameraSession.commitConfig();
  await this.cameraSession.start();
} catch (err) {
  Log.e(TAG, "startPreviewOutput error: " + JSON.stringify(err));
}
  }

  private async stopCamera(): Promise<void> {
try{
  if (this.cameraInput) {
await this.cameraInput.close();
  }
  if (this.previewOutput) {
await this.previewOutput.release();
  }
  if (this.cameraSession) {
await this.cameraSession.release();;
  }
} catch (err) {
  Log.e(TAG, "stopCamera failed! " + JSON.stringify(err))
}
  }

  private setSessionListener(): void {
if(this.cameraSession) {
  this.cameraSession.on("error", (err: BusinessError) => {
Log.e(TAG, "cameraSession error: " + JSON.stringify(err));
  })
}
  }

  public startCameraInView = () => {
this.startCameraIfReady();
  }

  public stopCameraInView = () => {
GpuImageNative.releaseRenderThread();
this.stopCamera();
  }
}

图片 ImageView.ets

@Component
struct ImageComponent {
  @Prop params: Params
  @StorageLink('picture') pixelMap: image.PixelMap | null = null;
  @StorageLink('width') w: number = 1080
  @StorageLink('Height') h: number = 1920
  build() {
Column() {
  Image(this.pixelMap)
.width(this.w.toString() + 'px')
.height(this.h.toString() + 'px')
}
  }

  aboutToAppear(): void {
  }

  aboutToDisappear(): void {
  }

  aboutToRecycle(): void {
  }
}

@Builder
function ImageBuilder(params: Params) {
  ImageComponent({params: params})
}

AppStorage.setOrCreate('picture', null)
AppStorage.setOrCreate('width', 1080)
AppStorage.setOrCreate('Height', 1920)

@Observed
export class ImageView extends PlatformView implements MethodCallHandler {
  private context: common.Context | null = null;
  private uiAbility: UIAbility | null = null;
  private width: number = 0;
  private height: number = 0;
  private path: string = "";
  private channel: MethodChannel | null = null;

  constructor(ctx: common.Context, uiAbility: UIAbility,
  messenger: BinaryMessenger, id: number, params: Map<string, ESObject>) {
super();
this.width = params.get("width") as number;
this.height = params.get("height") as number;
let widthLink: SubscribedAbstractProperty<number> = AppStorage.link("width");
widthLink.set(this.width);
let heightLink: SubscribedAbstractProperty<number> = AppStorage.link("Height");
heightLink.set(this.height);
this.path = params.get("path") as string;
this.channel = new MethodChannel(messenger, "com.gstory.gpu_image/image_" + id);
this.channel.setMethodCallHandler(this);
this.initImage();
  }

  private initImage() {
if(this.path.startsWith("http")) {
  let httpRequest = http.createHttp();
  httpRequest.request(this.path,(err, data)=>{
if(http.ResponseCode.OK == data.responseCode) {
  let imageResource = image.createImageSource(data.result as ArrayBuffer);
  let options: Record<string, ESObject> = {
'alphaType': 0,
'editable': false,
'pixelFormat': image.PixelMapFormat.RGBA_8888,
'scaleMode': 1,
'size': { height: 100, width: 100 }
  }
  imageResource.createPixelMap(options).then((pixelMap) => {
GpuImageNative.setImage(pixelMap);
  });
}
  });
} else {
  const imageSource = image.createImageSource(this.path);
  imageSource.createPixelMap((err: BusinessError, pixelMap: image.PixelMap) => {
pixelMap.getImageInfo().then((imageInfo) => {
  const readBuffer: ArrayBuffer = new ArrayBuffer(imageInfo.size.height*imageInfo.size.width*4);
  pixelMap.readPixelsToBufferSync(readBuffer);
})
try {
  let pictureLink: SubscribedAbstractProperty<image.PixelMap> = AppStorage.link("picture");
  pictureLink.set(pixelMap);
} catch (err) {
  Log.e(TAG, "setImage error:" + err);
}
  });
}
  }


  onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
  case "setFilter":
let args: Map<string, ESObject> = call.args as Map<string, ESObject>;
FilterTools.switchFilter(args);
break;
  case "saveImage":
let folderName: string = "GPUImage";
let fileName: string = systemDateTime.getTime(false).toString() + ".jpg";
GpuImageNative.savePixelMap((err: BusinessError, pixelMap: image.PixelMap)=>{
  const ctx = getContext(this);
  let folderPath = ctx.filesDir + "/" + folderName;
  if(!fs.accessSync(folderPath)) {
fs.mkdirSync(folderPath);
  }
  let imagePath = folderPath + "/" + fileName;
  let imageFile = fs.openSync(imagePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
  const imagePacker = image.createImagePacker();
  const packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
  imagePacker.packToFile(pixelMap, imageFile.fd, packOpts).then(()=>{
let args: Map<string, ESObject> = new Map();
args.set("path", imagePath);
this.channel!.invokeMethod("recordPhoto", args);
  });
});
break;
  default :
result.notImplemented();
break;
  }
}

  getView(): WrappedBuilder<[Params]> {
return new WrappedBuilder(ImageBuilder);
  }

  dispose(): void {
  }
}

标签:1.0,0.0,image,private,滤镜,let,null,Flutter
From: https://www.cnblogs.com/hongmengos/p/18613894

相关文章

  • 鸿蒙Flutter怎样调试dart代码
    鸿蒙Flutter怎么样调试dart代码ets代码使用DevEco-Studio进行调试。dart代码可以使用vscode和AndroidStudio进行调试。flutter鸿蒙化版本调试时,需要加上参数,如:--local-engine=/Users/xxx/ohos/engine/src/out/ohos_debug_unopt_arm64使用vscode调试dart代......
  • 鸿蒙Flutter性能调优之性能分析定界
    鸿蒙Flutter性能调优之性能分析定界flutter鸿蒙化的工程,也可以使用devtools对Dart代码进行调试.前置条件OpenHarmonyNext系统前台运行Flutter页面分析工具DevEcoStudioProfilerSmartPerfFlutter线程介绍Flutter使用多个线程来完成其必要的工作,图层中仅展示了其中两......
  • 鸿蒙Flutter环境相关问题解决方法
    鸿蒙Flutter环境相关问题建议使用的开发工具版本flutter3.7.12-ohos版本python3.8-python3.11java17node18ohpm1.6+HamonyOSSDKapi11Xcode14.3断网环境flutterpubget执行失败解决方案:加上--offline参数,完整命令flutterpubget--offlinemac环境releas......
  • harmony_flutter_udid
    harmony_flutter获取udidUDID说明:在恢复出厂设置后,UDID(唯一设备标识符)可能会发生变化!另外,如果设备通过OTA(在线更新)升级到了Android8.0,并且应用程序被重新安装了,由于Android8.0的安全性更改,UDID也可能会改变。对于已经获取root权限或越狱的设备,其ID是可以被更改的,请注意这一点......
  • harmony_flutter_FlutterEngineGroup
    harmony_flutter_FlutterEngineGroup介绍1.EntryAbility修改为继承自UIAbilityexportdefaultclassEntryAbilityextendsUIAbilityimplementsExclusiveAppComponent<UIAbility>{detachFromFlutterEngine():void{//thrownewError('Methodnotimplem......
  • flutter_ohos_5
    ohos开发鸿蒙(基于5.0.0版本)原始仓来源:https://github.com/flutter/flutter1.仓库说明本仓库是基于FlutterSDK对于OpenHarmony平台的兼容拓展,可支持IDE或者终端使用FlutterTools指令编译和构建OpenHarmony应用程序。2.环境依赖说明:1.FlutterTools指令目前已支持在Linux......
  • Flutter OHOS harmony_fluwx 集成微信服务(二)
    harmony_fluwx集成微信服务(2)fluwx链接:https://gitee.com/almost777/fluwx接入功能分享图片,文本,音乐,视频等。支持分享到会话,朋友圈以及收藏.微信支付.在微信登录时,获取AuthCode.拉起小程序.订阅消息.打开微信.从微信标签打开应用初始化注册WxAPIregisterWxApi(ap......
  • Flutter OHOS flutter_dart_native
    DartNativeDartNative作为Dart和原生API之间沟通的桥梁。用更快、更简洁的代码替换性能低下的Flutter通道。特征动态同步和异步通道DartNative动态调用任何原生API。它支持同步和异步通道。多语言接口直接调用不再需要像FlutterChannel那样对参数和返回值进行......
  • Flutter OHOS flutter appscheme插件
    FlutterAppScheme配置说明1、Android端配置说明在您项目中Android的AndroidManifest.xml文件中按照如下规范添加Scheme,例如android/app/src/main/AndroidManifest.xmla、在需要启动的Activity中新增以下格式的代码<!--AndroidScheme--><intent-filter><actionandro......
  • 谷歌发布升级版AI视频生成器Veo 2与图像生成器Imagen 3
      每周跟踪AI热点新闻动向和震撼发展想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领域的领跑者。点击订阅,与未来同行!订阅:https://......