flutter图片添加水印-优化

上一篇添加水印的代码有问题,在image高版本如4.5.4,水印中文字变成乱码花屏。

修复后效果

添加水印工具类

dart 复制代码
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:util/easy_loading_util.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'dart:ui' as ui;
import 'package:path_provider/path_provider.dart';

//制造带Logo和文字的水印图片
_makeWatermarkImage(String str, double fontSize, img.Image logoImage) async {
  // 调整Logo大小 - 保持原始宽高比,只限制高度
  final logoHeight = fontSize.toInt();
  final resizedLogo = img.copyResize(
    logoImage,
    height: logoHeight,
  );
  final logoWidth = resizedLogo.width;
  
  // 创建文字图片
  final picRecorder = ui.PictureRecorder();
  final paragraphBuilder = ui.ParagraphBuilder(
    ui.ParagraphStyle(
      textAlign: TextAlign.left,
      fontSize: fontSize,
    ),
  );
  paragraphBuilder.pushStyle(
    ui.TextStyle(
      fontSize: fontSize,
      color: Colors.white,
      shadows: <Shadow>[
        Shadow(
          color: Colors.black54,
          blurRadius: 1.0,
          offset: Offset(1, 1),
        ),
      ],
    ),
  );
  paragraphBuilder.addText(str);
  final paragraph = paragraphBuilder.build()
    ..layout(ui.ParagraphConstraints(width: fontSize * 15.0));
  
  final lineMetrics = paragraph.computeLineMetrics();
  double textWidth = 0;
  for (var element in lineMetrics) {
    if (element.width > textWidth) {
      textWidth = element.width;
    }
  }
  
  // 计算总宽度和高度
  final spacing = (fontSize * 0.5).toInt(); // Logo和文字之间的间距
  final totalWidth = logoWidth + spacing + textWidth;
  final totalHeight = logoHeight > paragraph.height ? logoHeight : paragraph.height;
  
  final cvs = Canvas(
    picRecorder,
    Rect.fromLTRB(0, 0, totalWidth, totalHeight.toDouble()),
  );
  
  // 计算垂直居中的偏移量
  final logoY = (totalHeight - logoHeight) / 2;
  final textY = (totalHeight - paragraph.height) / 2;
  
  // 绘制Logo(左侧)
  // 先将img.Image转换为ui.Image
  final logoUiImage = await _convertImgImageToUiImage(resizedLogo);
  if (logoUiImage != null) {
    cvs.drawImage(logoUiImage, Offset(0, logoY), Paint());
  }
  
  // 绘制文字(右侧,垂直居中)
  cvs.drawParagraph(paragraph, Offset((logoWidth + spacing).toDouble(), textY));
  
  final pic = picRecorder.endRecording();
  final waterMark = await pic.toImage(
    totalWidth.toInt(),
    totalHeight.toInt(),
  );
  return waterMark;
}

//将 img.Image 转换为 ui.Image
Future<ui.Image?> _convertImgImageToUiImage(img.Image imgImage) async {
  final pngBytes = img.encodePng(imgImage);
  final codec = await ui.instantiateImageCodec(pngBytes);
  final frame = await codec.getNextFrame();
  return frame.image;
}

//将 ui.Image 转换为 img.Image
Future<img.Image?> _convertUiImageToImgImage(ui.Image uiImage) async {
  final byteData = await uiImage.toByteData(format: ui.ImageByteFormat.png);
  if (byteData == null) return null;
  final pngBytes = byteData.buffer.asUint8List();
  return img.decodeImage(pngBytes);
}

//确定水印位置,并将水印添加到原图
_makeImageMark(
  img.Image image,
  String markStr,
  img.Image imageLogo, [
  bool isLeft = true,
]) async {
  final imgHeight = image.height;
  final imgWidth = image.width;
  final ratio = imgWidth / 375.0;
  final fontSize = (ratio * 14.0).toDouble();
  final edgeSize = (ratio * 12.0).toInt();
  
  // 创建组合水印(Logo + 文字)
  final waterMarkImage = await _makeWatermarkImage(markStr, fontSize, imageLogo);
  final waterMarkImg = await _convertUiImageToImgImage(waterMarkImage);
  
  if (waterMarkImg == null) {
    esLoadingToast('添加水印失败');
    return;
  }
  
  // 计算位置(右下角)
  int dstX;
  int dstY = imgHeight - waterMarkImg.height - edgeSize;
  if (isLeft) {
    dstX = edgeSize;
  } else {
    dstX = imgWidth - edgeSize - waterMarkImg.width;
  }
  
  // 将水印合成到原图
  img.compositeImage(image, waterMarkImg, dstX: dstX, dstY: dstY);
}

/*
  * 图片左、右下脚添加图片+文字水印
  * */
Future<String> addWatermarkToImage(
  Uint8List uInt8List, {
  Uint8List? markUInt8List,
  String? markUserName,
}) async {
  Completer<String> completer = Completer<String>();
  final markLogo = await getDecodeImage(markUInt8List!);
  final originImage = await getDecodeImage(uInt8List);

  Directory? directory;
  if (Platform.isAndroid) {
    directory = await getExternalStorageDirectory();
  } else if (Platform.isIOS) {
    directory = await getApplicationDocumentsDirectory();
  } else {
    directory = await getTemporaryDirectory();
  }

  final Directory imageDirectory = await Directory(
    '${directory?.path}/image/',
  ).create(recursive: true);
  String targetPath = imageDirectory.path;
  await _makeImageMark(
    originImage,
    markUserName == null ? '' : '@$markUserName',
    markLogo,
    false,
  );

  final bytes = img.encodeJpg(originImage);
  String imagePath =
      '${targetPath}watermark${DateTime.now().millisecondsSinceEpoch}.jpg';

  final success = await img.writeFile(imagePath, bytes);
  if (success == true) {
    completer.complete(imagePath);
    print('====targetFilePath=$imagePath');
  } else {
    completer.complete('');
  }
  return completer.future;
}

getDecodeImage(Uint8List uInt8List) {
  Completer<img.Image> completer = Completer<img.Image>();
  img.Image? originImage = img.decodeImage(uInt8List);
  if (originImage == null) {
    throw Exception('图片解码失败');
  }
  completer.complete(originImage);
  return completer.future;
}

调用

dart 复制代码
//获取水印图标
  Future<Uint8List> getWatermarkLogo() async {
    final f = await rootBundle.load(PathConfig.iconWatermark);
    DEBUG.show("图片数据=     ${f.buffer.asUint8List()}");
    Uint8List markUint8List = f.buffer.asUint8List();
    return Future.value(markUint8List);
  }

//图片添加水印
  addWatermarkToImages() async{
    waterImages.clear();
    watermarkUint8List = await getWatermarkLogo();
    if(addWaterMark && state.selectImageList.isNotEmpty){
      ByteData byteData;
      for (int i = 0; i < state.selectImageList.length; i++) {
        //  Asset asset = state.selectImageList[i];
        //   byteData = await asset.getByteData();

        File file = state.selectImageList[i];
        //  Uint8List uint8ListData = byteData.buffer.asUint8List();
        Uint8List uint8ListData = file.readAsBytesSync();
        String watermarkImagePath =
        await addWatermarkToImage(uint8ListData,
            markUInt8List: watermarkUint8List,markUserName: userInfo.user!.nickname);
        if(watermarkImagePath.isNotEmpty){
          waterImages.add(watermarkImagePath);
          print('==waterImages length=$i=${waterImages.length}');
        }
      }
      //发布
      submit();
      update();
    }
  }
相关推荐
2501_920627613 小时前
Flutter 框架跨平台鸿蒙开发 - 派对策划助手应用
flutter·华为·harmonyos
里欧跑得慢4 小时前
Flutter 组件 powersync_core 的适配 鸿蒙Harmony 实战 - 驾驭极致离线优先架构、实现鸿蒙端高性能 SQL 增量同步与数据安全治理方案
flutter·harmonyos·鸿蒙·openharmony·powersync_core
芙莉莲教你写代码5 小时前
Flutter 框架跨平台鸿蒙开发 - 网络安全学习应用
学习·web安全·flutter·华为·harmonyos
kobesdu9 小时前
ROS Flutter GUI App:跨平台机器人可视化控制界面使用记录
flutter·机器人·ros
芙莉莲教你写代码9 小时前
Flutter 框架跨平台鸿蒙开发 - 时区转换器应用
学习·flutter·华为·harmonyos
芙莉莲教你写代码9 小时前
Flutter 框架跨平台鸿蒙开发 - 冥想指导应用
flutter·华为·harmonyos
王码码20359 小时前
Flutter 三方库 preact_signals 的鸿蒙化适配指南 - 掌控极致信号响应、Signals 架构实战、鸿蒙级精密状态指控专家
flutter·harmonyos·鸿蒙·openharmony·preact_signals
芙莉莲教你写代码11 小时前
Flutter 框架跨平台鸿蒙开发 - 密码管理器应用
服务器·flutter·华为·harmonyos
阿桂有点桂12 小时前
Flutter打包的app增加签名
flutter
芙莉莲教你写代码13 小时前
Flutter 框架跨平台鸿蒙开发 - 谜语大全
flutter·华为·harmonyos