上一篇添加水印的代码有问题,在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();
}
}