效果图:三中代码实现的效果差不多
Swift:代码
import UIKit
class ImageWatermarking: NSObject {
static func textToImage(drawText text: String, inImage initImage: UIImage, atPoint point: CGPoint) -> UIImage {
let textColor = UIColor.white
let textFont = UIFont(name:"Helvetica", size: 26)!
// 新建底部拼接区域
let rect: CGRect = CGRect(x: 0, y: 0, width: initImage.size.width, height: 220)
UIGraphicsBeginImageContext(rect.size)
let context: CGContext = UIGraphicsGetCurrentContext()!
context.setFillColor(UIColor.kHexRGBA(0x000000,0.3).cgColor)
context.fill(rect)
let imagebottom = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsGetCurrentContext()
let style = NSMutableParagraphStyle()
style.lineSpacing = 6
style.lineBreakMode = NSLineBreakMode.byTruncatingTail
// 在底部拼接区域添加文字
let textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.paragraphStyle: style
] as [NSAttributedString.Key : Any]
imagebottom!.draw(in: CGRect(origin: CGPoint.zero, size: imagebottom!.size))
let rectText = CGRect(origin: point, size: initImage.size)
text.draw(in: rectText, withAttributes: textFontAttributes)
let newTextImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// 拼接原图和新建的底部区域
let topImage = initImage
let bottomImage = newTextImage
let allSize = CGSize(width: topImage.size.width, height: topImage.size.height )
UIGraphicsBeginImageContext(allSize)
let areaSizeTop = CGRect(x: 0, y: 0, width: topImage.size.width, height: topImage.size.height)
topImage.draw(in: areaSizeTop)
let areaSizeBottom = CGRect(x: 0, y: areaSizeTop.size.height-220, width: bottomImage!.size.width, height: bottomImage!.size.height)
bottomImage!.draw(in: areaSizeBottom)
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
Swift 调用:
let contentStr :String = currentTime + "\n" + address
let newIMage = ImageWatermarking.textToImage(drawText: contentStr, inImage: image!, atPoint: CGPoint(x: 20, y: 40))
Java 代码:
/**
* @description 形成水印工具类
@param
* @author ·Steve
* @date 2023/7/13 10:41
*/
public class WatermarkSettings {
public static WatermarkSettings mInstance;
public static Context mContext;
private static String watermarkText;
private static String TAG = "";
/**
* @description 图片添加水印的信息
@param
* @author ·Steve
* @date 2023/7/13 10:41
*/
public static WatermarkSettings getmInstance(Context context) {
mContext = context;
if (mInstance == null) {
mInstance = new WatermarkSettings();
}
TAG = mContext.getClass().getName();
return mInstance;
}
/**
* @description 创建水印文件,以下是水印上添加的文本信息
@param
* @author ·Steve
* @date 2023/7/13 10:42
*/
public static Bitmap createWatermark(String patch, String contextStr) {
Bitmap bitmap = BitmapFactory.decodeFile(patch);
int i = readPictureDegree(patch);
if (i != ExifInterface.ORIENTATION_UNDEFINED){
bitmap = rotaingImageView(i, bitmap);
}
// 获取图片的宽高
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
// 创建一个和图片一样大的背景图
Bitmap bmp = Bitmap.createBitmap(bitmapWidth, bitmapHeight+ (dp2px(mContext, 0) * bitmapWidth / getScreenWidth()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
canvas.drawColor(Color.parseColor("#333333"));
// 画背景图
canvas.drawBitmap(bitmap, 0, 0, null);
watermarkText = contextStr;
//-------------开始绘制文字--------------
if (!TextUtils.isEmpty(watermarkText)) {
int screenWidth = getScreenWidth();
float textSize = dp2px(mContext, 16) * bitmapWidth / screenWidth;
// 绘制矩形背景
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0x4d000000);
//绘制正方形
canvas.drawRect(0, bitmapHeight- (dp2px(mContext, 130) * bitmapWidth / getScreenWidth()), bitmapWidth, bitmapHeight, paint);
// 创建画笔
TextPaint mPaint = new TextPaint();
// 文字矩阵区域
Rect textBounds = new Rect();
// 水印的字体大小
mPaint.setTextSize(textSize);
// 文字阴影
mPaint.setShadowLayer(0.5f, 0f, 1f, Color.WHITE);
// 抗锯齿
mPaint.setAntiAlias(true);
// 水印的区域
mPaint.getTextBounds(watermarkText, 0, watermarkText.length(), textBounds);
// 水印的颜色
mPaint.setColor(Color.WHITE);
StaticLayout layout = new StaticLayout(watermarkText, 0, watermarkText.length(), mPaint, (int) (bitmapWidth - textSize),
Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.5F, true);
// 文字开始的坐标
float textX = dp2px(mContext, 8) * bitmapWidth / screenWidth;
float textY = bitmapHeight;//图片的中间
// float textY = dp2px(mContext, 8) * bitmapHeight / screenWidth;
// 画文字
canvas.translate(textX, bitmapHeight-(dp2px(mContext, 130) * bitmapWidth / getScreenWidth()));
layout.draw(canvas);
}
//保存所有元素
canvas.save();
canvas.restore();
return bmp;
}
private static int getScreenWidth() {
DisplayMetrics dm = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
private static int dp2px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
//获取图片旋转角度
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {
// 旋转图片 动作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
// 创建新的图片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* @description 保存图片到相册
@param
* @author ·Steve
* @date 2023/7/13 13:59
*/
public static File saveToLocal(Bitmap bitmap,Context context) throws IOException {
String bitName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
File file = new File("/sdcard/DCIM/Camera/" + bitName + ".jpg");
if (file.exists()) {
file.delete();
}
FileOutputStream out;
try {
out = new FileOutputStream(file);
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out)) {
out.flush();
out.close();
//保存图片后发送广播通知更新数据库
// Uri uri = Uri.fromFile(file);
// sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(file);
intent.setData(uri);
context.sendBroadcast(intent);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
}
Kotlin 调用:
val contentStr:String = "\n" +dateTimeStr+"\n" + userStr
WatermarkSettings.getmInstance(this)
val bitmap: Bitmap = WatermarkSettings.createWatermark(photo.path, contentStr)
val tmpFile = WatermarkSettings.saveToLocal(bitmap,this)
Flutter 代码:
/// 图片加载工具类
class ImageWaterMarkUtils {
//拿到图片的字节数组
static Future<ui.Image> loadImageByFile(String path) async {
var list = await File(path).readAsBytes();
return loadImageByUint8List(list);
}
//通过[Uint8List]获取图片
static Future<ui.Image> loadImageByUint8List(Uint8List list) async {
ui.Codec codec = await ui.instantiateImageCodec(list);
ui.FrameInfo frame = await codec.getNextFrame();
return frame.image;
}
//图片加文字
static imageAddWaterMark(String imagePath, String content) async {
double width, height;
//拿到Canvas
ui.PictureRecorder recorder = new ui.PictureRecorder();
Canvas canvas = new Canvas(recorder);
//拿到Image对象
ui.Image image = await loadImageByFile(imagePath);
width = image.width.toDouble();
height = image.height.toDouble();
// 计算四边形的对角线长度
// double dimension = math.sqrt(math.pow(image.width, 2) + math.pow(image.height, 2));
canvas.drawImage(image, Offset(0, 0), Paint());
canvas.saveLayer(Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), Paint()..blendMode = BlendMode.multiply);
Rect rectBg = Rect.fromLTWH(0, height-600, width, 600);
Paint paint =Paint()
..color = const Color(0x66000000)
..strokeCap = StrokeCap.round
..isAntiAlias = true
..style = PaintingStyle.fill;
canvas.drawRect(rectBg, paint);
canvas.restore();
// /// 1.生成 ParagraphStyle,可设置文本的基本信息
final paragraphStyle = ui.ParagraphStyle();
/// 2.根据 ParagraphStyle 生成 ParagraphBuilder
final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle);
/// 3.添加样式和文字
paragraphBuilder
..pushStyle(ui.TextStyle(color: Colors.white, fontSize:110))
..addText('拍照时间:${DateTools.getCurrentTimeHM()}\n')
..pushStyle(ui.TextStyle(color: Colors.white, fontSize:90))
..addText(content)
;
/// 4.通过 build 取到 Paragraph
final paragraph = paragraphBuilder.build();
/// 5.根据宽高进行布局layout
paragraph.layout(ui.ParagraphConstraints(width: width));
/// 6.绘制
canvas.drawParagraph(paragraph, Offset(100, height-500));
// canvas.restore();
ui.Picture picture = recorder.endRecording();
final img = await picture.toImage(width.toInt(), height.toInt());
final pngBytes = await img.toByteData(format: ui.ImageByteFormat.png);
final Directory directory = await getTemporaryDirectory();
final Directory imageDirectory = await Directory('${directory.path}/image/').create(recursive: true);
String targetPath = imageDirectory.path;
File file = File('${targetPath}watermark${DateTime.now().millisecondsSinceEpoch}.png');
file.writeAsBytesSync(pngBytes!.buffer.asInt8List());
// 图片压缩, 不然iOS端照片会很大, 传不上去
var result = await FlutterImageCompress.compressWithFile(
file.absolute.path,
quality: 95,
rotate: 0,
);
file.writeAsBytesSync(result as List<int>);
return file;
}
}
Flutter 调用
File file = await ImageWaterMarkUtils.imageAddWaterMark(image.path, '位置信息:北京市朝阳区三里屯');