效果图:三中代码实现的效果差不多
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))
-
- /**
- * @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
loadImageByFile(String path) async { - var list = await File(path).readAsBytes();
- return loadImageByUint8List(list);
- }
-
- //通过[Uint8List]获取图片
- static Future
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, '位置信息:北京市朝阳区三里屯');