所謂水印是向數(shù)據(jù)多媒體(如圖像、聲音、視頻信號(hào)等)中添加某些數(shù)字信息以達(dá)到文件真?zhèn)舞b別、版權(quán)保護(hù)等功能。嵌入的水印信息隱藏于宿主文件中,不影響原始文件的可觀性和完整性。也就是在圖片上打上半透明的標(biāo)記。我們先來(lái)看一下Demo的運(yùn)行效果圖。轉(zhuǎn)載請(qǐng)聯(lián)系作者獲取授權(quán),并標(biāo)明文章作者,謝謝!

核心技術(shù):CoreGraphics繪圖。閑話少說(shuō),上代碼。
#define HORIZONTAL_SPACE 30//水平間距
#define VERTICAL_SPACE 50//豎直間距
#define CG_TRANSFORM_ROTATION (M_PI_2 / 3)//旋轉(zhuǎn)角度(正旋45度 || 反旋45度)
/**
根據(jù)目標(biāo)圖片制作一個(gè)蓋水印的圖片
@param originalImage 源圖片
@param title 水印文字
@param markFont 水印文字font(如果不傳默認(rèn)為23)
@param markColor 水印文字顏色(如果不傳遞默認(rèn)為源圖片的對(duì)比色)
@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;
//為了防止圖片失真,繪制區(qū)域?qū)捀吆驮紙D片寬高一樣
UIGraphicsBeginImageContext(CGSizeMake(viewWidth, viewHeight));
//先將原始image繪制上
[originalImage drawInRect:CGRectMake(0, 0, viewWidth, viewHeight)];
//sqrtLength:原始image的對(duì)角線length。在水印旋轉(zhuǎn)矩陣中只要矩陣的寬高是原始image的對(duì)角線長(zhǎng)度,無(wú)論旋轉(zhuǎn)多少度都不會(huì)有空白。
CGFloat sqrtLength = sqrt(viewWidth*viewWidth + viewHeight*viewHeight);
//文字的屬性
NSDictionary *attr = @{
//設(shè)置字體大小
NSFontAttributeName: font,
//設(shè)置文字顏色
NSForegroundColorAttributeName :color,
};
NSString* mark = title;
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:mark attributes:attr];
//繪制文字的寬高
CGFloat strWidth = attrStr.size.width;
CGFloat strHeight = attrStr.size.height;
//開(kāi)始旋轉(zhuǎn)上下文矩陣,繪制水印文字
CGContextRef context = UIGraphicsGetCurrentContext();
//將繪制原點(diǎn)(0,0)調(diào)整到源image的中心
CGContextConcatCTM(context, CGAffineTransformMakeTranslation(viewWidth/2, viewHeight/2));
//以繪制原點(diǎn)為中心旋轉(zhuǎn)
CGContextConcatCTM(context, CGAffineTransformMakeRotation(CG_TRANSFORM_ROTATION));
//將繪制原點(diǎn)恢復(fù)初始值,保證當(dāng)前context中心和源image的中心處在一個(gè)點(diǎn)(當(dāng)前context已經(jīng)旋轉(zhuǎn),所以繪制出的任何layer都是傾斜的)
CGContextConcatCTM(context, CGAffineTransformMakeTranslation(-viewWidth/2, -viewHeight/2));
//計(jì)算需要繪制的列數(shù)和行數(shù)
int horCount = sqrtLength / (strWidth + HORIZONTAL_SPACE) + 1;
int verCount = sqrtLength / (strHeight + VERTICAL_SPACE) + 1;
//此處計(jì)算出需要繪制水印文字的起始點(diǎn),由于水印區(qū)域要大于圖片區(qū)域所以起點(diǎn)在原有基礎(chǔ)上移
CGFloat orignX = -(sqrtLength-viewWidth)/2;
CGFloat orignY = -(sqrtLength-viewHeight)/2;
//在每列繪制時(shí)X坐標(biāo)疊加
CGFloat tempOrignX = orignX;
//在每行繪制時(shí)Y坐標(biāo)疊加
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);
}
}
//根據(jù)上下文制作成圖片
UIImage *finalImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGContextRestoreGState(context);
return finalImg;
}
下面我們講一下原理
1.繪制源圖片
拿到源圖片,根據(jù)源圖片尺寸調(diào)用UIGraphicsBeginImageContext方法將源圖片繪制在畫(huà)布上。
2.繪制水印文字:
要想繪制出傾斜任意角度的文字首先我們需要將畫(huà)布傾斜,這樣畫(huà)出來(lái)的文字可以和沒(méi)有傾斜的圖片形成角度對(duì)比,給人感官上文字是傾斜的。
Demo中采用CGContextConcatCTM方法旋轉(zhuǎn)畫(huà)布,但是筆者查閱資料發(fā)現(xiàn)該方法并不是繞著中心點(diǎn)旋轉(zhuǎn),而是繞著繪制原點(diǎn)(也就是初始值0,0)旋轉(zhuǎn)。所以轉(zhuǎn)出來(lái)是這樣的效果。

如果在這樣的畫(huà)布上繪制水印很明顯不是我們想要的。所以我們先將繪制原點(diǎn)平移到源圖片區(qū)域的中心點(diǎn),然后旋轉(zhuǎn)角度,最后在按照之前平移的X軸,Y軸數(shù)據(jù)反方向平移回去。這樣就能保證旋轉(zhuǎn)完畫(huà)布的中心點(diǎn)和源圖片的中心點(diǎn)在同一位置。


注意:在旋轉(zhuǎn)過(guò)后當(dāng)前畫(huà)布的X軸,Y軸方向也跟著旋轉(zhuǎn),所以再次平移可以回到中心點(diǎn)。
這樣我們得到了一個(gè)傾斜的畫(huà)布開(kāi)始繪制文字就可以了,還需注意如果我們還按照源圖片大小的區(qū)域去繪制水印文字難免會(huì)有空白的地方(我們不知道水印要旋轉(zhuǎn)多少度),所以我們要保證不出現(xiàn)空白,采用以源圖片中心點(diǎn),長(zhǎng)寬為源圖片對(duì)角線長(zhǎng)度的繪制區(qū)域。這樣無(wú)論旋轉(zhuǎn)多少度都不會(huì)出現(xiàn)空白。如下圖:

所以我們需要根據(jù)源圖片的長(zhǎng)寬計(jì)算出orignX和orignY(向上和向左偏移多少開(kāi)始繪制)。同時(shí)根據(jù)水印文字的長(zhǎng)寬,計(jì)算出整個(gè)繪制區(qū)域可以繪制出多少個(gè)水印,然后for循環(huán)繪制。
最后結(jié)束繪圖調(diào)用UIGraphicsGetImageFromCurrentImageContext形成一個(gè)閉合區(qū)間的圖片返回。
注意:代碼中的mostColor方法作用是:根據(jù)圖片獲取一個(gè)對(duì)比色保證水印能清楚顯示。參考地址:iOS代碼獲取圖片主色調(diào)
總結(jié)
Core Graphics是一個(gè)古老的基于C的繪圖專用API,也被稱為QuartZ或QuartZ 2D,是一個(gè)二維繪圖引擎。真如我們所知UIView底層都是覆蓋著一層CALayer,而這個(gè)CALayer正是在/System/Library/Frameworks/QuartzCore.framework中定義,所以幾乎所有的UI控件都是基于Core Graphics完成的。想要自定制UI組件Core Graphics是個(gè)很不錯(cuò)的選擇。當(dāng)然還有基于它封裝的上層API UIBezierPath。
作者:Olivia_Zqy