iOS原生、Android 原生, flutter 三种方式给照片流添加文字(水印)

效果图:三中代码实现的效果差不多

0fb4a39137f44a19bc1c1cf66729a277.png

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, '位置信息:北京市朝阳区三里屯');

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值