iOS-为图片添加倾斜水印效果

作者: OliviaZqy | 来源:发表于2017-11-09 17:04 被阅读851次

    转载请联系作者获取授权,并标明文章作者,谢谢!

    所谓水印是向数据多媒体(如图像、声音、视频信号等)中添加某些数字信息以达到文件真伪鉴别、版权保护等功能。嵌入的水印信息隐藏于宿主文件中,不影响原始文件的可观性和完整性。也就是在图片上打上半透明的标记。我们先来看一下Demo的运行效果图。 源图片打上水印
    核心技术:CoreGraphics绘图。闲话少说,上代码。
    #define HORIZONTAL_SPACE 30//水平间距
    #define VERTICAL_SPACE 50//竖直间距
    #define CG_TRANSFORM_ROTATION (M_PI_2 / 3)//旋转角度(正旋45度 || 反旋45度)
    /**
     根据目标图片制作一个盖水印的图片
     
     @param originalImage 源图片
     @param title 水印文字
     @param markFont 水印文字font(如果不传默认为23)
     @param markColor 水印文字颜色(如果不传递默认为源图片的对比色)
     @return 返回盖水印的图片
     */
    + (UIImage *)getWaterMarkImage: (UIImage *)originalImage andTitle: (NSString *)title andMarkFont: (UIFont *)markFont andMarkColor: (UIColor *)markColor{
        
        UIFont *font = markFont;
        if (font == nil) {
            font = [UIFont systemFontOfSize:23];
        }
        UIColor *color = markColor;
        if (color == nil) {
            color = [self mostColor:originalImage];
        }
        //原始image的宽高
        CGFloat viewWidth = originalImage.size.width;
        CGFloat viewHeight = originalImage.size.height;
        //为了防止图片失真,绘制区域宽高和原始图片宽高一样
        UIGraphicsBeginImageContext(CGSizeMake(viewWidth, viewHeight));
        //先将原始image绘制上
        [originalImage drawInRect:CGRectMake(0, 0, viewWidth, viewHeight)];
        //sqrtLength:原始image的对角线length。在水印旋转矩阵中只要矩阵的宽高是原始image的对角线长度,无论旋转多少度都不会有空白。
        CGFloat sqrtLength = sqrt(viewWidth*viewWidth + viewHeight*viewHeight);
        //文字的属性
        NSDictionary *attr = @{
                               //设置字体大小
                               NSFontAttributeName: font,
                               //设置文字颜色
                               NSForegroundColorAttributeName :color,
                               };
        NSString* mark = title;
        NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:mark attributes:attr];
        //绘制文字的宽高
        CGFloat strWidth = attrStr.size.width;
        CGFloat strHeight = attrStr.size.height;
        
        //开始旋转上下文矩阵,绘制水印文字
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        //将绘制原点(0,0)调整到源image的中心
        CGContextConcatCTM(context, CGAffineTransformMakeTranslation(viewWidth/2, viewHeight/2));
        //以绘制原点为中心旋转
        CGContextConcatCTM(context, CGAffineTransformMakeRotation(CG_TRANSFORM_ROTATION));
        //将绘制原点恢复初始值,保证当前context中心和源image的中心处在一个点(当前context已经旋转,所以绘制出的任何layer都是倾斜的)
        CGContextConcatCTM(context, CGAffineTransformMakeTranslation(-viewWidth/2, -viewHeight/2));
        
        //计算需要绘制的列数和行数
        int horCount = sqrtLength / (strWidth + HORIZONTAL_SPACE) + 1;
        int verCount = sqrtLength / (strHeight + VERTICAL_SPACE) + 1;
        
        //此处计算出需要绘制水印文字的起始点,由于水印区域要大于图片区域所以起点在原有基础上移
        CGFloat orignX = -(sqrtLength-viewWidth)/2;
        CGFloat orignY = -(sqrtLength-viewHeight)/2;
        
        //在每列绘制时X坐标叠加
        CGFloat tempOrignX = orignX;
        //在每行绘制时Y坐标叠加
        CGFloat tempOrignY = orignY;
        for (int i = 0; i < horCount * verCount; i++) {
            [mark drawInRect:CGRectMake(tempOrignX, tempOrignY, strWidth, strHeight) withAttributes:attr];
            if (i % horCount == 0 && i != 0) {
                tempOrignX = orignX;
                tempOrignY += (strHeight + VERTICAL_SPACE);
            }else{
                tempOrignX += (strWidth + HORIZONTAL_SPACE);
            }
        }
        //根据上下文制作成图片
        UIImage *finalImg = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        CGContextRestoreGState(context);
        return finalImg;
    }
    
    下面我们讲一下原理
    1.绘制源图片

    拿到源图片,根据源图片尺寸调用UIGraphicsBeginImageContext方法将源图片绘制在画布上。

    2.绘制水印文字:

    要想绘制出倾斜任意角度的文字首先我们需要将画布倾斜,这样画出来的文字可以和没有倾斜的图片形成角度对比,给人感官上文字是倾斜的。
    Demo中采用CGContextConcatCTM方法旋转画布,但是笔者查阅资料发现该方法并不是绕着中心点旋转,而是绕着绘制原点(也就是初始值0,0)旋转。所以转出来是这样的效果。


    (0,0)点旋转45度

    如果在这样的画布上绘制水印很明显不是我们想要的。所以我们先将绘制原点平移到源图片区域的中心点,然后旋转角度,最后在按照之前平移的X轴,Y轴数据反方向平移回去。这样就能保证旋转完画布的中心点和源图片的中心点在同一位置。


    将绘制原点平移后旋转 旋转完后源路径平移回去

    注意:在旋转过后当前画布的X轴,Y轴方向也跟着旋转,所以再次平移可以回到中心点。
    这样我们得到了一个倾斜的画布开始绘制文字就可以了,还需注意如果我们还按照源图片大小的区域去绘制水印文字难免会有空白的地方(我们不知道水印要旋转多少度),所以我们要保证不出现空白,采用以源图片中心点,长宽为源图片对角线长度的绘制区域。这样无论旋转多少度都不会出现空白。如下图:

    外圈正方形为绘制区域
    所以我们需要根据源图片的长宽计算出orignX和orignY(向上和向左偏移多少开始绘制)。同时根据水印文字的长宽,计算出整个绘制区域可以绘制出多少个水印,然后for循环绘制。
    最后结束绘图调用UIGraphicsGetImageFromCurrentImageContext形成一个闭合区间的图片返回。
    注意:代码中的mostColor方法作用是:根据图片获取一个对比色保证水印能清楚显示。参考地址:iOS代码获取图片主色调

    总结

    Core Graphics是一个古老的基于C的绘图专用API,也被称为QuartZ或QuartZ 2D,是一个二维绘图引擎。真如我们所知UIView底层都是覆盖着一层CALayer,而这个CALayer正是在/System/Library/Frameworks/QuartzCore.framework中定义,所以几乎所有的UI控件都是基于Core Graphics完成的。想要自定制UI组件Core Graphics是个很不错的选择。当然还有基于它封装的上层API UIBezierPath。

    作者:Olivia_Zqy

    相关文章

      网友评论

      • Accepted_:您好,我这边是把一个倾斜的Label控件当做水印添加到图片上,现在字体大小没问题了,只是位置不对。推测是因为画布旋转,x,y,width,Height都改变了,您知道这种情况应该怎么办么
      • XIAOHANG:绘制原点的平移和旋转的图片的应该弄错了吧。
      • OliviaZqy:获取图片主色调的链接地址之前粘贴错了,已修正
        acon:@不修边幅8 怎么放到图片下面的呢?
        不修边幅8:怎么做到水印在图片下面呢
      • 没八阿哥的程序:能否贴一下获取对比色的方法,学习一下
      • ios开发者:我用swift语言改写的,怎么不能使用啊
        Suny8526:参考这段代码,用swift写了一下,确实无效。兄弟你解决了吗?
        ios开发者:@Olivia_Zqy 最后一句,在swift修改为context.restoreGState()
        但是直接闪退了,采用oc就正确的
        OliviaZqy:@ios开发者 遗漏了哪段代码了吧

      本文标题:iOS-为图片添加倾斜水印效果

      本文链接:https://www.haomeiwen.com/subject/wjtmmxtx.html